Index: /gsdl/trunk/configure.in
===================================================================
--- /gsdl/trunk/configure.in	(revision 16560)
+++ /gsdl/trunk/configure.in	(revision 16561)
@@ -454,23 +454,12 @@
 gsdlprefix=`pwd`
 ac_configure_args="$ac_configure_args --bindir=$gsdlprefix/bin/$gsdlos "
-AC_CONFIG_SUBDIRS(packages indexers)
+AC_CONFIG_SUBDIRS(gs2common gs2build gs2runtime)
 
 # the list of folders in the src folder
-srclist="src/lib/Makefile \
-	 src/hashfile/Makefile \
-         src/colservr/Makefile \
-         src/corba/Makefile \
-         src/protocol/Makefile \
-         src/recpt/Makefile \
-         src/gdbmedit/db2txt/Makefile \
-         src/gdbmedit/txt2db/Makefile \
-         src/gdbmedit/gdbmget/Makefile \
-         src/gdbmedit/gdbmset/Makefile \
-         src/oaiservr/Makefile \
-         src/z3950/Makefile \
-         src/getpw/Makefile \
-         src/phind/generate/Makefile"
-
-AC_OUTPUT(packages/Makefile Makefile $srclist $moduleDirs)
-
-
+srclist="gs2common/Makefile \
+         gs2build/Makefile \
+         gs2runtime/Makefile"
+
+AC_OUTPUT(Makefile $srclist $moduleDirs)
+
+
Index: /gsdl/trunk/gs2build/packages/cpan/README
===================================================================
--- /gsdl/trunk/gs2build/packages/cpan/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/cpan/README	(revision 16561)
@@ -0,0 +1,42 @@
+This directory contains perl modules from cpan for use with the Greenstone
+collection building code. After a "make install" these modules will be
+installed to the gsdl/perllib/cpan directory.
+
+Note that pure perl modules (i.e. those not containing any C code to
+compile) don't need building so they don't live in the packages directory
+tree (they live in perllib/cpan).
+
+
+The following modules are currently included:
+
+XML::Parser: Copyright (c) 1998-2000 Larry Wall and Clark Cooper
+ 
+ - Version 2.34
+
+  Originally, we used Version 2.27 cut down a bit for size, plus
+  a few modifications to Makefile.PL.  See below.
+
+
+HISTORY
+-------
+
+Mar 2005
+
+XML::Parser
+
+ - Version 2.34 and version 2.27
+added MAKEFILE_OLD => 'Makefile' to the Makefile.PL WriteMakeFile directive.
+previously, doing a make clean renamed the Makefile to Makefile.old, so then
+a make wouldn't work. Now Makefile gets renamed to Makefile. Hopefully this
+won't stuff anything else up.
+ 
+< Aug 2004
+
+XML::Parser: Copyright (c) 1998-2000 Larry Wall and Clark Cooper
+
+- Version 2.27 - as downloaded from cpan.org but with a few files removed
+  (to minimize the size of the tarball we distribute) and minor Greenstone
+  specific modifications to Makefile.PL. Originally used version 2.29 but
+  it appeared to be incompatible with perl 5.6.1.
+
+
Index: /gsdl/trunk/gs2build/packages/html-tidy/readme-tidy.txt
===================================================================
--- /gsdl/trunk/gs2build/packages/html-tidy/readme-tidy.txt	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/html-tidy/readme-tidy.txt	(revision 16561)
@@ -0,0 +1,10 @@
+This is HTML Tidy, which was based at:
+	http://www.w3.org/People/Raggett/tidy/
+
+It has now been maintained by Source Forge, and is currently based at:
+	http://tidy.sourceforge.net/
+
+We're currently using the source created at 4 August 2000 which is Dave Raggett's latest version of HTML Tidy.
+
+Veronica Liesaputra
+February 2007
Index: /gsdl/trunk/gs2build/packages/isis-gdl/AbstractIsisDb.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/AbstractIsisDb.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/AbstractIsisDb.h	(revision 16561)
@@ -0,0 +1,171 @@
+/**********************************************************************
+ *
+ * AbstractIsisDb.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+///////////////////////////////////////////////////////////////////////////////////
+// AbstractIsisDb.h -- Abstract Base Class Definitions for an ISIS db
+//
+//
+// The objective is to provide an interface for creating families of Isis
+// db without specifying their concrete classes. 
+// ABSTRACT FACTORY PATTERN is used.
+//
+// Author: J-C Dauphin, UNESCO, August 2000
+///////////////////////////////////////////////////////////////////////////////////
+ 
+#ifndef __ABSTRACT_ISISDB_H__
+#define __ABSTRACT_ISISDB_H__
+
+#include <vector>
+#include "IsisDef.h"
+#include "mytchar.h"
+
+
+/////////////////////////////////////////////////////////////////////////////////// 
+//   Abstract Base Class, set the DB interface. All db concrete classes must be
+//   derived from this class.
+//
+// Note that this abstract base class can be extended by specifying abstract base
+// classes for class class MfFile, class XrfFile, class CFdtCWorksheet, 
+// class CDisplayFormat, class CFieldSelectTable, class CSortTable, 
+// class MfRecord and struct SMfHeader. Pure virtual methods would then be added
+// in the class AbstractIsisDbFactory:
+//
+// 	virtual AbstractMfFile* makeMfFile() = 0;
+//	virtual AbstractXrfFile* makeXrfFile() = 0;
+//  ................
+ 
+
+class MfFile;
+class XrfFile;
+class CFdt; // Forward 
+
+class CWorksheet;
+class CDisplayFormat;
+class CFieldSelectTable;
+class CSortTable;
+class MfRecord;
+struct SMfHeader;
+ 
+class AbstractIsisDb 
+{
+protected:
+	ustring    	name;
+
+    MfFile*   mf_;   // Has-a master file
+    XrfFile*  xf_;   // Has-a cross ref file
+    CFdt*     fdt_;  // Has-a FDT
+
+    std::vector<CWorksheet*>            apWS_;   // Worksheets
+    std::vector<CDisplayFormat*>        apDFMT_; // Display formats
+    std::vector<CFieldSelectTable*>     apFST_;  // Field Select Tables
+    std::vector<CSortTable*>            apST_;   // Sort Tables   
+    
+public:
+	AbstractIsisDb() { };                    // Trivial default C++ constructor
+	virtual ~AbstractIsisDb() { };           // Trivial C++ destructor
+
+// Opening the database
+   virtual int OpenDb(const TCHAR *fname, FileSystem::AccessMode mode =
+	                                          FileSystem::FILE_READWRITE) = 0; 
+// Creating the database
+   virtual int CreateDb(const TCHAR* fname) = 0;
+
+// Closing the database
+   virtual void CloseDb()   = 0;    // Close the db
+   virtual void ClearDb()   = 0;    // Clear the db
+   virtual void DestroyDb() = 0;    // Delete the db
+
+// Recovery
+   virtual bool IsDamaged() = 0;   // Test if the database is damaged
+   virtual bool Recover()   = 0;   // Recover the db
+
+// Transactions
+   virtual void BeginTransaction()     = 0;   // Begin a transaction
+   virtual void CommitTRansaction()    = 0;   // Commit a transaction
+   virtual void RollbackTransaction()  = 0;   // Rollback a transaction
+   virtual bool InTransaction()        = 0;   // Test if transaction is pending
+
+// (FDT)
+   virtual void SetFdt(CFdt *pFdt) { fdt_ = pFdt; }
+   virtual CFdt* GetFdt() { return fdt_; }
+
+//  worksheets (WORKSHEETS). 
+// (used to create/update the master records of the db)
+   virtual void SetWorksheetAt(int i, CWorksheet* pWS) { apWS_.insert(apWS_.begin()+i, pWS); }
+   virtual CWorksheet* GetWorksheetAt(int i)           { return apWS_[i]; }
+   virtual void RemoveWorksheetAt(int i)               { apWS_.erase(apWS_.begin()+i); }
+
+// display formats (DISPLAY FORMATS).
+// (used for specifying the formatting requirements for either on-line
+// display of records during searching or for the generation of printed
+// output documents.
+   virtual void SetDisplayFormatAt(int i, CDisplayFormat* pDFMT) { apDFMT_.insert(apDFMT_.begin()+i, pDFMT); }
+   virtual CDisplayFormat* GetDisplayFormatAt(int i)             { return apDFMT_[i]; }
+   virtual void RemoveDisplayFormatAt(int i)                     { apDFMT_.erase(apDFMT_.begin()+i); }
+
+// Field Select Table (FST).
+// One FST defines the fields of the database to be made searchable
+// through the inverted dile.
+   virtual void SetFieldSelectTableAt(int i, CFieldSelectTable* pFST) { apFST_.insert(apFST_.begin()+i, pFST); }
+   virtual CFieldSelectTable* GetFieldSelectTableAt(int i)            { return apFST_[i]; }
+   virtual void RemoveFieldSelectTableAt(int i)                       { apFST_.erase(apFST_.begin()+i); }
+
+   virtual void CreateIndex(int i) = 0;
+   virtual void DropIndex(int i)   = 0;
+
+// Sort Select Table (SST).
+// One FST defines the fields of the database to be made searchable
+// through the inverted dile.
+
+   virtual void SetSortTableAt(int i, CSortTable* pST) { apST_.insert(apST_.begin()+i, pST); }
+   virtual CSortTable* GetSortTableAt(int i)           { return apST_[i]; }
+   virtual void RemoveSortTableAt(int i)               { apST_.erase(apST_.begin()+i); }
+
+   virtual void  CreateDbRecord(MfRecord &m) = 0;
+   virtual void  UpdateDbRecord(MfRecord &m) = 0;
+   
+   virtual int   ReadDbRecord(long mfn, MfRecord &m) = 0;
+   virtual int   ReadNextDbRecord(MfRecord &m)       = 0;
+   
+   virtual bool  DeleteRecord(long mfn)              = 0;
+
+   virtual void  PrintDbRecord(MfRecord &m)          = 0;
+   virtual void  PrintDbHdr()                        = 0;
+
+   virtual long GetNextMfn() = 0;
+   virtual long GetNextActiveMfn(long mfn=-1) = 0;
+
+   virtual SMfHeader& GetSMfHeader() = 0;
+   virtual _TCHAR *GetIdxFileName()  = 0;
+ 
+
+};
+ 
+class AbstractIsisDbFactory
+{
+public:
+	virtual AbstractIsisDb* MakeIsisDb() = 0;
+};
+#endif   // __ABSTRACT_ISISDB_H__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/BlkFile.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/BlkFile.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/BlkFile.h	(revision 16561)
@@ -0,0 +1,155 @@
+/**********************************************************************
+ *
+ * BlkFile.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+//////////////////////////////////////////////////////////////////////
+//  BLKFILE.H
+//
+//  Class for accessing a file per blocks of blksize bytes, blocks
+//  are sequential from 1 to nblock.
+//                                                                       
+//////////////////////////////////////////////////////////////////////
+ 
+#ifndef _BLKFILE_H_
+#define _BLKFILE_H_
+#include "CacheMan.h"
+#include "File.h"
+ 
+//////////////////////////
+// Block I/O file class
+//
+ 
+ 
+ 
+class BlkFile : public CFileBase
+{
+public:
+   enum { blk_size = 512 };
+protected:
+   unsigned     blkSize_;      // Block Size
+   CacheManager cache_;
+public:
+   BlkFile(int bs=BlkFile::blk_size);
+   BlkFile(const _TCHAR *fname, int bs=BlkFile::blk_size);
+   virtual FileError Close();
+   virtual ~BlkFile();
+   virtual void ReadBlk(void* d, long b);
+   virtual void WriteBlk(void* d, long b);
+   virtual void ReadBlk(void* d);
+   virtual void WriteBlk(void* d);
+
+   void SetBlockSize(int blockSize) { blkSize_ = blockSize; }
+   int  GetBlockSize() { return blkSize_; }
+};
+ 
+////////////////////////////////////////
+ 
+inline BlkFile::BlkFile(int bs) :
+// Makes a Blocked File object that isn't connected to a file
+// Block size is bs
+CFileBase(), blkSize_(bs)
+{
+	cache_.Create(bs, 40);
+	cache_.Connect(this);
+   // Nothing more to do
+}
+ 
+ 
+////////////////////////////////////////
+ 
+inline BlkFile::BlkFile(const _TCHAR *fname, int bs) :
+// Makes a Blocked File object connected to a file
+// Block size is bs
+CFileBase(), blkSize_(bs), cache_(bs, 20)
+{
+	CFileBase::Open(fname, FileSystem::FILE_READWRITE);
+	cache_.Create(bs, 40);
+	cache_.Connect(this);
+   // Nothing more to do
+}
+ 
+////////////////////////////////////////
+ 
+inline BlkFile::~BlkFile()
+// Destructor  for BlkFile object
+{
+	cache_.Disconnect();
+   // Nothing more to do
+}
+ 
+////////////////////////////////////////////
+
+
+inline FileError BlkFile::Close()
+{
+	if (IsOpen())
+	    cache_.Invalidate();
+	return CFileBase::Close();
+}
+////////////////////////////////////////
+ 
+inline void BlkFile::ReadBlk(void* d, long b)
+// Reads block number b into d, b is in range [1:NBLOCK]
+{
+	ASSERT(d!=NULL && b>0);
+	//TRACE("\nBlkFile::ReadBlk -- %s Read Block %ld", file_name, b);
+    //CFileBase::Fetch(d, blkSize_, (b-1)*blkSize_);
+	cache_.Read((b-1)*blkSize_, d);
+}
+ 
+////////////////////////////////////////
+ 
+inline void BlkFile::WriteBlk(void* d, long b)
+// Writes blksize bytes from d at block number b
+{
+	ASSERT(d!=NULL && b>0);
+	//TRACE("\nBlkFile::WriteBlk -- %s Write Block %ld", file_name, b);
+    //CFileBase::Store(d, blkSize_, (b-1)*blkSize_);
+	cache_.Write((b-1)*blkSize_, d);
+}
+ 
+ 
+////////////////////////////////////////
+ 
+inline void BlkFile::ReadBlk(void* d)
+// Reads block at current address into d
+{
+	ASSERT(d!=NULL);
+    //CFileBase::Fetch(d, blkSize_);
+	long addr = CFileBase::FilePosition();
+	cache_.Read(addr, d);
+}
+ 
+////////////////////////////////////////
+ 
+inline void BlkFile::WriteBlk(void* d)
+// Writes blkSize_ bytes from d at current address
+{
+	ASSERT(d!=NULL);
+    //CFileBase::Store(d, blkSize_);
+	long addr = CFileBase::FilePosition();
+	cache_.Write(addr, d);
+}
+ 
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.cpp	(revision 16561)
@@ -0,0 +1,170 @@
+/**********************************************************************
+ *
+ * CRC32.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+// CRC32.cpp --
+//  
+// The CRC32 functions (Cyclic Redundancy Check) are used to
+// calculate a sophisticated checksum based on the algebra of
+// polynomials. The Cyclic Redundancy Check, is a way to detect
+// bit errors that occur during data storage or transmission.
+// The CRC-32 algorithm operates on a block of data as a single
+// large numerical value. The algorithm divides this large value
+// by the CRC-32 polynomial or generator polynomial, leaving the
+// remainder 32-bit, which is the checksum. 
+//
+// #include "stdafx.h" 
+#include <iomanip>
+#include "CRC32.h"
+#include "CRCTab.h"
+
+unsigned long CalcCRC32(const char *buf, unsigned len)
+// Calculate a 32-bit CRC for a raw pattern of bytes.
+// Returns a 32-bit checksum.
+{
+ unsigned long CRC=0xffffffffL;
+ unsigned int n = len;
+
+ while(n--) 
+   CRC=crc32tab[(CRC ^ (*buf++)) & 0xFF] ^ ((CRC>>8) & 0x00ffffffL);
+
+ return CRC ^ 0xffffffffL;
+}
+
+unsigned long CalcCRC32(char *buf, unsigned len)
+// Calculate a 32-bit CRC for a raw pattern of bytes.
+// Returns a 32-bit checksum.
+{
+ unsigned long CRC=0xffffffffL;
+ unsigned int n = len;
+ char *p = (char *)buf;
+ while(n--) 
+   CRC=crc32tab[(CRC ^ (*p++)) & 0xFF] ^ ((CRC>>8) & 0x00ffffffL);
+
+ return CRC ^ 0xffffffffL;
+}
+
+unsigned long CalcCRC32(unsigned char c, unsigned long CRC)
+// Calculate a 32-bit CRC table value for a single byte.
+// NOTE: The initial CRC value must be set to 0xffffffffL
+// and the final 32-bit value that must be XOR'ed with
+// 0xffffffffL to obtain the checksum.
+{
+  unsigned int i;
+  i = (unsigned int)c;
+  i &= 0xFF; // Reset all the bits
+  CRC=crc32tab[(CRC ^ i) & 0xFF] ^ ((CRC>>8) & 0x00ffffffL);
+  return CRC;
+}
+
+unsigned long CalcCRC32(std::fstream &infile)
+// Calculate a 32-bit CRC for a file. Assumes the stream
+// is already open. Returns a 32-bit checksum.
+{
+ unsigned long CRC=0xffffffffL;
+#ifdef _WIN32
+ char c;
+#else
+ unsigned char c;
+#endif
+ unsigned int i;
+
+ // Rewind to the start of the stream
+ infile.clear(); 
+ infile.seekg(0, std::ios::beg);
+ infile.seekp(0, std::ios::beg);
+
+ while(!infile.eof()) {
+   c=infile.get();
+   i = (unsigned int)c;
+   i &= 0xFF; // Reset all the bits
+   if(infile.eof()) break;
+   CRC=crc32tab[(CRC ^ i) & 0xFF] ^ ((CRC>>8) & 0x00ffffffL);
+ }
+ return CRC ^ 0xffffffffL;
+}
+
+#ifdef __USE_CRC32_TABLE_FUNCTIONS__
+int MakeCRC32(ostream &stream)
+// Write a CRC 32 table for a byte-wise 32-bit CRC calculation
+// based on the Autodin/Ethernet/ADCCP polynomial of 0x4C11DB7:
+// 0000 0100 1100 0001 0001 1101 1011 0111 (binary) or a poly of
+// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7 +x^5+x^4+x^2+x^1+x^0
+// In this representation the coefficient of x^0 is stored in the 
+// MSB of the 32-bit word and the coefficient of x^31 is stored in 
+// the LSB. Thus 0x4C11DB7 becomes 0xEDB88320:
+// 1110 1101 1011 1000 1000 0011 0010 0000 (binary)
+
+// Adding the polynomials is performed using an exclusive-or, and
+// multiplying a polynomial by x is a right shift by one. If the 
+// polynomial is called "p", and each byte is represented as polynomial
+// "q", with the lowest power in the most significant bit (so the byte
+// 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) 
+// mod "p", where "a" mod "b" means the remainder after dividing "a" by
+// "b". This calculation is done using the shift-register method of
+// multiplying and taking the remainder. The register is initialized
+// to zero, and for each incoming bit, x^32 is added mod "p" to the
+// register if the bit is a one (where x^32 mod p is p+x^32 = x^26+...+1),
+// and the register is multiplied mod "p" by "x" (which is shifting right
+// by one and adding x^32 mod "p" if the bit shifted out is a one.) This
+// algorithm starts with the highest power (least significant bit) of "q"
+// and repeats for all eight bits of "q".
+{
+  unsigned long c; // CRC shift register 
+  unsigned long e; // Polynomial exclusive-or pattern 
+  int i;           // Counter for all possible eight bit values 
+  int k;           // Byte being shifted into crc apparatus 
+
+  // Terms of polynomial defining this crc (except x^32): 
+  static const int p[14] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+  // Make exclusive-or pattern from polynomial 
+  e = 0;
+  for (i = 0; i < sizeof(p)/sizeof(int); i++)
+    e |= 1L << (31 - p[i]);
+
+  // Compute and print table of CRC's, five per line 
+  stream << endl;
+  stream << "  0x00000000U";
+  for (i = 1; i < 256; i++)
+  {
+    c = 0;
+    for (k = i | 256; k != 1; k >>= 1)
+    {
+      c = c & 1 ? (c >> 1) ^ e : c >> 1;
+      if (k & 1)
+        c ^= e;
+    }
+
+    if(i % 5)
+      stream << ", 0x" << setfill('0') << setw(8) << hex << c << "U";
+    else
+      stream << "," << endl << "  0x" << setfill('0') << setw(8) << hex
+	     << c << "U";
+  }
+  stream << endl;
+  return 1;
+}
+#endif // __USE_CRC32_TABLE_FUNCTIONS__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/CRC32.h	(revision 16561)
@@ -0,0 +1,52 @@
+/**********************************************************************
+ *
+ * CRC32.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+// CRC32.H
+//
+// The CRC32 functions (Cyclic Redundancy Check) are used to
+// calculate a sophisticated checksum based on the algebra of
+// polynomials. The Cyclic Redundancy Check, is a way to detect
+// bit errors that occur during data storage or transmission.
+// The CRC-32 algorithm operates on a block of data as a single
+// large numerical value. The algorithm divides this large value
+// by the CRC-32 polynomial or generator polynomial, leaving the
+// remainder 32-bit, which is the checksum. 
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#include <fstream>
+
+unsigned long CalcCRC32(char *buf, unsigned len);
+unsigned long CalcCRC32(const char *buf, unsigned len);
+unsigned long CalcCRC32(unsigned char c, unsigned long CRC);
+unsigned long CalcCRC32(std::fstream &infile);
+
+#ifdef __USE_CRC32_TABLE_FUNCTIONS__
+#include <iostream>
+int makeCRC32(std::ostream &stream);
+#endif
+
+#endif  // __CRC32_H__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/CRCTab.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/CRCTab.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/CRCTab.h	(revision 16561)
@@ -0,0 +1,100 @@
+/**********************************************************************
+ *
+ * CRCTab.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+// CRCTAB.H --
+
+// CRC 32 table of all possible single-byte values. The table is
+// the CRC of all possible eight bit values. This is all the info
+// needed to generate 32-bit CRC's on data one a byte at a time 
+// for all combinations of CRC register values and incoming bytes.   
+// This table was generated with the makeCRC32() function. It is
+// based on the Autodin/Ethernet/ADCCP polynomial of 0x4C11DB7:
+// 0000 0100 1100 0001 0001 1101 1011 0111 (binary) or a poly of
+// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7 +x^5+x^4+x^2+x^1+x^0
+// In this representation the coefficient of x^0 is stored in the 
+// MSB of the 32-bit word and the coefficient of x^31 is stored in 
+// the LSB. Thus 0x4C11DB7 becomes 0xEDB88320:
+// 1110 1101 1011 1000 1000 0011 0010 0000 (binary)
+
+#ifndef __CRC32_TABLE_HPP__
+#define __CRC32_TABLE_HPP__
+
+const unsigned long crc32tab[256] = {
+  0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
+  0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
+  0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
+  0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
+  0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
+  0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
+  0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
+  0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
+  0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
+  0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
+  0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
+  0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
+  0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
+  0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
+  0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
+  0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
+  0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
+  0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
+  0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
+  0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
+  0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
+  0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
+  0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
+  0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
+  0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
+  0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
+  0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
+  0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
+  0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
+  0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
+  0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
+  0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
+  0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
+  0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
+  0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
+  0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
+  0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
+  0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
+  0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
+  0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
+  0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
+  0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
+  0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
+  0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
+  0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
+  0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
+  0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
+  0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
+  0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
+  0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
+  0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
+  0x2d02ef8dU
+};
+
+#endif // __CRC32_TABLE_HPP__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.cpp	(revision 16561)
@@ -0,0 +1,213 @@
+/**********************************************************************
+ *
+ * CacheMan.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#include "stdafx.h"
+#include "File.h"
+
+#include "CacheMan.h"
+
+
+CacheManager::CacheManager()
+{
+	maxblocks_ = 0;
+}
+CacheManager::CacheManager(unsigned blocksz, unsigned mxblks)
+{
+	PRECONDITION(maxblocks_ == 0);
+	maxblocks_ = mxblks;
+	nused_     = 0;
+	blocksize_ = blocksz;
+	buff_      = new char[blocksize_*maxblocks_];
+	diskAddrs_ = new FileSystem::FAU_t[maxblocks_];
+	useCounts_ = new unsigned[maxblocks_];
+	theFile_   = NULL;
+}
+/*
+ * Construct a cache manager for blocks of size blocksz and mxblks buffers
+ */
+void CacheManager::Create(unsigned blocksz, unsigned mxblks)
+{
+	PRECONDITION(maxblocks_ == 0);
+	maxblocks_ = mxblks;
+	nused_     = 0;
+	blocksize_ = blocksz;
+	buff_      = new char[blocksize_*maxblocks_];
+	diskAddrs_ = new FileSystem::FAU_t[maxblocks_];
+	useCounts_ = new unsigned[maxblocks_];
+	theFile_   = NULL;
+}
+
+//
+// Connect to a CFileBase
+//
+void CacheManager::Connect(CFileBase* file)
+{
+	PRECONDITION(nused_ == 0);
+	theFile_   = file;
+}
+//
+// Disconnect the cache from the CFileBase
+//
+void CacheManager::Disconnect()
+{
+	if (theFile_)
+	{
+		if (theFile_->IsOpen())
+		    Flush();
+	}
+	theFile_   = NULL;
+}
+
+void CacheManager::Erase()
+{
+	Disconnect();
+	delete[] useCounts_;
+	delete[] diskAddrs_;
+	delete[] buff_;
+	maxblocks_ = 0;
+}
+
+
+CacheManager::~CacheManager()
+{
+	Disconnect();
+	delete[] useCounts_;
+	delete[] diskAddrs_;
+	delete[] buff_;
+}
+
+bool CacheManager::Flush()
+{
+	PRECONDITION(theFile_ != NULL && theFile_->IsOpen());
+	// Because we use "write through", this is all that's necessary:
+	if (theFile_->Flush() == FILE_NO_ERROR)
+		return true;
+	return false;
+}
+
+void CacheManager::Invalidate()
+{
+	nused_ = 0;
+}
+
+bool CacheManager::Read(FileSystem::FAU_t locn, void* dat)
+{
+	PRECONDITION(dat != NULL);
+	size_t islot = AgeAndFindSlot(locn);
+	
+	if (islot == INVALID_NPOS)
+	{
+        // Not in buffer;  we'll have to read it in from disk.
+	    // Get a free slot.
+		if ((islot = GetFreeSlot()) == INVALID_NPOS)
+			return false;
+		diskAddrs_[islot] = locn;
+		if (!(theFile_->SeekTo(locn) == locn) || 
+			!(theFile_->Fetch(buff_ + islot*blocksize_, blocksize_) == FILE_NO_ERROR))
+			return false;
+	}
+	useCounts_[islot] = 0;
+	memcpy(dat, buff_ + islot*blocksize_, blocksize_);
+	return true;
+}
+
+bool CacheManager::Write(FileSystem::FAU_t locn, void* dat)
+{
+	PRECONDITION(dat != NULL);
+	PRECONDITION(theFile_->ReadyForWriting());
+	size_t islot = AgeAndFindSlot(locn);
+	
+	if (islot == INVALID_NPOS)
+	{
+        // Not in buffer; find a free slot.
+		if ((islot = GetFreeSlot()) == INVALID_NPOS)
+			return false;
+		diskAddrs_[islot] = locn;
+	}
+	
+	useCounts_[islot] = 0;
+	memcpy(buff_ + islot*blocksize_, dat, blocksize_);
+	if ((theFile_->SeekTo(locn) == locn) &&
+		(theFile_->Store(buff_ + islot*blocksize_, blocksize_) == FILE_NO_ERROR))
+		return true;
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Private helper functions	
+
+size_t CacheManager::AgeAndFindSlot(FileSystem::FAU_t locn)
+{
+	size_t islot = INVALID_NPOS;
+	for (register unsigned i = 0; i < nused_; i++)
+	{
+		if (diskAddrs_[i] == locn)
+			islot = i;
+		useCounts_[i]++;		// Age the blocks
+	}
+	return islot;
+}
+
+bool CacheManager::Flush(unsigned)
+{
+	if (theFile_->Flush() == FILE_NO_ERROR)
+		return true;
+	return false;
+}
+
+size_t CacheManager::GetFreeSlot()
+{
+	size_t islot;
+	
+	if (nused_ < maxblocks_)
+	{
+		islot = nused_++;		// Found an unused slot.
+	}
+	else 
+	{
+		// No free slots; get the Least Recently Used block
+		islot = LRU();
+
+		if (theFile_->ReadyForWriting())
+			if (!Flush(islot))
+			    islot = INVALID_NPOS;
+	}
+	return islot;
+}
+
+size_t CacheManager::LRU() const
+{
+	size_t islot = 0;
+	unsigned maxCount = useCounts_[0];
+	for (register unsigned i = 1; i < nused_; i++)
+	{
+		if (useCounts_[i] > maxCount)
+		{
+			maxCount = useCounts_[islot = i]; 
+		}
+	}
+	return islot;
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/CacheMan.h	(revision 16561)
@@ -0,0 +1,71 @@
+/**********************************************************************
+ *
+ * CacheMan.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef __CACHEMAN_H__
+#define __CACHEMAN_H__
+
+
+#include "File.h"
+
+class CacheManager
+{
+private:
+	
+	CFileBase*	       theFile_;
+	unsigned		   maxblocks_;  // Max # blocks in buff_er
+	unsigned		   nused_;	    // Number being used.
+	unsigned		   blocksize_;  // Size of a block
+	FileSystem::FAU_t* diskAddrs_;  // Its disk address
+	unsigned*		   useCounts_;  // Use count for the block
+	char*			   buff_;	    // The set of blocks.
+	
+public:
+	
+	CacheManager();
+	CacheManager(unsigned blocksz, unsigned mxblks);
+
+	~CacheManager();
+	
+	void        Create(unsigned blocksz, unsigned mxblks = 10);
+	void        Erase();
+	
+	void        Connect(CFileBase* file);
+	void        Disconnect();
+	bool  		Flush();	        // Perform any pending writes.
+	void		Invalidate();	    // Invalidate the entire cache
+	bool  		Read (FileSystem::FAU_t locn, void* dat);
+	bool  		Write(FileSystem::FAU_t locn, void* dat);
+	
+private:
+	
+	CacheManager(const CacheManager&);          // Private to insure no copies
+	void		operator=(const CacheManager&); // Ditto
+	size_t		AgeAndFindSlot(FileSystem::FAU_t);
+	bool  		Flush(unsigned);
+	size_t		GetFreeSlot();
+	size_t		LRU() const; // Find least recently used block
+};
+
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Checking.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Checking.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Checking.cpp	(revision 16561)
@@ -0,0 +1,65 @@
+/**********************************************************************
+ *
+ * Checking.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////
+// Checking -- 
+//               
+
+
+#include "stdafx.h"
+#include "File.h"
+#include "Master.h"
+#include "MFFile.h"
+#include "Checking.h"
+
+using namespace Checking;
+//---------------------------------------------------------------------
+// Checking::CheckMfHeader(const char *mfname, SMfHeader *mfh)
+//
+// Invariant to be respected by the mf header
+//---------------------------------------------------------------------
+void Checking::CheckMfHeader(const _TCHAR *mfname, SMfHeader &mfh)
+{
+	long size = FileSystem::FileSize(mfname);
+	TRACE(_T("\nsize=%ld "), size);
+	long mfSize = mfh.nxtmfb_ * MFBLKSIZE;
+	//ASSERT(size == mfSize);
+	ASSERT(mfh.nxtmfp_>0 && mfh.nxtmfp_<=MFBLKSIZE);
+	ASSERT(mfh.mftype_ == USERMST);
+	ASSERT(mfh.nxtmfn_>0);
+	ASSERT(mfh.ctlmfn_ == 0);
+	//assert(mfh.reccnt == 0);
+	//assert(mfh.mfcxx1 == 0);
+	//assert(mfh.mfcxx2 == 0);
+	//assert(mfh.mfcxx3 == 0);
+}
+
+void Checking::CheckMfRecordHeader(long addr)
+{
+
+}
+
+using namespace std;
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Checking.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Checking.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Checking.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**********************************************************************
+ *
+ * Checking.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+/////////////////////////////////////////////////////////////////////////////////
+// Checking.h --
+
+
+namespace Checking {
+   void CheckMfHeader(const _TCHAR *mfname, SMfHeader &mfh);
+   void CheckMfRecordHeader(long addr);
+
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Compat.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Compat.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Compat.h	(revision 16561)
@@ -0,0 +1,140 @@
+/**********************************************************************
+ *
+ * Compat.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef _WIN32
+
+#include <string>
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#define TCHAR  char
+#define BOOL   bool
+#define WORD   unsigned short
+#define DWORD  unsigned long
+#define INT32  int
+#define UINT32 unsigned int
+#define LONG   long int
+#define TRUE   true
+#define FALSE  false
+
+#define _MAX_PATH 512
+// #define SHRT_MAX  32732
+
+/* Formatted i/o */
+
+#define _tprintf    printf
+#define _ftprintf   fprintf
+#define _stprintf   sprintf
+#define _sntprintf  _snprintf
+#define _vtprintf   vprintf
+#define _vftprintf  vfprintf
+#define _vstprintf  vsprintf
+#define _vsntprintf _vsnprintf
+#define _tscanf     scanf
+#define _ftscanf    fscanf
+#define _stscanf    sscanf
+/* String conversion functions */
+
+#define _tcstod     strtod
+#define _tcstol     strtol
+#define _tcstoul    strtoul
+
+#define _itot       _itoa
+#define _ltot       _ltoa
+#define _ultot      _ultoa
+#define _ttoi       atoi
+#define _ttol       atol
+
+#define _ttoi64     _atoi64
+#define _i64tot     _i64toa
+#define _ui64tot    _ui64toa
+/* String functions */
+
+/* Note that _mbscat, _mbscpy and _mbsdup are functionally equivalent to 
+   strcat, strcpy and strdup, respectively. */
+
+#define _tcscat     strcat
+#define _tcscpy     strcpy
+#define _tcsdup     _strdup
+
+#define _tcslen     strlen
+#define _tcsxfrm    strxfrm
+
+
+// #define assert
+#define ASSERT
+#define AssertFailed
+
+// #define _T(par) par
+#define LPCTSTR const char *
+#define LPTSTR char *
+#define FAR
+#define TRACE
+#define PRECONDITION
+#define SIZE_T size_t
+
+
+
+#endif
+#define MoveMemory memmove
+#define ZeroMemory(p,size) memset(p,0,size)
+
+
+inline void fix_endianness(short int& x)
+{
+#if !defined(LITTLE_ENDIAN)
+  if (x < 0) {
+    unsigned short int ux = (unsigned short int) x;
+    x = (ux>>8) | 
+        (ux<<8);
+  }
+  else {
+    x = (x>>8) | 
+        (x<<8);
+  }
+#endif
+}
+
+inline void fix_endianness(long int& x)
+{
+#if !defined(LITTLE_ENDIAN)
+  if (x < 0) {
+    unsigned long ux = (unsigned long) x;
+    x = (ux>>24) | 
+      ((ux<<8) & 0x00FF0000) |
+      ((ux>>8) & 0x0000FF00) |
+      (ux<<24);
+  }
+  else {
+    x = (x>>24) | 
+      ((x<<8) & 0x00FF0000) |
+      ((x>>8) & 0x0000FF00) |
+      (x<<24);
+  }
+#endif
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/DLLCode.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/DLLCode.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/DLLCode.h	(revision 16561)
@@ -0,0 +1,74 @@
+/**********************************************************************
+ *
+ * DLLCode.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+/////////////////////////////////////////////////////////////////////////////
+// DLLCODE.H - include file used by all platforms to define macros required 
+//             to build static and dynamic libraries.
+//             The DLLCODE.H file is included in every include file extending
+//             the visibility of the macros defined here to the every source
+//             code file. 
+
+
+#ifndef __DLL_CODE_H__
+#define __DLL_CODE_H__
+
+#if defined (__MSVC_DLL__) // Defines the entry point for the DLL application
+//--------------------------------------------------------------------------
+// The following ifdef block is the standard way of creating macros which 
+// make exporting from a DLL simpler. All files within this DLL are compiled 
+// with the DLL_CODE_EXPORTS  symbol defined on the command line. This 
+// symbol should not be defined on any project that uses this DLL. This way 
+// any other project whose source files include this file see DLL_CODE_API 
+// functions as being imported from a DLL, whereas this DLL sees symbols
+// defined with this macro as being exported.
+//---------------------------------------------------------------------------
+#ifdef DLL_CODE_EXPORTS
+#define DLL_CODE_API __declspec(dllexport)
+#else
+#define DLL_CODE_API __declspec(dllimport)
+#endif
+#else // Macro used for non DLL code
+#define DLL_CODE_API
+#endif // __MSVC_DLL__
+
+// Force ANSI compatibility for Visual C++ .NET
+#if defined (__MSDEVNET__) && !defined (__USE_ANSI_CPP__)
+#define __USE_ANSI_CPP__
+#endif // __MSDEVNET__
+
+// Force ANSI compatibility for BCC32
+#if defined (__BCC32__) && !defined (__USE_ANSI_CPP__)
+#define __USE_ANSI_CPP__
+#endif // __BCC32__
+
+#if defined (__USE_ANSI_CPP__)
+// Macro used to define the std name space used by the ANSI 
+// Standard C++ library.
+#define GXSTD std
+#else // Use the old Standard C++ library by default  
+#define GXSTD
+#endif // __USE_ANSI_CPP__
+
+#endif // __DLL_CODE_H__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Debug.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Debug.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Debug.cpp	(revision 16561)
@@ -0,0 +1,213 @@
+#include "stdafx.h"
+#include "Debug.h"
+
+//---------------------------------------------------------------
+//
+// AssertFailed
+//
+//---------------------------------------------------------------
+#if DEBUG || ASSERTS_THROW
+void AssertFailed(const char* expr, const char* file, int line)
+{
+	/*
+	_TCHAR mesg[512];							
+
+	const char* fileName = file + strlen(file);		// strip off path
+	while (fileName > file && fileName[-1] != '\\')
+		--fileName;
+
+	_stprintf(mesg, _T("ASSERT(%s) in '%s' at line %d failed."), expr, fileName, line);
+	
+	MSG msg;							// remove pending quit messages so the message box displays
+	bool quitting = (bool) ::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
+
+	UINT32 flags = MB_OK +				// just an OK button
+				   MB_ICONERROR +		// display the icon for errors
+				   MB_DEFBUTTON1 +		// select the OK button
+				   MB_TASKMODAL +		// don't let the user do anything else in the app
+				   MB_SETFOREGROUND;	// bring the app to the front
+
+	(void) MessageBox(NULL, mesg, NULL, flags);
+//#if DEBUG
+//	if (gBreakOnAssert)
+//		BreakStrToDebugger(mesg);
+//#endif
+
+//#if ASSERTS_THROW
+	CString text = L"Internal (Assert) Error: ";
+	AfxThrowUserException( ); 
+//#endif
+*/
+}
+#endif	// DEBUG || ASSERTS_THROW
+
+
+//---------------------------------------------------------------
+//
+// RequireFailed
+//
+//---------------------------------------------------------------
+#if !DEBUG
+void RequireFailed(const char* expr, const char* file, int line)
+{
+	/*
+	char mesg[512];							
+
+	const char* fileName = file + strlen(file);		// strip off path
+	while (fileName > file && fileName[-1] != '\\')
+		--fileName;
+
+	sprintf(mesg, "There has been a fatal error in '%s' at line %d.", fileName, line);
+
+	MSG msg;							// remove pending quit messages so the message box displays
+	bool quitting = (bool) ::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
+
+	UINT32 flags = MB_OK +				// just an OK button
+				   MB_ICONERROR +		// display the icon for errors
+				   MB_DEFBUTTON1 +		// select the OK button
+				   MB_TASKMODAL +		// don't let the user do anything else in the app
+				   MB_SETFOREGROUND;	// bring the app to the front
+
+	(void) MessageBox(NULL, mesg, NULL, flags);
+
+	abort();
+	*/
+}
+#endif // !DEBUG
+
+//---------------------------------------------------------------
+//
+// ToStr (bool)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(bool value)
+{
+	return value ? L"true" : L"false";
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (char)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(char value)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%c", value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (int16, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(int16_t value, int32_t fieldWidth)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%*d", fieldWidth, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (uint16, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(uint16_t value, int32_t fieldWidth)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%*d", fieldWidth, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (int32, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(int32_t value, int32_t fieldWidth)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%*ld", fieldWidth, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (uint32, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(uint32_t value, int32_t fieldWidth)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%*lu", fieldWidth, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (float, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(float value, int32_t precision)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%.*f", precision, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (double, int32)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(double_t value, int32_t precision)
+{
+	wchar_t str[32];							
+	::swprintf(str, L"%.*f", precision, value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (const char*)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(const char* value)
+{
+	wchar_t str[2048];							
+	::swprintf(str, L"%s", value);
+	
+	return str;
+}
+
+
+//---------------------------------------------------------------
+//
+// ToStr (std::string)
+//
+//---------------------------------------------------------------
+std::wstring ToStr(const std::string& value)
+{
+	wchar_t str[2048];							
+	::swprintf(str, L"%s", value.c_str());
+	
+	return str;
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Debug.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Debug.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Debug.h	(revision 16561)
@@ -0,0 +1,161 @@
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+//	Abstract:	This file provides the following macros:
+//
+//				ASSERT(p) - If p is false and DEBUG is true drops into the 
+//				debugger, if ASSERTS_THROW is true throws a XAssertException exception.
+//
+//				VERIFY(p) - Like ASSERT except that p is evaluated in release builds.
+//
+//				REQUIRE(p) - Like VERIFY except that it exits the app if the test fails
+//				(this is needed because the boot strap code can't safely throw).
+//
+//				COMPILE_CHECK(p) - An ASSERT that fires at compile time. This is useful
+//				for doing things like verifying the layout of items within a struct.
+//				Note that this check is also done in release builds.
+//
+//				DEBUGSTR(formatStr, n1, n2, ...) - Drops into the debugger and
+//				displays a string.
+//
+//				TRACE(formatStr, n1, n2, ...) - Writes a string to a debug window
+//				without suspending the program.
+//
+//				TRACEFLOW(category, formatStr, n1, n2, ...) - Like TRACE, but with 
+//				the addition of a category string. The category string allows users 
+//				to turn off entire categories of messages. Note that DEBUGSTR, TRACE, 
+//				and TRACEFLOW may be used with boolean expressions by tacking an _IF 
+//				on the end (eg DEBUGSTR_IF(x < 0, "Sqrt was passed a negative argument.")).
+//
+//				PRECONDITION(p)  - Used in conjunction with POSTCONDITION to verify 
+//				the integrity of an object: each public method should include one call to 
+//				PRECONDITION and another to POSTCONDITION. The idea here is to check to 
+//				see if the object stays in a valid state given valid arguments. Note that 
+//				these macros call a method named Invariant. This method should use ASSERT's 
+//				to check to see if the object is in a sane state. See ZInvariant.h for
+//				more details.
+//
+//				CHECK_INVARIANT() - Calls the Invariant. This is useful in ctors because
+//				PRECONDITION doesn't call the dtor (PRECONDITION constructs an object on
+//				the stack which calls the Invariant when the function exits).
+
+#pragma once      // Open only once per build
+
+#define RUNTIME_EXPORT 
+
+RUNTIME_EXPORT std::wstring ToStr(bool value);				// I originally used iostreams, but that caused a ridiculous amount of bloat in object files (but much less so in the exe)
+RUNTIME_EXPORT std::wstring ToStr(char value);
+inline 		   std::wstring ToStr(wchar_t value)						{return std::wstring(1, value);}
+RUNTIME_EXPORT std::wstring ToStr(int16_t value, int32_t fieldWidth = 1);
+RUNTIME_EXPORT std::wstring ToStr(uint16_t value, int32_t fieldWidth = 1);
+RUNTIME_EXPORT std::wstring ToStr(int32_t value, int32_t fieldWidth = 1);
+RUNTIME_EXPORT std::wstring ToStr(uint32_t value, int32_t fieldWidth = 1);
+RUNTIME_EXPORT std::wstring ToStr(float value, int32_t precision = 6);
+RUNTIME_EXPORT std::wstring ToStr(double_t value, int32_t precision = 6);
+RUNTIME_EXPORT std::wstring ToStr(const char* value);
+inline 		   std::wstring ToStr(const wchar_t* value)					{return value;}
+RUNTIME_EXPORT std::wstring ToStr(const std::string& value);
+inline 		   std::wstring ToStr(const std::wstring& value)			{return value;}
+
+
+#ifdef _DEBUG
+	#define	DEBUG					1
+#else		
+	#define	DEBUG					0
+	
+	#if	!defined(NDEBUG)
+		#define	NDEBUG							// used by <assert.h>
+	#endif
+#endif
+
+#ifndef ASSERTS_THROW
+#define ASSERTS_THROW				0			
+#endif
+// #define ASSERTS_THROW               1
+// Synch with ANSI definitions.
+#if DEBUG && defined(NDEBUG)
+	#error DEBUG and NDEBUG are out of sync!
+#endif
+
+#if !DEBUG && !defined(NDEBUG)
+	#error DEBUG and NDEBUG are out of sync!
+#endif
+
+// Synch with MSVC.
+#if _MSC_VER && DEBUG != defined(_DEBUG)
+	#error DEBUG and _DEBUG are out of sync!
+#endif
+
+#ifndef _lint
+	#define COMPILE_CHECK(p)	{struct _CC {char a[(p) ? 1 : -1];};} (void) 0
+#else
+	#define COMPILE_CHECK(p)					
+#endif
+
+#if DEBUG
+	
+	void AssertFailed(const char* expr, const char* file, int line);
+	
+	#undef assert
+
+	#define ASSERT(p)					do {if (!(p)) AssertFailed(#p, __FILE__, __LINE__);} while (false)
+
+	#define ASSERT_IF(p, x)				do {if ((p) && !(x)) AssertFailed(#x, __FILE__, __LINE__);} while (false)
+	#define assert(p) 					ASSERT(p)
+	#define VERIFY(p)					ASSERT(p)
+	#define REQUIRE(p)					ASSERT(p)
+
+	#define	PRECONDITION(p)				ASSERT(p)
+	#define	POSTCONDITION(p)			ASSERT(p)
+
+    #ifdef NO_NESTED_QUOTES
+          #define PRECONDITION2(a,b)	ASSERT(a)
+          #define POSTCONDITION2(a,b)	ASSERT(a)
+    #else
+          #define PRECONDITION2(a,b)	ASSERT((b, (a) !=0))
+          #define POSTCONDITION2(a,b)	ASSERT((b, (a) !=0))
+    #endif
+
+#else	// #if DEBUG
+
+
+	#if ASSERTS_THROW
+		void AssertFailed(const char*, const char*, int);
+
+		#define ASSERT(p)				do {if (!(p)) AssertFailed(#p, __FILE__, __LINE__);} while (false)
+
+		#define ASSERT_IF(p, x)			do {if ((p) && !(x)) AssertFailed(#x, __FILE__, __LINE__);} while (false)
+		#define VERIFY(p)				ASSERT(p)
+
+		#define	PRECONDITION(p)			ASSERT(p)
+		#define	POSTCONDITION(p)		ASSERT(p)
+
+        #define PRECONDITION2(a,b)	    ASSERT(a)
+        #define POSTCONDITION2(a,b)	    ASSERT(a)
+	#else
+		#define	ASSERT(p)				((void) 0)
+
+		#define	ASSERT_IF(p, x)			((void) 0)
+		#define VERIFY(p)				do {if (p) 0;} while (false)
+
+		#define	PRECONDITION(p)			((void) 0)
+		#define	POSTCONDITION(p)		((void) 0)
+
+        #define PRECONDITION2(a,b)
+        #define POSTCONDITION2(a,b)
+	#endif
+
+	void RequireFailed(const char*, const char*, int);
+	
+	#define REQUIRE(p)					do {if (!(p)) RequireFailed(#p, __FILE__, __LINE__);} while (false)
+
+
+#endif	// DEBUG
+
+#ifdef _CONSOLE
+#define TRACE
+#endif
+	
+
+
+#endif /* __DEBUG_H__ */
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.cpp	(revision 16561)
@@ -0,0 +1,536 @@
+/**********************************************************************
+ *
+ * Fdt.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+///////////////////////////////////////////////////////////////////////////
+//  fdt.cpp
+
+#include "stdafx.h"
+#include "Fdt.h"
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <stdio.h>
+
+//////////////////////////////
+ 
+CFdt::CFdt()   
+// Constructor
+{
+   InitData();
+   InitFields();
+}
+
+//////////////////////////////
+
+CFdt::~CFdt()
+{
+	for (int i = 0; i < m_nFieldCount; i++)
+	{
+#ifndef _WIN32
+		CFieldDef* p = (CFieldDef*) m_fieldArray[i];
+#else
+		CFieldDef* p = (CFieldDef*) m_fieldArray.at(i);
+#endif
+    	delete p;
+	}
+	m_fieldArray.clear();
+
+	Close();
+}
+
+////////////////////////////
+
+bool CFdt::Open(const _TCHAR *szFileName, bool readOnly)
+{
+	if (!m_bIsEmpty)
+		Close();
+	m_sFileName = szFileName;
+	if (!Load(szFileName))
+	{
+		Close();
+		return false;
+	}
+
+	m_bReadOnly = readOnly;
+	return Seek(0);   // 
+}
+
+
+
+
+//////////////////////////////
+
+void CFdt::InitData()
+{
+   m_iCurrentRecord = -1;
+   m_sFileName.erase();
+   
+   m_bReadOnly = false;
+   m_bIsEmpty  = true;
+   m_fdtEntry.Clear();
+   m_bDirty    = false;
+   m_aFdt.clear();
+   m_asPrelude.clear();
+   // Suppose the fdt is empty and we are 
+   // going to append 
+   m_bAppend = true;
+}
+
+//////////////////////////////
+
+void CFdt::Close()
+{
+	if (!m_bIsEmpty)
+		DeleteContents();
+
+    InitData();        // Reset data members   
+	m_bIsEmpty = true;
+}
+
+////////////////////////////////////
+ 
+void CFdt::DeleteContents()
+// Delete the fdt data structures
+{
+    m_aFdt.clear();
+  
+}
+
+
+
+bool CFdt::Load(const _TCHAR *dbaseName)
+{
+	std::ifstream in((const char*)dbaseName);
+	if (!in)
+    {
+//#ifdef _WIN32
+//	   CString strMsg;
+//		strMsg.Format(_T("Error opening %s for reading. "), dbaseName);
+//		AfxMessageBox(strMsg, MB_OK | MB_ICONEXCLAMATION);
+//#else
+		std::cout << _T("Error opening") << dbaseName << _T("for reading") << std::endl;
+//#endif
+		return false;
+    }
+
+
+	std::string s;
+
+    while (std::getline(in,s) && s.substr(0,3) != "***")
+    {
+		m_asPrelude.push_back(s);
+    }
+
+    while (std::getline(in,s))
+    {
+        CFdtEntry e;
+        strncpy(e.name, s.c_str(), FDT_NAME_LENG);
+	    e.name[FDT_NAME_LENG] = _T('\0');
+	    for (int i=FDT_NAME_LENG-1; e.name[i]==_T(' '); e.name[i--]=_T('\0'))
+		   ;
+        if (s[FDT_NAME_LENG] == _T(' '))        //no subfield delimiters spec.
+		 	e.subfields[0] = _T('\0');
+	    else
+		{
+		 	strncpy(e.subfields, s.c_str()+FDT_NAME_LENG, FDT_SFLD_LENG);
+			e.subfields[FDT_SFLD_LENG] = _T('\0');
+			for (int i= FDT_SFLD_LENG-1; e.subfields[i]==_T(' '); e.subfields[i--]='\0')
+				 ;
+		}
+	    sscanf(s.c_str() + FDT_NAME_LENG + FDT_SFLD_LENG, "%d %d %d %d",
+										  &e.tag, &e.len, &e.type, &e.rep);
+	    m_aFdt.push_back(e);
+    }
+    in.close();
+    return true;
+}
+
+		
+		
+		
+
+
+// Define the fields used for the dictionary
+
+static CFieldDef fdtFields[7] =  { 
+   {"Name",       'C', 30, 30, 0, 0, 30},
+   {"Subfields",  'C', 20, 20, 0, 30, 20},
+   {"Tag",        'N', 4, 4, 0,50, 4},
+   {"Len",        'N', 4, 4, 0,54, 4},
+   {"Type",       'N', 1, 1, 0,58, 4},
+   {"Rep",        'C', 1, 1, 0,62, 4},
+   {0,             0, 0, 0, 0, 0, 0}
+}; 
+
+
+
+
+
+
+//+----------------------------------------------------------------
+//
+// Member:     Store(const char* fileName)
+//
+// Synopsis:   Store dictionary tables on a file
+//
+// Parameters: 
+//
+//     fileName -- Full path of dictionary file
+// 
+//-----------------------------------------------------------------   
+
+void FormatFdtEntry(CFdtEntry &e, ustring &s)
+{
+#ifdef _WIN32
+	ustringstream ost;
+
+	ost.setf(std::ios_base::left, std::ios_base::adjustfield);
+	ost << std::setw(30) << e.name << std::setw(20) << e.subfields;
+	ost.setf(std::ios_base::internal,std::ios_base::adjustfield);
+	ost << " " << e.tag << " " << e.len << " " << e.type << " " << e.rep;
+	s = ost.str();
+#endif
+}
+
+void CFdt::Store(const char* fileName)
+{
+	if (m_bDirty)
+	{
+		Flush();
+		m_bDirty = false;
+	}
+
+	std::ofstream out((const char *) fileName);
+	TRACE(_T("\n<CFdt::Store> fileName=%s"),fileName);
+	for (int i=0; i<m_asPrelude.size(); i++)
+		out << m_asPrelude[i] << std::endl;
+	
+	out << "***" << std::endl;
+
+	ustring s;
+#ifdef _WIN32
+	for (i=0; i< m_aFdt.size(); i++)
+#else
+	for (int i=0; i< m_aFdt.size(); i++)
+#endif
+	{
+		FormatFdtEntry(m_aFdt[i], s);
+		out << s << std::endl;
+	}
+    out.close();
+
+}
+
+//////////////////////////////
+
+int CFdt::InitFields ()
+{
+	m_nFieldCount = 0;
+
+	for (int i = 0; fdtFields[i].width != 0; i++)
+	{
+		CFieldDef* fld = (CFieldDef*) new CFieldDef();
+		//TRACE("\n<CFdt::InitFields>i=%d after new",i);
+		//TRACE("<CFdt::InitFields>After SetAtGrow");
+		strcpy(fld->name, fdtFields[i].name);
+		fld->type          = fdtFields[i].type;
+		fld->len           = fdtFields[i].len;
+		fld->width         = fdtFields[i].width;
+		fld->decimals      = fdtFields[i].decimals;
+		fld->offset        = fdtFields[i].offset;
+		fld->display_width = fdtFields[i].display_width;
+
+		int l = strlen(fld->name);
+		if (fld->width < l)
+			fld->width = static_cast<short>(l);
+
+		m_fieldArray.push_back(fld);
+		m_nFieldCount++;
+	}
+	//TRACE("\n<CFdt::InitFields>InitField After loop");
+	return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+
+CFieldDef* CFdt::GetField(int n) const
+// Returns field info
+{
+	ASSERT(n >= 0 && n <= m_nFieldCount);
+#ifndef _WIN32
+	return (CFieldDef*) m_fieldArray[n];
+#else
+	return (CFieldDef*) m_fieldArray.at(n);
+#endif
+}
+
+/////////////////////////////////////////////////////////////////////
+// Methods for accessing and maintaining the list of CDictTRecords 
+// pointers
+
+////////////////////////////
+// First the List Accessors
+////////////////////////////
+
+CFdtEntry CFdt::GetAt(int i)
+{
+#ifndef _WIN32
+	return m_aFdt[i];
+#else
+	return m_aFdt.at(i);
+#endif
+
+}
+
+
+//////////////////////////
+
+const CFdtEntry CFdt::GetAt(int i) const
+{
+#ifndef _WIN32
+	return m_aFdt[i];
+#else
+	return m_aFdt.at(i);
+#endif
+}
+
+//////////////////////////
+// And now the modifiers
+//////////////////////////
+
+int CFdt::Append(CFdtEntry& e)
+{
+	int n = m_aFdt.size();
+	m_aFdt.push_back(e);
+    m_iCurrentRecord = -1;
+    m_bIsEmpty = false;
+
+    return m_aFdt.size();
+}
+
+
+/**-------------------------------------------------------------------
+ * RemoveAt(int i)
+ *
+ *--------------------------------------------------------------------
+ */
+void CFdt::RemoveAt(int i)
+{
+
+	m_aFdt.erase(m_aFdt.begin()+i);
+    m_iCurrentRecord = -1;
+}
+
+/**-------------------------------------------------------------------
+ * SetAt(int i, CFdtEntry& e)
+ *
+ *--------------------------------------------------------------------
+ */
+void CFdt::SetAt(int i, CFdtEntry& e)
+{
+
+	m_aFdt[i] = e;
+	// Force reloading
+    m_iCurrentRecord = -1;
+}
+
+/////////////////////////////////////////////////////////////////
+// 
+// We use a buffer "m_fdtEntry" to hold the content of the current
+// fdt entry. This buffer is filled by the "Seek" member function
+// as follow:
+//
+
+//////////////////////////////
+
+bool CFdt::Seek(long iRecord)
+{
+
+	// move to new record and flush changes of previous record
+	if (iRecord < 0 || iRecord > GetRecordCount())
+		return false;
+	else if (iRecord == m_iCurrentRecord)
+		return true;
+
+	if (m_iCurrentRecord != -1 &&  m_bDirty)
+		Flush();
+	if (m_bDirty)
+	   /*AfxMessageBox(_T("Seek Alert Modification not saved"))*/;
+	m_iCurrentRecord = iRecord;
+	
+	if (iRecord == GetRecordCount())
+	{
+		m_fdtEntry.Clear();
+        m_bAppend = true;
+	}
+	else
+	{
+		TRACE(_T("\n<CFdt::Seek> Load iRecord=%d GetRecordCount()=%d"), iRecord, GetRecordCount());
+		m_fdtEntry = GetAt(iRecord);
+		m_bAppend = false;
+	}
+    m_bDirty = false;
+	return true;
+}
+
+
+
+//+------------------------------------------------------------------
+//
+// Member:   Flush()
+//
+// Synopsis: Moves the changes made to the current record from
+//           the buffer to the dictionary data structure so that
+//           they are effective.
+//-------------------------------------------------------------------    
+
+void CFdt::Flush()
+{
+	if (m_iCurrentRecord == GetRecordCount())
+	{
+		Append(m_fdtEntry); 
+	}
+	else
+	{
+		SetAt(m_iCurrentRecord, m_fdtEntry);
+	}
+	m_bDirty = false;
+}
+
+//////////////////////////////////////////////////////////////
+// Get the value of field n for the current record
+//
+
+bool CFdt::GetValue(int n, std::string& result) 
+// Read value from buffer
+{
+	ASSERT(n >= 0 && n <= m_nFieldCount);
+    ASSERT(m_iCurrentRecord >= 0 && m_iCurrentRecord <= GetRecordCount());
+	
+	if (!(n >= 0 && n <= m_nFieldCount))
+		return false;
+
+	result.erase();
+
+	std::ostringstream os;
+    switch (n)
+	{
+	    case 0:       // Field Name
+			os << m_fdtEntry.name;
+			result = os.str();
+		    break;
+	    case 1:       // Subfields
+			os << m_fdtEntry.subfields;
+			result = os.str();
+		    break;
+	    case 2:       // Tag
+		  	if (m_fdtEntry.tag == 0)
+				result = "";
+			else
+			{
+			    os << m_fdtEntry.tag;
+			    result = os.str();
+			}
+		    break;
+	    case 3:       // length
+		  	if (m_fdtEntry.len == 0)
+				result = "";
+			else
+			{
+			    os << m_fdtEntry.len;
+			    result = os.str();
+			}
+		    break;
+	    case 4:       // type
+		  	if (m_fdtEntry.type == 0)
+				result = "";
+			else
+			{
+			    os << m_fdtEntry.type;
+			    result = os.str();
+			}
+		    break;
+	    case 5:       // Rep
+		  	if (m_fdtEntry.rep == 0)
+				result = "";
+			else
+			{
+			    os << m_fdtEntry.rep;
+			    result = os.str();
+			}
+		    break;
+	}
+	return true;
+}
+
+
+//////////////////////////////
+
+bool CFdt::SetValue(int n, const char* pszValue)
+// Write value to buffer
+{
+	ASSERT(n >= 0 && n <= m_nFieldCount);
+    ASSERT(m_iCurrentRecord >= 0 && m_iCurrentRecord <= GetRecordCount());
+
+	if (m_bReadOnly)
+		return false;
+
+	if (!(n >= 0 && n <= m_nFieldCount))
+		return false;
+
+    bool bEmpty = true;
+
+
+	switch (n) 
+	{
+	case 0:       // Name
+		strcpy(m_fdtEntry.name,(const char*)pszValue);
+	//	TRACE("\nvalue=%s num=%d",pszValue,m_tRecord.num);
+		break;
+	case 1:       // Subfields
+		strcpy(m_fdtEntry.subfields,(const char*)pszValue);
+	//	TRACE("\nvalue=%s name=%d",pszValue,m_tRecord.name);
+		break;
+	case 2:       // Tag
+		m_fdtEntry.tag = _ttoi(pszValue);
+	//	TRACE("\nvalue=%s loc=%d",pszValue,m_tRecord.loc);
+		break;
+	case 3:       // Len
+		m_fdtEntry.len = _ttoi(pszValue);
+	//	TRACE("\nvalue=%s wid=%d",pszValue,m_tRecord.wid);
+		break;
+	case 4:       // Type
+		m_fdtEntry.type = _ttoi(pszValue);
+		break;
+	case 5:       // Rep
+		m_fdtEntry.type = _ttoi(pszValue);
+		break;
+	}
+	m_bDirty = true;
+	
+	return true;
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Fdt.h	(revision 16561)
@@ -0,0 +1,172 @@
+/**********************************************************************
+ *
+ * Fdt.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+// Fdt.h
+
+#ifndef __FDT_H__
+#define __FDT_H__
+
+#include <vector>
+#include "mytchar.h"
+
+#define FDT_NAME_LENG    30     // maximum name length in FDT_FILE
+#define FDT_SFLD_LENG    20    // maximum subfield length in FDT_FILE
+
+#ifndef __CFIELDDEF__
+#define __CFIELDDEF__
+
+class CFieldDef
+{
+public:
+	enum FieldType {
+		charField       = 'C',
+		dateField       = 'D',
+		numericField    = 'N',
+		logicalField    = 'L',
+		memoField       = 'M'
+	};
+
+	char  name[31];   // dbf field names are 10 chars long max.
+		// If this application is compiled with _UNICODE switch,
+		// the field name will be converted to wide chars
+		// when the column header is displayed in the grid.
+	char  type;
+	short len;
+	short width;
+	short decimals;
+	int   offset;
+	short display_width;
+};
+#endif // __CFIELDDEF__
+
+
+
+class CFdtEntry
+{
+public:
+	CFdtEntry() :  tag(0), len(0), type(0), rep(0) 
+	{ name[0] = '\0'; subfields[0] = '\0'; }
+    void Clear()
+	{
+		tag = len = type = rep = 0;
+        name[0] = '\0'; subfields[0] = '\0';
+	}
+    char    name[FDT_NAME_LENG+1];
+    char    subfields[FDT_SFLD_LENG+1];
+    int      tag;
+    int      len;
+    int      type;     //X=0
+    int      rep;      //repeatable 1, non rep. 0
+   CFdtEntry& operator=(const CFdtEntry& rhs)
+   {
+	   if (&name[0] != &rhs.name[0])
+	   {
+		   Copy(rhs);
+	   }
+	   return *this;
+   }
+
+   void Copy(const CFdtEntry &rhs)
+   {
+	   strcpy(name, rhs.name);
+	   strcpy(subfields, rhs.subfields);
+	   tag  = rhs.tag;
+	   len  = rhs.len;
+	   type = rhs.type;
+	   rep  = rhs.rep;
+   }
+};
+
+
+void FormatFdtEntry(CFdtEntry &e, ustring &s);
+
+
+
+class CFdt
+{
+protected:
+	std::vector<CFdtEntry>  m_aFdt;
+    int                     m_nFieldCount;       // Num. of fields
+	std::vector<CFieldDef*> m_fieldArray;        // Array with fields
+
+	long        m_iCurrentRecord;    // Current read record stored in
+	CFdtEntry   m_fdtEntry;
+	bool        m_bReadOnly;
+	bool        m_bIsEmpty;
+	bool        m_bDirty;            // True if m_fdtEntry modified 
+	bool        m_bAppend;
+public:
+	ustring  m_sFileName;              // Fdt full pathname
+	std::vector<std::string> m_asPrelude;  // What is bef ***
+public:
+	CFdt();          
+	virtual ~CFdt();
+
+	bool      Load(const _TCHAR *fname);
+	int       GetEntryCount() { return m_aFdt.size(); }
+
+	CFdtEntry GetEntryAt(int i) 
+	{ 
+      #ifdef _WIN32
+	     return m_aFdt.at(i); 
+      #else
+	     return m_aFdt[i];
+      #endif
+	}
+
+	int       GetFieldCount() { return  m_nFieldCount; }
+
+    bool      Open(const char* szFileName, bool readOnly=true);
+
+    void InitData();
+
+    void Close();
+ 
+    void DeleteContents();
+
+    void Store(const char* fileName);
+    int InitFields ();
+
+    CFieldDef* GetField(int n) const;
+
+    CFdtEntry GetAt(int i);
+
+    const CFdtEntry GetAt(int i) const;
+
+    int Append(CFdtEntry& e);
+
+    void RemoveAt(int i);
+    void SetAt(int i, CFdtEntry& e);
+
+    bool Seek(long iRecord);
+    void Flush();
+    bool GetValue(int n, std::string& result);
+    bool SetValue(int n, const char* pszValue);
+
+	int GetRecordCount() { return m_aFdt.size(); }
+};
+
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/File.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/File.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/File.cpp	(revision 16561)
@@ -0,0 +1,618 @@
+/**********************************************************************
+ *
+ * File.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// File.cpp   File object methods
+////////////////////////////////////////////////////////////
+#include "stdafx.h"
+
+ 
+#include <assert.h>
+#include "CRC32.h"
+#include "File.h"
+
+
+
+
+static TCHAR closed_file_name[] = _T("closed-file");
+
+static const int MaxExceptionMessages = 34;
+static const TCHAR *fileExceptionMsg[MaxExceptionMessages] =
+{
+  _T("File exception: No exception reported"),               // FILE_NO_ERROR
+  _T("File exception: Checksum error"),                      // FILE_CHECKSUM_ERROR
+  _T("File exception: Error closing file"),                  // FILE_CLOSE_ERROR
+  _T("File exception: File is corrupted"),
+  _T("File exception: Error creating file"),                 // FILE_CREATION_ERROR
+  _T("File exception: File already exists"),                 // FILE_EXISTS
+  _T("File exception: Trying to use a closed file"),         // FILE_NOT_OPEN_ERROR
+  _T("File exception: File not ready for reading/writing"),  // FILE_NOT_READY
+  _T("File exception: Could not write to file"),             // FILE_NOT_WRITEABLE
+  _T("File exception: Error opening file"),                  // FILE_OPEN_ERROR
+  _T("File exception: Cannot obtain a file position"),       // FILE_POSITION_ERROR
+  _T("File exception: Error reading from file"),             // FILE_READ_ERROR
+  _T("File exception: Error seeking in file"),               // FILE_SEEK_ERROR
+  _T("File exception: Error writing to file"),               // FILE_WRITE_ERROR
+
+  _T("Fmgr exception: No database open"),                    // FMGR_NO_DATABASE_OPEN
+  _T("Fmgr exception: No such file exists"),                 // FMGR_NO_FILE_EXISTS
+  _T("Fmgr exception: No objects exist"),                    // FMGR_NO_OBJECTS_EXIST
+  _T("Fmgr exception: Accessing a null pointer"),            // FMGR_NULL_PTR
+  _T("Fmgr exception: Object already exists"),               // FMGR_OBJECT_EXISTS
+  _T("Fmgr exception: Another object is referencing this file"), // FMGR_OBJECT_EXISTS
+  _T("Fmgr exception: Math overflow error"),                 // FMGR_OVERFLOW
+  _T("Fmgr exception: Parse error"),                         // FMGR_PARSE_ERROR
+  _T("Fmgr exception: Invalid path"),                        // FMGR_PATH_ERROR
+  _T("Fmgr exception: Trying to write to a read-only file"), // FMGR_READ_ONLY_FILE
+  _T("Fmgr exception: Stack empty"),                         // FMGR_STACK_EMPTY
+  _T("Fmgr exception: Stack full"),                          // FMGR_STACK_FULL
+  _T("Fmgr exception: Synchronization Error"),               // FMGR_SYNC_ERROR    
+  _T("Fmgr exception: Math under-flow error"),               // FMGR_UNDERFLOW
+  _T("Fmgr exception: Wrong file type"),                     // FMGR_WRONG_FILE_TYPE
+  
+  // Persistent lock error messages
+  _T("Fmgr exception: Invalid lock type specified"),          // FMGR_INVALID_LOCK_TYPE
+  _T("Fmgr exception: Cannot access the file lock header"),   // FMGR_FILELOCK_ACCESS_ERROR
+  _T("Fmgr exception: Cannot lock the file"),                 // FMGR_FILELOCK_ERROR
+  _T("Fmgr exception: Cannot access the record lock header"), // FMGR_RECORDLOCK_ACCESS_ERROR
+  _T("Fmgr exception: Cannot lock the specified record")      // FMGR_RECORDLOCK_ERROR
+};
+ 
+//---------------------------------------------------------------------------
+// CFileBase::CFileBase()
+//
+// Default constructor, makes a File object that isn't attached to a file
+//--------------------------------------------------------------------------- 
+CFileBase::CFileBase()
+{
+    _tcscpy(file_name, closed_file_name); // Set the initial file name
+    fp = 0;
+
+    // Reset the last reported error and the file status members
+    file_error        = FILE_NO_ERROR;
+    is_open           = 0;
+    is_ok             = 0;
+    ready_for_reading = 0;
+    ready_for_writing = 0;
+}
+  
+//---------------------------------------------------------------------------
+// CFileBase::~CFileBase()
+//
+// Destructor for CFileBase file object
+//--------------------------------------------------------------------------- 
+CFileBase::~CFileBase()
+{
+	Close();
+}
+
+//---------------------------------------------------------------------------
+// const char* CFileBase::FileExceptionMessage()
+//
+// Returns a null terminated string that can / be used to log or print a
+// file exception.
+//---------------------------------------------------------------------------
+const TCHAR *CFileBase::FileExceptionMessage()
+{
+	assert(file_error<MaxExceptionMessages);
+    return fileExceptionMsg[file_error];
+}
+ 
+//--------------------------------------------------------------------------------
+// int Create(const char* fname)
+//
+// Creates a new file, truncate the file if it already exists. The file open for
+// Read/Write access. 
+// Returns a non-zero value to indicate an error condition or zero if successful.
+//--------------------------------------------------------------------------------
+FileError CFileBase::Create(const TCHAR *fname)
+{
+    // Close any open files 
+    if (Close() != FILE_NO_ERROR) 
+		return file_error;
+	
+    // Create and truncate existing files
+    fp = FileSystem::Create(fname);
+	
+    if (fp == 0)
+	{
+		is_ok             = 0;
+		is_open           = 0;
+		ready_for_writing = 0;
+		ready_for_reading = 0;
+		file_error        = FILE_CREATION_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(file_error);
+#else
+		return file_error;
+#endif
+	}
+	else
+	{
+		is_open           = 1;
+		is_ok             = 1;
+		ready_for_writing = 1;
+		ready_for_reading = 1;
+		last_operation    = IO_READ;
+		_tcscpy(file_name, fname);
+	}
+	
+	// Returns 0 if the file was successfully created and opened.
+	return file_error = FILE_NO_ERROR; 
+}
+ 
+//----------------------------------------------------------------------------------------
+// FileError CFileBase::Open(const TCHAR *fname, FileSystem::AccessMode mode
+//                                               /* = FileSystem::FILE_READWRITE */ )
+//
+// Open an existing file. The "mode" variable determines if the file is opened for
+// read only or read/write access.  Returns a non-zero value to indicate an error
+// condition or zero if successful.
+// NOTE: This version of the open functions will only accept:
+// FILE_READONLY and FILE_READWRITE access modes.
+//---------------------------------------------------------------------------------------
+FileError CFileBase::Open(const TCHAR *fname, FileSystem::AccessMode mode /* = FileSystem::FILE_READWRITE */)
+{
+	// Close any open files
+	if (Close() != FILE_NO_ERROR) 
+		return file_error;
+	
+	if (mode == FileSystem::FILE_READONLY)
+	{   // Open with read only access
+		ready_for_reading = 1;
+		ready_for_writing = 0;
+		fp = FileSystem::Open(fname, FileSystem::FILE_READONLY);
+	}
+	else
+	{   // Open with read/write access
+		ready_for_reading = 1;
+		ready_for_writing = 1;
+		fp = FileSystem::Open(fname, FileSystem::FILE_READWRITE);
+	}
+    
+	if (fp == 0)
+	{
+		ready_for_reading = 0;
+		ready_for_writing = 0;
+		file_error        = FILE_OPEN_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(file_error);
+#else
+		return file_error;
+#endif
+	}
+	else
+	{
+		is_open         = 1;
+		is_ok           = 1;
+		last_operation  = IO_WRITE;
+		_tcscpy(file_name, fname);
+	}
+	return file_error = FILE_NO_ERROR;
+}
+ 
+//--------------------------------------------------------------------------------
+// void CFileBase::Close()
+//
+// Close the open database file. Returns a non-zero value
+// to indicate an error condition or zero if successful.
+//--------------------------------------------------------------------------------
+FileError CFileBase::Close()
+{
+   if (IsOpen()) 
+   {
+       if (FileSystem::Close(fp) != 0) 
+	   {
+		   file_error = FILE_CLOSE_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		   throw CFileBaseException(file_error);
+#else
+		   return file_error;
+#endif
+	   }
+       _tcscpy(file_name, closed_file_name); // Set the initial file name
+   }
+
+   is_open           = 0;
+   is_ok             = 0;
+   ready_for_reading = 0;
+   ready_for_writing = 0;
+   fp                = 0; // Reset the file pointer to zero after the file is closed
+
+   return file_error = FILE_NO_ERROR; 
+}
+
+//----------------------------------------------------------------------------------
+// FileError CFileBase::Seek(FAU offset, FileSystem::SeekMode mode)
+//
+// Seek to the specified offset starting at the beginning (SEEK_SET), end (SEEK_END)
+// or current offset (SEEK_CUR). Returns a non-zero value to indicate an error
+// condition or zero if successful.
+//----------------------------------------------------------------------------------
+FileError CFileBase::Seek(FileSystem::FAU_t offset, FileSystem::SeekMode mode)
+{
+	if (IsOK()) 
+	{
+		if (FileSystem::Seek(fp, offset, mode) == (isis::int32_t) - 1)
+		{
+			file_error = FILE_SEEK_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(file_error);
+#else
+			return file_error;
+#endif
+		}
+		last_operation = IO_SEEK;
+	}
+	else 
+	{
+		file_error = FILE_NOT_READY;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(file_error);
+#else
+		return file_error;
+#endif
+	}
+	return file_error = FILE_NO_ERROR;
+}
+
+//----------------------------------------------------------------------------------
+// FAU CFileBase::SeekTo(FileSystem::FAU_t file_address)
+//
+// Seek to the specified address, optimizing the seek operation by moving the 
+// file position indicator based on the current stream position. Returns the
+// current file position after performing the seek operation.
+//----------------------------------------------------------------------------------
+FileSystem::FAU_t CFileBase::SeekTo(FileSystem::FAU_t file_address)
+{
+	// Get the current stream position
+	FileSystem::StreamPos pos = FilePosition();
+	
+	if (file_address == CURRADDR) 
+	{   // Do not perform a seek operation
+		return pos;
+	}
+	else if (file_address > pos)
+	{   // Seek forward to the specified address
+		FileSystem::StreamPos offset = file_address - pos;
+		Seek(offset, FileSystem::FILE_SEEK_CUR);
+	}
+	else if (file_address < pos) 
+	{   // Seek backward to the specified address
+		Seek(file_address, FileSystem::FILE_SEEK_BEG);
+	}
+	else 
+	{  // Current file position equals the specified address
+		// Find current the position
+		Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR);
+	}
+	
+	return FilePosition(); // Return current file position after seeking
+}
+
+//-------------------------------------------------------------------------------
+// FileError Store(const void* buf, isis::uint32_t nBytes,
+//                             FAU file_addres = CFileBase::CURRADDR,
+//			                   int flush = 1, int bit_test = 1);
+//
+// Write a specific number of bytes from a memory buffer to a specified file
+// offset. If the "flush" variable is true, the file buffers will be flushed to
+// disk with each write operation. If the bit_test variable if true, the CRC of 
+// the buffer will be compared to the CRC of the actual bytes written to disk.
+// Returns a non-zero value to indicate an error condition or zero if successful.
+//
+// void* buf         - Pointer to the user-supplied buffer that is to receive
+//                     the data read from the file.
+// isis::uint32_t nBytes - The maximum number of bytes to be read from the 
+//                     file.  Note that an CFileBase is always a binary file,
+//                     so a CR/LF pair will always be read or written as two bytes.
+// FAU file_address  - The file byte address. The address is always interpreted to 
+//                     be from the beginning of the file, unless dwAddr is equal 
+//                     to CURRADDR, which means from the current position.
+//------------------------------------------------------------------------------- 
+FileError CFileBase::Store(const void* buf, isis::uint32_t nBytes,
+						   FileSystem::FAU_t file_address /*= CURRADDR */, 
+						   int flush_flag /* = 1 */,
+						   int bit_test /* = 1 */)
+{
+    FileSystem::FAU_t buf_address;
+	
+    if (ReadyForWriting())
+	{
+        if (file_address == CURRADDR)
+		{
+			if (last_operation == IO_READ)
+			{
+				if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR)
+					return file_error;
+			}
+		}
+        else
+		{
+			if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR)
+				return file_error;
+		}
+		
+        buf_address = FilePosition(); 
+		
+        if (file_error != FILE_NO_ERROR)
+			return file_error;
+		
+        if (FileSystem::Write(fp, buf, nBytes) != nBytes)
+		{
+			file_error = FILE_WRITE_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(file_error);
+#else
+			return file_error;
+#endif
+		}
+        last_operation = IO_WRITE;
+	}
+    else
+	{
+        file_error = FILE_NOT_WRITEABLE;
+#ifdef __CPP_EXCEPTIONS__
+        throw CFileBaseException(file_error);
+#else
+        return file_error;
+#endif
+	} 
+	
+    // Allow application to flush disk buffers after each write
+    // operation to ensure the file data stays in sync during multiple
+    // file access.
+    if (flush_flag)
+	{  
+		if (FileSystem::Flush(fp) != FILE_NO_ERROR)
+		{
+			file_error = FILE_WRITE_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(file_error);
+#else
+			return file_error;
+#endif
+		}
+        if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR)
+			return file_error;
+	}
+	
+    if (bit_test) 
+	{
+        isis::uint32_t w_csum = CalcCRC32((char* ) buf, nBytes);
+        isis::uint32_t r_csum = CalcChecksum(nBytes, buf_address);
+		
+		// Check for file errors
+		
+		if (file_error != FILE_NO_ERROR)
+			return file_error;
+		
+		if (w_csum ^ r_csum)
+		{
+			file_error = FILE_CHECKSUM_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(file_error);
+#else
+			return file_error;
+#endif
+		}
+	}
+	return file_error = FILE_NO_ERROR;	
+}
+ 
+//-------------------------------------------------------------------------------
+// FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes, FAU file_addres
+//                                                  /* = CFileBase::CURRADDR */)
+//
+// Read a specified number of bytes from the specified file offset into a memory
+// buffer. Returns a non-zero value to indicate an error condition or zero if
+// successful.
+// void* buf         - Pointer to the user-supplied buffer that is to receive
+//                     the data read from the file.
+// isis::uint32_t nBytes - The maximum number of bytes to be read from the file. 
+//                     Note that an CFileBase is always a binary file, so a 
+//                     CR/LF pair will always be read or written as two bytes.
+// FAU file_addres   - The file byte address. The address is always interpreted 
+//                     to be from the beginning of the file, unless dwAddr is 
+//                     equal to CURRADDR, which means from the current position.
+//------------------------------------------------------------------------------- 
+FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes, 
+						   FileSystem::FAU_t file_address /* = CFileBase::CURRADDR */)
+{
+    if (IsOK())
+	{
+        if (file_address == CURRADDR)
+		{
+            if (last_operation == IO_WRITE)
+			{
+	           if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR) 
+				   return file_error;
+			}
+		}
+        else
+		{
+            if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR)
+              	return file_error;
+		}
+
+		//TRACE("\nFetchfile_address=%ld nBytes=%u",file_address, nBytes);
+        if (FileSystem::Read(fp, buf, nBytes) != nBytes)
+		{
+            file_error = FILE_READ_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+            throw CFileBaseException(file_error);
+#else
+            return file_error;
+#endif
+		}
+        last_operation = IO_READ;
+	}
+    else 
+	{
+        file_error = FILE_NOT_READY;
+#ifdef __CPP_EXCEPTIONS__
+        throw CFileBaseException(file_error);
+#else
+        return file_error;
+#endif
+	}
+
+    return file_error = FILE_NO_ERROR;
+}
+//------------------------------------------------------------------------------- 
+// FileError Flush()
+//
+// Flush any open disk buffers. Returns a non-zero value to indicate an error
+// condition or zero if successful.
+//------------------------------------------------------------------------------- 
+FileError CFileBase::Flush()
+{
+	PRECONDITION(ReadyForWriting());
+	if (FileSystem::Flush(fp) != FILE_NO_ERROR)
+	{
+		file_error = FILE_WRITE_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(file_error);
+#else
+		return file_error;
+#endif
+	}
+	Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR);
+	return file_error = FILE_NO_ERROR;
+}
+
+
+//------------------------------------------------------------------------------- 
+// StreamPos CFileBase::FilePosition()
+//
+// Returns the current file position or -1 to indicate an error condition.
+//------------------------------------------------------------------------------- 
+FileSystem::StreamPos CFileBase::FilePosition()
+{
+   if (!IsOK())
+   {
+      file_error = FILE_NOT_READY;
+#ifdef __CPP_EXCEPTIONS__
+      throw CFileBase();
+#else
+      return (StreamPos) - 1;
+#endif
+   }
+
+   FileSystem::StreamPos pos = FileSystem::Tell(fp);
+   if (pos < 0)
+   {
+      file_error = FILE_POSITION_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+      throw CFileBaseException(file_error);
+#else
+      return (StreamPos) - 1;
+#endif
+   }
+   return pos;
+}
+
+//------------------------------------------------------------------------------- 
+// isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FAU file_address, 
+//                                                         int mem_alloc)
+//
+// Calculate a 32-bit CRC checksum for a given number of bytes starting at the
+// specified address. Returns a 32-bit CRC value. If the mem_alloc variable is
+// true, a buffer equal to the specified number of bytes will be created in
+// memory. If the mem_alloc variable is false or memory allocation fails, the CRC
+// will be calculated byte by byte starting at the specified address. 
+// NOTE: the calling function must check for disk file errors.
+// Returns the CRC value.
+//------------------------------------------------------------------------------- 
+
+isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FileSystem::FAU_t file_address, int mem_alloc)
+{
+   isis::uint32_t CRC;
+   isis::uint32_t len = bytes;
+   unsigned char data;
+   char *buf = 0;  
+
+   // Create a buffer equal to the object length
+   if (mem_alloc) 
+	   buf = new char[bytes]; 
+
+   if (buf)
+   {
+      if (IsOK())
+	  {
+         if (Fetch(buf, bytes, file_address) != FILE_NO_ERROR) return 0;
+         CRC = CalcCRC32(buf, bytes);
+         delete[] buf;
+	  }
+   }
+   else
+   {
+      if (IsOK())
+	  {
+          // Seek to the specified file address
+          SeekTo(file_address);
+          if (file_error != FILE_NO_ERROR) return 0; 
+          CRC = 0xffffffffL;
+          while (len--)
+		  {
+	         if (Fetch(&data, sizeof(data)) != FILE_NO_ERROR) return 0;
+	         CRC = CalcCRC32(data, CRC);
+		  }
+          CRC ^= 0xffffffffL;
+	  }
+   }
+
+   return CRC; 
+}
+///////////////////////////////////////////////////////////////////////////
+// General purpose file utilites 
+//
+int CFileBase::Exists(const TCHAR *fname)
+// Returns true if the file exists
+{
+	return FileSystem::Exists(fname);
+}
+
+FileSystem::FAU_t CFileBase::FileSize(const TCHAR *fname)
+// Returns the file size. Use after file has been closed
+// and re-opened to ensure that all the buffers are flushed
+// to disk. Returns -1 to indicate an error condition.
+{
+	return (FileSystem::FAU_t) FileSystem::FileSize(fname);
+}
+
+int CFileBase::CanOpenForWriting(const TCHAR *fname)
+// Returns true if the file is opened
+{
+	return 1;
+	//return FileSystem::CanOpenForWriting(fname);
+}
+
+int CFileBase::CanOpenReadOnly(const TCHAR *fname)
+// Returns true if the file is opened
+{
+	return 1;
+	//return FileSystem::CanOpenReadOnly(fname);
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/File.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/File.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/File.h	(revision 16561)
@@ -0,0 +1,188 @@
+/**********************************************************************
+ *
+ * File.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+//////////////////////////////////////////////////////////////////////////
+// File.h -- File Base class
+// 
+//////////////////////////////////////////////////////////////////////////
+ 
+
+ 
+#ifndef __FILE_BASE__
+#define __FILE_BASE__
+
+#include "FileSystem.h"
+# define __CPP_EXCEPTIONS__ 1
+
+// CRC-32 checksum used to detect bit errors
+typedef unsigned long Checksum;
+
+
+// Constants for file operations and stream position 
+const int MaxNameLength             = 512;        // Max length of names buffers
+const FileSystem::FAU_t StartOfFile = (FileSystem::FAU_t)0; // First byte in the file
+const FileSystem::FAU_t CURRADDR    = (FileSystem::FAU_t)-1;// Indicates current location
+const isis::uint32_t CheckWord      = 0xfefefefe;           // Default synchronization word
+const isis::int32_t  FSListCorrupt  = (FileSystem::FAU_t)-1;// Free space list is corrupt
+
+
+//------------------
+// File  error codes
+//------------------
+enum FileError { 
+  FILE_NO_ERROR = 0,   // No errors reported
+  FILE_CHECKSUM_ERROR, // Checksum Error
+  FILE_CLOSE_ERROR,    // Error closing file
+  FILE_CORRUPT,        // File corrupted
+  FILE_CREATION_ERROR, // Error creating file
+  FILE_EXISTS,         // File already exists
+  FILE_NOT_OPEN_ERROR, // Trying to use a closed file
+  FILE_NOT_READY,      // File not ready (failed or closed file)
+  FILE_NOT_WRITEABLE,  // Could not write to file 
+  FILE_OPEN_ERROR,     // Error opening file
+  FILE_POSITION_ERROR, // Cannot obtain the current file position
+  FILE_READ_ERROR,     // Error reading file  
+  FILE_SEEK_ERROR,     // Error seeking in file
+  FILE_WRITE_ERROR,    // Error writing to file
+
+  FMGR_NO_DATABASE_OPEN,    // No database open
+  FMGR_NO_FILE_EXISTS,      // No such file exists
+  FMGR_NO_OBJECTS_EXIST,    // No objects exist
+  FMGR_NULL_PTR,            // Accessing a null pointer 
+  FMGR_OBJECT_EXISTS,       // Object already exists
+  FMGR_OPEN_FILE_REFERENCE, // Another object is referencing this file
+  FMGR_OVERFLOW,            // Math overflow
+  FMGR_PARSE_ERROR,         // Parse error
+  FMGR_PATH_ERROR,          // Invalid path
+  FMGR_READ_ONLY_FILE,      // Trying to write to read-only file
+  FMGR_STACK_EMPTY,         // Stack empty
+  FMGR_STACK_FULL,          // Stack full
+  FMGR_SYNC_ERROR,          // Synchronization Error
+  FMGR_UNDERFLOW,           // Math under-flow
+  FMGR_WRONG_FILE_TYPE,     // Wrong file type
+
+  // Persistent lock error codes
+  FMGR_INVALID_LOCK_TYPE,        // Invalid lock type specified
+  FMGR_FILELOCK_ACCESS_ERROR,    // The file lock cannot be accessed
+  FMGR_FILELOCK_ERROR,           // Error locking the file
+  FMGR_RECORDLOCK_ACCESS_ERROR,  // The record lock cannot be accessed
+  FMGR_RECORDLOCK_ERROR          // Error locking a record};
+
+
+};
+
+
+
+
+class DLL_CODE_API CFileBase 
+{
+protected:
+    // File I/O operation codes
+  enum IO_Op { 
+     IO_READ,         // A read was performed
+     IO_WRITE,        // A write operation was performed
+     IO_REWIND,       // A rewind operation was performed
+     IO_NO_OPERATION, // No operation was performed
+     IO_SEEK          // A seek operation was preformed 
+  };
+
+  _TCHAR            file_name[MaxNameLength];  // Open file name
+  FileSystem::FPTR* fp;                        // Stream file handle
+  IO_Op             last_operation;            // Last I/O operation preformed
+  FileError         file_error;                // Last reported file error
+
+  // File status members
+  int is_ok;             // Used to signal a fatal error condition
+  int is_open;           // True if the file is open
+  int ready_for_reading; // True if the file is ready for reading
+  int ready_for_writing; // True if the file is ready for writing
+
+
+public:
+   CFileBase();
+   virtual ~CFileBase();
+
+public: // File functions
+   virtual FileError Create(const TCHAR *fname);
+   virtual FileError Open(const TCHAR *fname, FileSystem::AccessMode mode = FileSystem::FILE_READWRITE); 
+   virtual FileError Close();
+   
+   FileError         Seek(FileSystem::FAU_t offset, FileSystem::SeekMode mode = FileSystem::FILE_SEEK_BEG);
+ 
+   FileError         Fetch(void* buf, isis::uint32_t nBytes, FileSystem::FAU_t file_addres = CURRADDR);
+   FileError         Store(const void* buf, isis::uint32_t nBytes, FileSystem::FAU_t file_address = CURRADDR,
+			                int flush_flag = 1, int bit_test = 1);
+
+   FileSystem::FAU_t     SeekTo(FileSystem::FAU_t file_address);
+   FileSystem::StreamPos FilePosition();
+
+   FileError Flush();
+
+   _TCHAR *FileName() { return file_name; }
+
+public: // 32-bit CRC checksum routines 
+  isis::uint32_t CalcChecksum(isis::uint32_t bytes, FileSystem::FAU_t file_address, int mem_alloc = 1);
+
+public:  // File Status functions
+  int IsOK()                  { return ((is_ok == 1) && (is_open == 1)); }
+  int IsOK()            const { return ((is_ok == 1) && (is_open == 1)); }
+  int IsOpen()                { return ((is_ok == 1) && (is_open == 1)); }
+  int IsOpen()          const { return ((is_ok == 1) && (is_open == 1)); }
+  int ReadyForReading()       { return ready_for_reading == 1; }
+  int ReadyForReading() const { return ready_for_reading == 1; }
+  int ReadyForWriting()       { return ready_for_writing == 1; }
+  int ReadyForWriting() const { return ready_for_writing == 1; }
+
+public:  // Exception handling functions
+  FileError   GetFileError()              { return file_error; }
+  FileError   GetFileError() const        { return file_error; }
+  void        SetFileError(FileError err) { file_error = err; }
+  void        ResetFileError()            { file_error = FILE_NO_ERROR; }
+  const TCHAR *FileExceptionMessage();
+
+public: // General purpose file utilities
+  static int Exists(const TCHAR *fname);
+  static FileSystem::FAU_t FileSize(const TCHAR *fname);
+  static int CanOpenForWriting(const TCHAR *fname);
+  static int CanOpenReadOnly(const TCHAR *fname);
+  
+};
+
+
+#ifdef __CPP_EXCEPTIONS__
+// Class declarations for exceptions representing program errors. 
+// This implementation is provided for use C++'s built-in exception
+// handling routines.
+
+class CFileBaseException 
+{
+	int error_;
+public:
+	CFileBaseException(int error) { error_ = error; }
+};  
+
+#endif // __CPP_EXCEPTIONS__
+ 
+#endif   // __FILE_BASE__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.cpp	(revision 16561)
@@ -0,0 +1,482 @@
+/**********************************************************************
+ *
+ * FileSystem.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+//
+// FileSystem -- The generic file pointer routines are a collection of member
+//               functions used to wrap  the underlying file system.
+
+
+#include "errno.h"
+#include "stdafx.h"
+#include "FileSystem.h"
+
+
+using namespace FileSystem;
+//---------------------------------------------------------------------
+// FileSystem::FPTR *Create(const _TCHAR *fname)
+//
+// Create a new file and truncate existing files.
+// Returns a null value if the file cannot be
+// created.
+//---------------------------------------------------------------------
+DLL_CODE_API FileSystem::FPTR *FileSystem::Create(const char *fname)
+{
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Create(fname);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	FPTR *stream = new FPTR;
+	if (!stream)
+		return 0;
+	stream->fptr = CreateFile(fname, 
+		GENERIC_READ | GENERIC_WRITE, 
+		FILE_SHARE_READ | FILE_SHARE_WRITE, 
+		NULL, // No security
+		CREATE_ALWAYS, 
+		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
+		NULL // No attr. template 
+		);
+	
+	if (stream->fptr == INVALID_HANDLE_VALUE)
+	{
+		delete stream;
+		return 0;
+	}
+	return stream;
+	
+#else // Use the 32-bit stdio version by default
+	FPTR *stream = new FPTR;
+	if (!stream)
+		return 0;
+	stream->fptr = fopen(fname, "w+b");
+	if (stream->fptr == 0)
+	{
+		delete stream; // 01/30/2002: Prevent memory leak
+		return 0;
+	}
+	return stream;
+#endif
+}
+
+//---------------------------------------------------------------------
+// FileSystem::FPTR *Open(const _TCHAR *fname, FileSystemAccessMode mode)
+//
+// Open an existing file. The "mode" variable determines if the file
+// is opened for read only or read/write access. Returns a null value
+// if the specified file cannot be opened. NOTE: This version of the
+// open functions will only accept: FILE_READONLY and FILE_READWRITE
+// access modes.
+//---------------------------------------------------------------------
+DLL_CODE_API FileSystem::FPTR *FileSystem::Open(const char *fname, FileSystem::AccessMode mode)
+{
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FSOpen(fname, mode);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	FPTR *stream = new FPTR;
+	if (!stream)
+		return 0;
+	if (mode == FILE_READONLY)
+	{
+		// Open for read only access
+		stream->fptr = CreateFile(fname, 
+			GENERIC_READ,
+			FILE_SHARE_READ | FILE_SHARE_WRITE, 
+			NULL, // No security
+			OPEN_EXISTING, 
+			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 
+			NULL // No attr. template 
+			);
+		if (stream->fptr == INVALID_HANDLE_VALUE)
+		{
+			delete stream;
+			return 0;
+		}
+	}
+	else 
+	{ // Open for read/write access
+		stream->fptr = CreateFile(fname, 
+			GENERIC_READ | GENERIC_WRITE, 
+			FILE_SHARE_READ | FILE_SHARE_WRITE, 
+			NULL, // No security 
+			OPEN_EXISTING, 
+			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 
+			NULL // No attr. template 
+			);
+		
+		if (stream->fptr == INVALID_HANDLE_VALUE)
+		{
+			delete stream;
+			return 0;
+		}
+	}
+	return stream;
+	
+#else // Use the 32-bit stdio version by default
+	FPTR *stream = new FPTR;
+	if (!stream)
+		return 0;
+	
+	if (mode == FILE_READONLY)
+	{
+		// Open for read only access
+		stream->fptr = fopen(fname, "rb");
+		if (stream->fptr == 0)
+		{
+			delete stream; // 01/30/2002: Prevent memory leak
+			return 0;
+		}
+	}
+	else 
+	{ // Open for read/write access
+		stream->fptr = fopen(fname, "r+b");
+		if (stream->fptr == 0)
+		{
+			delete stream; // 01/30/2002: Prevent memory leak
+			return 0;
+		}
+	}
+	return stream;
+#endif
+}
+
+//---------------------------------------------------------------------
+// int Close(FileSystem::FPTR *stream)
+//
+// Close an open file. Returns a non-zero value to  indicate an error 
+// condition or zero if successful.
+//---------------------------------------------------------------------
+DLL_CODE_API int FileSystem::Close(FPTR *stream)
+{
+	if (!stream)
+		return 0; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Close(stream);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	if (!CloseHandle(stream->fptr))
+		return 1;
+	delete stream;
+	stream = 0;
+	return 0;
+	
+#else // Use the 32-bit stdio version by default
+	if (fclose(stream->fptr) != 0)
+		return 1;
+	delete stream;
+	stream = 0; // 02/02/2002: Set pointer to zero after releasing it
+	return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// int Flush(FileSystem::FPTR *stream)
+//
+// Flush the any open disk buffers. Returns a non-zero value to indicate
+// an error // condition or zero if successful. 
+//
+// ATTENTION: ****MS VC++*** YOU SHOULD LINK YOUR APPLICATION WITH COMMODE.OBJ 
+// file. COMMODE.OBJ changes the global commit flag such that 
+// calling fflush() or _flushall() commits the file buffer to disk.
+// The Windows NT operating system provides the WIN32 API FlushFileBuffers().
+// The _commit() function included with the Visual C++ 32-bit Edition 
+// CRT calls FlushFileBuffers to write buffered data to disk. 
+//-----------------------------------------------------------------------------
+DLL_CODE_API int FileSystem::Flush(FPTR *stream)
+{
+	if (!stream)
+		return 0; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Flush(stream);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	if (!FlushFileBuffers(stream->fptr))
+		return 1;
+	return 0;
+	
+#else // Use the 32-bit stdio version by default
+	int ret = fflush(stream->fptr);
+	if (ret != 0)
+	{
+		TRACE("\nerrno=%d\n\n\n", errno);
+		return 1;       // Error
+	}
+	else
+		return 0;
+#endif
+}
+
+//---------------------------------------------------------------------
+// int Read(FileSystem::FPTR *stream, void *buf, isis::uint32_t bytes)
+//
+// Read a specified number of bytes from the current file position
+// into a memory buffer. Returns the total number if bytes read from the
+// file.
+//---------------------------------------------------------------------
+DLL_CODE_API isis::ulong32_t FileSystem::Read(FPTR *stream, void *buf, 
+											  isis::ulong32_t bytes)
+{
+	if (!stream)
+		return (isis::ulong32_t)0; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Read(stream, buf, bytes);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	DWORD bytes_read; 
+	if (!ReadFile(stream->fptr, (LPVOID)buf, (DWORD)bytes, &bytes_read, NULL))
+	{
+		return (isis::ulong32_t)0;
+	}
+	return (isis::ulong32_t)bytes_read;
+	
+#else // Use the 32-bit stdio version by default
+	return (isis::ulong32_t)fread((unsigned char *)buf, 1, bytes, stream->fptr);
+#endif
+}
+//---------------------------------------------------------------------
+// int Write(FileSystem::FPTR *stream, const void *buf, isis::uint32_t bytes) 
+//
+// Write a specific number of bytes from a memory buffer to the
+// current file position. Returns the total number if bytes written
+// to the file.
+//---------------------------------------------------------------------
+DLL_CODE_API isis::ulong32_t FileSystem::Write(FPTR *stream, const void *buf, 
+				                   isis::ulong32_t bytes) 
+{
+	if (!stream)
+		return (isis::ulong32_t)0; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Write(stream, buf, bytes);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	DWORD bytes_moved; 
+	if (!WriteFile(stream->fptr, (LPVOID)buf, (DWORD)bytes, &bytes_moved, NULL))
+	{
+		return (isis::ulong32_t)0;
+	}
+	return (isis::ulong32_t)bytes_moved;
+	
+#else // Use the 32-bit stdio version by default
+	return (isis::ulong32_t)fwrite((const unsigned char *)buf, 1, bytes, stream->fptr);
+#endif
+}
+//---------------------------------------------------------------------------
+// isis::int32_t Seek(FileSystem::FPTR *stream, isis::int32_t offset,  FileSystemSeekMode mode)
+//
+// Seek to the specified offset starting at the beginning (SEEK_SET),
+// end (SEEK_END) or current offset (SEEK_CUR). Returns a negative 1
+// to indicate an error condition or the current stream position if
+// successful.
+//----------------------------------------------------------------------------
+DLL_CODE_API FAU_t FileSystem::Seek(FPTR *stream, FAU_t offset, FileSystem::SeekMode mode)
+{
+	if (!stream)
+		return (FAU_t) - 1; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Seek(stream, offset, mode);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	if (mode == FILE_SEEK_BEG)
+	{
+		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_BEGIN);
+	}
+	else if (mode == FILE_SEEK_CUR)
+	{
+		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_CURRENT);
+	}
+	else if (mode == FILE_SEEK_END)
+	{
+		return (FAU_t)SetFilePointer(stream->fptr, offset, NULL, FILE_END);
+	}
+	else 
+	{ // An invalid seek mode was specified
+		return (FAU_t) - 1;
+	}
+	
+#else // Use the 32-bit stdio version by default
+	if (mode == FILE_SEEK_BEG)
+	{
+		return fseek(stream->fptr, offset, SEEK_SET);
+	}
+	else if (mode == FILE_SEEK_CUR)
+	{
+		return fseek(stream->fptr, offset, SEEK_CUR);
+	}
+	else if (mode == FILE_SEEK_END)
+	{
+		return fseek(stream->fptr, offset, SEEK_CUR);
+	}
+	else 
+	{ // An invalid seek mode was specified
+		return (FAU_t) - 1;
+	}
+#endif
+}
+//----------------------------------------------------------------------------
+// isis::int32_t Tell(FileSystem::FPTR *stream)
+//
+//----------------------------------------------------------------------------
+DLL_CODE_API FAU_t FileSystem::Tell(FPTR *stream)
+{
+	if (!stream)
+		return (FAU_t) - 1; // The file stream is not open
+	
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Tell(stream);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	return (__LWORD__)SetFilePointer(stream->fptr, 0, NULL, FILE_CURRENT);
+	
+#else // Use the 32-bit stdio version by default
+	return ftell(stream->fptr);
+#endif
+}
+
+//----------------------------------------------------------------------------
+// int Exists(const char *fname)
+//
+// Returns true if the specified file exists.
+//----------------------------------------------------------------------------
+DLL_CODE_API int FileSystem::Exists(const char *fname)
+// Returns true if the specified file exists.
+{
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64Exists(fname);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	HANDLE hFile = CreateFile(fname, 
+		GENERIC_READ,
+		FILE_SHARE_READ | FILE_SHARE_WRITE, 
+		NULL, // No security
+		OPEN_EXISTING, 
+		FILE_ATTRIBUTE_NORMAL, 
+		NULL // No attr. template
+		);
+	
+	if (hFile == INVALID_HANDLE_VALUE)
+	{
+		return 0;
+	}
+	
+	CloseHandle(hFile);
+	return 1;
+	
+#else // Use the 32-bit stdio version by default
+	struct stat buf;
+	return stat(fname, &buf) ==(FAU_t)0;
+#endif
+}
+
+
+
+//----------------------------------------------------------------------------
+// FAU_t FileSize(const _TCHAR *fname)
+//
+// Returns the length of the specified file or negative 1 to indicate an
+// error condition.
+//----------------------------------------------------------------------------
+DLL_CODE_API FAU_t FileSystem::FileSize(const char *fname)
+{
+#if defined(__64_BIT_FILE_SYSTEM__)
+	return FS64FileSize(fname);
+	
+#elif defined(__WIN32__) && defined(__NTFS__)
+	HANDLE hFile = CreateFile(fname, 
+		GENERIC_READ,
+		FILE_SHARE_READ | FILE_SHARE_WRITE, 
+		NULL, // No security
+		OPEN_EXISTING, 
+		FILE_ATTRIBUTE_NORMAL, 
+		NULL // No attr. template 
+		);
+	
+	// Could not open the specified file
+	if (hFile == INVALID_HANDLE_VALUE)
+	{
+		return (FAU_t) - 1;
+	}
+	
+	BY_HANDLE_FILE_INFORMATION info;
+	
+	if (GetFileInformationByHandle(hFile, &info) == 0)
+	{
+		return (FAU_t) - 1;
+	}
+	CloseHandle(hFile);
+	return (FAU_t)info.nFileSizeLow;;
+	
+#else // Use the 32-bit stdio version by default
+	struct stat buf;
+	int result = stat(fname, &buf);
+	if (result != 0)
+		return -1;
+	return buf.st_size;
+#endif
+}
+
+//  #include <io.h>
+//  #include <share.h>
+
+
+//  int FileSystem::CanOpenForWriting(const _TCHAR *fname)
+//  {
+//  	// Try to open for writing
+//  	int fd = _tsopen(fname, _O_WRONLY | O_BINARY, _SH_DENYWR);
+//  	if (fd == -1)
+//  		return 0;
+//  	_close(fd);
+//  	return 1;
+//  }
+
+//  int FileSystem::CanOpenReadOnly(const _TCHAR *fname)
+//  {
+//  	// Try to open for read only
+//  	int fd = _tsopen(fname, _O_RDONLY | O_BINARY, _SH_DENYNO);
+//  	if (fd == -1)
+//  		return 0;
+//  	_close(fd);
+//  	return 1;
+//  }
+
+//----------------------------------------------------------------------------
+// int IsReadOnly(const _TCHAR* fname)
+//
+// Returns true if the specified file is read only.
+//----------------------------------------------------------------------------
+//  int FileSystem::IsReadOnly(const _TCHAR *fname)
+//  {
+//  	struct _stat buf;
+//  	_tstat(fname, &buf);
+//  	if (buf.st_mode & S_IWRITE)
+//  		return 0;
+//  	return 1;
+//  }
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/FileSystem.h	(revision 16561)
@@ -0,0 +1,121 @@
+/**********************************************************************
+ *
+ * FileSystem.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////
+// FileSystem.h -- The generic file pointer routines are a collection of member
+//                 functions used to wrap  the underlying file system.
+
+//
+
+  
+#ifndef __FILE_SYSTEM__
+#define __FILE_SYSTEM__
+
+// Non-platform specific include files
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include "DLLCode.h"
+#include "IsisTypes.h"
+
+namespace FileSystem {
+	
+// ----------------
+// Type definitions 
+// ----------------
+// typedefs for 64-bit and 32-bit file operations and stream position
+#if defined (__64_BIT_FILE_SYSTEM__)
+    typedef isis::int64_t FAU;       // 64-bit File Address Unit, physical file address type
+    typedef isis::int64_t FAU_t;     // Native 64-bit FAU type
+    typedef isis::int64_t StreamPos; // 64-bit stream position
+#else // Use the 32-bit version by default
+	typedef isis::int32_t FAU;       // 32-bit File Address Unit, physical file address type
+	typedef isis::int32_t FAU_t;     // Native 32-bit FAU type
+	typedef isis::int32_t StreamPos; // 32-bit stream position
+#endif
+
+
+// Select the underlying file system 
+#if defined (__64_BIT_FILE_SYSTEM__)
+	#include "FS64.H"              // WIN32 will always use NTFS for large files
+	typedef  FPTR64 FPTR;
+
+
+#elif defined (__WIN32__) && defined (__NTFS__)
+	// WIN32 file I/O API
+	#include <windows.h>
+	#include <io.h>
+	#include <string.h>
+	#include <stdlib.h>
+	#include <fcntl.h>
+	#include <sys/types.h>
+	#include <sys/stat.h>
+	#include <stdio.h>
+	#include "gxdtypes.h"
+	#include "gxheader.h"
+
+	struct DLL_CODE_API FPTR { // Platform specific file pointer type
+		HANDLE fptr;
+	};
+
+#else // Use the stdio version by default. Common to all platforms.
+	// Non-platform specific stdio include files
+	#include <string.h>
+	#include <stdlib.h>
+	#include <fcntl.h>
+	#include <sys/types.h>
+	#include <sys/stat.h>
+	#include <stdio.h>
+	#include "IsisTypes.h"
+	//#include "gxheader.h"
+
+	struct DLL_CODE_API FPTR { // File pointer type
+		 FILE *fptr;
+	};
+#endif // File system type
+
+
+
+    // NOTE: Any underlying file system used must provide the basic
+    // functionallity defined here.
+    DLL_CODE_API FPTR            *Create(const char  *fname);
+    DLL_CODE_API FPTR            *Open(const char *fname, AccessMode mode);
+    DLL_CODE_API int             Close(FPTR *stream);
+    DLL_CODE_API int             Flush(FPTR *stream);
+    DLL_CODE_API isis::ulong32_t Read(FPTR *stream, void *buf, isis::ulong32_t bytes);
+    DLL_CODE_API isis::ulong32_t Write(FPTR *stream, const void *buf, isis::ulong32_t bytes);
+    DLL_CODE_API FAU_t           Seek(FPTR *stream, FAU_t, SeekMode mode);
+    DLL_CODE_API FAU_t           Tell(FPTR *stream);
+    DLL_CODE_API int             Exists(const _TCHAR *fname);
+    DLL_CODE_API FAU_t           FileSize(const _TCHAR *fname);
+    // DLL_CODE_API int             CanOpenForWriting(const _TCHAR *fname);
+    // DLL_CODE_API int             CanOpenReadOnly(const _TCHAR *fname);
+    // DLL_CODE_API int             IsReadOnly(const _TCHAR *fname);
+}
+
+#endif // __FILE_SYSTEM__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.cpp	(revision 16561)
@@ -0,0 +1,113 @@
+/**********************************************************************
+ *
+ * IErrors.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////////////////////////////
+// Error.cpp
+
+// #include "stdafx.h"
+// The Error functions and classes are used to catch and/or
+// record database exceptions that occur during run-time. This
+// implementation can be used with or without C++ built-in exception
+// handling. If C++ exception handling is not enabled with the
+// __CPP_EXCEPTIONS__ macro, then the vbDatabaseError enumerated
+// constants can be used to record database errors.
+#include "IErrors.h"
+
+// NOTE: This array must contain the same number of exceptions as the
+// IsisError enumeration. 
+const int MaxExceptionMessages = 57;
+const char* isisExceptionMessages[MaxExceptionMessages] = {
+  "Database exception: No exception reported",               // None
+  "Database exception: Invalid error code reported",         // Invalid
+  "Database exception: Memory exhausted",
+  "Database exception: Error opening xrf file",
+  "Database exception: Error creating xrf file",
+  "Database exception: Error reading xrf file",
+  "Database exception: Error writing xrf file",
+  "Database exception: xrf file is corrupted",
+  "Database exception: xrf file, invalid block",
+  "Database exception: xrf file, invalid status",
+  "Database exception: Error creating an existing mf file",
+  "Database exception: Error opening mf file",
+  "Database exception: Error opening mf file",
+  "Database exception: Error creating mf file",
+  "Database exception: Error reading mf file",
+  "Database exception: Error writing mf file",
+  "Database exception: Access Violation",                    // AccessViolation
+  "Database exception: Assertion failed",                    // AssertError
+  "Database exception: Wrong object type",                   // BadClassID
+  "Database exception: Bad object address",                  // BadObjectAddress
+  "Database exception: Bad reference",                       // BadReference
+  "Database exception: Cache full",                          // CacheFull
+  "Database exception: Checksum error",                      // ChecksumError
+  "Database exception: Divide by zero error",                // DivideByZero
+  "Database exception: Unexpected end of file was reached",  // EOFError
+  "Database exception: Error closing file",                  // FileCloseError
+  "Database exception: File is corrupt",                     // FileCorrupt
+  "Database exception: Error creating file",                 // CreationError
+  "Database exception: File already exists",                 // FileExists
+  "Database exception: Trying to use a closed file",         // FileNotOpenError
+  "Database exception: File not ready for reading/writing",  // FileNotReady
+  "Database exception: Could not write to file",             // NotWriteable
+  "Database exception: Error opening file",                  // FileOpenError
+  "Database exception: Cannot obtain a file position",       // FilePostionError
+  "Database exception: Error reading from file",             // FileReadError
+  "Database exception: Error seeking in file",               // FileSeekError
+  "Database exception: Error writing to file",               // FileWriteError
+  "Database exception: No database open",                    // NoDatabaseOpen
+  "Database exception: No such file exists",                 // NoFileExists
+  "Database exception: No objects exist",                    // NoObjectsExist
+  "Database exception: Accessing a null pointer",            // NullPtr
+  "Database exception: Object already exists",               // ObjectExists
+  "Database exception: Another object is referencing this file", // Referenced
+  "Database exception: Math overflow error",                 // OverFlow
+  "Database exception: Parse error",                         // ParseError
+  "Database exception: Invalid path",                        // PathError
+  "Database exception: Trying to write to a read-only file", // ReadOnlyFile
+  "Database exception: Stack empty",                         // StackEmpty
+  "Database exception: Stack full",                          // StackFull
+  "Database exception: Synchronization Error",               // SyncError    
+  "Database exception: Math under-flow error",               // UnderFlow
+  "Database exception: Wrong file type",                     // WrongFileType
+
+  // Persistent lock error messages
+  "Database exception: Invalid lock type specified",          // LOCK_TYPE
+  "Database exception: Cannot access the file lock header",   // FLK_ACCESS
+  "Database exception: Cannot lock the file",                 // FLK_ERROR
+  "Database exception: Cannot access the record lock header", // RLK_ACCESS
+  "Database exception: Cannot lock the specified record"      // RLK_ERROR
+};
+
+const char *isisExceptionMessage(IsisError err)
+// Standalone function that returns a null terminated string that can
+// be use to log or print a database exception message.
+{
+  int error = (int)err;
+  if(error > MaxExceptionMessages) error = ISIS_INVALID_CODE;
+
+  // Find the corresponding message in the exception array
+  return isisExceptionMessages[error];
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IErrors.h	(revision 16561)
@@ -0,0 +1,104 @@
+/**********************************************************************
+ *
+ * IErrors.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef __ISIS_ERROR_H__
+#define __ISIS_ERROR_H__
+
+enum IsisError { // Database error codes
+  ISIS_NO_ERROR = 0,        // No errors reported
+  ISIS_INVALID_CODE,
+  ISIS_BAD_ALLOC,
+  ISIS_XRF_OPEN_ERROR,      // Xrf open error
+  ISIS_XRF_CREATE_ERROR,    // Xrf creation error
+  ISIS_XRF_READ_ERROR,      // Xrf read error
+  ISIS_XRF_WRITE_ERROR,     // Xrf write error
+  ISIS_XRF_CORRUPT,         // Xrf corrupted
+  ISIS_XRF_INVALID_BLOCK,   // Xrf invalid block
+  ISIS_XRF_INVALID_STATUS,  // Xrf invalid status
+  ISIS_MF_ALREADY_EXIST_ERROR, // Attempt to create an existing mf
+  ISIS_MF_OPEN_ERROR,       // Mf open error
+  ISIS_MF_CREATE_ERROR,     // Mf creation error
+  ISIS_MF_READ_ERROR,       // Mf read error
+  ISIS_MF_WRITE_ERROR,      // Mf write error 
+  ISIS_ASSERT_ERROR,        // Assertion failed
+  ISIS_BAD_CLASS_ID,        // Wrong object type
+  ISIS_BAD_OBJECT_ADDRESS,  // Bad object address  ISIS_BAD_REFERENCE,       // Bad Reference
+  ISIS_CACHE_FULL,          // Cache full
+  ISIS_CHECKSUM_ERROR,      // Checksum Error
+  ISIS_DIVIDEBY_ZERO,       // Divide By Zero Error
+  ISIS_EOF_ERROR,           // Unexpected end of file
+  ISIS_FILE_CLOSE_ERROR,    // Error closing file
+  ISIS_FILE_CORRUPT,        // File corrupted
+  ISIS_FILE_CREATION_ERROR, // Error creating file
+  ISIS_FILE_EXISTS,         // File already exists
+  ISIS_FILE_NOT_OPEN_ERROR, // Trying to use a closed file
+  ISIS_FILE_NOT_READY,      // File not ready (failed or closed file)
+  ISIS_FILE_NOT_WRITEABLE,  // Could not write to file 
+  ISIS_FILE_OPEN_ERROR,     // Error opening file
+  ISIS_FILE_POSITION_ERROR, // Cannot obtain the current file position
+  ISIS_FILE_READ_ERROR,     // Error reading file  
+  ISIS_FILE_SEEK_ERROR,     // Error seeking in file
+  ISIS_FILE_WRITE_ERROR,    // Error writing to file
+  ISIS_NO_DATABASE_OPEN,    // No database open
+  ISIS_NO_FILE_EXISTS,      // No such file exists
+  ISIS_NO_OBJECTS_EXIST,    // No objects exist
+  ISIS_NULL_PTR,            // Accessing a null pointer 
+  ISIS_OBJECT_EXISTS,       // Object already exists
+  ISIS_OPEN_FILE_REFERENCE, // Another object is referencing this file
+  ISIS_OVERFLOW,            // Math overflow
+  ISIS_PARSE_ERROR,         // Parse error
+  ISIS_PATH_ERROR,          // Invalid path
+  ISIS_READ_ONLY_FILE,      // Trying to write to read-only file
+  ISIS_STACK_EMPTY,         // Stack empty
+  ISIS_STACK_FULL,          // Stack full
+  ISIS_SYNC_ERROR,          // Synchronization Error
+  ISIS_UNDERFLOW,           // Math under-flow
+  ISIS_WRONG_FILE_TYPE,     // Wrong file type
+
+  // Persistent lock error codes
+  ISIS_INVALID_LOCK_TYPE,        // Invalid lock type specified
+  ISIS_MFLOCK_ACCESS_ERROR,    // The file lock cannot be accessed
+  ISIS_MFLOCK_ERROR,           // Error locking the file
+  ISIS_RECORDLOCK_ACCESS_ERROR,  // The record lock cannot be accessed
+  ISIS_RECORDLOCK_ERROR          // Error locking a record
+};
+
+// Standalone functions use to report database errors
+const char *isisExceptionMessage(IsisError err);
+
+#ifdef __CPP_EXCEPTIONS__
+// Class declarations for exceptions representing program errors. 
+// This implementation is provided for use C++'s built-in exception
+// handling routines.
+
+// A fatal file error condition has been reached. All database engine
+// calls must catch this exception and evaluate it by testing the
+// vbDatabase or vbDatabase64 internal error variable.
+class CIsisException { };  
+
+#endif // __CPP_EXCEPTIONS__
+
+#endif // __ISIS_ERROR_H__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.cpp	(revision 16561)
@@ -0,0 +1,252 @@
+/**********************************************************************
+ *
+ * IsisDb.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// ISISDB: Isis Master file methods
+////////////////////////////////////////////////////////////
+#include "stdafx.h"
+#include "IsisDb.h"
+#include "XRFFile.h"
+
+
+
+//-------------------------------------------------------------------------------- 
+// IsisDb::IsisDb()
+//
+// Constructor, Makes an ISIS Data Base object that isn't connected to a Master 
+// file, Cross Reference file and Fdt.
+//-------------------------------------------------------------------------------
+IsisDb::IsisDb()
+{
+	bReadOnly_ = false;
+	try
+	{
+        mf_  = new MfFile;
+	    xf_  = new XrfFile; 
+	    fdt_ = new CFdt;
+	}
+	catch(std::bad_alloc)
+	{
+		throw CIsisDbException(ISIS_BAD_ALLOC, __FILE__, __LINE__);
+	}
+}
+ 
+//////////////////////////////
+ 
+IsisDb::~IsisDb( )
+{
+   delete mf_; delete xf_; delete fdt_;
+}
+ 
+ 
+//////////////////////////////
+ 
+int IsisDb::CreateDb(const TCHAR *fname)
+{
+   name = fname;
+   MkNames();
+   int rc = ISIS_NO_ERROR;
+
+   // We should not try to create a mf_ which already exist!
+   if (CFileBase::Exists(mfname_))
+   {
+	   throw CIsisDbException(ISIS_MF_ALREADY_EXIST_ERROR, __FILE__, __LINE__);
+   }
+   else
+   {
+       try
+	   {
+           rc = mf_->CreateMf(mfname_, USERMST);   
+           rc = xf_->CreateXrf(xrfname_);
+           rc = fdt_->Load(fdtname_);
+	   }
+       catch(CIsisDbException)
+	   {
+    	   throw CIsisDbException(ISIS_MF_OPEN_ERROR, __FILE__, __LINE__);
+	   }
+   }
+   
+   return rc;
+}
+ 
+ 
+//////////////////////////////
+ 
+void IsisDb::CloseDb()
+{
+   mf_->CloseMf();
+   xf_->Close();
+}
+ 
+//////////////////////////////
+ 
+void IsisDb::MkNames()
+// Make file names
+{
+    const _TCHAR *s = name.c_str();
+	_TCHAR *ps = new _TCHAR[_tcslen(s)+1];
+	_tcscpy(ps, s);
+	_TCHAR * tmp = _tcsrchr(ps, _T('.'));
+	assert(tmp != NULL);
+    // Terminate the string at the last dot 
+    *tmp = _T('\0');
+
+   _tcscpy(mfname_,ps);
+   _tcscat(mfname_,_T(".mst"));
+   _tcscpy(xrfname_,ps);
+   _tcscat(xrfname_,_T(".xrf"));
+   _tcscpy(fdtname_,ps);
+   _tcscat(fdtname_,_T(".fdt"));
+   _tcscpy(idxname_,ps);
+   _tcscat(idxname_,_T(".idx"));
+   delete[] ps;
+}
+ 
+//////////////////////////////
+ 
+int IsisDb::OpenDb(const TCHAR *fname,  FileSystem::AccessMode mode
+				                              /* = FileSystem::FILE_READWRITE */)
+{
+   name = fname;
+   MkNames();
+   int rc;
+   try
+   {
+     // if (FileSystem::IsReadOnly(mfname_))
+     //	   mode = FileSystem::FILE_READONLY;
+       rc = mf_->OpenMf(mfname_, mode);
+       rc = xf_->OpenXrf(xrfname_, mode);
+       rc = fdt_->Load(fdtname_);
+   }
+   catch(CIsisDbException)
+   {
+	   throw CIsisDbException(ISIS_MF_OPEN_ERROR, __FILE__, __LINE__);
+   }
+
+   return rc;
+}
+ 
+//----------------------------------------------------------------------------------
+// int  IsisDb::ReadDbRecord(long mfn, MfRecord &m)
+//
+// This function reads Master File record with MFN "mfn" into m
+//
+// Returns the status of the record: 
+// Active=0, LogicalyDeleted=1, PhysicalyDeleted=2, EndOfFile=-1, NewRecord = 3
+//----------------------------------------------------------------------------------
+int  IsisDb::ReadDbRecord(long mfn, MfRecord &m)
+{
+   long mfb;
+   int mfp;
+   int status;
+   bool rc;
+
+   try
+   {
+       status = xf_->GetMfp(mfn, mfb, mfp);
+       if (status == Active || status == LogicalyDeleted)
+	       if (!mf_->ReadMfRecord(mfb, mfp, m))
+			   status = -1;    // Error in reading the record!
+   }
+   catch(CIsisDbException)
+   {
+	   throw CIsisDbException(ISIS_MF_OPEN_ERROR, __FILE__, __LINE__);
+   }
+   return status;
+}
+ 
+//--------------------------------------------------------------------------------
+// void CreateDbRecord(MfRecord &m)
+//
+// Create a new record at the end of the master file. The Master file address is
+// set into the pair mfb/mfp and the XRF file is updated.
+//---------------------------------------------------------------------------------
+ void IsisDb::CreateDbRecord(MfRecord &m)
+{
+ 
+}
+
+//--------------------------------------------------------------------------------
+// void CreateDbRecordWithMfn(MfRecord &m)
+//
+// Create a new record at the end of the master file. The Master file address is
+// set into the pair mfb/mfp and the XRF file is updated.
+//---------------------------------------------------------------------------------
+ void IsisDb::CreateDbRecordWithMfn(MfRecord &m)
+{
+ 
+}
+
+ 
+//----------------------------------------------------------------------------------
+// void  UpdateDbRecord(MfRecord &m)
+//
+// Update of existing records. Whenever a record is updated, the system writes
+// the record back to the master file. Where it is written depends on the status
+// of the record when it was initially read.
+//
+//----------------------------------------------------------------------------------
+ void  IsisDb::UpdateDbRecord(MfRecord &m)
+{
+}
+//----------------------------------------------------------------------------------
+// int  IsisDb::DeleteRecord(long mfn)
+//
+// This function logically deletes a Master File record with MFN "mfn".
+// On XRF, xrfmfb is negative and on MST, status is set to 1
+//
+// Returns true if successful 
+//----------------------------------------------------------------------------------
+bool  IsisDb::DeleteRecord(long mfn)
+{
+
+   return false;
+}
+
+long  IsisDb::GetNextActiveMfn(long mfn /* = -1 */)
+{
+	if (mfn == -1)
+		mfn = 1;
+	else
+		mfn++;
+	long  mfb;
+	int  mfp;
+	int iRet = xf_->GetMfp(mfn, mfb, mfp);
+	while (iRet != Active && iRet != EndOfFile)
+	{
+		mfn++;
+        iRet = xf_->GetMfp(mfn, mfb, mfp);
+	}
+	if (iRet == EndOfFile)
+		return 0;
+	else
+		return mfn;
+}
+	
+_TCHAR *IsisDb::GetIdxFileName()
+{
+	return idxname_;
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisDb.h	(revision 16561)
@@ -0,0 +1,130 @@
+/**********************************************************************
+ *
+ * IsisDb.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// Isisdb.h : Class Definition for an ISIS db
+////////////////////////////////////////////////////////////
+ 
+#ifndef __ISISDB_H__
+#define __ISISDB_H__
+
+#include "AbstractIsisDb.h"
+#include "IsisDef.h"
+#ifndef _MFFILE_H_
+#include "MFFile.h"
+#endif
+
+ 
+class IsisDb : public AbstractIsisDb
+{
+	friend class CDBaseFile;
+
+	bool  bReadOnly_;
+    TCHAR xrfname_[_MAX_PATH];
+    TCHAR mfname_[_MAX_PATH];
+    TCHAR fdtname_[_MAX_PATH];
+    TCHAR idxname_[_MAX_PATH];
+protected:
+   void MkNames();
+public:
+   IsisDb();
+   virtual ~IsisDb( );
+   int OpenDb(const TCHAR *fname, FileSystem::AccessMode mode =
+	                                          FileSystem::FILE_READWRITE);
+   int CreateDb(const TCHAR *fname);
+
+
+// Closing the database
+   virtual void CloseDb();       // Close the db
+
+   virtual void ClearDb()   { }    // Clear the db
+   virtual void DestroyDb() { }  // Delete the db
+
+// Recovery
+   virtual bool IsDamaged() { return false;}   // Test if the database is damaged
+   virtual bool Recover()   { return true; }   // Recover the db
+
+// Transactions
+   virtual void BeginTransaction()    { }  // Begin a transaction
+   virtual void CommitTRansaction()   { }  // Commit a transaction
+   virtual void RollbackTransaction() { }  // Rollback a transaction
+   virtual bool InTransaction()       { return false; }  // Test if transaction is pending
+
+   virtual void CreateIndex(int i) { }
+   virtual void DropIndex(int i)   { }
+
+   void  CreateDbRecord(MfRecord &m);
+   void  CreateDbRecordWithMfn(MfRecord &m);
+   void  UpdateDbRecord(MfRecord &m);
+   bool  DeleteRecord(long mfn);
+   int   ReadDbRecord(long mfn, MfRecord &m);
+   int   ReadNextDbRecord(MfRecord &m);
+   void  PrintDbRecord(MfRecord &m);
+   void  PrintDbHdr();
+
+   void SetNextMfn(long mfn) { mf_->setNextMfn(mfn); }
+   long GetNextMfn()        { return mf_->GetNextMfn(); }
+   long GetNextActiveMfn(long mfn=-1);
+
+   SMfHeader& GetSMfHeader() { return mf_->GetSMfHeader(); }
+   _TCHAR *GetIdxFileName();
+
+   bool IsReadOnly() { return bReadOnly_; }
+};
+ 
+//////////////////////////////
+ 
+inline int IsisDb::ReadNextDbRecord(MfRecord &m)
+{
+   return 1;
+}
+ 
+//////////////////////////////
+ 
+inline void  IsisDb::PrintDbRecord(MfRecord &m)
+{
+   mf_->PrintMfRecord(m);
+}
+ 
+//////////////////////////////
+ 
+inline void  IsisDb::PrintDbHdr()
+{
+   mf_->PrintMfHdr();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class IsisDbFactory : public AbstractIsisDbFactory
+{
+public:
+	virtual AbstractIsisDb* MakeIsisDb() { return new IsisDb(); }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+
+
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisDef.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisDef.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisDef.h	(revision 16561)
@@ -0,0 +1,138 @@
+/**********************************************************************
+ *
+ * IsisDef.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef __ISIS_DEFINITION_H__
+#define __ISIS_DEFINITION_H__
+
+// disable warning C4786: symbol greater than 255 character,
+// okay to ignore
+#pragma warning(disable: 4786)
+
+#ifndef __ISIS_TYPE_H__
+#include "IsisTypes.h"
+#endif
+
+#include <sstream>
+
+typedef std::string ustring;
+typedef std::string cstring;
+// typedef std::wstring awstring;
+typedef std::stringstream ustringstream;
+typedef std::stringstream cstringstream;
+typedef wchar_t WCHAR;
+
+
+const size_t INVALID_NPOS = ~(size_t)0;
+
+namespace FileSystem {
+    // Generic File access/open mode enumeration
+    enum AccessMode { 
+       FILE_READONLY,   // Open file with read access only
+       FILE_WRITEONLY,  // Open file with write access only
+       FILE_READWRITE,  // Open file with read and write access
+       FILE_CREATE,     // Create the file if it does not exist
+       FILE_NO_CREATE,  // Do not create the file if it does not exist
+       FILE_TRUNCATE,   // Truncate the file  
+       FILE_APPEND,     // Append to the file
+       FILE_SHARE,      // Enable file sharing  (Platform specific)
+       FILE_EXCLUSIVE   // Disable file sharing (Platform specific)
+	};
+
+
+    // Generic File seek mode enumeration
+    enum SeekMode { 
+      FILE_SEEK_BEG, // Seek starting from the beginning of the file
+      FILE_SEEK_CUR, // Seek starting from the current location
+      FILE_SEEK_END  // Seek starting from the end of the file
+    };
+};
+
+#ifndef __FILE_SYSTEM__
+#include "FileSystem.h"
+#endif
+
+#ifndef __FILE_BASE__
+#include "File.h"
+#endif
+
+#ifndef __ISIS_ERROR_H__
+#include "IErrors.h"
+#endif
+
+#ifdef _WIN32
+#ifndef __DEBUG_H__
+#define _DEBUG
+#define _CONSOLE
+#include "debug.h"
+#endif
+#endif
+
+#ifndef __ABSTRACT_ISISDB_H__
+#include "AbstractIsisDb.h"
+#endif
+
+#ifndef __ISISDB_H__
+#include "IsisDb.h"
+#endif
+
+
+#ifndef __MASTER_RECORD_H__
+#include "Master.h"
+#endif
+
+#ifndef __FDT_H__
+#include "Fdt.h"
+#endif
+
+
+enum { RecordDeleted=0, RecordUpdated=1, RecordCreated=2};
+enum { Active=0, LogicalyDeleted=1, PhysicalyDeleted=2,
+       EndOfFile=-1, NewRecord = 3 };
+
+
+
+
+// Class declarations for exceptions representing program errors. 
+// This implementation is provided for use C++'s built-in exception
+// handling routines.
+
+class CIsisDbException { 
+public:
+	int m_iError;
+	CIsisDbException(const int iError, const char* file_name, const long line_number) 
+	{
+		m_iError = iError;
+#ifdef _WIN32
+		AssertFailed(isisExceptionMessage((IsisError) m_iError), file_name, line_number);
+#else
+		std::cout << isisExceptionMessage((IsisError) m_iError) << file_name
+			      << " Line=" << line_number;
+#endif
+	}
+};  
+
+
+#endif // __ISIS_DEFINITION_H__
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisGdl.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisGdl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisGdl.cpp	(revision 16561)
@@ -0,0 +1,135 @@
+/**********************************************************************
+ *
+ * IsisGdl.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+// IsisGdl.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "IsisDb.h"
+#include <iostream>
+
+void PausePrg()
+{
+	std::cout << "\n";
+	std::cout << "Press enter to continue..." << "\n";
+	std::cin.get();
+}
+
+void ClearInputStream(std::istream &s)
+// Used to clear istream
+{
+	char c;
+	s.clear();
+	while(s.get(c) && c != '\n') { ; }
+}
+
+// Should be called with the master file name as parameter
+// IsisGdl "full_path\master_file_name" 
+// IsisGdl C:\IsisGdl\cds.mst for ex
+
+
+int main(int argc, _TCHAR* argv[])
+{
+
+	if (argc < 2 || argc > 3)
+	{
+		std::cout << "usage: IsisGdl [-include_logically_deleted_records] master_file_full_name" << std::endl;
+		return 1;
+	}
+	// Instantiation of the IsisDb Object
+	IsisDb db;
+	try
+	{
+		// The file name should be prefixed with the full path
+		// and should be the master file name with extension ".mst"
+		// "C:\IsisGdl\cds.mst" for ex
+		db.OpenDb(argv[argc - 1]);
+	}
+	catch(CIsisDbException e)
+	{
+		std::string msg = "Cannot open isis db: ";
+		msg += argv[argc - 1];
+		msg += isisExceptionMessage((IsisError) e.m_iError);
+		std::cerr<< msg << std::endl;
+		return false;
+	}
+
+	// Create a Master File object without the Field Definition
+	// Information
+	MfRecord mfr(0); 
+
+	// Extract the number of the next master file number to
+	// assign
+	const long nextMfn = db.GetNextMfn();
+
+
+
+	// Browse the master file in increasing order of Master
+	// file numbers (MFN is the record ID)
+	for (long i=1; i<nextMfn; i++)
+	{
+
+		int status = db.ReadDbRecord(i, mfr);
+
+		if (status == Active || (strcmp(argv[1], "-include_logically_deleted_records") == 0 && status == LogicalyDeleted))
+		{
+			// If the record is physically on the master file
+			// then get the vector of field tags.
+			
+			// 1) Repetitive fields have the same tag and are stored in
+			//    a single field but are separated by the "%" char
+			// 2) Subfields are separated by "^"letter (^a ^b ^c ...)
+			//
+			// 3) Terms or Phrases between "<>" are terms/Phrases to 
+			//    index
+			std::vector<int> vf = mfr.GetFieldTags();
+			std::cout << "----------" << std::endl;
+			  //				      << "mfn=" << i  
+				// 	  << " nvf=" << vf.size() << std::endl;
+
+			for (int j=0; j<vf.size(); j++)
+			{
+				std::string stemp;
+				// Special case when tag is <=0
+				if (vf[j]<=0 || vf[j]>SHRT_MAX)
+					continue;
+				// Extract the field with tag contained into vf[j]
+				bool rc = mfr.GetField(vf[j], stemp);
+				if (rc)
+				{
+					std::cout << "tag=" << vf[j]
+					          << " data=" << stemp << std::endl;
+				}
+			}
+
+		}
+		else
+			continue;
+		// PausePrg();
+
+	}
+	return 0;
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisTypes.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisTypes.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisTypes.h	(revision 16561)
@@ -0,0 +1,98 @@
+/**********************************************************************
+ *
+ * IsisTypes.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+///////////////////////////////////////////////////////////////////////////////
+//  isisType.h 
+
+
+#ifndef __ISIS_TYPE_H__
+#define __ISIS_TYPE_H__
+
+#include "DLLCode.h"
+
+#ifdef __USE_ISOC99 // get double_t type from math.h
+#include <math.h>
+#endif
+namespace isis
+{
+
+
+// ----------------------------------------
+// Type definitions common to all platforms
+// ----------------------------------------
+typedef char            int8_t;    // 8-bit signed value
+typedef unsigned char   uint8_t;   // 8-bit unsigned value
+typedef int             int32_t;   // Word size (4 bytes)
+typedef unsigned int    uint32_t;  // Unsigned word size (4 bytes)
+typedef long            long32_t;  // Long word (4 bytes)
+typedef unsigned long   ulong32_t; // Unsigned long word (4 bytes)
+typedef short           int16_t;   // short word (2 bytes)
+typedef unsigned short  uint16_t;  // Unsigned short word (2 bytes)
+#ifndef __USE_ISOC99 // gnu lic math.h has a double_t
+typedef double          double_t;  // Double precision floating point (8 bytes)
+#endif
+
+
+// --------------------------------------------------------------
+// Platform/compiler specific INT64 types
+// --------------------------------------------------------------
+#if defined (__64_BIT_FILE_SYSTEM__)
+#include "TYPES64.H" 
+#endif
+} // namespace isis
+
+namespace isis
+{
+    typedef  isis::int16_t  db_t;
+    typedef  isis::int16_t  server_t;
+    typedef  isis::int16_t  IsisClientId;
+    typedef  isis::uint32_t mfn_t;
+}
+// typedef char            int8_t;    // 8-bit signed value
+typedef unsigned char   uint8_t;   // 8-bit unsigned value
+typedef int             int32_t;   // Word size (4 bytes)
+typedef unsigned int    uint32_t;  // Unsigned word size (4 bytes)
+typedef long            long32_t;  // Long word (4 bytes)
+typedef unsigned long   ulong32_t; // Unsigned long word (4 bytes)
+typedef short           int16_t;   // short word (2 bytes)
+typedef unsigned short  uint16_t;  // Unsigned short word (2 bytes)
+#ifndef __USE_ISOC99 // gnu lic math.h has a double_t
+typedef double     double_t;  // Double precision floating point (8 bytes) // ****
+#endif
+
+typedef uint32_t        xrf_page_no_t;
+
+typedef uint32_t        mst_page_no_t;
+typedef uint16_t        mst_offset_t;
+typedef uint32_t        mfn_t;
+typedef uint32_t        mfrl_t;
+
+typedef isis::uint32_t file_size_t;
+
+
+#endif // __ISIS_TYPE_H__
+
+
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.cpp	(revision 16561)
@@ -0,0 +1,183 @@
+/**********************************************************************
+ *
+ * IsisUtil.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "stdafx.h"
+#include "IsisUtil.h" 
+#include <algorithm>
+
+
+//---------------------------------------------------------------------------------
+// bool RepFieldSep(TCHAR c)
+//
+// This function is a predicate that takes a TCHAR and returns a value that indicates
+// if that TCHAR is a repeteable field separator.
+//----------------------------------------------------------------------------------
+bool RepFieldSep(char c)
+{
+	return (c == SYSRSEP);
+}
+
+//--------------------------------------------------------------------------------
+// vector<string> SplitOccurences(const cstring& s)
+//
+// This function takes as input a string which contains a field and return a 
+// vector<string> which will contain an entry for each field occurence.
+//--------------------------------------------------------------------------------
+std::vector<cstring> SplitOccurences(const cstring& s)
+{
+	typedef cstring::const_iterator iter;
+	std::vector<cstring> ret;
+
+	iter i = s.begin();
+	//TRACE("\ni=%s",i);
+	while (i != s.end())
+	{
+		// find end of occurence
+		iter j = std::find_if(i,s.end(),RepFieldSep);
+
+		// copy the characters in [i, j]
+		ret.push_back(cstring(i, j));
+		//TRACE("\ni=%s j=%s string(i,j)=%s", i, j, string(i,j).c_str());
+
+		i = j;
+		if (j != s.end())
+		    i++;
+	}
+	return ret;
+}
+
+
+bool WSubFieldSep(WCHAR c)
+{
+	return (c == SUBFIELD_SEP);
+}
+
+//--------------------------------------------------------------------------------
+// vector<cstring> SplitSubfields(const cstring& s, TCHAR sc)
+//
+// This function takes as input a string which contains a field and return a 
+// vector<string> which will contain an entry for each subfield occurrence.
+//--------------------------------------------------------------------------------
+std::vector<cstring> SplitSubfields(const cstring& s, TCHAR sc/*=0*/)
+{
+	typedef cstring::const_iterator iter;
+	std::vector<cstring> ret;
+
+	iter i= s.begin();
+	iter j;
+	while (i < s.end())
+	{
+		// Look for subfield separator (^)
+		iter k =  std::find_if(i ,s.end(),WSubFieldSep);
+		if (k == s.end())
+		{
+			j = s.end(); // separator not found
+		}
+		else
+		{
+			// Special case when field is terminated by "^x" or "^"
+			if (k==s.end()-1 || k==s.end()-2)
+				break;
+			i = k + 2;  // separator found, skip next char
+			assert(i<s.end());
+			j = std::find_if(i ,s.end() ,WSubFieldSep);
+		}
+		ret.push_back(cstring(i, j));
+		i = j;
+	}
+	return ret;
+}
+
+
+//--------------------------------------------------------------------------------
+// vector<string> SplitOccurences(const awstring& s)
+//
+// This function takes as input a string which contains a field and return a 
+// vector<string> which will contain an entry for each field occurence.
+//--------------------------------------------------------------------------------
+//  std::vector<awstring> SplitOccurences(const awstring& s)
+//  {
+//  	typedef awstring::const_iterator iter;
+//  	std::vector<awstring> ret;
+
+//  	iter i = s.begin();
+//  	//TRACE("\ni=%s",i);
+//  	while (i != s.end())
+//  	{
+//  		// find end of occurence
+//  		iter j = std::find_if(i,s.end(),RepFieldSep);
+
+//  		// copy the characters in [i, j]
+//  		ret.push_back(awstring(i, j));
+//  		//TRACE("\ni=%s j=%s string(i,j)=%s", i, j, string(i,j).c_str());
+
+//  		i = j;
+//  		if (j != s.end())
+//  		    i++;
+//  	}
+//  	return ret;
+//  }
+
+
+bool SubFieldSep(TCHAR c)
+{
+	return (c == SUBFIELD_SEP);
+}
+
+//--------------------------------------------------------------------------------
+// vector<awstring> SplitSubfields(const awstring& s, TCHAR sc)
+//
+// This function takes as input a string which contains a field and return a 
+// vector<string> which will contain an entry for each subfield occurrence.
+//--------------------------------------------------------------------------------
+//  std::vector<awstring> SplitSubfields(const awstring& s, TCHAR sc/*=0*/)
+//  {
+//  	typedef awstring::const_iterator iter;
+//  	std::vector<awstring> ret;
+
+//  	iter i = s.begin();
+//  	//TRACE("\ni=%s",i);
+//  	bool found=false;
+//  	while (i != s.end())
+//  	{
+//  		// find index of subfield separator (^)
+//  		iter j = std::find_if(i,s.end(),SubFieldSep);
+//  		if (found && j<s.end()-1)
+//  		{
+//  		    // copy the characters in [i, j]
+//  			ret.push_back(awstring(i+1, j));
+//  		}
+//  		if (sc==0 || s.at(j-s.begin()+1)==sc)
+//  		    found = true;
+//  		else
+//  		    found = false;
+
+
+//  		i = j+1;
+//  		if (j != s.end())
+//  		    i++;
+//  	}
+//  	return ret;
+//  }
Index: /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/IsisUtil.h	(revision 16561)
@@ -0,0 +1,35 @@
+/**********************************************************************
+ *
+ * IsisUtil.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef __ISIS_UTIL__
+#define __ISIS_UTIL__
+#include <vector>
+#include <string>
+std::vector<cstring> SplitOccurences(const cstring &s);
+// std::vector<awstring> SplitOccurences(const awstring &s);
+
+std::vector<cstring> SplitSubfields(const cstring &s, TCHAR sc=0);
+// std::vector<awstring> SplitSubfields(const awstring &s, TCHAR sc=0);
+#endif // __ISIS_UTIL_
Index: /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.cpp	(revision 16561)
@@ -0,0 +1,479 @@
+/**********************************************************************
+ *
+ * MFFile.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// MfFile.cpp : Master file object methods using blocked I/O
+//
+////////////////////////////////////////////////////////////
+#include "stdafx.h"
+#include "IsisDb.h"
+#include "Checking.h"
+
+
+
+//---------------------------------------------------------------------------------- 
+// MfFile::MfFile() :
+//
+// Constructor, makes a Masterfile object that isn't connected to a file
+//---------------------------------------------------------------------------------- 
+MfFile::MfFile() : BlkFile(), hdr_created_(false), nextaddr_(MFCTLLEN)
+{
+    lastWrittenBlock_ = -1;
+}
+ 
+//----------------------------------------------------------------------------------
+// int MfFile::OpenMf(const TCHAR *fname, 
+//                    FileSystem::AccessMode mode /* = FileSystem::FILE_READWRITE */)
+//
+// Opens a Masterfile object
+//---------------------------------------------------------------------------------- 
+int MfFile::OpenMf(const TCHAR *fname, FileSystem::AccessMode mode
+				                              /* = FileSystem::FILE_READWRITE */)
+{
+  TCHAR mstFileName[_MAX_PATH];
+  _tcscpy(mstFileName, fname);
+
+  if (!CFileBase::Exists(mstFileName)) {
+    mstFileName[strlen(mstFileName) - 3] = 'M';
+    mstFileName[strlen(mstFileName) - 2] = 'S';
+    mstFileName[strlen(mstFileName) - 1] = 'T';
+  }
+
+	int rc;
+	try
+	{
+	    rc = CFileBase::Open(mstFileName, mode);
+		hdr_created_ = true;
+	    ReadMfHdr();
+		PrintMfHdr();
+		Checking::CheckMfHeader(mstFileName, mfh_);
+	}
+	catch (CFileBaseException e)
+	{
+        throw CIsisDbException(ISIS_MF_OPEN_ERROR, __FILE__, __LINE__);
+	}
+    return rc;
+}
+ 
+//----------------------------------------------------------------------------------
+// int MfFile::createMf(const TCHAR *fname, int  type)
+//
+// Creates a Masterfile object
+//---------------------------------------------------------------------------------- 
+int MfFile::CreateMf(const TCHAR *fname, int  type)
+{
+    return 0;
+}
+ 
+//////////////////////////////
+MfFile::~MfFile()
+{
+}
+ 
+//----------------------------------------------------------------------------------
+// void  MfFile::ReadMfRecord(long mfb, int mfp, MfRecord &m)
+//
+// Reads the master file record beginning at address (mfb,mfp) into m
+//----------------------------------------------------------------------------------
+bool  MfFile::ReadMfRecord(long mfb, int mfp, MfRecord &m)
+{
+	return m.Read(*this, mfb, mfp);
+}
+
+//-------------------------------------------------------------------------------
+//  void WriteMfRecord(long mfb, int mfp, MfRecord &m)
+//
+//  Helper protected method, which writes the Master file record m at address 
+//  (mfb, mfp)   mfb (1:MAXMFB),   mfp (0: MFBLKSIZE)
+//-------------------------------------------------------------------------------
+void  MfFile::WriteMfRecord(long mfb, int mfp, MfRecord &m)
+{
+	m.Write(*this, mfb, mfp);
+} 
+
+
+//--------------------------------------------------------------------------------
+// void CreateMfRecord(MfRecord &m, long &mfb, int &mfp)
+//
+//  Create a new record. The MFN to be assigned is obtained from the field nxtmfn_
+//  in the Master file header. Then addMfRecord is called for adding the record
+//  at the end of the master file, at the position indicated by the fields
+//  nxtmfb_/nxtmfb_. The next MFN to be assigned is updated.
+//
+//--------------------------------------------------------------------------------
+void  MfFile::CreateMfRecord(MfRecord &m, long &mfb, int &mfp)
+{
+}
+
+void  MfFile::AppendNewBlock()
+{
+}
+
+//--------------------------------------------------------------------------------
+// void AddMfRecord(MfRecord &m, long &mfb, int &mfp)
+//
+// Add the record at the end of the master file, at the position indicated by
+// the fields nxtmfb_/nxtmfp_. (nxtmfp_ is in range [1:MFBLKSIZE])
+// Return the address where the record was stored into mfb & mfp
+//--------------------------------------------------------------------------------
+void  MfFile::AddMfRecord(MfRecord &m, long &mfb, int &mfp)
+{
+}
+ 
+ 
+//--------------------------------------------------------------------------------
+// void UpdateMfRecord(MfRecord &m, long &mfb, int &mfp)
+//
+// Update a Master file record.If possible, i.e. if the record length was not
+// increased, the record is written back at its original location otherwise it is
+// written at the end of the file. In both cases, MFBWB/MFBWP are not changed.
+// 
+//--------------------------------------------------------------------------------
+void  MfFile::UpdateMfRecord(MfRecord &m, long &mfb, int &mfp)
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+// methods for dumping file and record headers
+
+ 
+void MfFile::PrintMfHdr(SMfHeader &h)
+// Prints header record
+{
+}
+ 
+//////////////////////////////
+ 
+void MfFile::PrintMfRecord(MfRecord &m)
+// Print a master file record structure
+{
+	m.Dump();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+// Methods for accessing records directly
+ 
+//----------------------------------------------------------------------------------
+// void MfFile::ReadMfRecord(long daddr, MfRecord &m)
+//
+// Reads a master file record into the master record object m.
+// Non blocked version
+//----------------------------------------------------------------------------------
+void MfFile::ReadMfRecord(long daddr, MfRecord &m)
+{
+	m.Clear();
+    ReadLeader(daddr, m);       // Read record leader (Leader is added to record)
+	m.ExtractSubLeader();
+    
+	//TRACE("\nReadMfRecord -- mfn=%ld mfrl=%d", m.GetMfn(), m.GetMfrl());
+	//assert(m.CheckIntegrity());
+	int mfrl = m.GetMfrl();
+	if (mfrl < 0) mfrl = -mfrl; 	// mfrl can be negative!!!!
+	char* p = new char[mfrl];
+    CFileBase::Fetch(p, mfrl, daddr);
+	m.AddToRecord(p+GetLeaderSize(), mfrl-GetLeaderSize());
+	delete[] p;
+    nextaddr_ += mfrl;
+ 	int mfb = nextaddr_ / MFBLKSIZE + 1;
+    int mfp = nextaddr_ % MFBLKSIZE;
+    if (mfp > MFBLKSIZE - GetMinLeaderSize())
+	{
+		mfb++;
+	    mfp = 0;
+	}
+	nextaddr_ = (mfb-1)*MFBLKSIZE + mfp;
+	m.DecomposeRecord();
+}
+
+//---------------------------------------------------------------------------------
+// void  MfFile::WriteMfRecord(long daddr, MfRecord &m)
+//
+// Stores Master record object m to disk address daddr
+// Non blocked version
+//---------------------------------------------------------------------------------
+void  MfFile::WriteMfRecord(long daddr, MfRecord &m)
+{
+}
+/////////////////////////////////////////////////////////////////////////////////////
+// Master file locking methods
+
+
+ 
+//-----------------------------------------------------------------------------------
+// void MfFile::InitFileLockHdr(SMfLockHeader &hdr)
+//
+// Initialize a the lock header for a newly constructed file lock header.
+//-----------------------------------------------------------------------------------
+void MfFile::InitFileLockHdr(SMfLockHeader &hdr)
+{
+	hdr.lockProtect_ = (uint32_t)0;
+	hdr.readLock_    = (uint32_t)0;
+	hdr.writeLock_   = (uint32_t)0;
+}
+
+//-----------------------------------------------------------------------------------
+// FileError MfFile::WriteFileLockHdr(const SMfLockHeader &hdr)
+//
+// Write the lock header to the file. 
+// Returns a non-zero value to indicate an error condition or zero if successful.
+//-----------------------------------------------------------------------------------
+FileError MfFile::WriteFileLockHdr(const SMfLockHeader &hdr)
+{
+	
+	return CFileBase::Store(&hdr, sizeof(SMfLockHeader), sizeof(SMfHeader));
+}
+
+//-----------------------------------------------------------------------------------
+// FileError MfFile::ReadFileLockHdr(SMfLockHeader &hdr)
+//
+// Read the lock header from the file.
+// Returns a non-zero value to indicate an error condition or zero if successful.
+//-----------------------------------------------------------------------------------
+FileError MfFile::ReadFileLockHdr(SMfLockHeader &hdr)
+{
+	
+	return CFileBase::Fetch(&hdr, sizeof(SMfLockHeader), sizeof(SMfHeader));
+}
+
+//-----------------------------------------------------------------------------------
+// int MfFile::LockFile(CFileMgrLockType l_type) 
+//
+// Lock the file and set the lock access mode to shared or exclusive.
+// Returns a non-zero value if the file cannot be locked or the lock variable cannot
+// be changed because it is exclusive or another thread is currently updating it. 
+//-----------------------------------------------------------------------------------
+int MfFile::LockFile(MfLockType l_type) 
+{
+	// Read the file lock to ensure that this file has a lock header
+	// and to keep the header in sync during multiple file access.
+	SMfLockHeader lock_header;
+	if (ReadFileLockHdr(lock_header) != FILE_NO_ERROR)
+		return fileError_;
+	
+	// Cannot modifiy this lock until the thread holding it releases the
+	// lock header.
+	if (lock_header.lockProtect_ != 0)
+	{
+		fileError_ = ISIS_MFLOCK_ACCESS_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(fileError_);
+#else
+		return fileError_;
+#endif
+	}
+	
+	switch (l_type)
+	{
+		case MF_WRITE_LOCK :
+			// Cannot obtain an exclusive lock until all the threads in the
+			// read lock queue have finished
+			if ((lock_header.readLock_ != 0) || 
+				(lock_header.writeLock_ != 0))
+			{
+				fileError_ = ISIS_MFLOCK_ERROR;
+	#ifdef __CPP_EXCEPTIONS__
+				throw CFileBaseException(fileError_);
+	#else
+				return fileError_;
+	#endif
+			}
+			else 
+			{
+				lock_header.writeLock_ = 1;
+			}
+			
+			lock_header.lockProtect_ = 1;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			
+			// Skip over the read lock member
+			SeekTo(sizeof(SMfHeader) + (sizeof(uint32_t) * 2));
+			if (CFileBase::Store(&lock_header.writeLock_, sizeof(uint32_t)) !=
+				FILE_NO_ERROR)
+				return fileError_;
+			
+			lock_header.lockProtect_ = (uint32_t)0;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			break;
+			
+		case MF_READ_LOCK :
+			// This lock is exclusively owned and cannot be read locked
+			// until the thread holding this lock release the exclusive lock.
+			if (lock_header.writeLock_ == 1)
+			{
+				fileError_ = ISIS_MFLOCK_ERROR;
+	#ifdef __CPP_EXCEPTIONS__
+				throw CFileBaseException(fileError_);
+	#else
+				return fileError_;
+	#endif
+			}
+			
+			if (lock_header.readLock_ < 0xFFFFFFFF)
+			{
+				lock_header.readLock_++;
+			}
+			else 
+			{
+				fileError_ = ISIS_MFLOCK_ERROR;
+	#ifdef __CPP_EXCEPTIONS__
+				throw CFileBaseException(fileError_);
+	#else
+				return fileError_;
+	#endif
+			}
+			
+			lock_header.lockProtect_ = 1;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			
+			if (CFileBase::Store(&lock_header.readLock_, sizeof(uint32_t)) !=
+				FILE_NO_ERROR)
+				return fileError_; 
+			
+			lock_header.lockProtect_ = (uint32_t)0;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			break;
+			
+		default: // Invalid lock type specified
+			fileError_ =  ISIS_INVALID_LOCK_TYPE;
+	#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(fileError_);
+	#else
+			return fileError_;
+	#endif
+	}
+	
+	return fileError_ = fileError_ = FILE_NO_ERROR;
+}
+
+//-----------------------------------------------------------------------------------
+// int MfFile::UnlockFile(CFileMgrLockType l_type)
+//
+// Unlock the file. Returns a non-zero value if the file cannot be unlocked or the
+// lock variable cannot be changed because it is exclusive or another thread is
+// currently updating it. 
+//-----------------------------------------------------------------------------------
+int MfFile::UnlockFile(MfLockType l_type)
+{
+	
+	// Read the file lock to ensure that this file has a lock header
+	// and to keep the header in sync during multiple file access.
+	SMfLockHeader lock_header;
+	if (ReadFileLockHdr(lock_header) != FILE_NO_ERROR)
+		return fileError_;
+	
+	// Cannot modifiy this lock until the thread holding it releases the
+	// lock header.
+	if (lock_header.lockProtect_ != 0)
+	{
+		fileError_ = ISIS_MFLOCK_ACCESS_ERROR;
+#ifdef __CPP_EXCEPTIONS__
+		throw CFileBaseException(fileError_);
+#else
+		return fileError_;
+#endif
+	}
+	
+	switch (l_type)
+	{
+		case MF_WRITE_LOCK :
+			if (lock_header.writeLock_ == 0)
+				return FILE_NO_ERROR;
+			else
+				lock_header.writeLock_ = 0;
+			
+			lock_header.lockProtect_ = 1;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			
+			// Skip over the read lock member
+			SeekTo(sizeof(SMfHeader) + (sizeof(uint32_t) * 2));
+			if (CFileBase::Store(&lock_header.writeLock_, sizeof(uint32_t)) !=
+				FILE_NO_ERROR)
+				return fileError_;
+			
+			lock_header.lockProtect_ = (uint32_t)0;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			break;
+			
+		case MF_READ_LOCK :
+			if (lock_header.readLock_ == 0)      // This record is not locked
+				return fileError_ = FILE_NO_ERROR;
+			else if (lock_header.readLock_  > 0) // Prevent read lock rollover
+				lock_header.readLock_--;
+			else
+				lock_header.readLock_ = 0;
+			
+			lock_header.lockProtect_ = 1;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			
+			if (CFileBase::Store(&lock_header.readLock_, sizeof(uint32_t)) !=
+				FILE_NO_ERROR)
+				return fileError_; 
+			
+			lock_header.lockProtect_ = (uint32_t)0;
+			if (CFileBase::Store(&lock_header.lockProtect_, sizeof(uint32_t),
+				sizeof(SMfHeader)) != FILE_NO_ERROR)
+				return fileError_;
+			break;
+			
+		default: // Invalid lock type specified
+			fileError_ =  FMGR_INVALID_LOCK_TYPE;
+	#ifdef __CPP_EXCEPTIONS__
+			throw CFileBaseException(fileError_);
+	#else
+			return fileError_;
+	#endif
+	}
+	
+	return fileError_ = fileError_ = FILE_NO_ERROR;
+}
+
+//-----------------------------------------------------------------------------------
+// FileError MfFile::ResetFileLock()
+//
+// Reset the file's lock header. This function will clear all the file lock fields
+// without testing the lock or the lock protect. Returns a non-zero value to indicate
+// an error condition or zero if successful.
+//-----------------------------------------------------------------------------------
+FileError MfFile::ResetFileLock()
+{
+	SMfLockHeader lock_header;
+	InitFileLockHdr(lock_header);
+	return WriteFileLockHdr(lock_header);
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/MFFile.h	(revision 16561)
@@ -0,0 +1,338 @@
+/**********************************************************************
+ *
+ * MFFile.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// MFFILE.H: Class definition for blocked I/O on the master
+//           file
+////////////////////////////////////////////////////////////
+ 
+#ifndef _MFFILE_H_
+#define _MFFILE_H_
+#ifndef _MASTER_H_
+#include "Master.h"
+#endif
+#ifndef _BLKFILE_H_
+#include "BlkFile.h"
+#endif
+#include <string>
+
+enum MfLockType
+{
+	MF_READ_LOCK,
+	MF_WRITE_LOCK
+};
+
+struct SMfLockHeader
+{
+	long lockProtect_; // Serialize access to the file lock members
+	long readLock_;    // Shared (or read_only) file lock
+	long writeLock_;   // Exclusive (or write-only) file lock
+
+	SMfLockHeader() : lockProtect_(0), readLock_(0), writeLock_(0)
+	{}
+};
+
+enum  { USERMST=0, MSGMST=1 };
+//  binary  hex
+//  ------  ---
+//  0000    0
+//  0001    1
+//  0010    2
+//  0011    3
+
+class MfAddress
+{
+private:
+	long  m_mfb;
+	short m_mfp;
+	char  m_status;
+public:
+	MfAddress() { m_status = 0x00; }
+	MfAddress(long mfb, short mfp)
+	{ ASSERT(mfb>0); ASSERT(mfp>=0); m_mfb = mfb; m_mfp = mfp; m_status = 0x03; }
+
+	long getMfb()          { ASSERT(m_status & 0x01); return m_mfb; }
+	short getMfp()         { ASSERT(m_status & 0x02); return m_mfp; } 
+
+	void setMfb(long mfb)  { ASSERT(mfb>0);  m_mfb = mfb; m_status |= 0x01; }
+	void setMfp(short mfp) { ASSERT(mfp>=0); m_mfp = mfp; m_status |= 0x02; }
+	bool isValid()         { return (m_status == 0x03); }
+};
+ 
+/////////////////////////////////////////////////////////////////////////
+// Master File header record aggregate structure
+//
+struct  SMfHeader 
+{
+   ISIS_LONG   ctlmfn_;    // Always 0
+   ISIS_LONG   nxtmfn_;    // MFN to be assigned to next record created
+   ISIS_LONG   nxtmfb_;    // Last block number allocated (1st is 1)
+   ISIS_INT    nxtmfp_;    // Offset to next available position in last block (1:MFBLKSIZE)
+   ISIS_INT    mftype_;    // Type of master file (USERMST or MSGMST)
+   ISIS_LONG   reccnt_;
+   ISIS_LONG   mfcxx1_;
+   ISIS_LONG   mfcxx2_;
+   ISIS_LONG   mfcxx3_;
+public:
+   SMfHeader(const char *dbname="");
+   ~SMfHeader()                 { }
+};
+ 
+//--------------------------------------------------------------------------
+// SMfHeader::SMfHeader(const char *dbname /* ="" */)
+//
+// Default constructor, initializes the Master file header to default values
+//--------------------------------------------------------------------------
+inline SMfHeader::SMfHeader(const char *dbname /* ="" */)
+{
+   ctlmfn_ = 0;
+   nxtmfn_ = 1L;
+   nxtmfb_ = 1L;
+   nxtmfp_ = MFCTLLEN+1;
+   mftype_ = USERMST;
+   reccnt_ = mfcxx1_=mfcxx2_=mfcxx3_=0;
+}
+ 
+//////////////////////
+// Master file class
+//
+class MfFile : public BlkFile  
+{
+   friend class IsisDb;
+   friend class MfRecord;
+
+protected:
+   SMfHeader mfh_;            // Header structure
+   long nextaddr_;            // Address of next record
+   char buf_[MFBLKSIZE];      // Buffer to hold 1 Master File block
+   bool hdr_created_;         // True if 1st block exists
+   int  fileError_;
+   long lastWrittenBlock_;
+
+protected:  
+   void WriteMfRecord(long mfb, int mfp, MfRecord &r);
+   void AppendNewBlock();
+
+public:
+   void setNextMfn(long mfn)       // Set MFN to be assigned to next record created
+   { mfh_.nxtmfn_ = mfn; }
+
+protected:
+   void setNextMfb(long nxtmfb_)  // Set last block number allocated (1st is 1)
+   { mfh_.nxtmfb_ = nxtmfb_; }
+   void setNextMfp(int nxtmfp_)   // Set offset to next available position in last block
+   { mfh_.nxtmfp_ = nxtmfp_; }
+   int setm_mftype(int mftype_)     // Set type of master file (USERMST or MSGMST)
+   { mfh_.mftype_ = mftype_; }     
+   virtual void ReadBlk(void* d, long b)  { BlkFile::ReadBlk(d, b);  }
+   virtual void WriteBlk(void* d, long b) { BlkFile::WriteBlk(d, b); }
+   void ReadMfHdr(SMfHeader &h);
+   void ReadMfHdr();
+   void WriteMfHdr(SMfHeader &h);
+   void WriteMfHdr();
+   void SetLastWrittenBlock(long b) { lastWrittenBlock_ = b; }
+public:
+   MfFile();
+   virtual ~MfFile();
+
+   int  OpenMf(const TCHAR* name, FileSystem::AccessMode mode = FileSystem::FILE_READWRITE);
+   int  CreateMf(const TCHAR* name, int MSTtype = USERMST);
+   void CloseMf() { if (ReadyForWriting()) WriteMfHdr(); Close(); }
+
+   // Accessors for the Master File Header
+   long GetNextMfn()        // Get MFN to be assigned to next record created
+   { return mfh_.nxtmfn_; }
+   long GetNextMfb()        // Get last block number allocated (1st is 1)
+   { return mfh_.nxtmfb_; }
+   int GetNextMfp()         // Get offset to next available position in last block
+   { return mfh_.nxtmfp_; }
+   int GetMftype()          // Get type of master file (USERMST or MSGMST)
+   { return mfh_.mftype_; }   
+   SMfHeader& GetSMfHeader() { return mfh_; }
+
+
+   void CreateMfRecord(MfRecord &m, long &mfb, int &mfp);
+   void AddMfRecord(MfRecord &m, long &mfb, int &mfp);
+   void UpdateMfRecord(MfRecord &m, long &mfb, int &mfp);
+   bool ReadMfRecord(long mfb, int mfp, MfRecord &r);
+
+   void ReadMfRecord(long addr, MfRecord &r);
+   void WriteMfRecord(long addr, MfRecord &r);
+   void RewindMf();
+   bool ReadMfNext(MfRecord &r);
+   void WriteMfNext(MfRecord &r);
+   void ReadLeader(long daddr, MfRecord& m)
+   {
+	   m.ReadLeader(*this, daddr);
+   }
+
+
+   void LockMfHdr();
+   void UnlockMfHdr();
+   void LockMfr(long mfn);
+   void UnlockMfr(long mfn);
+
+public: // File lock functions
+	void InitFileLockHdr(SMfLockHeader &hdr);
+	FileError ResetFileLock();
+	FileError WriteFileLockHdr(const SMfLockHeader &hdr);
+	FileError ReadFileLockHdr(SMfLockHeader &hdr);
+	int LockFile(MfLockType l_type = MF_WRITE_LOCK);
+	int UnlockFile(MfLockType l_type = MF_WRITE_LOCK);
+
+
+   void PrintMfRecord(MfRecord &r);
+   void PrintMfHdr(SMfHeader &h);
+   void PrintMfHdr();
+};
+ 
+
+class CMfIterator
+{
+	const MfFile& m_mf;
+	long cur;
+public:
+	CMfIterator(const MfFile& mf) : m_mf(mf) { }
+
+	bool More() const;
+	void Advance();
+
+	MfRecord Current() 
+	{ 
+		MfRecord mfr(NULL);
+		
+		return mfr;
+	}
+};
+
+ 
+//////////////////////////////
+ 
+inline void  MfFile::LockMfHdr()
+//  Locks the master file header record
+{
+	double_t dwPos   = 0;
+	double_t dwCount = sizeof(SMfHeader);
+	//CFile::LockRange(dwPos, dwCount);
+}
+ 
+//////////////////////////////
+ 
+inline void MfFile::UnlockMfHdr()
+//  Unlocks the master file header record
+{
+	double_t dwPos   = 0;
+	double_t dwCount = sizeof(SMfHeader);
+	//CFile::UnlockRange(dwPos, dwCount);
+}
+ 
+//////////////////////////////
+
+inline void MfFile::ReadMfHdr()
+//  Reads the master file header record
+{
+   ReadBlk(buf_, 1L);
+   TRACE(_T("\nsizeof(SMfHeader)=%d"), sizeof(SMfHeader));
+   ::MoveMemory(&mfh_, buf_, sizeof(SMfHeader));
+
+   fix_endianness(mfh_.ctlmfn_);
+   fix_endianness(mfh_.nxtmfn_);
+   fix_endianness(mfh_.nxtmfb_);
+   fix_endianness(mfh_.nxtmfp_);
+   fix_endianness(mfh_.mftype_);
+   fix_endianness(mfh_.reccnt_);
+   fix_endianness(mfh_.mfcxx1_);
+   fix_endianness(mfh_.mfcxx2_);
+   fix_endianness(mfh_.mfcxx3_);
+}
+ 
+//////////////////////////////
+ 
+inline void MfFile::WriteMfHdr()
+//  Updates the master file header record
+{
+    if (hdr_created_) 
+	   ReadBlk(buf_, 1L);
+    TRACE(_T("\nsizeof(SMfHeader)=%d"), sizeof(SMfHeader));
+    ::MoveMemory(buf_, &mfh_, sizeof(SMfHeader));
+    WriteBlk(buf_, 1L);
+}
+ 
+ 
+//////////////////////////////
+ 
+inline void MfFile::ReadMfHdr(SMfHeader &h)
+//  Reads the master file header record
+{
+   ReadBlk(buf_, 1L);
+   ::MoveMemory(&h, buf_, sizeof(SMfHeader));
+
+   fix_endianness(h.ctlmfn_);
+   fix_endianness(h.nxtmfn_);
+   fix_endianness(h.nxtmfb_);
+   fix_endianness(h.nxtmfp_);
+   fix_endianness(h.mftype_);
+   fix_endianness(h.reccnt_);
+   fix_endianness(h.mfcxx1_);
+   fix_endianness(h.mfcxx2_);
+   fix_endianness(h.mfcxx3_);
+}
+ 
+//////////////////////////////
+ 
+inline void MfFile::WriteMfHdr(SMfHeader &h)
+//  Updates the master file header record
+{
+   if (hdr_created_)
+	   ReadBlk(buf_, 1L);
+   ::MoveMemory(buf_, &h, sizeof(SMfHeader));
+   WriteBlk(buf_, 1L);
+}
+ 
+ 
+inline void MfFile::PrintMfHdr()
+{
+   PrintMfHdr(mfh_);
+}
+ 
+//////////////////////////////
+ 
+inline bool MfFile::ReadMfNext(MfRecord &r)
+// Reads the next physically record
+{
+	if (nextaddr_ >= CFileBase::FileSize(file_name))
+	    return false;
+   ReadMfRecord(nextaddr_, r);
+   return true;
+}
+ 
+//////////////////////////////
+ 
+inline void MfFile::WriteMfNext(MfRecord &r)
+// Writes physically at next position
+{
+   WriteMfRecord(nextaddr_, r);
+}
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Makefile.in
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Makefile.in	(revision 16561)
@@ -0,0 +1,33 @@
+prefix	    = @prefix@
+exec_prefix = @exec_prefix@
+bindir	    = @bindir@
+
+CXX	    = @CXX@
+CXXFLAGS    = @CXXFLAGS@
+EXEEXT	    = @EXEEXT@
+
+INSTALL     = @INSTALL@
+
+LDFLAGS     = @LDFLAGS@
+
+ISIS_OBJS   = IsisGdl.o CacheMan.o Checking.o CRC32.o Fdt.o File.o \
+              FileSystem.o IErrors.o IsisDb.o IsisUtil.o Master.o MFFile.o \
+              stdafx.o Unimarc.o XRFFile.o
+
+
+all: IsisGdl$(EXEEXT)
+
+IsisGdl$(EXEEXT): $(ISIS_OBJS)
+	$(CXX) -o IsisGdl$(EXEEXT) $(ISIS_OBJS) $(LDFLAGS)
+
+$(ISIS_OBJS) :%.o :%.cpp
+	$(CXX) -c $(CXXFLAGS) -o $@ $<
+
+install: all
+	$(INSTALL) IsisGdl$(EXEEXT) $(bindir)
+
+clean:
+	rm -f IsisGdl$(EXEEXT) $(ISIS_OBJS)
+
+distclean: clean
+	rm -f Makefile config.status config.cache config.log
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Master.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Master.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Master.cpp	(revision 16561)
@@ -0,0 +1,1084 @@
+/**********************************************************************
+ *
+ * Master.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+//  MASTER.CPP: Master file record object methods
+//                                                                        
+////////////////////////////////////////////////////////////
+#include "stdafx.h"
+#include <iomanip>
+#include "XRFFile.h"
+#include "IsisUtil.h" 
+#include "Master.h"
+#include "MFFile.h"
+#include "IsisDb.h"
+#include "Unimarc.h"
+#include <algorithm>
+
+
+using namespace std;
+
+   
+
+char MfRecord::m_buf[MFBLKSIZE];      // Buffer to hold 1 File block
+
+
+
+/////////////////////////////////////////////////////////////////////
+// LeaderAndDirectory methods
+
+int LeaderAndDirectory::GetFieldSize()
+{
+	int field_len = 0;
+	for (int i=0; i!=nvf_; ++i)
+		field_len  += dir_[i].len_;
+	return field_len;
+}
+
+bool LeaderAndDirectory::operator==(const LeaderAndDirectory& rhs) const
+{
+    if (mfn_ != rhs.mfn_)         // Master file number
+		return false;
+    if (mfrl_ != rhs.mfrl_)       // Record length
+		return false;
+	if (mfbwb_ != rhs.mfbwb_)     // Backward pointer block number
+		return false;
+    if (mfbwp_ != rhs.mfbwp_)     // Backward pointer offset
+		return false;
+    if (base_ != rhs.base_)       // Offset to begining of variable fields
+		return false;
+    if (nvf_ != rhs.nvf_)         // Number of directory entries
+		return false;
+    if (status_ != rhs.status_)   // 1 = DELETED
+		return false;
+	for(std::vector<DirEntry>::size_type i=0; i!=nvf_; i++) // Directory
+	{
+		if (dir_[i]!=rhs.dir_[i])
+		    return false;
+	}
+    return true;
+}
+
+bool LeaderAndDirectory::operator!=(const LeaderAndDirectory& rhs) const
+{
+    if (mfn_ != rhs.mfn_)         // Master file number
+		return true;
+    if (mfrl_ != rhs.mfrl_)       // Record length
+		return true;
+	if (mfbwb_ != rhs.mfbwb_)     // Backward pointer block number
+		return true;
+    if (mfbwp_ != rhs.mfbwp_)     // Backward pointer offset
+		return true;
+    if (base_ != rhs.base_)       // Offset to begining of variable fields
+		return true;
+    if (nvf_ != rhs.nvf_)         // Number of directory entries
+		return true;
+    if (status_ != rhs.status_)   // 1 = DELETED
+		return true;
+	for(std::vector<DirEntry>::size_type i=0; i!=nvf_; i++) // Directory
+	{
+		if (dir_[i]!=rhs.dir_[i])
+		    return true;
+	}
+    return false;
+}
+//-------------------------------------------------------------------------------
+// ostream& operator<<(ostream& s, const LeaderAndDirectory& leader_directory)
+//
+// Overloaded instance of the output operator for LeaderAndDirectory objects
+//---------------------------------------------------------------------------------
+cstringstream& operator<<(cstringstream& s, const LeaderAndDirectory& leader_directory)
+{
+	s.write((char *) &leader_directory.mfn_,    sizeof(ISIS_LONG));
+	s.write((char *) &leader_directory.mfrl_,   sizeof(ISIS_INT));
+	s.write((char *) &leader_directory.mfbwb_,  sizeof(ISIS_LONG));
+	s.write((char *) &leader_directory.mfbwp_,  sizeof(ISIS_INT));
+#ifdef UNIX_BIREME
+	ISIS_INT dummy = 0;
+	s.write((char *) &dummy,   sizeof(ISIS_INT));
+#endif
+
+	s.write((char *) &leader_directory.base_,   sizeof(ISIS_INT));
+	s.write((char *) &leader_directory.nvf_,    sizeof(ISIS_INT));
+	s.write((char *) &leader_directory.status_, sizeof(ISIS_INT));
+
+	int n = leader_directory.dir_.size();
+    assert(n==leader_directory.nvf_);	
+	for(vector<DirEntry>::size_type i=0; i!=leader_directory.dir_.size(); i++)
+	{
+	     assert(leader_directory.dir_[i].tag_>0 && leader_directory.dir_[i].tag_<SHRT_MAX);
+		 DirEntry de = leader_directory.dir_[i];
+         s.write((char *) &de, GetDirEntrySize());
+	}
+	
+	return s;
+}
+
+//----------------------------------------------------------------------------------
+// istream& operator>>(istream& s, LeaderAndDirectory& leader_directory)
+//
+// Overloaded instance of the input operator for LeaderAndDirectory objects
+//----------------------------------------------------------------------------------
+cstringstream& operator>>(cstringstream& s, LeaderAndDirectory& leader_directory)
+{
+	if (!s.good()) return s;
+
+	ios::iostate err = ios::goodbit;
+	try
+	{
+		s.read((char *) &leader_directory.mfn_,    sizeof(ISIS_LONG));
+	    s.read((char *) &leader_directory.mfrl_,   sizeof(ISIS_INT));
+	    s.read((char *) &leader_directory.mfbwb_,  sizeof(ISIS_LONG));
+	    s.read((char *) &leader_directory.mfbwp_,  sizeof(ISIS_INT));
+#ifdef UNIX_BIREME
+	    ISIS_INT dummy;
+	    s.read((char *) &dummy,   sizeof(ISIS_INT));
+#endif
+	    s.read((char *) &leader_directory.base_,   sizeof(ISIS_INT));
+	    s.read((char *) &leader_directory.nvf_,    sizeof(ISIS_INT));
+	    s.read((char *) &leader_directory.status_, sizeof(ISIS_INT));
+
+	    fix_endianness(leader_directory.mfn_);
+	    fix_endianness(leader_directory.mfrl_);
+	    fix_endianness(leader_directory.mfbwb_);
+	    fix_endianness(leader_directory.mfbwp_);
+	    fix_endianness(leader_directory.base_);
+	    fix_endianness(leader_directory.nvf_);
+	    fix_endianness(leader_directory.status_);
+
+		if (!leader_directory.dir_.empty())
+			leader_directory.dir_.clear();
+
+		// Try to recover if nvf_ = 0
+		if (leader_directory.nvf_==0)
+			leader_directory.nvf_ = (leader_directory.base_ - GetLeaderSize()) / GetDirEntrySize();
+
+		if (leader_directory.nvf_>0)
+		{
+			for(vector<DirEntry>::size_type i=0; i!=leader_directory.nvf_; i++)
+			{
+				DirEntry de;
+		        s.read((char *) &de, GetDirEntrySize());
+			    fix_endianness(de.tag_);
+			    fix_endianness(de.pos_);
+			    fix_endianness(de.len_);
+			    // Don't check integrity of directory entries here
+			    leader_directory.dir_.push_back(de);
+			}
+
+		}
+	}
+	catch(bad_alloc&)
+	{
+		err |= ios::badbit;
+		ios::iostate exception_mask = s.exceptions();
+		if (  (exception_mask & ios::failbit) &&
+			!(exception_mask & ios::badbit))
+		{
+			s.setstate(err);
+		}
+		else if (exception_mask & ios::badbit)
+		{
+		  s.setstate(err);
+		  // try { s.setstate(err); }
+		  // catch( ios::failure& ) { }
+			throw;
+		
+		}
+	}
+    catch(...)
+	{
+		err |= ios::failbit;
+		ios::iostate exception_mask = s.exceptions();
+		if ( (exception_mask & ios::badbit) &&
+			(err & ios::badbit))
+		{
+			s.setstate(err);
+		}
+		else if (exception_mask & ios::failbit)
+		{
+		  s.setstate(err);
+		  // try { s.setstate(err); }
+		  // catch( ios::failure& ) { }
+			throw;
+
+		}
+	}
+
+	if (err) s.setstate(err);
+	return s;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+
+//  void MfRecord::Copy(UMfRecord& rhs)           
+//  {
+//  	Clear();
+//  	pFdt_      = rhs.pFdt_;
+//      state_     = Record_Created;
+	
+//  	// 1 - Prepare the LeaderAndDirectory, directory, end data part
+//  	int n   = 0;
+//  	int pos = 0;
+
+//  	cstring all_fields_mb;
+//      leader_directory_.dir_.clear();	
+//  	all_fields_mb.erase();
+//  	for (vector<int>::size_type i=0; i!=rhs.tags_.size(); ++i)
+//  	{
+//  		bool bRep = false;
+//  		if (rhs.pFdt_)
+//  		{
+//  			if (rhs.pFdt_->GetEntryAt(i).rep)
+//  			   bRep = true;
+//  		}
+//  		// Get the field, all occurences combined
+//  		int tag = rhs.tags_[i];
+//  	    awstring ws;
+//  		bool rc = rhs.GetField(tag, ws);
+//  		vector<awstring> occ;
+//  		if (bRep)
+//  			vector<awstring> occ = SplitOccurences(ws);
+//  		else
+//  			occ.push_back(ws);
+//          // one entry per occurence
+//  		int nocc = occ.size();
+//  		for (vector<awstring>::size_type j=0; j!= nocc; ++j)
+//  		{
+//  			DirEntry de;
+//  			de.tag_ = tag;
+//  			de.pos_ = pos;
+//  			cstring cs; // For storing the unicode string
+//  			de.len_ = UnicodeToCodePage(occ[j], cs);
+//              leader_directory_.dir_.push_back(de);
+//  			all_fields_mb += cs;
+//  			n++;
+//  			pos += occ[j].length();
+			
+//  		}
+//  	}
+
+//  	leader_directory_.nvf_  = n;
+//  	leader_directory_.base_ = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();
+//  	leader_directory_.mfrl_ = ComputeMfrl();
+
+//  	// 2   - Make record
+
+//  	record_.erase(); // Empty the record
+
+//  	// Prepare the binary part, note that this part must be  independent
+//  	// of the character set.
+
+//  	cstringstream ost(std::ios::out | std::ios::binary);   
+//  	ost << leader_directory_;   // Output the LeaderAndDirectory to a string
+
+	
+//  	AddToRecord(ost.str().c_str(), ost.str().length());
+
+//  	// Here comes the part which is dependent from the character set,
+//  	// the data itself.
+//  	AddToRecord((char*)all_fields_mb.c_str(), all_fields_mb.length());
+
+//  	DecomposeRecord();
+//  }
+
+//  MfRecord::MfRecord(const UMfRecord& rhs)     // Copy constructor
+//  { 
+//     Copy(rhs);
+//  }
+
+//  MfRecord& MfRecord::operator=(const UMfRecord& rhs) // Assignment
+//  {
+//     if (&leader_directory_ != &rhs.leader_directory_)
+//     {
+//     	   Copy(rhs);
+//     }
+//     return *this;
+//  }
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Accessors
+ 
+ 
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Methods working on the in memory image of the record
+ 
+//-------------------------------------------------------------------------------
+// bool  MfRecord::GetField(int tag, string& s)
+//
+// This function takes as input a tag and will place the field into the string
+// s. It will return true if the field exists and false otherwise.
+//-------------------------------------------------------------------------------
+bool  MfRecord::GetField(int tag, ustring& s)
+{
+	PRECONDITION(tag>=0 && tag < SHRT_MAX);
+
+	map<int, ustring>::iterator it = fields_.find(tag);
+
+	bool ret = false;
+	if (it != fields_.end())
+	{
+		s   = it->second;
+		ret = true;
+	}
+	return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+// Modifiers, They synchronize the record structure with the internal structures
+
+
+//---------------------------------------------------------------------------------
+// AddField --- Add a field to the master record structure.
+//            
+//--------------------------------------------------------------------------------- 
+bool MfRecord::AddField(int tag, ustring& s)
+{
+	PRECONDITION(tag>=0 && tag < SHRT_MAX);
+
+	map<int, ustring>::iterator it = fields_.find(tag);
+
+	bool ret = false;
+	if (it == fields_.end())
+	{
+		fields_.insert(make_pair(tag, s));
+		tags_.push_back(tag);
+		sort(tags_.begin(), tags_.end());
+		ret = true;
+		ComposeRecord();
+	}
+	return ret;
+}
+//---------------------------------------------------------------------------------
+// AddOccurence --- Add an occurence to an existing field.
+//--------------------------------------------------------------------------------- 
+bool MfRecord::AddOccurence(int tag, ustring& o)
+{
+	PRECONDITION(tag>=0 && tag < SHRT_MAX);
+
+	map<int, ustring>::iterator it = fields_.find(tag);
+
+	bool ret = false;
+	if (it != fields_.end())
+	{
+	    it->second += SYSRSEP;
+		it->second += o;
+		ret = true;
+		ComposeRecord();
+	}
+	return ret;
+}
+
+//-----------------------------------------------------------------------------
+// bool  MfRecord::DeleteField(int tag)
+//
+//-----------------------------------------------------------------------------
+bool  MfRecord::DeleteField(int tag)
+{
+	PRECONDITION(tag>=0 && tag < SHRT_MAX);
+
+	map<int, ustring>::iterator it = fields_.find(tag);
+
+	bool ret = false;
+	if (it != fields_.end())
+	{
+		fields_.erase(it);
+		ret = true;
+		vector<int>::iterator j = find(tags_.begin(), tags_.end(), tag);
+		if (j != tags_.end())
+			tags_.erase(j);
+		ComposeRecord();
+	}
+	return ret;
+}
+
+//-------------------------------------------------------------------------------
+// bool ReplaceField(int tag, string& s)
+//
+//-------------------------------------------------------------------------------
+bool MfRecord::ReplaceField(int tag, ustring& s)
+{
+    
+	PRECONDITION(tag>=0 && tag < SHRT_MAX);
+
+	map<int, ustring>::iterator it = fields_.find(tag);
+
+	bool ret = false;
+	if (it != fields_.end())
+	{
+		it->second  = s;
+		ret         = true;
+	}
+	else
+		AddField(tag, s);
+	ComposeRecord();
+
+	return ret;
+}
+
+ustring MfRecord::GetAllFields()
+{
+	cstring all_fields; // As a string of ANSI characters
+	all_fields.erase();
+	all_fields = record_.substr(GetBase(), GetFieldSize());
+	return ustring(all_fields);
+}
+
+void MfRecord::ConvertToAnsi()
+{
+	/*
+	int mfrl = GetMfrl();
+	int size1 = record_.length();
+	if (GetFieldSize()==0)
+		return;
+	cstring s = record_.substr(GetBase(), GetFieldSize());
+	CString cs(s.c_str());
+	cs.OemToAnsi();
+	TRACE("\nbase=%d GetFieldSize=%d",GetBase(),GetFieldSize());
+	record_.replace(GetBase(),GetBase()+GetFieldSize(),
+		                                 cs.GetBuffer(GetFieldSize()));
+	int size2 = record_.length();
+	cs.ReleaseBuffer();
+	int padding = size1-size2;
+	for (int i=0; i<padding; i++)
+		record_ += " ";
+*/
+}
+
+long MfRecord::GetDirectorySize()
+{
+	return (GetNvf()*GetDirEntrySize());
+}
+
+long MfRecord::ComputeMfrl()
+{
+	return (GetLeaderSize() + GetDirectorySize() + GetFieldSize());
+}
+
+//--------------------------------------------------------------------------------------
+// void MfRecord::ComposeRecord()
+//
+// Build the record into continuous bytes for output
+//--------------------------------------------------------------------------------------
+void MfRecord::ComposeRecord()
+{
+	PRECONDITION(tags_.size() == fields_.size());
+	
+	// 1 - Prepare the LeaderAndDirectory, directory, end data part
+	int n   = 0;
+	int pos = 0;
+
+	ustring all_fields;
+    leader_directory_.dir_.clear();	
+	all_fields.erase();
+	for (vector<int>::size_type i=0; i!=tags_.size(); ++i)
+	{
+		int tag = tags_[i];
+		bool bRep = false;
+		if (pFdt_)
+		{
+			if (pFdt_->GetEntryAt(i).rep)
+			   bRep = true;
+		}
+		vector<ustring> occ;
+		if (bRep)
+			vector<ustring> occ = SplitOccurences(fields_[tags_[i]]);
+		else
+			occ.push_back(fields_[tags_[i]]);
+        // one entry per occurence
+		int nocc = occ.size();
+		for (vector<ustring>::size_type j=0; j!= nocc; ++j)
+		{
+			DirEntry de;
+			de.tag_ = tag;
+			de.pos_ = pos;
+			de.len_ = occ[j].length();
+            leader_directory_.dir_.push_back(de);
+			all_fields += occ[j];
+			n++;
+			pos += occ[j].length();
+			
+		}
+	}
+
+	leader_directory_.nvf_  = n;
+	leader_directory_.base_ = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();
+	leader_directory_.mfrl_ = ComputeMfrl();
+
+	// 2   - Make record
+
+	record_.erase(); // Empty the record
+
+	// Prepare the binary part, note that this part must be  independent
+	// of the character set.
+
+	cstringstream ost(std::ios::out | std::ios::binary);   
+	ost << leader_directory_;   // Output the LeaderAndDirectory to a string
+
+	
+	AddToRecord(ost.str().c_str(), ost.str().length());
+
+	// Here comes the part which is dependent from the character set,
+	// the data itself.
+	cstring all_fields_ansi(all_fields); 
+	AddToRecord(all_fields_ansi.c_str(), all_fields_ansi.length());
+	
+}
+
+//-------------------------------
+// Helper functions for decompose
+//-------------------------------
+
+bool MfRecord::CheckLeaderAndDirectory()
+{
+	if (GetMfrl() < 0)                  // Negative mfrl
+	{
+		//VLOG("DecomposeRecord -  Warning! Negative mfrl - mfn=%d", GetMfn());
+		SetMfrl(-GetMfrl());
+	}
+
+	if (GetMfrl()<=GetBase())
+	{
+		//VLOG("DecomposeRecord - Error! mfn=%d GetMfrl()=%d > GetBase()=%d", 
+		//	  GetMfn(),  GetMfrl(), GetBase());
+		return false;
+	}
+
+	
+
+	if (GetLeaderSize()>GetBase())
+	{
+		//VLOG("DecomposeRecord - Error! mfn=%d GetLeaderSize()=%d > GetBase()=%d", 
+		//	  GetMfn(),  GetLeaderSize(), GetBase());
+		return false;
+	}
+	if (GetNvf()==0) 
+	{
+	    // Null nvf      
+		//VLOG("DecomposeRecord -  Warning! mfn=%d nvf=0", GetMfn());
+		//SetNvf((GetBase() - GetLeaderSize()) / GetDirEntrySize());
+		return false;
+	}
+
+	if (GetMfn()<=0 || GetMfrl()<=0 || GetBase()<0 || GetNvf()<0 || GetStatus()< 0) 
+	{
+		//VLOG("DecomposeRecord - Error! mfn=%d GetMfrl()=%d GetLeaderSize()=%d GetBase()=%d GetNvf()=%d GetStatus()=%d", 
+		//      GetMfn(), GetMfrl(), GetLeaderSize(), GetBase(), GetNvf(), GetStatus());
+		return false;
+	}
+
+
+	int base = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();
+
+	if (record_.length() <= base)
+	{
+        //VLOG("DecomposeRecord - Error! mfn=%d record_.length()=%d < base=%d",
+		//	 GetMfn(), record_.length(), base);
+		return false;
+	}
+
+	int field_len = 0;
+	for (int i=0; i!=GetNvf(); ++i)
+	{
+		if (GetFieldTag(i)<0 || GetFieldTag(i)>SHRT_MAX ||
+			GetFieldPos(i)<0 || GetFieldPos(i)>SHRT_MAX ||
+			GetFieldLen(i)<0 || GetFieldLen(i)>SHRT_MAX)
+		{
+            //VLOG("DecomposeRecord - Error! mfn=%d Bad dir entry i=%d tag=%d pos=%d len=%d",
+			//    	GetMfn(), GetFieldTag(i), GetFieldPos(i), GetFieldLen(i));
+			return false;
+		}
+		field_len  += GetFieldLen(i);
+	}
+	
+    for (int j=0; j!=GetNvf(); ++j)
+	{
+		if (GetFieldPos(j)>=field_len || GetFieldPos(j)+GetFieldLen(j)>field_len)
+		{
+            //VLOG("DecomposeRecord - Error! mfn=%d Bad dir entry j=%d tag=%d pos=%d len=%d",
+			//    	GetMfn(), GetFieldTag(j), GetFieldPos(j), GetFieldLen(j));
+			return false;
+		}
+	}
+	// Check for bad values
+    if (GetBase() != base)	
+		SetBase(base);
+	
+	if (record_.length() < base + field_len)
+	{
+        //VLOG("DecomposeRecord - Error! mfn=%d record_.length()=%d < base=%d + field_len=%d",
+        //GetMfn(), record_.length(), base, field_len);  
+		return false;
+	}
+	return true;
+}
+
+void MfRecord::MakeTagToFieldMap()
+{
+	cstring all_fields_ansi;
+	all_fields_ansi.erase();
+	all_fields_ansi = record_.substr(leader_directory_.base_, GetFieldSize());
+	PRECONDITION(fields_.empty() && tags_.empty());
+
+
+	// Build the map from tags to fields
+	for (int i=0; i!=GetNvf(); ++i)
+	{
+		//int    all_len = all_fields.length();
+		//if (pos>all_len || pos+len>all_len)
+		//	return false;
+		cstring field_ansi = 
+			   all_fields_ansi.substr(GetFieldPos(i), GetFieldLen(i));
+		ustring field(field_ansi);
+	    map<int, ustring>::iterator it = fields_.find(GetFieldTag(i));
+	    if (it != fields_.end())
+		{
+			// Repeatable field, several occ
+		    it->second += SYSRSEP;
+			it->second += field;
+		}
+		else
+		{
+		    fields_.insert(make_pair(GetFieldTag(i), field));
+			
+		}
+		
+	}
+}
+void MfRecord::MakeTagVector()
+{
+	// Build a vector of tags
+	map<int, ustring>::const_iterator it;
+    for ( it = fields_.begin(); it != fields_.end(); ++it )
+		tags_.push_back(it->first);
+
+	// Sort in ascending order
+	sort(tags_.begin(), tags_.end());
+}
+
+
+//--------------------------------------------------------------------------------------
+// bool MfRecord::DecomposeRecord()
+//
+// Make a memory version of the record to facilitate manipulations.
+//--------------------------------------------------------------------------------------
+bool MfRecord::DecomposeRecord()
+{
+	PRECONDITION(!record_.empty());
+
+	if (record_.length() <= GetLeaderSize())
+	{
+		//VLOG("DecomposeRecord - Error! record_.length()=%d < GetLeaderSize()=%d",record_.length(), GetLeaderSize());
+		return false;
+	}
+
+	// Input the LeaderAndDirectory from a string
+    cstringstream ist(record_,cstringstream::in|cstringstream::binary);
+	if (!(ist >> leader_directory_))
+		return false;
+
+	if (!CheckLeaderAndDirectory())
+	{
+		//VLOG("Record rejected mfn=%ld", GetMfn());
+		return false;
+	}
+
+	//VLOG("Record accepted mfn=%ld", GetMfn());
+
+    MakeTagToFieldMap();
+    MakeTagVector();
+
+	return true;
+}
+
+
+
+void MfRecord::ExtractSubLeader()
+{
+	//PRECONDITION(!record_.empty() && record_.length()>=sizeof(leader_directory_.mfn_)+sizeof(leader_directory_.mfrl_));
+
+	leader_directory_.mfn_  = *(reinterpret_cast<ISIS_LONG*>((char *) record_.c_str()));
+	leader_directory_.mfrl_ = *(reinterpret_cast<ISIS_INT*> ((char *)(record_.c_str()+sizeof(ISIS_LONG))));
+	fix_endianness(leader_directory_.mfn_);
+	fix_endianness(leader_directory_.mfrl_);
+	if (leader_directory_.mfrl_ < 0) leader_directory_.mfrl_ = -leader_directory_.mfrl_; 	// mfrl can be negative!!!!
+
+	//POSTCONDITION(leader_directory_.mfn_>0 && leader_directory_.mfrl_>=0);
+}
+
+bool MfRecord::CheckLeader()
+{
+	int leader_size = GetLeaderSize();
+	if (leader_directory_.mfn_<=0)
+		return false;
+	if (abs(leader_directory_.mfrl_)<leader_size)
+		return false;
+	return true;
+}
+
+
+bool MfRecord::CheckIntegrity()
+{
+	int leader_size = GetLeaderSize();
+	if (leader_directory_.mfn_<=0)
+		return false;
+	if (abs(leader_directory_.mfrl_)<leader_size)
+		return false;
+	if (leader_directory_.nvf_<0)
+		return false;
+	//if (leader_directory_.base_ != leader_directory_size + leader_directory_.nvf_*sizeof(MAXDIRL))
+	//	return false;
+	if (leader_directory_.status_ == 1)
+		return true;
+	else if (leader_directory_.status_ == 0)
+		return true;
+	else
+		return false;
+
+	return true;
+}
+ 
+
+void  MfRecord::Dump()
+{
+
+	ustringstream os;
+    os.str("");
+
+
+    os <<  "\n================= entry in MfRecord::Dump==============";
+    os <<  "\n   MFN      " << GetMfn();
+    os <<  "\n   MFRL     " << GetMfrl();
+    os <<  "\n   MFBWB    " << GetMfbwb();
+    os <<  "\n   MFBWP    " << GetMfbwp();
+    os <<  "\n   BASE     " << GetBase();
+    os <<  "\n   NVF      " << GetNvf();
+    os <<  "\n   STATUS   " << GetStatus();
+ 	//TRACE(os.str().c_str());
+
+
+	for (int i=0; i<GetNvf(); i++)
+	{
+		  ustring stemp;
+		  bool rc = GetField(GetDirEntry(i).tag_, stemp);
+		  int len;
+		  if (rc)
+		  {
+		       len = 50;
+		       if (stemp.length()<len)
+			      len = stemp.length();
+		  }
+		  ustring fld = (rc==false) ? _T("Empty") : stemp.substr(0,len);
+	      os.str("");
+		  os << _T(" tag=") << ISIS_INT(GetDirEntry(i).tag_)
+             << _T(" pos=") << ISIS_INT(GetDirEntry(i).pos_)
+			 << _T(" len=") << ISIS_INT(GetDirEntry(i).len_)
+			 << _T(" ")     << fld.c_str() << std::endl; 
+          TRACE(_T("\n%s"),os.str().c_str());   
+	  }
+
+      TRACE("\n---------------- exit in MfRecord::Dump-------------");
+}
+
+
+
+
+bool MfRecord::CheckRecord()
+{
+	PRECONDITION(!record_.empty());
+
+	if (record_.length() < GetLeaderSize())
+	{
+		TRACE(_T("\nWrong LeaderAndDirectory in record!"));
+		return false;
+	}
+
+    cstringstream ist(record_,cstringstream::in|cstringstream::binary);
+
+	LeaderAndDirectory leader_directory;
+	ist >> leader_directory;
+
+	if (leader_directory.mfrl_ < 0)                  // Negative mfrl
+	{
+		//VLOG("CheckRecord -  Warning! Negative mfrl - mfn=%d", GetMfn());
+	}
+	
+	if (leader_directory.nvf_<=0) 
+	{
+		//VLOG("CheckRecord -  Warning! mfn=%d nvf=0", leader_directory.mfn_);
+	}
+
+	if (leader_directory.mfn_<=0 || leader_directory.mfrl_<=0 ||
+		leader_directory.base_<0 || leader_directory.nvf_ < 0 ||
+		leader_directory.status_ < 0) 
+	{
+		//VLOG("CheckRecord - Corrupted record! leader_directory.mfn_=%d leader_directory.mfrl_=%d leader_directory.base_=%d leader_directory.nvf_=%d leader_directory.status_=%d",
+        //      leader_directory.mfn_, leader_directory.mfrl_,
+		//	  leader_directory.base_, leader_directory.nvf_,
+		//	  leader_directory.status_);
+		return false;
+	}
+
+
+	int base = GetLeaderSize() + GetDirectorySize();
+
+	if (record_.length() < base)
+	{
+		//VLOG("CheckRecord - Corrupted record! mfn=%d record_.length()=%d base=%d",
+		//	  leader_directory.mfn_, record_.length(), base);
+		return false;
+	}
+
+	int field_len = 0;
+	for (int i=0; i!=leader_directory.nvf_; ++i)
+	{
+		DirEntry de = leader_directory.dir_[i];
+//  		if (de.tag_<0 || de.tag_>SHRT_MAX || de.pos_<0 || de.pos_>SHRT_MAX || de.len_<0 || de.len_>SHRT_MAX)
+//  		{
+//  		    //VLOG("CheckRecord - Corrupted record! mfn=%d tag=%d pos=%d len=%d",
+//  			//	  leader_directory.mfn_, de.tag_, de.pos_, de.len_);
+//  			return false;
+//  		}
+		if (i>0)
+		{
+			if(de.pos_ != leader_directory.dir_[i-1].pos_+leader_directory.dir_[i-1].len_)
+			{
+		    //VLOG("CheckRecord - Position not consistent - mfn=%d",
+			//	  leader_directory.mfn_);
+			return false;
+			}
+		}
+		field_len  += de.len_;
+	}
+	
+
+	// Check for bad values
+    if (leader_directory.base_ != base)	
+		leader_directory.base_ = base;
+	
+	if (record_.length() < base + field_len)
+	{
+		    //VLOG("CheckRecord - Corrupted record, record length < base+field_len  - mfn=%ld",
+			//	  leader_directory.mfn_);
+		return false;
+	}
+
+	return true;
+}
+
+void MfRecord::ReadLeader(MfFile& f, long daddr)
+{
+    int leader_size = GetLeaderSize();
+    char* buffer = new char[leader_size];
+    f.Fetch(buffer, leader_size, daddr);  // Read record leader
+    Clear();
+    AddToRecord(buffer, leader_size);
+    delete[] buffer;
+}
+
+bool  MfRecord::Read(MfFile& f, long mfb, int mfp)
+{
+    //TRACE("\nMfFile::ReadMfRecord -- mfb=%ld mfp=%d", mfb, mfp);
+	PRECONDITION(mfb>0 && mfb<=MAXMFB);
+    long  blk;
+    int   mfrr, offs, l;
+
+	try
+	{
+		Clear();
+	    // Save original mfb, mfp pair into m
+	    SetMfbRead(mfb);
+	    SetMfpRead(mfp);
+	    SetState(MfRecord::Record_Read);
+
+	    // Be sure that mfp is in range [0:MFBLKSIZE-1]
+	    mfp = mfp%MFBLKSIZE;
+		assert(mfp>=0 && mfp<MFBLKSIZE);
+        //TRACE("\nMfFile::ReadMfRecord - mfb=%ld mfp=%d", mfb, mfp);
+
+		// 1 - Read block where record starts 
+        f.ReadBlk(m_buf,mfb);                    
+
+        blk = mfb;
+		// 2 - Extract the record leader (Mfn+Mfrl) from the block buffer, the 
+		//     record leader cannot be split on two  consecutive blocks.
+        int len = GetSubLeaderSize(); // Mfn + Mfrl
+		ASSERT(mfp+len<MFBLKSIZE);
+
+        AddToRecord(m_buf+mfp, len); // Move 1st 6 bytes  (Mfn+Mfrl)
+		ExtractSubLeader();
+
+		// 3 - Continue to transfer data for building the MfRecord object
+        int mflen = GetMfrl();                // Get record length
+	    SetMflenRead(mflen);                  // Save it 
+        //TRACE("\nMfFile::readMfRecord - After reading leader mfn=%ld mflen=%d",GetMfn(), mflen);
+        mfrr = mflen - len;                     // Not yet moved
+        l = (mfrr > (f.GetBlockSize()-(mfp+len)))      // What remains to move
+            ? (f.GetBlockSize()-(mfp+len)) : mfrr;         // in this block
+        offs = len;
+        AddToRecord(m_buf+mfp+len, l); // Move-it
+        mfrr -= l;
+        while (mfrr>0)                        // Record continues on next block ?
+		{                
+            f.ReadBlk(m_buf,++blk);             // Yes read next block
+            offs += l;
+            l = (mfrr > f.GetBlockSize()) ? f.GetBlockSize() : mfrr;
+            AddToRecord(m_buf, l); // Move data
+            mfrr -= l;
+		}
+	    //TRACE("\nMfFile::ReadMfRecord -- mfb=%ld mfp=%d nvf=%d", mfb, mfp, GetNvf());
+	}
+	catch (CFileBaseException e)
+	{
+        throw CIsisDbException(ISIS_MF_READ_ERROR, __FILE__, __LINE__);
+	}
+
+	return DecomposeRecord();
+}
+
+
+void  MfRecord::Write(MfFile& f, long mfb, int mfp)
+{
+	TRACE(_T("\nMfFile::writeMfRecord - mfb=%ld mfp=%d"), mfb, mfp);
+	PRECONDITION(mfb>0 && mfb <= MAXMFB);
+	// Be sure that mfp is in range [0:MFBLKSIZE-1]
+	mfp = mfp%MFBLKSIZE;
+	
+    long hiblk = f.GetNextMfb(); // Highest block number in master file
+	
+    int s  = 0;              // Index in master file record [0:mfrl-1]
+    int d  = mfp;            // Index in block buffer [0:bsize-1]
+    long b = mfb;            // Starting block number
+	
+    // Adjust record length
+	int padding = GetMfrl() % MFALIGN;
+	if (padding>0)
+	{
+		SetMfrl(GetMfrl() + padding);
+		for (int i=0; i<padding;i++)
+			AddToRecord(" ", 1);
+	}
+	
+    int mfrl = GetMfrl();
+    TRACE(_T("\nMfFile::writeMfRecord - b=%ld d=%d mfrl=%ld hiblk=%ld"), b, d, mfrl, hiblk);
+    TRACE(_T("\nBefore writing->"));
+	do 
+    {
+		if (b <= hiblk)        
+			f.ReadBlk(m_buf, b);
+		else
+			::memset(m_buf, ' ', MFBLKSIZE);
+		
+		while (d < MFBLKSIZE && s < mfrl)
+			m_buf[d++] = record_.at(s++);
+		if (d >= MFBLKSIZE) 
+		{
+			TRACE(_T("MfFile::WriteMfRecord - Before WriteBlk b=%ld"), b);
+			f.SetLastWrittenBlock(b);
+			f.WriteBlk(m_buf, b++);
+			// Reset m_buf pos
+			d = 0;
+		}
+	} while (s < mfrl);
+	
+    // Write last block
+	if (d == 0)
+		::memset(m_buf, ' ', MFBLKSIZE); // Should be an empty block
+	
+	f.WriteBlk(m_buf, b);       
+	f.SetLastWrittenBlock(b);
+} 
+
+void IntToChar(int value, int width, std::string& s)
+{
+	std::ostringstream ost;
+
+#ifdef _WIN32
+	ost.setf(std::ios_base::right, std::ios_base::adjustfield);
+#endif
+	ost << std::setw(width) 
+		<< std::setfill('0') 
+		<< value;
+	s = ost.str();
+}
+//--------------------------------------------------------------------------------------
+// void MfRecord::MakeISORecord(std::string& s)
+//
+// Build the record into continuous bytes for output
+//--------------------------------------------------------------------------------------
+void MfRecord::MakeISORecord(std::string& s)
+{
+	std::string    tmp;
+
+	MarcLeader l;
+
+	// 1 - Complete the leader (base and record length), output the directory 
+	//     to a string and prepare a string with the data fields
+	int base = MARC_LEADER_LEN + GetNvf()*MARC_DIRENT_LEN + 1;
+	IntToChar(base, 5, tmp);
+	strncpy(l.base_ ,tmp.c_str(),5);
+
+	DirectoryEntry mde;
+	ustring    all_fields;
+	all_fields.erase();
+	ostringstream  osd;     // Output the directory to a string
+	int pos = 0; 
+	for (int i=0; i!=GetNvf(); ++i)
+	{
+		IntToChar(GetFieldTag(i), 3, tmp); 
+		strncpy(mde.tag_,tmp.c_str(),3);
+		int len = GetFieldLen(i)+1;  // Include space for separator
+		IntToChar(len, 4, tmp);
+		strncpy(mde.len_, tmp.c_str(), 4);
+		IntToChar(pos, 5, tmp);
+		strncpy(mde.scp_, tmp.c_str(), 5);
+		osd << mde; 
+		pos += len;
+
+		cstring field = record_.substr(GetBase()+GetFieldPos(i), GetFieldLen(i));
+		all_fields += ustring(field);
+		all_fields += FIELDTERM;
+	}
+	// (+2) ->Field sep at end of directory and record sep
+    int reclen =  MARC_LEADER_LEN + GetNvf()*MARC_DIRENT_LEN + pos +2;
+	IntToChar(reclen, 5, tmp);
+	strncpy(l.lreclen_,tmp.c_str(),5);
+
+
+	ostringstream osl;     // Output the Leader to a string
+	osl << l;
+
+	// 2   - Make record
+	s.erase();       // Empty the record
+    s += osl.str();  // Add the leader
+	s += osd.str();  // Add the directory
+	s += FIELDTERM;  // Separator
+	s += cstring(all_fields); // Data part
+	s += RECTERM;
+		
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Master.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Master.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Master.h	(revision 16561)
@@ -0,0 +1,609 @@
+/**********************************************************************
+ *
+ * Master.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+///////////////////////////////////////                                   */
+//  Master.h: Master File Record object
+///////////////////////////////////////                                   */
+ 
+#ifndef __MASTER_RECORD_H__
+#define __MASTER_RECORD_H__
+
+#include "BlkFile.h"
+#include <vector>
+#include <map>
+#include <sstream>
+
+
+ 
+// Length in bytes of master file header 
+#define MFCTLLEN sizeof(SMfHeader)+sizeof(SMfHeader) // Length in bytes of master file header
+
+const int MFALIGN   =    2;   // Master file record alignment
+const int MFBLKSIZE =  512;   // Master file block size
+ 
+typedef short int ISIS_INT;
+typedef long  int ISIS_LONG;
+
+#ifdef UNIX_BIREME
+    //Length of Leader in master record
+	inline 	int GetLeaderSize()
+	{
+		return (2*sizeof(ISIS_LONG)+6*sizeof(ISIS_INT));
+	}
+    // Mini Leader length guaranteed to be in same block
+	inline 	int GetMinLeaderSize()
+	{ 
+		return (2*sizeof(ISIS_LONG)+4*sizeof(ISIS_INT));
+	}
+#else
+    //Length of Leader in master record
+	inline 	int GetLeaderSize() 
+	{
+		return (2*sizeof(ISIS_LONG)+5*sizeof(ISIS_INT)); 
+	}
+    // Mini Leader length guaranteed to be in same block
+	inline 	int GetMinLeaderSize()
+	{ 
+		return (2*sizeof(ISIS_LONG)+3*sizeof(ISIS_INT));
+	}
+#endif
+
+// Size of Mfn and Mfrl in the leader part
+inline int GetSubLeaderSize() { return sizeof(ISIS_LONG)+sizeof(ISIS_INT); }
+ 
+inline int GetDirEntrySize() { return (3*sizeof(ISIS_INT)); }
+ 
+ 
+const TCHAR SYSRSEP      =  TCHAR('%'); // Repeatable field separator
+const TCHAR SUBFIELD_SEP =  TCHAR('^'); // Subfield separator
+
+////////////////////////////////////////
+// Directory entry aggregate structure
+//
+struct DirEntry  
+{
+   ISIS_INT  tag_;
+   ISIS_INT  pos_;
+   ISIS_INT  len_;
+   DirEntry() 
+	   : tag_(0), pos_(0), len_ (0) {  }
+
+   DirEntry(ISIS_INT tag, ISIS_INT pos, ISIS_INT len)
+	   : tag_(tag), pos_(pos), len_ (len) {  }
+   bool operator==(const DirEntry& rhs) const
+   {
+	   if (tag_!=rhs.tag_ || pos_!=rhs.pos_ || len_!=rhs.len_)
+		   return false;
+	   return true;
+   }
+   bool operator!=(const DirEntry& rhs) const
+   {
+	   if (tag_!=rhs.tag_ || pos_!=rhs.pos_ || len_!=rhs.len_)
+		   return true;
+	   return false;
+   }
+
+};
+ 
+
+//////////////////////////////////////////////////////////////////////////
+// Data Master record aggregate structure
+//
+struct LeaderAndDirectory 
+{
+   ISIS_LONG        mfn_;            // Master file number
+   ISIS_INT         mfrl_;           // Record length
+   ISIS_LONG        mfbwb_;          // Backward pointer block number
+   ISIS_INT         mfbwp_;          // Backward pointer offset
+   ISIS_INT         base_;           // Offset to begining of variable fields
+   ISIS_INT         nvf_;            // Number of directory entries
+   ISIS_INT         status_;         // 1 = DELETED
+   std::vector<DirEntry> dir_;            // Directory
+
+   LeaderAndDirectory()
+   {
+	   Clear();
+   }
+   LeaderAndDirectory(const LeaderAndDirectory& src)
+   {
+	   Copy(src);
+   }
+
+   LeaderAndDirectory& operator=(const LeaderAndDirectory& rhs)
+   {
+	   if (&mfn_ != &rhs.mfn_)
+	   {
+		   Copy(rhs);
+	   }
+	   return *this;
+   }
+
+   void Copy(const LeaderAndDirectory& rhs)
+   {
+	   mfn_    = rhs.mfn_;
+	   mfrl_   = rhs.mfrl_;
+	   mfbwb_  = rhs.mfbwb_;
+	   mfbwp_  = rhs.mfbwp_;
+       base_   = rhs.base_;
+       nvf_    = rhs.nvf_;
+	   status_ = rhs.status_;
+	   dir_    = rhs.dir_;
+   }
+   void Clear()
+   {
+	   mfn_ = mfrl_ = mfbwb_ = mfbwp_ = base_ = nvf_ = status_ = 0;
+	   dir_.clear();
+   }
+
+   bool operator==(const LeaderAndDirectory& rhs) const;
+   bool operator!=(const LeaderAndDirectory& rhs) const;
+   int GetFieldSize();
+};
+
+cstringstream& operator<<(cstringstream& s, const LeaderAndDirectory& leader);
+cstringstream& operator>>(cstringstream& s, LeaderAndDirectory& leader);
+
+////////////////////////////////////////////////////////////
+//
+ 
+/////////////////////////////////
+// Data master file record class
+//
+
+typedef std::map<int, ustring> TAG2FIELD;
+class MfFile;
+class MstFile;
+class IsisDb;
+class ACE_InputCDR;
+class ACE_OutputCDR;
+class UMfRecord;
+
+
+class  MfRecord
+{
+	friend class MfFile;
+	friend class MstFile;
+	friend class IsisDb;
+	friend class UMfRecord;
+    friend int operator>>(ACE_InputCDR& cdr, MfRecord& mfr);
+    friend int operator<<(ACE_OutputCDR& cdr, MfRecord& mfr);
+protected:
+
+	CFdt*                pFdt_;        // Use a copy of the FDT
+	cstring              record_;      // Record in continuous storage
+	                                   // always as ANSI!
+	
+	LeaderAndDirectory           leader_directory_;
+	
+	TAG2FIELD        fields_;
+	std::vector<int> tags_;
+    static char      m_buf[MFBLKSIZE];      // Buffer to hold 1 File block
+
+protected: // Additional data
+	short int state_;         // Record state in memory
+	long      mfb_read_;      // Record was read at Block number [1...]
+	int       mfp_read_;      // Record was read at position in block
+	int       mflen_read_;    // Record length was when record read 
+
+protected:
+   void         Copy(const MfRecord& rhs); 
+   
+   void         AddToRecord(const char* p, int len);
+   int          AddDirEntry(const DirEntry& d);
+   int          AddDirEntry(int tag, int pos, int len);
+   int          DelDirEntry(int i);
+   void         SetMfrl(int mfrl);
+   void         SetMfbwb(long mfbwb);
+   void         SetMfbwp(int mfbwp);
+   void         SetBase(int base);
+   void         SetNvf(int nvf);
+   void         SetStatus(int s);
+   int          SetDirEntry(int i, int tag, int pos, int len);
+   int          SetDirEntry(int i, const DirEntry& d);
+   void         SetMfbRead(long b);
+   void         SetMfpRead(int pos);
+   void         SetMflenRead(int len);
+
+ 
+   void         SetSubLeaderAndDirectory(const char* p, int len);
+
+
+   void         MakeTagToFieldMap();
+   void         MakeTagVector();
+
+   long         ComputeMfrl();
+
+   void         Copy(UMfRecord& rhs);           
+public:
+	enum { Record_Empty, Record_Read, Record_Changed, Record_Created };
+
+public:
+   //-----------------
+   // Public Interface
+   //-----------------
+   MfRecord(CFdt* pFdt=NULL);                // Default constructor   
+   virtual ~MfRecord();                      // Destructor
+
+   MfRecord(const MfRecord& rhs);            // Copy constructor
+   MfRecord& operator=(const MfRecord& rhs); // Assignment
+ 
+   MfRecord(const UMfRecord& umfr);
+   MfRecord& operator=(const UMfRecord& rhs);
+
+   void Clear();
+   void ClearData();
+
+
+   void ReadLeader(MfFile& f, long daddr);
+   bool Read (MfFile& f, long mfb, int mfp);
+   void Write(MfFile& f, long mfb, int mfp);
+
+   void MakeISORecord(std::string& s);
+ 
+   long         GetMfn()           const; // Get MFN
+   int          GetMfrl()          const; // Get Master File Record Length
+   long         GetMfbwb()         const; // Get Master File Backward Block number
+   int          GetMfbwp()         const; // Get Master File Backward block position
+   int          GetBase()          const; // Get Variable fields starting byte
+   int          GetNvf()           const; // Get Number of entries in the directory
+   int          GetStatus()        const; // Get Record Status
+   DirEntry     GetDirEntry(int i) const; // Get a reference to the ieme directory entry
+   int          GetState()         const; // Get record state in memory
+   long         GetMfbRead()       const;
+   int          GetMfpRead()       const;
+   int          GetMflenRead()     const;
+
+   ustring     GetAllFields(); // ANSI or UNICODE
+   cstring     GetRecordAsString() { return record_; }
+   const char* GetRecord()         { return record_.c_str(); }
+   int         GetRecordLength()   { return record_.length(); }
+
+   void        ConvertToAnsi();
+
+
+   bool         GetField(int tag, std::string& s);  // Get field content for field with tag "tag" 
+   bool         AddField(int tag, std::string& s);
+   bool         DeleteField(int tag);
+   bool         ReplaceField(int tag, std::string& s);
+   bool         AddOccurence(int tag, std::string& o);
+
+   int          GetFieldTag(int i);
+   int          GetFieldPos(int i);
+   int          GetFieldLen(int i);
+ 
+   void         SetMfn(long mfn);
+   void         SetState(int s);
+
+   long         GetDirectorySize();
+   int          GetFieldSize()     { return leader_directory_.GetFieldSize(); }
+
+   std::vector<int>  GetFieldTags(); // Get tags of fields in increasing order,
+                                     // Occurences made only 1 field
+   void         ExtractSubLeader();
+   void         ExtractLeader();
+   bool         CheckIntegrity();
+   bool         CheckLeader();
+   bool         CheckLeaderAndDirectory(); 
+
+   bool operator==(const MfRecord& rhs) const
+   {
+	   return (record_ == rhs.record_); 
+   }
+   bool operator!=(const MfRecord& rhs) const
+   {
+	   return (record_ != rhs.record_); 
+   }
+
+   void         SetRecord(const char *s) { record_ = s;  DecomposeRecord();}
+   void         SetRecord(const cstring &s) 
+				{ record_ = s;  DecomposeRecord();}
+   void         ComposeRecord();
+   bool         DecomposeRecord();
+
+   bool         CheckRecord();
+   void         Dump();
+} ;
+ 
+//////////////////////////////
+ 
+
+
+
+inline void MfRecord::AddToRecord(const char* p, int len)
+{
+	record_ += cstring(p,len);
+} 
+
+/////////////////////////////
+
+inline MfRecord::MfRecord(CFdt* pFdt) 
+// Makes a Master file Record object
+{
+	Clear();
+	pFdt_ = pFdt;
+   // Nothing more to do
+}
+ 
+inline void MfRecord::Copy(const MfRecord& rhs)           
+{
+   record_     = rhs.record_;
+   leader_directory_     = rhs.leader_directory_;
+   fields_     = rhs.fields_;
+   state_      = rhs.state_;
+   mfb_read_   = rhs.mfb_read_;
+   mfp_read_   = rhs.mfp_read_;
+   mflen_read_ = rhs.mflen_read_;
+   tags_       = rhs.tags_;         
+	pFdt_      = rhs.pFdt_;
+}
+
+inline MfRecord::MfRecord(const MfRecord& rhs)     // Copy constructor
+{ 
+   Copy(rhs);
+}
+
+inline MfRecord& MfRecord::operator=(const MfRecord& rhs) // Assignment
+{
+   if (&leader_directory_ != &rhs.leader_directory_)
+   {
+   	   Copy(rhs);
+   }
+   return *this;
+}
+ 
+inline void MfRecord::Clear()
+{
+   tags_.clear();
+   record_.erase();
+   leader_directory_.Clear();
+   fields_.clear();
+   SetBase(GetLeaderSize());
+   SetMfrl(GetLeaderSize());
+   state_ = Record_Empty;
+   mfb_read_ = mfp_read_ = mflen_read_ = 0;
+   pFdt_ = NULL;
+}
+
+// Clear the data part but keep other info intact so that
+// it appears as an empty record
+inline void MfRecord::ClearData()
+{
+   tags_.clear();
+   record_.erase();
+   leader_directory_.Clear();
+   fields_.clear();
+   SetBase(GetLeaderSize());
+   SetMfrl(GetLeaderSize());
+}
+
+
+//////////////////////////////
+ 
+inline MfRecord::~MfRecord()
+{
+}
+ 
+//////////////////////////////
+ 
+inline long MfRecord::GetMfn() const
+// Returns Master file number
+{
+   return leader_directory_.mfn_;
+}
+ 
+//////////////////////////////
+ 
+inline int MfRecord::GetMfrl() const
+// Returns Master file record length
+{
+   return leader_directory_.mfrl_;
+}
+ 
+//////////////////////////////
+ 
+inline long MfRecord::GetMfbwb() const
+// Returns backward pointer block number
+{
+   return leader_directory_.mfbwb_;
+}
+ 
+//////////////////////////////
+ 
+inline int MfRecord::GetMfbwp() const
+// Returns backward pointer offset
+{
+   return leader_directory_.mfbwp_;
+}
+ 
+//////////////////////////////
+ 
+inline int MfRecord::GetBase() const
+// Returns base value to field data
+{
+    return leader_directory_.base_;
+}
+ 
+//////////////////////////////
+ 
+inline int MfRecord::GetNvf() const
+// Returns the number of directory entries
+// in the master file record
+{
+   return leader_directory_.nvf_;
+}
+
+//////////////////////////////
+ 
+inline int MfRecord::GetStatus() const
+// Returns the status of the record
+{
+   return leader_directory_.status_;
+}
+
+//////////////////////////////
+ 
+ 
+//////////////////////////////
+ 
+inline DirEntry MfRecord::GetDirEntry(int i) const
+// Returns the ith dir entry
+{
+   return leader_directory_.dir_[i];
+} 
+
+//////////////////////////////
+ 
+inline int MfRecord::GetState() const
+{
+   return state_;
+} 
+
+//////////////////////////////
+ 
+inline long MfRecord::GetMfbRead() const
+{
+   return mfb_read_;
+} 
+//////////////////////////////
+ 
+inline int MfRecord::GetMfpRead() const
+{
+   return mfp_read_;
+} 
+    
+//////////////////////////////
+ 
+// Get tags of fields in increasing order,
+inline std::vector<int> MfRecord::GetFieldTags()
+{
+	PRECONDITION(tags_.size() == fields_.size());
+	return tags_;
+}
+//////////////////////////////
+ 
+inline int MfRecord::GetMflenRead() const
+{
+   return mflen_read_;
+} 
+    
+///////////////////////////////////////////////////////////////////////////////
+ 
+inline void MfRecord::SetMfn(long mfn)
+// Set Master file number
+{
+	assert(mfn>0);
+    leader_directory_.mfn_ = mfn;
+}
+//////////////////////////////
+ 
+inline void MfRecord::SetMfrl(int mfrl)
+// Set Master file record length
+{
+	assert(mfrl>0);
+    leader_directory_.mfrl_ = mfrl;
+}
+//////////////////////////////
+ 
+inline void MfRecord::SetMfbwb(long mfbwb)
+// Sets backward pointer block number to mfbwb
+{
+	assert(mfbwb>=0);
+    leader_directory_.mfbwb_ = mfbwb;
+}
+
+//////////////////////////////
+ 
+inline void MfRecord::SetMfbwp(int mfbwp)
+// Sets backward pointer block offset to mfbwp
+{
+	assert(mfbwp>=0);
+    leader_directory_.mfbwp_ = mfbwp;
+}
+//////////////////////////////
+ 
+inline void  MfRecord::SetBase(int base)
+// Sets the base value in the record aggregate
+{
+	assert(base>=0);
+    leader_directory_.base_ = base;
+}
+
+//////////////////////////////
+ 
+inline void MfRecord::SetNvf(int nvf)
+// Sets the number of entries in the directory part
+{
+	assert(nvf>=0);
+    leader_directory_.nvf_ = nvf;
+}
+ 
+//////////////////////////////
+ 
+inline void MfRecord::SetStatus(int s)
+// Sets the status of the record
+{
+   leader_directory_.status_ = s;
+}
+ 
+//////////////////////////////
+ 
+inline void MfRecord::SetState(int s)
+// Sets the status of the record
+{
+	assert(s==MfRecord::Record_Empty || s==MfRecord::Record_Read 
+		|| s==MfRecord::Record_Changed || s==MfRecord::Record_Created);
+   state_ = s;
+}
+
+inline void MfRecord::SetMfbRead(long b)
+{
+	mfb_read_ = b;
+}
+
+inline void MfRecord::SetMfpRead(int pos)
+{
+	mfp_read_ = pos;
+}
+
+inline void MfRecord::SetMflenRead(int len)
+{
+	mflen_read_ = len;
+}
+
+inline int MfRecord::GetFieldTag(int i)
+{
+	return leader_directory_.dir_[i].tag_;
+}
+inline int MfRecord::GetFieldPos(int i)
+{
+	return leader_directory_.dir_[i].pos_;
+}
+
+inline int MfRecord::GetFieldLen(int i)
+{
+	return leader_directory_.dir_[i].len_;
+}
+
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.cpp	(revision 16561)
@@ -0,0 +1,620 @@
+/**********************************************************************
+ *
+ * Unimarc.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "stdafx.h"
+
+///////////////////////////////////////////////////////////////////////////////////
+// MarcRecord - This class can instantiate objects that represent a MARC
+//              bibilographic record and includes methods for manipulating the 
+//              record.
+//
+//             example:
+//
+//	#include "Unimarc.h"
+//
+//     // Create a MarcRecord object with a raw MARC record
+//     MarcRecord mr(buf,1)
+//     mr.PrettyFormat("The title is %245, which is the field with tag 245");
+#include <vector>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Unimarc.h"
+
+//-------------------------------------------------------------------------------
+// ostream& operator<<(ostream& s, const MarcLeader& leader)
+//
+// Overloaded instance of the output operator for MarcLeader objects
+//---------------------------------------------------------------------------------
+std::ostream& operator<<(std::ostream& s, const MarcLeader& leader)
+{
+	s.write(leader.lreclen_, 5);	// Logical record length
+	s.write(&leader.status_, 1);	// Record status  
+	s.write(&leader.type_, 1);		// Type of record
+	s.write(&leader.bibLevel_, 1);	// Bibliographic level
+	s.write(&leader.ctrlType_, 1);	// Type of control
+	s.write(&leader.undPos_, 1);	// Undefined character position
+	s.write(&leader.indCount_, 1);	// Indicator count
+	s.write(&leader.scd_, 1);		// Subfield code count
+	s.write(leader.base_, 5);   	// Base address of data
+	s.write(&leader.encLevel_, 1);	// Encoding level
+	s.write(&leader.dcf_, 1);		// Descriptive cataloging form
+	s.write(&leader.lrr_, 1);		// Linked record requirement
+	  // Directory  entry map
+	s.write(&leader.llof_, 1);		// Length of the length-of-field portion
+	s.write(&leader.lscp_, 1);		// Length of starting-character-position portion
+	s.write(&leader.lidp_, 1);		// Length of implementation-defined portion
+	s.write(&leader.uep_, 1);		// Undefined entry map character position
+	return s;
+}
+
+//----------------------------------------------------------------------------------
+// istream& operator>>(istream& s, const MarcLeader& leader)
+//
+// Overloaded instance of the input operator for MarcLeader objects
+//----------------------------------------------------------------------------------
+std::istream& operator>>(std::istream& s, MarcLeader& leader)
+{
+	s.read(leader.lreclen_, 5);	    // Logical record length
+	s.read(&leader.status_, 1);	    // Record status  
+	s.read(&leader.type_, 1);		// Type of record
+	s.read(&leader.bibLevel_, 1);	// Bibliographic level
+	s.read(&leader.ctrlType_, 1);	// Type of control
+	s.read(&leader.undPos_, 1);	    // Undefined character position
+	s.read(&leader.indCount_, 1);	// Indicator count
+	s.read(&leader.scd_, 1);		// Subfield code count
+	s.read(leader.base_, 5);   	    // Base address of data
+	s.read(&leader.encLevel_, 1);	// Encoding level
+	s.read(&leader.dcf_, 1);		// Descriptive cataloging form
+	s.read(&leader.lrr_, 1);		// Linked record requirement
+	  // Directory  entry map
+	s.read(&leader.llof_, 1);		// Length of the length-of-field portion
+	s.read(&leader.lscp_, 1);		// Length of starting-character-position portion
+	s.read(&leader.lidp_, 1);		// Length of implementation-defined portion
+	s.read(&leader.uep_, 1);		// Undefined entry map character position
+	return s;
+}
+
+/************************************************************************
+Local prototypes
+ ***********************************************************************/
+static void Printx(FILE *fp, char *s, int x);
+
+//----------------------------------------------------------------------
+// MarcRecord::MarcRecord(char *data, int length, int copyData)
+// 
+// Constructor that creates a new MarcRecord object. 
+// data is raw marc record data for a single record.  If copyData is 1, 
+// data is encapsulated within this object.  If 0, does not copy data and 
+// responsibility falls to caller to maintain data pointer.
+//----------------------------------------------------------------------
+MarcRecord::MarcRecord(char* data, int length, int copyData)
+{
+	encapsulated_ = copyData;
+	
+	/* encapsulate or set pointer to data */
+	if (encapsulated_)
+	{
+		data_ = new char[length + 1]; 
+		memcpy(data_, data, length);
+		data_[length] = '\0';
+	}
+	else
+		data = data;
+	
+	// Lay leader overlay onto data
+	leader_ = *(MarcLeader *) (data_);
+	
+	// Parse the record.  sets dirCount_, directory_ 
+	if (!ParseMarcRecord()) 
+	{
+		// marcRecord_destroy(m);
+		// return NULL;
+	}
+}
+
+//----------------------------------------------------------------------
+// void MarcRecord::~MarcRecord()
+//
+// Destructor, destroys a MarcRecord object and all used resources.
+//----------------------------------------------------------------------
+MarcRecord::~MarcRecord()
+{
+	if ((encapsulated_) && (data_))
+		delete[] data_;
+
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+		directory_[i].field_.erase(directory_[i].field_.begin(),directory_[i].field_.end());
+	directory_.erase(directory_.begin(),directory_.end());
+}
+
+#define MARC_STATE_IDLE 0
+#define MARC_STATE_TAG 1
+#define MARC_STATE_WIDTH 2
+//----------------------------------------------------------------------
+// int MarcRecord::PrettyFormat(char* format, char* buf, int maxlen)
+//
+//  Formats the contents of the marc record according to a format string.
+//  Useful for generating a buffer to pretty-print the record.
+//  format contains a format-specifier string.  The format-specifier string
+//  is kindof like printf's, except the only control character is '%'.
+//  Following the '%' should be the integer MARC field code as defined in 
+//  the (huge) list of available MARC fields from the library of congress
+//  (http://lcweb.loc.gov/marc/).  buf is a previously allocated 
+// buffer into which the pretty version will be copied.  maxlen is the max 
+// length to write into buf.  After call, buf contains pretty formatted text.
+//----------------------------------------------------------------------
+int MarcRecord::PrettyFormat(char* format, char* buf, int maxlen)
+{
+	char b[1024];
+	int bufpos = 0, fmtpos = 0;
+	
+	b[0] = '\0';
+	buf[0] = '\0';
+	
+	for (; format[fmtpos] != '\0'; )
+	{
+		switch (format[fmtpos])
+		{
+			case '%': 
+				{
+					char tag[4];
+					strncpy(tag, (char *)(format + fmtpos + 1), 3);
+					tag[3] = '\0';
+					GetSubField(tag, format[fmtpos + 4], b,	sizeof(b) - 1);
+					if (*b)
+					{
+						int l = strlen(b);
+						strncat(buf, b, maxlen - bufpos - 1);
+						bufpos += l;
+						if (bufpos >= maxlen)
+						{
+							buf[maxlen - 1] = '\0';
+							return maxlen;
+						}
+					}
+					fmtpos += 5;
+					break;
+				}
+			default:
+				buf[bufpos] = format[fmtpos];
+				bufpos++;
+				fmtpos++;
+				buf[bufpos] = '\0';
+				break;
+		}
+	}
+	return bufpos;
+}
+
+//----------------------------------------------------------------------
+// int MarcRecord::GetField(char* tag, char* buf, int maxlen)
+//
+// append all subfields in a field together. 
+//----------------------------------------------------------------------
+int MarcRecord::GetField(char* tag, char* buf, int maxlen)
+{
+	int remain = maxlen - 1;
+	
+	buf[0] = '\0';
+	
+	if ((tag == NULL) || (*tag == '\0'))
+		return 0;
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+	{
+		if (!strncmp(tag, directory_[i].tag_, strlen(tag)))
+		{
+			for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+			{
+				int flen = directory_[i].field_[j].len_;
+				if (flen > remain)
+				{
+					strncat(buf, directory_[i].field_[j].data_ + 1, remain);
+					buf[maxlen] = '\0';
+					return 1;
+				}
+				else 
+				{
+					strncat(buf, directory_[i].field_[j].data_ + 1, flen);
+					remain -= flen;
+				}
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+//----------------------------------------------------------------------
+// int MarcRecord::GetSubFields(char* tag, char subtag, char* buf, int maxlen)
+//
+// append all fields with a certain subfield together.
+//----------------------------------------------------------------------
+int MarcRecord::GetSubFields(char* tag, char subtag, char* buf, int maxlen)
+{
+	int remain = maxlen - 1;
+	
+	buf[0] = '\0';
+	
+	if ((tag == NULL) || (*tag == '\0'))
+		return 0;
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+	{
+		if (!strncmp(tag, directory_[i].tag_, strlen(tag)))
+		{
+			for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+			{
+				if (*(directory_[i].field_[j].data_) == subtag)
+				{
+					int flen = directory_[i].field_[j].len_;
+					if (flen > remain)
+					{
+						strncpy(buf, directory_[i].field_[j].data_ + 1, remain);
+						buf[maxlen] = '\0';
+						return 1;
+					}
+					else 
+					{
+						strcpy(buf, directory_[i].field_[j].data_ + 1);
+						remain -= flen;
+					}
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+int MarcRecord::HasField(char* tag)
+{
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+		if (!strncmp(tag, directory_[i].tag_, strlen(tag)))
+			return 1;
+		
+		return 0;
+}
+
+int MarcRecord::HasSubField(char* tag, char subtag)
+{
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+		if (!strncmp(tag, directory_[i].tag_, strlen(tag)))
+			for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+				if (*directory_[i].field_[j].data_ == subtag)
+					return 1;
+				return 0;
+}
+
+int MarcRecord::GetSubField(char *tag, char subtag, char *buf, 	int maxlen)
+{
+	
+	buf[0] = '\0';
+	
+	if ((tag == NULL) || (*tag == '\0'))
+		return 0;
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+	{
+		if (!strncmp(tag, directory_[i].tag_, strlen(tag)))
+		{
+			for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+			{
+				if (*(directory_[i].field_[j].data_) == subtag)
+				{
+					int l = directory_[i].field_[j].len_ - 1 > maxlen ? maxlen : directory_[i].field_[j].len_ - 1;
+					strncpy(buf, directory_[i].field_[j].data_ + 1, l);
+					buf[l] = '\0';
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+
+int GetNum(char *s, int n)
+{
+	char nbuf[10]; /* no more than 9 digits */
+	
+	strncpy(&nbuf[0], s, n);
+	nbuf[n] = '\0';
+	return (atoi(nbuf));
+}
+
+void MarcRecord::Dump()
+{
+	int dc = 0, fc = 0;
+	
+	printf("tags: ");
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+	{
+		Printx(stdout, directory_[i].tag_, 3);
+		dc++;
+		for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+		{
+			if (directory_[i].field_[j].data_)
+			{
+				putchar((unsigned long)directory_[i].field_[j].data_);
+			}
+			fc++;
+			/*
+			printx(stdout, (char*)f->data + 1, f->len);
+			printf("\n"); */
+		}
+		printf(",");
+	}
+	putchar('\n');
+}
+
+
+
+
+
+void MarcRecord::PrintDetailed(FILE *fp)
+{
+	int count, i;
+	
+	fprintf(fp, "RecordLength:\t\t%i\n", GetNum(leader_.lreclen_, 5));
+	fprintf(fp, "RecordStatus:\t\t%c\n", leader_.status_);
+	fprintf(fp, "RecordType:\t\t%c\n", leader_.type_);
+	fprintf(fp, "BibLevel:\t\t%c\n", leader_.bibLevel_);
+	fprintf(fp, "ControlType:\t\t%c\n", leader_.ctrlType_);
+	fprintf(fp, "UndefCharPos:\t\t%c\n", leader_.undPos_);
+	fprintf(fp, "IndCount:\t\t%i\n", GetNum(&leader_.indCount_, 1));
+	fprintf(fp, "SubCodeCount:\t\t%i\n", GetNum(&leader_.scd_, 1));
+	fprintf(fp, "DataBaseAddr:\t\t%i\n", GetNum(leader_.base_, 5));
+	fprintf(fp, "EncodingLevel:\t\t%c\n", leader_.encLevel_);
+	fprintf(fp, "DescCatForm:\t\t%c\n", leader_.dcf_);
+	fprintf(fp, "LinkedRecReq:\t\t%c\n", leader_.lrr_);
+	fprintf(fp, "LengthOf LengthOfField:\t%i\n", GetNum(&leader_.llof_, 1));
+	fprintf(fp, "LengthOf StartCharPos:\t%i\n", GetNum(&leader_.lscp_, 1));
+	fprintf(fp, "LengthOf ImpDefined:\t%i\n", GetNum(&leader_.lidp_, 1));
+	fprintf(fp, "UndefinedEntry:\t\t%i\n", GetNum(&leader_.uep_, 1));
+	
+	count = (GetNum(leader_.base_, 5) - MARC_LEADER_LEN) / MARC_DIRENT_LEN;
+	for (i = 0; i < count; i++)
+	{
+		DirectoryEntry dir = *(DirectoryEntry*)(data_+MARC_LEADER_LEN+(i*MARC_DIRENT_LEN));
+		fprintf(fp, "\n");
+		fprintf(fp, "Tag:\t\t");
+		Printx(fp, dir.tag_, 3);
+		fprintf(fp, "\nLength:\t\t%i\n", GetNum(dir.len_, 4));
+		fprintf(fp, "StartPos:\t%i\n", GetNum(dir.scp_, 5));
+		fprintf(fp, "Data:\t\t");
+		Printx(fp, (char *)(data_ + GetNum(leader_.base_, 5) + GetNum(dir.scp_, 5)), GetNum(dir.len_, 4));
+		fprintf(fp, "\n");
+	}
+}
+
+/*
+desc:		parses the marc record and builds appropriate data structures in m
+
+params:
+
+	m		newly created marcRecord
+	
+returns:
+
+	1 on success
+	0 on parse or memory failure
+*/
+int MarcRecord::ParseMarcRecord()
+{
+	dirCount_ = (GetNum(leader_.base_, 5) - MARC_LEADER_LEN) / MARC_DIRENT_LEN;
+	
+	// For each MarcDirectory entry, create list of subfields
+	MarcDirectoryEntry dir_entry;
+	for (int i = 0; i < dirCount_; i++)
+	{
+		char*  data;
+		// Extract the directory component
+		DirectoryEntry dir_entry = *(DirectoryEntry*)(data_+MARC_LEADER_LEN+(i*MARC_DIRENT_LEN));
+
+		MarcDirectoryEntry marc_dir_entry;
+		
+		// Get a pointer to the data for this MarcDirectory
+		data = (char *)(data_ + GetNum(leader_.base_, 5) + GetNum(dir_entry.scp_, 5));
+		
+		// set the tag for this MarcDirectory 
+		marc_dir_entry.tag_ = dir_entry.tag_;
+		
+		/* add the list of subfields for this MarcDirectory */
+		if (!AddSubFields(marc_dir_entry, data))
+			return 0;
+		directory_.push_back(marc_dir_entry);
+		
+	}
+	
+	return 1;
+}
+
+void Printx(FILE* fp, char* s, int x)
+{
+	int i;
+	for (i = 0; i < x; i++)
+		fprintf(fp, "%c", s[i]);
+}
+
+//----------------------------------------------------------------------
+// int MarcRecord::AddSubFields(MarcDirectory& d, char* b)
+//
+// Parses and adds subfields to MarcDirectory
+//
+// params:
+//
+//	d		MarcDirectory to which subfields are added
+//	b		buffer of data for d
+//
+// returns:
+//
+//	1 on success
+//	0 on parse or memory error
+//----------------------------------------------------------------------
+int MarcRecord::AddSubFields(MarcDirectoryEntry& d, char* b)
+{
+	char *p, *start;
+	int cont = 1, length = 0;
+	
+	/* create new field */
+	MarcField field;
+	
+	
+	start = b;
+	for (p = b; ((*p != '\0') && (cont == 1)); p = (char *)p + 1)
+	{
+		switch (*p)
+		{
+			case SUBFDELIM:
+				/* end of current subfield, but more exist */
+				
+				/* set length and pointer to data for this field */
+				field.len_  = length;
+				field.data_ = start;
+				d.field_.push_back(field);
+				/* by definition, there should be more subfields, but we'll
+				check for NULL just in case
+				*/
+				if (*p + 1)
+				{
+				}
+				else 
+				{
+					/* uh oh */
+					return 0;
+				}
+				length = 0;
+				start = (char *)p + 1;
+				break;
+			case FIELDTERM:
+				/* end of subfield list */
+				
+				/* set length and pointer to data for this field */
+				field.len_  = length;
+				field.data_ = start;
+				d.field_.push_back(field);
+				
+				/* break out of loop */
+				cont = 0;
+				break;
+			case RECTERM:
+				/* end of record */
+				cont = 0;
+				break;
+			default:
+				/* regular character */			
+				length++;
+				break;
+		}
+	}
+	return 1;
+}
+
+void MarcRecord::Format(char *buf, int maxlen, int format)
+{
+	char b[1024];
+	char date[24];
+	char title[128];
+	
+	b[0] = '\0';
+	buf[0] = '\0';
+	
+	/* FIXME: get rid of this debugging crap sometime */
+	Dump();
+	
+	switch (format)
+	{
+		case MARC_FMT_BRIEF: 
+			{
+				GetSubField("245", 'a', title, 	sizeof(title) - 1);
+				GetSubField("260", 'c', date, sizeof(date) - 1);
+				if ((strlen(date) + strlen(title) + 2) <=(size_t)maxlen)
+				{
+					sprintf(buf, "%s %s", date, title);
+				}
+				else 
+				{
+					/* FIXME: we're overflowing the buffer! */
+				}
+				break;
+			}
+		case MARC_FMT_FULL: 
+			{
+				int dc = 0, fc = 0;
+				int remain = maxlen;
+				
+	            for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+				{
+					dc++;
+		            for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+					{
+						fc++;
+						if ((directory_[i].field_[j].len_ + 2) > remain)
+						{
+							strncat(buf, (char *)directory_[i].field_[j].data_ + 1, remain - 2);
+							* (buf + remain - 2) = '\n';
+							* (buf + remain - 1) = '\0';
+							/* we're out of room in the buffer. */
+							return;
+						}
+						if (directory_[i].field_[j].len_ > 0)
+						{
+							strncat(buf, (char*)directory_[i].field_[j].data_ + 1, directory_[i].field_[j].len_);
+							strncat(buf, "\n\0", 2);
+							remain -=(directory_[i].field_[j].len_ + 2);
+						}
+					} 
+				}
+				/*
+				marcRecord_getSubField(m, "245", 'a', title, 
+				sizeof(title) - 1);
+				marcRecord_getSubField(m, "245", 'a', title, 
+				sizeof(title) - 1);
+				marcRecord_getSubField(m, "260", 'c', date, 
+				sizeof(date) - 1);
+				sprintf(buf, "%s %s", date, title);
+				*/
+				break;
+			}
+		default:
+			break;
+	}
+}
+
+int MarcRecord::RecordLength()
+{
+	register int l = 0;
+	
+	
+	for (std::vector<MarcDirectoryEntry>::size_type i=0; i!=directory_.size(); ++i)
+		for (std::vector<MarcField>::size_type j=0; j!=directory_[i].field_.size(); ++j)
+			l += directory_[i].field_[i].len_;
+		
+        return l;
+}
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/Unimarc.h	(revision 16561)
@@ -0,0 +1,175 @@
+/**********************************************************************
+ *
+ * Unimarc.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef MARC_RECORD
+#define MARC_RECORD
+
+#define SUBFDELIM '\037'   // %
+#define FIELDTERM '\036'   // $
+#define RECTERM   '\035'   // # sharp
+
+#define MARC_LEADER_LEN 24
+#define MARC_DIRENT_LEN 12
+
+#define MARC_FMT_BRIEF 0
+#define MARC_FMT_FULL 1
+
+//---------------------
+// UNIMARC Record Label
+//---------------------
+struct  MarcLeader
+{
+	char	lreclen_[5];	/* logical record length */
+	char	status_;		/* record status_ */
+	char	type_;			/* type_ of record */
+	char	bibLevel_;	    /* bibliographic level */
+	char	ctrlType_;	    /* type_ of control */
+	char	undPos_;		/* undefined character position */
+	char	indCount_;	    /* indicator count */
+	char	scd_;			/* subfield code count */
+	char	base_[5];		/* base_ address of data */
+	char	encLevel_;	    /* encoding level */
+	char	dcf_;			/* descriptive cataloging form */
+	char	lrr_;			/* linked record requirement */
+	char	llof_;			/* length of the length-of-field portion */
+	char	lscp_;			/* length of starting-character-position portion */
+	char	lidp_;			/* length of implementation-defined portion */
+	char	uep_;			/* undefined entry map character position */
+
+	MarcLeader()
+	{
+		lreclen_[0]=' ';
+		lreclen_[1]=' ';
+		lreclen_[2]=' ';
+		lreclen_[3]=' ';
+		lreclen_[4]=' ';
+		status_   = '0';     // New record
+        type_     = '0';     // Language materials, printed
+	    bibLevel_ = '0';	     // Monographic
+	    ctrlType_ = '0';     // Hierarchical relationship undefined
+	    undPos_   = '0';     // Undefined, contains a blank
+	    indCount_ = '0';     // Invariably 2 in UNIMARC
+	    scd_      = '0';     // Invariably 2 in UNIMARC
+	    base_[0]  = ' ';     // Base_ address of data
+		base_[1]  = ' ';
+		base_[2]  = ' ';
+		base_[3]  = ' ';
+		base_[4]  = ' ';
+	    encLevel_ = '0';     // (Blank) full level
+	    dcf_      = '0';     // (Blank) record is in full ISBD form
+	    lrr_      = '0';     // Undefined, contains a blank
+	    llof_     = '4';     // Length of the length-of-field portion
+	    lscp_     = '5';     // Length of starting-character-position portion 
+	    lidp_     = '0';     // Length of implementation-defined portion
+	    uep_      = '0';     // Undefined, contains a blank
+	}
+
+   friend std::ostream& operator<<(std::ostream& s, const MarcLeader& leader);
+   friend std::istream& operator>>(std::istream& s, MarcLeader& leader);
+};
+
+//----------------
+// Directory entry
+//----------------
+struct DirectoryEntry
+{
+	char	tag_[3];		/* tag */
+	char	len_[4];		/* field length */
+	char	scp_[5];		/* starting character position */
+   friend std::ostream& operator<<(std::ostream& s, const DirectoryEntry& de);
+   friend std::istream& operator>>(std::istream& s, DirectoryEntry& leader);
+};
+inline std::istream& operator>>(std::istream& s, DirectoryEntry& de)
+{
+	s.read(de.tag_, 3);
+	s.read(de.len_, 4);
+	s.read(de.scp_, 5);
+	return s;
+}
+inline std::ostream& operator<<(std::ostream& s, const DirectoryEntry& de)
+{
+	s.write(de.tag_, 3);
+	s.write(de.len_, 4);
+	s.write(de.scp_, 5);
+	return s;
+}
+
+
+
+//------------------
+// Subfield item 
+//------------------
+struct MarcField
+{
+	int			len_;
+	char*       data_;
+};
+
+//-------------------
+// Directory entry
+//-------------------
+struct MarcDirectoryEntry
+{
+	char*                  tag_;
+	std::vector<MarcField> field_;
+};
+
+
+//---------------------------
+// UNIMARC record structure 
+//---------------------------
+
+class MarcRecord
+{
+protected:
+	MarcLeader      leader_;
+	char*           data_;
+	int			    length_;
+	int				dirCount_;
+	int				encapsulated_;
+	std::vector<MarcDirectoryEntry>   directory_;
+	
+    int ParseMarcRecord();
+    int AddSubFields(MarcDirectoryEntry& d, char* b);
+public:
+    MarcRecord(char* data, int length, int copyData);
+    ~MarcRecord();
+    void PrintDetailed(FILE* fp);
+    void Get(char* buf,int maxlen,int format);
+    int  GetSubField(char* tag, char subtag, char* buf, int maxlen);
+    int  GetField(char* tag, char* buf, int maxlen);
+    int  GetSubFields(char* tag, char subtag, char* buf, int maxlen);
+
+    int  HasField(char* tag);
+    int  HasSubField(char* tag, char subtag);
+
+    void Format(char* buf, int maxlen, int format);
+    int  RecordLength();
+    int  PrettyFormat(char* format, char* buf, int maxlen);
+
+    void Dump ();
+};
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.cpp	(revision 16561)
@@ -0,0 +1,305 @@
+/**********************************************************************
+ *
+ * XRFFile.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// XrfFile.cpp: XrfFile object methods
+//
+////////////////////////////////////////////////////////////
+#include "stdafx.h"
+#include "XRFFile.h"
+
+
+
+//-----------------------------------------------------------------------------------
+// IsisError  XrfFile::OpenXrf(const _TCHAR *fname, FileSystem::AccessMode mode
+//				                              /* = FileSystem::FILE_READWRITE */)
+//
+// Open xrf_ file with file name fname 
+//-----------------------------------------------------------------------------------
+IsisError  XrfFile::OpenXrf(const _TCHAR *fname, FileSystem::AccessMode mode
+				                              /* = FileSystem::FILE_READWRITE */)
+{
+  TCHAR xrfFileName[_MAX_PATH];
+  _tcscpy(xrfFileName, fname);
+
+  if (!CFileBase::Exists(xrfFileName)) {
+    xrfFileName[strlen(xrfFileName) - 3] = 'X';
+    xrfFileName[strlen(xrfFileName) - 2] = 'R';
+    xrfFileName[strlen(xrfFileName) - 1] = 'F';
+  }
+
+    IsisError isis_error;
+    xrfa_->xrfpos_ = -1;   // No block into buffer
+	try {
+        FileError rc = CFileBase::Open(xrfFileName, mode);
+	}
+	catch(CFileBaseException)
+	{
+		isis_error = ISIS_XRF_OPEN_ERROR;
+        throw CIsisDbException(isis_error, __FILE__, __LINE__); 
+	}
+  	long file_size   = CFileBase::FileSize(file_name);
+	
+	last_block_  = file_size/XRFBLKSIZE;
+
+	if (last_block_<1 || last_block_*XRFBLKSIZE != file_size)
+	{
+		isis_error =  ISIS_XRF_CORRUPT;
+        throw CIsisDbException(isis_error, __FILE__, __LINE__); 
+	}
+
+	return ISIS_NO_ERROR;
+}
+ 
+//////////////////////////////
+ 
+ 
+IsisError  XrfFile::CreateXrf(const _TCHAR *fname)
+// Creates an xrf_ file
+{
+    IsisError isis_error;
+	try 
+	{
+        FileError rc = CFileBase::Create(fname);
+    	// Initialize an empty block and write-it
+		xrfa_->Init();
+        xrfa_->xrfpos_ = 1; // Block number
+		xrfa_->xrfeof_ = 1; // Last xrf_ block
+        last_block_    = 1;		
+        WriteXrf(1L);
+	}
+	catch(CFileBaseException)
+	{
+		isis_error = ISIS_XRF_CREATE_ERROR;
+        throw CIsisDbException(isis_error, __FILE__, __LINE__);
+	}
+	
+    return ISIS_NO_ERROR;
+}
+//-----------------------------------------------------------------------------------
+// IsisError  XrfFile::CreateXrf(const _TCHAR *fname, long nextMfn)
+//
+// Special xrf_ file creation for building xrf_ blocks of an existing mst
+//-----------------------------------------------------------------------------------
+IsisError  XrfFile::CreateXrf(const _TCHAR *fname, long nextMfn)
+// Creates an xrf_ file
+{
+    IsisError isis_error;
+    long nBlocks = (nextMfn-1)/MAXTIV + 1;       // Number of blocks to allocate
+	try 
+	{
+        FileError rc = CFileBase::Create(fname);
+		xrfa_->Init();
+    	// Initialize an empty block and write-it
+		for (int iBlock=1; iBlock<= nBlocks; iBlock++)
+		{
+			TRACE("\niBlock=%d", iBlock);
+			xrfa_->xrfpos_ = iBlock;
+			xrfa_->xrfeof_ = 0;
+			if (iBlock == nBlocks)
+			    xrfa_->xrfeof_ = 1;
+
+			WriteXrf(iBlock);
+		}
+	}
+	catch(CFileBaseException)
+	{
+		isis_error = ISIS_XRF_CREATE_ERROR;
+        throw CIsisDbException(isis_error, __FILE__, __LINE__);
+	}
+
+    last_block_ = nBlocks;	
+    return ISIS_NO_ERROR;
+}
+
+//------------------------------------------------------------------------------
+// void XrfFile::UnPack(void)
+//
+// Unpack addresses of master file records. The current packed XRF block 
+// contained in xrf_ structure will be unpacked in xrfa_ structure.
+//
+//  A master file address is packed as follow: addr = mfb*2048 + mfp
+//
+//  2048 = 2**11 e.g. mfp->[0:2047] and block number -> [1: 2**20]
+//  2**20 = 1 048 576
+//
+// As we can put 127 addresses per block, then the limit on number of records
+// is 127 * 1 048 576
+//------------------------------------------------------------------------------
+void XrfFile::UnPack(void)
+{
+   fix_endianness(xrf_->xrfpos_);
+
+   xrfa_->xrfpos_ = xrf_->xrfpos_;
+   if (xrfa_->xrfpos_ < 0)
+   {
+      xrfa_->xrfpos_ = - xrf_->xrfpos_;
+      xrfa_->xrfeof_ = 1;
+   }
+   else
+      xrfa_->xrfeof_ = 0;
+ 
+   for (int i=0; i<MAXTIV; i++)
+   {
+      long j = xrf_->xrftiv_[i];             // Packed value
+      fix_endianness(j);
+      bool b = (j < 0);
+      if (b)
+         j = - j;
+      xrfa_->xrftiv_[i].xrfmfb_ = int(j / 2048L);   // Unpack block number
+      if (b)
+         xrfa_->xrftiv_[i].xrfmfb_ = -xrfa_->xrftiv_[i].xrfmfb_;
+      xrfa_->xrftiv_[i].xrfmfp_ = int(j % 2048L);   // Unpack offset
+   }
+}
+ 
+//------------------------------------------------------------------------------
+// void XrfFile::Pack(void)
+//
+// Pack addresses of master file records. The current unpacked XRF block 
+// contained in xrfa_ will be packed in xrf_ structure.
+//------------------------------------------------------------------------------
+void XrfFile::Pack(void)
+{
+   long j = xrfa_->xrfpos_;
+   if (xrfa_->xrfeof_ != 0)
+      j = -j;
+   xrf_->xrfpos_ = j;
+
+ 
+   for (int i=0; i<MAXTIV; i++)
+   {
+      j = xrfa_->xrftiv_[i].xrfmfb_;    // Unpacked block number
+      int b = (j < 0);
+      if (b)
+         j = - j;
+      //-------------------------------------------
+      // pack block number and offset in a long int
+      //-------------------------------------------
+      j = j * 2048 + xrfa_->xrftiv_[i].xrfmfp_;
+      if (b) j = -j;
+      xrf_->xrftiv_[i] = j;     // Put value in xrf_
+   }
+}
+ 
+//------------------------------------------------------------------------------
+// int XrfFile::GetMfp(long mfn, long& mfb, int& mfp)
+//
+// Gets the address of master file data record with MFN "mfn" by accessing the 
+// xrf_ file). mfb is the master file block number [1:NBLK] and mfp is the offset
+// in the master file block where the record starts [0:BLKSIZE-1]
+//
+//  Returns   Active              = Record active
+//            LogicalyDeleted     = Record logically deleted
+//            PhysicalyDeleted    = Record physically deleted
+//            EndOfFile
+//  Note: mfp can be >=      than BLKSIZE if the inverted file
+//  wasn't updated, then true mfp is mfp%BLKSIZE
+//------------------------------------------------------------------------------
+int XrfFile::GetMfp(long mfn, long& mfb, int& mfp)
+{
+	PRECONDITION(mfn>0);
+	PRECONDITION(last_block_>0);
+    int  comp;
+    long comb;
+    int  rc;
+	
+    long psxrf=(mfn-1)/MAXTIV+1;       // Block number
+	if (psxrf > last_block_)
+	{
+        rc = EndOfFile;                // Inexistent record (eof)
+		return rc;
+		throw CIsisDbException(ISIS_XRF_INVALID_BLOCK, __FILE__, __LINE__);
+	}
+    if (psxrf != xrfa_->xrfpos_) 
+	{                                  // If current block in xrf_ different
+		try
+		{
+            ReadXrf(psxrf);            // then read the block
+		}
+		catch (CFileBaseException e)
+		{
+            throw CIsisDbException(ISIS_XRF_READ_ERROR, __FILE__, __LINE__);
+		}
+
+    }
+    int i = int((mfn-1) % MAXTIV);     // Index
+    comb = xrfa_->xrftiv_[i].xrfmfb_;     // Master file block #
+    comp = xrfa_->xrftiv_[i].xrfmfp_;     // Offset in block
+
+	if (comb > 0)
+        rc = Active;                   // Record active 
+    else if (comb < 0 && comp > 0)
+        rc = LogicalyDeleted;          // Record logicaly deleted 
+	else if (comb == -1 && comp == 0)
+        rc = PhysicalyDeleted;         // Record physically deleted 
+    else if (comb == 0 && comp == 0)
+        rc = EndOfFile;                // Inexistent record (eof)
+	else
+	{
+        throw CIsisDbException(ISIS_XRF_INVALID_STATUS, __FILE__, __LINE__); 
+	}
+    mfb = labs(comb);
+    mfp = comp;
+	if (rc == Active)
+	{
+	    ASSERT(mfb>0 && mfb <= MAXMFB);
+		ASSERT(mfp>=0 && mfp < 2048);
+	    int mfpTemp = mfp%XRFBLKSIZE;
+	    ASSERT(mfpTemp>=0 && mfpTemp<XRFBLKSIZE);
+	}
+    return(rc);
+}
+ 
+void XrfFile::Grow(long from_lastb, long to_lastb)
+{
+	for (long i=from_lastb+1; i<=to_lastb; i++)
+	{
+        //--------------------
+        // append empty blocks
+        //--------------------
+ 		xrfa_->Init();
+		xrfa_->xrfpos_ = i;
+		xrfa_->xrfeof_ = (i==to_lastb) ? 1 : 0; // Eof flag
+	    try
+		{
+           WriteXrf(i);           // Write xrf_ at block address xrfb
+		}
+		catch (CFileBaseException)
+		{
+			throw(ISIS_XRF_WRITE_ERROR);
+		}
+	}
+}
+ 
+//------------------------------------------------------------------------------
+// void XrfFile::PutMfp(long mfn, long mfb, int mfp, int status)
+//
+// Puts the address of a master file record (mfb,mfp) as the address for MFN 
+// "mfn" in xrf_ file
+//------------------------------------------------------------------------------
+void XrfFile::PutMfp(long mfn, long mfb, int mfp, int status)
+{
+}
Index: /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/XRFFile.h	(revision 16561)
@@ -0,0 +1,151 @@
+/**********************************************************************
+ *
+ * XRFFile.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+////////////////////////////////////////////////////////////
+// XRFFILE.H : Cross Ref File object
+//
+////////////////////////////////////////////////////////////
+ 
+#ifndef _XRFFILE_H_
+#define _XRFFILE_H_
+#ifndef _BLKFILE_H_
+#include "blkfile.h"
+#endif
+#include <assert.h>
+#include "IErrors.h"
+ 
+const int MAXTIV     =     127; //  # of XRF entries per block
+const int XRFBLKSIZE =     512; //
+const int MAXMFB     = 1048576; 
+
+ 
+ 
+/////////////////////////////////////////
+// Cross reference file data structures
+//
+ 
+ 
+struct MSTA_ENTRY                 // Unpacked MST address
+{             
+   long       xrfmfb_;             // Master file block
+   short int  xrfmfp_;             // Offset
+} ;
+ 
+struct XRFA                       // To strore unpacked addresses
+{                
+   long          xrfpos_;          // Block number on xrf_ file or -1 if empty
+   short int     xrfeof_;          // 1 if last block of xrf_ file
+   MSTA_ENTRY xrftiv_[MAXTIV];     // MAXTIV unpacked MST addresses
+
+   XRFA() { Init(); }
+
+   void Init()
+   {
+	   xrfeof_ = 0;              // Not eof
+	   xrfpos_ = -1;             // empty   
+	   for (int i=0; i<MAXTIV; i++)
+	   {
+		   xrftiv_[i].xrfmfb_  = -1;
+	       xrftiv_[i].xrfmfp_  = 0;
+	   }
+   }
+
+
+} ;
+ 
+struct XRF                       // To store packed addresses
+{                                // for reading and wrting
+   long xrfpos_;                  // block number on xrf_ file
+   long xrftiv_[MAXTIV];          // MAXTIV packed MST addresses 
+} ;
+ 
+///////////////////
+// XrfFile class
+//
+
+ 
+class XrfFile : public BlkFile 
+{
+private:
+   XRF*  xrf_;             // Buffer to store packed addresses
+   XRFA* xrfa_;            // Buffer to store unpacked addresses
+   long  last_block_;      // Should always contain the last allocated block
+   
+   void ReadXrf(long b);
+   void WriteXrf(long b);
+   void UnPack();
+   void Pack();
+   void Grow(long from_lastb, long to_lastb);
+public:
+   XrfFile();
+   IsisError OpenXrf(const _TCHAR *fname,
+	                 FileSystem::AccessMode mode = FileSystem::FILE_READWRITE);
+   IsisError CreateXrf(const _TCHAR *fname);
+   IsisError CreateXrf(const _TCHAR *fname, long nextMfn);
+  ~XrfFile();
+   int  GetMfp(long mfn, long& mfb, int& mfp);
+   void PutMfp(long mfn, long mfb, int mfp, int status);
+};
+ 
+//////////////////////////////
+ 
+inline XrfFile::XrfFile()  :
+// Makes a XrfFile object that isn't connected to a file
+   xrf_(new XRF),
+   xrfa_(new XRFA)
+{
+   last_block_ = -1;   // Not yet initialize
+   // Nothing more to do
+}
+ 
+//////////////////////////////
+ 
+ 
+inline XrfFile::~XrfFile()
+{
+   if (xrf_)  delete xrf_;
+   if (xrfa_) delete xrfa_;
+}
+ 
+//////////////////////////////
+ 
+inline void XrfFile::ReadXrf(long b)
+// Reads block # b into xrf_ buffer
+{
+	assert(b>0);
+    ReadBlk(xrf_, b);
+    UnPack();
+}
+ 
+//////////////////////////////
+ 
+inline void XrfFile::WriteXrf(long b)
+// Writes the content of xrf_ buffer as block number b
+{
+	assert(b>0);
+    Pack();
+    WriteBlk(xrf_, b);
+}
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/configure
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/configure	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/configure	(revision 16561)
@@ -0,0 +1,2828 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_default_prefix=../..
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Some influential environment variables:
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_aux_dir=
+for ac_dir in ../.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in ../.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in ../.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5
+echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5
+echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	    break 3
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+          ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[	 `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output.  A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/configure.in
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/configure.in	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/configure.in	(revision 16561)
@@ -0,0 +1,19 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT()
+
+dnl  default installation
+dnl will be overridden by command-line args from packages/configure
+AC_PREFIX_DEFAULT(../..)
+
+dnl this uses the config.guess, config.sub and install-sh from gsdl.
+AC_CONFIG_AUX_DIR(../..)
+
+dnl compiler - need c++
+AC_PROG_CXX
+
+dnl Windows extension
+AC_EXEEXT
+
+AC_PROG_INSTALL
+
+AC_OUTPUT(Makefile)
Index: /gsdl/trunk/gs2build/packages/isis-gdl/mytchar.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/mytchar.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/mytchar.h	(revision 16561)
@@ -0,0 +1,35 @@
+/**********************************************************************
+ *
+ * mytchar.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef __MY_TCHAR__
+#define __MY_TCHAR__
+
+#include "tchar.h"
+
+#ifndef _WIN32
+typedef wchar_t TCHAR;
+#endif
+
+#endif
Index: /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.cpp
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.cpp	(revision 16561)
@@ -0,0 +1,33 @@
+/**********************************************************************
+ *
+ * stdafx.cpp
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+// stdafx.cpp : source file that includes just the standard includes
+// IsisGdl.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
Index: /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/stdafx.h	(revision 16561)
@@ -0,0 +1,40 @@
+/**********************************************************************
+ *
+ * stdafx.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+// #pragma once
+
+
+#include <iostream>
+#include <assert.h>
+#include "mytchar.h"
+#include "Compat.h"
+// TODO: reference additional headers your program requires here
+#include "IsisDef.h"
+
Index: /gsdl/trunk/gs2build/packages/isis-gdl/tchar.h
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/tchar.h	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/tchar.h	(revision 16561)
@@ -0,0 +1,1046 @@
+/**********************************************************************
+ *
+ * tchar.h
+ * Copyright (C) 2003  UNESCO
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+/***
+*tchar.h - definitions for generic international text functions
+*
+*       Copyright (c) 1991-1997, Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+*       Definitions for generic international functions, mostly defines
+*       which map string/formatted-io/ctype functions to char, wchar_t, or
+*       MBCS versions.  To be used for compatibility between single-byte,
+*       multi-byte and Unicode text models.
+*
+*       [Public]
+*
+****/
+
+#if     _MSC_VER > 1000 /*IFSTRIP=IGN*/
+#pragma once
+#endif
+
+#ifndef _INC_TCHAR
+#define _INC_TCHAR
+
+#ifdef  _MSC_VER
+#pragma warning(disable:4514)       /* disable unwanted C++ /W4 warning */
+/* #pragma warning(default:4514) */ /* use this to reenable, if necessary */
+#endif  /* _MSC_VER */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+/* Define __cdecl for non-Microsoft compilers */
+
+#if     ( !defined(_MSC_VER) && !defined(__cdecl) )
+#define __cdecl
+#endif
+
+
+/* Define _CRTIMP */
+
+#ifndef _CRTIMP
+#ifdef  CRTDLL
+#define _CRTIMP __declspec(dllexport)
+#else   /* ndef CRTDLL */
+#ifdef  _DLL
+#define _CRTIMP __declspec(dllimport)
+#else   /* ndef _DLL */
+#define _CRTIMP
+#endif  /* _DLL */
+#endif  /* CRTDLL */
+#endif  /* _CRTIMP */
+
+
+#define _ftcscat    _tcscat
+#define _ftcschr    _tcschr
+#define _ftcscpy    _tcscpy
+#define _ftcscspn   _tcscspn
+#define _ftcslen    _tcslen
+#define _ftcsncat   _tcsncat
+#define _ftcsncpy   _tcsncpy
+#define _ftcspbrk   _tcspbrk
+#define _ftcsrchr   _tcsrchr
+#define _ftcsspn    _tcsspn
+#define _ftcsstr    _tcsstr
+#define _ftcstok    _tcstok
+
+#define _ftcsdup    _tcsdup
+#define _ftcsnset   _tcsnset
+#define _ftcsrev    _tcsrev
+#define _ftcsset    _tcsset
+
+#define _ftcscmp      _tcscmp
+#define _ftcsicmp     _tcsicmp
+#define _ftcsnccmp    _tcsnccmp
+#define _ftcsncmp     _tcsncmp
+#define _ftcsncicmp   _tcsncicmp
+#define _ftcsnicmp    _tcsnicmp
+
+#define _ftcscoll     _tcscoll
+#define _ftcsicoll    _tcsicoll
+#define _ftcsnccoll   _tcsnccoll
+#define _ftcsncoll    _tcsncoll
+#define _ftcsncicoll  _tcsncicoll
+#define _ftcsnicoll   _tcsnicoll
+
+/* Redundant "logical-character" mappings */
+
+#define _ftcsclen   _tcsclen
+#define _ftcsnccat  _tcsnccat
+#define _ftcsnccpy  _tcsnccpy
+#define _ftcsncset  _tcsncset
+
+#define _ftcsdec    _tcsdec
+#define _ftcsinc    _tcsinc
+#define _ftcsnbcnt  _tcsnbcnt
+#define _ftcsnccnt  _tcsnccnt
+#define _ftcsnextc  _tcsnextc
+#define _ftcsninc   _tcsninc
+#define _ftcsspnp   _tcsspnp
+
+#define _ftcslwr    _tcslwr
+#define _ftcsupr    _tcsupr
+
+#define _ftclen     _tclen
+#define _ftccpy     _tccpy
+#define _ftccmp     _tccmp
+
+
+#ifdef  _UNICODE
+
+#ifdef  __cplusplus
+}   /* ... extern "C" */
+#endif
+
+/* ++++++++++++++++++++ UNICODE ++++++++++++++++++++ */
+
+#include <wchar.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifndef _WCTYPE_T_DEFINED
+typedef wchar_t wint_t;
+typedef wchar_t wctype_t;
+#define _WCTYPE_T_DEFINED
+#endif
+
+#ifndef __TCHAR_DEFINED
+typedef wchar_t     _TCHAR;
+typedef wchar_t     _TSCHAR;
+typedef wchar_t     _TUCHAR;
+typedef wchar_t     _TXCHAR;
+typedef wint_t      _TINT;
+#define __TCHAR_DEFINED
+#endif
+
+#ifndef _TCHAR_DEFINED
+#if     !__STDC__
+typedef wchar_t     TCHAR;
+#endif
+#define _TCHAR_DEFINED
+#endif
+
+#define _TEOF       WEOF
+
+#define __T(x)      L ## x
+
+
+/* Program */
+
+#define _tmain      wmain
+#define _tWinMain   wWinMain
+#define _tenviron   _wenviron
+#define __targv     __wargv
+
+/* Formatted i/o */
+
+#define _tprintf    wprintf
+#define _ftprintf   fwprintf
+#define _stprintf   swprintf
+#define _sntprintf  _snwprintf
+#define _vtprintf   vwprintf
+#define _vftprintf  vfwprintf
+#define _vstprintf  vswprintf
+#define _vsntprintf _vsnwprintf
+#define _tscanf     wscanf
+#define _ftscanf    fwscanf
+#define _stscanf    swscanf
+
+
+/* Unformatted i/o */
+
+#define _fgettc     fgetwc
+#define _fgettchar  _fgetwchar
+#define _fgetts     fgetws
+#define _fputtc     fputwc
+#define _fputtchar  _fputwchar
+#define _fputts     fputws
+#define _gettc      getwc
+#define _gettchar   getwchar
+#define _getts      _getws
+#define _puttc      putwc
+#define _puttchar   putwchar
+#define _putts      _putws
+#define _ungettc    ungetwc
+
+
+/* String conversion functions */
+
+#define _tcstod     wcstod
+#define _tcstol     wcstol
+#define _tcstoul    wcstoul
+
+#define _itot       _itow
+#define _ltot       _ltow
+#define _ultot      _ultow
+#define _ttoi       _wtoi
+#define _ttol       _wtol
+
+#define _ttoi64     _wtoi64
+#define _i64tot     _i64tow
+#define _ui64tot    _ui64tow
+
+/* String functions */
+
+#define _tcscat     wcscat
+#define _tcschr     wcschr
+#define _tcscpy     wcscpy
+#define _tcscspn    wcscspn
+#define _tcslen     wcslen
+#define _tcsncat    wcsncat
+#define _tcsncpy    wcsncpy
+#define _tcspbrk    wcspbrk
+#define _tcsrchr    wcsrchr
+#define _tcsspn     wcsspn
+#define _tcsstr     wcsstr
+#define _tcstok     wcstok
+
+#define _tcsdup     _wcsdup
+#define _tcsnset    _wcsnset
+#define _tcsrev     _wcsrev
+#define _tcsset     _wcsset
+
+#define _tcscmp     wcscmp
+#define _tcsicmp    _wcsicmp
+#define _tcsnccmp   wcsncmp
+#define _tcsncmp    wcsncmp
+#define _tcsncicmp  _wcsnicmp
+#define _tcsnicmp   _wcsnicmp
+
+#define _tcscoll    wcscoll
+#define _tcsicoll   _wcsicoll
+#define _tcsnccoll  _wcsncoll
+#define _tcsncoll   _wcsncoll
+#define _tcsncicoll _wcsnicoll
+#define _tcsnicoll  _wcsnicoll
+
+
+/* Execute functions */
+
+#define _texecl     _wexecl
+#define _texecle    _wexecle
+#define _texeclp    _wexeclp
+#define _texeclpe   _wexeclpe
+#define _texecv     _wexecv
+#define _texecve    _wexecve
+#define _texecvp    _wexecvp
+#define _texecvpe   _wexecvpe
+
+#define _tspawnl    _wspawnl
+#define _tspawnle   _wspawnle
+#define _tspawnlp   _wspawnlp
+#define _tspawnlpe  _wspawnlpe
+#define _tspawnv    _wspawnv
+#define _tspawnve   _wspawnve
+#define _tspawnvp   _wspawnvp
+#define _tspawnvp   _wspawnvp
+#define _tspawnvpe  _wspawnvpe
+
+#define _tsystem    _wsystem
+
+
+/* Time functions */
+
+#define _tasctime   _wasctime
+#define _tctime     _wctime
+#define _tstrdate   _wstrdate
+#define _tstrtime   _wstrtime
+#define _tutime     _wutime
+#define _tcsftime   wcsftime
+
+
+/* Directory functions */
+
+#define _tchdir     _wchdir
+#define _tgetcwd    _wgetcwd
+#define _tgetdcwd   _wgetdcwd
+#define _tmkdir     _wmkdir
+#define _trmdir     _wrmdir
+
+
+/* Environment/Path functions */
+
+#define _tfullpath  _wfullpath
+#define _tgetenv    _wgetenv
+#define _tmakepath  _wmakepath
+#define _tputenv    _wputenv
+#define _tsearchenv _wsearchenv
+#define _tsplitpath _wsplitpath
+
+
+/* Stdio functions */
+
+#define _tfdopen    _wfdopen
+#define _tfsopen    _wfsopen
+#define _tfopen     _wfopen
+#define _tfreopen   _wfreopen
+#define _tperror    _wperror
+#define _tpopen     _wpopen
+#define _ttempnam   _wtempnam
+#define _ttmpnam    _wtmpnam
+
+
+/* Io functions */
+
+#define _taccess    _waccess
+#define _tchmod     _wchmod
+#define _tcreat     _wcreat
+#define _tfindfirst _wfindfirst
+#define _tfindfirsti64  _wfindfirsti64
+#define _tfindnext  _wfindnext
+#define _tfindnexti64   _wfindnexti64
+#define _tmktemp    _wmktemp
+#define _topen      _wopen
+#define _tremove    _wremove
+#define _trename    _wrename
+#define _tsopen     _wsopen
+#define _tunlink    _wunlink
+
+#define _tfinddata_t    _wfinddata_t
+#define _tfinddatai64_t _wfinddatai64_t
+
+
+/* Stat functions */
+
+#define _tstat      _wstat
+#define _tstati64   _wstati64
+
+
+/* Setlocale functions */
+
+#define _tsetlocale _wsetlocale
+
+
+/* Redundant "logical-character" mappings */
+
+#define _tcsclen    wcslen
+#define _tcsnccat   wcsncat
+#define _tcsnccpy   wcsncpy
+#define _tcsncset   _wcsnset
+
+#define _tcsdec     _wcsdec
+#define _tcsinc     _wcsinc
+#define _tcsnbcnt   _wcsncnt
+#define _tcsnccnt   _wcsncnt
+#define _tcsnextc   _wcsnextc
+#define _tcsninc    _wcsninc
+#define _tcsspnp    _wcsspnp
+
+#define _tcslwr     _wcslwr
+#define _tcsupr     _wcsupr
+#define _tcsxfrm    wcsxfrm
+
+
+#if     (__STDC__ || defined(_NO_INLINING)) && !defined(_M_M68K)
+#define _tclen(_pc) (1)
+#define _tccpy(_pc1,_cpc2) ((*(_pc1) = *(_cpc2)))
+#define _tccmp(_cpc1,_cpc2) ((*(_cpc1))-(*(_cpc2)))
+#else   /* __STDC__ */
+__inline size_t __cdecl _tclen(const wchar_t *_cpc) { return (_cpc,1); }
+__inline void __cdecl _tccpy(wchar_t *_pc1, const wchar_t *_cpc2) { *_pc1 = (wchar_t)*_cpc2; }
+__inline int __cdecl _tccmp(const wchar_t *_cpc1, const wchar_t *_cpc2) { return (int) ((*_cpc1)-(*_cpc2)); }
+#endif  /* __STDC__ */
+
+
+/* ctype functions */
+
+#define _istalnum   iswalnum
+#define _istalpha   iswalpha
+#define _istascii   iswascii
+#define _istcntrl   iswcntrl
+#define _istdigit   iswdigit
+#define _istgraph   iswgraph
+#define _istlower   iswlower
+#define _istprint   iswprint
+#define _istpunct   iswpunct
+#define _istspace   iswspace
+#define _istupper   iswupper
+#define _istxdigit  iswxdigit
+
+#define _totupper   towupper
+#define _totlower   towlower
+
+#define _istlegal(_c)   (1)
+#define _istlead(_c)    (0)
+#define _istleadbyte(_c)    (0)
+
+
+#if     (__STDC__ || defined(_NO_INLINING)) && !defined(_M_M68K)
+#define _wcsdec(_cpc1, _cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1)
+#define _wcsinc(_pc)    ((_pc)+1)
+#define _wcsnextc(_cpc) ((unsigned int) *(_cpc))
+#define _wcsninc(_pc, _sz) (((_pc)+(_sz)))
+#define _wcsncnt(_cpc, _sz) ((wcslen(_cpc)>_sz) ? _sz : wcslen(_cpc))
+#define _wcsspnp(_cpc1, _cpc2) ((*((_cpc1)+wcsspn(_cpc1,_cpc2))) ? ((_cpc1)+wcsspn(_cpc1,_cpc2)) : NULL)
+#else   /* __STDC__ */
+__inline wchar_t * __cdecl _wcsdec(const wchar_t * _cpc1, const wchar_t * _cpc2) { return (wchar_t *)((_cpc1)>=(_cpc2) ? NULL : ((_cpc2)-1)); }
+__inline wchar_t * __cdecl _wcsinc(const wchar_t * _pc) { return (wchar_t *)(_pc+1); }
+__inline unsigned int __cdecl _wcsnextc(const wchar_t * _cpc) { return (unsigned int)*_cpc; }
+__inline wchar_t * __cdecl _wcsninc(const wchar_t * _pc, size_t _sz) { return (wchar_t *)(_pc+_sz); }
+__inline size_t __cdecl _wcsncnt( const wchar_t * _cpc, size_t _sz) { size_t len; len = wcslen(_cpc); return (len>_sz) ? _sz : len; }
+__inline wchar_t * __cdecl _wcsspnp( const wchar_t * _cpc1, const wchar_t * _cpc2) { return (*(_cpc1 += wcsspn(_cpc1,_cpc2))!='\0') ? (wchar_t*)_cpc1 : NULL; }
+#endif  /* __STDC__ */
+
+
+#else   /* ndef _UNICODE */
+
+/* ++++++++++++++++++++ SBCS and MBCS ++++++++++++++++++++ */
+
+#ifdef  __cplusplus
+}   /* ... extern "C" */
+#endif
+
+#include <string.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#define _TEOF       EOF
+
+#define __T(x)      x
+
+
+/* Program */
+
+#define _tmain      main
+#define _tWinMain   WinMain
+#ifdef  _POSIX_
+#define _tenviron   environ
+#else
+#define _tenviron  _environ
+#endif
+#define __targv     __argv
+
+
+/* Formatted i/o */
+
+#define _tprintf    printf
+#define _ftprintf   fprintf
+#define _stprintf   sprintf
+#define _sntprintf  _snprintf
+#define _vtprintf   vprintf
+#define _vftprintf  vfprintf
+#define _vstprintf  vsprintf
+#define _vsntprintf _vsnprintf
+#define _tscanf     scanf
+#define _ftscanf    fscanf
+#define _stscanf    sscanf
+
+
+/* Unformatted i/o */
+
+#define _fgettc     fgetc
+#define _fgettchar  _fgetchar
+#define _fgetts     fgets
+#define _fputtc     fputc
+#define _fputtchar  _fputchar
+#define _fputts     fputs
+#define _gettc      getc
+#define _gettchar   getchar
+#define _getts      gets
+#define _puttc      putc
+#define _puttchar   putchar
+#define _putts      puts
+#define _ungettc    ungetc
+
+
+/* String conversion functions */
+
+#define _tcstod     strtod
+#define _tcstol     strtol
+#define _tcstoul    strtoul
+
+#define _itot       _itoa
+#define _ltot       _ltoa
+#define _ultot      _ultoa
+#define _ttoi       atoi
+#define _ttol       atol
+
+#define _ttoi64     _atoi64
+#define _i64tot     _i64toa
+#define _ui64tot    _ui64toa
+
+/* String functions */
+
+/* Note that _mbscat, _mbscpy and _mbsdup are functionally equivalent to 
+   strcat, strcpy and strdup, respectively. */
+
+#define _tcscat     strcat
+#define _tcscpy     strcpy
+#define _tcsdup     _strdup
+
+#define _tcslen     strlen
+#define _tcsxfrm    strxfrm
+
+
+/* Execute functions */
+
+#define _texecl     _execl
+#define _texecle    _execle
+#define _texeclp    _execlp
+#define _texeclpe   _execlpe
+#define _texecv     _execv
+#define _texecve    _execve
+#define _texecvp    _execvp
+#define _texecvpe   _execvpe
+
+#define _tspawnl    _spawnl
+#define _tspawnle   _spawnle
+#define _tspawnlp   _spawnlp
+#define _tspawnlpe  _spawnlpe
+#define _tspawnv    _spawnv
+#define _tspawnve   _spawnve
+#define _tspawnvp   _spawnvp
+#define _tspawnvpe  _spawnvpe
+
+#define _tsystem    system
+
+
+/* Time functions */
+
+#define _tasctime   asctime
+#define _tctime     ctime
+#define _tstrdate   _strdate
+#define _tstrtime   _strtime
+#define _tutime     _utime
+#define _tcsftime   strftime
+
+
+/* Directory functions */
+
+#define _tchdir     _chdir
+#define _tgetcwd    _getcwd
+#define _tgetdcwd   _getdcwd
+#define _tmkdir     _mkdir
+#define _trmdir     _rmdir
+
+
+/* Environment/Path functions */
+
+#define _tfullpath  _fullpath
+#define _tgetenv    getenv
+#define _tmakepath  _makepath
+#define _tputenv    _putenv
+#define _tsearchenv _searchenv
+#define _tsplitpath _splitpath
+
+
+/* Stdio functions */
+
+#ifdef  _POSIX_
+#define _tfdopen    fdopen
+#else
+#define _tfdopen    _fdopen
+#endif
+#define _tfsopen    _fsopen
+#define _tfopen     fopen
+#define _tfreopen   freopen
+#define _tperror    perror
+#define _tpopen     _popen
+#define _ttempnam   _tempnam
+#define _ttmpnam    tmpnam
+
+
+/* Io functions */
+
+#define _tchmod     _chmod
+#define _tcreat     _creat
+#define _tfindfirst _findfirst
+#define _tfindfirsti64  _findfirsti64
+#define _tfindnext  _findnext
+#define _tfindnexti64   _findnexti64
+#define _tmktemp    _mktemp
+
+#ifdef  _POSIX_
+#define _topen      open
+#define _taccess    access
+#else
+#define _topen      _open
+#define _taccess    _access
+#endif
+
+#define _tremove    remove
+#define _trename    rename
+#define _tsopen     _sopen
+#define _tunlink    _unlink
+
+#define _tfinddata_t    _finddata_t
+#define _tfinddatai64_t _finddatai64_t
+
+
+/* ctype functions */
+
+#define _istascii   isascii
+#define _istcntrl   iscntrl
+#define _istxdigit  isxdigit
+
+
+/* Stat functions */
+
+#define _tstat      _stat
+#define _tstati64   _stati64
+
+
+/* Setlocale functions */
+
+#define _tsetlocale setlocale
+
+
+#ifdef  _MBCS
+
+/* ++++++++++++++++++++ MBCS ++++++++++++++++++++ */
+
+#ifdef  __cplusplus
+}   /* ... extern "C" */
+#endif
+
+#include <mbstring.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef __TCHAR_DEFINED
+typedef char            _TCHAR;
+typedef signed char     _TSCHAR;
+typedef unsigned char   _TUCHAR;
+typedef unsigned char   _TXCHAR;
+typedef unsigned int    _TINT;
+#define __TCHAR_DEFINED
+#endif
+
+#ifndef _TCHAR_DEFINED
+#if     !__STDC__
+typedef char            TCHAR;
+#endif
+#define _TCHAR_DEFINED
+#endif
+
+
+#ifdef  _MB_MAP_DIRECT
+
+/* use mb functions directly - types must match */
+
+/* String functions */
+
+#define _tcschr     _mbschr
+#define _tcscspn    _mbscspn
+#define _tcsncat    _mbsnbcat
+#define _tcsncpy    _mbsnbcpy
+#define _tcspbrk    _mbspbrk
+#define _tcsrchr    _mbsrchr
+#define _tcsspn     _mbsspn
+#define _tcsstr     _mbsstr
+#define _tcstok     _mbstok
+
+#define _tcsnset    _mbsnbset
+#define _tcsrev     _mbsrev
+#define _tcsset     _mbsset
+
+#define _tcscmp     _mbscmp
+#define _tcsicmp    _mbsicmp
+#define _tcsnccmp   _mbsncmp
+#define _tcsncmp    _mbsnbcmp
+#define _tcsncicmp  _mbsnicmp
+#define _tcsnicmp   _mbsnbicmp
+
+#define _tcscoll    _mbscoll
+#define _tcsicoll   _mbsicoll
+#define _tcsnccoll  _mbsncoll
+#define _tcsncoll   _mbsnbcoll
+#define _tcsncicoll _mbsnicoll
+#define _tcsnicoll  _mbsnbicoll
+
+
+/* "logical-character" mappings */
+
+#define _tcsclen    _mbslen
+#define _tcsnccat   _mbsncat
+#define _tcsnccpy   _mbsncpy
+#define _tcsncset   _mbsnset
+
+
+/* MBCS-specific mappings */
+
+#define _tcsdec     _mbsdec
+#define _tcsinc     _mbsinc
+#define _tcsnbcnt   _mbsnbcnt
+#define _tcsnccnt   _mbsnccnt
+#define _tcsnextc   _mbsnextc
+#define _tcsninc    _mbsninc
+#define _tcsspnp    _mbsspnp
+
+#define _tcslwr     _mbslwr
+#define _tcsupr     _mbsupr
+
+#define _tclen      _mbclen
+#define _tccpy      _mbccpy
+
+#define _tccmp(_cpuc1,_cpuc2)   _tcsnccmp(_cpuc1,_cpuc2,1)
+
+
+#else /* _MB_MAP_DIRECT */
+
+#if     (__STDC__ || defined(_NO_INLINING)) && !defined(_M_M68K)
+
+/* use type-safe linked-in function thunks */
+
+/* String functions */
+
+_CRTIMP char * __cdecl _tcschr(const char *, unsigned int);
+_CRTIMP size_t __cdecl _tcscspn(const char *, const char *);
+_CRTIMP char * __cdecl _tcsncat(char *, const char *, size_t);
+_CRTIMP char * __cdecl _tcsncpy(char *, const char *, size_t);
+_CRTIMP char * __cdecl _tcspbrk(const char *, const char *);
+_CRTIMP char * __cdecl _tcsrchr(const char *, int);
+_CRTIMP size_t __cdecl _tcsspn(const char *, const char *);
+_CRTIMP char * __cdecl _tcsstr(const char *, const char *);
+_CRTIMP char * __cdecl _tcstok(char *, const char *);
+
+_CRTIMP char * __cdecl _tcsnset(char *, unsigned int, size_t);
+_CRTIMP char * __cdecl _tcsrev(char *);
+_CRTIMP char * __cdecl _tcsset(char *, unsigned int);
+
+_CRTIMP int __cdecl _tcscmp(const char *, const char *);
+_CRTIMP int __cdecl _tcsicmp(const char *, const char *);
+_CRTIMP int __cdecl _tcsnccmp(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsncmp(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsncicmp(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsnicmp(const char *, const char *, size_t);
+
+_CRTIMP int __cdecl _tcscoll(const char *, const char *);
+_CRTIMP int __cdecl _tcsicoll(const char *, const char *);
+_CRTIMP int __cdecl _tcsnccoll(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsncoll(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsncicoll(const char *, const char *, size_t);
+_CRTIMP int __cdecl _tcsnicoll(const char *, const char *, size_t);
+
+
+/* "logical-character" mappings */
+
+_CRTIMP size_t __cdecl _tcsclen(const char *);
+_CRTIMP char * __cdecl _tcsnccat(char *, const char *, size_t);
+_CRTIMP char * __cdecl _tcsnccpy(char *, const char *, size_t);
+_CRTIMP char * __cdecl _tcsncset(char *, unsigned int, size_t);
+
+
+/* MBCS-specific mappings */
+
+_CRTIMP char * __cdecl _tcsdec(const char *, const char *);
+_CRTIMP char * __cdecl _tcsinc(const char *);
+_CRTIMP size_t __cdecl _tcsnbcnt(const char *, size_t);
+_CRTIMP size_t __cdecl _tcsnccnt(const char *, size_t);
+_CRTIMP unsigned int __cdecl _tcsnextc (const char *);
+_CRTIMP char * __cdecl _tcsninc(const char *, size_t);
+_CRTIMP char * __cdecl _tcsspnp(const char *, const char *);
+
+_CRTIMP char * __cdecl _tcslwr(char *);
+_CRTIMP char * __cdecl _tcsupr(char *);
+
+_CRTIMP size_t __cdecl _tclen(const char *);
+_CRTIMP void __cdecl _tccpy(char *, const char *);
+
+
+#else   /* __STDC__ */
+
+/* the default: use type-safe inline function thunks */
+
+#define _PUC    unsigned char *
+#define _CPUC   const unsigned char *
+#define _PC     char *
+#define _CPC    const char *
+#define _UI     unsigned int
+
+
+/* String functions */
+
+__inline _PC _tcschr(_CPC _s1,_UI _c) {return (_PC)_mbschr((_CPUC)_s1,_c);}
+__inline size_t _tcscspn(_CPC _s1,_CPC _s2) {return _mbscspn((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcsncat(_PC _s1,_CPC _s2,size_t _n) {return (_PC)_mbsnbcat((_PUC)_s1,(_CPUC)_s2,_n);}
+__inline _PC _tcsncpy(_PC _s1,_CPC _s2,size_t _n) {return (_PC)_mbsnbcpy((_PUC)_s1,(_CPUC)_s2,_n);}
+__inline _PC _tcspbrk(_CPC _s1,_CPC _s2) {return (_PC)_mbspbrk((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcsrchr(_CPC _s1,_UI _c) {return (_PC)_mbsrchr((_CPUC)_s1,_c);}
+__inline size_t _tcsspn(_CPC _s1,_CPC _s2) {return _mbsspn((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcsstr(_CPC _s1,_CPC _s2) {return (_PC)_mbsstr((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcstok(_PC _s1,_CPC _s2) {return (_PC)_mbstok((_PUC)_s1,(_CPUC)_s2);}
+
+__inline _PC _tcsnset(_PC _s1,_UI _c,size_t _n) {return (_PC)_mbsnbset((_PUC)_s1,_c,_n);}
+__inline _PC _tcsrev(_PC _s1) {return (_PC)_mbsrev((_PUC)_s1);}
+__inline _PC _tcsset(_PC _s1,_UI _c) {return (_PC)_mbsset((_PUC)_s1,_c);}
+
+__inline int _tcscmp(_CPC _s1,_CPC _s2) {return _mbscmp((_CPUC)_s1,(_CPUC)_s2);}
+__inline int _tcsicmp(_CPC _s1,_CPC _s2) {return _mbsicmp((_CPUC)_s1,(_CPUC)_s2);}
+__inline int _tcsnccmp(_CPC _s1,_CPC _s2,size_t _n) {return _mbsncmp((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsncmp(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnbcmp((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsncicmp(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnicmp((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsnicmp(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnbicmp((_CPUC)_s1,(_CPUC)_s2,_n);}
+
+__inline int _tcscoll(_CPC _s1,_CPC _s2) {return _mbscoll((_CPUC)_s1,(_CPUC)_s2);}
+__inline int _tcsicoll(_CPC _s1,_CPC _s2) {return _mbsicoll((_CPUC)_s1,(_CPUC)_s2);}
+__inline int _tcsnccoll(_CPC _s1,_CPC _s2,size_t _n) {return _mbsncoll((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsncoll(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnbcoll((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsncicoll(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnicoll((_CPUC)_s1,(_CPUC)_s2,_n);}
+__inline int _tcsnicoll(_CPC _s1,_CPC _s2,size_t _n) {return _mbsnbicoll((_CPUC)_s1,(_CPUC)_s2,_n);}
+
+
+/* "logical-character" mappings */
+
+__inline size_t _tcsclen(_CPC _s1) {return _mbslen((_CPUC)_s1);}
+__inline _PC _tcsnccat(_PC _s1,_CPC _s2,size_t _n) {return (_PC)_mbsncat((_PUC)_s1,(_CPUC)_s2,_n);}
+__inline _PC _tcsnccpy(_PC _s1,_CPC _s2,size_t _n) {return (_PC)_mbsncpy((_PUC)_s1,(_CPUC)_s2,_n);}
+__inline _PC _tcsncset(_PC _s1,_UI _c,size_t _n) {return (_PC)_mbsnset((_PUC)_s1,_c,_n);}
+
+
+/* MBCS-specific mappings */
+
+__inline _PC _tcsdec(_CPC _s1,_CPC _s2) {return (_PC)_mbsdec((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcsinc(_CPC _s1) {return (_PC)_mbsinc((_CPUC)_s1);}
+__inline size_t _tcsnbcnt(_CPC _s1,size_t _n) {return _mbsnbcnt((_CPUC)_s1,_n);}
+__inline size_t _tcsnccnt(_CPC _s1,size_t _n) {return _mbsnccnt((_CPUC)_s1,_n);}
+__inline _PC _tcsninc(_CPC _s1,size_t _n) {return (_PC)_mbsninc((_CPUC)_s1,_n);}
+__inline _PC _tcsspnp(_CPC _s1,_CPC _s2) {return (_PC)_mbsspnp((_CPUC)_s1,(_CPUC)_s2);}
+__inline _PC _tcslwr(_PC _s1) {return (_PC)_mbslwr((_PUC)_s1);}
+__inline _PC _tcsupr(_PC _s1) {return (_PC)_mbsupr((_PUC)_s1);}
+
+__inline size_t _tclen(_CPC _s1) {return _mbclen((_CPUC)_s1);}
+__inline void _tccpy(_PC _s1,_CPC _s2) {_mbccpy((_PUC)_s1,(_CPUC)_s2); return;}
+
+
+/* inline helper */
+__inline _UI _tcsnextc(_CPC _s1) {_UI _n=0; if (_ismbblead((_UI)*(_PUC)_s1)) _n=((_UI)*_s1++)<<8; _n+=(_UI)*_s1; return(_n);}
+
+
+#endif  /* __STDC__ */
+
+#endif  /* _MB_MAP_DIRECT */
+
+
+/* MBCS-specific mappings */
+
+#define _tccmp(_cp1,_cp2)   _tcsnccmp(_cp1,_cp2,1)
+
+
+/* ctype functions */
+
+#define _istalnum   _ismbcalnum
+#define _istalpha   _ismbcalpha
+#define _istdigit   _ismbcdigit
+#define _istgraph   _ismbcgraph
+#define _istlegal   _ismbclegal
+#define _istlower   _ismbclower
+#define _istprint   _ismbcprint
+#define _istpunct   _ismbcpunct
+#define _istspace   _ismbcspace
+#define _istupper   _ismbcupper
+
+#define _totupper   _mbctoupper
+#define _totlower   _mbctolower
+
+#define _istlead    _ismbblead
+#define _istleadbyte    isleadbyte
+
+#else   /* !_MBCS */
+
+/* ++++++++++++++++++++ SBCS ++++++++++++++++++++ */
+
+
+#ifndef __TCHAR_DEFINED
+typedef char            _TCHAR;
+typedef signed char     _TSCHAR;
+typedef unsigned char   _TUCHAR;
+typedef char            _TXCHAR;
+typedef int             _TINT;
+#define __TCHAR_DEFINED
+#endif
+
+#ifndef _TCHAR_DEFINED
+#if     !__STDC__
+typedef char            TCHAR;
+#endif
+#define _TCHAR_DEFINED
+#endif
+
+
+/* String functions */
+
+#define _tcschr     strchr
+#define _tcscspn    strcspn
+#define _tcsncat    strncat
+#define _tcsncpy    strncpy
+#define _tcspbrk    strpbrk
+#define _tcsrchr    strrchr
+#define _tcsspn     strspn
+#define _tcsstr     strstr
+#define _tcstok     strtok
+
+#define _tcsnset    _strnset
+#define _tcsrev     _strrev
+#define _tcsset     _strset
+
+#define _tcscmp     strcmp
+#define _tcsicmp    _stricmp
+#define _tcsnccmp   strncmp
+#define _tcsncmp    strncmp
+#define _tcsncicmp  _strnicmp
+#define _tcsnicmp   _strnicmp
+
+#define _tcscoll    strcoll
+#define _tcsicoll   _stricoll
+#define _tcsnccoll  _strncoll
+#define _tcsncoll   _strncoll
+#define _tcsncicoll _strnicoll
+#define _tcsnicoll  _strnicoll
+
+
+/* "logical-character" mappings */
+
+#define _tcsclen    strlen
+#define _tcsnccat   strncat
+#define _tcsnccpy   strncpy
+#define _tcsncset   _strnset
+
+
+/* MBCS-specific functions */
+
+#define _tcsdec     _strdec
+#define _tcsinc     _strinc
+#define _tcsnbcnt   _strncnt
+#define _tcsnccnt   _strncnt
+#define _tcsnextc   _strnextc
+#define _tcsninc    _strninc
+#define _tcsspnp    _strspnp
+
+#define _tcslwr     _strlwr
+#define _tcsupr     _strupr
+#define _tcsxfrm    strxfrm
+
+#define _istlead(_c)    (0)
+#define _istleadbyte(_c)    (0)
+
+#if     (__STDC__ || defined(_NO_INLINING)) && !defined(_M_M68K)
+#define _tclen(_pc) (1)
+#define _tccpy(_pc1,_cpc2) (*(_pc1) = *(_cpc2))
+#define _tccmp(_cpc1,_cpc2) (((unsigned char)*(_cpc1))-((unsigned char)*(_cpc2)))
+#else   /* __STDC__ */
+__inline size_t __cdecl _tclen(const char *_cpc) { return (_cpc,1); }
+__inline void __cdecl _tccpy(char *_pc1, const char *_cpc2) { *_pc1 = *_cpc2; }
+__inline int __cdecl _tccmp(const char *_cpc1, const char *_cpc2) { return (int) (((unsigned char)*_cpc1)-((unsigned char)*_cpc2)); }
+#endif  /* __STDC__ */
+
+
+/* ctype-functions */
+
+#define _istalnum   isalnum
+#define _istalpha   isalpha
+#define _istdigit   isdigit
+#define _istgraph   isgraph
+#define _istlower   islower
+#define _istprint   isprint
+#define _istpunct   ispunct
+#define _istspace   isspace
+#define _istupper   isupper
+
+#define _totupper   toupper
+#define _totlower   tolower
+
+#define _istlegal(_c)   (1)
+
+
+/* the following is optional if functional versions are available */
+
+/* define NULL pointer value */
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL    0
+#else
+#define NULL    ((void *)0)
+#endif
+#endif
+
+
+#if     (__STDC__ || defined(_NO_INLINING)) && !defined(_M_M68K)
+#define _strdec(_cpc1, _cpc2) ((_cpc1)>=(_cpc2) ? NULL : (_cpc2)-1)
+#define _strinc(_pc)    ((_pc)+1)
+#define _strnextc(_cpc) ((unsigned int) *(_cpc))
+#define _strninc(_pc, _sz) (((_pc)+(_sz)))
+#define _strncnt(_cpc, _sz) ((strlen(_cpc)>_sz) ? _sz : strlen(_cpc))
+#define _strspnp(_cpc1, _cpc2) ((*((_cpc1)+strspn(_cpc1,_cpc2))) ? ((_cpc1)+strspn(_cpc1,_cpc2)) : NULL)
+#else   /* __STDC__ */
+__inline char * __cdecl _strdec(const char * _cpc1, const char * _cpc2) { return (char *)((_cpc1)>=(_cpc2) ? NULL : (_cpc2-1)); }
+__inline char * __cdecl _strinc(const char * _pc) { return (char *)(_pc+1); }
+__inline unsigned int __cdecl _strnextc(const char * _cpc) { return (unsigned int)*_cpc; }
+__inline char * __cdecl _strninc(const char * _pc, size_t _sz) { return (char *)(_pc+_sz); }
+__inline size_t __cdecl _strncnt( const char * _cpc, size_t _sz) { size_t len; len = strlen(_cpc); return (len>_sz) ? _sz : len; }
+__inline char * __cdecl _strspnp( const char * _cpc1, const char * _cpc2) { return (*(_cpc1 += strspn(_cpc1,_cpc2))!='\0') ? (char*)_cpc1 : NULL; }
+#endif  /* __STDC__ */
+
+
+#endif  /* _MBCS */
+
+#endif  /* _UNICODE */
+
+
+/* Generic text macros to be used with string literals and character constants.
+   Will also allow symbolic constants that resolve to same. */
+
+#define _T(x)       __T(x)
+#define _TEXT(x)    __T(x)
+
+
+#ifdef  __cplusplus
+}   /* ... extern "C" */
+#endif
+
+#endif  /* _INC_TCHAR */
Index: /gsdl/trunk/gs2build/packages/isis-gdl/win32.mak
===================================================================
--- /gsdl/trunk/gs2build/packages/isis-gdl/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/isis-gdl/win32.mak	(revision 16561)
@@ -0,0 +1,67 @@
+# ISIS-GDL
+
+GSDLHOME = ..\..
+
+CC = cl
+AR = lib
+
+DLL = 0
+DLLDEBUG = 0
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD 
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+HEADERS = AbstractIsisDb.h  CacheMan.h  Debug.h IErrors.h IsisUtil.h  \
+	XRFFile.h BlkFile.h Checking.h  Fdt.h IsisDb.h MFFile.h  mytchar.h \
+	CRC32.h Compat.h  File.h IsisDef.h  Master.h  stdafx.h \
+	CRCTab.h  DLLCode.h FileSystem.h  IsisTypes.h Unimarc.h  tchar.h
+
+
+SOURCES = CRC32.cpp Debug.cpp  FileSystem.cpp  IsisGdl.cpp Master.cpp  \
+	 stdafx.cpp CacheMan.cpp  Fdt.cpp  IErrors.cpp IsisUtil.cpp  \
+	 Unimarc.cpp Checking.cpp File.cpp IsisDb.cpp MFFile.cpp XRFFile.cpp
+
+OBJECTS = IsisGdl$o CacheMan$o Checking$o CRC32$o Fdt$o File$o Debug$o \
+	FileSystem$o IErrors$o IsisDb$o IsisUtil$o Master$o MFFile$o \
+	stdafx$o Unimarc$o XRFFile$o
+
+EXEC = IsisGdl$e
+
+DEFS = 
+
+all: $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *.obj
+	del $(EXEC)
+
+IsisGdl$e: $(OBJECTS) $(LIBS)
+	  $(LINK) $(OBJECTS) $(LIBS)
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/ACKNOWLEDGMENTS
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/ACKNOWLEDGMENTS	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/ACKNOWLEDGMENTS	(revision 16561)
@@ -0,0 +1,2 @@
+This product includes software developed by the
+Apache Software Foundation (http://www.apache.org/).
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/COPYING
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/COPYING	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/COPYING	(revision 16561)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 		59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/Counter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/Counter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/Counter.java	(revision 16561)
@@ -0,0 +1,73 @@
+/*
+ *    Counter.java
+ *    Copyright (C) 2000 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.io.*;
+
+/**
+ * Class that implements a simple counter.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class Counter implements Serializable {
+  
+  /** Integer value stored */
+    private int m_val = 1;
+  
+  /**
+   * Initializes the counter to 1
+   */
+  public Counter() {
+    
+    m_val = 1;
+  }
+  
+  /**
+   * Initializes the counter to the given value
+   */
+  public Counter(int val) {
+    
+    m_val = val;
+  }
+  
+  /**
+   * Increments the counter.
+   */
+  public void increment() {
+    
+    m_val++;
+  }
+  
+  /**
+   * Gets the value.
+   * @return the value
+   */
+  public int value() {
+    
+    return m_val;
+  }
+  
+  /**
+   * Returns string containing value.
+   */
+  public String toString() {
+    
+    return String.valueOf(m_val);
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/GermanStemmer.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/GermanStemmer.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/GermanStemmer.java	(revision 16561)
@@ -0,0 +1,309 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ *    "Apache Lucene" must not be used to endorse or promote products
+ *    derived from this software without prior written permission. For
+ *    written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    "Apache Lucene", nor may "Apache" appear in their name, without
+ *    prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+/**
+ * A stemmer for German words. The algorithm is based on the report
+ * "A Fast and Simple Stemming Algorithm for German Words" by Jörg
+ * Caumanns (joerg.caumanns@isst.fhg.de).
+ *
+ * Changed stem() from protected to public.
+ * Changed coding for umlaute to unicode.
+ *
+ * @author    Gerhard Schwarz
+ * @version   $Id$
+ */
+public class GermanStemmer extends Stemmer
+{
+    /**
+     * Buffer for the terms while stemming them.
+     */
+    private StringBuffer sb = new StringBuffer();
+
+    /**
+     * Indicates if a term is handled as a noun.
+     */
+    private boolean uppercase = false;
+
+    /**
+     * Amount of characters that are removed with <tt>substitute()</tt> while stemming.
+     */
+    private int substCount = 0;
+
+    /**
+     * Stemms the given term to an unique <tt>discriminator</tt>.
+     *
+     * @param term  The term that should be stemmed.
+     * @return      Discriminator for <tt>term</tt>
+     */
+    public String stem( String term )
+    {
+	// Mark a possible noun.
+	uppercase = Character.isUpperCase( term.charAt( 0 ) );
+	// Use lowercase for medium stemming.
+	term = term.toLowerCase();
+	if ( !isStemmable( term ) )
+	    return term;
+	// Reset the StringBuffer.
+	sb.delete( 0, sb.length() );
+	sb.insert( 0, term );
+	// Stemming starts here...
+	substitute( sb );
+	strip( sb );
+	optimize( sb );
+	resubstitute( sb );
+	removeParticleDenotion( sb );
+	return sb.toString();
+    }
+
+    /**
+     * Checks if a term could be stemmed.
+     *
+     * @return  true if, and only if, the given term consists in letters.
+     */
+    private boolean isStemmable( String term )
+    {
+	for ( int c = 0; c < term.length(); c++ ) {
+	    if ( !Character.isLetter( term.charAt( c ) ) ) return false;
+	}
+	return true;
+    }
+
+    /**
+     * suffix stripping (stemming) on the current term. The stripping is reduced
+     * to the seven "base" suffixes "e", "s", "n", "t", "em", "er" and * "nd",
+     * from which all regular suffixes are build of. The simplification causes
+     * some overstemming, and way more irregular stems, but still provides unique.
+     * discriminators in the most of those cases.
+     * The algorithm is context free, except of the length restrictions.
+     */
+    private void strip( StringBuffer buffer )
+    {
+	boolean doMore = true;
+	while ( doMore && buffer.length() > 3 ) {
+	    if ( ( buffer.length() + substCount > 5 ) &&
+		buffer.substring( buffer.length() - 2, buffer.length() ).equals( "nd" ) )
+	    {
+		buffer.delete( buffer.length() - 2, buffer.length() );
+	    }
+	    else if ( ( buffer.length() + substCount > 4 ) &&
+		buffer.substring( buffer.length() - 2, buffer.length() ).equals( "em" ) ) {
+		buffer.delete( buffer.length() - 2, buffer.length() );
+	    }
+	    else if ( ( buffer.length() + substCount > 4 ) &&
+		buffer.substring( buffer.length() - 2, buffer.length() ).equals( "er" ) ) {
+		buffer.delete( buffer.length() - 2, buffer.length() );
+	    }
+	    else if ( buffer.charAt( buffer.length() - 1 ) == 'e' ) {
+		buffer.deleteCharAt( buffer.length() - 1 );
+	    }
+	    else if ( buffer.charAt( buffer.length() - 1 ) == 's' ) {
+		buffer.deleteCharAt( buffer.length() - 1 );
+	    }
+	    else if ( buffer.charAt( buffer.length() - 1 ) == 'n' ) {
+		buffer.deleteCharAt( buffer.length() - 1 );
+	    }
+	    // "t" occurs only as suffix of verbs.
+	    else if ( buffer.charAt( buffer.length() - 1 ) == 't' && !uppercase ) {
+		buffer.deleteCharAt( buffer.length() - 1 );
+	    }
+	    else {
+		doMore = false;
+	    }
+	}
+    }
+
+    /**
+     * Does some optimizations on the term. This optimisations are
+     * contextual.
+     */
+    private void optimize( StringBuffer buffer )
+    {
+	// Additional step for female plurals of professions and inhabitants.
+	if ( buffer.length() > 5 && buffer.substring( buffer.length() - 5, buffer.length() ).equals( "erin*" ) ) {
+	    buffer.deleteCharAt( buffer.length() -1 );
+	    strip( buffer );
+	}
+	// Additional step for irregular plural nouns like "Matrizen -> Matrix".
+	if ( buffer.charAt( buffer.length() - 1 ) == ( 'z' ) ) {
+	    buffer.setCharAt( buffer.length() - 1, 'x' );
+	}
+    }
+
+    /**
+     * Removes a particle denotion ("ge") from a term.
+     */
+    private void removeParticleDenotion( StringBuffer buffer )
+    {
+	if ( buffer.length() > 4 ) {
+	    for ( int c = 0; c < buffer.length() - 3; c++ ) {
+		if ( buffer.substring( c, c + 4 ).equals( "gege" ) ) {
+		    buffer.delete( c, c + 2 );
+		    return;
+		}
+	    }
+	}
+    }
+
+    /**
+     * Do some substitutions for the term to reduce overstemming:
+     *
+     * - Substitute Umlauts with their corresponding vowel: äöü -> aou,
+     *   "ß" is substituted by "ss"
+     * - Substitute a second char of a pair of equal characters with
+     *   an asterisk: ?? -> ?*
+     * - Substitute some common character combinations with a token:
+     *   sch/ch/ei/ie/ig/st -> $/§/%/&/#/!
+     */
+    private void substitute( StringBuffer buffer )
+    {
+	substCount = 0;
+	for ( int c = 0; c < buffer.length(); c++ ) {
+	    // Replace the second char of a pair of the equal characters with an asterisk
+	    if ( c > 0 && buffer.charAt( c ) == buffer.charAt ( c - 1 )  ) {
+		buffer.setCharAt( c, '*' );
+	    }
+	    // Substitute Umlauts.
+	    else if ( buffer.charAt( c ) == '\u00E4' ) {
+		buffer.setCharAt( c, 'a' );
+	    }
+	    else if ( buffer.charAt( c ) == '\u00F6' ) {
+		buffer.setCharAt( c, 'o' );
+	    }
+	    else if ( buffer.charAt( c ) == '\u00FC' ) {
+		buffer.setCharAt( c, 'u' );
+	    }
+	    // Take care that at least one character is left left side from the current one
+	    if ( c < buffer.length() - 1 ) {
+		if ( buffer.charAt( c ) == '\u00DF' ) {
+		    buffer.setCharAt( c, 's' );
+		    buffer.insert( c + 1, 's' );
+		    substCount++;
+		}
+		// Masking several common character combinations with an token
+		else if ( ( c < buffer.length() - 2 ) && buffer.charAt( c ) == 's' &&
+		    buffer.charAt( c + 1 ) == 'c' && buffer.charAt( c + 2 ) == 'h' )
+		{
+		    buffer.setCharAt( c, '$' );
+		    buffer.delete( c + 1, c + 3 );
+		    substCount =+ 2;
+		}
+		else if ( buffer.charAt( c ) == 'c' && buffer.charAt( c + 1 ) == 'h' ) {
+		    buffer.setCharAt( c, '§' );
+		    buffer.deleteCharAt( c + 1 );
+		    substCount++;
+		}
+		else if ( buffer.charAt( c ) == 'e' && buffer.charAt( c + 1 ) == 'i' ) {
+		    buffer.setCharAt( c, '%' );
+		    buffer.deleteCharAt( c + 1 );
+		    substCount++;
+		}
+		else if ( buffer.charAt( c ) == 'i' && buffer.charAt( c + 1 ) == 'e' ) {
+		    buffer.setCharAt( c, '&' );
+		    buffer.deleteCharAt( c + 1 );
+		    substCount++;
+		}
+		else if ( buffer.charAt( c ) == 'i' && buffer.charAt( c + 1 ) == 'g' ) {
+		    buffer.setCharAt( c, '#' );
+		    buffer.deleteCharAt( c + 1 );
+		    substCount++;
+		}
+		else if ( buffer.charAt( c ) == 's' && buffer.charAt( c + 1 ) == 't' ) {
+		    buffer.setCharAt( c, '!' );
+		    buffer.deleteCharAt( c + 1 );
+		    substCount++;
+		}
+	    }
+	}
+    }
+
+    /**
+     * Undoes the changes made by substitute(). That are character pairs and
+     * character combinations. Umlauts will remain as their corresponding vowel,
+     * as "ß" remains as "ss".
+     */
+    private void resubstitute( StringBuffer buffer )
+    {
+	for ( int c = 0; c < buffer.length(); c++ ) {
+	    if ( buffer.charAt( c ) == '*' ) {
+		char x = buffer.charAt( c - 1 );
+		buffer.setCharAt( c, x );
+	    }
+	    else if ( buffer.charAt( c ) == '$' ) {
+		buffer.setCharAt( c, 's' );
+		buffer.insert( c + 1, new char[]{'c', 'h'}, 0, 2 );
+	    }
+	    else if ( buffer.charAt( c ) == '§' ) {
+		buffer.setCharAt( c, 'c' );
+		buffer.insert( c + 1, 'h' );
+	    }
+	    else if ( buffer.charAt( c ) == '%' ) {
+		buffer.setCharAt( c, 'e' );
+		buffer.insert( c + 1, 'i' );
+	    }
+	    else if ( buffer.charAt( c ) == '&' ) {
+		buffer.setCharAt( c, 'i' );
+		buffer.insert( c + 1, 'e' );
+	    }
+	    else if ( buffer.charAt( c ) == '#' ) {
+		buffer.setCharAt( c, 'i' );
+		buffer.insert( c + 1, 'g' );
+	    }
+	    else if ( buffer.charAt( c ) == '!' ) {
+		buffer.setCharAt( c, 's' );
+		buffer.insert( c + 1, 't' );
+	    }
+	}
+    }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/IteratedLovinsStemmer.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/IteratedLovinsStemmer.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/IteratedLovinsStemmer.java	(revision 16561)
@@ -0,0 +1,76 @@
+/*
+ *    IteratedLovinsStemmer.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+
+/**
+ * Implements the iterated version of the Lovins stemmer.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class IteratedLovinsStemmer extends LovinsStemmer {
+
+  /**
+   * Iterated stemming of the given word.
+   */
+  public String stem(String str) {
+
+    if (str.length() <= 2) {
+      return str;
+    }
+    String stemmed = super.stem(str);
+    while (!stemmed.equals(str)) {
+      str = stemmed;
+      stemmed = super.stem(stemmed);
+    }
+    return stemmed;
+  }
+
+  /**
+   * Stems text coming into stdin and writes it to stdout.
+   */
+  public static void main(String[] ops) {
+
+    IteratedLovinsStemmer ls = new IteratedLovinsStemmer();
+
+    try {
+      int num;
+      StringBuffer wordBuffer = new StringBuffer();
+      while ((num = System.in.read()) != -1) {
+	char c = (char)num;
+	if (((num >= (int)'A') && (num <= (int)'Z')) ||
+	    ((num >= (int)'a') && (num <= (int)'z'))) {
+	  wordBuffer.append(c);
+	} else {
+	  if (wordBuffer.length() > 0) {
+	    System.out.print(ls.stem(wordBuffer.toString().
+				     toLowerCase()));
+	    wordBuffer = new StringBuffer();
+	  }
+	  System.out.print(c);
+	}
+      }
+    } catch (Exception e) {
+      System.err.println(e.getMessage());
+    }
+  }
+}
+    
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAFilter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAFilter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAFilter.java	(revision 16561)
@@ -0,0 +1,1496 @@
+/*
+ *    KEAFilter.java
+ *    Copyright (C) 2000, 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+import weka.core.*;
+import weka.filters.*;
+import weka.classifiers.*;
+
+/**
+ * This filter converts the incoming data into data appropriate for
+ * keyphrase classification. It assumes that the dataset contains two
+ * string attributes. The first attribute should contain the text of a
+ * document. The second attribute should contain the keyphrases
+ * associated with that document (if present). 
+ *
+ * The filter converts every instance (i.e. document) into a set of
+ * instances, one for each word-based n-gram in the document. The
+ * string attribute representing the document is replaced by some
+ * numeric features, the estimated probability of each n-gram being a
+ * keyphrase, and the rank of this phrase in the document according to
+ * the probability.  Each new instances also has a class value
+ * associated with it. The class is "true" if the n-gram is a true
+ * keyphrase, and "false" otherwise. Of course, if the input document
+ * doesn't come with author-assigned keyphrases, the class values for
+ * that document will be missing.  
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+  */
+public class KEAFilter extends Filter implements OptionHandler {
+
+  /** Index of attribute containing the documents */
+  private int m_DocumentAtt = 0;
+
+  /** Index of attribute containing the keyphrases */
+  private int m_KeyphrasesAtt = 1;
+
+  /** The maximum length of phrases */
+  private int m_MaxPhraseLength = 3;
+
+  /** The minimum length of phrases */
+  private int m_MinPhraseLength = 1;
+
+  /** Is keyphrase frequency attribute being used? */
+  private boolean m_KFused = false;
+
+  /** Flag for debugging mode */
+  private boolean m_Debug = false;
+
+  /** Determines whether internal periods are allowed */
+  private boolean m_DisallowInternalPeriods = false;
+
+  /** The minimum number of occurences of a phrase */
+  private int m_MinNumOccur = 2;
+
+  /** The number of features describing a phrase */
+  private int m_NumFeatures = 2;
+
+  /* Indices of attributes in m_ClassifierData */
+  private int m_TfidfIndex = 0;
+  private int m_FirstOccurIndex = 1;
+  private int m_KeyFreqIndex = 2;
+
+  /** The punctuation filter used by this filter */
+  private KEAPhraseFilter m_PunctFilter = null;
+
+  /** The numbers filter used by this filter */
+  private NumbersFilter m_NumbersFilter = null;
+ 
+  /** The actual classifier used to compute probabilities */
+  private DistributionClassifier m_Classifier = null;
+
+  /** The dictionary containing the document frequencies */
+  private HashMap m_Dictionary = null;
+
+  /** The dictionary containing the keyphrases */
+  private HashMap m_KeyphraseDictionary = null;
+
+  /** The number of documents in the global frequencies corpus */
+  private int m_NumDocs = 0;
+
+  /** Template for the classifier data */
+  private Instances m_ClassifierData = null;
+
+  /** The stemmer to be used */
+  private Stemmer m_Stemmer = new IteratedLovinsStemmer();
+
+  /** The list of stop words to be used */
+  private Stopwords m_Stopwords = new StopwordsEnglish();
+
+  /** Determines whether check for proper nouns is performed */
+  private boolean m_CheckForProperNouns = true;
+
+  /**
+   * Get the M_CheckProperNouns value.
+   * @return the M_CheckProperNouns value.
+   */
+  public boolean getCheckForProperNouns() {
+    return m_CheckForProperNouns;
+  }
+
+  /**
+   * Set the M_CheckProperNouns value.
+   * @param newM_CheckProperNouns The new M_CheckProperNouns value.
+   */
+  public void setCheckForProperNouns(boolean newM_CheckProperNouns) {
+    this.m_CheckForProperNouns = newM_CheckProperNouns;
+  }
+  
+  /**
+   * Get the M_Stopwords value.
+   * @return the M_Stopwords value.
+   */
+  public Stopwords getStopwords() {
+    return m_Stopwords;
+  }
+
+  /**
+   * Set the M_Stopwords value.
+   * @param newM_Stopwords The new M_Stopwords value.
+   */
+  public void setStopwords(Stopwords newM_Stopwords) {
+    this.m_Stopwords = newM_Stopwords;
+  }
+
+  
+  /**
+   * Get the Stemmer value.
+   * @return the Stemmer value.
+   */
+  public Stemmer getStemmer() {
+
+    return m_Stemmer;
+  }
+
+  /**
+   * Set the Stemmer value.
+   * @param newStemmer The new Stemmer value.
+   */
+  public void setStemmer(Stemmer newStemmer) {
+
+    this.m_Stemmer = newStemmer;
+  }
+
+  /**
+   * Get the value of MinNumOccur.
+   *
+   * @return Value of MinNumOccur.
+   */
+  public int getMinNumOccur() {
+    
+    return m_MinNumOccur;
+  }
+  
+  /**
+   * Set the value of MinNumOccur.
+   *
+   * @param newMinNumOccur Value to assign to MinNumOccur.
+   */
+  public void setMinNumOccur(int newMinNumOccur) {
+    
+    m_MinNumOccur = newMinNumOccur;
+  }
+  
+  /**
+   * Get the value of MaxPhraseLength.
+   *
+   * @return Value of MaxPhraseLength.
+   */
+  public int getMaxPhraseLength() {
+    
+    return m_MaxPhraseLength;
+  }
+  
+  /**
+   * Set the value of MaxPhraseLength.
+   *
+   * @param newMaxPhraseLength Value to assign to MaxPhraseLength.
+   */
+  public void setMaxPhraseLength(int newMaxPhraseLength) {
+    
+    m_MaxPhraseLength = newMaxPhraseLength;
+  }
+  
+  /**
+   * Get the value of MinPhraseLength.
+   *
+   * @return Value of MinPhraseLength.
+   */
+  public int getMinPhraseLength() {
+    
+    return m_MinPhraseLength;
+  }
+  
+  /**
+   * Set the value of MinPhraseLength.
+   *
+   * @param newMinPhraseLength Value to assign to MinPhraseLength.
+   */
+  public void setMinPhraseLength(int newMinPhraseLength) {
+    
+    m_MinPhraseLength = newMinPhraseLength;
+  }
+
+  /**
+   * Returns the index of the stemmed phrases in the output ARFF file.
+   */
+  public int getStemmedPhraseIndex() {
+
+    return m_DocumentAtt;
+  }
+
+  /**
+   * Returns the index of the unstemmed phrases in the output ARFF file.
+   */
+  public int getUnstemmedPhraseIndex() {
+
+    return m_DocumentAtt + 1;
+  }
+
+  /**
+   * Returns the index of the phrases' probabilities in the output ARFF file.
+   */
+  public int getProbabilityIndex() {
+
+    int index = m_DocumentAtt + 4;
+
+    if (m_Debug) {
+      if (m_KFused) {
+	index++;
+      }
+    }
+    return index;
+  }
+
+  /**
+   * Returns the index of the phrases' ranks in the output ARFF file.
+   */
+  public int getRankIndex() {
+
+    return getProbabilityIndex() + 1;
+  }
+
+  /**
+   * Get the value of DocumentAtt.
+   *
+   * @return Value of DocumentAtt.
+   */
+  public int getDocumentAtt() {
+    
+    return m_DocumentAtt;
+  }
+  
+  /**
+   * Set the value of DocumentAtt.
+   *
+   * @param newDocumentAtt Value to assign to DocumentAtt.
+   */
+  public void setDocumentAtt(int newDocumentAtt) {
+    
+    m_DocumentAtt = newDocumentAtt;
+  }
+  
+  /**
+   * Get the value of KeyphraseAtt.
+   *
+   * @return Value of KeyphraseAtt.
+   */
+  public int getKeyphrasesAtt() {
+    
+    return m_KeyphrasesAtt;
+  }
+  
+  /**
+   * Set the value of KeyphrasesAtt.
+   *
+   * @param newKeyphrasesAtt Value to assign to KeyphrasesAtt.
+   */
+  public void setKeyphrasesAtt(int newKeyphrasesAtt) {
+    
+    m_KeyphrasesAtt = newKeyphrasesAtt;
+  }
+  
+  
+  /**
+   * Get the value of Debug.
+   *
+   * @return Value of Debug.
+   */
+  public boolean getDebug() {
+    
+    return m_Debug;
+  }
+  
+  /**
+   * Set the value of Debug.
+   *
+   * @param newDebug Value to assign to Debug.
+   */
+  public void setDebug(boolean newDebug) {
+    
+    m_Debug = newDebug;
+  }
+
+  /**
+   * Sets whether keyphrase frequency attribute is used.
+   */
+  public void setKFused(boolean flag) {
+
+    m_KFused = flag;
+    if (flag) {
+      m_NumFeatures = 3;
+    } else {
+      m_NumFeatures = 2;
+    }
+  }
+
+  /**
+   * Gets whether keyphrase frequency attribute is used.
+   */
+  public boolean getKFused() {
+
+    return m_KFused;
+  }
+
+  /**
+   * Get whether the supplied columns are to be processed
+   *
+   * @return true if the supplied columns won't be processed
+   */
+  public boolean getDisallowInternalPeriods() {
+
+    return m_DisallowInternalPeriods;
+  }
+
+  /**
+   * Set whether selected columns should be processed. If true the 
+   * selected columns won't be processed.
+   *
+   * @param invert the new invert setting
+   */
+  public void setDisallowInternalPeriods(boolean disallow) {
+
+    m_DisallowInternalPeriods = disallow;
+  }
+
+  /**
+   * Parses a given list of options controlling the behaviour of this object.
+   * Valid options are:<p>
+   *
+   * -K<br>
+   * Specifies whether keyphrase frequency statistic is used.<p>
+   *
+   * -M length<br>
+   * Sets the maximum phrase length (default: 3).<p>
+   *
+   * -L length<br>
+   * Sets the minimum phrase length (default: 1).<p>
+   *
+   * -D<br>
+   * Turns debugging mode on.<p>
+   *
+   * -I index<br>
+   * Sets the index of the attribute containing the documents (default: 0).<p>
+   *
+   * -J index<br>
+   * Sets the index of the attribute containing the keyphrases (default: 1).<p>
+   *
+   * -P<br>
+   * Disallow internal periods <p>
+   *
+   * -O number<br>
+   * The minimum number of times a phrase needs to occur (default: 2). <p>
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    setKFused(Utils.getFlag('K', options));
+    setDebug(Utils.getFlag('D', options));
+    String docAttIndexString = Utils.getOption('I', options);
+    if (docAttIndexString.length() > 0) {
+      setDocumentAtt(Integer.parseInt(docAttIndexString) - 1);
+    } else {
+      setDocumentAtt(0);
+    }
+    String keyphraseAttIndexString = Utils.getOption('J', options);
+    if (keyphraseAttIndexString.length() > 0) {
+      setKeyphrasesAtt(Integer.parseInt(keyphraseAttIndexString) - 1);
+    } else {
+      setKeyphrasesAtt(1);
+    }
+    String maxPhraseLengthString = Utils.getOption('M', options);
+    if (maxPhraseLengthString.length() > 0) {
+      setMaxPhraseLength(Integer.parseInt(maxPhraseLengthString));
+    } else {
+      setMaxPhraseLength(3);
+    }
+    String minPhraseLengthString = Utils.getOption('M', options);
+    if (minPhraseLengthString.length() > 0) {
+      setMinPhraseLength(Integer.parseInt(minPhraseLengthString));
+    } else {
+      setMinPhraseLength(1);
+    }
+    String minNumOccurString = Utils.getOption('O', options);
+    if (minNumOccurString.length() > 0) {
+      setMinNumOccur(Integer.parseInt(minNumOccurString));
+    } else {
+      setMinNumOccur(2);
+    }
+    setDisallowInternalPeriods(Utils.getFlag('P', options));
+   }
+
+  /**
+   * Gets the current settings of the filter.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [13];
+    int current = 0;
+
+    if (getKFused()) {
+      options[current++] = "-K";
+    }
+    if (getDebug()) {
+      options[current++] = "-D";
+    }
+    options[current++] = "-I"; 
+    options[current++] = "" + (getDocumentAtt() + 1);
+    options[current++] = "-J"; 
+    options[current++] = "" + (getKeyphrasesAtt() + 1);
+    options[current++] = "-M"; 
+    options[current++] = "" + (getMaxPhraseLength());
+    options[current++] = "-L"; 
+    options[current++] = "" + (getMinPhraseLength());
+    options[current++] = "-O"; 
+    options[current++] = "" + (getMinNumOccur());
+
+    if (getDisallowInternalPeriods()) {
+      options[current++] = "-P";
+    }
+
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+  /**
+   * Returns an enumeration describing the available options
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(7);
+
+    newVector.addElement(new Option(
+	      "\tSpecifies whether keyphrase frequency statistic is used.",
+              "K", 0, "-K"));
+    newVector.addElement(new Option(
+	      "\tSets the maximum phrase length (default: 3).",
+              "M", 1, "-M <length>"));
+    newVector.addElement(new Option(
+	      "\tSets the minimum phrase length (default: 1).",
+              "L", 1, "-L <length>"));
+    newVector.addElement(new Option(
+	      "\tTurns debugging mode on.",
+              "D", 0, "-D"));
+    newVector.addElement(new Option(
+	      "\tSets the index of the document attribute (default: 0).",
+              "I", 1, "-I"));
+    newVector.addElement(new Option(
+	      "\tSets the index of the keyphrase attribute (default: 1).",
+              "J", 1, "-J"));
+    newVector.addElement(new Option(
+	      "\tDisallow internal periods.",
+              "P", 0, "-P"));
+    newVector.addElement(new Option(
+	      "\tSet the minimum number of occurences (default: 2).",
+              "O", 1, "-O"));
+
+    return newVector.elements();
+  }
+
+  /**
+   * Returns a string describing this filter
+   *
+   * @return a description of the filter suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String globalInfo() {
+    return "Converts incoming data into data appropriate for " +
+      "keyphrase classification.";
+  }
+
+  /**
+   * Sets the format of the input instances.
+   *
+   * @param instanceInfo an Instances object containing the input
+   * instance structure (any instances contained in the object are
+   * ignored - only the structure is required).
+   * @return true if the outputFormat may be collected immediately 
+   */
+  public boolean setInputFormat(Instances instanceInfo) throws Exception {
+
+    if (instanceInfo.classIndex() >= 0) {
+      throw new Exception("Don't know what do to if class index set!");
+    }
+    if (!instanceInfo.attribute(m_KeyphrasesAtt).isString() ||
+	!instanceInfo.attribute(m_DocumentAtt).isString()) {
+      throw new Exception("Keyphrase attribute and document attribute " +
+			  "need to be string attributes.");
+    }
+    m_PunctFilter = new KEAPhraseFilter();
+    int[] arr = new int[1];
+    arr[0] = m_DocumentAtt;
+    m_PunctFilter.setAttributeIndicesArray(arr);
+    m_PunctFilter.setInputFormat(instanceInfo);
+    m_PunctFilter.setDisallowInternalPeriods(getDisallowInternalPeriods());
+    m_NumbersFilter = new NumbersFilter();
+    m_NumbersFilter.setInputFormat(m_PunctFilter.getOutputFormat());
+    super.setInputFormat(m_NumbersFilter.getOutputFormat());
+    return false;
+  }
+
+  /**
+   * Input an instance for filtering. Ordinarily the instance is processed
+   * and made available for output immediately. Some filters require all
+   * instances be read before producing output.
+   *
+   * @param instance the input instance
+   * @return true if the filtered instance may now be
+   * collected with output().
+   * @exception Exception if the input instance was not of the correct 
+   * format or if there was a problem with the filtering.
+   */
+  public boolean input(Instance instance) throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new Exception("No input instance format defined");
+    }
+    if (m_NewBatch) {
+      resetQueue();
+      m_NewBatch = false;
+    }
+
+    if (m_Debug) {
+      System.err.println("-- Reading instance");
+    }
+
+    m_PunctFilter.input(instance);
+    m_PunctFilter.batchFinished();
+    instance = m_PunctFilter.output();
+
+    m_NumbersFilter.input(instance);
+    m_NumbersFilter.batchFinished();
+    instance = m_NumbersFilter.output();
+    
+    if (m_Dictionary == null) {
+      bufferInput(instance);
+      return false;
+    } else {
+      FastVector vector = convertInstance(instance, false);
+      Enumeration enum = vector.elements();
+      while (enum.hasMoreElements()) {
+	Instance inst = (Instance)enum.nextElement();
+	push(inst);
+      }
+      return true;
+    }
+  }
+
+  /**
+   * Signify that this batch of input to the filter is finished. 
+   * If the filter requires all instances prior to filtering,
+   * output() may now be called to retrieve the filtered instances.
+   *
+   * @return true if there are instances pending output
+   * @exception Exception if no input structure has been defined
+   */
+  public boolean batchFinished() throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new Exception("No input instance format defined");
+    }
+    if (m_Dictionary == null) {
+      buildGlobalDictionaries();
+      buildClassifier();
+      convertPendingInstances();
+    } 
+    flushInput();
+    m_NewBatch = true;
+    return (numPendingOutput() != 0);
+  }
+
+  /**
+   * Builds the global dictionaries.
+   */
+  private void buildGlobalDictionaries() throws Exception {
+
+    if (m_Debug) {
+      System.err.println("--- Building global dictionaries");
+    }
+    
+    // Build dictionary of n-grams with associated
+    // document frequencies
+    m_Dictionary = new HashMap();
+    for (int i = 0; i < getInputFormat().numInstances(); i++) {
+      String str = getInputFormat().instance(i).stringValue(m_DocumentAtt);
+      HashMap hash = getPhrasesForDictionary(str);
+      Iterator it = hash.keySet().iterator();
+      while (it.hasNext()) {
+	String phrase = (String)it.next();
+	Counter counter = (Counter)m_Dictionary.get(phrase);
+	if (counter == null) {
+	  m_Dictionary.put(phrase, new Counter());
+	} else {
+	  counter.increment();
+	}
+      }
+    }
+    
+    if (m_KFused) {
+      
+      // Build dictionary of n-grams that occur as keyphrases
+      // with associated keyphrase frequencies
+      m_KeyphraseDictionary = new HashMap();
+      for (int i = 0; i < getInputFormat().numInstances(); i++) {
+	String str = getInputFormat().instance(i).stringValue(m_KeyphrasesAtt);
+	HashMap hash = getGivenKeyphrases(str, false);
+	if (hash != null) {
+	  Iterator it = hash.keySet().iterator();
+	  while (it.hasNext()) {
+	    String phrase = (String)it.next();
+	    Counter counter = (Counter)m_KeyphraseDictionary.get(phrase);
+	    if (counter == null) {
+	      m_KeyphraseDictionary.put(phrase, new Counter());
+	    } else {
+	      counter.increment();
+	    }
+	  }
+	}
+      }
+    } else {
+      m_KeyphraseDictionary = null;
+    }
+    
+    // Set the number of documents in the global corpus
+    m_NumDocs = getInputFormat().numInstances();
+  }
+ 
+  /**
+   * Builds the classifier.
+   */
+  private void buildClassifier() throws Exception {
+    
+    // Generate input format for classifier
+    FastVector atts = new FastVector();
+    for (int i = 0; i < getInputFormat().numAttributes(); i++) {
+      if (i == m_DocumentAtt) {
+	atts.addElement(new Attribute("TFxIDF"));
+	atts.addElement(new Attribute("First_occurrence"));
+	if (m_KFused) {
+	  atts.addElement(new Attribute("Keyphrase_frequency"));
+	}
+      } else if (i == m_KeyphrasesAtt) {
+	FastVector vals = new FastVector(2);
+	vals.addElement("False");
+	vals.addElement("True");
+	atts.addElement(new Attribute("Keyphrase?", vals));
+      } 
+    }
+    m_ClassifierData = new Instances("ClassifierData", atts, 0);
+    m_ClassifierData.setClassIndex(m_NumFeatures);
+    
+    if (m_Debug) {
+      System.err.println("--- Converting instances for classifier");
+    }
+    
+    // Convert pending input instances into data for classifier
+    for(int i = 0; i < getInputFormat().numInstances(); i++) {
+      Instance current = getInputFormat().instance(i);
+
+      // Get the key phrases for the document
+      String keyphrases = current.stringValue(m_KeyphrasesAtt);
+      HashMap hashKeyphrases = getGivenKeyphrases(keyphrases, false);
+      HashMap hashKeysEval = getGivenKeyphrases(keyphrases, true);
+
+      // Get the phrases for the document
+      HashMap hash = new HashMap();
+      int length = getPhrases(hash, current.stringValue(m_DocumentAtt));
+
+      // Compute the feature values for each phrase and
+      // add the instance to the data for the classifier
+      Iterator it = hash.keySet().iterator();
+      while (it.hasNext()) {
+	String phrase = (String)it.next();
+	FastVector phraseInfo = (FastVector)hash.get(phrase);
+	double[] vals =  featVals(phrase, phraseInfo, true,
+				  hashKeysEval, hashKeyphrases, length);
+	Instance inst = new Instance(current.weight(), vals);
+	m_ClassifierData.add(inst);
+      }
+    }
+    
+    if (m_Debug) {
+      System.err.println("--- Building classifier");
+    }
+    
+    // Build classifier
+    FilteredClassifier fclass = new FilteredClassifier();
+    fclass.setClassifier(new weka.classifiers.NaiveBayesSimple());
+    fclass.setFilter(new DiscretizeFilter());
+    m_Classifier = fclass;
+    m_Classifier.buildClassifier(m_ClassifierData);
+
+    if (m_Debug) {
+      System.err.println(m_Classifier);
+    }
+
+    // Save space
+    m_ClassifierData = new Instances(m_ClassifierData, 0);
+  }
+
+  /** 
+   * Conmputes the feature values for a given phrase.
+   */
+  private double[] featVals(String phrase, FastVector phraseInfo, 
+			    boolean training, HashMap hashKeysEval,
+			    HashMap hashKeyphrases, int length) {
+
+    // Compute feature values
+    Counter counterLocal = (Counter)phraseInfo.elementAt(1);
+    double[] newInst = new double[m_NumFeatures + 1];
+	  
+    // Compute TFxIDF
+    Counter counterGlobal = (Counter)m_Dictionary.get(phrase);
+    double localVal = counterLocal.value(), globalVal = 0;
+    if (counterGlobal != null) {
+      globalVal = counterGlobal.value();
+      if (training) {
+	globalVal = globalVal - 1;
+      }
+    }
+    
+    // Just devide by length to get approximation of probability
+    // that phrase in document is our phrase
+    newInst[m_TfidfIndex] = (localVal / ((double)length)) *
+      (-Math.log((globalVal + 1)/ ((double)m_NumDocs + 1)));
+	  
+    // Compute first occurrence
+    Counter counterFirst = (Counter)phraseInfo.elementAt(0);
+    newInst[m_FirstOccurIndex] = (double)counterFirst.value() /
+      (double)length;
+    
+    // Is keyphrase frequency attribute being used?
+    if (m_KFused) {
+      Counter keyphraseC = (Counter)m_KeyphraseDictionary.get(phrase);
+      if ((training) && (hashKeyphrases != null) &&
+	  (hashKeyphrases.containsKey(phrase))) {
+	newInst[m_KeyFreqIndex] = keyphraseC.value() - 1;
+      } else {
+	if (keyphraseC != null) {
+	  newInst[m_KeyFreqIndex] = keyphraseC.value();
+	} else {
+	  newInst[m_KeyFreqIndex] = 0;
+	}
+      }
+    }
+
+    // Compute class value
+    String phraseInEvalFormat = evalFormat((String)phraseInfo.elementAt(2));
+    if (hashKeysEval == null) { // no author-assigned keyphrases
+      newInst[m_NumFeatures] = Instance.missingValue();
+    } else if (!hashKeysEval.containsKey(phraseInEvalFormat)) {
+      newInst[m_NumFeatures] = 0; // No keyphrase
+    } else {
+      hashKeysEval.remove(phraseInEvalFormat);
+      newInst[m_NumFeatures] = 1; // Keyphrase
+    }
+    return newInst;
+  }
+
+  /**
+   * Sets output format and converts pending input instances.
+   */
+  private void convertPendingInstances() throws Exception {
+
+    if (m_Debug) {
+      System.err.println("--- Converting pending instances");
+    }
+    
+    // Create output format for filter
+    FastVector atts = new FastVector();
+    for (int i = 0; i < getInputFormat().numAttributes(); i++) {
+      if (i == m_DocumentAtt) {
+	atts.addElement(new Attribute("N-gram", null));
+	atts.addElement(new Attribute("N-gram-original", null));
+	atts.addElement(new Attribute("TFxIDF"));
+	atts.addElement(new Attribute("First_occurrence"));
+	if (m_Debug) {
+	  if (m_KFused) {
+	    atts.addElement(new Attribute("Keyphrase_frequency"));
+	  }
+	}
+	atts.addElement(new Attribute("Probability"));
+	atts.addElement(new Attribute("Rank"));
+      } else if (i == m_KeyphrasesAtt) {
+	FastVector vals = new FastVector(2);
+	vals.addElement("False");
+	vals.addElement("True");
+	atts.addElement(new Attribute("Keyphrase?", vals));
+      } else {
+	atts.addElement(getInputFormat().attribute(i));
+      }
+    }
+    Instances outFormat = new Instances("KEAdata", atts, 0);
+    setOutputFormat(outFormat);
+    
+    // Convert pending input instances into output data
+    for(int i = 0; i < getInputFormat().numInstances(); i++) {
+      Instance current = getInputFormat().instance(i);
+      FastVector vector = convertInstance(current, true);
+      Enumeration enum = vector.elements();
+      while (enum.hasMoreElements()) {
+	Instance inst = (Instance)enum.nextElement();
+	push(inst);
+      }
+    }
+  } 
+
+  /**
+   * Converts an instance.
+   */
+  private FastVector convertInstance(Instance instance, boolean training) 
+    throws Exception {
+
+    FastVector vector = new FastVector();
+
+    if (m_Debug) {
+      System.err.println("-- Converting instance");
+    }
+
+    // Get the key phrases for the document
+    HashMap hashKeyphrases = null;
+    HashMap hashKeysEval = null;
+    if (!instance.isMissing(m_KeyphrasesAtt)) {
+      String keyphrases = instance.stringValue(m_KeyphrasesAtt);
+      hashKeyphrases = getGivenKeyphrases(keyphrases, false);
+      hashKeysEval = getGivenKeyphrases(keyphrases, true);
+    }
+
+    // Get the phrases for the document
+    HashMap hash = new HashMap();
+    int length = getPhrases(hash, instance.stringValue(m_DocumentAtt));
+
+    // Compute number of extra attributes
+    int numFeatures = 5;
+    if (m_Debug) {
+      if (m_KFused) {
+	numFeatures = numFeatures + 1;
+      }
+    } 
+
+    // Set indices of key attributes
+    int phraseAttIndex = m_DocumentAtt;
+    int tfidfAttIndex = m_DocumentAtt + 2;
+    int distAttIndex = m_DocumentAtt + 3;
+    int probsAttIndex = m_DocumentAtt + numFeatures - 1;
+
+    // Go through the phrases and convert them into instances
+    Iterator it = hash.keySet().iterator();
+    while (it.hasNext()) {
+      String phrase = (String)it.next();
+      FastVector phraseInfo = (FastVector)hash.get(phrase);
+      double[] vals =  featVals(phrase, phraseInfo, training,
+				hashKeysEval, hashKeyphrases, length);
+      Instance inst = new Instance(instance.weight(), vals);
+      inst.setDataset(m_ClassifierData);
+
+      // Get probability of phrase being key phrase
+      double[] probs = m_Classifier.distributionForInstance(inst);
+      double prob = probs[1];
+
+      // Compute attribute values for final instance
+      double[] newInst = 
+	new double[instance.numAttributes() + numFeatures];
+      int pos = 0;
+      for (int i = 0; i < instance.numAttributes(); i++) {
+	if (i == m_DocumentAtt) {
+	    
+	  // Add phrase
+	  int index = outputFormatPeek().attribute(pos).
+	    addStringValue(phrase);
+	  newInst[pos++] = index;
+	  
+	  // Add original version
+	  index = outputFormatPeek().attribute(pos).
+	    addStringValue((String)phraseInfo.elementAt(2));
+	  newInst[pos++] = index;
+
+	  // Add TFxIDF
+	  newInst[pos++] = inst.value(m_TfidfIndex);
+	    
+	  // Add distance
+	  newInst[pos++] = inst.value(m_FirstOccurIndex);
+
+	    // Add other features
+	  if (m_Debug) {
+	    if (m_KFused) {
+	      newInst[pos++] = inst.value(m_KeyFreqIndex);
+	    }
+	  }
+
+	  // Add probability 
+	  probsAttIndex = pos;
+	  newInst[pos++] = prob;
+
+	  // Set rank to missing (computed below)
+	  newInst[pos++] = Instance.missingValue();
+	} else if (i == m_KeyphrasesAtt) {
+	  newInst[pos++] = inst.classValue();
+	} else {
+	  newInst[pos++] = instance.value(i);
+	}
+      }
+      Instance ins = new Instance(instance.weight(), newInst);
+      ins.setDataset(outputFormatPeek());
+      vector.addElement(ins);
+    }
+
+    // Add dummy instances for keyphrases that don't occur
+    // in the document
+    if (hashKeysEval != null) {
+      Iterator phrases = hashKeysEval.keySet().iterator();
+      while (phrases.hasNext()) {
+	String phrase = (String)phrases.next();
+	double[] newInst = 
+	  new double[instance.numAttributes() + numFeatures];
+	int pos = 0;
+	for (int i = 0; i < instance.numAttributes(); i++) {
+	  if (i == m_DocumentAtt) {
+	    
+	    // Add phrase
+	    int index = outputFormatPeek().attribute(pos).
+	      addStringValue(phrase);
+	    newInst[pos++] = (double)index;
+	    
+	    // Add original version
+	    index = outputFormatPeek().attribute(pos).
+	      addStringValue((String)hashKeysEval.get(phrase));
+	    newInst[pos++] = (double)index;
+	    
+	    // Add TFxIDF
+	    newInst[pos++] = Instance.missingValue();
+	    
+	    // Add distance
+	    newInst[pos++] = Instance.missingValue();
+	      
+	    // Add other features
+	    if (m_Debug) {
+	      if (m_KFused) {
+		newInst[pos++] = Instance.missingValue();
+	      }
+	    }
+
+	    // Add probability and rank
+	    newInst[pos++] = -Double.MAX_VALUE;
+	    newInst[pos++] = Instance.missingValue();
+	  } else if (i == m_KeyphrasesAtt) {
+	    newInst[pos++] = 1; // Keyphrase
+	  } else {
+	    newInst[pos++] = instance.value(i);
+	  } 
+	}
+	Instance inst = new Instance(instance.weight(), newInst);
+	inst.setDataset(outputFormatPeek());
+	vector.addElement(inst);
+      }
+    }
+
+    // Sort phrases according to their distance (stable sort)
+    double[] vals = new double[vector.size()];
+    for (int i = 0; i < vals.length; i++) {
+      vals[i] = ((Instance)vector.elementAt(i)).value(distAttIndex);
+    }
+    FastVector newVector = new FastVector(vector.size());
+    int[] sortedIndices = Utils.stableSort(vals);
+    for (int i = 0; i < vals.length; i++) {
+      newVector.addElement(vector.elementAt(sortedIndices[i]));
+    }
+    vector = newVector;
+
+    // Sort phrases according to their tfxidf value (stable sort)
+    for (int i = 0; i < vals.length; i++) {
+      vals[i] = -((Instance)vector.elementAt(i)).value(tfidfAttIndex);
+    }
+    newVector = new FastVector(vector.size());
+    sortedIndices = Utils.stableSort(vals);
+    for (int i = 0; i < vals.length; i++) {
+      newVector.addElement(vector.elementAt(sortedIndices[i]));
+    }
+    vector = newVector;
+
+    // Sort phrases according to their probability (stable sort)
+    for (int i = 0; i < vals.length; i++) {
+      vals[i] = 1 - ((Instance)vector.elementAt(i)).value(probsAttIndex);
+    }
+    newVector = new FastVector(vector.size());
+    sortedIndices = Utils.stableSort(vals);
+    for (int i = 0; i < vals.length; i++) {
+      newVector.addElement(vector.elementAt(sortedIndices[i]));
+    }
+    vector = newVector;
+
+    // Compute rank of phrases. Check for subphrases that are ranked
+    // lower than superphrases and assign probability -1 and set the
+    // rank to Integer.MAX_VALUE
+    int rank = 1;
+    for (int i = 0; i < vals.length; i++) {
+      Instance currentInstance = (Instance)vector.elementAt(i);
+
+      // Short cut: if phrase very unlikely make rank very low and continue
+      if (Utils.grOrEq(vals[i], 1.0)) {
+	currentInstance.setValue(probsAttIndex + 1, Integer.MAX_VALUE);
+	continue;
+      }
+
+      // Otherwise look for super phrase starting with first phrase
+      // in list that has same probability, TFxIDF value, and distance as
+      // current phrase. We do this to catch all superphrases
+      // that have same probability, TFxIDF value and distance as current phrase.
+      int startInd = i;
+      while (startInd < vals.length) {
+	Instance inst = (Instance)vector.elementAt(startInd);
+	if ((inst.value(tfidfAttIndex) != 
+	     currentInstance.value(tfidfAttIndex)) ||
+	    (inst.value(probsAttIndex) != 
+	     currentInstance.value(probsAttIndex)) ||
+	    (inst.value(distAttIndex) !=
+	     currentInstance.value(distAttIndex))) {
+	  break;
+	}
+	startInd++;
+      }
+      String val = currentInstance.stringValue(phraseAttIndex);
+      boolean foundSuperphrase = false;
+      for (int j = startInd - 1; j >= 0; j--) {
+	if (j != i) {
+	  Instance candidate = (Instance)vector.elementAt(j);
+	  String potSuperphrase = candidate.stringValue(phraseAttIndex);
+	  if (val.length() <= potSuperphrase.length()) {
+	    if (KEAFilter.contains(val, potSuperphrase)) {
+	      foundSuperphrase = true;
+	      break;
+	    }
+	  }
+	}
+      }
+      if (foundSuperphrase) {
+	currentInstance.setValue(probsAttIndex + 1, Integer.MAX_VALUE);
+      } else {
+	currentInstance.setValue(probsAttIndex + 1, rank++);
+      }
+    }
+    return vector;
+  }
+    
+  /** 
+   * Checks whether one phrase is a subphrase of another phrase.
+   */
+  private static boolean contains(String sub, String sup) {
+
+    int i = 0;
+    while (i + sub.length() - 1 < sup.length()) {
+      int j;
+      for (j = 0; j < sub.length(); j++) {
+	if (sub.charAt(j) != sup.charAt(i + j)) {
+	  break;
+	}
+      }
+      if (j == sub.length()) {
+	if ((i + j) < sup.length()) {
+	  if (sup.charAt(i + j) == ' ') {
+	    return true;
+	  } else {
+	    return false;
+	  }
+	} else {
+	  return true;
+	}
+      }
+
+      // Skip forward to next space
+      do {
+	i++;
+      } while ((i < sup.length()) && (sup.charAt(i) != ' '));
+      i++;
+    }
+    return false;
+  }
+
+  /**
+   * Returns a hashtable. Fills the hashtable
+   * with the stemmed n-grams occuring in the given string
+   * (as keys) and the number of times it occurs.
+   */
+  private HashMap getPhrasesForDictionary(String str) {
+
+    String[] buffer = new String[m_MaxPhraseLength];
+    HashMap hash = new HashMap();
+
+    StringTokenizer tok = new StringTokenizer(str, "\n");
+    while (tok.hasMoreTokens()) {
+      String phrase = tok.nextToken();
+      int numSeen = 0;
+      StringTokenizer wordTok = new StringTokenizer(phrase, " ");
+      while (wordTok.hasMoreTokens()) {
+	String word = wordTok.nextToken();
+	
+	// Store word in buffer
+	for (int i = 0; i < m_MaxPhraseLength - 1; i++) {
+	  buffer[i] = buffer[i + 1];
+	}
+	buffer[m_MaxPhraseLength - 1] = word;
+	
+	// How many are buffered?
+	numSeen++;
+	if (numSeen > m_MaxPhraseLength) {
+	  numSeen = m_MaxPhraseLength;
+	}
+	
+	// Don't consider phrases that end with a stop word
+	if (m_Stopwords.isStopword(buffer[m_MaxPhraseLength - 1])) {
+	  continue;
+	}
+	
+	// Loop through buffer and add phrases to hashtable
+	StringBuffer phraseBuffer = new StringBuffer();
+	for (int i = 1; i <= numSeen; i++) {
+	  if (i > 1) {
+	    phraseBuffer.insert(0, ' ');
+	  }
+	  phraseBuffer.insert(0, buffer[m_MaxPhraseLength - i]);
+	  
+	  // Don't consider phrases that begin with a stop word
+	  if ((i > 1) && 
+	      (m_Stopwords.isStopword(buffer[m_MaxPhraseLength - i]))) {
+	    continue;
+	  }
+	  
+	  // Only consider phrases with minimum length
+	  if (i >= m_MinPhraseLength) {
+	    
+	    // Stem string
+	    String orig = phraseBuffer.toString();
+	    String internal = internalFormat(orig);
+	    Counter count = (Counter)hash.get(internal);
+	    if (count == null) {
+	      hash.put(internal, new Counter());
+	    } else {
+	      count.increment();
+	    }
+	    
+	    // Add components if phrase is single word before
+	    // conversion into internal format (i.e. error-correcting)
+	    /*if ((orig.indexOf(' ') == -1) &&
+	      (internal.indexOf(' ') != -1)) {
+	      StringTokenizer tokW = new StringTokenizer(internal, " ");
+	      while (tokW.hasMoreTokens()) {
+	      String comp = (String)tokW.nextToken();
+	      Counter countW = (Counter)hash.get(comp);
+	      if (countW == null) {
+	      hash.put(comp, new Counter());
+	      } else {
+	      countW.increment();
+	      }
+	      }
+	      }*/
+	  }
+	}
+      }
+    }
+    return hash;
+  }
+
+  /**
+   * Expects an empty hashtable. Fills the hashtable
+   * with the stemmed n-grams occuring in the given string
+   * (as keys). Stores the position, the number of occurences,
+   * and the most commonly occurring orgininal version of
+   * each n-gram.
+   *
+   * N-grams that occur less than m_MinNumOccur are not used.
+   *
+   * Returns the total number of words (!) in the string.
+   */
+  private int getPhrases(HashMap hash, String str) {
+
+    String[] buffer = new String[m_MaxPhraseLength];
+    
+    StringTokenizer tok = new StringTokenizer(str, "\n");
+    int pos = 1; 
+    while (tok.hasMoreTokens()) {
+      String phrase = tok.nextToken();
+      int numSeen = 0;
+      StringTokenizer wordTok = new StringTokenizer(phrase, " ");
+      while (wordTok.hasMoreTokens()) {
+	String word = wordTok.nextToken();
+	
+	// Store word in buffer
+	for (int i = 0; i < m_MaxPhraseLength - 1; i++) {
+	  buffer[i] = buffer[i + 1];
+	}
+	buffer[m_MaxPhraseLength - 1] = word;
+	
+	// How many are buffered?
+	numSeen++;
+	if (numSeen > m_MaxPhraseLength) {
+	  numSeen = m_MaxPhraseLength;
+	}
+	
+	// Don't consider phrases that end with a stop word
+	if (m_Stopwords.isStopword(buffer[m_MaxPhraseLength - 1])) {
+	  pos++;
+	  continue;
+	}
+	
+	// Loop through buffer and add phrases to hashtable
+	StringBuffer phraseBuffer = new StringBuffer();
+	for (int i = 1; i <= numSeen; i++) {
+	  if (i > 1) {
+	    phraseBuffer.insert(0, ' ');
+	  }
+	  phraseBuffer.insert(0, buffer[m_MaxPhraseLength - i]);
+	  
+	  // Don't consider phrases that begin with a stop word
+	  if ((i > 1) && 
+	      (m_Stopwords.isStopword(buffer[m_MaxPhraseLength - i]))) {
+	    continue;
+	  }
+	  
+	  // Only consider phrases with minimum length
+	  if (i >= m_MinPhraseLength) {
+	    
+	    // Stem string
+	    String phrStr = phraseBuffer.toString();
+	    String internal = internalFormat(phrStr);
+	    FastVector vec = (FastVector)hash.get(internal);
+	    if (vec == null) {
+	      vec = new FastVector(3);
+	      
+	      // HashMap for storing all versions
+	      HashMap secHash = new HashMap();
+	      secHash.put(phrStr, new Counter());
+	      
+	      // Update hashtable with all the info
+	      vec.addElement(new Counter(pos + 1 - i));
+	      vec.addElement(new Counter());
+	      vec.addElement(secHash);
+	      hash.put(internal, vec);
+	    } else {
+	      
+	      // Update number of occurrences
+	      ((Counter)((FastVector)vec).elementAt(1)).increment();
+	      
+	      // Update hashtable storing different versions
+	      HashMap secHash = (HashMap)vec.elementAt(2);
+	      Counter count = (Counter)secHash.get(phrStr);
+	      if (count == null) {
+		secHash.put(phrStr, new Counter());
+	      } else {
+		count.increment();
+	      }
+	    }
+	  }
+	}
+	pos++;
+      }
+    }
+    
+    // Replace secondary hashtables with most commonly occurring
+    // version of each phrase (canonical) form. Delete all words
+    // that are proper nouns.
+    Iterator phrases = hash.keySet().iterator();
+    while (phrases.hasNext()) {
+      String phrase = (String)phrases.next();
+      FastVector info = (FastVector)hash.get(phrase);
+
+      // Occurring less than m_MinNumOccur?
+      if (((Counter)((FastVector)info).elementAt(1)).value() < m_MinNumOccur) {
+	phrases.remove();
+	continue;
+      }
+
+      // Get canonical form
+      String canForm = canonicalForm((HashMap)info.elementAt(2));
+      if (canForm == null) {
+	phrases.remove();
+      } else {
+	info.setElementAt(canForm, 2);
+      }
+    }
+    return pos;
+  }
+
+  /**
+   * Create canonical form of phrase.
+   */
+  private String canonicalForm(HashMap secHash) {
+
+    int max = 0; String bestVersion = null;
+    boolean allFullyHyphenated = true;
+    int num = 0;
+    Iterator versions = secHash.keySet().iterator();
+    while (versions.hasNext()) {
+      num++;
+      String version = (String)versions.next();
+
+      // Are all the words joined up?
+      if (!isFullyHyphenated(version)) {
+	allFullyHyphenated = false;
+      }
+
+      // Check for how often this version occurs
+      Counter count = (Counter)secHash.get(version);
+      if (count.value() > max) {
+	max = count.value();
+	bestVersion = version;
+      }
+    }
+    if ((getCheckForProperNouns()) && (num == 1) && properNoun(bestVersion)) {
+    //if (allProperNouns) {
+      return null;
+    } else {
+      if (isFullyHyphenated(bestVersion) &&
+	  !allFullyHyphenated) {
+	bestVersion = bestVersion.replace('-', ' ');
+      }
+      return bestVersion;
+    }
+  }
+
+  /**
+   * Checks whether the given phrase is
+   * fully hyphenated.
+   */
+  private boolean isFullyHyphenated(String str) {
+    
+    return (str.indexOf(' ') == -1);
+  }
+
+  /**
+   * Checks whether the given string is a 
+   * proper noun.
+   *
+   * @return true if it is a potential proper noun
+   */
+  private static boolean properNoun(String str) {
+
+    // Is it more than one word?
+    if (str.indexOf(' ') != -1) {
+      return false;
+    }
+
+    // Does it start with an upper-case character?
+    if (Character.isLowerCase(str.charAt(0))) {
+      return false;
+    } 
+
+    // Is there at least one character that's
+    // not upper-case?
+    for (int i = 1; i < str.length(); i++) {
+      /*if (Character.isUpperCase(str.charAt(i))) {
+	return false;
+	}*/
+      if (!Character.isUpperCase(str.charAt(i))) {
+	return true;
+      }
+    }
+    //return true;
+    return false;
+  }
+
+  /**
+   * Generates the evaluation format of a phrase.
+   */
+  private String evalFormat(String str) {
+    
+    return m_Stemmer.stemString(str);
+  }
+
+  /** 
+   * Generates the internal format of a phrase.
+   */
+  private String internalFormat(String str) {
+
+    // Remove some non-alphanumeric characters
+    str = str.replace('-', ' ');
+    str = str.replace('/', ' ');
+    str = str.replace('&', ' ');
+
+    // Stem string
+    return m_Stemmer.stemString(str);
+  }
+
+  /**
+   * Gets all the phrases in the given string and puts them into the
+   * hashtable.  Also stores the original version of the stemmed
+   * phrase in the hash table.  
+   */
+  private HashMap getGivenKeyphrases(String str,
+				       boolean forEval) {
+
+    FastVector vector = new FastVector();
+    HashMap hash = new HashMap();
+    
+    StringTokenizer tok = new StringTokenizer(str, "\n");
+    while (tok.hasMoreTokens()) {
+      String orig = tok.nextToken();
+      orig = orig.trim();
+      if (orig.length() > 0) {
+	String modified;
+	if (!forEval) {
+	  modified = internalFormat(orig);
+	} else {
+	  modified = evalFormat(orig);
+	}
+	if (!hash.containsKey(modified)) {
+	  hash.put(modified, orig);
+	} else {
+	  if (forEval) {
+	    System.err.println("WARNING: stem of author-assigned keyphrase " +
+			       orig + " matches existing stem (skipping it)!");
+	  }
+	}
+      }
+    }
+    if (hash.size() == 0) {
+      return null;
+    } else {
+      return hash;
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain arguments to the filter: use -h for help
+   */
+  public static void main(String [] argv) {
+    
+    try {
+      if (Utils.getFlag('b', argv)) {
+	Filter.batchFilterFile(new KEAFilter(), argv);
+      } else {
+	Filter.filterFile(new KEAFilter(), argv);
+      }
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAKeyphraseExtractor.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAKeyphraseExtractor.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAKeyphraseExtractor.java	(revision 16561)
@@ -0,0 +1,532 @@
+/*
+ *    KEAKeyphraseExtractor.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+import java.io.*;
+import java.util.*;
+import weka.core.*;
+import weka.filters.*;
+
+/**
+ * Extracts keyphrases from the documents in a given directory.
+ * Assumes that the file names for the documents end with ".txt".
+ * Puts extracted keyphrases into corresponding files ending with
+ * ".key" (if those are not already present). Optionally an encoding
+ * for the documents/keyphrases can be defined (e.g. for Chinese
+ * text). Documents for which ".key" exists, are used for evaluation.
+ *
+ * Valid options are:<p>
+ *
+ * -l "directory name"<br>
+ * Specifies name of directory.<p>
+ *
+ * -m "model name"<br>
+ * Specifies name of model.<p>
+ *
+ * -e "encoding"<br>
+ * Specifies encoding.<p>
+ *
+ * -n <br>
+ * Specifies number of phrases to be output (default: 5).<p>
+ *
+ * -d<br>
+ * Turns debugging mode on.<p>
+ *
+ * -a<br>
+ * Also write stemmed phrase and score into ".key" file.<p>
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class KEAKeyphraseExtractor implements OptionHandler {
+  
+  /** Name of directory */
+  String m_dirName = null;
+  
+  /** Name of model */
+  String m_modelName = null;
+
+  /** Encoding */
+  String m_encoding = "default";
+
+  /** Debugging mode? */
+  boolean m_debug = false;
+
+  /** The KEA filter object */
+  KEAFilter m_KEAFilter = null;
+  
+  /** The number of phrases to extract. */
+  int m_numPhrases = 5;
+
+  /** Also write stemmed phrase and score into .key file. */
+  boolean m_AdditionalInfo = false;
+  
+  /**
+   * Get the value of AdditionalInfo.
+   *
+   * @return Value of AdditionalInfo.
+   */
+  public boolean getAdditionalInfo() {
+    
+    return m_AdditionalInfo;
+  }
+  
+  /**
+   * Set the value of AdditionalInfo.
+   *
+   * @param newAdditionalInfo Value to assign to AdditionalInfo.
+   */
+  public void setAdditionalInfo(boolean newAdditionalInfo) {
+    
+    m_AdditionalInfo = newAdditionalInfo;
+  }
+  
+  /**
+   * Get the value of numPhrases.
+   *
+   * @return Value of numPhrases.
+   */
+  public int getNumPhrases() {
+    
+    return m_numPhrases;
+  }
+  
+  /**
+   * Set the value of numPhrases.
+   *
+   * @param newnumPhrases Value to assign to numPhrases.
+   */
+  public void setNumPhrases(int newnumPhrases) {
+    
+    m_numPhrases = newnumPhrases;
+  }
+  
+  /**
+   * Get the value of debug.
+   *
+   * @return Value of debug.
+   */
+  public boolean getDebug() {
+    
+    return m_debug;
+  }
+  
+  /**
+   * Set the value of debug.
+   *
+   * @param newdebug Value to assign to debug.
+   */
+  public void setDebug(boolean newdebug) {
+    
+    m_debug = newdebug;
+  }
+  
+  /**
+   * Get the value of encoding.
+   *
+   * @return Value of encoding.
+   */
+  public String getEncoding() {
+    
+    return m_encoding;
+  }
+  
+  /**
+   * Set the value of encoding.
+   *
+   * @param newencoding Value to assign to encoding.
+   */
+  public void setEncoding(String newencoding) {
+    
+    m_encoding = newencoding;
+  }
+  
+  /**
+   * Get the value of modelName.
+   *
+   * @return Value of modelName.
+   */
+  public String getModelName() {
+    
+    return m_modelName;
+  }
+  
+  /**
+   * Set the value of modelName.
+   *
+   * @param newmodelName Value to assign to modelName.
+   */
+  public void setModelName(String newmodelName) {
+    
+    m_modelName = newmodelName;
+  }
+  
+  /**
+   * Get the value of dirName.
+   *
+   * @return Value of dirName.
+   */
+  public String getDirName() {
+    
+    return m_dirName;
+  }
+  
+  /**
+   * Set the value of dirName.
+   *
+   * @param newdirName Value to assign to dirName.
+   */
+  public void setDirName(String newdirName) {
+    
+    m_dirName = newdirName;
+  }
+  
+  /**
+   * Parses a given list of options controlling the behaviour of this object.
+   * Valid options are:<p>
+   *
+   * -l "directory name"<br>
+   * Specifies name of directory.<p>
+   *
+   * -m "model name"<br>
+   * Specifies name of model.<p>
+   *
+   * -e "encoding"<br>
+   * Specifies encoding.<p>
+   *
+   * -n<br>
+   * Specifies number of phrases to be output (default: 5).<p>
+   *
+   * -d<br>
+   * Turns debugging mode on.<p>
+   *
+   * -a<br>
+   * Also write stemmed phrase and score into ".key" file.<p>
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    String dirName = Utils.getOption('l', options);
+    if (dirName.length() > 0) {
+      setDirName(dirName);
+    } else {
+      setDirName(null);
+      throw new Exception("Name of directory required argument.");
+    }
+    String modelName = Utils.getOption('m', options);
+    if (modelName.length() > 0) {
+      setModelName(modelName);
+    } else {
+      setModelName(null);
+      throw new Exception("Name of model required argument.");
+    }
+    String encoding = Utils.getOption('e', options);
+    if (encoding.length() > 0) {
+      setEncoding(encoding);
+    } else {
+      setEncoding("default");
+    }
+    String numPhrases = Utils.getOption('n', options);
+    if (numPhrases.length() > 0) {
+      setNumPhrases(Integer.parseInt(numPhrases));
+    } else {
+      setNumPhrases(5);
+    }
+    setDebug(Utils.getFlag('d', options));
+    setAdditionalInfo(Utils.getFlag('a', options));
+    Utils.checkForRemainingOptions(options);
+   }
+
+  /**
+   * Gets the current option settings.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [10];
+    int current = 0;
+
+    options[current++] = "-l"; 
+    options[current++] = "" + (getDirName());
+    options[current++] = "-m"; 
+    options[current++] = "" + (getModelName());
+    options[current++] = "-e"; 
+    options[current++] = "" + (getEncoding());
+    options[current++] = "-n"; 
+    options[current++] = "" + (getNumPhrases());
+    if (getDebug()) {
+      options[current++] = "-d";
+    }
+    if (getAdditionalInfo()) {
+      options[current++] = "-a";
+    }
+
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+  /**
+   * Returns an enumeration describing the available options.
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(6);
+
+    newVector.addElement(new Option(
+	      "\tSpecifies name of directory.",
+              "l", 1, "-l <directory name>"));
+    newVector.addElement(new Option(
+	      "\tSpecifies name of model.",
+              "m", 1, "-m <model name>"));
+    newVector.addElement(new Option(
+	      "\tSpecifies encoding.",
+              "e", 1, "-e <encoding>"));
+    newVector.addElement(new Option(
+	      "\tSpecifies number of phrases to be output (default: 5).",
+              "n", 1, "-n"));
+    newVector.addElement(new Option(
+	      "\tTurns debugging mode on.",
+              "d", 0, "-d"));
+    newVector.addElement(new Option(
+	      "\tAlso write stemmed phrase and score into \".key\" file.",
+              "a", 0, "-a"));
+
+    return newVector.elements();
+  }
+
+  /**
+   * Collects the stems of the file names.
+   */
+  public Hashtable collectStems() throws Exception {
+
+    Hashtable stems = new Hashtable();
+
+    try {
+      File dir = new File(m_dirName);
+      String[] files = dir.list();
+      for (int i = 0; i < files.length; i++) {
+	if (files[i].endsWith(".txt")) {
+	  String stem = files[i].substring(0, files[i].length() - 4);
+	  if (!stems.containsKey(stem)) {
+	    stems.put(stem, new Double(0));
+	  }
+	}
+      }
+    } catch (Exception e) {
+      throw new Exception("Problem opening directory " + m_dirName);
+    }
+    return stems;
+  }
+
+  /**
+   * Builds the model from the files
+   */
+  public void extractKeyphrases(Hashtable stems) throws Exception {
+    
+    Vector stats = new Vector();
+
+    // Check whether there is actually any data
+    if (stems.size() == 0) {
+      throw new Exception("Couldn't find any data!");
+    }
+
+    FastVector atts = new FastVector(2);
+    atts.addElement(new Attribute("doc", null));
+    atts.addElement(new Attribute("keyphrases", null));
+    Instances data = new Instances("keyphrase_training_data", atts, 0);
+
+    // Extract keyphrases
+    Enumeration elem = stems.keys();
+    while (elem.hasMoreElements()) {
+      String str = (String)elem.nextElement();
+      double[] newInst = new double[2];
+      try {
+	File txt = new File(m_dirName + "/" + str + ".txt");
+	InputStreamReader is;
+	if (!m_encoding.equals("default")) {
+	  is = new InputStreamReader(new FileInputStream(txt), m_encoding);
+	} else {
+	  is = new InputStreamReader(new FileInputStream(txt));
+	}
+	StringBuffer txtStr = new StringBuffer();
+	int c;
+	while ((c = is.read()) != -1) {
+	  txtStr.append((char)c);
+	}
+	newInst[0] = (double)data.attribute(0).addStringValue(txtStr.toString());
+      } catch (Exception e) {
+	if (m_debug) {
+	  System.err.println("Can't read document " + str + ".txt");
+	}
+	newInst[0] = Instance.missingValue();
+      }
+      try {
+	File key = new File(m_dirName + "/" + str + ".key");
+	InputStreamReader is; 
+	if (!m_encoding.equals("default")) {
+	  is = new InputStreamReader(new FileInputStream(key), m_encoding);
+	} else {
+	  is = new InputStreamReader(new FileInputStream(key));
+	}
+	StringBuffer keyStr = new StringBuffer();
+	int c;
+	while ((c = is.read()) != -1) {
+	  keyStr.append((char)c);
+	}      
+	newInst[1] = (double)data.attribute(1).addStringValue(keyStr.toString());
+      } catch (Exception e) {
+	if (m_debug) {
+	  System.err.println("No keyphrases for stem " + str + ".");
+	}
+	newInst[1] = Instance.missingValue();
+      }
+      data.add(new Instance(1.0, newInst));
+      m_KEAFilter.input(data.instance(0));
+      data = data.stringFreeStructure();
+      if (m_debug) {
+	System.err.println("-- Document: " + str);
+      }
+      Instance[] topRankedInstances = new Instance[m_numPhrases];
+      Instance inst;
+      while ((inst = m_KEAFilter.output()) != null) {
+	int index = (int)inst.value(m_KEAFilter.getRankIndex()) - 1;
+	if (index < m_numPhrases) {
+	  topRankedInstances[index] = inst;
+	}
+      }
+      if (m_debug) {
+	System.err.println("-- Keyphrases and feature values:");
+      }
+      FileOutputStream out = null;
+      PrintWriter printer = null; 
+      File key = new File(m_dirName + "/" + str + ".key");
+      if (!key.exists()) {
+	out = new FileOutputStream(m_dirName + "/" + str + ".key");
+	if (!m_encoding.equals("default")) {
+	  printer = new PrintWriter(new OutputStreamWriter(out, m_encoding));
+	} else {
+	  printer = new PrintWriter(out);
+	}
+      }
+      double numExtracted = 0, numCorrect = 0;
+      for (int i = 0; i < m_numPhrases; i++) {
+	if (topRankedInstances[i] != null) {
+	  if (!topRankedInstances[i].
+	      isMissing(topRankedInstances[i].numAttributes() - 1)) {
+	    numExtracted += 1.0;
+	  }
+	  if ((int)topRankedInstances[i].
+	      value(topRankedInstances[i].numAttributes() - 1) == 
+	      topRankedInstances[i].
+	      attribute(topRankedInstances[i].numAttributes() - 1).
+	      indexOfValue("True")) {
+	    numCorrect += 1.0;
+	  }
+	  if (printer != null) {
+	    printer.print(topRankedInstances[i].
+			  stringValue(m_KEAFilter.getUnstemmedPhraseIndex()));
+	    if (m_AdditionalInfo) {
+	      printer.print("\t");
+	      printer.print(topRankedInstances[i].
+			  stringValue(m_KEAFilter.getStemmedPhraseIndex()));
+	      printer.print("\t");
+	      printer.print(Utils.
+			    doubleToString(topRankedInstances[i].
+					   value(m_KEAFilter.
+						 getProbabilityIndex()), 4));
+	    }
+	    printer.println();
+	  }
+	  if (m_debug) {
+	    System.err.println(topRankedInstances[i]);
+	  }
+	}
+      }
+      if (numExtracted > 0) {
+	if (m_debug) {
+	  System.err.println("-- " + numCorrect + " correct");
+	}
+	stats.addElement(new Double(numCorrect));
+      }
+      if (printer != null) {
+	printer.flush();
+	printer.close();
+	out.close();
+      }
+    }
+    double[] st = new double[stats.size()];
+    for (int i = 0; i < stats.size(); i++) {
+      st[i] = ((Double)stats.elementAt(i)).doubleValue();
+    }
+    double avg = Utils.mean(st);
+    double stdDev = Math.sqrt(Utils.variance(st));
+    System.err.println("Avg. number of correct keyphrases: " +
+		       Utils.doubleToString(avg, 2) + " +/- " + 
+		       Utils.doubleToString(stdDev, 2));
+    System.err.println("Based on " + stats.size() + " documents");
+    m_KEAFilter.batchFinished();
+  }
+  
+  /** 
+   * Loads the extraction model from the file.
+   */
+  public void loadModel() throws Exception {
+    
+    BufferedInputStream inStream =
+      new BufferedInputStream(new FileInputStream(m_modelName));
+    ObjectInputStream in = new ObjectInputStream(inStream);
+    m_KEAFilter = (KEAFilter)in.readObject();
+    in.close();
+  }
+  
+  /**
+   * The main method.  
+   */
+  public static void main(String[] ops) {
+    
+    KEAKeyphraseExtractor kmb = new KEAKeyphraseExtractor();
+    try {
+      kmb.setOptions(ops);
+      System.err.print("Extracting keyphrases with options: ");
+      String[] optionSettings = kmb.getOptions();
+      for (int i = 0; i < optionSettings.length; i++) {
+	System.err.print(optionSettings[i] + " ");
+      }
+      System.err.println();
+      kmb.loadModel();
+      kmb.extractKeyphrases(kmb.collectStems());
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.err.println(e.getMessage());
+      System.err.println("\nOptions:\n");
+      Enumeration enum = kmb.listOptions();
+      while (enum.hasMoreElements()) {
+	Option option = (Option) enum.nextElement();
+	System.err.println(option.synopsis());
+	System.err.println(option.description());
+      }
+    }
+  }
+}
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAModelBuilder.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAModelBuilder.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAModelBuilder.java	(revision 16561)
@@ -0,0 +1,689 @@
+/*
+ *    KEAModelBuilder.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.io.*;
+import java.util.*;
+import weka.core.*;
+import weka.filters.*;
+
+/**
+ * Builds a keyphrase extraction model from the documents in a given
+ * directory.  Assumes that the file names for the documents end with
+ * ".txt".  Assumes that files containing corresponding
+ * author-assigned keyphrases end with ".key". Optionally an encoding
+ * for the documents/keyphrases can be defined (e.g. for Chinese
+ * text).
+ *
+ * Valid options are:<p>
+ *
+ * -l "directory name"<br>
+ * Specifies name of directory.<p>
+ *
+ * -m "model name"<br>
+ * Specifies name of model.<p>
+ *
+ * -e "encoding"<br>
+ * Specifies encoding.<p>
+ *
+ * -d<br>
+ * Turns debugging mode on.<p>
+ *
+ * -k<br>
+ * Use keyphrase frequency statistic.<p>
+ *
+ * -p<br>
+ * Disallow internal periods.<p>
+ *
+ * -x "length"<br>
+ * Sets maximum phrase length (default: 3).<p>
+ *
+ * -y "length"<br>
+ * Sets minimum phrase length (default: 1).<p>
+ *
+ * -o "number"<br>
+ * The minimum number of times a phrase needs to occur (default: 2). <p>
+ *
+ * -s "name of class implementing list of stop words"<br>
+ * Sets list of stop words to used (default: StopwordsEnglish).<p>
+ *
+ * -t "name of class implementing stemmer"<br>
+ * Sets stemmer to use (default: IteratedLovinsStemmer). <p>
+ *
+ * -n<br>
+ * Do not check for proper nouns. <p>
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class KEAModelBuilder implements OptionHandler {
+  
+  /** Name of directory */
+  String m_dirName = null;
+  
+  /** Name of model */
+  String m_modelName = null;
+
+  /** Encoding */
+  String m_encoding = "default";
+
+  /** Debugging mode? */
+  boolean m_debug = false;
+
+  /** Use keyphrase frequency attribute? */
+  boolean m_useKFrequency = false;
+
+  /** Disallow internal periods? */
+  boolean m_disallowIPeriods = false;
+
+  /** The maximum length of phrases */
+  private int m_MaxPhraseLength = 3;
+
+  /** The minimum length of phrases */
+  private int m_MinPhraseLength = 1;
+
+  /** The minimum number of occurences of a phrase */
+  private int m_MinNumOccur = 2;
+
+  /** The KEA filter object */
+  KEAFilter m_KEAFilter = null;
+
+  /** The stemmer to be used */
+  private Stemmer m_Stemmer = new IteratedLovinsStemmer();
+
+  /** The list of stop words to be used */
+  private Stopwords m_Stopwords = new StopwordsEnglish();
+
+  /** Determines whether check for proper nouns is performed */
+  private boolean m_CheckForProperNouns = true;
+
+  /**
+   * Get the M_CheckProperNouns value.
+   * @return the M_CheckProperNouns value.
+   */
+  public boolean getCheckForProperNouns() {
+    return m_CheckForProperNouns;
+  }
+
+  /**
+   * Set the M_CheckProperNouns value.
+   * @param newM_CheckProperNouns The new M_CheckProperNouns value.
+   */
+  public void setCheckForProperNouns(boolean newM_CheckProperNouns) {
+    this.m_CheckForProperNouns = newM_CheckProperNouns;
+  }
+
+  /**
+   * Get the M_Stopwords value.
+   * @return the M_Stopwords value.
+   */
+  public Stopwords getStopwords() {
+
+    return m_Stopwords;
+  }
+
+  /**
+   * Set the M_Stopwords value.
+   * @param newM_Stopwords The new M_Stopwords value.
+   */
+  public void setStopwords(Stopwords newM_Stopwords) {
+
+    this.m_Stopwords = newM_Stopwords;
+  }
+
+  
+  /**
+   * Get the Stemmer value.
+   * @return the Stemmer value.
+   */
+  public Stemmer getStemmer() {
+
+    return m_Stemmer;
+  }
+
+  /**
+   * Set the Stemmer value.
+   * @param newStemmer The new Stemmer value.
+   */
+  public void setStemmer(Stemmer newStemmer) {
+
+    this.m_Stemmer = newStemmer;
+  }
+  
+  /**
+   * Get the value of MinNumOccur.
+   *
+   * @return Value of MinNumOccur.
+   */
+  public int getMinNumOccur() {
+    
+    return m_MinNumOccur;
+  }
+  
+  /**
+   * Set the value of MinNumOccur.
+   *
+   * @param newMinNumOccur Value to assign to MinNumOccur.
+   */
+  public void setMinNumOccur(int newMinNumOccur) {
+    
+    m_MinNumOccur = newMinNumOccur;
+  }
+  
+  /**
+   * Get the value of MaxPhraseLength.
+   *
+   * @return Value of MaxPhraseLength.
+   */
+  public int getMaxPhraseLength() {
+    
+    return m_MaxPhraseLength;
+  }
+  
+  /**
+   * Set the value of MaxPhraseLength.
+   *
+   * @param newMaxPhraseLength Value to assign to MaxPhraseLength.
+   */
+  public void setMaxPhraseLength(int newMaxPhraseLength) {
+    
+    m_MaxPhraseLength = newMaxPhraseLength;
+  }
+  
+  /**
+   * Get the value of MinPhraseLength.
+   *
+   * @return Value of MinPhraseLength.
+   */
+  public int getMinPhraseLength() {
+    
+    return m_MinPhraseLength;
+  }
+  
+  /**
+   * Set the value of MinPhraseLength.
+   *
+   * @param newMinPhraseLength Value to assign to MinPhraseLength.
+   */
+  public void setMinPhraseLength(int newMinPhraseLength) {
+    
+    m_MinPhraseLength = newMinPhraseLength;
+  }
+  
+  /**
+   * Get the value of disallowIPeriods.
+   *
+   * @return Value of disallowIPeriods.
+   */
+  public boolean getDisallowIPeriods() {
+    
+    return m_disallowIPeriods;
+  }
+  
+  /**
+   * Set the value of disallowIPeriods.
+   *
+   * @param newdisallowIPeriods Value to assign to disallowIPeriods.
+   */
+  public void setDisallowIPeriods(boolean newdisallowIPeriods) {
+    
+    m_disallowIPeriods = newdisallowIPeriods;
+  }
+  
+  /**
+   * Get the value of useKFrequency.
+   *
+   * @return Value of useKFrequency.
+   */
+  public boolean getUseKFrequency() {
+    
+    return m_useKFrequency;
+  }
+  
+  /**
+   * Set the value of useKFrequency.
+   *
+   * @param newuseKFrequency Value to assign to useKFrequency.
+   */
+  public void setUseKFrequency(boolean newuseKFrequency) {
+    
+    m_useKFrequency = newuseKFrequency;
+  }
+  
+  /**
+   * Get the value of debug.
+   *
+   * @return Value of debug.
+   */
+  public boolean getDebug() {
+    
+    return m_debug;
+  }
+  
+  /**
+   * Set the value of debug.
+   *
+   * @param newdebug Value to assign to debug.
+   */
+  public void setDebug(boolean newdebug) {
+    
+    m_debug = newdebug;
+  }
+  
+  /**
+   * Get the value of encoding.
+   *
+   * @return Value of encoding.
+   */
+  public String getEncoding() {
+    
+    return m_encoding;
+  }
+  
+  /**
+   * Set the value of encoding.
+   *
+   * @param newencoding Value to assign to encoding.
+   */
+  public void setEncoding(String newencoding) {
+    
+    m_encoding = newencoding;
+  }
+  
+  /**
+   * Get the value of modelName.
+   *
+   * @return Value of modelName.
+   */
+  public String getModelName() {
+    
+    return m_modelName;
+  }
+  
+  /**
+   * Set the value of modelName.
+   *
+   * @param newmodelName Value to assign to modelName.
+   */
+  public void setModelName(String newmodelName) {
+    
+    m_modelName = newmodelName;
+  }
+  
+  /**
+   * Get the value of dirName.
+   *
+   * @return Value of dirName.
+   */
+  public String getDirName() {
+    
+    return m_dirName;
+  }
+  
+  /**
+   * Set the value of dirName.
+   *
+   * @param newdirName Value to assign to dirName.
+   */
+  public void setDirName(String newdirName) {
+    
+    m_dirName = newdirName;
+  }
+  
+  /**
+   * Parses a given list of options controlling the behaviour of this object.
+   * Valid options are:<p>
+   *
+   * -l "directory name" <br>
+   * Specifies name of directory.<p>
+   *
+   * -m "model name" <br>
+   * Specifies name of model.<p>
+   *
+   * -e "encoding" <br>
+   * Specifies encoding.<p>
+   *
+   * -d<br>
+   * Turns debugging mode on.<p>
+   *
+   * -k<br>
+   * Use keyphrase frequency statistic.<p>
+   *
+   * -p<br>
+   * Disallow internal periods. <p>
+   *
+   * -x "length"<br>
+   * Sets maximum phrase length (default: 3).<p>
+   *
+   * -y "length"<br>
+   * Sets minimum phrase length (default: 3).<p>
+   *
+   * -o "number"<br>
+   * The minimum number of times a phrase needs to occur (default: 2). <p>
+   *
+   * -s "name of class implementing list of stop words"<br>
+   * Sets list of stop words to used (default: StopwordsEnglish).<p>
+   *
+   * -t "name of class implementing stemmer"<br>
+   * Sets stemmer to use (default: IteratedLovinsStemmer). <p>
+   *
+   * -n<br>
+   * Do not check for proper nouns. <p>
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    String dirName = Utils.getOption('l', options);
+    if (dirName.length() > 0) {
+      setDirName(dirName);
+    } else {
+      setDirName(null);
+      throw new Exception("Name of directory required argument.");
+    }
+    String modelName = Utils.getOption('m', options);
+    if (modelName.length() > 0) {
+      setModelName(modelName);
+    } else {
+      setModelName(null);
+      throw new Exception("Name of model required argument.");
+    }
+    String encoding = Utils.getOption('e', options);
+    if (encoding.length() > 0) {
+      setEncoding(encoding);
+    } else {
+      setEncoding("default");
+    }
+    String maxPhraseLengthString = Utils.getOption('x', options);
+    if (maxPhraseLengthString.length() > 0) {
+      setMaxPhraseLength(Integer.parseInt(maxPhraseLengthString));
+    } else {
+      setMaxPhraseLength(3);
+    }
+    String minPhraseLengthString = Utils.getOption('y', options);
+    if (minPhraseLengthString.length() > 0) {
+      setMinPhraseLength(Integer.parseInt(minPhraseLengthString));
+    } else {
+      setMinPhraseLength(1);
+    }
+    String minNumOccurString = Utils.getOption('o', options);
+    if (minNumOccurString.length() > 0) {
+      setMinNumOccur(Integer.parseInt(minNumOccurString));
+    } else {
+      setMinNumOccur(2);
+    }
+    String stopwordsString = Utils.getOption('s', options);
+    if (stopwordsString.length() > 0) {
+      setStopwords((Stopwords)Class.forName(stopwordsString).newInstance());
+    }
+    String stemmerString = Utils.getOption('t', options);
+    if (stemmerString.length() > 0) {
+      setStemmer((Stemmer)Class.forName(stemmerString).newInstance());
+    }
+    setDebug(Utils.getFlag('d', options));
+    setUseKFrequency(Utils.getFlag('k', options));
+    setDisallowIPeriods(Utils.getFlag('p', options));
+    setCheckForProperNouns(!Utils.getFlag('n', options));
+    Utils.checkForRemainingOptions(options);
+   }
+
+  /**
+   * Gets the current option settings.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [20];
+    int current = 0;
+
+    options[current++] = "-l"; 
+    options[current++] = "" + (getDirName());
+    options[current++] = "-m"; 
+    options[current++] = "" + (getModelName());
+    options[current++] = "-e"; 
+    options[current++] = "" + (getEncoding());
+    if (getUseKFrequency()) {
+      options[current++] = "-k";
+    }
+    if (getDebug()) {
+      options[current++] = "-d";
+    }
+    if (getDisallowIPeriods()) {
+      options[current++] = "-p";
+    }
+    options[current++] = "-x"; 
+    options[current++] = "" + (getMaxPhraseLength());
+    options[current++] = "-y"; 
+    options[current++] = "" + (getMinPhraseLength());
+    options[current++] = "-o"; 
+    options[current++] = "" + (getMinNumOccur());
+    options[current++] = "-s"; 
+    options[current++] = "" + (getStopwords().getClass().getName());
+    options[current++] = "-t"; 
+    options[current++] = "" + (getStemmer().getClass().getName());
+    if (getCheckForProperNouns()) {
+      options[current++] = "-n";
+    }
+
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+  /**
+   * Returns an enumeration describing the available options.
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(12);
+
+    newVector.addElement(new Option(
+	      "\tSpecifies name of directory.",
+              "l", 1, "-l <directory name>"));
+    newVector.addElement(new Option(
+	      "\tSpecifies name of model.",
+              "m", 1, "-m <model name>"));
+    newVector.addElement(new Option(
+	      "\tSpecifies encoding.",
+              "e", 1, "-e <encoding>"));
+    newVector.addElement(new Option(
+	      "\tTurns debugging mode on.",
+              "d", 0, "-d"));
+    newVector.addElement(new Option(
+	      "\tUse keyphrase frequency statistic.",
+              "k", 0, "-k"));
+    newVector.addElement(new Option(
+	      "\tDisallow internal periods.",
+              "p", 0, "-p"));
+    newVector.addElement(new Option(
+	      "\tSets the maximum phrase length (default: 3).",
+              "x", 1, "-x <length>"));
+    newVector.addElement(new Option(
+	      "\tSets the minimum phrase length (default: 1).",
+              "y", 1, "-y <length>"));
+    newVector.addElement(new Option(
+	      "\tSet the minimum number of occurences (default: 2).",
+              "o", 1, "-o"));
+    newVector.addElement(new Option(
+	      "\tSets the list of stopwords to use (default: StopwordsEnglish).",
+              "s", 1, "-s <name of stopwords class>"));
+    newVector.addElement(new Option(
+	      "\tSet the stemmer to use (default: IteratedLovinsStemmer).",
+              "t", 1, "-t <name of stemmer class>"));
+    newVector.addElement(new Option(
+	      "\tDo not check for proper nouns.",
+              "n", 0, "-n"));
+ 
+    return newVector.elements();
+  }
+
+  /**
+   * Collects the stems of the file names.
+   */
+  public Hashtable collectStems() throws Exception {
+
+    Hashtable stems = new Hashtable();
+
+    try {
+      File dir = new File(m_dirName);
+      String[] files = dir.list();
+      for (int i = 0; i < files.length; i++) {
+	if (files[i].endsWith(".key") ||
+	    files[i].endsWith(".txt")) {
+	  String stem = files[i].substring(0, files[i].length() - 4);
+	  if (!stems.containsKey(stem)) {
+	    stems.put(stem, new Double(0));
+	  }
+	}
+      }
+    } catch (Exception e) {
+      throw new Exception("Problem opening directory " + m_dirName);
+    }
+    return stems;
+  }
+
+  /**
+   * Builds the model from the files
+   */
+  public void buildModel(Hashtable stems) throws Exception {
+    
+    // Check whether there is actually any data
+    if (stems.size() == 0) {
+      throw new Exception("Couldn't find any data!");
+    }
+
+    FastVector atts = new FastVector(2);
+    atts.addElement(new Attribute("doc", null));
+    atts.addElement(new Attribute("keyphrases", null));
+    Instances data = new Instances("keyphrase_training_data", atts, 0);
+
+    // Build model
+    m_KEAFilter = new KEAFilter();
+    m_KEAFilter.setDebug(m_debug);
+    m_KEAFilter.setDisallowInternalPeriods(getDisallowIPeriods());
+    m_KEAFilter.setKFused(getUseKFrequency());
+    m_KEAFilter.setMaxPhraseLength(getMaxPhraseLength());
+    m_KEAFilter.setMinPhraseLength(getMinPhraseLength());
+    m_KEAFilter.setMinNumOccur(getMinNumOccur());
+    m_KEAFilter.setInputFormat(data);
+    m_KEAFilter.setStemmer(getStemmer());
+    m_KEAFilter.setStopwords(getStopwords());
+    m_KEAFilter.setCheckForProperNouns(getCheckForProperNouns());
+    Enumeration elem = stems.keys();
+    while (elem.hasMoreElements()) {
+      String str = (String)elem.nextElement();
+      double[] newInst = new double[2];
+      try {
+	File txt = new File(m_dirName + "/" + str + ".txt");
+	InputStreamReader is;
+	if (!m_encoding.equals("default")) {
+	  is = new InputStreamReader(new FileInputStream(txt), m_encoding);
+	} else {
+	  is = new InputStreamReader(new FileInputStream(txt));
+	}
+	StringBuffer txtStr = new StringBuffer();
+	int c;
+	while ((c = is.read()) != -1) {
+	  txtStr.append((char)c);
+	}
+	newInst[0] = (double)data.attribute(0).addStringValue(txtStr.toString());
+      } catch (Exception e) {
+	if (m_debug) {
+	  System.err.println("Can't find document for stem " + str + ".");
+	}
+	newInst[0] = Instance.missingValue();
+      }
+      try {
+	File key = new File(m_dirName + "/" + str + ".key");
+	InputStreamReader is; 
+	if (!m_encoding.equals("default")) {
+	  is = new InputStreamReader(new FileInputStream(key), m_encoding);
+	} else {
+	  is = new InputStreamReader(new FileInputStream(key));
+	}
+	StringBuffer keyStr = new StringBuffer();
+	int c;
+	while ((c = is.read()) != -1) {
+	  keyStr.append((char)c);
+	}      
+	newInst[1] = (double)data.attribute(1).addStringValue(keyStr.toString());
+      } catch (Exception e) {
+	if (m_debug) {
+	  System.err.println("Can't find keyphrases for stem " + str + ".");
+	}
+	newInst[1] = Instance.missingValue();
+      }
+      data.add(new Instance(1.0, newInst));
+      m_KEAFilter.input(data.instance(0));
+      data = data.stringFreeStructure();
+    }
+    m_KEAFilter.batchFinished();
+    
+    // Get rid of instances in filter
+    Instance dummy;
+    while ((dummy = m_KEAFilter.output()) != null) {};
+  }
+  
+  /** 
+   * Saves the extraction model to the file.
+   */
+  public void saveModel() throws Exception {
+    
+    BufferedOutputStream bufferedOut = 
+      new BufferedOutputStream(new FileOutputStream(m_modelName));
+    ObjectOutputStream out = new ObjectOutputStream(bufferedOut);
+    out.writeObject(m_KEAFilter);
+    out.flush();
+    out.close();
+  }
+  
+  /**
+   * The main method.  
+   */
+  public static void main(String[] ops) {
+    
+    KEAModelBuilder kmb = new KEAModelBuilder();
+    try {
+      kmb.setOptions(ops);
+      System.err.print("Building model with options: ");
+      String[] optionSettings = kmb.getOptions();
+      for (int i = 0; i < optionSettings.length; i++) {
+	System.err.print(optionSettings[i] + " ");
+      }
+      System.err.println();
+      kmb.buildModel(kmb.collectStems());
+      kmb.saveModel();
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.err.println(e.getMessage());
+      System.err.println("\nOptions:\n");
+      Enumeration enum = kmb.listOptions();
+      while (enum.hasMoreElements()) {
+	Option option = (Option) enum.nextElement();
+	System.err.println(option.synopsis());
+	System.err.println(option.description());
+      }
+    }
+  }
+}
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAPhraseFilter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAPhraseFilter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/KEAPhraseFilter.java	(revision 16561)
@@ -0,0 +1,487 @@
+/*
+ *    KEAPhraseFilter.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+
+import weka.core.*;
+import weka.filters.*;
+
+/**
+ * This filter splits the text in selected string
+ * attributes into phrases. The resulting
+ * string attributes contain these phrases
+ * separated by '\n' characters.
+ *
+ * Phrases are identified according to the
+ * following definitions:
+ * 
+ * A phrase is a sequence of words interrupted
+ * only by sequences of whitespace characters,
+ * where each sequence of whitespace characters
+ * contains at most one '\n'.
+ *
+ * A word is a sequence of letters or digits
+ * that contains at least one letter, with
+ * the following exceptions:
+ *
+ * a) '.', '@', '_', '&', '/', '-' are allowed
+ * if surrounded by letters or digits,
+ *
+ * b) '\'' is allowed if preceeded by a letter
+ * or digit,
+ * 
+ * c) '-', '/' are also allowed if succeeded by
+ * whitespace characters followed by another
+ * word. In that case the whitespace characters
+ * will be deleted.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class KEAPhraseFilter extends Filter implements OptionHandler {
+
+  /** Stores which columns to select as a funky range */
+  protected Range m_SelectCols = new Range();
+
+  /** Determines whether internal periods are allowed */
+  protected boolean m_DisallowInternalPeriods = false;
+
+  /**
+   * Returns a string describing this filter
+   *
+   * @return a description of the filter suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String globalInfo() {
+    return "This filter splits the text contained " +
+      "by the selected string attributes into phrases.";
+  }
+
+  /**
+   * Returns an enumeration describing the available options
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(3);
+
+    newVector.addElement(new Option(
+              "\tSpecify list of attributes to process. First and last are valid\n"
+	      +"\tindexes. (default none)",
+              "R", 1, "-R <index1,index2-index4,...>"));
+    newVector.addElement(new Option(
+	      "\tInvert matching sense",
+              "V", 0, "-V"));
+    newVector.addElement(new Option(
+	      "\tDisallow internal periods",
+              "P", 0, "-P"));
+
+    return newVector.elements();
+  }
+
+  /**
+   * Parses a given list of options controlling the behaviour of this object.
+   * Valid options are:<p>
+   *
+   * -R index1,index2-index4,...<br>
+   * Specify list of attributes to process. First and last are valid indexes.
+   * (default none)<p>
+   *
+   * -V<br>
+   * Invert matching sense <p>
+   *
+   * -P<br>
+   * Disallow internal periods <p>
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    String list = Utils.getOption('R', options);
+    if (list.length() != 0) {
+      setAttributeIndices(list);
+    }
+    setInvertSelection(Utils.getFlag('V', options));
+
+    setDisallowInternalPeriods(Utils.getFlag('P', options));
+    
+    if (getInputFormat() != null) {
+      setInputFormat(getInputFormat());
+    }
+  }
+
+  /**
+   * Gets the current settings of the filter.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [4];
+    int current = 0;
+
+    if (getInvertSelection()) {
+      options[current++] = "-V";
+    }
+    if (getDisallowInternalPeriods()) {
+      options[current++] = "-P";
+    }
+    if (!getAttributeIndices().equals("")) {
+      options[current++] = "-R"; options[current++] = getAttributeIndices();
+    }
+
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+  /**
+   * Sets the format of the input instances.
+   *
+   * @param instanceInfo an Instances object containing the input
+   * instance structure (any instances contained in the object are
+   * ignored - only the structure is required).
+   * @return true if the outputFormat may be collected immediately 
+   */
+  public boolean setInputFormat(Instances instanceInfo) throws Exception {
+
+    super.setInputFormat(instanceInfo);
+    setOutputFormat(instanceInfo);
+    m_SelectCols.setUpper(instanceInfo.numAttributes() - 1);
+
+    return true;
+  }
+
+  /**
+   * Input an instance for filtering. Ordinarily the instance is processed
+   * and made available for output immediately. Some filters require all
+   * instances be read before producing output.
+   *
+   * @param instance the input instance
+   * @return true if the filtered instance may now be
+   * collected with output().
+   * @exception Exception if the input instance was not of the correct 
+   * format or if there was a problem with the filtering.
+   */
+  public boolean input(Instance instance) throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new Exception("No input instance format defined");
+    }
+    if (m_NewBatch) {
+      resetQueue();
+      m_NewBatch = false;
+    }
+    convertInstance(instance);
+    return true;
+  }
+
+  /**
+   * Signify that this batch of input to the filter is finished. If
+   * the filter requires all instances prior to filtering, output()
+   * may now be called to retrieve the filtered instances. Any
+   * subsequent instances filtered should be filtered based on setting
+   * obtained from the first batch (unless the inputFormat has been
+   * re-assigned or new options have been set). This default
+   * implementation assumes all instance processing occurs during
+   * inputFormat() and input().
+   *
+   * @return true if there are instances pending output
+   * @exception NullPointerException if no input structure has been defined,
+   * @exception Exception if there was a problem finishing the batch.
+   */
+  public boolean batchFinished() throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new NullPointerException("No input instance format defined");
+    }
+    m_NewBatch = true;
+    return (numPendingOutput() != 0);
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain arguments to the filter: use -h for help
+   */
+  public static void main(String [] argv) {
+    
+    try {
+      if (Utils.getFlag('b', argv)) {
+	Filter.batchFilterFile(new KEAPhraseFilter(), argv);
+      } else {
+	Filter.filterFile(new KEAPhraseFilter(), argv);
+      }
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+ 
+  /** 
+   * Converts an instance by removing all non-alphanumeric characters
+   * from its string attribute values.
+   */
+  private void convertInstance(Instance instance) throws Exception {
+  
+    double[] instVals = new double[instance.numAttributes()];
+
+    for (int i = 0; i < instance.numAttributes(); i++) {
+      if (!instance.attribute(i).isString() || 
+	  instance.isMissing(i)) {
+	instVals[i] = instance.value(i);
+      } else {
+	if (!m_SelectCols.isInRange(i)) {
+	  int index = getOutputFormat().attribute(i).
+	    addStringValue(instance.stringValue(i));
+	  instVals[i] = (double)index;
+	  continue;
+	}
+	String str = instance.stringValue(i);
+	StringBuffer resultStr = new StringBuffer();
+	int j = 0;
+	boolean phraseStart = true;
+	boolean seenNewLine = false;
+	boolean haveSeenHyphen = false;
+	boolean haveSeenSlash = false;
+	while (j < str.length()) {
+	  boolean isWord = false;
+	  boolean potNumber = false;
+	  int startj = j;
+	  while (j < str.length()) {
+	    char ch = str.charAt(j);
+	    if (Character.isLetterOrDigit(ch)) {
+	      potNumber = true;
+	      if (Character.isLetter(ch)) {
+		isWord = true;
+	      }
+	      j++;
+	    } else if ((!m_DisallowInternalPeriods && (ch == '.')) ||
+		       (ch == '@') ||
+		       (ch == '_') ||
+		       (ch == '&') ||
+		       (ch == '/') ||
+		       (ch == '-')) {
+	      if ((j > 0) && (j  + 1 < str.length()) &&
+		  Character.isLetterOrDigit(str.charAt(j - 1)) &&
+		  Character.isLetterOrDigit(str.charAt(j + 1))) {
+		j++;
+	      } else {
+		break;
+	      }
+	    } else if (ch == '\'') {
+	      if ((j > 0) &&
+		  Character.isLetterOrDigit(str.charAt(j - 1))) {
+		j++;
+	      } else {
+		break;
+	      }
+	    } else {
+	      break;
+	    }
+	  }
+	  if (isWord == true) {
+	    if (!phraseStart) {
+	      if (haveSeenHyphen) {
+		resultStr.append('-');
+	      } else if (haveSeenSlash) {
+		resultStr.append('/');
+	      } else {
+		resultStr.append(' ');
+	      }
+	    }
+	    resultStr.append(str.substring(startj, j));
+	    if (j == str.length()) {
+	      break;
+	    }
+	    phraseStart = false;
+	    seenNewLine = false;
+	    haveSeenHyphen = false;
+	    haveSeenSlash = false;
+	    if (Character.isWhitespace(str.charAt(j))) {
+	      if (str.charAt(j) == '\n') {
+		seenNewLine = true;
+	      } 
+	    } else if (str.charAt(j) == '-') {
+	      haveSeenHyphen = true;
+	    } else if (str.charAt(j) == '/') {
+	      haveSeenSlash = true;
+	    } else {
+	      phraseStart = true;
+	      resultStr.append('\n');
+	    }
+	    j++;
+	  } else if (j == str.length()) {
+	    break;
+	  } else if (str.charAt(j) == '\n') {
+	    if (seenNewLine) {
+	      if (phraseStart == false) {
+		resultStr.append('\n');
+		phraseStart = true;
+	      }
+	    } else if (potNumber) {
+	      if (phraseStart == false) {
+		phraseStart = true;
+		resultStr.append('\n');
+	      }
+	    }
+	    seenNewLine = true;
+	    j++;
+	  } else if (Character.isWhitespace(str.charAt(j))) {
+	    if (potNumber) {
+	      if (phraseStart == false) {
+		phraseStart = true;
+		resultStr.append('\n');
+	      }
+	    }
+	    j++;
+	  } else {
+	    if (phraseStart == false) {
+	      resultStr.append('\n');
+	      phraseStart = true;
+	    }
+	    j++;
+	  }
+	}
+	int index = getOutputFormat().attribute(i).
+	  addStringValue(resultStr.toString());
+	instVals[i] = (double)index;
+      }
+    }
+    Instance inst = new Instance(instance.weight(), instVals);
+    inst.setDataset(getOutputFormat());
+    push(inst);
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String invertSelectionTipText() {
+
+    return "If set to false, the specified attributes will be processed;"
+      + " If set to true, specified attributes won't be processed.";
+  }
+
+  /**
+   * Get whether the supplied columns are to be processed
+   *
+   * @return true if the supplied columns won't be processed
+   */
+  public boolean getInvertSelection() {
+
+    return m_SelectCols.getInvert();
+  }
+
+  /**
+   * Set whether selected columns should be processed. If true the 
+   * selected columns won't be processed.
+   *
+   * @param invert the new invert setting
+   */
+  public void setInvertSelection(boolean invert) {
+
+    m_SelectCols.setInvert(invert);
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String disallowInternalPeriodsTipText() {
+
+    return "If set to false, internal periods are allowed.";
+  }
+
+  /**
+   * Get whether the supplied columns are to be processed
+   *
+   * @return true if the supplied columns won't be processed
+   */
+  public boolean getDisallowInternalPeriods() {
+
+    return m_DisallowInternalPeriods;
+  }
+
+  /**
+   * Set whether selected columns should be processed. If true the 
+   * selected columns won't be processed.
+   *
+   * @param invert the new invert setting
+   */
+  public void setDisallowInternalPeriods(boolean disallow) {
+
+    m_DisallowInternalPeriods = disallow;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String attributeIndicesTipText() {
+
+    return "Specify range of attributes to act on."
+      + " This is a comma separated list of attribute indices, with"
+      + " \"first\" and \"last\" valid values. Specify an inclusive"
+      + " range with \"-\". E.g: \"first-3,5,6-10,last\".";
+  }
+
+  /**
+   * Get the current range selection.
+   *
+   * @return a string containing a comma separated list of ranges
+   */
+  public String getAttributeIndices() {
+
+    return m_SelectCols.getRanges();
+  }
+
+  /**
+   * Set which attributes are to be processed
+   *
+   * @param rangeList a string representing the list of attributes.  Since
+   * the string will typically come from a user, attributes are indexed from
+   * 1. <br>
+   * eg: first-3,5,6-last
+   */
+  public void setAttributeIndices(String rangeList) {
+
+    m_SelectCols.setRanges(rangeList);
+  }
+
+  /**
+   * Set which attributes are to be processed
+   *
+   * @param attributes an array containing indexes of attributes to select.
+   * Since the array will typically come from a program, attributes are indexed
+   * from 0.
+   */
+  public void setAttributeIndicesArray(int [] attributes) {
+    
+    setAttributeIndices(Range.indicesToRangeList(attributes));
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/LovinsStemmer.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/LovinsStemmer.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/LovinsStemmer.java	(revision 16561)
@@ -0,0 +1,890 @@
+/*
+ *    LovinsStemmer.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+/** 
+ * Implements the Lovins stemmer.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class LovinsStemmer extends Stemmer implements Serializable {
+
+  /** C version compatibility mode (emulates bugs
+      in original C implementation) */
+  private static boolean m_CompMode = true;
+
+  /** The hash tables containing the list of endings. */
+  private static HashMap m_l11 = null;
+  private static HashMap m_l10 = null;
+  private static HashMap m_l9 = null;
+  private static HashMap m_l8 = null;
+  private static HashMap m_l7 = null;
+  private static HashMap m_l6 = null;
+  private static HashMap m_l5 = null;
+  private static HashMap m_l4 = null;
+  private static HashMap m_l3 = null;
+  private static HashMap m_l2 = null;
+  private static HashMap m_l1 = null;
+
+  static {
+
+    m_l11 = new HashMap();
+    m_l11.put("alistically", "B");
+    m_l11.put("arizability", "A");
+    m_l11.put("izationally", "B");
+    m_l10 = new HashMap();
+    m_l10.put("antialness", "A");
+    m_l10.put("arisations", "A");
+    m_l10.put("arizations", "A");
+    m_l10.put("entialness", "A");
+    m_l9 = new HashMap();
+    m_l9.put("allically", "C");
+    m_l9.put("antaneous", "A");
+    m_l9.put("antiality", "A");
+    m_l9.put("arisation", "A");
+    m_l9.put("arization", "A");
+    m_l9.put("ationally", "B");
+    m_l9.put("ativeness", "A");
+    m_l9.put("eableness", "E");
+    m_l9.put("entations", "A");
+    m_l9.put("entiality", "A");
+    m_l9.put("entialize", "A");
+    m_l9.put("entiation", "A");
+    m_l9.put("ionalness", "A");
+    m_l9.put("istically", "A");
+    m_l9.put("itousness", "A");
+    m_l9.put("izability", "A");
+    m_l9.put("izational", "A");
+    m_l8 = new HashMap();
+    m_l8.put("ableness", "A");
+    m_l8.put("arizable", "A");
+    m_l8.put("entation", "A");
+    m_l8.put("entially", "A");
+    m_l8.put("eousness", "A");
+    m_l8.put("ibleness", "A");
+    m_l8.put("icalness", "A");
+    m_l8.put("ionalism", "A");
+    m_l8.put("ionality", "A");
+    m_l8.put("ionalize", "A");
+    m_l8.put("iousness", "A");
+    m_l8.put("izations", "A");
+    m_l8.put("lessness", "A");
+    m_l7 = new HashMap();
+    m_l7.put("ability", "A");
+    m_l7.put("aically", "A");
+    m_l7.put("alistic", "B");
+    m_l7.put("alities", "A");
+    m_l7.put("ariness", "E");
+    m_l7.put("aristic", "A");
+    m_l7.put("arizing", "A");
+    m_l7.put("ateness", "A");
+    m_l7.put("atingly", "A");
+    m_l7.put("ational", "B");
+    m_l7.put("atively", "A");
+    m_l7.put("ativism", "A");
+    m_l7.put("elihood", "E");
+    m_l7.put("encible", "A");
+    m_l7.put("entally", "A");
+    m_l7.put("entials", "A");
+    m_l7.put("entiate", "A");
+    m_l7.put("entness", "A");
+    m_l7.put("fulness", "A");
+    m_l7.put("ibility", "A");
+    m_l7.put("icalism", "A");
+    m_l7.put("icalist", "A");
+    m_l7.put("icality", "A");
+    m_l7.put("icalize", "A");
+    m_l7.put("ication", "G");
+    m_l7.put("icianry", "A");
+    m_l7.put("ination", "A");
+    m_l7.put("ingness", "A");
+    m_l7.put("ionally", "A");
+    m_l7.put("isation", "A");
+    m_l7.put("ishness", "A");
+    m_l7.put("istical", "A");
+    m_l7.put("iteness", "A");
+    m_l7.put("iveness", "A");
+    m_l7.put("ivistic", "A");
+    m_l7.put("ivities", "A");
+    m_l7.put("ization", "F");
+    m_l7.put("izement", "A");
+    m_l7.put("oidally", "A");
+    m_l7.put("ousness", "A");
+    m_l6 = new HashMap();
+    m_l6.put("aceous", "A");
+    m_l6.put("acious", "B");
+    m_l6.put("action", "G");
+    m_l6.put("alness", "A");
+    m_l6.put("ancial", "A");
+    m_l6.put("ancies", "A");
+    m_l6.put("ancing", "B");
+    m_l6.put("ariser", "A");
+    m_l6.put("arized", "A");
+    m_l6.put("arizer", "A");
+    m_l6.put("atable", "A");
+    m_l6.put("ations", "B");
+    m_l6.put("atives", "A");
+    m_l6.put("eature", "Z");
+    m_l6.put("efully", "A");
+    m_l6.put("encies", "A");
+    m_l6.put("encing", "A");
+    m_l6.put("ential", "A");
+    m_l6.put("enting", "C");
+    m_l6.put("entist", "A");
+    m_l6.put("eously", "A");
+    m_l6.put("ialist", "A");
+    m_l6.put("iality", "A");
+    m_l6.put("ialize", "A");
+    m_l6.put("ically", "A");
+    m_l6.put("icance", "A");
+    m_l6.put("icians", "A");
+    m_l6.put("icists", "A");
+    m_l6.put("ifully", "A");
+    m_l6.put("ionals", "A");
+    m_l6.put("ionate", "D");
+    m_l6.put("ioning", "A");
+    m_l6.put("ionist", "A");
+    m_l6.put("iously", "A");
+    m_l6.put("istics", "A");
+    m_l6.put("izable", "E");
+    m_l6.put("lessly", "A");
+    m_l6.put("nesses", "A");
+    m_l6.put("oidism", "A");
+    m_l5 = new HashMap();
+    m_l5.put("acies", "A");
+    m_l5.put("acity", "A");
+    m_l5.put("aging", "B");
+    m_l5.put("aical", "A");
+    if (!m_CompMode) {
+      m_l5.put("alist", "A");
+    }
+    m_l5.put("alism", "B");
+    m_l5.put("ality", "A");
+    m_l5.put("alize", "A");
+    m_l5.put("allic", "b");
+    m_l5.put("anced", "B");
+    m_l5.put("ances", "B");
+    m_l5.put("antic", "C");
+    m_l5.put("arial", "A");
+    m_l5.put("aries", "A");
+    m_l5.put("arily", "A");
+    m_l5.put("arity", "B");
+    m_l5.put("arize", "A");
+    m_l5.put("aroid", "A");
+    m_l5.put("ately", "A");
+    m_l5.put("ating", "I");
+    m_l5.put("ation", "B");
+    m_l5.put("ative", "A");
+    m_l5.put("ators", "A");
+    m_l5.put("atory", "A");
+    m_l5.put("ature", "E");
+    m_l5.put("early", "Y");
+    m_l5.put("ehood", "A");
+    m_l5.put("eless", "A");
+    if (!m_CompMode) {
+      m_l5.put("elily", "A");
+    } else {
+      m_l5.put("elity", "A");
+    }
+    m_l5.put("ement", "A");
+    m_l5.put("enced", "A");
+    m_l5.put("ences", "A");
+    m_l5.put("eness", "E");
+    m_l5.put("ening", "E");
+    m_l5.put("ental", "A");
+    m_l5.put("ented", "C");
+    m_l5.put("ently", "A");
+    m_l5.put("fully", "A");
+    m_l5.put("ially", "A");
+    m_l5.put("icant", "A");
+    m_l5.put("ician", "A");
+    m_l5.put("icide", "A");
+    m_l5.put("icism", "A");
+    m_l5.put("icist", "A");
+    m_l5.put("icity", "A");
+    m_l5.put("idine", "I");
+    m_l5.put("iedly", "A");
+    m_l5.put("ihood", "A");
+    m_l5.put("inate", "A");
+    m_l5.put("iness", "A");
+    m_l5.put("ingly", "B");
+    m_l5.put("inism", "J");
+    m_l5.put("inity", "c");
+    m_l5.put("ional", "A");
+    m_l5.put("ioned", "A");
+    m_l5.put("ished", "A");
+    m_l5.put("istic", "A");
+    m_l5.put("ities", "A");
+    m_l5.put("itous", "A");
+    m_l5.put("ively", "A");
+    m_l5.put("ivity", "A");
+    m_l5.put("izers", "F");
+    m_l5.put("izing", "F");
+    m_l5.put("oidal", "A");
+    m_l5.put("oides", "A");
+    m_l5.put("otide", "A");
+    m_l5.put("ously", "A");
+    m_l4 = new HashMap();
+    m_l4.put("able", "A");
+    m_l4.put("ably", "A");
+    m_l4.put("ages", "B");
+    m_l4.put("ally", "B");
+    m_l4.put("ance", "B");
+    m_l4.put("ancy", "B");
+    m_l4.put("ants", "B");
+    m_l4.put("aric", "A");
+    m_l4.put("arly", "K");
+    m_l4.put("ated", "I");
+    m_l4.put("ates", "A");
+    m_l4.put("atic", "B");
+    m_l4.put("ator", "A");
+    m_l4.put("ealy", "Y");
+    m_l4.put("edly", "E");
+    m_l4.put("eful", "A");
+    m_l4.put("eity", "A");
+    m_l4.put("ence", "A");
+    m_l4.put("ency", "A");
+    m_l4.put("ened", "E");
+    m_l4.put("enly", "E");
+    m_l4.put("eous", "A");
+    m_l4.put("hood", "A");
+    m_l4.put("ials", "A");
+    m_l4.put("ians", "A");
+    m_l4.put("ible", "A");
+    m_l4.put("ibly", "A");
+    m_l4.put("ical", "A");
+    m_l4.put("ides", "L");
+    m_l4.put("iers", "A");
+    m_l4.put("iful", "A");
+    m_l4.put("ines", "M");
+    m_l4.put("ings", "N");
+    m_l4.put("ions", "B");
+    m_l4.put("ious", "A");
+    m_l4.put("isms", "B");
+    m_l4.put("ists", "A");
+    m_l4.put("itic", "H");
+    m_l4.put("ized", "F");
+    m_l4.put("izer", "F");
+    m_l4.put("less", "A");
+    m_l4.put("lily", "A");
+    m_l4.put("ness", "A");
+    m_l4.put("ogen", "A");
+    m_l4.put("ward", "A");
+    m_l4.put("wise", "A");
+    m_l4.put("ying", "B");
+    m_l4.put("yish", "A");
+    m_l3 = new HashMap();
+    m_l3.put("acy", "A");
+    m_l3.put("age", "B");
+    m_l3.put("aic", "A");
+    m_l3.put("als", "b");
+    m_l3.put("ant", "B");
+    m_l3.put("ars", "O");
+    m_l3.put("ary", "F");
+    m_l3.put("ata", "A");
+    m_l3.put("ate", "A");
+    m_l3.put("eal", "Y");
+    m_l3.put("ear", "Y");
+    m_l3.put("ely", "E");
+    m_l3.put("ene", "E");
+    m_l3.put("ent", "C");
+    m_l3.put("ery", "E");
+    m_l3.put("ese", "A");
+    m_l3.put("ful", "A");
+    m_l3.put("ial", "A");
+    m_l3.put("ian", "A");
+    m_l3.put("ics", "A");
+    m_l3.put("ide", "L");
+    m_l3.put("ied", "A");
+    m_l3.put("ier", "A");
+    m_l3.put("ies", "P");
+    m_l3.put("ily", "A");
+    m_l3.put("ine", "M");
+    m_l3.put("ing", "N");
+    m_l3.put("ion", "Q");
+    m_l3.put("ish", "C");
+    m_l3.put("ism", "B");
+    m_l3.put("ist", "A");
+    m_l3.put("ite", "a");
+    m_l3.put("ity", "A");
+    m_l3.put("ium", "A");
+    m_l3.put("ive", "A");
+    m_l3.put("ize", "F");
+    m_l3.put("oid", "A");
+    m_l3.put("one", "R");
+    m_l3.put("ous", "A");
+    m_l2 = new HashMap();
+    m_l2.put("ae", "A"); 
+    m_l2.put("al", "b");
+    m_l2.put("ar", "X");
+    m_l2.put("as", "B");
+    m_l2.put("ed", "E");
+    m_l2.put("en", "F");
+    m_l2.put("es", "E");
+    m_l2.put("ia", "A");
+    m_l2.put("ic", "A");
+    m_l2.put("is", "A");
+    m_l2.put("ly", "B");
+    m_l2.put("on", "S");
+    m_l2.put("or", "T");
+    m_l2.put("um", "U");
+    m_l2.put("us", "V");
+    m_l2.put("yl", "R");
+    m_l2.put("s\'", "A");
+    m_l2.put("\'s", "A");
+    m_l1 = new HashMap();
+    m_l1.put("a", "A");
+    m_l1.put("e", "A");
+    m_l1.put("i", "A");
+    m_l1.put("o", "A");
+    m_l1.put("s", "W");
+    m_l1.put("y", "B");	
+  }
+
+  /**
+   * Finds and removes ending from given word.
+   */
+  private String removeEnding(String word) {
+
+    int length = word.length();
+    int el = 11;
+
+    while (el > 0) {
+      if (length - el > 1) {
+	String ending = word.substring(length - el);
+	String conditionCode = null;
+	switch (el) {
+	case 11: conditionCode = (String)m_l11.get(ending);
+	  break;
+	case 10: conditionCode = (String)m_l10.get(ending);
+	  break; 
+	case 9: conditionCode = (String)m_l9.get(ending);
+	  break;
+	case 8: conditionCode = (String)m_l8.get(ending);
+	  break;   
+	case 7: conditionCode = (String)m_l7.get(ending);
+	  break;   
+	case 6: conditionCode = (String)m_l6.get(ending);
+	  break;   
+	case 5: conditionCode = (String)m_l5.get(ending);
+	  break;   
+	case 4: conditionCode = (String)m_l4.get(ending);
+	  break;   
+	case 3: conditionCode = (String)m_l3.get(ending);
+	  break;   
+	case 2: conditionCode = (String)m_l2.get(ending);
+	  break;   
+	case 1: conditionCode = (String)m_l1.get(ending);
+	  break;   
+	default:
+	}
+	if (conditionCode != null) {
+	  switch (conditionCode.charAt(0)) {
+	  case 'A':
+	    return word.substring(0, length - el);
+	  case 'B':
+	    if (length - el > 2) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'C':
+	    if (length - el > 3) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'D':
+	    if (length - el > 4) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'E':
+	    if (word.charAt(length - el - 1) != 'e') {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'F':
+	    if ((length - el > 2) &&
+		(word.charAt(length - el - 1) != 'e')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'G':
+	    if ((length - el > 2) &&
+		(word.charAt(length - el - 1) == 'f')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'H':
+	    if ((word.charAt(length - el - 1) == 't') ||
+		((word.charAt(length - el - 1) == 'l') &&
+		 (word.charAt(length - el - 2) == 'l'))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'I':
+	    if ((word.charAt(length - el - 1) != 'o') &&
+		(word.charAt(length - el - 1) != 'e')) { 
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'J':
+	    if ((word.charAt(length - el - 1) != 'a') &&
+		(word.charAt(length - el - 1) != 'e')) { 
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'K':
+	    if ((length - el > 2) &&
+		((word.charAt(length - el - 1) == 'l') ||
+		 (word.charAt(length - el - 1) == 'i') ||
+		 ((word.charAt(length - el - 1) == 'e') &&
+		  (word.charAt(length - el - 3) == 'u')))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'L':
+	    if ((word.charAt(length - el - 1) != 'u') &&
+		(word.charAt(length - el - 1) != 'x') &&
+		((word.charAt(length - el - 1) != 's') ||
+		 (word.charAt(length - el - 2) == 'o'))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'M':
+	    if ((word.charAt(length - el - 1) != 'a') &&
+		(word.charAt(length - el - 1) != 'c') &&
+		(word.charAt(length - el - 1) != 'e') &&
+		(word.charAt(length - el - 1) != 'm')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'N':
+	    if ((length - el > 3) || 
+		((length - el == 3) &&
+		 ((word.charAt(length - el - 3) != 's')))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'O':
+	    if ((word.charAt(length - el - 1) == 'l') ||
+		(word.charAt(length - el - 1) == 'i')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'P':
+	    if (word.charAt(length - el - 1) != 'c') {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'Q':
+	    if ((length - el > 2) &&
+		(word.charAt(length - el - 1) != 'l') &&
+		(word.charAt(length - el - 1) != 'n')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'R':
+	    if ((word.charAt(length - el - 1) == 'n') ||
+		(word.charAt(length - el - 1) == 'r')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'S':
+	    if (((word.charAt(length - el - 1) == 'r') &&
+		 (word.charAt(length - el - 2) == 'd')) ||
+		((word.charAt(length - el - 1) == 't') &&
+		 (word.charAt(length - el - 2) != 't'))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'T':
+	    if ((word.charAt(length - el - 1) == 's') ||
+		((word.charAt(length - el - 1) == 't') &&
+		 (word.charAt(length - el - 2) != 'o'))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'U':
+	    if ((word.charAt(length - el - 1) == 'l') ||
+		(word.charAt(length - el - 1) == 'm') ||
+		(word.charAt(length - el - 1) == 'n') ||
+		(word.charAt(length - el - 1) == 'r')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'V':
+	    if (word.charAt(length - el - 1) == 'c') {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'W':
+	    if ((word.charAt(length - el - 1) != 's') &&
+		(word.charAt(length - el - 1) != 'u')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'X':
+	    if ((word.charAt(length - el - 1) == 'l') ||
+		(word.charAt(length - el - 1) == 'i') ||
+		((length - el > 2) &&
+		 (word.charAt(length - el - 1) == 'e') &&
+		 (word.charAt(length - el - 3) == 'u'))) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'Y':
+	    if ((word.charAt(length - el - 1) == 'n') &&
+		(word.charAt(length - el - 2) == 'i')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'Z':
+	    if (word.charAt(length - el - 1) != 'f') {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'a':
+	    if ((word.charAt(length - el - 1) == 'd') ||
+		(word.charAt(length - el - 1) == 'f') ||
+		(((word.charAt(length - el - 1) == 'h') &&
+		  (word.charAt(length - el - 2) == 'p'))) ||
+		(((word.charAt(length - el - 1) == 'h') &&
+		  (word.charAt(length - el - 2) == 't'))) ||
+		(word.charAt(length - el - 1) == 'l') ||
+		(((word.charAt(length - el - 1) == 'r') &&
+		  (word.charAt(length - el - 2) == 'e'))) ||
+		(((word.charAt(length - el - 1) == 'r') &&
+		  (word.charAt(length - el - 2) == 'o'))) ||
+		(((word.charAt(length - el - 1) == 's') &&
+		  (word.charAt(length - el - 2) == 'e'))) ||
+		(word.charAt(length - el - 1) == 't')) {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  case 'b':
+	    if (m_CompMode) {
+	      if (((length - el == 3 ) &&
+		   (!((word.charAt(length - el - 1) == 't') &&
+		      (word.charAt(length - el - 2) == 'e') &&
+		      (word.charAt(length - el - 3) == 'm')))) ||
+		  ((length - el > 3) &&
+		   (!((word.charAt(length - el - 1) == 't') &&
+		      (word.charAt(length - el - 2) == 's') &&
+		      (word.charAt(length - el - 3) == 'y') &&
+		      (word.charAt(length - el - 4) == 'r'))))) {
+		return word.substring(0, length - el);
+	      }
+	    } else {
+	      if ((length - el > 2) &&
+		  (!((word.charAt(length - el - 1) == 't') &&
+		     (word.charAt(length - el - 2) == 'e') &&
+		     (word.charAt(length - el - 3) == 'm'))) &&
+		  ((length - el < 4) ||
+		   (!((word.charAt(length - el - 1) == 't') &&
+		      (word.charAt(length - el - 2) == 's') &&
+		      (word.charAt(length - el - 3) == 'y') &&
+		      (word.charAt(length - el - 4) == 'r'))))) {
+		return word.substring(0, length - el);
+	      }
+	    } 
+	    break;
+	  case 'c':
+	    if (word.charAt(length - el - 1) == 'l') {
+	      return word.substring(0, length - el);
+	    }
+	    break;
+	  default:
+	    throw new IllegalArgumentException("Fatal error.");
+	  }
+	}
+      }
+      el--;
+    }
+    return word;
+  }
+
+  /**
+   * Recodes ending of given word.
+   */
+  private String recodeEnding(String word) {
+
+    int lastPos = word.length() - 1;
+
+    // Rule 1
+    if (word.endsWith("bb") ||
+	word.endsWith("dd") ||
+	word.endsWith("gg") ||
+	word.endsWith("ll") ||
+	word.endsWith("mm") ||
+	word.endsWith("nn") ||
+	word.endsWith("pp") ||
+	word.endsWith("rr") ||
+	word.endsWith("ss") ||
+	word.endsWith("tt")) {
+      word = word.substring(0, lastPos);
+      lastPos--;
+    }
+
+    // Rule 2
+    if (word.endsWith("iev")) {
+      word = word.substring(0, lastPos - 2).concat("ief");
+    }
+
+    // Rule 3
+    if (word.endsWith("uct")) {
+      word = word.substring(0, lastPos - 2).concat("uc");
+      lastPos--;
+    }
+
+    // Rule 4
+    if (word.endsWith("umpt")) {
+      word = word.substring(0, lastPos - 3).concat("um");
+      lastPos -= 2;
+    }
+
+    // Rule 5
+    if (word.endsWith("rpt")) {
+      word = word.substring(0, lastPos - 2).concat("rb");
+      lastPos--;
+    }
+
+    // Rule 6
+    if (word.endsWith("urs")) {
+      word = word.substring(0, lastPos - 2).concat("ur");
+      lastPos--;
+    }
+
+    // Rule 7
+    if (word.endsWith("istr")) {
+      word = word.substring(0, lastPos - 3).concat("ister");
+      lastPos++;
+    }
+ 
+    // Rule 7a
+    if (word.endsWith("metr")) {
+      word = word.substring(0, lastPos - 3).concat("meter");
+      lastPos++;
+    }
+
+    // Rule 8
+    if (word.endsWith("olv")) {
+      word = word.substring(0, lastPos - 2).concat("olut");
+      lastPos++;
+    }
+ 
+    // Rule 9
+    if (word.endsWith("ul")) {
+      if ((lastPos - 2 < 0) ||
+	  ((word.charAt(lastPos - 2) != 'a') &&
+	   (word.charAt(lastPos - 2) != 'i') &&
+	   (word.charAt(lastPos - 2) != 'o'))) {
+	word = word.substring(0, lastPos - 1).concat("l");
+	lastPos--;
+      }
+    }
+
+    // Rule 10
+    if (word.endsWith("bex")) {
+      word = word.substring(0, lastPos - 2).concat("bic");
+    }
+
+    // Rule 11
+    if (word.endsWith("dex")) {
+      word = word.substring(0, lastPos - 2).concat("dic");
+    }
+
+    // Rule 12
+    if (word.endsWith("pex")) {
+      word = word.substring(0, lastPos - 2).concat("pic");
+    }
+
+    // Rule 13
+    if (word.endsWith("tex")) {
+      word = word.substring(0, lastPos - 2).concat("tic");
+    }
+
+    // Rule 14
+    if (word.endsWith("ax")) {
+      word = word.substring(0, lastPos - 1).concat("ac");
+    }
+
+    // Rule 15
+    if (word.endsWith("ex")) {
+      word = word.substring(0, lastPos - 1).concat("ec");
+    }
+
+    // Rule 16
+    if (word.endsWith("ix")) {
+      word = word.substring(0, lastPos - 1).concat("ic");
+    }
+
+    // Rule 17
+    if (word.endsWith("lux")) {
+      word = word.substring(0, lastPos - 2).concat("luc");
+    }
+
+    // Rule 18
+    if (word.endsWith("uad")) {
+      word = word.substring(0, lastPos - 2).concat("uas");
+    }
+
+    // Rule 19
+    if (word.endsWith("vad")) {
+      word = word.substring(0, lastPos - 2).concat("vas");
+    }
+
+    // Rule 20
+    if (word.endsWith("cid")) {
+      word = word.substring(0, lastPos - 2).concat("cis");
+    }
+
+    // Rule 21
+    if (word.endsWith("lid")) {
+      word = word.substring(0, lastPos - 2).concat("lis");
+    }
+
+    // Rule 22
+    if (word.endsWith("erid")) {
+      word = word.substring(0, lastPos - 3).concat("eris");
+    }
+
+    // Rule 23
+    if (word.endsWith("pand")) {
+      word = word.substring(0, lastPos - 3).concat("pans");
+    }
+ 
+    // Rule 24
+    if (word.endsWith("end")) {
+      if ((lastPos - 3 < 0) ||
+	   (word.charAt(lastPos - 3) != 's')) {
+	word = word.substring(0, lastPos - 2).concat("ens");
+      }
+    }
+
+    // Rule 25
+    if (word.endsWith("ond")) {
+      word = word.substring(0, lastPos - 2).concat("ons");
+    }
+
+    // Rule 26
+    if (word.endsWith("lud")) {
+      word = word.substring(0, lastPos - 2).concat("lus");
+    }
+
+    // Rule 27
+    if (word.endsWith("rud")) {
+      word = word.substring(0, lastPos - 2).concat("rus");
+    }
+
+    // Rule 28
+    if (word.endsWith("her")) {
+      if ((lastPos - 3 < 0) ||
+	  ((word.charAt(lastPos - 3) != 'p') &&
+	   (word.charAt(lastPos - 3) != 't'))) {
+	word = word.substring(0, lastPos - 2).concat("hes");
+      }
+    }
+
+    // Rule 29
+    if (word.endsWith("mit")) {
+      word = word.substring(0, lastPos - 2).concat("mis");
+    }
+
+    // Rule 30
+    if (word.endsWith("end")) {
+      if ((lastPos - 3 < 0) ||
+	  (word.charAt(lastPos - 3) != 'm')) {
+	word = word.substring(0, lastPos - 2).concat("ens");
+      }
+    }
+
+    // Rule 31
+    if (word.endsWith("ert")) {
+      word = word.substring(0, lastPos - 2).concat("ers");
+    }
+
+    // Rule 32
+    if (word.endsWith("et")) {
+      if ((lastPos - 2 < 0) ||
+	  (word.charAt(lastPos - 2) != 'n')) {
+	word = word.substring(0, lastPos - 1).concat("es");
+      }
+    }
+
+    // Rule 33
+    if (word.endsWith("yt")) {
+      word = word.substring(0, lastPos - 1).concat("ys");
+    }
+
+    // Rule 34
+    if (word.endsWith("yz")) {
+      word = word.substring(0, lastPos - 1).concat("ys");
+    }
+
+    return word;
+  }
+
+  /**
+   * Returns the stemmed version of the given word.
+   * 
+   * @param word a string consisting of a single word
+   */
+  public String stem(String word) {
+
+    if (word.length() > 2) {
+      return recodeEnding(removeEnding(word.toLowerCase()));
+    } else {
+      return word.toLowerCase();
+    }
+  }
+
+  /**
+   * Stems text coming into stdin and writes it to stdout.
+   */
+  public static void main(String[] ops) {
+
+    LovinsStemmer ls = new LovinsStemmer();
+
+    try {
+      int num;
+      StringBuffer wordBuffer = new StringBuffer();
+      while ((num = System.in.read()) != -1) {
+	char c = (char)num;
+	if (((num >= (int)'A') && (num <= (int)'Z')) ||
+	    ((num >= (int)'a') && (num <= (int)'z'))) {
+	  wordBuffer.append(c);
+	} else {
+	  if (wordBuffer.length() > 0) {
+	    System.out.print(ls.stem(wordBuffer.toString().
+				     toLowerCase()));
+	    wordBuffer = new StringBuffer();
+	  }
+	  System.out.print(c);
+	}
+      }
+    } catch (Exception e) {
+      System.err.println(e.getMessage());
+    }
+  }
+}
+    
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/NumbersFilter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/NumbersFilter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/NumbersFilter.java	(revision 16561)
@@ -0,0 +1,182 @@
+/*
+ *    NumbersFilter.java
+ *    Copyright (C) 2000 Eibe Frank
+ *
+ *    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.
+ */
+
+import weka.core.*;
+import weka.filters.*;
+
+import java.util.*;
+
+/**
+ * Removes all numbers from all the string attributes in the given
+ * dataset. Assumes that words are separated by whitespace.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class NumbersFilter extends Filter {
+
+  /**
+   * Returns a string describing this filter
+   *
+   * @return a description of the filter suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String globalInfo() {
+      return "Removes all numbers from all the string attributes in " +
+      "the given dataset. Assumes that words are separated by whitespace.";
+  }
+
+  /**
+   * Signify that this batch of input to the filter is finished. If
+   * the filter requires all instances prior to filtering, output()
+   * may now be called to retrieve the filtered instances. Any
+   * subsequent instances filtered should be filtered based on setting
+   * obtained from the first batch (unless the inputFormat has been
+   * re-assigned or new options have been set). This default
+   * implementation assumes all instance processing occurs during
+   * inputFormat() and input().
+   *
+   * @return true if there are instances pending output
+   * @exception NullPointerException if no input structure has been defined,
+   * @exception Exception if there was a problem finishing the batch.
+   */
+  public boolean batchFinished() throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new NullPointerException("No input instance format defined");
+    }
+    m_NewBatch = true;
+    return (numPendingOutput() != 0);
+  }
+
+  /**
+   * Sets the format of the input instances.
+   *
+   * @param instanceInfo an Instances object containing the input
+   * instance structure (any instances contained in the object are
+   * ignored - only the structure is required).
+   * @return true if the outputFormat may be collected immediately 
+   */
+  public boolean setInputFormat(Instances instanceInfo) throws Exception {
+
+    super.setInputFormat(instanceInfo);
+    setOutputFormat(instanceInfo);
+    return true;
+  }
+
+  /**
+   * Input an instance for filtering. Ordinarily the instance is processed
+   * and made available for output immediately. Some filters require all
+   * instances be read before producing output.
+   *
+   * @param instance the input instance
+   * @return true if the filtered instance may now be
+   * collected with output().
+   * @exception Exception if the input instance was not of the correct 
+   * format or if there was a problem with the filtering.
+   */
+  public boolean input(Instance instance) throws Exception {
+
+    if (getInputFormat() == null) {
+      throw new Exception("No input instance format defined");
+    }
+    if (m_NewBatch) {
+      resetQueue();
+      m_NewBatch = false;
+    }
+    convertInstance(instance);
+    return true;
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain arguments to the filter: use -h for help
+   */
+  public static void main(String [] argv) {
+    
+    try {
+      if (Utils.getFlag('b', argv)) {
+	Filter.batchFilterFile(new NumbersFilter(), argv);
+      } else {
+	Filter.filterFile(new NumbersFilter(), argv);
+      }
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+ 
+  /** 
+   * Converts an instance. A phrase boundary is inserted where
+   * a number is found.
+   */
+  private void convertInstance(Instance instance) throws Exception {
+  
+    double[] instVals = new double[instance.numAttributes()];
+
+    for (int i = 0; i < instance.numAttributes(); i++) {
+      if ((!instance.attribute(i).isString()) || instance.isMissing(i)) {
+	instVals[i] = instance.value(i);
+      } else {
+	String str = instance.stringValue(i);
+	StringBuffer resultStr = new StringBuffer();
+	StringTokenizer tok = new StringTokenizer(str, " \t\n", true);
+	while (tok.hasMoreTokens()) {
+	  String token = tok.nextToken();
+
+	  // Everything that doesn't contain at least
+	  // one letter is considered to be a number
+	  boolean isNumber = true;
+	  for (int j = 0; j < token.length(); j++) {
+	    if (Character.isLetter(token.charAt(j))) {
+	      isNumber = false;
+	      break;
+	    }
+	  }
+	  if (!isNumber) {
+	    resultStr.append(token);
+	  } else {
+	    if (token.equals(" ") || token.equals("\t") ||
+		token.equals("\n")) {
+	      resultStr.append(token);
+	    } else {
+	      resultStr.append(" \n ");
+	    }
+	  }
+	}
+	int index = getOutputFormat().attribute(i).addStringValue(resultStr.toString());
+	instVals[i] = (double)index;
+      }
+    }
+    Instance inst = new Instance(instance.weight(), instVals);
+    inst.setDataset(getOutputFormat());
+    push(inst);
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/README
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/README	(revision 16561)
@@ -0,0 +1,284 @@
+=====================================================================
+                                                                       
+                              ======                                   
+                              README                                   
+                              ======                                   
+                                                                       
+                             KEA 3.0
+                          18 March 2004
+                                                                       
+           Java Programs for Automatic Keyphrase Extraction
+                                                                       
+               Copyright (C) 2000, 2001, 2004  Eibe Frank
+
+                   email: eibe@cs.waikato.ac.nz            
+                                                                       
+=====================================================================
+
+Contents:
+---------
+
+1. Installation
+
+2. Getting started
+
+   - Building a keyphrase extraction model
+   - Extracting keyphrases
+   - Important comment
+
+3. Examples
+
+4. Other documentation
+
+5. Copyright
+
+----------------------------------------------------------------------
+
+NOTE:
+-----
+
+This distribution includes a cut-down version of WEKA, the GPL'ed
+machine learning workbench available from 
+
+  http://www.cs.waikato.ac.nz/ml/weka.
+
+----------------------------------------------------------------------
+
+1. Installation:
+----------------
+
+KEA is implemented as a set of Java classes (located in the same
+directory as this README file). To run KEA you need to tell the Java
+Virtual Machine where to look for KEA classes.  One possible way of
+doing this is to add the directory that contains this README file to
+the CLASSPATH environment variable that is used by the Java Virtual
+Machine.
+
+Under Linux you would do the following:
+
+a) Set KEAHOME to be the directory which contains this README.
+
+b) Add $KEAHOME to your CLASSPATH environment variable.
+
+The on-line documentation (generated from the source code) is located
+in the doc directory. You might want to do the following to have the
+documentation handy in you web browser:
+
+c) Bookmark $KEAHOME/doc/packages.html in your web browser.
+
+----------------------------------------------------------------------
+
+2. Getting started:
+-------------------
+
+Building a keyphrase extraction model
+=====================================
+
+To extract keyphrases for new documents, you first need to build a KEA
+keyphrase extraction model from a set of documents (preferably from
+the same domain) for which you have author- assigned keyphrases. To
+this end you have to go through the following steps:
+
+a) Create a directory, called, for example, "training_documents",
+   containing the documents that you want to use for training the
+   keyphrase extractor.
+
+b) Rename the document files in that directory so that they end with
+   the suffix ".txt".
+
+c) Delete the author-assigned keyphrases from those documents
+   and put them into separate ".key" files. For example, if
+   your document file is called doc1.txt, move the keyphrases
+   into a new file called "doc1.key". It is important that
+   you put each keyphrase on a separate line in the .key file!
+
+d) Build the keyphrase extraction model by running the
+   KEAModelBuilder:
+
+   java KEAModelBuilder -l <name_of_directory> -m <name_of_model>
+
+   This will use the documents in <name_of_directory> to build a
+   keyphrase extraction model and save it in <name_of_model>.
+
+KEAModelBuilder has a few other options that you can view if you run
+KEAModelBuilder without any arguments. Here is a list of all the
+options:
+
+-l <directory name>
+	Specifies name of directory.
+-m <model name>
+	Specifies name of model.
+-e <encoding>
+	Specifies encoding.
+-d
+	Turns debugging mode on.
+-k
+	Use keyphrase frequency statistic.
+-p
+	Disallow internal periods.
+-x <length>
+	Sets the maximum phrase length (default: 3).
+-y <length>
+	Sets the minimum phrase length (default: 1).
+-o <number>
+	The minimum number of times a phrase needs to occur 
+	(default: 2).
+-s <name of stopwords class>
+        Sets the list of stopwords to use (default: StopwordsEnglish).
+-t <name of stemmer class>
+        Set the stemmer to use (default: IteratedLovinsStemmer).
+-n
+        Do not check for proper nouns.
+   
+The -e option allows you to specify a different character encoding
+supported by Java. For example, to extract keyphrases from Chinese
+documents encoded using GBK, you would use specify "-e GBK" as an
+argument.
+
+The -d option generates some output that shows the progress of the
+model builder.
+
+If -k is set, the keyphrase frequency attribute is used in the
+model. For more info on this, have a look at the paper on
+"Domain-specific keyphrase extraction" listed below. Using this option
+improves accuracy if the domain of the documents for which you want to
+extract keyphrases is the same as the domain of the training
+documents. In other words, if you want to extract keyphrases from
+papers on radiology, and your training documents are about radiology,
+you should use this option.
+
+If -p is set, KEA does not consider phrases with internal periods as
+candidate keyphrases. It is important to use this if a full stop is
+not always followed by white space in the documents.
+
+Using -s and -t you can set different classes for stopword detection
+and stemming respectively (for languages other than English).
+
+Using -d you turn KEA's heuristic for detecting proper nouns off. This
+is important for languages like German, where all nouns start with an
+uppercase letter, not just proper nouns.
+
+Extracting keyphrases
+=====================
+
+To extract keyphrases for some documents, put them into an empty
+directory. Then rename them so that they end with the suffix ".txt".
+
+If you've previously built a keyphrase extraction model you can now
+apply keyphrases for these documents using:
+
+java KEAKeyphraseExtractor -l <name_of_directory> -m <name_of_model>
+
+This will create a ".key" file for each document in the
+directory. Each file will contain five extracted keyphrases for the
+corresponding document.
+
+If a ".key" file is already present it won't be overwritten.  Instead,
+the keyphrases present in that file will be used to evaluate the
+extraction model. The stemmed extracted phrases are compared to the
+stemmed versions of the phrases in the ".key"
+file. KEAKeyphraseExtractor reports the number of hits among the total
+number of extracted phrases for those documents that have associated
+".key" files in the directory.
+
+KEAKeyphraseExtractor has a few options. Here they are:
+
+-l <directory name>
+	Specifies name of directory.
+-m <model name>
+	Specifies name of model.
+-e <encoding>
+	Specifies encoding.
+-n
+	Specifies number of phrases to be output (default: 5).
+-d
+	Turns debugging mode on.
+-a
+	Also write stemmed phrase and score into ".key" file.
+
+Important comment
+-----------------
+
+To get good results, it is important that the input text for KEA is as
+"clean" as possible. That means html tags etc. in the input documents
+need to be deleted before the model is built and before keyphrases are
+extracted from new documents.
+
+----------------------------------------------------------------------
+
+3. Examples:
+------------
+
+The directory contains two example collections, each split up into a
+train and test directory. Note that these collections are only
+included to show how the system can be applied to actual documents.
+Due to the lack of data, the accuracy isn't very good on either
+example collection.
+
+Collection A
+------------
+
+A collection of abstracts taken from computer science technical
+reports:
+
+   CSTR_abstracts_train
+   CSTR_abstracts_test
+
+To build a model from the training data, try:
+
+   java KEAModelBuilder -l CSTR_abstracts_train -m CSTR_abstracts_model
+
+To evaluate that model on the test data, try:
+
+   java KEAKeyphraseExtractor -l CSTR_abstracts_test -m CSTR_abstracts_model
+
+Collection B
+------------
+
+A small collection of Chinese documents (in GBK encoding):
+
+   Journals_train
+   Journals_test
+
+To build a model from the training data, try:
+
+   java KEAModelBuilder -l Chinese_train -m Chinese_model -e GBK
+
+To evaluate that model on the test data, try:
+
+   java KEAKeyphraseExtractor -l Chinese_test -m Chinese_model -e GBK 
+
+----------------------------------------------------------------------
+
+4. Other documentation:
+-----------------------
+
+There are several papers on the KEA algorithm, listed below. Note that
+this implementation differs slightly from the version described in the
+papers, mainly in the pre-processing step (i.e. in the way candidate
+keyphrases are generated). For more info on the new method please
+consult the online documentation.
+
+Witten I.H., Paynter G.W., Frank E., Gutwin C. and Nevill-Manning
+C.G. (2000) "KEA: Practical automatic keyphrase extraction." Working
+Paper 00/5, Department of Computer Science, The University of Waikato.
+
+Witten I.H., Paynter G.W., Frank E., Gutwin C. and Nevill-Manning
+C.G. (1999) "KEA: Practical automatic keyphrase extraction." Proc. DL
+'99, pp. 254-256. (Poster presentation.)
+
+Frank E., Paynter G.W., Witten I.H., Gutwin C. and Nevill-Manning
+C.G. (1999) "Domain-specific keyphrase extraction" Proc. Sixteenth
+International Joint Conference on Artificial Intelligence, Morgan
+Kaufmann Publishers, San Francisco, CA, pp. 668-673.
+
+-----------------------------------------------------------------------
+
+5. Copyright:
+-------------
+
+KEA is distributed under the GNU public license. Please read the file
+COPYING.
+
+-----------------------------------------------------------------------
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stemmer.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stemmer.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stemmer.java	(revision 16561)
@@ -0,0 +1,68 @@
+/*
+ *    Stemmer.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Abstract class for stemmers.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public abstract class Stemmer implements Serializable {
+
+  /**
+   * Iterated stemming of the given word.
+   */
+  public abstract String stem(String str);
+
+  /**
+   * Stems everything in the given string.
+   */
+  public String stemString(String str) {
+
+    StringBuffer result = new StringBuffer();
+    int start = -1;
+    for (int j = 0; j < str.length(); j++) {
+      char c = str.charAt(j);
+      if (Character.isLetterOrDigit(c)) {
+	if (start == -1) {
+	  start = j;
+	}
+      } else if (c == '\'') {
+	if (start == -1) {
+	  result.append(c);
+	}
+      } else {
+	if (start != -1) {
+	  result.append(stem(str.substring(start, j)));
+	  start = -1;
+	}
+	result.append(c);
+      }
+    }
+    if (start != -1) {
+      result.append(stem(str.substring(start, str.length())));
+    }
+    return result.toString();  
+  }
+}
+    
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stopwords.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stopwords.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/Stopwords.java	(revision 16561)
@@ -0,0 +1,38 @@
+/*
+ *    Stopwords.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Class that can test whether a given string is a stop word.
+ * Lowercases all words before the test.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public abstract class Stopwords implements Serializable {
+  
+  /** 
+   * Returns true if the given string is a stop word.
+   */
+  public abstract boolean isStopword(String str);
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsEnglish.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsEnglish.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsEnglish.java	(revision 16561)
@@ -0,0 +1,504 @@
+/*
+ *    StopwordsEnglish.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+
+/**
+ * Class that can test whether a given string is a stop word.
+ * Lowercases all words before the test.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class StopwordsEnglish extends Stopwords {
+  
+  /** The hashtable containing the list of stopwords */
+  private static Hashtable m_Stopwords = null;
+
+  static {
+   
+    if (m_Stopwords == null) {
+      m_Stopwords = new Hashtable();
+      Double dummy = new Double(0);
+      
+      m_Stopwords.put("a", dummy);
+      m_Stopwords.put("abaft", dummy);
+      m_Stopwords.put("aboard", dummy);
+      m_Stopwords.put("about", dummy);
+      m_Stopwords.put("above", dummy);
+      m_Stopwords.put("across", dummy);
+      m_Stopwords.put("afore", dummy);
+      m_Stopwords.put("aforesaid", dummy);
+      m_Stopwords.put("after", dummy);
+      m_Stopwords.put("again", dummy);
+      m_Stopwords.put("against", dummy);
+      m_Stopwords.put("agin", dummy);
+      m_Stopwords.put("ago", dummy);
+      m_Stopwords.put("aint", dummy);
+      m_Stopwords.put("albeit", dummy);
+      m_Stopwords.put("all", dummy);
+      m_Stopwords.put("almost", dummy);
+      m_Stopwords.put("alone", dummy);
+      m_Stopwords.put("along", dummy);
+      m_Stopwords.put("alongside", dummy);
+      m_Stopwords.put("already", dummy);
+      m_Stopwords.put("also", dummy);
+      m_Stopwords.put("although", dummy);
+      m_Stopwords.put("always", dummy);
+      m_Stopwords.put("am", dummy);
+      m_Stopwords.put("american", dummy);
+      m_Stopwords.put("amid", dummy);
+      m_Stopwords.put("amidst", dummy);
+      m_Stopwords.put("among", dummy);
+      m_Stopwords.put("amongst", dummy);
+      m_Stopwords.put("an", dummy);
+      m_Stopwords.put("and", dummy);
+      m_Stopwords.put("anent", dummy);
+      m_Stopwords.put("another", dummy);
+      m_Stopwords.put("any", dummy);
+      m_Stopwords.put("anybody", dummy);
+      m_Stopwords.put("anyone", dummy);
+      m_Stopwords.put("anything", dummy);
+      m_Stopwords.put("are", dummy);
+      m_Stopwords.put("aren't", dummy);
+      m_Stopwords.put("around", dummy);
+      m_Stopwords.put("as", dummy);
+      m_Stopwords.put("aslant", dummy);
+      m_Stopwords.put("astride", dummy);
+      m_Stopwords.put("at", dummy);
+      m_Stopwords.put("athwart", dummy);
+      m_Stopwords.put("away", dummy);
+      m_Stopwords.put("b", dummy);
+      m_Stopwords.put("back", dummy);
+      m_Stopwords.put("bar", dummy);
+      m_Stopwords.put("barring", dummy);
+      m_Stopwords.put("be", dummy);
+      m_Stopwords.put("because", dummy);
+      m_Stopwords.put("been", dummy);
+      m_Stopwords.put("before", dummy);
+      m_Stopwords.put("behind", dummy);
+      m_Stopwords.put("being", dummy);
+      m_Stopwords.put("below", dummy);
+      m_Stopwords.put("beneath", dummy);
+      m_Stopwords.put("beside", dummy);
+      m_Stopwords.put("besides", dummy);
+      m_Stopwords.put("best", dummy);
+      m_Stopwords.put("better", dummy);
+      m_Stopwords.put("between", dummy);
+      m_Stopwords.put("betwixt", dummy);
+      m_Stopwords.put("beyond", dummy);
+      m_Stopwords.put("both", dummy);
+      m_Stopwords.put("but", dummy);
+      m_Stopwords.put("by", dummy);
+      m_Stopwords.put("c", dummy);
+      m_Stopwords.put("can", dummy);
+      m_Stopwords.put("cannot", dummy);
+      m_Stopwords.put("can't", dummy);
+      m_Stopwords.put("certain", dummy);
+      m_Stopwords.put("circa", dummy);
+      m_Stopwords.put("close", dummy);
+      m_Stopwords.put("concerning", dummy);
+      m_Stopwords.put("considering", dummy);
+      m_Stopwords.put("cos", dummy);
+      m_Stopwords.put("could", dummy);
+      m_Stopwords.put("couldn't", dummy);
+      m_Stopwords.put("couldst", dummy);
+      m_Stopwords.put("d", dummy);
+      m_Stopwords.put("dare", dummy);
+      m_Stopwords.put("dared", dummy);
+      m_Stopwords.put("daren't", dummy);
+      m_Stopwords.put("dares", dummy);
+      m_Stopwords.put("daring", dummy);
+      m_Stopwords.put("despite", dummy);
+      m_Stopwords.put("did", dummy);
+      m_Stopwords.put("didn't", dummy);
+      m_Stopwords.put("different", dummy);
+      m_Stopwords.put("directly", dummy);
+      m_Stopwords.put("do", dummy);
+      m_Stopwords.put("does", dummy);
+      m_Stopwords.put("doesn't", dummy);
+      m_Stopwords.put("doing", dummy);
+      m_Stopwords.put("done", dummy);
+      m_Stopwords.put("don't", dummy);
+      m_Stopwords.put("dost", dummy);
+      m_Stopwords.put("doth", dummy);
+      m_Stopwords.put("down", dummy);
+      m_Stopwords.put("during", dummy);
+      m_Stopwords.put("durst", dummy);
+      m_Stopwords.put("e", dummy);
+      m_Stopwords.put("each", dummy);
+      m_Stopwords.put("early", dummy);
+      m_Stopwords.put("either", dummy);
+      m_Stopwords.put("em", dummy);
+      m_Stopwords.put("english", dummy);
+      m_Stopwords.put("enough", dummy);
+      m_Stopwords.put("ere", dummy);
+      m_Stopwords.put("even", dummy);
+      m_Stopwords.put("ever", dummy);
+      m_Stopwords.put("every", dummy);
+      m_Stopwords.put("everybody", dummy);
+      m_Stopwords.put("everyone", dummy);
+      m_Stopwords.put("everything", dummy);
+      m_Stopwords.put("except", dummy);
+      m_Stopwords.put("excepting", dummy);
+      m_Stopwords.put("f", dummy);
+      m_Stopwords.put("failing", dummy);
+      m_Stopwords.put("far", dummy);
+      m_Stopwords.put("few", dummy);
+      m_Stopwords.put("first", dummy);
+      m_Stopwords.put("five", dummy);
+      m_Stopwords.put("following", dummy);
+      m_Stopwords.put("for", dummy);
+      m_Stopwords.put("four", dummy);
+      m_Stopwords.put("from", dummy);
+      m_Stopwords.put("g", dummy);
+      m_Stopwords.put("gonna", dummy);
+      m_Stopwords.put("gotta", dummy);
+      m_Stopwords.put("h", dummy);
+      m_Stopwords.put("had", dummy);
+      m_Stopwords.put("hadn't", dummy);
+      m_Stopwords.put("hard", dummy);
+      m_Stopwords.put("has", dummy);
+      m_Stopwords.put("hasn't", dummy);
+      m_Stopwords.put("hast", dummy);
+      m_Stopwords.put("hath", dummy);
+      m_Stopwords.put("have", dummy);
+      m_Stopwords.put("haven't", dummy);
+      m_Stopwords.put("having", dummy);
+      m_Stopwords.put("he", dummy);
+      m_Stopwords.put("he'd", dummy);
+      m_Stopwords.put("he'll", dummy);
+      m_Stopwords.put("her", dummy);
+      m_Stopwords.put("here", dummy);
+      m_Stopwords.put("here's", dummy);
+      m_Stopwords.put("hers", dummy);
+      m_Stopwords.put("herself", dummy);
+      m_Stopwords.put("he's", dummy);
+      m_Stopwords.put("high", dummy);
+      m_Stopwords.put("him", dummy);
+      m_Stopwords.put("himself", dummy);
+      m_Stopwords.put("his", dummy);
+      m_Stopwords.put("home", dummy);
+      m_Stopwords.put("how", dummy);
+      m_Stopwords.put("howbeit", dummy);
+      m_Stopwords.put("however", dummy);
+      m_Stopwords.put("how's", dummy);
+      m_Stopwords.put("i", dummy);
+      m_Stopwords.put("id", dummy);
+      m_Stopwords.put("if", dummy);
+      m_Stopwords.put("ill", dummy);
+      m_Stopwords.put("i'm", dummy);
+      m_Stopwords.put("immediately", dummy);
+      m_Stopwords.put("important", dummy);
+      m_Stopwords.put("in", dummy);
+      m_Stopwords.put("inside", dummy);
+      m_Stopwords.put("instantly", dummy);
+      m_Stopwords.put("into", dummy);
+      m_Stopwords.put("is", dummy);
+      m_Stopwords.put("isn't", dummy);
+      m_Stopwords.put("it", dummy);
+      m_Stopwords.put("it'll", dummy);
+      m_Stopwords.put("it's", dummy);
+      m_Stopwords.put("its", dummy);
+      m_Stopwords.put("itself", dummy);
+      m_Stopwords.put("i've", dummy);
+      m_Stopwords.put("j", dummy);
+      m_Stopwords.put("just", dummy);
+      m_Stopwords.put("k", dummy);
+      m_Stopwords.put("l", dummy);
+      m_Stopwords.put("large", dummy);
+      m_Stopwords.put("last", dummy);
+      m_Stopwords.put("later", dummy);
+      m_Stopwords.put("least", dummy);
+      m_Stopwords.put("left", dummy);
+      m_Stopwords.put("less", dummy);
+      m_Stopwords.put("lest", dummy);
+      m_Stopwords.put("let's", dummy);
+      m_Stopwords.put("like", dummy);
+      m_Stopwords.put("likewise", dummy);
+      m_Stopwords.put("little", dummy);
+      m_Stopwords.put("living", dummy);
+      m_Stopwords.put("long", dummy);
+      m_Stopwords.put("m", dummy);
+      m_Stopwords.put("many", dummy);
+      m_Stopwords.put("may", dummy);
+      m_Stopwords.put("mayn't", dummy);
+      m_Stopwords.put("me", dummy);
+      m_Stopwords.put("mid", dummy);
+      m_Stopwords.put("midst", dummy);
+      m_Stopwords.put("might", dummy);
+      m_Stopwords.put("mightn't", dummy);
+      m_Stopwords.put("mine", dummy);
+      m_Stopwords.put("minus", dummy);
+      m_Stopwords.put("more", dummy);
+      m_Stopwords.put("most", dummy);
+      m_Stopwords.put("much", dummy);
+      m_Stopwords.put("must", dummy);
+      m_Stopwords.put("mustn't", dummy);
+      m_Stopwords.put("my", dummy);
+      m_Stopwords.put("myself", dummy);
+      m_Stopwords.put("n", dummy);
+      m_Stopwords.put("near", dummy);
+      m_Stopwords.put("'neath", dummy);
+      m_Stopwords.put("need", dummy);
+      m_Stopwords.put("needed", dummy);
+      m_Stopwords.put("needing", dummy);
+      m_Stopwords.put("needn't", dummy);
+      m_Stopwords.put("needs", dummy);
+      m_Stopwords.put("neither", dummy);
+      m_Stopwords.put("never", dummy);
+      m_Stopwords.put("nevertheless", dummy);
+      m_Stopwords.put("new", dummy);
+      m_Stopwords.put("next", dummy);
+      m_Stopwords.put("nigh", dummy);
+      m_Stopwords.put("nigher", dummy);
+      m_Stopwords.put("nighest", dummy);
+      m_Stopwords.put("nisi", dummy);
+      m_Stopwords.put("no", dummy);
+      m_Stopwords.put("no-one", dummy);
+      m_Stopwords.put("nobody", dummy);
+      m_Stopwords.put("none", dummy);
+      m_Stopwords.put("nor", dummy);
+      m_Stopwords.put("not", dummy);
+      m_Stopwords.put("nothing", dummy);
+      m_Stopwords.put("notwithstanding", dummy);
+      m_Stopwords.put("now", dummy);
+      m_Stopwords.put("o", dummy);
+      m_Stopwords.put("o'er", dummy);
+      m_Stopwords.put("of", dummy);
+      m_Stopwords.put("off", dummy);
+      m_Stopwords.put("often", dummy);
+      m_Stopwords.put("on", dummy);
+      m_Stopwords.put("once", dummy);
+      m_Stopwords.put("one", dummy);
+      m_Stopwords.put("oneself", dummy);
+      m_Stopwords.put("only", dummy);
+      m_Stopwords.put("onto", dummy);
+      m_Stopwords.put("open", dummy);
+      m_Stopwords.put("or", dummy);
+      m_Stopwords.put("other", dummy);
+      m_Stopwords.put("otherwise", dummy);
+      m_Stopwords.put("ought", dummy);
+      m_Stopwords.put("oughtn't", dummy);
+      m_Stopwords.put("our", dummy);
+      m_Stopwords.put("ours", dummy);
+      m_Stopwords.put("ourselves", dummy);
+      m_Stopwords.put("out", dummy);
+      m_Stopwords.put("outside", dummy);
+      m_Stopwords.put("over", dummy);
+      m_Stopwords.put("own", dummy);
+      m_Stopwords.put("p", dummy);
+      m_Stopwords.put("past", dummy);
+      m_Stopwords.put("pending", dummy);
+      m_Stopwords.put("per", dummy);
+      m_Stopwords.put("perhaps", dummy);
+      m_Stopwords.put("plus", dummy);
+      m_Stopwords.put("possible", dummy);
+      m_Stopwords.put("present", dummy);
+      m_Stopwords.put("probably", dummy);
+      m_Stopwords.put("provided", dummy);
+      m_Stopwords.put("providing", dummy);
+      m_Stopwords.put("public", dummy);
+      m_Stopwords.put("q", dummy);
+      m_Stopwords.put("qua", dummy);
+      m_Stopwords.put("quite", dummy);
+      m_Stopwords.put("r", dummy);
+      m_Stopwords.put("rather", dummy);
+      m_Stopwords.put("re", dummy);
+      m_Stopwords.put("real", dummy);
+      m_Stopwords.put("really", dummy);
+      m_Stopwords.put("respecting", dummy);
+      m_Stopwords.put("right", dummy);
+      m_Stopwords.put("round", dummy);
+      m_Stopwords.put("s", dummy);
+      m_Stopwords.put("same", dummy);
+      m_Stopwords.put("sans", dummy);
+      m_Stopwords.put("save", dummy);
+      m_Stopwords.put("saving", dummy);
+      m_Stopwords.put("second", dummy);
+      m_Stopwords.put("several", dummy);
+      m_Stopwords.put("shall", dummy);
+      m_Stopwords.put("shalt", dummy);
+      m_Stopwords.put("shan't", dummy);
+      m_Stopwords.put("she", dummy);
+      m_Stopwords.put("shed", dummy);
+      m_Stopwords.put("shell", dummy);
+      m_Stopwords.put("she's", dummy);
+      m_Stopwords.put("short", dummy);
+      m_Stopwords.put("should", dummy);
+      m_Stopwords.put("shouldn't", dummy);
+      m_Stopwords.put("since", dummy);
+      m_Stopwords.put("six", dummy);
+      m_Stopwords.put("small", dummy);
+      m_Stopwords.put("so", dummy);
+      m_Stopwords.put("some", dummy);
+      m_Stopwords.put("somebody", dummy);
+      m_Stopwords.put("someone", dummy);
+      m_Stopwords.put("something", dummy);
+      m_Stopwords.put("sometimes", dummy);
+      m_Stopwords.put("soon", dummy);
+      m_Stopwords.put("special", dummy);
+      m_Stopwords.put("still", dummy);
+      m_Stopwords.put("such", dummy);
+      m_Stopwords.put("summat", dummy);
+      m_Stopwords.put("supposing", dummy);
+      m_Stopwords.put("sure", dummy);
+      m_Stopwords.put("t", dummy);
+      m_Stopwords.put("than", dummy);
+      m_Stopwords.put("that", dummy);
+      m_Stopwords.put("that'd", dummy);
+      m_Stopwords.put("that'll", dummy);
+      m_Stopwords.put("that's", dummy);
+      m_Stopwords.put("the", dummy);
+      m_Stopwords.put("thee", dummy);
+      m_Stopwords.put("their", dummy);
+      m_Stopwords.put("theirs", dummy);
+      m_Stopwords.put("their's", dummy);
+      m_Stopwords.put("them", dummy);
+      m_Stopwords.put("themselves", dummy);
+      m_Stopwords.put("then", dummy);
+      m_Stopwords.put("there", dummy);
+      m_Stopwords.put("there's", dummy);
+      m_Stopwords.put("these", dummy);
+      m_Stopwords.put("they", dummy);
+      m_Stopwords.put("they'd", dummy);
+      m_Stopwords.put("they'll", dummy);
+      m_Stopwords.put("they're", dummy);
+      m_Stopwords.put("they've", dummy);
+      m_Stopwords.put("thine", dummy);
+      m_Stopwords.put("this", dummy);
+      m_Stopwords.put("tho", dummy);
+      m_Stopwords.put("those", dummy);
+      m_Stopwords.put("thou", dummy);
+      m_Stopwords.put("though", dummy);
+      m_Stopwords.put("three", dummy);
+      m_Stopwords.put("thro'", dummy);
+      m_Stopwords.put("through", dummy);
+      m_Stopwords.put("throughout", dummy);
+      m_Stopwords.put("thru", dummy);
+      m_Stopwords.put("thyself", dummy);
+      m_Stopwords.put("till", dummy);
+      m_Stopwords.put("to", dummy);
+      m_Stopwords.put("today", dummy);
+      m_Stopwords.put("together", dummy);
+      m_Stopwords.put("too", dummy);
+      m_Stopwords.put("touching", dummy);
+      m_Stopwords.put("toward", dummy);
+      m_Stopwords.put("towards", dummy);
+      m_Stopwords.put("true", dummy);
+      m_Stopwords.put("'twas", dummy);
+      m_Stopwords.put("'tween", dummy);
+      m_Stopwords.put("'twere", dummy);
+      m_Stopwords.put("'twill", dummy);
+      m_Stopwords.put("'twixt", dummy);
+      m_Stopwords.put("two", dummy);
+      m_Stopwords.put("'twould", dummy);
+      m_Stopwords.put("u", dummy);
+      m_Stopwords.put("under", dummy);
+      m_Stopwords.put("underneath", dummy);
+      m_Stopwords.put("unless", dummy);
+      m_Stopwords.put("unlike", dummy);
+      m_Stopwords.put("until", dummy);
+      m_Stopwords.put("unto", dummy);
+      m_Stopwords.put("up", dummy);
+      m_Stopwords.put("upon", dummy);
+      m_Stopwords.put("us", dummy);
+      m_Stopwords.put("used", dummy);
+      m_Stopwords.put("usually", dummy);
+      m_Stopwords.put("v", dummy);
+      m_Stopwords.put("versus", dummy);
+      m_Stopwords.put("very", dummy);
+      m_Stopwords.put("via", dummy);
+      m_Stopwords.put("vice", dummy);
+      m_Stopwords.put("vis-a-vis", dummy);
+      m_Stopwords.put("w", dummy);
+      m_Stopwords.put("wanna", dummy);
+      m_Stopwords.put("wanting", dummy);
+      m_Stopwords.put("was", dummy);
+      m_Stopwords.put("wasn't", dummy);
+      m_Stopwords.put("way", dummy);
+      m_Stopwords.put("we", dummy);
+      m_Stopwords.put("we'd", dummy);
+      m_Stopwords.put("well", dummy);
+      m_Stopwords.put("were", dummy);
+      m_Stopwords.put("weren't", dummy);
+      m_Stopwords.put("wert", dummy);
+      m_Stopwords.put("we've", dummy);
+      m_Stopwords.put("what", dummy);
+      m_Stopwords.put("whatever", dummy);
+      m_Stopwords.put("what'll", dummy);
+      m_Stopwords.put("what's", dummy);
+      m_Stopwords.put("when", dummy);
+      m_Stopwords.put("whencesoever", dummy);
+      m_Stopwords.put("whenever", dummy);
+      m_Stopwords.put("when's", dummy);
+      m_Stopwords.put("whereas", dummy);
+      m_Stopwords.put("where's", dummy);
+      m_Stopwords.put("whether", dummy);
+      m_Stopwords.put("which", dummy);
+      m_Stopwords.put("whichever", dummy);
+      m_Stopwords.put("whichsoever", dummy);
+      m_Stopwords.put("while", dummy);
+      m_Stopwords.put("whilst", dummy);
+      m_Stopwords.put("who", dummy);
+      m_Stopwords.put("who'd", dummy);
+      m_Stopwords.put("whoever", dummy);
+      m_Stopwords.put("whole", dummy);
+      m_Stopwords.put("who'll", dummy);
+      m_Stopwords.put("whom", dummy);
+      m_Stopwords.put("whore", dummy);
+      m_Stopwords.put("who's", dummy);
+      m_Stopwords.put("whose", dummy);
+      m_Stopwords.put("whoso", dummy);
+      m_Stopwords.put("whosoever", dummy);
+      m_Stopwords.put("will", dummy);
+      m_Stopwords.put("with", dummy);
+      m_Stopwords.put("within", dummy);
+      m_Stopwords.put("without", dummy);
+      m_Stopwords.put("wont", dummy);
+      m_Stopwords.put("would", dummy);
+      m_Stopwords.put("wouldn't", dummy);
+      m_Stopwords.put("wouldst", dummy);
+      m_Stopwords.put("x", dummy);
+      m_Stopwords.put("y", dummy);
+      m_Stopwords.put("ye", dummy);
+      m_Stopwords.put("yet", dummy);
+      m_Stopwords.put("you", dummy);
+      m_Stopwords.put("you'd", dummy);
+      m_Stopwords.put("you'll", dummy);
+      m_Stopwords.put("your", dummy);
+      m_Stopwords.put("you're", dummy);
+      m_Stopwords.put("yours", dummy);
+      m_Stopwords.put("yourself", dummy);
+      m_Stopwords.put("yourselves", dummy);
+      m_Stopwords.put("you've", dummy);
+      m_Stopwords.put("z", dummy);
+    }
+  }
+  
+  /** 
+   * Returns true if the given string is a stop word.
+   */
+  public boolean isStopword(String str) {
+
+    return m_Stopwords.containsKey(str.toLowerCase());
+  }
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsGerman.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsGerman.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/StopwordsGerman.java	(revision 16561)
@@ -0,0 +1,330 @@
+/*
+ *    StopwordsGerman.java
+ *    Copyright (C) 2001 Eibe Frank
+ *
+ *    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.
+ */
+
+import java.util.*;
+
+/**
+ * Class that can test whether a given string is a stop word.
+ * Lowercases all words before the test.
+ *
+ * This list of German stop words has been obtained from
+ * http://snowball.tartarus.org/german/stop.txt
+ *
+ * But I have deleted/changed some words that I haven't seen before.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version 1.0
+ */
+public class StopwordsGerman extends Stopwords {
+  
+  /** The hashtable containing the list of stopwords */
+  private static Hashtable m_Stopwords = null;
+
+  static {
+   
+    if (m_Stopwords == null) {
+      m_Stopwords = new Hashtable();
+      Double dummy = new Double(0);
+
+      m_Stopwords.put("aber", dummy);
+
+      m_Stopwords.put("alle", dummy);
+      m_Stopwords.put("allem", dummy);
+      m_Stopwords.put("allen", dummy);
+      m_Stopwords.put("aller", dummy);
+      m_Stopwords.put("alles", dummy);
+
+      m_Stopwords.put("als", dummy);
+      m_Stopwords.put("also", dummy);
+      m_Stopwords.put("am", dummy);
+      m_Stopwords.put("an", dummy);
+
+      m_Stopwords.put("ander", dummy);
+      m_Stopwords.put("andere", dummy);
+      m_Stopwords.put("anderem", dummy);
+      m_Stopwords.put("anderen", dummy);
+      m_Stopwords.put("anderer", dummy);
+      m_Stopwords.put("anderes", dummy);
+      m_Stopwords.put("anderm", dummy);
+      m_Stopwords.put("andern", dummy);
+      m_Stopwords.put("anders", dummy);
+
+      m_Stopwords.put("auch", dummy);
+      m_Stopwords.put("auf", dummy);
+      m_Stopwords.put("aus", dummy);
+      m_Stopwords.put("bei", dummy);
+      m_Stopwords.put("bin", dummy);
+      m_Stopwords.put("bis", dummy);
+      m_Stopwords.put("bist", dummy);
+      m_Stopwords.put("da", dummy);
+      m_Stopwords.put("damit", dummy);
+      m_Stopwords.put("dann", dummy);
+      
+      m_Stopwords.put("der", dummy);
+      m_Stopwords.put("den", dummy);
+      m_Stopwords.put("des", dummy);
+      m_Stopwords.put("dem", dummy);
+      m_Stopwords.put("die", dummy);
+      m_Stopwords.put("das", dummy);
+
+      m_Stopwords.put("da\u00df", dummy);
+
+      m_Stopwords.put("derselbe", dummy);
+      m_Stopwords.put("derselben", dummy);
+      m_Stopwords.put("denselben", dummy);
+      m_Stopwords.put("desselben", dummy);
+      m_Stopwords.put("demselben", dummy);
+      m_Stopwords.put("dieselbe", dummy);
+      m_Stopwords.put("dieselben", dummy);
+      m_Stopwords.put("dasselbe", dummy);
+
+      m_Stopwords.put("dazu", dummy);
+
+      m_Stopwords.put("dein", dummy);
+      m_Stopwords.put("deine", dummy);
+      m_Stopwords.put("deinem", dummy);
+      m_Stopwords.put("deinen", dummy);
+      m_Stopwords.put("deiner", dummy);
+      m_Stopwords.put("deines", dummy);
+
+      m_Stopwords.put("denn", dummy);
+      
+      m_Stopwords.put("derer", dummy);
+      m_Stopwords.put("dessen", dummy);
+      
+      m_Stopwords.put("dich", dummy);
+      m_Stopwords.put("dir", dummy);
+      m_Stopwords.put("du", dummy);
+
+      m_Stopwords.put("dies", dummy);
+      m_Stopwords.put("diese", dummy);
+      m_Stopwords.put("diesem", dummy);
+      m_Stopwords.put("diesen", dummy);
+      m_Stopwords.put("dieser", dummy);
+      m_Stopwords.put("dieses", dummy);
+
+      m_Stopwords.put("doch", dummy);
+      m_Stopwords.put("dort", dummy);
+
+      m_Stopwords.put("durch", dummy);
+
+      m_Stopwords.put("ein", dummy);
+      m_Stopwords.put("eine", dummy);
+      m_Stopwords.put("einem", dummy);
+      m_Stopwords.put("einen", dummy);
+      m_Stopwords.put("einer", dummy);
+      m_Stopwords.put("eines", dummy);
+
+      m_Stopwords.put("einig", dummy);
+      m_Stopwords.put("einige", dummy);
+      m_Stopwords.put("einigem", dummy);
+      m_Stopwords.put("einigen", dummy);
+      m_Stopwords.put("einiger", dummy);
+      m_Stopwords.put("einiges", dummy);
+
+      m_Stopwords.put("einmal", dummy);
+
+      m_Stopwords.put("er", dummy);
+      m_Stopwords.put("ihn", dummy);
+      m_Stopwords.put("ihm", dummy);
+
+      m_Stopwords.put("es", dummy);
+      m_Stopwords.put("etwas", dummy);
+
+      m_Stopwords.put("euer", dummy);
+      m_Stopwords.put("eure", dummy);
+      m_Stopwords.put("eurem", dummy);
+      m_Stopwords.put("euren", dummy);
+      m_Stopwords.put("eurer", dummy);
+      m_Stopwords.put("eures", dummy);
+
+      m_Stopwords.put("f\u00fcr", dummy);
+      m_Stopwords.put("gegen", dummy);
+      m_Stopwords.put("gewesen", dummy);
+      m_Stopwords.put("hab", dummy);
+      m_Stopwords.put("habe", dummy);
+      m_Stopwords.put("haben", dummy);
+      m_Stopwords.put("hat", dummy);
+      m_Stopwords.put("hatte", dummy);
+      m_Stopwords.put("hatten", dummy);
+      m_Stopwords.put("hier", dummy);
+      m_Stopwords.put("hin", dummy);
+      m_Stopwords.put("hinter", dummy);
+      
+      m_Stopwords.put("ich", dummy);
+      m_Stopwords.put("mich", dummy);
+      m_Stopwords.put("mir", dummy);
+
+      m_Stopwords.put("ihr", dummy);
+      m_Stopwords.put("ihre", dummy);
+      m_Stopwords.put("ihrem", dummy);
+      m_Stopwords.put("ihren", dummy);
+      m_Stopwords.put("ihrer", dummy);
+      m_Stopwords.put("ihres", dummy);
+      m_Stopwords.put("euch", dummy);
+
+      m_Stopwords.put("im", dummy);
+      m_Stopwords.put("in", dummy);
+      m_Stopwords.put("indem", dummy);
+      m_Stopwords.put("ins", dummy);
+      m_Stopwords.put("ist", dummy);
+
+      m_Stopwords.put("jede", dummy);
+      m_Stopwords.put("jedem", dummy);
+      m_Stopwords.put("jeden", dummy);
+      m_Stopwords.put("jeder", dummy);
+      m_Stopwords.put("jedes", dummy);
+
+      m_Stopwords.put("jene", dummy);
+      m_Stopwords.put("jenem", dummy);
+      m_Stopwords.put("jenen", dummy);
+      m_Stopwords.put("jener", dummy);
+      m_Stopwords.put("jenes", dummy);
+
+      m_Stopwords.put("jetzt", dummy);
+      m_Stopwords.put("kann", dummy);
+
+      m_Stopwords.put("kein", dummy);
+      m_Stopwords.put("keine", dummy);
+      m_Stopwords.put("keinem", dummy);
+      m_Stopwords.put("keinen", dummy);
+      m_Stopwords.put("keiner", dummy);
+      m_Stopwords.put("keines", dummy);
+
+      m_Stopwords.put("k\u00f6nnen", dummy);
+      m_Stopwords.put("k\u00f6nnte", dummy);
+      m_Stopwords.put("machen", dummy);
+      m_Stopwords.put("man", dummy);
+
+      m_Stopwords.put("manche", dummy);
+      m_Stopwords.put("manchem", dummy);
+      m_Stopwords.put("manchen", dummy);
+      m_Stopwords.put("mancher", dummy);
+      m_Stopwords.put("manches", dummy);
+
+      m_Stopwords.put("mein", dummy);
+      m_Stopwords.put("meine", dummy);
+      m_Stopwords.put("meinem", dummy);
+      m_Stopwords.put("meinen", dummy);
+      m_Stopwords.put("meiner", dummy);
+      m_Stopwords.put("meines", dummy);
+
+      m_Stopwords.put("mit", dummy);
+      m_Stopwords.put("muss", dummy);
+      m_Stopwords.put("musste", dummy);
+      m_Stopwords.put("nach", dummy);
+      m_Stopwords.put("nicht", dummy);
+      m_Stopwords.put("nichts", dummy);
+      m_Stopwords.put("noch", dummy);
+      m_Stopwords.put("nun", dummy);
+      m_Stopwords.put("nur", dummy);
+      m_Stopwords.put("ob", dummy);
+      m_Stopwords.put("oder", dummy);
+      m_Stopwords.put("ohne", dummy);
+      m_Stopwords.put("sehr", dummy);
+
+      m_Stopwords.put("sein", dummy);
+      m_Stopwords.put("seine", dummy);
+      m_Stopwords.put("seinem", dummy);
+      m_Stopwords.put("seinen", dummy);
+      m_Stopwords.put("seiner", dummy);
+      m_Stopwords.put("seines", dummy);
+
+      m_Stopwords.put("selbst", dummy);
+      m_Stopwords.put("sich", dummy);
+      
+      m_Stopwords.put("sie", dummy);
+      m_Stopwords.put("ihnen", dummy);
+
+      m_Stopwords.put("sind", dummy);
+      m_Stopwords.put("so", dummy);
+
+      m_Stopwords.put("solche", dummy);
+      m_Stopwords.put("solchem", dummy);
+      m_Stopwords.put("solchen", dummy);
+      m_Stopwords.put("solcher", dummy);
+      m_Stopwords.put("solches", dummy);
+
+      m_Stopwords.put("soll", dummy);
+      m_Stopwords.put("sollte", dummy);
+      m_Stopwords.put("sondern", dummy);
+      m_Stopwords.put("sonst", dummy);
+      m_Stopwords.put("\00fcber", dummy);
+      m_Stopwords.put("um", dummy);
+      m_Stopwords.put("und", dummy);
+
+      m_Stopwords.put("uns", dummy);
+      m_Stopwords.put("unser", dummy);
+      m_Stopwords.put("unserem", dummy);
+      m_Stopwords.put("unseren", dummy);
+      m_Stopwords.put("unsere", dummy);
+      m_Stopwords.put("unseres", dummy);
+
+      m_Stopwords.put("unter", dummy);
+      m_Stopwords.put("viel", dummy);
+      m_Stopwords.put("vom", dummy);
+      m_Stopwords.put("von", dummy);
+      m_Stopwords.put("vor", dummy);
+      m_Stopwords.put("w\u00e4hrend", dummy);
+      m_Stopwords.put("war", dummy);
+      m_Stopwords.put("waren", dummy);
+      m_Stopwords.put("warst", dummy);
+      m_Stopwords.put("was", dummy);
+      m_Stopwords.put("weg", dummy);
+      m_Stopwords.put("weil", dummy);
+      m_Stopwords.put("weiter", dummy);
+
+      m_Stopwords.put("welche", dummy);
+      m_Stopwords.put("welchem", dummy);
+      m_Stopwords.put("welchen", dummy);
+      m_Stopwords.put("welcher", dummy);
+      m_Stopwords.put("welches", dummy);
+
+      m_Stopwords.put("wenn", dummy);
+      m_Stopwords.put("werde", dummy);
+      m_Stopwords.put("werden", dummy);
+      m_Stopwords.put("wie", dummy);
+      m_Stopwords.put("wieder", dummy);
+      m_Stopwords.put("will", dummy);
+      m_Stopwords.put("wir", dummy);
+      m_Stopwords.put("wird", dummy);
+      m_Stopwords.put("wirst", dummy);
+      m_Stopwords.put("wo", dummy);
+      m_Stopwords.put("wollen", dummy);
+      m_Stopwords.put("wollte", dummy);
+      m_Stopwords.put("w\u00fcrde", dummy);
+      m_Stopwords.put("w\u00fcrden", dummy);
+      m_Stopwords.put("zu", dummy);
+      m_Stopwords.put("zum", dummy);
+      m_Stopwords.put("zur", dummy);
+      m_Stopwords.put("zwar", dummy);
+      m_Stopwords.put("zwischen", dummy);
+    }
+  }
+  
+  /** 
+   * Returns true if the given string is a stop word.
+   */
+  public boolean isStopword(String str) {
+
+    return m_Stopwords.containsKey(str.toLowerCase());
+  }
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Counter.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Counter.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Counter.html	(revision 16561)
@@ -0,0 +1,318 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+Counter
+</TITLE>
+<META NAME="keywords" CONTENT="Counter,Counter class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Counter";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV CLASS&nbsp;
+&nbsp;<A HREF="GermanStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Counter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class Counter</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<B>Counter</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>Counter</B><DT>extends java.lang.Object<DT>implements java.io.Serializable</DL>
+
+<P>
+Class that implements a simple counter.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="Counter">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="Counter.html#Counter()">Counter</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initializes the counter to 1</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="Counter.html#Counter(int)">Counter</A></B>(int&nbsp;val)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initializes the counter to the given value</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Counter.html#increment()">increment</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Increments the counter.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Counter.html#toString()">toString</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns string containing value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Counter.html#value()">value</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets the value.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="Counter()"><!-- --></A><H3>
+Counter</H3>
+<PRE>
+public <B>Counter</B>()</PRE>
+<DL>
+<DD>Initializes the counter to 1
+<P>
+</DL>
+<HR>
+
+<A NAME="Counter(int)"><!-- --></A><H3>
+Counter</H3>
+<PRE>
+public <B>Counter</B>(int&nbsp;val)</PRE>
+<DL>
+<DD>Initializes the counter to the given value
+<P>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="increment()"><!-- --></A><H3>
+increment</H3>
+<PRE>
+public void <B>increment</B>()</PRE>
+<DL>
+<DD>Increments the counter.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="value()"><!-- --></A><H3>
+value</H3>
+<PRE>
+public int <B>value</B>()</PRE>
+<DL>
+<DD>Gets the value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the value</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="toString()"><!-- --></A><H3>
+toString</H3>
+<PRE>
+public java.lang.String <B>toString</B>()</PRE>
+<DL>
+<DD>Returns string containing value.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>toString</CODE> in class <CODE>java.lang.Object</CODE></DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV CLASS&nbsp;
+&nbsp;<A HREF="GermanStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Counter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/GermanStemmer.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/GermanStemmer.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/GermanStemmer.html	(revision 16561)
@@ -0,0 +1,268 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+GermanStemmer
+</TITLE>
+<META NAME="keywords" CONTENT="GermanStemmer,GermanStemmer class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="GermanStemmer";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Counter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="IteratedLovinsStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="GermanStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class GermanStemmer</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<A HREF="Stemmer.html">Stemmer</A>
+        |
+        +--<B>GermanStemmer</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>GermanStemmer</B><DT>extends <A HREF="Stemmer.html">Stemmer</A></DL>
+
+<P>
+A stemmer for German words. The algorithm is based on the report
+ "A Fast and Simple Stemming Algorithm for German Words" by Jörg
+ Caumanns (joerg.caumanns@isst.fhg.de).
+
+ Changed stem() from protected to public.
+ Changed coding for umlaute to unicode.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="GermanStemmer">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="GermanStemmer.html#GermanStemmer()">GermanStemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="GermanStemmer.html#stem(java.lang.String)">stem</A></B>(java.lang.String&nbsp;term)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stemms the given term to an unique <tt>discriminator</tt>.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_Stemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class <A HREF="Stemmer.html">Stemmer</A></B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><A HREF="Stemmer.html#stemString(java.lang.String)">stemString</A></CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="GermanStemmer()"><!-- --></A><H3>
+GermanStemmer</H3>
+<PRE>
+public <B>GermanStemmer</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="stem(java.lang.String)"><!-- --></A><H3>
+stem</H3>
+<PRE>
+public java.lang.String <B>stem</B>(java.lang.String&nbsp;term)</PRE>
+<DL>
+<DD>Stemms the given term to an unique <tt>discriminator</tt>.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE><A HREF="Stemmer.html#stem(java.lang.String)">stem</A></CODE> in class <CODE><A HREF="Stemmer.html">Stemmer</A></CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>term</CODE> - The term that should be stemmed.
+<DT><B>Returns:</B><DD>Discriminator for <tt>term</tt></DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Counter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="IteratedLovinsStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="GermanStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/IteratedLovinsStemmer.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/IteratedLovinsStemmer.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/IteratedLovinsStemmer.html	(revision 16561)
@@ -0,0 +1,285 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+IteratedLovinsStemmer
+</TITLE>
+<META NAME="keywords" CONTENT="IteratedLovinsStemmer,IteratedLovinsStemmer class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="IteratedLovinsStemmer";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="GermanStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="IteratedLovinsStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class IteratedLovinsStemmer</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<A HREF="Stemmer.html">Stemmer</A>
+        |
+        +--<A HREF="LovinsStemmer.html">LovinsStemmer</A>
+              |
+              +--<B>IteratedLovinsStemmer</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>IteratedLovinsStemmer</B><DT>extends <A HREF="LovinsStemmer.html">LovinsStemmer</A></DL>
+
+<P>
+Implements the iterated version of the Lovins stemmer.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="IteratedLovinsStemmer">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="IteratedLovinsStemmer.html#IteratedLovinsStemmer()">IteratedLovinsStemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="IteratedLovinsStemmer.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;ops)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stems text coming into stdin and writes it to stdout.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="IteratedLovinsStemmer.html#stem(java.lang.String)">stem</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Iterated stemming of the given word.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_Stemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class <A HREF="Stemmer.html">Stemmer</A></B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><A HREF="Stemmer.html#stemString(java.lang.String)">stemString</A></CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="IteratedLovinsStemmer()"><!-- --></A><H3>
+IteratedLovinsStemmer</H3>
+<PRE>
+public <B>IteratedLovinsStemmer</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="stem(java.lang.String)"><!-- --></A><H3>
+stem</H3>
+<PRE>
+public java.lang.String <B>stem</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Iterated stemming of the given word.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE><A HREF="LovinsStemmer.html#stem(java.lang.String)">stem</A></CODE> in class <CODE><A HREF="LovinsStemmer.html">LovinsStemmer</A></CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>str</CODE> - a string consisting of a single word</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;ops)</PRE>
+<DL>
+<DD>Stems text coming into stdin and writes it to stdout.
+<P>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="GermanStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="IteratedLovinsStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAFilter.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAFilter.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAFilter.html	(revision 16561)
@@ -0,0 +1,1147 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+KEAFilter
+</TITLE>
+<META NAME="keywords" CONTENT="KEAFilter,KEAFilter class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="KEAFilter";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="IteratedLovinsStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAKeyphraseExtractor.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_weka.filters.Filter">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class KEAFilter</H2>
+<PRE>
+java.lang.Object
+  |
+  +--weka.filters.Filter
+        |
+        +--<B>KEAFilter</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>weka.core.OptionHandler, java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>KEAFilter</B><DT>extends weka.filters.Filter<DT>implements weka.core.OptionHandler</DL>
+
+<P>
+This filter converts the incoming data into data appropriate for
+ keyphrase classification. It assumes that the dataset contains two
+ string attributes. The first attribute should contain the text of a
+ document. The second attribute should contain the keyphrases
+ associated with that document (if present). 
+
+ The filter converts every instance (i.e. document) into a set of
+ instances, one for each word-based n-gram in the document. The
+ string attribute representing the document is replaced by some
+ numeric features, the estimated probability of each n-gram being a
+ keyphrase, and the rank of this phrase in the document according to
+ the probability.  Each new instances also has a class value
+ associated with it. The class is "true" if the n-gram is a true
+ keyphrase, and "false" otherwise. Of course, if the input document
+ doesn't come with author-assigned keyphrases, the class values for
+ that document will be missing.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="KEAFilter">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+<A NAME="field_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Field Summary</B></FONT></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="fields_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Fields inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>m_NewBatch</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="KEAFilter.html#KEAFilter()">KEAFilter</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#batchFinished()">batchFinished</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Signify that this batch of input to the filter is finished.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getCheckForProperNouns()">getCheckForProperNouns</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the M_CheckProperNouns value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getDebug()">getDebug</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of Debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getDisallowInternalPeriods()">getDisallowInternalPeriods</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get whether the supplied columns are to be processed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getDocumentAtt()">getDocumentAtt</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of DocumentAtt.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getKeyphrasesAtt()">getKeyphrasesAtt</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of KeyphraseAtt.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getKFused()">getKFused</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets whether keyphrase frequency attribute is used.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getMaxPhraseLength()">getMaxPhraseLength</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MaxPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getMinNumOccur()">getMinNumOccur</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MinNumOccur.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getMinPhraseLength()">getMinPhraseLength</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MinPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String[]</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getOptions()">getOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets the current settings of the filter.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getProbabilityIndex()">getProbabilityIndex</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the index of the phrases' probabilities in the output ARFF file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getRankIndex()">getRankIndex</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the index of the phrases' ranks in the output ARFF file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getStemmedPhraseIndex()">getStemmedPhraseIndex</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the index of the stemmed phrases in the output ARFF file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;Stemmer</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getStemmer()">getStemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the Stemmer value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;Stopwords</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getStopwords()">getStopwords</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the M_Stopwords value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#getUnstemmedPhraseIndex()">getUnstemmedPhraseIndex</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the index of the unstemmed phrases in the output ARFF file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#globalInfo()">globalInfo</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns a string describing this filter</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#input(weka.core.Instance)">input</A></B>(weka.core.Instance&nbsp;instance)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Input an instance for filtering.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Enumeration</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#listOptions()">listOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns an enumeration describing the available options</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;argv)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Main method for testing this class.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setCheckForProperNouns(boolean)">setCheckForProperNouns</A></B>(boolean&nbsp;newM_CheckProperNouns)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the M_CheckProperNouns value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setDebug(boolean)">setDebug</A></B>(boolean&nbsp;newDebug)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of Debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setDisallowInternalPeriods(boolean)">setDisallowInternalPeriods</A></B>(boolean&nbsp;disallow)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set whether selected columns should be processed.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setDocumentAtt(int)">setDocumentAtt</A></B>(int&nbsp;newDocumentAtt)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of DocumentAtt.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setInputFormat(weka.core.Instances)">setInputFormat</A></B>(weka.core.Instances&nbsp;instanceInfo)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the format of the input instances.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setKeyphrasesAtt(int)">setKeyphrasesAtt</A></B>(int&nbsp;newKeyphrasesAtt)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of KeyphrasesAtt.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setKFused(boolean)">setKFused</A></B>(boolean&nbsp;flag)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets whether keyphrase frequency attribute is used.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setMaxPhraseLength(int)">setMaxPhraseLength</A></B>(int&nbsp;newMaxPhraseLength)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MaxPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setMinNumOccur(int)">setMinNumOccur</A></B>(int&nbsp;newMinNumOccur)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MinNumOccur.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setMinPhraseLength(int)">setMinPhraseLength</A></B>(int&nbsp;newMinPhraseLength)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MinPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setOptions(java.lang.String[])">setOptions</A></B>(java.lang.String[]&nbsp;options)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parses a given list of options controlling the behaviour of this object.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setStemmer(Stemmer)">setStemmer</A></B>(Stemmer&nbsp;newStemmer)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the Stemmer value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAFilter.html#setStopwords(Stopwords)">setStopwords</A></B>(Stopwords&nbsp;newM_Stopwords)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the M_Stopwords value.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>batchFilterFile, bufferInput, copyStringValues, copyStringValues, filterFile, flushInput, getInputFormat, getInputStringIndex, getOutputFormat, getOutputStringIndex, getStringIndices, inputFormat, isOutputFormatDefined, numPendingOutput, output, outputFormat, outputFormatPeek, outputPeek, push, resetQueue, setOutputFormat, useFilter</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="KEAFilter()"><!-- --></A><H3>
+KEAFilter</H3>
+<PRE>
+public <B>KEAFilter</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="getCheckForProperNouns()"><!-- --></A><H3>
+getCheckForProperNouns</H3>
+<PRE>
+public boolean <B>getCheckForProperNouns</B>()</PRE>
+<DL>
+<DD>Get the M_CheckProperNouns value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the M_CheckProperNouns value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setCheckForProperNouns(boolean)"><!-- --></A><H3>
+setCheckForProperNouns</H3>
+<PRE>
+public void <B>setCheckForProperNouns</B>(boolean&nbsp;newM_CheckProperNouns)</PRE>
+<DL>
+<DD>Set the M_CheckProperNouns value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newM_CheckProperNouns</CODE> - The new M_CheckProperNouns value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getStopwords()"><!-- --></A><H3>
+getStopwords</H3>
+<PRE>
+public Stopwords <B>getStopwords</B>()</PRE>
+<DL>
+<DD>Get the M_Stopwords value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the M_Stopwords value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setStopwords(Stopwords)"><!-- --></A><H3>
+setStopwords</H3>
+<PRE>
+public void <B>setStopwords</B>(Stopwords&nbsp;newM_Stopwords)</PRE>
+<DL>
+<DD>Set the M_Stopwords value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newM_Stopwords</CODE> - The new M_Stopwords value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getStemmer()"><!-- --></A><H3>
+getStemmer</H3>
+<PRE>
+public Stemmer <B>getStemmer</B>()</PRE>
+<DL>
+<DD>Get the Stemmer value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the Stemmer value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setStemmer(Stemmer)"><!-- --></A><H3>
+setStemmer</H3>
+<PRE>
+public void <B>setStemmer</B>(Stemmer&nbsp;newStemmer)</PRE>
+<DL>
+<DD>Set the Stemmer value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newStemmer</CODE> - The new Stemmer value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMinNumOccur()"><!-- --></A><H3>
+getMinNumOccur</H3>
+<PRE>
+public int <B>getMinNumOccur</B>()</PRE>
+<DL>
+<DD>Get the value of MinNumOccur.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MinNumOccur.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMinNumOccur(int)"><!-- --></A><H3>
+setMinNumOccur</H3>
+<PRE>
+public void <B>setMinNumOccur</B>(int&nbsp;newMinNumOccur)</PRE>
+<DL>
+<DD>Set the value of MinNumOccur.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMinNumOccur</CODE> - Value to assign to MinNumOccur.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMaxPhraseLength()"><!-- --></A><H3>
+getMaxPhraseLength</H3>
+<PRE>
+public int <B>getMaxPhraseLength</B>()</PRE>
+<DL>
+<DD>Get the value of MaxPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MaxPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMaxPhraseLength(int)"><!-- --></A><H3>
+setMaxPhraseLength</H3>
+<PRE>
+public void <B>setMaxPhraseLength</B>(int&nbsp;newMaxPhraseLength)</PRE>
+<DL>
+<DD>Set the value of MaxPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMaxPhraseLength</CODE> - Value to assign to MaxPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMinPhraseLength()"><!-- --></A><H3>
+getMinPhraseLength</H3>
+<PRE>
+public int <B>getMinPhraseLength</B>()</PRE>
+<DL>
+<DD>Get the value of MinPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MinPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMinPhraseLength(int)"><!-- --></A><H3>
+setMinPhraseLength</H3>
+<PRE>
+public void <B>setMinPhraseLength</B>(int&nbsp;newMinPhraseLength)</PRE>
+<DL>
+<DD>Set the value of MinPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMinPhraseLength</CODE> - Value to assign to MinPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getStemmedPhraseIndex()"><!-- --></A><H3>
+getStemmedPhraseIndex</H3>
+<PRE>
+public int <B>getStemmedPhraseIndex</B>()</PRE>
+<DL>
+<DD>Returns the index of the stemmed phrases in the output ARFF file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getUnstemmedPhraseIndex()"><!-- --></A><H3>
+getUnstemmedPhraseIndex</H3>
+<PRE>
+public int <B>getUnstemmedPhraseIndex</B>()</PRE>
+<DL>
+<DD>Returns the index of the unstemmed phrases in the output ARFF file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getProbabilityIndex()"><!-- --></A><H3>
+getProbabilityIndex</H3>
+<PRE>
+public int <B>getProbabilityIndex</B>()</PRE>
+<DL>
+<DD>Returns the index of the phrases' probabilities in the output ARFF file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getRankIndex()"><!-- --></A><H3>
+getRankIndex</H3>
+<PRE>
+public int <B>getRankIndex</B>()</PRE>
+<DL>
+<DD>Returns the index of the phrases' ranks in the output ARFF file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDocumentAtt()"><!-- --></A><H3>
+getDocumentAtt</H3>
+<PRE>
+public int <B>getDocumentAtt</B>()</PRE>
+<DL>
+<DD>Get the value of DocumentAtt.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of DocumentAtt.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDocumentAtt(int)"><!-- --></A><H3>
+setDocumentAtt</H3>
+<PRE>
+public void <B>setDocumentAtt</B>(int&nbsp;newDocumentAtt)</PRE>
+<DL>
+<DD>Set the value of DocumentAtt.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newDocumentAtt</CODE> - Value to assign to DocumentAtt.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getKeyphrasesAtt()"><!-- --></A><H3>
+getKeyphrasesAtt</H3>
+<PRE>
+public int <B>getKeyphrasesAtt</B>()</PRE>
+<DL>
+<DD>Get the value of KeyphraseAtt.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of KeyphraseAtt.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setKeyphrasesAtt(int)"><!-- --></A><H3>
+setKeyphrasesAtt</H3>
+<PRE>
+public void <B>setKeyphrasesAtt</B>(int&nbsp;newKeyphrasesAtt)</PRE>
+<DL>
+<DD>Set the value of KeyphrasesAtt.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newKeyphrasesAtt</CODE> - Value to assign to KeyphrasesAtt.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDebug()"><!-- --></A><H3>
+getDebug</H3>
+<PRE>
+public boolean <B>getDebug</B>()</PRE>
+<DL>
+<DD>Get the value of Debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of Debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDebug(boolean)"><!-- --></A><H3>
+setDebug</H3>
+<PRE>
+public void <B>setDebug</B>(boolean&nbsp;newDebug)</PRE>
+<DL>
+<DD>Set the value of Debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newDebug</CODE> - Value to assign to Debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setKFused(boolean)"><!-- --></A><H3>
+setKFused</H3>
+<PRE>
+public void <B>setKFused</B>(boolean&nbsp;flag)</PRE>
+<DL>
+<DD>Sets whether keyphrase frequency attribute is used.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getKFused()"><!-- --></A><H3>
+getKFused</H3>
+<PRE>
+public boolean <B>getKFused</B>()</PRE>
+<DL>
+<DD>Gets whether keyphrase frequency attribute is used.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDisallowInternalPeriods()"><!-- --></A><H3>
+getDisallowInternalPeriods</H3>
+<PRE>
+public boolean <B>getDisallowInternalPeriods</B>()</PRE>
+<DL>
+<DD>Get whether the supplied columns are to be processed
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if the supplied columns won't be processed</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDisallowInternalPeriods(boolean)"><!-- --></A><H3>
+setDisallowInternalPeriods</H3>
+<PRE>
+public void <B>setDisallowInternalPeriods</B>(boolean&nbsp;disallow)</PRE>
+<DL>
+<DD>Set whether selected columns should be processed. If true the 
+ selected columns won't be processed.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setOptions(java.lang.String[])"><!-- --></A><H3>
+setOptions</H3>
+<PRE>
+public void <B>setOptions</B>(java.lang.String[]&nbsp;options)
+                throws java.lang.Exception</PRE>
+<DL>
+<DD>Parses a given list of options controlling the behaviour of this object.
+ Valid options are:<p>
+
+ -K<br>
+ Specifies whether keyphrase frequency statistic is used.<p>
+
+ -M length<br>
+ Sets the maximum phrase length (default: 3).<p>
+
+ -L length<br>
+ Sets the minimum phrase length (default: 1).<p>
+
+ -D<br>
+ Turns debugging mode on.<p>
+
+ -I index<br>
+ Sets the index of the attribute containing the documents (default: 0).<p>
+
+ -J index<br>
+ Sets the index of the attribute containing the keyphrases (default: 1).<p>
+
+ -P<br>
+ Disallow internal periods <p>
+
+ -O number<br>
+ The minimum number of times a phrase needs to occur (default: 2). <p>
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>setOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>options</CODE> - the list of options as an array of strings
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if an option is not supported</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getOptions()"><!-- --></A><H3>
+getOptions</H3>
+<PRE>
+public java.lang.String[] <B>getOptions</B>()</PRE>
+<DL>
+<DD>Gets the current settings of the filter.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>getOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an array of strings suitable for passing to setOptions</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="listOptions()"><!-- --></A><H3>
+listOptions</H3>
+<PRE>
+public java.util.Enumeration <B>listOptions</B>()</PRE>
+<DL>
+<DD>Returns an enumeration describing the available options
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>listOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an enumeration of all the available options</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="globalInfo()"><!-- --></A><H3>
+globalInfo</H3>
+<PRE>
+public java.lang.String <B>globalInfo</B>()</PRE>
+<DL>
+<DD>Returns a string describing this filter
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>a description of the filter suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setInputFormat(weka.core.Instances)"><!-- --></A><H3>
+setInputFormat</H3>
+<PRE>
+public boolean <B>setInputFormat</B>(weka.core.Instances&nbsp;instanceInfo)
+                       throws java.lang.Exception</PRE>
+<DL>
+<DD>Sets the format of the input instances.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>setInputFormat</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instanceInfo</CODE> - an Instances object containing the input
+ instance structure (any instances contained in the object are
+ ignored - only the structure is required).
+<DT><B>Returns:</B><DD>true if the outputFormat may be collected immediately
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="input(weka.core.Instance)"><!-- --></A><H3>
+input</H3>
+<PRE>
+public boolean <B>input</B>(weka.core.Instance&nbsp;instance)
+              throws java.lang.Exception</PRE>
+<DL>
+<DD>Input an instance for filtering. Ordinarily the instance is processed
+ and made available for output immediately. Some filters require all
+ instances be read before producing output.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>input</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instance</CODE> - the input instance
+<DT><B>Returns:</B><DD>true if the filtered instance may now be
+ collected with output().
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if the input instance was not of the correct 
+ format or if there was a problem with the filtering.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="batchFinished()"><!-- --></A><H3>
+batchFinished</H3>
+<PRE>
+public boolean <B>batchFinished</B>()
+                      throws java.lang.Exception</PRE>
+<DL>
+<DD>Signify that this batch of input to the filter is finished. 
+ If the filter requires all instances prior to filtering,
+ output() may now be called to retrieve the filtered instances.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>batchFinished</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if there are instances pending output
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if no input structure has been defined</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;argv)</PRE>
+<DL>
+<DD>Main method for testing this class.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>argv</CODE> - should contain arguments to the filter: use -h for help</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="IteratedLovinsStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAKeyphraseExtractor.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_weka.filters.Filter">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAKeyphraseExtractor.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAKeyphraseExtractor.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAKeyphraseExtractor.html	(revision 16561)
@@ -0,0 +1,742 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+KEAKeyphraseExtractor
+</TITLE>
+<META NAME="keywords" CONTENT="KEAKeyphraseExtractor,KEAKeyphraseExtractor class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="KEAKeyphraseExtractor";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAModelBuilder.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAKeyphraseExtractor.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class KEAKeyphraseExtractor</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<B>KEAKeyphraseExtractor</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>weka.core.OptionHandler</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>KEAKeyphraseExtractor</B><DT>extends java.lang.Object<DT>implements weka.core.OptionHandler</DL>
+
+<P>
+Extracts keyphrases from the documents in a given directory.
+ Assumes that the file names for the documents end with ".txt".
+ Puts extracted keyphrases into corresponding files ending with
+ ".key" (if those are not already present). Optionally an encoding
+ for the documents/keyphrases can be defined (e.g. for Chinese
+ text). Documents for which ".key" exists, are used for evaluation.
+
+ Valid options are:<p>
+
+ -l "directory name"<br>
+ Specifies name of directory.<p>
+
+ -m "model name"<br>
+ Specifies name of model.<p>
+
+ -e "encoding"<br>
+ Specifies encoding.<p>
+
+ -n <br>
+ Specifies number of phrases to be output (default: 5).<p>
+
+ -d<br>
+ Turns debugging mode on.<p>
+
+ -a<br>
+ Also write stemmed phrase and score into ".key" file.<p>
+<P>
+
+<P>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#KEAKeyphraseExtractor()">KEAKeyphraseExtractor</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Hashtable</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#collectStems()">collectStems</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collects the stems of the file names.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#extractKeyphrases(java.util.Hashtable)">extractKeyphrases</A></B>(java.util.Hashtable&nbsp;stems)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Builds the model from the files</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getAdditionalInfo()">getAdditionalInfo</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of AdditionalInfo.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getDebug()">getDebug</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getDirName()">getDirName</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of dirName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getEncoding()">getEncoding</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of encoding.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getModelName()">getModelName</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of modelName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getNumPhrases()">getNumPhrases</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of numPhrases.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String[]</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#getOptions()">getOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets the current option settings.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Enumeration</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#listOptions()">listOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns an enumeration describing the available options.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#loadModel()">loadModel</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Loads the extraction model from the file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;ops)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The main method.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setAdditionalInfo(boolean)">setAdditionalInfo</A></B>(boolean&nbsp;newAdditionalInfo)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of AdditionalInfo.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setDebug(boolean)">setDebug</A></B>(boolean&nbsp;newdebug)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setDirName(java.lang.String)">setDirName</A></B>(java.lang.String&nbsp;newdirName)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of dirName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setEncoding(java.lang.String)">setEncoding</A></B>(java.lang.String&nbsp;newencoding)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of encoding.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setModelName(java.lang.String)">setModelName</A></B>(java.lang.String&nbsp;newmodelName)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of modelName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setNumPhrases(int)">setNumPhrases</A></B>(int&nbsp;newnumPhrases)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of numPhrases.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAKeyphraseExtractor.html#setOptions(java.lang.String[])">setOptions</A></B>(java.lang.String[]&nbsp;options)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parses a given list of options controlling the behaviour of this object.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="KEAKeyphraseExtractor()"><!-- --></A><H3>
+KEAKeyphraseExtractor</H3>
+<PRE>
+public <B>KEAKeyphraseExtractor</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="getAdditionalInfo()"><!-- --></A><H3>
+getAdditionalInfo</H3>
+<PRE>
+public boolean <B>getAdditionalInfo</B>()</PRE>
+<DL>
+<DD>Get the value of AdditionalInfo.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of AdditionalInfo.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setAdditionalInfo(boolean)"><!-- --></A><H3>
+setAdditionalInfo</H3>
+<PRE>
+public void <B>setAdditionalInfo</B>(boolean&nbsp;newAdditionalInfo)</PRE>
+<DL>
+<DD>Set the value of AdditionalInfo.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newAdditionalInfo</CODE> - Value to assign to AdditionalInfo.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getNumPhrases()"><!-- --></A><H3>
+getNumPhrases</H3>
+<PRE>
+public int <B>getNumPhrases</B>()</PRE>
+<DL>
+<DD>Get the value of numPhrases.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of numPhrases.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setNumPhrases(int)"><!-- --></A><H3>
+setNumPhrases</H3>
+<PRE>
+public void <B>setNumPhrases</B>(int&nbsp;newnumPhrases)</PRE>
+<DL>
+<DD>Set the value of numPhrases.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newnumPhrases</CODE> - Value to assign to numPhrases.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDebug()"><!-- --></A><H3>
+getDebug</H3>
+<PRE>
+public boolean <B>getDebug</B>()</PRE>
+<DL>
+<DD>Get the value of debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDebug(boolean)"><!-- --></A><H3>
+setDebug</H3>
+<PRE>
+public void <B>setDebug</B>(boolean&nbsp;newdebug)</PRE>
+<DL>
+<DD>Set the value of debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newdebug</CODE> - Value to assign to debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getEncoding()"><!-- --></A><H3>
+getEncoding</H3>
+<PRE>
+public java.lang.String <B>getEncoding</B>()</PRE>
+<DL>
+<DD>Get the value of encoding.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of encoding.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setEncoding(java.lang.String)"><!-- --></A><H3>
+setEncoding</H3>
+<PRE>
+public void <B>setEncoding</B>(java.lang.String&nbsp;newencoding)</PRE>
+<DL>
+<DD>Set the value of encoding.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newencoding</CODE> - Value to assign to encoding.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getModelName()"><!-- --></A><H3>
+getModelName</H3>
+<PRE>
+public java.lang.String <B>getModelName</B>()</PRE>
+<DL>
+<DD>Get the value of modelName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of modelName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setModelName(java.lang.String)"><!-- --></A><H3>
+setModelName</H3>
+<PRE>
+public void <B>setModelName</B>(java.lang.String&nbsp;newmodelName)</PRE>
+<DL>
+<DD>Set the value of modelName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newmodelName</CODE> - Value to assign to modelName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDirName()"><!-- --></A><H3>
+getDirName</H3>
+<PRE>
+public java.lang.String <B>getDirName</B>()</PRE>
+<DL>
+<DD>Get the value of dirName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of dirName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDirName(java.lang.String)"><!-- --></A><H3>
+setDirName</H3>
+<PRE>
+public void <B>setDirName</B>(java.lang.String&nbsp;newdirName)</PRE>
+<DL>
+<DD>Set the value of dirName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newdirName</CODE> - Value to assign to dirName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setOptions(java.lang.String[])"><!-- --></A><H3>
+setOptions</H3>
+<PRE>
+public void <B>setOptions</B>(java.lang.String[]&nbsp;options)
+                throws java.lang.Exception</PRE>
+<DL>
+<DD>Parses a given list of options controlling the behaviour of this object.
+ Valid options are:<p>
+
+ -l "directory name"<br>
+ Specifies name of directory.<p>
+
+ -m "model name"<br>
+ Specifies name of model.<p>
+
+ -e "encoding"<br>
+ Specifies encoding.<p>
+
+ -n<br>
+ Specifies number of phrases to be output (default: 5).<p>
+
+ -d<br>
+ Turns debugging mode on.<p>
+
+ -a<br>
+ Also write stemmed phrase and score into ".key" file.<p>
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>setOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>options</CODE> - the list of options as an array of strings
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if an option is not supported</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getOptions()"><!-- --></A><H3>
+getOptions</H3>
+<PRE>
+public java.lang.String[] <B>getOptions</B>()</PRE>
+<DL>
+<DD>Gets the current option settings.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>getOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an array of strings suitable for passing to setOptions</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="listOptions()"><!-- --></A><H3>
+listOptions</H3>
+<PRE>
+public java.util.Enumeration <B>listOptions</B>()</PRE>
+<DL>
+<DD>Returns an enumeration describing the available options.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>listOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an enumeration of all the available options</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="collectStems()"><!-- --></A><H3>
+collectStems</H3>
+<PRE>
+public java.util.Hashtable <B>collectStems</B>()
+                                 throws java.lang.Exception</PRE>
+<DL>
+<DD>Collects the stems of the file names.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="extractKeyphrases(java.util.Hashtable)"><!-- --></A><H3>
+extractKeyphrases</H3>
+<PRE>
+public void <B>extractKeyphrases</B>(java.util.Hashtable&nbsp;stems)
+                       throws java.lang.Exception</PRE>
+<DL>
+<DD>Builds the model from the files
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="loadModel()"><!-- --></A><H3>
+loadModel</H3>
+<PRE>
+public void <B>loadModel</B>()
+               throws java.lang.Exception</PRE>
+<DL>
+<DD>Loads the extraction model from the file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;ops)</PRE>
+<DL>
+<DD>The main method.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAModelBuilder.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAKeyphraseExtractor.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAModelBuilder.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAModelBuilder.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAModelBuilder.html	(revision 16561)
@@ -0,0 +1,1072 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+KEAModelBuilder
+</TITLE>
+<META NAME="keywords" CONTENT="KEAModelBuilder,KEAModelBuilder class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="KEAModelBuilder";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAKeyphraseExtractor.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAPhraseFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAModelBuilder.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class KEAModelBuilder</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<B>KEAModelBuilder</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>weka.core.OptionHandler</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>KEAModelBuilder</B><DT>extends java.lang.Object<DT>implements weka.core.OptionHandler</DL>
+
+<P>
+Builds a keyphrase extraction model from the documents in a given
+ directory.  Assumes that the file names for the documents end with
+ ".txt".  Assumes that files containing corresponding
+ author-assigned keyphrases end with ".key". Optionally an encoding
+ for the documents/keyphrases can be defined (e.g. for Chinese
+ text).
+
+ Valid options are:<p>
+
+ -l "directory name"<br>
+ Specifies name of directory.<p>
+
+ -m "model name"<br>
+ Specifies name of model.<p>
+
+ -e "encoding"<br>
+ Specifies encoding.<p>
+
+ -d<br>
+ Turns debugging mode on.<p>
+
+ -k<br>
+ Use keyphrase frequency statistic.<p>
+
+ -p<br>
+ Disallow internal periods.<p>
+
+ -x "length"<br>
+ Sets maximum phrase length (default: 3).<p>
+
+ -y "length"<br>
+ Sets minimum phrase length (default: 1).<p>
+
+ -o "number"<br>
+ The minimum number of times a phrase needs to occur (default: 2). <p>
+
+ -s "name of class implementing list of stop words"<br>
+ Sets list of stop words to used (default: StopwordsEnglish).<p>
+
+ -t "name of class implementing stemmer"<br>
+ Sets stemmer to use (default: IteratedLovinsStemmer). <p>
+
+ -n<br>
+ Do not check for proper nouns. <p>
+<P>
+
+<P>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="KEAModelBuilder.html#KEAModelBuilder()">KEAModelBuilder</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#buildModel(java.util.Hashtable)">buildModel</A></B>(java.util.Hashtable&nbsp;stems)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Builds the model from the files</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Hashtable</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#collectStems()">collectStems</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Collects the stems of the file names.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getCheckForProperNouns()">getCheckForProperNouns</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the M_CheckProperNouns value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getDebug()">getDebug</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getDirName()">getDirName</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of dirName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getDisallowIPeriods()">getDisallowIPeriods</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of disallowIPeriods.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getEncoding()">getEncoding</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of encoding.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getMaxPhraseLength()">getMaxPhraseLength</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MaxPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getMinNumOccur()">getMinNumOccur</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MinNumOccur.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;int</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getMinPhraseLength()">getMinPhraseLength</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of MinPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getModelName()">getModelName</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of modelName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String[]</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getOptions()">getOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets the current option settings.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;Stemmer</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getStemmer()">getStemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the Stemmer value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;Stopwords</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getStopwords()">getStopwords</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the M_Stopwords value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#getUseKFrequency()">getUseKFrequency</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the value of useKFrequency.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Enumeration</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#listOptions()">listOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns an enumeration describing the available options.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;ops)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The main method.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#saveModel()">saveModel</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Saves the extraction model to the file.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setCheckForProperNouns(boolean)">setCheckForProperNouns</A></B>(boolean&nbsp;newM_CheckProperNouns)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the M_CheckProperNouns value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setDebug(boolean)">setDebug</A></B>(boolean&nbsp;newdebug)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of debug.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setDirName(java.lang.String)">setDirName</A></B>(java.lang.String&nbsp;newdirName)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of dirName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setDisallowIPeriods(boolean)">setDisallowIPeriods</A></B>(boolean&nbsp;newdisallowIPeriods)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of disallowIPeriods.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setEncoding(java.lang.String)">setEncoding</A></B>(java.lang.String&nbsp;newencoding)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of encoding.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setMaxPhraseLength(int)">setMaxPhraseLength</A></B>(int&nbsp;newMaxPhraseLength)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MaxPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setMinNumOccur(int)">setMinNumOccur</A></B>(int&nbsp;newMinNumOccur)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MinNumOccur.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setMinPhraseLength(int)">setMinPhraseLength</A></B>(int&nbsp;newMinPhraseLength)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of MinPhraseLength.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setModelName(java.lang.String)">setModelName</A></B>(java.lang.String&nbsp;newmodelName)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of modelName.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setOptions(java.lang.String[])">setOptions</A></B>(java.lang.String[]&nbsp;options)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parses a given list of options controlling the behaviour of this object.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setStemmer(Stemmer)">setStemmer</A></B>(Stemmer&nbsp;newStemmer)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the Stemmer value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setStopwords(Stopwords)">setStopwords</A></B>(Stopwords&nbsp;newM_Stopwords)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the M_Stopwords value.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAModelBuilder.html#setUseKFrequency(boolean)">setUseKFrequency</A></B>(boolean&nbsp;newuseKFrequency)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the value of useKFrequency.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="KEAModelBuilder()"><!-- --></A><H3>
+KEAModelBuilder</H3>
+<PRE>
+public <B>KEAModelBuilder</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="getCheckForProperNouns()"><!-- --></A><H3>
+getCheckForProperNouns</H3>
+<PRE>
+public boolean <B>getCheckForProperNouns</B>()</PRE>
+<DL>
+<DD>Get the M_CheckProperNouns value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the M_CheckProperNouns value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setCheckForProperNouns(boolean)"><!-- --></A><H3>
+setCheckForProperNouns</H3>
+<PRE>
+public void <B>setCheckForProperNouns</B>(boolean&nbsp;newM_CheckProperNouns)</PRE>
+<DL>
+<DD>Set the M_CheckProperNouns value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newM_CheckProperNouns</CODE> - The new M_CheckProperNouns value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getStopwords()"><!-- --></A><H3>
+getStopwords</H3>
+<PRE>
+public Stopwords <B>getStopwords</B>()</PRE>
+<DL>
+<DD>Get the M_Stopwords value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the M_Stopwords value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setStopwords(Stopwords)"><!-- --></A><H3>
+setStopwords</H3>
+<PRE>
+public void <B>setStopwords</B>(Stopwords&nbsp;newM_Stopwords)</PRE>
+<DL>
+<DD>Set the M_Stopwords value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newM_Stopwords</CODE> - The new M_Stopwords value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getStemmer()"><!-- --></A><H3>
+getStemmer</H3>
+<PRE>
+public Stemmer <B>getStemmer</B>()</PRE>
+<DL>
+<DD>Get the Stemmer value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>the Stemmer value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setStemmer(Stemmer)"><!-- --></A><H3>
+setStemmer</H3>
+<PRE>
+public void <B>setStemmer</B>(Stemmer&nbsp;newStemmer)</PRE>
+<DL>
+<DD>Set the Stemmer value.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newStemmer</CODE> - The new Stemmer value.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMinNumOccur()"><!-- --></A><H3>
+getMinNumOccur</H3>
+<PRE>
+public int <B>getMinNumOccur</B>()</PRE>
+<DL>
+<DD>Get the value of MinNumOccur.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MinNumOccur.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMinNumOccur(int)"><!-- --></A><H3>
+setMinNumOccur</H3>
+<PRE>
+public void <B>setMinNumOccur</B>(int&nbsp;newMinNumOccur)</PRE>
+<DL>
+<DD>Set the value of MinNumOccur.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMinNumOccur</CODE> - Value to assign to MinNumOccur.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMaxPhraseLength()"><!-- --></A><H3>
+getMaxPhraseLength</H3>
+<PRE>
+public int <B>getMaxPhraseLength</B>()</PRE>
+<DL>
+<DD>Get the value of MaxPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MaxPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMaxPhraseLength(int)"><!-- --></A><H3>
+setMaxPhraseLength</H3>
+<PRE>
+public void <B>setMaxPhraseLength</B>(int&nbsp;newMaxPhraseLength)</PRE>
+<DL>
+<DD>Set the value of MaxPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMaxPhraseLength</CODE> - Value to assign to MaxPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getMinPhraseLength()"><!-- --></A><H3>
+getMinPhraseLength</H3>
+<PRE>
+public int <B>getMinPhraseLength</B>()</PRE>
+<DL>
+<DD>Get the value of MinPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of MinPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setMinPhraseLength(int)"><!-- --></A><H3>
+setMinPhraseLength</H3>
+<PRE>
+public void <B>setMinPhraseLength</B>(int&nbsp;newMinPhraseLength)</PRE>
+<DL>
+<DD>Set the value of MinPhraseLength.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newMinPhraseLength</CODE> - Value to assign to MinPhraseLength.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDisallowIPeriods()"><!-- --></A><H3>
+getDisallowIPeriods</H3>
+<PRE>
+public boolean <B>getDisallowIPeriods</B>()</PRE>
+<DL>
+<DD>Get the value of disallowIPeriods.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of disallowIPeriods.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDisallowIPeriods(boolean)"><!-- --></A><H3>
+setDisallowIPeriods</H3>
+<PRE>
+public void <B>setDisallowIPeriods</B>(boolean&nbsp;newdisallowIPeriods)</PRE>
+<DL>
+<DD>Set the value of disallowIPeriods.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newdisallowIPeriods</CODE> - Value to assign to disallowIPeriods.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getUseKFrequency()"><!-- --></A><H3>
+getUseKFrequency</H3>
+<PRE>
+public boolean <B>getUseKFrequency</B>()</PRE>
+<DL>
+<DD>Get the value of useKFrequency.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of useKFrequency.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setUseKFrequency(boolean)"><!-- --></A><H3>
+setUseKFrequency</H3>
+<PRE>
+public void <B>setUseKFrequency</B>(boolean&nbsp;newuseKFrequency)</PRE>
+<DL>
+<DD>Set the value of useKFrequency.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newuseKFrequency</CODE> - Value to assign to useKFrequency.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDebug()"><!-- --></A><H3>
+getDebug</H3>
+<PRE>
+public boolean <B>getDebug</B>()</PRE>
+<DL>
+<DD>Get the value of debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDebug(boolean)"><!-- --></A><H3>
+setDebug</H3>
+<PRE>
+public void <B>setDebug</B>(boolean&nbsp;newdebug)</PRE>
+<DL>
+<DD>Set the value of debug.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newdebug</CODE> - Value to assign to debug.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getEncoding()"><!-- --></A><H3>
+getEncoding</H3>
+<PRE>
+public java.lang.String <B>getEncoding</B>()</PRE>
+<DL>
+<DD>Get the value of encoding.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of encoding.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setEncoding(java.lang.String)"><!-- --></A><H3>
+setEncoding</H3>
+<PRE>
+public void <B>setEncoding</B>(java.lang.String&nbsp;newencoding)</PRE>
+<DL>
+<DD>Set the value of encoding.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newencoding</CODE> - Value to assign to encoding.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getModelName()"><!-- --></A><H3>
+getModelName</H3>
+<PRE>
+public java.lang.String <B>getModelName</B>()</PRE>
+<DL>
+<DD>Get the value of modelName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of modelName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setModelName(java.lang.String)"><!-- --></A><H3>
+setModelName</H3>
+<PRE>
+public void <B>setModelName</B>(java.lang.String&nbsp;newmodelName)</PRE>
+<DL>
+<DD>Set the value of modelName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newmodelName</CODE> - Value to assign to modelName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDirName()"><!-- --></A><H3>
+getDirName</H3>
+<PRE>
+public java.lang.String <B>getDirName</B>()</PRE>
+<DL>
+<DD>Get the value of dirName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>Value of dirName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDirName(java.lang.String)"><!-- --></A><H3>
+setDirName</H3>
+<PRE>
+public void <B>setDirName</B>(java.lang.String&nbsp;newdirName)</PRE>
+<DL>
+<DD>Set the value of dirName.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>newdirName</CODE> - Value to assign to dirName.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setOptions(java.lang.String[])"><!-- --></A><H3>
+setOptions</H3>
+<PRE>
+public void <B>setOptions</B>(java.lang.String[]&nbsp;options)
+                throws java.lang.Exception</PRE>
+<DL>
+<DD>Parses a given list of options controlling the behaviour of this object.
+ Valid options are:<p>
+
+ -l "directory name" <br>
+ Specifies name of directory.<p>
+
+ -m "model name" <br>
+ Specifies name of model.<p>
+
+ -e "encoding" <br>
+ Specifies encoding.<p>
+
+ -d<br>
+ Turns debugging mode on.<p>
+
+ -k<br>
+ Use keyphrase frequency statistic.<p>
+
+ -p<br>
+ Disallow internal periods. <p>
+
+ -x "length"<br>
+ Sets maximum phrase length (default: 3).<p>
+
+ -y "length"<br>
+ Sets minimum phrase length (default: 3).<p>
+
+ -o "number"<br>
+ The minimum number of times a phrase needs to occur (default: 2). <p>
+
+ -s "name of class implementing list of stop words"<br>
+ Sets list of stop words to used (default: StopwordsEnglish).<p>
+
+ -t "name of class implementing stemmer"<br>
+ Sets stemmer to use (default: IteratedLovinsStemmer). <p>
+
+ -n<br>
+ Do not check for proper nouns. <p>
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>setOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>options</CODE> - the list of options as an array of strings
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if an option is not supported</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getOptions()"><!-- --></A><H3>
+getOptions</H3>
+<PRE>
+public java.lang.String[] <B>getOptions</B>()</PRE>
+<DL>
+<DD>Gets the current option settings.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>getOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an array of strings suitable for passing to setOptions</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="listOptions()"><!-- --></A><H3>
+listOptions</H3>
+<PRE>
+public java.util.Enumeration <B>listOptions</B>()</PRE>
+<DL>
+<DD>Returns an enumeration describing the available options.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>listOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an enumeration of all the available options</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="collectStems()"><!-- --></A><H3>
+collectStems</H3>
+<PRE>
+public java.util.Hashtable <B>collectStems</B>()
+                                 throws java.lang.Exception</PRE>
+<DL>
+<DD>Collects the stems of the file names.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="buildModel(java.util.Hashtable)"><!-- --></A><H3>
+buildModel</H3>
+<PRE>
+public void <B>buildModel</B>(java.util.Hashtable&nbsp;stems)
+                throws java.lang.Exception</PRE>
+<DL>
+<DD>Builds the model from the files
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="saveModel()"><!-- --></A><H3>
+saveModel</H3>
+<PRE>
+public void <B>saveModel</B>()
+               throws java.lang.Exception</PRE>
+<DL>
+<DD>Saves the extraction model to the file.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;ops)</PRE>
+<DL>
+<DD>The main method.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAKeyphraseExtractor.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="KEAPhraseFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAModelBuilder.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAPhraseFilter.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAPhraseFilter.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/KEAPhraseFilter.html	(revision 16561)
@@ -0,0 +1,817 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:49 NZDT 2004 -->
+<TITLE>
+KEAPhraseFilter
+</TITLE>
+<META NAME="keywords" CONTENT="KEAPhraseFilter,KEAPhraseFilter class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="KEAPhraseFilter";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAModelBuilder.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="LovinsStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAPhraseFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class KEAPhraseFilter</H2>
+<PRE>
+java.lang.Object
+  |
+  +--weka.filters.Filter
+        |
+        +--<B>KEAPhraseFilter</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>weka.core.OptionHandler, java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>KEAPhraseFilter</B><DT>extends weka.filters.Filter<DT>implements weka.core.OptionHandler</DL>
+
+<P>
+This filter splits the text in selected string
+ attributes into phrases. The resulting
+ string attributes contain these phrases
+ separated by '\n' characters.
+
+ Phrases are identified according to the
+ following definitions:
+ 
+ A phrase is a sequence of words interrupted
+ only by sequences of whitespace characters,
+ where each sequence of whitespace characters
+ contains at most one '\n'.
+
+ A word is a sequence of letters or digits
+ that contains at least one letter, with
+ the following exceptions:
+
+ a) '.', '@', '_', '&', '/', '-' are allowed
+ if surrounded by letters or digits,
+
+ b) '\'' is allowed if preceeded by a letter
+ or digit,
+ 
+ c) '-', '/' are also allowed if succeeded by
+ whitespace characters followed by another
+ word. In that case the whitespace characters
+ will be deleted.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="KEAPhraseFilter">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+<A NAME="field_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Field Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>protected &nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#m_DisallowInternalPeriods">m_DisallowInternalPeriods</A></B></CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Determines whether internal periods are allowed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>protected &nbsp;weka.core.Range</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#m_SelectCols">m_SelectCols</A></B></CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stores which columns to select as a funky range</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="fields_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Fields inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>m_NewBatch</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#KEAPhraseFilter()">KEAPhraseFilter</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#attributeIndicesTipText()">attributeIndicesTipText</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the tip text for this property</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#batchFinished()">batchFinished</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Signify that this batch of input to the filter is finished.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#disallowInternalPeriodsTipText()">disallowInternalPeriodsTipText</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the tip text for this property</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#getAttributeIndices()">getAttributeIndices</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the current range selection.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#getDisallowInternalPeriods()">getDisallowInternalPeriods</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get whether the supplied columns are to be processed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#getInvertSelection()">getInvertSelection</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get whether the supplied columns are to be processed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String[]</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#getOptions()">getOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gets the current settings of the filter.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#globalInfo()">globalInfo</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns a string describing this filter</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#input(weka.core.Instance)">input</A></B>(weka.core.Instance&nbsp;instance)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Input an instance for filtering.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#invertSelectionTipText()">invertSelectionTipText</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the tip text for this property</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.util.Enumeration</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#listOptions()">listOptions</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns an enumeration describing the available options</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;argv)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Main method for testing this class.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setAttributeIndices(java.lang.String)">setAttributeIndices</A></B>(java.lang.String&nbsp;rangeList)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set which attributes are to be processed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setAttributeIndicesArray(int[])">setAttributeIndicesArray</A></B>(int[]&nbsp;attributes)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set which attributes are to be processed</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setDisallowInternalPeriods(boolean)">setDisallowInternalPeriods</A></B>(boolean&nbsp;disallow)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set whether selected columns should be processed.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setInputFormat(weka.core.Instances)">setInputFormat</A></B>(weka.core.Instances&nbsp;instanceInfo)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the format of the input instances.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setInvertSelection(boolean)">setInvertSelection</A></B>(boolean&nbsp;invert)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set whether selected columns should be processed.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="KEAPhraseFilter.html#setOptions(java.lang.String[])">setOptions</A></B>(java.lang.String[]&nbsp;options)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parses a given list of options controlling the behaviour of this object.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>batchFilterFile, bufferInput, copyStringValues, copyStringValues, filterFile, flushInput, getInputFormat, getInputStringIndex, getOutputFormat, getOutputStringIndex, getStringIndices, inputFormat, isOutputFormatDefined, numPendingOutput, output, outputFormat, outputFormatPeek, outputPeek, push, resetQueue, setOutputFormat, useFilter</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+<A NAME="field_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Field Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="m_SelectCols"><!-- --></A><H3>
+m_SelectCols</H3>
+<PRE>
+protected weka.core.Range <B>m_SelectCols</B></PRE>
+<DL>
+<DD>Stores which columns to select as a funky range
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<A NAME="m_DisallowInternalPeriods"><!-- --></A><H3>
+m_DisallowInternalPeriods</H3>
+<PRE>
+protected boolean <B>m_DisallowInternalPeriods</B></PRE>
+<DL>
+<DD>Determines whether internal periods are allowed
+<P>
+<DL>
+</DL>
+</DL>
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="KEAPhraseFilter()"><!-- --></A><H3>
+KEAPhraseFilter</H3>
+<PRE>
+public <B>KEAPhraseFilter</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="globalInfo()"><!-- --></A><H3>
+globalInfo</H3>
+<PRE>
+public java.lang.String <B>globalInfo</B>()</PRE>
+<DL>
+<DD>Returns a string describing this filter
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>a description of the filter suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="listOptions()"><!-- --></A><H3>
+listOptions</H3>
+<PRE>
+public java.util.Enumeration <B>listOptions</B>()</PRE>
+<DL>
+<DD>Returns an enumeration describing the available options
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>listOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an enumeration of all the available options</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setOptions(java.lang.String[])"><!-- --></A><H3>
+setOptions</H3>
+<PRE>
+public void <B>setOptions</B>(java.lang.String[]&nbsp;options)
+                throws java.lang.Exception</PRE>
+<DL>
+<DD>Parses a given list of options controlling the behaviour of this object.
+ Valid options are:<p>
+
+ -R index1,index2-index4,...<br>
+ Specify list of attributes to process. First and last are valid indexes.
+ (default none)<p>
+
+ -V<br>
+ Invert matching sense <p>
+
+ -P<br>
+ Disallow internal periods <p>
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>setOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>options</CODE> - the list of options as an array of strings
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if an option is not supported</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getOptions()"><!-- --></A><H3>
+getOptions</H3>
+<PRE>
+public java.lang.String[] <B>getOptions</B>()</PRE>
+<DL>
+<DD>Gets the current settings of the filter.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE>getOptions</CODE> in interface <CODE>weka.core.OptionHandler</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>an array of strings suitable for passing to setOptions</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setInputFormat(weka.core.Instances)"><!-- --></A><H3>
+setInputFormat</H3>
+<PRE>
+public boolean <B>setInputFormat</B>(weka.core.Instances&nbsp;instanceInfo)
+                       throws java.lang.Exception</PRE>
+<DL>
+<DD>Sets the format of the input instances.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>setInputFormat</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instanceInfo</CODE> - an Instances object containing the input
+ instance structure (any instances contained in the object are
+ ignored - only the structure is required).
+<DT><B>Returns:</B><DD>true if the outputFormat may be collected immediately
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="input(weka.core.Instance)"><!-- --></A><H3>
+input</H3>
+<PRE>
+public boolean <B>input</B>(weka.core.Instance&nbsp;instance)
+              throws java.lang.Exception</PRE>
+<DL>
+<DD>Input an instance for filtering. Ordinarily the instance is processed
+ and made available for output immediately. Some filters require all
+ instances be read before producing output.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>input</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instance</CODE> - the input instance
+<DT><B>Returns:</B><DD>true if the filtered instance may now be
+ collected with output().
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if the input instance was not of the correct 
+ format or if there was a problem with the filtering.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="batchFinished()"><!-- --></A><H3>
+batchFinished</H3>
+<PRE>
+public boolean <B>batchFinished</B>()
+                      throws java.lang.Exception</PRE>
+<DL>
+<DD>Signify that this batch of input to the filter is finished. If
+ the filter requires all instances prior to filtering, output()
+ may now be called to retrieve the filtered instances. Any
+ subsequent instances filtered should be filtered based on setting
+ obtained from the first batch (unless the inputFormat has been
+ re-assigned or new options have been set). This default
+ implementation assumes all instance processing occurs during
+ inputFormat() and input().
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>batchFinished</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if there are instances pending output
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.NullPointerException</CODE> - if no input structure has been defined,
+<DD><CODE>java.lang.Exception</CODE> - if there was a problem finishing the batch.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;argv)</PRE>
+<DL>
+<DD>Main method for testing this class.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>argv</CODE> - should contain arguments to the filter: use -h for help</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="invertSelectionTipText()"><!-- --></A><H3>
+invertSelectionTipText</H3>
+<PRE>
+public java.lang.String <B>invertSelectionTipText</B>()</PRE>
+<DL>
+<DD>Returns the tip text for this property
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>tip text for this property suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getInvertSelection()"><!-- --></A><H3>
+getInvertSelection</H3>
+<PRE>
+public boolean <B>getInvertSelection</B>()</PRE>
+<DL>
+<DD>Get whether the supplied columns are to be processed
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if the supplied columns won't be processed</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setInvertSelection(boolean)"><!-- --></A><H3>
+setInvertSelection</H3>
+<PRE>
+public void <B>setInvertSelection</B>(boolean&nbsp;invert)</PRE>
+<DL>
+<DD>Set whether selected columns should be processed. If true the 
+ selected columns won't be processed.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>invert</CODE> - the new invert setting</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="disallowInternalPeriodsTipText()"><!-- --></A><H3>
+disallowInternalPeriodsTipText</H3>
+<PRE>
+public java.lang.String <B>disallowInternalPeriodsTipText</B>()</PRE>
+<DL>
+<DD>Returns the tip text for this property
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>tip text for this property suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getDisallowInternalPeriods()"><!-- --></A><H3>
+getDisallowInternalPeriods</H3>
+<PRE>
+public boolean <B>getDisallowInternalPeriods</B>()</PRE>
+<DL>
+<DD>Get whether the supplied columns are to be processed
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if the supplied columns won't be processed</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setDisallowInternalPeriods(boolean)"><!-- --></A><H3>
+setDisallowInternalPeriods</H3>
+<PRE>
+public void <B>setDisallowInternalPeriods</B>(boolean&nbsp;disallow)</PRE>
+<DL>
+<DD>Set whether selected columns should be processed. If true the 
+ selected columns won't be processed.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="attributeIndicesTipText()"><!-- --></A><H3>
+attributeIndicesTipText</H3>
+<PRE>
+public java.lang.String <B>attributeIndicesTipText</B>()</PRE>
+<DL>
+<DD>Returns the tip text for this property
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>tip text for this property suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="getAttributeIndices()"><!-- --></A><H3>
+getAttributeIndices</H3>
+<PRE>
+public java.lang.String <B>getAttributeIndices</B>()</PRE>
+<DL>
+<DD>Get the current range selection.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>a string containing a comma separated list of ranges</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setAttributeIndices(java.lang.String)"><!-- --></A><H3>
+setAttributeIndices</H3>
+<PRE>
+public void <B>setAttributeIndices</B>(java.lang.String&nbsp;rangeList)</PRE>
+<DL>
+<DD>Set which attributes are to be processed
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>rangeList</CODE> - a string representing the list of attributes.  Since
+ the string will typically come from a user, attributes are indexed from
+ 1. <br>
+ eg: first-3,5,6-last</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setAttributeIndicesArray(int[])"><!-- --></A><H3>
+setAttributeIndicesArray</H3>
+<PRE>
+public void <B>setAttributeIndicesArray</B>(int[]&nbsp;attributes)</PRE>
+<DL>
+<DD>Set which attributes are to be processed
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>attributes</CODE> - an array containing indexes of attributes to select.
+ Since the array will typically come from a program, attributes are indexed
+ from 0.</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAModelBuilder.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="LovinsStemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="KEAPhraseFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#field_summary">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;<A HREF="#field_detail">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/LovinsStemmer.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/LovinsStemmer.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/LovinsStemmer.html	(revision 16561)
@@ -0,0 +1,289 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+LovinsStemmer
+</TITLE>
+<META NAME="keywords" CONTENT="LovinsStemmer,LovinsStemmer class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="LovinsStemmer";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAPhraseFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="NumbersFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="LovinsStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class LovinsStemmer</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<A HREF="Stemmer.html">Stemmer</A>
+        |
+        +--<B>LovinsStemmer</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<DL>
+<DT><B>Direct Known Subclasses:</B> <DD><A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A></DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>LovinsStemmer</B><DT>extends <A HREF="Stemmer.html">Stemmer</A><DT>implements java.io.Serializable</DL>
+
+<P>
+Implements the Lovins stemmer.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="LovinsStemmer">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="LovinsStemmer.html#LovinsStemmer()">LovinsStemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="LovinsStemmer.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;ops)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stems text coming into stdin and writes it to stdout.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="LovinsStemmer.html#stem(java.lang.String)">stem</A></B>(java.lang.String&nbsp;word)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns the stemmed version of the given word.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_Stemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class <A HREF="Stemmer.html">Stemmer</A></B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><A HREF="Stemmer.html#stemString(java.lang.String)">stemString</A></CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="LovinsStemmer()"><!-- --></A><H3>
+LovinsStemmer</H3>
+<PRE>
+public <B>LovinsStemmer</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="stem(java.lang.String)"><!-- --></A><H3>
+stem</H3>
+<PRE>
+public java.lang.String <B>stem</B>(java.lang.String&nbsp;word)</PRE>
+<DL>
+<DD>Returns the stemmed version of the given word.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE><A HREF="Stemmer.html#stem(java.lang.String)">stem</A></CODE> in class <CODE><A HREF="Stemmer.html">Stemmer</A></CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>word</CODE> - a string consisting of a single word</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;ops)</PRE>
+<DL>
+<DD>Stems text coming into stdin and writes it to stdout.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="KEAPhraseFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="NumbersFilter.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="LovinsStemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/NumbersFilter.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/NumbersFilter.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/NumbersFilter.html	(revision 16561)
@@ -0,0 +1,396 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+NumbersFilter
+</TITLE>
+<META NAME="keywords" CONTENT="NumbersFilter,NumbersFilter class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="NumbersFilter";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="LovinsStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="Stemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="NumbersFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_weka.filters.Filter">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class NumbersFilter</H2>
+<PRE>
+java.lang.Object
+  |
+  +--weka.filters.Filter
+        |
+        +--<B>NumbersFilter</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>NumbersFilter</B><DT>extends weka.filters.Filter</DL>
+
+<P>
+Removes all numbers from all the string attributes in the given
+ dataset. Assumes that words are separated by whitespace.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="NumbersFilter">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+<A NAME="field_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Field Summary</B></FONT></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="fields_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Fields inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>m_NewBatch</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="NumbersFilter.html#NumbersFilter()">NumbersFilter</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="NumbersFilter.html#batchFinished()">batchFinished</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Signify that this batch of input to the filter is finished.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="NumbersFilter.html#globalInfo()">globalInfo</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns a string describing this filter</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="NumbersFilter.html#input(weka.core.Instance)">input</A></B>(weka.core.Instance&nbsp;instance)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Input an instance for filtering.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>static&nbsp;void</CODE></FONT></TD>
+<TD><CODE><B><A HREF="NumbersFilter.html#main(java.lang.String[])">main</A></B>(java.lang.String[]&nbsp;argv)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Main method for testing this class.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="NumbersFilter.html#setInputFormat(weka.core.Instances)">setInputFormat</A></B>(weka.core.Instances&nbsp;instanceInfo)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sets the format of the input instances.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_weka.filters.Filter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class weka.filters.Filter</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>batchFilterFile, bufferInput, copyStringValues, copyStringValues, filterFile, flushInput, getInputFormat, getInputStringIndex, getOutputFormat, getOutputStringIndex, getStringIndices, inputFormat, isOutputFormatDefined, numPendingOutput, output, outputFormat, outputFormatPeek, outputPeek, push, resetQueue, setOutputFormat, useFilter</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="NumbersFilter()"><!-- --></A><H3>
+NumbersFilter</H3>
+<PRE>
+public <B>NumbersFilter</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="globalInfo()"><!-- --></A><H3>
+globalInfo</H3>
+<PRE>
+public java.lang.String <B>globalInfo</B>()</PRE>
+<DL>
+<DD>Returns a string describing this filter
+<P>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>a description of the filter suitable for
+ displaying in the explorer/experimenter gui</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="batchFinished()"><!-- --></A><H3>
+batchFinished</H3>
+<PRE>
+public boolean <B>batchFinished</B>()
+                      throws java.lang.Exception</PRE>
+<DL>
+<DD>Signify that this batch of input to the filter is finished. If
+ the filter requires all instances prior to filtering, output()
+ may now be called to retrieve the filtered instances. Any
+ subsequent instances filtered should be filtered based on setting
+ obtained from the first batch (unless the inputFormat has been
+ re-assigned or new options have been set). This default
+ implementation assumes all instance processing occurs during
+ inputFormat() and input().
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>batchFinished</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+
+<DT><B>Returns:</B><DD>true if there are instances pending output
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.NullPointerException</CODE> - if no input structure has been defined,
+<DD><CODE>java.lang.Exception</CODE> - if there was a problem finishing the batch.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="setInputFormat(weka.core.Instances)"><!-- --></A><H3>
+setInputFormat</H3>
+<PRE>
+public boolean <B>setInputFormat</B>(weka.core.Instances&nbsp;instanceInfo)
+                       throws java.lang.Exception</PRE>
+<DL>
+<DD>Sets the format of the input instances.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>setInputFormat</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instanceInfo</CODE> - an Instances object containing the input
+ instance structure (any instances contained in the object are
+ ignored - only the structure is required).
+<DT><B>Returns:</B><DD>true if the outputFormat may be collected immediately
+<DD><CODE>java.lang.Exception</CODE></DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="input(weka.core.Instance)"><!-- --></A><H3>
+input</H3>
+<PRE>
+public boolean <B>input</B>(weka.core.Instance&nbsp;instance)
+              throws java.lang.Exception</PRE>
+<DL>
+<DD>Input an instance for filtering. Ordinarily the instance is processed
+ and made available for output immediately. Some filters require all
+ instances be read before producing output.
+<P>
+<DD><DL>
+<DT><B>Overrides:</B><DD><CODE>input</CODE> in class <CODE>weka.filters.Filter</CODE></DL>
+</DD>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>instance</CODE> - the input instance
+<DT><B>Returns:</B><DD>true if the filtered instance may now be
+ collected with output().
+<DT><B>Throws:</B>
+<DD><CODE>java.lang.Exception</CODE> - if the input instance was not of the correct 
+ format or if there was a problem with the filtering.</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="main(java.lang.String[])"><!-- --></A><H3>
+main</H3>
+<PRE>
+public static void <B>main</B>(java.lang.String[]&nbsp;argv)</PRE>
+<DL>
+<DD>Main method for testing this class.
+<P>
+<DD><DL>
+<DT><B>Parameters:</B><DD><CODE>argv</CODE> - should contain arguments to the filter: use -h for help</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="LovinsStemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="Stemmer.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="NumbersFilter.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;<A HREF="#fields_inherited_from_class_weka.filters.Filter">FIELD</A>&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stemmer.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stemmer.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stemmer.html	(revision 16561)
@@ -0,0 +1,278 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+Stemmer
+</TITLE>
+<META NAME="keywords" CONTENT="Stemmer,Stemmer class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Stemmer";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="NumbersFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="Stopwords.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Stemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class Stemmer</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<B>Stemmer</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<DL>
+<DT><B>Direct Known Subclasses:</B> <DD><A HREF="GermanStemmer.html">GermanStemmer</A>, <A HREF="LovinsStemmer.html">LovinsStemmer</A></DD>
+</DL>
+<HR>
+<DL>
+<DT>public abstract class <B>Stemmer</B><DT>extends java.lang.Object<DT>implements java.io.Serializable</DL>
+
+<P>
+Abstract class for stemmers.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="Stemmer">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="Stemmer.html#Stemmer()">Stemmer</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>abstract &nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Stemmer.html#stem(java.lang.String)">stem</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Iterated stemming of the given word.</TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;java.lang.String</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Stemmer.html#stemString(java.lang.String)">stemString</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stems everything in the given string.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="Stemmer()"><!-- --></A><H3>
+Stemmer</H3>
+<PRE>
+public <B>Stemmer</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="stem(java.lang.String)"><!-- --></A><H3>
+stem</H3>
+<PRE>
+public abstract java.lang.String <B>stem</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Iterated stemming of the given word.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<HR>
+
+<A NAME="stemString(java.lang.String)"><!-- --></A><H3>
+stemString</H3>
+<PRE>
+public java.lang.String <B>stemString</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Stems everything in the given string.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="NumbersFilter.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="Stopwords.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Stemmer.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stopwords.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stopwords.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/Stopwords.html	(revision 16561)
@@ -0,0 +1,255 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+Stopwords
+</TITLE>
+<META NAME="keywords" CONTENT="Stopwords,Stopwords class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Stopwords";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Stemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="StopwordsEnglish.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Stopwords.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class Stopwords</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<B>Stopwords</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<DL>
+<DT><B>Direct Known Subclasses:</B> <DD><A HREF="StopwordsEnglish.html">StopwordsEnglish</A>, <A HREF="StopwordsGerman.html">StopwordsGerman</A></DD>
+</DL>
+<HR>
+<DL>
+<DT>public abstract class <B>Stopwords</B><DT>extends java.lang.Object<DT>implements java.io.Serializable</DL>
+
+<P>
+Class that can test whether a given string is a stop word.
+ Lowercases all words before the test.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="Stopwords">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="Stopwords.html#Stopwords()">Stopwords</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>abstract &nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="Stopwords.html#isStopword(java.lang.String)">isStopword</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns true if the given string is a stop word.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="Stopwords()"><!-- --></A><H3>
+Stopwords</H3>
+<PRE>
+public <B>Stopwords</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="isStopword(java.lang.String)"><!-- --></A><H3>
+isStopword</H3>
+<PRE>
+public abstract boolean <B>isStopword</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Returns true if the given string is a stop word.
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Stemmer.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="StopwordsEnglish.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="Stopwords.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsEnglish.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsEnglish.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsEnglish.html	(revision 16561)
@@ -0,0 +1,254 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+StopwordsEnglish
+</TITLE>
+<META NAME="keywords" CONTENT="StopwordsEnglish,StopwordsEnglish class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="StopwordsEnglish";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Stopwords.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="StopwordsGerman.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="StopwordsEnglish.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class StopwordsEnglish</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<A HREF="Stopwords.html">Stopwords</A>
+        |
+        +--<B>StopwordsEnglish</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>StopwordsEnglish</B><DT>extends <A HREF="Stopwords.html">Stopwords</A></DL>
+
+<P>
+Class that can test whether a given string is a stop word.
+ Lowercases all words before the test.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="StopwordsEnglish">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="StopwordsEnglish.html#StopwordsEnglish()">StopwordsEnglish</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="StopwordsEnglish.html#isStopword(java.lang.String)">isStopword</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns true if the given string is a stop word.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="StopwordsEnglish()"><!-- --></A><H3>
+StopwordsEnglish</H3>
+<PRE>
+public <B>StopwordsEnglish</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="isStopword(java.lang.String)"><!-- --></A><H3>
+isStopword</H3>
+<PRE>
+public boolean <B>isStopword</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Returns true if the given string is a stop word.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE><A HREF="Stopwords.html#isStopword(java.lang.String)">isStopword</A></CODE> in class <CODE><A HREF="Stopwords.html">Stopwords</A></CODE></DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="Stopwords.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;<A HREF="StopwordsGerman.html"><B>NEXT CLASS</B></A></FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="StopwordsEnglish.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsGerman.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsGerman.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/StopwordsGerman.html	(revision 16561)
@@ -0,0 +1,259 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+StopwordsGerman
+</TITLE>
+<META NAME="keywords" CONTENT="StopwordsGerman,StopwordsGerman class">
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="StopwordsGerman";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="StopwordsEnglish.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;NEXT CLASS</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="StopwordsGerman.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+Class StopwordsGerman</H2>
+<PRE>
+java.lang.Object
+  |
+  +--<A HREF="Stopwords.html">Stopwords</A>
+        |
+        +--<B>StopwordsGerman</B>
+</PRE>
+<DL>
+<DT><B>All Implemented Interfaces:</B> <DD>java.io.Serializable</DD>
+</DL>
+<HR>
+<DL>
+<DT>public class <B>StopwordsGerman</B><DT>extends <A HREF="Stopwords.html">Stopwords</A></DL>
+
+<P>
+Class that can test whether a given string is a stop word.
+ Lowercases all words before the test.
+
+ This list of German stop words has been obtained from
+ http://snowball.tartarus.org/german/stop.txt
+
+ But I have deleted/changed some words that I haven't seen before.
+<P>
+
+<P>
+<DL>
+<DT><B>See Also:</B><DD><A HREF="serialized-form.html" TARGET="StopwordsGerman">Serialized Form</A></DL>
+<HR>
+
+<P>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+
+
+<!-- =========== FIELD SUMMARY =========== -->
+
+
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><A HREF="StopwordsGerman.html#StopwordsGerman()">StopwordsGerman</A></B>()</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</TD>
+</TR>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
+<CODE>&nbsp;boolean</CODE></FONT></TD>
+<TD><CODE><B><A HREF="StopwordsGerman.html#isStopword(java.lang.String)">isStopword</A></B>(java.lang.String&nbsp;str)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Returns true if the given string is a stop word.</TD>
+</TR>
+</TABLE>
+&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
+<TD><B>Methods inherited from class java.lang.Object</B></TD>
+</TR>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</CODE></TD>
+</TR>
+</TABLE>
+&nbsp;
+<P>
+
+<!-- ============ FIELD DETAIL =========== -->
+
+
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="StopwordsGerman()"><!-- --></A><H3>
+StopwordsGerman</H3>
+<PRE>
+public <B>StopwordsGerman</B>()</PRE>
+<DL>
+</DL>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TD>
+</TR>
+</TABLE>
+
+<A NAME="isStopword(java.lang.String)"><!-- --></A><H3>
+isStopword</H3>
+<PRE>
+public boolean <B>isStopword</B>(java.lang.String&nbsp;str)</PRE>
+<DL>
+<DD>Returns true if the given string is a stop word.
+<P>
+<DD><DL>
+<DT><B>Specified by:</B><DD><CODE><A HREF="Stopwords.html#isStopword(java.lang.String)">isStopword</A></CODE> in class <CODE><A HREF="Stopwords.html">Stopwords</A></CODE></DL>
+</DD>
+<DD><DL>
+</DL>
+</DD>
+</DL>
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">      <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;<A HREF="StopwordsEnglish.html"><B>PREV CLASS</B></A>&nbsp;
+&nbsp;NEXT CLASS</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="StopwordsGerman.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+<TR>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+  SUMMARY:&nbsp;NESTED&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
+<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
+DETAIL:&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-frame.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-frame.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-frame.html	(revision 16561)
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+All Classes
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="All Classes";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+<FONT size="+1" CLASS="FrameHeadingFont">
+<B>All Classes</B></FONT>
+<BR>
+
+<TABLE BORDER="0" WIDTH="100%">
+<TR>
+<TD NOWRAP><FONT CLASS="FrameItemFont"><A HREF="Counter.html" TARGET="classFrame">Counter</A>
+<BR>
+<A HREF="GermanStemmer.html" TARGET="classFrame">GermanStemmer</A>
+<BR>
+<A HREF="IteratedLovinsStemmer.html" TARGET="classFrame">IteratedLovinsStemmer</A>
+<BR>
+<A HREF="KEAFilter.html" TARGET="classFrame">KEAFilter</A>
+<BR>
+<A HREF="KEAKeyphraseExtractor.html" TARGET="classFrame">KEAKeyphraseExtractor</A>
+<BR>
+<A HREF="KEAModelBuilder.html" TARGET="classFrame">KEAModelBuilder</A>
+<BR>
+<A HREF="KEAPhraseFilter.html" TARGET="classFrame">KEAPhraseFilter</A>
+<BR>
+<A HREF="LovinsStemmer.html" TARGET="classFrame">LovinsStemmer</A>
+<BR>
+<A HREF="NumbersFilter.html" TARGET="classFrame">NumbersFilter</A>
+<BR>
+<A HREF="Stemmer.html" TARGET="classFrame">Stemmer</A>
+<BR>
+<A HREF="Stopwords.html" TARGET="classFrame">Stopwords</A>
+<BR>
+<A HREF="StopwordsEnglish.html" TARGET="classFrame">StopwordsEnglish</A>
+<BR>
+<A HREF="StopwordsGerman.html" TARGET="classFrame">StopwordsGerman</A>
+<BR>
+</FONT></TD>
+</TR>
+</TABLE>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-noframe.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-noframe.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/allclasses-noframe.html	(revision 16561)
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+All Classes
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="All Classes";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+<FONT size="+1" CLASS="FrameHeadingFont">
+<B>All Classes</B></FONT>
+<BR>
+
+<TABLE BORDER="0" WIDTH="100%">
+<TR>
+<TD NOWRAP><FONT CLASS="FrameItemFont"><A HREF="Counter.html" TARGET="">Counter</A>
+<BR>
+<A HREF="GermanStemmer.html" TARGET="">GermanStemmer</A>
+<BR>
+<A HREF="IteratedLovinsStemmer.html" TARGET="">IteratedLovinsStemmer</A>
+<BR>
+<A HREF="KEAFilter.html" TARGET="">KEAFilter</A>
+<BR>
+<A HREF="KEAKeyphraseExtractor.html" TARGET="">KEAKeyphraseExtractor</A>
+<BR>
+<A HREF="KEAModelBuilder.html" TARGET="">KEAModelBuilder</A>
+<BR>
+<A HREF="KEAPhraseFilter.html" TARGET="">KEAPhraseFilter</A>
+<BR>
+<A HREF="LovinsStemmer.html" TARGET="">LovinsStemmer</A>
+<BR>
+<A HREF="NumbersFilter.html" TARGET="">NumbersFilter</A>
+<BR>
+<A HREF="Stemmer.html" TARGET="">Stemmer</A>
+<BR>
+<A HREF="Stopwords.html" TARGET="">Stopwords</A>
+<BR>
+<A HREF="StopwordsEnglish.html" TARGET="">StopwordsEnglish</A>
+<BR>
+<A HREF="StopwordsGerman.html" TARGET="">StopwordsGerman</A>
+<BR>
+</FONT></TD>
+</TR>
+</TABLE>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/constant-values.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/constant-values.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/constant-values.html	(revision 16561)
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:47 NZDT 2004 -->
+<TITLE>
+Constant Field Values
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Constant Field Values";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="constant-values.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<CENTER>
+<H1>
+Constant Field Values</H1>
+</CENTER>
+<HR SIZE="4" NOSHADE>
+<B>Contents</B><UL>
+</UL>
+
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="constant-values.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/deprecated-list.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/deprecated-list.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/deprecated-list.html	(revision 16561)
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+Deprecated List
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Deprecated List";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<CENTER>
+<H2>
+<B>Deprecated API</B></H2>
+</CENTER>
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/help-doc.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/help-doc.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/help-doc.html	(revision 16561)
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+API Help
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="API Help";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<CENTER>
+<H1>
+How This API Document Is Organized</H1>
+</CENTER>
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.<H3>
+Package</H3>
+<BLOCKQUOTE>
+
+<P>
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:<UL>
+<LI>Interfaces (italic)<LI>Classes<LI>Exceptions<LI>Errors</UL>
+</BLOCKQUOTE>
+<H3>
+Class/Interface</H3>
+<BLOCKQUOTE>
+
+<P>
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:<UL>
+<LI>Class inheritance diagram<LI>Direct Subclasses<LI>All Known Subinterfaces<LI>All Known Implementing Classes<LI>Class/interface declaration<LI>Class/interface description
+<P>
+<LI>Nested Class Summary<LI>Field Summary<LI>Constructor Summary<LI>Method Summary
+<P>
+<LI>Field Detail<LI>Constructor Detail<LI>Method Detail</UL>
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.</BLOCKQUOTE>
+<H3>
+Tree (Class Hierarchy)</H3>
+<BLOCKQUOTE>
+There is a <A HREF="overview-tree.html">Class Hierarchy</A> page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with <code>java.lang.Object</code>. The interfaces do not inherit from <code>java.lang.Object</code>.<UL>
+<LI>When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.<LI>When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.</UL>
+</BLOCKQUOTE>
+<H3>
+Deprecated API</H3>
+<BLOCKQUOTE>
+The <A HREF="deprecated-list.html">Deprecated API</A> page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.</BLOCKQUOTE>
+<H3>
+Index</H3>
+<BLOCKQUOTE>
+The <A HREF="index-all.html">Index</A> contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.</BLOCKQUOTE>
+<H3>
+Prev/Next</H3>
+These links take you to the next or previous class, interface, package, or related page.<H3>
+Frames/No Frames</H3>
+These links show and hide the HTML frames.  All pages are available with or without frames.
+<P>
+<H3>
+Serialized Form</H3>
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+<P>
+<FONT SIZE="-1">
+<EM>
+This help file applies to API documentation generated using the standard doclet.</EM>
+</FONT>
+<BR>
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index-all.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index-all.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index-all.html	(revision 16561)
@@ -0,0 +1,596 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+Index
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Index";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<A HREF="#_A_">A</A> <A HREF="#_B_">B</A> <A HREF="#_C_">C</A> <A HREF="#_D_">D</A> <A HREF="#_E_">E</A> <A HREF="#_G_">G</A> <A HREF="#_I_">I</A> <A HREF="#_K_">K</A> <A HREF="#_L_">L</A> <A HREF="#_M_">M</A> <A HREF="#_N_">N</A> <A HREF="#_S_">S</A> <A HREF="#_T_">T</A> <A HREF="#_V_">V</A> <HR>
+<A NAME="_A_"><!-- --></A><H2>
+<B>A</B></H2>
+<DL>
+<DT><A HREF="KEAPhraseFilter.html#attributeIndicesTipText()"><B>attributeIndicesTipText()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Returns the tip text for this property
+</DL>
+<HR>
+<A NAME="_B_"><!-- --></A><H2>
+<B>B</B></H2>
+<DL>
+<DT><A HREF="KEAFilter.html#batchFinished()"><B>batchFinished()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Signify that this batch of input to the filter is finished.
+<DT><A HREF="KEAPhraseFilter.html#batchFinished()"><B>batchFinished()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Signify that this batch of input to the filter is finished.
+<DT><A HREF="NumbersFilter.html#batchFinished()"><B>batchFinished()</B></A> - 
+Method in class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>Signify that this batch of input to the filter is finished.
+<DT><A HREF="KEAModelBuilder.html#buildModel(java.util.Hashtable)"><B>buildModel(Hashtable)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Builds the model from the files
+</DL>
+<HR>
+<A NAME="_C_"><!-- --></A><H2>
+<B>C</B></H2>
+<DL>
+<DT><A HREF="KEAKeyphraseExtractor.html#collectStems()"><B>collectStems()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Collects the stems of the file names.
+<DT><A HREF="KEAModelBuilder.html#collectStems()"><B>collectStems()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Collects the stems of the file names.
+<DT><A HREF="Counter.html"><B>Counter</B></A> - class <A HREF="Counter.html">Counter</A>.<DD>Class that implements a simple counter.<DT><A HREF="Counter.html#Counter()"><B>Counter()</B></A> - 
+Constructor for class <A HREF="Counter.html">Counter</A>
+<DD>Initializes the counter to 1
+<DT><A HREF="Counter.html#Counter(int)"><B>Counter(int)</B></A> - 
+Constructor for class <A HREF="Counter.html">Counter</A>
+<DD>Initializes the counter to the given value
+</DL>
+<HR>
+<A NAME="_D_"><!-- --></A><H2>
+<B>D</B></H2>
+<DL>
+<DT><A HREF="KEAPhraseFilter.html#disallowInternalPeriodsTipText()"><B>disallowInternalPeriodsTipText()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Returns the tip text for this property
+</DL>
+<HR>
+<A NAME="_E_"><!-- --></A><H2>
+<B>E</B></H2>
+<DL>
+<DT><A HREF="KEAKeyphraseExtractor.html#extractKeyphrases(java.util.Hashtable)"><B>extractKeyphrases(Hashtable)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Builds the model from the files
+</DL>
+<HR>
+<A NAME="_G_"><!-- --></A><H2>
+<B>G</B></H2>
+<DL>
+<DT><A HREF="GermanStemmer.html"><B>GermanStemmer</B></A> - class <A HREF="GermanStemmer.html">GermanStemmer</A>.<DD>A stemmer for German words.<DT><A HREF="GermanStemmer.html#GermanStemmer()"><B>GermanStemmer()</B></A> - 
+Constructor for class <A HREF="GermanStemmer.html">GermanStemmer</A>
+<DD>&nbsp;
+<DT><A HREF="KEAKeyphraseExtractor.html#getAdditionalInfo()"><B>getAdditionalInfo()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of AdditionalInfo.
+<DT><A HREF="KEAPhraseFilter.html#getAttributeIndices()"><B>getAttributeIndices()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Get the current range selection.
+<DT><A HREF="KEAFilter.html#getCheckForProperNouns()"><B>getCheckForProperNouns()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the M_CheckProperNouns value.
+<DT><A HREF="KEAModelBuilder.html#getCheckForProperNouns()"><B>getCheckForProperNouns()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the M_CheckProperNouns value.
+<DT><A HREF="KEAFilter.html#getDebug()"><B>getDebug()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of Debug.
+<DT><A HREF="KEAKeyphraseExtractor.html#getDebug()"><B>getDebug()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of debug.
+<DT><A HREF="KEAModelBuilder.html#getDebug()"><B>getDebug()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of debug.
+<DT><A HREF="KEAKeyphraseExtractor.html#getDirName()"><B>getDirName()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of dirName.
+<DT><A HREF="KEAModelBuilder.html#getDirName()"><B>getDirName()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of dirName.
+<DT><A HREF="KEAFilter.html#getDisallowInternalPeriods()"><B>getDisallowInternalPeriods()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get whether the supplied columns are to be processed
+<DT><A HREF="KEAPhraseFilter.html#getDisallowInternalPeriods()"><B>getDisallowInternalPeriods()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Get whether the supplied columns are to be processed
+<DT><A HREF="KEAModelBuilder.html#getDisallowIPeriods()"><B>getDisallowIPeriods()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of disallowIPeriods.
+<DT><A HREF="KEAFilter.html#getDocumentAtt()"><B>getDocumentAtt()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of DocumentAtt.
+<DT><A HREF="KEAKeyphraseExtractor.html#getEncoding()"><B>getEncoding()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of encoding.
+<DT><A HREF="KEAModelBuilder.html#getEncoding()"><B>getEncoding()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of encoding.
+<DT><A HREF="KEAPhraseFilter.html#getInvertSelection()"><B>getInvertSelection()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Get whether the supplied columns are to be processed
+<DT><A HREF="KEAFilter.html#getKeyphrasesAtt()"><B>getKeyphrasesAtt()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of KeyphraseAtt.
+<DT><A HREF="KEAFilter.html#getKFused()"><B>getKFused()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Gets whether keyphrase frequency attribute is used.
+<DT><A HREF="KEAFilter.html#getMaxPhraseLength()"><B>getMaxPhraseLength()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of MaxPhraseLength.
+<DT><A HREF="KEAModelBuilder.html#getMaxPhraseLength()"><B>getMaxPhraseLength()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of MaxPhraseLength.
+<DT><A HREF="KEAFilter.html#getMinNumOccur()"><B>getMinNumOccur()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of MinNumOccur.
+<DT><A HREF="KEAModelBuilder.html#getMinNumOccur()"><B>getMinNumOccur()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of MinNumOccur.
+<DT><A HREF="KEAFilter.html#getMinPhraseLength()"><B>getMinPhraseLength()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the value of MinPhraseLength.
+<DT><A HREF="KEAModelBuilder.html#getMinPhraseLength()"><B>getMinPhraseLength()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of MinPhraseLength.
+<DT><A HREF="KEAKeyphraseExtractor.html#getModelName()"><B>getModelName()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of modelName.
+<DT><A HREF="KEAModelBuilder.html#getModelName()"><B>getModelName()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of modelName.
+<DT><A HREF="KEAKeyphraseExtractor.html#getNumPhrases()"><B>getNumPhrases()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Get the value of numPhrases.
+<DT><A HREF="KEAFilter.html#getOptions()"><B>getOptions()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Gets the current settings of the filter.
+<DT><A HREF="KEAKeyphraseExtractor.html#getOptions()"><B>getOptions()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Gets the current option settings.
+<DT><A HREF="KEAModelBuilder.html#getOptions()"><B>getOptions()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Gets the current option settings.
+<DT><A HREF="KEAPhraseFilter.html#getOptions()"><B>getOptions()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Gets the current settings of the filter.
+<DT><A HREF="KEAFilter.html#getProbabilityIndex()"><B>getProbabilityIndex()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns the index of the phrases' probabilities in the output ARFF file.
+<DT><A HREF="KEAFilter.html#getRankIndex()"><B>getRankIndex()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns the index of the phrases' ranks in the output ARFF file.
+<DT><A HREF="KEAFilter.html#getStemmedPhraseIndex()"><B>getStemmedPhraseIndex()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns the index of the stemmed phrases in the output ARFF file.
+<DT><A HREF="KEAFilter.html#getStemmer()"><B>getStemmer()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the Stemmer value.
+<DT><A HREF="KEAModelBuilder.html#getStemmer()"><B>getStemmer()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the Stemmer value.
+<DT><A HREF="KEAFilter.html#getStopwords()"><B>getStopwords()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Get the M_Stopwords value.
+<DT><A HREF="KEAModelBuilder.html#getStopwords()"><B>getStopwords()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the M_Stopwords value.
+<DT><A HREF="KEAFilter.html#getUnstemmedPhraseIndex()"><B>getUnstemmedPhraseIndex()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns the index of the unstemmed phrases in the output ARFF file.
+<DT><A HREF="KEAModelBuilder.html#getUseKFrequency()"><B>getUseKFrequency()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Get the value of useKFrequency.
+<DT><A HREF="KEAFilter.html#globalInfo()"><B>globalInfo()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns a string describing this filter
+<DT><A HREF="KEAPhraseFilter.html#globalInfo()"><B>globalInfo()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Returns a string describing this filter
+<DT><A HREF="NumbersFilter.html#globalInfo()"><B>globalInfo()</B></A> - 
+Method in class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>Returns a string describing this filter
+</DL>
+<HR>
+<A NAME="_I_"><!-- --></A><H2>
+<B>I</B></H2>
+<DL>
+<DT><A HREF="Counter.html#increment()"><B>increment()</B></A> - 
+Method in class <A HREF="Counter.html">Counter</A>
+<DD>Increments the counter.
+<DT><A HREF="KEAFilter.html#input(weka.core.Instance)"><B>input(Instance)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Input an instance for filtering.
+<DT><A HREF="KEAPhraseFilter.html#input(weka.core.Instance)"><B>input(Instance)</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Input an instance for filtering.
+<DT><A HREF="NumbersFilter.html#input(weka.core.Instance)"><B>input(Instance)</B></A> - 
+Method in class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>Input an instance for filtering.
+<DT><A HREF="KEAPhraseFilter.html#invertSelectionTipText()"><B>invertSelectionTipText()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Returns the tip text for this property
+<DT><A HREF="Stopwords.html#isStopword(java.lang.String)"><B>isStopword(String)</B></A> - 
+Method in class <A HREF="Stopwords.html">Stopwords</A>
+<DD>Returns true if the given string is a stop word.
+<DT><A HREF="StopwordsEnglish.html#isStopword(java.lang.String)"><B>isStopword(String)</B></A> - 
+Method in class <A HREF="StopwordsEnglish.html">StopwordsEnglish</A>
+<DD>Returns true if the given string is a stop word.
+<DT><A HREF="StopwordsGerman.html#isStopword(java.lang.String)"><B>isStopword(String)</B></A> - 
+Method in class <A HREF="StopwordsGerman.html">StopwordsGerman</A>
+<DD>Returns true if the given string is a stop word.
+<DT><A HREF="IteratedLovinsStemmer.html"><B>IteratedLovinsStemmer</B></A> - class <A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A>.<DD>Implements the iterated version of the Lovins stemmer.<DT><A HREF="IteratedLovinsStemmer.html#IteratedLovinsStemmer()"><B>IteratedLovinsStemmer()</B></A> - 
+Constructor for class <A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A>
+<DD>&nbsp;
+</DL>
+<HR>
+<A NAME="_K_"><!-- --></A><H2>
+<B>K</B></H2>
+<DL>
+<DT><A HREF="KEAFilter.html"><B>KEAFilter</B></A> - class <A HREF="KEAFilter.html">KEAFilter</A>.<DD>This filter converts the incoming data into data appropriate for
+ keyphrase classification.<DT><A HREF="KEAFilter.html#KEAFilter()"><B>KEAFilter()</B></A> - 
+Constructor for class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>&nbsp;
+<DT><A HREF="KEAKeyphraseExtractor.html"><B>KEAKeyphraseExtractor</B></A> - class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>.<DD>Extracts keyphrases from the documents in a given directory.<DT><A HREF="KEAKeyphraseExtractor.html#KEAKeyphraseExtractor()"><B>KEAKeyphraseExtractor()</B></A> - 
+Constructor for class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>&nbsp;
+<DT><A HREF="KEAModelBuilder.html"><B>KEAModelBuilder</B></A> - class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>.<DD>Builds a keyphrase extraction model from the documents in a given
+ directory.<DT><A HREF="KEAModelBuilder.html#KEAModelBuilder()"><B>KEAModelBuilder()</B></A> - 
+Constructor for class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>&nbsp;
+<DT><A HREF="KEAPhraseFilter.html"><B>KEAPhraseFilter</B></A> - class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>.<DD>This filter splits the text in selected string
+ attributes into phrases.<DT><A HREF="KEAPhraseFilter.html#KEAPhraseFilter()"><B>KEAPhraseFilter()</B></A> - 
+Constructor for class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>&nbsp;
+</DL>
+<HR>
+<A NAME="_L_"><!-- --></A><H2>
+<B>L</B></H2>
+<DL>
+<DT><A HREF="KEAFilter.html#listOptions()"><B>listOptions()</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Returns an enumeration describing the available options
+<DT><A HREF="KEAKeyphraseExtractor.html#listOptions()"><B>listOptions()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Returns an enumeration describing the available options.
+<DT><A HREF="KEAModelBuilder.html#listOptions()"><B>listOptions()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Returns an enumeration describing the available options.
+<DT><A HREF="KEAPhraseFilter.html#listOptions()"><B>listOptions()</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Returns an enumeration describing the available options
+<DT><A HREF="KEAKeyphraseExtractor.html#loadModel()"><B>loadModel()</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Loads the extraction model from the file.
+<DT><A HREF="LovinsStemmer.html"><B>LovinsStemmer</B></A> - class <A HREF="LovinsStemmer.html">LovinsStemmer</A>.<DD>Implements the Lovins stemmer.<DT><A HREF="LovinsStemmer.html#LovinsStemmer()"><B>LovinsStemmer()</B></A> - 
+Constructor for class <A HREF="LovinsStemmer.html">LovinsStemmer</A>
+<DD>&nbsp;
+</DL>
+<HR>
+<A NAME="_M_"><!-- --></A><H2>
+<B>M</B></H2>
+<DL>
+<DT><A HREF="KEAPhraseFilter.html#m_DisallowInternalPeriods"><B>m_DisallowInternalPeriods</B></A> - 
+Variable in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Determines whether internal periods are allowed
+<DT><A HREF="KEAPhraseFilter.html#m_SelectCols"><B>m_SelectCols</B></A> - 
+Variable in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Stores which columns to select as a funky range
+<DT><A HREF="IteratedLovinsStemmer.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A>
+<DD>Stems text coming into stdin and writes it to stdout.
+<DT><A HREF="KEAFilter.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Main method for testing this class.
+<DT><A HREF="KEAKeyphraseExtractor.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>The main method.
+<DT><A HREF="KEAModelBuilder.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>The main method.
+<DT><A HREF="KEAPhraseFilter.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Main method for testing this class.
+<DT><A HREF="LovinsStemmer.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="LovinsStemmer.html">LovinsStemmer</A>
+<DD>Stems text coming into stdin and writes it to stdout.
+<DT><A HREF="NumbersFilter.html#main(java.lang.String[])"><B>main(String[])</B></A> - 
+Static method in class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>Main method for testing this class.
+</DL>
+<HR>
+<A NAME="_N_"><!-- --></A><H2>
+<B>N</B></H2>
+<DL>
+<DT><A HREF="NumbersFilter.html"><B>NumbersFilter</B></A> - class <A HREF="NumbersFilter.html">NumbersFilter</A>.<DD>Removes all numbers from all the string attributes in the given
+ dataset.<DT><A HREF="NumbersFilter.html#NumbersFilter()"><B>NumbersFilter()</B></A> - 
+Constructor for class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>&nbsp;
+</DL>
+<HR>
+<A NAME="_S_"><!-- --></A><H2>
+<B>S</B></H2>
+<DL>
+<DT><A HREF="KEAModelBuilder.html#saveModel()"><B>saveModel()</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Saves the extraction model to the file.
+<DT><A HREF="KEAKeyphraseExtractor.html#setAdditionalInfo(boolean)"><B>setAdditionalInfo(boolean)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of AdditionalInfo.
+<DT><A HREF="KEAPhraseFilter.html#setAttributeIndices(java.lang.String)"><B>setAttributeIndices(String)</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Set which attributes are to be processed
+<DT><A HREF="KEAPhraseFilter.html#setAttributeIndicesArray(int[])"><B>setAttributeIndicesArray(int[])</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Set which attributes are to be processed
+<DT><A HREF="KEAFilter.html#setCheckForProperNouns(boolean)"><B>setCheckForProperNouns(boolean)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the M_CheckProperNouns value.
+<DT><A HREF="KEAModelBuilder.html#setCheckForProperNouns(boolean)"><B>setCheckForProperNouns(boolean)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the M_CheckProperNouns value.
+<DT><A HREF="KEAFilter.html#setDebug(boolean)"><B>setDebug(boolean)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of Debug.
+<DT><A HREF="KEAKeyphraseExtractor.html#setDebug(boolean)"><B>setDebug(boolean)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of debug.
+<DT><A HREF="KEAModelBuilder.html#setDebug(boolean)"><B>setDebug(boolean)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of debug.
+<DT><A HREF="KEAKeyphraseExtractor.html#setDirName(java.lang.String)"><B>setDirName(String)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of dirName.
+<DT><A HREF="KEAModelBuilder.html#setDirName(java.lang.String)"><B>setDirName(String)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of dirName.
+<DT><A HREF="KEAFilter.html#setDisallowInternalPeriods(boolean)"><B>setDisallowInternalPeriods(boolean)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set whether selected columns should be processed.
+<DT><A HREF="KEAPhraseFilter.html#setDisallowInternalPeriods(boolean)"><B>setDisallowInternalPeriods(boolean)</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Set whether selected columns should be processed.
+<DT><A HREF="KEAModelBuilder.html#setDisallowIPeriods(boolean)"><B>setDisallowIPeriods(boolean)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of disallowIPeriods.
+<DT><A HREF="KEAFilter.html#setDocumentAtt(int)"><B>setDocumentAtt(int)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of DocumentAtt.
+<DT><A HREF="KEAKeyphraseExtractor.html#setEncoding(java.lang.String)"><B>setEncoding(String)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of encoding.
+<DT><A HREF="KEAModelBuilder.html#setEncoding(java.lang.String)"><B>setEncoding(String)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of encoding.
+<DT><A HREF="KEAFilter.html#setInputFormat(weka.core.Instances)"><B>setInputFormat(Instances)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Sets the format of the input instances.
+<DT><A HREF="KEAPhraseFilter.html#setInputFormat(weka.core.Instances)"><B>setInputFormat(Instances)</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Sets the format of the input instances.
+<DT><A HREF="NumbersFilter.html#setInputFormat(weka.core.Instances)"><B>setInputFormat(Instances)</B></A> - 
+Method in class <A HREF="NumbersFilter.html">NumbersFilter</A>
+<DD>Sets the format of the input instances.
+<DT><A HREF="KEAPhraseFilter.html#setInvertSelection(boolean)"><B>setInvertSelection(boolean)</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Set whether selected columns should be processed.
+<DT><A HREF="KEAFilter.html#setKeyphrasesAtt(int)"><B>setKeyphrasesAtt(int)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of KeyphrasesAtt.
+<DT><A HREF="KEAFilter.html#setKFused(boolean)"><B>setKFused(boolean)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Sets whether keyphrase frequency attribute is used.
+<DT><A HREF="KEAFilter.html#setMaxPhraseLength(int)"><B>setMaxPhraseLength(int)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of MaxPhraseLength.
+<DT><A HREF="KEAModelBuilder.html#setMaxPhraseLength(int)"><B>setMaxPhraseLength(int)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of MaxPhraseLength.
+<DT><A HREF="KEAFilter.html#setMinNumOccur(int)"><B>setMinNumOccur(int)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of MinNumOccur.
+<DT><A HREF="KEAModelBuilder.html#setMinNumOccur(int)"><B>setMinNumOccur(int)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of MinNumOccur.
+<DT><A HREF="KEAFilter.html#setMinPhraseLength(int)"><B>setMinPhraseLength(int)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the value of MinPhraseLength.
+<DT><A HREF="KEAModelBuilder.html#setMinPhraseLength(int)"><B>setMinPhraseLength(int)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of MinPhraseLength.
+<DT><A HREF="KEAKeyphraseExtractor.html#setModelName(java.lang.String)"><B>setModelName(String)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of modelName.
+<DT><A HREF="KEAModelBuilder.html#setModelName(java.lang.String)"><B>setModelName(String)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of modelName.
+<DT><A HREF="KEAKeyphraseExtractor.html#setNumPhrases(int)"><B>setNumPhrases(int)</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Set the value of numPhrases.
+<DT><A HREF="KEAFilter.html#setOptions(java.lang.String[])"><B>setOptions(String[])</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Parses a given list of options controlling the behaviour of this object.
+<DT><A HREF="KEAKeyphraseExtractor.html#setOptions(java.lang.String[])"><B>setOptions(String[])</B></A> - 
+Method in class <A HREF="KEAKeyphraseExtractor.html">KEAKeyphraseExtractor</A>
+<DD>Parses a given list of options controlling the behaviour of this object.
+<DT><A HREF="KEAModelBuilder.html#setOptions(java.lang.String[])"><B>setOptions(String[])</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Parses a given list of options controlling the behaviour of this object.
+<DT><A HREF="KEAPhraseFilter.html#setOptions(java.lang.String[])"><B>setOptions(String[])</B></A> - 
+Method in class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A>
+<DD>Parses a given list of options controlling the behaviour of this object.
+<DT><A HREF="KEAFilter.html#setStemmer(Stemmer)"><B>setStemmer(Stemmer)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the Stemmer value.
+<DT><A HREF="KEAModelBuilder.html#setStemmer(Stemmer)"><B>setStemmer(Stemmer)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the Stemmer value.
+<DT><A HREF="KEAFilter.html#setStopwords(Stopwords)"><B>setStopwords(Stopwords)</B></A> - 
+Method in class <A HREF="KEAFilter.html">KEAFilter</A>
+<DD>Set the M_Stopwords value.
+<DT><A HREF="KEAModelBuilder.html#setStopwords(Stopwords)"><B>setStopwords(Stopwords)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the M_Stopwords value.
+<DT><A HREF="KEAModelBuilder.html#setUseKFrequency(boolean)"><B>setUseKFrequency(boolean)</B></A> - 
+Method in class <A HREF="KEAModelBuilder.html">KEAModelBuilder</A>
+<DD>Set the value of useKFrequency.
+<DT><A HREF="GermanStemmer.html#stem(java.lang.String)"><B>stem(String)</B></A> - 
+Method in class <A HREF="GermanStemmer.html">GermanStemmer</A>
+<DD>Stemms the given term to an unique <tt>discriminator</tt>.
+<DT><A HREF="IteratedLovinsStemmer.html#stem(java.lang.String)"><B>stem(String)</B></A> - 
+Method in class <A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A>
+<DD>Iterated stemming of the given word.
+<DT><A HREF="LovinsStemmer.html#stem(java.lang.String)"><B>stem(String)</B></A> - 
+Method in class <A HREF="LovinsStemmer.html">LovinsStemmer</A>
+<DD>Returns the stemmed version of the given word.
+<DT><A HREF="Stemmer.html#stem(java.lang.String)"><B>stem(String)</B></A> - 
+Method in class <A HREF="Stemmer.html">Stemmer</A>
+<DD>Iterated stemming of the given word.
+<DT><A HREF="Stemmer.html"><B>Stemmer</B></A> - class <A HREF="Stemmer.html">Stemmer</A>.<DD>Abstract class for stemmers.<DT><A HREF="Stemmer.html#Stemmer()"><B>Stemmer()</B></A> - 
+Constructor for class <A HREF="Stemmer.html">Stemmer</A>
+<DD>&nbsp;
+<DT><A HREF="Stemmer.html#stemString(java.lang.String)"><B>stemString(String)</B></A> - 
+Method in class <A HREF="Stemmer.html">Stemmer</A>
+<DD>Stems everything in the given string.
+<DT><A HREF="Stopwords.html"><B>Stopwords</B></A> - class <A HREF="Stopwords.html">Stopwords</A>.<DD>Class that can test whether a given string is a stop word.<DT><A HREF="Stopwords.html#Stopwords()"><B>Stopwords()</B></A> - 
+Constructor for class <A HREF="Stopwords.html">Stopwords</A>
+<DD>&nbsp;
+<DT><A HREF="StopwordsEnglish.html"><B>StopwordsEnglish</B></A> - class <A HREF="StopwordsEnglish.html">StopwordsEnglish</A>.<DD>Class that can test whether a given string is a stop word.<DT><A HREF="StopwordsEnglish.html#StopwordsEnglish()"><B>StopwordsEnglish()</B></A> - 
+Constructor for class <A HREF="StopwordsEnglish.html">StopwordsEnglish</A>
+<DD>&nbsp;
+<DT><A HREF="StopwordsGerman.html"><B>StopwordsGerman</B></A> - class <A HREF="StopwordsGerman.html">StopwordsGerman</A>.<DD>Class that can test whether a given string is a stop word.<DT><A HREF="StopwordsGerman.html#StopwordsGerman()"><B>StopwordsGerman()</B></A> - 
+Constructor for class <A HREF="StopwordsGerman.html">StopwordsGerman</A>
+<DD>&nbsp;
+</DL>
+<HR>
+<A NAME="_T_"><!-- --></A><H2>
+<B>T</B></H2>
+<DL>
+<DT><A HREF="Counter.html#toString()"><B>toString()</B></A> - 
+Method in class <A HREF="Counter.html">Counter</A>
+<DD>Returns string containing value.
+</DL>
+<HR>
+<A NAME="_V_"><!-- --></A><H2>
+<B>V</B></H2>
+<DL>
+<DT><A HREF="Counter.html#value()"><B>value()</B></A> - 
+Method in class <A HREF="Counter.html">Counter</A>
+<DD>Gets the value.
+</DL>
+<HR>
+<A HREF="#_A_">A</A> <A HREF="#_B_">B</A> <A HREF="#_C_">C</A> <A HREF="#_D_">D</A> <A HREF="#_E_">E</A> <A HREF="#_G_">G</A> <A HREF="#_I_">I</A> <A HREF="#_K_">K</A> <A HREF="#_L_">L</A> <A HREF="#_M_">M</A> <A HREF="#_N_">N</A> <A HREF="#_S_">S</A> <A HREF="#_T_">T</A> <A HREF="#_V_">V</A> 
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/index.html	(revision 16561)
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004-->
+<TITLE>
+Generated Documentation (Untitled)
+</TITLE>
+</HEAD>
+<FRAMESET cols="20%,80%">
+<FRAME src="allclasses-frame.html" name="packageFrame">
+<FRAME src="Counter.html" name="classFrame">
+</FRAMESET>
+<NOFRAMES>
+<H2>
+Frame Alert</H2>
+
+<P>
+This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+<BR>
+Link to<A HREF="Counter.html">Non-frame version.</A></NOFRAMES>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/overview-tree.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/overview-tree.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/overview-tree.html	(revision 16561)
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+Class Hierarchy
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Class Hierarchy";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<CENTER>
+<H2>
+Hierarchy For All Packages</H2>
+</CENTER>
+<H2>
+Class Hierarchy
+</H2>
+<UL>
+<LI TYPE="circle">class java.lang.Object<UL>
+<LI TYPE="circle">class <A HREF="Counter.html"><B>Counter</B></A> (implements java.io.Serializable)
+<LI TYPE="circle">class weka.filters.Filter (implements java.io.Serializable)
+<UL>
+<LI TYPE="circle">class <A HREF="KEAFilter.html"><B>KEAFilter</B></A> (implements weka.core.OptionHandler)
+<LI TYPE="circle">class <A HREF="KEAPhraseFilter.html"><B>KEAPhraseFilter</B></A> (implements weka.core.OptionHandler)
+<LI TYPE="circle">class <A HREF="NumbersFilter.html"><B>NumbersFilter</B></A></UL>
+<LI TYPE="circle">class <A HREF="KEAKeyphraseExtractor.html"><B>KEAKeyphraseExtractor</B></A> (implements weka.core.OptionHandler)
+<LI TYPE="circle">class <A HREF="KEAModelBuilder.html"><B>KEAModelBuilder</B></A> (implements weka.core.OptionHandler)
+<LI TYPE="circle">class <A HREF="Stemmer.html"><B>Stemmer</B></A> (implements java.io.Serializable)
+<UL>
+<LI TYPE="circle">class <A HREF="GermanStemmer.html"><B>GermanStemmer</B></A><LI TYPE="circle">class <A HREF="LovinsStemmer.html"><B>LovinsStemmer</B></A> (implements java.io.Serializable)
+<UL>
+<LI TYPE="circle">class <A HREF="IteratedLovinsStemmer.html"><B>IteratedLovinsStemmer</B></A></UL>
+</UL>
+<LI TYPE="circle">class <A HREF="Stopwords.html"><B>Stopwords</B></A> (implements java.io.Serializable)
+<UL>
+<LI TYPE="circle">class <A HREF="StopwordsEnglish.html"><B>StopwordsEnglish</B></A><LI TYPE="circle">class <A HREF="StopwordsGerman.html"><B>StopwordsGerman</B></A></UL>
+</UL>
+</UL>
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/package-list
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/package-list	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/package-list	(revision 16561)
@@ -0,0 +1,1 @@
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/packages.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/packages.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/packages.html	(revision 16561)
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:48 NZDT 2004 -->
+<TITLE>
+
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<BODY BGCOLOR="white">
+
+<BR>
+
+<BR>
+
+<BR>
+<CENTER>
+The front page has been relocated.Please see:
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="index.html">Frame version</A>
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="Counter.html">Non-frame version.</A></CENTER>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/serialized-form.html
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/serialized-form.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/serialized-form.html	(revision 16561)
@@ -0,0 +1,604 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc on Fri Mar 19 17:50:50 NZDT 2004 -->
+<TITLE>
+Serialized Form
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title="Serialized Form";
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_top"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_top_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+<CENTER>
+<H1>
+Serialized Form</H1>
+</CENTER>
+<A NAME="Counter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="Counter.html">Counter</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+<A NAME="serializedForm"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Serialized Fields</B></FONT></TD>
+</TR>
+</TABLE>
+
+<H3>
+m_val</H3>
+<PRE>
+int <B>m_val</B></PRE>
+<DL>
+<DD>Integer value stored
+<P>
+<DL>
+</DL>
+</DL>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="GermanStemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="GermanStemmer.html">GermanStemmer</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+<A NAME="serializedForm"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Serialized Fields</B></FONT></TD>
+</TR>
+</TABLE>
+
+<H3>
+sb</H3>
+<PRE>
+java.lang.StringBuffer <B>sb</B></PRE>
+<DL>
+<DD>Buffer for the terms while stemming them.
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+uppercase</H3>
+<PRE>
+boolean <B>uppercase</B></PRE>
+<DL>
+<DD>Indicates if a term is handled as a noun.
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+substCount</H3>
+<PRE>
+int <B>substCount</B></PRE>
+<DL>
+<DD>Amount of characters that are removed with <tt>substitute()</tt> while stemming.
+<P>
+<DL>
+</DL>
+</DL>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="IteratedLovinsStemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="IteratedLovinsStemmer.html">IteratedLovinsStemmer</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="KEAFilter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="KEAFilter.html">KEAFilter</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+<A NAME="serializedForm"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Serialized Fields</B></FONT></TD>
+</TR>
+</TABLE>
+
+<H3>
+m_DocumentAtt</H3>
+<PRE>
+int <B>m_DocumentAtt</B></PRE>
+<DL>
+<DD>Index of attribute containing the documents
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_KeyphrasesAtt</H3>
+<PRE>
+int <B>m_KeyphrasesAtt</B></PRE>
+<DL>
+<DD>Index of attribute containing the keyphrases
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_MaxPhraseLength</H3>
+<PRE>
+int <B>m_MaxPhraseLength</B></PRE>
+<DL>
+<DD>The maximum length of phrases
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_MinPhraseLength</H3>
+<PRE>
+int <B>m_MinPhraseLength</B></PRE>
+<DL>
+<DD>The minimum length of phrases
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_KFused</H3>
+<PRE>
+boolean <B>m_KFused</B></PRE>
+<DL>
+<DD>Is keyphrase frequency attribute being used?
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_Debug</H3>
+<PRE>
+boolean <B>m_Debug</B></PRE>
+<DL>
+<DD>Flag for debugging mode
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_DisallowInternalPeriods</H3>
+<PRE>
+boolean <B>m_DisallowInternalPeriods</B></PRE>
+<DL>
+<DD>Determines whether internal periods are allowed
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_MinNumOccur</H3>
+<PRE>
+int <B>m_MinNumOccur</B></PRE>
+<DL>
+<DD>The minimum number of occurences of a phrase
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_NumFeatures</H3>
+<PRE>
+int <B>m_NumFeatures</B></PRE>
+<DL>
+<DD>The number of features describing a phrase
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_TfidfIndex</H3>
+<PRE>
+int <B>m_TfidfIndex</B></PRE>
+<DL>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_FirstOccurIndex</H3>
+<PRE>
+int <B>m_FirstOccurIndex</B></PRE>
+<DL>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_KeyFreqIndex</H3>
+<PRE>
+int <B>m_KeyFreqIndex</B></PRE>
+<DL>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_PunctFilter</H3>
+<PRE>
+KEAPhraseFilter <B>m_PunctFilter</B></PRE>
+<DL>
+<DD>The punctuation filter used by this filter
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_NumbersFilter</H3>
+<PRE>
+NumbersFilter <B>m_NumbersFilter</B></PRE>
+<DL>
+<DD>The numbers filter used by this filter
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_Classifier</H3>
+<PRE>
+weka.classifiers.DistributionClassifier <B>m_Classifier</B></PRE>
+<DL>
+<DD>The actual classifier used to compute probabilities
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_Dictionary</H3>
+<PRE>
+java.util.HashMap <B>m_Dictionary</B></PRE>
+<DL>
+<DD>The dictionary containing the document frequencies
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_KeyphraseDictionary</H3>
+<PRE>
+java.util.HashMap <B>m_KeyphraseDictionary</B></PRE>
+<DL>
+<DD>The dictionary containing the keyphrases
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_NumDocs</H3>
+<PRE>
+int <B>m_NumDocs</B></PRE>
+<DL>
+<DD>The number of documents in the global frequencies corpus
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_ClassifierData</H3>
+<PRE>
+weka.core.Instances <B>m_ClassifierData</B></PRE>
+<DL>
+<DD>Template for the classifier data
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_Stemmer</H3>
+<PRE>
+Stemmer <B>m_Stemmer</B></PRE>
+<DL>
+<DD>The stemmer to be used
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_Stopwords</H3>
+<PRE>
+Stopwords <B>m_Stopwords</B></PRE>
+<DL>
+<DD>The list of stop words to be used
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_CheckForProperNouns</H3>
+<PRE>
+boolean <B>m_CheckForProperNouns</B></PRE>
+<DL>
+<DD>Determines whether check for proper nouns is performed
+<P>
+<DL>
+</DL>
+</DL>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="KEAPhraseFilter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="KEAPhraseFilter.html">KEAPhraseFilter</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+<A NAME="serializedForm"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TD COLSPAN=1><FONT SIZE="+2">
+<B>Serialized Fields</B></FONT></TD>
+</TR>
+</TABLE>
+
+<H3>
+m_SelectCols</H3>
+<PRE>
+weka.core.Range <B>m_SelectCols</B></PRE>
+<DL>
+<DD>Stores which columns to select as a funky range
+<P>
+<DL>
+</DL>
+</DL>
+<HR>
+
+<H3>
+m_DisallowInternalPeriods</H3>
+<PRE>
+boolean <B>m_DisallowInternalPeriods</B></PRE>
+<DL>
+<DD>Determines whether internal periods are allowed
+<P>
+<DL>
+</DL>
+</DL>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="LovinsStemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="LovinsStemmer.html">LovinsStemmer</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="NumbersFilter"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="NumbersFilter.html">NumbersFilter</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="Stemmer"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="Stemmer.html">Stemmer</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="Stopwords"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="Stopwords.html">Stopwords</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="StopwordsEnglish"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="StopwordsEnglish.html">StopwordsEnglish</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR SIZE="4" NOSHADE>
+<A NAME="StopwordsGerman"><!-- --></A>
+<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
+<TR BGCOLOR="#CCCCFF" CLASS="TableSubHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Class <A HREF="StopwordsGerman.html">StopwordsGerman</A> implements Serializable</B></FONT></TD>
+</TR>
+</TABLE>
+
+<P>
+
+<P>
+<HR>
+
+<!-- ========== START OF NAVBAR ========== -->
+<A NAME="navbar_bottom"><!-- --></A>
+<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
+<TR>
+<TD COLSPAN=3 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
+<A NAME="navbar_bottom_firstrow"><!-- --></A>
+<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
+  <TR ALIGN="center" VALIGN="top">
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="overview-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
+  <TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1">    <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
+  </TR>
+</TABLE>
+</TD>
+<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
+</EM>
+</TD>
+</TR>
+
+<TR>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+&nbsp;PREV&nbsp;
+&nbsp;NEXT</FONT></TD>
+<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
+  <A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>  &nbsp;
+&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A>  &nbsp;
+&nbsp;
+<SCRIPT>
+  <!--
+  if(window==top) {
+    document.writeln('<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>');
+  }
+  //-->
+</SCRIPT>
+<NOSCRIPT>
+<A HREF="allclasses-noframe.html" TARGET=""><B>All Classes</B></A>
+</NOSCRIPT>
+</FONT></TD>
+</TR>
+</TABLE>
+<!-- =========== END OF NAVBAR =========== -->
+
+<HR>
+
+</BODY>
+</HTML>
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/stylesheet.css
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/stylesheet.css	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/doc/stylesheet.css	(revision 16561)
@@ -0,0 +1,29 @@
+/* Javadoc style sheet */
+
+/* Define colors, fonts and other style attributes here to override the defaults */
+
+/* Page background color */
+body { background-color: #FFFFFF }
+
+/* Table colors */
+.TableHeadingColor     { background: #CCCCFF } /* Dark mauve */
+.TableSubHeadingColor  { background: #EEEEFF } /* Light mauve */
+.TableRowColor         { background: #FFFFFF } /* White */
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont   { font-size: 10pts; font-family: Helvetica, Arial, san-serif }
+.FrameHeadingFont { font-size: 10pts; font-family: Helvetica, Arial, san-serif }
+.FrameItemFont    { font-size: 10pts; font-family: Helvetica, Arial, san-serif }
+
+/* Example of smaller, sans-serif font in frames */
+/* .FrameItemFont  { font-size: 10pt; font-family: Helvetica, Arial, sans-serif } */
+
+/* Navigation bar fonts and colors */
+.NavBarCell1    { background-color:#EEEEFF;}/* Light mauve */
+.NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */
+.NavBarFont1    { font-family: Arial, Helvetica, sans-serif; color:#000000;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+
+.NavBarCell2    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+.NavBarCell3    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/Classifier.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/Classifier.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/Classifier.java	(revision 16561)
@@ -0,0 +1,115 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Classifier.java
+ *    Copyright (C) 1999 Eibe Frank, Len Trigg
+ *
+ */
+
+package weka.classifiers;
+
+import java.io.Serializable;
+import weka.core.Instance;
+import weka.core.Instances;
+import weka.core.SerializedObject;
+import weka.core.Utils;
+
+
+/** 
+ * Abstract classifier. All schemes for numeric or nominal prediction in
+ * Weka extend this class.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public abstract class Classifier implements Cloneable, Serializable {
+ 
+  /**
+   * Generates a classifier. Must initialize all fields of the classifier
+   * that are not being set via options (ie. multiple calls of buildClassifier
+   * must always lead to the same result). Must not change the dataset
+   * in any way.
+   *
+   * @param data set of instances serving as training data 
+   * @exception Exception if the classifier has not been 
+   * generated successfully
+   */
+  public abstract void buildClassifier(Instances data) throws Exception;
+
+  /**
+   * Classifies a given instance.
+   *
+   * @param instance the instance to be classified
+   * @return index of the predicted class as a double
+   * if the class is nominal, otherwise the predicted value
+   * @exception Exception if instance could not be classified
+   * successfully
+   */
+  public abstract double classifyInstance(Instance instance) throws Exception; 
+
+
+  
+  /**
+   * Creates a new instance of a classifier given it's class name and
+   * (optional) arguments to pass to it's setOptions method. If the
+   * classifier implements OptionHandler and the options parameter is
+   * non-null, the classifier will have it's options set.
+   *
+   * @param classifierName the fully qualified class name of the classifier
+   * @param options an array of options suitable for passing to setOptions. May
+   * be null.
+   * @return the newly created classifier, ready for use.
+   * @exception Exception if the classifier name is invalid, or the options
+   * supplied are not acceptable to the classifier
+   */
+  public static Classifier forName(String classifierName,
+				   String [] options) throws Exception {
+
+    return (Classifier)Utils.forName(Classifier.class,
+				     classifierName,
+				     options);
+  }
+
+  /**
+   * Creates copies of the current classifier, which can then
+   * be used for boosting etc. Note that this method now uses
+   * Serialization to perform a deep copy, so the Classifier
+   * object must be fully Serializable. Any currently built model
+   * will now be copied as well.
+   *
+   * @param model an example classifier to copy
+   * @param num the number of classifiers copies to create.
+   * @return an array of classifiers.
+   * @exception Exception if an error occurs
+   */
+  public static Classifier [] makeCopies(Classifier model,
+					 int num) throws Exception {
+
+    if (model == null) {
+      throw new Exception("No model classifier set");
+    }
+    Classifier [] classifiers = new Classifier [num];
+    SerializedObject so = new SerializedObject(model);
+    for(int i = 0; i < classifiers.length; i++) {
+      classifiers[i] = (Classifier) so.getObject();
+    }
+    return classifiers;
+  }
+
+}
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/DistributionClassifier.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/DistributionClassifier.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/DistributionClassifier.java	(revision 16561)
@@ -0,0 +1,92 @@
+/*
+ *    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.
+ */
+
+/*
+ *    DistributionClassifier.java
+ *    Copyright (C) 1999 Eibe Frank, Len Trigg
+ *
+ */
+
+package weka.classifiers;
+
+import weka.core.*;
+
+/** 
+ * Abstract classification model that produces (for each test instance)
+ * an estimate of the membership in each class 
+ * (ie. a probability distribution).
+ *
+ * @author   Eibe Frank (trigg@cs.waikato.ac.nz)
+ * @author   Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public abstract class DistributionClassifier extends Classifier {
+
+  /**
+   * Predicts the class memberships for a given instance. If
+   * an instance is unclassified, the returned array elements
+   * must be all zero. If the class is numeric, the array
+   * must consist of only one element, which contains the
+   * predicted value.
+   *
+   * @param instance the instance to be classified
+   * @return an array containing the estimated membership 
+   * probabilities of the test instance in each class (this 
+   * should sum to at most 1)
+   * @exception Exception if distribution could not be 
+   * computed successfully
+   */
+  public abstract double[] distributionForInstance(Instance instance) 
+       throws Exception;
+
+  /**
+   * Classifies the given test instance. The instance has to belong to a
+   * dataset when it's being classified.
+   *
+   * @param instance the instance to be classified
+   * @return the predicted most likely class for the instance or 
+   * Instance.missingValue() if no prediction is made
+   * @exception Exception if an error occurred during the prediction
+   */
+  public double classifyInstance(Instance instance) throws Exception {
+
+    double [] dist = distributionForInstance(instance);
+    if (dist == null) {
+      throw new Exception("Null distribution predicted");
+    }
+    switch (instance.classAttribute().type()) {
+    case Attribute.NOMINAL:
+      double max = 0;
+      int maxIndex = 0;
+      
+      for (int i = 0; i < dist.length; i++) {
+	if (dist[i] > max) {
+	  maxIndex = i;
+	  max = dist[i];
+	}
+      }
+      if (max > 0) {
+	return maxIndex;
+      } else {
+	return Instance.missingValue();
+      }
+    case Attribute.NUMERIC:
+      return dist[0];
+    default:
+      return Instance.missingValue();
+    }
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/FilteredClassifier.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/FilteredClassifier.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/FilteredClassifier.java	(revision 16561)
@@ -0,0 +1,374 @@
+/*
+ *    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.
+ */
+
+/*
+ *    FilteredClassifier.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+package weka.classifiers;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import weka.core.Instance;
+import weka.core.Instances;
+import weka.core.Option;
+import weka.core.OptionHandler;
+import weka.core.Utils;
+import weka.filters.Filter;
+import weka.core.Attribute;
+
+
+/**
+ * Class for running an arbitrary classifier on data that has been passed
+ * through an arbitrary filter.<p>
+ *
+ * Valid options from the command line are:<p>
+ *
+ * -B classifierstring <br>
+ * Classifierstring should contain the full class name of a classifier
+ * followed by options to the classifier.
+ * (required).<p>
+ *
+ * -F filterstring <br>
+ * Filterstring should contain the full class name of a filter
+ * followed by options to the filter.
+ * (required).<p>
+ *
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class FilteredClassifier extends DistributionClassifier
+  implements OptionHandler {
+
+  /** The classifier */
+  protected Classifier m_Classifier = new weka.classifiers.NaiveBayesSimple();
+
+  /** The filter */
+  protected Filter m_Filter = new weka.filters.DiscretizeFilter();
+
+  /** The instance structure of the filtered instances */
+  protected Instances m_FilteredInstances;
+  
+  /**
+   * Default constructor specifying NaiveBayesSimple as the classifier and
+   * DiscretizeFilter as the filter. Both of these are just placeholders
+   * for more useful selections.
+   */
+  public FilteredClassifier() {
+
+    this(new weka.classifiers.NaiveBayesSimple(), 
+	 new weka.filters.DiscretizeFilter());
+  }
+
+  /**
+   * Constructor that specifies the subclassifier and filter to use.
+   *
+   * @param classifier the Classifier to receive filtered instances.
+   * @param filter the Filter that will process instances before
+   * passing to the Classifier.
+   */
+  public FilteredClassifier(Classifier classifier, Filter filter) {
+
+    m_Classifier = classifier;
+    m_Filter = filter;
+  }
+
+  /**
+   * Returns an enumeration describing the available options
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(2);
+
+    newVector.addElement(new Option(
+	      "\tFull class name of classifier to use, followed\n"
+	      + "\tby scheme options. (required)\n"
+	      + "\teg: \"weka.classifiers.NaiveBayes -D\"",
+	      "B", 1, "-B <classifier specification>"));
+    newVector.addElement(new Option(
+	      "\tFull class name of filter to use, followed\n"
+	      + "\tby filter options. (required)\n"
+	      + "\teg: \"weka.filters.AttributeFilter -V -R 1,2\"",
+	      "F", 1, "-F <filter specification>"));
+    return newVector.elements();
+  }
+
+  /**
+   * Parses a given list of options. Valid options are:<p>
+   *
+   * -B classifierstring <br>
+   * Classifierstring should contain the full class name of a classifier
+   * followed by options to the classifier.
+   * (required).<p>
+   *
+   * -F filterstring <br>
+   * Filterstring should contain the full class name of a filter
+   * followed by options to the filter.
+   * (required).<p>
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    String classifierString = Utils.getOption('B', options);
+    if (classifierString.length() == 0) {
+      throw new Exception("A classifier must be specified"
+			  + " with the -B option.");
+    }
+    String [] classifierSpec = Utils.splitOptions(classifierString);
+    if (classifierSpec.length == 0) {
+      throw new Exception("Invalid classifier specification string");
+    }
+    String classifierName = classifierSpec[0];
+    classifierSpec[0] = "";
+    setClassifier(Classifier.forName(classifierName, classifierSpec));
+    
+    // Same for filter
+    String filterString = Utils.getOption('F', options);
+    if (filterString.length() == 0) {
+      throw new Exception("A filter must be specified"
+			  + " with the -F option.");
+    }
+    String [] filterSpec = Utils.splitOptions(filterString);
+    if (filterSpec.length == 0) {
+      throw new Exception("Invalid filter specification string");
+    }
+    String filterName = filterSpec[0];
+    filterSpec[0] = "";
+    setFilter((Filter) Utils.forName(Filter.class, filterName, filterSpec));
+  }
+
+  /**
+   * Gets the current settings of the Classifier.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [4];
+    int current = 0;
+
+    options[current++] = "-B";
+    options[current++] = "" + getClassifierSpec();
+
+    // Same for filter
+    options[current++] = "-F";
+    options[current++] = "" + getFilterSpec();
+    
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+  /**
+   * Sets the classifier
+   *
+   * @param classifier the classifier with all options set.
+   */
+  public void setClassifier(Classifier classifier) {
+
+    m_Classifier = classifier;
+  }
+
+  /**
+   * Gets the classifier used.
+   *
+   * @return the classifier
+   */
+  public Classifier getClassifier() {
+
+    return m_Classifier;
+  }
+  
+  /**
+   * Gets the classifier specification string, which contains the class name of
+   * the classifier and any options to the classifier
+   *
+   * @return the classifier string.
+   */
+  protected String getClassifierSpec() {
+    
+    Classifier c = getClassifier();
+    if (c instanceof OptionHandler) {
+      return c.getClass().getName() + " "
+	+ Utils.joinOptions(((OptionHandler)c).getOptions());
+    }
+    return c.getClass().getName();
+  }
+
+  /**
+   * Sets the filter
+   *
+   * @param filter the filter with all options set.
+   */
+  public void setFilter(Filter filter) {
+
+    m_Filter = filter;
+  }
+
+  /**
+   * Gets the filter used.
+   *
+   * @return the filter
+   */
+  public Filter getFilter() {
+
+    return m_Filter;
+  }
+  
+  /**
+   * Gets the filter specification string, which contains the class name of
+   * the filter and any options to the filter
+   *
+   * @return the filter string.
+   */
+  protected String getFilterSpec() {
+    
+    Filter c = getFilter();
+    if (c instanceof OptionHandler) {
+      return c.getClass().getName() + " "
+	+ Utils.joinOptions(((OptionHandler)c).getOptions());
+    }
+    return c.getClass().getName();
+  }
+
+  /**
+   * Build the classifier on the filtered data.
+   *
+   * @param data the training data
+   * @exception Exception if the classifier could not be built successfully
+   */
+  public void buildClassifier(Instances data) throws Exception {
+
+    if (m_Classifier == null) {
+      throw new Exception("No base classifiers have been set!");
+    }
+    /*
+    String fname = m_Filter.getClass().getName();
+    fname = fname.substring(fname.lastIndexOf('.') + 1);
+    util.Timer t = util.Timer.getTimer("FilteredClassifier::" + fname);
+    t.start();
+    */
+    m_Filter.setInputFormat(data);
+    data = Filter.useFilter(data, m_Filter);
+    //t.stop();
+    m_FilteredInstances = data.stringFreeStructure();
+    m_Classifier.buildClassifier(data);
+  }
+
+  /**
+   * Classifies a given instance after filtering.
+   *
+   * @param instance the instance to be classified
+   * @exception Exception if instance could not be classified
+   * successfully
+   */
+  public double [] distributionForInstance(Instance instance)
+    throws Exception {
+
+    /*
+      System.err.println("FilteredClassifier:: " 
+                         + m_Filter.getClass().getName()
+                         + " in: " + instance);
+    */
+    if (m_Filter.numPendingOutput() > 0) {
+      throw new Exception("Filter output queue not empty!");
+    }
+    /*
+    String fname = m_Filter.getClass().getName();
+    fname = fname.substring(fname.lastIndexOf('.') + 1);
+    util.Timer t = util.Timer.getTimer("FilteredClassifier::" + fname);
+    t.start();
+    */
+    if (!m_Filter.input(instance)) {
+      throw new Exception("Filter didn't make the test instance"
+			  + " immediately available!");
+    }
+    m_Filter.batchFinished();
+    Instance newInstance = m_Filter.output();
+    //t.stop();
+    /*
+    System.err.println("FilteredClassifier:: " 
+                       + m_Filter.getClass().getName()
+                       + " out: " + newInstance);
+    */
+    if (m_Classifier instanceof DistributionClassifier) {
+      return ((DistributionClassifier)m_Classifier)
+	.distributionForInstance(newInstance);
+    }
+    double pred = m_Classifier.classifyInstance(newInstance);
+    double [] result = new double [m_FilteredInstances.numClasses()];
+    if (Instance.isMissingValue(pred)) {
+      return result;
+    }
+    switch (instance.classAttribute().type()) {
+    case Attribute.NOMINAL:
+      result[(int) pred] = 1.0;
+      break;
+    case Attribute.NUMERIC:
+      result[0] = pred;
+      break;
+    default:
+      throw new Exception("Unknown class type");
+    }
+    return result;
+  }
+
+  /**
+   * Output a representation of this classifier
+   */
+  public String toString() {
+
+    if (m_FilteredInstances == null) {
+      return "FilteredClassifier: No model built yet.";
+    }
+
+    String result = "FilteredClassifier using "
+      + getClassifierSpec()
+      + " on data filtered through "
+      + getFilterSpec()
+      + "\n\nFiltered Header\n"
+      + m_FilteredInstances.toString()
+      + "\n\nClassifier Model\n"
+      + m_Classifier.toString();
+    return result;
+  }
+
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain the following arguments:
+   * -t training file [-T test file] [-c class index]
+   */
+  public static void main(String [] argv) {
+
+    try {
+      System.out.println("Evaluation disabled!");
+      //System.out.println(Evaluation.evaluateModel(new FilteredClassifier(),
+      //					  argv));
+    } catch (Exception e) {
+      System.err.println(e.getMessage());
+    }
+  }
+
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/NaiveBayesSimple.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/NaiveBayesSimple.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/classifiers/NaiveBayesSimple.java	(revision 16561)
@@ -0,0 +1,331 @@
+/*
+ *    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.
+ */
+
+/*
+ *    NaiveBayesSimple.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.classifiers;
+
+import java.io.*;
+import java.util.*;
+import weka.core.*;
+
+/**
+ * Class for building and using a simple Naive Bayes classifier.
+ * Numeric attributes are modelled by a normal distribution. For more
+ * information, see<p>
+ *
+ * Richard Duda and Peter Hart (1973).<i>Pattern
+ * Classification and Scene Analysis</i>. Wiley, New York.
+
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$ 
+*/
+public class NaiveBayesSimple extends DistributionClassifier {
+
+  /** All the counts for nominal attributes. */
+  private double [][][] m_Counts;
+  
+  /** The means for numeric attributes. */
+  private double [][] m_Means;
+
+  /** The standard deviations for numeric attributes. */
+  private double [][] m_Devs;
+
+  /** The prior probabilities of the classes. */
+  private double [] m_Priors;
+
+  /** The instances used for training. */
+  private Instances m_Instances;
+
+  /** Constant for normal distribution. */
+  private static double NORM_CONST = Math.sqrt(2 * Math.PI);
+
+  /**
+   * Generates the classifier.
+   *
+   * @param instances set of instances serving as training data 
+   * @exception Exception if the classifier has not been generated successfully
+   */
+  public void buildClassifier(Instances instances) throws Exception {
+
+    int attIndex = 0;
+    double sum;
+    
+    if (instances.checkForStringAttributes()) {
+      throw new Exception("Can't handle string attributes!");
+    }
+    if (instances.classAttribute().isNumeric()) {
+      throw new Exception("Naive Bayes: Class is numeric!");
+    }
+    
+    m_Instances = new Instances(instances, 0);
+    
+    // Reserve space
+    m_Counts = new double[instances.numClasses()]
+      [instances.numAttributes() - 1][0];
+    m_Means = new double[instances.numClasses()]
+      [instances.numAttributes() - 1];
+    m_Devs = new double[instances.numClasses()]
+      [instances.numAttributes() - 1];
+    m_Priors = new double[instances.numClasses()];
+    Enumeration enum = instances.enumerateAttributes();
+    while (enum.hasMoreElements()) {
+      Attribute attribute = (Attribute) enum.nextElement();
+      if (attribute.isNominal()) {
+	for (int j = 0; j < instances.numClasses(); j++) {
+	  m_Counts[j][attIndex] = new double[attribute.numValues()];
+	}
+      } else {
+	for (int j = 0; j < instances.numClasses(); j++) {
+	  m_Counts[j][attIndex] = new double[1];
+	}
+      }
+      attIndex++;
+    }
+    
+    // Compute counts and sums
+    Enumeration enumInsts = instances.enumerateInstances();
+    while (enumInsts.hasMoreElements()) {
+      Instance instance = (Instance) enumInsts.nextElement();
+      if (!instance.classIsMissing()) {
+	Enumeration enumAtts = instances.enumerateAttributes();
+	attIndex = 0;
+	while (enumAtts.hasMoreElements()) {
+	  Attribute attribute = (Attribute) enumAtts.nextElement();
+	  if (!instance.isMissing(attribute)) {
+	    if (attribute.isNominal()) {
+	      m_Counts[(int)instance.classValue()][attIndex]
+		[(int)instance.value(attribute)]++;
+	    } else {
+	      m_Means[(int)instance.classValue()][attIndex] +=
+		instance.value(attribute);
+	      m_Counts[(int)instance.classValue()][attIndex][0]++;
+	    }
+	  }
+	  attIndex++;
+	}
+	m_Priors[(int)instance.classValue()]++;
+      }
+    }
+    
+    // Compute means
+    Enumeration enumAtts = instances.enumerateAttributes();
+    attIndex = 0;
+    while (enumAtts.hasMoreElements()) {
+      Attribute attribute = (Attribute) enumAtts.nextElement();
+      if (attribute.isNumeric()) {
+	for (int j = 0; j < instances.numClasses(); j++) {
+	  if (m_Counts[j][attIndex][0] < 2) {
+	    throw new Exception("attribute " + attribute.name() +
+				": less than two values for class " +
+				instances.classAttribute().value(j));
+	  }
+	  m_Means[j][attIndex] /= m_Counts[j][attIndex][0];
+	}
+      }
+      attIndex++;
+    }    
+    
+    // Compute standard deviations
+    enumInsts = instances.enumerateInstances();
+    while (enumInsts.hasMoreElements()) {
+      Instance instance = 
+	(Instance) enumInsts.nextElement();
+      if (!instance.classIsMissing()) {
+	enumAtts = instances.enumerateAttributes();
+	attIndex = 0;
+	while (enumAtts.hasMoreElements()) {
+	  Attribute attribute = (Attribute) enumAtts.nextElement();
+	  if (!instance.isMissing(attribute)) {
+	    if (attribute.isNumeric()) {
+	      m_Devs[(int)instance.classValue()][attIndex] +=
+		(m_Means[(int)instance.classValue()][attIndex]-
+		 instance.value(attribute))*
+		(m_Means[(int)instance.classValue()][attIndex]-
+		 instance.value(attribute));
+	    }
+	  }
+	  attIndex++;
+	}
+      }
+    }
+    enumAtts = instances.enumerateAttributes();
+    attIndex = 0;
+    while (enumAtts.hasMoreElements()) {
+      Attribute attribute = (Attribute) enumAtts.nextElement();
+      if (attribute.isNumeric()) {
+	for (int j = 0; j < instances.numClasses(); j++) {
+	  if (m_Devs[j][attIndex] <= 0) {
+	    throw new Exception("attribute " + attribute.name() +
+				": standard deviation is 0 for class " +
+				instances.classAttribute().value(j));
+	  }
+	  else {
+	    m_Devs[j][attIndex] /= m_Counts[j][attIndex][0] - 1;
+	    m_Devs[j][attIndex] = Math.sqrt(m_Devs[j][attIndex]);
+	  }
+	}
+      }
+      attIndex++;
+    } 
+    
+    // Normalize counts
+    enumAtts = instances.enumerateAttributes();
+    attIndex = 0;
+    while (enumAtts.hasMoreElements()) {
+      Attribute attribute = (Attribute) enumAtts.nextElement();
+      if (attribute.isNominal()) {
+	for (int j = 0; j < instances.numClasses(); j++) {
+	  sum = Utils.sum(m_Counts[j][attIndex]);
+	  for (int i = 0; i < attribute.numValues(); i++) {
+	    m_Counts[j][attIndex][i] =
+	      (m_Counts[j][attIndex][i] + 1) 
+	      / (sum + (double)attribute.numValues());
+	  }
+	}
+      }
+      attIndex++;
+    }
+    
+    // Normalize priors
+    sum = Utils.sum(m_Priors);
+    for (int j = 0; j < instances.numClasses(); j++)
+      m_Priors[j] = (m_Priors[j] + 1) 
+	/ (sum + (double)instances.numClasses());
+  }
+
+  /**
+   * Calculates the class membership probabilities for the given test instance.
+   *
+   * @param instance the instance to be classified
+   * @return predicted class probability distribution
+   * @exception Exception if distribution can't be computed
+   */
+  public double[] distributionForInstance(Instance instance) throws Exception {
+    
+    double [] probs = new double[instance.numClasses()];
+    int attIndex;
+    
+    for (int j = 0; j < instance.numClasses(); j++) {
+      probs[j] = 1;
+      Enumeration enumAtts = instance.enumerateAttributes();
+      attIndex = 0;
+      while (enumAtts.hasMoreElements()) {
+	Attribute attribute = (Attribute) enumAtts.nextElement();
+	if (!instance.isMissing(attribute)) {
+	  if (attribute.isNominal()) {
+	    probs[j] *= m_Counts[j][attIndex][(int)instance.value(attribute)];
+	  } else {
+	    probs[j] *= normalDens(instance.value(attribute),
+				   m_Means[j][attIndex],
+				   m_Devs[j][attIndex]);}
+	}
+	attIndex++;
+      }
+      probs[j] *= m_Priors[j];
+    }
+    
+    // Normalize probabilities
+    Utils.normalize(probs);
+
+    return probs;
+  }
+
+  /**
+   * Returns a description of the classifier.
+   *
+   * @return a description of the classifier as a string.
+   */
+  public String toString() {
+
+    if (m_Instances == null) {
+      return "Naive Bayes (simple): No model built yet.";
+    }
+    try {
+      StringBuffer text = new StringBuffer("Naive Bayes (simple)");
+      int attIndex;
+      
+      for (int i = 0; i < m_Instances.numClasses(); i++) {
+	text.append("\n\nClass " + m_Instances.classAttribute().value(i) 
+		    + ": P(C) = " 
+		    + Utils.doubleToString(m_Priors[i], 10, 8)
+		    + "\n\n");
+	Enumeration enumAtts = m_Instances.enumerateAttributes();
+	attIndex = 0;
+	while (enumAtts.hasMoreElements()) {
+	  Attribute attribute = (Attribute) enumAtts.nextElement();
+	  text.append("Attribute " + attribute.name() + "\n");
+	  if (attribute.isNominal()) {
+	    for (int j = 0; j < attribute.numValues(); j++) {
+	      text.append(attribute.value(j) + "\t");
+	    }
+	    text.append("\n");
+	    for (int j = 0; j < attribute.numValues(); j++)
+	      text.append(Utils.
+			  doubleToString(m_Counts[i][attIndex][j], 10, 8)
+			  + "\t");
+	  } else {
+	    text.append("Mean: " + Utils.
+			doubleToString(m_Means[i][attIndex], 10, 8) + "\t");
+	    text.append("Standard Deviation: " 
+			+ Utils.doubleToString(m_Devs[i][attIndex], 10, 8));
+	  }
+	  text.append("\n\n");
+	  attIndex++;
+	}
+      }
+      
+      return text.toString();
+    } catch (Exception e) {
+      return "Can't print Naive Bayes classifier!";
+    }
+  }
+
+  /**
+   * Density function of normal distribution.
+   */
+  private double normalDens(double x, double mean, double stdDev) {
+    
+    double diff = x - mean;
+    
+    return (1 / (NORM_CONST * stdDev)) 
+      * Math.exp(-(diff * diff / (2 * stdDev * stdDev)));
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv the options
+   */
+  public static void main(String [] argv) {
+
+    Classifier scheme;
+
+    try {
+      scheme = new NaiveBayesSimple();
+      System.out.println("Evaluation disabled!");
+      //System.out.println(Evaluation.evaluateModel(scheme, argv));
+    } catch (Exception e) {
+      System.err.println(e.getMessage());
+    }
+  }
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Attribute.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Attribute.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Attribute.java	(revision 16561)
@@ -0,0 +1,682 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Attribute.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.io.*;
+import java.util.*;
+
+/** 
+ * Class for handling an attribute. Once an attribute has been created,
+ * it can't be changed. <p>
+ *
+ * Three attribute types are supported:
+ * <ul>
+ *    <li> numeric: <ul>
+ *         This type of attribute represents a floating-point number.
+ *    </ul>
+ *    <li> nominal: <ul>
+ *         This type of attribute represents a fixed set of nominal values.
+ *    </ul>
+ *    <li> string: <ul>
+ *         This type of attribute represents a dynamically expanding set of
+ *         nominal values. String attributes are not used by the learning
+ *         schemes in Weka. They can be used, for example,  to store an 
+ *         identifier with each instance in a dataset.
+ *    </ul>
+ * </ul>
+ * Typical usage (code from the main() method of this class): <p>
+ *
+ * <code>
+ * ... <br>
+ *
+ * // Create numeric attributes "length" and "weight" <br>
+ * Attribute length = new Attribute("length"); <br>
+ * Attribute weight = new Attribute("weight"); <br><br>
+ * 
+ * // Create vector to hold nominal values "first", "second", "third" <br>
+ * FastVector my_nominal_values = new FastVector(3); <br>
+ * my_nominal_values.addElement("first"); <br>
+ * my_nominal_values.addElement("second"); <br>
+ * my_nominal_values.addElement("third"); <br><br>
+ *
+ * // Create nominal attribute "position" <br>
+ * Attribute position = new Attribute("position", my_nominal_values);<br>
+ *
+ * ... <br>
+ * </code><p>
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Attribute implements Copyable, Serializable {
+
+  /** Constant set for numeric attributes. */
+  public final static int NUMERIC = 0;
+
+  /** Constant set for nominal attributes. */
+  public final static int NOMINAL = 1;
+
+  /** Constant set for attributes with string values. */
+  public final static int STRING = 2;
+
+  /** Strings longer than this will be stored compressed. */
+  private final static int STRING_COMPRESS_THRESHOLD = 200;
+  //private final static int STRING_COMPRESS_THRESHOLD = Integer.MAX_VALUE;
+
+  /** The attribute's name. */
+  private String m_Name;
+
+  /** The attribute's type. */
+  private int m_Type;
+
+  /** The attribute's values (if nominal or string). */
+  private FastVector m_Values;
+
+  /** Mapping of values to indices (if nominal or string). */
+  private Hashtable m_Hashtable;
+
+  /** The attribute's index. */
+  private int m_Index;
+
+  /**
+   * Constructor for a numeric attribute.
+   *
+   * @param attributeName the name for the attribute
+   */
+  public Attribute(String attributeName) {
+
+    m_Name = attributeName;
+    m_Index = -1;
+    m_Values = null;
+    m_Hashtable = null;
+    m_Type = NUMERIC;
+  }
+
+  /**
+   * Constructor for nominal attributes and string attributes.
+   * If a null vector of attribute values is passed to the method,
+   * the attribute is assumed to be a string.
+   *
+   * @param attributeName the name for the attribute
+   * @param attributeValues a vector of strings denoting the 
+   * attribute values. Null if the attribute is a string attribute.
+   */
+  public Attribute(String attributeName, 
+		   FastVector attributeValues) {
+
+    m_Name = attributeName;
+    m_Index = -1;
+    if (attributeValues == null) {
+      m_Values = new FastVector();
+      m_Hashtable = new Hashtable();
+      m_Type = STRING;
+    } else {
+      m_Values = (FastVector) attributeValues.copy();
+      m_Hashtable = new Hashtable(m_Values.size());
+      for (int i = 0; i < m_Values.size(); i++) {
+	m_Hashtable.put(m_Values.elementAt(i), new Integer(i));
+      }
+      m_Type = NOMINAL;
+    }
+  }
+
+  /**
+   * Produces a shallow copy of this attribute.
+   *
+   * @return a copy of this attribute with the same index
+   */
+  public Object copy() {
+
+    Attribute copy = new Attribute(m_Name);
+
+    copy.m_Index = m_Index;
+    if (!isNominal() && !isString())
+      return copy;
+    copy.m_Type = m_Type;
+    copy.m_Values = m_Values;
+    copy.m_Hashtable = m_Hashtable;
+ 
+    return copy;
+  }
+
+  /**
+   * Returns an enumeration of all the attribute's values if
+   * the attribute is nominal or a string, null otherwise. 
+   *
+   * @return enumeration of all the attribute's values
+   */
+  public final Enumeration enumerateValues() {
+
+    if (isNominal() || isString()) {
+      final Enumeration ee = m_Values.elements();
+      return new Enumeration () {
+          public boolean hasMoreElements() {
+            return ee.hasMoreElements();
+          }
+          public Object nextElement() {
+            Object oo = ee.nextElement();
+            if (oo instanceof SerializedObject) {
+              return ((SerializedObject)oo).getObject();
+            } else {
+              return oo;
+            }
+          }
+        };
+    }
+    return null;
+  }
+
+  /**
+   * Tests if given attribute is equal to this attribute.
+   *
+   * @param other the Object to be compared to this attribute
+   * @return true if the given attribute is equal to this attribute
+   */
+  public final boolean equals(Object other) {
+
+    if ((other == null) || !(other.getClass().equals(this.getClass()))) {
+      return false;
+    }
+    Attribute att = (Attribute) other;
+    if (!m_Name.equals(att.m_Name)) {
+      return false;
+    }
+    if (isNumeric() && att.isNumeric()) {
+      return true;
+    }
+    if (isNumeric() || att.isNumeric()) {
+      return false;
+    }
+    if (m_Values.size() != att.m_Values.size()) {
+      return false;
+    }
+    for (int i = 0; i < m_Values.size(); i++) {
+      if (!m_Values.elementAt(i).equals(att.m_Values.elementAt(i))) {
+	return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns the index of this attribute.
+   *
+   * @return the index of this attribute
+   */
+  public final int index() {
+
+    return m_Index;
+  }
+
+  /**
+   * Returns the index of a given attribute value. (The index of
+   * the first occurence of this value.)
+   *
+   * @param value the value for which the index is to be returned
+   * @return the index of the given attribute value if attribute
+   * is nominal or a string, -1 if it is numeric or the value 
+   * can't be found
+   */
+  public final int indexOfValue(String value) {
+
+    if (!isNominal() && !isString())
+      return -1;
+    Object store = value;
+    if (value.length() > STRING_COMPRESS_THRESHOLD) {
+      try {
+        store = new SerializedObject(value, true);
+      } catch (Exception ex) {
+        System.err.println("Couldn't compress string attribute value -"
+                           + " searching uncompressed.");
+      }
+    }
+    Integer val = (Integer)m_Hashtable.get(store);
+    if (val == null) {
+      throw new IllegalArgumentException("Value \"" + value + 
+					 "\" not found in attribute " +
+					 "declaration!");
+    } else {
+      return val.intValue();
+    }
+  }
+
+  /**
+   * Test if the attribute is nominal.
+   *
+   * @return true if the attribute is nominal
+   */
+  public final boolean isNominal() {
+
+    return (m_Type == NOMINAL);
+  }
+
+  /**
+   * Tests if the attribute is numeric.
+   *
+   * @return true if the attribute is numeric
+   */
+  public final boolean isNumeric() {
+
+    return (m_Type == NUMERIC);
+  }
+
+  /**
+   * Tests if the attribute is a string.
+   *
+   * @return true if the attribute is a string
+   */
+  public final boolean isString() {
+
+    return (m_Type == STRING);
+  }
+
+  /**
+   * Returns the attribute's name.
+   *
+   * @return the attribute's name as a string
+   */
+  public final String name() {
+
+    return m_Name;
+  }
+  
+  /**
+   * Returns the number of attribute values. Returns 0 for numeric attributes.
+   *
+   * @return the number of attribute values
+   */
+  public final int numValues() {
+
+    if (!isNominal() && !isString()) {
+      return 0;
+    } else {
+      return m_Values.size();
+    }
+  }
+
+  /**
+   * Returns a description of this attribute in ARFF format. Quotes
+   * strings if they contain whitespace characters, or if they
+   * are a question mark.
+   *
+   * @return a description of this attribute as a string
+   */
+  public final String toString() {
+    
+    StringBuffer text = new StringBuffer();
+    
+    text.append("@attribute " + Utils.quote(m_Name) + " ");
+    if (isNominal()) {
+      text.append('{');
+      Enumeration enum = enumerateValues();
+      while (enum.hasMoreElements()) {
+	text.append(Utils.quote((String) enum.nextElement()));
+	if (enum.hasMoreElements())
+	  text.append(',');
+      }
+      text.append('}');
+    } else {
+      if (isNumeric()) {
+	text.append("numeric");
+      } else {
+	text.append("string");
+      }
+    }
+    return text.toString();
+  }
+
+  /**
+   * Returns the attribute's type as an integer.
+   *
+   * @returns the attribute's type.
+   */
+  public final int type() {
+
+    return m_Type;
+  }
+
+  /**
+   * Returns a value of a nominal or string attribute. 
+   * Returns an empty string if the attribute is neither
+   * nominal nor a string attribute.
+   *
+   * @param valIndex the value's index
+   * @return the attribute's value as a string
+   */
+  public final String value(int valIndex) {
+    
+    if (!isNominal() && !isString()) {
+      return "";
+    } else {
+      Object val = m_Values.elementAt(valIndex);
+      
+      // If we're storing strings compressed, uncompress it.
+      if (val instanceof SerializedObject) {
+        val = ((SerializedObject)val).getObject();
+      }
+      return (String) val;
+    }
+  }
+
+  /**
+   * Constructor for a numeric attribute with a particular index.
+   *
+   * @param attributeName the name for the attribute
+   * @param index the attribute's index
+   */
+  Attribute(String attributeName, int index) {
+
+    this(attributeName);
+
+    m_Index = index;
+  }
+
+  /**
+   * Constructor for nominal attributes and string attributes with
+   * a particular index.
+   * If a null vector of attribute values is passed to the method,
+   * the attribute is assumed to be a string.
+   *
+   * @param attributeName the name for the attribute
+   * @param attributeValues a vector of strings denoting the attribute values.
+   * Null if the attribute is a string attribute.
+   * @param index the attribute's index
+   */
+  Attribute(String attributeName, FastVector attributeValues, 
+	    int index) {
+
+    this(attributeName, attributeValues);
+
+    m_Index = index;
+  }
+
+  /**
+   * Adds a string value to the list of valid strings for attributes
+   * of type STRING and returns the index of the string.
+   *
+   * @param value The string value to add
+   * @return the index assigned to the string, or -1 if the attribute is not
+   * of type Attribute.STRING 
+   */
+  public int addStringValue(String value) {
+
+    if (!isString()) {
+      return -1;
+    }
+    Object store = value;
+
+    if (value.length() > STRING_COMPRESS_THRESHOLD) {
+      try {
+        store = new SerializedObject(value, true);
+      } catch (Exception ex) {
+        System.err.println("Couldn't compress string attribute value -"
+                           + " storing uncompressed.");
+      }
+    }
+    Integer index = (Integer)m_Hashtable.get(store);
+    if (index != null) {
+      return index.intValue();
+    } else {
+      int intIndex = m_Values.size();
+      m_Values.addElement(store);
+      m_Hashtable.put(store, new Integer(intIndex));
+      return intIndex;
+    }
+  }
+
+  /**
+   * Adds a string value to the list of valid strings for attributes
+   * of type STRING and returns the index of the string. This method is
+   * more efficient than addStringValue(String) for long strings.
+   *
+   * @param src The Attribute containing the string value to add.
+   * @param int index the index of the string value in the source attribute.
+   * @return the index assigned to the string, or -1 if the attribute is not
+   * of type Attribute.STRING 
+   */
+  public int addStringValue(Attribute src, int index) {
+
+    if (!isString()) {
+      return -1;
+    }
+    Object store = src.m_Values.elementAt(index);
+    Integer oldIndex = (Integer)m_Hashtable.get(store);
+    if (oldIndex != null) {
+      return oldIndex.intValue();
+    } else {
+      int intIndex = m_Values.size();
+      m_Values.addElement(store);
+      m_Hashtable.put(store, new Integer(intIndex));
+      return intIndex;
+    }
+  }
+
+  /**
+   * Adds an attribute value. Creates a fresh list of attribute
+   * values before adding it.
+   *
+   * @param value the attribute value
+   */
+  final void addValue(String value) {
+
+    m_Values = (FastVector)m_Values.copy();
+    m_Hashtable = (Hashtable)m_Hashtable.clone();
+    forceAddValue(value);
+  }
+
+  /**
+   * Produces a shallow copy of this attribute with a new name.
+   *
+   * @param newName the name of the new attribute
+   * @return a copy of this attribute with the same index
+   */
+  final Attribute copy(String newName) {
+
+    Attribute copy = new Attribute(newName);
+
+    copy.m_Index = m_Index;
+    if (!isNominal() && !isString())
+      return copy;
+    copy.m_Type = m_Type;
+    copy.m_Values = m_Values;
+    copy.m_Hashtable = m_Hashtable;
+ 
+    return copy;
+  }
+
+  /**
+   * Removes a value of a nominal or string attribute. Creates a 
+   * fresh list of attribute values before removing it.
+   *
+   * @param index the value's index
+   * @exception IllegalArgumentException if the attribute is not nominal
+   */
+  final void delete(int index) {
+    
+    if (!isNominal() && !isString()) 
+      throw new IllegalArgumentException("Can only remove value of" +
+                                         "nominal or string attribute!");
+    else {
+      m_Values = (FastVector)m_Values.copy();
+      m_Values.removeElementAt(index);
+      Hashtable hash = new Hashtable(m_Hashtable.size());
+      Enumeration enum = hash.keys();
+      while (enum.hasMoreElements()) {
+	String string = (String)enum.nextElement();
+	Integer valIndexObject = (Integer)m_Hashtable.get(string);
+	int valIndex = valIndexObject.intValue();
+	if (valIndex > index) {
+	  hash.put(string, new Integer(valIndex - 1));
+	} else if (valIndex < index) {
+	  hash.put(string, valIndexObject);
+	}
+      }
+      m_Hashtable = hash;
+    }
+  }
+
+  /**
+   * Adds an attribute value.
+   *
+   * @param value the attribute value
+   */
+  final void forceAddValue(String value) {
+
+    Object store = value;
+    if (value.length() > STRING_COMPRESS_THRESHOLD) {
+      try {
+        store = new SerializedObject(value, true);
+      } catch (Exception ex) {
+        System.err.println("Couldn't compress string attribute value -"
+                           + " storing uncompressed.");
+      }
+    }
+    m_Values.addElement(store);
+    m_Hashtable.put(store, new Integer(m_Values.size() - 1));
+  }
+
+  /**
+   * Sets the index of this attribute.
+   *
+   * @param the index of this attribute
+   */
+  final void setIndex(int index) {
+
+    m_Index = index;
+  }
+
+  /**
+   * Sets a value of a nominal attribute or string attribute.
+   * Creates a fresh list of attribute values before it is set.
+   *
+   * @param index the value's index
+   * @param string the value
+   * @exception IllegalArgumentException if the attribute is not nominal or 
+   * string.
+   */
+  final void setValue(int index, String string) {
+
+    if (!isNominal() && !isString()) {
+      throw new IllegalArgumentException("Can only set value of nominal"+
+                                         "or string attribute!");
+    } else {
+      m_Values = (FastVector)m_Values.copy();
+      m_Hashtable = (Hashtable)m_Hashtable.clone();
+      Object store = string;
+      if (string.length() > STRING_COMPRESS_THRESHOLD) {
+        try {
+          store = new SerializedObject(string, true);
+        } catch (Exception ex) {
+          System.err.println("Couldn't compress string attribute value -"
+                             + " storing uncompressed.");
+        }
+      }
+      m_Hashtable.remove(m_Values.elementAt(index));
+      m_Values.setElementAt(store, index);
+      m_Hashtable.put(store, new Integer(index));
+    }
+  }
+
+  /**
+   * Simple main method for testing this class.
+   */
+  public static void main(String[] ops) {
+
+    try {
+      
+      // Create numeric attributes "length" and "weight"
+      Attribute length = new Attribute("length");
+      Attribute weight = new Attribute("weight");
+      
+      // Create vector to hold nominal values "first", "second", "third" 
+      FastVector my_nominal_values = new FastVector(3); 
+      my_nominal_values.addElement("first"); 
+      my_nominal_values.addElement("second"); 
+      my_nominal_values.addElement("third"); 
+      
+      // Create nominal attribute "position" 
+      Attribute position = new Attribute("position", my_nominal_values);
+
+      // Print the name of "position"
+      System.out.println("Name of \"position\": " + position.name());
+
+      // Print the values of "position"
+      Enumeration attValues = position.enumerateValues();
+      while (attValues.hasMoreElements()) {
+	String string = (String)attValues.nextElement();
+	System.out.println("Value of \"position\": " + string);
+      }
+
+      // Shallow copy attribute "position"
+      Attribute copy = (Attribute) position.copy();
+
+      // Test if attributes are the same
+      System.out.println("Copy is the same as original: " + copy.equals(position));
+
+      // Print index of attribute "weight" (should be unset: -1)
+      System.out.println("Index of attribute \"weight\" (should be -1): " + 
+			 weight.index());
+
+      // Print index of value "first" of attribute "position"
+      System.out.println("Index of value \"first\" of \"position\" (should be 0): " +
+			 position.indexOfValue("first"));
+
+      // Tests type of attribute "position"
+      System.out.println("\"position\" is numeric: " + position.isNumeric());
+      System.out.println("\"position\" is nominal: " + position.isNominal());
+      System.out.println("\"position\" is string: " + position.isString());
+
+      // Prints name of attribute "position"
+      System.out.println("Name of \"position\": " + position.name());
+    
+      // Prints number of values of attribute "position"
+      System.out.println("Number of values for \"position\": " + position.numValues());
+
+      // Prints the values (againg)
+      for (int i = 0; i < position.numValues(); i++) {
+	System.out.println("Value " + i + ": " + position.value(i));
+      }
+
+      // Prints the attribute "position" in ARFF format
+      System.out.println(position);
+
+      // Checks type of attribute "position" using constants
+      switch (position.type()) {
+      case Attribute.NUMERIC:
+	System.out.println("\"position\" is numeric");
+	break;
+      case Attribute.NOMINAL:
+	System.out.println("\"position\" is nominal");
+	break;
+      case Attribute.STRING:
+	System.out.println("\"position\" is string");
+	break;
+      default:
+	System.out.println("\"position\" has unknown type");
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
+  
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/AttributeStats.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/AttributeStats.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/AttributeStats.java	(revision 16561)
@@ -0,0 +1,136 @@
+/*
+ *    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.
+ */
+
+/*
+ *    AttributeStats.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+package weka.core;
+
+/**
+ * A Utility class that contains summary information on an
+ * the values that appear in a dataset for a particular attribute.
+ *
+ * @author <a href="mailto:len@webmind.com">Len Trigg</a>
+ * @version $Revision$
+ */
+public class AttributeStats {    
+  
+  /** The number of int-like values */
+  public int intCount = 0;
+  
+  /** The number of real-like values (i.e. have a fractional part) */
+  public int realCount = 0;
+  
+  /** The number of missing values */
+  public int missingCount = 0;
+  
+  /** The number of distinct values */
+  public int distinctCount = 0;
+  
+  /** The number of values that only appear once */
+  public int uniqueCount = 0;
+  
+  /** The total number of values (i.e. number of instances) */
+  public int totalCount = 0;
+  
+  /** Stats on numeric value distributions */
+  // perhaps Stats should be moved from weka.experiment to weka.core
+  public weka.experiment.Stats numericStats;
+  
+  /** Counts of each nominal value */
+  public int [] nominalCounts;
+    
+  /**
+   * Updates the counters for one more observed distinct value.
+   *
+   * @param value the value that has just been seen
+   * @param count the number of times the value appeared
+   */
+  protected void addDistinct(double value, int count) {
+    
+    if (count > 0) {
+      if (count == 1) {
+	uniqueCount++;
+	}
+      if (Utils.eq(value, (double)((int)value))) {
+	intCount += count;
+      } else {
+	realCount += count;
+      }
+      if (nominalCounts != null) {
+	nominalCounts[(int)value] = count;
+      }
+      if (numericStats != null) {
+	  numericStats.add(value, count);
+	  numericStats.calculateDerived();
+      }
+    }
+    distinctCount++;
+  }
+
+  /**
+   * Returns a human readable representation of this AttributeStats instance.
+   *
+   * @return a String represtinging these AttributeStats.
+   */
+  public String toString() {
+
+    StringBuffer sb = new StringBuffer();
+    sb.append(Utils.padLeft("Type", 4)).append(Utils.padLeft("Nom", 5));
+    sb.append(Utils.padLeft("Int", 5)).append(Utils.padLeft("Real", 5));
+    sb.append(Utils.padLeft("Missing", 12));
+    sb.append(Utils.padLeft("Unique", 12));
+    sb.append(Utils.padLeft("Dist", 6));
+    if (nominalCounts != null) {
+      sb.append(' ');
+      for (int i = 0; i < nominalCounts.length; i++) {
+        sb.append(Utils.padLeft("C[" + i + "]", 5));
+      }
+    }
+    sb.append('\n');
+
+    long percent;
+    percent = Math.round(100.0 * intCount / totalCount);
+    if (nominalCounts != null) {
+      sb.append(Utils.padLeft("Nom", 4)).append(' ');
+      sb.append(Utils.padLeft("" + percent, 3)).append("% ");
+      sb.append(Utils.padLeft("" + 0, 3)).append("% ");
+    } else {
+      sb.append(Utils.padLeft("Num", 4)).append(' ');
+      sb.append(Utils.padLeft("" + 0, 3)).append("% ");
+      sb.append(Utils.padLeft("" + percent, 3)).append("% ");
+    }
+    percent = Math.round(100.0 * realCount / totalCount);
+    sb.append(Utils.padLeft("" + percent, 3)).append("% ");
+    sb.append(Utils.padLeft("" + missingCount, 5)).append(" /");
+    percent = Math.round(100.0 * missingCount / totalCount);
+    sb.append(Utils.padLeft("" + percent, 3)).append("% ");
+    sb.append(Utils.padLeft("" + uniqueCount, 5)).append(" /");
+    percent = Math.round(100.0 * uniqueCount / totalCount);
+    sb.append(Utils.padLeft("" + percent, 3)).append("% ");
+    sb.append(Utils.padLeft("" + distinctCount, 5)).append(' ');
+    if (nominalCounts != null) {
+      for (int i = 0; i < nominalCounts.length; i++) {
+        sb.append(Utils.padLeft("" + nominalCounts[i], 5));
+      }
+    }
+    sb.append('\n');
+    return sb.toString();
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/ContingencyTables.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/ContingencyTables.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/ContingencyTables.java	(revision 16561)
@@ -0,0 +1,635 @@
+/*
+ *    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.
+ */
+
+/*
+ *    ContingencyTables.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+/**
+ * Class implementing some statistical routines for contingency tables.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class ContingencyTables {
+
+  /** The natural logarithm of 2 */
+  private static double log2 = Math.log(2);
+
+  /**
+   * Returns chi-squared probability for a given matrix.
+   *
+   * @param matrix the contigency table
+   * @param yates is Yates' correction to be used?
+   * @return the chi-squared probability
+   */
+
+  public static double chiSquared(double [][] matrix, boolean yates) {
+
+    int df = (matrix.length - 1) * (matrix[0].length - 1);
+
+    return Statistics.chiSquaredProbability(chiVal(matrix, yates), df);
+  }
+
+  /**
+   * Computes chi-squared statistic for a contingency table.
+   *
+   * @param matrix the contigency table
+   * @param yates is Yates' correction to be used?
+   * @return the value of the chi-squared statistic
+   */
+  public static double chiVal(double [][] matrix, boolean useYates) {
+    
+    int df, nrows, ncols, row, col;
+    double[] rtotal, ctotal;
+    double expect = 0, chival = 0, n = 0;
+    boolean yates = true;
+    
+    nrows = matrix.length;
+    ncols = matrix[0].length;
+    rtotal = new double [nrows];
+    ctotal = new double [ncols];
+    for (row = 0; row < nrows; row++) {
+      for (col = 0; col < ncols; col++) {
+	rtotal[row] += matrix[row][col];
+	ctotal[col] += matrix[row][col];
+	n += matrix[row][col];
+      }
+    }
+    df = (nrows - 1)*(ncols - 1);
+    if ((df > 1) || (!useYates)) {
+      yates = false;
+    } else if (df <= 0) {
+      return 0;
+    }
+    chival = 0.0;
+    for (row = 0; row < nrows; row++) {
+      if (Utils.gr(rtotal[row], 0)) {
+	for (col = 0; col < ncols; col++) {
+	  if (Utils.gr(ctotal[col], 0)) {
+	    expect = (ctotal[col] * rtotal[row]) / n;
+	    chival += chiCell (matrix[row][col], expect, yates);
+	  }
+	}
+      }
+    }
+    return chival;
+  }
+
+  /**
+   * Tests if Cochran's criterion is fullfilled for the given
+   * contingency table. Rows and columns with all zeros are not considered
+   * relevant.
+   *
+   * @param matrix the contigency table to be tested
+   * @return true if contingency table is ok, false if not
+   */
+  public static boolean cochransCriterion(double[][] matrix) {
+
+    double[] rtotal, ctotal;
+    double n = 0, expect, smallfreq = 5;
+    int smallcount = 0, nonZeroRows = 0, nonZeroColumns = 0, nrows, ncols, 
+      row, col;
+
+    nrows = matrix.length;
+    ncols = matrix[0].length;
+
+    rtotal = new double [nrows];
+    ctotal = new double [ncols];
+    for (row = 0; row < nrows; row++) {
+      for (col = 0; col < ncols; col++) {
+	rtotal[row] += matrix[row][col];
+	ctotal[col] += matrix[row][col];
+	n += matrix[row][col];
+      }
+    }
+    for (row = 0; row < nrows; row++) {
+      if (Utils.gr(rtotal[row], 0)) {
+	nonZeroRows++;
+      }
+    }
+    for (col = 0; col < ncols; col++) {
+      if (Utils.gr(ctotal[col], 0)) {
+	nonZeroColumns++;
+      }
+    }
+    for (row = 0; row < nrows; row++) {
+      if (Utils.gr(rtotal[row], 0)) {
+	for (col = 0; col < ncols; col++) {
+	  if (Utils.gr(ctotal[col], 0)) {
+	    expect = (ctotal[col] * rtotal[row]) / n;
+	    if (Utils.sm(expect, smallfreq)) {
+	      if (Utils.sm(expect, 1)) {
+		return false;
+	      } else {
+		smallcount++;
+		if (smallcount > (nonZeroRows * nonZeroColumns) / smallfreq) {
+		  return false;
+		}
+	      }
+	    }
+	  }
+	}
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Computes Cramer's V for a contingency table.
+   *
+   * @param matrix the contingency table
+   * @return Cramer's V
+   */
+  public static double CramersV(double [][] matrix) {
+
+    int row, col, nrows,ncols, min;
+    double n = 0;
+    
+    nrows = matrix.length;
+    ncols = matrix[0].length;
+    for (row = 0; row < nrows; row++) {
+      for (col = 0; col < ncols; col++) {
+	n += matrix[row][col];
+      }
+    }
+    min = nrows < ncols ? nrows-1 : ncols-1;
+    if ((min == 0) || Utils.eq(n, 0))
+      return 0;
+    return Math.sqrt(chiVal(matrix, false) / (n * (double)min)); 
+  } 
+
+  /**
+   * Computes the entropy of the given array.
+   *
+   * @param array the array
+   * @return the entropy
+   */
+  public static double entropy(double[] array) {
+
+    double returnValue = 0, sum = 0;
+
+    for (int i = 0; i < array.length; i++) {
+      returnValue -= lnFunc(array[i]);
+      sum += array[i];
+    }
+    if (Utils.eq(sum, 0)) {
+      return 0;
+    } else {
+      return (returnValue + lnFunc(sum)) / (sum * log2);
+    }
+  }
+
+  /**
+   * Computes conditional entropy of the rows given
+   * the columns.
+   *
+   * @param matrix the contingency table
+   * @return the conditional entropy of the rows given the columns
+   */
+  public static double entropyConditionedOnColumns(double[][] matrix) {
+    
+    double returnValue = 0, sumForColumn, total = 0;
+
+    for (int j = 0; j < matrix[0].length; j++) {
+      sumForColumn = 0;
+      for (int i = 0; i < matrix.length; i++) {
+	returnValue = returnValue + lnFunc(matrix[i][j]);
+	sumForColumn += matrix[i][j];
+      }
+      returnValue = returnValue - lnFunc(sumForColumn);
+      total += sumForColumn;
+    }
+    if (Utils.eq(total, 0)) {
+      return 0;
+    }
+    return -returnValue / (total * log2);
+  }
+
+  /**
+   * Computes conditional entropy of the columns given
+   * the rows.
+   *
+   * @param matrix the contingency table
+   * @return the conditional entropy of the columns given the rows
+   */
+  public static double entropyConditionedOnRows(double[][] matrix) {
+    
+    double returnValue = 0, sumForRow, total = 0;
+
+    for (int i = 0; i < matrix.length; i++) {
+      sumForRow = 0;
+      for (int j = 0; j < matrix[0].length; j++) {
+	returnValue = returnValue + lnFunc(matrix[i][j]);
+	sumForRow += matrix[i][j];
+      }
+      returnValue = returnValue - lnFunc(sumForRow);
+      total += sumForRow;
+    }
+    if (Utils.eq(total, 0)) {
+      return 0;
+    }
+    return -returnValue / (total * log2);
+  }
+
+  /**
+   * Computes conditional entropy of the columns given the rows
+   * of the test matrix with respect to the train matrix. Uses a
+   * Laplace prior. Does NOT normalize the entropy.
+   *
+   * @param train the train matrix 
+   * @param test the test matrix
+   * @param the number of symbols for Laplace
+   * @return the entropy
+   */
+  public static double entropyConditionedOnRows(double[][] train, 
+						double[][] test,
+						double numClasses) {
+    
+    double returnValue = 0, trainSumForRow, testSumForRow, testSum = 0;
+
+    for (int i = 0; i < test.length; i++) {
+      trainSumForRow = 0;
+      testSumForRow = 0;
+      for (int j = 0; j < test[0].length; j++) {
+	returnValue -= test[i][j] * Math.log(train[i][j] + 1);
+	trainSumForRow += train[i][j];
+	testSumForRow += test[i][j];
+      }
+      testSum = testSumForRow;
+      returnValue += testSumForRow * Math.log(trainSumForRow + 
+					     numClasses);
+    }
+    return returnValue / (testSum * log2);
+  }
+
+  /**
+   * Computes the rows' entropy for the given contingency table.
+   *
+   * @param matrix the contingency table
+   * @return the rows' entropy
+   */
+  public static double entropyOverRows(double[][] matrix) {
+    
+    double returnValue = 0, sumForRow, total = 0;
+
+    for (int i = 0; i < matrix.length; i++) {
+      sumForRow = 0;
+      for (int j = 0; j < matrix[0].length; j++) {
+	sumForRow += matrix[i][j];
+      }
+      returnValue = returnValue - lnFunc(sumForRow);
+      total += sumForRow;
+    }
+    if (Utils.eq(total, 0)) {
+      return 0;
+    }
+    return (returnValue + lnFunc(total)) / (total * log2);
+  }
+
+  /**
+   * Computes the columns' entropy for the given contingency table.
+   *
+   * @param matrix the contingency table
+   * @return the columns' entropy
+   */
+  public static double entropyOverColumns(double[][] matrix){
+    
+    double returnValue = 0, sumForColumn, total = 0;
+
+    for (int j = 0; j < matrix[0].length; j++){
+      sumForColumn = 0;
+      for (int i = 0; i < matrix.length; i++) {
+	sumForColumn += matrix[i][j];
+      }
+      returnValue = returnValue - lnFunc(sumForColumn);
+      total += sumForColumn;
+    }
+    if (Utils.eq(total, 0)) {
+      return 0;
+    }
+    return (returnValue + lnFunc(total)) / (total * log2);
+  }
+
+  /**
+   * Computes gain ratio for contingency table (split on rows).
+   * Returns Double.MAX_VALUE if the split entropy is 0.
+   *
+   * @param matrix the contingency table
+   * @return the gain ratio
+   */
+  public static double gainRatio(double[][] matrix){
+    
+    double preSplit = 0, postSplit = 0, splitEnt = 0,
+      sumForRow, sumForColumn, total = 0, infoGain;
+
+    // Compute entropy before split
+    for (int i = 0; i < matrix[0].length; i++) {
+      sumForColumn = 0;
+      for (int j = 0; j < matrix.length; j++) 
+	sumForColumn += matrix[j][i];
+      preSplit += lnFunc(sumForColumn);
+      total += sumForColumn;
+    }
+    preSplit -= lnFunc(total);
+
+    // Compute entropy after split and split entropy
+    for (int i = 0; i < matrix.length; i++) {
+      sumForRow = 0;
+      for (int j = 0; j < matrix[0].length; j++) {
+	postSplit += lnFunc(matrix[i][j]);
+	sumForRow += matrix[i][j];
+      }
+      splitEnt += lnFunc(sumForRow);
+    }
+    postSplit -= splitEnt;
+    splitEnt -= lnFunc(total);
+
+    infoGain = preSplit - postSplit;
+    if (Utils.eq(splitEnt, 0))
+      return 0;
+    return infoGain / splitEnt;
+  }
+
+  /**
+   * Returns negative base 2 logarithm of multiple hypergeometric
+   * probability for a contingency table.
+   *
+   * @param matrix the contingency table
+   * @return the log of the hypergeometric probability of the contingency table 
+   */
+  public static double log2MultipleHypergeometric(double[][] matrix) {
+
+    double sum = 0, sumForRow, sumForColumn, total = 0;
+
+    for (int i = 0; i < matrix.length; i++) {
+      sumForRow = 0;
+      for (int j = 0; j < matrix[i].length; j++) {
+	sumForRow += matrix[i][j];
+      }
+      sum += SpecialFunctions.lnFactorial(sumForRow);
+      total += sumForRow;
+    }
+    for (int j = 0; j < matrix[0].length; j++) {
+      sumForColumn = 0;
+      for (int i = 0; i < matrix.length; i++) {
+	sumForColumn += matrix [i][j];
+      }
+      sum += SpecialFunctions.lnFactorial(sumForColumn);
+    }
+    for (int i = 0; i < matrix.length; i++) {
+      for (int j = 0; j < matrix[i].length; j++) {
+	sum -= SpecialFunctions.lnFactorial(matrix[i][j]);
+      }
+    }
+    sum -= SpecialFunctions.lnFactorial(total);
+    return -sum / log2;
+  }
+
+  /**
+   * Reduces a matrix by deleting all zero rows and columns.
+   *
+   * @param matrix the matrix to be reduced
+   * @param the matrix with all zero rows and columns deleted
+   */
+  public static double[][] reduceMatrix(double[][] matrix) {
+
+    int row, col, currCol, currRow, nrows, ncols, 
+      nonZeroRows = 0, nonZeroColumns = 0;
+    double[] rtotal, ctotal;
+    double[][] newMatrix;
+
+    nrows = matrix.length;
+    ncols = matrix[0].length;
+    rtotal = new double [nrows];
+    ctotal = new double [ncols];
+    for (row = 0; row < nrows; row++) {
+      for (col = 0; col < ncols; col++) {
+	rtotal[row] += matrix[row][col];
+	ctotal[col] += matrix[row][col];
+      }
+    }
+    for (row = 0; row < nrows; row++) {
+      if (Utils.gr(rtotal[row],0)) {
+	nonZeroRows++;
+      }
+    }
+    for (col = 0; col < ncols; col++) {
+      if (Utils.gr(ctotal[col],0)) {
+	nonZeroColumns++;
+      }
+    }
+    newMatrix = new double[nonZeroRows][nonZeroColumns];
+    currRow = 0;
+    for (row = 0; row < nrows; row++) {
+      if (Utils.gr(rtotal[row],0)) {
+	currCol = 0;
+	for (col = 0; col < ncols; col++) {
+	  if (Utils.gr(ctotal[col],0)) {
+	    newMatrix[currRow][currCol] = matrix[row][col];
+	    currCol++;
+	  }
+	}
+	currRow++;
+      }
+    }
+    return newMatrix;
+  }
+    
+   /**
+    * Calculates the symmetrical uncertainty for base 2.
+    *
+    * @param matrix the contingency table
+    * @return the calculated symmetrical uncertainty
+    *
+    */
+  public static double symmetricalUncertainty(double matrix[][]) {
+
+    double sumForColumn, sumForRow, total = 0, columnEntropy = 0, 
+      rowEntropy = 0, entropyConditionedOnRows = 0, infoGain = 0;
+
+    // Compute entropy for columns
+    for (int i = 0; i < matrix[0].length; i++) {
+      sumForColumn = 0;
+      for (int j = 0; j < matrix.length; j++) {
+	sumForColumn += matrix[j][i];
+      }
+      columnEntropy += lnFunc(sumForColumn);
+      total += sumForColumn;
+    }
+    columnEntropy -= lnFunc(total);
+
+    // Compute entropy for rows and conditional entropy
+    for (int i = 0; i < matrix.length; i++) {
+      sumForRow = 0;
+      for (int j = 0; j < matrix[0].length; j++) { 
+	sumForRow += matrix[i][j];
+	entropyConditionedOnRows += lnFunc(matrix[i][j]);
+      }
+      rowEntropy += lnFunc(sumForRow);
+    }
+    entropyConditionedOnRows -= rowEntropy;
+    rowEntropy -= lnFunc(total);
+    infoGain = columnEntropy - entropyConditionedOnRows;
+    if (Utils.eq(columnEntropy, 0) || Utils.eq(rowEntropy, 0))
+      return 0;
+    return 2.0 * (infoGain / (columnEntropy + rowEntropy));
+  }
+
+  /**
+   * Computes Goodman and Kruskal's tau-value for a contingency table.
+   *
+   * @param matrix the contingency table
+   * @param Goodman and Kruskal's tau-value
+   */
+  public static double tauVal(double[][] matrix) {
+    
+    int nrows, ncols, row, col;
+    double [] ctotal;
+    double maxcol = 0, max, maxtotal = 0, n = 0;
+    
+    nrows = matrix.length;
+    ncols = matrix[0].length;
+    ctotal = new double [ncols];
+    for (row = 0; row < nrows; row++) {
+      max = 0;
+      for (col = 0; col < ncols; col++) {
+	if (Utils.gr(matrix[row][col], max)) 
+	  max = matrix[row][col];
+	ctotal[col] += matrix[row][col];
+	n += matrix[row][col];
+      }
+      maxtotal += max;
+    }
+    if (Utils.eq(n, 0)) {
+      return 0;
+    }
+    maxcol = ctotal[Utils.maxIndex(ctotal)];
+    return (maxtotal - maxcol)/(n - maxcol);
+  }
+
+  /**
+   * Help method for computing entropy.
+   */
+  private static double lnFunc(double num){
+    
+    // Constant hard coded for efficiency reasons
+    if (num < 1e-6) {
+      return 0;
+    } else {
+      return num * Math.log(num);
+    }
+  }
+  
+  /**
+   * Computes chi-value for one cell in matrix.
+   * From Gary Perlman's unixstat.
+   */
+  private static double chiCell(double freq, double expect, boolean yates){
+
+    double  diff = freq - expect;
+       
+    if (yates) {
+      diff = Math.abs (diff) - 0.5;
+      if (diff < 0.0) { // over-correction
+	diff = 0.0;
+      }
+    }
+    if (Math.abs(expect) < 10e-10) {
+      return (0.0);
+    } else {
+      return (diff * diff / expect);
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   */
+  public static void main(String[] ops) {
+
+    double[] firstRow = {10, 5, 20};
+    double[] secondRow = {2, 10, 6};
+    double[] thirdRow = {5, 10, 10};
+    double[][] matrix = new double[3][0];
+
+    matrix[0] = firstRow; matrix[1] = secondRow; matrix[2] = thirdRow;
+    for (int i = 0; i < matrix.length; i++) {
+      for (int j = 0; j < matrix[i].length; j++) {
+	System.out.print(matrix[i][j] + " ");
+      }
+      System.out.println();
+    }
+    System.out.println("Chi-squared probability: " +
+		       ContingencyTables.chiSquared(matrix, false));
+    System.out.println("Chi-squared value: " +
+		       ContingencyTables.chiVal(matrix, false));
+    System.out.println("Cochran's criterion fullfilled: " +
+		       ContingencyTables.cochransCriterion(matrix));
+    System.out.println("Cramer's V: " +
+		       ContingencyTables.CramersV(matrix));
+    System.out.println("Entropy of first row: " +
+		       ContingencyTables.entropy(firstRow));
+    System.out.println("Entropy conditioned on columns: " +
+		       ContingencyTables.entropyConditionedOnColumns(matrix));
+    System.out.println("Entropy conditioned on rows: " +
+		       ContingencyTables.entropyConditionedOnRows(matrix));
+    System.out.println("Entropy conditioned on rows (with Laplace): " +
+		       ContingencyTables.entropyConditionedOnRows(matrix, matrix, 3));
+    System.out.println("Entropy of rows: " +
+		       ContingencyTables.entropyOverRows(matrix));
+    System.out.println("Entropy of columns: " +
+		       ContingencyTables.entropyOverColumns(matrix));
+    System.out.println("Gain ratio: " +
+		       ContingencyTables.gainRatio(matrix));
+    System.out.println("Negative log2 of multiple hypergeometric probability: " +
+		       ContingencyTables.log2MultipleHypergeometric(matrix));
+    System.out.println("Symmetrical uncertainty: " +
+		       ContingencyTables.symmetricalUncertainty(matrix));
+    System.out.println("Tau value: " +
+		       ContingencyTables.tauVal(matrix));
+    double[][] newMatrix = new double[3][3];
+    newMatrix[0][0] = 1; newMatrix[0][1] = 0; newMatrix[0][2] = 1;
+    newMatrix[1][0] = 0; newMatrix[1][1] = 0; newMatrix[1][2] = 0;
+    newMatrix[2][0] = 1; newMatrix[2][1] = 0; newMatrix[2][2] = 1;
+    System.out.println("Matrix with empty row and column: ");
+    for (int i = 0; i < newMatrix.length; i++) {
+      for (int j = 0; j < newMatrix[i].length; j++) {
+	System.out.print(newMatrix[i][j] + " ");
+      }
+      System.out.println();
+    }
+    System.out.println("Reduced matrix: ");
+    newMatrix = ContingencyTables.reduceMatrix(newMatrix);
+    for (int i = 0; i < newMatrix.length; i++) {
+      for (int j = 0; j < newMatrix[i].length; j++) {
+	System.out.print(newMatrix[i][j] + " ");
+      }
+      System.out.println();
+    }
+  }
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Copyable.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Copyable.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Copyable.java	(revision 16561)
@@ -0,0 +1,41 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Copyable.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+/**
+ * Interface implemented by classes that can produce "shallow" copies
+ * of their objects. (As opposed to clone(), which is supposed to
+ * produce a "deep" copy.)
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public interface Copyable {
+
+  /**
+   * This method produces a shallow copy of an object.
+   * It does the same as the clone() method in Object, which also produces
+   * a shallow copy.
+   */
+  public Object copy();
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/FastVector.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/FastVector.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/FastVector.java	(revision 16561)
@@ -0,0 +1,432 @@
+/*
+ *    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.
+ */
+
+/*
+ *    FastVector.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Implements a fast vector class without synchronized
+ * methods. Replaces java.util.Vector. (Synchronized methods tend to
+ * be slow.)
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$ */
+public class FastVector implements Copyable, Serializable {
+
+  /**
+   * Class for enumerating the vector's elements.
+   */
+  public class FastVectorEnumeration implements Enumeration {
+
+    /** The counter. */
+    private int m_Counter;
+
+    /** The vector. */
+    private FastVector m_Vector;
+
+    /** Special element. Skipped during enumeration. */
+    private int m_SpecialElement;
+
+    /**
+     * Constructs an enumeration.
+     *
+     * @param vector the vector which is to be enumerated
+     */
+    public FastVectorEnumeration(FastVector vector) {
+
+      m_Counter = 0;
+      m_Vector = vector;
+      m_SpecialElement = -1;
+    }
+
+    /**
+     * Constructs an enumeration with a special element.
+     * The special element is skipped during the enumeration.
+     *
+     * @param vector the vector which is to be enumerated
+     * @param special the index of the special element
+     */
+    public FastVectorEnumeration(FastVector vector, int special) {
+
+      m_Vector = vector;
+      m_SpecialElement = special;
+      if (special == 0) {
+	m_Counter = 1;
+      } else {
+	m_Counter = 0;
+      }
+    }
+
+
+    /**
+     * Tests if there are any more elements to enumerate.
+     *
+     * @return true if there are some elements left
+     */
+    public final boolean hasMoreElements() {
+
+      if (m_Counter < m_Vector.size()) {
+	return true;
+      }
+      return false;
+    }
+
+    /**
+     * Returns the next element.
+     *
+     * @return the next element to be enumerated
+     */
+    public final Object nextElement() {
+  
+      Object result = m_Vector.elementAt(m_Counter);
+
+      m_Counter++;
+      if (m_Counter == m_SpecialElement) {
+	m_Counter++;
+      }
+      return result;
+    }
+  }
+
+  /** The array of objects. */
+  private Object[] m_Objects;
+
+  /** The current size; */
+  private int m_Size;
+
+  /** The capacity increment */
+  private int m_CapacityIncrement;
+  
+  /** The capacity multiplier. */
+  private double m_CapacityMultiplier;
+
+  /**
+   * Constructs an empty vector with initial
+   * capacity zero.
+   */
+  public FastVector() {
+  
+    m_Objects = new Object[0];
+    m_Size = 0;
+    m_CapacityIncrement = 1;
+    m_CapacityMultiplier = 2;
+  }
+
+  /**
+   * Constructs a vector with the given capacity.
+   *
+   * @param capacity the vector's initial capacity
+   */
+  public FastVector(int capacity) {
+
+    m_Objects = new Object[capacity];
+    m_Size = 0;
+    m_CapacityIncrement = 1;
+    m_CapacityMultiplier = 2;
+  }
+
+  /**
+   * Constructs a vector with the given capacity, capacity 
+   * increment and capacity mulitplier.
+   *
+   * @param capacity the vector's initial capacity
+   */
+  public FastVector(int capacity, int capIncrement, 
+		    double capMultiplier) {
+
+    m_Objects = new Object[capacity];
+    m_Size = 0;
+    m_CapacityIncrement = capIncrement;
+    m_CapacityMultiplier = capMultiplier;
+  }
+
+  /**
+   * Adds an element to this vector. Increases its
+   * capacity if its not large enough.
+   *
+   * @param element the element to add
+   */
+  public final void addElement(Object element) {
+
+    Object[] newObjects;
+
+    if (m_Size == m_Objects.length) {
+      newObjects = new Object[(int)m_CapacityMultiplier *
+			     (m_Objects.length +
+			      m_CapacityIncrement)];
+      System.arraycopy(m_Objects, 0, newObjects, 0, m_Size);
+      m_Objects = newObjects;
+    }
+    m_Objects[m_Size] = element;
+    m_Size++;
+  }
+
+  /**
+   * Returns the capacity of the vector.
+   *
+   * @return the capacity of the vector
+   */
+  public final int capacity() {
+  
+    return m_Objects.length;
+  }
+
+  /**
+   * Produces a shallow copy of this vector.
+   *
+   * @return the new vector
+   */
+  public final Object copy() {
+
+    FastVector copy = new FastVector(m_Objects.length, 
+				     m_CapacityIncrement,
+				     m_CapacityMultiplier);
+    copy.m_Size = m_Size;
+    System.arraycopy(m_Objects, 0, copy.m_Objects, 0, m_Size);
+    return copy;
+  }
+
+  /**
+   * Clones the vector and shallow copies all its elements.
+   * The elements have to implement the Copyable interface.
+   * 
+   * @return the new vector
+   */
+  public final Object copyElements() {
+
+    FastVector copy = new FastVector(m_Objects.length, 
+				     m_CapacityIncrement,
+				     m_CapacityMultiplier);
+    copy.m_Size = m_Size;
+    for (int i = 0; i < m_Size; i++) {
+      copy.m_Objects[i] = ((Copyable)m_Objects[i]).copy();
+    }
+    return copy;
+  }
+
+  /**
+   * Returns the element at the given position.
+   *
+   * @param index the element's index
+   * @return the element with the given index
+   */
+  public final Object elementAt(int index) {
+
+    return m_Objects[index];
+  }
+
+  /**
+   * Returns an enumeration of this vector.
+   *
+   * @return an enumeration of this vector
+   */
+  public final Enumeration elements() {
+  
+    return new FastVectorEnumeration(this);
+  }
+
+  /**
+   * Returns an enumeration of this vector, skipping the
+   * element with the given index.
+   *
+   * @param index the element to skip
+   * @return an enumeration of this vector
+   */
+  public final Enumeration elements(int index) {
+  
+    return new FastVectorEnumeration(this, index);
+  }
+
+  /**
+   * Returns the first element of the vector.
+   *
+   * @return the first element of the vector
+   */
+  public final Object firstElement() {
+
+    return m_Objects[0];
+  }
+
+  /**
+   * Searches for the first occurence of the given argument, 
+   * testing for equality using the equals method. 
+   *
+   * @param element the element to be found
+   * @return the index of the first occurrence of the argument 
+   * in this vector; returns -1 if the object is not found
+   */
+  public final int indexOf(Object element) {
+
+    for (int i = 0; i < m_Size; i++) {
+      if (element.equals(m_Objects[i])) {
+	return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Inserts an element at the given position.
+   *
+   * @param element the element to be inserted
+   * @param index the element's index
+   */
+  public final void insertElementAt(Object element, int index) {
+
+    Object[] newObjects;
+
+    if (m_Size < m_Objects.length) {
+      System.arraycopy(m_Objects, index, m_Objects, index + 1, 
+                       m_Size - index);
+      m_Objects[index] = element;
+    } else {
+      newObjects = new Object[(int)m_CapacityMultiplier *
+			     (m_Objects.length +
+			      m_CapacityIncrement)];
+      System.arraycopy(m_Objects, 0, newObjects, 0, index);
+      newObjects[index] = element;
+      System.arraycopy(m_Objects, index, newObjects, index + 1,
+		       m_Size - index);
+      m_Objects = newObjects;
+    }
+    m_Size++;
+  }
+
+  /**
+   * Returns the last element of the vector.
+   *
+   * @return the last element of the vector
+   */
+  public final Object lastElement() {
+
+    return m_Objects[m_Size - 1];
+  }
+
+  /**
+   * Deletes an element from this vector.
+   *
+   * @param index the index of the element to be deleted
+   */
+  public final void removeElementAt(int index) {
+
+    System.arraycopy(m_Objects, index + 1, m_Objects, index, 
+                     m_Size - index - 1);
+    m_Size--;
+  }
+
+  /**
+   * Removes all components from this vector and sets its 
+   * size to zero. 
+   */
+  public final void removeAllElements() {
+
+    m_Objects = new Object[m_Objects.length];
+    m_Size = 0;
+  }
+
+  /**
+   * Appends all elements of the supplied vector to this vector.
+   *
+   * @param toAppend the FastVector containing elements to append.
+   */
+  public final void appendElements(FastVector toAppend) {
+
+    setCapacity(size() + toAppend.size());
+    System.arraycopy(toAppend.m_Objects, 0, m_Objects, size(), toAppend.size());
+    m_Size = m_Objects.length;
+  }
+
+  /** 
+   * Returns all the elements of this vector as an array
+   *
+   * @param an array containing all the elements of this vector
+   */
+  public final Object [] toArray() {
+
+    Object [] newObjects = new Object[size()];
+    System.arraycopy(m_Objects, 0, newObjects, 0, size());
+    return newObjects;
+  }
+
+  /**
+   * Sets the vector's capacity to the given value.
+   *
+   * @param capacity the new capacity
+   */
+  public final void setCapacity(int capacity) {
+
+    Object[] newObjects = new Object[capacity];
+   
+    System.arraycopy(m_Objects, 0, newObjects, 0, Math.min(capacity, m_Size));
+    m_Objects = newObjects;
+    if (m_Objects.length < m_Size)
+      m_Size = m_Objects.length;
+  }
+
+  /**
+   * Sets the element at the given index.
+   *
+   * @param element the element to be put into the vector
+   * @param index the index at which the element is to be placed
+   */
+  public final void setElementAt(Object element, int index) {
+
+    m_Objects[index] = element;
+  }
+
+  /**
+   * Returns the vector's current size.
+   *
+   * @return the vector's current size
+   */
+  public final int size() {
+
+    return m_Size;
+  }
+
+  /**
+   * Swaps two elements in the vector.
+   *
+   * @param first index of the first element
+   * @param second index of the second element
+   */
+  public final void swap(int first, int second) {
+
+    Object help = m_Objects[first];
+
+    m_Objects[first] = m_Objects[second];
+    m_Objects[second] = help;
+  }
+
+  /**
+   * Sets the vector's capacity to its size.
+   */
+  public final void trimToSize() {
+
+    Object[] newObjects = new Object[m_Size];
+    
+    System.arraycopy(m_Objects, 0, newObjects, 0, m_Size);
+    m_Objects = newObjects;
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instance.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instance.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instance.java	(revision 16561)
@@ -0,0 +1,1064 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Instance.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Class for handling an instance. All values (numeric, nominal, or
+ * string) are internally stored as floating-point numbers. If an
+ * attribute is nominal (or a string), the stored value is the index
+ * of the corresponding nominal (or string) value in the attribute's
+ * definition. We have chosen this approach in favor of a more elegant
+ * object-oriented approach because it is much faster. <p>
+ *
+ * Typical usage (code from the main() method of this class): <p>
+ *
+ * <code>
+ * ... <br>
+ *      
+ * // Create empty instance with three attribute values <br>
+ * Instance inst = new Instance(3); <br><br>
+ *     
+ * // Set instance's values for the attributes "length", "weight", and "position"<br>
+ * inst.setValue(length, 5.3); <br>
+ * inst.setValue(weight, 300); <br>
+ * inst.setValue(position, "first"); <br><br>
+ *   
+ * // Set instance's dataset to be the dataset "race" <br>
+ * inst.setDataset(race); <br><br>
+ *   
+ * // Print the instance <br>
+ * System.out.println("The instance: " + inst); <br>
+ *
+ * ... <br>
+ * </code><p>
+ *
+ * All methods that change an instance are safe, ie. a change of an
+ * instance does not affect any other instances. All methods that
+ * change an instance's attribute values clone the attribute value
+ * vector before it is changed. If your application heavily modifies
+ * instance values, it may be faster to create a new instance from scratch.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$ 
+ */
+public class Instance implements Copyable, Serializable {
+  
+  /** Constant representing a missing value. */
+  protected final static double MISSING_VALUE = Double.NaN;
+
+  /** 
+   * The dataset the instance has access to.  Null if the instance
+   * doesn't have access to any dataset.  Only if an instance has
+   * access to a dataset, it knows about the actual attribute types.  
+   */
+  protected Instances m_Dataset;
+
+  /** The instance's attribute values. */
+  protected double[] m_AttValues;
+
+  /** The instance's weight. */
+  protected double m_Weight;
+
+  /**
+   * Constructor that copies the attribute values and the weight from
+   * the given instance. Reference to the dataset is set to null.
+   * (ie. the instance doesn't have access to information about the
+   * attribute types)
+   *
+   * @param instance the instance from which the attribute
+   * values and the weight are to be copied 
+   */
+  public Instance(Instance instance) {
+    
+    m_AttValues = instance.m_AttValues;
+    m_Weight = instance.m_Weight;
+    m_Dataset = null;
+  }
+
+  /**
+   * Constructor that inititalizes instance variable with given
+   * values. Reference to the dataset is set to null. (ie. the instance
+   * doesn't have access to information about the attribute types)
+   *
+   * @param weight the instance's weight
+   * @param attValues a vector of attribute values 
+   */
+  public Instance(double weight, double[] attValues){
+    
+    m_AttValues = attValues;
+    m_Weight = weight;
+    m_Dataset = null;
+  }
+
+  /**
+   * Constructor of an instance that sets weight to one, all values to
+   * be missing, and the reference to the dataset to null. (ie. the instance
+   * doesn't have access to information about the attribute types)
+   *
+   * @param numAttributes the size of the instance 
+   */
+  public Instance(int numAttributes) {
+    
+    m_AttValues = new double[numAttributes];
+    for (int i = 0; i < m_AttValues.length; i++) {
+      m_AttValues[i] = MISSING_VALUE;
+    }
+    m_Weight = 1;
+    m_Dataset = null;
+  }
+
+  /**
+   * Returns the attribute with the given index.
+   *
+   * @param index the attribute's index
+   * @return the attribute at the given position
+   * @exception UnassignedDatasetException if instance doesn't have access to a
+   * dataset
+   */ 
+  public Attribute attribute(int index) {
+   
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.attribute(index);
+  }
+
+  /**
+   * Returns the attribute with the given index. Does the same
+   * thing as attribute().
+   *
+   * @param indexOfIndex the index of the attribute's index 
+   * @return the attribute at the given position
+   * @exception UnassignedDatasetException if instance doesn't have access to a
+   * dataset
+   */ 
+  public Attribute attributeSparse(int indexOfIndex) {
+   
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.attribute(indexOfIndex);
+  }
+
+  /**
+   * Returns class attribute.
+   *
+   * @return the class attribute
+   * @exception UnassignedDatasetException if the class is not set or the
+   * instance doesn't have access to a dataset
+   */
+  public Attribute classAttribute() {
+
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.classAttribute();
+  }
+
+  /**
+   * Returns the class attribute's index.
+   *
+   * @return the class index as an integer 
+   * @exception UnassignedDatasetException if instance doesn't have access to a dataset 
+   */
+  public int classIndex() {
+    
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.classIndex();
+  }
+
+  /**
+   * Tests if an instance's class is missing.
+   *
+   * @return true if the instance's class is missing
+   * @exception UnassignedClassException if the class is not set or the instance doesn't
+   * have access to a dataset
+   */
+  public boolean classIsMissing() {
+
+    if (classIndex() < 0) {
+      throw new UnassignedClassException("Class is not set!");
+    }
+    return isMissing(classIndex());
+  }
+
+  /**
+   * Returns an instance's class value in internal format. (ie. as a
+   * floating-point number)
+   *
+   * @return the corresponding value as a double (If the 
+   * corresponding attribute is nominal (or a string) then it returns the 
+   * value's index as a double).
+   * @exception UnassignedClassException if the class is not set or the instance doesn't
+   * have access to a dataset 
+   */
+  public double classValue() {
+    
+    if (classIndex() < 0) {
+      throw new UnassignedClassException("Class is not set!");
+    }
+    return value(classIndex());
+  }
+
+  /**
+   * Produces a shallow copy of this instance. The copy has
+   * access to the same dataset. (if you want to make a copy
+   * that doesn't have access to the dataset, use 
+   * <code>new Instance(instance)</code>
+   *
+   * @return the shallow copy
+   */
+  public Object copy() {
+
+    Instance result = new Instance(this);
+    result.m_Dataset = m_Dataset;
+    return result;
+  }
+
+  /**
+   * Returns the dataset this instance has access to. (ie. obtains
+   * information about attribute types from) Null if the instance
+   * doesn't have access to a dataset.
+   *
+   * @return the dataset the instance has accesss to
+   */
+  public Instances dataset() {
+
+    return m_Dataset;
+  }
+
+  /**
+   * Deletes an attribute at the given position (0 to 
+   * numAttributes() - 1). Only succeeds if the instance does not
+   * have access to any dataset because otherwise inconsistencies
+   * could be introduced.
+   *
+   * @param pos the attribute's position
+   * @exception RuntimeException if the instance has access to a
+   * dataset 
+   */
+  public void deleteAttributeAt(int position) {
+
+    if (m_Dataset != null) {
+      throw new RuntimeException("Instance has access to a dataset!");
+    }
+    forceDeleteAttributeAt(position);
+  }
+
+  /**
+   * Returns an enumeration of all the attributes.
+   *
+   * @return enumeration of all the attributes
+   * @exception UnassignedDatasetException if the instance doesn't
+   * have access to a dataset 
+   */
+  public Enumeration enumerateAttributes() {
+
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.enumerateAttributes();
+  }
+
+  /**
+   * Tests if the headers of two instances are equivalent.
+   *
+   * @param instance another instance
+   * @return true if the header of the given instance is 
+   * equivalent to this instance's header
+   * @exception UnassignedDatasetException if instance doesn't have access to any
+   * dataset
+   */
+  public boolean equalHeaders(Instance inst) {
+
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.equalHeaders(inst.m_Dataset);
+  }
+
+  /**
+   * Returns the index of the attribute stored at the given position.
+   * Just returns the given value.
+   *
+   * @param position the position 
+   * @return the index of the attribute stored at the given position
+   */
+  public int index(int position) {
+
+    return position;
+  }
+
+  /**
+   * Inserts an attribute at the given position (0 to 
+   * numAttributes()). Only succeeds if the instance does not
+   * have access to any dataset because otherwise inconsistencies
+   * could be introduced.
+   *
+   * @param pos the attribute's position
+   * @exception RuntimeException if the instance has accesss to a
+   * dataset
+   * @exception IllegalArgumentException if the position is out of range
+   */
+  public void insertAttributeAt(int position) {
+
+    if (m_Dataset != null) {
+      throw new RuntimeException("Instance has accesss to a dataset!");
+    }
+    if ((position < 0) ||
+	(position > numAttributes())) {
+      throw new IllegalArgumentException("Can't insert attribute: index out "+
+                                         "of range");
+    }
+    forceInsertAttributeAt(position);
+  }
+
+  /**
+   * Tests if a specific value is "missing".
+   *
+   * @param attIndex the attribute's index
+   */
+  public boolean isMissing(int attIndex) {
+
+    if (Double.isNaN(m_AttValues[attIndex])) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Tests if a specific value is "missing". Does
+   * the same thing as isMissing() if applied to an Instance.
+   *
+   * @param indexOfIndex the index of the attribute's index 
+   */
+  public boolean isMissingSparse(int indexOfIndex) {
+
+    if (Double.isNaN(m_AttValues[indexOfIndex])) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Tests if a specific value is "missing".
+   * The given attribute has to belong to a dataset.
+   *
+   * @param att the attribute
+   */
+  public boolean isMissing(Attribute att) {
+
+    return isMissing(att.index());
+  }
+
+  /**
+   * Tests if the given value codes "missing".
+   *
+   * @param val the value to be tested
+   * @return true if val codes "missing"
+   */
+  public static boolean isMissingValue(double val) {
+
+    return Double.isNaN(val);
+  }
+
+  /**
+   * Merges this instance with the given instance and returns
+   * the result. Dataset is set to null.
+   *
+   * @param inst the instance to be merged with this one
+   * @return the merged instances
+   */
+  public Instance mergeInstance(Instance inst) {
+
+    int m = 0;
+    double [] newVals = new double[numAttributes() + inst.numAttributes()];
+    for (int j = 0; j < numAttributes(); j++, m++) {
+      newVals[m] = value(j);
+    }
+    for (int j = 0; j < inst.numAttributes(); j++, m++) {
+      newVals[m] = inst.value(j);
+    }
+    return new Instance(1.0, newVals);
+  }
+
+  /**
+   * Returns the double that codes "missing".
+   *
+   * @return the double that codes "missing"
+   */
+  public static double missingValue() {
+
+    return MISSING_VALUE;
+  }
+
+  /**
+   * Returns the number of attributes.
+   *
+   * @return the number of attributes as an integer
+   */
+  public int numAttributes() {
+
+    return m_AttValues.length;
+  }
+
+  /**
+   * Returns the number of class labels.
+   *
+   * @return the number of class labels as an integer if the 
+   * class attribute is nominal, 1 otherwise.
+   * @exception UnassignedDatasetException if instance doesn't have access to any
+   * dataset
+   */
+  public int numClasses() {
+    
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.numClasses();
+  }
+
+  /**
+   * Returns the number of values present. Always the same as numAttributes().
+   *
+   * @return the number of values
+   */
+  public int numValues() {
+
+    return m_AttValues.length;
+  }
+
+  /** 
+   * Replaces all missing values in the instance with the
+   * values contained in the given array. A deep copy of
+   * the vector of attribute values is performed before the
+   * values are replaced.
+   *
+   * @param array containing the means and modes
+   * @exception IllegalArgumentException if numbers of attributes are unequal
+   */
+  public void replaceMissingValues(double[] array) {
+	 
+    if ((array == null) || 
+	(array.length != m_AttValues.length)) {
+      throw new IllegalArgumentException("Unequal number of attributes!");
+    }
+    freshAttributeVector();
+    for (int i = 0; i < m_AttValues.length; i++) {
+      if (isMissing(i)) {
+	m_AttValues[i] = array[i];
+      }
+    }
+  }
+
+  /**
+   * Sets the class value of an instance to be "missing". A deep copy of
+   * the vector of attribute values is performed before the
+   * value is set to be missing.
+   *
+   * @exception UnassignedClassException if the class is not set
+   * @exception UnassignedDatasetException if the instance doesn't
+   * have access to a dataset
+   */
+  public void setClassMissing() {
+
+    if (classIndex() < 0) {
+      throw new UnassignedClassException("Class is not set!");
+    }
+    setMissing(classIndex());
+  }
+
+  /**
+   * Sets the class value of an instance to the given value (internal
+   * floating-point format).  A deep copy of the vector of attribute
+   * values is performed before the value is set.
+   *
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   * @exception UnassignedClassException if the class is not set
+   * @exception UnaddignedDatasetException if the instance doesn't
+   * have access to a dataset 
+   */
+  public void setClassValue(double value) {
+
+    if (classIndex() < 0) {
+      throw new UnassignedClassException("Class is not set!");
+    }
+    setValue(classIndex(), value);
+  }
+
+  /**
+   * Sets the class value of an instance to the given value. A deep
+   * copy of the vector of attribute values is performed before the
+   * value is set.
+   *
+   * @param value the new class value (If the class
+   * is a string attribute and the value can't be found,
+   * the value is added to the attribute).
+   * @exception UnassignedClassException if the class is not set
+   * @exception UnassignedDatasetException if the dataset is not set
+   * @exception IllegalArgumentException if the attribute is not
+   * nominal or a string, or the value couldn't be found for a nominal
+   * attribute 
+   */
+  public final void setClassValue(String value) {
+
+    if (classIndex() < 0) {
+      throw new UnassignedClassException("Class is not set!");
+    }
+    setValue(classIndex(), value);
+  }
+
+  /**
+   * Sets the reference to the dataset. Does not check if the instance
+   * is compatible with the dataset. Note: the dataset does not know
+   * about this instance. If the structure of the dataset's header
+   * gets changed, this instance will not be adjusted automatically.
+   *
+   * @param instances the reference to the dataset 
+   */
+  public final void setDataset(Instances instances) {
+    
+    m_Dataset = instances;
+  }
+
+  /**
+   * Sets a specific value to be "missing". Performs a deep copy
+   * of the vector of attribute values before the value is set to
+   * be missing.
+   *
+   * @param attIndex the attribute's index
+   */
+  public final void setMissing(int attIndex) {
+
+    setValue(attIndex, MISSING_VALUE);
+  }
+
+  /**
+   * Sets a specific value to be "missing". Performs a deep copy
+   * of the vector of attribute values before the value is set to
+   * be missing. The given attribute has to belong to a dataset.
+   *
+   * @param att the attribute
+   */
+  public final void setMissing(Attribute att) {
+
+    setMissing(att.index());
+  }
+
+  /**
+   * Sets a specific value in the instance to the given value 
+   * (internal floating-point format). Performs a deep copy
+   * of the vector of attribute values before the value is set.
+   *
+   * @param attIndex the attribute's index 
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   */
+  public void setValue(int attIndex, double value) {
+    
+    freshAttributeVector();
+    m_AttValues[attIndex] = value;
+  }
+
+  /**
+   * Sets a specific value in the instance to the given value 
+   * (internal floating-point format). Performs a deep copy
+   * of the vector of attribute values before the value is set.
+   * Does exactly the same thing as setValue().
+   *
+   * @param indexOfIndex the index of the attribute's index 
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   */
+  public void setValueSparse(int indexOfIndex, double value) {
+    
+    freshAttributeVector();
+    m_AttValues[indexOfIndex] = value;
+  }
+
+  /**
+   * Sets a value of a nominal or string attribute to the given
+   * value. Performs a deep copy of the vector of attribute values
+   * before the value is set.
+   *
+   * @param attIndex the attribute's index
+   * @param value the new attribute value (If the attribute
+   * is a string attribute and the value can't be found,
+   * the value is added to the attribute).
+   * @exception UnassignedDatasetException if the dataset is not set
+   * @exception IllegalArgumentException if the selected
+   * attribute is not nominal or a string, or the supplied value couldn't 
+   * be found for a nominal attribute 
+   */
+  public final void setValue(int attIndex, String value) {
+    
+    int valIndex;
+
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    if (!attribute(attIndex).isNominal() &&
+	!attribute(attIndex).isString()) {
+      throw new IllegalArgumentException("Attribute neither nominal nor string!");
+    }
+    valIndex = attribute(attIndex).indexOfValue(value);
+    if (valIndex == -1) {
+      if (attribute(attIndex).isNominal()) {
+	throw new IllegalArgumentException("Value not defined for given nominal attribute!");
+      } else {
+	attribute(attIndex).forceAddValue(value);
+	valIndex = attribute(attIndex).indexOfValue(value);
+      }
+    }
+    setValue(attIndex, (double)valIndex); 
+  }
+
+  /**
+   * Sets a specific value in the instance to the given value
+   * (internal floating-point format). Performs a deep copy of the
+   * vector of attribute values before the value is set, so if you are
+   * planning on calling setValue many times it may be faster to
+   * create a new instance using toDoubleArray.  The given attribute
+   * has to belong to a dataset.
+   *
+   * @param att the attribute 
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   */
+  public final void setValue(Attribute att, double value) {
+
+    setValue(att.index(), value);
+  }
+
+  /**
+   * Sets a value of an nominal or string attribute to the given
+   * value. Performs a deep copy of the vector of attribute values
+   * before the value is set, so if you are planning on calling setValue many
+   * times it may be faster to create a new instance using toDoubleArray.
+   * The given attribute has to belong to a dataset.
+   *
+   * @param att the attribute
+   * @param value the new attribute value (If the attribute
+   * is a string attribute and the value can't be found,
+   * the value is added to the attribute).
+   * @exception IllegalArgumentException if the the attribute is not
+   * nominal or a string, or the value couldn't be found for a nominal
+   * attribute 
+   */
+  public final void setValue(Attribute att, String value) {
+
+    if (!att.isNominal() &&
+	!att.isString()) {
+      throw new IllegalArgumentException("Attribute neither nominal nor string!");
+    }
+    int valIndex = att.indexOfValue(value);
+    if (valIndex == -1) {
+      if (att.isNominal()) {
+	throw new IllegalArgumentException("Value not defined for given nominal attribute!");
+      } else {
+	att.forceAddValue(value);
+	valIndex = att.indexOfValue(value);
+      }
+    }
+    setValue(att.index(), (double)valIndex);
+  }
+
+  /**
+   * Sets the weight of an instance.
+   *
+   * @param weight the weight
+   */
+  public final void setWeight(double weight) {
+
+    m_Weight = weight;
+  }
+
+  /** 
+   * Returns the value of a nominal (or string) attribute
+   * for the instance.
+   *
+   * @param attIndex the attribute's index
+   * @return the value as a string
+   * @exception IllegalArgumentException if the attribute is not a nominal
+   * (or string) attribute.
+   * @exception UnassignedDatasetException if the instance doesn't belong
+   * to a dataset.
+   */
+  public final String stringValue(int attIndex) {
+
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    } 
+    if (!m_Dataset.attribute(attIndex).isNominal() &&
+	!m_Dataset.attribute(attIndex).isString()) {
+      throw new IllegalArgumentException("Attribute neither nominal nor string!");
+    }
+    return m_Dataset.attribute(attIndex).
+      value((int) value(attIndex));
+  }
+
+  /** 
+   * Returns the value of a nominal (or string) attribute
+   * for the instance.
+   *
+   * @param att the attribute
+   * @return the value as a string
+   * @exception IllegalArgumentException if the attribute is not a nominal
+   * (or string) attribute.
+   * @exception UnassignedDatasetException if the instance doesn't belong
+   * to a dataset.
+   */
+  public final String stringValue(Attribute att) {
+
+    return stringValue(att.index());
+  }
+
+  /**
+   * Returns the values of each attribute as an array of doubles.
+   *
+   * @return an array containing all the instance attribute values
+   */
+  public double[] toDoubleArray() {
+
+    double[] newValues = new double[m_AttValues.length];
+    System.arraycopy(m_AttValues, 0, newValues, 0, 
+		     m_AttValues.length);
+    return newValues;
+  }
+
+  /**
+   * Returns the description of one instance. If the instance
+   * doesn't have access to a dataset, it returns the internal
+   * floating-point values. Quotes string
+   * values that contain whitespace characters.
+   *
+   * @return the instance's description as a string
+   */
+  public String toString() {
+
+    StringBuffer text = new StringBuffer();
+    
+    for (int i = 0; i < m_AttValues.length; i++) {
+      if (i > 0) text.append(",");
+      text.append(toString(i));
+    }
+
+    return text.toString();
+  }
+
+  /**
+   * Returns the description of one value of the instance as a 
+   * string. If the instance doesn't have access to a dataset, it 
+   * returns the internal floating-point value. Quotes string
+   * values that contain whitespace characters, or if they
+   * are a question mark.
+   *
+   * @param attIndex the attribute's index
+   * @return the value's description as a string
+   */
+  public final String toString(int attIndex) {
+
+   StringBuffer text = new StringBuffer();
+   
+   if (isMissing(attIndex)) {
+     text.append("?");
+   } else {
+     if (m_Dataset == null) {
+       text.append(Utils.doubleToString(m_AttValues[attIndex],6));
+     } else {
+       if (m_Dataset.attribute(attIndex).isNominal() || 
+	   m_Dataset.attribute(attIndex).isString()) {
+         text.append(Utils.quote(stringValue(attIndex)));
+       } else {
+	 text.append(Utils.doubleToString(value(attIndex),6));
+       }
+     }
+   }
+   return text.toString();
+  }
+
+  /**
+   * Returns the description of one value of the instance as a 
+   * string. If the instance doesn't have access to a dataset it 
+   * returns the internal floating-point value. Quotes string
+   * values that contain whitespace characters, or if they
+   * are a question mark.
+   * The given attribute has to belong to a dataset.
+   *
+   * @param att the attribute
+   * @return the value's description as a string
+   */
+  public final String toString(Attribute att) {
+   
+   return toString(att.index());
+  }
+
+  /**
+   * Returns an instance's attribute value in internal format.
+   *
+   * @param attIndex the attribute's index
+   * @return the specified value as a double (If the corresponding
+   * attribute is nominal (or a string) then it returns the value's index as a 
+   * double).
+   */
+  public double value(int attIndex) {
+
+    return m_AttValues[attIndex];
+  }
+
+  /**
+   * Returns an instance's attribute value in internal format.
+   * Does exactly the same thing as value() if applied to an Instance.
+   *
+   * @param indexOfIndex the index of the attribute's index
+   * @return the specified value as a double (If the corresponding
+   * attribute is nominal (or a string) then it returns the value's index as a 
+   * double).
+   */
+  public double valueSparse(int indexOfIndex) {
+
+    return m_AttValues[indexOfIndex];
+  }  
+
+  /**
+   * Returns an instance's attribute value in internal format.
+   * The given attribute has to belong to a dataset.
+   *
+   * @param att the attribute
+   * @return the specified value as a double (If the corresponding
+   * attribute is nominal (or a string) then it returns the value's index as a
+   * double).
+   */
+  public double value(Attribute att) {
+
+    return value(att.index());
+  }
+
+  /**
+   * Returns the instance's weight.
+   *
+   * @return the instance's weight as a double
+   */
+  public final double weight() {
+
+    return m_Weight;
+  }
+
+  /**
+   * Deletes an attribute at the given position (0 to 
+   * numAttributes() - 1).
+   *
+   * @param pos the attribute's position
+   */
+
+  void forceDeleteAttributeAt(int position) {
+
+    double[] newValues = new double[m_AttValues.length - 1];
+
+    System.arraycopy(m_AttValues, 0, newValues, 0, position);
+    if (position < m_AttValues.length - 1) {
+      System.arraycopy(m_AttValues, position + 1, 
+		       newValues, position, 
+		       m_AttValues.length - (position + 1));
+    }
+    m_AttValues = newValues;
+  }
+
+  /**
+   * Inserts an attribute at the given position
+   * (0 to numAttributes()) and sets its value to be missing. 
+   *
+   * @param pos the attribute's position
+   */
+  void forceInsertAttributeAt(int position)  {
+
+    double[] newValues = new double[m_AttValues.length + 1];
+
+    System.arraycopy(m_AttValues, 0, newValues, 0, position);
+    newValues[position] = MISSING_VALUE;
+    System.arraycopy(m_AttValues, position, newValues, 
+		     position + 1, m_AttValues.length - position);
+    m_AttValues = newValues;
+  }
+
+  /**
+   * Private constructor for subclasses. Does nothing.
+   */
+  protected Instance() {
+  }
+
+  /**
+   * Clones the attribute vector of the instance and
+   * overwrites it with the clone.
+   */
+  private void freshAttributeVector() {
+
+    m_AttValues = toDoubleArray();
+  }
+
+  /**
+   * Main method for testing this class.
+   */
+  public static void main(String[] options) {
+
+    try {
+
+      // Create numeric attributes "length" and "weight"
+      Attribute length = new Attribute("length");
+      Attribute weight = new Attribute("weight");
+      
+      // Create vector to hold nominal values "first", "second", "third" 
+      FastVector my_nominal_values = new FastVector(3); 
+      my_nominal_values.addElement("first"); 
+      my_nominal_values.addElement("second"); 
+      my_nominal_values.addElement("third"); 
+      
+      // Create nominal attribute "position" 
+      Attribute position = new Attribute("position", my_nominal_values);
+      
+      // Create vector of the above attributes 
+      FastVector attributes = new FastVector(3);
+      attributes.addElement(length);
+      attributes.addElement(weight);
+      attributes.addElement(position);
+      
+      // Create the empty dataset "race" with above attributes
+      Instances race = new Instances("race", attributes, 0);
+      
+      // Make position the class attribute
+      race.setClassIndex(position.index());
+      
+      // Create empty instance with three attribute values
+      Instance inst = new Instance(3);
+      
+      // Set instance's values for the attributes "length", "weight", and "position"
+      inst.setValue(length, 5.3);
+      inst.setValue(weight, 300);
+      inst.setValue(position, "first");
+      
+      // Set instance's dataset to be the dataset "race"
+      inst.setDataset(race);
+      
+      // Print the instance
+      System.out.println("The instance: " + inst);
+      
+      // Print the first attribute
+      System.out.println("First attribute: " + inst.attribute(0));
+      
+      // Print the class attribute
+      System.out.println("Class attribute: " + inst.classAttribute());
+      
+      // Print the class index
+      System.out.println("Class index: " + inst.classIndex());
+      
+      // Say if class is missing
+      System.out.println("Class is missing: " + inst.classIsMissing());
+      
+      // Print the instance's class value in internal format
+      System.out.println("Class value (internal format): " + inst.classValue());
+      
+      // Print a shallow copy of this instance
+      Instance copy = (Instance) inst.copy();
+      System.out.println("Shallow copy: " + copy);
+      
+      // Set dataset for shallow copy
+      copy.setDataset(inst.dataset());
+      System.out.println("Shallow copy with dataset set: " + copy);
+      
+      // Unset dataset for copy, delete first attribute, and insert it again
+      copy.setDataset(null);
+      copy.deleteAttributeAt(0);
+      copy.insertAttributeAt(0);
+      copy.setDataset(inst.dataset());
+      System.out.println("Copy with first attribute deleted and inserted: " + copy); 
+      
+      // Enumerate attributes (leaving out the class attribute)
+      System.out.println("Enumerating attributes (leaving out class):");
+      Enumeration enum = inst.enumerateAttributes();
+      while (enum.hasMoreElements()) {
+	Attribute att = (Attribute) enum.nextElement();
+	System.out.println(att);
+      }
+      
+      // Headers are equivalent?
+      System.out.println("Header of original and copy equivalent: " +
+			 inst.equalHeaders(copy));
+
+      // Test for missing values
+      System.out.println("Length of copy missing: " + copy.isMissing(length));
+      System.out.println("Weight of copy missing: " + copy.isMissing(weight.index()));
+      System.out.println("Length of copy missing: " + 
+			 Instance.isMissingValue(copy.value(length)));
+      System.out.println("Missing value coded as: " + Instance.missingValue());
+
+      // Prints number of attributes and classes
+      System.out.println("Number of attributes: " + copy.numAttributes());
+      System.out.println("Number of classes: " + copy.numClasses());
+
+      // Replace missing values
+      double[] meansAndModes = {2, 3, 0};
+      copy.replaceMissingValues(meansAndModes);
+      System.out.println("Copy with missing value replaced: " + copy);
+
+      // Setting and getting values and weights
+      copy.setClassMissing();
+      System.out.println("Copy with missing class: " + copy);
+      copy.setClassValue(0);
+      System.out.println("Copy with class value set to first value: " + copy);
+      copy.setClassValue("third");
+      System.out.println("Copy with class value set to \"third\": " + copy);
+      copy.setMissing(1);
+      System.out.println("Copy with second attribute set to be missing: " + copy);
+      copy.setMissing(length);
+      System.out.println("Copy with length set to be missing: " + copy);
+      copy.setValue(0, 0);
+      System.out.println("Copy with first attribute set to 0: " + copy);
+      copy.setValue(weight, 1);
+      System.out.println("Copy with weight attribute set to 1: " + copy);
+      copy.setValue(position, "second");
+      System.out.println("Copy with position set to \"second\": " + copy);
+      copy.setValue(2, "first");
+      System.out.println("Copy with last attribute set to \"first\": " + copy);
+      System.out.println("Current weight of instance copy: " + copy.weight());
+      copy.setWeight(2);
+      System.out.println("Current weight of instance copy (set to 2): " + copy.weight());
+      System.out.println("Last value of copy: " + copy.toString(2));
+      System.out.println("Value of position for copy: " + copy.toString(position));
+      System.out.println("Last value of copy (internal format): " + copy.value(2));
+      System.out.println("Value of position for copy (internal format): " + 
+			 copy.value(position));
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instances.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instances.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Instances.java	(revision 16561)
@@ -0,0 +1,2150 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Instances.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Class for handling an ordered set of weighted instances. <p>
+ *
+ * Typical usage (code from the main() method of this class): <p>
+ *
+ * <code>
+ * ... <br>
+ * 
+ * // Read all the instances in the file <br>
+ * reader = new FileReader(filename); <br>
+ * instances = new Instances(reader); <br><br>
+ *
+ * // Make the last attribute be the class <br>
+ * instances.setClassIndex(instances.numAttributes() - 1); <br><br>
+ * 
+ * // Print header and instances. <br>
+ * System.out.println("\nDataset:\n"); <br> 
+ * System.out.println(instances); <br><br>
+ *
+ * ... <br>
+ * </code><p>
+ *
+ * All methods that change a set of instances are safe, ie. a change
+ * of a set of instances does not affect any other sets of
+ * instances. All methods that change a datasets's attribute
+ * information clone the dataset before it is changed.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$ 
+ */
+public class Instances implements Serializable {
+ 
+  /** The filename extension that should be used for arff files */
+  public static String FILE_EXTENSION = ".arff";
+
+  /** The dataset's name. */
+  protected String m_RelationName;         
+
+  /** The attribute information. */
+  protected FastVector m_Attributes;
+
+  /** The instances. */
+  protected FastVector m_Instances;
+
+  /** The class attribute's index */
+  protected int m_ClassIndex;
+
+  /** Buffer of values for sparse instance */
+  protected double[] m_ValueBuffer;
+
+  /** Buffer of indices for sparse instance */
+  protected int[] m_IndicesBuffer;
+
+  /**
+   * Reads an ARFF file from a reader, and assigns a weight of
+   * one to each instance. Lets the index of the class 
+   * attribute be undefined (negative).
+   *
+   * @param reader the reader
+   * @exception IOException if the ARFF file is not read 
+   * successfully
+   */
+  public Instances(Reader reader) throws IOException {
+
+    StreamTokenizer tokenizer;
+
+    tokenizer = new StreamTokenizer(reader);
+    initTokenizer(tokenizer);
+    readHeader(tokenizer);
+    m_ClassIndex = -1;
+    m_Instances = new FastVector(1000);
+    while (getInstance(tokenizer, true)) {};
+    compactify();
+  }
+ 
+  /**
+   * Reads the header of an ARFF file from a reader and 
+   * reserves space for the given number of instances. Lets
+   * the class index be undefined (negative).
+   *
+   * @param reader the reader
+   * @param capacity the capacity
+   * @exception IllegalArgumentException if the header is not read successfully
+   * or the capacity is negative.
+   * @exception IOException if there is a problem with the reader.
+   */
+   public Instances(Reader reader, int capacity) throws IOException {
+
+    StreamTokenizer tokenizer;
+
+    if (capacity < 0) {
+      throw new IllegalArgumentException("Capacity has to be positive!");
+    }
+    tokenizer = new StreamTokenizer(reader); 
+    initTokenizer(tokenizer);
+    readHeader(tokenizer);
+    m_ClassIndex = -1;
+    m_Instances = new FastVector(capacity);
+  }
+
+  /**
+   * Constructor copying all instances and references to
+   * the header information from the given set of instances.
+   *
+   * @param instances the set to be copied
+   */
+  public Instances(Instances dataset) {
+
+    this(dataset, dataset.numInstances());
+
+    dataset.copyInstances(0, this, dataset.numInstances());
+  }
+
+  /**
+   * Constructor creating an empty set of instances. Copies references
+   * to the header information from the given set of instances. Sets
+   * the capacity of the set of instances to 0 if its negative.
+   *
+   * @param instances the instances from which the header 
+   * information is to be taken
+   * @param capacity the capacity of the new dataset 
+   */
+  public Instances(Instances dataset, int capacity) {
+    
+    if (capacity < 0) {
+      capacity = 0;
+    }
+    
+    // Strings only have to be "shallow" copied because
+    // they can't be modified.
+    m_ClassIndex = dataset.m_ClassIndex;
+    m_RelationName = dataset.m_RelationName;
+    m_Attributes = dataset.m_Attributes;
+    m_Instances = new FastVector(capacity);
+  }
+
+  /**
+   * Creates a new set of instances by copying a 
+   * subset of another set.
+   *
+   * @param source the set of instances from which a subset 
+   * is to be created
+   * @param first the index of the first instance to be copied
+   * @param toCopy the number of instances to be copied
+   * @exception IllegalArgumentException if first and toCopy are out of range
+   */
+  public Instances(Instances source, int first, int toCopy) {
+    
+    this(source, toCopy);
+
+    if ((first < 0) || ((first + toCopy) > source.numInstances())) {
+      throw new IllegalArgumentException("Parameters first and/or toCopy out "+
+                                         "of range");
+    }
+    source.copyInstances(first, this, toCopy);
+  }
+
+  /**
+   * Creates an empty set of instances. Uses the given
+   * attribute information. Sets the capacity of the set of 
+   * instances to 0 if its negative. Given attribute information
+   * must not be changed after this constructor has been used.
+   *
+   * @param name the name of the relation
+   * @param attInfo the attribute information
+   * @param capacity the capacity of the set
+   */
+  public Instances(String name, FastVector attInfo, int capacity) {
+
+    m_RelationName = name;
+    m_ClassIndex = -1;
+    m_Attributes = attInfo;
+    for (int i = 0; i < numAttributes(); i++) {
+      attribute(i).setIndex(i);
+    }
+    m_Instances = new FastVector(capacity);
+  }
+ 
+  /**
+   * Create a copy of the structure, but "cleanse" string types (i.e.
+   * doesn't contain references to the strings seen in the past).
+   *
+   * @return a copy of the instance structure.
+   */
+  public Instances stringFreeStructure() {
+
+    FastVector atts = (FastVector)m_Attributes.copy();
+    for (int i = 0 ; i < atts.size(); i++) {
+      Attribute att = (Attribute)atts.elementAt(i);
+      if (att.type() == Attribute.STRING) {
+        atts.setElementAt(new Attribute(att.name(), null), i);
+      }
+    }
+    Instances result = new Instances(relationName(), atts, 0);
+    result.m_ClassIndex = m_ClassIndex;
+    return result;
+  }
+
+  /**
+   * Adds one instance to the end of the set. 
+   * Shallow copies instance before it is added. Increases the
+   * size of the dataset if it is not large enough. Does not
+   * check if the instance is compatible with the dataset.
+   *
+   * @param instance the instance to be added
+   */
+  public final void add(Instance instance) {
+
+    Instance newInstance = (Instance)instance.copy();
+
+    newInstance.setDataset(this);
+    m_Instances.addElement(newInstance);
+  }
+
+  /**
+   * Returns an attribute.
+   *
+   * @param index the attribute's index
+   * @return the attribute at the given position
+   */ 
+  public final Attribute attribute(int index) {
+    
+    return (Attribute) m_Attributes.elementAt(index);
+  }
+
+  /**
+   * Returns an attribute given its name. If there is more than
+   * one attribute with the same name, it returns the first one.
+   * Returns null if the attribute can't be found.
+   *
+   * @param name the attribute's name
+   * @return the attribute with the given name, null if the
+   * attribute can't be found
+   */ 
+  public final Attribute attribute(String name) {
+    
+    for (int i = 0; i < numAttributes(); i++) {
+      if (attribute(i).name().equals(name)) {
+	return attribute(i);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Checks for string attributes in the dataset
+   *
+   * @return true if string attributes are present, false otherwise
+   */
+  public boolean checkForStringAttributes() {
+
+    int i = 0;
+   
+    while (i < m_Attributes.size()) {
+      if (attribute(i++).isString()) {
+	return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the given instance is compatible
+   * with this dataset. Only looks at the size of
+   * the instance and the ranges of the values for 
+   * nominal and string attributes.
+   *
+   * @return true if the instance is compatible with the dataset 
+   */
+  public final boolean checkInstance(Instance instance) {
+
+    if (instance.numAttributes() != numAttributes()) {
+      return false;
+    }
+    for (int i = 0; i < numAttributes(); i++) {
+      if (instance.isMissing(i)) {
+	continue;
+      } else if (attribute(i).isNominal() ||
+		 attribute(i).isString()) {
+	if (!(Utils.eq(instance.value(i),
+		       (double)(int)instance.value(i)))) {
+	  return false;
+	} else if (Utils.sm(instance.value(i), 0) ||
+		   Utils.gr(instance.value(i),
+			    attribute(i).numValues())) {
+	  return false;
+	}
+      }
+    }
+    return true;
+  }
+	
+  /**
+   * Returns the class attribute.
+   *
+   * @return the class attribute
+   * @exception UnassignedClassException if the class is not set
+   */
+  public final Attribute classAttribute() {
+
+    if (m_ClassIndex < 0) {
+      throw new UnassignedClassException("Class index is negative (not set)!");
+    }
+    return attribute(m_ClassIndex);
+  }
+
+  /**
+   * Returns the class attribute's index. Returns negative number
+   * if it's undefined.
+   *
+   * @return the class index as an integer
+   */
+  public final int classIndex() {
+    
+    return m_ClassIndex;
+  }
+ 
+  /**
+   * Compactifies the set of instances. Decreases the capacity of
+   * the set so that it matches the number of instances in the set.
+   */
+  public final void compactify() {
+
+    m_Instances.trimToSize();
+  }
+
+  /**
+   * Removes all instances from the set.
+   */
+  public final void delete() {
+    
+    m_Instances = new FastVector();
+  }
+
+  /**
+   * Removes an instance at the given position from the set.
+   *
+   * @param index the instance's position
+   */
+  public final void delete(int index) {
+    
+    m_Instances.removeElementAt(index);
+  }
+
+  /**
+   * Deletes an attribute at the given position 
+   * (0 to numAttributes() - 1). A deep copy of the attribute
+   * information is performed before the attribute is deleted.
+   *
+   * @param pos the attribute's position
+   * @exception IllegalArgumentException if the given index is out of range or the
+   * class attribute is being deleted
+   */
+  public void deleteAttributeAt(int position) {
+	 
+    if ((position < 0) || (position >= m_Attributes.size())) {
+      throw new IllegalArgumentException("Index out of range");
+    }
+    if (position == m_ClassIndex) {
+      throw new IllegalArgumentException("Can't delete class attribute");
+    }
+    freshAttributeInfo();
+    if (m_ClassIndex > position) {
+      m_ClassIndex--;
+    }
+    m_Attributes.removeElementAt(position);
+    for (int i = position; i < m_Attributes.size(); i++) {
+      Attribute current = (Attribute)m_Attributes.elementAt(i);
+      current.setIndex(current.index() - 1);
+    }
+    for (int i = 0; i < numInstances(); i++) {
+      instance(i).forceDeleteAttributeAt(position); 
+    }
+  }
+
+  /**
+   * Deletes all string attributes in the dataset. A deep copy of the attribute
+   * information is performed before an attribute is deleted.
+   *
+   * @exception IllegalArgumentException if string attribute couldn't be 
+   * successfully deleted (probably because it is the class attribute).
+   */
+  public void deleteStringAttributes() {
+
+    int i = 0;
+    while (i < m_Attributes.size()) {
+      if (attribute(i).isString()) {
+	deleteAttributeAt(i);
+      } else {
+	i++;
+      }
+    }
+  }
+
+  /**
+   * Removes all instances with missing values for a particular
+   * attribute from the dataset.
+   *
+   * @param attIndex the attribute's index
+   */
+  public final void deleteWithMissing(int attIndex) {
+
+    FastVector newInstances = new FastVector(numInstances());
+
+    for (int i = 0; i < numInstances(); i++) {
+      if (!instance(i).isMissing(attIndex)) {
+	newInstances.addElement(instance(i));
+      }
+    }
+    m_Instances = newInstances;
+  }
+
+  /**
+   * Removes all instances with missing values for a particular
+   * attribute from the dataset.
+   *
+   * @param att the attribute
+   */
+  public final void deleteWithMissing(Attribute att) {
+
+    deleteWithMissing(att.index());
+  }
+
+  /**
+   * Removes all instances with a missing class value
+   * from the dataset.
+   *
+   * @exception UnassignedClassException if class is not set
+   */
+  public final void deleteWithMissingClass() {
+
+    if (m_ClassIndex < 0) {
+      throw new UnassignedClassException("Class index is negative (not set)!");
+    }
+    deleteWithMissing(m_ClassIndex);
+  }
+
+  /**
+   * Returns an enumeration of all the attributes.
+   *
+   * @return enumeration of all the attributes.
+   */
+  public Enumeration enumerateAttributes() {
+
+    return m_Attributes.elements(m_ClassIndex);
+  }
+
+  /**
+   * Returns an enumeration of all instances in the dataset.
+   *
+   * @return enumeration of all instances in the dataset
+   */
+  public final Enumeration enumerateInstances() {
+
+    return m_Instances.elements();
+  }
+
+  /**
+   * Checks if two headers are equivalent.
+   *
+   * @param dataset another dataset
+   * @return true if the header of the given dataset is equivalent 
+   * to this header
+   */
+  public final boolean equalHeaders(Instances dataset){
+
+    // Check class and all attributes
+    if (m_ClassIndex != dataset.m_ClassIndex) {
+      return false;
+    }
+    if (m_Attributes.size() != dataset.m_Attributes.size()) {
+      return false;
+    }
+    for (int i = 0; i < m_Attributes.size(); i++) {
+      if (!(attribute(i).equals(dataset.attribute(i)))) {
+	return false;
+      }
+    }
+    return true;
+  }
+ 
+  /**
+   * Returns the first instance in the set.
+   *
+   * @return the first instance in the set
+   */
+  public final Instance firstInstance() {
+    
+    return (Instance)m_Instances.firstElement();
+  }
+ 
+  /**
+   * Inserts an attribute at the given position (0 to 
+   * numAttributes()) and sets all values to be missing.
+   * Shallow copies the attribute before it is inserted, and performs
+   * a deep copy of the existing attribute information.
+   *
+   * @param att the attribute to be inserted
+   * @param pos the attribute's position
+   * @exception IllegalArgumentException if the given index is out of range
+   */
+  public void insertAttributeAt(Attribute att, int position) {
+	 
+    if ((position < 0) ||
+	(position > m_Attributes.size())) {
+      throw new IllegalArgumentException("Index out of range");
+    }
+    att = (Attribute)att.copy();
+    freshAttributeInfo();
+    att.setIndex(position);
+    m_Attributes.insertElementAt(att, position);
+    for (int i = position + 1; i < m_Attributes.size(); i++) {
+      Attribute current = (Attribute)m_Attributes.elementAt(i);
+      current.setIndex(current.index() + 1);
+    }
+    for (int i = 0; i < numInstances(); i++) {
+      instance(i).forceInsertAttributeAt(position);
+    }
+    if (m_ClassIndex >= position) {
+      m_ClassIndex++;
+    }
+  }
+
+  /**
+   * Returns the instance at the given position.
+   *
+   * @param index the instance's index
+   * @return the instance at the given position
+   */
+  public final Instance instance(int index) {
+
+    return (Instance)m_Instances.elementAt(index);
+  }
+
+  /**
+   * Returns the last instance in the set.
+   *
+   * @return the last instance in the set
+   */
+  public final Instance lastInstance() {
+    
+    return (Instance)m_Instances.lastElement();
+  }
+
+  /**
+   * Returns the mean (mode) for a numeric (nominal) attribute as
+   * a floating-point value. Returns 0 if the attribute is neither nominal nor 
+   * numeric. If all values are missing it returns zero.
+   *
+   * @param attIndex the attribute's index
+   * @return the mean or the mode
+   */
+  public final double meanOrMode(int attIndex) {
+
+    double result, found;
+    int [] counts;
+
+    if (attribute(attIndex).isNumeric()) {
+      result = found = 0;
+      for (int j = 0; j < numInstances(); j++) {
+	if (!instance(j).isMissing(attIndex)) {
+	  found += instance(j).weight();
+	  result += instance(j).weight()*instance(j).value(attIndex);
+	}
+      }
+      if (Utils.eq(found, 0)) {
+	return 0;
+      } else {
+	return result / found;
+      }
+    } else if (attribute(attIndex).isNominal()) {
+      counts = new int[attribute(attIndex).numValues()];
+      for (int j = 0; j < numInstances(); j++) {
+	if (!instance(j).isMissing(attIndex)) {
+	  counts[(int) instance(j).value(attIndex)] += instance(j).weight();
+	}
+      }
+      return (double)Utils.maxIndex(counts);
+    } else {
+      return 0;
+    }
+  }
+
+  /**
+   * Returns the mean (mode) for a numeric (nominal) attribute as a
+   * floating-point value.  Returns 0 if the attribute is neither
+   * nominal nor numeric.  If all values are missing it returns zero.
+   *
+   * @param att the attribute
+   * @return the mean or the mode 
+   */
+  public final double meanOrMode(Attribute att) {
+
+    return meanOrMode(att.index());
+  }
+
+  /**
+   * Returns the number of attributes.
+   *
+   * @return the number of attributes as an integer
+   */
+  public final int numAttributes() {
+
+    return m_Attributes.size();
+  }
+
+  /**
+   * Returns the number of class labels.
+   *
+   * @return the number of class labels as an integer if the class 
+   * attribute is nominal, 1 otherwise.
+   * @exception UnassignedClassException if the class is not set
+   */
+  public final int numClasses() {
+    
+    if (m_ClassIndex < 0) {
+      throw new UnassignedClassException("Class index is negative (not set)!");
+    }
+    if (!classAttribute().isNominal()) {
+      return 1;
+    } else {
+      return classAttribute().numValues();
+    }
+  }
+
+  /**
+   * Returns the number of distinct values of a given attribute.
+   * Returns the number of instances if the attribute is a
+   * string attribute. The value 'missing' is not counted.
+   *
+   * @param attIndex the attribute
+   * @return the number of distinct values of a given attribute
+   */
+  public final int numDistinctValues(int attIndex) {
+
+    if (attribute(attIndex).isNumeric()) {
+      double [] attVals = attributeToDoubleArray(attIndex);
+      int [] sorted = Utils.sort(attVals);
+      double prev = 0;
+      int counter = 0;
+      for (int i = 0; i < sorted.length; i++) {
+	Instance current = instance(sorted[i]);
+	if (current.isMissing(attIndex)) {
+	  break;
+	}
+	if ((i == 0) || 
+	    Utils.gr(current.value(attIndex), prev)) {
+	  prev = current.value(attIndex);
+	  counter++;
+	}
+      }
+      return counter;
+    } else {
+      return attribute(attIndex).numValues();
+    }
+  }
+
+  /**
+   * Returns the number of distinct values of a given attribute.
+   * Returns the number of instances if the attribute is a
+   * string attribute. The value 'missing' is not counted.
+   *
+   * @param att the attribute
+   * @return the number of distinct values of a given attribute
+   */
+  public final int numDistinctValues(Attribute att) {
+
+    return numDistinctValues(att.index());
+  }
+  
+  /**
+   * Returns the number of instances in the dataset.
+   *
+   * @return the number of instances in the dataset as an integer
+   */
+  public final int numInstances() {
+
+    return m_Instances.size();
+  }
+
+  /**
+   * Shuffles the instances in the set so that they are ordered 
+   * randomly.
+   *
+   * @param random a random number generator
+   */
+  public final void randomize(Random random) {
+
+    for (int j = numInstances() - 1; j > 0; j--)
+      swap(j,(int)(random.nextDouble()*(double)j));
+  }
+
+  /**
+   * Reads a single instance from the reader and appends it
+   * to the dataset.  Automatically expands the dataset if it
+   * is not large enough to hold the instance. This method does
+   * not check for carriage return at the end of the line.
+   *
+   * @param reader the reader 
+   * @return false if end of file has been reached
+   * @exception IOException if the information is not read 
+   * successfully
+   */ 
+  public final boolean readInstance(Reader reader) 
+       throws IOException {
+
+    StreamTokenizer tokenizer = new StreamTokenizer(reader);
+    
+    initTokenizer(tokenizer);
+    return getInstance(tokenizer, false);
+  }    
+
+  /**
+   * Returns the relation's name.
+   *
+   * @return the relation's name as a string
+   */
+  public final String relationName() {
+
+    return m_RelationName;
+  }
+
+  /**
+   * Renames an attribute. This change only affects this
+   * dataset.
+   *
+   * @param att the attribute's index
+   * @param name the new name
+   */
+  public final void renameAttribute(int att, String name) {
+
+    Attribute newAtt = attribute(att).copy(name);
+    FastVector newVec = new FastVector(numAttributes());
+
+    for (int i = 0; i < numAttributes(); i++) {
+      if (i == att) {
+	newVec.addElement(newAtt);
+      } else {
+	newVec.addElement(attribute(i));
+      }
+    }
+    m_Attributes = newVec;
+  }
+
+  /**
+   * Renames an attribute. This change only affects this
+   * dataset.
+   *
+   * @param att the attribute
+   * @param name the new name
+   */
+  public final void renameAttribute(Attribute att, String name) {
+
+    renameAttribute(att.index(), name);
+  }
+
+  /**
+   * Renames the value of a nominal (or string) attribute value. This
+   * change only affects this dataset.
+   *
+   * @param att the attribute's index
+   * @param val the value's index
+   * @param name the new name 
+   */
+  public final void renameAttributeValue(int att, int val, String name) {
+
+    Attribute newAtt = (Attribute)attribute(att).copy();
+    FastVector newVec = new FastVector(numAttributes());
+
+    newAtt.setValue(val, name);
+    for (int i = 0; i < numAttributes(); i++) {
+      if (i == att) {
+	newVec.addElement(newAtt);
+      } else {
+	newVec.addElement(attribute(i));
+      }
+    }
+    m_Attributes = newVec;
+  }
+
+  /**
+   * Renames the value of a nominal (or string) attribute value. This
+   * change only affects this dataset.
+   *
+   * @param att the attribute
+   * @param val the value
+   * @param name the new name
+   */
+  public final void renameAttributeValue(Attribute att, String val, 
+                                         String name) {
+
+    renameAttributeValue(att.index(), att.indexOfValue(val), name);
+  }
+
+  /**
+   * Creates a new dataset of the same size using random sampling
+   * with replacement.
+   *
+   * @param random a random number generator
+   * @return the new dataset
+   */
+  public final Instances resample(Random random) {
+
+    Instances newData = new Instances(this, numInstances());
+    while (newData.numInstances() < numInstances()) {
+      int i = (int) (random.nextDouble() * (double) numInstances());
+      newData.add(instance(i));
+    }
+    return newData;
+  }
+
+  /**
+   * Creates a new dataset of the same size using random sampling
+   * with replacement according to the current instance weights. The
+   * weights of the instances in the new dataset are set to one.
+   *
+   * @param random a random number generator
+   * @return the new dataset
+   */
+  public final Instances resampleWithWeights(Random random) {
+
+    double [] weights = new double[numInstances()];
+    boolean foundOne = false;
+    for (int i = 0; i < weights.length; i++) {
+      weights[i] = instance(i).weight();
+      if (!Utils.eq(weights[i], weights[0])) {
+        foundOne = true;
+      }
+    }
+    if (foundOne) {
+      return resampleWithWeights(random, weights);
+    } else {
+      return new Instances(this);
+    }
+  }
+
+
+  /**
+   * Creates a new dataset of the same size using random sampling
+   * with replacement according to the given weight vector. The
+   * weights of the instances in the new dataset are set to one.
+   * The length of the weight vector has to be the same as the
+   * number of instances in the dataset, and all weights have to
+   * be positive.
+   *
+   * @param random a random number generator
+   * @param weights the weight vector
+   * @return the new dataset
+   * @exception IllegalArgumentException if the weights array is of the wrong
+   * length or contains negative weights.
+   */
+  public final Instances resampleWithWeights(Random random, 
+					     double[] weights) {
+
+    if (weights.length != numInstances()) {
+      throw new IllegalArgumentException("weights.length != numInstances.");
+    }
+    Instances newData = new Instances(this, numInstances());
+    double[] probabilities = new double[numInstances()];
+    double sumProbs = 0, sumOfWeights = Utils.sum(weights);
+    for (int i = 0; i < numInstances(); i++) {
+      sumProbs += random.nextDouble();
+      probabilities[i] = sumProbs;
+    }
+    Utils.normalize(probabilities, sumProbs / sumOfWeights);
+
+    // Make sure that rounding errors don't mess things up
+    probabilities[numInstances() - 1] = sumOfWeights;
+    int k = 0; int l = 0;
+    sumProbs = 0;
+    while ((k < numInstances() && (l < numInstances()))) {
+      if (weights[l] < 0) {
+	throw new IllegalArgumentException("Weights have to be positive.");
+      }
+      sumProbs += weights[l];
+      while ((k < numInstances()) &&
+	     (probabilities[k] <= sumProbs)) { 
+	newData.add(instance(l));
+	newData.instance(k).setWeight(1);
+	k++;
+      }
+      l++;
+    }
+    return newData;
+  }
+
+  /** 
+   * Sets the class attribute.
+   *
+   * @param att attribute to be the class
+   */
+  public final void setClass(Attribute att) {
+
+    m_ClassIndex = att.index();
+  }
+
+  /** 
+   * Sets the class index of the set.
+   * If the class index is negative there is assumed to be no class.
+   * (ie. it is undefined)
+   *
+   * @param classIndex the new class index
+   * @exception IllegalArgumentException if the class index is too big or < 0
+   */
+  public final void setClassIndex(int classIndex) {
+
+    if (classIndex >= numAttributes()) {
+      throw new IllegalArgumentException("Invalid class index: " + classIndex);
+    }
+    m_ClassIndex = classIndex;
+  }
+
+  /**
+   * Sets the relation's name.
+   *
+   * @param newName the new relation name.
+   */
+  public final void setRelationName(String newName) {
+    
+    m_RelationName = newName;
+  }
+
+  /**
+   * Sorts the instances based on an attribute. For numeric attributes, 
+   * instances are sorted in ascending order. For nominal attributes, 
+   * instances are sorted based on the attribute label ordering 
+   * specified in the header. Instances with missing values for the 
+   * attribute are placed at the end of the dataset.
+   *
+   * @param attIndex the attribute's index
+   */
+  public final void sort(int attIndex) {
+
+    int i,j;
+
+    // move all instances with missing values to end
+    j = numInstances() - 1;
+    i = 0;
+    while (i <= j) {
+      if (instance(j).isMissing(attIndex)) {
+	j--;
+      } else {
+	if (instance(i).isMissing(attIndex)) {
+	  swap(i,j);
+	  j--;
+	}
+	i++;
+      }
+    }
+    quickSort(attIndex, 0, j);
+  }
+
+  /**
+   * Sorts the instances based on an attribute. For numeric attributes, 
+   * instances are sorted into ascending order. For nominal attributes, 
+   * instances are sorted based on the attribute label ordering 
+   * specified in the header. Instances with missing values for the 
+   * attribute are placed at the end of the dataset.
+   *
+   * @param att the attribute
+   */
+  public final void sort(Attribute att) {
+
+    sort(att.index());
+  }
+
+  /**
+   * Stratifies a set of instances according to its class values 
+   * if the class attribute is nominal (so that afterwards a 
+   * stratified cross-validation can be performed).
+   *
+   * @param numFolds the number of folds in the cross-validation
+   * @exception UnassignedClassException if the class is not set
+   */
+  public final void stratify(int numFolds) {
+    
+    if (numFolds <= 0) {
+      throw new IllegalArgumentException("Number of folds must be greater than 1");
+    }
+    if (m_ClassIndex < 0) {
+      throw new UnassignedClassException("Class index is negative (not set)!");
+    }
+    if (classAttribute().isNominal()) {
+
+      // sort by class
+      int index = 1;
+      while (index < numInstances()) {
+	Instance instance1 = instance(index - 1);
+	for (int j = index; j < numInstances(); j++) {
+	  Instance instance2 = instance(j);
+	  if ((instance1.classValue() == instance2.classValue()) ||
+	      (instance1.classIsMissing() && 
+	       instance2.classIsMissing())) {
+	    swap(index,j);
+	    index++;
+	  }
+	}
+	index++;
+      }
+      stratStep(numFolds);
+    }
+  }
+ 
+  /**
+   * Computes the sum of all the instances' weights.
+   *
+   * @return the sum of all the instances' weights as a double
+   */
+  public final double sumOfWeights() {
+    
+    double sum = 0;
+
+    for (int i = 0; i < numInstances(); i++) {
+      sum += instance(i).weight();
+    }
+    return sum;
+  }
+
+  /**
+   * Creates the test set for one fold of a cross-validation on 
+   * the dataset.
+   *
+   * @param numFolds the number of folds in the cross-validation. Must
+   * be greater than 1.
+   * @param numFold 0 for the first fold, 1 for the second, ...
+   * @return the test set as a set of weighted instances
+   * @exception IllegalArgumentException if the number of folds is less than 2
+   * or greater than the number of instances.
+   */
+  public Instances testCV(int numFolds, int numFold) {
+
+    int numInstForFold, first, offset;
+    Instances test;
+    
+    if (numFolds < 2) {
+      throw new IllegalArgumentException("Number of folds must be at least 2!");
+    }
+    if (numFolds > numInstances()) {
+      throw new IllegalArgumentException("Can't have more folds than instances!");
+    }
+    numInstForFold = numInstances() / numFolds;
+    if (numFold < numInstances() % numFolds){
+      numInstForFold++;
+      offset = numFold;
+    }else
+      offset = numInstances() % numFolds;
+    test = new Instances(this, numInstForFold);
+    first = numFold * (numInstances() / numFolds) + offset;
+    copyInstances(first, test, numInstForFold);
+    return test;
+  }
+ 
+  /**
+   * Returns the dataset as a string in ARFF format. Strings
+   * are quoted if they contain whitespace characters, or if they
+   * are a question mark.
+   *
+   * @return the dataset in ARFF format as a string
+   */
+  public final String toString() {
+    
+    StringBuffer text = new StringBuffer();
+    
+    text.append("@relation " + Utils.quote(m_RelationName) + "\n\n");
+    for (int i = 0; i < numAttributes(); i++) {
+      text.append(attribute(i) + "\n");
+    }
+    text.append("\n@data\n");
+    for (int i = 0; i < numInstances(); i++) {
+      text.append(instance(i));
+      if (i < numInstances() - 1) {
+	text.append('\n');
+      }
+    }
+    return text.toString();
+  }
+
+  /**
+   * Creates the training set for one fold of a cross-validation 
+   * on the dataset.
+   *
+   * @param numFolds the number of folds in the cross-validation. Must
+   * be greater than 1.
+   * @param numFold 0 for the first fold, 1 for the second, ...
+   * @return the training set as a set of weighted 
+   * instances
+   * @exception IllegalArgumentException if the number of folds is less than 2
+   * or greater than the number of instances.
+   */
+  public Instances trainCV(int numFolds, int numFold) {
+
+    int numInstForFold, first, offset;
+    Instances train;
+ 
+    if (numFolds < 2) {
+      throw new IllegalArgumentException("Number of folds must be at least 2!");
+    }
+    if (numFolds > numInstances()) {
+      throw new IllegalArgumentException("Can't have more folds than instances!");
+    }
+    numInstForFold = numInstances() / numFolds;
+    if (numFold < numInstances() % numFolds) {
+      numInstForFold++;
+      offset = numFold;
+    }else
+      offset = numInstances() % numFolds;
+    train = new Instances(this, numInstances() - numInstForFold);
+    first = numFold * (numInstances() / numFolds) + offset;
+    copyInstances(0, train, first);
+    copyInstances(first + numInstForFold, train,
+		  numInstances() - first - numInstForFold);
+
+    return train;
+  }
+
+  /**
+   * Computes the variance for a numeric attribute.
+   *
+   * @param attIndex the numeric attribute
+   * @return the variance if the attribute is numeric
+   * @exception IllegalArgumentException if the attribute is not numeric
+   */
+  public final double variance(int attIndex) {
+  
+    double sum = 0, sumSquared = 0, sumOfWeights = 0;
+
+    if (!attribute(attIndex).isNumeric()) {
+      throw new IllegalArgumentException("Can't compute variance because attribute is " +
+			  "not numeric!");
+    }
+    for (int i = 0; i < numInstances(); i++) {
+      if (!instance(i).isMissing(attIndex)) {
+	sum += instance(i).weight() * 
+	  instance(i).value(attIndex);
+	sumSquared += instance(i).weight() * 
+	  instance(i).value(attIndex) *
+	  instance(i).value(attIndex);
+	sumOfWeights += instance(i).weight();
+      }
+    }
+    if (Utils.smOrEq(sumOfWeights, 1)) {
+      return 0;
+    }
+    return (sumSquared - (sum * sum / sumOfWeights)) / 
+      (sumOfWeights - 1);
+  }
+
+  /**
+   * Computes the variance for a numeric attribute.
+   *
+   * @param att the numeric attribute
+   * @return the variance if the attribute is numeric
+   * @exception IllegalArgumentException if the attribute is not numeric
+   */
+  public final double variance(Attribute att) {
+    
+    return variance(att.index());
+  }
+  
+  /**
+   * Calculates summary statistics on the values that appear in this
+   * set of instances for a specified attribute.
+   *
+   * @param index the index of the attribute to summarize.
+   * @return an AttributeStats object with it's fields calculated.
+   */
+  public AttributeStats attributeStats(int index) {
+
+    AttributeStats result = new AttributeStats();
+    if (attribute(index).isNominal()) {
+      result.nominalCounts = new int [attribute(index).numValues()];
+    }
+    if (attribute(index).isNumeric()) {
+      result.numericStats = new weka.experiment.Stats();
+    }
+    result.totalCount = numInstances();
+
+    double [] attVals = attributeToDoubleArray(index);
+    int [] sorted = Utils.sort(attVals);
+    int currentCount = 0;
+    double prev = Instance.missingValue();
+    for (int j = 0; j < numInstances(); j++) {
+      Instance current = instance(sorted[j]);
+      if (current.isMissing(index)) {
+	result.missingCount = numInstances() - j;
+	break;
+      }
+      if (Utils.eq(current.value(index), prev)) {
+	currentCount++;
+      } else {
+	result.addDistinct(prev, currentCount);
+	currentCount = 1;
+	prev = current.value(index);
+      }
+    }
+    result.addDistinct(prev, currentCount);
+    result.distinctCount--; // So we don't count "missing" as a value 
+    return result;
+  }
+  
+  /**
+   * Gets the value of all instances in this dataset for a particular
+   * attribute. Useful in conjunction with Utils.sort to allow iterating
+   * through the dataset in sorted order for some attribute.
+   *
+   * @param index the index of the attribute.
+   * @return an array containing the value of the desired attribute for
+   * each instance in the dataset. 
+   */
+  public double [] attributeToDoubleArray(int index) {
+
+    double [] result = new double[numInstances()];
+    for (int i = 0; i < result.length; i++) {
+      result[i] = instance(i).value(index);
+    }
+    return result;
+  }
+
+  /**
+   * Generates a string summarizing the set of instances. Gives a breakdown
+   * for each attribute indicating the number of missing/discrete/unique
+   * values and other information.
+   *
+   * @return a string summarizing the dataset
+   */
+  public String toSummaryString() {
+
+    StringBuffer result = new StringBuffer();
+    result.append("Relation Name:  ").append(relationName()).append('\n');
+    result.append("Num Instances:  ").append(numInstances()).append('\n');
+    result.append("Num Attributes: ").append(numAttributes()).append('\n');
+    result.append('\n');
+
+    result.append(Utils.padLeft("", 5)).append(Utils.padRight("Name", 25));
+    result.append(Utils.padLeft("Type", 5)).append(Utils.padLeft("Nom", 5));
+    result.append(Utils.padLeft("Int", 5)).append(Utils.padLeft("Real", 5));
+    result.append(Utils.padLeft("Missing", 12));
+    result.append(Utils.padLeft("Unique", 12));
+    result.append(Utils.padLeft("Dist", 6)).append('\n');
+    for (int i = 0; i < numAttributes(); i++) {
+      Attribute a = attribute(i);
+      AttributeStats as = attributeStats(i);
+      result.append(Utils.padLeft("" + (i + 1), 4)).append(' ');
+      result.append(Utils.padRight(a.name(), 25)).append(' ');
+      long percent;
+      switch (a.type()) {
+      case Attribute.NOMINAL:
+	result.append(Utils.padLeft("Nom", 4)).append(' ');
+	percent = Math.round(100.0 * as.intCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	result.append(Utils.padLeft("" + 0, 3)).append("% ");
+	percent = Math.round(100.0 * as.realCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	break;
+      case Attribute.NUMERIC:
+	result.append(Utils.padLeft("Num", 4)).append(' ');
+	result.append(Utils.padLeft("" + 0, 3)).append("% ");
+	percent = Math.round(100.0 * as.intCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	percent = Math.round(100.0 * as.realCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	break;
+      case Attribute.STRING:
+	result.append(Utils.padLeft("Str", 4)).append(' ');
+	percent = Math.round(100.0 * as.intCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	result.append(Utils.padLeft("" + 0, 3)).append("% ");
+	percent = Math.round(100.0 * as.realCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	break;
+      default:
+	result.append(Utils.padLeft("???", 4)).append(' ');
+	result.append(Utils.padLeft("" + 0, 3)).append("% ");
+	percent = Math.round(100.0 * as.intCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	percent = Math.round(100.0 * as.realCount / as.totalCount);
+	result.append(Utils.padLeft("" + percent, 3)).append("% ");
+	break;
+      }
+      result.append(Utils.padLeft("" + as.missingCount, 5)).append(" /");
+      percent = Math.round(100.0 * as.missingCount / as.totalCount);
+      result.append(Utils.padLeft("" + percent, 3)).append("% ");
+      result.append(Utils.padLeft("" + as.uniqueCount, 5)).append(" /");
+      percent = Math.round(100.0 * as.uniqueCount / as.totalCount);
+      result.append(Utils.padLeft("" + percent, 3)).append("% ");
+      result.append(Utils.padLeft("" + as.distinctCount, 5)).append(' ');
+      result.append('\n');
+    }
+    return result.toString();
+  }
+  
+  /**
+   * Reads a single instance using the tokenizer and appends it
+   * to the dataset. Automatically expands the dataset if it
+   * is not large enough to hold the instance.
+   *
+   * @param tokenizer the tokenizer to be used
+   * @param flag if method should test for carriage return after 
+   * each instance
+   * @return false if end of file has been reached
+   * @exception IOException if the information is not read 
+   * successfully
+   */ 
+  protected boolean getInstance(StreamTokenizer tokenizer, 
+				boolean flag) 
+       throws IOException {
+    
+    // Check if any attributes have been declared.
+    if (m_Attributes.size() == 0) {
+      errms(tokenizer,"no header information available");
+    }
+
+    // Check if end of file reached.
+    getFirstToken(tokenizer);
+    if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
+      return false;
+    }
+    
+    // Parse instance
+    if (tokenizer.ttype == '{') {
+      return getInstanceSparse(tokenizer, flag);
+    } else {
+      return getInstanceFull(tokenizer, flag);
+    }
+  }
+
+  /**
+   * Reads a single instance using the tokenizer and appends it
+   * to the dataset. Automatically expands the dataset if it
+   * is not large enough to hold the instance.
+   *
+   * @param tokenizer the tokenizer to be used
+   * @param flag if method should test for carriage return after 
+   * each instance
+   * @return false if end of file has been reached
+   * @exception IOException if the information is not read 
+   * successfully
+   */ 
+  protected boolean getInstanceSparse(StreamTokenizer tokenizer, 
+				      boolean flag) 
+       throws IOException {
+
+    int valIndex, numValues = 0, maxIndex = -1;
+    
+    // Get values
+    do {
+      
+      // Get index
+      getIndex(tokenizer);
+      if (tokenizer.ttype == '}') {
+	break;
+      }
+       
+      // Is index valid?
+      try{
+	m_IndicesBuffer[numValues] = Integer.valueOf(tokenizer.sval).intValue();
+      } catch (NumberFormatException e) {
+	errms(tokenizer,"index number expected");
+      }
+      if (m_IndicesBuffer[numValues] <= maxIndex) {
+	errms(tokenizer,"indices have to be ordered");
+      }
+      if ((m_IndicesBuffer[numValues] < 0) || 
+	  (m_IndicesBuffer[numValues] >= numAttributes())) {
+	errms(tokenizer,"index out of bounds");
+      }
+      maxIndex = m_IndicesBuffer[numValues];
+
+      // Get value;
+      getNextToken(tokenizer);
+
+      // Check if value is missing.
+      if  (tokenizer.ttype == '?') {
+	m_ValueBuffer[numValues] = Instance.missingValue();
+      } else {
+
+	// Check if token is valid.
+	if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
+	  errms(tokenizer,"not a valid value");
+	}
+	if (attribute(m_IndicesBuffer[numValues]).isNominal()) {
+	  
+	  // Check if value appears in header.
+	  valIndex = 
+	    attribute(m_IndicesBuffer[numValues]).indexOfValue(tokenizer.sval);
+	  if (valIndex == -1) {
+	    errms(tokenizer,"nominal value not declared in header");
+	  }
+	  m_ValueBuffer[numValues] = (double)valIndex;
+	} else if (attribute(m_IndicesBuffer[numValues]).isNumeric()) {
+	  
+	  // Check if value is really a number.
+	  try{
+	    m_ValueBuffer[numValues] = Double.valueOf(tokenizer.sval).
+	      doubleValue();
+	  } catch (NumberFormatException e) {
+	    errms(tokenizer,"number expected");
+	  }
+	} else { 
+	  m_ValueBuffer[numValues] = 
+	    attribute(m_IndicesBuffer[numValues]).addStringValue(tokenizer.sval);
+	}
+      }
+      numValues++;
+    } while (true);
+    if (flag) {
+      getLastToken(tokenizer,true);
+    }
+      
+    // Add instance to dataset
+    double[] tempValues = new double[numValues];
+    int[] tempIndices = new int[numValues];
+    System.arraycopy(m_ValueBuffer, 0, tempValues, 0, numValues);
+    System.arraycopy(m_IndicesBuffer, 0, tempIndices, 0, numValues);
+    add(new SparseInstance(1, tempValues, tempIndices, numAttributes()));
+    return true;
+  }
+
+  /**
+   * Reads a single instance using the tokenizer and appends it
+   * to the dataset. Automatically expands the dataset if it
+   * is not large enough to hold the instance.
+   *
+   * @param tokenizer the tokenizer to be used
+   * @param flag if method should test for carriage return after 
+   * each instance
+   * @return false if end of file has been reached
+   * @exception IOException if the information is not read 
+   * successfully
+   */ 
+  protected boolean getInstanceFull(StreamTokenizer tokenizer, 
+				    boolean flag) 
+       throws IOException {
+
+    double[] instance = new double[numAttributes()];
+    int index;
+    
+    // Get values for all attributes.
+    for (int i = 0; i < numAttributes(); i++){
+      
+      // Get next token
+      if (i > 0) {
+	getNextToken(tokenizer);
+      }
+            
+      // Check if value is missing.
+      if  (tokenizer.ttype == '?') {
+	instance[i] = Instance.missingValue();
+      } else {
+
+	// Check if token is valid.
+	if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
+	  errms(tokenizer,"not a valid value");
+	}
+	if (attribute(i).isNominal()) {
+	  
+	  // Check if value appears in header.
+	  index = attribute(i).indexOfValue(tokenizer.sval);
+	  if (index == -1) {
+	    errms(tokenizer,"nominal value not declared in header");
+	  }
+	  instance[i] = (double)index;
+	} else if (attribute(i).isNumeric()) {
+	  
+	  // Check if value is really a number.
+	  try{
+	    instance[i] = Double.valueOf(tokenizer.sval).
+	      doubleValue();
+	  } catch (NumberFormatException e) {
+	    errms(tokenizer,"number expected");
+	  }
+	} else { 
+	  instance[i] = attribute(i).addStringValue(tokenizer.sval);
+	}
+      }
+    }
+    if (flag) {
+      getLastToken(tokenizer,true);
+    }
+      
+    // Add instance to dataset
+    add(new Instance(1, instance));
+    return true;
+  }
+
+  /**
+   * Reads and stores header of an ARFF file.
+   *
+   * @param tokenizer the stream tokenizer
+   * @exception IOException if the information is not read 
+   * successfully
+   */ 
+  protected void readHeader(StreamTokenizer tokenizer) 
+     throws IOException{
+    
+    String attributeName;
+    FastVector attributeValues;
+    int i;
+
+    // Get name of relation.
+    getFirstToken(tokenizer);
+    if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
+      errms(tokenizer,"premature end of file");
+    }
+    if (tokenizer.sval.equalsIgnoreCase("@relation")){
+      getNextToken(tokenizer);
+      m_RelationName = tokenizer.sval;
+      getLastToken(tokenizer,false);
+    } else {
+      errms(tokenizer,"keyword @relation expected");
+    }
+
+    // Create vectors to hold information temporarily.
+    m_Attributes = new FastVector();
+ 
+    // Get attribute declarations.
+    getFirstToken(tokenizer);
+    if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
+      errms(tokenizer,"premature end of file");
+    }
+    while (tokenizer.sval.equalsIgnoreCase("@attribute")) {
+
+      // Get attribute name.
+      getNextToken(tokenizer);
+      attributeName = tokenizer.sval;
+      getNextToken(tokenizer);
+
+      // Check if attribute is nominal.
+      if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
+
+	// Attribute is real, integer, or string.
+	if (tokenizer.sval.equalsIgnoreCase("real") ||
+	    tokenizer.sval.equalsIgnoreCase("integer") ||
+	    tokenizer.sval.equalsIgnoreCase("numeric")) {
+	  m_Attributes.addElement(new Attribute(attributeName,
+						 numAttributes()));
+	  readTillEOL(tokenizer);
+	} else if (tokenizer.sval.equalsIgnoreCase("string")) {
+	  m_Attributes.
+	    addElement(new Attribute(attributeName, null,
+				     numAttributes()));
+	  readTillEOL(tokenizer);
+	} else {
+	  errms(tokenizer,"no valid attribute type or invalid "+
+		"enumeration");
+	}
+      } else {
+
+	// Attribute is nominal.
+	attributeValues = new FastVector();
+	tokenizer.pushBack();
+	
+	// Get values for nominal attribute.
+	if (tokenizer.nextToken() != '{') {
+	  errms(tokenizer,"{ expected at beginning of enumeration");
+	}
+	while (tokenizer.nextToken() != '}') {
+	  if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
+	    errms(tokenizer,"} expected at end of enumeration");
+	  } else {
+	    attributeValues.addElement(tokenizer.sval);
+	  }
+	}
+	if (attributeValues.size() == 0) {
+	  errms(tokenizer,"no nominal values found");
+	}
+	m_Attributes.
+	  addElement(new Attribute(attributeName, attributeValues,
+				   numAttributes()));
+      }
+      getLastToken(tokenizer,false);
+      getFirstToken(tokenizer);
+      if (tokenizer.ttype == StreamTokenizer.TT_EOF)
+	errms(tokenizer,"premature end of file");
+    }
+
+    // Check if data part follows. We can't easily check for EOL.
+    if (!tokenizer.sval.equalsIgnoreCase("@data")) {
+      errms(tokenizer,"keyword @data expected");
+    }
+    
+    // Check if any attributes have been declared.
+    if (m_Attributes.size() == 0) {
+      errms(tokenizer,"no attributes declared");
+    }
+
+    // Allocate buffers in case sparse instances have to be read
+    m_ValueBuffer = new double[numAttributes()];
+    m_IndicesBuffer = new int[numAttributes()];
+  }
+
+  /**
+   * Copies instances from one set to the end of another 
+   * one.
+   *
+   * @param source the source of the instances
+   * @param from the position of the first instance to be copied
+   * @param dest the destination for the instances
+   * @param num the number of instances to be copied
+   */
+  private void copyInstances(int from, Instances dest, int num) {
+    
+    for (int i = 0; i < num; i++) {
+      dest.add(instance(from + i));
+    }
+  }
+  
+  /**
+   * Throws error message with line number and last token read.
+   *
+   * @param theMsg the error message to be thrown
+   * @param tokenizer the stream tokenizer
+   * @throws IOExcpetion containing the error message
+   */
+  private void errms(StreamTokenizer tokenizer, String theMsg) 
+       throws IOException {
+    
+    throw new IOException(theMsg + ", read " + tokenizer.toString());
+  }
+  
+  /**
+   * Replaces the attribute information by a clone of
+   * itself.
+   */
+  private void freshAttributeInfo() {
+
+    m_Attributes = (FastVector) m_Attributes.copyElements();
+  }
+
+  /**
+   * Gets next token, skipping empty lines.
+   *
+   * @param tokenizer the stream tokenizer
+   * @exception IOException if reading the next token fails
+   */
+  private void getFirstToken(StreamTokenizer tokenizer) 
+    throws IOException{
+    
+    while (tokenizer.nextToken() == StreamTokenizer.TT_EOL){};
+    if ((tokenizer.ttype == '\'') ||
+	(tokenizer.ttype == '"')) {
+      tokenizer.ttype = StreamTokenizer.TT_WORD;
+    } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
+	       (tokenizer.sval.equals("?"))){
+      tokenizer.ttype = '?';
+    }
+  }
+
+  /**
+   * Gets index, checking for a premature and of line.
+   *
+   * @param tokenizer the stream tokenizer
+   * @exception IOException if it finds a premature end of line
+   */
+  private void getIndex(StreamTokenizer tokenizer) throws IOException{
+    
+    if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
+      errms(tokenizer,"premature end of line");
+    }
+    if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
+      errms(tokenizer,"premature end of file");
+    }
+  }
+  
+  /**
+   * Gets token and checks if its end of line.
+   *
+   * @param tokenizer the stream tokenizer
+   * @exception IOException if it doesn't find an end of line
+   */
+  private void getLastToken(StreamTokenizer tokenizer, boolean endOfFileOk) 
+       throws IOException{
+
+    if ((tokenizer.nextToken() != StreamTokenizer.TT_EOL) &&
+	((tokenizer.nextToken() != StreamTokenizer.TT_EOF) || !endOfFileOk)) {
+      errms(tokenizer,"end of line expected");
+    }
+  }
+
+  /**
+   * Gets next token, checking for a premature and of line.
+   *
+   * @param tokenizer the stream tokenizer
+   * @exception IOException if it finds a premature end of line
+   */
+  private void getNextToken(StreamTokenizer tokenizer) 
+       throws IOException{
+    
+    if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
+      errms(tokenizer,"premature end of line");
+    }
+    if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
+      errms(tokenizer,"premature end of file");
+    } else if ((tokenizer.ttype == '\'') ||
+	       (tokenizer.ttype == '"')) {
+      tokenizer.ttype = StreamTokenizer.TT_WORD;
+    } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
+	       (tokenizer.sval.equals("?"))){
+      tokenizer.ttype = '?';
+    }
+  }
+	
+  /**
+   * Initializes the StreamTokenizer used for reading the ARFF file.
+   *
+   * @param tokenizer the stream tokenizer
+   */
+  private void initTokenizer(StreamTokenizer tokenizer){
+
+    tokenizer.resetSyntax();         
+    tokenizer.whitespaceChars(0, ' ');    
+    tokenizer.wordChars(' '+1,'\u00FF');
+    tokenizer.whitespaceChars(',',',');
+    tokenizer.commentChar('%');
+    tokenizer.quoteChar('"');
+    tokenizer.quoteChar('\'');
+    tokenizer.ordinaryChar('{');
+    tokenizer.ordinaryChar('}');
+    tokenizer.eolIsSignificant(true);
+  }
+ 
+  /**
+   * Returns string including all instances, their weights and
+   * their indices in the original dataset.
+   *
+   * @return description of instance and its weight as a string
+   */
+  private String instancesAndWeights(){
+
+    StringBuffer text = new StringBuffer();
+
+    for (int i = 0; i < numInstances(); i++) {
+      text.append(instance(i) + " " + instance(i).weight());
+      if (i < numInstances() - 1) {
+	text.append("\n");
+      }
+    }
+    return text.toString();
+  }
+  
+  /**
+   * Implements quicksort.
+   *
+   * @param attIndex the attribute's index
+   * @param lo0 the first index of the subset to be sorted
+   * @param hi0 the last index of the subset to be sorted
+   */
+  private void quickSort(int attIndex, int lo0, int hi0) {
+    
+    int lo = lo0, hi = hi0;
+    double mid, midPlus, midMinus;
+    
+    if (hi0 > lo0) {
+      
+      // Arbitrarily establishing partition element as the 
+      // midpoint of the array.
+      mid = instance((lo0 + hi0) / 2).value(attIndex);
+      midPlus = mid + 1e-6;
+      midMinus = mid - 1e-6;
+
+      // loop through the array until indices cross
+      while(lo <= hi) {
+	
+	// find the first element that is greater than or equal to 
+	// the partition element starting from the left Index.
+	while ((instance(lo).value(attIndex) < 
+		midMinus) && (lo < hi0)) {
+	  ++lo;
+	}
+	
+	// find an element that is smaller than or equal to
+	// the partition element starting from the right Index.
+	while ((instance(hi).value(attIndex)  > 
+		midPlus) && (hi > lo0)) {
+	  --hi;
+	}
+	
+	// if the indexes have not crossed, swap
+	if(lo <= hi) {
+	  swap(lo,hi);
+	  ++lo;
+	  --hi;
+	}
+      }
+      
+      // If the right index has not reached the left side of array
+      // must now sort the left partition.
+      if(lo0 < hi) {
+	quickSort(attIndex,lo0,hi);
+      }
+      
+      // If the left index has not reached the right side of array
+      // must now sort the right partition.
+      if(lo < hi0) {
+	quickSort(attIndex,lo,hi0);
+      }
+    }
+  }
+
+  /**
+   * Reads and skips all tokens before next end of line token.
+   *
+   * @param tokenizer the stream tokenizer
+   */
+  private void readTillEOL(StreamTokenizer tokenizer) 
+       throws IOException{
+    
+    while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {};
+    tokenizer.pushBack();
+  }
+
+  /**
+   * Help function needed for stratification of set.
+   *
+   * @param numFolds the number of folds for the stratification
+   */
+  private void stratStep (int numFolds){
+    
+    FastVector newVec = new FastVector(m_Instances.capacity());
+    int start = 0, j;
+
+    // create stratified batch
+    while (newVec.size() < numInstances()) {
+      j = start;
+      while (j < numInstances()) {
+	newVec.addElement(instance(j));
+	j = j + numFolds;
+      }
+      start++;
+    }
+    m_Instances = newVec;
+  }
+  
+  /**
+   * Swaps two instances in the set.
+   *
+   * @param i the first instance's index
+   * @param j the second instance's index
+   */
+  private void swap(int i, int j){
+    
+    m_Instances.swap(i, j);
+  }
+
+  /**
+   * Merges two sets of Instances together. The resulting set will have
+   * all the attributes of the first set plus all the attributes of the 
+   * second set. The number of instances in both sets must be the same.
+   *
+   * @param first the first set of Instances
+   * @param second the second set of Instances
+   * @return the merged set of Instances
+   * @exception IllegalArgumentException if the datasets are not the same size
+   */
+  public static Instances mergeInstances(Instances first, Instances second) {
+
+    if (first.numInstances() != second.numInstances()) {
+      throw new IllegalArgumentException("Instance sets must be of the same size");
+    }
+
+    // Create the vector of merged attributes
+    FastVector newAttributes = new FastVector();
+    for (int i = 0; i < first.numAttributes(); i++) {
+      newAttributes.addElement(first.attribute(i));
+    }
+    for (int i = 0; i < second.numAttributes(); i++) {
+      newAttributes.addElement(second.attribute(i));
+    }
+    
+    // Create the set of Instances
+    Instances merged = new Instances(first.relationName() + '_'
+				     + second.relationName(), 
+				     newAttributes, 
+				     first.numInstances());
+    // Merge each instance
+    for (int i = 0; i < first.numInstances(); i++) {
+      merged.add(first.instance(i).mergeInstance(second.instance(i)));
+    }
+    return merged;
+  }
+
+  /**
+   * Method for testing this class.
+   *
+   * @param argv should contain one element: the name of an ARFF file
+   */
+  public static void test(String [] argv) {
+
+    Instances instances, secondInstances, train, test, transformed, empty;
+    Instance instance;
+    Random random = new Random(2);
+    Reader reader;
+    int start, num;
+    double newWeight;
+    FastVector testAtts, testVals;
+    int i,j;
+    
+    try{
+      if (argv.length > 1) {
+	throw (new Exception("Usage: Instances [<filename>]"));
+      }
+      
+      // Creating set of instances from scratch
+      testVals = new FastVector(2);
+      testVals.addElement("first_value");
+      testVals.addElement("second_value");
+      testAtts = new FastVector(2);
+      testAtts.addElement(new Attribute("nominal_attribute", testVals));
+      testAtts.addElement(new Attribute("numeric_attribute"));
+      instances = new Instances("test_set", testAtts, 10);
+      instances.add(new Instance(instances.numAttributes()));
+      instances.add(new Instance(instances.numAttributes()));
+      instances.add(new Instance(instances.numAttributes()));
+      instances.setClassIndex(0);
+      System.out.println("\nSet of instances created from scratch:\n");
+      System.out.println(instances);
+      
+      if (argv.length == 1) {
+	String filename = argv[0];
+	reader = new FileReader(filename);
+	
+	// Read first five instances and print them
+	System.out.println("\nFirst five instances from file:\n");
+	instances = new Instances(reader, 1);
+	instances.setClassIndex(instances.numAttributes() - 1);
+	i = 0;
+	while ((i < 5) && (instances.readInstance(reader))) {
+	  i++;
+	}
+	System.out.println(instances);
+
+	// Read all the instances in the file
+	reader = new FileReader(filename);
+	instances = new Instances(reader);
+
+	// Make the last attribute be the class 
+	instances.setClassIndex(instances.numAttributes() - 1);
+	
+	// Print header and instances.
+	System.out.println("\nDataset:\n");
+	System.out.println(instances);
+	System.out.println("\nClass index: "+instances.classIndex());
+      }
+      
+      // Test basic methods based on class index.
+      System.out.println("\nClass name: "+instances.classAttribute().name());
+      System.out.println("\nClass index: "+instances.classIndex());
+      System.out.println("\nClass is nominal: " +
+			 instances.classAttribute().isNominal());
+      System.out.println("\nClass is numeric: " +
+			 instances.classAttribute().isNumeric());
+      System.out.println("\nClasses:\n");
+      for (i = 0; i < instances.numClasses(); i++) {
+	System.out.println(instances.classAttribute().value(i));
+      }
+      System.out.println("\nClass values and labels of instances:\n");
+      for (i = 0; i < instances.numInstances(); i++) {
+	Instance inst = instances.instance(i);
+	System.out.print(inst.classValue() + "\t");
+	System.out.print(inst.toString(inst.classIndex()));
+	if (instances.instance(i).classIsMissing()) {
+	  System.out.println("\tis missing");
+	} else {
+	  System.out.println();
+	}
+      }
+      
+      // Create random weights.
+      System.out.println("\nCreating random weights for instances.");
+      for (i = 0; i < instances.numInstances(); i++) {
+	instances.instance(i).setWeight(random.nextDouble()); 
+      }
+      
+      // Print all instances and their weights (and the sum of weights).
+      System.out.println("\nInstances and their weights:\n");
+      System.out.println(instances.instancesAndWeights());
+      System.out.print("\nSum of weights: ");
+      System.out.println(instances.sumOfWeights());
+      
+      // Insert an attribute
+      secondInstances = new Instances(instances);
+      Attribute testAtt = new Attribute("Inserted");
+      secondInstances.insertAttributeAt(testAtt, 0);
+      System.out.println("\nSet with inserted attribute:\n");
+      System.out.println(secondInstances);
+      System.out.println("\nClass name: "
+			 + secondInstances.classAttribute().name());
+      
+      // Delete the attribute
+      secondInstances.deleteAttributeAt(0);
+      System.out.println("\nSet with attribute deleted:\n");
+      System.out.println(secondInstances);
+      System.out.println("\nClass name: "
+			 + secondInstances.classAttribute().name());
+      
+      // Test if headers are equal
+      System.out.println("\nHeaders equal: "+
+			 instances.equalHeaders(secondInstances) + "\n");
+      
+      // Print data in internal format.
+      System.out.println("\nData (internal values):\n");
+      for (i = 0; i < instances.numInstances(); i++) {
+	for (j = 0; j < instances.numAttributes(); j++) {
+	  if (instances.instance(i).isMissing(j)) {
+	    System.out.print("? ");
+	  } else {
+	    System.out.print(instances.instance(i).value(j) + " ");
+	  }
+	}
+	System.out.println();
+      }
+      
+      // Just print header
+      System.out.println("\nEmpty dataset:\n");
+      empty = new Instances(instances, 0);
+      System.out.println(empty);
+      System.out.println("\nClass name: "+empty.classAttribute().name());
+
+      // Create copy and rename an attribute and a value (if possible)
+      if (empty.classAttribute().isNominal()) {
+	Instances copy = new Instances(empty, 0);
+	copy.renameAttribute(copy.classAttribute(), "new_name");
+	copy.renameAttributeValue(copy.classAttribute(), 
+				  copy.classAttribute().value(0), 
+				  "new_val_name");
+	System.out.println("\nDataset with names changed:\n" + copy);
+	System.out.println("\nOriginal dataset:\n" + empty);
+      }
+
+      // Create and prints subset of instances.
+      start = instances.numInstances() / 4;
+      num = instances.numInstances() / 2;
+      System.out.print("\nSubset of dataset: ");
+      System.out.println(num + " instances from " + (start + 1) 
+			 + ". instance");
+      secondInstances = new Instances(instances, start, num);
+      System.out.println("\nClass name: "
+			 + secondInstances.classAttribute().name());
+
+      // Print all instances and their weights (and the sum of weights).
+      System.out.println("\nInstances and their weights:\n");
+      System.out.println(secondInstances.instancesAndWeights());
+      System.out.print("\nSum of weights: ");
+      System.out.println(secondInstances.sumOfWeights());
+      
+      // Create and print training and test sets for 3-fold
+      // cross-validation.
+      System.out.println("\nTrain and test folds for 3-fold CV:");
+      if (instances.classAttribute().isNominal()) {
+	instances.stratify(3);
+      }
+      for (j = 0; j < 3; j++) {
+        train = instances.trainCV(3,j);
+	test = instances.testCV(3,j);
+                      
+	// Print all instances and their weights (and the sum of weights).
+	System.out.println("\nTrain: ");
+	System.out.println("\nInstances and their weights:\n");
+	System.out.println(train.instancesAndWeights());
+	System.out.print("\nSum of weights: ");
+	System.out.println(train.sumOfWeights());
+	System.out.println("\nClass name: "+train.classAttribute().name());
+	System.out.println("\nTest: ");
+	System.out.println("\nInstances and their weights:\n");
+	System.out.println(test.instancesAndWeights());
+	System.out.print("\nSum of weights: ");
+	System.out.println(test.sumOfWeights());
+	System.out.println("\nClass name: "+test.classAttribute().name());
+      }
+
+      // Randomize instances and print them.
+      System.out.println("\nRandomized dataset:");
+      instances.randomize(random);
+      
+      // Print all instances and their weights (and the sum of weights).
+      System.out.println("\nInstances and their weights:\n");
+      System.out.println(instances.instancesAndWeights());
+      System.out.print("\nSum of weights: ");
+      System.out.println(instances.sumOfWeights());
+
+      // Sort instances according to first attribute and
+      // print them.
+      System.out.print("\nInstances sorted according to first attribute:\n ");
+      instances.sort(0);
+        
+      // Print all instances and their weights (and the sum of weights).
+      System.out.println("\nInstances and their weights:\n");
+      System.out.println(instances.instancesAndWeights());
+      System.out.print("\nSum of weights: ");
+      System.out.println(instances.sumOfWeights());
+    } catch (Exception e) {
+      e.printStackTrace(); 
+    }
+  }
+
+  /**
+   * Main method for this class -- just prints a summary of a set
+   * of instances.
+   *
+   * @param argv should contain one element: the name of an ARFF file
+   */
+  public static void main(String [] args) {
+
+    try {
+      Reader r = null;
+      if (args.length > 1) {
+	throw (new Exception("Usage: Instances <filename>"));
+      } else if (args.length == 0) {
+        r = new BufferedReader(new InputStreamReader(System.in));
+      } else {
+        r = new BufferedReader(new FileReader(args[0]));
+      }
+      Instances i = new Instances(r);
+      System.out.println(i.toSummaryString());
+    } catch (Exception ex) {
+      System.err.println(ex.getMessage());
+    }
+  }
+}
+
+     
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Option.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Option.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Option.java	(revision 16561)
@@ -0,0 +1,105 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Option.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+/** 
+ * Class to store information about an option. <p>
+ *
+ * Typical usage: <p>
+ *
+ * <code>Option myOption = new Option("Uses extended mode.", "E", 0, "-E")); </code><p>
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Option {
+
+  /** What does this option do? */
+  private String m_Description;
+
+  /** The synopsis. */
+  private String m_Synopsis;
+
+  /** What's the option's name? */
+  private String m_Name;
+
+  /** How many arguments does it take? */
+  private int m_NumArguments;
+
+  /**
+   * Creates new option with the given parameters.
+   *
+   * @String description the option's description
+   * @String name the option's name
+   * @String numArguments the number of arguments
+   */
+  public Option(String description, String name, 
+		int numArguments, String synopsis) {
+  
+    m_Description = description;
+    m_Name = name;
+    m_NumArguments = numArguments;
+    m_Synopsis = synopsis;
+  }
+
+  /**
+   * Returns the option's description.
+   *
+   * @return the option's description
+   */
+  public String description() {
+  
+    return m_Description;
+  }
+
+  /**
+   * Returns the option's name.
+   *
+   * @return the option's name
+   */
+  public String name() {
+
+    return m_Name;
+  }
+
+  /**
+   * Returns the option's number of arguments.
+   *
+   * @return the option's number of arguments
+   */
+  public int numArguments() {
+  
+    return m_NumArguments;
+  }
+
+  /**
+   * Returns the option's synopsis.
+   *
+   * @return the option's synopsis
+   */
+  public String synopsis() {
+  
+    return m_Synopsis;
+  }
+}
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/OptionHandler.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/OptionHandler.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/OptionHandler.java	(revision 16561)
@@ -0,0 +1,67 @@
+/*
+ *    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.
+ */
+
+/*
+ *    OptionHandler.java
+ *    Copyright (C) 1999 Eibe Frank,Len Trigg
+ *
+ */
+
+package weka.core;
+
+import java.util.*;
+
+/** 
+ * Interface to something that understands options.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public interface OptionHandler {
+
+  /**
+   * Returns an enumeration of all the available options.
+   *
+   * @return an enumeration of all available options
+   */
+  public Enumeration listOptions();
+
+  /**
+   * Sets the OptionHandler's options using the given list. All options
+   * will be set (or reset) during this call (i.e. incremental setting
+   * of options is not possible).
+   *
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception;
+
+  /**
+   * Gets the current option settings for the OptionHandler.
+   *
+   * @return the list of current option settings as an array of strings
+   */
+  public String[] getOptions();
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Queue.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Queue.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Queue.java	(revision 16561)
@@ -0,0 +1,229 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Queue.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+package weka.core;
+
+import java.io.*;
+
+/** 
+ * Class representing a FIFO queue.
+ *
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Queue extends Object implements Serializable {
+
+  /**
+   * Represents one node in the queue.
+   */
+  protected class QueueNode implements Serializable {
+
+    /** The next node in the queue */
+    protected QueueNode m_Next;
+
+    /** The nodes contents */
+    protected Object m_Contents;
+
+    /** 
+     * Creates a queue node with the given contents 
+     */
+    public QueueNode(Object contents) {
+
+      m_Contents = contents;
+      next(null);
+    }
+
+    /**
+     * Sets the next node in the queue, and returns it.
+     */
+    public QueueNode next(QueueNode next) {
+
+      return m_Next = next;
+    }
+
+    /**
+     * Gets the next node in the queue. 
+     */
+    public QueueNode next() {
+
+      return m_Next;
+    }
+
+    /**
+     * Sets the contents of the node.
+     */
+    public Object contents(Object contents) {
+
+      return m_Contents = contents;
+    }
+
+    /**
+     * Returns the contents in the node.
+     */
+    public Object contents() {
+
+      return m_Contents;
+    }
+  }
+
+  /** Store a reference to the head of the queue */
+  protected QueueNode m_Head = null;
+
+  /** Store a reference to the tail of the queue */
+  protected QueueNode m_Tail = null;
+
+  /** Store the current number of elements in the queue */
+  protected int m_Size = 0;
+
+  /**
+   * Removes all objects from the queue.
+   */
+  public final synchronized void removeAllElements() {
+
+    m_Size = 0;
+    m_Head = null;
+    m_Tail = null;
+  }
+
+  /**
+   * Appends an object to the back of the queue.
+   *
+   * @param item the object to be appended
+   * @return the object appended
+   */
+  public synchronized Object push(Object item) {
+
+    QueueNode newNode = new QueueNode(item);
+    if (m_Head == null) {
+      m_Head = m_Tail = newNode;
+    } else {
+      m_Tail = m_Tail.next(newNode);
+    }
+    m_Size++;
+    return item;
+  }
+
+  /**
+   * Pops an object from the front of the queue.
+   *
+   * @return the object at the front of the queue
+   * @exception RuntimeException if the queue is empty
+   */
+  public synchronized Object pop() {
+
+    if (m_Head == null) {
+      throw new RuntimeException("Queue is empty");
+    }
+    Object retval = m_Head.contents();
+    m_Size--;
+    m_Head = m_Head.next();
+    if (m_Head == null) {
+      m_Tail = null;
+    }
+    return retval;
+  }
+
+  /**
+   * Gets object from the front of the queue.
+   *
+   * @return the object at the front of the queue
+   * @exception RuntimeException if the queue is empty
+   */
+  public synchronized Object peek() {
+    
+    if (m_Head == null) {
+      throw new RuntimeException("Queue is empty");
+    }
+    return m_Head.contents();
+  }
+
+  /**
+   * Checks if queue is empty.
+   * 
+   * @return true if queue is empty
+   */
+  public boolean empty() {
+
+    return (m_Head == null);
+  }
+
+  /** 
+   * Gets queue's size.
+   *
+   * @return size of queue
+   */
+  public int size() {
+
+    return m_Size;
+  }
+
+  /**
+   * Produces textual description of queue.
+   *
+   * @return textual description of queue
+   */
+  public String toString() {
+
+    String retval = "Queue Contents "+m_Size+" elements\n";
+    QueueNode current = m_Head;
+    if (current == null) {
+      return retval + "Empty\n";
+    } else {
+      while (current != null) {
+	retval += current.contents().toString()+"\n";
+	current = current.next();
+      }
+    }
+    return retval;
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv a set of strings that are pushed on a test queue
+   */
+  public static void main(String [] argv) {
+
+    try {
+      Queue queue = new Queue();
+      for(int i = 0; i < argv.length; i++) {
+	queue.push(argv[i]);
+      }
+      System.out.println("After Pushing");
+      System.out.println(queue.toString());
+      System.out.println("Popping...");
+      while (!queue.empty()) {
+	System.out.println(queue.pop().toString());
+      }
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Range.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Range.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Range.java	(revision 16561)
@@ -0,0 +1,439 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Range.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+package weka.core;
+
+import java.io.*;
+import java.util.*;
+
+/** 
+ * Class representing a range of cardinal numbers. The range is set by a 
+ * string representation such as: <P>
+ *
+ * <code>
+ *   all
+ *   first-last
+ *   1,2,3,4
+ * </code> <P>
+ * or combinations thereof. The range is internally converted from
+ * 1-based to 0-based (so methods that set or get numbers not in string
+ * format should use 0-based numbers).
+ *
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Range implements Serializable {
+
+  /** Record the string representations of the columns to delete */
+  Vector m_RangeStrings = new Vector();
+
+  /** Whether matching should be inverted */
+  boolean m_Invert;
+
+  /** The array of flags for whether an column is selected */
+  boolean [] m_SelectFlags;
+
+  /** Store the maximum value permitted in the range. -1 indicates that
+      no upper value has been set */
+  int m_Upper = -1;
+
+  /** Default constructor. */
+  public Range() {
+  }
+
+  /**
+   * Constructor to set initial range.
+   *
+   * @param rangeList the initial range
+   * @exception IllegalArgumentException if the range list is invalid
+   */
+  public Range(String rangeList) {
+
+    setRanges(rangeList);
+  }
+
+  /**
+   * Sets the value of "last".
+   *
+   * @param newUpper the value of "last"
+   */
+  public void setUpper(int newUpper) {
+
+    if (newUpper >= 0) {
+      m_Upper = newUpper;
+      setFlags();
+    }
+  }
+  
+  /**
+   * Gets whether the range sense is inverted, i.e. all <i>except</i>
+   * the values included by the range string are selected.
+   * 
+   * @return whether the matching sense is inverted
+   */
+  public boolean getInvert() {
+
+    return m_Invert;
+  }
+
+  /**
+   * Sets whether the range sense is inverted, i.e. all <i>except</i>
+   * the values included by the range string are selected.
+   * 
+   * @param newSetting true if the matching sense is inverted
+   */
+  public void setInvert(boolean newSetting) {
+
+    m_Invert = newSetting;
+  }
+
+  /**
+   * Gets the string representing the selected range of values
+   *
+   * @return the range selection string
+   */
+  public String getRanges() {
+
+    String result = null;
+    Enumeration enum = m_RangeStrings.elements();
+    while (enum.hasMoreElements()) {
+      if (result == null) {
+	result = (String)enum.nextElement();
+      } else {
+	result += ',' + (String)enum.nextElement();
+      }
+    }
+    return (result == null) ? "" : result;
+  }
+
+  /**
+   * Sets the ranges from a string representation.
+   *
+   * @param rangeList the comma separated list of ranges. The empty
+   * string sets the range to empty.
+   * @exception IllegalArgumentException if the rangeList was not well formed
+   */
+  public void setRanges(String rangeList) {
+
+    Vector ranges = new Vector (10);
+
+    // Split the rangeList up into the vector
+    while (!rangeList.equals("")) {
+      String range = rangeList.trim();
+      int commaLoc = rangeList.indexOf(',');
+      if (commaLoc != -1) {
+	range = rangeList.substring(0, commaLoc).trim();
+	rangeList = rangeList.substring(commaLoc + 1).trim();
+      } else {
+	rangeList = "";
+      }
+      if (!range.equals("")) {
+	if (isValidRange(range)) {
+	  ranges.addElement(range);
+	} else {
+	  throw new IllegalArgumentException("Invalid range list at " + range
+                                             + rangeList);
+	}
+      }
+    }
+    m_RangeStrings = ranges;
+    
+    if (m_Upper >= 0) {
+      setFlags();
+    }
+  }
+
+  /**
+   * Gets whether the supplied cardinal number is included in the current
+   * range.
+   *
+   * @param index the number of interest
+   * @return true if index is in the current range
+   * @exception RuntimeException if the upper limit of the range hasn't been defined
+   */
+  public boolean isInRange(int index) {
+
+    if (m_Upper == -1) {
+      throw new RuntimeException("No upper limit has been specified for range");
+    }
+    if (m_Invert) {
+      return !m_SelectFlags[index];
+    } else {
+      return m_SelectFlags[index];
+    }
+  }
+
+  /**
+   * Constructs a representation of the current range. Being a string
+   * representation, the numbers are based from 1.
+   * 
+   * @return the string representation of the current range
+   */
+  public String toString() {
+
+    if (m_RangeStrings.size() == 0) {
+      return "Empty";
+    }
+    String result ="Strings: ";
+    Enumeration enum = m_RangeStrings.elements();
+    while (enum.hasMoreElements()) {
+      result += (String)enum.nextElement() + " ";
+    }
+    result += "\n";
+
+    result += "Invert: " + m_Invert + "\n";
+
+    try {
+      if (m_Upper == -1) {
+	throw new RuntimeException("Upper limit has not been specified");
+      }
+      String cols = null;
+      for (int i = 0; i < m_SelectFlags.length; i++) {
+	if (isInRange(i)) {
+	  if (cols == null) {
+	    cols = "Cols: " + (i + 1);
+	  } else {
+	    cols += "," + (i + 1);
+	  }
+	}
+      }
+      if (cols != null) {
+	result += cols + "\n";
+      }
+    } catch (Exception ex) {
+      result += ex.getMessage();
+    }
+    return result;
+  }
+
+  /**
+   * Gets an array containing all the selected values, in the order
+   * that they were selected (or ascending order if range inversion is on)
+   *
+   * @return the array of selected values
+   * @exception RuntimeException if the upper limit of the range hasn't been defined
+   */
+  public int [] getSelection() {
+
+    if (m_Upper == -1) {
+      throw new RuntimeException("No upper limit has been specified for range");
+    }
+    int [] selectIndices = new int [m_Upper + 1];
+    int numSelected = 0;
+    if (m_Invert)
+    {
+      for (int i = 0; i <= m_Upper; i++) {
+	if (!m_SelectFlags[i]) {
+	  selectIndices[numSelected++] = i;
+	}
+      }
+    }
+    else
+    {
+      Enumeration enum = m_RangeStrings.elements();
+      while (enum.hasMoreElements()) {
+	String currentRange = (String)enum.nextElement();
+	int start = rangeLower(currentRange);
+	int end = rangeUpper(currentRange);
+	for (int i = start; (i <= m_Upper) && (i <= end); i++) {
+	  if (m_SelectFlags[i]) {
+	    selectIndices[numSelected++] = i;
+	  }
+	}
+      }
+    }
+    int [] result = new int [numSelected];
+    System.arraycopy(selectIndices, 0, result, 0, numSelected);
+    return result;
+  }
+
+  /**
+   * Creates a string representation of the indices in the supplied array.
+   *
+   * @param indices an array containing indices to select.
+   * Since the array will typically come from a program, indices are assumed
+   * from 0, and thus will have 1 added in the String representation.
+   */
+  public static String indicesToRangeList(int []indices) {
+
+    StringBuffer rl = new StringBuffer();
+    int last = -2;
+    boolean range = false;
+    for(int i = 0; i < indices.length; i++) {
+      if (i == 0) {
+	rl.append(indices[i] + 1);
+      } else if (indices[i] == last) {
+        range = true;
+      } else {
+        if (range) {
+          rl.append('-').append(last);
+          range = false;
+        }
+	rl.append(',').append(indices[i] + 1);
+      }
+      last = indices[i] + 1;
+    }
+    if (range) {
+      rl.append('-').append(last);
+    }
+    return rl.toString();
+  }
+
+  /** Sets the flags array. */
+  protected void setFlags() {
+
+    m_SelectFlags = new boolean [m_Upper + 1];
+    Enumeration enum = m_RangeStrings.elements();
+    while (enum.hasMoreElements()) {
+      String currentRange = (String)enum.nextElement();
+      int start = rangeLower(currentRange);
+      int end = rangeUpper(currentRange);
+      for (int i = start; (i <= m_Upper) && (i <= end); i++) {
+	m_SelectFlags[i] = true;
+      }
+    }
+  }
+
+
+  /**
+   * Translates a single string selection into it's internal 0-based equivalent
+   *
+   * @param single the string representing the selection (eg: 1 first last)
+   * @return the number corresponding to the selected value
+   */
+  protected int rangeSingle(String single) {
+
+    if (single.toLowerCase().equals("first")) {
+      return 0;
+    }
+    if (single.toLowerCase().equals("last")) {
+      return m_Upper;
+    }
+    int index = Integer.parseInt(single) - 1;
+    if (index < 0) {
+      index = 0;
+    }
+    if (index > m_Upper) {
+      index = m_Upper;
+    }
+    return index;
+  }
+
+  /**
+   * Translates a range into it's lower index.
+   *
+   * @param range the string representation of the range
+   * @return the lower index of the range
+   */
+  protected int rangeLower(String range) {
+
+    int hyphenIndex;
+    if ((hyphenIndex = range.indexOf('-')) >= 0) {
+      return Math.min(rangeLower(range.substring(0, hyphenIndex)),
+		       rangeLower(range.substring(hyphenIndex + 1)));
+    }
+    return rangeSingle(range);
+  }
+
+  /**
+   * Translates a range into it's upper index. Must only be called once
+   * setUpper has been called.
+   *
+   * @param range the string representation of the range
+   * @return the upper index of the range
+   */
+  protected int rangeUpper(String range) {
+
+    int hyphenIndex;
+    if ((hyphenIndex = range.indexOf('-')) >= 0) {
+      return Math.max(rangeUpper(range.substring(0, hyphenIndex)),
+		       rangeUpper(range.substring(hyphenIndex + 1)));
+    }
+    return rangeSingle(range);
+  }
+
+  /**
+   * Determines if a string represents a valid index or simple range.
+   * Examples: <code>first  last   2   first-last  first-4  4-last</code>
+   * Doesn't check that a < b for a-b
+   *
+   * @param range
+   * @return true if the range is valid
+   */
+  protected boolean isValidRange(String range) {
+
+    if (range == null) {
+      return false;
+    }
+    int hyphenIndex;
+    if ((hyphenIndex = range.indexOf('-')) >= 0) {
+      if (isValidRange(range.substring(0, hyphenIndex)) &&
+	  isValidRange(range.substring(hyphenIndex + 1))) {
+	return true;
+      }
+      return false;
+    }
+    if (range.toLowerCase().equals("first")) {
+      return true;
+    }
+    if (range.toLowerCase().equals("last")) {
+      return true;
+    }
+    try {
+      int index = Integer.parseInt(range);
+      if (index > 0) {
+	return true;
+      }
+      return false;
+    } catch (NumberFormatException ex) {
+      return false;
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv one parameter: a test range specification
+   */
+  public static void main(String [] argv) {
+
+    try {
+      if (argv.length == 0) {
+	throw new Exception("Usage: Range <rangespec>");
+      }
+      Range range = new Range();
+      range.setRanges(argv[0]);
+      range.setUpper(9);
+      range.setInvert(false);
+      System.out.println("Input: " + argv[0] + "\n"
+			 + range.toString());
+      int [] rangeIndices = range.getSelection();
+      for (int i = 0; i < rangeIndices.length; i++)
+	System.out.print(" " + (rangeIndices[i] + 1));
+      System.out.println("");
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SerializedObject.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SerializedObject.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SerializedObject.java	(revision 16561)
@@ -0,0 +1,249 @@
+/*
+ *    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.
+ */
+
+/*
+ *    SerializedObject.java
+ *    Copyright (C) 2000 Webmind Inc.
+ *
+ */
+
+package weka.core;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * This class stores an object serialized in memory. It allows compression,
+ * to be used to conserve memory (for example, when storing large strings
+ * in memory), or can be used as a mechanism for deep copying objects.
+ *
+ * @author Len Trigg (len@intelligenesis.net)
+ * @version $Revision$
+ */
+public class SerializedObject implements Serializable {
+
+  /** Stores the serialized object */
+  private byte [] m_Serialized;
+
+  /** True if the object has been compressed during storage */
+  private boolean m_Compressed;
+
+  /**
+   * Serializes the supplied object into a byte array without compression.
+   *
+   * @param obj the Object to serialize.
+   * @exception Exception if the object is not Serializable.
+   */
+  public SerializedObject(Object obj) throws Exception {
+
+    this(obj, false);
+  }
+
+  /**
+   * Serializes the supplied object into a byte array.
+   *
+   * @param obj the Object to serialize.
+   * @param compress true if the object should be stored compressed.
+   * @exception Exception if the object is not Serializable.
+   */
+  public SerializedObject(Object obj, boolean compress) throws Exception {
+
+    //System.err.print("."); System.err.flush();
+    m_Compressed = compress;
+    m_Serialized = toByteArray(obj, m_Compressed);
+  }
+
+  /**
+   * Serializes the supplied object to a byte array.
+   *
+   * @param obj the Object to serialize
+   * @param compress true if the object should be compressed.
+   * @return the byte array containing the serialized object.
+   * @exception Exception if the object is not Serializable.
+   */
+  protected static byte [] toByteArray(Object obj, boolean compress) 
+    throws Exception {
+
+    ByteArrayOutputStream bo = new ByteArrayOutputStream();
+    OutputStream os = bo;
+    if (compress) {
+      os = new GZIPOutputStream(os);
+    }
+    os = new BufferedOutputStream(os);
+    ObjectOutputStream oo = new ObjectOutputStream(os);
+    oo.writeObject(obj);
+    oo.close();
+    return bo.toByteArray();
+  }
+
+  /**
+   * Gets the object stored in this SerializedObject. The object returned
+   * will be a deep copy of the original stored object.
+   *
+   * @return the deserialized Object.
+   */
+  public Object getObject() {
+    try {
+      InputStream is = new ByteArrayInputStream(m_Serialized);
+      if (m_Compressed) {
+        is = new GZIPInputStream(is);
+      }
+      is = new BufferedInputStream(is);
+      ObjectInputStream oi = new ObjectInputStream(is);
+      Object result = oi.readObject();
+      oi.close();
+      return result;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Compares this object with another for equality.
+   *
+   * @param other the other Object.
+   * @return true if the objects are equal.
+   */
+  public final boolean equals(Object other) {
+
+    // Check class type
+    if ((other == null) || !(other.getClass().equals(this.getClass()))) {
+      return false;
+    }
+    // Check serialized length
+    byte [] os = ((SerializedObject)other).m_Serialized;
+    if (os.length != m_Serialized.length) {
+      return false;
+    }
+    // Check serialized contents
+    for (int i = 0; i < m_Serialized.length; i++) {
+      if (m_Serialized[i] != os[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns a hashcode for this object.
+   *
+   * @return the hashcode for this object.
+   */
+  public final int hashCode() {
+    
+    return m_Serialized.length;
+  }
+
+  /**
+   * Returns a text representation of the state of this object.
+   *
+   * @return a String representing this object.
+   */
+  public String toString() {
+    
+    return (m_Compressed ? "Compressed object: " : "Uncompressed object: ")
+      + m_Serialized.length + " bytes";
+  }
+
+  /**
+   * Test routine, reads text from stdin and measures memory usage
+   */
+  public static void main2(String []args) {
+
+    try {
+      Runtime r = Runtime.getRuntime();
+      r.gc();
+      java.io.LineNumberReader lnr = new java.io.LineNumberReader(new java.io.InputStreamReader(System.in));
+      StringBuffer sb = new StringBuffer();
+      String line;
+      while ((line = lnr.readLine()) != null) {
+        sb.append(line).append('\n');
+      }
+      String text = sb.toString();
+      //System.err.println("TEXT:");
+      //System.err.println(text);
+      r.gc();
+
+      System.err.print("TOTAL: " + r.totalMemory());
+      System.err.println("\tFREE: " + r.freeMemory());
+      long used = r.totalMemory() - r.freeMemory();
+      
+      // convert to a compressedobject
+      SerializedObject co = new SerializedObject(text, true);
+
+      System.err.print("TOTAL: " + r.totalMemory());
+      System.err.println("\tFREE: " + r.freeMemory());
+      r.gc();
+      long used1 = r.totalMemory() - r.freeMemory();
+      long csize = used1 - used;
+      System.err.println("CompressedSize = " + csize);
+
+      String newstr = (String)co.getObject();
+      r.gc();
+      System.err.print("TOTAL: " + r.totalMemory());
+      System.err.println("\tFREE: " + r.freeMemory());
+      long used2 = r.totalMemory() - r.freeMemory();
+      long usize = used2 - used1;
+      System.err.println("UncompressedSize = " + usize);
+
+      // A couple of references to the original objects
+      // so they don't get freed prematurely and muck up the
+      // measurements.
+      newstr = newstr.toLowerCase();
+      text = text.toLowerCase();
+      System.err.println("Byte array: " + co.toString());
+      System.err.println("Length of text: " + text.length());
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+
+  /**
+   * Test routine, reads an arff file from stdin and measures memory usage
+   * (the arff file should have long string attribute values)
+   */
+  public static void main(String []args) {
+
+    try {
+      Runtime r = Runtime.getRuntime();
+      r.gc();
+      System.err.print("TOTAL: " + r.totalMemory());
+      System.err.println("\tFREE: " + r.freeMemory());
+      long used = r.totalMemory() - r.freeMemory();
+      Instances inst = new Instances(new java.io.BufferedReader(new java.io.InputStreamReader(System.in)));
+      r.gc();
+      long used1 = r.totalMemory() - r.freeMemory();
+      long csize = used1 - used;
+      System.err.print("\nTOTAL: " + r.totalMemory());
+      System.err.println("\tFREE: " + r.freeMemory());
+      System.err.println("size = " + csize);
+      int blah = inst.numAttributes();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SparseInstance.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SparseInstance.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SparseInstance.java	(revision 16561)
@@ -0,0 +1,773 @@
+/*
+ *    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.
+ */
+
+/*
+ *    SparseInstance.java
+ *    Copyright (C) 2000 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Class for storing an instance as a sparse vector. A sparse instance
+ * only requires storage for those attribute values that are non-zero.
+ * Since the objective is to reduce storage requirements for datasets
+ * with large numbers of default values, this also includes nominal
+ * attributes -- the first nominal value (i.e. that which has index 0)
+ * will not require explicit storage, so rearrange your nominal attribute
+ * value orderings if necessary. Missing values will be stored
+ * explicitly.
+ */
+public class SparseInstance extends Instance {
+
+  /** The index of the attribute associated with each stored value. */
+  protected int[] m_Indices;
+
+  /** The maximum number of values that can be stored. */
+  protected int m_NumAttributes;
+
+  protected SparseInstance() {
+
+  }
+
+  /**
+   * Constructor that generates a sparse instance from the given
+   * instance. Reference to the dataset is set to null.
+   * (ie. the instance doesn't have access to information about the
+   * attribute types)
+   *
+   * @param instance the instance from which the attribute values
+   * and the weight are to be copied
+   */
+  public SparseInstance(Instance instance) {
+    
+    m_Weight = instance.m_Weight;
+    m_Dataset = null;
+    m_NumAttributes = instance.numAttributes();
+    if (instance instanceof SparseInstance) {
+      m_AttValues = ((SparseInstance)instance).m_AttValues;
+      m_Indices = ((SparseInstance)instance).m_Indices;
+    } else {
+      double[] tempValues = new double[instance.numAttributes()];
+      int[] tempIndices = new int[instance.numAttributes()];
+      int vals = 0;
+      for (int i = 0; i < instance.numAttributes(); i++) {
+	if (instance.value(i) != 0) {
+	  tempValues[vals] = instance.value(i);
+	  tempIndices[vals] = i;
+	  vals++;
+	}
+      }
+      m_AttValues = new double[vals];
+      m_Indices = new int[vals];
+      System.arraycopy(tempValues, 0, m_AttValues, 0, vals);
+      System.arraycopy(tempIndices, 0, m_Indices, 0, vals);
+    }
+  }
+
+  /**
+   * Constructor that copies the info from the given instance. 
+   * Reference to the dataset is set to null.
+   * (ie. the instance doesn't have access to information about the
+   * attribute types)
+   *
+   * @param instance the instance from which the attribute
+   * info is to be copied 
+   */
+  public SparseInstance(SparseInstance instance) {
+    
+    m_AttValues = instance.m_AttValues;
+    m_Indices = instance.m_Indices;
+    m_Weight = instance.m_Weight;
+    m_NumAttributes = instance.m_NumAttributes;
+    m_Dataset = null;
+  }
+
+  /**
+   * Constructor that generates a sparse instance from the given
+   * parameters. Reference to the dataset is set to null.
+   * (ie. the instance doesn't have access to information about the
+   * attribute types)
+   *
+   * @param weight the instance's weight
+   * @param attValues a vector of attribute values 
+   */
+  public SparseInstance(double weight, double[] attValues) {
+    
+    m_Weight = weight;
+    m_Dataset = null;
+    m_NumAttributes = attValues.length;
+    double[] tempValues = new double[m_NumAttributes];
+    int[] tempIndices = new int[m_NumAttributes];
+    int vals = 0;
+    for (int i = 0; i < m_NumAttributes; i++) {
+      if (attValues[i] != 0) {
+	tempValues[vals] = attValues[i];
+	tempIndices[vals] = i;
+	vals++;
+      }
+    }
+    m_AttValues = new double[vals];
+    m_Indices = new int[vals];
+    System.arraycopy(tempValues, 0, m_AttValues, 0, vals);
+    System.arraycopy(tempIndices, 0, m_Indices, 0, vals);
+  }
+  
+  /**
+   * Constructor that inititalizes instance variable with given
+   * values. Reference to the dataset is set to null. (ie. the instance
+   * doesn't have access to information about the attribute types)
+   *
+   * @param weight the instance's weight
+   * @param attValues a vector of attribute values (just the ones to be stored)
+   * @param indices the indices of the given values in the full vector
+   * @param maxNumValues the maximium number of values that can be stored
+   */
+  public SparseInstance(double weight, double[] attValues,
+			int[] indices, int maxNumValues){
+    
+    int vals = 0; 
+    m_AttValues = new double [attValues.length];
+    m_Indices = new int [indices.length];
+    for (int i = 0; i < attValues.length; i++) {
+      if (attValues[i] != 0) {
+        m_AttValues[vals] = attValues[i];
+        m_Indices[vals] = indices[i];
+        vals++;
+      }
+    }
+    if (vals != attValues.length) {
+      // Need to truncate.
+      double [] newVals = new double[vals];
+      System.arraycopy(m_AttValues, 0, newVals, 0, vals);
+      m_AttValues = newVals;
+      int [] newIndices = new int[vals];
+      System.arraycopy(m_Indices, 0, newIndices, 0, vals);
+      m_Indices = newIndices;
+    }
+    m_Weight = weight;
+    m_NumAttributes = maxNumValues;
+    m_Dataset = null;
+  }
+
+  /**
+   * Constructor of an instance that sets weight to one, all values to
+   * be missing, and the reference to the dataset to null. (ie. the instance
+   * doesn't have access to information about the attribute types)
+   *
+   * @param numAttributes the size of the instance 
+   */
+  public SparseInstance(int numAttributes) {
+    
+    m_AttValues = new double[numAttributes];
+    m_NumAttributes = numAttributes;
+    m_Indices = new int[numAttributes];
+    for (int i = 0; i < m_AttValues.length; i++) {
+      m_AttValues[i] = MISSING_VALUE;
+      m_Indices[i] = i;
+    }
+    m_Weight = 1;
+    m_Dataset = null;
+  }
+
+  /**
+   * Returns the attribute associated with the internal index. 
+   *
+   * @param indexOfIndex the index of the attribute's index 
+   * @return the attribute at the given position
+   * @exception UnassignedDatasetException if instance doesn't have access to a
+   * dataset
+   */ 
+  public Attribute attributeSparse(int indexOfIndex) {
+   
+    if (m_Dataset == null) {
+      throw new UnassignedDatasetException("Instance doesn't have access to a dataset!");
+    }
+    return m_Dataset.attribute(m_Indices[indexOfIndex]);
+  }
+
+  /**
+   * Produces a shallow copy of this instance. The copy has
+   * access to the same dataset. (if you want to make a copy
+   * that doesn't have access to the dataset, use 
+   * <code>new SparseInstance(instance)</code>
+   *
+   * @return the shallow copy
+   */
+  public Object copy() {
+
+    Instance result = new SparseInstance(this);
+    result.m_Dataset = m_Dataset;
+    return result;
+  }
+
+  /**
+   * Returns the index of the attribute stored at the given position.
+   *
+   * @param position the position 
+   * @return the index of the attribute stored at the given position
+   */
+  public int index(int position) {
+
+    return m_Indices[position];
+  }
+
+  /**
+   * Tests if a specific value is "missing".
+   *
+   * @param attIndex the attribute's index
+   */
+  public boolean isMissing(int attIndex) {
+
+    if (Double.isNaN(value(attIndex))) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Locates the greatest index that is not greater than the
+   * given index.
+   *
+   * @return the internal index of the attribute index. Returns
+   * -1 if no index with this property couldn't be found
+   */
+  public int locateIndex(int index) {
+
+    int min = 0, max = m_Indices.length - 1;
+
+    // Binary search
+    while (max >= min) {
+      int current = (max + min) / 2;
+      if (m_Indices[current] > index) {
+	max = current - 1;
+      } else if (m_Indices[current] < index) {
+	min = current + 1;
+      } else {
+	return current;
+      }
+    }
+    return max;
+  }
+
+  /**
+   * Merges this instance with the given instance and returns
+   * the result. Dataset is set to null.
+   *
+   * @param inst the instance to be merged with this one
+   * @return the merged instances
+   */
+  public Instance mergeInstance(Instance inst) {
+
+    double[] values = new double[numValues() + inst.numValues()];
+    int[] indices = new int[numValues() + inst.numValues()];
+
+    int m = 0;
+    for (int j = 0; j < numValues(); j++, m++) {
+      values[m] = valueSparse(j);
+      indices[m] = index(j);
+    }
+    for (int j = 0; j < inst.numValues(); j++, m++) {
+      values[m] = inst.valueSparse(j);
+      indices[m] = inst.index(j) + inst.numAttributes();
+    }
+    
+    return new SparseInstance(1.0, values, indices, numAttributes() +
+			      inst.numAttributes());
+  }
+
+  /**
+   * Returns the number of attributes.
+   *
+   * @return the number of attributes as an integer
+   */
+  public int numAttributes() {
+
+    return m_NumAttributes;
+  }
+
+  /**
+   * Returns the number of values in the sparse vector.
+   *
+   * @return the number of values
+   */
+  public int numValues() {
+
+    return m_Indices.length;
+  }
+
+  /** 
+   * Replaces all missing values in the instance with the 
+   * values contained in the given array. A deep copy of
+   * the vector of attribute values is performed before the
+   * values are replaced.
+   *
+   * @param array containing the means and modes
+   * @exception IllegalArgumentException if numbers of attributes are unequal
+   */
+  public void replaceMissingValues(double[] array) {
+	 
+    if ((array == null) || (array.length != m_NumAttributes)) {
+      throw new IllegalArgumentException("Unequal number of attributes!");
+    }
+    double[] tempValues = new double[m_AttValues.length];
+    int[] tempIndices = new int[m_AttValues.length];
+    int vals = 0;
+    for (int i = 0; i < m_AttValues.length; i++) {
+      if (isMissingValue(m_AttValues[i])) {
+	if (array[m_Indices[i]] != 0) {
+	  tempValues[vals] = array[m_Indices[i]];
+	  tempIndices[vals] = m_Indices[i];
+	  vals++;
+	} 
+      } else {
+	tempValues[vals] = m_AttValues[i];
+	tempIndices[vals] = m_Indices[i];
+	vals++;
+      }
+    }
+    m_AttValues = new double[vals];
+    m_Indices = new int[vals];
+    System.arraycopy(tempValues, 0, m_AttValues, 0, vals);
+    System.arraycopy(tempIndices, 0, m_Indices, 0, vals);
+  }
+
+  /**
+   * Sets a specific value in the instance to the given value 
+   * (internal floating-point format). Performs a deep copy
+   * of the vector of attribute values before the value is set.
+   *
+   * @param attIndex the attribute's index 
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   */
+  public void setValue(int attIndex, double value) {
+
+    int index = locateIndex(attIndex);
+    
+    if ((index >= 0) && (m_Indices[index] == attIndex)) {
+      if (value != 0) {
+	double[] tempValues = new double[m_AttValues.length];
+	System.arraycopy(m_AttValues, 0, tempValues, 0, m_AttValues.length);
+	tempValues[index] = value;
+	m_AttValues = tempValues;
+      } else {
+	double[] tempValues = new double[m_AttValues.length - 1];
+	int[] tempIndices = new int[m_Indices.length - 1];
+	System.arraycopy(m_AttValues, 0, tempValues, 0, index);
+	System.arraycopy(m_Indices, 0, tempIndices, 0, index);
+	System.arraycopy(m_AttValues, index + 1, tempValues, index, 
+			 m_AttValues.length - index - 1);
+	System.arraycopy(m_Indices, index + 1, tempIndices, index, 
+			 m_Indices.length - index - 1);
+	m_AttValues = tempValues;
+	m_Indices = tempIndices;
+      }
+    } else {
+      if (value != 0) {
+	double[] tempValues = new double[m_AttValues.length + 1];
+	int[] tempIndices = new int[m_Indices.length + 1];
+	System.arraycopy(m_AttValues, 0, tempValues, 0, index + 1);
+	System.arraycopy(m_Indices, 0, tempIndices, 0, index + 1);
+	tempIndices[index + 1] = attIndex;
+	tempValues[index + 1] = value;
+	System.arraycopy(m_AttValues, index + 1, tempValues, index + 2, 
+			 m_AttValues.length - index - 1);
+	System.arraycopy(m_Indices, index + 1, tempIndices, index + 2, 
+			 m_Indices.length - index - 1);
+	m_AttValues = tempValues;
+	m_Indices = tempIndices;
+      }
+    }
+  }
+
+  /**
+   * Sets a specific value in the instance to the given value 
+   * (internal floating-point format). Performs a deep copy
+   * of the vector of attribute values before the value is set.
+   *
+   * @param indexOfIndex the index of the attribute's index 
+   * @param value the new attribute value (If the corresponding
+   * attribute is nominal (or a string) then this is the new value's
+   * index as a double).  
+   */
+  public void setValueSparse(int indexOfIndex, double value) {
+
+    if (value != 0) {
+      double[] tempValues = new double[m_AttValues.length];
+      System.arraycopy(m_AttValues, 0, tempValues, 0, m_AttValues.length);
+      m_AttValues = tempValues;
+      m_AttValues[indexOfIndex] = value;
+    } else {
+      double[] tempValues = new double[m_AttValues.length - 1];
+      int[] tempIndices = new int[m_Indices.length - 1];
+      System.arraycopy(m_AttValues, 0, tempValues, 0, indexOfIndex);
+      System.arraycopy(m_Indices, 0, tempIndices, 0, indexOfIndex);
+      System.arraycopy(m_AttValues, indexOfIndex + 1, tempValues, indexOfIndex, 
+		       m_AttValues.length - indexOfIndex - 1);
+      System.arraycopy(m_Indices, indexOfIndex + 1, tempIndices, indexOfIndex, 
+		       m_Indices.length - indexOfIndex - 1);
+      m_AttValues = tempValues;
+      m_Indices = tempIndices;
+    }
+  }
+
+  /**
+   * Returns the values of each attribute as an array of doubles.
+   *
+   * @return an array containing all the instance attribute values
+   */
+  public double[] toDoubleArray() {
+
+    double[] newValues = new double[m_NumAttributes];
+    for (int i = 0; i < m_AttValues.length; i++) {
+      newValues[m_Indices[i]] = m_AttValues[i];
+    }
+    return newValues;
+  }
+
+  /**
+   * Returns the description of one instance in sparse format. 
+   * If the instance doesn't have access to a dataset, it returns the 
+   * internal floating-point values. Quotes string values that contain 
+   * whitespace characters.
+   *
+   * @return the instance's description as a string
+   */
+  public String toString() {
+
+    StringBuffer text = new StringBuffer();
+    
+    text.append('{');
+    for (int i = 0; i < m_Indices.length; i++) {
+      if (i > 0) text.append(",");
+      if (isMissingValue(m_AttValues[i])) {
+	text.append(m_Indices[i] + " ?");
+      } else {
+	if (m_Dataset == null) {
+	  text.append(m_Indices[i] + " " + 
+		      Utils.doubleToString(m_AttValues[i],6));
+	} else {
+	  if (m_Dataset.attribute(m_Indices[i]).isNominal() || 
+	      m_Dataset.attribute(m_Indices[i]).isString()) {
+	    try {
+	      text.append(m_Indices[i] + " " +
+			  Utils.quote(m_Dataset.attribute(m_Indices[i]).
+				      value((int)valueSparse(i))));
+	    } catch (Exception e) {
+              e.printStackTrace();
+              System.err.println(new Instances(m_Dataset, 0));
+              System.err.println("Att:" + m_Indices[i] + " Val:" + valueSparse(i));
+	      throw new Error("This should never happen!");
+	    }
+	  } else {
+	    text.append(m_Indices[i] + " " +
+			Utils.doubleToString(m_AttValues[i],6));
+	  }
+	}
+      }
+    }
+    text.append('}');
+    return text.toString();
+  }
+
+  /**
+   * Returns an instance's attribute value in internal format.
+   *
+   * @param attIndex the attribute's index
+   * @return the specified value as a double (If the corresponding
+   * attribute is nominal (or a string) then it returns the value's index as a 
+   * double).
+   */
+  public double value(int attIndex) {
+
+    int index = locateIndex(attIndex);
+    if ((index >= 0) && (m_Indices[index] == attIndex)) {
+      return m_AttValues[index];
+    } else {
+      return 0.0;
+    }
+  }  
+
+  /**
+   * Deletes an attribute at the given position (0 to 
+   * numAttributes() - 1).
+   *
+   * @param pos the attribute's position
+   */
+  void forceDeleteAttributeAt(int position) {
+
+    int index = locateIndex(position);
+
+    m_NumAttributes--;
+    if ((index >= 0) && (m_Indices[index] == position)) {
+      int[] tempIndices = new int[m_Indices.length - 1];
+      double[] tempValues = new double[m_AttValues.length - 1];
+      System.arraycopy(m_Indices, 0, tempIndices, 0, index);
+      System.arraycopy(m_AttValues, 0, tempValues, 0, index);
+      for (int i = index; i < m_Indices.length - 1; i++) {
+	tempIndices[i] = m_Indices[i + 1] - 1;
+	tempValues[i] = m_AttValues[i + 1];
+      }
+      m_Indices = tempIndices;
+      m_AttValues = tempValues;
+    } else {
+      int[] tempIndices = new int[m_Indices.length];
+      double[] tempValues = new double[m_AttValues.length];
+      System.arraycopy(m_Indices, 0, tempIndices, 0, index + 1);
+      System.arraycopy(m_AttValues, 0, tempValues, 0, index + 1);
+      for (int i = index + 1; i < m_Indices.length - 1; i++) {
+	tempIndices[i] = m_Indices[i] - 1;
+	tempValues[i] = m_AttValues[i];
+      }
+      m_Indices = tempIndices;
+      m_AttValues = tempValues;
+    }
+  }
+
+  /**
+   * Inserts an attribute at the given position
+   * (0 to numAttributes()) and sets its value to be missing. 
+   *
+   * @param pos the attribute's position
+   */
+  void forceInsertAttributeAt(int position)  {
+
+    int index = locateIndex(position);
+
+    m_NumAttributes++;
+    if ((index >= 0) && (m_Indices[index] == position)) {
+      int[] tempIndices = new int[m_Indices.length + 1];
+      double[] tempValues = new double[m_AttValues.length + 1];
+      System.arraycopy(m_Indices, 0, tempIndices, 0, index);
+      System.arraycopy(m_AttValues, 0, tempValues, 0, index);
+      tempIndices[index] = position;
+      tempValues[index] = MISSING_VALUE;
+      for (int i = index; i < m_Indices.length; i++) {
+	tempIndices[i + 1] = m_Indices[i] + 1;
+	tempValues[i + 1] = m_AttValues[i];
+      }
+      m_Indices = tempIndices;
+      m_AttValues = tempValues;
+    } else {
+      int[] tempIndices = new int[m_Indices.length + 1];
+      double[] tempValues = new double[m_AttValues.length + 1];
+      System.arraycopy(m_Indices, 0, tempIndices, 0, index + 1);
+      System.arraycopy(m_AttValues, 0, tempValues, 0, index + 1);
+      tempIndices[index + 1] = position;
+      tempValues[index + 1] = MISSING_VALUE;
+      for (int i = index + 1; i < m_Indices.length; i++) {
+	tempIndices[i + 1] = m_Indices[i] + 1;
+	tempValues[i + 1] = m_AttValues[i];
+      }
+      m_Indices = tempIndices;
+      m_AttValues = tempValues;
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   */
+  public static void main(String[] options) {
+
+    try {
+
+      // Create numeric attributes "length" and "weight"
+      Attribute length = new Attribute("length");
+      Attribute weight = new Attribute("weight");
+      
+      // Create vector to hold nominal values "first", "second", "third" 
+      FastVector my_nominal_values = new FastVector(3); 
+      my_nominal_values.addElement("first"); 
+      my_nominal_values.addElement("second"); 
+      my_nominal_values.addElement("third"); 
+      
+      // Create nominal attribute "position" 
+      Attribute position = new Attribute("position", my_nominal_values);
+      
+      // Create vector of the above attributes 
+      FastVector attributes = new FastVector(3);
+      attributes.addElement(length);
+      attributes.addElement(weight);
+      attributes.addElement(position);
+      
+      // Create the empty dataset "race" with above attributes
+      Instances race = new Instances("race", attributes, 0);
+      
+      // Make position the class attribute
+      race.setClassIndex(position.index());
+      
+      // Create empty instance with three attribute values
+      SparseInstance inst = new SparseInstance(3);
+      
+      // Set instance's values for the attributes "length", "weight", and "position"
+      inst.setValue(length, 5.3);
+      inst.setValue(weight, 300);
+      inst.setValue(position, "first");
+      
+      // Set instance's dataset to be the dataset "race"
+      inst.setDataset(race);
+      
+      // Print the instance
+      System.out.println("The instance: " + inst);
+      
+      // Print the first attribute
+      System.out.println("First attribute: " + inst.attribute(0));
+      
+      // Print the class attribute
+      System.out.println("Class attribute: " + inst.classAttribute());
+      
+      // Print the class index
+      System.out.println("Class index: " + inst.classIndex());
+      
+      // Say if class is missing
+      System.out.println("Class is missing: " + inst.classIsMissing());
+      
+      // Print the instance's class value in internal format
+      System.out.println("Class value (internal format): " + inst.classValue());
+      
+      // Print a shallow copy of this instance
+      SparseInstance copy = (SparseInstance) inst.copy();
+      System.out.println("Shallow copy: " + copy);
+      
+      // Set dataset for shallow copy
+      copy.setDataset(inst.dataset());
+      System.out.println("Shallow copy with dataset set: " + copy);
+
+      // Print out all values in internal format
+      System.out.print("All stored values in internal format: ");
+      for (int i = 0; i < inst.numValues(); i++) {
+	if (i > 0) {
+	  System.out.print(",");
+	}
+	System.out.print(inst.valueSparse(i));
+      }
+      System.out.println();
+
+      // Set all values to zero
+      System.out.print("All values set to zero: ");
+      while (inst.numValues() > 0) {
+	inst.setValueSparse(0, 0);
+      }
+      for (int i = 0; i < inst.numValues(); i++) {
+	if (i > 0) {
+	  System.out.print(",");
+	}
+	System.out.print(inst.valueSparse(i));
+      }
+      System.out.println();
+
+      // Set all values to one
+      System.out.print("All values set to one: ");
+      for (int i = 0; i < inst.numAttributes(); i++) {
+	inst.setValue(i, 1);
+      }
+      for (int i = 0; i < inst.numValues(); i++) {
+	if (i > 0) {
+	  System.out.print(",");
+	}
+	System.out.print(inst.valueSparse(i));
+      }
+      System.out.println();
+
+      // Unset dataset for copy, delete first attribute, and insert it again
+      copy.setDataset(null);
+      copy.deleteAttributeAt(0);
+      copy.insertAttributeAt(0);
+      copy.setDataset(inst.dataset());
+      System.out.println("Copy with first attribute deleted and inserted: " + copy); 
+
+      // Same for second attribute
+      copy.setDataset(null);
+      copy.deleteAttributeAt(1);
+      copy.insertAttributeAt(1);
+      copy.setDataset(inst.dataset());
+      System.out.println("Copy with second attribute deleted and inserted: " + copy); 
+
+      // Same for last attribute
+      copy.setDataset(null);
+      copy.deleteAttributeAt(2);
+      copy.insertAttributeAt(2);
+      copy.setDataset(inst.dataset());
+      System.out.println("Copy with third attribute deleted and inserted: " + copy); 
+      
+      // Enumerate attributes (leaving out the class attribute)
+      System.out.println("Enumerating attributes (leaving out class):");
+      Enumeration enum = inst.enumerateAttributes();
+      while (enum.hasMoreElements()) {
+	Attribute att = (Attribute) enum.nextElement();
+	System.out.println(att);
+      }
+      
+      // Headers are equivalent?
+      System.out.println("Header of original and copy equivalent: " +
+			 inst.equalHeaders(copy));
+
+      // Test for missing values
+      System.out.println("Length of copy missing: " + copy.isMissing(length));
+      System.out.println("Weight of copy missing: " + copy.isMissing(weight.index()));
+      System.out.println("Length of copy missing: " + 
+			 Instance.isMissingValue(copy.value(length)));
+      System.out.println("Missing value coded as: " + Instance.missingValue());
+
+      // Prints number of attributes and classes
+      System.out.println("Number of attributes: " + copy.numAttributes());
+      System.out.println("Number of classes: " + copy.numClasses());
+
+      // Replace missing values
+      double[] meansAndModes = {2, 3, 0};
+      copy.replaceMissingValues(meansAndModes);
+      System.out.println("Copy with missing value replaced: " + copy);
+
+      // Setting and getting values and weights
+      copy.setClassMissing();
+      System.out.println("Copy with missing class: " + copy);
+      copy.setClassValue(0);
+      System.out.println("Copy with class value set to first value: " + copy);
+      copy.setClassValue("third");
+      System.out.println("Copy with class value set to \"third\": " + copy);
+      copy.setMissing(1);
+      System.out.println("Copy with second attribute set to be missing: " + copy);
+      copy.setMissing(length);
+      System.out.println("Copy with length set to be missing: " + copy);
+      copy.setValue(0, 0);
+      System.out.println("Copy with first attribute set to 0: " + copy);
+      copy.setValue(weight, 1);
+      System.out.println("Copy with weight attribute set to 1: " + copy);
+      copy.setValue(position, "second");
+      System.out.println("Copy with position set to \"second\": " + copy);
+      copy.setValue(2, "first");
+      System.out.println("Copy with last attribute set to \"first\": " + copy);
+      System.out.println("Current weight of instance copy: " + copy.weight());
+      copy.setWeight(2);
+      System.out.println("Current weight of instance copy (set to 2): " + copy.weight());
+      System.out.println("Last value of copy: " + copy.toString(2));
+      System.out.println("Value of position for copy: " + copy.toString(position));
+      System.out.println("Last value of copy (internal format): " + copy.value(2));
+      System.out.println("Value of position for copy (internal format): " + 
+			 copy.value(position));
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SpecialFunctions.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SpecialFunctions.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/SpecialFunctions.java	(revision 16561)
@@ -0,0 +1,127 @@
+/*
+ *    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.
+ */
+
+/*
+ *    SpecialFunctions.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+import java.lang.Math;
+
+/**
+ * Class implementing some mathematical functions.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public final class SpecialFunctions {
+
+  /** Some constants */
+  private static double log2 = Math.log(2);
+  private static double[] cofLogGamma = {76.18009172947146,-86.50532032941677,
+					 24.01409824083091,-1.231739572450155,
+					 0.1208650973866179e-2,-0.5395239384953e-5};
+
+  /**
+   * Returns natural logarithm of factorial using gamma function.
+   *
+   * @param x the value
+   * @return natural logarithm of factorial
+   */
+  public static double lnFactorial(double x){
+
+    return lnGamma(x+1);
+  }
+
+  /**
+   * Returns natural logarithm of gamma function. Converted to java
+   * from Numerical Recipes in C.
+   *
+   * @param x the value
+   * @return natural logarithm of gamma function
+   */
+  public static double lnGamma(double x){
+    
+    double y,tmp,ser;
+    int j;
+    
+    y=x;
+    tmp=x+5.5;
+    tmp -= (x+0.5)*Math.log(tmp);
+    ser=1.000000000190015;
+    for (j=0;j<=5;j++) {
+      ser += cofLogGamma[j]/++y;
+    }
+    return -tmp+Math.log(2.5066282746310005*ser/x);
+  }
+
+  /**
+   * Returns base 2 logarithm of binomial coefficient using gamma function.
+   *
+   * @param a upper part of binomial coefficient
+   * @param b lower part
+   * @return the base 2 logarithm of the binominal coefficient a over b
+   */
+  public static double log2Binomial(double a, double b) throws ArithmeticException{
+    
+    if (Utils.gr(b,a)) {
+      throw new ArithmeticException("Can't compute binomial coefficient.");
+    }
+    return (lnFactorial(a)-lnFactorial(b)-lnFactorial(a-b))/log2;
+  }
+
+  /**
+   * Returns base 2 logarithm of multinomial using gamma function.
+   *
+   * @param a upper part of multinomial coefficient
+   * @param bs lower part
+   * @return multinomial coefficient of a over the bs
+   */
+  public static double log2Multinomial(double a, double[] bs)
+       throws ArithmeticException{
+    
+    double sum = 0;
+    int i;
+    
+    for (i=0;i<bs.length;i++) {
+      if (Utils.gr(bs[i],a)) {
+	throw 
+	  new ArithmeticException("Can't compute multinomial coefficient.");
+      } else {
+	sum = sum+lnFactorial(bs[i]);
+      }
+    }
+    return (lnFactorial(a)-sum)/log2;
+  }
+
+  /**
+   * Main method for testing this class.
+   */
+  public static void main(String[] ops) {
+
+    double[] doubles = {1, 2, 3};
+
+    System.out.println("6!: " + Math.exp(SpecialFunctions.lnFactorial(6)));
+    System.out.println("lnGamma(6): "+ SpecialFunctions.lnGamma(6));
+    System.out.println("Binomial 6 over 2: " +
+		       Math.pow(2, SpecialFunctions.log2Binomial(6, 2)));
+    System.out.println("Multinomial 6 over 1, 2, 3: " +
+		       Math.pow(2, SpecialFunctions.log2Multinomial(6, doubles)));
+  }    
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Statistics.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Statistics.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Statistics.java	(revision 16561)
@@ -0,0 +1,280 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Statistics.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+/**
+ * Class implementing some distributions, tests, etc. Most of the
+ * code is adapted from Gary Perlman's unixstat.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Statistics {
+
+  /** Some constants */
+  private static double logSqrtPi = Math.log(Math.sqrt(Math.PI));
+  private static double rezSqrtPi = 1/Math.sqrt(Math.PI);
+  private static double bigx = 20.0;
+
+  /**
+   * Computes standard error for observed values of a binomial
+   * random variable.
+   *
+   * @param p the probability of success
+   * @param n the size of the sample
+   * @return the standard error
+   */
+  public static double binomialStandardError(double p, int n) {
+
+    if (n == 0) {
+      return 0; 
+    }
+    return Math.sqrt((p*(1-p))/(double) n);
+  }
+
+  /**
+   * Returns chi-squared probability for given value and degrees
+   * of freedom. (The probability that the chi-squared variate
+   * will be greater than x for the given degrees of freedom.)
+   * Adapted from unixstat by Gary Perlman.
+   *
+   * @param x the value
+   * @param df the number of degrees of freedom
+   */
+  public static double chiSquaredProbability(double x, int df) {
+
+    double a, y = 0, s, e, c, z, val;
+    boolean even;
+   
+    if (x <= 0 || df < 1)
+      return (1);
+    a = 0.5 * x;
+    even = (((int)(2*(df/2))) == df);
+    if (df > 1)
+      y = Math.exp(-a); //((-a < -bigx) ? 0.0 : Math.exp (-a));
+    s = (even ? y : (2.0 * normalProbability(-Math.sqrt (x))));
+    if (df > 2){
+      x = 0.5 * (df - 1.0);
+      z = (even ? 1.0 : 0.5);
+      if (a > bigx){
+	e = (even ? 0.0 : logSqrtPi);
+	c = Math.log (a);
+	while (z <= x){
+	  e = Math.log (z) + e;
+	  val = c*z-a-e;
+	  s += Math.exp (val); //((val < -bigx) ? 0.0 : Math.exp (val));
+	  z += 1.0;
+	}
+	return (s);
+      }else{
+	e = (even ? 1.0 : (rezSqrtPi / Math.sqrt (a)));
+	c = 0.0;
+	while (z <= x){
+	  e = e * (a / z);
+	  c = c + e;
+	  z += 1.0;
+	}
+	return (c * y + s);
+      }
+    }else{
+      return (s);
+    }
+  }
+
+  /**
+   * Critical value for given probability of F-distribution.
+   * Adapted from unixstat by Gary Perlman.
+   *
+   * @param p the probability
+   * @param df1 the first number of degrees of freedom
+   * @param df2 the second number of degrees of freedom
+   * @return the critical value for the given probability
+   */
+  public static double FCriticalValue(double p, int df1, int df2) {
+
+    double  fval;
+    double  maxf = 99999.0;     /* maximum possible F ratio */
+    double  minf = .000001;     /* minimum possible F ratio */
+    
+    if (p <= 0.0 || p >= 1.0)
+      return (0.0);
+    
+    fval = 1.0 / p; /* the smaller the p, the larger the F */
+    
+    while (Math.abs (maxf - minf) > .000001) {
+      if (FProbability(fval, df1, df2) < p) /* F too large */
+	maxf = fval;
+      else /* F too small */
+	minf = fval;
+      fval = (maxf + minf) * 0.5;
+    }
+    
+    return (fval);
+  }
+
+  /**
+   * Computes probability of F-ratio.
+   * Adapted from unixstat by Gary Perlman.
+   * Collected Algorithms of the CACM 
+   * Algorithm 322
+   * Egon Dorrer
+   *
+   * @param F the F-ratio
+   * @param df1 the first number of degrees of freedom
+   * @param df2 the second number of degrees of freedom
+   * @return the probability of the F-ratio.
+   */
+  public static double FProbability(double F, int df1, int df2) {
+
+    int     i, j;
+    int     a, b;
+    double  w, y, z, d, p;
+
+    if ((Math.abs(F) < 10e-10) || df1 <= 0 || df2 <= 0)
+      return (1.0);
+    a = (df1%2 == 1) ? 1 : 2;
+    b = (df2%2 == 1) ? 1 : 2;
+    w = (F * df1) / df2;
+    z = 1.0 / (1.0 + w);
+    if (a == 1)
+      if (b == 1) {
+	p = Math.sqrt (w);
+	y = 1/Math.PI; /* 1 / 3.14159 */
+	d = y * z / p;
+	p = 2.0 * y * Math.atan (p);
+      } else {
+	p = Math.sqrt (w * z);
+	d = 0.5 * p * z / w;
+      } else if (b == 1) {
+	p = Math.sqrt (z);
+	d = 0.5 * z * p;
+	p = 1.0 - p;
+      } else {
+	d = z * z;
+	p = w * z;
+      }
+    y = 2.0 * w / z;
+    for (j = b + 2; j <= df2; j += 2) {
+      d *= (1.0 + a / (j - 2.0)) * z;
+      p = (a == 1 ? p + d * y / (j - 1.0) : (p + w) * z);
+    }
+    y = w * z;
+    z = 2.0 / z;
+    b = df2 - 2;
+    for (i = a + 2; i <= df1; i += 2) {
+      j = i + b;
+      d *= y * j / (i - 2.0);
+      p -= z * d / j;
+    }
+    
+    // correction for approximation errors suggested in certification
+    if (p < 0.0)
+      p = 0.0;
+    else if (p > 1.0)
+      p = 1.0;
+    return (1.0-p);
+  }
+  
+  /**
+   * Returns probability that the standardized normal variate Z (mean = 0, standard
+   * deviation = 1) is less than z.
+   * Adapted from unixstat by Gary Perlman.
+   *
+   * @param the z-value
+   * @return the probability of the z value according to the normal pdf
+   */
+  public static double normalProbability(double z) {
+
+    double  y, x, w;
+    
+    if (z == 0.0)
+      x = 0.0;
+    else{
+      y = 0.5 * Math.abs (z);
+      if (y >= 3.0)
+	x = 1.0;
+      else if (y < 1.0){
+	w = y*y;
+	x = ((((((((0.000124818987 * w
+                   -0.001075204047) * w +0.005198775019) * w
+                   -0.019198292004) * w +0.059054035642) * w
+                   -0.151968751364) * w +0.319152932694) * w
+                   -0.531923007300) * w +0.797884560593) * y * 2.0;
+      }else{
+	y -= 2.0;
+	x = (((((((((((((-0.000045255659 * y
+                         +0.000152529290) * y -0.000019538132) * y
+                         -0.000676904986) * y +0.001390604284) * y
+                         -0.000794620820) * y -0.002034254874) * y
+                         +0.006549791214) * y -0.010557625006) * y
+                         +0.011630447319) * y -0.009279453341) * y
+                         +0.005353579108) * y -0.002141268741) * y
+                         +0.000535310849) * y +0.999936657524;
+      }
+    }
+  
+    return (z > 0.0 ? ((x + 1.0) / 2.0) : ((1.0 - x) / 2.0));
+  }
+
+  /**
+   * Computes absolute size of half of a student-t confidence interval 
+   * for given degrees of freedom, probability, and observed value.
+   *
+   * @param df the number of degrees of freedom
+   * @param p the probability
+   * @param se the observed value
+   * @return absolute size of half of a student-t confidence interval
+   */
+  public static double studentTConfidenceInterval(int df, double p,
+						  double se) {
+
+    return Math.sqrt(FCriticalValue(p, 1, df))*se;
+  }
+
+  /**
+   * Main method for testing this class.
+   */
+  public static void main(String[] ops) {
+
+    System.out.println("Binomial standard error (0.5, 100): " + 
+		       Statistics.binomialStandardError(0.5, 100));
+    System.out.println("Chi-squared probability (2.558, 10): " +
+		       Statistics.chiSquaredProbability(2.558, 10));
+    System.out.println("Normal probability (0.2): " +
+		       Statistics.normalProbability(0.2));
+    System.out.println("F critical value (0.05, 4, 5): " +
+		       Statistics.FCriticalValue(0.05, 4, 5));
+    System.out.println("F probability (5.1922, 4, 5): " +
+		       Statistics.FProbability(5.1922, 4, 5));
+    System.out.println("Student-t confidence interval (9, 0.01, 2): " +
+		       Statistics.studentTConfidenceInterval(9, 0.01, 2));
+  }
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedClassException.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedClassException.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedClassException.java	(revision 16561)
@@ -0,0 +1,52 @@
+/*
+ *    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.
+ */
+
+/*
+ *    UnassignedClassException.java
+ *    Copyright (C) 2001 Webmind Corp.
+ */
+
+package weka.core;
+
+/**
+ * <code>UnassignedClassException</code> is used when
+ * a method requires access to the Attribute designated as 
+ * the class attribute in a set of Instances, but the Instances does not
+ * have any class attribute assigned (such as by setClassIndex()).
+ *
+ * @author <a href="mailto:len@webmind.com">Len Trigg</a>
+ * @version $Revision$
+ */
+public class UnassignedClassException extends RuntimeException {
+
+  /**
+   * Creates a new <code>UnassignedClassException</code> instance
+   * with no detail message.
+   */
+  public UnassignedClassException() { 
+    super(); 
+  }
+
+  /**
+   * Creates a new <code>UnassignedClassException</code> instance
+   * with a specified message.
+   *
+   * @param messagae a <code>String</code> containing the message.
+   */
+  public UnassignedClassException(String message) { 
+    super(message); 
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedDatasetException.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedDatasetException.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnassignedDatasetException.java	(revision 16561)
@@ -0,0 +1,53 @@
+/*
+ *    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.
+ */
+
+/*
+ *    UnassignedDatasetException.java
+ *    Copyright (C) 2001 Webmind Corp.
+ */
+
+package weka.core;
+
+/**
+ * <code>UnassignedDatasetException</code> is used when
+ * a method of an Instance is called that requires access to
+ * the Instance structure, but that the Instance does not contain
+ * a reference to any Instances (as set by Instance.setDataset(), or when
+ * an Instance is added to a set of Instances)).
+ *
+ * @author <a href="mailto:len@webmind.com">Len Trigg</a>
+ * @version $Revision$
+ */
+public class UnassignedDatasetException extends RuntimeException {
+
+  /**
+   * Creates a new <code>UnassignedDatasetException</code> instance
+   * with no detail message.
+   */
+  public UnassignedDatasetException() { 
+    super(); 
+  }
+
+  /**
+   * Creates a new <code>UnassignedDatasetException</code> instance
+   * with a specified message.
+   *
+   * @param messagae a <code>String</code> containing the message.
+   */
+  public UnassignedDatasetException(String message) { 
+    super(message); 
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnsupportedClassTypeException.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnsupportedClassTypeException.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/UnsupportedClassTypeException.java	(revision 16561)
@@ -0,0 +1,51 @@
+/*
+ *    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.
+ */
+
+/*
+ *    UnsupportedClassTypeException.java
+ *    Copyright (C) 2001 Webmind Corp.
+ */
+
+package weka.core;
+
+/**
+ * <code>UnsupportedClassTypeException</code> is used in situations
+ * where the throwing object is not able to accept Instances with the
+ * supplied structure, because the class Attribute is of the wrong type.
+ *
+ * @author <a href="mailto:len@webmind.com">Len Trigg</a>
+ * @version $Revision$
+ */
+public class UnsupportedClassTypeException extends WekaException {
+
+  /**
+   * Creates a new <code>UnsupportedClassTypeException</code> instance
+   * with no detail message.
+   */
+  public UnsupportedClassTypeException() { 
+    super(); 
+  }
+
+  /**
+   * Creates a new <code>UnsupportedClassTypeException</code> instance
+   * with a specified message.
+   *
+   * @param messagae a <code>String</code> containing the message.
+   */
+  public UnsupportedClassTypeException(String message) { 
+    super(message); 
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Utils.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Utils.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/Utils.java	(revision 16561)
@@ -0,0 +1,1384 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Utils.java
+ *    Copyright (C) 1999 Eibe Frank,Len Trigg,Yong Wang
+ *
+ */
+
+package weka.core;
+
+import java.lang.Math;
+import java.util.StringTokenizer;
+import java.util.Properties;
+import java.io.File;
+import java.io.FileInputStream;
+
+/**
+ * Class implementing some simple utility methods.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @author Yong Wang (yongwang@cs.waikato.ac.nz)
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public final class Utils {
+
+  /** The natural logarithm of 2. */
+  public static double log2 = Math.log(2);
+
+  /** The small deviation allowed in double comparisons */
+  public static double SMALL = 1e-6;
+
+  
+  /**
+   * Reads properties that inherit from three locations. Properties
+   * are first defined in the system resource location (i.e. in the
+   * CLASSPATH).  These default properties must exist. Properties
+   * defined in the users home directory (optional) override default
+   * settings. Properties defined in the current directory (optional)
+   * override all these settings.
+   *
+   * @param resourceName the location of the resource that should be
+   * loaded.  e.g.: "weka/core/Utils.props". (The use of hardcoded
+   * forward slashes here is OK - see
+   * jdk1.1/docs/guide/misc/resources.html) This routine will also
+   * look for the file (in this case) "Utils.props" in the users home
+   * directory and the current directory.
+   * @return the Properties
+   * @exception Exception if no default properties are defined, or if
+   * an error occurs reading the properties files.  
+   */
+  public static Properties readProperties(String resourceName)
+    throws Exception{
+
+    Properties defaultProps = new Properties();
+    try {
+      // Apparently hardcoded slashes are OK here
+      // jdk1.1/docs/guide/misc/resources.html
+      defaultProps.load(ClassLoader.getSystemResourceAsStream(resourceName));
+    } catch (Exception ex) {
+/*      throw new Exception("Problem reading default properties: "
+	+ ex.getMessage()); */
+      System.err.println("Warning, unable to load properties file from "
+			 +"system resource (Utils.java)");
+    }
+
+    // Hardcoded slash is OK here
+    // eg: see jdk1.1/docs/guide/misc/resources.html
+    int slInd = resourceName.lastIndexOf('/');
+    if (slInd != -1) {
+      resourceName = resourceName.substring(slInd + 1);
+    }
+
+    // Allow a properties file in the home directory to override
+    Properties userProps = new Properties(defaultProps);    
+    File propFile = new File(System.getProperties().getProperty("user.home")
+                             + File.separatorChar
+                             + resourceName);
+    if (propFile.exists()) {
+      try {
+        userProps.load(new FileInputStream(propFile));
+      } catch (Exception ex) {
+        throw new Exception("Problem reading user properties: " + propFile);
+      }
+    }
+
+    // Allow a properties file in the current directory to override
+    Properties localProps = new Properties(userProps);
+    propFile = new File(resourceName);
+    if (propFile.exists()) {
+      try {
+        localProps.load(new FileInputStream(propFile));
+      } catch (Exception ex) {
+        throw new Exception("Problem reading local properties: " + propFile);
+      }
+    }
+    
+    return localProps;
+  }
+
+  /**
+   * Returns the correlation coefficient of two double vectors.
+   *
+   * @param y1 double vector 1
+   * @param y2 double vector 2
+   * @param n the length of two double vectors
+   * @return the correlation coefficient
+   */
+  public final static double correlation(double y1[],double y2[],int n) {
+
+    int i;
+    double av1 = 0.0, av2 = 0.0, y11 = 0.0, y22 = 0.0, y12 = 0.0, c;
+    
+    if (n <= 1) {
+      return 1.0;
+    }
+    for (i = 0; i < n; i++) {
+      av1 += y1[i];
+      av2 += y2[i];
+    }
+    av1 /= (double) n;
+    av2 /= (double) n;
+    for (i = 0; i < n; i++) {
+      y11 += (y1[i] - av1) * (y1[i] - av1);
+      y22 += (y2[i] - av2) * (y2[i] - av2);
+      y12 += (y1[i] - av1) * (y2[i] - av2);
+    }
+    if (y11 * y22 == 0.0) {
+      c=1.0;
+    } else {
+      c = y12 / Math.sqrt(Math.abs(y11 * y22));
+    }
+    
+    return c;
+  }
+
+  /**
+   * Removes all occurrences of a string from another string.
+   *
+   * @param inString the string to remove substrings from.
+   * @param substring the substring to remove.
+   * @return the input string with occurrences of substring removed.
+   */
+  public static String removeSubstring(String inString, String substring) {
+
+    StringBuffer result = new StringBuffer();
+    int oldLoc = 0, loc = 0;
+    while ((loc = inString.indexOf(substring, oldLoc))!= -1) {
+      result.append(inString.substring(oldLoc, loc));
+      oldLoc = loc + substring.length();
+    }
+    result.append(inString.substring(oldLoc));
+    return result.toString();
+  }
+
+  /**
+   * Replaces with a new string, all occurrences of a string from 
+   * another string.
+   *
+   * @param inString the string to replace substrings in.
+   * @param substring the substring to replace.
+   * @param replaceString the replacement substring
+   * @return the input string with occurrences of substring replaced.
+   */
+  public static String replaceSubstring(String inString, String subString,
+					String replaceString) {
+
+    StringBuffer result = new StringBuffer();
+    int oldLoc = 0, loc = 0;
+    while ((loc = inString.indexOf(subString, oldLoc))!= -1) {
+      result.append(inString.substring(oldLoc, loc));
+      result.append(replaceString);
+      oldLoc = loc + subString.length();
+    }
+    result.append(inString.substring(oldLoc));
+    return result.toString();
+  }
+
+
+  /**
+   * Pads a string to a specified length, inserting spaces on the left
+   * as required. If the string is too long, characters are removed (from
+   * the right).
+   *
+   * @param inString the input string
+   * @param length the desired length of the output string
+   * @return the output string
+   */
+  public static String padLeft(String inString, int length) {
+
+    return fixStringLength(inString, length, false);
+  }
+  
+  /**
+   * Pads a string to a specified length, inserting spaces on the right
+   * as required. If the string is too long, characters are removed (from
+   * the right).
+   *
+   * @param inString the input string
+   * @param length the desired length of the output string
+   * @return the output string
+   */
+  public static String padRight(String inString, int length) {
+
+    return fixStringLength(inString, length, true);
+  }
+  
+  /**
+   * Pads a string to a specified length, inserting spaces as
+   * required. If the string is too long, characters are removed (from
+   * the right).
+   *
+   * @param inString the input string
+   * @param length the desired length of the output string
+   * @param right true if inserted spaces should be added to the right
+   * @return the output string
+   */
+  private static String fixStringLength(String inString, int length,
+					boolean right) {
+
+    if (inString.length() < length) {
+      while (inString.length() < length) {
+	inString = (right ? inString.concat(" ") : " ".concat(inString));
+      }
+    } else if (inString.length() > length) {
+      inString = inString.substring(0, length);
+    }
+    return inString;
+  }
+ 
+  /**
+   * Rounds a double and converts it into String.
+   *
+   * @param value the double value
+   * @param afterDecimalPoint the (maximum) number of digits permitted
+   * after the decimal point
+   * @return the double as a formatted string
+   */
+  public static String doubleToString(double value, int afterDecimalPoint) {
+    
+    StringBuffer stringBuffer;
+    double temp;
+    int i,dotPosition;
+    long precisionValue;
+    
+    temp = value * Math.pow(10.0, afterDecimalPoint);
+    if (Math.abs(temp) < Long.MAX_VALUE) {
+      precisionValue = 	(temp > 0) ? (long)(temp + 0.5) 
+                                   : -(long)(Math.abs(temp) + 0.5);
+      if (precisionValue == 0) {
+	stringBuffer = new StringBuffer(String.valueOf(0));
+      } else {
+	stringBuffer = new StringBuffer(String.valueOf(precisionValue));
+      }
+      if (afterDecimalPoint == 0) {
+	return stringBuffer.toString();
+      }
+      dotPosition = stringBuffer.length() - afterDecimalPoint;
+      while (((precisionValue < 0) && (dotPosition < 1)) ||
+	     (dotPosition < 0)) {
+	if (precisionValue < 0) {
+	  stringBuffer.insert(1, 0);
+	} else {
+	  stringBuffer.insert(0, 0);
+	}
+	dotPosition++;
+      }
+      stringBuffer.insert(dotPosition, '.');
+      if ((precisionValue < 0) && (stringBuffer.charAt(1) == '.')) {
+	stringBuffer.insert(1, 0);
+      } else if (stringBuffer.charAt(0) == '.') {
+	stringBuffer.insert(0, 0);
+      }
+      int currentPos = stringBuffer.length() - 1;
+      while ((currentPos > dotPosition) &&
+	     (stringBuffer.charAt(currentPos) == '0')) {
+	stringBuffer.setCharAt(currentPos--, ' ');
+      }
+      if (stringBuffer.charAt(currentPos) == '.') {
+	stringBuffer.setCharAt(currentPos, ' ');
+      }
+      
+      return stringBuffer.toString().trim();
+    }
+    return new String("" + value);
+  }
+
+  /**
+   * Rounds a double and converts it into a formatted decimal-justified String.
+   * Trailing 0's are replaced with spaces.
+   *
+   * @param value the double value
+   * @param width the width of the string
+   * @param afterDecimalPoint the number of digits after the decimal point
+   * @return the double as a formatted string
+   */
+  public static String doubleToString(double value, int width,
+				      int afterDecimalPoint) {
+    
+    String tempString = doubleToString(value, afterDecimalPoint);
+    char[] result;
+    int dotPosition;
+
+    if ((afterDecimalPoint >= width) 
+        || (tempString.indexOf('E') != -1)) { // Protects sci notation
+      return tempString;
+    }
+
+    // Initialize result
+    result = new char[width];
+    for (int i = 0; i < result.length; i++) {
+      result[i] = ' ';
+    }
+
+    if (afterDecimalPoint > 0) {
+      // Get position of decimal point and insert decimal point
+      dotPosition = tempString.indexOf('.');
+      if (dotPosition == -1) {
+	dotPosition = tempString.length();
+      } else {
+	result[width - afterDecimalPoint - 1] = '.';
+      }
+    } else {
+      dotPosition = tempString.length();
+    }
+    
+
+    int offset = width - afterDecimalPoint - dotPosition;
+    if (afterDecimalPoint > 0) {
+      offset--;
+    }
+
+    // Not enough room to decimal align within the supplied width
+    if (offset < 0) {
+      return tempString;
+    }
+
+    // Copy characters before decimal point
+    for (int i = 0; i < dotPosition; i++) {
+      result[offset + i] = tempString.charAt(i);
+    }
+
+    // Copy characters after decimal point
+    for (int i = dotPosition + 1; i < tempString.length(); i++) {
+      result[offset + i] = tempString.charAt(i);
+    }
+
+    return new String(result);
+  }
+
+  /**
+   * Tests if a is equal to b.
+   *
+   * @param a a double
+   * @param b a double
+   */
+  public static boolean eq(double a, double b){
+    
+    return (a - b < SMALL) && (b - a < SMALL); 
+  }
+
+  /**
+   * Checks if the given array contains any non-empty options.
+   *
+   * @param strings an array of strings
+   * @exception Exception if there are any non-empty options
+   */
+  public static void checkForRemainingOptions(String [] options) 
+    throws Exception {
+    
+    int illegalOptionsFound = 0;
+    StringBuffer text = new StringBuffer();
+
+    if (options == null) {
+      return;
+    }
+    for (int i = 0; i < options.length; i++) {
+      if (options[i].length() > 0) {
+	illegalOptionsFound++;
+	text.append(options[i] + ' ');
+      }
+    }
+    if (illegalOptionsFound > 0) {
+      throw new Exception("Illegal options: " + text);
+    }
+  }
+  
+  /**
+   * Checks if the given array contains the flag "-Char". Stops
+   * searching at the first marker "--". If the flag is found,
+   * it is replaced with the empty string.
+   *
+   * @param flag the character indicating the flag.
+   * @param strings the array of strings containing all the options.
+   * @return true if the flag was found
+   * @exception Exception if an illegal option was found
+   */
+
+  public static boolean getFlag(char flag, String [] options) 
+    throws Exception {
+
+    if (options == null) {
+      return false;
+    }
+    for (int i = 0; i < options.length; i++) {
+      if ((options[i].length() > 1) && (options[i].charAt(0) == '-')) {
+	try {
+	  Double dummy = Double.valueOf(options[i]);
+	} catch (NumberFormatException e) {
+	  if (options[i].length() > 2) {
+	    throw new Exception("Illegal option: " + options[i]);
+	  }
+	  if (options[i].charAt(1) == flag) {
+	    options[i] = "";
+	    return true;
+	  }
+	  if (options[i].charAt(1) == '-') {
+	    return false;
+	  }
+	}
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Gets an option indicated by a flag "-Char" from the given array
+   * of strings. Stops searching at the first marker "--". Replaces 
+   * flag and option with empty strings.
+   *
+   * @param flag the character indicating the option.
+   * @param options the array of strings containing all the options.
+   * @return the indicated option or an empty string
+   * @exception Exception if the option indicated by the flag can't be found
+   */
+  public static String getOption(char flag, String [] options) 
+    throws Exception {
+
+    String newString;
+
+    if (options == null)
+      return "";
+    for (int i = 0; i < options.length; i++) {
+      if ((options[i].length() > 0) && (options[i].charAt(0) == '-')) {
+	
+	// Check if it is a negative number
+	try {
+	  Double dummy = Double.valueOf(options[i]);
+	} catch (NumberFormatException e) {
+	  if (options[i].length() > 2) {
+	    throw new Exception("Illegal option: " + options[i]);
+	  }
+	  if (options[i].charAt(1) == flag) {
+	    if (i + 1 == options.length) {
+	      throw new Exception("No value given for -" + flag + " option.");
+	    }
+	    options[i] = "";
+	    newString = new String(options[i + 1]);
+	    options[i + 1] = "";
+	    return newString;
+	  }
+	  if (options[i].charAt(1) == '-') {
+	    return "";
+	  }
+	}
+      }
+    }
+    return "";
+  }
+
+  /**
+   * Quotes a string if it contains special characters.
+   * 
+   * The following rules are applied:
+   *
+   * A character is backquoted version of it is one 
+   * of <tt>" ' % \ \n \r \t</tt>.
+   *
+   * A string is enclosed within single quotes if a character has been
+   * backquoted using the previous rule above or contains 
+   * <tt>{ }</tt> or is exactly equal to the strings 
+   * <tt>, ? space or ""</tt> (empty string).
+   *
+   * A quoted question mark distinguishes it from the missing value which
+   * is represented as an unquoted question mark in arff files.
+   *
+   * @param string the string to be quoted
+   * @return the string (possibly quoted)
+   */
+  public static String quote(String string) {
+      boolean quote = false;
+
+      // backquote the following characters 
+      if ((string.indexOf('\n') != -1) || (string.indexOf('\r') != -1) || 
+	  (string.indexOf('\'') != -1) || (string.indexOf('"') != -1) || 
+	  (string.indexOf('\\') != -1) || 
+	  (string.indexOf('\t') != -1) || (string.indexOf('%') != -1)) {
+	  string = backQuoteChars(string);
+	  quote = true;
+      }
+
+      // Enclose the string in 's if the string contains a recently added
+      // backquote or contains one of the following characters.
+      if((quote == true) || 
+	 (string.indexOf('{') != -1) || (string.indexOf('}') != -1) ||
+	 (string.indexOf(',') != -1) || (string.equals("?")) ||
+	 (string.indexOf(' ') != -1) || (string.equals(""))) {
+	  string = ("'".concat(string)).concat("'");
+      }
+
+      return string;
+  }
+
+  /**
+   * Converts carriage returns and new lines in a string into \r and \n.
+   * Backquotes the following characters: ` " \ \t and %
+   * @param string the string
+   * @return the converted string
+   */
+  public static String backQuoteChars(String string) {
+
+    int index;
+    StringBuffer newStringBuffer;
+
+    // replace each of the following characters with the backquoted version
+    char   charsFind[] =    {'\\',   '\'',  '\t',  '"',    '%'};
+    String charsReplace[] = {"\\\\", "\\'", "\\t", "\\\"", "\\%"};
+    for(int i = 0; i < charsFind.length; i++) {
+	if (string.indexOf(charsFind[i]) != -1 ) {
+	    newStringBuffer = new StringBuffer();
+	    while ((index = string.indexOf(charsFind[i])) != -1) {
+		if (index > 0) {
+		    newStringBuffer.append(string.substring(0, index));
+		}
+		newStringBuffer.append(charsReplace[i]);
+		if ((index + 1) < string.length()) {
+		    string = string.substring(index + 1);
+		} else {
+		    string = "";
+		}
+	    }
+	    newStringBuffer.append(string);
+	    string = newStringBuffer.toString();
+	}
+    }
+
+    return Utils.convertNewLines(string);
+  }
+
+  /**
+   * Converts carriage returns and new lines in a string into \r and \n.
+   * @param string the string
+   * @return the converted string
+   */
+  public static String convertNewLines(String string) {
+    
+    int index;
+
+    // Replace with \n
+    StringBuffer newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf('\n')) != -1) {
+      if (index > 0) {
+	newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\\');
+      newStringBuffer.append('n');
+      if ((index + 1) < string.length()) {
+	string = string.substring(index + 1);
+      } else {
+	string = "";
+      }
+    }
+    newStringBuffer.append(string);
+    string = newStringBuffer.toString();
+
+    // Replace with \r
+    newStringBuffer = new StringBuffer();
+    while ((index = string.indexOf('\r')) != -1) {
+      if (index > 0) {
+	newStringBuffer.append(string.substring(0, index));
+      }
+      newStringBuffer.append('\\');
+      newStringBuffer.append('r');
+      if ((index + 1) < string.length()){
+	string = string.substring(index + 1);
+      } else {
+	string = "";
+      }
+    }
+    newStringBuffer.append(string);
+    return newStringBuffer.toString();
+  }
+    
+
+  /**
+   * Returns the secondary set of options (if any) contained in
+   * the supplied options array. The secondary set is defined to
+   * be any options after the first "--". These options are removed from
+   * the original options array.
+   *
+   * @param options the input array of options
+   * @return the array of secondary options
+   */
+  public static String [] partitionOptions(String [] options) {
+
+    for (int i = 0; i < options.length; i++) {
+      if (options[i].equals("--")) {
+	options[i++] = "";
+	String [] result = new String [options.length - i];
+	for (int j = i; j < options.length; j++) {
+	  result[j - i] = options[j];
+	  options[j] = "";
+	}
+	return result;
+      }
+    }
+    return new String [0];
+  }
+
+  /**
+   * Split up a string containing options into an array of strings,
+   * one for each option.
+   *
+   * @param optionString the string containing the options
+   * @return the array of options
+   */
+  public static String [] splitOptions(String optionString) {
+
+    FastVector optionsVec = new FastVector();
+    StringTokenizer st = new StringTokenizer(optionString);
+    while (st.hasMoreTokens())
+      optionsVec.addElement(st.nextToken());
+
+    String [] options = new String[optionsVec.size()];
+    for (int i = 0; i < optionsVec.size(); i++) {
+      options[i] = (String)optionsVec.elementAt(i);
+    }
+    return options;
+  }
+
+  /**
+   * Joins all the options in an option array into a single string,
+   * as might be used on the command line.
+   *
+   * @param optionArray the array of options
+   * @return the string containing all options.
+   */
+  public static String joinOptions(String [] optionArray) {
+
+    String optionString = "";
+    for (int i = 0; i < optionArray.length; i++) {
+      if (optionArray[i].equals("")) {
+	continue;
+      }
+      if (optionArray[i].indexOf(' ') != -1) {
+	optionString += '"' + optionArray[i] + '"';
+      } else {
+	optionString += optionArray[i];
+      }
+      optionString += " ";
+    }
+    return optionString.trim();
+  }
+  
+  /**
+   * Creates a new instance of an object given it's class name and
+   * (optional) arguments to pass to it's setOptions method. If the
+   * object implements OptionHandler and the options parameter is
+   * non-null, the object will have it's options set. Example use:<p>
+   *
+   * <code> <pre>
+   * String classifierName = Utils.getOption('W', options);
+   * Classifier c = (Classifier)Utils.forName(Classifier.class,
+   *                                          classifierName,
+   *                                          options);
+   * setClassifier(c);
+   * </pre></code>
+   *
+   * @param classType the class that the instantiated object should
+   * be assignable to -- an exception is thrown if this is not the case
+   * @param className the fully qualified class name of the object
+   * @param options an array of options suitable for passing to setOptions. May
+   * be null. Any options accepted by the object will be removed from the
+   * array.
+   * @return the newly created object, ready for use.
+   * @exception Exception if the class name is invalid, or if the
+   * class is not assignable to the desired class type, or the options
+   * supplied are not acceptable to the object
+   */
+  public static Object forName(Class classType,
+			       String className,
+			       String [] options) throws Exception {
+
+    Class c = null;
+    try {
+      c = Class.forName(className);
+    } catch (Exception ex) {
+      throw new Exception("Can't find class called: " + className);
+    }
+    if (!classType.isAssignableFrom(c)) {
+      throw new Exception(classType.getName() + " is not assignable from "
+			  + className);
+    }
+    Object o = c.newInstance();
+    if ((o instanceof OptionHandler)
+	&& (options != null)) {
+      ((OptionHandler)o).setOptions(options);
+      Utils.checkForRemainingOptions(options);
+    }
+    return o;
+  }
+
+  /**
+   * Computes entropy for an array of integers.
+   *
+   * @param counts array of counts
+   * @returns - a log2 a - b log2 b - c log2 c + (a+b+c) log2 (a+b+c)
+   * when given array [a b c]
+   */
+  public static double info(int counts[]) {
+    
+    int total = 0; int c;
+    double x = 0;
+    for (int j = 0; j < counts.length; j++) {
+      x -= xlogx(counts[j]);
+      total += counts[j];
+    }
+    return x + xlogx(total);
+  }
+
+  /**
+   * Tests if a is smaller or equal to b.
+   *
+   * @param a a double
+   * @param b a double
+   */
+  public static boolean smOrEq(double a,double b) {
+    
+    return (a-b < SMALL);
+  }
+
+  /**
+   * Tests if a is greater or equal to b.
+   *
+   * @param a a double
+   * @param b a double
+   */
+  public static boolean grOrEq(double a,double b) {
+    
+    return (b-a < SMALL);
+  }
+  
+  /**
+   * Tests if a is smaller than b.
+   *
+   * @param a a double
+   * @param b a double
+   */
+  public static boolean sm(double a,double b) {
+    
+    return (b-a > SMALL);
+  }
+
+  /**
+   * Tests if a is smaller than b.
+   *
+   * @param a a double
+   * @param b a double 
+   */
+  public static boolean gr(double a,double b) {
+    
+    return (a-b > SMALL);
+  }
+
+  /**
+   * Returns the logarithm of a for base 2.
+   *
+   * @param a a double
+   */
+  public static double log2(double a) {
+    
+    return Math.log(a) / log2;
+  }
+
+  /**
+   * Returns index of maximum element in a given
+   * array of doubles. First maximum is returned.
+   *
+   * @param doubles the array of doubles
+   * @return the index of the maximum element
+   */
+  public static int maxIndex(double [] doubles) {
+
+    double maximum = 0;
+    int maxIndex = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      if ((i == 0) || (doubles[i] > maximum)) {
+	maxIndex = i;
+	maximum = doubles[i];
+      }
+    }
+
+    return maxIndex;
+  }
+
+  /**
+   * Returns index of maximum element in a given
+   * array of integers. First maximum is returned.
+   *
+   * @param ints the array of integers
+   * @return the index of the maximum element
+   */
+  public static int maxIndex(int [] ints) {
+
+    int maximum = 0;
+    int maxIndex = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      if ((i == 0) || (ints[i] > maximum)) {
+	maxIndex = i;
+	maximum = ints[i];
+      }
+    }
+
+    return maxIndex;
+  }
+
+  /**
+   * Computes the mean for an array of doubles.
+   *
+   * @param vector the array
+   * @return the mean
+   */
+  public static double mean(double[] vector) {
+  
+    double sum = 0;
+
+    if (vector.length == 0) {
+      return 0;
+    }
+    for (int i = 0; i < vector.length; i++) {
+      sum += vector[i];
+    }
+    return sum / (double) vector.length;
+  }
+
+  /**
+   * Returns index of minimum element in a given
+   * array of integers. First minimum is returned.
+   *
+   * @param ints the array of integers
+   * @return the index of the minimum element
+   */
+  public static int minIndex(int [] ints) {
+
+    int minimum = 0;
+    int minIndex = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      if ((i == 0) || (ints[i] < minimum)) {
+	minIndex = i;
+	minimum = ints[i];
+      }
+    }
+
+    return minIndex;
+  }
+
+  /**
+   * Returns index of minimum element in a given
+   * array of doubles. First minimum is returned.
+   *
+   * @param doubles the array of doubles
+   * @return the index of the minimum element
+   */
+  public static int minIndex(double [] doubles) {
+
+    double minimum = 0;
+    int minIndex = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      if ((i == 0) || (doubles[i] < minimum)) {
+	minIndex = i;
+	minimum = doubles[i];
+      }
+    }
+
+    return minIndex;
+  }
+
+  /**
+   * Normalizes the doubles in the array by their sum.
+   *
+   * @param doubles the array of double
+   * @exception IllegalArgumentException if sum is Zero or NaN
+   */
+  public static void normalize(double[] doubles) {
+
+    double sum = 0;
+    for (int i = 0; i < doubles.length; i++) {
+      sum += doubles[i];
+    }
+    normalize(doubles, sum);
+  }
+
+  /**
+   * Normalizes the doubles in the array using the given value.
+   *
+   * @param doubles the array of double
+   * @param sum the value by which the doubles are to be normalized
+   * @exception IllegalArgumentException if sum is zero or NaN
+   */
+  public static void normalize(double[] doubles, double sum) {
+
+    if (Double.isNaN(sum)) {
+      throw new IllegalArgumentException("Can't normalize array. Sum is NaN.");
+    }
+    if (sum == 0) {
+      // Maybe this should just be a return.
+      throw new IllegalArgumentException("Can't normalize array. Sum is zero.");
+    }
+    for (int i = 0; i < doubles.length; i++) {
+      doubles[i] /= sum;
+    }
+  }
+
+  /**
+   * Rounds a double to the next nearest integer value. The JDK version
+   * of it doesn't work properly.
+   *
+   * @param value the double value
+   * @return the resulting integer value
+   */
+  public static int round(double value) {
+
+    int roundedValue = value > 0
+      ? (int)(value + 0.5)
+      : -(int)(Math.abs(value) + 0.5);
+    
+    return roundedValue;
+  }
+
+  /**
+   * Rounds a double to the given number of decimal places.
+   *
+   * @param value the double value
+   * @param afterDecimalPoint the number of digits after the decimal point
+   * @return the double rounded to the given precision
+   */
+  public static double roundDouble(double value,int afterDecimalPoint) {
+
+    double mask = Math.pow(10.0, (double)afterDecimalPoint);
+
+    return (double)(Math.round(value * mask)) / mask;
+  }
+
+  /**
+   * Sorts a given array of integers in ascending order and returns an 
+   * array of integers with the positions of the elements of the original 
+   * array in the sorted array. The sort is stable. (Equal elements remain
+   * in their original order.)
+   *
+   * @param array this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted
+   * array.
+   */
+  public static int[] sort(int [] array) {
+
+    int [] index = new int[array.length];
+    int [] newIndex = new int[array.length];
+    int [] helpIndex;
+    int numEqual;
+    
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+    }
+    quickSort(array, index, 0, array.length - 1);
+
+    // Make sort stable
+    int i = 0;
+    while (i < index.length) {
+      numEqual = 1;
+      for (int j = i + 1; ((j < index.length)
+			   && (array[index[i]] == array[index[j]]));
+	   j++) {
+	numEqual++;
+      }
+      if (numEqual > 1) {
+	helpIndex = new int[numEqual];
+	for (int j = 0; j < numEqual; j++) {
+	  helpIndex[j] = i + j;
+	}
+	quickSort(index, helpIndex, 0, numEqual - 1);
+	for (int j = 0; j < numEqual; j++) {
+	  newIndex[i + j] = index[helpIndex[j]];
+	}
+	i += numEqual;
+      } else {
+	newIndex[i] = index[i];
+	i++;
+      }
+    }
+    return newIndex;
+  }
+
+  /**
+   * Sorts a given array of doubles in ascending order and returns an
+   * array of integers with the positions of the elements of the
+   * original array in the sorted array. NOTE THESE CHANGES: the sort
+   * is no longer stable and it doesn't use safe floating-point
+   * comparisons anymore. Occurrences of Double.NaN are treated as 
+   * Double.MAX_VALUE
+   *
+   * @param array this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted
+   * array.  
+   */
+  public static int[] sort(double [] array) {
+
+    int [] index = new int[array.length];
+    array = (double [])array.clone();
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+      if (Double.isNaN(array[i])) {
+        array[i] = Double.MAX_VALUE;
+      }
+    }
+    quickSort(array, index, 0, array.length - 1);
+    return index;
+  }
+
+  /**
+   * Sorts a given array of doubles in ascending order and returns an 
+   * array of integers with the positions of the elements of the original 
+   * array in the sorted array. The sort is stable (Equal elements remain
+   * in their original order.) Occurrences of Double.NaN are treated as 
+   * Double.MAX_VALUE
+   *
+   * @param array this array is not changed by the method!
+   * @return an array of integers with the positions in the sorted
+   * array.
+   */
+  public static int[] stableSort(double [] array){
+
+    int [] index = new int[array.length];
+    int [] newIndex = new int[array.length];
+    int [] helpIndex;
+    int numEqual;
+    
+    array = (double [])array.clone();
+    for (int i = 0; i < index.length; i++) {
+      index[i] = i;
+      if (Double.isNaN(array[i])) {
+        array[i] = Double.MAX_VALUE;
+      }
+    }
+    quickSort(array,index,0,array.length-1);
+
+    // Make sort stable
+
+    int i = 0;
+    while (i < index.length) {
+      numEqual = 1;
+      for (int j = i+1; ((j < index.length) && Utils.eq(array[index[i]],
+							array[index[j]])); j++)
+	numEqual++;
+      if (numEqual > 1) {
+	helpIndex = new int[numEqual];
+	for (int j = 0; j < numEqual; j++)
+	  helpIndex[j] = i+j;
+	quickSort(index, helpIndex, 0, numEqual-1);
+	for (int j = 0; j < numEqual; j++) 
+	  newIndex[i+j] = index[helpIndex[j]];
+	i += numEqual;
+      } else {
+	newIndex[i] = index[i];
+	i++;
+      }
+    }
+
+    return newIndex;
+  }
+
+  /**
+   * Computes the variance for an array of doubles.
+   *
+   * @param vector the array
+   * @return the variance
+   */
+  public static double variance(double[] vector) {
+  
+    double sum = 0, sumSquared = 0;
+
+    if (vector.length <= 1) {
+      return 0;
+    }
+    for (int i = 0; i < vector.length; i++) {
+      sum += vector[i];
+      sumSquared += (vector[i] * vector[i]);
+    }
+    return (sumSquared - (sum * sum / (double) vector.length)) / 
+      (double) (vector.length - 1);
+  }
+
+  /**
+   * Computes the sum of the elements of an array of doubles.
+   *
+   * @param doubles the array of double
+   * @returns the sum of the elements
+   */
+  public static double sum(double[] doubles) {
+
+    double sum = 0;
+
+    for (int i = 0; i < doubles.length; i++) {
+      sum += doubles[i];
+    }
+    return sum;
+  }
+
+  /**
+   * Computes the sum of the elements of an array of integers.
+   *
+   * @param ints the array of integers
+   * @returns the sum of the elements
+   */
+  public static int sum(int[] ints) {
+
+    int sum = 0;
+
+    for (int i = 0; i < ints.length; i++) {
+      sum += ints[i];
+    }
+    return sum;
+  }
+
+  /**
+   * Returns c*log2(c) for a given integer value c.
+   *
+   * @param c an integer value
+   * @returns c*log2(c) (but is careful to return 0 if c is 0)
+   */
+  public static double xlogx(int c) {
+    
+    if (c == 0) {
+      return 0.0;
+    }
+    return c * Utils.log2((double) c);
+  }
+
+  /**
+   * Implements quicksort for an array of indices.
+   *
+   * @param array the array of integers to be sorted
+   * @param index the index which should contain the positions in the
+   * sorted array
+   * @param lo0 the first index of the subset to be sorted
+   * @param hi0 the last index of the subset to be sorted
+   */
+  private static void quickSort(int [] array, int [] index,
+				int lo0, int hi0) {
+
+    int lo = lo0;
+    int hi = hi0;
+    int mid;
+    int help;
+    
+    if (hi0 > lo0) {
+      
+      // Arbitrarily establishing partition element as the midpoint of
+      // the array.
+      mid = array[index[(lo0 + hi0) / 2]];
+
+      // loop through the array until indices cross
+      while (lo <= hi) {
+	
+	// find the first element that is greater than or equal to  
+	// the partition element starting from the left Index.
+	while ((array[index[lo]] < mid) && (lo < hi0)) {
+	  ++lo;
+	}
+	
+	// find an element that is smaller than or equal to 
+	// the partition element starting from the right Index.
+	while ((array[index[hi]] > mid) && (hi > lo0)) {
+	  --hi;
+	}
+	
+	// if the indexes have not crossed, swap
+	if (lo <= hi) {
+	  help = index[lo];
+	  index[lo] = index[hi];
+	  index[hi] = help;
+	  ++lo;
+	  --hi;
+	}
+      }
+      
+      // If the right index has not reached the left side of array
+      // must now sort the left partition.
+      if (lo0 < hi) {
+	quickSort(array, index, lo0, hi);
+      }
+      
+      // If the left index has not reached the right side of array
+      // must now sort the right partition.
+      if (lo < hi0) {
+	quickSort(array, index, lo, hi0);
+      }
+    }
+  }
+
+  /**
+   * Implements unsafe quicksort for an array of indices.
+   *
+   * @param array the array of doubles to be sorted
+   * @param index the index which should contain the positions in the
+   * sorted array
+   * @param lo0 the first index of the subset to be sorted
+   * @param hi0 the last index of the subset to be sorted
+   */
+  private static void quickSort(double [] array, int [] index, int lo0, int hi0) {
+
+    int lo = lo0;
+    int hi = hi0;
+    double mid;
+    int help;
+    
+    if (hi0 > lo0) {
+      
+      // Arbitrarily establishing partition element as the midpoint of
+      // the array.
+      mid = array[index[(lo0 + hi0) / 2]];
+
+      // loop through the array until indices cross
+      while (lo <= hi) {
+	
+	// find the first element that is greater than or equal to  
+	// the partition element starting from the left Index.
+	while ((array[index[lo]] < mid) && (lo < hi0)) {
+	  ++lo;
+	}
+	
+	// find an element that is smaller than or equal to 
+	// the partition element starting from the right Index.
+	while ((array[index[hi]] > mid) && (hi > lo0)) {
+	  --hi;
+	}
+	
+	// if the indexes have not crossed, swap
+	if (lo <= hi) {
+	  help = index[lo];
+	  index[lo] = index[hi];
+	  index[hi] = help;
+	  ++lo;
+	  --hi;
+	}
+      }
+      
+      // If the right index has not reached the left side of array
+      // must now sort the left partition.
+      if (lo0 < hi) {
+	quickSort(array, index, lo0, hi);
+      }
+      
+      // If the left index has not reached the right side of array
+      // must now sort the right partition.
+      if (lo < hi0) {
+	quickSort(array, index, lo, hi0);
+      }
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param ops some dummy options
+   */
+  public static void main(String[] ops) {
+
+    double[] doubles = {4.5, 6.7, Double.NaN, 3.4, 4.8, 1.2, 3.4};
+    int[] ints = {12, 6, 2, 18, 16, 6, 7, 5};
+
+    try {
+
+      // Option handling
+      System.out.println("First option split up:");
+      if (ops.length > 0) {
+	String[] firstOptionSplitUp = Utils.splitOptions(ops[0]);
+	for (int i = 0; i < firstOptionSplitUp.length; i ++) {
+	  System.out.println(firstOptionSplitUp[i]);
+	}
+      }					       
+      System.out.println("Partitioned options: ");
+      String[] partitionedOptions = Utils.partitionOptions(ops);
+      for (int i  = 0; i < partitionedOptions.length; i++) {
+	System.out.println(partitionedOptions[i]);
+      }
+      System.out.println("Get flag -f: " + Utils.getFlag('f', ops));
+      System.out.println("Get option -o: " + Utils.getOption('o', ops));
+      System.out.println("Checking for remaining options... ");
+      Utils.checkForRemainingOptions(ops);
+      
+      // Statistics
+      System.out.println("Original array (doubles): ");
+      for (int i = 0; i < doubles.length; i++) {
+	System.out.print(doubles[i] + " ");
+      }
+      System.out.println();
+      System.out.println("Original array (ints): ");
+      for (int i = 0; i < ints.length; i++) {
+	System.out.print(ints[i] + " ");
+      }
+      System.out.println();
+      System.out.println("Correlation: " + Utils.correlation(doubles, doubles, 
+							     doubles.length));
+      System.out.println("Mean: " + Utils.mean(doubles));
+      System.out.println("Variance: " + Utils.variance(doubles));
+      System.out.println("Sum (doubles): " + Utils.sum(doubles));
+      System.out.println("Sum (ints): " + Utils.sum(ints));
+      System.out.println("Max index (doubles): " + Utils.maxIndex(doubles));
+      System.out.println("Max index (ints): " + Utils.maxIndex(ints));
+      System.out.println("Min index (doubles): " + Utils.minIndex(doubles));
+      System.out.println("Min index (ints): " + Utils.minIndex(ints));
+      
+      // Sorting and normalizing
+      System.out.println("Sorted array (doubles): ");
+      int[] sorted = Utils.sort(doubles);
+      for (int i = 0; i < doubles.length; i++) {
+	System.out.print(doubles[sorted[i]] + " ");
+      }
+      System.out.println();
+      System.out.println("Normalized array (doubles): ");
+      Utils.normalize(doubles);
+      for (int i = 0; i < doubles.length; i++) {
+	System.out.print(doubles[i] + " ");
+      }
+      System.out.println();
+      System.out.println("Normalized again (doubles): ");
+      Utils.normalize(doubles, Utils.sum(doubles));
+      for (int i = 0; i < doubles.length; i++) {
+	System.out.print(doubles[i] + " ");
+      }
+      System.out.println();
+      
+      // Pretty-printing
+      System.out.println("-4.58: " + Utils.doubleToString(-4.57826535, 2));
+      System.out.println("-6.78: " + Utils.doubleToString(-6.78214234, 6,2));
+      
+      // Comparisons
+      System.out.println("5.70001 == 5.7 ? " + Utils.eq(5.70001, 5.7));
+      System.out.println("5.70001 > 5.7 ? " + Utils.gr(5.70001, 5.7));
+      System.out.println("5.70001 >= 5.7 ? " + Utils.grOrEq(5.70001, 5.7));
+      System.out.println("5.7 < 5.70001 ? " + Utils.sm(5.7, 5.70001));
+      System.out.println("5.7 <= 5.70001 ? " + Utils.smOrEq(5.7, 5.70001));
+      
+      // Math
+      System.out.println("Info (ints): " + Utils.info(ints));
+      System.out.println("log2(4.6): " + Utils.log2(4.6));
+      System.out.println("5 * log(5): " + Utils.xlogx(5));
+      System.out.println("5.5 rounded: " + Utils.round(5.5));
+      System.out.println("5.55555 rounded to 2 decimal places: " + 
+			 Utils.roundDouble(5.55555, 2));
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
+  
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WeightedInstancesHandler.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WeightedInstancesHandler.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WeightedInstancesHandler.java	(revision 16561)
@@ -0,0 +1,43 @@
+/*
+ *    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.
+ */
+
+/*
+ *    WeightedInstancesHandler.java
+ *    Copyright (C) 1999 Eibe Frank
+ *
+ */
+
+package weka.core;
+
+/** 
+ * Interface to something that makes use of the information provided
+ * by instance weights.
+ *
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$ 
+*/
+public interface WeightedInstancesHandler {
+
+  // Nothing in here, because the class is just used as an indicator
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WekaException.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WekaException.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/core/WekaException.java	(revision 16561)
@@ -0,0 +1,50 @@
+/*
+ *    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.
+ */
+
+/*
+ *    WekaException.java
+ *    Copyright (C) 2001 Webmind Corp.
+ */
+
+package weka.core;
+
+/**
+ * <code>WekaException</code> is used when some Weka-specific
+ * checked exception must be raised.
+ *
+ * @author <a href="mailto:len@webmind.com">Len Trigg</a>
+ * @version $Revision$
+ */
+public class WekaException extends Exception {
+
+  /**
+   * Creates a new <code>WekaException</code> instance
+   * with no detail message.
+   */
+  public WekaException() { 
+    super(); 
+  }
+
+  /**
+   * Creates a new <code>WekaException</code> instance
+   * with a specified message.
+   *
+   * @param messagae a <code>String</code> containing the message.
+   */
+  public WekaException(String message) { 
+    super(message); 
+  }
+}
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/experiment/Stats.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/experiment/Stats.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/experiment/Stats.java	(revision 16561)
@@ -0,0 +1,188 @@
+/*
+ *    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.
+ */
+
+/*
+ *    Stats.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+
+package weka.experiment;
+import weka.core.Utils;
+
+/**
+ * A class to store simple statistics
+ *
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class Stats {
+  
+  /** The number of values seen */
+  public double count = 0;
+
+  /** The sum of values seen */
+  public double sum = 0;
+
+  /** The sum of values squared seen */
+  public double sumSq = 0;
+
+  /** The std deviation of values at the last calculateDerived() call */    
+  public double stdDev = Double.NaN;
+
+  /** The mean of values at the last calculateDerived() call */    
+  public double mean = Double.NaN;
+
+  /** The minimum value seen, or Double.NaN if no values seen */
+  public double min = Double.NaN;
+
+  /** The maximum value seen, or Double.NaN if no values seen */
+  public double max = Double.NaN;
+    
+  /**
+   * Adds a value to the observed values
+   *
+   * @param value the observed value
+   */
+  public void add(double value) {
+
+    add(value, 1);
+  }
+
+  /**
+   * Adds a value that has been seen n times to the observed values
+   *
+   * @param value the observed value
+   * @param n the number of times to add value
+   */
+  public void add(double value, double n) {
+
+    sum += value * n;
+    sumSq += value * value * n;
+    count += n;
+    if (Double.isNaN(min)) {
+      min = max = value;
+    } else if (value < min) {
+      min = value;
+    } else if (value > max) {
+      max = value;
+    }
+  }
+
+  /**
+   * Removes a value to the observed values (no checking is done
+   * that the value being removed was actually added). 
+   *
+   * @param value the observed value
+   */
+  public void subtract(double value) {
+
+    sum -= value;
+    sumSq -= value * value;
+    count --;
+  }
+
+  /**
+   * Tells the object to calculate any statistics that don't have their
+   * values automatically updated during add. Currently updates the mean
+   * and standard deviation.
+   */
+  public void calculateDerived() {
+
+    mean = Double.NaN;
+    stdDev = Double.NaN;
+    if (count > 0) {
+      mean = sum / count;
+      stdDev = Double.POSITIVE_INFINITY;
+      if (count > 1) {
+	stdDev = sumSq - (sum * sum) / count;
+	stdDev /= (count - 1);
+        if (stdDev < 0) {
+          System.err.println("Warning: stdDev value = " + stdDev 
+                             + " -- rounded to zero.");
+          stdDev = 0;
+        }
+	stdDev = Math.sqrt(stdDev);
+      }
+    }
+  }
+    
+  /**
+   * Returns a string summarising the stats so far.
+   *
+   * @return the summary string
+   */
+  public String toString() {
+
+    calculateDerived();
+    return
+      "Count   " + Utils.doubleToString(count, 8) + '\n'
+      + "Min     " + Utils.doubleToString(min, 8) + '\n'
+      + "Max     " + Utils.doubleToString(max, 8) + '\n'
+      + "Sum     " + Utils.doubleToString(sum, 8) + '\n'
+      + "SumSq   " + Utils.doubleToString(sumSq, 8) + '\n'
+      + "Mean    " + Utils.doubleToString(mean, 8) + '\n'
+      + "StdDev  " + Utils.doubleToString(stdDev, 8) + '\n';
+  }
+
+  /**
+   * Tests the paired stats object from the command line.
+   * reads line from stdin, expecting two values per line.
+   *
+   * @param args ignored.
+   */
+  public static void main(String [] args) {
+
+    try {
+      Stats ps = new Stats();
+      java.io.LineNumberReader r = new java.io.LineNumberReader(
+				   new java.io.InputStreamReader(System.in));
+      String line;
+      while ((line = r.readLine()) != null) {
+        line = line.trim();
+        if (line.equals("") || line.startsWith("@") || line.startsWith("%")) {
+          continue;
+        }
+	java.util.StringTokenizer s 
+          = new java.util.StringTokenizer(line, " ,\t\n\r\f");
+	int count = 0;
+	double v1 = 0;
+	while (s.hasMoreTokens()) {
+	  double val = (new Double(s.nextToken())).doubleValue();
+	  if (count == 0) {
+	    v1 = val;
+	  } else {
+            System.err.println("MSG: Too many values in line \"" 
+                               + line + "\", skipped.");
+	    break;
+	  }
+	  count++;
+	}
+        if (count == 1) {
+          ps.add(v1);
+        }
+      }
+      ps.calculateDerived();
+      System.err.println(ps);
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      System.err.println(ex.getMessage());
+    }
+  }
+
+} // Stats
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/DiscretizeFilter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/DiscretizeFilter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/DiscretizeFilter.java	(revision 16561)
@@ -0,0 +1,1292 @@
+/*
+ *    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.
+ */
+
+/*
+ *    DiscretizeFilter.java
+ *    Copyright (C) 1999 Eibe Frank,Len Trigg
+ *
+ */
+
+
+package weka.filters;
+
+import java.io.*;
+import java.util.*;
+import weka.core.*;
+
+/** 
+ * An instance filter that discretizes a range of numeric attributes in 
+ * the dataset into nominal attributes. Discretization can be either by 
+ * simple binning, or by Fayyad & Irani's MDL method (the default).<p>
+ *
+ * Valid filter-specific options are: <p>
+ *
+ * -B num <br>
+ * Specifies the (maximum) number of bins to divide numeric attributes into.
+ * (default: class-based discretisation).<p>
+ *
+ * -F <br>
+ * Use equal-frequency instead of equal-width discretization if 
+ * class-based discretisation is turned off.<p>
+ *
+ * -O <br>
+ * Optimize the number of bins using a leave-one-out estimate of the 
+ * entropy (for equal-width binning).<p>
+ *
+ * -R col1,col2-col4,... <br>
+ * Specifies list of columns to Discretize. First
+ * and last are valid indexes. (default: none) <p>
+ *
+ * -V <br>
+ * Invert matching sense.<p>
+ *
+ * -D <br>
+ * Make binary nominal attributes. <p>
+ *
+ * -E <br>
+ * Use better encoding of split point for MDL. <p>
+ *   
+ * -K <br>
+ * Use Kononeko's MDL criterion. <p>
+ * 
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @author Eibe Frank (eibe@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public class DiscretizeFilter extends Filter 
+  implements OptionHandler, WeightedInstancesHandler {
+
+  /** Stores which columns to Discretize */
+  protected Range m_DiscretizeCols = new Range();
+
+  /** The number of bins to divide the attribute into */
+  protected int m_NumBins = 10;
+
+  /** Store the current cutpoints */
+  protected double [][] m_CutPoints = null;
+
+  /** True if discretisation will be done by MDL rather than binning */
+  protected boolean m_UseMDL = true;
+
+  /** Output binary attributes for discretized attributes. */
+  protected boolean m_MakeBinary = false;
+
+  /** Use better encoding of split point for MDL. */
+  protected boolean m_UseBetterEncoding = false;
+
+  /** Use Kononenko's MDL criterion instead of Fayyad et al.'s */
+  protected boolean m_UseKononenko = false;
+
+  /** Find the number of bins using cross-validated entropy. */
+  protected boolean m_FindNumBins = false;
+
+  /** Use equal-frequency binning if unsupervised discretization turned on */
+  protected boolean m_UseEqualFrequency = false;
+
+  /** Constructor - initialises the filter */
+  public DiscretizeFilter() {
+
+    setAttributeIndices("first-last");
+  }
+
+
+  /**
+   * Gets an enumeration describing the available options
+   *
+   * @return an enumeration of all the available options
+   */
+  public Enumeration listOptions() {
+
+    Vector newVector = new Vector(7);
+
+    newVector.addElement(new Option(
+              "\tSpecifies the (maximum) number of bins to divide numeric"
+	      + " attributes into.\n"
+	      + "\t(default class-based discretization)",
+              "B", 1, "-B <num>"));
+
+    newVector.addElement(new Option(
+              "\tUse equal-frequency instead of equal-width with\n"+
+	      "\tunsupervised discretization.",
+              "F", 0, "-F"));
+
+    newVector.addElement(new Option(
+              "\tOptimize number of bins using leave-one-out estimate\n"+
+	      "\tof estimated entropy (for equal-width discretization).",
+              "O", 0, "-O"));
+
+    /* If we decide to implement loading and saving cutfiles like 
+     * the C Discretizer (which is probably not necessary)
+    newVector.addElement(new Option(
+              "\tSpecify that the cutpoints should be loaded from a file.",
+              "L", 1, "-L <file>"));
+    newVector.addElement(new Option(
+              "\tSpecify that the chosen cutpoints should be saved to a file.",
+              "S", 1, "-S <file>"));
+    */
+
+    newVector.addElement(new Option(
+              "\tSpecifies list of columns to Discretize. First"
+	      + " and last are valid indexes.\n"
+	      + "\t(default none)",
+              "R", 1, "-R <col1,col2-col4,...>"));
+
+    newVector.addElement(new Option(
+              "\tInvert matching sense of column indexes.",
+              "V", 0, "-V"));
+
+    newVector.addElement(new Option(
+              "\tOutput binary attributes for discretized attributes.",
+              "D", 0, "-D"));
+
+    newVector.addElement(new Option(
+              "\tUse better encoding of split point for MDL.",
+              "E", 0, "-E"));
+
+    newVector.addElement(new Option(
+              "\tUse Kononenko's MDL criterion.",
+              "K", 0, "-K"));
+
+    return newVector.elements();
+  }
+
+
+  /**
+   * Parses the options for this object. Valid options are: <p>
+   *
+   * -B num <br>
+   * Specifies the (maximum) number of bins to divide numeric attributes into.
+   * (default class-based discretisation).<p>
+   *
+   * -F <br>
+   * Use equal-frequency instead of equal-width discretization if 
+   * class-based discretisation is turned off.<p>
+   *
+   * -O <br>
+   * Optimize the number of bins using a leave-one-out estimate of the 
+   * entropy (for equal-width binning).<p>
+   *
+   * -R col1,col2-col4,... <br>
+   * Specifies list of columns to Discretize. First
+   * and last are valid indexes. (default none) <p>
+   *
+   * -V <br>
+   * Invert matching sense.<p>
+   *
+   * -D <br>
+   * Make binary nominal attributes. <p>
+   *
+   * -E <br>
+   * Use better encoding of split point for MDL. <p>
+   *   
+   * -K <br>
+   * Use Kononeko's MDL criterion. <p>
+   * 
+   * @param options the list of options as an array of strings
+   * @exception Exception if an option is not supported
+   */
+  public void setOptions(String[] options) throws Exception {
+
+    setMakeBinary(Utils.getFlag('D', options));
+    setUseEqualFrequency(Utils.getFlag('F', options));
+    setUseBetterEncoding(Utils.getFlag('E', options));
+    setUseKononenko(Utils.getFlag('K', options));
+    setFindNumBins(Utils.getFlag('O', options));
+    setInvertSelection(Utils.getFlag('V', options));
+    setUseMDL(true);
+
+    String numBins = Utils.getOption('B', options);
+    if (numBins.length() != 0) {
+      setBins(Integer.parseInt(numBins));
+      setUseMDL(false);
+    } else {
+      setBins(10);
+    }
+    
+    String convertList = Utils.getOption('R', options);
+    if (convertList.length() != 0) {
+      setAttributeIndices(convertList);
+    } else {
+      setAttributeIndices("first-last");
+    }
+
+    if (getInputFormat() != null) {
+      setInputFormat(getInputFormat());
+    }
+  }
+  /**
+   * Gets the current settings of the filter.
+   *
+   * @return an array of strings suitable for passing to setOptions
+   */
+  public String [] getOptions() {
+
+    String [] options = new String [12];
+    int current = 0;
+
+    if (getMakeBinary()) {
+      options[current++] = "-D";
+    }
+    if (getUseEqualFrequency()) {
+      options[current++] = "-F";
+    }
+    if (getUseBetterEncoding()) {
+      options[current++] = "-E";
+    }
+    if (getUseKononenko()) {
+      options[current++] = "-K";
+    }
+    if (getFindNumBins()) {
+      options[current++] = "-O";
+    }
+    if (getInvertSelection()) {
+      options[current++] = "-V";
+    }
+    if (!getUseMDL()) {
+      options[current++] = "-B"; options[current++] = "" + getBins();
+    }
+    if (!getAttributeIndices().equals("")) {
+      options[current++] = "-R"; options[current++] = getAttributeIndices();
+    }
+    while (current < options.length) {
+      options[current++] = "";
+    }
+    return options;
+  }
+
+
+  /**
+   * Sets the format of the input instances.
+   *
+   * @param instanceInfo an Instances object containing the input instance
+   * structure (any instances contained in the object are ignored - only the
+   * structure is required).
+   * @return true if the outputFormat may be collected immediately
+   * @exception Exception if the input format can't be set successfully
+   */
+  public boolean setInputFormat(Instances instanceInfo) throws Exception {
+
+    super.setInputFormat(instanceInfo);
+
+    m_DiscretizeCols.setUpper(instanceInfo.numAttributes() - 1);
+    m_CutPoints = null;
+    if (m_UseMDL) {
+      if (instanceInfo.classIndex() < 0) {
+	throw new UnassignedClassException("Cannot use class-based discretization: "
+                                           + "no class assigned to the dataset");
+      }
+      if (!instanceInfo.classAttribute().isNominal()) {
+	throw new UnsupportedClassTypeException("Supervised discretization not possible:"
+                                                + " class is not nominal!");
+      }
+    } else {
+      if (getFindNumBins() && getUseEqualFrequency()) {
+	throw new IllegalArgumentException("Bin number optimization in conjunction "+
+					   "with equal-frequency binning not implemented.");
+      }
+    }
+
+    // If we implement loading cutfiles, then load 
+    //them here and set the output format
+    return false;
+  }
+
+  
+
+  /**
+   * Input an instance for filtering. Ordinarily the instance is processed
+   * and made available for output immediately. Some filters require all
+   * instances be read before producing output.
+   *
+   * @param instance the input instance
+   * @return true if the filtered instance may now be
+   * collected with output().
+   * @exception IllegalStateException if no input format has been defined.
+   */
+  public boolean input(Instance instance) {
+
+    if (getInputFormat() == null) {
+      throw new IllegalStateException("No input instance format defined");
+    }
+    if (m_NewBatch) {
+      resetQueue();
+      m_NewBatch = false;
+    }
+    
+    if (m_CutPoints != null) {
+      convertInstance(instance);
+      return true;
+    }
+
+    bufferInput(instance);
+    return false;
+  }
+
+
+  /**
+   * Signifies that this batch of input to the filter is finished. If the 
+   * filter requires all instances prior to filtering, output() may now 
+   * be called to retrieve the filtered instances.
+   *
+   * @return true if there are instances pending output
+   * @exception IllegalStateException if no input structure has been defined
+   */
+  public boolean batchFinished() {
+
+    if (getInputFormat() == null) {
+      throw new IllegalStateException("No input instance format defined");
+    }
+    if (m_CutPoints == null) {
+      calculateCutPoints();
+
+      setOutputFormat();
+
+      // If we implement saving cutfiles, save the cuts here
+
+      // Convert pending input instances
+      for(int i = 0; i < getInputFormat().numInstances(); i++) {
+	convertInstance(getInputFormat().instance(i));
+      }
+    } 
+    flushInput();
+
+    m_NewBatch = true;
+    return (numPendingOutput() != 0);
+  }
+
+  /**
+   * Returns a string describing this filter
+   *
+   * @return a description of the filter suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String globalInfo() {
+
+    return "An instance filter that discretizes a range of numeric"
+      + " attributes in the dataset into nominal attributes."
+      + " Discretization can be either by simple binning, or by"
+      + " Fayyad & Irani's MDL method (the default).";
+  }
+  
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String findNumBinsTipText() {
+
+    return "Optimize number of equal-width bins using leave-one-out.";
+  }
+
+  /**
+   * Get the value of FindNumBins.
+   *
+   * @return Value of FindNumBins.
+   */
+  public boolean getFindNumBins() {
+    
+    return m_FindNumBins;
+  }
+  
+  /**
+   * Set the value of FindNumBins.
+   *
+   * @param newFindNumBins Value to assign to FindNumBins.
+   */
+  public void setFindNumBins(boolean newFindNumBins) {
+    
+    m_FindNumBins = newFindNumBins;
+  }
+  
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String makeBinaryTipText() {
+
+    return "Make resulting attributes binary.";
+  }
+
+  /**
+   * Gets whether binary attributes should be made for discretized ones.
+   *
+   * @return true if attributes will be binarized
+   */
+  public boolean getMakeBinary() {
+
+    return m_MakeBinary;
+  }
+
+  /** 
+   * Sets whether binary attributes should be made for discretized ones.
+   *
+   * @param makeBinary if binary attributes are to be made
+   */
+  public void setMakeBinary(boolean makeBinary) {
+
+    m_MakeBinary = makeBinary;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String useMDLTipText() {
+
+    return "Use class-based discretization. If set to false, does"
+      + " not require a class attribute, and uses a fixed number"
+      + " of bins (according to bins setting).";
+  }
+
+  /**
+   * Gets whether MDL will be used as the discretisation method.
+   *
+   * @return true if so, false if fixed bins should be used.
+   */
+  public boolean getUseMDL() {
+
+    return m_UseMDL;
+  }
+
+  /** 
+   * Sets whether MDL will be used as the discretisation method.
+   *
+   * @param useMDL true if MDL should be used, false if fixed bins should
+   * be used.
+   */
+  public void setUseMDL(boolean useMDL) {
+
+    m_UseMDL = useMDL;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String useKononenkoTipText() {
+
+    return "Use Kononenko's MDL criterion. If set to false"
+      + " uses the Fayyad & Irani criterion.";
+  }
+  
+  /**
+   * Get the value of UseEqualFrequency.
+   *
+   * @return Value of UseEqualFrequency.
+   */
+  public boolean getUseEqualFrequency() {
+    
+    return m_UseEqualFrequency;
+  }
+  
+  /**
+   * Set the value of UseEqualFrequency.
+   *
+   * @param newUseEqualFrequency Value to assign to UseEqualFrequency.
+   */
+  public void setUseEqualFrequency(boolean newUseEqualFrequency) {
+    
+    m_UseEqualFrequency = newUseEqualFrequency;
+  }
+  
+  /**
+   * Gets whether Kononenko's MDL criterion is to be used.
+   *
+   * @return true if Kononenko's criterion will be used.
+   */
+  public boolean getUseKononenko() {
+
+    return m_UseKononenko;
+  }
+
+  /** 
+   * Sets whether Kononenko's MDL criterion is to be used.
+   *
+   * @param useKon true if Kononenko's one is to be used
+   */
+  public void setUseKononenko(boolean useKon) {
+
+    m_UseKononenko = useKon;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String useBetterEncodingTipText() {
+
+    return "Uses a more efficient split point encoding.";
+  }
+
+  /**
+   * Gets whether better encoding is to be used for MDL.
+   *
+   * @return true if the better MDL encoding will be used
+   */
+  public boolean getUseBetterEncoding() {
+
+    return m_UseBetterEncoding;
+  }
+
+  /** 
+   * Sets whether better encoding is to be used for MDL.
+   *
+   * @param useBetterEncoding true if better encoding to be used.
+   */
+  public void setUseBetterEncoding(boolean useBetterEncoding) {
+
+    m_UseBetterEncoding = useBetterEncoding;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String binsTipText() {
+
+    return "Number of bins for class-blind discretisation. This"
+      + " setting is ignored if MDL-based discretisation is used.";
+  }
+
+  /**
+   * Gets the number of bins numeric attributes will be divided into
+   *
+   * @return the number of bins.
+   */
+  public int getBins() {
+
+    return m_NumBins;
+  }
+
+  /**
+   * Sets the number of bins to divide each selected numeric attribute into
+   *
+   * @param numBins the number of bins
+   */
+  public void setBins(int numBins) {
+
+    m_NumBins = numBins;
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String invertSelectionTipText() {
+
+    return "Set attribute selection mode. If false, only selected"
+      + " (numeric) attributes in the range will be discretized; if"
+      + " true, only non-selected attributes will be discretized.";
+  }
+
+  /**
+   * Gets whether the supplied columns are to be removed or kept
+   *
+   * @return true if the supplied columns will be kept
+   */
+  public boolean getInvertSelection() {
+
+    return m_DiscretizeCols.getInvert();
+  }
+
+  /**
+   * Sets whether selected columns should be removed or kept. If true the 
+   * selected columns are kept and unselected columns are deleted. If false
+   * selected columns are deleted and unselected columns are kept.
+   *
+   * @param invert the new invert setting
+   */
+  public void setInvertSelection(boolean invert) {
+
+    m_DiscretizeCols.setInvert(invert);
+  }
+
+  /**
+   * Returns the tip text for this property
+   *
+   * @return tip text for this property suitable for
+   * displaying in the explorer/experimenter gui
+   */
+  public String attributeIndicesTipText() {
+    return "Specify range of attributes to act on."
+      + " This is a comma separated list of attribute indices, with"
+      + " \"first\" and \"last\" valid values. Specify an inclusive"
+      + " range with \"-\". E.g: \"first-3,5,6-10,last\".";
+  }
+
+  /**
+   * Gets the current range selection
+   *
+   * @return a string containing a comma separated list of ranges
+   */
+  public String getAttributeIndices() {
+
+    return m_DiscretizeCols.getRanges();
+  }
+
+  /**
+   * Sets which attributes are to be Discretized (only numeric
+   * attributes among the selection will be Discretized).
+   *
+   * @param rangeList a string representing the list of attributes. Since
+   * the string will typically come from a user, attributes are indexed from
+   * 1. <br>
+   * eg: first-3,5,6-last
+   * @exception IllegalArgumentException if an invalid range list is supplied 
+   */
+  public void setAttributeIndices(String rangeList) {
+
+    m_DiscretizeCols.setRanges(rangeList);
+  }
+
+  /**
+   * Sets which attributes are to be Discretized (only numeric
+   * attributes among the selection will be Discretized).
+   *
+   * @param attributes an array containing indexes of attributes to Discretize.
+   * Since the array will typically come from a program, attributes are indexed
+   * from 0.
+   * @exception IllegalArgumentException if an invalid set of ranges
+   * is supplied 
+   */
+  public void setAttributeIndicesArray(int [] attributes) {
+
+    setAttributeIndices(Range.indicesToRangeList(attributes));
+  }
+
+  /**
+   * Gets the cut points for an attribute
+   *
+   * @param the index (from 0) of the attribute to get the cut points of
+   * @return an array containing the cutpoints (or null if the
+   * attribute requested isn't being Discretized
+   */
+  public double [] getCutPoints(int attributeIndex) {
+
+    if (m_CutPoints == null) {
+      return null;
+    }
+    return m_CutPoints[attributeIndex];
+  }
+
+  /** Generate the cutpoints for each attribute */
+  protected void calculateCutPoints() {
+
+    Instances copy = null;
+
+    m_CutPoints = new double [getInputFormat().numAttributes()] [];
+    for(int i = getInputFormat().numAttributes() - 1; i >= 0; i--) {
+      if ((m_DiscretizeCols.isInRange(i)) && 
+	  (getInputFormat().attribute(i).isNumeric())) {
+	if (m_UseMDL) {
+
+	  // Use copy to preserve order
+	  if (copy == null) {
+	    copy = new Instances(getInputFormat());
+	  }
+	  calculateCutPointsByMDL(i, copy);
+	} else {
+	  if (m_FindNumBins) {
+	    findNumBins(i);
+	  } else if (!m_UseEqualFrequency) {
+	    calculateCutPointsByEqualWidthBinning(i);
+	  } else {
+	    calculateCutPointsByEqualFrequencyBinning(i);
+	  }
+	}
+      }
+    }
+  }
+
+  /**
+   * Set cutpoints for a single attribute using MDL.
+   *
+   * @param index the index of the attribute to set cutpoints for
+   */
+  protected void calculateCutPointsByMDL(int index,
+					 Instances data) {
+
+    // Sort instances
+    data.sort(data.attribute(index));
+
+    // Find first instances that's missing
+    int firstMissing = data.numInstances();
+    for (int i = 0; i < data.numInstances(); i++) {
+      if (data.instance(i).isMissing(index)) {
+        firstMissing = i;
+        break;
+      }
+    }
+    m_CutPoints[index] = cutPointsForSubset(data, index, 0, firstMissing);
+  }
+
+  /** Test using Kononenko's MDL criterion. */
+  private boolean KononenkosMDL(double[] priorCounts,
+				double[][] bestCounts,
+				double numInstances,
+				int numCutPoints) {
+
+    double distPrior, instPrior, distAfter = 0, sum, instAfter = 0;
+    double before, after;
+    int numClassesTotal;
+
+    // Number of classes occuring in the set
+    numClassesTotal = 0;
+    for (int i = 0; i < priorCounts.length; i++) {
+      if (priorCounts[i] > 0) {
+	numClassesTotal++;
+      }
+    }
+
+    // Encode distribution prior to split
+    distPrior = SpecialFunctions.log2Binomial(numInstances 
+					      + numClassesTotal - 1,
+					      numClassesTotal - 1);
+
+    // Encode instances prior to split.
+    instPrior = SpecialFunctions.log2Multinomial(numInstances,
+						 priorCounts);
+
+    before = instPrior + distPrior;
+
+    // Encode distributions and instances after split.
+    for (int i = 0; i < bestCounts.length; i++) {
+      sum = Utils.sum(bestCounts[i]);
+      distAfter += SpecialFunctions.log2Binomial(sum + numClassesTotal - 1,
+						 numClassesTotal - 1);
+      instAfter += SpecialFunctions.log2Multinomial(sum,
+						    bestCounts[i]);
+    }
+
+    // Coding cost after split
+    after = Utils.log2(numCutPoints) + distAfter + instAfter;
+
+    // Check if split is to be accepted
+    return (Utils.gr(before, after));
+  }
+
+
+  /** Test using Fayyad and Irani's MDL criterion. */
+  private boolean FayyadAndIranisMDL(double[] priorCounts,
+				     double[][] bestCounts,
+				     double numInstances,
+				     int numCutPoints) {
+
+    double priorEntropy, entropy, gain; 
+    double entropyLeft, entropyRight, delta;
+    int numClassesTotal, numClassesRight, numClassesLeft;
+
+    // Compute entropy before split.
+    priorEntropy = ContingencyTables.entropy(priorCounts);
+
+    // Compute entropy after split.
+    entropy = ContingencyTables.entropyConditionedOnRows(bestCounts);
+
+    // Compute information gain.
+    gain = priorEntropy - entropy;
+
+    // Number of classes occuring in the set
+    numClassesTotal = 0;
+    for (int i = 0; i < priorCounts.length; i++) {
+      if (priorCounts[i] > 0) {
+	numClassesTotal++;
+      }
+    }
+
+    // Number of classes occuring in the left subset
+    numClassesLeft = 0;
+    for (int i = 0; i < bestCounts[0].length; i++) {
+      if (bestCounts[0][i] > 0) {
+	numClassesLeft++;
+      }
+    }
+
+    // Number of classes occuring in the right subset
+    numClassesRight = 0;
+    for (int i = 0; i < bestCounts[1].length; i++) {
+      if (bestCounts[1][i] > 0) {
+	numClassesRight++;
+      }
+    }
+
+    // Entropy of the left and the right subsets
+    entropyLeft = ContingencyTables.entropy(bestCounts[0]);
+    entropyRight = ContingencyTables.entropy(bestCounts[1]);
+
+    // Compute terms for MDL formula
+    delta = Utils.log2(Math.pow(3, numClassesTotal) - 2) - 
+      (((double) numClassesTotal * priorEntropy) - 
+       (numClassesRight * entropyRight) - 
+       (numClassesLeft * entropyLeft));
+
+    // Check if split is to be accepted
+    return (Utils.gr(gain, (Utils.log2(numCutPoints) + delta) /
+		     (double)numInstances));
+  }
+    
+
+  /** Selects cutpoints for sorted subset. */
+  private double[] cutPointsForSubset(Instances instances, int attIndex, 
+				      int first, int lastPlusOne) { 
+
+    double[][] counts, bestCounts;
+    double[] priorCounts, left, right, cutPoints;
+    double currentCutPoint = -Double.MAX_VALUE, bestCutPoint = -1, 
+      currentEntropy, bestEntropy, priorEntropy, gain;
+    int bestIndex = -1, numInstances = 0, numCutPoints = 0;
+
+    // Compute number of instances in set
+    if ((lastPlusOne - first) < 2) {
+      return null;
+    }
+
+    // Compute class counts.
+    counts = new double[2][instances.numClasses()];
+    for (int i = first; i < lastPlusOne; i++) {
+      numInstances += instances.instance(i).weight();
+      counts[1][(int)instances.instance(i).classValue()] +=
+	instances.instance(i).weight();
+    }
+
+    // Save prior counts
+    priorCounts = new double[instances.numClasses()];
+    System.arraycopy(counts[1], 0, priorCounts, 0, 
+		     instances.numClasses());
+
+    // Entropy of the full set
+    priorEntropy = ContingencyTables.entropy(priorCounts);
+    bestEntropy = priorEntropy;
+    
+    // Find best entropy.
+    bestCounts = new double[2][instances.numClasses()];
+    for (int i = first; i < (lastPlusOne - 1); i++) {
+      counts[0][(int)instances.instance(i).classValue()] +=
+	instances.instance(i).weight();
+      counts[1][(int)instances.instance(i).classValue()] -=
+	instances.instance(i).weight();
+      if (Utils.sm(instances.instance(i).value(attIndex), 
+		   instances.instance(i + 1).value(attIndex))) {
+	currentCutPoint = instances.instance(i).value(attIndex); //+ 
+	//instances.instance(i + 1).value(attIndex)) / 2.0;
+	currentEntropy = ContingencyTables.entropyConditionedOnRows(counts);
+	if (Utils.sm(currentEntropy, bestEntropy)) {
+	  bestCutPoint = currentCutPoint;
+	  bestEntropy = currentEntropy;
+	  bestIndex = i;
+	  System.arraycopy(counts[0], 0, 
+			   bestCounts[0], 0, instances.numClasses());
+	  System.arraycopy(counts[1], 0, 
+			   bestCounts[1], 0, instances.numClasses()); 
+	}
+	numCutPoints++;
+      }
+    }
+
+    // Use worse encoding?
+    if (!m_UseBetterEncoding) {
+      numCutPoints = (lastPlusOne - first) - 1;
+    }
+
+    // Checks if gain is zero
+    gain = priorEntropy - bestEntropy;
+    if (Utils.eq(gain, 0)) {
+      return null;
+    }
+
+    // Check if split is to be accepted
+    if ((m_UseKononenko && KononenkosMDL(priorCounts, bestCounts,
+					 numInstances, numCutPoints)) ||
+	(!m_UseKononenko && FayyadAndIranisMDL(priorCounts, bestCounts,
+					       numInstances, numCutPoints))) {
+      
+      // Select split points for the left and right subsets
+      left = cutPointsForSubset(instances, attIndex, first, bestIndex + 1);
+      right = cutPointsForSubset(instances, attIndex, 
+				 bestIndex + 1, lastPlusOne);
+      
+      // Merge cutpoints and return them
+      if ((left == null) && (right) == null) {
+	cutPoints = new double[1];
+	cutPoints[0] = bestCutPoint;
+      } else if (right == null) {
+	cutPoints = new double[left.length + 1];
+	System.arraycopy(left, 0, cutPoints, 0, left.length);
+	cutPoints[left.length] = bestCutPoint;
+      } else if (left == null) {
+	cutPoints = new double[1 + right.length];
+	cutPoints[0] = bestCutPoint;
+	System.arraycopy(right, 0, cutPoints, 1, right.length);
+      } else {
+	cutPoints =  new double[left.length + right.length + 1];
+	cutPoints = new double[left.length + right.length + 1];
+	System.arraycopy(left, 0, cutPoints, 0, left.length);
+	cutPoints[left.length] = bestCutPoint;
+	System.arraycopy(right, 0, cutPoints, left.length + 1, right.length);
+      }
+      
+      return cutPoints;
+    } else
+      return null;
+  }
+ 
+  /**
+   * Set cutpoints for a single attribute.
+   *
+   * @param index the index of the attribute to set cutpoints for
+   */
+  protected void calculateCutPointsByEqualWidthBinning(int index) {
+
+    // Scan for max and min values
+    double max = 0, min = 1, currentVal;
+    Instance currentInstance;
+    for(int i = 0; i < getInputFormat().numInstances(); i++) {
+      currentInstance = getInputFormat().instance(i);
+      if (!currentInstance.isMissing(index)) {
+	currentVal = currentInstance.value(index);
+	if (max < min) {
+	  max = min = currentVal;
+	}
+	if (currentVal > max) {
+	  max = currentVal;
+	}
+	if (currentVal < min) {
+	  min = currentVal;
+	}
+      }
+    }
+    double binWidth = (max - min) / m_NumBins;
+    double [] cutPoints = null;
+    if ((m_NumBins > 1) && (binWidth > 0)) {
+      cutPoints = new double [m_NumBins - 1];
+      for(int i = 1; i < m_NumBins; i++) {
+	cutPoints[i - 1] = min + binWidth * i;
+      }
+    }
+    m_CutPoints[index] = cutPoints;
+  }
+ 
+  /**
+   * Set cutpoints for a single attribute.
+   *
+   * @param index the index of the attribute to set cutpoints for
+   */
+  protected void calculateCutPointsByEqualFrequencyBinning(int index) {
+
+    // Copy data so that it can be sorted
+    Instances data = new Instances(getInputFormat());
+
+    // Sort input data
+    data.sort(index);
+
+    // Compute weight of instances without missing values
+    double sumOfWeights = 0;
+    for (int i = 0; i < data.numInstances(); i++) {
+      if (data.instance(i).isMissing(index)) {
+	break;
+      } else {
+	sumOfWeights += data.instance(i).weight();
+      }
+    }
+    double freq = sumOfWeights / m_NumBins;
+
+    // Compute break points
+    double[] cutPoints = new double[m_NumBins - 1];
+    double counter = 0;
+    int cpindex = 0;
+    for (int i = 0; i < data.numInstances() - 1; i++) {
+
+      // Stop if value missing
+      if (data.instance(i).isMissing(index)) {
+	break;
+      }
+      counter += data.instance(i).weight();
+
+      // Do we have a potential breakpoint?
+      if (data.instance(i).value(index) < 
+	  data.instance(i + 1).value(index)) {
+	if (counter >= freq) {
+	  cutPoints[cpindex] = (data.instance(i).value(index) +
+				data.instance(i + 1).value(index)) / 2;
+	  cpindex++;
+	  counter = counter - freq;
+	}
+      }
+    }
+
+    // Did we find any cutpoints?
+    if (cpindex == 0) {
+      m_CutPoints[index] = null;
+    } else {
+      double[] cp = new double[cpindex];
+      for (int i = 0; i < cpindex; i++) {
+	cp[i] = cutPoints[i];
+      }
+      m_CutPoints[index] = cp;
+    }
+  }
+
+  /**
+   * Optimizes the number of bins using leave-one-out cross-validation.
+   *
+   * @param index the attribute index
+   */
+  protected void findNumBins(int index) {
+
+    double min = Double.MAX_VALUE, max = -Double.MIN_VALUE, binWidth = 0, 
+      entropy, bestEntropy = Double.MAX_VALUE, currentVal;
+    double[] distribution;
+    int bestNumBins  = 1;
+    Instance currentInstance;
+
+    // Find minimum and maximum
+    for (int i = 0; i < getInputFormat().numInstances(); i++) {
+      currentInstance = getInputFormat().instance(i);
+      if (!currentInstance.isMissing(index)) {
+	currentVal = currentInstance.value(index);
+	if (currentVal > max) {
+	  max = currentVal;
+	}
+	if (currentVal < min) {
+	  min = currentVal;
+	}
+      }
+    }
+
+    // Find best number of bins
+    for (int i = 0; i < m_NumBins; i++) {
+      distribution = new double[i + 1];
+      binWidth = (max - min) / (i + 1);
+
+      // Compute distribution
+      for (int j = 0; j < getInputFormat().numInstances(); j++) {
+	currentInstance = getInputFormat().instance(j);
+	if (!currentInstance.isMissing(index)) {
+	  for (int k = 0; k < i + 1; k++) {
+	    if (currentInstance.value(index) <= 
+		(min + (((double)k + 1) * binWidth))) {
+	      distribution[k] += currentInstance.weight();
+	      break;
+	    }
+	  }
+	}
+      }
+
+      // Compute cross-validated entropy
+      entropy = 0;
+      for (int k = 0; k < i + 1; k++) {
+	if (distribution[k] < 2) {
+	  entropy = Double.MAX_VALUE;
+	  break;
+	}
+	entropy -= distribution[k] * Math.log((distribution[k] - 1) / 
+					      binWidth);
+      }
+
+      // Best entropy so far?
+      if (entropy < bestEntropy) {
+	bestEntropy = entropy;
+	bestNumBins = i + 1;
+      }
+    }
+
+    // Compute cut points
+    double [] cutPoints = null;
+    if ((bestNumBins > 1) && (binWidth > 0)) {
+      cutPoints = new double [bestNumBins - 1];
+      for(int i = 1; i < bestNumBins; i++) {
+	cutPoints[i - 1] = min + binWidth * i;
+      }
+    }
+    m_CutPoints[index] = cutPoints;
+   }
+
+  /**
+   * Set the output format. Takes the currently defined cutpoints and 
+   * m_InputFormat and calls setOutputFormat(Instances) appropriately.
+   */
+  protected void setOutputFormat() {
+
+    if (m_CutPoints == null) {
+      setOutputFormat(null);
+      return;
+    }
+    FastVector attributes = new FastVector(getInputFormat().numAttributes());
+    int classIndex = getInputFormat().classIndex();
+    for(int i = 0; i < getInputFormat().numAttributes(); i++) {
+      if ((m_DiscretizeCols.isInRange(i)) 
+	  && (getInputFormat().attribute(i).isNumeric())) {
+	if (!m_MakeBinary) {
+	  FastVector attribValues = new FastVector(1);
+	  if (m_CutPoints[i] == null) {
+	    attribValues.addElement("'All'");
+	  } else {
+	    for(int j = 0; j <= m_CutPoints[i].length; j++) {
+	      if (j == 0) {
+		attribValues.addElement("'(-inf-"
+			+ Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
+	      } else if (j == m_CutPoints[i].length) {
+		attribValues.addElement("'("
+			+ Utils.doubleToString(m_CutPoints[i][j - 1], 6) 
+					+ "-inf)'");
+	      } else {
+		attribValues.addElement("'("
+			+ Utils.doubleToString(m_CutPoints[i][j - 1], 6) + "-"
+			+ Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
+	      }
+	    }
+	  }
+	  attributes.addElement(new Attribute(getInputFormat().
+					      attribute(i).name(),
+					      attribValues));
+	} else {
+	  if (m_CutPoints[i] == null) {
+	    FastVector attribValues = new FastVector(1);
+	    attribValues.addElement("'All'");
+	    attributes.addElement(new Attribute(getInputFormat().
+						attribute(i).name(),
+						attribValues));
+	  } else {
+	    if (i < getInputFormat().classIndex()) {
+	      classIndex += m_CutPoints[i].length - 1;
+	    }
+	    for(int j = 0; j < m_CutPoints[i].length; j++) {
+	      FastVector attribValues = new FastVector(2);
+	      attribValues.addElement("'(-inf-"
+		      + Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
+	      attribValues.addElement("'("
+		      + Utils.doubleToString(m_CutPoints[i][j], 6) + "-inf)'");
+	      attributes.addElement(new Attribute(getInputFormat().
+						  attribute(i).name(),
+						  attribValues));
+	    }
+	  }
+	}
+      } else {
+	attributes.addElement(getInputFormat().attribute(i).copy());
+      }
+    }
+    Instances outputFormat = 
+      new Instances(getInputFormat().relationName(), attributes, 0);
+    outputFormat.setClassIndex(classIndex);
+    setOutputFormat(outputFormat);
+  }
+
+  /**
+   * Convert a single instance over. The converted instance is added to 
+   * the end of the output queue.
+   *
+   * @param instance the instance to convert
+   */
+  protected void convertInstance(Instance instance) {
+
+    int index = 0;
+    double [] vals = new double [outputFormatPeek().numAttributes()];
+    // Copy and convert the values
+    for(int i = 0; i < getInputFormat().numAttributes(); i++) {
+      if (m_DiscretizeCols.isInRange(i) && 
+	  getInputFormat().attribute(i).isNumeric()) {
+	int j;
+	double currentVal = instance.value(i);
+	if (m_CutPoints[i] == null) {
+	  if (instance.isMissing(i)) {
+	    vals[index] = Instance.missingValue();
+	  } else {
+	    vals[index] = 0;
+	  }
+	  index++;
+	} else {
+	  if (!m_MakeBinary) {
+	    if (instance.isMissing(i)) {
+	      vals[index] = Instance.missingValue();
+	    } else {
+	      for (j = 0; j < m_CutPoints[i].length; j++) {
+		if (currentVal <= m_CutPoints[i][j]) {
+		  break;
+		}
+	      }
+              vals[index] = j;
+	    }
+	    index++;
+	  } else {
+	    for (j = 0; j < m_CutPoints[i].length; j++) {
+	      if (instance.isMissing(i)) {
+                vals[index] = Instance.missingValue();
+	      } else if (currentVal <= m_CutPoints[i][j]) {
+                vals[index] = 0;
+	      } else {
+                vals[index] = 1;
+	      }
+	      index++;
+	    }
+	  }   
+	}
+      } else {
+        vals[index] = instance.value(i);
+	index++;
+      }
+    }
+    
+    Instance inst = null;
+    if (instance instanceof SparseInstance) {
+      inst = new SparseInstance(instance.weight(), vals);
+    } else {
+      inst = new Instance(instance.weight(), vals);
+    }
+    copyStringValues(inst, false, instance.dataset(), getInputStringIndex(),
+                     getOutputFormat(), getOutputStringIndex());
+    inst.setDataset(getOutputFormat());
+    push(inst);
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain arguments to the filter: use -h for help
+   */
+  public static void main(String [] argv) {
+
+    try {
+      if (Utils.getFlag('b', argv)) {
+ 	Filter.batchFilterFile(new DiscretizeFilter(), argv);
+      } else {
+	Filter.filterFile(new DiscretizeFilter(), argv);
+      }
+    } catch (Exception ex) {
+      System.out.println(ex.getMessage());
+    }
+  }
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/Filter.java
===================================================================
--- /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/Filter.java	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/kea/kea-3.0/weka/filters/Filter.java	(revision 16561)
@@ -0,0 +1,934 @@
+/*
+ *    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.
+ */
+
+
+/*
+ *    Filter.java
+ *    Copyright (C) 1999 Len Trigg
+ *
+ */
+
+
+package weka.filters;
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Serializable;
+import java.util.Enumeration;
+import weka.core.Attribute;
+import weka.core.Instance;
+import weka.core.Instances;
+import weka.core.Option;
+import weka.core.OptionHandler;
+import weka.core.Queue;
+import weka.core.Utils;
+
+/** 
+ * An abstract class for instance filters: objects that take instances
+ * as input, carry out some transformation on the instance and then
+ * output the instance. The method implementations in this class
+ * assume that most of the work will be done in the methods overridden
+ * by subclasses.<p>
+ *
+ * A simple example of filter use. This example doesn't remove
+ * instances from the output queue until all instances have been
+ * input, so has higher memory consumption than an approach that
+ * uses output instances as they are made available:<p>
+ *
+ * <code> <pre>
+ *  Filter filter = ..some type of filter..
+ *  Instances instances = ..some instances..
+ *  for (int i = 0; i < data.numInstances(); i++) {
+ *    filter.input(data.instance(i));
+ *  }
+ *  filter.batchFinished();
+ *  Instances newData = filter.outputFormat();
+ *  Instance processed;
+ *  while ((processed = filter.output()) != null) {
+ *    newData.add(processed);
+ *  }
+ *  ..do something with newData..
+ * </pre> </code>
+ *
+ * @author Len Trigg (trigg@cs.waikato.ac.nz)
+ * @version $Revision$
+ */
+public abstract class Filter implements Serializable {
+
+  /*
+   * Filter refactoring TODO:
+   *
+   * - Update all filters to use getOutputFormat and setInputFormat
+   * instead of outputFormat, outputFormatPeek and inputFormat.
+   * - Update users of filters to use getOutputFormat and setInputFormat
+   * - remove outputFormat, outputFormatPeek and inputFormat
+   *
+   */
+
+  /** Debugging mode */
+  private boolean m_Debug = false;
+
+  /** The output format for instances */
+  private Instances m_OutputFormat = null;
+
+  /** The output instance queue */
+  private Queue m_OutputQueue = null;
+
+  /** Indices of string attributes in the output format */
+  private int [] m_OutputStringAtts = null;
+
+  /** Indices of string attributes in the input format */
+  private int [] m_InputStringAtts = null;
+
+  /** The input format for instances */
+  private Instances m_InputFormat = null;
+
+  /** Record whether the filter is at the start of a batch */
+  protected boolean m_NewBatch = true;
+
+  /**
+   * Sets the format of output instances. The derived class should use this
+   * method once it has determined the outputformat. The 
+   * output queue is cleared.
+   *
+   * @param outputFormat the new output format
+   */
+  protected void setOutputFormat(Instances outputFormat) {
+
+    if (outputFormat != null) {
+      m_OutputFormat = outputFormat.stringFreeStructure();
+      m_OutputStringAtts = getStringIndices(m_OutputFormat);
+
+      // Rename the attribute
+      String relationName = outputFormat.relationName() 
+        + "-" + this.getClass().getName();
+      if (this instanceof OptionHandler) {
+        String [] options = ((OptionHandler)this).getOptions();
+        for (int i = 0; i < options.length; i++) {
+          relationName += options[i].trim();
+        }
+      }
+      m_OutputFormat.setRelationName(relationName);
+    } else {
+      m_OutputFormat = null;
+    }
+    m_OutputQueue = new Queue();
+  }
+
+  /**
+   * Gets the currently set inputformat instances. This dataset may contain
+   * buffered instances.
+   *
+   * @return the input Instances.
+   */
+  protected Instances getInputFormat() {
+
+    return m_InputFormat;
+  }
+
+  /**
+   * Returns a reference to the current output format without
+   * copying it.
+   *
+   * @return a reference to the current output format
+   */
+  protected Instances outputFormatPeek() {
+
+    return m_OutputFormat;
+  }
+
+  /**
+   * Adds an output instance to the queue. The derived class should use this
+   * method for each output instance it makes available. 
+   *
+   * @param instance the instance to be added to the queue
+   */
+  protected void push(Instance instance) {
+
+    if (instance != null) {
+      copyStringValues(instance, m_OutputFormat, m_OutputStringAtts);
+      instance.setDataset(m_OutputFormat);
+      m_OutputQueue.push(instance);
+    }
+  }
+
+  /**
+   * Clears the output queue.
+   */
+  protected void resetQueue() {
+
+    m_OutputQueue = new Queue();
+  }
+
+  /**
+   * Adds the supplied input instance to the inputformat dataset for
+   * later processing.  Use this method rather than
+   * getInputFormat().add(instance). Or else.
+   *
+   * @param instance the <code>Instance</code> to buffer.  
+   */
+  protected void bufferInput(Instance instance) {
+
+    if (instance != null) {
+      copyStringValues(instance, m_InputFormat, m_InputStringAtts);
+      instance.setDataset(m_InputFormat);
+      m_InputFormat.add(instance);
+    }
+  }
+
+  /**
+   * Returns an array containing the indices of all string attributes in the
+   * input format. This index is created during setInputFormat()
+   *
+   * @return an array containing the indices of string attributes in the 
+   * input dataset.
+   */
+  protected int [] getInputStringIndex() {
+
+    return m_InputStringAtts;
+  }
+
+  /**
+   * Returns an array containing the indices of all string attributes in the
+   * output format. This index is created during setOutputFormat()
+   *
+   * @return an array containing the indices of string attributes in the 
+   * output dataset.
+   */
+  protected int [] getOutputStringIndex() {
+
+    return m_OutputStringAtts;
+  }
+
+  /**
+   * Copies string values contained in the instance copied to a new
+   * dataset. The Instance must already be assigned to a dataset. This
+   * dataset and the destination dataset must have the same structure.
+   *
+   * @param instance the Instance containing the string values to copy.
+   * @param destDataset the destination set of Instances
+   * @param strAtts an array containing the indices of any string attributes
+   * in the dataset.  
+   */
+  private void copyStringValues(Instance inst, Instances destDataset, 
+                                int []strAtts) {
+
+    if (strAtts.length == 0) {
+      return;
+    }
+    if (inst.dataset() == null) {
+      throw new IllegalArgumentException("Instance has no dataset assigned!!");
+    } else if (inst.dataset().numAttributes() != destDataset.numAttributes()) {
+      throw new IllegalArgumentException("Src and Dest differ in # of attributes!!");
+    } 
+    copyStringValues(inst, true, inst.dataset(), strAtts,
+                     destDataset, strAtts);
+  }
+
+  /**
+   * Takes string values referenced by an Instance and copies them from a
+   * source dataset to a destination dataset. The instance references are
+   * updated to be valid for the destination dataset. The instance may have the 
+   * structure (i.e. number and attribute position) of either dataset (this
+   * affects where references are obtained from). The source dataset must
+   * have the same structure as the filter input format and the destination
+   * must have the same structure as the filter output format.
+   *
+   * @param instance the instance containing references to strings in the source
+   * dataset that will have references updated to be valid for the destination
+   * dataset.
+   * @param instSrcCompat true if the instance structure is the same as the
+   * source, or false if it is the same as the destination
+   * @param srcDataset the dataset for which the current instance string
+   * references are valid (after any position mapping if needed)
+   * @param destDataset the dataset for which the current instance string
+   * references need to be inserted (after any position mapping if needed)
+   */
+  protected void copyStringValues(Instance instance, boolean instSrcCompat,
+                                  Instances srcDataset, Instances destDataset) {
+
+    copyStringValues(instance, instSrcCompat, srcDataset, m_InputStringAtts,
+                     destDataset, m_OutputStringAtts);
+  }
+
+  /**
+   * Takes string values referenced by an Instance and copies them from a
+   * source dataset to a destination dataset. The instance references are
+   * updated to be valid for the destination dataset. The instance may have the 
+   * structure (i.e. number and attribute position) of either dataset (this
+   * affects where references are obtained from). Only works if the number
+   * of string attributes is the same in both indices (implicitly these string
+   * attributes should be semantically same but just with shifted positions).
+   *
+   * @param instance the instance containing references to strings in the source
+   * dataset that will have references updated to be valid for the destination
+   * dataset.
+   * @param instSrcCompat true if the instance structure is the same as the
+   * source, or false if it is the same as the destination (i.e. which of the
+   * string attribute indices contains the correct locations for this instance).
+   * @param srcDataset the dataset for which the current instance string
+   * references are valid (after any position mapping if needed)
+   * @param srcStrAtts an array containing the indices of string attributes
+   * in the source datset.
+   * @param destDataset the dataset for which the current instance string
+   * references need to be inserted (after any position mapping if needed)
+   * @param destStrAtts an array containing the indices of string attributes
+   * in the destination datset.
+   */
+  protected void copyStringValues(Instance instance, boolean instSrcCompat,
+                                  Instances srcDataset, int []srcStrAtts,
+                                  Instances destDataset, int []destStrAtts) {
+    if (srcDataset == destDataset) {
+      return;
+    }
+    if (srcStrAtts.length != destStrAtts.length) {
+      throw new IllegalArgumentException("Src and Dest string indices differ in length!!");
+    }
+    for (int i = 0; i < srcStrAtts.length; i++) {
+      int instIndex = instSrcCompat ? srcStrAtts[i] : destStrAtts[i];
+      Attribute src = srcDataset.attribute(srcStrAtts[i]);
+      Attribute dest = destDataset.attribute(destStrAtts[i]);
+      if (!instance.isMissing(instIndex)) {
+        //System.err.println(instance.value(srcIndex) 
+        //                   + " " + src.numValues()
+        //                   + " " + dest.numValues());
+        int valIndex = dest.addStringValue(src, (int)instance.value(instIndex));
+        // setValue here shouldn't be too slow here unless your dataset has
+        // squillions of string attributes
+        instance.setValue(instIndex, (double)valIndex);
+      }
+    }
+  }
+
+  /**
+   * This will remove all buffered instances from the inputformat dataset.
+   * Use this method rather than getInputFormat().delete();
+   */
+  protected void flushInput() {
+
+    if (m_InputStringAtts.length > 0) {
+      m_InputFormat = m_InputFormat.stringFreeStructure();
+    } else {
+      // This more efficient than new Instances(m_InputFormat, 0);
+      m_InputFormat.delete();
+    }
+  }
+
+  /**
+   * @deprecated use <code>setInputFormat(Instances)</code> instead.
+   */
+  public boolean inputFormat(Instances instanceInfo) throws Exception {
+
+    return setInputFormat(instanceInfo);
+  }
+
+  /**
+   * Sets the format of the input instances. If the filter is able to
+   * determine the output format before seeing any input instances, it
+   * does so here. This default implementation clears the output format
+   * and output queue, and the new batch flag is set. Overriders should
+   * call <code>super.setInputFormat(Instances)</code>
+   *
+   * @param instanceInfo an Instances object containing the input instance
+   * structure (any instances contained in the object are ignored - only the
+   * structure is required).
+   * @return true if the outputFormat may be collected immediately
+   * @exception Exception if the inputFormat can't be set successfully 
+   */
+  public boolean setInputFormat(Instances instanceInfo) throws Exception {
+
+    m_InputFormat = instanceInfo.stringFreeStructure();
+    m_InputStringAtts = getStringIndices(instanceInfo);
+    m_OutputFormat = null;
+    m_OutputQueue = new Queue();
+    m_NewBatch = true;
+    return false;
+  }
+
+  /**
+   * @deprecated use <code>getOutputFormat()</code> instead.
+   */
+  public final Instances outputFormat() {
+
+    return getOutputFormat();
+  }
+
+  /**
+   * Gets the format of the output instances. This should only be called
+   * after input() or batchFinished() has returned true. The relation
+   * name of the output instances should be changed to reflect the
+   * action of the filter (eg: add the filter name and options).
+   *
+   * @return an Instances object containing the output instance
+   * structure only.
+   * @exception NullPointerException if no input structure has been
+   * defined (or the output format hasn't been determined yet) 
+   */
+  public final Instances getOutputFormat() {
+
+    if (m_OutputFormat == null) {
+      throw new NullPointerException("No output format defined.");
+    }
+    return new Instances(m_OutputFormat, 0);
+  }
+
+  /**
+   * Input an instance for filtering. Ordinarily the instance is
+   * processed and made available for output immediately. Some filters
+   * require all instances be read before producing output, in which
+   * case output instances should be collected after calling
+   * batchFinished(). If the input marks the start of a new batch, the
+   * output queue is cleared. This default implementation assumes all
+   * instance conversion will occur when batchFinished() is called.
+   *
+   * @param instance the input instance
+   * @return true if the filtered instance may now be
+   * collected with output().
+   * @exception NullPointerException if the input format has not been
+   * defined.
+   * @exception Exception if the input instance was not of the correct 
+   * format or if there was a problem with the filtering.  
+   */
+  public boolean input(Instance instance) throws Exception {
+
+    if (m_InputFormat == null) {
+      throw new NullPointerException("No input instance format defined");
+    }
+    if (m_NewBatch) {
+      m_OutputQueue = new Queue();
+      m_NewBatch = false;
+    }
+    bufferInput(instance);
+    return false;
+  }
+
+  /**
+   * Signify that this batch of input to the filter is finished. If
+   * the filter requires all instances prior to filtering, output()
+   * may now be called to retrieve the filtered instances. Any
+   * subsequent instances filtered should be filtered based on setting
+   * obtained from the first batch (unless the inputFormat has been
+   * re-assigned or new options have been set). This default
+   * implementation assumes all instance processing occurs during
+   * inputFormat() and input().
+   *
+   * @return true if there are instances pending output
+   * @exception NullPointerException if no input structure has been defined,
+   * @exception Exception if there was a problem finishing the batch.
+   */
+  public boolean batchFinished() throws Exception {
+
+    if (m_InputFormat == null) {
+      throw new NullPointerException("No input instance format defined");
+    }
+    flushInput();
+    m_NewBatch = true;
+    return (numPendingOutput() != 0);
+  }
+
+
+  /**
+   * Output an instance after filtering and remove from the output queue.
+   *
+   * @return the instance that has most recently been filtered (or null if
+   * the queue is empty).
+   * @exception NullPointerException if no output structure has been defined
+   */
+  public Instance output() {
+
+    if (m_OutputFormat == null) {
+      throw new NullPointerException("No output instance format defined");
+    }
+    if (m_OutputQueue.empty()) {
+      return null;
+    }
+    Instance result = (Instance)m_OutputQueue.pop();
+    // Clear out references to old strings occasionally
+    if (m_OutputQueue.empty() && m_NewBatch) {
+      if (m_OutputStringAtts.length > 0) {
+        m_OutputFormat = m_OutputFormat.stringFreeStructure();
+      }
+    }
+    return result;
+  }
+  
+  /**
+   * Output an instance after filtering but do not remove from the
+   * output queue.
+   *
+   * @return the instance that has most recently been filtered (or null if
+   * the queue is empty).
+   * @exception NullPointerException if no input structure has been defined 
+   */
+  public Instance outputPeek() {
+
+    if (m_OutputFormat == null) {
+      throw new NullPointerException("No output instance format defined");
+    }
+    if (m_OutputQueue.empty()) {
+      return null;
+    }
+    Instance result = (Instance)m_OutputQueue.peek();
+    return result;
+  }
+
+  /**
+   * Returns the number of instances pending output
+   *
+   * @return the number of instances  pending output
+   * @exception NullPointerException if no input structure has been defined
+   */
+  public int numPendingOutput() {
+
+    if (m_OutputFormat == null) {
+      throw new NullPointerException("No output instance format defined");
+    }
+    return m_OutputQueue.size();
+  }
+
+  /**
+   * Returns whether the output format is ready to be collected
+   *
+   * @return true if the output format is set
+   */
+  public boolean isOutputFormatDefined() {
+
+    return (m_OutputFormat != null);
+  }
+
+  /**
+   * Gets an array containing the indices of all string attributes.
+   *
+   * @param insts the Instances to scan for string attributes. 
+   * @return an array containing the indices of string attributes in
+   * the input structure. Will be zero-length if there are no
+   * string attributes
+   */
+  protected int [] getStringIndices(Instances insts) {
+    
+    // Scan through getting the indices of String attributes
+    int [] index = new int [insts.numAttributes()];
+    int indexSize = 0;
+    for (int i = 0; i < insts.numAttributes(); i++) {
+      if (insts.attribute(i).type() == Attribute.STRING) {
+        index[indexSize++] = i;
+      }
+    }
+    int [] result = new int [indexSize];
+    System.arraycopy(index, 0, result, 0, indexSize);
+    return result;
+  }
+  
+  /**
+   * Filters an entire set of instances through a filter and returns
+   * the new set. 
+   *
+   * @param data the data to be filtered
+   * @param filter the filter to be used
+   * @return the filtered set of data
+   * @exception Exception if the filter can't be used successfully
+   */
+  public static Instances useFilter(Instances data,
+				    Filter filter) throws Exception {
+    /*
+    System.err.println(filter.getClass().getName() 
+                       + " in:" + data.numInstances());
+    */
+    for (int i = 0; i < data.numInstances(); i++) {
+      filter.input(data.instance(i));
+    }
+    filter.batchFinished();
+    Instances newData = filter.getOutputFormat();
+    Instance processed;
+    while ((processed = filter.output()) != null) {
+      newData.add(processed);
+    }
+
+    /*
+    System.err.println(filter.getClass().getName() 
+                       + " out:" + newData.numInstances());
+    */
+    return newData;
+  }
+
+  /**
+   * Method for testing filters.
+   *
+   * @param argv should contain the following arguments: <br>
+   * -i input_file <br>
+   * -o output_file <br>
+   * -c class_index <br>
+   * or -h for help on options
+   * @exception Exception if something goes wrong or the user requests help on
+   * command options
+   */
+  public static void filterFile(Filter filter, String [] options) 
+    throws Exception {
+
+    boolean debug = false;
+    Instances data = null;
+    Reader input = null;
+    PrintWriter output = null;
+    boolean helpRequest;
+
+    try {
+       helpRequest = Utils.getFlag('h', options);
+
+      if (Utils.getFlag('d', options)) {
+	debug = true;
+      }
+      String infileName = Utils.getOption('i', options);
+      String outfileName = Utils.getOption('o', options); 
+      String classIndex = Utils.getOption('c', options);
+      
+      if (filter instanceof OptionHandler) {
+	((OptionHandler)filter).setOptions(options);
+      }
+
+      Utils.checkForRemainingOptions(options);
+      if (helpRequest) {
+	throw new Exception("Help requested.\n");
+      }
+      if (infileName.length() != 0) {
+	input = new BufferedReader(new FileReader(infileName));
+      } else {
+	input = new BufferedReader(new InputStreamReader(System.in));
+      }
+      if (outfileName.length() != 0) {
+	output = new PrintWriter(new FileOutputStream(outfileName));
+      } else { 
+	output = new PrintWriter(System.out);
+      }
+
+      data = new Instances(input, 1);
+      if (classIndex.length() != 0) {
+	if (classIndex.equals("first")) {
+	  data.setClassIndex(0);
+	} else if (classIndex.equals("last")) {
+	  data.setClassIndex(data.numAttributes() - 1);
+	} else {
+	  data.setClassIndex(Integer.parseInt(classIndex) - 1);
+	}
+      }
+    } catch (Exception ex) {
+      String filterOptions = "";
+      // Output the error and also the valid options
+      if (filter instanceof OptionHandler) {
+	filterOptions += "\nFilter options:\n\n";
+	Enumeration enum = ((OptionHandler)filter).listOptions();
+	while (enum.hasMoreElements()) {
+	  Option option = (Option) enum.nextElement();
+	  filterOptions += option.synopsis() + '\n'
+	    + option.description() + "\n";
+	}
+      }
+
+      String genericOptions = "\nGeneral options:\n\n"
+	+ "-h\n"
+	+ "\tGet help on available options.\n"
+	+ "\t(use -b -h for help on batch mode.)\n"
+	+ "-i <file>\n"
+	+ "\tThe name of the file containing input instances.\n"
+	+ "\tIf not supplied then instances will be read from stdin.\n"
+	+ "-o <file>\n"
+	+ "\tThe name of the file output instances will be written to.\n"
+	+ "\tIf not supplied then instances will be written to stdout.\n"
+	+ "-c <class index>\n"
+	+ "\tThe number of the attribute to use as the class.\n"
+	+ "\t\"first\" and \"last\" are also valid entries.\n"
+	+ "\tIf not supplied then no class is assigned.\n";
+
+      throw new Exception('\n' + ex.getMessage()
+			  + filterOptions+genericOptions);
+    }
+    
+    if (debug) {
+      System.err.println("Setting input format");
+    }
+    boolean printedHeader = false;
+    if (filter.setInputFormat(data)) {
+      if (debug) {
+	System.err.println("Getting output format");
+      }
+      output.println(filter.getOutputFormat().toString());
+      printedHeader = true;
+    }
+    
+    // Pass all the instances to the filter
+    while (data.readInstance(input)) {
+      if (debug) {
+	System.err.println("Input instance to filter");
+      }
+      if (filter.input(data.instance(0))) {
+	if (debug) {
+	  System.err.println("Filter said collect immediately");
+	}
+	if (!printedHeader) {
+	  throw new Error("Filter didn't return true from setInputFormat() "
+			  + "earlier!");
+	}
+	if (debug) {
+	  System.err.println("Getting output instance");
+	}
+	output.println(filter.output().toString());
+      }
+      data.delete(0);
+    }
+    
+    // Say that input has finished, and print any pending output instances
+    if (debug) {
+      System.err.println("Setting end of batch");
+    }
+    if (filter.batchFinished()) {
+      if (debug) {
+	System.err.println("Filter said collect output");
+      }
+      if (!printedHeader) {
+	if (debug) {
+	  System.err.println("Getting output format");
+	}
+	output.println(filter.getOutputFormat().toString());
+      }
+      if (debug) {
+	System.err.println("Getting output instance");
+      }
+      while (filter.numPendingOutput() > 0) {
+	output.println(filter.output().toString());
+	if (debug){
+	  System.err.println("Getting output instance");
+	}
+      }
+    }
+    if (debug) {
+      System.err.println("Done");
+    }
+    
+    if (output != null) {
+      output.close();
+    }
+  }
+
+  /**
+   * Method for testing filters ability to process multiple batches.
+   *
+   * @param argv should contain the following arguments:<br>
+   * -i (first) input file <br>
+   * -o (first) output file <br>
+   * -r (second) input file <br>
+   * -s (second) output file <br>
+   * -c class_index <br>
+   * or -h for help on options
+   * @exception Exception if something goes wrong or the user requests help on
+   * command options
+   */
+  public static void batchFilterFile(Filter filter, String [] options) 
+    throws Exception {
+
+    Instances firstData = null;
+    Instances secondData = null;
+    Reader firstInput = null;
+    Reader secondInput = null;
+    PrintWriter firstOutput = null;
+    PrintWriter secondOutput = null;
+    boolean helpRequest;
+    try {
+      helpRequest = Utils.getFlag('h', options);
+
+      String fileName = Utils.getOption('i', options); 
+      if (fileName.length() != 0) {
+	firstInput = new BufferedReader(new FileReader(fileName));
+      } else {
+	throw new Exception("No first input file given.\n");
+      }
+
+      fileName = Utils.getOption('r', options); 
+      if (fileName.length() != 0) {
+	secondInput = new BufferedReader(new FileReader(fileName));
+      } else {
+	throw new Exception("No second input file given.\n");
+      }
+
+      fileName = Utils.getOption('o', options); 
+      if (fileName.length() != 0) {
+	firstOutput = new PrintWriter(new FileOutputStream(fileName));
+      } else {
+	firstOutput = new PrintWriter(System.out);
+      }
+      
+      fileName = Utils.getOption('s', options); 
+      if (fileName.length() != 0) {
+	secondOutput = new PrintWriter(new FileOutputStream(fileName));
+      } else {
+	secondOutput = new PrintWriter(System.out);
+      }
+      String classIndex = Utils.getOption('c', options);
+
+      if (filter instanceof OptionHandler) {
+	((OptionHandler)filter).setOptions(options);
+      }
+      Utils.checkForRemainingOptions(options);
+      
+      if (helpRequest) {
+	throw new Exception("Help requested.\n");
+      }
+      firstData = new Instances(firstInput, 1);
+      secondData = new Instances(secondInput, 1);
+      if (!secondData.equalHeaders(firstData)) {
+	throw new Exception("Input file formats differ.\n");
+      }
+      if (classIndex.length() != 0) {
+	if (classIndex.equals("first")) {
+	  firstData.setClassIndex(0);
+	  secondData.setClassIndex(0);
+	} else if (classIndex.equals("last")) {
+	  firstData.setClassIndex(firstData.numAttributes() - 1);
+	  secondData.setClassIndex(secondData.numAttributes() - 1);
+	} else {
+	  firstData.setClassIndex(Integer.parseInt(classIndex) - 1);
+	  secondData.setClassIndex(Integer.parseInt(classIndex) - 1);
+	}
+      }
+    } catch (Exception ex) {
+      String filterOptions = "";
+      // Output the error and also the valid options
+      if (filter instanceof OptionHandler) {
+	filterOptions += "\nFilter options:\n\n";
+	Enumeration enum = ((OptionHandler)filter).listOptions();
+	while (enum.hasMoreElements()) {
+	  Option option = (Option) enum.nextElement();
+	  filterOptions += option.synopsis() + '\n'
+	    + option.description() + "\n";
+	}
+      }
+
+      String genericOptions = "\nGeneral options:\n\n"
+	+ "-h\n"
+	+ "\tGet help on available options.\n"
+	+ "-i <filename>\n"
+	+ "\tThe file containing first input instances.\n"
+	+ "-o <filename>\n"
+	+ "\tThe file first output instances will be written to.\n"
+	+ "-r <filename>\n"
+	+ "\tThe file containing second input instances.\n"
+	+ "-s <filename>\n"
+	+ "\tThe file second output instances will be written to.\n"
+	+ "-c <class index>\n"
+	+ "\tThe number of the attribute to use as the class.\n"
+	+ "\t\"first\" and \"last\" are also valid entries.\n"
+	+ "\tIf not supplied then no class is assigned.\n";
+
+      throw new Exception('\n' + ex.getMessage()
+			  + filterOptions+genericOptions);
+    }
+    boolean printedHeader = false;
+    if (filter.setInputFormat(firstData)) {
+      firstOutput.println(filter.getOutputFormat().toString());
+      printedHeader = true;
+    }
+    
+    // Pass all the instances to the filter
+    while (firstData.readInstance(firstInput)) {
+      if (filter.input(firstData.instance(0))) {
+	if (!printedHeader) {
+	  throw new Error("Filter didn't return true from setInputFormat() "
+			  + "earlier!");
+	}
+	firstOutput.println(filter.output().toString());
+      }
+      firstData.delete(0);
+    }
+    
+    // Say that input has finished, and print any pending output instances
+    if (filter.batchFinished()) {
+      if (!printedHeader) {
+	firstOutput.println(filter.getOutputFormat().toString());
+      }
+      while (filter.numPendingOutput() > 0) {
+	firstOutput.println(filter.output().toString());
+      }
+    }
+    
+    if (firstOutput != null) {
+      firstOutput.close();
+    }    
+    printedHeader = false;
+    if (filter.isOutputFormatDefined()) {
+      secondOutput.println(filter.getOutputFormat().toString());
+      printedHeader = true;
+    }
+    // Pass all the second instances to the filter
+    while (secondData.readInstance(secondInput)) {
+      if (filter.input(secondData.instance(0))) {
+	if (!printedHeader) {
+	  throw new Error("Filter didn't return true from"
+			  + " isOutputFormatDefined() earlier!");
+	}
+	secondOutput.println(filter.output().toString());
+      }
+      secondData.delete(0);
+    }
+    
+    // Say that input has finished, and print any pending output instances
+    if (filter.batchFinished()) {
+      if (!printedHeader) {
+	secondOutput.println(filter.getOutputFormat().toString());
+      }
+      while (filter.numPendingOutput() > 0) {
+	secondOutput.println(filter.output().toString());
+      }
+    }
+    if (secondOutput != null) {
+      secondOutput.close();
+    }
+  }
+
+  /**
+   * Main method for testing this class.
+   *
+   * @param argv should contain arguments to the filter: use -h for help
+   */
+  public static void main(String [] args) {
+    
+    try {
+      if (args.length == 0) {
+        throw new Exception("First argument must be the class name of a Filter");
+      }
+      String fname = args[0];
+      Filter f = (Filter)Class.forName(fname).newInstance();
+      args[0] = "";
+      if (Utils.getFlag('b', args)) {
+	Filter.batchFilterFile(f, args);
+      } else {
+	Filter.filterFile(f, args);
+      }
+    } catch (Exception ex) {
+      ex.printStackTrace();
+      System.out.println(ex.getMessage());
+    }
+  }
+}
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2build/packages/pdftohtml/readme-gs.txt
===================================================================
--- /gsdl/trunk/gs2build/packages/pdftohtml/readme-gs.txt	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/pdftohtml/readme-gs.txt	(revision 16561)
@@ -0,0 +1,16 @@
+This is pdftohtml, which was based at:
+     http://www.ra.informatik.uni-stuttgart.de/~gosho/pdftohtml/
+
+It has recently been picked up again, and is currently based at:
+    http://pdftohtml.sourceforge.net/
+
+We're currently using version 0.34 as downloaded from
+http://pdftohtml.sourceforge.net. The only alterations made to the source
+package were to remove some "CVS" directories and tidy up the makefiles
+slightly (to use ./configure as with the rest of Greenstone).
+
+Also, the #pragma statements were removed to work around a compiler bug
+on Mac OS X.
+
+Stefan Boddie
+December 2002
Index: /gsdl/trunk/gs2build/packages/rtftohtml/rtftohtml-gs.txt
===================================================================
--- /gsdl/trunk/gs2build/packages/rtftohtml/rtftohtml-gs.txt	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/rtftohtml/rtftohtml-gs.txt	(revision 16561)
@@ -0,0 +1,56 @@
+RTF TO HTML
+-----------
+
+This is based on version 2.7.5 of rtftohtml, which was last modified in
+November, 1994.
+It contains the text:
+###
+Initial Implementation by Chris Hector (cjh@cray.com)
+(c) 1994 by Chris Hector
+May be freely distributed.
+This translator was based on rtfskel.
+###
+The last known email address I can find for him is <cjh@cray.com>.
+I think that <chris@sunpack.com> might reach him, but I'm not sure.
+
+
+Versions 3.0 of rtftohtml and up are released under a shareware license,
+and available from http://www.logictran.com (although it now has a new
+name). You can download a free binary version of the software for most
+platforms from this site.
+
+
+
+
+rtftohtml is based on RTF Tools, which is (c) April 1994 by Paul DuBois
+<dubois@primate.wisc.edu>
+
+RTF Tools has the notice:
+##
+Distribution Availability
+This software may be redistributed without restriction and used for any
+purpose whatsoever.
+##
+
+The supplied version understands RTF version 1.2, while the
+latest version of the specification is RTF 1.6 (May 1999).
+
+I have added some of the newer rtf codes that were preventing some files from
+being parsed properly, but they are not acted on. Most of them do not
+actually affect the content anyway. Depending on how the rtf was created,
+there might be tags that cause us to trip up and fail. If you have
+rtf files that Greenstone is not extracting text from, please send me
+some email (jrm21@cs.waikato.ac.nz).
+
+Note - we extract but do not convert Windows Metafile Format (.wmf) images.
+On unix systems, these can be converted with newer versions of ImageMagick
+(ie "convert" version 4.2.9 or newer).
+Also, libwmf (for unix and windows) is available for conversion at:
+http://wvware.sourceforge.net/projects/libwmf.html
+but we do not include it here due to its dependencies
+(eg libttf for truetype fonts).
+
+Embedded images such as jpegs and pngs are extracted fine, though.
+
+John McPherson (jrm21@cs.waikato.ac.nz)
+June 2001.
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.pm	(revision 16561)
@@ -0,0 +1,198 @@
+#
+# $Id$
+
+package MIME::Base64;
+
+=head1 NAME
+
+MIME::Base64 - Encoding and decoding of base64 strings
+
+=head1 SYNOPSIS
+
+ use MIME::Base64;
+
+ $encoded = encode_base64('Aladdin:open sesame');
+ $decoded = decode_base64($encoded);
+
+=head1 DESCRIPTION
+
+This module provides functions to encode and decode strings into the
+Base64 encoding specified in RFC 2045 - I<MIME (Multipurpose Internet
+Mail Extensions)>. The Base64 encoding is designed to represent
+arbitrary sequences of octets in a form that need not be humanly
+readable. A 65-character subset ([A-Za-z0-9+/=]) of US-ASCII is used,
+enabling 6 bits to be represented per printable character.
+
+The following functions are provided:
+
+=over 4
+
+=item encode_base64($str, [$eol])
+
+Encode data by calling the encode_base64() function.  The first
+argument is the string to encode.  The second argument is the line
+ending sequence to use (it is optional and defaults to C<"\n">).  The
+returned encoded string is broken into lines of no more than 76
+characters each and it will end with $eol unless it is empty.  Pass an
+empty string as second argument if you do not want the encoded string
+broken into lines.
+
+=item decode_base64($str)
+
+Decode a base64 string by calling the decode_base64() function.  This
+function takes a single argument which is the string to decode and
+returns the decoded data.
+
+Any character not part of the 65-character base64 subset set is
+silently ignored.  Characters occuring after a '=' padding character
+are never decoded.
+
+If the length of the string to decode (after ignoring
+non-base64 chars) is not a multiple of 4 or padding occurs too ealy,
+then a warning is generated if perl is running under C<-w>.
+
+=back
+
+If you prefer not to import these routines into your namespace you can
+call them as:
+
+    use MIME::Base64 ();
+    $encoded = MIME::Base64::encode($decoded);
+    $decoded = MIME::Base64::decode($encoded);
+
+=head1 DIAGNOSTICS
+
+The following warnings might be generated if perl is invoked with the
+C<-w> switch:
+
+=over 4
+
+=item Premature end of base64 data
+
+The number of characters to decode is not a multiple of 4.  Legal
+base64 data should be padded with one or two "=" characters to make
+its length a multiple of 4.  The decoded result will anyway be as if
+the padding was there.
+
+=item Premature padding of base64 data
+
+The '=' padding character occurs as the first or second character
+in a base64 quartet.
+
+=back
+
+=head1 EXAMPLES
+
+If you want to encode a large file, you should encode it in chunks
+that are a multiple of 57 bytes.  This ensures that the base64 lines
+line up and that you do not end up with padding in the middle. 57
+bytes of data fills one complete base64 line (76 == 57*4/3):
+
+   use MIME::Base64 qw(encode_base64);
+
+   open(FILE, "/var/log/wtmp") or die "$!";
+   while (read(FILE, $buf, 60*57)) {
+       print encode_base64($buf);
+   }
+
+or if you know you have enough memory
+
+   use MIME::Base64 qw(encode_base64);
+   local($/) = undef;  # slurp
+   print encode_base64(<STDIN>);
+
+=head1 COPYRIGHT
+
+Copyright 1995-1999 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+Distantly based on LWP::Base64 written by Martijn Koster
+<m.koster@nexor.co.uk> and Joerg Reichelt <j.reichelt@nexor.co.uk> and
+code posted to comp.lang.perl <3pd2lp$6gf@wsinti07.win.tue.nl> by Hans
+Mulder <hansm@wsinti07.win.tue.nl>
+
+The XS implementation use code from metamail.  Copyright 1991 Bell
+Communications Research, Inc. (Bellcore)
+
+=cut
+
+use strict;
+use vars qw(@ISA @EXPORT $VERSION $OLD_CODE);
+
+require Exporter;
+require DynaLoader;
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw(encode_base64 decode_base64);
+
+$VERSION = '2.11';
+
+eval { bootstrap MIME::Base64 $VERSION; };
+if ($@) {
+    # can't bootstrap XS implementation, use perl implementation
+    *encode_base64 = \&old_encode_base64;
+    *decode_base64 = \&old_decode_base64;
+
+    $OLD_CODE = $@;
+    #warn $@ if $^W;
+}
+
+# Historically this module has been implemented as pure perl code.
+# The XS implementation runs about 20 times faster, but the Perl
+# code might be more portable, so it is still here.
+
+use integer;
+
+sub old_encode_base64 ($;$)
+{
+    my $res = "";
+    my $eol = $_[1];
+    $eol = "\n" unless defined $eol;
+    pos($_[0]) = 0;                          # ensure start at the beginning
+    while ($_[0] =~ /(.{1,45})/gs) {
+	$res .= substr(pack('u', $1), 1);
+	chop($res);
+    }
+    $res =~ tr|` -_|AA-Za-z0-9+/|;               # `# help emacs
+    # fix padding at the end
+    my $padding = (3 - length($_[0]) % 3) % 3;
+    $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+    # break encoded string into lines of no more than 76 characters each
+    if (length $eol) {
+	$res =~ s/(.{1,76})/$1$eol/g;
+    }
+    $res;
+}
+
+
+sub old_decode_base64 ($)
+{
+    local($^W) = 0; # unpack("u",...) gives bogus warning in 5.00[123]
+
+    my $str = shift;
+    my $res = "";
+
+    $str =~ tr|A-Za-z0-9+=/||cd;            # remove non-base64 chars
+    if (length($str) % 4) {
+	require Carp;
+	Carp::carp("Length of base64 data not a multiple of 4")
+    }
+    $str =~ s/=+$//;                        # remove padding
+    $str =~ tr|A-Za-z0-9+/| -_|;            # convert to uuencoded format
+    while ($str =~ /(.{1,60})/gs) {
+	my $len = chr(32 + length($1)*3/4); # compute length byte
+	$res .= unpack("u", $len . $1 );    # uudecode
+    }
+    $res;
+}
+
+# Set up aliases so that these functions also can be called as
+#
+#    MIME::Base64::encode();
+#    MIME::Base64::decode();
+
+*encode = \&encode_base64;
+*decode = \&decode_base64;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.xs
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.xs	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Base64.xs	(revision 16561)
@@ -0,0 +1,215 @@
+/* $Id$
+
+Copyright 1997-1998 Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+The tables and some of the code that used to be here was borrowed from
+metamail, which comes with this message:
+
+  Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+
+  Permission to use, copy, modify, and distribute this material 
+  for any purpose and without fee is hereby granted, provided 
+  that the above copyright notice and this permission notice 
+  appear in all copies, and that the name of Bellcore not be 
+  used in advertising or publicity pertaining to this 
+  material without the specific, prior written permission 
+  of an authorized representative of Bellcore.	BELLCORE 
+  MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
+  OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
+  WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include "patchlevel.h"
+#if PATCHLEVEL <= 4 && !defined(PL_dowarn)
+   #define PL_dowarn dowarn
+#endif
+
+#define MAX_LINE  76 /* size of encoded lines */
+
+static char basis_64[] =
+   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define XX      255	/* illegal base64 char */
+#define EQ      254	/* padding */
+#define INVALID XX
+
+static unsigned char index_64[256] = {
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
+    52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX,
+    XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
+    XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
+
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+
+
+
+MODULE = MIME::Base64		PACKAGE = MIME::Base64
+
+SV*
+encode_base64(sv,...)
+	SV* sv
+	PROTOTYPE: $;$
+
+	PREINIT:
+	char *str;     /* string to encode */
+	SSize_t len;   /* length of the string */
+	char *eol;     /* the end-of-line sequence to use */
+	STRLEN eollen; /* length of the EOL sequence */
+	char *r;       /* result string */
+	STRLEN rlen;   /* length of result string */
+	unsigned char c1, c2, c3;
+	int chunk;
+
+	CODE:
+	str = SvPV(sv, rlen); /* SvPV(sv, len) gives warning for signed len */
+	len = (SSize_t)rlen;
+
+	/* set up EOL from the second argument if present, default to "\n" */
+	if (items > 1 && SvOK(ST(1))) {
+	    eol = SvPV(ST(1), eollen);
+	} else {
+	    eol = "\n";
+	    eollen = 1;
+	}
+
+	/* calculate the length of the result */
+	rlen = (len+2) / 3 * 4;	 /* encoded bytes */
+	if (rlen) {
+	    /* add space for EOL */
+	    rlen += ((rlen-1) / MAX_LINE + 1) * eollen;
+	}
+
+	/* allocate a result buffer */
+	RETVAL = newSV(rlen ? rlen : 1);
+	SvPOK_on(RETVAL);	
+	SvCUR_set(RETVAL, rlen);
+	r = SvPVX(RETVAL);
+
+	/* encode */
+	for (chunk=0; len > 0; len -= 3, chunk++) {
+	    if (chunk == (MAX_LINE/4)) {
+		char *c = eol;
+		char *e = eol + eollen;
+		while (c < e)
+		    *r++ = *c++;
+		chunk = 0;
+	    }
+	    c1 = *str++;
+	    c2 = *str++;
+	    *r++ = basis_64[c1>>2];
+	    *r++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
+	    if (len > 2) {
+		c3 = *str++;
+		*r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+		*r++ = basis_64[c3 & 0x3F];
+	    } else if (len == 2) {
+		*r++ = basis_64[(c2 & 0xF) << 2];
+		*r++ = '=';
+	    } else { /* len == 1 */
+		*r++ = '=';
+		*r++ = '=';
+	    }
+	}
+	if (rlen) {
+	    /* append eol to the result string */
+	    char *c = eol;
+	    char *e = eol + eollen;
+	    while (c < e)
+		*r++ = *c++;
+	}
+	*r = '\0';  /* every SV in perl should be NUL-terminated */
+
+	OUTPUT:
+	RETVAL
+
+SV*
+decode_base64(sv)
+	SV* sv
+	PROTOTYPE: $
+
+	PREINIT:
+	STRLEN len;
+	register unsigned char *str = (unsigned char*)SvPV(sv, len);
+	unsigned char const* end = str + len;
+	char *r;
+	unsigned char c[4];
+
+	CODE:
+	{
+	    /* always enough, but might be too much */
+	    STRLEN rlen = len * 3 / 4;
+	    RETVAL = newSV(rlen ? rlen : 1);
+	}
+        SvPOK_on(RETVAL);
+        r = SvPVX(RETVAL);
+
+	while (str < end) {
+	    int i = 0;
+            do {
+		unsigned char uc = index_64[*str++];
+		if (uc != INVALID)
+		    c[i++] = uc;
+
+		if (str == end) {
+		    if (i < 4) {
+			if (i && PL_dowarn)
+			    warn("Premature end of base64 data");
+			if (i < 2) goto thats_it;
+			if (i == 2) c[2] = EQ;
+			c[3] = EQ;
+		    }
+		    break;
+		}
+            } while (i < 4);
+	    
+	    if (c[0] == EQ || c[1] == EQ) {
+		if (PL_dowarn) warn("Premature padding of base64 data");
+		break;
+            }
+	    /* printf("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]);/**/
+
+	    *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
+
+	    if (c[2] == EQ)
+		break;
+	    *r++ = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2);
+
+	    if (c[3] == EQ)
+		break;
+	    *r++ = ((c[2] & 0x03) << 6) | c[3];
+	}
+
+      thats_it:
+	SvCUR_set(RETVAL, r - SvPVX(RETVAL));
+	*r = '\0';
+
+	OUTPUT:
+	RETVAL
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Changes
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Changes	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Changes	(revision 16561)
@@ -0,0 +1,119 @@
+1998-02-27   Gisle Aas <gisle@aas.no>
+
+   Release 2.11
+
+   Fixed bogues "Premature end of base64 data" warning.  Bug spotted
+   by Dwayne Jacques Fontenot.
+
+   Workaround for Redhat shipping trial releases of perl.
+
+
+
+1998-12-18   Gisle Aas <aas@sn.no>
+
+   Release 2.10
+
+   A tweak that should make compilation with some old perl5.00[23]
+   perls better.
+
+   A cast that make some compilers more happy.
+
+
+
+1998-11-13   Gisle Aas <aas@sn.no>
+
+   Release 2.09
+
+   The 2.08 release did not compile with perl5.005_53, because
+   all simple globals now need to be prefixed with "PL_".
+
+
+
+1998-10-22   Gisle Aas <aas@sn.no>
+
+   Release 2.08
+
+   Found another tweak to speed up decode_base64() with another 3%.
+
+   Improved MIME::Base64 documentation a little.
+
+
+
+1998-10-21   Gisle Aas <aas@sn.no>
+
+   Release 2.07
+
+   Faster and smarter C implementation of the decode_base64()
+   function.  The new decode_base64() was 25% faster when tested
+   on Linux, i586, gcc -O2.
+
+
+
+1998-07-15   Gisle Aas <aas@sn.no>
+
+   Release 2.06
+
+   The decode_base64() implemented in pure perl will only carp
+   (not croak) if length of data to decode is not a multiple 4.  This
+   actually made 'make test' fail after 'rm Base64.xs'.
+
+
+
+1998-01-27   Gisle Aas <aas@sn.no>
+
+   Release 2.05
+
+   The decode_base64() would previously allocate a too short buffer for the
+   result string when the trailing "==" padding was missing in the string to
+   be decoded.
+
+   The encode_base64() now allocate one byte less space in the result
+   strings returned.
+
+
+
+1997-12-02   Gisle Aas <aas@sn.no>
+
+   Release 2.04
+
+   Documentation expanded a bit.
+
+
+
+1997-07-10   Gisle Aas <aas@sn.no>
+
+   Release 2.03
+
+   Decode_base64() doesn't croak on premature ended data any more.
+   A warning is generated instead if running under -w.
+   
+
+
+1997-06-27   Gisle Aas <aas@sn.no>
+
+   Release 2.02
+
+   QuotedPrint fix by Roderick Schertler <roderick@argon.org>:
+
+      - Long lines were not broken unless they're at the beginning
+        of the text
+
+      - Lines near but not over 76 chars were broken when they
+        shouldn't be
+
+
+
+1997-06-13   Gisle Aas <aas@sn.no>
+
+   Release 2.01
+
+   Base64.xs: Avoid type convertion warnings with some compilers
+
+   Minor documentation updates
+
+
+
+1997-04-24   Gisle Aas <aas@sn.no>
+
+   Release 2.00, based on libwww-perl-5.08.
+
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/INSTALL.w3mir
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/INSTALL.w3mir	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/INSTALL.w3mir	(revision 16561)
@@ -0,0 +1,27 @@
+- You also have to get the MIME-Base64 package
+  (http://www.perl.com/CPAN/modules/by-module/MIME/)
+
+- Unpack it:
+
+    gzip -dc MIME-Base64-*.tar.gz | tar xvBf -
+
+- Install it:
+
+    cd MIME-Base64-*
+
+  Then, as above, either
+
+    perl Makefile.PL
+
+  or
+
+    perl Makefile.PL PREFIX=/some/dir
+
+  Continue:
+
+    make
+    make test      (it should print 'All tests successful.')
+    make install
+
+  Now MIME-Base64 should be installed and working.
+
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/MANIFEST
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/MANIFEST	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/MANIFEST	(revision 16561)
@@ -0,0 +1,9 @@
+Base64.pm	Bootstrap and perl implementation of MIME::Base64
+Base64.xs	Encoder and decoder written in C
+Changes		What's new
+MANIFEST	This file
+Makefile.PL	Writes the Makefile
+QuotedPrint.pm  MIME::QuotedPrint module, encoder and decoder
+README		Please do!
+t/base64.t	Test that it works
+t/quoted-print.t
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Makefile.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Makefile.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/Makefile.PL	(revision 16561)
@@ -0,0 +1,8 @@
+require 5.002;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME	 => 'MIME::Base64',
+    VERSION_FROM => 'Base64.pm',
+    dist         => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+);
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/QuotedPrint.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/QuotedPrint.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/QuotedPrint.pm	(revision 16561)
@@ -0,0 +1,115 @@
+#
+# $Id$
+
+package MIME::QuotedPrint;
+
+=head1 NAME
+
+MIME::QuotedPrint - Encoding and decoding of quoted-printable strings
+
+=head1 SYNOPSIS
+
+ use MIME::QuotedPrint;
+
+ $encoded = encode_qp($decoded);
+ $decoded = decode_qp($encoded);
+
+=head1 DESCRIPTION
+
+This module provides functions to encode and decode strings into the
+Quoted-Printable encoding specified in RFC 2045 - I<MIME (Multipurpose
+Internet Mail Extensions)>.  The Quoted-Printable encoding is intended
+to represent data that largely consists of bytes that correspond to
+printable characters in the ASCII character set.  Non-printable
+characters (as defined by english americans) are represented by a
+triplet consisting of the character "=" followed by two hexadecimal
+digits.
+
+The following functions are provided:
+
+=over 4
+
+=item encode_qp($str)
+
+This function will return an encoded version of the string given as
+argument.
+
+Note that encode_qp() does not change newlines C<"\n"> to the CRLF
+sequence even though this might be considered the right thing to do
+(RFC 2045 (Q-P Rule #4)).
+
+=item decode_qp($str);
+
+This function will return the plain text version of the string given
+as argument.
+
+=back
+
+
+If you prefer not to import these routines into your namespace you can
+call them as:
+
+  use MIME::QuotedPrint ();
+  $encoded = MIME::QuotedPrint::encode($decoded);
+  $decoded = MIME::QuotedPrint::decode($encoded);
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+use vars qw(@ISA @EXPORT $VERSION);
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(encode_qp decode_qp);
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+
+sub encode_qp ($)
+{
+    my $res = shift;
+    $res =~ s/([^ \t\n!-<>-~])/sprintf("=%02X", ord($1))/eg;  # rule #2,#3
+    $res =~ s/([ \t]+)$/
+      join('', map { sprintf("=%02X", ord($_)) }
+		   split('', $1)
+      )/egm;                        # rule #3 (encode whitespace at eol)
+
+    # rule #5 (lines must be shorter than 76 chars, but we are not allowed
+    # to break =XX escapes.  This makes things complicated :-( )
+    my $brokenlines = "";
+    $brokenlines .= "$1=\n"
+	while $res =~ s/(.*?^[^\n]{73} (?:
+		 [^=\n]{2} (?! [^=\n]{0,1} $) # 75 not followed by .?\n
+		|[^=\n]    (?! [^=\n]{0,2} $) # 74 not followed by .?.?\n
+		|          (?! [^=\n]{0,3} $) # 73 not followed by .?.?.?\n
+	    ))//xsm;
+
+    "$brokenlines$res";
+}
+
+
+sub decode_qp ($)
+{
+    my $res = shift;
+    $res =~ s/[ \t]+?(\r?\n)/$1/g;  # rule #3 (trailing space must be deleted)
+    $res =~ s/=\r?\n//g;            # rule #5 (soft line breaks)
+    $res =~ s/=([\da-fA-F]{2})/pack("C", hex($1))/ge;
+    $res;
+}
+
+# Set up aliases so that these functions also can be called as
+#
+# MIME::QuotedPrint::encode();
+# MIME::QuotedPrint::decode();
+
+*encode = \&encode_qp;
+*decode = \&decode_qp;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/README
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/README	(revision 16561)
@@ -0,0 +1,38 @@
+This package contains a base64 encoder/decoder and a quoted-printable
+encoder/decoder.  These encoding methods are specified in RFC 2045 -
+MIME (Multipurpose Internet Mail Extensions).
+
+The Base64 encoding is designed to represent arbitrary sequences of
+octets in a form that need not be humanly readable. A 65-character
+subset ([A-Za-z0-9+/=]) of US-ASCII is used, enabling 6 bits to be
+represented per printable character.
+
+The quoted-printable encoding is intended to represent data that
+largely consists of bytes that correspond to printable characters in
+the ASCII character set.  Non-printable characters are represented by
+a triplet consisting of the character "=" followed by two hexadecimal
+digits.
+
+The MIME::Base64 and MIME::QuotedPrint modules used to be part of
+libwww-perl package.  They are now distributed separately (as this
+package).  The main improvement is that the base64 encoder/decoder is
+implemented by XS functions.  This makes it about 20 times faster than
+the old implementation as pure Perl.
+
+In order to install and use this package you will need Perl version
+5.002 or better.  Installation as usual:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+The library can also be installed without XS support (for instance because
+you don't have a working C compiler installed).  Simply remove the Base64.xs
+file before running 'perl Makefile.PL' and everything should work the same,
+although encoding and decoding will be much slower.
+
+Copyright 1995-1999 Gisle Aas <gisle@aas.no>
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/install.sh
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/install.sh	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/install.sh	(revision 16561)
@@ -0,0 +1,10 @@
+#!/bin/sh -v
+
+export PERL5LIB=$GSDLHOME/perllib/cpan/lib:$PERL5LIB
+export PATH=$GSDLHOME/perllib/cpan/bin:$PATH
+
+perl Makefile.PL PREFIX=$GSDLHOME/perllib/cpan
+
+make
+make test
+make install
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/base64.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/base64.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/base64.t	(revision 16561)
@@ -0,0 +1,362 @@
+use MIME::Base64;
+
+print "1..282\n";
+
+print "Testing MIME::Base64-", $MIME::Base64::VERSION, "\n";
+
+$testno = 1;
+
+encodeTest();
+decodeTest();
+
+# This used to generate a warning
+print "not " unless decode_base64(encode_base64("foo")) eq "foo";
+print "ok ", $testno++, "\n";
+
+sub encodeTest
+{
+    print "encode test\n";
+
+    my @encode_tests = (
+        [''    => ''],
+	['a'   => 'YQ=='],
+	['aa'  => 'YWE='],
+	['aaa' => 'YWFh'],
+
+	['aaa' => 'YWFh'],
+	['aaa' => 'YWFh'],
+	['aaa' => 'YWFh'],
+
+	["\000\377" => "AP8="],
+	["\377\000" => "/wA="],
+	["\000\000\000" => "AAAA"],
+
+	# from HTTP spec
+	['Aladdin:open sesame' => 'QWxhZGRpbjpvcGVuIHNlc2FtZQ=='],
+
+	['a' x 100 => 'YWFh' x 33 . 'YQ=='],
+
+	['Multipurpose Internet Mail Extensions: The Base64 Content-Transfer-Encoding is designed to represent sequences of octets in a form that is not humanly readable. '
+	=> "TXVsdGlwdXJwb3NlIEludGVybmV0IE1haWwgRXh0ZW5zaW9uczogVGhlIEJhc2U2NCBDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nIGlzIGRlc2lnbmVkIHRvIHJlcHJlc2VudCBzZXF1ZW5jZXMgb2Ygb2N0ZXRzIGluIGEgZm9ybSB0aGF0IGlzIG5vdCBodW1hbmx5IHJlYWRhYmxlLiA="],
+
+	# All values
+	["\000" => "AA=="],
+	["\001" => "AQ=="],
+	["\002" => "Ag=="],
+	["\003" => "Aw=="],
+	["\004" => "BA=="],
+	["\005" => "BQ=="],
+	["\006" => "Bg=="],
+	["\007" => "Bw=="],
+	["\010" => "CA=="],
+	["\011" => "CQ=="],
+	["\012" => "Cg=="],
+	["\013" => "Cw=="],
+	["\014" => "DA=="],
+	["\015" => "DQ=="],
+	["\016" => "Dg=="],
+	["\017" => "Dw=="],
+	["\020" => "EA=="],
+	["\021" => "EQ=="],
+	["\022" => "Eg=="],
+	["\023" => "Ew=="],
+	["\024" => "FA=="],
+	["\025" => "FQ=="],
+	["\026" => "Fg=="],
+	["\027" => "Fw=="],
+	["\030" => "GA=="],
+	["\031" => "GQ=="],
+	["\032" => "Gg=="],
+	["\033" => "Gw=="],
+	["\034" => "HA=="],
+	["\035" => "HQ=="],
+	["\036" => "Hg=="],
+	["\037" => "Hw=="],
+	["\040" => "IA=="],
+	["\041" => "IQ=="],
+	["\042" => "Ig=="],
+	["\043" => "Iw=="],
+	["\044" => "JA=="],
+	["\045" => "JQ=="],
+	["\046" => "Jg=="],
+	["\047" => "Jw=="],
+	["\050" => "KA=="],
+	["\051" => "KQ=="],
+	["\052" => "Kg=="],
+	["\053" => "Kw=="],
+	["\054" => "LA=="],
+	["\055" => "LQ=="],
+	["\056" => "Lg=="],
+	["\057" => "Lw=="],
+	["\060" => "MA=="],
+	["\061" => "MQ=="],
+	["\062" => "Mg=="],
+	["\063" => "Mw=="],
+	["\064" => "NA=="],
+	["\065" => "NQ=="],
+	["\066" => "Ng=="],
+	["\067" => "Nw=="],
+	["\070" => "OA=="],
+	["\071" => "OQ=="],
+	["\072" => "Og=="],
+	["\073" => "Ow=="],
+	["\074" => "PA=="],
+	["\075" => "PQ=="],
+	["\076" => "Pg=="],
+	["\077" => "Pw=="],
+	["\100" => "QA=="],
+	["\101" => "QQ=="],
+	["\102" => "Qg=="],
+	["\103" => "Qw=="],
+	["\104" => "RA=="],
+	["\105" => "RQ=="],
+	["\106" => "Rg=="],
+	["\107" => "Rw=="],
+	["\110" => "SA=="],
+	["\111" => "SQ=="],
+	["\112" => "Sg=="],
+	["\113" => "Sw=="],
+	["\114" => "TA=="],
+	["\115" => "TQ=="],
+	["\116" => "Tg=="],
+	["\117" => "Tw=="],
+	["\120" => "UA=="],
+	["\121" => "UQ=="],
+	["\122" => "Ug=="],
+	["\123" => "Uw=="],
+	["\124" => "VA=="],
+	["\125" => "VQ=="],
+	["\126" => "Vg=="],
+	["\127" => "Vw=="],
+	["\130" => "WA=="],
+	["\131" => "WQ=="],
+	["\132" => "Wg=="],
+	["\133" => "Ww=="],
+	["\134" => "XA=="],
+	["\135" => "XQ=="],
+	["\136" => "Xg=="],
+	["\137" => "Xw=="],
+	["\140" => "YA=="],
+	["\141" => "YQ=="],
+	["\142" => "Yg=="],
+	["\143" => "Yw=="],
+	["\144" => "ZA=="],
+	["\145" => "ZQ=="],
+	["\146" => "Zg=="],
+	["\147" => "Zw=="],
+	["\150" => "aA=="],
+	["\151" => "aQ=="],
+	["\152" => "ag=="],
+	["\153" => "aw=="],
+	["\154" => "bA=="],
+	["\155" => "bQ=="],
+	["\156" => "bg=="],
+	["\157" => "bw=="],
+	["\160" => "cA=="],
+	["\161" => "cQ=="],
+	["\162" => "cg=="],
+	["\163" => "cw=="],
+	["\164" => "dA=="],
+	["\165" => "dQ=="],
+	["\166" => "dg=="],
+	["\167" => "dw=="],
+	["\170" => "eA=="],
+	["\171" => "eQ=="],
+	["\172" => "eg=="],
+	["\173" => "ew=="],
+	["\174" => "fA=="],
+	["\175" => "fQ=="],
+	["\176" => "fg=="],
+	["\177" => "fw=="],
+	["\200" => "gA=="],
+	["\201" => "gQ=="],
+	["\202" => "gg=="],
+	["\203" => "gw=="],
+	["\204" => "hA=="],
+	["\205" => "hQ=="],
+	["\206" => "hg=="],
+	["\207" => "hw=="],
+	["\210" => "iA=="],
+	["\211" => "iQ=="],
+	["\212" => "ig=="],
+	["\213" => "iw=="],
+	["\214" => "jA=="],
+	["\215" => "jQ=="],
+	["\216" => "jg=="],
+	["\217" => "jw=="],
+	["\220" => "kA=="],
+	["\221" => "kQ=="],
+	["\222" => "kg=="],
+	["\223" => "kw=="],
+	["\224" => "lA=="],
+	["\225" => "lQ=="],
+	["\226" => "lg=="],
+	["\227" => "lw=="],
+	["\230" => "mA=="],
+	["\231" => "mQ=="],
+	["\232" => "mg=="],
+	["\233" => "mw=="],
+	["\234" => "nA=="],
+	["\235" => "nQ=="],
+	["\236" => "ng=="],
+	["\237" => "nw=="],
+	["\240" => "oA=="],
+	["\241" => "oQ=="],
+	["\242" => "og=="],
+	["\243" => "ow=="],
+	["\244" => "pA=="],
+	["\245" => "pQ=="],
+	["\246" => "pg=="],
+	["\247" => "pw=="],
+	["\250" => "qA=="],
+	["\251" => "qQ=="],
+	["\252" => "qg=="],
+	["\253" => "qw=="],
+	["\254" => "rA=="],
+	["\255" => "rQ=="],
+	["\256" => "rg=="],
+	["\257" => "rw=="],
+	["\260" => "sA=="],
+	["\261" => "sQ=="],
+	["\262" => "sg=="],
+	["\263" => "sw=="],
+	["\264" => "tA=="],
+	["\265" => "tQ=="],
+	["\266" => "tg=="],
+	["\267" => "tw=="],
+	["\270" => "uA=="],
+	["\271" => "uQ=="],
+	["\272" => "ug=="],
+	["\273" => "uw=="],
+	["\274" => "vA=="],
+	["\275" => "vQ=="],
+	["\276" => "vg=="],
+	["\277" => "vw=="],
+	["\300" => "wA=="],
+	["\301" => "wQ=="],
+	["\302" => "wg=="],
+	["\303" => "ww=="],
+	["\304" => "xA=="],
+	["\305" => "xQ=="],
+	["\306" => "xg=="],
+	["\307" => "xw=="],
+	["\310" => "yA=="],
+	["\311" => "yQ=="],
+	["\312" => "yg=="],
+	["\313" => "yw=="],
+	["\314" => "zA=="],
+	["\315" => "zQ=="],
+	["\316" => "zg=="],
+	["\317" => "zw=="],
+	["\320" => "0A=="],
+	["\321" => "0Q=="],
+	["\322" => "0g=="],
+	["\323" => "0w=="],
+	["\324" => "1A=="],
+	["\325" => "1Q=="],
+	["\326" => "1g=="],
+	["\327" => "1w=="],
+	["\330" => "2A=="],
+	["\331" => "2Q=="],
+	["\332" => "2g=="],
+	["\333" => "2w=="],
+	["\334" => "3A=="],
+	["\335" => "3Q=="],
+	["\336" => "3g=="],
+	["\337" => "3w=="],
+	["\340" => "4A=="],
+	["\341" => "4Q=="],
+	["\342" => "4g=="],
+	["\343" => "4w=="],
+	["\344" => "5A=="],
+	["\345" => "5Q=="],
+	["\346" => "5g=="],
+	["\347" => "5w=="],
+	["\350" => "6A=="],
+	["\351" => "6Q=="],
+	["\352" => "6g=="],
+	["\353" => "6w=="],
+	["\354" => "7A=="],
+	["\355" => "7Q=="],
+	["\356" => "7g=="],
+	["\357" => "7w=="],
+	["\360" => "8A=="],
+	["\361" => "8Q=="],
+	["\362" => "8g=="],
+	["\363" => "8w=="],
+	["\364" => "9A=="],
+	["\365" => "9Q=="],
+	["\366" => "9g=="],
+	["\367" => "9w=="],
+	["\370" => "+A=="],
+	["\371" => "+Q=="],
+	["\372" => "+g=="],
+	["\373" => "+w=="],
+	["\374" => "/A=="],
+	["\375" => "/Q=="],
+	["\376" => "/g=="],
+	["\377" => "/w=="],
+    );
+
+    for $test (@encode_tests) {
+	my($plain, $expected) = ($$test[0], $$test[1]);
+
+	my $encoded = encode_base64($plain, '');
+	if ($encoded ne $expected) {
+	    print "test $testno ($plain): expected $expected, got $encoded\n";
+            print "not ";
+	}
+	my $decoded = decode_base64($encoded);
+	if ($decoded ne $plain) {
+	    print "test $testno ($plain): expected $expected, got $encoded\n";
+            print "not ";
+	}
+
+	# Try the old C versions too
+	if ($encoded ne MIME::Base64::old_encode_base64($plain, '')) {
+	    print "old_encode_base64 give different result.\n";
+	    print "not ";
+        }
+	if ($plain ne MIME::Base64::old_decode_base64($encoded)) {
+	    print "old_decode_base64 give different result.\n";
+	    print "not ";
+        }
+		
+	print "ok $testno\n";
+	$testno++;
+    }
+}
+
+sub decodeTest
+{
+    print "decode test:\n";
+    local $SIG{__WARN__} = sub { print $_[0] };  # avoid warnings on stderr
+
+    my @decode_tests = (
+	['YWE='   => 'aa'],
+	[' YWE='  => 'aa'],
+	['Y WE='  => 'aa'],
+	['YWE= '  => 'aa'],
+	["Y\nW\r\nE=" => 'aa'],
+
+	# These will generate some warnings
+        ['YWE=====' => 'aa'],    # extra padding
+	['YWE'      => 'aa'],    # missing padding
+        ['YWFh====' => 'aaa'],
+        ['YQ'       => 'a'],
+        ['Y'        => ''],
+        [''         => ''],
+        [undef()    => ''],
+    );
+
+    for $test (@decode_tests) {
+	my($encoded, $expected) = ($$test[0], $$test[1]);
+
+	my $decoded = decode_base64($encoded);
+	if ($decoded ne $expected) {
+	    die "test $testno ($encoded): expected $expected, got $decoded\n";
+	}
+	print "ok $testno\n";
+	$testno++;
+    }
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/quoted-print.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/quoted-print.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/MIME-Base64-2.11/t/quoted-print.t	(revision 16561)
@@ -0,0 +1,106 @@
+#!/local/bin/perl -w
+
+use MIME::QuotedPrint;
+
+$x70 = "x" x 70;
+
+@tests =
+  (
+   # plain ascii should not be encoded
+   ["quoted printable"  =>
+    "quoted printable"],
+
+   # 8-bit chars should be encoded
+   ["våre kjære norske tegn bør æres" =>
+    "v=E5re kj=E6re norske tegn b=F8r =E6res"],
+
+   # trailing space should be encoded
+   ["  " => "=20=20"],
+   ["\tt\t" => "\tt=09"],
+   ["test  \ntest\n\t \t \n" => "test=20=20\ntest\n=09=20=09=20\n"],
+
+   # "=" is special an should be decoded
+   ["=\n" => "=3D\n"],
+   ["\0\xff" => "=00=FF"],
+
+   # Very long lines should be broken (not more than 76 chars
+   ["The Quoted-Printable encoding is intended to represent data that largly consists of octets that correspond to printable characters in the ASCII character set." =>
+    "The Quoted-Printable encoding is intended to represent data that largly con=
+sists of octets that correspond to printable characters in the ASCII charac=
+ter set."
+    ],
+
+   # Long lines after short lines were broken through 2.01.
+   ["short line
+In America, any boy may become president and I suppose that's just one of the risks he takes. -- Adlai Stevenson" =>
+    "short line
+In America, any boy may become president and I suppose that's just one of t=
+he risks he takes. -- Adlai Stevenson"],
+
+   # My (roderick@argon.org) first crack at fixing that bug failed for
+   # multiple long lines.
+   ["College football is a game which would be much more interesting if the faculty played instead of the students, and even more interesting if the
+trustees played.  There would be a great increase in broken arms, legs, and necks, and simultaneously an appreciable diminution in the loss to humanity. -- H. L. Mencken" =>
+    "College football is a game which would be much more interesting if the facu=
+lty played instead of the students, and even more interesting if the
+trustees played.  There would be a great increase in broken arms, legs, and=
+ necks, and simultaneously an appreciable diminution in the loss to humanit=
+y. -- H. L. Mencken"],
+
+   # Don't break a line that's near but not over 76 chars.
+   ["$x70!23"		=> "$x70!23"],
+   ["$x70!234"		=> "$x70!234"],
+   ["$x70!2345"		=> "$x70!2345"],
+   ["$x70!23456"	=> "$x70!23456"],
+   ["$x70!23\n"		=> "$x70!23\n"],
+   ["$x70!234\n"	=> "$x70!234\n"],
+   ["$x70!2345\n"	=> "$x70!2345\n"],
+   ["$x70!23456\n"	=> "$x70!23456\n"],
+
+   # Not allowed to break =XX escapes using soft line break
+   ["$x70===xxxx" => "$x70=3D=\n=3D=3Dxxxx"],
+   ["$x70!===xxx" => "$x70!=3D=\n=3D=3Dxxx"],
+   ["$x70!!===xx" => "$x70!!=3D=\n=3D=3Dxx"],
+   ["$x70!!!===x" => "$x70!!!=\n=3D=3D=3Dx"],
+   #                            ^
+   #                    70123456|
+   #                           max
+   #                        line width
+);
+
+$notests = @tests + 2;
+print "1..$notests\n";
+
+$testno = 0;
+for (@tests) {
+    $testno++;
+    ($plain, $encoded) = @$_;
+    $x = encode_qp($plain);
+    if ($x ne $encoded) {
+	print "Encode test failed\n";
+	print "Got:      '$x'\n";
+	print "Expected: '$encoded'\n";
+	print "not ok $testno\n";
+	next;
+    }
+    $x = decode_qp($encoded);
+    if ($x ne $plain) {
+	print "Decode test failed\n";
+	print "Got:      '$x'\n";
+	print "Expected: '$plain'\n";
+	print "not ok $testno\n";
+	next;
+    }
+    print "ok $testno\n";
+}
+
+# Some extra testing for a case that was wrong until libwww-perl-5.09
+print "not " unless decode_qp("foo  \n\nfoo =\n\nfoo=20\n\n") eq
+                                "foo\n\nfoo \nfoo \n\n";
+$testno++; print "ok $testno\n";
+
+# Same test but with "\r\n" terminated lines
+print "not " unless decode_qp("foo  \r\n\r\nfoo =\r\n\r\nfoo=20\r\n\r\n") eq
+                                "foo\r\n\r\nfoo \r\nfoo \r\n\r\n";
+$testno++; print "ok $testno\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/ChangeLog
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/ChangeLog	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/ChangeLog	(revision 16561)
@@ -0,0 +1,1535 @@
+Tue Aug  4 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.36
+
+ o  The lwp-download script will call $ua->env_proxy now.
+
+ o  The lwp-request script allows content types (specified with the -c
+    option) with optional parameters like: multipart/mixed; boundary="--".
+
+ o  LWP::UserAgent will lowercase all authentication parameter names
+    before passing it to the authentication module.  Previous releases
+    ignored parameters like; Realm="Foo" (bacause Realm contained
+    upper case letters).
+
+ o  LWP::Protocol::ftp test for If-Modified-Since was wrong.
+
+ o  How the $url->abs method works can now be configured with the global
+    variables $URI::URL::ABS_ALLOW_RELATIVE_SCHEME and
+    $URI::URL::ABS_REMOTE_LEADING_DOTS.
+
+ o  The anonymous password guesser for ftp URLs will now call the external
+    `whoami` program any more.  Patch by Charles C. Fu <ccwf@bacchus.com>.
+
+ o  LWP::Protocol::http now allow dynamic requests without any
+    Content-Length specified when Content-Type is multipart/*
+
+ o  HTTP::Request::Common can now upload infinite files.
+    (Controlled by the $DYNAMIC_FILE_UPLOAD configuration variable.)
+
+
+
+Fri Jul 10 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.35
+
+ o  More lwp-rget patches from David D. Kilzer <ddkilzer@madison.dseg.ti.com>.
+    Adds the following new options: --iis, --keepext, --tolower
+
+ o  LWP::MediaTypes patches from MacEachern <dougm@pobox.com>.  Adds new
+    functions: add_type(), add_encoding(), read_media_types()
+
+
+
+Tue Jul  7 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.34
+
+ o  LWP::Protocol::ftp now try to use the MDTM command to support
+    the Last-Modified response header as well as
+    If-Modified-Since in requests.  Original and final patch by
+    Charles C. Fu <ccwf@bacchus.com>
+
+ o  $url->path_components will not escape "." any more.
+
+ o  WWW::RobotRules will now work for Mac text files too (lines delimited
+    by CR only).  Patch by Olly Betts <olly@muscat.co.uk>
+
+ o  lwp-rget support <area ..> links too.
+
+
+
+Thu May  7 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.33
+
+ o  LWP::Simple::get() did try to handle too many of the 3xx
+    codes as redirect when it bypasses full LWP.
+
+ o  LWP::UserAgent->mirror will now use utime(2) to set the
+    file modification time corresponding to the Last-Modified
+    header.
+
+ o  LWP::Protocol::http will not modify the HTTP::Request that
+    it is processing.  This avoids sticky Host header for
+    redirects.
+
+ o  URI::Heuristic and lwp-download documentation update.
+
+
+
+Wed Apr 15 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.32
+
+ o  Much improved HTTP::Daemon class. We now support persistent
+    connections.  Changes include:
+       - $c->get_request can be told to return after reading and
+         parsing headers only.
+       - $c->reason (new method)
+       - $c->read_buffer (new method)
+       - $c->proto_ge (new method)
+       - $c->force_last_request (new method)
+       - $c->send_response now support CODE reference content
+         and will use chunked transfer encoding for HTTP/1.1 clients.
+       - expanded the documentation.
+
+
+
+Fri Apr 10 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.31
+
+ o  Makefile.PL now checks that HTML::HeadParser is present.
+
+ o  Updated HTTP::Cookies according to draft-ietf-http-state-man-mec-08.txt
+    It now supports the .local domain and value less 'port' attribute in
+    the Set-Cookie2 header.
+
+ o  HTTP::Headers update:
+       - $h->content_type now always return a defined value
+       - $h->header($field) will now concatenate multi-valued header
+         fields with "," as separator in scalar context.
+
+ o  HTTP::Request::Common update:
+       - used to destroy the content of the  hash/array arguments
+         passed to its constructor functions.
+       - allow a hash reference to specify form-data content.
+       - you can override Content-Disposition for form-data now.
+       - set content-encoding for files if applicable
+       - default boundary string is now always "--000"
+
+ o  LWP::UserAgent will not follow more than 13 redirects
+    automatically.
+
+
+
+Wed Apr  1 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.30
+
+ o  Unbundled the following modules:
+
+       * HTML-Parser  (HTML::Parser, HTML::Entites, HTML::LinkExtor,...)
+       * HTML-Tree    (HTML::Element, HTML::TreeBuilder,...)
+       * Font-AFM     (Font::AFM, Font::Metrics::*)
+       * File-CounterFile
+
+ o  Simplified internal structure of HTML::Headers.  Hopefully,
+    nobody will notice.
+
+ o  New modules HTTP::Headers::Auth,  HTTP::Headers::ETag that adds
+    additional convenience methods to the HTTP::Headers class.
+
+ o  Removed split_etag_list() from HTTP::Headers::Util, in the hope
+    that nobody had starting using it.
+
+
+
+Tue Mar 24 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.22
+
+ o  HTTP::Cookies made more compatible with Netscape cookies.  Allow
+    the domain to match host, allow dots in the part of the hostname
+    not covered by domain.  Don't quote the cookie value even when it
+    contains non-token chars.  Based on patch from Kartik Subbarao
+    <subbarao@computer.org>.
+
+ o  Updated HTTP::Status to reflect <draft-ietf-http-v11-spec-rev-03>.
+    RC_MOVED_TEMPORARILY renamed to RC_FOUND.  Added codes
+    RC_TEMPORARY_REDIRECT (307) and RC_EXPECTATION_FAILED (417).
+    Slightly more documentation too.
+
+ o  The split_header_words() function HTTP::Headers::Util could go
+    into infinite loop on some header values.  Implemented split_etag_list()
+    too.  Added more documentation and test script for this module.
+
+ o  LWP::Simple now switch to full LWP implementation even for systems
+    that force all environment keys to be upper case.  Modification
+    suggested by Dale Couch <dcouch@training.orl.lmco.com>.
+
+ o  LWP::UserAgent allows redirects to a relative URL with scheme to be
+    made.  Suggested by Chris W. Unger <cunger@cas.org>.
+
+ o  Applied dtd2pm.pl patches from <peterm@zeta.org.au>.  It can now
+    extract information from the HTML40.dtd
+
+
+
+Thu Mar 12 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.21
+
+ o  lwp-rget patches from David D. Kilzer <ddkilzer@madison.dseg.ti.com>
+    (modified by Gisle).  Support the --hier  and the --auth options
+    and <frame>s.
+
+ o  File::CounterFile protect against bad $/ and $\ as suggested
+    by Frank Hoehne.
+ 
+ o  File::Listing used "next" when return was more appropriate.
+    Patch by erik@mediator.uni-c.dk.
+
+ o  HTML::Element support for multiple boolean attributes for a single
+    element.  Patch by Philip Guenther.
+
+ o  Can set $HTTP::Headers::TRANSLATE_UNDERSCORE to FALSE value to
+    suppress tr/_/-/ of header keys.
+
+ o  LWP::Protocol::http will not initialize the Host header if it is
+    already set. Patch by Ed Jordan.
+
+ o  LWP::Protocol::http did not handle responses with no header lines
+    correctly.  Patch by Daniel Buenzli <buenzli@rzu.unizh.ch>
+
+ o  $url->rel() handles path segments without leading "/" better.
+
+
+
+Fri Feb 13 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.20
+
+ o  Fixed the "500 Offset outside string" bug that affected perl
+    5.004_03 and older version of Perl.
+
+ o  Fixed a documentation typo spotted by Michael Quaranta
+    <quaranta@vnet.IBM.COM>
+
+ o  HTTP::Date: Protect against croaking from timegm/timelocal.
+
+
+
+Mon Jan 26 1998   Gisle Aas <aas@sn.no>
+
+ o  Release 5.19
+
+ o  HTML::Parser does not call $self->text() callback for empty text
+    any more.
+
+ o  LWP::Protocol::https was noisy when connections failed and the
+    script was running with '-w' (noise inherited from IO::Socket::INET)
+
+ o  $ua->use_alarm(BOOL) now gives a warning if running with -w
+
+
+
+Tue Jan 20 1998   Gisle Aas <aas@sn.no>
+
+ o  Developer release 5.18_05
+
+ o  HTTPS support based on my Crypt-SSLeay module.  The Net-SSLeay module
+    is not supported any more.
+
+ o  lwp-request documentation typo spotted Martijn Koster.
+
+ o  Removed superfluous \\ in the URI::Escape regex. This was also
+    spotted by Martijn.
+
+ o  File::Listing now handles timezones correctly.
+
+ o  Added $VERSION to modules that was missing it.
+
+ o  Added 'use strict' to several modules that was missing it.
+
+ o  LWP::Protocol::http now adds the Client-Peer header to responses and
+    has hooks for more callbacks.
+
+ o  LWP::Protocol::https adds Client-SSL-Cipher, Client-SSL-Cert-Subject
+    and Client-SSL-Cert-Issuer headers to the response.  The requests can
+    also be made conditional based on the peer certificate using the
+    If-SSL-Cert-Subject header in requests.
+
+ o  HTML::Parse is back.  (It was even used in lwpcook.pod)
+
+
+
+Wed Dec 17 1997   Gisle Aas <aas@sn.no>
+
+ o  Developer release 5.18_04
+
+ o  Makefile.PL fix based on report from Pon Hwa Lin <koala@fragment.com>
+
+ o  lwp-request will now print the response code message with -s and -S
+    options.
+
+ o  Hide IO::Socket::INET noise when running under -w
+
+ o  Don't set 'Content-Length: 0' in HTTP requests.
+
+ o  LWP::Protocol::http now calls LWP::Debug::conns again
+
+
+
+Tue Dec 16 1997   Gisle Aas <aas@sn.no>
+
+ o  Developer release 5.18_03
+
+ o  Got rid of alarms() and replaced LWP::Socket with IO::Socket::INET.
+    New protocol implementations for http, https, gopher, nntp.
+    $ua->use_alarm() is now a noop.
+
+ o  LWP::Protocol::ftp patch from  Tony Finch <fanf@demon.net>.
+
+ o  Removed depreciated modules from the distibution; HTML::Parse,
+    LWP::Socket, LWP::SecureSocket, LWP::IO, LWP::TkIO.
+
+
+
+Fri Dec 12 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.18
+
+ o  HTTP authorization patches from Tony Finch <fanf@demon.net>.
+    Allows "user:pass@" in HTTP URLs.
+
+ o  HTML::Parser patch by Brian McCauley <B.A.McCauley@bham.ac.uk>.
+    Pass original text to end() method.
+
+ o  The HTML::Parser->netscape_buggy_comment method is depreciated.
+    Use HTML::Parser->strict_comment instead.  The default value
+    has changed with the name.
+
+ o  Some HTML::Parser optimization tweaks.
+
+ o  New module named HTML::Filter
+
+ o  Updated HTTP::Headers to the latest HTTP spec.  Added knowledge
+    about the "Trailer", "Expect", "TE", "Accept-Range" headers.
+    "Public" header is gone.
+
+ o  Added some more header convenience methods: if_unmodified_since,
+    content_language, and proxy_authorization methods.
+
+ o  HTTP::{Request,Response}->clone can handle subclasses now.
+
+ o  HTTP::Request->url() can now undefine the URL.
+
+ o  HTTP::{Request,Response}->as_string format looks more like
+    the HTTP protocol formatting now.  Dashed lines above and
+    below is gone.
+
+ o  Documented HTTP::Response->status_line method
+
+ o  Compressed HTML::Response->error_as_HTML output
+
+ o  HTTP::Status updated to latest HTTP spec.  Added 
+    RC_REQUEST_RANGE_NOT_SATISFIABLE (416)
+
+
+
+Tue Dec  2 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.17
+
+ o  All authentication handling moved out of LWP::UserAgent and into
+    LWP::Authen::Basic and LWP::Authen::Digest.  We now also support
+    PROXY_AUTHENTICATION_REQUIRED responses.
+
+ o  HTML::Formatter will always add a blank line for <br>.
+
+ o  Avoid use of uninitialized value in HTTP::Daemon.
+
+ o  HTTP::Date allows seconds when recognizing 'ls -l' dates.  This
+    allows us to parse syslog time stamps.
+
+ o  HTTP::Request::Common::POST allows a hash reference as second
+    argument (in addition to an array reference).
+
+ o  LWP::Simple will initialize the $ua if it is exported.
+
+ o  Various documentation updates.
+
+
+
+Fri Nov 21 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.16
+
+ o  LWP::Simple::head() would die in array context because str2time
+    was not imported any more.
+
+ o  HTTP::Daemon->accept now takes an optional package argument like
+    IO::Socket->accept does.
+
+ o  Made HTTP::Request and HTTP::Response subclassable.
+
+ o  Added Proxy-Authorization example to lwpcook.
+
+
+
+Thu Nov  6 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.15
+
+ o  New module URI::Heuristic
+
+ o  The lwp-request script now use URI::Heuristic for it's URL arguments.
+    It means that 'lwp-request perl' will not get a file called "./perl"
+    but will fetch the page "http://www.perl.com" or something similar.
+    If you want to get the file you have to prefix it with "./".  Full
+    URLs are never affected by this.
+
+ o  LWP::Simple::get() will bypass LWP for simple HTTP requests.  This
+    should make it somewhat faster.
+
+ o  LWP::RobotUA has a new method called $ua->use_sleep() that
+    controls how niceness towards the servers are enforced.
+    Previously $ua->use_alarm() used to control this, but this did
+    not work well on Win32 systems.
+
+ o  URI::URL::rel() will handle URLs to a fragment within the same
+    document better. Initial patch from Nicolai Langfeldt
+    <janl@math.uio.no>.
+
+ o  HTML::Element don't consider </th>, </tr> and </td> optional any
+    more.  I wonder how Netscape managed to not implement this
+    correctly all this time.
+
+ o  Added lots of modern tags to HTML::AsSubs.
+
+ o  HTTP::Request::Common will read uploaded files in binmode().
+    This should be better for Win32 systems.  Contributed by
+    <Steve_Kilbane@cegelecproj.co.uk>.
+
+
+
+Sun Oct 12 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.14
+
+ o  HTML::Formatter patches from Andreas Gustafsson <gson@araneus.fi>.
+    The formatters handling of whitespace is much better now.  Thanks!
+
+ o  HTML::FormatText: can specify margins in the constructor.
+
+ o  URI::URL: the base will be absolutized internally now.
+
+ o  URI::URL will take advantage of void context provided by perl5.004.
+    This means that using $u->path and $u->query should be safer now.
+
+ o  URI::URL->print_on defaults to STDERR now (used to be STDOUT).
+
+ o  URI::URL: removed version 3 compatibility stuff ($COMPAT_VER_3).
+
+ o  $ua->mirror should work better on dosish systems (can not
+    rename when target file exists).
+
+ o  Typo in lwp-download prevented it from compiling.
+
+ o  Some minor documentations typos corrected.
+
+
+
+Sat Sep 20 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.13
+
+ o  Brand new module called HTTP::Cookies.  It stores cookies
+    (Set-Cookie and Set-Cookie2 headers) from responses and can
+    create appropriate Cookie headers for requests.  It can also
+    share cookie files with Netscape.
+
+ o  LWP::UserAgent now support the cookie_jar() attribute.  When
+    set to an HTTP::Cookies object, it will automatically manage
+    the cookies sent to the servers.  Off by default.
+
+ o  New header utility functions in HTTP::Headers::Util.
+
+ o  Win32 and OS/2 patches for the lwp-xxx utilities.  Turn on
+    binary mode by default (option to turn it off), avoid modifying $0,
+    and don't be confused about suffixes in the script names.
+    Contributed by Ben Coleman <bcoleman@mindspring.com>
+
+ o  OpenVMS patch for Font:AFM by Brad Hughes <brad@tmc.naecker.com>
+
+
+
+Fri Sep  5 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.12
+
+ o  decode_entities() would sometimes introduce ";" after
+    things that looked like they were entities.
+
+ o  HTML::LinkExtor knows about <applet code="...">
+
+ o  Patch from Gary Shea <shea@gtsdesign.com> that makes the
+    tests work even if perl is not called "perl"
+
+ o  HTTP::Date handles 12:00PM correctly now. Patch from
+    William York <william@mathworks.com>
+
+ o  HTTP::Request::Common don't quote the boundary string for
+    multipart/form-data messages any more.
+
+ o  Font::AFM works for encodings where .notdef is defined to
+    have some size.  Documentation and efficiency update.
+
+
+
+Wed Aug  6 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.11
+
+ o  Perl version 5.004 is now required for libwww-perl.
+
+ o  Win32 patches from Gurusamy Sarathy <gsar@engin.umich.edu>.
+    Now passes all tests on that platform.
+
+ o  HTTPS support contributed by Josh Kronengold <mneme@mcny.com>
+
+ o  Support hex entities &#xFF;  HTML::Entities::(en|de)code only
+    modify their argument in void context.
+
+ o  Fixed formatter bug with <font> tags which did not specify size.
+
+ o  Better HTML::HeadParser documentation
+
+ o  Fixed HTML::LinkExtor documentation typo spotted by Martijn.
+
+ o  HTTP::Request::Common now use CRLF for multipart/form-data
+
+
+
+Fri Jun 20 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.10
+
+ o  Make '+' a reserved URL character.  Decode unescaped '+' as
+    space in $url->query_form().
+
+ o  Use $Config{d_alarm} to determine default for $ua->use_alarm()
+
+
+
+Tue Jun 10 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.09
+
+ o  Removed the MIME modules from the distribution.  They are distributed
+    separately now.
+
+ o  Added a new module called HTTP::Request::Common
+
+ o  Improved HTTP::Status documentation.  It is now also possible
+    to import the is_client_error/is_server_error functions.
+
+ o  LWP::MediaTypes::guess_media_type() can now take an optional
+    HTTP::Header parameter.
+
+ o  LWP::Protocol ensures that scheme is legal as module name.
+
+ o  LWP::Protocol::http is not as strict when trying to verify the
+    method name.  It now also accepts responses without a message
+    on the status line.
+
+ o  WWW::RobotRules::AnyDBM_File: Some DBMs fail to allow multiple
+    opens of the same file.  Patch from Mark James <jamesm@skate.org>
+
+ o  Created Bundle::LWP
+
+
+
+Sat Apr  5 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.08
+
+ o  Made libwww-perl warning compatible with upcoming perl5.004beta2
+    (aka 5.003_98)
+
+ o  encode_base64() did not work properly if pos() of the string to
+    encode was different from 0.
+
+ o  HTML::Parser was confused about "</" when it did not start an end tag.
+
+ o  HTML::FormatPS will provide ISOLatin1Encoding in its output.
+
+ o  Calling HTML::LinkExtor->links will clear out old links.
+
+ o  url()->rel($base) would ignore the $base argument.
+
+ o  Don't croak() when setting url()->path().
+
+
+
+Tue Feb 11 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.07
+
+ o  Can limit the size of the response content with $ua->max_size()
+
+ o  Added time2iso() functions to HTTP::Date.
+
+ o  Made LWP::Protocol::http more portable to the MacPerl. /./ match
+    different things on MacPerl.
+
+
+
+Mon Jan 27 1997   Gisle Aas <aas@sn.no>
+
+ o  Release 5.06
+
+ o  URI::URL is now compatible with perl5.004 overloading.
+
+ o  HTML::HeadParser makes X-Meta-Name headers for <meta> elements
+    that does not specify an 'http-equiv' attribute.
+
+ o  URI::URL::ftp does not die if Net::Domain is not installed and
+    you ask for an anonymous username or password.
+
+ o  WWW::RobotRules:  The robots.txt parser did not ignore comment lines
+    as it should.
+
+ o  LWP::Protocol::http is more forgiving towards servers that return
+    bad responses.
+
+ o  Allow "?" before search string in gopher URLs.
+
+ o  LWP::Protocol::file did not escape funny filenames when generating
+    HTML directory listings.
+
+ o  LWP::Protocol::ftp now gets the Content-Encoding correct. 'CODE'
+    content in PUT requests also work now.
+
+ o  Relative locations in redirects did not work with URI::URL::strict.
+
+ o  OS/2 portability patches from Ilya Zakharevich
+
+ o  LWP::Authen::* patch from Doug MacEachern
+
+ o  Support experimental data:- URLs
+
+ o  Some tests (those using HTTP::Daemon) now die more gracefully if
+    IO::* modules is not installed.
+
+
+
+Wed Dec  4 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.05
+
+ o  LWP::UserAgent::simple_request: local($SIG{__DIE__}) protects us
+    against user defined die handlers.
+ 	
+ o  Use Net::Domain (instead of Sys::Hostname) to determine FQDN.  It
+    is used by URI::URL when it determines anonymous ftp login address.
+	
+ o  lwp-download: New program in the bin directory
+
+ o  HTML::Parser: Allow '_' in attribute names.  This makes it possible
+    to parse Netscape's bookmarks.html file.
+
+ o  HTTP::Daemon: Fixed chunked transfer encoding and multipart content
+    in get_request().  Support HTTP/0.9 clients.
+
+ o  Don't clobber regex variables when HTTP::Message delegates methods
+    to the header.
+
+ o  Base64::decode_base64 now checks that the length input string to
+    decode is a multiple of 4.
+
+ o  t/robot/rules-dbm.t clean up better and will use AnyDBM for dumping
+
+ o  File::CounterFile: $/ strikes again by Andreas König
+
+ o  File::Listing updates from William York <william@mathworks.com>. We
+    can now parse the MS-Windows ftp server listings.
+
+ o  HTTP::Date now supports the MS-Windows 'dir' date format.  Patch by
+    William York.
+
+ o  LWP::MediaTypes::media_suffix will return first type in scalar context.
+
+
+
+Tue Oct 22 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.04
+
+ o  Added HTTP::Daemon.  This is a HTTP/1.1 server class.  This means
+    that libwww-perl no longer is a client library only.  The HTTP::Daemon
+    is also used in the new test suite.
+
+ o  HTTP::Message support the protocol() method.  Used by HTTP::Daemon.
+
+ o  HTTP::Response can be constructed with a header and content as
+    argument.
+
+ o  Typo corrections in the documentation.
+
+ o  File::Listing::parse_dir accepts "GMT" as timezone now.
+
+ o  HTML::Parser will call the start() method with two new parameters;
+    $attrseq, $origtext.
+
+ o  Integrated HTML::FormatPS patches from
+    Jim Stern <jstern@world.northgrum.com>
+
+ o  Class modules don't inherit from AutoLoader any more.  They just
+    import the AUTOLOAD method.
+
+ o  LWP::Protocol will untaints scheme before loading protocol module.
+
+ o  Digest does not send "opaque" if it was not present in the request.
+    The "Extension" header is not returned any more.
+
+ o  New method: $url->crack that will return a list of the various
+    elements in a URI::URL.
+
+ o  WWW::RobotRules did not use the agent() method when determining
+    who we are.  This affected WWW::RobotRules::AnyDBM_File parsing
+    for robots.txt.  Visit count did not increment for
+    WWW::RobotRules::InCore.
+
+
+
+Tue Oct  1 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.03
+
+ o  Hakan Ardo's persistent robot rules is now part of the standard
+    distribution.  This is still experimental and might change in the
+    future.  It includes the new WWW::RobotRules::AnyDBM_File class
+    and updates to LWP::RobotUA.
+
+ o  HTML::Parser now supports buggy Netscape comment parsing.  Enable
+    it by calling $p->netscape_buggy_comment(1).  The previous version
+    of the parser could also (under very unlucky and unlikely
+    circumstances) call the $self->comment() method several times for
+    the same comment text.
+
+ o  HTML::Parser: Use new $p->eof to signal end of document instead of
+    $p->parse(undef).
+
+ o  HTML::Element::starttag() is now smarter about which quotes it
+    use around attribute values.
+
+ o  New HTTP::Response methods: current_age(), freshness_lifetime(),
+    is_fresh(), fresh_until().
+
+ o  HTTP::Message:  New method ($mess->content_ref) which will return
+    a reference to the current content.
+
+ o  URI::URL:  New method ($url->rel) which does the opposite of abs().
+    Example: url("http://host/a/b/c", "http://host/c/d/e")->rel would
+    return url("../../a/b/c", "http://host/c/d/e").  This was
+    suggested by Nicolai Langfeldt <janl@ifi.uio.no>
+
+ o  URI::URL:  $url->query_form can now take array references as value
+    specification.  For instance: $url->query_form(foo => ['bar', 'baz']
+
+ o  Avoid '"my" variable $var masks earlier declaration in same scope'
+    warnings in perl5.003_05.
+
+
+
+Wed Sep 11 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.02
+
+ o  lwp-rget:  Initialize proxy settings from envirionment
+
+ o  HTML::Entities::encode_entities: Don't encode $ and %
+
+ o  HTML::LinkExtor::links: Now works when no links were found.
+
+ o  HTTP::Headers::as_string: Allow \n in header value
+
+
+
+Tue Aug  1 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.01.
+
+ o  Updated ftp protocol module to be compatible with Net::FTP 
+    version 2.00 (the version found in libnet-1.00)
+
+ o  New HTML parser module called HTML::LinkExtor
+
+ o  Various documentation typo corrections.  Most of these contributed
+    by Bob Dalgleish.
+
+ o  HTML::HeadParser updates 'Content-Base' instead of 'Base'.  It also
+    updates the 'Link' header based on <link ...>
+
+ o  HTTP::Headers and HTTP::Status updated according to
+    draft-ietf-http-v11-spec-06
+
+ o  HTTP::Headers can now use "_" as alternative to "-" in field names.
+
+ o  HTTP::Response::base now looks for 'Content-Base',
+    'Content-Location' and 'Base' headers.
+
+ o  Avoid warning in LWP::MediaTypes if $ENV{HOME} is not defined.
+
+ o  The new $ua->parse_head() method can be used to turn off
+    automatic initialization of response headers from the <HEAD>
+    section of HTML documents.
+
+ o  Added eq() method for URI::URL objects
+
+ o  The HTML::Formatter recovers even if a handle method is not defined
+    for all tags found during traversal
+
+
+
+Sun May 26 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5.00.
+
+ o  LWP::Simple::head() now return something useful in scalar context.
+
+ o  Rewritten the HTML::Parse stuff.  Introduced the HTML::Parser class
+    that will tokenize a HTML document.  The rest of the old
+    HTML::Parse functionality has moved to HTML::TreeBuilder class.
+    Note, that the HTML stuff is still alpha.
+
+ o  Implemented HTML::HeadParser.  This is a lightweight parser for
+    the <HEAD> section of a HTML document.
+
+ o  HTML::Element had problem with presenting things like <foo
+    bar="bar">.
+
+ o  HTML::Entities: Included additional ISO-8859/1 entities listed in
+    RFC1866.
+
+ o  HTML::AsSubs exported 'header' instead of 'head'
+
+ o  We know about a few more of the HTML 3.2 element.
+
+ o  HTTP::Date had problems with years before 1970, because Time::Local
+    went into an infinite loop.  Check for this.
+
+ o  Added $header->title method.
+
+ o  Made $header->authorization_basic return "uname:passwd" in scalar
+    context
+
+ o  LWP::Protocol::collect() will let the HTML::HeadParser look at the
+    document content as it arrives.  This will initialize headers from
+    elements like <base href="...">, <title>...</title> and <meta
+    http-equiv="..." ...>.
+
+ o  Simplified $response->base implementation, because we don't have
+    to look into the content any more.
+
+ o  Added -quiet option to lwp-rget
+
+ o  Typo fixes and some documentation additions.
+
+
+
+Thu May  9 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b13
+
+ o  Made URI::URL::strict(0) the default.  I got tired of all this
+    eval { } stuff just to recover.  The URI::URL::strict'ness also
+    determine if calling some standard method that happens to be
+    illegal for some protocol scheme will croak or just be ignored.
+
+ o  Ensure correct $INPUT_RECORD_SEPARATOR and $OUTPUT_RECORD_SEPARATOR
+    at places where we <> or print.
+
+ o  Always localize $_ before any 'while(<FILE>) {}'-loops
+
+ o  Implemented $protocol->collect_once() and simplified several
+    of the protocol implementations by using it.
+
+ o  The HTML parser used to get it wrong if you were unlucky about the
+    breakup of the text. An example of broken behaviour was this:
+
+        $html = parse_html "<!-- A comment -";
+        $html = parse_html "-> and some text.";
+
+ o  The HTML parser does not ignore whitespace-only text any more.
+
+ o  HTML::Parse warnings are now optional and turned off by default.
+
+ o  New start for $html->as_HTML().
+
+ o  Fixed some typos
+
+
+
+Wed Apr 24 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b12
+
+ o  New utility program called 'lwp-rget'.
+
+ o  $response->base was broken for HTML documents
+
+ o  New fancy LWP::Debug import() method.  Can now turn on debugging with
+    "use LWP::Debug '+';"
+
+ o  Trap exceptions (die) from the response callback routine
+
+ o  The RobotUA now requires an e-mail address of the person reponsible
+    for the robot.
+
+ o  New $ua->from() method.
+
+ o  Support for gopher Index-Search (gopher type '7' requests).
+    Contributed by Harry Bochner <bochner@das.harvard.edu>
+
+ o  Cleaned up white-space usage in the source.
+
+
+
+Wed Apr  3 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b11
+
+ o  Implemented a NNTP protocol module.  The library can now fetch and
+    post news articles.
+
+ o  More documentation
+
+ o  Don't look at the URI header for redirects
+
+ o  New $res->base() method for HTTP::Responses
+
+ o  Graham Barr's patch to File::Listing to make it more OO internally
+
+ o  content_type() return value is canonicalized
+
+ o  $ua->request() does not die on bad URLs any more
+
+ o  LWP::MediaTypes merge all media.types files that if finds
+
+ o  FTP request with content to file or callback did not work
+
+ o  The value of HTTP Host: header is now $url->netloc;
+
+ o  The URI::URL constructor now accept URLs wrapped up in "<>"
+
+ o  $url->abs() now has a second optional argument that makes it accept
+    that relative URLs can have scheme, i.e. treat "http:img.gif" as a
+    relative URL.
+
+ o  Added prototypes to the HTTP::Status::is_xxx() functions
+
+ o  Added prototypes to the MIME:: encoding/decoding functions
+
+ o  Always return scheme for mailto and news URLs (as_string)
+
+ o  RobotRules patches from Henry A Rowley.
+
+ o  More tests
+
+ o  <SMALL> and <BIG> again
+
+
+
+Thu Mar 14 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b10
+
+ o  GET ftp://host/doc was never successful for normal files.
+
+ o  LWP::Socket: read_until() did not notice EOF condidtion.  I.e. if
+    a HTTP server closed the connection before any output was generated,
+    the we continued to read 0 bytes in a busy loop until the alarm()
+    killed us.
+
+ o  Added support for Digest Access Authentication.  Contributed by
+    Doug MacEachern <dougm@osf.org>.
+
+ o  Makefile.PL: check for MD5 library
+
+ o  No longer print message content in HTTP::Response::error_as_HTML()
+
+ o  Access to "file:/path" gave warning when the envirionment variable
+    no_proxy was set.
+
+ o  The http-post test sends a Content-Type header.  Some servers hang
+    if this header is missing.
+
+ o  HTML::Parse:
+     -   allow <SMALL> and <BIG> tags
+     -   allow empty attribute values
+
+
+
+Tue Mar  5 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b9
+
+ o  Started to write on the libwww-perl cookbook (lwpcook.pod)
+
+ o  The URI::URL module now exports the function url().  This is an
+    alternative (easy to use) constructor function.
+
+ o  Expanding relative file URLs starting with "#" did not work.
+
+ o  Fixed autoloaded DESTROY problem by adding empty DESTROY routine
+    to URI::URL.
+
+ o  Does not try generate password for ftp-URLs unless the username is
+    "anonymous" or "ftp"
+
+ o  The LWP::Simple user agent proxy settings are intialized from
+    the proxy environment variables. 
+
+ o  LWP::Protocol::ftp: Use the Net::FTP library to access ftp servers.
+    Convert directories to HTML on request (Accept: text/html).
+
+ o  New module HTTP::Negotiate
+
+ o  New module File::Listing
+
+ o  HTTP::Date::str2time can parse a few more formats, like the 'ls -l'
+    format and ISO 8601.  The function now also takes an optional second
+    parameter which specify a default time zone.
+
+ o  Added prototypes to the HTTP::Date functions.
+
+ o  The library adds a timestamp to responses ("Client-Date")
+
+ o  HTTP::Status:  Updated to proposed HTTP/1.1
+
+ o  HTTP::Headers: Updated to proposed HTTP/1.1
+
+ o  LWP::Protocol::http:  Updated to HTTP/1.1 methods
+
+ o  Took out stringify overloading in HTML::Element.
+
+
+
+Mon Feb 26 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b8
+
+ o  Renamed functions using thisStyleOfNames to this_style_of_names.
+    Provided a script called 'update_from_5b7'
+
+ o  Renamed the 'request' and 'mirror' scripts to 'lwp-request' and
+    'lwp-mirror'.  The GET, POST, HEAD aliases for 'lwp-request' are
+    the same.
+
+ o  Implemented LWP::RobotUA
+
+ o  Class name for RobotRules did not match the file name
+
+ o  URI::URL
+      - perl5.002gamma is required (because use vars).
+      - The leading slash in now part of the path if it is present.
+      - More documentation
+      - Use AutoLoader to speed things up.
+      - New class URI::URL::_login and made telnet, rlogin, tn3270
+        subclasses from this one.
+      - URI::URL::wais is better supported.
+      - URI::URL::news is better supported.
+      - New URI::URL::mailto methods: user/host
+
+ o  HTTP::Date::time2str now works correctly with '0' as argument
+
+ o  HTTP::Message delegates unknown methods to the headers.
+
+ o  HTTP::Request::uri is an alias for HTTP::Request::url.  Can set
+    the URL to undef.
+
+ o  Added convenience methods to HTTP::Headers for easy access to
+    frequently used headers.
+
+ o  Simplified LWP::Debug
+
+ o  Use standard AutoLoader for LWP::IO functions.
+
+ o  Played with the profiler (Devel::DProf) and made some things
+    go quicker.
+
+ o  Included the File::CounterFile module.  Excluded Mail::Cap module
+    as it is also part of the MailTools package.
+
+
+
+Mon Feb  5 1996   Gisle Aas <aas@sn.no>
+
+ o  Release 5b7
+
+ o  Perl5.002 is required now
+
+ o  Rewrite of the URI::URL module (version 4.00)
+      - escaping/unsafe stuff redone (and removed)
+      - URI::URL::_generic moved out of URL.pm
+      - netloc, path, params, query is now stored internally in escaped form
+      - new methods for URI::URL::_generic are:
+           epath
+           eparams
+           equery
+	   path_components
+           absolute_path
+      - new methods for URI::URL::http are:
+           keywords
+           query_form
+      - new methods for URI::URL::file are:
+           newlocal
+           local_path
+           unix_path
+           dos_path
+           mac_path
+           vms_path
+
+ o  URI::Escape now semi-compile regular expressions (by evaling an
+    anonymous sub).  Same techinque is also used in HTML::Entities.
+
+ o  HTTP::Date parser rewritten using regular expressions.
+
+ o  HTTP::Headers->as_string() now croaks if any field values
+    contain newline.
+
+ o  HTTP::Status constants use empty prototype.
+
+ o  Font metrics moved to a new subdirectory (lib/Font/Metrics)
+
+ o  Don't use the VERSION script any more (even if it was very clever)
+
+ o  HTML::Entities will now export the names decode_entities() and
+    encode_entities().
+
+ o  Andreas Koenig's black patch to HTML::Element.
+
+ o  The HTML::Formatter now knows about <menu> and <dir> tags
+
+ o  The construct "defined ref($arg)" did not work on perl5.002
+    becase ref now always return something defined.
+
+ o  LWP::UserAgent sends by default an 'User-Agent' header.
+
+ o  LWP::Simple sends 'User-Agent' header to servers.
+
+ o  Updated the LWP::Socket module to use the new Socket.pm interface.
+
+ o  LWP::Protocol::http sends the new HTTP/1.1 'Host' header.
+
+ o  LWP::Protocol::file use $url->local_path to get a file to open.
+    It also inserts a <BASE> tag in directories instead of a redirect.
+
+ o  MIME::Base64 routines can be called as MIME::Base64::encode() and
+    MIME::Base64::decode().  Same kind of thing for QuotedPrint.
+
+
+
+Mon Nov  6 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Release 5b6
+
+ o  Installation should work better for those that still runs
+    perl4 as 'perl'.  The mirror script is not installed by
+    default.
+
+ o  LWP::Socket::_getaddress() Numerical IP adresses did not work.
+
+ o  LWP::Socket::pushback() did not work.  This also avoids the bad
+    pp_select() core dump from perl.
+
+ o  LWP::IO now also selects on socket exceptions.
+
+ o  HTML::Parse:  Ignore <!DOCTYPE ...> regardless for case.  Some
+    bad insertElement() calls made infinite loops.
+
+ o  The uri.t test works better for places where /tmp is a sym-link.
+
+
+
+Sat Sep 16 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Release 5b5
+
+ o  newlocal URI::URL does not put "//localhost" into the URLs any
+    longer.
+
+ o  The request program: New -o option to reformat the HTML code
+    New -C option to provide credentials on the command line.
+    The -b option never worked.
+
+ o  LWP::Protocol::file now returns redirect for access to directories
+    where the trailing slash is missing.
+
+
+
+Thu Sep 14 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Speedups and bug fixes in the HTML parser.  The parser now
+    understands some more depreciated tags (like <xmp> and <listing>).
+
+ o  HTML::Elements are now stingified using perl overloading.
+    The interface to the traverse() callback has changed.
+
+ o  Implemented HTML formatters for plain text and Postscript.
+
+ o  Added lib/Font stuff to support the Postscript formatter.
+
+ o  Inspired by Tim Bunce, I implemented the HTML::AsSubs module.
+    Don't know if it is really useful.
+
+ o  The local/get test does not use /etc/passwd any more.
+
+
+
+Thu Sep  7 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Changed package name to libwww-perl-5xx
+
+ o  Made LWP::Protocol::ftp actually transfer data
+
+ o  Implemented methods for LWP::Socket to act as a server:
+    bind(), listen(), accept(), getsockname(), host(), port()
+
+
+
+Wed Sep  6 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Release 0.04
+
+ o  Implemented modules to parse HTML.
+
+
+
+Mon Sep  4 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Implemented Mail::Cap which will become part of the MailTools
+    package.
+
+ o  Moved Base64 to MIME::Base64.  Reimplemented MIME::Base64 by using
+    [un]pack("u",...)   Implemented LWP::MIME::QuotedPrint for
+    completness sake.  Routine names has changed as suggested by Tim
+    Bunce.
+
+ o  LWP::MediaType reads default types from media.types file.
+    guessMediaType() now also returns encodings.  New function mediaSuffix()
+
+ o  Pass $url to $ua->getBasicCredentials().  This also fixes security
+    hole with the old implementation of getBasicCredentials().
+
+ o  LWP::Protocol::file now sets Content-Encoding headers
+
+ o  Allow request content to be provided by a callback routine.
+
+ o  Fix bug that prevented response callback to work.  The first parameter
+    (data) is no longer a reference, because $_[0] is already a reference.
+    Don't callback unless successful response.  Callbacks during redirects
+    was confusing.
+
+ o  URI::URL.  Remove port from netloc if it is the default port.
+    Don't use anything, just require.
+
+ o  HTTP::Message->addContent() does not need a reference parameter.
+
+ o  LWP::Socket->open() has been renamed top LWP::Socket->connect()
+    LWP::Socket->close has gone.  Implemented new method LWP::Socket->read()
+    that returns as fast as it has some data to offer.  Implemented
+    LWP::Socket->pushback().
+
+ o  Does not die in LWP::UserAgent->request()
+
+ o  LWP::Socket now use LWP::IO for low level IO
+
+ o  Implemented LWP::TkIO as a replacement module for LWP::IO when using Tk.
+
+
+
+Thu Aug 17 1995   Gisle Aas <aas@oslonett.no>
+
+ o  $ua->redirectOK() for checking redirects
+
+ o  reorganized tests in the "t" directory.
+
+
+
+Fri Aug 11 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Release 0.03
+
+ o  Included RobotRules.pm from Martijn Koster
+
+
+
+Thu Aug 10 1995   Gisle Aas <aas@oslonett.no>
+
+ o  New module URI::Escape (URI::URL use this module for default
+    escaping) that provide the uri_escape() and uri_unescape()
+    functions.
+
+ o  Setting $url->scheme now changes the class of the object.
+
+ o  Made $httpurl->user() and $httpurl->password() illegal.
+    Likewise for other URL schemes.
+
+
+
+
+Wed Aug  9 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Reorganisation as discussed on <libwww-perl@ics.uci.edu>
+       LWP::Date       --> HTTP::Date
+       LWP::MIMEheader --> HTTP::Headers
+       LWP::StatusCode --> HTTP::Status
+       LWP::Message    --> HTTP::Message
+       LWP::Request    --> HTTP::Request
+       LWP::Response   --> HTTP::Response
+       LWP::MIMEtypes  --> LWP::MediaTypes
+
+ o  HTTP::Date parses ctime format with missing timezone as suggested
+    by Roy Fielding <fielding@beach.w3.org>
+
+ o  HTTP::Status and LWP::MediaTypes exports their functions by default.
+
+ o  Splitted up the URI::URL module.  Schemes are implemented by separate
+    files that are autoloaded when used.  Self test moved to "t/uri.t".
+
+
+       
+Mon Aug  7 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Applied patch from Marc Hedlund <hedlund@best.com>
+       - Update the @header_order according to the August 3rd draft.
+       - Added Response Header fields: 'Location', 'Public', 'Retry-After',
+         'Server', and 'WWW-Authenticate'.
+       - Moved 'unknown header' handling from &scan to &header. The old
+         implementation was forcing all unknown header-words to begin with
+	 an uppercase (as it should be), but disallowed other uppercase
+	 letters.
+       - updates the status code messages under the August
+         3rd HTTP/1.0 draft.  '203' became 'Non-Authoritative Information',
+	 '303' became 'See Other', and a new code,
+	 '411 Authorization Refused', was added.
+
+ o  Can remove multiple headers with single removeHeader() call in MIMEheader.
+
+ o  Can assign multiple field/value pairs in header() method of MIMEheader.
+
+ o  A field with multiple values is printed as separate values in
+    MIMEheader::as_string().
+
+ o  LWP::Response contain new attributes: previous() and request().  These
+    attributes are updated by the UserAgent.
+
+ o  Appended \n to some die statements in Socket so that line numbers are
+    suppressed in error messages.
+
+ o  Made UserAgent::clone work for reference members
+
+ o  Check for redirect loops and multiple authorization failures by
+    examination of the response chain.
+
+ o  Use "\015\012" instead of "\r\n" in protocol modules.  Some systems
+    define \r and \n differently.
+
+ o  request program can now handle documents that needs autorization by
+    prompting the user for username/password. Added new -S option to print
+    request/response chain.
+
+
+  
+Tue Jul 25 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Release 0.02
+
+ o  Included URI::URL in the release
+
+
+
+Mon Jul 24 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Incorporated Makemake.PL and VERSION from Andreas Koenig <koenig@mind.de>
+    As a result of this the following things have changed:
+       - programs in "bin" are extracted from .PL-files
+       - reintroduced "lib"
+       - "test" has been renamed as "t"
+       - test programs in "t" has been made Test::Harness compatible
+       - we now have a MANIFEST file
+       - no more need fro update_version, make-dist, lwp-install
+
+ o  Renamed bin/get to bin/request.  Links to it are now all upper case.
+
+ o  Proxy support in bin/request (provided by Martijn Koster)
+
+ o  UserAgent can now load proxy settings from environment
+
+ o  LWP::Protocol::ftp is under way but not really finished
+
+
+
+Tue Jul 18 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Implemented LWP::Protocol::gopher
+
+ o  Implemented LWP::Protocol::mailto
+
+ o  Fixed proxy typo
+
+
+
+Mon Jul 17 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Made release 0.01
+
+
+
+Mon Jul 17 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Don't loose first line of HTTP/0.9 requests
+
+ o  LWP::Socket use syswrite() for writing
+
+ o  Added RC_* documentation to LWP::StatusCode
+
+ o  LWP::Date now use hash to look up month numbers
+
+ o  Added -f option to "get"
+
+ o  Untabify
+
+ o  Added a "TODO" list
+
+ o  Fixed various typos
+
+
+
+Fri Jul 14 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Reorganized directories.  Moved LWP.pm up.  Moved file.pm and http.pm
+    into the LWP::Protocol direcotory.  Moved LWP out of the lib directory
+    and removed lib.
+
+ o  Implemented the "get" and "mirror" scripts in the "bin" directory.
+
+ o  Implemented "install-lwp", "update_version" and "make-dist".  The library
+    version number is found in the VERSION file.
+
+ o  Always adds 1900 to the year in LWP::Date
+
+ o  In LWP::MIMEheader:  Implemented clone(), removeHeader() and scan() 
+    methods.  Reimplemented asString.  Removed asMIME().  Moved "Good
+    Practice" into this file, and reimplemented it.
+
+ o  Moved "header" and "content" into LWP::Message class.  This change made
+    LWP::Request and LWP::Response much simpler.  Made clone() method
+    actually work.
+
+ o  LWP::Protocol::implementor does not die if it cannot load package.
+
+ o  Moved UserAgent convenience methods into LWP::Simple.  Made LWP::Simple
+    export LWP::StatusCode symbols and functions.
+
+ o  Implemented $ua->isProtocolSupported($scheme) method.
+
+ o  Nicer directory listing in LWP::Protocol::file.pm
+
+ o  Rely on URI::URL 3.00 behaviour for $url->full_path
+
+ o  Library version number now in LWP.pm.  You should be able to say
+    "use LWP 1.0;" if you need at least this version.
+
+ o  Various cleanups and arranging layout as I like it.  Use fooBar-style
+    (as opposed to foo_bar stype) everywhere.  This means that as_string()
+    now is called asString().
+
+ o  Added more documentation.
+
+
+
+Wed Jun 14 1995   Gisle Aas <aas@oslonett.no>
+
+ o  Removed lot of rendundant & before function calls.
+
+ o  $this --> $self
+
+ o  &collector passes content as a reference, don't want to copy so much
+
+ o  parameterlist to collect callback has been rearranged
+
+ o  Response::addContent gets a reference to the data
+
+ o  Added some callback documentation to UserAgent.pm
+
+ o  Protocol::file now uses the collector
+
+ o  Introduced LWP::Simple
+
+
+
+Sun Jun 11 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Added basic authentication support
+
+ o  Added mirroring of single documents
+
+ o  Change Protocol construction from whacky URL.pm (constructor returns
+    subclass) to more normal C++'ish way.
+
+
+
+Wed June 7 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Minor cleanups from printed code inspection
+
+
+
+Wed May 24 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Added redirection resolution
+
+ o  Added optional autoloading of protocols
+
+
+
+Tue May 23 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Separated socket stuff into separate module
+
+ o  Added HTTP proxy support
+
+ o  Made alarm handling optional
+
+ o  Added a LWP::Message for completeness sake
+
+ o  Added LWP::MemberMixin to reduce code duplication
+
+ o  Cosmetic changes to LWP::Date
+
+ o  Renamed LWP::Error to LWP::StatusCode
+
+ o  Renamed LWP::MIME to LWP::MIMEtype
+
+ o  Changed the tests to cope with all this
+
+It's getting there...
+
+
+
+Mon May 22 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Changed the socket reading to use sysread.  This will have to go
+    into a module of its own.
+
+
+
+Thu 18 May 1995   Martijn Koster <m.koster@nexor.co.uk>
+
+ o  Mentioned on libwww-perl that I had changed the classes around lots.
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/INSTALL.w3mir
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/INSTALL.w3mir	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/INSTALL.w3mir	(revision 16561)
@@ -0,0 +1,48 @@
+
+- Get libwww-perl from CPAN 
+  (http://www.perl.com/CPAN/modules/by-module/WWW/).  W3mir does not work
+  with LWP 5.40 and later versions.  Please get an earlier version.
+  It also does not work with the new URI package.  Use the URI package
+  enclosed in the older LWP.
+
+- Unpack it:
+
+    gzip -dc libwww-perl-*.tar.gz | tar xvBf -
+
+- Install it.  W3mir does not require the whole libwww-perl package and
+  all the packages that goes with it to work so you may follow these
+  install instructions instead of the libwww-perl install instructions
+  to get an easier install which will work with w3mir.
+
+    cd libwww-perl-*
+
+  Are you going to install it in the standard perl place (you need to
+  be the system administrator to do this)? If so:
+
+    perl Makefile.PL
+
+  If not (if you are a user):
+
+    perl Makefile.PL PREFIX=/some/dir
+
+  /some/dir could be ~ (as in your home directory).
+
+  IF you are installing an old LWP just for w3mir you can install both
+  the old LWP, the Base64 module and w3mir in a special PREFIX
+  separate from your usual install PREFIX.  This ensures that w3mir will
+  access the older version of LWP and that other software will access
+  the newer version.  If you do this make sure you install all of LWP,
+  Base64 and w3mir with the same PREFIX.
+
+  This step might produce some complaints;  A standard perl
+  installation will typically get messages about missing these
+  modules: IO::Socket, NET::FTP and MD5.  None of these need be
+  installed to run w3mir and so any error messages about these may
+  safely be ignored.
+
+    make
+    make test      (robot/ua and local/http might fail, that's OK)
+    make install
+
+  Now libwww-perl should be installed and working.
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/MANIFEST
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/MANIFEST	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/MANIFEST	(revision 16561)
@@ -0,0 +1,106 @@
+ChangeLog			History of this package
+MANIFEST			This file
+Makefile.PL			Makefile generator
+README				Get you started with this package
+TODO				The To-Do list for the library
+bin/lwp-download.PL             Writes bin/lwp-download script
+bin/lwp-mirror.PL		Writes bin/lwp-mirror script
+bin/lwp-request.PL		Writes bin/lwp-request script
+bin/lwp-rget.PL			Writes bin/lwp-rget script
+lwpcook.pod			Libwww-perl examples
+lib/Bundle/LWP.pm               Defines prereq modules
+lib/File/Listing.pm		Parse directory listings
+lib/HTTP/Cookies.pm             Cookie storage and management
+lib/HTTP/Daemon.pm		A simple httpd
+lib/HTTP/Date.pm		Date conversion routines
+lib/HTTP/Headers.pm		Class encapsulating HTTP Message headers
+lib/HTTP/Headers/Auth.pm	Some methods that deal with authorization.
+lib/HTTP/Headers/ETag.pm	Some methods that deal with entity tags
+lib/HTTP/Headers/Util.pm        Some utility functions for header values.
+lib/HTTP/Message.pm		Class encapsulating HTTP messages
+lib/HTTP/Negotiate.pm		Evaluate HTTP content negotiation algoritm
+lib/HTTP/Request.pm		Class encapsulating HTTP Requests
+lib/HTTP/Request/Common.pm      Generate common requests
+lib/HTTP/Response.pm		Class encapsulating HTTP Responses
+lib/HTTP/Status.pm		HTTP Status code processing
+lib/LWP.pm			Includes what you need
+lib/LWP/Authen/Basic.pm		Basic authentication scheme
+lib/LWP/Authen/Digest.pm	Digest authentication scheme
+lib/LWP/Debug.pm		Debugging support
+lib/LWP/MediaTypes.pm		Library for guessing media types
+lib/LWP/MemberMixin.pm		Helps you access %$self
+lib/LWP/Protocol.pm		Virtual base class for LWP protocols
+lib/LWP/Protocol/data.pm        Access to data URLs
+lib/LWP/Protocol/file.pm	Access local files
+lib/LWP/Protocol/ftp.pm		Access with the FTP protocol
+lib/LWP/Protocol/gopher.pm	Access with the Gopher protocol
+lib/LWP/Protocol/http.pm	Access with HTTP protocol
+lib/LWP/Protocol/https.pm	Access with HTTP protocol over SSL
+lib/LWP/Protocol/mailto.pm	Allows you to POST mail using sendmail
+lib/LWP/Protocol/nntp.pm	Handles access to news: and nntp: URLs
+lib/LWP/RobotUA.pm		Easy creation of conforming robots
+lib/LWP/Simple.pm		Procedural LWP interface
+lib/LWP/UserAgent.pm		A WWW UserAgent class
+lib/LWP/media.types		Mapping from file extentions media types
+lib/URI/Escape.pm		General URI escaping functions
+lib/URI/Heuristic.pm            User friendly URLs
+lib/URI/URL.pm			Class encapsulating URLs
+lib/URI/URL/_generic.pm		Implements generic hierarchical RL (RFC 1808)
+lib/URI/URL/_login.pm		Base class for telnet, tn3270 and rlogin
+lib/URI/URL/data.pm             Implements data URLs
+lib/URI/URL/file.pm		Implements file URLs
+lib/URI/URL/finger.pm		Implements finger URLs
+lib/URI/URL/ftp.pm		Implements ftp URLs
+lib/URI/URL/gopher.pm		Implements gopher URLs
+lib/URI/URL/http.pm		Implements http URLs
+lib/URI/URL/https.pm		Implements https URLs
+lib/URI/URL/mailto.pm		Implements mailto URLs
+lib/URI/URL/news.pm		Implements news URLs
+lib/URI/URL/nntp.pm		Implements nntp URLs
+lib/URI/URL/prospero.pm		Implements prospero URLs
+lib/URI/URL/rlogin.pm		Implements rlogin URLs
+lib/URI/URL/telnet.pm		Implements telnet URLs
+lib/URI/URL/tn3270.pm		Implements tn3270 URLs
+lib/URI/URL/wais.pm		Implements wais URLs
+lib/URI/URL/webster.pm		Implements webster URLs
+lib/URI/URL/whois.pm		Implements whois URLs
+lib/WWW/RobotRules.pm		Parse robot.txt files
+lib/WWW/RobotRules/AnyDBM_File.pm	Persistent robot rules
+t/README			How to run and set up tests
+t/TEST				Run tests
+t/base/common-req.t		Test HTTP::Request::Common module
+t/base/cookies.t                Test HTTP::Cookies module
+t/base/date.t			Test HTTP::Date module
+t/base/headers.t		Test HTTP::Headers module
+t/base/headers-auth.t		Test HTTP::Headers::Auth module
+t/base/headers-etag.t		Test HTTP::Headers::ETag module
+t/base/headers-util.t		Test HTTP::Headers::Util module
+t/base/listing.t		Test File::Listing module
+t/base/mediatypes.t		Test LWP::MediaTypes module
+t/base/message.t		Test HTTP::Request/HTTP::Response
+t/base/negotiate.t		Test HTTP::Negotiation module
+t/base/response.t		Test additional HTTP::Response methods
+t/base/status.t			Test HTTP::Status module
+t/local/autoload.t		Test autoloading of LWP::Protocol modules
+t/local/get.t			Try to get a local file
+t/local/http.t			Test http to local server
+t/local/protosub.t              Test with other protocol module
+t/net/cgi-bin/moved
+t/net/cgi-bin/nph-slowdata
+t/net/cgi-bin/slowread
+t/net/cgi-bin/test
+t/net/cgi-bin/timeout
+t/net/config.pl.dist		Suggested configuration for net tests
+t/net/http-get.t
+t/net/http-post.t
+t/net/http-timeout.t
+t/net/mirror.t
+t/net/moved.t
+t/net/proxy.t
+t/robot/rules-dbm.t		Test WWW::RobotRules::AnyDBM_File
+t/robot/rules.t			Test WWW::RobotRules
+t/robot/ua.t			Test LWP::RobotUA
+t/uri/abs-config.t              Test URI::URL configuration variables
+t/uri/base.t			Test URI::URL module
+t/uri/rel-base.t		Test URI::URL module some more
+t/uri/heuristic.t               Test URI::Heuristic module
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/Makefile.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/Makefile.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/Makefile.PL	(revision 16561)
@@ -0,0 +1,184 @@
+# This -*- perl -*- script writes the Makefile for libwww-perl
+# $Id$
+
+require 5.004;
+use strict;
+
+#--- Configuration section ---
+
+my @programs_to_install = qw(lwp-request lwp-mirror lwp-rget lwp-download);
+my @request_aliases     = qw(GET HEAD POST);
+
+#--- End Configuration - You should not have to change anything below this line
+
+
+# Allow us to suppress all program installation with the -n (library only)
+# option.  This is for those that don't want to mess with the configuration
+# section of this file.
+use Getopt::Std;
+use vars qw($opt_n);
+unless (getopts("n")) {
+    die "Usage: $0 [-n]\n";
+}
+@programs_to_install = () if $opt_n;
+
+
+# Check for non-standard modules that are used by this library.
+$| = 1;
+my $missing_modules = 0;
+
+print "Checking for HTML::Parser..";
+eval {
+    require HTML::HeadParser;
+};
+if ($@) {
+    print " failed\n";
+    $missing_modules++;
+    print <<EOT;
+$@
+The HTML::Parser is needed to extract correct <BASE> information from
+HTML so that we can resolv relative links correctly.
+
+EOT
+    sleep(2);  # Don't hurry too much
+} else {
+    print " ok\n";
+}
+
+print "Checking for MIME::Base64..";
+eval {
+    require MIME::Base64;
+    #MIME::Base64->require_version('2.00');
+};
+if ($@) {
+    print " failed\n";
+    $missing_modules++;
+    print <<EOT;
+$@
+The Base64 encoding is used in authentication headers in HTTP.
+
+EOT
+    sleep(2);  # Don't hurry too much
+} else {
+    print " ok\n";
+}
+
+print "Checking for Net::FTP......";
+eval {
+    require Net::FTP;
+    Net::FTP->require_version('2.00');
+};
+if ($@) {
+    print " failed\n";
+    $missing_modules++;
+    print <<EOT;
+$@
+The libwww-perl library normally use the Net::FTP module when
+accessing ftp servers.  You would have to install this package or
+configure your application to use a proxy server for making ftp
+requests work.  Net::FTP is part of the 'libnet' distribution.
+
+EOT
+    sleep(2);  # Don't hurry too much
+} else {
+    print " ok\n";
+}
+
+print "Checking for MD5 ..........";
+eval {
+    require MD5;
+};
+if ($@) {
+    print " failed\n";
+    $missing_modules++;
+    print <<EOT;
+$@
+The MD5 library is needed if you want to be able use the experimental
+"Digest Access Authentication" scheme.  Since very few servers
+implement this authentication scheme, you should normally not worry
+too much about this.
+
+EOT
+} else {
+    print " ok\n";
+}
+
+print <<EOT if $missing_modules;
+The missing modules can be obtained from CPAN.  Visit
+<URL:http://www.perl.com/CPAN/> to find a CPAN site near you.
+
+EOT
+
+
+# Ok, now it is time to really generate the Makefile
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+   NAME          => 'libwww-perl',
+   VERSION_FROM  => 'lib/LWP.pm',
+   PL_FILES      => { map {("bin/$_.PL" => "bin/$_")} @programs_to_install },
+   EXE_FILES     => [ map {"bin/$_"} @programs_to_install ],
+   'clean'       => { FILES => '$(EXE_FILES)' },
+   'dist'        => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+);
+
+
+
+package MY;
+
+sub postamble
+{
+    my @m;
+    if (@request_aliases && grep($_ eq 'lwp-request', @programs_to_install)) {
+	push @m, "all ::\n";
+	if ($^O eq 'MSWin32') {
+	    push @m, "\t\$(FULLPERL) -e \"use Config; chdir q[\$(INST_EXE)]; "
+		    ."foreach (qw(@request_aliases)) { \" \\\n";
+	    push @m, <<'EOT';
+	-e "unlink \"$$_\"; " \
+	-e "system(\"copy lwp-request $$_\") && die; }"
+EOT
+	} else {
+	    push @m, "\t\$(FULLPERL) -e 'use Config; chdir q{\$(INST_EXE)}; "
+		    ."foreach (qw(@request_aliases)) {' \\\n";
+	    push @m, <<'EOT';
+	-e 'unlink "$$_";' \
+	-e 'system("$$Config{\"lns\"} lwp-request $$_") && die; }'
+EOT
+	}
+    }
+    join "", @m;
+}
+
+# What happens when we say 'make test'
+sub test
+{
+    q(
+TEST_VERBOSE=0
+
+test: all
+	$(FULLPERL) t/TEST $(TEST_VERBOSE)
+
+);
+}
+
+
+# Determine things that should *not* be installed
+sub libscan
+{
+    my($self, $path) = @_;
+    return '' if $path =~ m/.(pl|dtd|sgml)$/;
+    return '' if $path =~ m:\bCVS/:;
+    return '' if $path =~ m/~$/;
+    $path;
+}
+
+# Pass libwww-perl version number to pod2man
+sub manifypods
+{
+    my $self = shift;
+    my $ver = $self->{VERSION} || "";
+    local($_) = $self->SUPER::manifypods(@_);
+    s/pod2man\s*$/pod2man --release libwww-perl-$ver/m;
+    $_;
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/README
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/README	(revision 16561)
@@ -0,0 +1,124 @@
+                     L I B W W W - P E R L - 5
+                   -----------------------------
+
+Libwww-perl is a collection of Perl modules which provides a simple
+and consistent programming interface (API) to the World-Wide Web.  The
+main focus of the library is to provide classes and functions that
+allow you to write WWW clients, thus libwww-perl said to be a WWW
+client library. The library also contain modules that are of more
+general use and even classes that helps you implement simple HTTP
+servers.
+
+There are actually two versions of libwww-perl: one for Perl4, and one
+for Perl5. Both have a similar architecture, loosely based on the CERN
+Library of Common Code (nowadays known as 'w3c-libwww').
+
+The Perl4 version is still(?) maintained by Roy Fielding, and was the
+basis for tools such as MOMSpider. The perl4 version of libwww-perl
+and much more information about its libraries can be found at:
+http://www.ics.uci.edu/pub/websoft/libwww-perl/
+
+The Perl5 version (this package) is a complete rewrite for Perl5: the
+code is organized in Modules, provides an Object Oriented API, is
+updated for the latest HTTP and URL specification, and offers some
+extended functionality.
+
+Full HTTP/1.1 support is on its way.  Alpha releases of the LWPng
+package is currently available from CPAN.
+
+
+PREREQUISITES
+
+In order to install and use this package you will need Perl version
+5.004 or better.  Some modules within this package depend on other
+packages that are distributed separately from Perl.  We recommend that
+you have the following packages installed before you install
+libwww-perl:
+
+  MIME-Base64
+  HTML-Parser
+  libnet
+  MD5
+
+These packages should be available on CPAN.
+
+If you want to access sites using the https protocol, then you need to
+install the Crypt-SSLeay modules first.  (The Net::SSLeay module is not
+supported any more.)
+
+
+INSTALLATION
+
+You install libwww-perl, as you would install any perl module library,
+by running these commands:
+
+   perl Makefile.PL
+   make
+   make test
+   make install
+
+You can edit the configuration section of Makefile.PL to select which
+programs to install in addition to the library itself.  If you don't
+want to install any programs (only the library files) and don't want
+to mess with the Makefile.PL then pass the '-n' option to Makefile.PL:
+
+   perl Makefile.PL -n
+
+If you want to install a private copy of libwww-perl in your home
+directory, then you should try to produce the initial Makefile with
+something like this command:
+
+  perl Makefile.PL LIB=~/perl
+
+The Makefile.PL program will start out by checking your perl
+installation for a few packages that are recommended to be installed
+together with libwww-perl.
+
+Optional network tests are _not_ enabled automatically when you unpack
+this package.  Consult the t/README file for instructions on how to
+enable these tests.
+
+
+DOCUMENTATION
+
+See lib/LWP.pm for an overview of the library. See ChangeLog for
+recent changes.  POD style documentation is included in all modules
+and scripts.  These are normally converted to manual pages and
+installed as part of the "make install" process.  You should also be
+able to use the 'perldoc' utility to extract and read documentation
+from the module files directly.
+
+
+SUPPORT
+
+Question about how to use this library should be directed to the
+comp.lang.perl.modules USENET Newsgroup.  Bug reports and suggestions
+for improvements can be sendt to the <libwww-perl@ics.uci.edu> mailing
+list.  This mailing list is also the place for general discussions and
+development of the libwww-perl package.
+
+You can join the mailing list by sending a message to
+<libwww-perl-request@ics.uci.edu> with "Subscribe" as subject.
+            ^^^^^^^^
+
+AVAILABILITY
+
+The latest version of libwww-perl should always be available from:
+
+     http://www.linpro.no/lwp/
+
+The library is also available from the Comprehensive Perl Archive
+Network (CPAN).  Visit <URL:http://www.perl.com/CPAN/> to find a CPAN
+site near you.
+
+
+COPYRIGHT
+
+  © 1995-1998 Gisle Aas. All rights reserved.
+  © 1995 Martijn Koster. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+
+Enjoy!
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/TODO
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/TODO	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/TODO	(revision 16561)
@@ -0,0 +1,46 @@
+                                            $Date$
+
+This is a list over things that we plan to implement in libwww-perl-5.
+The order of items does not reflect priority.
+
+  o  HTTP/1.1 support is on its way.  This also includes support of
+     multiple parallel connections.  Take a look at LWPng (aka
+     libwww-perl-6).
+
+  o  Provide initial credentials so that we don't always have to get a
+     401 response first.  Both for Basic and Digest authentication.  This
+     is in fact implemented for LWPng.
+
+  o  HTTP::Request::Common::POST() should be able to create large
+     file upload requests by making a closure content.
+
+  o  Support PICS??
+
+  o  Cache previous ftp connections.  "Keep-Alive"?
+
+  o  Support MOVE, DELETE method for ftp.
+
+  o  Support If-Modified-Since for ftp.
+
+  o  Use Mail::Send to implement LWP::Protocol::mailto.  Today we just
+     invoke sendmail directly, which is not very portable.
+
+  o  The lwp-request -o processing should check for Content-Encoding as
+     well as Content-Type.
+
+  o  The lwp-request script should optionally consult Mail::Cap for
+     handling of media types.
+
+  o  The lwp-request script should use the callback interface so that it
+     prints data as it is received.
+
+  o  The lwp-mirror script should be able to work recursively.
+
+  o  Include more useful programs
+
+  o  More documentation
+
+  o  More tests
+
+  o  Fix bugs, if there are any...
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-download.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-download.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-download.PL	(revision 16561)
@@ -0,0 +1,248 @@
+use Config;
+use File::Basename qw(basename dirname);
+chdir(dirname($0));
+($file = basename($0)) =~ s/\.PL$//;
+$file =~ s/\.pl$//
+	if ($Config{'osname'} eq 'VMS' or
+	    $Config{'osname'} eq 'OS2');  # "case-forgiving"
+open OUT,">$file" or die "Can't create $file: $!";
+chmod(0755, $file);
+print "Extracting $file (with variable substitutions)\n";
+
+print OUT <<"!GROK!THIS!";
+$Config{'startperl'} -w
+
+!GROK!THIS!
+
+print OUT <<'!NO!SUBS!';
+# $Id$
+
+=head1 NAME
+
+lwp-download - fetch large files from the net
+
+=head1 SYNOPSIS
+
+ lwp-download [-a] <url> [<local file>]
+
+=head1 DESCRIPTION
+
+The I<lwp-download> program will down load the document specified by the URL
+given as the first command line argument to a local file.  The local
+filename used to save the document is guessed from the URL unless
+specified as the second command line argument.
+
+The I<lwp-download> program is implemented using the I<libwww-perl>
+library.  It is better suited to down load big files than the
+I<lwp-request> program because it does not store the file in memory.
+Another benefit is that it will keep you updated about its progress
+and that you don't have much options to worry about.
+
+Use the C<-a> option to save the file in text (ascii) mode.  Might make a
+difference on dosish systems.
+
+=head1 EXAMPLE
+
+Fetch the newest and greatest perl version:
+
+ $ lwp-download http://www.perl.com/CPAN/src/latest.tar.gz
+ Saving to 'latest.tar.gz'...
+ 1.47 MB received in 22 seconds (68.7 KB/sec)
+
+=head1 AUTHOR
+
+Gisle Aas <gisle@aas.no>
+
+=cut
+
+use LWP::UserAgent;
+use LWP::MediaTypes;
+use URI::URL;
+use strict;
+
+my $progname = $0;
+$progname =~ s,.*/,,;    # only basename left in progname
+$progname =~ s/\.\w*$//; # strip extension if any
+
+#parse option
+use Getopt::Std;
+my %opts;
+unless (getopts('a', \%opts)) {
+    usage();
+}
+my $opt_a = $opts{a}; # save in binary mode
+
+my $url = url(shift || usage());
+my $argfile = shift;
+
+my $ua = new LWP::UserAgent;
+
+$ua->agent("lwp-download/0.1 " . $ua->agent);
+$ua->env_proxy;
+
+my $req = new HTTP::Request GET => $url;
+
+my $file;      # name of file we download into
+my $length;    # total number of bytes to download
+my $flength;   # formatted length
+my $size = 0;  # number of bytes received
+my $start_t;   # start time of download
+my $last_dur;  # time of last callback
+
+my $shown = 0; # have we called the show() function yet
+
+$SIG{INT} = sub { die "Interrupted\n"; };
+
+$| = 1;  # autoflush
+
+my $res = $ua->request($req,
+  sub {
+      unless($file) {
+	  my $res = $_[1];
+	  unless ($argfile) {
+	      # must find a suitable name to use.  First thing
+	      # to do is to look for the "Content-Disposition"
+	      # header defined by RFC1806.  This is also supported
+	      # by Netscape
+	      my $cd = $res->header("Content-Disposition");
+	      if ($cd && $cd =~ /\bfilename\s*=\s*(\S+)/) {
+		  $file = $1;
+		  $file =~ s/;$//;
+		  $file =~ s/^([\"\'])(.*)\1$/$2/;
+	      }
+		
+	      # if this fails we try to make something from the URL
+	      unless ($file) {
+		  my $req = $res->request;  # now always there
+		  my $rurl = $req ? $req->url : $url;
+		  
+		  $file = ($rurl->path_components)[-1];
+		  unless (length $file) {
+		      $file = "index";
+		      my $suffix = media_suffix($res->content_type);
+		      $file .= ".$suffix" if $suffix;
+		  } elsif ($rurl->scheme eq 'ftp' ||
+			   $file =~ /\.tgz$/      ||
+			   $file =~ /\.tar(\.(Z|gz))?$/
+			  ) {
+		      # leave the filename as it was
+		  } else {
+		      my $ct = guess_media_type($file);
+		      unless ($ct eq $res->content_type) {
+			  # need a better suffix for this type
+			  my $suffix = media_suffix($res->content_type);
+			  $file .= ".$suffix" if $suffix;
+		      }
+		  }
+	      }
+
+	      # Check if the file is already present
+	      if (-f $file && -t) {
+		  print "Overwrite $file? [y] ";
+		  my $ans = <STDIN>;
+		  exit if !defined($ans) || !($ans =~ /^y?\n/);
+	      } else {
+		  print "Saving to '$file'...\n";
+	      }
+	  } else {
+	      $file = $argfile;
+	  }
+	  open(FILE, ">$file") || die "Can't open $file: $!";
+          binmode FILE unless $opt_a;
+	  $length = $res->content_length;
+	  $flength = fbytes($length) if defined $length;
+	  $start_t = time;
+	  $last_dur = 0;
+      }
+      $size += length($_[0]);
+      print FILE $_[0];
+      if (defined $length) {
+	  my $dur  = time - $start_t;
+	  if ($dur != $last_dur) {  # don't update too often
+	      $last_dur = $dur;
+	      my $perc = $size / $length;
+	      my $speed;
+	      $speed = fbytes($size/$dur) . "/sec" if $dur > 3;
+	      my $secs_left = fduration($dur/$perc - $dur);
+	      $perc = int($perc*100);
+	      my $show = "$perc% of $flength";
+	      $show .= " (at $speed, $secs_left remaining)" if $speed;
+	      show($show);
+	  }
+      } else {
+	  show( fbytes($size) . " received");
+      }
+  }
+);
+
+if ($res->is_success || $res->message =~ /^Interrupted/) {
+    show("");  # clear text
+    print "\r";
+    print fbytes($size);
+    print " of ", fbytes($length) if defined($length) && $length != $size;
+    print " received";
+    my $dur = time - $start_t;
+    if ($dur) {
+	my $speed = fbytes($size/$dur) . "/sec";
+	print " in ", fduration($dur), " ($speed)";
+    }
+    print "\n";
+    my $died = $res->header("X-Died");
+    if ($died || !$res->is_success) {
+	if (-t) {
+	    print "Transfer aborted.  Delete $file? [n] ";
+	    my $ans = <STDIN>;
+	    unlink($file) if defined($ans) && $ans =~ /^y\n/;
+	} else {
+	    print "Transfer aborted, $file kept\n";
+	}
+    }
+} else {
+    print "\n" if $shown;
+    print "$progname: Can't download: ", $res->code, " ", $res->message, "\n";
+    exit 1;
+}
+
+
+sub fbytes
+{
+    my $n = int(shift);
+    if ($n >= 1024 * 1024) {
+	return sprintf "%.3g MB", $n / (1024.0 * 1024);
+    } elsif ($n >= 1024) {
+	return sprintf "%.3g KB", $n / 1024.0;
+    } else {
+	return "$n bytes";
+    }
+}
+
+sub fduration
+{
+    use integer;
+    my $secs = int(shift);
+    my $hours = $secs / (60*60);
+    $secs -= $hours * 60*60;
+    my $mins = $secs / 60;
+    $secs %= 60;
+    if ($hours) {
+	return "$hours hours $mins minutes";
+    } elsif ($mins >= 2) {
+	return "$mins minutes";
+    } else {
+	$secs += $mins * 60;
+	return "$secs seconds";
+    }
+}
+
+sub show
+{
+    my $mess = shift;
+    print "\r$mess", (" " x (75 - length $mess));
+    $shown++;
+}
+
+sub usage
+{
+    die "Usage: $progname [-a] <url> [<lpath>]\n";
+}
+!NO!SUBS!
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-mirror.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-mirror.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-mirror.PL	(revision 16561)
@@ -0,0 +1,120 @@
+use Config;
+use File::Basename qw(basename dirname);
+chdir(dirname($0));
+($file = basename($0)) =~ s/\.PL$//;
+$file =~ s/\.pl$//
+	if ($Config{'osname'} eq 'VMS' or
+	    $Config{'osname'} eq 'OS2');  # "case-forgiving"
+open OUT,">$file" or die "Can't create $file: $!";
+chmod(0755, $file);
+print "Extracting $file (with variable substitutions)\n";
+
+print OUT <<"!GROK!THIS!";
+$Config{'startperl'} -w
+
+!GROK!THIS!
+
+print OUT <<'!NO!SUBS!';
+# $Id$
+#
+# Simple mirror utility using LWP
+
+=head1 NAME
+
+lwp-mirror - Simple mirror utility for WWW
+
+=head1 SYNOPSIS
+
+ lwp-mirror [-v] [-t timeout] <url> <local file>
+
+=head1 DESCRIPTION
+
+This program can be used to mirror a document from a WWW server.  The
+document is only transfered if the remote copy is newer than the local
+copy.  If the local copy is newer nothing happens.
+
+Use the C<-v> option to print the version number of this program.
+
+The timeout value specified with the C<-t> option.  The timeout value
+is the time that the program will wait for response from the remote
+server before it fails.  The default unit for the timeout value is
+seconds.  You might append "m" or "h" to the timeout value to make it
+minutes or hours, repectively.
+
+Because this program is implemented using the LWP library, it only
+supports the protocols that LWP supports.
+
+=head1 SEE ALSO
+
+L<lwp-request>, L<LWP>
+
+=head1 AUTHOR
+
+Gisle Aas <aas@a.sn.no>
+
+=cut
+
+
+use LWP::Simple;
+use Getopt::Std;
+
+$progname = $0;
+$progname =~ s,.*/,,;  # use basename only
+$progname =~ s/\.\w*$//; #strip extension if any
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+$opt_h = undef;  # print usage
+$opt_v = undef;  # print version
+$opt_t = undef;  # timeout
+
+unless (getopts("hvt:")) {
+    usage();
+}
+
+if ($opt_v) {
+    require LWP;
+    my $DISTNAME = 'libwww-perl-' . LWP::Version();
+    die <<"EOT";
+This is lwp-mirror version $VERSION ($DISTNAME)
+
+Copyright 1995-1996, Gisle Aas.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+EOT
+}
+
+$url  = shift or usage();
+$file = shift or usage();
+usage() if $opt_h or @ARGV;
+
+if (defined $opt_t) {
+    $opt_t =~ /^(\d+)([smh])?/;
+    die "$progname: Illegal timeout value!\n" unless defined $1;
+    $timeout = $1;
+    $timeout *= 60   if ($2 eq "m");
+    $timeout *= 3600 if ($2 eq "h");
+    $LWP::Simple::ua->timeout($timeout);
+}
+
+$rc = mirror($url, $file);
+
+if ($rc == 304) {
+    print STDERR "$progname: $file is up to date\n"
+} elsif (!is_success($rc)) {
+    print STDERR "$progname: $rc ", status_message($rc), "   ($url)\n";
+    exit 1;
+}
+exit;
+
+
+sub usage
+{
+    die <<"EOT";
+Usage: $progname [-options] <url> <file>
+    -v           print version number of program
+    -t <timeout> Set timeout value
+EOT
+}
+!NO!SUBS!
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-request.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-request.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-request.PL	(revision 16561)
@@ -0,0 +1,521 @@
+use Config;
+use File::Basename qw(basename dirname);
+chdir(dirname($0));
+($file = basename($0)) =~ s/\.PL$//;
+$file =~ s/\.pl$//
+	if ($Config{'osname'} eq 'VMS' or
+	    $Config{'osname'} eq 'OS2');  # "case-forgiving"
+open OUT,">$file" or die "Can't create $file: $!";
+chmod(0755, $file);
+print "Extracting $file (with variable substitutions)\n";
+
+print OUT <<"!GROK!THIS!";
+$Config{'startperl'} -w
+
+!GROK!THIS!
+
+print OUT <<'!NO!SUBS!';
+# $Id$
+#
+# Simple user agent using LWP library.
+
+=head1 NAME
+
+lwp-request, GET, HEAD, POST - Simple WWW user agent
+
+=head1 SYNOPSIS
+
+ lwp-request [-aeEdvhx] [-m method] [-b <base URL>] [-t <timeout>]
+             [-i <if-modified-since>] [-c <content-type>] [-C <credentials>]
+             [-p <proxy-url>] [-o <format>] <url>...
+
+=head1 DESCRIPTION
+
+This program can be used to send requests to WWW servers and your
+local file system. The request content for POST, PUT and CHECKIN
+methods is read from stdin.  The content of the response is printed on
+stdout.  Error messages are printed on stderr.  The program returns a
+status value indicating the number of URLs that failed.
+
+The options are:
+
+=over 4
+
+=item -m <method>
+
+Set which method to use for the request.  If this option is not used,
+then the method is derived from the name of the program.
+
+=item -f
+
+Force request through, even if the program believes that the method is
+illegal.  The server will probably reject the request.
+
+=item -b <url>
+
+This URL will be used as the base URL for the URLs that the method is
+applied to.  The base URL only takes effect for relative URLs.  If you
+do not provide this option and the URLs are relative, then they will
+be treated as files in the local file system.
+
+=item -t <timeout>
+
+Set the timeout value for the requests.  The timeout is the amount of
+time that the program will wait for a response from the remote server
+before it fails.  The default unit for the timeout value is seconds.
+You might append "m" or "h" to the timeout value to make it minutes or
+hours, respectively.  The default timeout is '3m', i.e. 3 minutes.
+
+=item -i <time>
+
+Set the If-Modified-Since header in the request. If I<time> it the
+name of a file, use the modification timestamp for this file. If
+I<time> is not a file, it is parsed as a literal date. Take a look at
+L<HTTP::Date> for recogniced formats.
+
+=item -c <content-type>
+
+Set the Content-Type for the request.  This option is only allowed for
+requests that take a content, i.e. POST, PUT and CHECKIN.  You can
+force methods to take content by using the C<-f> option together with
+C<-c>.  The default Content-Type for POST is
+C<application/x-www-form-urlencoded>.  The default Content-type for
+the others is C<text/plain>.
+
+=item -p <proxy-url>
+
+Set the proxy to be used for the requests.  The program also loads
+proxy settings from the environment.  You can disable this with the
+C<-P> option.
+
+=item -C <username>:<password>
+
+Provide credentials for documents that are protected by Basic
+Authentication.  If the document is protected and you did not specify
+the username and password with this option, then you will be prompted
+to provide these values.
+
+=back
+
+The following options controls what is displayed by the program:
+
+=over 4
+
+=item -u
+
+Print request method and absolute URL as requests are made.
+
+=item -U
+
+Print request headers in addition to request method and absolute URL.
+
+=item -s
+
+Print response status code.  This option is always on for HEAD requests.
+
+=item -S
+
+Print response status chain. This shows redirect and autorization
+requests that are handled by the library.
+
+=item -e
+
+Print response headers.  This option is always on for HEAD requests.
+
+=item -d
+
+Do B<not> print the content of the response.
+
+=item -o <format>
+
+Process HTML content in various ways before printing it.  If the
+content type of the response is not HTML, then this option has no
+effect.  The legal format values are; I<text>, I<ps>, I<links>,
+I<html> and I<dump>.
+
+If you specify the I<text> format then the HTML will be formatted as
+plain latin1 text.  If you specify the I<ps> format then it will be
+formatted as Postscript.
+
+The I<links> format will output all links found in the HTML document.
+Relative links will be expanded to absolute ones.
+
+The I<html> format will reformat the HTML code and the I<dump> format
+will just dump the HTML syntax tree.
+
+=item -v
+
+Print the version number of the program and quit.
+
+=item -h
+
+Print usage message and quit.
+
+=item -x
+
+Extra debugging output.
+
+=item -a
+
+Set text(ascii) mode for content input and output.  If this option is not 
+used, content input and output is done in binary mode.
+
+=back
+
+Because this program is implemented using the LWP library, it will
+only support the protocols that LWP supports.
+
+=head1 SEE ALSO
+
+L<lwp-mirror>, L<LWP>
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+Gisle Aas <aas@sn.no>
+
+=cut
+
+$progname = $0;
+$progname =~ s,.*/,,;  # use basename only
+$progname =~ s/\.\w*$//; # strip extension, if any
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+
+require LWP;
+require LWP::Debug;
+require URI::URL;
+
+use URI::Heuristic qw(uf_url);
+
+use HTTP::Status qw(status_message);
+use HTTP::Date qw(time2str str2time);
+
+
+# This table lists the methods that are allowed.  It should really be
+# a superset for all methods supported for every scheme that may be
+# supported by the library.  Currently it might be a bit too HTTP
+# specific.  You might use the -f option to force a method through.
+#
+# "" = No content in request, "C" = Needs content in request
+#
+%allowed_methods = (
+    GET        => "",
+    HEAD       => "",
+    POST       => "C",
+    PUT        => "C",
+    DELETE     => "",
+    TRACE      => "",
+    OPTIONS    => "",
+);
+
+
+# We make our own specialization of LWP::UserAgent that asks for
+# user/password if document is protected.
+{
+    package RequestAgent;
+    @ISA = qw(LWP::UserAgent);
+
+    sub new
+    { 
+	my $self = LWP::UserAgent::new(@_);
+	$self->agent("lwp-request/$main::VERSION");
+	$self;
+    }
+
+    sub get_basic_credentials
+    {
+	my($self, $realm, $uri) = @_;
+	if ($main::opt_C) {
+	    return split(':', $main::opt_C, 2);
+	} elsif (-t) {
+	    my $netloc = $uri->netloc;
+	    print "Enter username for $realm at $netloc: ";
+	    my $user = <STDIN>;
+	    chomp($user);
+	    return (undef, undef) unless length $user;
+	    print "Password: ";
+	    system("stty -echo");
+	    my $password = <STDIN>;
+	    system("stty echo");
+	    print "\n";  # because we disabled echo
+	    chomp($password);
+	    return ($user, $password);
+	} else {
+	    return (undef, undef)
+	}
+    }
+}
+
+$method = uc(lc($progname) eq "lwp-request" ? "GET" : $progname);
+
+# Parse command line
+use Getopt::Std;
+
+$opt_a = undef;  # content i/o in text(ascii) mode
+$opt_m = undef;  # set method
+$opt_f = undef;  # make request even if method is not in %allowed_methods
+$opt_b = undef;  # base url
+$opt_t = undef;  # timeout
+$opt_i = undef;  # if-modified-since
+$opt_c = undef;  # content type for POST
+$opt_C = undef;  # credidentials for basic authorization
+
+$opt_u = undef;  # display method, URL and headers of request
+$opt_U = undef;  # display request headers also
+$opt_s = undef;  # display status code
+$opt_S = undef;  # display whole chain of status codes
+$opt_e = undef;  # display response headers (default for HEAD)
+$opt_d = undef;  # don't display content
+
+$opt_h = undef;  # print usage
+$opt_v = undef;  # print version
+
+$opt_x = undef;  # extra debugging info
+$opt_p = undef;  # proxy URL
+$opt_P = undef;  # don't load proxy setting from environment
+
+$opt_o = undef;  # output format
+
+unless (getopts("axhvuUsSedPp:b:t:i:c:C:m:fo:")) {
+    usage();
+}
+
+if ($opt_v) {
+    require LWP;
+    my $DISTNAME = 'libwww-perl-' . LWP::Version();
+    die <<"EOT";
+This is lwp-request version $VERSION ($DISTNAME)
+
+Copyright 1995-1997, Gisle Aas.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+EOT
+}
+
+usage() if $opt_h || !@ARGV;
+
+LWP::Debug::level('+') if $opt_x;
+
+# Create the user agent object
+$ua = new RequestAgent;
+
+# Load proxy settings from *_proxy environment variables.
+$ua->env_proxy unless $opt_P;
+
+$method = uc($opt_m) if defined $opt_m;
+
+if ($opt_f) {
+    if ($opt_c) {
+        $allowed_methods{$method} = "C";  # force content
+    } else {
+        $allowed_methods{$method} = "";
+    }
+} elsif (!defined $allowed_methods{$method}) {
+    die "$progname: $method is not an allowed method\n";
+}
+
+if ($method eq "HEAD") {
+    $opt_s = 1;
+    $opt_e = 1 unless $opt_d;
+    $opt_d = 1;
+}
+
+if (defined $opt_t) {
+    $opt_t =~ /^(\d+)([smh])?/;
+    die "$progname: Illegal timeout value!\n" unless defined $1;
+    $timeout = $1;
+    $timeout *= 60   if ($2 eq "m");
+    $timeout *= 3600 if ($2 eq "h");
+    $ua->timeout($timeout);
+}
+
+if (defined $opt_i) {
+    if (-e $opt_i) {
+        $time = (stat _)[9];
+    } else {
+        $time = str2time($opt_i);
+        die "$progname: Illegal time syntax for -i option\n"
+            unless defined $time;
+    }
+    $opt_i = time2str($time);
+}
+
+$content = undef;
+if ($allowed_methods{$method} eq "C") {
+    # This request needs some content
+    unless (defined $opt_c) {
+        # set default content type
+        $opt_c = ($method eq "POST") ?
+              "application/x-www-form-urlencoded"
+            : "text/plain";
+    } else {
+        die "$progname: Illegal Content-type format\n"
+            unless $opt_c =~ m,^[\w\-]+/[\w\-]+(?:\s*;.*)?$,
+    }
+    print "Please enter content ($opt_c) to be ${method}ed:\n"
+        if -t;
+    binmode STDIN unless -t or $opt_a;
+    $content = join("", <STDIN>);
+} else {
+    die "$progname: Can't set Content-type for $method requests\n"
+        if defined $opt_c;
+}
+
+# Set up a request.  We will use the same request object for all URLs.
+$request = new HTTP::Request $method;
+$request->header('If-Modified-Since', $opt_i) if defined $opt_i;
+#$request->header('Accept', '*/*');
+if ($opt_c) { # will always be set for request that wants content
+    $request->header('Content-Type', $opt_c);
+    $request->header('Content-Length', length $content);  # Not really needed
+    $request->content($content);
+}
+
+
+$errors = 0;
+
+# Ok, now we perform the requests, one URL at a time
+while ($url = shift) {
+    # Create the URL object, but protect us against bad URLs
+    eval {
+	if ($url =~ /^\w+:/ || $opt_b) {  # is there any scheme specification
+	    $url = URI::URL->new($url, $opt_b);
+	} else {
+	    $url = uf_url($url);
+        }
+    };
+    if ($@) {
+	$@ =~ s/at\s+\S+\s+line\s\d+//;
+	print STDERR $@;
+	$errors++;
+	next;
+    }
+
+    $ua->proxy($url->scheme, $opt_p) if $opt_p;
+
+    # Send the request and get a response back from the server
+    $request->url($url);
+    $response = $ua->request($request);
+
+    if ($opt_u || $opt_U) {
+        my $url = $response->request->url->as_string;
+        print "$method $url\n";
+        print $response->request->headers_as_string, "\n" if $opt_U;
+    }
+
+    if ($opt_S) {
+	printResponseChain($response);
+    } elsif ($opt_s) {
+        print $response->status_line, "\n";
+    }
+
+    if ($opt_e) {
+        # Display headers
+        print $response->headers_as_string;
+        print "\n";  # separate headers and content
+    }
+
+    if ($response->is_success) {
+	unless ($opt_d) {
+	    if ($opt_o &&
+		$response->content_type eq 'text/html') {
+		require HTML::Parse;
+		my $html = HTML::Parse::parse_html($response->content);
+		{
+		    $opt_o eq 'ps' && do {
+			require HTML::FormatPS;
+			my $f = new HTML::FormatPS;
+			print $f->format($html);
+			last;
+		    };
+		    $opt_o eq 'text' && do {
+			require HTML::FormatText;
+			my $f = new HTML::FormatText;
+			print $f->format($html);
+			last;
+		    };
+		    $opt_o eq 'html' && do {
+			print $html->as_HTML;
+			last;
+		    };
+		    $opt_o eq 'links' && do {
+			my $base = $response->base;
+			for ( @{ $html->extract_links } ) {
+			    my($link, $elem) = @$_;
+			    my $tag = uc $elem->tag;
+			    $link = new URI::URL $link, $base;
+			    print "$tag\t", $link->abs->as_string, "\n";
+			}
+			last;
+		    };
+		    $opt_o eq 'dump' && do {
+			$html->dump;
+			last;
+		    };
+		    # It is bad to not notice this before now :-(
+		    die "Illegal -o option value ($opt_o)\n";
+		}
+	    } else {
+                binmode STDOUT unless $opt_a;
+		print $response->content;
+	    }
+	}
+    } else {
+        print STDERR $response->error_as_HTML unless $opt_d;
+        $errors++;
+    }
+}
+
+exit $errors;
+
+
+sub printResponseChain
+{
+    my($response) = @_;
+    return unless defined $response;
+    printResponseChain($response->previous);
+    my $method = $response->request->method;
+    my $url = $response->request->url->as_string;
+    my $code = $response->code;
+    print "$method $url --> ", $response->status_line, "\n";
+}
+
+
+sub usage
+{
+    die <<"EOT";
+Usage: $progname [-options] <url>...
+    -m <method>   use method for the request (default is '$method')
+    -f            make request even if $progname believes method is illegal
+    -b <base>     Use the specified URL as base
+    -t <timeout>  Set timeout value
+    -i <time>     Set the If-Modified-Since header on the request
+    -c <conttype> use this content-type for POST, PUT, CHECKIN
+    -a            Use text mode for content I/O
+    -p <proxyurl> use this as a proxy
+    -P            don't load proxy settings from environment
+
+    -u            Display method and URL before any response
+    -U            Display request headers (implies -u)
+    -s            Display response status code
+    -S            Display response status chain
+    -e            Display response headers
+    -d            Do not display content
+    -o <format>   Process HTML content in various ways
+
+    -v            Show program version
+    -h            Print this message
+
+    -x            Extra debugging output
+EOT
+}
+!NO!SUBS!
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-rget.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-rget.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/bin/lwp-rget.PL	(revision 16561)
@@ -0,0 +1,600 @@
+use Config;
+use File::Basename qw(basename dirname);
+chdir(dirname($0));
+($file = basename($0)) =~ s/\.PL$//;
+$file =~ s/\.pl$//
+	if ($Config{'osname'} eq 'VMS' or
+	    $Config{'osname'} eq 'OS2');  # "case-forgiving"
+open OUT,">$file" or die "Can't create $file: $!";
+chmod(0755, $file);
+print "Extracting $file (with variable substitutions)\n";
+
+print OUT <<"!GROK!THIS!";
+$Config{'startperl'} -w
+
+!GROK!THIS!
+
+print OUT <<'!NO!SUBS!';
+#line 18
+
+=head1 NAME
+
+lwp-rget - Retrieve WWW documents recursively
+
+=head1 SYNOPSIS
+
+ lwp-rget [--verbose] [--auth=USER:PASS] [--depth=N] [--hier] [--iis]
+	  [--keepext=mime/type[,mime/type]] [--limit=N] [--nospace]
+	  [--prefix=URL] [--sleep=N] [--tolower] <URL>
+ lwp-rget --version
+
+=head1 DESCRIPTION
+
+This program will retrieve a document and store it in a local file.  It
+will follow any links found in the document and store these documents
+as well, patching links so that they refer to these local copies.
+This process continues until there are no more unvisited links or the
+process is stopped by the one or more of the limits which can be
+controlled by the command line arguments.
+
+This program is useful if you want to make a local copy of a
+collection of documents or want to do web reading off-line.
+
+All documents are stored as plain files in the current directory. The
+file names chosen are derived from the last component of URL paths.
+
+The options are:
+
+=over 3
+
+=item --auth=USER:PASS<n>
+
+Set the authentication credentials to user "USER" and password "PASS" if
+any restricted parts of the web site are hit.  If there are restricted
+parts of the web site and authentication credentials are not available,
+those pages will not be downloaded.
+
+=item --depth=I<n>
+
+Limit the recursive level. Embedded images are always loaded, even if
+they fall outside the I<--depth>. This means that one can use
+I<--depth=0> in order to fetch a single document together with all
+inline graphics.
+
+The default depth is 5.
+
+=item --hier
+
+Download files into a hierarchy that mimics the web site structure.
+The default is to put all files in the current directory.
+
+=item --iis
+
+Sends an "Accept: */*" on all URL requests as a workaround for a bug in
+IIS 2.0.  If no Accept MIME header is present, IIS 2.0 returns with a
+"406 No acceptable objects were found" error.  Also converts any back
+slashes (\\) in URLs to forward slashes (/).
+
+=item --keepext=I<mime/type[,mime/type]>
+
+Keeps the current extension for the list MIME types.  Useful when
+downloading text/plain documents that shouldn't all be translated to
+*.txt files.
+
+=item --limit=I<n>
+
+Limit the number of documents to get.  The default limit is 50.
+
+=item --nospace
+
+Changes spaces in all URLs to underscore characters (_).  Useful when
+downloading files from sites serving URLs with spaces in them.	Does not
+remove spaces from fragments, e.g., "file.html#somewhere in here".
+
+=item --prefix=I<url_prefix>
+
+Limit the links to follow. Only URLs that start the prefix string are
+followed.
+
+The default prefix is set as the "directory" of the initial URL to
+follow.	 For instance if we start lwp-rget with the URL
+C<http://www.sn.no/foo/bar.html>, then prefix will be set to
+C<http://www.sn.no/foo/>.
+
+Use C<--prefix=''> if you don't want the fetching to be limited by any
+prefix.
+
+=item --sleep=I<n>
+
+Sleep I<n> seconds before retrieving each document. This options allows
+you to go slowly, not loading the server you visiting too much.
+
+=item --tolower
+
+Translates all links to lowercase.  Useful when downloading files from
+IIS since it does not serve files in a case sensitive manner.
+
+=item --verbose
+
+Make more noise while running.
+
+=item --quiet
+
+Don't make any noise.
+
+=item --version
+
+Print program version number and quit.
+
+=item --help
+
+Print the usage message and quit.
+
+=back
+
+Before the program exits the name of the file, where the initial URL
+is stored, is printed on stdout.  All used filenames are also printed
+on stderr as they are loaded.  This printing can be suppressed with
+the I<--quiet> option.
+
+=head1 SEE ALSO
+
+L<lwp-request>, L<LWP>
+
+=head1 AUTHOR
+
+Gisle Aas <aas@sn.no>
+
+=cut
+
+use strict;
+
+use Getopt::Long    qw(GetOptions);
+use URI::URL	    qw(url);
+use LWP::MediaTypes qw(media_suffix);
+
+use vars qw($VERSION);
+use vars qw($MAX_DEPTH $MAX_DOCS $PREFIX $VERBOSE $QUIET $SLEEP $HIER $AUTH $IIS $TOLOWER $NOSPACE %KEEPEXT);
+
+my $progname = $0;
+$progname =~ s|.*/||;  # only basename left
+$progname =~ s/\.\w*$//; #strip extension if any
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+#$Getopt::Long::debug = 1;
+#$Getopt::Long::ignorecase = 0;
+
+# Defaults
+$MAX_DEPTH = 5;
+$MAX_DOCS  = 50;
+
+GetOptions('version'  => \&print_version,
+	   'help'     => \&usage,
+	   'depth=i'  => \$MAX_DEPTH,
+	   'limit=i'  => \$MAX_DOCS,
+	   'verbose!' => \$VERBOSE,
+	   'quiet!'   => \$QUIET,
+	   'sleep=i'  => \$SLEEP,
+	   'prefix:s' => \$PREFIX,
+	   'hier'     => \$HIER,
+	   'auth=s'   => \$AUTH,
+	   'iis'      => \$IIS,
+	   'tolower'  => \$TOLOWER,
+	   'nospace'  => \$NOSPACE,
+	   'keepext=s' => \$KEEPEXT{'OPT'},
+	  ) || usage();
+
+sub print_version {
+    require LWP;
+    my $DISTNAME = 'libwww-perl-' . LWP::Version();
+    print <<"EOT";
+This is lwp-rget version $VERSION ($DISTNAME)
+
+Copyright 1996-1998, Gisle Aas.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+EOT
+    exit 0;
+}
+
+my $start_url = shift || usage();
+usage() if @ARGV;
+
+require LWP::UserAgent;
+my $ua = new LWP::UserAgent;
+$ua->agent("$progname/$VERSION " . $ua->agent);
+$ua->env_proxy;
+
+unless (defined $PREFIX) {
+    $PREFIX = url($start_url);	 # limit to URLs below this one
+    eval {
+	$PREFIX->eparams(undef);
+	$PREFIX->equery(undef);
+    };
+
+    $_ = $PREFIX->epath;
+    s|[^/]+$||;
+    $PREFIX->epath($_);
+    $PREFIX = $PREFIX->as_string;
+}
+
+%KEEPEXT = map { lc($_) => 1 } split(/\s*,\s*/, $KEEPEXT{'OPT'});
+
+print <<"" if $VERBOSE;
+START	  = $start_url
+MAX_DEPTH = $MAX_DEPTH
+MAX_DOCS  = $MAX_DOCS
+PREFIX	  = $PREFIX
+
+
+my $no_docs = 0;
+my %seen = ();	   # mapping from URL => local_file
+
+my $filename = fetch($start_url);
+print "$filename\n" unless $QUIET;
+
+sub fetch
+{
+    my($url, $type, $depth) = @_;
+
+    # Fix http://sitename.com/../blah/blah.html to
+    #	  http://sitename.com/blah/blah.html
+    $url = $url->as_string if (ref($url));
+    while ($url =~ s#(https?://[^/]+/)\.\.\/#$1#) {}
+
+    # Fix backslashes (\) in URL if $IIS defined
+    $url = fix_backslashes($url) if (defined $IIS);
+
+    $url = url($url) unless ref($url);
+    $type  ||= 'a';
+    # Might be the background attribute
+    $type = 'img' if ($type eq 'body' || $type eq 'td');
+    $depth ||= 0;
+
+    # Print the URL before we start checking...
+    my $out = (" " x $depth) . $url . " ";
+    $out .= "." x (60 - length($out));
+    print STDERR $out . " " if $VERBOSE;
+
+    # Can't get mailto things
+    if ($url->scheme eq 'mailto') {
+	print STDERR "*skipping mailto*\n" if $VERBOSE;
+	return $url->as_string;
+    }
+
+    # The $plain_url is a URL without the fragment part
+    my $plain_url = $url->clone;
+    $plain_url->frag(undef);
+
+    # Check PREFIX, but not for <IMG ...> links
+    if ($type ne 'img' and  $url->as_string !~ /^\Q$PREFIX/o) {
+	print STDERR "*outsider*\n" if $VERBOSE;
+	return $url->as_string;
+    }
+
+    # Translate URL to lowercase if $TOLOWER defined
+    $plain_url = to_lower($plain_url) if (defined $TOLOWER);
+
+    # If we already have it, then there is nothing to be done
+    my $seen = $seen{$plain_url->as_string};
+    if ($seen) {
+	my $frag = $url->frag;
+	$seen .= "#$frag" if defined($frag);
+	$seen = protect_frag_spaces($seen);
+	print STDERR "$seen (again)\n" if $VERBOSE;
+	return $seen;
+    }
+
+    # Too much or too deep
+    if ($depth > $MAX_DEPTH and $type ne 'img') {
+	print STDERR "*too deep*\n" if $VERBOSE;
+	return $url;
+    }
+    if ($no_docs > $MAX_DOCS) {
+	print STDERR "*too many*\n" if $VERBOSE;
+	return $url;
+    }
+
+    # Fetch document 
+    $no_docs++;
+    sleep($SLEEP) if $SLEEP;
+    my $req = HTTP::Request->new(GET => $url);
+    # See: http://ftp.sunet.se/pub/NT/mirror-microsoft/kb/Q163/7/74.TXT
+    $req->header ('Accept', '*/*') if (defined $IIS);  # GIF/JPG from IIS 2.0
+    $req->authorization_basic(split (/:/, $AUTH)) if (defined $AUTH);
+    my $res = $ua->request($req);
+
+    # Check outcome
+    if ($res->is_success) {
+	my $doc = $res->content;
+	my $ct = $res->content_type;
+	my $name = find_name($res->request->url, $ct);
+	print STDERR "$name\n" unless $QUIET;
+	$seen{$plain_url->as_string} = $name;
+
+	# If the file is HTML, then we look for internal links
+	if ($ct eq "text/html") {
+	    # Save an unprosessed version of the HTML document.	 This
+	    # both reserves the name used, and it also ensures that we
+	    # don't loose everything if this program is killed before
+	    # we finish.
+	    save($name, $doc);
+	    my $base = $res->base;
+
+	    # Follow and substitute links...
+	    $doc =~
+s/
+  (
+    <(img|a|body|area|frame|td)\b   # some interesting tag
+    [^>]+			    # still inside tag (not strictly correct)
+    \b(?:src|href|background)	    # some link attribute
+    \s*=\s*			    # =
+  )
+    (?:				    # scope of OR-ing
+	 (")([^"]*)"	|	    # value in double quotes  OR
+	 (')([^']*)'	|	    # value in single quotes  OR
+	    ([^\s>]+)		    # quoteless value
+    )
+/
+  new_link($1, lc($2), $3||$5, $4||$6||$7, $base, $name, $depth+1)
+/giex;
+	   # XXX
+	   # The regular expression above is not strictly correct.
+	   # It is not really possible to parse HTML with a single
+	   # regular expression, but it is faster.  Tags that might
+	   # confuse us include:
+	   #	<a alt="href" href=link.html>
+	   #	<a alt=">" href="link.html">
+	   #
+	}
+	save($name, $doc);
+	return $name;
+    } else {
+	print STDERR $res->code . " " . $res->message . "\n" if $VERBOSE;
+	$seen{$plain_url->as_string} = $url->as_string;
+	return $url->as_string;
+    }
+}
+
+sub new_link
+{
+    my($pre, $type, $quote, $url, $base, $localbase, $depth) = @_;
+
+    $url = protect_frag_spaces($url);
+
+    $url = fetch(url($url, $base)->abs, $type, $depth);
+    $url = url("file:$url", "file:$localbase")->rel
+	unless $url =~ /^[.+\-\w]+:/;
+
+    $url = unprotect_frag_spaces($url);
+
+    return $pre . $quote . $url . $quote;
+}
+
+
+sub protect_frag_spaces
+{
+    my ($url) = @_;
+
+    $url = $url->as_string if (ref($url));
+
+    if ($url =~ m/^([^#]*#)(.+)$/)
+    {
+      my ($base, $frag) = ($1, $2);
+      $frag =~ s/ /%20/g;
+      $url = $base . $frag;
+    }
+
+    return $url;
+}
+
+
+sub unprotect_frag_spaces
+{
+    my ($url) = @_;
+
+    $url = $url->as_string if (ref($url));
+
+    if ($url =~ m/^([^#]*#)(.+)$/)
+    {
+      my ($base, $frag) = ($1, $2);
+      $frag =~ s/%20/ /g;
+      $url = $base . $frag;
+    }
+
+    return $url;
+}
+
+
+sub fix_backslashes
+{
+    my ($url) = @_;
+    my ($base, $frag);
+
+    $url = $url->as_string if (ref($url));
+
+    if ($url =~ m/([^#]+)(#.*)/)
+    {
+      ($base, $frag) = ($1, $2);
+    }
+    else
+    {
+      $base = $url;
+      $frag = "";
+    }
+
+    $base =~ tr/\\/\//;
+    $base =~ s/%5[cC]/\//g;	# URL-encoded back slash is %5C
+
+    return $base . $frag;
+}
+
+
+sub to_lower
+{
+    my ($url) = @_;
+    my $was_object = 0;
+
+    if (ref($url))
+    {
+      $url = $url->as_string;
+      $was_object = 1;
+    }
+
+    if ($url =~ m/([^#]+)(#.*)/)
+    {
+      $url = lc($1) . $2;
+    }
+    else
+    {
+      $url = lc($url);
+    }
+
+    if ($was_object == 1)
+    {
+      return url($url);
+    }
+    else
+    {
+      return $url;
+    }
+}
+
+
+sub translate_spaces
+{
+    my ($url) = @_;
+    my ($base, $frag);
+
+    $url = $url->as_string if (ref($url));
+
+    if ($url =~ m/([^#]+)(#.*)/)
+    {
+      ($base, $frag) = ($1, $2);
+    }
+    else
+    {
+      $base = $url;
+      $frag = "";
+    }
+
+    $base =~ s/^ *//;	# Remove initial spaces from base
+    $base =~ s/ *$//;	# Remove trailing spaces from base
+
+    $base =~ tr/ /_/;
+    $base =~ s/%20/_/g; # URL-encoded space is %20
+
+    return $base . $frag;
+}
+
+
+sub mkdirp
+{
+    my($directory, $mode) = @_;
+    my @dirs = split(/\//, $directory);
+    my $path = shift(@dirs);   # build it as we go
+    my $result = 1;   # assume it will work
+
+    unless (-d $path) {
+	$result &&= mkdir($path, $mode);
+    }
+
+    foreach (@dirs) {
+	$path .= "/$_";
+	if ( ! -d $path) {
+	    $result &&= mkdir($path, $mode);
+	}
+    }
+
+    return $result;
+}
+
+
+sub find_name
+{
+    my($url, $type) = @_;
+    #print "find_name($url, $type)\n";
+
+    # Translate spaces in URL to underscores (_) if $NOSPACE defined
+    $url = translate_spaces($url) if (defined $NOSPACE);
+
+    # Translate URL to lowercase if $TOLOWER defined
+    $url = to_lower($url) if (defined $TOLOWER);
+
+    $url = url($url) unless ref($url);
+
+    my $path = $url->path;
+
+    # trim path until only the basename is left
+    $path =~ s|(.*/)||;
+    my $dirname = ".$1";
+    if (!$HIER) {
+	$dirname = "";
+    } elsif (! -d $dirname) {
+	mkdirp($dirname, 0775);
+    }
+
+    my $extra = "";  # something to make the name unique
+    my $suffix;
+
+    if ($KEEPEXT{lc($type)}) {
+        $suffix = ($path =~ m/\.(.*)/) ? $1 : "";
+    } else {
+        $suffix = media_suffix($type);
+    }
+
+    $path =~ s|\..*||;	# trim suffix
+    $path = "index" unless length $path;
+
+    while (1) {
+	# Construct a new file name
+	my $file = $dirname . $path . $extra;
+	$file .= ".$suffix" if $suffix;
+	# Check if it is unique
+	return $file unless -f $file;
+
+	# Try something extra
+	unless ($extra) {
+	    $extra = "001";
+	    next;
+	}
+	$extra++;
+    }
+}
+
+
+sub save
+{
+    my $name = shift;
+    #print "save($name,...)\n";
+    open(FILE, ">$name") || die "Can't save $name: $!";
+    binmode FILE;
+    print FILE $_[0];
+    close(FILE);
+}
+
+
+sub usage
+{
+    die <<"";
+Usage: $progname [options] <URL>
+Allowed options are:
+  --auth=USER:PASS  Set authentication credentials for web site
+  --depth=N	    Maximum depth to traverse (default is $MAX_DEPTH)
+  --hier	    Download into hierarchy (not all files into cwd)
+  --iis		    Workaround IIS 2.0 bug by sending "Accept: */*" MIME
+		    header; translates backslashes (\\) to forward slashes (/)
+  --keepext=type    Keep file extension for MIME types (comma-separated list)
+  --limit=N	    A limit on the number documents to get (default is $MAX_DOCS)
+  --nospace	    Translate spaces URLs (not #fragments) to underscores (_)
+  --version	    Print version number and quit
+  --verbose	    More output
+  --quiet	    No output
+  --sleep=SECS	    Sleep between gets, ie. go slowly
+  --prefix=PREFIX   Limit URLs to follow to those which begin with PREFIX
+  --tolower	    Translate all URLs to lowercase (useful with IIS servers)
+
+}
+!NO!SUBS!
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/install.sh
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/install.sh	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/install.sh	(revision 16561)
@@ -0,0 +1,10 @@
+#!/bin/sh -v
+
+export PERL5LIB=$GSDLHOME/perllib/cpan/lib:$PERL5LIB
+export PATH=$GSDLHOME/perllib/cpan/bin:$PATH
+
+perl Makefile.PL PREFIX=$GSDLHOME/perllib/cpan
+
+make
+make test
+make install
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/Bundle/LWP.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/Bundle/LWP.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/Bundle/LWP.pm	(revision 16561)
@@ -0,0 +1,38 @@
+package Bundle::LWP;
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bundle::LWP - A bundle to install all libwww-perl related modules
+
+=head1 SYNOPSIS
+
+ perl -MCPAN -e 'install Bundle::LWP'
+
+=head1 CONTENTS
+
+Net::FTP 2.00      - If you want ftp://-support
+
+MIME::Base64       - Used in authentication headers
+
+MD5                - Needed to do Digest authentication
+
+HTML::HeadParser   - To get the correct $res->base
+
+LWP                - The reason why you need the modules above
+
+=head1 DESCRIPTION
+
+This bundle defines all reqreq modules for libwww-perl.
+
+
+=head1 AUTHOR
+
+Gisle Aas E<lt>aas@sn.no>
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/File/Listing.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/File/Listing.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/File/Listing.pm	(revision 16561)
@@ -0,0 +1,314 @@
+#
+# $Id$
+
+package File::Listing;
+
+sub Version { $VERSION; }
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+=head1 NAME
+
+parse_dir - parse directory listing
+
+=head1 SYNOPSIS
+
+ use File::Listing;
+ for (parse_dir(`ls -l`)) {
+     ($name, $type, $size, $mtime, $mode) = @$_;
+     next if $type ne 'f'; # plain file
+     #...
+ }
+
+ # directory listing can also be read from a file
+ open(LISTING, "zcat ls-lR.gz|");
+ $dir = parse_dir(\*LISTING, '+0000');
+
+=head1 DESCRIPTION
+
+The parse_dir() routine can be used to parse directory
+listings. Currently it only understand Unix C<'ls -l'> and C<'ls -lR'>
+format.  It should eventually be able to most things you might get
+back from a ftp server file listing (LIST command), i.e. VMS listings,
+NT listings, DOS listings,...
+
+The first parameter to parse_dir() is the directory listing to parse.
+It can be a scalar, a reference to an array of directory lines or a
+glob representing a filehandle to read the directory listing from.
+
+The second parameter is the time zone to use when parsing time stamps
+in the listing. If this value is undefined, then the local time zone is
+assumed.
+
+The third parameter is the type of listing to assume.  The values will
+be strings like 'unix', 'vms', 'dos'.  Currently only 'unix' is
+implemented and this is also the default value.  Ideally, the listing
+type should be determined automatically.
+
+The fourth parameter specify how unparseable lines should be treated.
+Values can be 'ignore', 'warn' or a code reference.  Warn means that
+the perl warn() function will be called.  If a code reference is
+passed, then this routine will be called and the return value from it
+will be incorporated in the listing.  The default is 'ignore'.
+
+Only the first parameter is mandatory.  The parse_dir() prototype is
+($;$$$).
+
+The return value from parse_dir() is a list of directory entries.  In
+scalar context the return value is a reference to the list.  The
+directory entries are represented by an array consisting of [
+$filename, $filetype, $filesize, $filetime, $filemode ].  The
+$filetype value is one of the letters 'f', 'd', 'l' or '?'.  The
+$filetime value is converted to seconds since Jan 1, 1970.  The
+$filemode is a bitmask like the mode returned by stat().
+
+=head1 CREDITS
+
+Based on lsparse.pl (from Lee McLoughlin's ftp mirror package) and
+Net::FTP's parse_dir (Graham Barr).
+
+=cut
+
+require Exporter;
+@ISA = qw(Exporter);
+
+@EXPORT = qw(parse_dir);
+
+use strict;
+
+use Carp ();
+use HTTP::Date qw(str2time);
+
+sub parse_dir ($;$$$)
+{
+   my($dir, $tz, $fstype, $error) = @_;
+
+   $fstype ||= 'unix';
+   $fstype = "File::Listing::" . lc $fstype;
+
+   my @args = $_[0];
+   push(@args, $tz) if(@_ >= 2);
+   push(@args, $error) if(@_ >= 4);
+
+   $fstype->parse(@args);
+}
+
+sub line { Carp::croak("Not implemented yet"); }
+sub init { } # Dummy sub
+
+sub file_mode ($)
+{
+    # This routine was originally borrowed from Graham Barr's
+    # Net::FTP package.
+
+    local $_ = shift;
+    my $mode = 0;
+    my($type,$ch);
+
+    s/^(.)// and $type = $1;
+
+    while (/(.)/g) {
+	$mode <<= 1;
+	$mode |= 1 if $1 ne "-" &&
+		      $1 ne 'S' &&
+		      $1 ne 't' &&
+		      $1 ne 'T';
+    }
+
+    $type eq "d" and $mode |= 0040000 or	# Directory
+      $type eq "l" and $mode |= 0120000 or	# Symbolic Link
+	$mode |= 0100000;			# Regular File
+
+    $mode |= 0004000 if /^...s....../i;
+    $mode |= 0002000 if /^......s.../i;
+    $mode |= 0001000 if /^.........t/i;
+
+    $mode;
+}
+
+sub parse
+{
+   my($pkg, $dir, $tz, $error) = @_;
+
+   # First let's try to determine what kind of dir parameter we have
+   # received.  We allow both listings, reference to arrays and
+   # file handles to read from.
+
+   if (ref($dir) eq 'ARRAY') {
+       # Already splitted up
+   } elsif (ref($dir) eq 'GLOB') {
+       # A file handle
+   } elsif (ref($dir)) {
+      Carp::croak("Illegal argument to parse_dir()");
+   } elsif ($dir =~ /^\*\w+(::\w+)+$/) {
+      # This scalar looks like a file handle, so we assume it is
+   } else {
+      # A normal scalar listing
+      $dir = [ split(/\n/, $dir) ];
+   }
+
+   $pkg->init();
+
+   my @files = ();
+   if (ref($dir) eq 'ARRAY') {
+       for (@$dir) {
+	   push(@files, $pkg->line($_, $tz, $error));
+       }
+   } else {
+       local($_);
+       while (<$dir>) {
+	   chomp;
+	   push(@files, $pkg->line($_, $tz, $error));
+       }
+   }
+   wantarray ? @files : \@files;
+}
+
+
+package File::Listing::unix;
+
+use HTTP::Date qw(str2time);
+
+# A place to remember current directory from last line parsed.
+use vars qw($curdir);
+no strict qw(vars);
+
+@ISA = qw(File::Listing);
+
+
+sub init
+{
+    $curdir = '';
+}
+
+sub line
+{
+    shift; # package name
+    local($_) = shift;
+    my($tz, $error) = @_;
+
+    s/\015//g;
+    #study;
+
+    my ($kind, $size, $date, $name);
+    if (($kind, $size, $date, $name) =
+	/^([\-FlrwxsStTdD]{10})                   # Type and permission bits
+	 .*                                       # Graps
+	 \D(\d+)                                  # File size
+	 \s+                                      # Some space
+	 (\w{3}\s+\d+\s+(?:\d{1,2}:\d{2}|\d{4}))  # Date
+	 \s+                                      # Some more space
+	 (.*)$                                    # File name
+	/x )
+
+    {
+	return if $name eq '.' || $name eq '..';
+	$name = "$curdir/$name" if length $curdir;
+	my $type = '?';
+	if ($kind =~ /^l/ && $name =~ /(.*) -> (.*)/ ) {
+	    $name = $1;
+	    $type = "l $2";
+	} elsif ($kind =~ /^[\-F]/) { # (hopefully) a regular file
+	    $type = 'f';
+	} elsif ($kind =~ /^[dD]/) {
+	    $type = 'd';
+	    $size = undef;  # Don't believe the reported size
+	}
+	return [$name, $type, $size, str2time($date, $tz), 
+              File::Listing::file_mode($kind)];
+
+    } elsif (/^(.+):$/ && !/^[dcbsp].*\s.*\s.*:$/ ) {
+	my $dir = $1;
+	return () if $dir eq '.';
+	$curdir = $dir;
+	return ();
+    } elsif (/^[Tt]otal\s+(\d+)$/ || /^\s*$/) {
+	return ();
+    } elsif (/not found/    || # OSF1, HPUX, and SunOS return
+             # "$file not found"
+             /No such file/ || # IRIX returns
+             # "UX:ls: ERROR: Cannot access $file: No such file or directory"
+                               # Solaris returns
+             # "$file: No such file or directory"
+             /cannot find/     # Windows NT returns
+             # "The system cannot find the path specified."
+             ) {
+	return () unless defined $error;
+	&$error($_) if ref($error) eq 'CODE';
+	warn "Error: $_\n" if $error eq 'warn';
+	return ();
+    } elsif ($_ eq '') {       # AIX, and Linux return nothing
+	return () unless defined $error;
+	&$error("No such file or directory") if ref($error) eq 'CODE';
+	warn "Warning: No such file or directory\n" if $error eq 'warn';
+	return ();
+    } else {
+        # parse failed, check if the dosftp parse understands it
+        return(File::Listing::dosftp->line($_,$tz,$error));
+    }
+
+}
+
+package File::Listing::dosftp;
+
+use HTTP::Date qw(str2time);
+
+# A place to remember current directory from last line parsed.
+use vars qw($curdir);
+no strict qw(vars);
+
+@ISA = qw(File::Listing);
+
+sub init
+{
+    $curdir = '';
+}
+
+sub line
+{
+    shift; # package name
+    local($_) = shift;
+    my($tz, $error) = @_;
+
+    s/\015//g;
+
+    my ($kind, $size, $date, $name);
+
+    # 02-05-96  10:48AM                 1415 src.slf
+    # 09-10-96  09:18AM       <DIR>          sl_util
+    if (($date,$size_or_dir,$name) =
+        /^(\d\d-\d\d-\d\d\s+\d\d:\d\d\wM)         # Date and time info
+         \s+                                      # Some space
+         (<\w{3}>|\d+)                            # Dir or Size
+         \s+                                      # Some more space
+         (.+)$                                    # File name
+        /x )
+    {
+	return if $name eq '.' || $name eq '..';
+	$name = "$curdir/$name" if length $curdir;
+	my $type = '?';
+	if ($size_or_dir eq '<DIR>') {
+	    $type = "d";
+            $size = ""; # directories have no size in the pc listing
+        } else {
+	    $type = 'f';
+            $size = $size_or_dir;
+	}
+	return [$name, $type, $size, str2time($date, $tz),
+              File::Listing::file_mode($kind)];
+
+    } else {
+	return () unless defined $error;
+	&$error($_) if ref($error) eq 'CODE';
+	warn "Can't parse: $_\n" if $error eq 'warn';
+	return ();
+    }
+
+}
+
+package File::Listing::vms;
+@File::Listing::unix::ISA = qw(File::Listing);
+
+package File::Listing::netware;
+@File::Listing::unix::ISA = qw(File::Listing);
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Cookies.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Cookies.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Cookies.pm	(revision 16561)
@@ -0,0 +1,725 @@
+package HTTP::Cookies;
+
+# Based on draft-ietf-http-state-man-mec-08.txt and
+# http://www.netscape.com/newsref/std/cookie_spec.html
+
+use strict;
+use HTTP::Date qw(str2time time2str);
+use HTTP::Headers::Util qw(split_header_words join_header_words);
+use LWP::Debug ();
+
+use vars qw($VERSION);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+=head1 NAME
+
+HTTP::Cookies - Cookie storage and management
+
+=head1 SYNOPSIS
+
+ use HTTP::Cookies;
+ $cookie_jar = HTTP::Cookies->new;
+
+ $cookie_jar->add_cookie_header($request);
+ $cookie_jar->extract_cookies($response);
+
+=head1 DESCRIPTION
+
+Cookies are a general mechanism which server side connections can use
+to both store and retrieve information on the client side of the
+connection.  For more information about cookies referrer to
+<URL:http://www.netscape.com/newsref/std/cookie_spec.html> and
+<URL:http://www.cookiecentral.com/>.  This module also implements the
+new style cookies as described in I<draft-ietf-http-state-man-mec-08.txt>.
+The two variants of cookies is supposed to be able to coexist happily.
+
+Instances of the class I<HTTP::Cookies> are able to store a collection
+of Set-Cookie2: and Set-Cookie:-headers and is able to use this
+information to initialize Cookie-headers in I<HTTP::Request> objects.
+The state of the I<HTTP::Cookies> can be saved and restored from
+files.
+
+=head1 METHODS
+
+The following methods are provided:
+
+=over 4
+
+=cut
+
+# A HTTP::Cookies object is a hash.  The main attribute is the
+# COOKIES 3 level hash:  $self->{COOKIES}{$domain}{$path}{$key}.
+
+
+=item $cookie_jar = HTTP::Cookies->new;
+
+The constructor.  Takes hash style parameters.  The following
+parameters are recognized:
+
+  file:            name of the file to restore and save cookies to
+  autosave:        should we save during destruction (bool)
+  ignore_discard:  save even cookies that are requested to be discarded (bool)
+
+Future parameters might include (not yet implemented):
+
+  max_cookies               300
+  max_cookies_per_domain    20
+  max_cookie_size           4096
+
+  no_cookies   list of domain names that we never return cookies to
+
+=cut
+
+sub new
+{
+    my $class = shift;
+    my $self = bless {
+	COOKIES => {},
+    }, $class;
+    my %cnf = @_;
+    for (keys %cnf) {
+	$self->{lc($_)} = $cnf{$_};
+    }
+    $self->load;
+    $self;
+}
+
+
+=item $cookie_jar->add_cookie_header($request);
+
+The add_cookie_header() method will set the appropriate Cookie:-header
+for the I<HTTP::Request> object given as argument.  The $request must
+have a valid url() attribute before this method is called.
+
+=cut
+
+sub add_cookie_header
+{
+    my $self = shift;
+    my $request = shift || return;
+    my $url = $request->url;
+    my $domain = $url->host;
+    $domain = "$domain.local" unless $domain =~ /\./;
+    my $secure_request = ($url->scheme eq "https");
+    my $req_path = $url->epath;
+    my $req_port = $url->port;
+    my $now = time();
+    $self->_normalize_path($req_path) if $req_path =~ /%/;
+
+    my @cval;    # cookie values for the "Cookie" header
+    my $set_ver;
+
+    while (($domain =~ tr/././) >= 2 || # must be at least 2 dots
+           $domain =~ /\.local$/)
+    {
+
+        LWP::Debug::debug("Checking $domain for cookies");
+	my $cookies = $self->{COOKIES}{$domain};
+	next unless $cookies;
+
+	# Want to add cookies corresponding to the most specific paths
+	# first (i.e. longest path first)
+	my $path;
+	for $path (sort {length($b) <=> length($a) } keys %$cookies) {
+            LWP::Debug::debug("- checking cookie path=$path");
+	    if (index($req_path, $path) != 0) {
+	        LWP::Debug::debug("  path $path:$req_path does not fit");
+		next;
+	    }
+
+	    my($key,$array);
+	    while (($key,$array) = each %{$cookies->{$path}}) {
+		my($version,$val,$port,$path_spec,$secure,$expires) = @$array;
+	        LWP::Debug::debug(" - checking cookie $key=$val");
+		if ($secure && !$secure_request) {
+		    LWP::Debug::debug("   not a secure requests");
+		    next;
+		}
+		if ($expires && $expires < $now) {
+		    LWP::Debug::debug("   expired");
+		    next;
+		}
+		if ($port) {
+		    my $found;
+		    if ($port =~ s/^_//) {
+			# The correponding Set-Cookie attribute was empty
+			$found++ if $port eq $req_port;
+			$port = "";
+		    } else {
+			my $p;
+			for $p (split(/,/, $port)) {
+			    $found++, last if $p eq $req_port;
+			}
+		    }
+		    unless ($found) {
+		        LWP::Debug::debug("   port $port:$req_port does not fit");
+			next;
+		    }
+		}
+	        LWP::Debug::debug("   it's a match");
+
+		# set version number of cookie header.
+	        # XXX: What should it be if multiple matching
+                #      Set-Cookie headers have different versions themselves
+		if (!$set_ver++) {
+		    if ($version >= 1) {
+			push(@cval, "\$Version=$version");
+		    } else {
+			$request->header(Cookie2 => "\$Version=1");
+		    }
+		}
+
+		# do we need to quote the value
+		if ($val =~ /\W/ && $version) {
+		    $val =~ s/([\\\"])/\\$1/g;
+		    $val = qq("$val");
+		}
+
+		# and finally remember this cookie
+		push(@cval, "$key=$val");
+		if ($version >= 1) {
+		    push(@cval, qq(\$Path="$path"))     if $path_spec;
+		    push(@cval, qq(\$Domain="$domain")) if $domain =~ /^\./;
+		    if (defined $port) {
+			my $p = '$Port';
+			$p .= qq(="$port") if length $port;
+			push(@cval, $p);
+		    }
+		}
+
+	    }
+        }
+
+    } continue {
+	# Try with a more general domain:  www.sol.no ==> .sol.no
+	$domain =~ s/^\.?[^.]*//;
+    }
+
+    $request->header(Cookie => join("; ", @cval)) if @cval;
+
+    $request;
+}
+
+
+=item $cookie_jar->extract_cookies($response);
+
+The extract_cookies() method will look for Set-Cookie: and
+Set-Cookie2:-headers in the I<HTTP::Response> object passed as
+argument.  If some of these headers are found they are used to update
+the state of the $cookie_jar.
+
+=cut
+
+sub extract_cookies
+{
+    my $self = shift;
+    my $response = shift || return;
+    my @set = split_header_words($response->_header("Set-Cookie2"));
+    my $netscape_cookies;
+    unless (@set) {
+	@set = $response->_header("Set-Cookie");
+	return $response unless @set;
+	$netscape_cookies++;
+    }
+
+    my $url = $response->request->url;
+    my $req_host = $url->host;
+    $req_host = "$req_host.local" unless $req_host =~ /\./;
+    my $req_port = $url->port;
+    my $req_path = $url->epath;
+    $self->_normalize_path($req_path) if $req_path =~ /%/;
+    
+    if ($netscape_cookies) {
+	# The old Netscape cookie format for Set-Cookie
+        # http://www.netscape.com/newsref/std/cookie_spec.html
+	# can for instance contain an unquoted "," in the expires
+	# field, so we have to use this ad-hoc parser.
+	my $now = time();
+	my @old = @set;
+	@set = ();
+	my $set;
+	for $set (@old) {
+	    my @cur;
+	    my $param;
+	    my $expires;
+	    for $param (split(/\s*;\s*/, $set)) {
+		my($k,$v) = split(/\s*=\s*/, $param, 2);
+		#print "$k => $v\n";
+		my $lc = lc($k);
+		if ($lc eq "expires") {
+		    push(@cur, "Max-Age" => str2time($v) - $now);
+		    $expires++;
+		} else {
+		    push(@cur, $k => $v);
+		}
+	    }
+	    push(@cur, "Port" => $req_port);
+	    push(@cur, "Discard" => undef) unless $expires;
+	    push(@cur, "Version" => 0);
+	    push(@set, \@cur);
+	}
+    }
+
+  SET_COOKIE:
+    for my $set (@set) {
+	next unless @$set >= 2;
+
+	my $key = shift @$set;
+	my $val = shift @$set;
+
+        LWP::Debug::debug("Set cookie $key => $val");
+
+	my %hash;
+	while (@$set) {
+	    my $k = shift @$set;
+	    my $v = shift @$set;
+	    my $lc = lc($k);
+	    # don't loose case distinction for unknown fields
+	    $k = $lc if $lc =~ /^(?:discard|domain|max-age|
+                                    path|port|secure|version)$/x;
+	    if ($k eq "discard" || $k eq "secure") {
+		$v = 1 unless defined $v;
+	    }
+	    next if exists $hash{$k};  # only first value is signigicant
+	    $hash{$k} = $v;
+	};
+
+	my %orig_hash = %hash;
+	my $version   = delete $hash{version};
+	$version = 1 unless defined($version);
+	my $discard   = delete $hash{discard};
+	my $secure    = delete $hash{secure};
+	my $maxage    = delete $hash{'max-age'};
+
+	# Check domain
+	my $domain  = delete $hash{domain};
+	if (defined($domain) && $domain ne $req_host) {
+	    if ($domain !~ /\./ && $domain ne "local") {
+	        LWP::Debug::debug("Domain $domain contains no dot");
+		next SET_COOKIE;
+	    }
+	    $domain = ".$domain" unless $domain =~ /^\./;
+	    if ($domain =~ /\.\d+$/) {
+	        LWP::Debug::debug("IP-address $domain illeagal as domain");
+		next SET_COOKIE;
+	    }
+	    my $len = length($domain);
+	    unless (substr($req_host, -$len) eq $domain) {
+	        LWP::Debug::debug("Domain $domain does not match host $req_host");
+		next SET_COOKIE;
+	    }
+	    my $hostpre = substr($req_host, 0, length($req_host) - $len);
+	    if ($hostpre =~ /\./ && !$netscape_cookies) {
+	        LWP::Debug::debug("Host prefix contain a dot: $hostpre => $domain");
+		next SET_COOKIE;
+	    }
+	} else {
+	    $domain = $req_host;
+	}
+
+	my $path = delete $hash{path};
+	my $path_spec;
+	if (defined $path) {
+	    $path_spec++;
+	    $self->_normalize_path($path) if $path =~ /%/;
+	    if (!$netscape_cookies &&
+                substr($req_path, 0, length($path)) ne $path) {
+	        LWP::Debug::debug("Path $path is not a prefix of $req_path");
+		next SET_COOKIE;
+	    }
+	} else {
+	    $path = $req_path;
+	    $path =~ s,/[^/]*$,,;
+	    $path = "/" unless length($path);
+	}
+
+	my $port;
+	if (exists $hash{port}) {
+	    $port = delete $hash{port};
+	    if (defined $port) {
+		$port =~ s/\s+//g;
+		my $found;
+		for my $p (split(/,/, $port)) {
+		    unless ($p =~ /^\d+$/) {
+		      LWP::Debug::debug("Bad port $port (not numeric)");
+			next SET_COOKIE;
+		    }
+		    $found++ if $p eq $req_port;
+		}
+		unless ($found) {
+		    LWP::Debug::debug("Request port ($req_port) not found in $port");
+		    next SET_COOKIE;
+		}
+	    } else {
+		$port = "_$req_port";
+	    }
+	}
+	$self->set_cookie($version,$key,$val,$path,$domain,$port,$path_spec,$secure,$maxage,$discard, \%hash)
+	    if $self->set_cookie_ok(\%orig_hash);
+    }
+
+    $response;
+}
+
+sub set_cookie_ok { 1 };
+
+=item $cookie_jar->set_cookie($version, $key, $val, $path, $domain, $port, $path_spec, $secure, $maxage, $discard, \%rest)
+
+The set_cookie() method updates the state of the $cookie_jar.  The
+$key, $val, $domain, $port and $path arguments are strings.  The
+$path_spec, $secure, $discard arguments are boolean values. The $maxage
+value is a number indicating number of seconds that this cookie will
+live.  A value <= 0 will delete this cookie.  The %rest are a place
+for various other attributes like "Comment" and "CommentURL".
+
+=cut
+
+sub set_cookie
+{
+    my $self = shift;
+    my($version,
+       $key, $val, $path, $domain, $port,
+       $path_spec, $secure, $maxage, $discard, $rest) = @_;
+
+    # there must always be at least 2 dots in a domain
+    return $self if ($domain =~ tr/././) < 2 &&
+                     $domain !~ /\.local$/;
+
+    # path and key can not be empty (key can't start with '$')
+    return $self if !defined($path) || $path !~ m,^/, ||
+	            !defined($key)  || $key  !~ m,[^\$],;
+
+    # ensure legal port
+    if (defined $port) {
+	return $self unless $port =~ /^_?\d+(?:,\d+)*$/;
+    }
+
+    my $expires;
+    if (defined $maxage) {
+	if ($maxage <= 0) {
+	    delete $self->{COOKIES}{$domain}{$path}{$key};
+	    return $self;
+	}
+	$expires = time() + $maxage;
+    }
+    $version = 0 unless defined $version;
+
+    my @array = ($version, $val,$port,
+		 $path_spec,
+		 $secure, $expires, $discard);
+    push(@array, {%$rest}) if defined($rest) && %$rest;
+    # trim off undefined values at end
+    pop(@array) while !defined $array[-1];
+
+    $self->{COOKIES}{$domain}{$path}{$key} = \@array;
+    $self;
+}
+
+=item $cookie_jar->save( [$file] );
+
+Calling this method file save the state of the $cookie_jar to a file.
+The state can then be restored later using the load() method.  If a
+filename is not specified we will use the name specified during
+construction.  If the attribute I<ignore_discared> is set, then we
+will even save cookies that are marked to be discarded.
+
+The default is to save a sequence of "Set-Cookie3" lines.  The
+"Set-Cookie3" is a proprietary LWP format, not known to be compatible
+with any other browser.  The I<HTTP::Cookies::Netscape> sub-class can
+be used to save in a format compatible with Netscape.
+
+=cut
+
+sub save
+{
+    my $self = shift;
+    my $file = shift || $self->{'file'} || return;
+    local(*FILE);
+    open(FILE, ">$file") or die "Can't open $file: $!";
+    print FILE "#LWP-Cookies-1.0\n";
+    print FILE $self->as_string(!$self->{ignore_discard});
+    close(FILE);
+    1;
+}
+
+=item $cookie_jar->load( [$file] );
+
+This method will read the cookies from the file and add them to the
+$cookie_jar.  The file must be in the format written by the save()
+method.
+
+=cut
+
+sub load
+{
+    my $self = shift;
+    my $file = shift || $self->{'file'} || return;
+    local(*FILE, $_);
+    open(FILE, $file) or return;
+    my $magic = <FILE>;
+    unless ($magic =~ /^\#LWP-Cookies-(\d+\.\d+)/) {
+	warn "$file does not seem to contain cookies";
+	return;
+    }
+    while (<FILE>) {
+	next unless s/^Set-Cookie3:\s*//;
+	chomp;
+	my $cookie;
+	for $cookie (split_header_words($_)) {
+	    my($key,$val) = splice(@$cookie, 0, 2);
+	    my %hash;
+	    while (@$cookie) {
+		my $k = shift @$cookie;
+		my $v = shift @$cookie;
+		$hash{$k} = $v;
+	    }
+	    my $version   = delete $hash{version};
+	    my $path      = delete $hash{path};
+	    my $domain    = delete $hash{domain};
+	    my $port      = delete $hash{port};
+	    my $expires   = str2time(delete $hash{expires});
+
+	    my $path_spec = exists $hash{path_spec}; delete $hash{path_spec};
+	    my $secure    = exists $hash{secure};    delete $hash{secure};
+	    my $discard   = exists $hash{discard};   delete $hash{discard};
+
+	    my @array =	($version,$val,$port,
+			 $path_spec,$secure,$expires,$discard);
+	    push(@array, \%hash) if %hash;
+	    $self->{COOKIES}{$domain}{$path}{$key} = \@array;
+	}
+    }
+    close(FILE);
+    1;
+}
+
+=item $cookie_jar->revert;
+
+Will revert to the state of last save.
+
+=cut
+
+sub revert
+{
+    my $self = shift;
+    $self->clear->load;
+    $self;
+}
+
+=item $cookie_jar->clear( [$domain, [$path, [$key] ] ]);
+
+Invoking this method without arguments will empty the whole
+$cookie_jar.  If given a single argument only cookies belonging to
+that domain will be removed.  If given two arguments, cookies
+belonging to the specified path within that domain is removed.  If
+given three arguments, then the cookie with the specified key, path
+and domain is removed.
+
+=cut
+
+sub clear
+{
+    my $self = shift;
+    if (@_ == 0) {
+	$self->{COOKIES} = {};
+    } elsif (@_ == 1) {
+	delete $self->{COOKIES}{$_[0]};
+    } elsif (@_ == 2) {
+	delete $self->{COOKIES}{$_[0]}{$_[1]};
+    } elsif (@_ == 3) {
+	delete $self->{COOKIES}{$_[0]}{$_[1]}{$_[2]};
+    } else {
+	require Carp;
+        Carp::carp('Usage: $c->clear([domain [,path [,key]]])');
+    }
+    $self;
+}
+
+sub DESTROY
+{
+    my $self = shift;
+    $self->save if $self->{'autosave'};
+}
+
+
+=item $cookie_jar->scan( \&callback );
+
+The argument is a subroutine that will be invoked for each cookie
+stored within the $cookie_jar.  The subroutine will be invoked with
+the following arguments:
+
+  0  version
+  1  key
+  2  val
+  3  path
+  4  domain
+  5  port
+  6  path_spec
+  7  secure
+  8  expires
+  9  discard
+ 10  hash
+
+=cut
+
+sub scan
+{
+    my($self, $cb) = @_;
+    my($domain,$path,$key);
+    for $domain (sort keys %{$self->{COOKIES}}) {
+	for $path (sort keys %{$self->{COOKIES}{$domain}}) {
+	    for $key (sort keys %{$self->{COOKIES}{$domain}{$path}}) {
+		my($version,$val,$port,$path_spec,
+		   $secure,$expires,$discard,$rest) =
+		       @{$self->{COOKIES}{$domain}{$path}{$key}};
+		$rest = {} unless defined($rest);
+		&$cb($version,$key,$val,$path,$domain,$port,
+		     $path_spec,$secure,$expires,$discard,$rest);
+	    }
+	}
+    }
+}
+
+=item $cookie_jar->as_string( [$skip_discard] );
+
+The as_string() method will return the state of the $cookie_jar
+represented as a sequence of "Set-Cookie3" header lines separated by
+"\n".  If given a argument that is TRUE, it will not return lines for
+cookies with the I<Discard> attribute.
+
+=cut
+
+sub as_string
+{
+    my($self, $skip_discard) = @_;
+    my @res;
+    $self->scan(sub {
+	my($version,$key,$val,$path,$domain,$port,
+	   $path_spec,$secure,$expires,$discard,$rest) = @_;
+	return if $discard && $skip_discard;
+	my @h = ($key, $val);
+	push(@h, "path", $path);
+	push(@h, "domain" => $domain);
+	push(@h, "port" => $port) if defined $port;
+	push(@h, "path_spec" => undef) if $path_spec;
+	push(@h, "secure" => undef) if $secure;
+	push(@h, "expires" => HTTP::Date::time2isoz($expires)) if $expires;
+	push(@h, "discard" => undef) if $discard;
+	my $k;
+	for $k (sort keys %$rest) {
+	    push(@h, $k, $rest->{$k});
+	}
+	push(@h, "version" => $version);
+	push(@res, "Set-Cookie3: " . join_header_words(\@h));
+    });
+    join("\n", @res, "");
+}
+
+
+sub _normalize_path  # so that plain string compare can be used
+{
+    shift;  # $self
+    my $x;
+    $_[0] =~ s/%([0-9a-fA-F][0-9a-fA-F])/
+	         $x = uc($1);
+                 $x eq "2F" || $x eq "25" ? "%$x" :
+                                            pack("c", hex($x));
+              /eg;
+    $_[0] =~ s/([\0-\x20\x7f-\xff])/sprintf("%%%02X",ord($1))/eg;
+}
+
+
+
+=back
+
+=head1 SUB CLASSES
+
+We also provide a subclass called I<HTTP::Cookies::Netscape> which make
+cookie loading and saving compatible with Netscape cookie files.  You
+should be able to have LWP share Netscape's cookies by constructing
+your $cookie_jar like this:
+
+ $cookie_jar = HTTP::Cookies::Netscape->new(
+                   File     => "$ENV{HOME}/.netscape/cookies",
+                   AutoSave => 1,
+               );
+
+Please note that the Netscape cookie file format is not able to store
+all the information available in the Set-Cookie2 headers, so you will
+probably loose some information if you save using this format.
+
+=cut
+
+package HTTP::Cookies::Netscape;
+
+use vars qw(@ISA);
+@ISA=qw(HTTP::Cookies);
+
+sub load
+{
+    my($self, $file) = @_;
+    $file ||= $self->{'file'} || return;
+    local(*FILE, $_);
+    my @cookies;
+    open(FILE, $file) || return;
+    my $magic = <FILE>;
+    unless ($magic =~ /^\# Netscape HTTP Cookie File/) {
+	warn "$file does not look like a netscape cookies file" if $^W;
+	close(FILE);
+	return;
+    }
+    my $now = time();
+    while (<FILE>) {
+	next if /^\s*\#/;
+	next if /^\s*$/;
+	chomp;
+	my($domain,$bool1,$path,$secure, $expires,$key,$val) = split(/\t/, $_);
+	$secure = ($secure eq "TRUE");
+	$self->set_cookie(undef,$key,$val,$path,$domain,undef,
+			  0,$secure,$expires-$now, 0);
+    }
+    close(FILE);
+    1;
+}
+
+sub save
+{
+    my($self, $file) = @_;
+    $file ||= $self->{'file'} || return;
+    local(*FILE, $_);
+    open(FILE, ">$file") || return;
+
+    print FILE <<EOT;
+# Netscape HTTP Cookie File
+# http://www.netscape.com/newsref/std/cookie_spec.html
+# This is a generated file!  Do not edit.
+
+EOT
+
+    my $now = time;
+    $self->scan(sub {
+	my($version,$key,$val,$path,$domain,$port,
+	   $path_spec,$secure,$expires,$discard,$rest) = @_;
+	return if $discard && !$self->{ignore_discard};
+	$expires ||= 0;
+	return if $now > $expires;
+	$secure = $secure ? "TRUE" : "FALSE";
+	my $bool = $domain =~ /^\./ ? "TRUE" : "FALSE";
+	print FILE join("\t", $domain, $bool, $path, $secure, $expires, $key, $val), "\n";
+    });
+    close(FILE);
+    1;
+}
+
+1;
+
+__END__
+
+=head1 COPYRIGHT
+
+Copyright 1997, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Daemon.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Daemon.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Daemon.pm	(revision 16561)
@@ -0,0 +1,805 @@
+# $Id$
+#
+
+use strict;
+
+package HTTP::Daemon;
+
+=head1 NAME
+
+HTTP::Daemon - a simple http server class
+
+=head1 SYNOPSIS
+
+  use HTTP::Daemon;
+  use HTTP::Status;
+
+  my $d = new HTTP::Daemon;
+  print "Please contact me at: <URL:", $d->url, ">\n";
+  while (my $c = $d->accept) {
+      while (my $r = $c->get_request) {
+	  if ($r->method eq 'GET' and $r->url->path eq "/xyzzy") {
+              # remember, this is *not* recommened practice :-)
+	      $c->send_file_response("/etc/passwd");
+	  } else {
+	      $c->send_error(RC_FORBIDDEN)
+	  }
+      }
+      $c->close;
+      undef($c);
+  }
+
+=head1 DESCRIPTION
+
+Instances of the I<HTTP::Daemon> class are HTTP/1.1 servers that
+listens on a socket for incoming requests. The I<HTTP::Daemon> is a
+sub-class of I<IO::Socket::INET>, so you can do socket operations
+directly on it too.
+
+The accept() method will return when a connection from a client is
+available. The returned value will be a reference to a object of the
+I<HTTP::Daemon::ClientConn> class which is another I<IO::Socket::INET>
+subclass. Calling the get_request() method on this object will read
+data from the client and return an I<HTTP::Request> object reference.
+
+This HTTP daemon does not fork(2) for you.  Your application, i.e. the
+user of the I<HTTP::Daemon> is reponsible for forking if that is
+desirable.  Also note that the user is responsible for generating
+responses that conforms to the HTTP/1.1 protocol.  The
+I<HTTP::Daemon::ClientConn> provide some methods that make this easier.
+
+=head1 METHODS
+
+The following is a list of methods that are new (or enhanced) relative
+to the I<IO::Socket::INET> base class.
+
+=over 4
+
+=cut
+
+
+use vars qw($VERSION @ISA $PROTO $DEBUG);
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use IO::Socket ();
+@ISA=qw(IO::Socket::INET);
+
+$PROTO = "HTTP/1.1";
+
+=item $d = new HTTP::Daemon
+
+The object constructor takes the same parameters as the
+I<IO::Socket::INET> constructor.  It can be called without specifying
+any parameters. The daemon will then set up a listen queue of 5
+connections and allocate some random port number.  A server that want
+to bind to some specific address on the standard HTTP port will be
+constructed like this:
+
+  $d = new HTTP::Daemon
+        LocalAddr => 'www.someplace.com',
+        LocalPort => 80;
+
+=cut
+
+sub new
+{
+    my($class, %args) = @_;
+    $args{Listen} ||= 5;
+    $args{Proto}  ||= 'tcp';
+    my $self = $class->SUPER::new(%args);
+    return undef unless $self;
+
+    my $host = $args{LocalAddr};
+    unless ($host) {
+	require Sys::Hostname;
+	$host = Sys::Hostname::hostname();
+    }
+    ${*$self}{'httpd_server_name'} = $host;
+    $self;
+}
+
+
+=item $c = $d->accept([$pkg])
+
+Same as I<IO::Socket::accept> but will return an
+I<HTTP::Daemon::ClientConn> reference by default.  It will return
+undef if you have specified a timeout and no connection is made within
+that time.
+
+=cut
+
+sub accept
+{
+    my $self = shift;
+    my $pkg = shift || "HTTP::Daemon::ClientConn";
+    my $sock = $self->SUPER::accept($pkg);
+    ${*$sock}{'httpd_daemon'} = $self if $sock;
+    $sock;
+}
+
+
+=item $d->url
+
+Returns a URL string that can be used to access the server root.
+
+=cut
+
+sub url
+{
+    my $self = shift;
+    my $url = "http://";
+    $url .= ${*$self}{'httpd_server_name'};
+    my $port = $self->sockport;
+    $url .= ":$port" if $port != 80;
+    $url .= "/";
+    $url;
+}
+
+
+=item $d->product_tokens
+
+Returns the name that this server will use to identify itself.  This
+is the string that is sent with the I<Server> response header.  The
+main reason to have this method is that subclasses can override it if
+they want to use another product name.
+
+=cut
+
+sub product_tokens
+{
+    "libwww-perl-daemon/$HTTP::Daemon::VERSION";
+}
+
+
+package HTTP::Daemon::ClientConn;
+
+use vars qw(@ISA $DEBUG);
+use IO::Socket ();
+@ISA=qw(IO::Socket::INET);
+*DEBUG = \$HTTP::Daemon::DEBUG;
+
+use HTTP::Request  ();
+use HTTP::Response ();
+use HTTP::Status;
+use HTTP::Date qw(time2str);
+use URI::URL qw(url);
+use LWP::MediaTypes qw(guess_media_type);
+use Carp ();
+
+my $CRLF = "\015\012";   # "\r\n" is not portable
+my $HTTP_1_0 = _http_version("HTTP/1.0");
+my $HTTP_1_1 = _http_version("HTTP/1.1");
+
+=back
+
+The I<HTTP::Daemon::ClientConn> is also a I<IO::Socket::INET>
+subclass. Instances of this class are returned by the accept() method
+of the I<HTTP::Daemon>.  The following additional methods are
+provided:
+
+=over 4
+
+=item $c->get_request([$headers_only])
+
+This method will read data from the client and turn it into a
+I<HTTP::Request> object which is then returned.  It returns C<undef>
+if reading of the request fails.  If it fails, then the
+I<HTTP::Daemon::ClientConn> object ($c) should be discarded, and you
+should not call this method again.  The $c->reason method might give
+you some information on why $c->get_request returned C<undef>.
+
+The $c->get_request method support HTTP/1.1 request content bodies,
+including I<chunked> transfer encoding with footer and self delimiting
+I<multipart/*> content types.
+
+The $c->get_request method will normally not return until the whole
+request has been received from the client.  This might not be what you
+want if the request is an upload of a multi-mega-byte file (and with
+chunked transfer encoding HTTP can even support infinite request
+messages - uploading live audio for instance).  If you pass a TRUE
+value as the $headers_only argument, then $c->get_request will return
+immediately after parsing the request headers and you are responsible
+for reading the rest of the request content (and if you are going to
+call $c->get_request again on the same connection you better read the
+correct number of bytes).
+
+=cut
+
+sub get_request
+{
+    my($self, $only_headers) = @_;
+    if (${*$self}{'httpd_nomore'}) {
+        $self->reason("No more requests from this connection");
+	return;
+    }
+
+    $self->reason("");
+    my $buf = ${*$self}{'httpd_rbuf'};
+    $buf = "" unless defined $buf;
+    
+    my $timeout = $ {*$self}{'io_socket_timeout'};
+    my $fdset = "";
+    vec($fdset, $self->fileno, 1) = 1;
+    local($_);
+
+  READ_HEADER:
+    while (1) {
+	# loop until we have the whole header in $buf
+	$buf =~ s/^(?:\015?\012)+//;  # ignore leading blank lines
+	if ($buf =~ /\012/) {  # potential, has at least one line
+	    if ($buf =~ /^\w+[^\012]+HTTP\/\d+\.\d+\015?\012/) {
+		if ($buf =~ /\015?\012\015?\012/) {
+		    last READ_HEADER;  # we have it
+		} elsif (length($buf) > 16*1024) {
+		    $self->send_error(413); # REQUEST_ENTITY_TOO_LARGE
+		    $self->reason("Very long header");
+		    return;
+		}
+	    } else {
+		last READ_HEADER;  # HTTP/0.9 client
+	    }
+	} elsif (length($buf) > 16*1024) {
+	    $self->send_error(414); # REQUEST_URI_TOO_LARGE
+	    $self->reason("Very long first line");
+	    return;
+	}
+	print STDERR "Need more data for complete header\n" if $DEBUG;
+	return unless $self->_need_more($buf, $timeout, $fdset);
+    }
+    if ($buf !~ s/^(\w+)[ \t]+(\S+)(?:[ \t]+(HTTP\/\d+\.\d+))?[^\012]*\012//) {
+	$self->send_error(400);  # BAD_REQUEST
+	$self->reason("Bad request line");
+	return;
+    }
+    my $proto = $3 || "HTTP/0.9";
+    my $r = HTTP::Request->new($1, url($2, $self->daemon->url));
+    $r->protocol($proto);
+    ${*$self}{'httpd_client_proto'} = $proto = _http_version($proto);
+
+    if ($proto >= $HTTP_1_0) {
+	# we expect to find some headers
+	my($key, $val);
+      HEADER:
+	while ($buf =~ s/^([^\012]*)\012//) {
+	    $_ = $1;
+	    s/\015$//;
+	    if (/^([\w\-]+)\s*:\s*(.*)/) {
+		$r->push_header($key, $val) if $key;
+		($key, $val) = ($1, $2);
+	    } elsif (/^\s+(.*)/) {
+		$val .= " $1";
+	    } else {
+		last HEADER;
+	    }
+	}
+	$r->push_header($key, $val) if $key;
+    }
+
+    my $conn = $r->header('Connection');
+    if ($proto >= $HTTP_1_1) {
+	${*$self}{'httpd_nomore'}++ if $conn && lc($conn) =~ /\bclose\b/;
+    } else {
+	${*$self}{'httpd_nomore'}++ unless $conn &&
+                                           lc($conn) =~ /\bkeep-alive\b/;
+    }
+
+    if ($only_headers) {
+	${*$self}{'httpd_rbuf'} = $buf;
+        return $r;
+    }
+
+    # Find out how much content to read
+    my $te  = $r->header('Transfer-Encoding');
+    my $ct  = $r->header('Content-Type');
+    my $len = $r->header('Content-Length');
+
+    if ($te && lc($te) eq 'chunked') {
+	# Handle chunked transfer encoding
+	my $body = "";
+      CHUNK:
+	while (1) {
+	    print STDERR "Chunked\n" if $DEBUG;
+	    if ($buf =~ s/^([^\012]*)\012//) {
+		my $chunk_head = $1;
+		unless ($chunk_head =~ /^([0-9A-Fa-f]+)/) {
+		    $self->send_error(400);
+		    $self->reason("Bad chunk header $chunk_head");
+		    return;
+		}
+		my $size = hex($1);
+		last CHUNK if $size == 0;
+
+		my $missing = $size - length($buf) + 2; # 2=CRLF at chunk end
+		# must read until we have a complete chunk
+		while ($missing > 0) {
+		    print STDERR "Need $missing more bytes\n" if $DEBUG;
+		    my $n = $self->_need_more($buf, $timeout, $fdset);
+		    return unless $n;
+		    $missing -= $n;
+		}
+		$body .= substr($buf, 0, $size);
+		substr($buf, 0, $size+2) = '';
+
+	    } else {
+		# need more data in order to have a complete chunk header
+		return unless $self->_need_more($buf, $timeout, $fdset);
+	    }
+	}
+	$r->content($body);
+
+	# pretend it was a normal entity body
+	$r->remove_header('Transfer-Encoding');
+	$r->header('Content-Length', length($body));
+
+	my($key, $val);
+      FOOTER:
+	while (1) {
+	    if ($buf !~ /\012/) {
+		# need at least one line to look at
+		return unless $self->_need_more($buf, $timeout, $fdset);
+	    } else {
+		$buf =~ s/^([^\012]*)\012//;
+		$_ = $1;
+		s/\015$//;
+		if (/^([\w\-]+)\s*:\s*(.*)/) {
+		    $r->push_header($key, $val) if $key;
+		    ($key, $val) = ($1, $2);
+		} elsif (/^\s+(.*)/) {
+		    $val .= " $1";
+		} elsif (!length) {
+		    last FOOTER;
+		} else {
+		    $self->reason("Bad footer syntax");
+		    return;
+		}
+	    }
+	}
+	$r->push_header($key, $val) if $key;
+
+    } elsif ($te) {
+	$self->send_error(501); 	# Unknown transfer encoding
+	$self->reason("Unknown transfer encoding '$te'");
+	return;
+
+    } elsif ($ct && lc($ct) =~ m/^multipart\/\w+\s*;.*boundary\s*=\s*(\w+)/) {
+	# Handle multipart content type
+	my $boundary = "$CRLF--$1--$CRLF";
+	my $index;
+	while (1) {
+	    $index = index($buf, $boundary);
+	    last if $index >= 0;
+	    # end marker not yet found
+	    return unless $self->_need_more($buf, $timeout, $fdset);
+	}
+	$index += length($boundary);
+	$r->content(substr($buf, 0, $index));
+	substr($buf, 0, $index) = '';
+
+    } elsif ($len) {
+	# Plain body specified by "Content-Length"
+	my $missing = $len - length($buf);
+	while ($missing > 0) {
+	    print "Need $missing more bytes of content\n" if $DEBUG;
+	    my $n = $self->_need_more($buf, $timeout, $fdset);
+	    return unless $n;
+	    $missing -= $n;
+	}
+	if (length($buf) > $len) {
+	    $r->content(substr($buf,0,$len));
+	    substr($buf, 0, $len) = '';
+	} else {
+	    $r->content($buf);
+	    $buf='';
+	}
+    }
+    ${*$self}{'httpd_rbuf'} = $buf;
+
+    $r;
+}
+
+sub _need_more
+{
+    my $self = shift;
+    #my($buf,$timeout,$fdset) = @_;
+    if ($_[1]) {
+	my($timeout, $fdset) = @_[1,2];
+	print STDERR "select(,,,$timeout)\n" if $DEBUG;
+	my $n = select($fdset,undef,undef,$timeout);
+	unless ($n) {
+	    $self->reason(defined($n) ? "Timeout" : "select: $!");
+	    return;
+	}
+    }
+    print STDERR "sysread()\n" if $DEBUG;
+    my $n = sysread($self, $_[0], 2048, length($_[0]));
+    $self->reason(defined($n) ? "Client closed" : "sysread: $!") unless $n;
+    $n;
+}
+
+=item $c->read_buffer([$new_value])
+
+Bytes read by $c->get_request, but not used are placed in the I<read
+buffer>.  The next time $c->get_request is called it will consume the
+bytes in this buffer before reading more data from the network
+connection itself.  The read buffer is invalid after $c->get_request
+has returned an undefined value.
+
+If you handle the reading of the request content yourself you need to
+empty this buffer before you read more and you need to place
+unconsumed bytes here.  You also need this buffer if you implement
+services like I<101 Switching Protocols>.
+
+This method always return the old buffer content and can optionally
+update the buffer content if you pass it an argument.
+
+=cut
+
+sub read_buffer
+{
+    my $self = shift;
+    my $old = ${*$self}{'httpd_rbuf'};
+    if (@_) {
+	${*$self}{'httpd_rbuf'} = shift;
+    }
+    $old;
+}
+
+
+=item $c->reason
+
+When $c->get_request returns C<undef> you can obtain a short string
+describing why it happened by calling $c->reason.
+
+=cut
+
+sub reason
+{
+    my $self = shift;
+    my $old = ${*$self}{'httpd_reason'};
+    if (@_) {
+        ${*$self}{'httpd_reason'} = shift;
+    }
+    $old;
+}
+
+
+=item $c->proto_ge($proto)
+
+Returns TRUE if the client announced a protocol with version number
+greater or equal to the given argument.  The $proto argument can be a
+string like "HTTP/1.1" or just "1.1".
+
+=cut
+
+sub proto_ge
+{
+    my $self = shift;
+    ${*$self}{'httpd_client_proto'} >= _http_version(shift);
+}
+
+sub _http_version
+{
+    local($_) = shift;
+    return 0 unless m,^(?:HTTP/)?(\d+)\.(\d+)$,i;
+    $1 * 1000 + $2;
+}
+
+=item $c->antique_client
+
+Returns TRUE if the client speaks the HTTP/0.9 protocol.  No status
+code and no headers should be returned to such a client.  This should
+be the same as !$c->proto_ge("HTTP/1.0").
+
+=cut
+
+sub antique_client
+{
+    my $self = shift;
+    ${*$self}{'httpd_client_proto'} < $HTTP_1_0;
+}
+
+
+=item $c->force_last_request
+
+Make sure that $c->get_request will not try to read more requests off
+this connection.  If you generate a response that is not self
+delimiting, then you should signal this fact by calling this method.
+
+This attribute is turned on automatically if the client announce
+protocol HTTP/1.0 or worse and does not include a "Connection:
+Keep-Alive" header.  It is also turned on automatically when HTTP/1.1
+or better clients send the "Connection: close" request header.
+
+=cut
+
+sub force_last_request
+{
+    my $self = shift;
+    ${*$self}{'httpd_nomore'}++;
+}
+
+
+=item $c->send_status_line( [$code, [$mess, [$proto]]] )
+
+Sends the status line back to the client.  If $code is omitted 200 is
+assumed.  If $mess is omitted, then a message corresponding to $code
+is inserted.  If $proto is missing the content of the
+$HTTP::Daemon::PROTO variable is used.
+
+=cut
+
+sub send_status_line
+{
+    my($self, $status, $message, $proto) = @_;
+    return if $self->antique_client;
+    $status  ||= RC_OK;
+    $message ||= status_message($status) || "";
+    $proto   ||= $HTTP::Daemon::PROTO || "HTTP/1.1";
+    print $self "$proto $status $message$CRLF";
+}
+
+=item $c->send_crlf
+
+Send the CRLF sequence to the client.
+
+=cut
+
+
+sub send_crlf
+{
+    my $self = shift;
+    print $self $CRLF;
+}
+
+
+=item $c->send_basic_header( [$code, [$mess, [$proto]]] )
+
+Sends the status line and the "Date:" and "Server:" headers back to
+the client.  This header is assumed to be continued and does not end
+with an empty CRLF line.
+
+=cut
+
+sub send_basic_header
+{
+    my $self = shift;
+    return if $self->antique_client;
+    $self->send_status_line(@_);
+    print $self "Date: ", time2str(time), $CRLF;
+    my $product = $self->daemon->product_tokens;
+    print $self "Server: $product$CRLF" if $product;
+}
+
+
+=item $c->send_response( [$res] )
+
+Takes a I<HTTP::Response> object as parameter and write it back to the
+client as the response.  We try hard to make sure that the response is
+self delimiting so that the connection can stay persistent for further
+request/response exchanges.
+
+The content attribute of the I<HTTP::Response> object can be a normal
+string or a subroutine reference.  If it is a subroutine, then
+whatever this callback routine returns will be written back to the
+client as the response content.  The routine will be called until it
+return an undefined or empty value.  If the client is HTTP/1.1 aware
+then we will use the chunked transfer encoding for the response.
+
+=cut
+
+sub send_response
+{
+    my $self = shift;
+    my $res = shift;
+    if (!ref $res) {
+	$res ||= RC_OK;
+	$res = HTTP::Response->new($res, @_);
+    }
+    my $content = $res->content;
+    my $chunked;
+    unless ($self->antique_client) {
+	my $code = $res->code;
+	$self->send_basic_header($code, $res->message, $res->protocol);
+	if ($code =~ /^(1\d\d|[23]04)$/) {
+	    # make sure content is empty
+	    $res->remove_header("Content-Length");
+	    $content = "";
+	} elsif ($res->request && $res->request->method eq "HEAD") {
+	    # probably OK
+	} elsif (ref($content) eq "CODE") {
+	    if ($self->proto_ge("HTTP/1.1")) {
+		$res->push_header("Transfer-Encoding" => "chunked");
+		$chunked++;
+	    } else {
+		$self->force_last_request;
+	    }
+	} elsif (length($content)) {
+	    $res->header("Content-Length" => length($content));
+	} else {
+	    $self->force_last_request;
+	}
+	print $self $res->headers_as_string($CRLF);
+	print $self $CRLF;  # separates headers and content
+    }
+    if (ref($content) eq "CODE") {
+	while (1) {
+	    my $chunk = &$content();
+	    last unless defined($chunk) && length($chunk);
+	    if ($chunked) {
+		printf $self "%x%s%s%s", length($chunk), $CRLF, $chunk, $CRLF;
+	    } else {
+		print $self $chunk;
+	    }
+	}
+	print $self "0$CRLF$CRLF" if $chunked;  # no trailers either
+    } elsif (length $content) {
+	print $self $content;
+    }
+}
+
+
+=item $c->send_redirect( $loc, [$code, [$entity_body]] )
+
+Sends a redirect response back to the client.  The location ($loc) can
+be an absolute or a relative URL. The $code must be one the redirect
+status codes, and it defaults to "301 Moved Permanently"
+
+=cut
+
+sub send_redirect
+{
+    my($self, $loc, $status, $content) = @_;
+    $status ||= RC_MOVED_PERMANENTLY;
+    Carp::croak("Status '$status' is not redirect") unless is_redirect($status);
+    $self->send_basic_header($status);
+    $loc = url($loc, $self->daemon->url) unless ref($loc);
+    $loc = $loc->abs;
+    print $self "Location: $loc$CRLF";
+    if ($content) {
+	my $ct = $content =~ /^\s*</ ? "text/html" : "text/plain";
+	print $self "Content-Type: $ct$CRLF";
+    }
+    print $self $CRLF;
+    print $self $content if $content;
+    $self->force_last_request;  # no use keeping the connection open
+}
+
+
+=item $c->send_error( [$code, [$error_message]] )
+
+Send an error response back to the client.  If the $code is missing a
+"Bad Request" error is reported.  The $error_message is a string that
+is incorporated in the body of the HTML entity body.
+
+=cut
+
+sub send_error
+{
+    my($self, $status, $error) = @_;
+    $status ||= RC_BAD_REQUEST;
+    Carp::croak("Status '$status' is not an error") unless is_error($status);
+    my $mess = status_message($status);
+    $error  ||= "";
+    $mess = <<EOT;
+<title>$status $mess</title>
+<h1>$status $mess</h1>
+$error
+EOT
+    unless ($self->antique_client) {
+        $self->send_basic_header($status);
+        print $self "Content-Type: text/html$CRLF";
+	print $self "Content-Length: " . length($mess) . $CRLF;
+        print $self $CRLF;
+    }
+    print $self $mess;
+    $status;
+}
+
+
+=item $c->send_file_response($filename)
+
+Send back a response with the specified $filename as content.  If the
+file happen to be a directory we will try to generate an HTML index
+of it.
+
+=cut
+
+sub send_file_response
+{
+    my($self, $file) = @_;
+    if (-d $file) {
+	$self->send_dir($file);
+    } elsif (-f _) {
+	# plain file
+	local(*F);
+	sysopen(F, $file, 0) or 
+	  return $self->send_error(RC_FORBIDDEN);
+	my($ct,$ce) = guess_media_type($file);
+	my($size,$mtime) = (stat _)[7,9];
+	unless ($self->antique_client) {
+	    $self->send_basic_header;
+	    print $self "Content-Type: $ct$CRLF";
+	    print $self "Content-Encoding: $ce$CRLF" if $ce;
+	    print $self "Content-Length: $size$CRLF" if $size;
+	    print $self "Last-Modified: ", time2str($mtime), "$CRLF" if $mtime;
+	    print $self $CRLF;
+	}
+	$self->send_file(\*F);
+	return RC_OK;
+    } else {
+	$self->send_error(RC_NOT_FOUND);
+    }
+}
+
+
+sub send_dir
+{
+    my($self, $dir) = @_;
+    $self->send_error(RC_NOT_FOUND) unless -d $dir;
+    $self->send_error(RC_NOT_IMPLEMENTED);
+}
+
+
+=item $c->send_file($fd);
+
+Copies the file back to the client.  The file can be a string (which
+will be interpreted as a filename) or a reference to an I<IO::Handle>
+or glob.
+
+=cut
+
+sub send_file
+{
+    my($self, $file) = @_;
+    my $opened = 0;
+    if (!ref($file)) {
+	local(*F);
+	open(F, $file) || return undef;
+	binmode(F);
+	$file = \*F;
+	$opened++;
+    }
+    my $cnt = 0;
+    my $buf = "";
+    my $n;
+    while ($n = sysread($file, $buf, 8*1024)) {
+	last if !$n;
+	$cnt += $n;
+	print $self $buf;
+    }
+    close($file) if $opened;
+    $cnt;
+}
+
+
+=item $c->daemon
+
+Return a reference to the corresponding I<HTTP::Daemon> object.
+
+=cut
+
+sub daemon
+{
+    my $self = shift;
+    ${*$self}{'httpd_daemon'};
+}
+
+=back
+
+=head1 SEE ALSO
+
+RFC 2068
+
+L<IO::Socket>, L<Apache>
+
+=head1 COPYRIGHT
+
+Copyright 1996-1998, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Date.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Date.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Date.pm	(revision 16561)
@@ -0,0 +1,330 @@
+# $Id$
+#
+package HTTP::Date;
+
+=head1 NAME
+
+HTTP::Date - date conversion routines
+
+=head1 SYNOPSIS
+
+ use HTTP::Date;
+
+ $string = time2str($time);    # Format as GMT ASCII time
+ $time = str2time($string);    # convert ASCII date to machine time
+
+=head1 DESCRIPTION
+
+This module provides two functions that deal with the HTTP date
+format.  The following functions are provided:
+
+=over 4
+
+=item time2str([$time])
+
+The time2str() function converts a machine time (seconds since epoch)
+to a string.  If the function is called without an argument, it will
+use the current time.
+
+The string returned is in the format defined by the HTTP/1.0
+specification.  This is a fixed length subset of the format defined by
+RFC 1123, represented in Universal Time (GMT).  An example of this
+format is:
+
+   Thu, 03 Feb 1994 17:09:00 GMT
+
+=item str2time($str [, $zone])
+
+The str2time() function converts a string to machine time.  It returns
+C<undef> if the format is unrecognized, or the year is not between 1970
+and 2038.  The function is able to parse the following formats:
+
+ "Wed, 09 Feb 1994 22:23:32 GMT"       -- HTTP format
+ "Thu Feb  3 17:03:55 GMT 1994"        -- ctime(3) format
+ "Thu Feb  3 00:00:00 1994",           -- ANSI C asctime() format
+ "Tuesday, 08-Feb-94 14:15:29 GMT"     -- old rfc850 HTTP format
+ "Tuesday, 08-Feb-1994 14:15:29 GMT"   -- broken rfc850 HTTP format
+
+ "03/Feb/1994:17:03:55 -0700"   -- common logfile format
+ "09 Feb 1994 22:23:32 GMT"     -- HTTP format (no weekday)
+ "08-Feb-94 14:15:29 GMT"       -- rfc850 format (no weekday)
+ "08-Feb-1994 14:15:29 GMT"     -- broken rfc850 format (no weekday)
+
+ "1994-02-03 14:15:29 -0100"    -- ISO 8601 format
+ "1994-02-03 14:15:29"          -- zone is optional
+ "1994-02-03"                   -- only date
+ "1994-02-03T14:15:29"          -- Use T as separator
+ "19940203T141529Z"             -- ISO 8601 compact format
+ "19940203"                     -- only date
+
+ "08-Feb-94"         -- old rfc850 HTTP format    (no weekday, no time)
+ "08-Feb-1994"       -- broken rfc850 HTTP format (no weekday, no time)
+ "09 Feb 1994"       -- proposed new HTTP format  (no weekday, no time)
+ "03/Feb/1994"       -- common logfile format     (no time, no offset)
+
+ "Feb  3  1994"      -- Unix 'ls -l' format
+ "Feb  3 17:03"      -- Unix 'ls -l' format
+
+ "11-15-96  03:52PM" -- Windows 'dir' format
+
+The parser ignores leading and trailing whitespace.  It also allow the
+seconds to be missing and the month to be numerical in most formats.
+
+The str2time() function takes an optional second argument that
+specifies the default time zone to use when converting the date.  This
+zone specification should be numerical (like "-0800" or "+0100") or
+"GMT".  This parameter is ignored if the zone is specified in the date
+string itself.  It this parameter is missing, and the date string
+format does not contain any zone specification then the local time
+zone is assumed.
+
+If the year is missing, then we assume that the date is the first
+matching date I<before> current time.
+
+=back
+
+=head1 BUGS
+
+Non-numerical time zones (like MET, PST) are all treated like GMT.
+Do not use them.  HTTP does not use them.
+
+The str2time() function has been told how to parse far too many
+formats.  This makes the module name misleading. To be sure it is
+really misleading you can also import the time2iso() and time2isoz()
+functions.  They work like time2str() but produce ISO-8601 formated
+strings (YYYY-MM-DD hh:mm:ss).
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+sub Version { $VERSION; }
+
+require 5.002;
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(time2str str2time);
+@EXPORT_OK = qw(time2iso time2isoz);
+
+use Time::Local ();
+
+use strict;
+use vars qw(@DoW @MoY %MoY);
+
+#@DoW = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
+@DoW = qw(Sun Mon Tue Wed Thu Fri Sat);
+@MoY = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+# Build %MoY hash
+my $i = 0;
+foreach(@MoY) {
+   $MoY{lc $_} = $i++;
+}
+
+my($current_month, $current_year) = (localtime)[4, 5];
+
+
+
+
+sub time2str (;$)
+{
+   my $time = shift;
+   $time = time unless defined $time;
+   my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($time);
+   sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
+	   $DoW[$wday],
+	   $mday, $MoY[$mon], $year+1900,
+	   $hour, $min, $sec);
+}
+
+
+
+sub str2time ($;$)
+{
+   local($_) = shift;
+   return undef unless defined;
+   my($default_zone) = @_;
+
+   # Remove useless weekday, if it exists
+   s/^\s*(?:sun|mon|tue|wed|thu|fri|sat)\w*,?\s*//i;
+
+   my($day, $mon, $yr, $hr, $min, $sec, $tz, $aorp);
+   my $offset = 0;  # used when compensating for timezone
+
+ PARSEDATE: {
+      # Then we are able to check for most of the formats with this regexp
+      ($day,$mon,$yr,$hr,$min,$sec,$tz) =
+	/^\s*
+	 (\d\d?)               # day
+	    (?:\s+|[-\/])
+	 (\w+)                 # month
+	    (?:\s+|[-\/])
+	 (\d+)                 # year
+	 (?:
+	       (?:\s+|:)       # separator before clock
+	    (\d\d?):(\d\d)     # hour:min
+	    (?::(\d\d))?       # optional seconds
+	 )?                    # optional clock
+	    \s*
+	 ([-+]?\d{2,4}|GMT|gmt)? # timezone
+	    \s*$
+	/x
+	  and last PARSEDATE;
+
+      # Try the ctime and asctime format
+      ($mon, $day, $hr, $min, $sec, $tz, $yr) =
+	/^\s*                  # allow intial whitespace
+	 (\w{1,3})             # month
+	    \s+
+	 (\d\d?)               # day
+	    \s+
+	 (\d\d?):(\d\d)        # hour:min
+	 (?::(\d\d))?          # optional seconds
+	    \s+
+	 (?:(GMT|gmt)\s+)?     # optional GMT timezone
+	 (\d+)                 # year
+	    \s*$               # allow trailing whitespace
+	/x
+	  and last PARSEDATE;
+
+      # Then the Unix 'ls -l' date format
+      ($mon, $day, $yr, $hr, $min, $sec) =
+	/^\s*
+	 (\w{3})               # month
+	    \s+
+	 (\d\d?)               # day
+	    \s+
+	 (?:
+	    (\d\d\d\d) |       # year
+	    (\d{1,2}):(\d{2})  # hour:min
+            (?::(\d\d))?       # optional seconds
+	 )
+	 \s*$
+       /x
+	 and last PARSEDATE;
+
+      # ISO 8601 format '1996-02-29 12:00:00 -0100' and variants
+      ($yr, $mon, $day, $hr, $min, $sec, $tz) =
+	/^\s*
+	  (\d{4})              # year
+	     [-\/]?
+	  (\d\d?)              # numerical month
+	     [-\/]?
+	  (\d\d?)              # day
+	 (?:
+	       (?:\s+|:|T|-)   # separator before clock
+	    (\d\d?):?(\d\d)    # hour:min
+	    (?::?(\d\d))?      # optional seconds
+	 )?                    # optional clock
+	    \s*
+	 ([-+]?\d\d?:?(:?\d\d)?
+	  |Z|z)?               # timezone  (Z is "zero meridian", i.e. GMT)
+	    \s*$
+	/x
+	  and last PARSEDATE;
+
+      # Windows 'dir' 11-12-96  03:52PM
+      ($mon, $day, $yr, $hr, $min, $aorp) =
+        /^\s*
+          (\d{2})                # numerical month
+             -
+          (\d{2})                # day
+             -
+          (\d{2})                # year
+             \s+
+          (\d\d?):(\d\d)([apAP][mM])  # hour:min AM or PM
+             \s*$
+        /x
+          and last PARSEDATE;
+
+      # If it is not recognized by now we give up
+      return undef;
+   }
+
+   # Translate month name to number
+   if ($mon =~ /^\d+$/) {
+     # numeric month
+     return undef if $mon < 1 || $mon > 12;
+     $mon--;
+   } else {
+     $mon = lc $mon;
+     return undef unless exists $MoY{$mon};
+     $mon = $MoY{$mon};
+   }
+
+   # If the year is missing, we assume some date before the current,
+   # because these date are mostly present on "ls -l" listings.
+   unless (defined $yr) {
+	$yr = $current_year;
+	$yr-- if $mon > $current_month;
+    }
+
+   # Then we check if the year is acceptable
+   return undef if $yr > 99 && $yr < 1900;  # We ignore these years
+   $yr += 100 if $yr < 50;  # a stupid thing to do???
+   $yr -= 1900 if $yr >= 1900;
+   # The $yr is now relative to 1900 (as expected by timelocal())
+
+   # timelocal() seems to go into an infinite loop if it is given out
+   # of range parameters.  Let's check the year at least.
+
+   # Epoch counter maxes out in year 2038, assuming "time_t" is 32 bit
+   return undef if $yr > 138;
+   return undef if $yr <  70;  # 1970 is Unix epoch
+
+   # Compensate for AM/PM
+   if ($aorp) {
+       $aorp = uc $aorp;
+       $hr = 0 if $hr == 12 && $aorp eq 'AM';
+       $hr += 12 if $aorp eq 'PM' && $hr != 12;
+   }
+
+   # Make sure things are defined
+   for ($sec, $min, $hr) {  $_ = 0 unless defined   }
+
+   # Should we compensate for the timezone?
+   $tz = $default_zone unless defined $tz;
+   return eval {Time::Local::timelocal($sec, $min, $hr, $day, $mon, $yr)}
+     unless defined $tz;
+
+   # We can calculate offset for numerical time zones
+   if ($tz =~ /^([-+])?(\d\d?):?(\d\d)?$/) {
+       $offset = 3600 * $2;
+       $offset += 60 * $3 if $3;
+       $offset *= -1 if $1 && $1 ne '-';
+   }
+   eval{Time::Local::timegm($sec, $min, $hr, $day, $mon, $yr) + $offset};
+}
+
+
+
+# And then some bloat because I happen to like the ISO 8601 time
+# format.
+
+sub time2iso (;$)
+{
+   my $time = shift;
+   $time = time unless defined $time;
+   my($sec,$min,$hour,$mday,$mon,$year) = localtime($time);
+   sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+	   $year+1900, $mon+1, $mday, $hour, $min, $sec);
+}
+
+
+sub time2isoz (;$)
+{
+    my $time = shift;
+    $time = time unless defined $time;
+    my($sec,$min,$hour,$mday,$mon,$year) = gmtime($time);
+    sprintf("%04d-%02d-%02d %02d:%02d:%02dZ",
+            $year+1900, $mon+1, $mday, $hour, $min, $sec);
+}
+
+1;
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers.pm	(revision 16561)
@@ -0,0 +1,540 @@
+#
+# $Id$
+
+package HTTP::Headers;
+
+=head1 NAME
+
+HTTP::Headers - Class encapsulating HTTP Message headers
+
+=head1 SYNOPSIS
+
+ require HTTP::Headers;
+ $h = new HTTP::Headers;
+
+=head1 DESCRIPTION
+
+The C<HTTP::Headers> class encapsulates HTTP-style message headers.
+The headers consist of attribute-value pairs, which may be repeated,
+and which are printed in a particular order.
+
+Instances of this class are usually created as member variables of the
+C<HTTP::Request> and C<HTTP::Response> classes, internal to the
+library.
+
+The following methods are available:
+
+=over 4
+
+=cut
+
+use strict;
+use vars qw($VERSION $TRANSLATE_UNDERSCORE);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use Carp ();
+
+# Could not use the AutoLoader becase several of the method names are
+# not unique in the first 8 characters.
+#use SelfLoader;
+
+
+# "Good Practice" order of HTTP message headers:
+#    - General-Headers
+#    - Request-Headers
+#    - Response-Headers
+#    - Entity-Headers
+# (From draft-ietf-http-v11-spec-rev-01, Nov 21, 1997)
+
+my @header_order = qw(
+   Cache-Control Connection Date Pragma Transfer-Encoding Upgrade Trailer Via
+
+   Accept Accept-Charset Accept-Encoding Accept-Language
+   Authorization Expect From Host
+   If-Modified-Since If-Match If-None-Match If-Range If-Unmodified-Since
+   Max-Forwards Proxy-Authorization Range Referer TE User-Agent
+
+   Accept-Ranges Age Location Proxy-Authenticate Retry-After Server Vary
+   Warning WWW-Authenticate
+
+   Allow Content-Base Content-Encoding Content-Language Content-Length
+   Content-Location Content-MD5 Content-Range Content-Type
+   ETag Expires Last-Modified
+);
+
+# Make alternative representations of @header_order.  This is used
+# for sorting and case matching.
+my $i = 0;
+my %header_order;
+my %standard_case;
+for (@header_order) {
+    my $lc = lc $_;
+    $header_order{$lc} = $i++;
+    $standard_case{$lc} = $_;
+}
+
+$TRANSLATE_UNDERSCORE = 1 unless defined $TRANSLATE_UNDERSCORE;
+
+
+
+=item $h = new HTTP::Headers
+
+Constructs a new C<HTTP::Headers> object.  You might pass some initial
+attribute-value pairs as parameters to the constructor.  I<E.g.>:
+
+ $h = new HTTP::Headers
+     Date         => 'Thu, 03 Feb 1994 00:00:00 GMT',
+     Content_Type => 'text/html; version=3.2',
+     Content_Base => 'http://www.sn.no/';
+
+=cut
+
+sub new
+{
+    my($class) = shift;
+    my $self = bless {}, $class;
+    $self->header(@_); # set up initial headers
+    $self;
+}
+
+
+=item $h->header($field [=> $value],...)
+
+Get or set the value of a header.  The header field name is not case
+sensitive.  To make the life easier for perl users who wants to avoid
+quoting before the => operator, you can use '_' as a synonym for '-'
+in header names (this behaviour can be suppressed by setting
+$HTTP::Headers::TRANSLATE_UNDERSCORE to a FALSE value).
+
+The header() method accepts multiple ($field => $value) pairs, so you
+can update several fields with a single invocation.
+
+The optional $value argument may be a scalar or a reference to a list
+of scalars. If the $value argument is undefined or not given, then the
+header is not modified.
+
+The old value of the last of the $field values is returned.
+Multi-valued fields will be concatenated with "," as separator in
+scalar context.
+
+ $header->header(MIME_Version => '1.0',
+		 User_Agent   => 'My-Web-Client/0.01');
+ $header->header(Accept => "text/html, text/plain, image/*");
+ $header->header(Accept => [qw(text/html text/plain image/*)]);
+ @accepts = $header->header('Accept');
+
+=cut
+
+sub header
+{
+    my $self = shift;
+    my($field, $val, @old);
+    while (($field, $val) = splice(@_, 0, 2)) {
+	@old = $self->_header($field, $val);
+    }
+    return @old if wantarray;
+    return $old[0] if @old <= 1;
+    join(", ", @old);
+}
+
+sub _header
+{
+    my($self, $field, $val, $push) = @_;
+    $field =~ tr/_/-/ if $TRANSLATE_UNDERSCORE;
+
+    # $push is only used interally sub push_header
+    Carp::croak('Need a field name') unless length($field);
+
+    my $lc_field = lc $field;
+    unless(defined $standard_case{$lc_field}) {
+	# generate a %stadard_case entry for this field
+	$field =~ s/\b(\w)/\u$1/g;
+	$standard_case{$lc_field} = $field;
+    }
+
+    my $h = $self->{$lc_field};
+    my @old = ref($h) ? @$h : (defined($h) ? ($h) : ());
+
+    if (defined $val) {
+	my @new = $push ? @old : ();
+	if (!ref($val)) {
+	    push(@new, $val);
+	} elsif (ref($val) eq 'ARRAY') {
+	    push(@new, @$val);
+	} else {
+	    Carp::croak("Unexpected field value $val");
+	}
+	$self->{$lc_field} = @new > 1 ? \@new : $new[0];
+    }
+    @old;
+}
+
+
+# Compare function which makes it easy to sort headers in the
+# recommended "Good Practice" order.
+sub _header_cmp
+{
+    # Unknown headers are assign a large value so that they are
+    # sorted last.  This also helps avoiding a warning from -w
+    # about comparing undefined values.
+    $header_order{$a} = 999 unless defined $header_order{$a};
+    $header_order{$b} = 999 unless defined $header_order{$b};
+
+    $header_order{$a} <=> $header_order{$b} || $a cmp $b;
+}
+
+
+=item $h->scan(\&doit)
+
+Apply a subroutine to each header in turn.  The callback routine is
+called with two parameters; the name of the field and a single value.
+If the header has more than one value, then the routine is called once
+for each value.  The field name passed to the callback routine has
+case as suggested by HTTP Spec, and the headers will be visited in the
+recommended "Good Practice" order.
+
+=cut
+
+sub scan
+{
+    my($self, $sub) = @_;
+    my $key;
+    foreach $key (sort _header_cmp keys %$self) {
+        next if $key =~ /^_/;
+	my $vals = $self->{$key};
+	if (ref($vals)) {
+	    my $val;
+	    for $val (@$vals) {
+		&$sub($standard_case{$key} || $key, $val);
+	    }
+	} else {
+	    &$sub($standard_case{$key} || $key, $vals);
+	}
+    }
+}
+
+
+=item $h->as_string([$endl])
+
+Return the header fields as a formatted MIME header.  Since it
+internally uses the C<scan()> method to build the string, the result
+will use case as suggested by HTTP Spec, and it will follow
+recommended "Good Practice" of ordering the header fieds.  Long header
+values are not folded. 
+
+The optional parameter specifies the line ending sequence to use.  The
+default is C<"\n">.  Embedded "\n" characters in the header will be
+substitued with this line ending sequence.
+
+=cut
+
+sub as_string
+{
+    my($self, $endl) = @_;
+    $endl = "\n" unless defined $endl;
+
+    my @result = ();
+    $self->scan(sub {
+	my($field, $val) = @_;
+	if ($val =~ /\n/) {
+	    # must handle header values with embedded newlines with care
+	    $val =~ s/\s+$//;          # trailing newlines and space must go
+	    $val =~ s/\n\n+/\n/g;      # no empty lines
+	    $val =~ s/\n([^\040\t])/\n $1/g;  # intial space for continuation
+	    $val =~ s/\n/$endl/g;      # substitute with requested line ending
+	}
+	push(@result, "$field: $val");
+    });
+
+    join($endl, @result, '');
+}
+
+
+# The remaining functions should autoloaded only when needed
+
+# A bug in 5.002gamma makes it risky to have POD text inside the
+# autoloaded section of the code, so we keep the documentation before
+# the __DATA__ token.
+
+=item $h->push_header($field, $val)
+
+Add a new field value of the specified header.  The header field name
+is not case sensitive.  The field need not already have a
+value. Previous values for the same field are retained.  The argument
+may be a scalar or a reference to a list of scalars.
+
+ $header->push_header(Accept => 'image/jpeg');
+
+=item $h->remove_header($field,...)
+
+This function removes the headers with the specified names.
+
+=item $h->clone
+
+Returns a copy of this HTTP::Headers object.
+
+=back
+
+=head1 CONVENIENCE METHODS
+
+The most frequently used headers can also be accessed through the
+following convenience methods.  These methods can both be used to read
+and to set the value of a header.  The header value is set if you pass
+an argument to the method.  The old header value is always returned.
+
+Methods that deal with dates/times always convert their value to system
+time (seconds since Jan 1, 1970) and they also expect this kind of
+value when the header value is set.
+
+=over 4
+
+=item $h->date
+
+This header represents the date and time at which the message was
+originated. I<E.g.>:
+
+  $h->date(time);  # set current date
+
+=item $h->expires
+
+This header gives the date and time after which the entity should be
+considered stale.
+
+=item $h->if_modified_since
+
+=item $h->if_unmodified_since
+
+This header is used to make a request conditional.  If the requested
+resource has (not) been modified since the time specified in this field,
+then the server will return a C<"304 Not Modified"> response instead of
+the document itself.
+
+=item $h->last_modified
+
+This header indicates the date and time at which the resource was last
+modified. I<E.g.>:
+
+  # check if document is more than 1 hour old
+  if ($h->last_modified < time - 60*60) {
+	...
+  }
+
+=item $h->content_type
+
+The Content-Type header field indicates the media type of the message
+content. I<E.g.>:
+
+  $h->content_type('text/html');
+
+The value returned will be converted to lower case, and potential
+parameters will be chopped off and returned as a separate value if in
+an array context.  This makes it safe to do the following:
+
+  if ($h->content_type eq 'text/html') {
+     # we enter this place even if the real header value happens to
+     # be 'TEXT/HTML; version=3.0'
+     ...
+  }
+
+=item $h->content_encoding
+
+The Content-Encoding header field is used as a modifier to the
+media type.  When present, its value indicates what additional
+encoding mechanism has been applied to the resource.
+
+=item $h->content_length
+
+A decimal number indicating the size in bytes of the message content.
+
+=item $h->content_language
+
+The natural language(s) of the intended audience for the message
+content.  The value is one or more language tags as defined by RFC
+1766.  Eg. "no" for Norwegian and "en-US" for US-English.
+
+=item $h->title
+
+The title of the document.  In libwww-perl this header will be
+initialized automatically from the E<lt>TITLE>...E<lt>/TITLE> element
+of HTML documents.  I<This header is no longer part of the HTTP
+standard.>
+
+=item $h->user_agent
+
+This header field is used in request messages and contains information
+about the user agent originating the request.  I<E.g.>:
+
+  $h->user_agent('Mozilla/1.2');
+
+=item $h->server
+
+The server header field contains information about the software being
+used by the originating server program handling the request.
+
+=item $h->from
+
+This header should contain an Internet e-mail address for the human
+user who controls the requesting user agent.  The address should be
+machine-usable, as defined by RFC822.  E.g.:
+
+  $h->from('Gisle Aas <aas@sn.no>');
+
+=item $h->referer
+
+Used to specify the address (URI) of the document from which the
+requested resouce address was obtained.
+
+=item $h->www_authenticate
+
+This header must be included as part of a "401 Unauthorized" response.
+The field value consist of a challenge that indicates the
+authentication scheme and parameters applicable to the requested URI.
+
+=item $h->proxy_authenticate
+
+This header must be included in a "407 Proxy Authentication Required"
+response.
+
+=item $h->authorization
+
+=item $h->proxy_authorization
+
+A user agent that wishes to authenticate itself with a server or a
+proxy, may do so by including these headers.
+
+=item $h->authorization_basic
+
+This method is used to get or set an authorization header that use the
+"Basic Authentication Scheme".  In array context it will return two
+values; the user name and the password.  In scalar context it will
+return I<"uname:password"> as a single string value.
+
+When used to set the header value, it expects two arguments.  I<E.g.>:
+
+  $h->authorization_basic($uname, $password);
+
+The method will croak if the $uname contains a colon ':'.
+
+=item $h->proxy_authorization_basic
+
+Same as authorization_basic() but will set the "Proxy-Authorization"
+header instead.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 1995-1998 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+1;
+
+#__DATA__
+
+sub clone
+{
+    my $self = shift;
+    my $clone = new HTTP::Headers;
+    $self->scan(sub { $clone->push_header(@_);} );
+    $clone;
+}
+
+sub push_header
+{
+    Carp::croak('Usage: $h->push_header($field, $val)') if @_ != 3;
+    shift->_header(@_, 'PUSH');
+}
+
+
+sub remove_header
+{
+    my($self, @fields) = @_;
+    my $field;
+    foreach $field (@fields) {
+	$field =~ tr/_/-/ if $TRANSLATE_UNDERSCORE;
+	delete $self->{lc $field};
+    }
+}
+
+# Convenience access functions
+
+sub _date_header
+{
+    require HTTP::Date;
+    my($self, $header, $time) = @_;
+    my($old) = $self->_header($header);
+    if (defined $time) {
+	$self->_header($header, HTTP::Date::time2str($time));
+    }
+    HTTP::Date::str2time($old);
+}
+
+sub date                { shift->_date_header('Date',                @_); }
+sub expires             { shift->_date_header('Expires',             @_); }
+sub if_modified_since   { shift->_date_header('If-Modified-Since',   @_); }
+sub if_unmodified_since { shift->_date_header('If-Unmodified-Since', @_); }
+sub last_modified       { shift->_date_header('Last-Modified',       @_); }
+
+# This is used as a private LWP extention.  The Client-Date header is
+# added as a timestamp to a response when it has been received.
+sub client_date         { shift->_date_header('Client-Date',         @_); }
+
+# The retry_after field is dual format (can also be a expressed as
+# number of seconds from now), so we don't provide an easy way to
+# access it until we have know how both these interfaces can be
+# addressed.  One possibility is to return a negative value for
+# relative seconds and a positive value for epoch based time values.
+#sub retry_after       { shift->_date_header('Retry-After',       @_); }
+
+sub content_type      {
+  my $ct = (shift->_header('Content-Type', @_))[0];
+  return '' unless defined($ct) && length($ct);
+  my @ct = split(/\s*;\s*/, lc($ct));
+  wantarray ? @ct : $ct[0];
+}
+
+sub title             { (shift->_header('Title',            @_))[0] }
+sub content_encoding  { (shift->_header('Content-Encoding', @_))[0] }
+sub content_language  { (shift->_header('Content-Language', @_))[0] }
+sub content_length    { (shift->_header('Content-Length',   @_))[0] }
+
+sub user_agent        { (shift->_header('User-Agent',       @_))[0] }
+sub server            { (shift->_header('Server',           @_))[0] }
+
+sub from              { (shift->_header('From',             @_))[0] }
+sub referer           { (shift->_header('Referer',          @_))[0] }
+sub warning           { (shift->_header('Warning',          @_))[0] }
+
+sub www_authenticate  { (shift->_header('WWW-Authenticate', @_))[0] }
+sub authorization     { (shift->_header('Authorization',    @_))[0] }
+
+sub proxy_authenticate  { (shift->_header('Proxy-Authenticate',  @_))[0] }
+sub proxy_authorization { (shift->_header('Proxy-Authorization', @_))[0] }
+
+sub authorization_basic       { shift->_basic_auth("Authorization",       @_) }
+sub proxy_authorization_basic { shift->_basic_auth("Proxy-Authorization", @_) }
+
+sub _basic_auth {
+    require MIME::Base64;
+    my($self, $h, $user, $passwd) = @_;
+    my($old) = $self->_header($h);
+    if (defined $user) {
+	Carp::croak("Basic authorization user name can't contain ':'")
+	  if $user =~ /:/;
+	$passwd = '' unless defined $passwd;
+	$self->_header($h => 'Basic ' .
+                             MIME::Base64::encode("$user:$passwd", ''));
+    }
+    if (defined $old && $old =~ s/^\s*Basic\s+//) {
+	my $val = MIME::Base64::decode($old);
+	return $val unless wantarray;
+	return split(/:/, $val, 2);
+    }
+    return;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Auth.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Auth.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Auth.pm	(revision 16561)
@@ -0,0 +1,94 @@
+package HTTP::Headers::Auth;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+require HTTP::Headers;
+package HTTP::Headers;
+
+BEGIN {
+    # we provide a new (and better) implementations below
+    undef(&www_authenticate);
+    undef(&proxy_authenticate);
+}
+
+require HTTP::Headers::Util;
+
+sub _parse_authenticate
+{
+    my @ret;
+    for (HTTP::Headers::Util::split_header_words(@_)) {
+	if (!defined($_->[1])) {
+	    # this is a new auth scheme
+	    push(@ret, lc(shift @$_) => {});
+	    shift @$_;
+	}
+	if (@ret) {
+	    # this a new parameter pair for the last auth scheme
+	    while (@$_) {
+		my $k = lc(shift @$_);
+		my $v = shift @$_;
+	        $ret[-1]{$k} = $v;
+	    }
+	} else {
+	    # something wrong, parameter pair without any scheme seen
+	    # IGNORE
+	}
+    }
+    @ret;
+}
+
+sub _authenticate
+{
+    my $self = shift;
+    my $header = shift;
+    my @old = $self->_header($header);
+    if (@_) {
+	$self->remove_header($header);
+	my @new = @_;
+	while (@new) {
+	    my $a_scheme = shift(@new);
+	    if ($a_scheme =~ /\s/) {
+		# assume complete valid value, pass it through
+		$self->push_header($header, $a_scheme);
+	    } else {
+		my @param;
+		if (@new) {
+		    my $p = $new[0];
+		    if (ref($p) eq "ARRAY") {
+			@param = @$p;
+			shift(@new);
+		    } elsif (ref($p) eq "HASH") {
+			@param = %$p;
+			shift(@new);
+		    }
+		}
+		my $val = ucfirst(lc($a_scheme));
+		if (@param) {
+		    my $sep = " ";
+		    while (@param) {
+			my $k = shift @param;
+			my $v = shift @param;
+			if ($v =~ /[^0-9a-zA-Z]/ || lc($k) eq "realm") {
+			    # must quote the value
+			    $v =~ s,([\\\"]),\\$1,g;
+			    $v = qq("$v");
+			}
+			$val .= "$sep$k=$v";
+			$sep = ", ";
+		    }
+		}
+		$self->push_header($header, $val);
+	    }
+	}
+    }
+    return unless defined wantarray;
+    wantarray ? _parse_authenticate(@old) : join(", ", @old);
+}
+
+
+sub www_authenticate    { shift->_authenticate("WWW-Authenticate", @_)   }
+sub proxy_authenticate  { shift->_authenticate("Proxy-Authenticate", @_) }
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/ETag.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/ETag.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/ETag.pm	(revision 16561)
@@ -0,0 +1,88 @@
+package HTTP::Header::ETag;
+
+use strict;
+use vars qw($VERSION);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+require HTTP::Date;
+
+require HTTP::Headers;
+package HTTP::Headers;
+
+sub _etags
+{
+    my $self = shift;
+    my $header = shift;
+    my @old = _split_etag_list($self->_header($header));
+    if (@_) {
+	$self->_header($header => join(", ", _split_etag_list(@_)));
+    }
+    wantarray ? @old : join(", ", @old);
+}
+
+sub etag          { shift->_etags("ETag", @_); }
+sub if_match      { shift->_etags("If-Match", @_); }
+sub if_none_match { shift->_etags("If-None-Match", @_); }
+
+sub if_range {
+    # Either a date or an entity-tag
+    my $self = shift;
+    my @old = $self->_header("If-Range");
+    if (@_) {
+	my $new = shift;
+	if (!defined $new) {
+	    $self->remove_header("If-Range");
+	} elsif ($new =~ /^\d+$/) {
+	    $self->_date_header("If-Range", $new);
+	} else {
+	    $self->_etags("If-Range", $new);
+	}
+    }
+    return unless defined(wantarray);
+    for (@old) {
+	my $t = HTTP::Date::str2time($_);
+	$_ = $t if $t;
+    }
+    wantarray ? @old : join(", ", @old);
+}
+
+
+# Split a list of entity tag values.  The return value is a list
+# consisting of one element per entity tag.  Suitable for parsing
+# headers like C<If-Match>, C<If-None-Match>.  You might even want to
+# use it on C<ETag> and C<If-Range> entity tag values, because it will
+# normalize them to the common form.
+#
+#  entity-tag	  = [ weak ] opaque-tag
+#  weak		  = "W/"
+#  opaque-tag	  = quoted-string
+
+
+sub _split_etag_list
+{
+    my(@val) = @_;
+    my @res;
+    for (@val) {
+        while (length) {
+            my $weak = "";
+	    $weak = "W/" if s,^\s*[wW]/,,;
+            my $etag = "";
+	    if (s/^\s*(\"[^\"\\]*(?:\\.[^\"\\]*)*\")//) {
+		push(@res, "$weak$1");
+            } elsif (s/^\s*,//) {
+                push(@res, qq(W/"")) if $weak;
+            } elsif (s/^\s*([^,\s]+)//) {
+                $etag = $1;
+		$etag =~ s/([\"\\])/\\$1/g;
+	        push(@res, qq($weak"$etag"));
+            } elsif (s/^\s+// || !length) {
+                push(@res, qq(W/"")) if $weak;
+            } else {
+	 	die "This should not happen: '$_'";
+            }
+        }
+   }
+   @res;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Util.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Util.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Headers/Util.pm	(revision 16561)
@@ -0,0 +1,180 @@
+package HTTP::Headers::Util;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT_OK);
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+require Exporter;
+@ISA=qw(Exporter);
+
+@EXPORT_OK=qw(split_header_words join_header_words);
+
+=head1 NAME
+
+HTTP::Headers::Util - Header value parsing utility functions
+
+=head1 SYNOPSIS
+
+  use HTTP::Headers::Util qw(split_header_words);
+  @values = split_header_words($h->header("Content-Type"));
+
+=head1 DESCRIPTION
+
+This module provide a few functions that helps parsing and
+construction of valid HTTP header values.  None of the functions are
+exported by default.
+
+The following functions are provided:
+
+=over 4
+
+
+=item split_header_words( @header_values )
+
+This function will parse the header values given as argument into a
+list of anonymous arrays containing key/value pairs.  The function
+know how to deal with ",", ";" and "=" as well as quoted values after
+"=".  A list of space separated tokens are parsed as if they were
+separated by ";".
+
+If the @header_values passed as argument contains multiple values,
+then they are treated as if they were a single value separated by
+comma ",".
+
+This means that this function is useful to parse header fields that
+follow this syntax (BNF as from the HTTP/1.1 specification, but we relax
+the requirement for tokens).
+
+  headers           = #header
+  header            = (token | parameter) *( [";"] (token | parameter))
+
+  token             = 1*<any CHAR except CTLs or separators>
+  separators        = "(" | ")" | "<" | ">" | "@"
+                    | "," | ";" | ":" | "\" | <">
+                    | "/" | "[" | "]" | "?" | "="
+                    | "{" | "}" | SP | HT
+
+  quoted-string     = ( <"> *(qdtext | quoted-pair ) <"> )
+  qdtext            = <any TEXT except <">>
+  quoted-pair       = "\" CHAR
+
+  parameter         = attribute "=" value
+  attribute         = token
+  value             = token | quoted-string
+
+Each I<header> is represented by an anonymous array of key/value
+pairs.  If a token is recognized then the value will be C<undef>.
+Syntactically incorrect headers will not necessary be parsed as you
+would want.
+
+This is easier to describe with some examples:
+
+   split_header_words('foo="bar"; port="80,81"; discard, bar=baz')
+   split_header_words('text/html; charset="iso-8859-1");
+   split_header_words('Basic realm="\"foo\\bar\""');
+
+will return
+
+   [foo=>'bar', port=>'80,81', discard=> undef], [bar=>'baz' ]
+   ['text/html' => undef, charset => 'iso-8859-1']
+   [Basic => undef, realm => '"foo\bar"']
+
+=cut
+
+
+sub split_header_words
+{
+    my(@val) = @_;
+    my @res;
+    for (@val) {
+	my @cur;
+	while (length) {
+	    if (s/^\s*(=*[^\s=;,]+)//) {  # 'token' or parameter 'attribute'
+		push(@cur, $1);
+		# a quoted value
+		if (s/^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"//) {
+		    my $val = $1;
+		    $val =~ s/\\(.)/$1/g;
+		    push(@cur, $val);
+		# some unquoted value
+		} elsif (s/^\s*=\s*([^;,\s]*)//) {
+		    my $val = $1;
+		    $val =~ s/\s+$//;
+		    push(@cur, $val);
+		# no value, a lone token
+		} else {
+		    push(@cur, undef);
+		}
+	    } elsif (s/^\s*,//) {
+		push(@res, [@cur]) if @cur;
+		@cur = ();
+	    } elsif (s/^\s*;// || s/^\s+//) {
+		# continue
+	    } else {
+		die "This should not happen: '$_'";
+	    }
+	}
+	push(@res, \@cur) if @cur;
+    }
+    @res;
+}
+
+
+=item join_header_words( @arrays )
+
+This will do the opposite convertion of what split_header_words()
+does.  It takes a list of anonymous arrays as argument (or a list of
+key/value pairs) and produce a single header value.  Attribute values
+are quoted if needed.
+
+Example:
+
+   join_header_words(["text/plain" => undef, charset => "iso-8859/1"]);
+   join_header_words(""text/plain" => undef, charset => "iso-8859/1");
+
+will both return the string:
+
+   text/plain; charset="iso-8859/1"
+
+=cut
+
+sub join_header_words
+{
+    @_ = ([@_]) if @_ && !ref($_[0]);
+    my @res;
+    for (@_) {
+	my @cur = @$_;
+	my @attr;
+	while (@cur) {
+	    my $k = shift @cur;
+	    my $v = shift @cur;
+	    if (defined $v) {
+		if ($v =~ /^\w+$/) {
+		    $k .= "=$v";
+		} else {
+		    $v =~ s/([\"\\])/\\$1/g;  # escape " and \
+		    $k .= qq(="$v");
+		}
+	    }
+	    push(@attr, $k);
+	}
+	push(@res, join("; ", @attr)) if @attr;
+    }
+    join(", ", @res);
+}
+
+1;
+
+__END__
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 1997-1998, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Message.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Message.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Message.pm	(revision 16561)
@@ -0,0 +1,208 @@
+#
+# $Id$
+
+package HTTP::Message;
+
+=head1 NAME
+
+HTTP::Message - Class encapsulating HTTP messages
+
+=head1 SYNOPSIS
+
+ package HTTP::Request;  # or HTTP::Response
+ require HTTP::Message;
+ @ISA=qw(HTTP::Message);
+
+=head1 DESCRIPTION
+
+A C<HTTP::Message> object contains some headers and a content (body).
+The class is abstract, i.e. it only used as a base class for
+C<HTTP::Request> and C<HTTP::Response> and should never instantiated
+as itself.
+
+The following methods are available:
+
+=over 4
+
+=cut
+
+#####################################################################
+
+require HTTP::Headers;
+require Carp;
+use strict;
+use vars qw($VERSION $AUTOLOAD);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+=item $mess = new HTTP::Message;
+
+This is the object constructor.  It should only be called internally
+by this library.  External code should construct C<HTTP::Request> or
+C<HTTP::Response> objects.
+
+=cut
+
+sub new
+{
+    my($class, $header, $content) = @_;
+    if (defined $header) {
+	Carp::croak("Bad header argument") unless ref $header;
+	$header = $header->clone;
+    } else {
+	$header = HTTP::Headers->new;
+    }
+    $content = '' unless defined $content;
+    bless {
+	'_headers' => $header,
+	'_content' => $content,
+    }, $class;
+}
+
+
+=item $mess->clone()
+
+Returns a copy of the object.
+
+=cut
+
+sub clone
+{
+    my $self  = shift;
+    my $clone = HTTP::Message->new($self->{'_headers'}, $self->{'_content'});
+    $clone;
+}
+
+=item $mess->protocol([$proto])
+
+Sets the HTTP protocol used for the message.  The protocol() is a string
+like "HTTP/1.0" or "HTTP/1.1".
+
+=cut
+
+sub protocol { shift->_elem('_protocol',  @_); }
+
+=item $mess->content([$content])
+
+The content() method sets the content if an argument is given.  If no
+argument is given the content is not touched.  In either case the
+previous content is returned.
+
+=item $mess->add_content($data)
+
+The add_content() methods appends more data to the end of the previous
+content.
+
+=cut
+
+sub content   { shift->_elem('_content',  @_); }
+
+sub add_content
+{
+    my $self = shift;
+    if (ref($_[0])) {
+	$self->{'_content'} .= ${$_[0]};  # for backwards compatability
+    } else {
+	$self->{'_content'} .= $_[0];
+    }
+}
+
+=item $mess->content_ref
+
+The content_ref() method will return a reference to content string.
+It can be more efficient to access the content this way if the content
+is huge, and it can be used for direct manipulation of the content,
+for instance:
+
+  ${$res->content_ref} =~ s/\bfoo\b/bar/g;
+
+=cut
+
+sub content_ref
+{
+    my $self = shift;
+    \$self->{'_content'};
+}
+
+sub as_string
+{
+    "";  # To be overridden in subclasses
+}
+
+=item $mess->headers;
+
+Return the embedded HTTP::Headers object.
+
+=item $mess->headers_as_string([$endl])
+
+Call the HTTP::Headers->as_string() method for the headers in the
+message.
+
+=cut
+
+sub headers            { shift->{'_headers'};                }
+sub headers_as_string  { shift->{'_headers'}->as_string(@_); }
+
+=back
+
+All unknown C<HTTP::Message> methods are delegated to the
+C<HTTP::Headers> object that is part of every message.  This allows
+convenient access to these methods.  Refer to L<HTTP::Headers> for
+details of these methods:
+
+  $mess->header($field => $val);
+  $mess->scan(\&doit);
+  $mess->push_header($field => $val);
+  $mess->remove_header($field);
+
+  $mess->date;
+  $mess->expires;
+  $mess->if_modified_since;
+  $mess->if_unmodified_since;
+  $mess->last_modified;
+  $mess->content_type;
+  $mess->content_encoding;
+  $mess->content_length;
+  $mess->content_language
+  $mess->title;
+  $mess->user_agent;
+  $mess->server;
+  $mess->from;
+  $mess->referer;
+  $mess->www_authenticate;
+  $mess->authorization;
+  $mess->proxy_authorization;
+  $mess->authorization_basic;
+  $mess->proxy_authorization_basic;
+
+=cut
+
+
+# delegate all other method calls the the _headers object.
+sub AUTOLOAD
+{
+    my $self = shift;
+    my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::')+2);
+    return if $method eq "DESTROY";
+    $self->{'_headers'}->$method(@_);
+}
+
+# Private method to access members in %$self
+sub _elem
+{
+    my $self = shift;
+    my $elem = shift;
+    my $old = $self->{$elem};
+    $self->{$elem} = $_[0] if @_;
+    return $old;
+}
+
+1;
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Negotiate.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Negotiate.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Negotiate.pm	(revision 16561)
@@ -0,0 +1,505 @@
+# $Id$
+#
+
+package HTTP::Negotiate;
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+sub Version { $VERSION; }
+
+require 5.002;
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(choose);
+
+require HTTP::Headers;
+
+$DEBUG = 0;
+
+sub choose ($;$)
+{
+    my($variants, $request) = @_;
+    my(%accept);
+
+    unless (defined $request) {
+	# Create a request object from the CGI envirionment variables
+	$request = new HTTP::Headers;
+	$request->header('Accept', $ENV{HTTP_ACCEPT})
+	  if $ENV{HTTP_ACCEPT};
+	$request->header('Accept-Charset', $ENV{HTTP_ACCEPT_CHARSET})
+	  if $ENV{HTTP_ACCEPT_CHARSET};
+	$request->header('Accept-Encoding', $ENV{HTTP_ACCEPT_ENCODING})
+	  if $ENV{HTTP_ACCEPT_ENCODING};
+	$request->header('Accept-Language', $ENV{HTTP_ACCEPT_LANGUAGE})
+	  if $ENV{HTTP_ACCEPT_LANGUAGE};
+    }
+
+    # Get all Accept values from the request.  Build a hash initialized
+    # like this:
+    #
+    #   %accept = ( type =>     { 'audio/*'     => { q => 0.2, mbx => 20000 },
+    #                             'audio/basic' => { q => 1 },
+    #                           },
+    #               language => { 'no'          => { q => 1 },
+    #                           }
+    #             );
+
+    $request->scan(sub {
+	my($key, $val) = @_;
+	return unless $key =~ s/^Accept-?//;
+	my $type = lc $key;
+	$type = "type" unless length $key;
+	$val =~ s/\s+//g;
+	my $name;
+	for $name (split(/,/, $val)) {
+	    my(%param, $param);
+	    if ($name =~ s/;(.*)//) {
+		for $param (split(/;/, $1)) {
+		    my ($pk, $pv) = split(/=/, $param, 2);
+		    $param{$pk} = $pv;
+		}
+	    }
+	    $name = lc $name;
+	    if (defined $param{'q'}) {
+		$param{'q'} = 1 if $param{'q'} > 1;
+		$param{'q'} = 0 if $param{'q'} < 0;
+	    } else {
+		$param{'q'} = 1;
+	    }
+
+	    $param{'q'} = 1 unless defined $param{'q'};
+	    $accept{$type}{$name} = \%param;
+	}
+    });
+
+    # Check if any of the variants specify a language.  We do this
+    # because it influence how we treat those without (they default to
+    # 0.5 instead of 1).
+    my $any_lang = 0;
+    for $var (@$variants) {
+	if ($var->[5]) {
+	    $any_lang = 1;
+	    last;
+	}
+    }
+
+    if ($DEBUG) {
+	print "Netgotiantion parameters in the request\n";
+	for $type (keys %accept) {
+	    print " $type:\n";
+	    for $name (keys %{$accept{$type}}) {
+		print "    $name\n";
+		for $pv (keys %{$accept{$type}{$name}}) {
+		    print "      $pv = $accept{$type}{$name}{$pv}\n";
+		}
+	    }
+	}
+    }
+
+    my @Q = ();  # This is where we collect the results of the
+		 # quality calcualtions
+
+    # Calculate quality for all the variant's that are available.
+    for (@$variants) {
+	my($id, $qs, $ct, $enc, $cs, $lang, $bs) = @$_;
+	$qs = 1 unless defined $qs;
+	$bs = 0 unless defined $bs;
+	if ($DEBUG) {
+	    print "\nEvaluating $id ($ct)\n";
+	    printf "  qs   = %.3f\n", $qs;
+	    print  "  enc  = $enc\n"  if $enc && !ref($enc);
+	    print  "  enc  = @$enc\n" if $enc && ref($enc);
+	    print  "  cs   = $cs\n"   if $cs;
+	    print  "  lang = $lang\n" if $lang;
+	    print  "  bs   = $bs\n"   if $bs;
+	}
+
+	# Calculate encoding quality
+	my $qe = 1;
+	# If the variant has no assignes Content-Encoding, or if no
+	# Accept-Encoding field is present, then the value assigned
+	# is "qe=1".  If *all* of the variant's content encoddings
+	# are listed in the Accept-Encoding field, then the value
+	# assigned is "qw=1".  If *any* of the variant's content
+	# encodings are not listed in the provided Accept-Encoding
+	# field, then the value assigned is "qe=0"
+	if (exists $accept{'encoding'} && $enc) {
+	    my @enc = ref($enc) ? @$enc : ($enc);
+	    for (@enc) {
+		print "Is encoding $_ accepted? " if $DEBUG;
+		unless(exists $accept{'encoding'}{$_}) {
+		    print "no\n" if $DEBUG;
+		    $qe = 0;
+		    last;
+		} else {
+		    print "yes\n" if $DEBUG;
+		}
+	    }
+	}
+
+	# Calculate charset quality
+	my $qc  = 1;
+	# If the variant's media-type has not charset parameter,
+	# or the variant's charset is US-ASCII, or if no Accept-Charset
+	# field is present, then the value assigned is "qc=1".  If the
+	# variantæs charset is listed in the Accept-Charset field,
+	# then the value assigned is "qc=1.  Otherwise, if the variant's
+	# charset is not listed in the provided Accept-Encoding field,
+	# then the value assigned is "qc=0".
+	if (exists $accept{'charset'} && $cs && $cs ne 'us-ascii' ) {
+	    $qc = 0 unless $accept{'charset'}{$cs};
+	}
+
+	# Calculate language quality
+	my $ql  = 1;
+	if ($lang && exists $accept{'language'}) {
+	    my @lang = ref($lang) ? @$lang : ($lang);
+	    # If any of the variant's content languages are listed
+	    # in the Accept-Language field, the the value assigned is
+	    # the maximus of the "q" paramet values for thos language
+	    # tags.
+	    my $q = undef;
+	    for (@lang) {
+		next unless exists $accept{'language'}{$_};
+		my $this_q = $accept{'language'}{$_}{'q'};
+		$q = $this_q unless defined $q;
+		$q = $this_q if $this_q > $q;
+	    }
+	    unless (defined $q) {
+		# If there was no exact match and at least one of
+		# the Accept-Language field values is a complete
+		# subtag prefix of the content language tag(s), then
+		# the "q" parameter value of the largest matching
+		# prefix is used.
+		my $selected = undef;
+		for $al (keys %{ $accept{'language'} }) {
+		    if (substr($lang, 0, length($al)) eq $al) {
+			$selected = $al unless defined $selected;
+			$selected = $al if length($al) > length($selected);
+		    }
+		}
+		$q = $accept{'language'}{$selected}{'q'} if $selected;
+
+		# If none of the variant's content language tags or
+		# tag prefixes are listed in the provided
+		# Accept-Language field, then the value assigned
+		# is "ql=0.001"
+		$q = 0.001 unless defined $q;
+	    }
+	    $ql = $q;
+	} else {
+	    $ql = 0.5 if $any_lang && exists $accept{'language'};
+	}
+
+	my $q   = 1;
+	my $mbx = undef;
+	# If no Accept field is given, then the value assigned is "q=1".
+	# If at least one listed media range matches the variant's media
+	# type, then the "q" parameter value assigned to the most specific
+	# of those matched is used (e.g. "text/html;version=3.0" is more
+	# specific than "text/html", which is more specific than "text/*",
+	# which in turn is more specific than "*/*"). If not media range
+	# in the provided Accept field matches the variant's media type,
+	# then the value assigned is "q=0".
+	if (exists $accept{'type'} && $ct) {
+	    # First we clean up our content-type
+	    $ct =~ s/\s+//g;
+	    my $params = "";
+	    $params = $1 if $ct =~ s/;(.*)//;
+	    my($type, $subtype) = split("/", $ct, 2);
+	    my %param = ();
+	    for $param (split(/;/, $params)) {
+		my($pk,$pv) = split(/=/, $param, 2);
+		$param{$pk} = $pv;
+	    }
+
+	    my $sel_q = undef;
+	    my $sel_mbx = undef;
+	    my $sel_specificness = 0;
+
+	    ACCEPT_TYPE:
+	    for $at (keys %{ $accept{'type'} }) {
+		print "Consider $at...\n" if $DEBUG;
+		my($at_type, $at_subtype) = split("/", $at, 2);
+		# Is it a match on the type
+		next if $at_type    ne '*' && $at_type    ne $type;
+		next if $at_subtype ne '*' && $at_subtype ne $subtype;
+		my $specificness = 0;
+		$specificness++ if $at_type ne '*';
+		$specificness++ if $at_subtype ne '*';
+		# Let's see if content-type parameters also match
+		while (($pk, $pv) = each %param) {
+		    print "Check if $pk = $pv is true\n" if $DEBUG;
+		    next unless exists $accept{'type'}{$at}{$pk};
+		    next ACCEPT_TYPE
+		      unless $accept{'type'}{$at}{$pk} eq $pv;
+		    print "yes it is!!\n" if $DEBUG;
+		    $specificness++;
+		}
+		print "Hurray, type match with specificness = $specificness\n"
+		  if $DEBUG;
+
+		if (!defined($sel_q) || $sel_specificness < $specificness) {
+		    $sel_q   = $accept{'type'}{$at}{'q'};
+		    $sel_mbx = $accept{'type'}{$at}{'mbx'};
+		    $sel_specificness = $specificness;
+		}
+	    }
+	    $q   = $sel_q || 0;
+	    $mbx = $sel_mbx;
+	}
+
+	my $Q;
+	if (!defined($mbx) || $mbx >= $bs) {
+	    $Q = $qs * $qe * $qc * $ql * $q;
+	} else {
+	    $Q = 0;
+	    print "Variant's size is too large ==> Q=0\n" if $DEBUG;
+	}
+
+	if ($DEBUG) {
+	    $mbx = "undef" unless defined $mbx;
+	    printf "Q=%.3f", $Q;
+	    print "  (q=$q, mbx=$mbx, qe=$qe, qc=$qc, ql=$ql, qs=$qs)\n";
+	}
+
+	push(@Q, [$id, $Q, $bs]);
+    }
+
+
+    @Q = sort { $b->[1] <=> $a->[1] || $a->[2] <=> $b->[2] } @Q;
+
+    return @Q if wantarray;
+    return undef unless @Q;
+    return undef if $Q[0][1] == 0;
+    $Q[0][0];
+}
+
+1;
+
+__END__
+
+
+=head1 NAME
+
+choose - choose a variant of a document to serve (HTTP content negotiation)
+
+=head1 SYNOPSIS
+
+ use HTTP::Negotiate;
+
+ #  ID       QS     Content-Type   Encoding Char-Set        Lang   Size
+ $variants =
+  [['var1',  1.000, 'text/html',   undef,   'iso-8859-1',   'en',   3000],
+   ['var2',  0.950, 'text/plain',  'gzip',  'us-ascii',     'no',    400],
+   ['var3',  0.3,   'image/gif',   undef,   undef,          undef, 43555],
+  ];
+
+ @prefered = choose($variants, $request_headers);
+ $the_one  = choose($variants);
+
+=head1 DESCRIPTION
+
+This module provide a complete implementation of the HTTP content
+negotiation algorithm specified in F<draft-ietf-http-v11-spec-00.ps>
+chapter 12.  Content negotiation allows for the selection of a
+preferred content representation based upon attributes of the
+negotiable variants and the value of the various Accept* header fields
+in the request.
+
+The variants are ordered by preference by calling the function
+choose().
+
+The first parameter is a description of the variants that we can
+choose among.  The variants are described by a reference to an array.
+Each element in this array is an array with the values [$id, $qs,
+$content_type, $content_encoding, $charset, $content_language,
+$content_length].  The meaning of these values are described
+below. The $content_encoding and $content_language can be either a
+single scalar value or an array reference if there are several values.
+
+The second optional parameter is a reference to the request headers.
+This is used to look for "Accept*" headers.  You can pass a reference
+to either a HTTP::Request or a HTTP::Headers object.  If this
+parameter is missing, then the accept specification is initialized
+from the CGI environment variables HTTP_ACCEPT, HTTP_ACCEPT_CHARSET,
+HTTP_ACCEPT_ENCODING and HTTP_ACCEPT_LANGUAGE.
+
+In array context, choose() returns a list of variant
+identifier/calculated quality pairs.  The values are sorted by
+quality, highest quality first.  If the calculated quality is the same
+for two variants, then they are sorted by size (smallest first). I<E.g.>:
+
+  (['var1' => 1], ['var2', 0.3], ['var3' => 0]);
+
+Note that also zero quality variants are included in the return list
+even if these should never be served to the client.
+
+In scalar context, it returns the identifier of the variant with the
+highest score or C<undef> if none have non-zero quality.
+
+If the $HTTP::Negotiate::DEBUG variable is set to TRUE, then a lot of
+noise is generated on STDOUT during evaluation of choose().
+
+=head1 VARIANTS
+
+A variant is described by a list of the following values.  If the
+attribute does not make sense or is unknown for a variant, then use
+C<undef> instead.
+
+=over 3
+
+=item identifier
+
+This is just some string that you use as a name for the variant.  The
+identifier of the preferred variant is returned by choose().
+
+=item qs
+
+This is a number between 0.000 and 1.000 that describes the "source
+quality".  This is what F<draft-ietf-http-v11-spec-00.ps> says about this
+value:
+
+Source quality is measured by the content provider as representing the
+amount of degradation from the original source.  For example, a
+picture in JPEG form would have a lower qs when translated to the XBM
+format, and much lower qs when translated to an ASCII-art
+representation.  Note, however, that this is a function of the source
+- an original piece of ASCII-art may degrade in quality if it is
+captured in JPEG form.  The qs values should be assigned to each
+variant by the content provider; if no qs value has been assigned, the
+default is generally "qs=1".
+
+=item content-type
+
+This is the media type of the variant.  The media type does not
+include a charset attribute, but might contain other parameters.
+Examples are:
+
+  text/html
+  text/html;version=2.0
+  text/plain
+  image/gif
+  image/jpg
+
+=item content-encoding
+
+This is one or more content encodings that has been applied to the
+variant.  The content encoding is generally used as a modifier to the
+content media type.  The most common content encodings are:
+
+  gzip
+  compress
+
+=item content-charset
+
+This is the character set used when the variant contains textual
+content.  The charset value should generally be C<undef> or one of these:
+
+  us-ascii
+  iso-8859-1 ... iso-8859-9
+  iso-2022-jp
+  iso-2022-jp-2
+  iso-2022-kr
+  unicode-1-1
+  unicode-1-1-utf-7
+  unicode-1-1-utf-8
+
+=item content-language
+
+This describes one or more languages that are used in the variant.
+Language is described like this in F<draft-ietf-http-v11-spec-00.ps>: A
+language is in this context a natural language spoken, written, or
+otherwise conveyed by human beings for communication of information to
+other human beings.  Computer languages are explicitly excluded.
+
+The language tags are the same as those defined by RFC-1766.  Examples
+are:
+
+  no               Norwegian
+  en               International English
+  en-US            US English
+  en-cockney
+
+=item content-length
+
+This is the number of bytes used to represent the content.
+
+=back
+
+=head1 ACCEPT HEADERS
+
+The following Accept* headers can be used for describing content
+preferences in a request (This description is an edited extract from
+F<draft-ietf-http-v11-spec-00.ps>):
+
+=over 3
+
+=item Accept
+
+This header can be used to indicate a list of media ranges which are
+acceptable as a reponse to the request.  The "*" character is used to
+group media types into ranges, with "*/*" indicating all media types
+and "type/*" indicating all subtypes of that type.
+
+The parameter q is used to indicate the quality factor, which
+represents the user's preference for that range of media types.  The
+parameter mbx gives the maximum acceptable size of the response
+content. The default values are: q=1 and mbx=infinity. If no Accept
+header is present, then the client accepts all media types with q=1.
+
+For example:
+
+  Accept: audio/*;q=0.2;mbx=200000, audio/basic
+
+would mean: "I prefer audio/basic (of any size), but send me any audio
+type if it is the best available after an 80% mark-down in quality and
+its size is less than 200000 bytes"
+
+
+=item Accept-Charset
+
+Used to indicate what character sets are acceptable for the response.
+The "us-ascii" character set is assumed to be acceptable for all user
+agents.  If no Accept-Charset field is given, the default is that any
+charset is acceptable.  Example:
+
+  Accept-Charset: iso-8859-1, unicode-1-1
+
+
+=item Accept-Encoding
+
+Restricts the Content-Encoding values which are acceptable in the
+response.  If no Accept-Encoding field is present, the server may
+assume that the client will accept any content encoding.  An empty
+Accept-Encoding means that no content encoding is acceptable.  Example:
+
+  Accept-Encoding: compress, gzip
+
+
+=item Accept-Language
+
+This field is similar to Accept, but restrict the set of natural
+languages that are preferred as a response.  Each language may be
+given an associated quality value which represents an estimate of the
+user's comprehension of that language.  For example:
+
+  Accept-Language: no, en-gb;q=0.8, de;q=0.55
+
+would mean: "I prefer Norwegian, but will accept British English (with
+80% comprehension) or German (with 55% comprehension).
+
+=back
+
+
+=head1 COPYRIGHT
+
+Copyright 1996, Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+Gisle Aas <aas@sn.no>
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request.pm	(revision 16561)
@@ -0,0 +1,160 @@
+#
+# $Id$
+
+package HTTP::Request;
+
+=head1 NAME
+
+HTTP::Request - Class encapsulating HTTP Requests
+
+=head1 SYNOPSIS
+
+ require HTTP::Request;
+ $request = HTTP::Request->new(GET => 'http://www.oslonett.no/');
+
+=head1 DESCRIPTION
+
+C<HTTP::Request> is a class encapsulating HTTP style requests,
+consisting of a request line, some headers, and some (potentially empty)
+content. Note that the LWP library also uses this HTTP style requests
+for non-HTTP protocols.
+
+Instances of this class are usually passed to the C<request()> method
+of an C<LWP::UserAgent> object:
+
+ $ua = LWP::UserAgent->new;
+ $request = HTTP::Request->new(GET => 'http://www.oslonett.no/');
+ $response = $ua->request($request);
+
+C<HTTP::Request> is a subclass of C<HTTP::Message> and therefore
+inherits its methods.  The inherited methods often used are header(),
+push_header(), remove_header(), headers_as_string() and content().
+See L<HTTP::Message> for details.
+
+The following additional methods are available:
+
+=over 4
+
+=cut
+
+require HTTP::Message;
+@ISA = qw(HTTP::Message);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use URI::URL ();
+use strict;
+
+=item $r = HTTP::Request->new($method, $url, [$header, [$content]])
+
+Constructs a new C<HTTP::Request> object describing a request on the
+object C<$url> using method C<$method>.  The C<$url> argument can be
+either a string, or a reference to a C<URI::URL> object.  The $header
+argument should be a reference to an C<HTTP::Headers> object.
+
+=cut
+
+sub new
+{
+    my($class, $method, $url, $header, $content) = @_;
+    my $self = $class->SUPER::new($header, $content);
+    $self->method($method);
+    $self->url($url);
+    $self;
+}
+
+
+sub clone
+{
+    my $self = shift;
+    my $clone = bless $self->SUPER::clone, ref($self);
+    $clone->method($self->method);
+    $clone->url($self->url);
+    $clone;
+}
+
+
+=item $r->method([$val])
+
+=item $r->url([$val])
+
+These methods provide public access to the member variables containing
+respectively the method of the request and the URL object of the
+request.
+
+If an argument is given the member variable is given that as its new
+value. If no argument is given the value is not touched. In either
+case the previous value is returned.
+
+The url() method accept both a reference to a URI::URL object and a
+string as its argument.  If a string is given, then it should be
+parseable as an absolute URL.
+
+=cut
+
+sub method  { shift->_elem('_method', @_); }
+
+sub url
+{
+    my $self = shift;
+    my $old = $self->{'_url'};
+    if (@_) {
+	my $url = shift;
+	if (!defined $url) {
+	    # that's ok
+	} elsif (ref $url) {
+	    $url = $url->abs;
+	} else {
+	    $url = eval { URI::URL->new($url) };
+	}
+	$self->{'_url'} = $url;
+    }
+    $old;
+}
+
+*uri = \&url;  # this is the same for now
+
+=item $r->as_string()
+
+Method returning a textual representation of the request.
+Mainly useful for debugging purposes. It takes no arguments.
+
+=cut
+
+sub as_string
+{
+    my $self = shift;
+    my @result;
+    #push(@result, "---- $self -----");
+    my $req_line = $self->method || "[NO METHOD]";
+    my $url = $self->url;
+    $url = (defined $url) ? $url->as_string : "[NO URL]";
+    $req_line .= " $url";
+    my $proto = $self->protocol;
+    $req_line .= " $proto" if $proto;
+
+    push(@result, $req_line);
+    push(@result, $self->headers_as_string);
+    my $content = $self->content;
+    if (defined $content) {
+	push(@result, $content);
+    }
+    #push(@result, ("-" x 40));
+    join("\n", @result, "");
+}
+
+1;
+
+=back
+
+=head1 SEE ALSO
+
+L<HTTP::Headers>, L<HTTP::Message>, L<HTTP::Request::Common>
+
+=head1 COPYRIGHT
+
+Copyright 1995-1998 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request/Common.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request/Common.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Request/Common.pm	(revision 16561)
@@ -0,0 +1,389 @@
+# $Id$
+#
+package HTTP::Request::Common;
+
+use strict;
+use vars qw(@EXPORT @EXPORT_OK $VERSION $DYNAMIC_FILE_UPLOAD);
+
+$DYNAMIC_FILE_UPLOAD ||= 0;  # make it defined (don't know why)
+
+require Exporter;
+*import = \&Exporter::import;
+@EXPORT =qw(GET HEAD PUT POST);
+@EXPORT_OK = qw($DYNAMIC_FILE_UPLOAD);
+
+require HTTP::Request;
+use Carp();
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+my $CRLF = "\015\012";   # "\r\n" is not portable
+
+sub GET  { _simple_req('GET',  @_); }
+sub HEAD { _simple_req('HEAD', @_); }
+sub PUT  { _simple_req('PUT' , @_); }
+
+sub POST
+{
+    my $url = shift;
+    my $req = HTTP::Request->new(POST => $url);
+    my $content;
+    $content = shift if @_ and ref $_[0];
+    my($k, $v);
+    while (($k,$v) = splice(@_, 0, 2)) {
+	if (lc($k) eq 'content') {
+	    $content = $v;
+	} else {
+	    $req->push_header($k, $v);
+	}
+    }
+    my $ct = $req->header('Content-Type');
+    unless ($ct) {
+	$ct = 'application/x-www-form-urlencoded';
+    } elsif ($ct eq 'form-data') {
+	$ct = 'multipart/form-data';
+    }
+
+    if (ref $content) {
+	if (lc($ct) eq 'multipart/form-data') {    #XXX: boundary="..."
+	    my $boundary;
+	    ($content, $boundary) = form_data($content, $boundary);
+	    $boundary = qq("$boundary") if $boundary =~ /\W/;
+	    $ct = qq(multipart/form-data; boundary=$boundary);
+	} else {
+	    # We use a temporary URI::URL object to format
+	    # the application/x-www-form-urlencoded content.
+	    require URI::URL;
+	    my $url = URI::URL->new('http:');
+	    $url->query_form(ref($content) eq "HASH" ? %$content : @$content);
+	    $content = $url->equery;
+	}
+    }
+
+    $req->header('Content-Type' => $ct);  # might be redundant
+    if (defined($content)) {
+	$req->header('Content-Length' =>
+		     length($content)) unless ref($content);
+	$req->content($content);
+    }
+    $req;
+}
+
+
+sub _simple_req
+{
+    my($method, $url) = splice(@_, 0, 2);
+    my $req = HTTP::Request->new($method => $url);
+    my($k, $v);
+    while (($k,$v) = splice(@_, 0, 2)) {
+	if (lc($k) eq 'content') {
+	    $req->add_content($v);
+	} else {
+	    $req->push_header($k, $v);
+	}
+    }
+    $req;
+}
+
+
+sub form_data   # RFC1867
+{
+    my($data, $boundary) = @_;
+    my @data = ref($data) eq "HASH" ? %$data : @$data;  # copy
+    my $fhparts;
+    my @parts;
+    my($k,$v);
+    while (($k,$v) = splice(@data, 0, 2)) {
+	if (!ref($v)) {
+	    $k =~ s/([\\\"])/\\$1/g;  # escape quotes and backslashes
+	    push(@parts,
+		 qq(Content-Disposition: form-data; name="$k"$CRLF$CRLF$v));
+	} else {
+	    my($file, $usename, @headers) = @$v;
+	    unless (defined $usename) {
+		$usename = $file;
+		$usename =~ s,.*/,, if defined($usename);
+	    }
+	    my $disp = qq(form-data; name="$k");
+	    $disp .= qq(; filename="$usename") if $usename;
+	    my $content = "";
+	    my $h = HTTP::Headers->new(@headers);
+	    my $ct = $h->header("Content-Type");
+	    if ($file) {
+		require Symbol;
+		my $fh = Symbol::gensym();
+		open($fh, $file) or Carp::croak("Can't open file $file: $!");
+		binmode($fh);
+		if ($DYNAMIC_FILE_UPLOAD) {
+		    # will read file later
+		    $content = $fh;
+		} else {
+		    local($/) = undef; # slurp files
+		    $content = <$fh>;
+		    close($fh);
+		    $h->header("Content-Length" => length($content));
+		}
+		unless ($ct) {
+		    require LWP::MediaTypes;
+		    $ct = LWP::MediaTypes::guess_media_type($file, $h);
+		}
+	    }
+	    if ($h->header("Content-Disposition")) {
+		# just to get it sorted first
+		$disp = $h->header("Content-Disposition");
+		$h->remove_header("Content-Disposition");
+	    }
+	    if ($h->header("Content")) {
+		$content = $h->header("Content");
+		$h->remove_header("Content");
+	    }
+	    my $head = join($CRLF, "Content-Disposition: $disp",
+			           $h->as_string($CRLF),
+			           "");
+	    if (ref $content) {
+		push(@parts, [$head, $content]);
+		$fhparts++;
+	    } else {
+		push(@parts, $head . $content);
+	    }
+	}
+    }
+    return "" unless @parts;
+
+    my $content;
+    if ($fhparts) {
+	$boundary = boundary(10) # hopefully enough randomness
+	    unless $boundary;
+
+	# add the boundaries to the @parts array
+	for (1..@parts-1) {
+	    splice(@parts, $_*2-1, 0, "$CRLF--$boundary$CRLF");
+	}
+	unshift(@parts, "--$boundary$CRLF");
+	push(@parts, "$CRLF--$boundary--$CRLF");
+
+	# set up a closure that will return content piecemeal
+	$content = sub {
+	    for (;;) {
+		return unless @parts;
+		my $p = shift @parts;
+		unless (ref $p) {
+		    $p .= shift @parts while @parts && !ref($parts[0]);
+		    return $p;
+		}
+		my($buf, $fh) = @$p;
+		my $n = read($fh, $buf, 2048, length($buf));
+		if ($n) {
+		    unshift(@parts, ["", $fh]);
+		} else {
+		    close($fh);
+		}
+		return $buf if length $buf;
+	    }
+	};
+
+    } else {
+	$boundary = boundary() unless $boundary;
+
+	my $bno = 0;
+      CHECK_BOUNDARY:
+	{
+	    for (@parts) {
+		if (index($_, "--$boundary") >= 0) {
+		    # must have a better boundary
+		    #warn "Need something better that '$boundary' as boundary\n";
+		    $boundary = boundary(++$bno);
+		    redo CHECK_BOUNDARY;
+		}
+	    }
+	    last;
+	}
+	$content = "--$boundary$CRLF" .
+	           join("$CRLF--$boundary$CRLF", @parts) .
+		   "$CRLF--$boundary--$CRLF";
+    }
+
+    wantarray ? ($content, $boundary) : $content;
+}
+
+
+sub boundary
+{
+    my $size = shift || return "000";
+    require MIME::Base64;
+    my $b = MIME::Base64::encode(join("", map chr(rand(256)), 1..$size*3), "");
+    $b =~ s/[\W]/X/g;  # ensure alnum only
+    $b;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+HTTP::Request::Common - Construct common HTTP::Request objects
+
+=head1 SYNOPSIS
+
+  use HTTP::Request::Common;
+  $ua = LWP::UserAgent->new;
+  $ua->request(GET 'http://www.sn.no/');
+  $ua->request(POST 'http://somewhere/foo', [foo => bar, bar => foo]);
+
+=head1 DESCRIPTION
+
+This module provide functions that return newly created HTTP::Request
+objects.  These functions are usually more convenient to use than the
+standard HTTP::Request constructor for these common requests.  The
+following functions are provided.
+
+=over 4
+
+=item GET $url, Header => Value,...
+
+The GET() function returns a HTTP::Request object initialized with the
+GET method and the specified URL.  Without additional arguments it
+is exactly equivalent to the following call
+
+  HTTP::Request->new(GET => $url)
+
+but is less clutter.  It also reads better when used together with the
+LWP::UserAgent->request() method:
+
+  my $ua = new LWP::UserAgent;
+  my $res = $ua->request(GET 'http://www.sn.no')
+  if ($res->is_success) { ...
+
+You can also initialize the header values in the request by specifying
+some key/value pairs as optional arguments.  For instance:
+
+  $ua->request(GET 'http://www.sn.no',
+	           If_Match => 'foo',
+                   From     => 'gisle@aas.no',
+              );
+
+A header key called 'Content' is special and when seen the value will
+initialize the content part of the request instead of setting a header.
+
+=item HEAD $url, [Header => Value,...]
+
+Like GET() but the method in the request is HEAD.
+
+=item PUT $url, [Header => Value,...]
+
+Like GET() but the method in the request is PUT.
+
+=item POST $url, [$form_ref], [Header => Value,...]
+
+This works mostly like GET() with POST as method, but this function
+also takes a second optional array or hash reference parameter
+($form_ref).  This argument can be used to pass key/value pairs for
+the form content.  By default we will initialize a request using the
+C<application/x-www-form-urlencoded> content type.  This means that
+you can emulate a HTML E<lt>form> POSTing like this:
+
+  POST 'http://www.perl.org/survey.cgi',
+       [ name   => 'Gisle Aas',
+         email  => 'gisle@aas.no',
+         gender => 'M',
+         born   => '1964',
+         perc   => '3%',
+       ];
+
+This will create a HTTP::Request object that looks like this:
+
+  POST http://www.perl.org/survey.cgi
+  Content-Length: 66
+  Content-Type: application/x-www-form-urlencoded
+
+  name=Gisle%20Aas&email=gisle%40aas.no&gender=M&born=1964&perc=3%25
+
+The POST method also supports the C<multipart/form-data> content used
+for I<Form-based File Upload> as specified in RFC 1867.  You trigger
+this content format by specifying a content type of C<'form-data'> as
+one of the request headers.  If one of the values in the $form_ref is
+an array reference, then it is treated as a file part specification
+with the following interpretation:
+
+  [ $file, $filename, Header => Value... ]
+
+The first value in the array ($file) is the name of a file to open.
+This file will be read an its content placed in the request.  The
+routine will croak if the file can't be opened.  Use an C<undef> as $file
+value if you want to specify the content directly.  The $filename is
+the filename to report in the request.  If this value is undefined,
+then the basename of the $file will be used.  You can specify an empty
+string as $filename if you don't want any filename in the request.
+
+Sending my F<~/.profile> to the survey used as example above can be
+achieved by this:
+
+  POST 'http://www.perl.org/survey.cgi',
+       Content_Type => 'form-data',
+       Content      => [ name  => 'Gisle Aas',
+                         email => 'gisle@aas.no',
+                         gender => 'M',
+                         born   => '1964',
+                         init   => ["$ENV{HOME}/.profile"],
+                       ]
+
+This will create a HTTP::Request object that almost looks this (the
+boundary and the content of your F<~/.profile> is likely to be
+different):
+
+  POST http://www.perl.org/survey.cgi
+  Content-Length: 388
+  Content-Type: multipart/form-data; boundary="6G+f"
+
+  --6G+f
+  Content-Disposition: form-data; name="name"
+  
+  Gisle Aas
+  --6G+f
+  Content-Disposition: form-data; name="email"
+  
+  gisle@aas.no
+  --6G+f
+  Content-Disposition: form-data; name="gender"
+  
+  M
+  --6G+f
+  Content-Disposition: form-data; name="born"
+  
+  1964
+  --6G+f
+  Content-Disposition: form-data; name="init"; filename=".profile"
+  Content-Type: text/plain
+  
+  PATH=/local/perl/bin:$PATH
+  export PATH
+
+  --6G+f--
+
+If you set the $DYNAMIC_FILE_UPLOAD variable (exportable) to some TRUE
+value, then you get back a request object with a subroutine closure as
+the content attribute.  This subroutine will read the content of any
+files on demand and return it in suitable chunks.  This allow you to
+upload arbitrary big files without using lots of memory.  You can even
+upload infinite files like F</dev/audio> if you wish.  Another
+difference is that there will be no Content-Length header defined for
+the request if you use this feature.  Not all servers (or server
+applications) like this.
+
+=back
+
+=head1 SEE ALSO
+
+L<HTTP::Request>, L<LWP::UserAgent>
+
+
+=head1 COPYRIGHT
+
+Copyright 1997-1998, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Response.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Response.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Response.pm	(revision 16561)
@@ -0,0 +1,396 @@
+#
+# $Id$
+
+package HTTP::Response;
+
+
+=head1 NAME
+
+HTTP::Response - Class encapsulating HTTP Responses
+
+=head1 SYNOPSIS
+
+ require HTTP::Response;
+
+=head1 DESCRIPTION
+
+The C<HTTP::Response> class encapsulate HTTP style responses.  A
+response consist of a response line, some headers, and a (potential
+empty) content. Note that the LWP library will use HTTP style
+responses also for non-HTTP protocol schemes.
+
+Instances of this class are usually created and returned by the
+C<request()> method of an C<LWP::UserAgent> object:
+
+ #...
+ $response = $ua->request($request)
+ if ($response->is_success) {
+     print $response->content;
+ } else {
+     print $response->error_as_HTML;
+ }
+
+C<HTTP::Response> is a subclass of C<HTTP::Message> and therefore
+inherits its methods.  The inherited methods often used are header(),
+push_header(), remove_header(), headers_as_string(), and content().
+The header convenience methods are also available.  See
+L<HTTP::Message> for details.
+
+The following additional methods are available:
+
+=over 4
+
+=cut
+
+
+require HTTP::Message;
+@ISA = qw(HTTP::Message);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use HTTP::Status ();
+use URI::URL ();
+use strict;
+
+
+=item $r = HTTP::Response->new($rc, [$msg, [$header, [$content]]])
+
+Constructs a new C<HTTP::Response> object describing a response with
+response code C<$rc> and optional message C<$msg>.  The message is a
+short human readable single line string that explains the response
+code.
+
+=cut
+
+sub new
+{
+    my($class, $rc, $msg, $header, $content) = @_;
+    my $self = $class->SUPER::new($header, $content);
+    $self->code($rc);
+    $self->message($msg);
+    $self;
+}
+
+
+sub clone
+{
+    my $self = shift;
+    my $clone = bless $self->SUPER::clone, ref($self);
+    $clone->code($self->code);
+    $clone->message($self->message);
+    $clone->request($self->request->clone) if $self->request;
+    # we don't clone previous
+    $clone;
+}
+
+=item $r->code([$code])
+
+=item $r->message([$message])
+
+=item $r->request([$request])
+
+=item $r->previous([$previousResponse])
+
+These methods provide public access to the member variables.  The
+first two containing respectively the response code and the message
+of the response.
+
+The request attribute is a reference the request that gave this
+response.  It does not have to be the same request as passed to the
+$ua->request() method, because there might have been redirects and
+authorization retries in between.
+
+The previous attribute is used to link together chains of responses.
+You get chains of responses if the first response is redirect or
+unauthorized.
+
+=cut
+
+sub code      { shift->_elem('_rc',      @_); }
+sub message   { shift->_elem('_msg',     @_); }
+sub previous  { shift->_elem('_previous',@_); }
+sub request   { shift->_elem('_request', @_); }
+
+=item $r->status_line
+
+Returns the string "E<lt>code> E<lt>message>".  If the message attribute
+is not set then the official name of E<lt>code> (see L<HTTP::Status>)
+is substituted.
+
+=cut
+
+sub status_line
+{
+    my $self = shift;
+    my $code = $self->{'_rc'}  || "000";
+    my $mess = $self->{'_msg'} || HTTP::Status::status_message($code) || "?";
+    return "$code $mess";
+}
+
+=item $r->base
+
+Returns the base URL for this response.  The return value will be a
+reference to a URI::URL object.
+
+The base URL is obtained from one the following sources (in priority
+order):
+
+=over 4
+
+=item 1.
+
+Embedded in the document content, for instance <BASE HREF="...">
+in HTML documents.
+
+=item 2.
+
+A "Content-Base:" or a "Content-Location:" header in the response.
+
+For backwards compatability with older HTTP implementations we will
+also look for the "Base:" header.
+
+
+=item 3.
+
+The URL used to request this response. This might not be the original
+URL that was passed to $ua->request() method, because we might have
+received some redirect responses first.
+
+=back
+
+When the LWP protocol modules produce the HTTP::Response object, then
+any base URL embedded in the document (step 1) will already have
+initialized the "Content-Base:" header. This means that this method
+only perform the last 2 steps (the content is not always available
+either).
+
+=cut
+
+sub base
+{
+    my $self = shift;
+    my $base = $self->header('Content-Base')     ||  # HTTP/1.1
+               $self->header('Content-Location') ||  # HTTP/1.1
+               $self->header('Base')             ||  # backwards compatability HTTP/1.0
+               $self->request->url;
+    $base = URI::URL->new($base) unless ref $base;
+    $base;
+}
+
+
+=item $r->as_string
+
+Method returning a textual representation of the response.  Mainly
+useful for debugging purposes. It takes no arguments.
+
+=cut
+
+sub as_string
+{
+    require HTTP::Status;
+    my $self = shift;
+    my @result;
+    #push(@result, "---- $self ----");
+    my $code = $self->code;
+    my $status_message = HTTP::Status::status_message($code) || "Unknown code";
+    my $message = $self->message || "";
+
+    my $status_line = "$code";
+    my $proto = $self->protocol;
+    $status_line = "$proto $status_line" if $proto;
+    $status_line .= " ($status_message)" if $status_message ne $message;
+    $status_line .= " $message";
+    push(@result, $status_line);
+    push(@result, $self->headers_as_string);
+    my $content = $self->content;
+    if (defined $content) {
+	push(@result, $content);
+    }
+    #push(@result, ("-" x 40));
+    join("\n", @result, "");
+}
+
+=item $r->is_info
+
+=item $r->is_success
+
+=item $r->is_redirect
+
+=item $r->is_error
+
+These methods indicate if the response was informational, sucessful, a
+redirection, or an error.
+
+=cut
+
+sub is_info     { HTTP::Status::is_info     (shift->{'_rc'}); }
+sub is_success  { HTTP::Status::is_success  (shift->{'_rc'}); }
+sub is_redirect { HTTP::Status::is_redirect (shift->{'_rc'}); }
+sub is_error    { HTTP::Status::is_error    (shift->{'_rc'}); }
+
+
+=item $r->error_as_HTML()
+
+Return a string containing a complete HTML document indicating what
+error occurred.  This method should only be called when $r->is_error
+is TRUE.
+
+=cut
+
+sub error_as_HTML
+{
+    my $self = shift;
+    my $title = 'An Error Occurred';
+    my $body  = $self->status_line;
+    return <<EOM;
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+<H1>$title</h1>
+$body
+</BODY>
+</HTML>
+EOM
+}
+
+
+=item $r->current_age
+
+This function will calculate the "current age" of the response as
+specified by E<lt>draft-ietf-http-v11-spec-07> section 13.2.3.  The
+age of a response is the time since it was sent by the origin server.
+The returned value is a number representing the age in seconds.
+
+=cut
+
+sub current_age
+{
+    my $self = shift;
+    # Implementation of <draft-ietf-http-v11-spec-07> section 13.2.3
+    # (age calculations)
+    my $response_time = $self->client_date;
+    my $date = $self->date;
+
+    my $age = 0;
+    if ($response_time && $date) {
+	$age = $response_time - $date;  # apparent_age
+	$age = 0 if $age < 0;
+    }
+
+    my $age_v = $self->header('Age');
+    if ($age_v && $age_v > $age) {
+	$age = $age_v;   # corrected_received_age
+    }
+
+    my $request = $self->request;
+    if ($request) {
+	my $request_time = $request->date;
+	if ($request_time) {
+	    # Add response_delay to age to get 'corrected_initial_age'
+	    $age += $response_time - $request_time;
+	}
+    }
+    if ($response_time) {
+	$age += time - $response_time;
+    }
+    return $age;
+}
+
+
+=item $r->freshness_lifetime
+
+This function will calculate the "freshness lifetime" of the response
+as specified by E<lt>draft-ietf-http-v11-spec-07> section 13.2.4.  The
+"freshness lifetime" is the length of time between the generation of a
+response and its expiration time.  The returned value is a number
+representing the freshness lifetime in seconds.
+
+If the response does not contain an "Expires" or a "Cache-Control"
+header, then this function will apply some simple heuristic based on
+'Last-Modified' to determine a suitable lifetime.
+
+=cut
+
+sub freshness_lifetime
+{
+    my $self = shift;
+
+    # First look for the Cache-Control: max-age=n header
+    my @cc = $self->header('Cache-Control');
+    if (@cc) {
+	my $cc;
+	for $cc (@cc) {
+	    my $cc_dir;
+	    for $cc_dir (split(/\s*,\s*/, $cc)) {
+		if ($cc_dir =~ /max-age\s*=\s*(\d+)/i) {
+		    return $1;
+		}
+	    }
+	}
+    }
+
+    # Next possibility is to look at the "Expires" header
+    my $date = $self->date || $self->client_date || time;      
+    my $expires = $self->expires;
+    unless ($expires) {
+	# Must apply heuristic expiration
+	my $last_modified = $self->last_modified;
+	if ($last_modified) {
+	    my $h_exp = ($date - $last_modified) * 0.10;  # 10% since last-mod
+	    if ($h_exp < 60) {
+		return 60;  # minimum
+	    } elsif ($h_exp > 24 * 3600) {
+		# Should give a warning if more than 24 hours according to
+		# <draft-ietf-http-v11-spec-07> section 13.2.4, but I don't
+		# know how to do it from this function interface, so I just
+		# make this the maximum value.
+		return 24 * 3600;
+	    }
+	    return $h_exp;
+	} else {
+	    return 3600;  # 1 hour is fallback when all else fails
+	}
+    }
+    return $expires - $date;
+}
+
+
+=item $r->is_fresh
+
+Returns TRUE if the response is fresh, based on the values of
+freshness_lifetime() and current_age().  If the response is not longer
+fresh, then it has to be refetched or revalidated by the origin
+server.
+
+=cut
+
+sub is_fresh
+{
+    my $self = shift;
+    $self->freshness_lifetime > $self->current_age;
+}
+
+
+=item $r->fresh_until
+
+Returns the time when this entiy is no longer fresh.
+
+=cut
+
+sub fresh_until
+{
+    my $self = shift;
+    return $self->freshness_lifetime - $self->current_age + time;
+}
+
+1;
+
+=back 
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Status.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Status.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/HTTP/Status.pm	(revision 16561)
@@ -0,0 +1,233 @@
+#
+# $Id$
+
+package HTTP::Status;
+
+use strict;
+require 5.002;   # becase we use prototypes
+
+=head1 NAME
+
+HTTP::Status - HTTP Status code processing
+
+=head1 SYNOPSIS
+
+ use HTTP::Status;
+
+ if ($rc != RC_OK) {
+     print status_message($rc), "\n";
+ }
+
+ if (is_success($rc)) { ... }
+ if (is_error($rc)) { ... }
+ if (is_redirect($rc)) { ... }
+
+=head1 DESCRIPTION
+
+I<HTTP::Status> is a library of routines for defining and
+classification of HTTP status codes for libwww-perl.  Status codes are
+used to encode the overall outcome of a HTTP response message.  Codes
+correspond to those defined in F<draft-ietf-http-v11-spec-rev-03> (an
+update to RFC 2068).
+
+=head1 CONSTANTS
+
+The following constant functions can be used as mnemonic status code
+names:
+
+   RC_CONTINUE				(100)
+   RC_SWITCHING_PROTOCOLS		(101)
+
+   RC_OK				(200)
+   RC_CREATED				(201)
+   RC_ACCEPTED				(202)
+   RC_NON_AUTHORITATIVE_INFORMATION	(203)
+   RC_NO_CONTENT			(204)
+   RC_RESET_CONTENT			(205)
+   RC_PARTIAL_CONTENT			(206)
+
+   RC_MULTIPLE_CHOICES			(300)
+   RC_MOVED_PERMANENTLY			(301)
+   RC_FOUND				(302)
+   RC_SEE_OTHER				(303)
+   RC_NOT_MODIFIED			(304)
+   RC_USE_PROXY				(305)
+   RC_TEMPORARY_REDIRECT		(307)
+
+   RC_BAD_REQUEST			(400)
+   RC_UNAUTHORIZED			(401)
+   RC_PAYMENT_REQUIRED			(402)
+   RC_FORBIDDEN				(403)
+   RC_NOT_FOUND				(404)
+   RC_METHOD_NOT_ALLOWED		(405)
+   RC_NOT_ACCEPTABLE			(406)
+   RC_PROXY_AUTHENTICATION_REQUIRED	(407)
+   RC_REQUEST_TIMEOUT			(408)
+   RC_CONFLICT				(409)
+   RC_GONE				(410)
+   RC_LENGTH_REQUIRED			(411)
+   RC_PRECONDITION_FAILED		(412)
+   RC_REQUEST_ENTITY_TOO_LARGE		(413)
+   RC_REQUEST_URI_TOO_LARGE		(414)
+   RC_UNSUPPORTED_MEDIA_TYPE		(415)
+   RC_REQUEST_RANGE_NOT_SATISFIABLE     (416)
+   RC_EXPECTATION_FAILED		(417)
+
+   RC_INTERNAL_SERVER_ERROR		(500)
+   RC_NOT_IMPLEMENTED			(501)
+   RC_BAD_GATEWAY			(502)
+   RC_SERVICE_UNAVAILABLE		(503)
+   RC_GATEWAY_TIMEOUT			(504)
+   RC_HTTP_VERSION_NOT_SUPPORTED	(505)
+
+=cut
+
+#####################################################################
+
+use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(is_info is_success is_redirect is_error status_message);
+@EXPORT_OK = qw(is_client_error is_server_error);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+# Note also addition of mnemonics to @EXPORT below
+
+my %StatusCode = (
+    100 => 'Continue',
+    101 => 'Switching Protocols',
+    200 => 'OK',
+    201 => 'Created',
+    202 => 'Accepted',
+    203 => 'Non-Authoritative Information',
+    204 => 'No Content',
+    205 => 'Reset Content',
+    206 => 'Partial Content',
+    300 => 'Multiple Choices',
+    301 => 'Moved Permanently',
+    302 => 'Found',
+    303 => 'See Other',
+    304 => 'Not Modified',
+    305 => 'Use Proxy',
+    307 => 'Temporary Redirect',
+    400 => 'Bad Request',
+    401 => 'Unauthorized',
+    402 => 'Payment Required',
+    403 => 'Forbidden',
+    404 => 'Not Found',
+    405 => 'Method Not Allowed',
+    406 => 'Not Acceptable',
+    407 => 'Proxy Authentication Required',
+    408 => 'Request Timeout',
+    409 => 'Conflict',
+    410 => 'Gone',
+    411 => 'Length Required',
+    412 => 'Precondition Failed',
+    413 => 'Request Entity Too Large',
+    414 => 'Request-URI Too Large',
+    415 => 'Unsupported Media Type',
+    416 => 'Request Range Not Satisfiable',
+    417 => 'Expectation Failed',
+    500 => 'Internal Server Error',
+    501 => 'Not Implemented',
+    502 => 'Bad Gateway',
+    503 => 'Service Unavailable',
+    504 => 'Gateway Timeout',
+    505 => 'HTTP Version Not Supported',
+);
+
+my $mnemonicCode = '';
+my ($code, $message);
+while (($code, $message) = each %StatusCode) {
+    # create mnemonic subroutines
+    $message =~ tr/a-z \-/A-Z__/;
+    $mnemonicCode .= "sub RC_$message () { $code }\t";
+    # make them exportable
+    $mnemonicCode .= "push(\@EXPORT, 'RC_$message');\n";
+}
+# warn $mnemonicCode; # for development
+eval $mnemonicCode; # only one eval for speed
+die if $@;
+
+# backwards compatibility
+*RC_MOVED_TEMPORARILY = \&RC_FOUND;  # 302 was renamed in the standard
+push(@EXPORT, "RC_MOVED_TEMPORARILY");
+
+
+=head1 FUNCTIONS
+
+The following additional functions are provided.  Most of them are
+exported by default.
+
+=over 4
+
+=item status_message($code)
+
+The status_message() function will translate status codes to human
+readable strings. The string is the same as found in the constant
+names above.  If the $code is unknown, then C<undef> is returned.
+
+=cut
+
+sub status_message ($)
+{
+    $StatusCode{$_[0]};
+}
+
+=item is_info($code)
+
+Return TRUE if C<$code> is an I<Informational> status code.  This
+class of status code indicates a provisional response which can't have
+any content.
+
+=item is_success($code)
+
+Return TRUE if C<$code> is a I<Successful> status code.
+
+=item is_redirect($code)
+
+Return TRUE if C<$code> is a I<Redirection> status code. This class of
+status code indicates that further action needs to be taken by the
+user agent in order to fulfill the request.
+
+=item is_error($code)
+
+Return TRUE if C<$code> is an I<Error> status code.  The function
+return TRUE for both client error or a server error status codes.
+
+=item is_client_error($code)
+
+Return TRUE if C<$code> is an I<Client Error> status code. This class
+of status code is intended for cases in which the client seems to have
+erred.
+
+This function is B<not> exported by default.
+
+=item is_server_error($code)
+
+Return TRUE if C<$code> is an I<Server Error> status code. This class
+of status codes is intended for cases in which the server is aware
+that it has erred or is incapable of performing the request.
+
+This function is B<not> exported by default.
+
+=back
+
+=cut
+
+sub is_info         ($) { $_[0] >= 100 && $_[0] < 200; }
+sub is_success      ($) { $_[0] >= 200 && $_[0] < 300; }
+sub is_redirect     ($) { $_[0] >= 300 && $_[0] < 400; }
+sub is_error        ($) { $_[0] >= 400 && $_[0] < 600; }
+sub is_client_error ($) { $_[0] >= 400 && $_[0] < 500; }
+sub is_server_error ($) { $_[0] >= 500 && $_[0] < 600; }
+
+1;
+
+=head1 BUGS
+
+Wished @EXPORT_OK had been used instead of @EXPORT in the beginning.
+Now too much is exported by default.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP.pm	(revision 16561)
@@ -0,0 +1,593 @@
+#
+# $Id$
+
+package LWP;
+
+$VERSION = "5.36";
+sub Version { $VERSION; }
+
+require 5.004;
+require LWP::UserAgent;  # this should load everything you need
+
+1;
+
+__END__
+
+=head1 NAME
+
+LWP - Library for WWW access in Perl
+
+=head1 SYNOPSIS
+
+  use LWP;
+  print "This is libwww-perl-$LWP::VERSION\n";
+
+
+=head1 DESCRIPTION
+
+Libwww-perl is a collection of Perl modules which provides a simple
+and consistent programming interface (API) to the World-Wide Web.  The
+main focus of the library is to provide classes and functions that
+allow you to write WWW clients, thus libwww-perl said to be a WWW
+client library. The library also contain modules that are of more
+general use.
+
+Most modules in this library are object oriented.  The user
+agent, requests sent and responses received from the WWW server are
+all represented by objects.  This makes a simple and powerful
+interface to these services.  The interface should be easy to extend
+and customize for your needs.
+
+The main features of the library are:
+
+=over 3
+
+=item *
+
+Contains various reusable components (modules) that can be
+used separately or together.
+
+=item *
+
+Provides an object oriented model of HTTP-style communication.  Within
+this framework we currently support access to http, https, gopher, ftp, news,
+file, and mailto resources.
+
+=item *
+
+The library be used through the full object oriented interface or
+through a very simple procedural interface.
+
+=item *
+
+Support the basic and digest authorization schemes.
+
+=item *
+
+Transparent redirect handling.
+
+=item *
+
+Supports access through proxy servers.
+
+=item *
+
+URL handling (both absolute and relative URLs are supported).
+
+=item *
+
+A parser for F<robots.txt> files and a framework for constructing robots.
+
+=item *
+
+The library can cooperate with Tk.  A simple Tk-based GUI browser
+called 'tkweb' is distributed with the Tk extension for perl.
+
+=item *
+
+An implementation of the HTTP content negotiation algorithm that can
+be used both in protocol modules and in server scripts (like CGI
+scripts).
+
+=item *
+
+It can deal with HTTP cookies.
+
+=item *
+
+A simple command line client application called C<lwp-request>.
+
+=back
+
+
+=head1 HTTP STYLE COMMUNICATION
+
+
+The libwww-perl library is based on HTTP style communication. This
+section try to describe what that means.
+
+Let us start with this quote from the HTTP specification document
+<URL:http://www.w3.org/pub/WWW/Protocols/>:
+
+=over 3
+
+=item
+
+The HTTP protocol is based on a request/response paradigm. A client
+establishes a connection with a server and sends a request to the
+server in the form of a request method, URI, and protocol version,
+followed by a MIME-like message containing request modifiers, client
+information, and possible body content. The server responds with a
+status line, including the message's protocol version and a success or
+error code, followed by a MIME-like message containing server
+information, entity meta-information, and possible body content.
+
+=back
+
+What this means to libwww-perl is that communication always take place
+through these steps: First a I<request> object is created and
+configured. This object is then passed to a server and we get a
+I<response> object in return that we can examine. A request is always
+independent of any previous requests, i.e. the service is stateless.
+The same simple model is used for any kind of service we want to
+access.
+
+For example, if we want to fetch a document from a remote file server,
+then we send it a request that contains a name for that document and
+the response will contain the document itself.  If we access a search
+engine, then the content of the request will contain the query
+parameters and the response will contain the query result.  If we want
+to send a mail message to somebody then we send a request object which
+contains our message to the mail server and the response object will
+contain an acknowledgment that tells us that the message has been
+accepted and will be forwarded to the recipient(s).
+
+It is as simple as that!
+
+
+=head2 The Request Object
+
+The request object has the class name C<HTTP::Request> in
+libwww-perl. The fact that the class name use C<HTTP::> as a name
+prefix only implies that we use the HTTP model of communication. It
+does not limit the kind of services we can try to pass this I<request>
+to.  For instance, we will send C<HTTP::Request>s both to ftp and
+gopher servers, as well as to the local file system.
+
+The main attributes of the request objects are:
+
+=over 3
+
+=item *
+
+The B<method> is a short string that tells what kind of
+request this is.  The most used methods are B<GET>, B<PUT>,
+B<POST> and B<HEAD>.
+
+=item *
+
+The B<url> is a string denoting the protocol, server and
+the name of the "document" we want to access.  The B<url> might
+also encode various other parameters.
+
+=item *
+
+The B<headers> contain additional information about the
+request and can also used to describe the content.  The headers
+is a set of keyword/value pairs.
+
+=item *
+
+The B<content> is an arbitrary amount of data.
+
+=back
+
+=head2 The Response Object
+
+The response object has the class name C<HTTP::Response> in
+libwww-perl.  The main attributes of objects of this class are:
+
+=over 3
+
+=item *
+
+The B<code> is a numerical value that encode the overall
+outcome of the request.
+
+=item *
+
+The B<message> is a short (human readable) string that
+corresponds to the I<code>.
+
+=item *
+
+The B<headers> contain additional information about the
+response and they also describe the content.
+
+=item *
+
+The B<content> is an arbitrary amount of data.
+
+=back
+
+Since we don't want to handle all possible I<code> values directly in
+our programs, the libwww-perl response object have methods that can be
+used to query what kind of response this is.  The most commonly used
+response classification methods are:
+
+=over 3
+
+=item is_success()
+
+The request was was successfully received, understood or accepted.
+
+=item is_error()
+
+The request failed.  The server or the resource might not be
+available, access to the resource might be denied or other things might
+have failed for some reason.
+
+=back
+
+=head2 The User Agent
+
+Let us assume that we have created a I<request> object. What do we
+actually do with it in order to receive a I<response>?
+
+The answer is that you pass it on to a I<user agent> object and this
+object will take care of all the things that need to be done
+(low-level communication and error handling). The user agent will give
+you back a I<response> object. The user agent represents your
+application on the network and it provides you with an interface that
+can accept I<requests> and will return I<responses>.
+
+You should think about the user agent as an interface layer between
+your application code and the network.  Through this interface you are
+able to access the various servers on the network.
+
+The libwww-perl class name for the user agent is
+C<LWP::UserAgent>. Every libwww-perl application that wants to
+communicate should create at least one object of this kind. The main
+method provided by this object is request(). This method takes an
+C<HTTP::Request> object as argument and will (eventually) return a
+C<HTTP::Response> object.
+
+The user agent has many other attributes that lets you
+configure how it will interact with the network and with your
+application code.
+
+=over 3
+
+=item *
+
+The B<timeout> specify how much time we give remote servers in
+creating responses before the library disconnect and creates an
+internal I<timeout> response.
+
+=item *
+
+The B<agent> specify the name that your application should use when it
+presents itself on the network.
+
+=item *
+
+The B<from> attribute can be set to the e-mail address of the person
+responsible for running the application.  If this is set, then the
+address will be sent to the servers with every request.
+
+=item *
+
+The B<parse_head> specify whether we should initialize response
+headers from the E<lt>head> section of HTML documents.
+
+=item *
+
+The B<proxy> and B<no_proxy> specify if and when communication should
+go through a proxy server. <URL:http://www.w3.org/pub/WWW/Proxies/>
+
+=item *
+
+The B<credentials> provide a way to set up user names and
+passwords that is needed to access certain services.
+
+=back
+
+Many applications would want even more control over how they interact
+with the network and they get this by specializing the
+C<LWP::UserAgent> by sub-classing.  The library provide a
+specialization called C<LWP::RobotUA> that is used by robot
+applications.
+
+=head2 An Example
+
+This example shows how the user agent, a request and a response are
+represented in actual perl code:
+
+  # Create a user agent object
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+  $ua->agent("AgentName/0.1 " . $ua->agent);
+
+  # Create a request
+  my $req = new HTTP::Request POST => 'http://www.perl.com/cgi-bin/BugGlimpse';
+  $req->content_type('application/x-www-form-urlencoded');
+  $req->content('match=www&errors=0');
+
+  # Pass request to the user agent and get a response back
+  my $res = $ua->request($req);
+
+  # Check the outcome of the response
+  if ($res->is_success) {
+      print $res->content;
+  } else {
+      print "Bad luck this time\n";
+  }
+
+The $ua is created once when the application starts up.  New request
+objects are normally created for each request sent.
+
+
+=head1 NETWORK SUPPORT
+
+This section goes through the various protocol schemes and describe
+the HTTP style methods that are supported and the headers that might
+have any effect.
+
+For all requests, a "User-Agent" header is added and initialized from
+the $ua->agent value before the request is handed to the network
+layer.  In the same way, a "From" header is initialized from the
+$ua->from value.
+
+For all responses, the library will add a header called "Client-Date".
+This header will encode the time when the response was received by
+your application.  This format and semantics of the header is just
+like the server created "Date" header.  You can also encounter other
+"Client-XXX" headers.  They are all generated by the library
+internally and not something really passed on from the servers.
+
+=head2 HTTP Requests
+
+HTTP request are really just handed off to an HTTP server and it will
+decide what happens.  Few servers implement methods beside the usual
+"GET", "HEAD", "POST" and "PUT" but CGI-scripts can really implement
+any method they like.
+
+If the server is not available then the library will generate an
+internal error response.
+
+The library automatically adds a "Host" and a "Content-Length" header
+to the HTTP request before it is sent over the network.
+
+For GET request you might want to add the "If-Modified-Since" header
+to make the request conditional.
+
+For POST request you should add the "Content-Type" header.  When you
+try to emulate HTML E<lt>FORM> handling you should usually let the value
+of the "Content-Type" header be "application/x-www-form-urlencoded".
+See L<lwpcook> for examples of this.
+
+The libwww-perl HTTP implementation currently support the HTTP/1.0
+protocol.  HTTP/0.9 servers are also handled correctly.
+
+The library allows you to access proxy server through HTTP.  This
+means that you can set up the library to forward all types of request
+through the HTTP protocol module.  See L<LWP::UserAgent> for
+documentation of this.
+
+
+=head2 HTTPS Requests
+
+HTTPS requests are HTTP requests over an encrypted network connection
+using the SSL protocol developed by Netscape.  Everything about HTTP
+requests above also hold for HTTPS requests.  In addition the library
+will add the headers "Client-SSL-Cipher", "Client-SSL-Cert-Subject" and
+"Client-SSL-Cert-Issuer" to the response.  These headers denote the
+encryption method used and the name of the server owner.
+
+The request can contain the header "If-SSL-Cert-Subject" in order to
+make the request conditional on the content of the server certificate.
+If the certificate subject does not match, no request is sent to the
+server and an internally generated error response is returned.  The
+value of the "If-SSL-Cert-Subject" header is interpreted as a Perl
+regular expression.
+
+
+=head2 FTP Requests
+
+The library currently support GET, HEAD and PUT requests.  GET will
+retrieve a file or a directory listing from an FTP server.  PUT will
+store a file on a ftp server.
+
+You can specify a ftp account for servers that want this in addition
+user name and password.  This is specified by passing an "Account"
+header in the request.
+
+User name/password can be specified using basic authorization or be
+encoded in the URL.  Bad logins return an UNAUTHORIZED response with
+"WWW-Authenticate: Basic" and can be treated as basic authorization
+for HTTP.
+
+The library support ftp ASCII transfer mode by specifying the "type=a"
+parameter in the URL.
+
+Directory listings are by default returned unprocessed (as returned
+from the ftp server) with the content media type reported to be
+"text/ftp-dir-listing". The C<File::Listing> module provide functionality
+for parsing of these directory listing.
+
+The ftp module is also able to convert directory listings to HTML and
+this can be requested via the standard HTTP content negotiation
+mechanisms (add an "Accept: text/html" header in the request if you
+want this).
+
+The normal file retrievals, the "Content-Type" is guessed based on the
+file name suffix. See L<LWP::MediaTypes>.
+
+The "If-Modified-Since" request header works for servers that implement
+the MDTM command.  It will probably not work for directory listings though.
+
+Example:
+
+  $req = HTTP::Request->new(GET => 'ftp://me:passwd@ftp.some.where.com/');
+  $req->header(Accept => "text/html, */*;q=0.1");
+
+=head2 News Requests
+
+Access to the USENET News system is implemented through the NNTP
+protocol.  The name of the news server is obtained from the
+NNTP_SERVER environment variable and defaults to "news".  It is not
+possible to specify the hostname of the NNTP server in the news:-URLs.
+
+The library support GET and HEAD to retrieve news articles through the
+NNTP protocol.  You can also post articles to newsgroups by using
+(surprise!) the POST method.
+
+GET on newsgroups is not implemented yet.
+
+Examples:
+
+  $req = HTTP::Request->new(GET => 'news:abc1234@a.sn.no');
+
+  $req = HTTP::Request->new(POST => 'news:comp.lang.perl.test');
+  $req->header(Subject => 'This is a test',
+               From    => 'me@some.where.org');
+  $req->content(<<EOT);
+  This is the content of the message that we are sending to
+  the world.
+  EOT
+
+
+=head2 Gopher Request
+
+The library supports the GET and HEAD method for gopher request.  All
+request header values are ignored.  HEAD cheats and will return a
+response without even talking to server.
+
+Gopher menus are always converted to HTML.
+
+The response "Content-Type" is generated from the document type
+encoded (as the first letter) in the request URL path itself.
+
+Example:
+
+  $req = HTTP::Request->new(GET => 'gopher://gopher.sn.no/');
+
+
+
+=head2 File Request
+
+The library supports GET and HEAD methods for file requests.  The
+"If-Modified-Since" header is supported.  All other headers are
+ignored.  The I<host> component of the file URL must be empty or set
+to "localhost".  Any other I<host> value will be treated as an error.
+
+Directories are always converted to an HTML document.  For normal
+files, the "Content-Type" and "Content-Encoding" in the response are
+guessed based on the file suffix.
+
+Example:
+
+  $req = HTTP::Request->new(GET => 'file:/etc/passwd');
+
+
+=head2 Mailto Request
+
+You can send (aka "POST") mail messages using the library.  All
+headers specified for the request are passed on to the mail system.
+The "To" header is initialized from the mail address in the URL.
+
+Example:
+
+  $req = HTTP::Request->new(POST => 'mailto:libwww-perl-request@ics.uci.edu');
+  $req->header(Subject => "subscribe");
+  $req->content("Please subscribe me to the libwww-perl mailing list!\n");
+
+
+=head1 OVERVIEW OF CLASSES AND PACKAGES
+
+This table should give you a quick overview of the classes provided by the
+library. Indentation shows class inheritance.
+
+ LWP::MemberMixin   -- Access to member variables of Perl5 classes
+   LWP::UserAgent   -- WWW user agent class
+     LWP::RobotUA   -- When developing a robot applications
+   LWP::Protocol          -- Interface to various protocol schemes
+     LWP::Protocol::http  -- http:// access
+     LWP::Protocol::file  -- file:// access
+     LWP::Protocol::ftp   -- ftp:// access
+     ...
+
+ LWP::Authen::Basic -- Handle 401 and 407 responses
+ LWP::Authen::Digest
+
+ HTTP::Headers      -- MIME/RFC822 style header (used by HTTP::Message)
+ HTTP::Message      -- HTTP style message
+   HTTP::Request    -- HTTP request
+   HTTP::Response   -- HTTP response
+ HTTP::Daemon       -- A HTTP server class
+
+ URI::URL           -- Uniform Resource Locators
+
+ WWW::RobotRules    -- Parse robots.txt files
+   WWW::RobotRules::AnyDBM_File -- Persistent RobotRules
+
+The following modules provide various functions and definitions.
+
+ LWP                -- This file.  Library version number and documentation.
+ LWP::MediaTypes    -- MIME types configuration (text/html etc.)
+ LWP::Debug         -- Debug logging module
+ LWP::Simple        -- Simplified procedural interface for common functions
+ HTTP::Status       -- HTTP status code (200 OK etc)
+ HTTP::Date         -- Date parsing module for HTTP date formats
+ HTTP::Negotiate    -- HTTP content negotiation calculation
+ File::Listing      -- Parse directory listings
+
+
+=head1 MORE DOCUMENTATION
+
+All modules contain detailed information on the interfaces they
+provide.  The I<lwpcook> manpage is the libwww-perl cookbook that contain
+examples of typical usage of the library.  You might want to take a
+look at how the scripts C<lwp-request>, C<lwp-rget> and C<lwp-mirror>
+are implemented.
+
+=head1 BUGS
+
+The library can not handle multiple simultaneous requests yet.  Also,
+check out what's left in the TODO file.
+
+=head1 ACKNOWLEDGEMENTS
+
+This package owes a lot in motivation, design, and code, to the
+libwww-perl library for Perl 4, maintained by Roy Fielding
+E<lt>fielding@ics.uci.edu>.
+
+That package used work from Alberto Accomazzi, James Casey, Brooks
+Cutter, Martijn Koster, Oscar Nierstrasz, Mel Melchner, Gertjan van
+Oosten, Jared Rhine, Jack Shirazi, Gene Spafford, Marc VanHeyningen,
+Steven E. Brenner, Marion Hakanson, Waldemar Kebsch, Tony Sanders, and
+Larry Wall; see the libwww-perl-0.40 library for details.
+
+The primary architect for this Perl 5 library is Martijn Koster and
+Gisle Aas, with lots of help from Graham Barr, Tim Bunce, Andreas
+Koenig, Jared Rhine, and Jack Shirazi.
+
+
+=head1 COPYRIGHT
+
+  Copyright 1995-1998, Gisle Aas
+  Copyright 1995, Martijn Koster
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 AVAILABILITY
+
+The latest version of this library is likely to be available from:
+
+ http://www.sn.no/libwww-perl/
+
+The best place to discuss this code is on the
+<libwww-perl@ics.uci.edu> mailing list.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Basic.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Basic.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Basic.pm	(revision 16561)
@@ -0,0 +1,36 @@
+package LWP::Authen::Basic;
+use strict;
+
+require MIME::Base64;
+
+sub authenticate
+{
+    my($class, $ua, $proxy, $auth_param, $response,
+       $request, $arg, $size) = @_;
+
+    my($user, $pass) = $ua->get_basic_credentials($auth_param->{realm},
+                                                  $request->url, $proxy);
+    return $response unless defined $user and defined $pass;
+
+    my $auth_header = $proxy ? "Proxy-Authorization" : "Authorization";
+    my $auth_value = "Basic " . MIME::Base64::encode("$user:$pass", "");
+
+    # Need to check this isn't a repeated fail!
+    my $r = $response;
+    while ($r) {
+	my $auth = $r->request->header($auth_header);
+	if ($auth && $auth eq $auth_value) {
+	    # here we know this failed before
+	    $response->header("Client-Warning" =>
+			      "Credentials for '$user' failed before");
+	    return $response;
+	}
+	$r = $r->previous;
+    }
+
+    my $referral = $request->clone;
+    $referral->header($auth_header => $auth_value);
+    return $ua->request($referral, $arg, $size, $response);
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Digest.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Digest.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Authen/Digest.pm	(revision 16561)
@@ -0,0 +1,73 @@
+package LWP::Authen::Digest;
+use strict;
+
+require MD5;
+
+sub authenticate
+{
+    my($class, $ua, $proxy, $auth_param, $response,
+       $request, $arg, $size) = @_;
+
+    my($user, $pass) = $ua->get_basic_credentials($auth_param->{realm},
+                                                  $request->url, $proxy);
+    return $response unless defined $user and defined $pass;
+
+    my $md5 = new MD5;
+
+    my(@digest);
+    $md5->add(join(":", $user, $auth_param->{realm}, $pass));
+    push(@digest, $md5->hexdigest);
+    $md5->reset;
+
+    push(@digest, $auth_param->{nonce});
+
+    $md5->add(join(":", $request->method, $request->url->path));
+    push(@digest, $md5->hexdigest);
+    $md5->reset;
+
+    $md5->add(join(":", @digest));
+    my($digest) = $md5->hexdigest;
+    $md5->reset;
+
+    my %resp = map { $_ => $auth_param->{$_} } qw(realm nonce opaque);
+    @resp{qw(username uri response)} = ($user, $request->url->path, $digest);
+
+    my(@order) = qw(username realm nonce uri response);
+    if($request->method =~ /^(?:POST|PUT)$/) {
+	$md5->add($request->content);
+	my $content = $md5->hexdigest;
+	$md5->reset;
+	$md5->add(join(":", @digest[0..1], $content));
+	$md5->reset;
+	$resp{"message-digest"} = $md5->hexdigest;
+	push(@order, "message-digest");
+    }
+    push(@order, "opaque");
+    my @pairs;
+    for (@order) {
+	next unless defined $resp{$_};
+	push(@pairs, "$_=" . qq("$resp{$_}"));
+    }
+
+    my $auth_header = $proxy ? "Proxy-Authorization" : "Authorization";
+    my $auth_value  = "Digest " . join(", ", @pairs);
+    
+    # Need to check this isn't a repeated fail!
+    my $r = $response;
+    while ($r) {
+	my $auth = $r->request->header($auth_header);
+	if ($auth && $auth eq $auth_value) {
+	    # here we know this failed before
+	    $response->header("Client-Warning" =>
+			      "Credentials for '$user' failed before");
+	    return $response;
+	}
+	$r = $r->previous;
+    }
+
+    my $referral = $request->clone;
+    $referral->header($auth_header => $auth_value);
+    return $ua->request($referral, $arg, $size, $response);
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Debug.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Debug.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Debug.pm	(revision 16561)
@@ -0,0 +1,129 @@
+#!/usr/local/bin/perl -w
+#
+# $Id$
+#
+package LWP::Debug;
+
+=head1 NAME
+
+LWP::Debug - debug routines for the libwww-perl library
+
+=head1 SYNOPSIS
+
+ use LWP::Debug qw(+ -conns);
+
+ # Used internally in the library
+ LWP::Debug::trace('send()');
+ LWP::Debug::debug('url ok');
+ LWP::Debug::conns("read $n bytes: $data");
+
+=head1 DESCRIPTION
+
+LWP::Debug provides tracing facilities. The trace(), debug() and
+conns() function are called within the library and they log
+information at increasing levels of detail. Which level of detail is
+actually printed is controlled with the C<level()> function.
+
+The following functions are available:
+
+=over 4
+
+=item level(...)
+
+The C<level()> function controls the level of detail being
+logged. Passing '+' or '-' indicates full and no logging
+respectively. Inidividual levels can switched on and of by passing the
+name of the level with a '+' or '-' prepended.  The levels are:
+
+  trace   : trace function calls
+  debug   : print debug messages
+  conns   : show all data transfered over the connections
+
+The LWP::Debug module provide a special import() method that allows
+you to pass the level() arguments with initial use statement.  If a
+use argument start with '+' or '-' then it is passed to the level
+function, else the name is exported as usual.  The following two
+statements are thus equivalent (if you ignore that the second pollutes
+your namespace):
+
+  use LWP::Debug qw(+);
+  use LWP::Debug qw(level); level('+');
+
+=item trace($msg)
+
+The C<trace()> function is used for tracing function
+calls. The package and calling subroutine name is
+printed along with the passed argument. This should
+be called at the start of every major function.
+
+=item debug($msg)
+
+The C<debug()> function is used for high-granularity
+reporting of state in functions.
+
+=item conns($msg)
+
+The C<conns()> function is used to show data being
+transferred over the connections. This may generate
+considerable output.
+
+=back
+
+=cut
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(level trace debug conns);
+
+use Carp ();
+
+my @levels = qw(trace debug conns);
+%current_level = ();
+
+sub import
+{
+    my $pack = shift;
+    my $callpkg = caller(0);
+    my @symbols = ();
+    my @levels = ();
+    for (@_) {
+	if (/^[-+]/) {
+	    push(@levels, $_);
+	} else {
+	    push(@symbols, $_);
+	}
+    }
+    Exporter::export($pack, $callpkg, @symbols);
+    level(@levels);
+}
+
+sub level
+{
+    for (@_) {
+	if ($_ eq '+') {              # all on
+	    # switch on all levels
+	    %current_level = map { $_ => 1 } @levels;
+	} elsif ($_ eq '-') {           # all off
+	    %current_level = ();
+	} elsif (/^([-+])(\w+)$/) {
+	    $current_level{$2} = $1 eq '+';
+	} else {
+	    Carp::croak("Illegal level format $_");
+	}
+    }
+}
+
+sub trace  { _log(@_) if $current_level{'trace'}; }
+sub debug  { _log(@_) if $current_level{'debug'}; }
+sub conns  { _log(@_) if $current_level{'conns'}; }
+
+sub _log
+{
+    my $msg = shift;
+    $msg .= "\n" unless $msg =~ /\n$/;  # ensure trailing "\n"
+
+    my($package,$filename,$line,$sub) = caller(2);
+    print STDERR "$sub: $msg";
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MediaTypes.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MediaTypes.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MediaTypes.pm	(revision 16561)
@@ -0,0 +1,298 @@
+#
+# $Id$
+
+package LWP::MediaTypes;
+
+=head1 NAME
+
+LWP::MediaTypes - guess media type for a file or a URL
+
+=head1 SYNOPSIS
+
+ use LWP::MediaTypes qw(guess_media_type);
+ $type = guess_media_type("/tmp/foo.gif");
+
+=head1 DESCRIPTION
+
+This module provides functions for handling of media (also known as
+MIME) types and encodings.  The mapping from file extentions to media
+types is defined by the F<media.types> file.  If the F<~/.media.types>
+file exist it is used as a replacement.
+For backwards compatability we will also look for F<~/.mime.types>.
+
+The following functions are exported by default:
+
+=over 4
+
+=cut
+
+####################################################################
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(guess_media_type media_suffix);
+@EXPORT_OK = qw(add_type add_encoding);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+require LWP::Debug;
+use strict;
+
+# note: These hashes will also be filled with the entries found in
+# the 'media.types' file.
+
+my %suffixType = (
+    'txt'   => 'text/plain',
+    'html'  => 'text/html',
+    'gif'   => 'image/gif',
+    'jpg'   => 'image/jpeg',
+);
+
+my %suffixExt = (
+    'text/plain' => 'txt',
+    'text/html'  => 'h',
+    'image/gif'  => 'gif',
+    'image/jpeg' => 'jpg',
+);
+
+#XXX: there should be some way to define this in the media.types files.
+my %suffixEncoding = (
+    'Z'   => 'compress',
+    'gz'  => 'gzip',
+    'hqx' => 'x-hqx',
+    'uu'  => 'x-uuencode',
+    'z'   => 'x-pack'
+);
+
+sub _dump {
+    require Data::Dumper;
+    Data::Dumper->new([\%suffixType, \%suffixExt, \%suffixEncoding],
+		      [qw(*suffixType *suffixExt *suffixEncoding)])->Dump;
+}
+
+read_media_types();
+
+
+
+=item guess_media_type($filename_or_url, [$header_to_modify])
+
+This function tries to guess media type and encoding for given file.
+It returns the content-type, which is a string like C<"text/html">.
+In array context it also returns any content-encodings applied (in the
+order used to encode the file).  You can pass a URI::URL object
+reference, instead of the file name, as the first parameter too.
+
+If the type can not be deduced from looking at the file name only,
+then guess_media_type() will let the C<-T> Perl operator take a look.
+If this works (and C<-T> returns a TRUE value) then we return
+I<text/plain> as the type, otherwise we return
+I<application/octet-stream> as the type.
+
+The optional second argument should be a reference to a HTTP::Headers
+object (or any object that implement the $obj->header method in a
+similar way).  When present we will set the values of the
+'Content-Type' and 'Content-Encoding' for this header.
+
+=cut
+
+sub guess_media_type
+{
+    my($file, $header) = @_;
+    return undef unless defined $file;
+
+    my $fullname;
+    if (ref($file)) {
+	# assume URI::URL object
+	$file = $file->path;
+	#XXX should handle non http:, file: or ftp: URLs differently
+    } else {
+	$fullname = $file;  # enable peek at actual file
+    }
+
+    my @encoding = ();
+    my $ct = undef;
+    for (file_exts($file)) {
+	# first check this dot part as encoding spec
+	if (exists $suffixEncoding{$_}) {
+	    unshift(@encoding, $suffixEncoding{$_});
+	    next;
+	}
+	if (exists $suffixEncoding{lc $_}) {
+	    unshift(@encoding, $suffixEncoding{lc $_});
+	    next;
+	}
+
+	# check content-type
+	if (exists $suffixType{$_}) {
+	    $ct = $suffixType{$_};
+	    last;
+	}
+	if (exists $suffixType{lc $_}) {
+	    $ct = $suffixType{lc $_};
+	    last;
+	}
+
+	# don't know nothing about this dot part, bail out
+	last;
+    }
+    unless (defined $ct) {
+	# Take a look at the file
+	if (defined $fullname) {
+	    $ct = (-T $fullname) ? "text/plain" : "application/octet-stream";
+	} else {
+	    $ct = "application/octet-stream";
+	}
+    }
+
+    if ($header) {
+	$header->header('Content-Type' => $ct);
+	$header->header('Content-Encoding' => \@encoding) if @encoding;
+    }
+
+    wantarray ? ($ct, @encoding) : $ct;
+}
+
+
+=item media_suffix($type,...)
+
+This function will return all suffixes that can be used to denote the
+specified media type(s).  Wildcard types can be used.  In scalar
+context it will return the first suffix found.
+
+Examples:
+
+  @suffixes = media_suffix('image/*', 'audio/basic');
+  $suffix = media_suffix('text/html');
+
+=cut
+
+sub media_suffix {
+    if (!wantarray && @_ == 1 && $_[0] !~ /\*/) {
+	return $suffixExt{$_[0]};
+    }
+    my(@type) = @_;
+    my(@suffix, $ext, $type);
+    foreach (@type) {
+	if (s/\*/.*/) {
+	    while(($ext,$type) = each(%suffixType)) {
+		push(@suffix, $ext) if $type =~ /^$_$/;
+	    }
+	} else {
+	    while(($ext,$type) = each(%suffixType)) {
+		push(@suffix, $ext) if $type eq $_;
+	    }
+	}
+    }
+    wantarray ? @suffix : $suffix[0];
+}
+
+
+sub file_exts 
+{
+    my($file) = @_;
+    $file =~ s,.*/,,;   # only basename left
+    my @parts = reverse split(/\./, $file);
+    pop(@parts);        # never concider first part
+    @parts;
+}
+
+
+=back
+
+The following functions are only exported by explict request:
+
+=over 4
+
+=item add_type($type, @exts)
+
+Associate a list of file extensions with the given media type.
+
+Example:
+
+    add_type("x-world/x-vrml" => qw(wrl vrml));
+
+=cut
+
+sub add_type 
+{
+    my($type, @exts) = @_;
+    for my $ext (@exts) {
+	$ext =~ s/^\.//;
+	$suffixType{$ext} = $type;
+    }
+    $suffixExt{$type} = $exts[0] if @exts;
+}
+
+
+=item add_encoding($type, @ext)
+
+Associate a list of file extensions with and encoding type.
+
+Example:
+
+ add_encoding("x-gzip" => "gz");
+
+=cut
+
+sub add_encoding
+{
+    my($type, @exts) = @_;
+    for my $ext (@exts) {
+	$ext =~ s/^\.//;
+	$suffixEncoding{$ext} = $type;
+    }
+}
+
+
+=item read_media_types(@files)
+
+Parse a media types file from disk and add the type mappings found there.
+
+Example:
+
+    read_media_types("conf/mime.types");
+
+=cut
+
+sub read_media_types 
+{
+    my(@files) = @_;
+
+    local($/, $_) = ("\n", undef);  # ensure correct $INPUT_RECORD_SEPARATOR
+
+    my @priv_files = ();
+    push(@priv_files, "$ENV{HOME}/.media.types", "$ENV{HOME}/.mime.types")
+	if defined $ENV{HOME};  # Some doesn't have a home (for instance Win32)
+
+    # Try to locate "media.types" file, and initialize %suffixType from it
+    my $typefile;
+    unless (@files) {
+	@files = map {"$_/LWP/media.types"} @INC;
+	push @files, @priv_files;
+    }
+    for $typefile (@files) {
+	local(*TYPE);
+	open(TYPE, $typefile) || next;
+        LWP::Debug::debug("Reading media types from $typefile");
+	while (<TYPE>) {
+	    next if /^\s*#/; # comment line
+	    next if /^\s*$/; # blank line
+	    s/#.*//;         # remove end-of-line comments
+	    my($type, @exts) = split(' ', $_);
+	    add_type($type, @exts);
+	}
+	close(TYPE);
+    }
+}
+
+1;
+
+=back 
+
+=head1 COPYRIGHT
+
+Copyright 1995-1998 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MemberMixin.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MemberMixin.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/MemberMixin.pm	(revision 16561)
@@ -0,0 +1,46 @@
+#
+# $Id$
+
+package LWP::MemberMixin;
+
+=head1 NAME
+
+LWP::MemberMixin - Member access mixin class
+
+=head1 SYNOPSIS
+
+ package Foo;
+ require LWP::MemberMixin;
+ @ISA=qw(LWP::MemberMixin);
+
+=head1 DESCRIPTION
+
+A mixin class to get methods that provide easy access to member
+variables in the %$self.
+Ideally there should be better Perl langauge support for this.
+
+There is only one method provided:
+
+=over 4
+
+=item _elem($elem [, $val])
+
+Internal method to get/set the value of member variable
+C<$elem>. If C<$val> is defined it is used as the new value
+for the member variable.  If it is undefined the current
+value is not touched. In both cases the previous value of
+the member variable is returned.
+
+=back
+
+=cut
+
+sub _elem
+{
+    my($self, $elem, $val) = @_;
+    my $old = $self->{$elem};
+    $self->{$elem} = $val if defined $val;
+    return $old;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol.pm	(revision 16561)
@@ -0,0 +1,303 @@
+# $Id$
+
+package LWP::Protocol;
+
+=head1 NAME
+
+LWP::Protocol - Base class for LWP protocols
+
+=head1 SYNOPSIS
+
+ package LWP::Protocol::foo;
+ require LWP::Protocol;
+ @ISA=qw(LWP::Protocol);
+
+=head1 DESCRIPTION
+
+This class is used a the base class for all protocol implementations
+supported by the LWP library.
+
+When creating an instance of this class using
+C<LWP::Protocol::create($url)>, and you get an initialised subclass
+appropriate for that access method. In other words, the
+LWP::Protocol::create() function calls the constructor for one of its
+subclasses.
+
+All derived LWP::Protocol classes need to override the request()
+method which is used to service a request. The overridden method can
+make use of the collect() function to collect together chunks of data
+as it is received.
+
+The following methods and functions are provided:
+
+=over 4
+
+=cut
+
+#####################################################################
+
+require LWP::MemberMixin;
+@ISA = qw(LWP::MemberMixin);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use strict;
+use Carp ();
+use HTTP::Status 'RC_INTERNAL_SERVER_ERROR';
+require HTML::HeadParser;
+
+my %ImplementedBy = (); # scheme => classname
+
+
+=item $prot = new HTTP::Protocol;
+
+The LWP::Protocol constructor is inherited by subclasses. As this is a
+virtual base class this method should B<not> be called directly.
+
+=cut
+
+sub new
+{
+    my($class) = @_;
+
+    my $self = bless {
+	'timeout' => 0,
+	'parse_head' => 1,
+    }, $class;
+    $self;
+}
+
+
+=item $prot = LWP::Protocol::create($url)
+
+Create an object of the class implementing the protocol to handle the
+given scheme. This is a function, not a method. It is more an object
+factory than a constructor. This is the function user agents should
+use to access protocols.
+
+=cut
+
+sub create
+{
+    my $scheme = shift;
+    my $impclass = LWP::Protocol::implementor($scheme) or
+	Carp::croak("Protocol scheme '$scheme' is not supported");
+
+    # hand-off to scheme specific implementation sub-class
+    return $impclass->new($scheme);
+}
+
+
+=item $class = LWP::Protocol::implementor($scheme, [$class])
+
+Get and/or set implementor class for a scheme.  Returns '' if the
+specified scheme is not supported.
+
+=cut
+
+sub implementor
+{
+    my($scheme, $impclass) = @_;
+
+    if ($impclass) {
+	$ImplementedBy{$scheme} = $impclass;
+    }
+    my $ic = $ImplementedBy{$scheme};
+    return $ic if $ic;
+
+    return '' unless $scheme =~ /^([.+\-\w]+)$/;  # check valid URL schemes
+    $scheme = $1; # untaint
+    $scheme =~ s/[.+\-]/_/g;  # make it a legal module name
+
+    # scheme not yet known, look for a 'use'd implementation
+    $ic = "LWP::Protocol::$scheme";  # default location
+    $ic = "LWP::Protocol::nntp" if $scheme eq 'news'; #XXX ugly hack
+    no strict 'refs';
+    # check we actually have one for the scheme:
+    unless (defined @{"${ic}::ISA"}) {
+	# try to autoload it
+	eval "require $ic";
+	if ($@) {
+	    if ($@ =~ /^Can't locate/) { #' #emacs get confused by '
+		$ic = '';
+	    } else {
+		die "$@\n";
+	    }
+	}
+    }
+    $ImplementedBy{$scheme} = $ic if $ic;
+    $ic;
+}
+
+
+=item $prot->request(...)
+
+ $response = $protocol->request($request, $proxy, undef);
+ $response = $protocol->request($request, $proxy, '/tmp/sss');
+ $response = $protocol->request($request, $proxy, \&callback, 1024);
+
+Dispactches a request over the protocol, and returns a response
+object. This method needs to be overridden in subclasses.  Referer to
+L<LWP::UserAgent> for description of the arguments.
+
+=cut
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size, $timeout) = @_;
+    Carp::croak('LWP::Protocol::request() needs to be overridden in subclasses');
+}
+
+
+=item $prot->timeout($seconds)
+
+Get and set the timeout value in seconds
+
+
+=item $prot->parse_head($yesno)
+
+Should we initialize response headers from the <head> section of HTML
+documents.
+
+=cut
+
+sub timeout    { shift->_elem('timeout',    @_); }
+sub parse_head { shift->_elem('parse_head', @_); }
+sub max_size   { shift->_elem('max_size',   @_); }
+
+
+=item $prot->collect($arg, $response, $collector)
+
+Called to collect the content of a request, and process it
+appropriately into a scalar, file, or by calling a callback.  If $arg
+is undefined, then the content is stored within the $response.  If
+$arg is a simple scalar, then $arg is interpreted as a file name and
+the content is written to this file.  If $arg is a reference to a
+routine, then content is passed to this routine.
+
+The $collector is a routine that will be called and which is
+reponsible for returning pieces (as ref to scalar) of the content to
+process.  The $collector signals EOF by returning a reference to an
+empty sting.
+
+The return value from collect() is the $response object reference.
+
+B<Note:> We will only use the callback or file argument if
+$response->is_success().  This avoids sendig content data for
+redirects and authentization responses to the callback which would be
+confusing.
+
+=cut
+
+sub collect
+{
+    my ($self, $arg, $response, $collector) = @_;
+    my $content;
+    my($parse_head, $timeout, $max_size) =
+      @{$self}{qw(parse_head timeout max_size)};
+
+    my $parser;
+    if ($parse_head && $response->content_type eq 'text/html') {
+	$parser = HTML::HeadParser->new($response->{'_headers'});
+    }
+    my $content_size = 0;
+
+    if (!defined($arg) || !$response->is_success) {
+	# scalar
+	while ($content = &$collector, length $$content) {
+	    if ($parser) {
+		$parser->parse($$content) or undef($parser);
+	    }
+	    LWP::Debug::debug("read " . length($$content) . " bytes");
+	    $response->add_content($$content);
+	    $content_size += length($$content);
+	    if ($max_size && $content_size > $max_size) {
+		LWP::Debug::debug("Aborting because size limit exceeded");
+		my $tot = $response->header("Content-Length") || 0;
+		$response->header("X-Content-Range", "bytes 0-$content_size/$tot");
+		last;
+	    }
+	}
+    }
+    elsif (!ref($arg)) {
+	# filename
+	open(OUT, ">$arg") or
+	    return new HTTP::Response RC_INTERNAL_SERVER_ERROR,
+			  "Cannot write to '$arg': $!";
+        binmode(OUT);
+        local($\) = ""; # ensure standard $OUTPUT_RECORD_SEPARATOR
+	while ($content = &$collector, length $$content) {
+	    if ($parser) {
+		$parser->parse($$content) or undef($parser);
+	    }
+	    LWP::Debug::debug("read " . length($$content) . " bytes");
+	    print OUT $$content;
+	    $content_size += length($$content);
+	    if ($max_size && $content_size > $max_size) {
+		LWP::Debug::debug("Aborting because size limit exceeded");
+		my $tot = $response->header("Content-Length") || 0;
+		$response->header("X-Content-Range", "bytes 0-$content_size/$tot");
+		last;
+	    }
+	}
+	close(OUT);
+    }
+    elsif (ref($arg) eq 'CODE') {
+	# read into callback
+	while ($content = &$collector, length $$content) {
+	    if ($parser) {
+		$parser->parse($$content) or undef($parser);
+	    }
+	    LWP::Debug::debug("read " . length($$content) . " bytes");
+            eval {
+		&$arg($$content, $response, $self);
+	    };
+	    if ($@) {
+	        chomp($@);
+		$response->header('X-Died' => $@);
+		last;
+	    }
+	}
+    }
+    else {
+	return new HTTP::Response RC_INTERNAL_SERVER_ERROR,
+				  "Unexpected collect argument  '$arg'";
+    }
+    $response;
+}
+
+
+=item $prot->collect_once($arg, $response, $content)
+
+Can be called when the whole response content is available as
+$content.  This will invoke collect() with a collector callback that
+returns a reference to $content the first time and an empty string the
+next.
+
+=cut
+
+sub collect_once
+{
+    my($self, $arg, $response) = @_;
+    my $content = \ $_[3];
+    my $first = 1;
+    $self->collect($arg, $response, sub {
+	return $content if $first--;
+	return \ "";
+    });
+}
+
+1;
+
+=head1 SEE ALSO
+
+Inspect the F<LWP/Protocol/file.pm> and F<LWP/Protocol/http.pm> files
+for examples of usage.
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/data.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/data.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/data.pm	(revision 16561)
@@ -0,0 +1,53 @@
+#
+# $Id$
+#
+# Implements access to data:-URLs as specified in
+# draft-masinter-url-data-02.txt
+
+package LWP::Protocol::data;
+
+require HTTP::Response;
+require HTTP::Status;
+
+require LWP::Protocol;
+@ISA = qw(LWP::Protocol);
+
+use HTTP::Date qw(time2str);
+require LWP;  # needs version number
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size) = @_;
+
+    # check proxy
+    if (defined $proxy)
+    {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'You can not proxy with data';
+    }
+
+    # check method
+    if ($request->method ne 'GET') {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'Library does not allow method ' .
+				  "$method for 'data:' URLs";
+    }
+
+    my $url = $request->url;
+    my $response = new HTTP::Response &HTTP::Status::RC_OK, "Document follows";
+
+    my($media_type, $params) = $url->media_type;
+    $media_type .= ";$params" if $params;
+
+    my $data = $url->data;
+    $response->header('Content-Type'   => $media_type,
+		      'Content-Length' => length($data),
+		      'Date'           => time2str(time),
+		      'Server'         => "libwww-perl-internal/$LWP::VERSION"
+		     );
+    $response->content($data);
+
+    return $response;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/file.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/file.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/file.pm	(revision 16561)
@@ -0,0 +1,154 @@
+#
+# $Id$
+
+package LWP::Protocol::file;
+
+require LWP::Protocol;
+require LWP::MediaTypes;
+require HTTP::Request;
+require HTTP::Response;
+require HTTP::Status;
+require HTTP::Date;
+
+require URI::Escape;
+require HTML::Entities;
+
+use Carp;
+
+@ISA = qw(LWP::Protocol);
+
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size) = @_;
+
+    LWP::Debug::trace('()');
+
+    $size = 4096 unless defined $size and $size > 0;
+
+    # check proxy
+    if (defined $proxy)
+    {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'You can not proxy through the filesystem';
+    }
+
+    # check method
+    $method = $request->method;
+
+    unless ($method eq 'GET' || $method eq 'HEAD') {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'Library does not allow method ' .
+				  "$method for 'file:' URLs";
+    }
+
+    # check url
+    my $url = $request->url;
+
+    my $scheme = $url->scheme;
+    if ($scheme ne 'file') {
+	return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				  "LWP::file::request called for '$scheme'";
+    }
+
+    my $host = $url->host;
+    if ($host and $host !~ /^localhost$/i) {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST_CLIENT,
+				  'Only file://localhost/ allowed';
+    }
+
+    # URL OK, look at file
+    my $path  = $url->local_path;
+
+    # test file exists and is readable
+    unless (-e $path) {
+	return new HTTP::Response &HTTP::Status::RC_NOT_FOUND,
+				  "File `$path' does not exist";
+    }
+    unless (-r _) {
+	return new HTTP::Response &HTTP::Status::RC_FORBIDDEN,
+				  'User does not have read permission';
+    }
+
+    # looks like file exists
+    my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$filesize,
+       $atime,$mtime,$ctime,$blksize,$blocks)
+	    = stat(_);
+
+    # XXX should check Accept headers?
+
+    # check if-modified-since
+    my $ims = $request->header('If-Modified-Since');
+    if (defined $ims) {
+	my $time = HTTP::Date::str2time($ims);
+	if (defined $time and $time >= $mtime) {
+	    return new HTTP::Response &HTTP::Status::RC_NOT_MODIFIED,
+				      "$method $path";
+	}
+    }
+
+    # Ok, should be an OK response by now...
+    $response = new HTTP::Response &HTTP::Status::RC_OK;
+
+    # fill in response headers
+    $response->header('Last-Modified', HTTP::Date::time2str($mtime));
+
+    if (-d _) {         # If the path is a directory, process it
+	# generate the HTML for directory
+	opendir(D, $path) or
+	   return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				     "Cannot read directory '$path': $!";
+	my(@files) = sort readdir(D);
+	closedir(D);
+
+	# Make directory listing
+	for (@files) {
+	    $_ .= "/" if -d "$path/$_";
+	    my $furl = URI::Escape::uri_escape($_);
+	    my $desc = HTML::Entities::encode($_);
+	    $_ = qq{<LI><A HREF="$furl">$desc</A>};
+	}
+	# Ensure that the base URL is "/" terminated
+	my $base = $url->clone;
+	unless ($base->epath =~ m|/$|) {
+	    $base->epath($base->epath . "/");
+	}
+	my $html = join("\n",
+			"<HTML>\n<HEAD>",
+			"<TITLE>Directory $path</TITLE>",
+			"<BASE HREF=\"$base\">",
+			"</HEAD>\n<BODY>",
+			"<H1>Directory listing of $path</H1>",
+			"<UL>", @files, "</UL>",
+			"</BODY>\n</HTML>\n");
+
+	$response->header('Content-Type',   'text/html');
+	$response->header('Content-Length', length $html);
+
+	return $self->collect_once($arg, $response, $html);
+
+    } else {            # path is a regular file
+	my($type, @enc) = LWP::MediaTypes::guess_media_type($path);
+	$response->header('Content-Type',   $type) if $type;
+	$response->header('Content-Length', $filesize);
+	for (@enc) {
+	    $response->push_header('Content-Encoding', $_);
+	}
+
+	# read the file
+	open(F, $path) or return new
+	   HTTP::Response(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+			  "Cannot read file '$path': $!");
+	$response =  $self->collect($arg, $response, sub {
+	    my $content = "";
+	    my $bytes = sysread(F, $content, $size);
+	    return \$content if $bytes > 0;
+	    return \ "";
+	});
+	close(F);
+    }
+
+    $response;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/ftp.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/ftp.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/ftp.pm	(revision 16561)
@@ -0,0 +1,396 @@
+#
+# $Id$
+
+# Implementation of the ftp protocol (RFC 959). We let the Net::FTP
+# package do all the dirty work.
+
+package LWP::Protocol::ftp;
+
+use Carp ();
+
+use HTTP::Status ();
+use HTTP::Negotiate ();
+use HTTP::Response ();
+use LWP::MediaTypes ();
+use File::Listing ();
+
+require LWP::Protocol;
+@ISA = qw(LWP::Protocol);
+
+use strict;
+eval {
+    require Net::FTP;
+    Net::FTP->require_version(2.00);
+};
+my $init_failed = $@;
+
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size, $timeout) = @_;
+
+    $size = 4096 unless $size;
+
+    LWP::Debug::trace('()');
+
+    # check proxy
+    if (defined $proxy)
+    {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'You can not proxy through the ftp');
+    }
+
+    my $url = $request->url;
+    if ($url->scheme ne 'ftp') {
+	my $scheme = $url->scheme;
+	return HTTP::Response->new(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+		       "LWP::Protocol::ftp::request called for '$scheme'");
+    }
+
+    # check method
+    my $method = $request->method;
+
+    unless ($method eq 'GET' || $method eq 'HEAD' || $method eq 'PUT') {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'Library does not allow method ' .
+				   "$method for 'ftp:' URLs");
+    }
+
+    if ($init_failed) {
+	return HTTP::Response->new(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				   $init_failed);
+    }
+
+    my $host     = $url->host;
+    my $port     = $url->port;
+    my $user     = $url->user;
+    my $password = $url->password;
+
+    # If a basic autorization header is present than we prefer these over
+    # the username/password specified in the URL.
+    {
+	my($u,$p) = $request->authorization_basic;
+	if (defined $u) {
+	    $user = $u;
+	    $password = $p;
+	}
+    }
+
+    # We allow the account to be specified in the "Account" header
+    my $acct     = $request->header('Account');
+
+    # try to make a connection
+    my $ftp = Net::FTP->new($host, Port => $port);
+    unless ($ftp) {
+       $@ =~ s/^Net::FTP: //;
+       return HTTP::Response->new(&HTTP::Status::RC_INTERNAL_SERVER_ERROR, $@);
+    }
+
+    # Create an initial response object
+    my $response = HTTP::Response->new(&HTTP::Status::RC_OK,
+				       "Document follows");
+    $response->request($request);
+
+    my $mess = $ftp->message;  # welcome message
+    LWP::Debug::debug($mess);
+    $mess =~ s|\n.*||s; # only first line left
+    $mess =~ s|\s*ready\.?$||;
+    # Make the version number more HTTP like
+    $mess =~ s|\s*\(Version\s*|/| and $mess =~ s|\)$||;
+    $response->header("Server", $mess);
+
+    $ftp->timeout($timeout) if $timeout;
+
+    LWP::Debug::debug("Logging in as $user (password $password)...");
+    unless ($ftp->login($user, $password, $acct)) {
+	# Unauthorized.  Let's fake a RC_UNAUTHORIZED response
+	my $res =  HTTP::Response->new(&HTTP::Status::RC_UNAUTHORIZED,
+				       scalar($ftp->message));
+	$res->header("WWW-Authenticate", qq(Basic Realm="FTP login"));
+	return $res;
+    }
+    LWP::Debug::debug($ftp->message);
+
+    # Get & fix the path
+    my @path =  $url->path_components;
+    shift(@path);  # There will always be an empty first component
+    pop(@path) while @path && $path[-1] eq '';  # remove empty tailing comps
+    my $remote_file = pop(@path);
+    $remote_file = '' unless defined $remote_file;
+
+    my $params = $url->params;
+    if (defined($params) && $params eq 'type=a') {
+	$ftp->ascii;
+    } else {
+	$ftp->binary;
+    }
+
+    for (@path) {
+	LWP::Debug::debug("CWD $_");
+	unless ($ftp->cwd($_)) {
+	    return HTTP::Response->new(&HTTP::Status::RC_NOT_FOUND,
+				       "Can't chdir to $_");
+	}
+    }
+
+    if ($method eq 'GET' || $method eq 'HEAD') {
+	LWP::Debug::debug("MDTM");
+	if (my $mod_time = $ftp->mdtm($remote_file)) {
+	    $response->last_modified($mod_time);
+	    if (my $ims = $request->if_modified_since) {
+		if ($mod_time <= $ims) {
+		    $response->code(&HTTP::Status::RC_NOT_MODIFIED);
+		    $response->message("Not modified");
+		    return $response;
+		}
+	    }
+	}
+
+	my $data;  # the data handle
+	LWP::Debug::debug("retrieve file?");
+	if (length($remote_file) and $data = $ftp->retr($remote_file)) {
+	    my($type, @enc) = LWP::MediaTypes::guess_media_type($remote_file);
+	    $response->header('Content-Type',   $type) if $type;
+	    for (@enc) {
+		$response->push_header('Content-Encoding', $_);
+	    }
+	    my $mess = $ftp->message;
+	    LWP::Debug::debug($mess);
+	    if ($mess =~ /\((\d+)\s+bytes\)/) {
+		$response->header('Content-Length', "$1");
+	    }
+
+	    if ($method ne 'HEAD') {
+		# Read data from server
+		$response = $self->collect($arg, $response, sub {
+		    my $content = '';
+		    my $result = $data->read($content, $size);
+		    return \$content;
+		} );
+	    }
+	    unless ($data->close) {
+		# Something did not work too well
+		if ($method ne 'HEAD') {
+		    $response->code(&HTTP::Status::RC_INTERNAL_SERVER_ERROR);
+		    $response->message("FTP close response: " . $ftp->code .
+				       " " . $ftp->message);
+		}
+	    }
+	} elsif (!length($remote_file) || $ftp->code == 550) {
+	    # 550 not a plain file, try to list instead
+	    if (length($remote_file) && !$ftp->cwd($remote_file)) {
+		LWP::Debug::debug("chdir before listing failed");
+		return HTTP::Response->new(&HTTP::Status::RC_NOT_FOUND,
+					   "File '$remote_file' not found");
+	    }
+
+	    # It should now be safe to try to list the directory
+	    LWP::Debug::debug("dir");
+	    my @lsl = $ftp->dir;
+
+	    # Try to figure out if the user want us to convert the
+	    # directory listing to HTML.
+	    my @variants =
+	      (
+	       ['html',  0.60, 'text/html'            ],
+	       ['dir',   1.00, 'text/ftp-dir-listing' ]
+	      );
+	    #$HTTP::Negotiate::DEBUG=1;
+	    my $prefer = HTTP::Negotiate::choose(\@variants, $request);
+
+	    my $content = '';
+
+	    if (!defined($prefer)) {
+		return HTTP::Response->new(&HTTP::Status::RC_NOT_ACCEPTABLE,
+			       "Neither HTML nor directory listing wanted");
+	    } elsif ($prefer eq 'html') {
+		$response->header('Content-Type' => 'text/html');
+		$content = "<HEAD><TITLE>File Listing</TITLE>\n";
+		my $base = $request->url->clone;
+		my $path = $base->epath;
+		$base->epath("$path/") unless $path =~ m|/$|;
+		$content .= qq(<BASE HREF="$base">\n</HEAD>\n);
+		$content .= "<BODY>\n<UL>\n";
+		for (File::Listing::parse_dir(\@lsl, 'GMT')) {
+		    my($name, $type, $size, $mtime, $mode) = @$_;
+		    $content .= qq(  <LI> <a href="$name">$name</a>);
+		    $content .= " $size bytes" if $type eq 'f';
+		    $content .= "\n";
+		}
+		$content .= "</UL></body>\n";
+	    } else {
+		$response->header('Content-Type', 'text/ftp-dir-listing');
+		$content = join("\n", @lsl, '');
+	    }
+
+	    $response->header('Content-Length', length($content));
+
+	    if ($method ne 'HEAD') {
+		$response = $self->collect_once($arg, $response, $content);
+	    }
+	} else {
+	    my $res = HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+			  "FTP return code " . $ftp->code);
+	    $res->content_type("text/plain");
+	    $res->content($ftp->message);
+	    return $res;
+	}
+    } elsif ($method eq 'PUT') {
+	# method must be PUT
+	unless (length($remote_file)) {
+	    return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				       "Must have a file name to PUT to");
+	}
+	my $data;
+	if ($data = $ftp->stor($remote_file)) {
+	    LWP::Debug::debug($ftp->message);
+	    LWP::Debug::debug("$data");
+	    my $content = $request->content;
+	    my $bytes = 0;
+	    if (defined $content) {
+		if (ref($content) eq 'SCALAR') {
+		    $bytes = $data->write($$content, length($$content));
+		} elsif (ref($content) eq 'CODE') {
+		    my($buf, $n);
+		    while (length($buf = &$content)) {
+			$n = $data->write($buf, length($buf));
+			last unless $n;
+			$bytes += $n;
+		    }
+		} elsif (!ref($content)) {
+		    if (defined $content && length($content)) {
+			$bytes = $data->write($content, length($content));
+		    }
+		} else {
+		    die "Bad content";
+		}
+	    }
+	    $data->close;
+	    LWP::Debug::debug($ftp->message);
+
+	    $response->code(&HTTP::Status::RC_CREATED);
+	    $response->header('Content-Type', 'text/plain');
+	    $response->content("$bytes bytes stored as $remote_file on $host\n")
+
+	} else {
+	    my $res = HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+					  "FTP return code " . $ftp->code);
+	    $res->content_type("text/plain");
+	    $res->content($ftp->message);
+	    return $res;
+	}
+    } else {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   "Illegal method $method");
+    }
+
+    $response;
+}
+
+1;
+
+__END__
+
+# This is what RFC 1738 has to say about FTP access:
+# --------------------------------------------------
+#
+# 3.2. FTP
+#
+#    The FTP URL scheme is used to designate files and directories on
+#    Internet hosts accessible using the FTP protocol (RFC959).
+#
+#    A FTP URL follow the syntax described in Section 3.1.  If :<port> is
+#    omitted, the port defaults to 21.
+#
+# 3.2.1. FTP Name and Password
+#
+#    A user name and password may be supplied; they are used in the ftp
+#    "USER" and "PASS" commands after first making the connection to the
+#    FTP server.  If no user name or password is supplied and one is
+#    requested by the FTP server, the conventions for "anonymous" FTP are
+#    to be used, as follows:
+#
+#         The user name "anonymous" is supplied.
+#
+#         The password is supplied as the Internet e-mail address
+#         of the end user accessing the resource.
+#
+#    If the URL supplies a user name but no password, and the remote
+#    server requests a password, the program interpreting the FTP URL
+#    should request one from the user.
+#
+# 3.2.2. FTP url-path
+#
+#    The url-path of a FTP URL has the following syntax:
+#
+#         <cwd1>/<cwd2>/.../<cwdN>/<name>;type=<typecode>
+#
+#    Where <cwd1> through <cwdN> and <name> are (possibly encoded) strings
+#    and <typecode> is one of the characters "a", "i", or "d".  The part
+#    ";type=<typecode>" may be omitted. The <cwdx> and <name> parts may be
+#    empty. The whole url-path may be omitted, including the "/"
+#    delimiting it from the prefix containing user, password, host, and
+#    port.
+#
+#    The url-path is interpreted as a series of FTP commands as follows:
+#
+#       Each of the <cwd> elements is to be supplied, sequentially, as the
+#       argument to a CWD (change working directory) command.
+#
+#       If the typecode is "d", perform a NLST (name list) command with
+#       <name> as the argument, and interpret the results as a file
+#       directory listing.
+#
+#       Otherwise, perform a TYPE command with <typecode> as the argument,
+#       and then access the file whose name is <name> (for example, using
+#       the RETR command.)
+#
+#    Within a name or CWD component, the characters "/" and ";" are
+#    reserved and must be encoded. The components are decoded prior to
+#    their use in the FTP protocol.  In particular, if the appropriate FTP
+#    sequence to access a particular file requires supplying a string
+#    containing a "/" as an argument to a CWD or RETR command, it is
+#    necessary to encode each "/".
+#
+#    For example, the URL <URL:ftp://myname@host.dom/%2Fetc/motd> is
+#    interpreted by FTP-ing to "host.dom", logging in as "myname"
+#    (prompting for a password if it is asked for), and then executing
+#    "CWD /etc" and then "RETR motd". This has a different meaning from
+#    <URL:ftp://myname@host.dom/etc/motd> which would "CWD etc" and then
+#    "RETR motd"; the initial "CWD" might be executed relative to the
+#    default directory for "myname". On the other hand,
+#    <URL:ftp://myname@host.dom//etc/motd>, would "CWD " with a null
+#    argument, then "CWD etc", and then "RETR motd".
+#
+#    FTP URLs may also be used for other operations; for example, it is
+#    possible to update a file on a remote file server, or infer
+#    information about it from the directory listings. The mechanism for
+#    doing so is not spelled out here.
+#
+# 3.2.3. FTP Typecode is Optional
+#
+#    The entire ;type=<typecode> part of a FTP URL is optional. If it is
+#    omitted, the client program interpreting the URL must guess the
+#    appropriate mode to use. In general, the data content type of a file
+#    can only be guessed from the name, e.g., from the suffix of the name;
+#    the appropriate type code to be used for transfer of the file can
+#    then be deduced from the data content of the file.
+#
+# 3.2.4 Hierarchy
+#
+#    For some file systems, the "/" used to denote the hierarchical
+#    structure of the URL corresponds to the delimiter used to construct a
+#    file name hierarchy, and thus, the filename will look similar to the
+#    URL path. This does NOT mean that the URL is a Unix filename.
+#
+# 3.2.5. Optimization
+#
+#    Clients accessing resources via FTP may employ additional heuristics
+#    to optimize the interaction. For some FTP servers, for example, it
+#    may be reasonable to keep the control connection open while accessing
+#    multiple URLs from the same server. However, there is no common
+#    hierarchical model to the FTP protocol, so if a directory change
+#    command has been given, it is impossible in general to deduce what
+#    sequence should be given to navigate to another directory for a
+#    second retrieval, if the paths are different.  The only reliable
+#    algorithm is to disconnect and reestablish the control connection.
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/gopher.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/gopher.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/gopher.pm	(revision 16561)
@@ -0,0 +1,215 @@
+#
+# $Id$
+
+# Implementation of the gopher protocol (RFC 1436)
+#
+# This code is based on 'wwwgopher.pl,v 0.10 1994/10/17 18:12:34 shelden'
+# which in turn is a vastly modified version of Oscar's http'get()
+# dated 28/3/94 in <ftp://cui.unige.ch/PUBLIC/oscar/scripts/http.pl>
+# including contributions from Marc van Heyningen and Martijn Koster.
+#
+
+package LWP::Protocol::gopher;
+
+use strict;
+use vars qw(@ISA);
+
+require HTTP::Response;
+require HTTP::Status;
+require IO::Socket;
+require IO::Select;
+
+require LWP::Protocol;
+@ISA = qw(LWP::Protocol);
+
+
+my %gopher2mimetype = (
+    '0' => 'text/plain',                # 0 file
+    '1' => 'text/html',                 # 1 menu
+					# 2 CSO phone-book server
+					# 3 Error
+    '4' => 'application/mac-binhex40',  # 4 BinHexed Macintosh file
+    '5' => 'application/zip',           # 5 DOS binary archive of some sort
+    '6' => 'application/octet-stream',  # 6 UNIX uuencoded file.
+    '7' => 'text/html',                 # 7 Index-Search server
+					# 8 telnet session
+    '9' => 'application/octet-stream',  # 9 binary file
+    'h' => 'text/html',                 # html
+    'g' => 'image/gif',                 # gif
+    'I' => 'image/*',                   # some kind of image
+);
+
+my %gopher2encoding = (
+    '6' => 'x_uuencode',                # 6 UNIX uuencoded file.
+);
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size, $timeout) = @_;
+
+    LWP::Debug::trace('()');
+
+    $size = 4096 unless $size;
+
+    # check proxy
+    if (defined $proxy) {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'You can not proxy through the gopher');
+    }
+
+    my $url = $request->url;
+    die "bad scheme" if $url->scheme ne 'gopher';
+
+
+    my $method = $request->method;
+    unless ($method eq 'GET' || $method eq 'HEAD') {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'Library does not allow method ' .
+				   "$method for 'gopher:' URLs");
+    }
+
+    my $gophertype = $url->gtype;
+    unless (exists $gopher2mimetype{$gophertype}) {
+	return HTTP::Response->new(&HTTP::Status::RC_NOT_IMPLEMENTED,
+				   'Library does not support gophertype ' .
+				   $gophertype);
+    }
+
+    my $response = HTTP::Response->new(&HTTP::Status::RC_OK, "OK");
+    $response->header('Content-type' => $gopher2mimetype{$gophertype}
+					|| 'text/plain');
+    $response->header('Content-Encoding' => $gopher2encoding{$gophertype})
+	if exists $gopher2encoding{$gophertype};
+
+    if ($method eq 'HEAD') {
+	# XXX: don't even try it so we set this header
+	$response->header('Client-Warning' => 'Client answer only');
+	return $response;
+    }
+    
+    if ($gophertype eq '7' && ! $url->search) {
+      # the url is the prompt for a gopher search; supply boiler-plate
+      return $self->collect_once($arg, $response, <<"EOT");
+<HEAD>
+<TITLE>Gopher Index</TITLE>
+<ISINDEX>
+</HEAD>
+<BODY>
+<H1>$url<BR>Gopher Search</H1>
+This is a searchable Gopher index.
+Use the search function of your browser to enter search terms.
+</BODY>
+EOT
+    }
+
+    my $host = $url->host;
+    my $port = $url->port;
+
+    my $requestLine = "";
+
+    my $selector = $url->selector;
+    if (defined $selector) {
+	$requestLine .= $selector;
+	my $search = $url->search;
+	if (defined $search) {
+	    $requestLine .= "\t$search";
+	    my $string = $url->string;
+	    if (defined $string) {
+		$requestLine .= "\t$string";
+	    }
+	}
+    }
+    $requestLine .= "\015\012";
+
+    # potential request headers are just ignored
+
+    # Ok, lets make the request
+    my $socket = IO::Socket::INET->new(PeerAddr => $host,
+				       PeerPort => $port,
+				       Timeout  => $timeout);
+    die "Can't connect to $host:$port" unless $socket;
+    my $sel = IO::Select->new($socket);
+
+    {
+	die "write timeout" if $timeout && !$sel->can_write($timeout);
+	my $n = syswrite($socket, $requestLine, length($requestLine));
+	die $! unless defined($n);
+	die "short write" if $n != length($requestLine);
+    }
+
+    my $user_arg = $arg;
+
+    # must handle menus in a special way since they are to be
+    # converted to HTML.  Undefing $arg ensures that the user does
+    # not see the data before we get a change to convert it.
+    $arg = undef if $gophertype eq '1' || $gophertype eq '7';
+
+    # collect response
+    my $buf = '';
+    $response = $self->collect($arg, $response, sub {
+	die "read timeout" if $timeout && !$sel->can_read($timeout);
+        my $n = sysread($socket, $buf, $size);
+	die $! unless defined($n);
+	return \$buf;
+      } );
+
+    # Convert menu to HTML and return data to user.
+    if ($gophertype eq '1' || $gophertype eq '7') {
+	my $content = menu2html($response->content);
+	if (defined $user_arg) {
+	    $response = $self->collect_once($user_arg, $response, $content);
+	} else {
+	    $response->content($content);
+	}
+    }
+
+    $response;
+}
+
+
+sub gopher2url
+{
+    my($gophertype, $path, $host, $port) = @_;
+
+    my $url;
+
+    if ($gophertype eq '8' || $gophertype eq 'T') {
+	# telnet session
+	$url = URI::URL->new($gophertype eq '8' ? 'telnet:' : 'tn3270:');
+	$url->user($path) if defined $path;
+    } else {
+	$path = URI::Escape::uri_escape($path);
+	$url = URI::URL->new("gopher:/$gophertype$path");
+    }
+    $url->host($host);
+    $url->port($port);
+    $url;
+}
+
+sub menu2html {
+    my($menu) = @_;
+
+    $menu =~ s/\015//g;  # remove carriage return
+    my $tmp = <<"EOT";
+<HTML>
+<HEAD>
+   <TITLE>Gopher menu</TITLE>
+</HEAD>
+<BODY>
+<H1>Gopher menu</H1>
+EOT
+    for (split("\n", $menu)) {
+	last if /^\./;
+	my($pretty, $path, $host, $port) = split("\t");
+
+	$pretty =~ s/^(.)//;
+	my $type = $1;
+
+	my $url = gopher2url($type, $path, $host, $port)->as_string;
+	$tmp .= qq{<A HREF="$url">$pretty</A><BR>\n};
+    }
+    $tmp .= "</BODY>\n</HTML>\n";
+    $tmp;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/http.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/http.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/http.pm	(revision 16561)
@@ -0,0 +1,252 @@
+#
+# $Id$
+
+package LWP::Protocol::http;
+
+require LWP::Debug;
+require HTTP::Response;
+require HTTP::Status;
+require IO::Socket;
+require IO::Select;
+
+require LWP::Protocol;
+@ISA = qw(LWP::Protocol);
+
+use strict;
+my $CRLF         = "\015\012";     # how lines should be terminated;
+				   # "\r\n" is not correct on all systems, for
+				   # instance MacPerl defines it to "\012\015"
+
+sub _new_socket
+{
+    my($self, $host, $port, $timeout) = @_;
+
+    local($^W) = 0;  # IO::Socket::INET can be noisy
+    my $sock = IO::Socket::INET->new(PeerAddr => $host,
+				     PeerPort => $port,
+				     Proto    => 'tcp',
+				     Timeout  => $timeout,
+				    );
+    unless ($sock) {
+	# IO::Socket::INET leaves additional error messages in $@
+	$@ =~ s/^.*?: //;
+	die "Can't connect to $host:$port ($@)";
+    }
+    $sock;
+}
+
+
+sub _check_sock
+{
+    #my($self, $req, $sock) = @_;
+}
+
+sub _get_sock_info
+{
+    my($self, $res, $sock) = @_;
+    $res->header("Client-Peer" =>
+		 $sock->peerhost . ":" . $sock->peerport);
+}
+
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size, $timeout) = @_;
+    LWP::Debug::trace('()');
+
+    $size ||= 4096;
+
+    # check method
+    my $method = $request->method;
+    unless ($method =~ /^[A-Za-z0-9_!\#\$%&\'*+\-.^\`|~]+$/) {  # HTTP token
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'Library does not allow method ' .
+				  "$method for 'http:' URLs";
+    }
+
+    my $url = $request->url;
+    my($host, $port, $fullpath);
+
+    # Check if we're proxy'ing
+    if (defined $proxy) {
+	# $proxy is an URL to an HTTP server which will proxy this request
+	$host = $proxy->host;
+	$port = $proxy->port;
+	$fullpath = $url->as_string;
+    }
+    else {
+	$host = $url->host;
+	$port = $url->port;
+	$fullpath = $url->full_path;
+    }
+
+    # connect to remote site
+    my $socket = $self->_new_socket($host, $port, $timeout);
+    $self->_check_sock($request, $socket);
+	    
+    my $sel = IO::Select->new($socket) if $timeout;
+
+    my $request_line = "$method $fullpath HTTP/1.0$CRLF";
+
+    my $h = $request->headers->clone;
+    my $cont_ref = $request->content_ref;
+    $cont_ref = $$cont_ref if ref($$cont_ref);
+    my $ctype = ref($cont_ref);
+
+    # If we're sending content we *have* to specify a content length
+    # otherwise the server won't know a messagebody is coming.
+    if ($ctype eq 'CODE') {
+	die 'No Content-Length header for request with dynamic content'
+	    unless defined($h->header('Content-Length')) ||
+		   $h->content_type =~ /^multipart\//;
+	# For HTTP/1.1 we could have used chunked transfer encoding...
+    } else {
+	$h->header('Content-Length' => length $$cont_ref)
+	        if defined($$cont_ref) && length($$cont_ref);
+    }
+    
+    # HTTP/1.1 will require us to send the 'Host' header, so we might
+    # as well start now.
+    my $hhost = $url->netloc;
+    $hhost =~ s/^([^\@]*)\@//;  # get rid of potential "user:pass@"
+    $h->header('Host' => $hhost) unless defined $h->header('Host');
+
+    # add authorization header if we need them.  HTTP URLs do
+    # not really support specification of user and password, but
+    # we allow it.
+    if (defined($1) && not $h->header('Authorization')) {
+	$h->authorization_basic($url->user, $url->password);
+    }
+
+    my $buf = $request_line . $h->as_string($CRLF) . $CRLF;
+    my $n;  # used for return value from syswrite/sysread
+
+    die "write timeout" if $timeout && !$sel->can_write($timeout);
+    $n = $socket->syswrite($buf, length($buf));
+    die $! unless defined($n);
+    die "short write" unless $n == length($buf);
+    LWP::Debug::conns($buf);
+
+    if ($ctype eq 'CODE') {
+	while ( ($buf = &$cont_ref()), defined($buf) && length($buf)) {
+	    die "write timeout" if $timeout && !$sel->can_write($timeout);
+	    $n = $socket->syswrite($buf, length($buf));
+	    die $! unless defined($n);
+	    die "short write" unless $n == length($buf);
+	    LWP::Debug::conns($buf);
+	}
+    } elsif (defined($$cont_ref) && length($$cont_ref)) {
+	die "write timeout" if $timeout && !$sel->can_write($timeout);
+	$n = $socket->syswrite($$cont_ref, length($$cont_ref));
+	die $! unless defined($n);
+	die "short write" unless $n == length($$cont_ref);
+	LWP::Debug::conns($buf);
+    }
+    
+    # read response line from server
+    LWP::Debug::debug('reading response');
+
+    my $response;
+    $buf = '';
+
+    # Inside this loop we will read the response line and all headers
+    # found in the response.
+    while (1) {
+	{
+	    die "read timeout" if $timeout && !$sel->can_read($timeout);
+	    $n = $socket->sysread($buf, $size, length($buf));
+	    die $! unless defined($n);
+	    die "unexpected EOF before status line seen" unless $n;
+	    LWP::Debug::conns($buf);
+	}
+	if ($buf =~ s/^(HTTP\/\d+\.\d+)[ \t]+(\d+)[ \t]*([^\012]*)\012//) {
+	    # HTTP/1.0 response or better
+	    my($ver,$code,$msg) = ($1, $2, $3);
+	    $msg =~ s/\015$//;
+	    LWP::Debug::debug("$ver $code $msg");
+	    $response = HTTP::Response->new($code, $msg);
+	    $response->protocol($ver);
+
+	    # ensure that we have read all headers.  The headers will be
+	    # terminated by two blank lines
+	    until ($buf =~ /^\015?\012/ || $buf =~ /\015?\012\015?\012/) {
+		# must read more if we can...
+		LWP::Debug::debug("need more header data");
+		die "read timeout" if $timeout && !$sel->can_read($timeout);
+		$n = $socket->sysread($buf, $size, length($buf));
+		die $! unless defined($n);
+		die "unexpected EOF before all headers seen" unless $n;
+		#LWP::Debug::conns($buf);
+	    }
+
+	    # now we start parsing the headers.  The strategy is to
+	    # remove one line at a time from the beginning of the header
+	    # buffer ($res).
+	    my($key, $val);
+	    while ($buf =~ s/([^\012]*)\012//) {
+		my $line = $1;
+
+		# if we need to restore as content when illegal headers
+		# are found.
+		my $save = "$line\012"; 
+
+		$line =~ s/\015$//;
+		last unless length $line;
+
+		if ($line =~ /^([a-zA-Z0-9_\-]+)\s*:\s*(.*)/) {
+		    $response->push_header($key, $val) if $key;
+		    ($key, $val) = ($1, $2);
+		} elsif ($line =~ /^\s+(.*)/) {
+		    unless ($key) {
+			$response->header("Client-Warning" =>
+					 => "Illegal continuation header");
+			$buf = "$save$buf";
+			last;
+		    }
+		    $val .= " $1";
+		} else {
+		    $response->header("Client-Warning" =>
+				      "Illegal header '$line'");
+		    $buf = "$save$buf";
+		    last;
+		}
+	    }
+	    $response->push_header($key, $val) if $key;
+	    last;
+
+	} elsif ((length($buf) >= 5 and $buf !~ /^HTTP\//) or
+		 $buf =~ /\012/ ) {
+	    # HTTP/0.9 or worse
+	    LWP::Debug::debug("HTTP/0.9 assume OK");
+	    $response = HTTP::Response->new(&HTTP::Status::RC_OK, "OK");
+	    $response->protocol('HTTP/0.9');
+	    last;
+
+	} else {
+	    # need more data
+	    LWP::Debug::debug("need more status line data");
+	}
+    };
+    $response->request($request);
+    $self->_get_sock_info($response, $socket);
+
+
+    my $usebuf = length($buf) > 0;
+    $response = $self->collect($arg, $response, sub {
+        if ($usebuf) {
+	    $usebuf = 0;
+	    return \$buf;
+	}
+	die "read timeout" if $timeout && !$sel->can_read($timeout);
+	my $n = $socket->sysread($buf, $size);
+	die $! unless defined($n);
+	#LWP::Debug::conns($buf);
+	return \$buf;
+	} );
+
+    $socket->close;
+
+    $response;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/https.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/https.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/https.pm	(revision 16561)
@@ -0,0 +1,59 @@
+#
+# $Id$
+
+use strict;
+
+package LWP::Protocol::https;
+require Net::SSL;  # from Crypt-SSLeay
+
+use vars qw(@ISA);
+
+require LWP::Protocol::http;
+@ISA=qw(LWP::Protocol::http);
+
+sub _new_socket
+{
+    my($self, $host, $port, $timeout) = @_;
+    local($^W) = 0;  # IO::Socket::INET can be noisy
+    my $sock = Net::SSL->new(PeerAddr => $host,
+			     PeerPort => $port,
+			     Proto    => 'tcp',
+			     Timeout  => $timeout,
+			    );
+    unless ($sock) {
+	# IO::Socket::INET leaves additional error messages in $@
+	$@ =~ s/^.*?: //;
+	die "Can't connect to $host:$port ($@)";
+    }
+    $sock;
+}
+
+sub _check_sock
+{
+    my($self, $req, $sock) = @_;
+    my $check = $req->header("If-SSL-Cert-Subject");
+    if (defined $check) {
+	my $cert = $sock->get_peer_certificate ||
+	    die "Missing SSL certificate";
+	my $subject = $cert->subject_name;
+	die "Bad SSL certificate subject: '$subject' !~ /$check/"
+	    unless $subject =~ /$check/;
+	$req->remove_header("If-SSL-Cert-Subject");  # don't pass it on
+    }
+}
+
+sub _get_sock_info
+{
+    my $self = shift;
+    $self->SUPER::_get_sock_info(@_);
+    my($res, $sock) = @_;
+    $res->header("Client-SSL-Cipher" => $sock->get_cipher);
+    my $cert = $sock->get_peer_certificate;
+    if ($cert) {
+	$res->header("Client-SSL-Cert-Subject" => $cert->subject_name);
+	$res->header("Client-SSL-Cert-Issuer" => $cert->issuer_name);
+    }
+    $res->header("Client-SSL-Warning" => "Peer certificate not verified");
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/mailto.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/mailto.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/mailto.pm	(revision 16561)
@@ -0,0 +1,87 @@
+#
+# $Id$
+#
+# This module implements the mailto protocol.  It is just a simple
+# frontend to the Unix sendmail program.  In the long run this module
+# will built using the Mail::Send module.
+
+package LWP::Protocol::mailto;
+
+require LWP::Protocol;
+require HTTP::Request;
+require HTTP::Response;
+require HTTP::Status;
+
+use Carp;
+
+@ISA = qw(LWP::Protocol);
+
+$SENDMAIL = "/usr/lib/sendmail";
+
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size) = @_;
+
+    # check proxy
+    if (defined $proxy)
+    {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'You can not proxy with mail';
+    }
+
+    # check method
+    $method = $request->method;
+
+    if ($method ne 'POST') {
+	return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST,
+				  'Library does not allow method ' .
+				  "$method for 'mailto:' URLs";
+    }
+
+    # check url
+    my $url = $request->url;
+
+    my $scheme = $url->scheme;
+    if ($scheme ne 'mailto') {
+	return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				  "LWP::file::request called for '$scheme'";
+    }
+    unless (-x $SENDMAIL) {
+	return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				  "You don't have $SENDMAIL";
+    }
+
+    open(SENDMAIL, "| $SENDMAIL -oi -t") or
+	return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				  "Can't run $SENDMAIL: $!";
+
+    my $addr = $url->encoded822addr;
+
+    $request->header('To', $addr);
+    print SENDMAIL $request->headers_as_string;
+    print SENDMAIL "\n";
+    my $content = $request->content;
+    if (defined $content) {
+	my $contRef = ref($content) ? $content : \$content;
+	if (ref($contRef) eq 'SCALAR') {
+	    print SENDMAIL $$contRef;
+	} elsif (ref($contRef) eq 'CODE') {
+	    # Callback provides data
+	    my $d;
+	    while (length($d = &$contRef)) {
+		print SENDMAIL $d;
+	    }
+	}
+    }
+    close(SENDMAIL);
+
+    my $response = new HTTP::Response &HTTP::Status::RC_ACCEPTED,
+				     'Mail accepted by sendmail';
+    $response->header('Content-Type', 'text/plain');
+    $response->content("Mail sent to <$addr>\n");
+
+    return $response;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/nntp.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/nntp.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Protocol/nntp.pm	(revision 16561)
@@ -0,0 +1,142 @@
+#
+# $Id$
+
+# Implementation of the Network News Transfer Protocol (RFC 977)
+#
+
+package LWP::Protocol::nntp;
+
+require LWP::Protocol;
+@ISA = qw(LWP::Protocol);
+
+require LWP::Debug;
+require HTTP::Response;
+require HTTP::Status;
+require Net::NNTP;
+
+use strict;
+
+
+sub request
+{
+    my($self, $request, $proxy, $arg, $size, $timeout) = @_;
+
+    LWP::Debug::trace('()');
+
+    $size = 4096 unless $size;
+
+    # Check for proxy
+    if (defined $proxy) {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'You can not proxy through NNTP');
+    }
+
+    # Check that the scheme is as expected
+    my $url = $request->url;
+    my $scheme = $url->scheme;
+    unless ($scheme eq 'news') {
+	return HTTP::Response->new(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				   "LWP::Protocol::nntp::request called for '$scheme'");
+    }
+
+    # check for a valid method
+    my $method = $request->method;
+    unless ($method eq 'GET' || $method eq 'HEAD' || $method eq 'POST') {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   'Library does not allow method ' .
+				   "$method for 'news:' URLs");
+    }
+
+    # extract the identifier and check against posting to an article
+    my $groupart = $url->groupart;
+    my $is_art = $groupart =~ /@/;
+
+    if ($is_art && $method eq 'POST') {
+	return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST,
+				   "Can't post to an article <$groupart>");
+    }
+
+    my $nntp = Net::NNTP->new(undef,
+			      #Port    => 18574,
+			      Timeout => $timeout,
+			      #Debug   => 1,
+			     );
+    die "Can't connect to nntp server" unless $nntp;
+
+    # Check the initial welcome message from the NNTP server
+    if ($nntp->status != 2) {
+	return HTTP::Response->new(&HTTP::Status::RC_SERVICE_UNAVAILABLE,
+				   $nntp->message);
+    }
+    my $response = HTTP::Response->new(&HTTP::Status::RC_OK, "OK");
+
+    my $mess = $nntp->message;
+    LWP::Debug::debug($mess);
+
+    # Try to extract server name from greating message.
+    # Don't know if this works well for a large class of servers, but
+    # this works for our server.
+    $mess =~ s/\s+ready\b.*//;
+    $mess =~ s/^\S+\s+//;
+    $response->header(Server => $mess);
+
+
+    # First we handle posting of articles
+    if ($method eq 'POST') {
+	return HTTP::Response->new(&HTTP::Status::RC_NOT_IMPLEMENTED,
+				   "POST not implemented yet");
+    }
+
+    # The method must be "GET" or "HEAD" by now
+    if (!$is_art) {
+	if (!$nntp->group($groupart)) {
+	    return HTTP::Response->new(&HTTP::Status::RC_NOT_FOUND,
+				       $nntp->message);
+	}
+	return HTTP::Response->new(&HTTP::Status::RC_NOT_IMPLEMENTED,
+				   "GET newsgroup not implemented yet");
+    }
+
+    # Send command to server to retrieve an article (or just the headers)
+    my $get = $method eq 'HEAD' ? "head" : "article";
+    my $art = $nntp->$get("<$groupart>");
+    unless ($art) {
+	return HTTP::Response->new(&HTTP::Status::RC_NOT_FOUND,
+				   $nntp->message);
+    }
+    LWP::Debug::debug($nntp->message);
+    
+    # Parse headers
+    my($key, $val);
+    while ($_ = shift @$art) {
+	if (/^\s+$/) {
+	    last;  # end of headers
+	} elsif (/^(\S+):\s*(.*)/) {
+	    $response->push_header($key, $val) if $key;
+	    ($key, $val) = ($1, $2);
+	} elsif (/^\s+(.*)/) {
+	    next unless $key;
+	    $val .= $1;
+	} else {
+	    unshift(@$art, $_);
+	    last;
+	}
+    }
+    $response->push_header($key, $val) if $key;
+
+    # Ensure that there is a Content-Type header
+    $response->header("Content-Type", "text/plain")
+	unless $response->header("Content-Type");
+
+    # Collect the body
+    $response = $self->collect_once($arg, $response, join("", @$art))
+      if @$art;
+
+    # Say godbye to the server
+    $nntp->quit;
+    $nntp = undef;
+
+    $response;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/RobotUA.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/RobotUA.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/RobotUA.pm	(revision 16561)
@@ -0,0 +1,285 @@
+# $Id$
+
+package LWP::RobotUA;
+
+require LWP::UserAgent;
+@ISA = qw(LWP::UserAgent);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+require WWW::RobotRules;
+require HTTP::Request;
+require HTTP::Response;
+
+use Carp ();
+use LWP::Debug ();
+use HTTP::Status ();
+use HTTP::Date qw(time2str);
+use strict;
+
+=head1 NAME
+
+LWP::RobotUA - A class for Web Robots
+
+=head1 SYNOPSIS
+
+  require LWP::RobotUA;
+  $ua = new LWP::RobotUA 'my-robot/0.1', 'me@foo.com';
+  $ua->delay(10);  # be very nice, go slowly
+  ...
+  # just use it just like a normal LWP::UserAgent
+  $res = $ua->request($req);
+
+=head1 DESCRIPTION
+
+This class implements a user agent that is suitable for robot
+applications.  Robots should be nice to the servers they visit.  They
+should consult the F<robots.txt> file to ensure that they are welcomed
+and they should not send too frequent requests.
+
+But, before you consider writing a robot take a look at
+<URL:http://info.webcrawler.com/mak/projects/robots/robots.html>.
+
+When you use a I<LWP::RobotUA> as your user agent, then you do not
+really have to think about these things yourself.  Just send requests
+as you do when you are using a normal I<LWP::UserAgent> and this
+special agent will make sure you are nice.
+
+=head1 METHODS
+
+The LWP::RobotUA is a sub-class of LWP::UserAgent and implements the
+same methods. In addition the following methods are provided:
+
+=over 4
+
+=cut
+
+
+#
+# Additional attributes in addition to those found in LWP::UserAgent:
+#
+# $self->{'delay'}    Required delay between request to the same
+#                     server in minutes.
+#
+# $self->{'rules'}     A WWW::RobotRules object
+#
+
+
+=item $ua = LWP::RobotUA->new($agent_name, $from, [$rules])
+
+Your robot's name and the mail address of the human responsible for
+the robot (i.e. you) is required by the constructor.
+
+Optionally it allows you to specify the I<WWW::RobotRules> object to
+use.
+
+=cut
+
+sub new
+{
+    my($class,$name,$from,$rules) = @_;
+
+    Carp::croak('LWP::RobotUA name required') unless $name;
+    Carp::croak('LWP::RobotUA from address required') unless $from;
+
+    my $self = new LWP::UserAgent;
+    $self = bless $self, $class;
+
+    $self->{'delay'} = 1;   # minutes
+    $self->{'agent'} = $name;
+    $self->{'from'}  = $from;
+    $self->{'use_sleep'} = 1;
+
+    if ($rules) {
+	$rules->agent($name);
+	$self->{'rules'} = $rules;
+    } else {
+	$self->{'rules'} = new WWW::RobotRules $name;
+    }
+
+    $self;
+}
+
+
+=item $ua->delay([$minutes])
+
+Set the minimum delay between requests to the same server.  The
+default is 1 minute.
+
+=item $ua->use_sleep([$boolean])
+
+Get/set a value indicating wether the UA should sleep() if request
+arrive to fast (before $ua->delay minutes has passed).  The default is
+TRUE.  If this value is FALSE then an internal SERVICE_UNAVAILABLE
+response will be generated.  It will have an Retry-After header that
+indicate when it is OK to send another request to this server.
+
+=cut
+
+sub delay     { shift->_elem('delay',     @_); }
+sub use_sleep { shift->_elem('use_sleep', @_); }
+
+sub agent
+{
+    my $self = shift;
+    my $old = $self->SUPER::agent(@_);
+    if (@_) {
+	# Changing our name means to start fresh
+	$self->{'rules'}->agent($self->{'agent'}); 
+    }
+    $old;
+}
+
+
+=item $ua->rules([$rules])
+
+Set/get which I<WWW::RobotRules> object to use. 
+
+=cut
+
+sub rules {
+    my $self = shift;
+    my $old = $self->_elem('rules', @_);
+    $self->{'rules'}->agent($self->{'agent'}) if @_;
+    $old;
+}
+
+
+=item $ua->no_visits($netloc)
+
+Returns the number of documents fetched from this server host. Yes I
+know, this method should probably have been named num_visits() or
+something like that :-(
+
+=cut
+
+sub no_visits
+{
+    my($self, $netloc) = @_;
+    $self->{'rules'}->no_visits($netloc);
+}
+
+*host_count = \&no_visits;  # backwards compatibility with LWP-5.02
+
+
+=item $ua->host_wait($netloc)
+
+Returns the number of seconds (from now) you must wait before you can
+make a new request to this host.
+
+=cut
+
+sub host_wait
+{
+    my($self, $netloc) = @_;
+    return undef unless defined $netloc;
+    my $last = $self->{'rules'}->last_visit($netloc);
+    if ($last) {
+	my $wait = int($self->{'delay'} * 60 - (time - $last));
+	$wait = 0 if $wait < 0;
+	return $wait;
+    }
+    return 0;
+}
+
+
+sub simple_request
+{
+    my($self, $request, $arg, $size) = @_;
+
+    LWP::Debug::trace('()');
+
+    # Do we try to access a new server?
+    my $allowed = $self->{'rules'}->allowed($request->url);
+
+    if ($allowed < 0) {
+	LWP::Debug::debug("Host is not visited before, or robots.txt expired.");
+	# fetch "robots.txt"
+	my $robot_url = $request->url->clone;
+	$robot_url->path("robots.txt");
+	$robot_url->params(undef);
+	$robot_url->query(undef);
+	LWP::Debug::debug("Requesting $robot_url");
+
+	# make access to robot.txt legal since this will be a recursive call
+	$self->{'rules'}->parse($robot_url, ""); 
+
+	my $robot_req = new HTTP::Request 'GET', $robot_url;
+	my $robot_res = $self->request($robot_req);
+	my $fresh_until = $robot_res->fresh_until;
+	if ($robot_res->is_success) {
+	    LWP::Debug::debug("Parsing robot rules");
+	    $self->{'rules'}->parse($robot_url, $robot_res->content, 
+				    $fresh_until);
+	} else {
+	    LWP::Debug::debug("No robots.txt file found");
+	    $self->{'rules'}->parse($robot_url, "", $fresh_until);
+	}
+
+	# recalculate allowed...
+	$allowed = $self->{'rules'}->allowed($request->url);
+    }
+
+    # Check rules
+    unless ($allowed) {
+	return new HTTP::Response
+	  &HTTP::Status::RC_FORBIDDEN, 'Forbidden by robots.txt';
+    }
+
+    my $netloc = $request->url->netloc;
+    my $wait = $self->host_wait($netloc);
+
+    if ($wait) {
+	LWP::Debug::debug("Must wait $wait seconds");
+	if ($self->{'use_sleep'}) {
+	    sleep($wait)
+	} else {
+	    my $res = new HTTP::Response
+	      &HTTP::Status::RC_SERVICE_UNAVAILABLE, 'Please, slow down';
+	    $res->header('Retry-After', time2str(time + $wait));
+	    return $res;
+	}
+    }
+
+    # Perform the request
+    my $res = $self->SUPER::simple_request($request, $arg, $size);
+
+    $self->{'rules'}->visit($netloc);
+
+    $res;
+}
+
+
+=item $ua->as_string
+
+Returns a text that describe the state of the UA.
+Mainly useful for debugging.
+
+=cut
+
+sub as_string
+{
+    my $self = shift;
+    my @s;
+    push(@s, "Robot: $self->{'agent'} operated by $self->{'from'}  [$self]");
+    push(@s, "    Minimum delay: " . int($self->{'delay'}*60) . "s");
+    push(@s, "    Will sleep if too early") if $self->{'use_sleep'};
+    push(@s, "    Rules = $self->{'rules'}");
+    join("\n", @s, '');
+}
+
+1;
+
+=back
+
+=head1 SEE ALSO
+
+L<LWP::UserAgent>, L<WWW::RobotRules>
+
+=head1 COPYRIGHT
+
+Copyright 1996-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Simple.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Simple.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/Simple.pm	(revision 16561)
@@ -0,0 +1,329 @@
+#
+# $Id$
+
+=head1 NAME
+
+get, head, getprint, getstore, mirror - Procedural LWP interface
+
+=head1 SYNOPSIS
+
+ perl -MLWP::Simple -e 'getprint "http://www.sn.no"'
+
+ use LWP::Simple;
+ $content = get("http://www.sn.no/")
+ if (mirror("http://www.sn.no/", "foo") == RC_NOT_MODIFIED) {
+     ...
+ }
+
+ if (is_success(getprint("http://www.sn.no/"))) {
+     ...
+ }
+
+=head1 DESCRIPTION
+
+This interface is intended for those who want a simplified view of the
+libwww-perl library.  It should also be suitable for one-liners.  If
+you need more control or access to the header fields in the requests
+sent and responses received you should use the full object oriented
+interface provided by the C<LWP::UserAgent> module.
+
+The following functions are provided (and exported) by this module:
+
+=over 3
+
+=item get($url)
+
+The get() function will fetch the document identified by the given URL
+and return it.  It returns C<undef> if it fails.  The $url argument can
+be either a simple string or a reference to a URI::URL object.
+
+You will not be able to examine the response code or response headers
+(like 'Content-Type') when you are accessing the web using this
+function.  If you need this information you should use the full OO
+interface (see L<LWP::UserAgent>).
+
+=item head($url)
+
+Get document headers. Returns the following 5 values if successful:
+($content_type, $document_length, $modified_time, $expires, $server)
+
+Returns an empty list if it fails.  In scalar context returns TRUE if
+successful.
+
+=item getprint($url)
+
+Get and print a document identified by a URL. The document is printed
+on STDOUT as data is received from the network.  If the request fails,
+then the status code and message is printed on STDERR.  The return
+value is the HTTP response code.
+
+=item getstore($url, $file)
+
+Gets a document identified by a URL and stores it in the file. The
+return value is the HTTP response code.
+
+=item mirror($url, $file)
+
+Get and store a document identified by a URL, using
+I<If-modified-since>, and checking of the I<Content-Length>.  Returns
+the HTTP response code.
+
+=back
+
+This module also exports the HTTP::Status constants and procedures.
+These can be used when you check the response code from getprint(),
+getstore() and mirror().  The constants are:
+
+   RC_CONTINUE
+   RC_SWITCHING_PROTOCOLS
+   RC_OK
+   RC_CREATED
+   RC_ACCEPTED
+   RC_NON_AUTHORITATIVE_INFORMATION
+   RC_NO_CONTENT
+   RC_RESET_CONTENT
+   RC_PARTIAL_CONTENT
+   RC_MULTIPLE_CHOICES
+   RC_MOVED_PERMANENTLY
+   RC_MOVED_TEMPORARILY
+   RC_SEE_OTHER
+   RC_NOT_MODIFIED
+   RC_USE_PROXY
+   RC_BAD_REQUEST
+   RC_UNAUTHORIZED
+   RC_PAYMENT_REQUIRED
+   RC_FORBIDDEN
+   RC_NOT_FOUND
+   RC_METHOD_NOT_ALLOWED
+   RC_NOT_ACCEPTABLE
+   RC_PROXY_AUTHENTICATION_REQUIRED
+   RC_REQUEST_TIMEOUT
+   RC_CONFLICT
+   RC_GONE
+   RC_LENGTH_REQUIRED
+   RC_PRECONDITION_FAILED
+   RC_REQUEST_ENTITY_TOO_LARGE
+   RC_REQUEST_URI_TOO_LARGE
+   RC_UNSUPPORTED_MEDIA_TYPE
+   RC_INTERNAL_SERVER_ERROR
+   RC_NOT_IMPLEMENTED
+   RC_BAD_GATEWAY
+   RC_SERVICE_UNAVAILABLE
+   RC_GATEWAY_TIMEOUT
+   RC_HTTP_VERSION_NOT_SUPPORTED
+
+The HTTP::Status classification functions are:
+
+=over 3
+
+=item is_success($rc)
+
+Check if response code indicated successfull request.
+
+=item is_error($rc)
+
+Check if response code indicated that an error occured.
+
+=back
+
+The module will also export the LWP::UserAgent object as C<$ua> if you
+ask for it explicitly.
+
+The user agent created by this module will identify itself as
+"LWP::Simple/#.##" (where "#.##" is the libwww-perl version number)
+and will initialize its proxy defaults from the environment (by
+calling $ua->env_proxy).
+
+=head1 SEE ALSO
+
+L<LWP>, L<LWP::UserAgent>, L<HTTP::Status>, L<lwp-request>,
+L<lwp-mirror>
+
+=cut
+
+
+package LWP::Simple;
+
+use strict;
+use vars qw($ua %loop_check $FULL_LWP @EXPORT @EXPORT_OK $VERSION);
+
+require Exporter;
+
+@EXPORT = qw(get head getprint getstore mirror);
+@EXPORT_OK = qw($ua);
+
+# I really hate this.  I was a bad idea to do it in the first place.
+# Wonder how to get rid of it???  (It even makes LWP::Simple 7% slower
+# for trivial tests)
+use HTTP::Status;
+push(@EXPORT, @HTTP::Status::EXPORT);
+
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+$FULL_LWP++ if grep {lc($_) eq "http_proxy"} keys %ENV;
+
+
+sub import
+{
+    my $pkg = shift;
+    my $callpkg = caller;
+    if (grep $_ eq '$ua', @_) {
+	$FULL_LWP++;
+	_init_ua();
+    }
+    Exporter::export($pkg, $callpkg, @_);
+}
+
+
+sub _init_ua
+{
+    require LWP;
+    require LWP::UserAgent;
+    require HTTP::Status;
+    require HTTP::Date;
+    $ua = new LWP::UserAgent;  # we create a global UserAgent object
+    my $ver = $LWP::VERSION = $LWP::VERSION;  # avoid warning
+    $ua->agent("LWP::Simple/$LWP::VERSION");
+    $ua->env_proxy;
+}
+
+
+sub get ($)
+{
+    %loop_check = ();
+    goto \&_get;
+}
+
+
+sub get_old ($)
+{
+    my($url) = @_;
+    _init_ua() unless $ua;
+
+    my $request = HTTP::Request->new(GET => $url);
+    my $response = $ua->request($request);
+
+    return $response->content if $response->is_success;
+    return undef;
+}
+
+
+sub head ($)
+{
+    my($url) = @_;
+    _init_ua() unless $ua;
+
+    my $request = HTTP::Request->new(HEAD => $url);
+    my $response = $ua->request($request);
+
+    if ($response->is_success) {
+	return $response unless wantarray;
+	return ($response->header('Content-Type'),
+		$response->header('Content-Length'),
+		HTTP::Date::str2time($response->header('Last-Modified')),
+		HTTP::Date::str2time($response->header('Expires')),
+		$response->header('Server'),
+	       );
+    }
+    return;
+}
+
+
+sub getprint ($)
+{
+    my($url) = @_;
+    _init_ua() unless $ua;
+
+    my $request = HTTP::Request->new(GET => $url);
+    local($\) = ""; # ensure standard $OUTPUT_RECORD_SEPARATOR
+    my $response = $ua->request($request, sub { print $_[0] });
+    unless ($response->is_success) {
+	print STDERR $response->status_line, " <URL:$url>\n";
+    }
+    $response->code;
+}
+
+
+sub getstore ($$)
+{
+    my($url, $file) = @_;
+    _init_ua() unless $ua;
+
+    my $request = HTTP::Request->new(GET => $url);
+    my $response = $ua->request($request, $file);
+
+    $response->code;
+}
+
+
+sub mirror ($$)
+{
+    my($url, $file) = @_;
+    _init_ua() unless $ua;
+    my $response = $ua->mirror($url, $file);
+    $response->code;
+}
+
+
+sub _get
+{
+    my $url = shift;
+    my $ret;
+    if (!$FULL_LWP && $url =~ m,^http://([^/:]+)(?::(\d+))?(/\S*)?$,) {
+	my $host = $1;
+	my $port = $2 || 80;
+	my $path = $3;
+	$path = "/" unless defined($path);
+	return _trivial_http_get($host, $port, $path);
+    } else {
+        _init_ua() unless $ua;
+	my $request = HTTP::Request->new(GET => $url);
+	my $response = $ua->request($request);
+	return $response->is_success ? $response->content : undef;
+    }
+}
+
+
+sub _trivial_http_get
+{
+   my($host, $port, $path) = @_;
+   #print "HOST=$host, PORT=$port, PATH=$path\n";
+
+   require IO::Socket;
+   local($^W) = 0;
+   my $sock = IO::Socket::INET->new(PeerAddr => $host,
+                                    PeerPort => $port,
+                                    Proto    => 'tcp',
+                                    Timeout  => 60) || return;
+   $sock->autoflush;
+   my $netloc = $host;
+   $netloc .= ":$port" if $port != 80;
+   print $sock join("\015\012" =>
+                    "GET $path HTTP/1.0",
+                    "Host: $netloc",
+                    "User-Agent: lwp-trivial/$VERSION",
+                    "", "");
+
+   my $buf = "";
+   my $n;
+   1 while $n = sysread($sock, $buf, 8*1024, length($buf));
+   return undef unless defined($n);
+
+   if ($buf =~ m,^HTTP/\d+\.\d+\s+(\d+)[^\012]*\012,) {
+       my $code = $1;
+       #print "CODE=$code\n$buf\n";
+       if ($code =~ /^30[1237]/ && $buf =~ /\012Location:\s*(\S+)/) {
+           # redirect
+           my $url = $1;
+           return undef if $loop_check{$url}++;
+           return _get($url);
+       }
+       return undef unless $code =~ /^2/;
+       $buf =~ s/.+?\015?\012\015?\012//s;  # zap header
+   }
+
+   return $buf;
+}
+
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/UserAgent.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/UserAgent.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/UserAgent.pm	(revision 16561)
@@ -0,0 +1,732 @@
+# $Id$
+
+package LWP::UserAgent;
+use strict;
+
+=head1 NAME
+
+LWP::UserAgent - A WWW UserAgent class
+
+=head1 SYNOPSIS
+
+ require LWP::UserAgent;
+ $ua = new LWP::UserAgent;
+
+ $request = new HTTP::Request('GET', 'file://localhost/etc/motd');
+
+ $response = $ua->request($request); # or
+ $response = $ua->request($request, '/tmp/sss'); # or
+ $response = $ua->request($request, \&callback, 4096);
+
+ sub callback { my($data, $response, $protocol) = @_; .... }
+
+=head1 DESCRIPTION
+
+The C<LWP::UserAgent> is a class implementing a simple World-Wide Web
+user agent in Perl. It brings together the HTTP::Request,
+HTTP::Response and the LWP::Protocol classes that form the rest of the
+core of libwww-perl library. For simple uses this class can be used
+directly to dispatch WWW requests, alternatively it can be subclassed
+for application-specific behaviour.
+
+In normal usage the application creates a UserAgent object, and then
+configures it with values for timeouts proxies, name, etc. The next
+step is to create an instance of C<HTTP::Request> for the request that
+needs to be performed. This request is then passed to the UserAgent
+request() method, which dispatches it using the relevant protocol,
+and returns a C<HTTP::Response> object.
+
+The basic approach of the library is to use HTTP style communication
+for all protocol schemes, i.e. you will receive an C<HTTP::Response>
+object also for gopher or ftp requests.  In order to achieve even more
+similarities with HTTP style communications, gopher menus and file
+directories will be converted to HTML documents.
+
+The request() method can process the content of the response in one of
+three ways: in core, into a file, or into repeated calls of a
+subroutine.  You choose which one by the kind of value passed as the
+second argument to request().
+
+The in core variant simply returns the content in a scalar attribute
+called content() of the response object, and is suitable for small
+HTML replies that might need further parsing.  This variant is used if
+the second argument is missing (or is undef).
+
+The filename variant requires a scalar containing a filename as the
+second argument to request(), and is suitable for large WWW objects
+which need to be written directly to the file, without requiring large
+amounts of memory. In this case the response object returned from
+request() will have empty content().  If the request fails, then the
+content() might not be empty, and the file will be untouched.
+
+The subroutine variant requires a reference to callback routine as the
+second argument to request() and it can also take an optional chuck
+size as third argument.  This variant can be used to construct
+"pipe-lined" processing, where processing of received chuncks can
+begin before the complete data has arrived.  The callback function is
+called with 3 arguments: the data received this time, a reference to
+the response object and a reference to the protocol object.  The
+response object returned from request() will have empty content().  If
+the request fails, then the the callback routine will not have been
+called, and the response->content() might not be empty.
+
+The request can be aborted by calling die() within the callback
+routine.  The die message will be available as the "X-Died" special
+response header field.
+
+The library also accepts that you put a subroutine reference as
+content in the request object.  This subroutine should return the
+content (possibly in pieces) when called.  It should return an empty
+string when there is no more content.
+
+=head1 METHODS
+
+The following methods are available:
+
+=over 4
+
+=cut
+
+
+use vars qw(@ISA $VERSION);
+
+require LWP::MemberMixin;
+@ISA = qw(LWP::MemberMixin);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+
+require URI::URL;
+require HTTP::Request;
+require HTTP::Response;
+
+use HTTP::Date ();
+
+use LWP ();
+use LWP::Debug ();
+use LWP::Protocol ();
+
+use Carp ();
+
+use AutoLoader ();
+*AUTOLOAD = \&AutoLoader::AUTOLOAD;  # import the AUTOLOAD method
+
+
+=item $ua = new LWP::UserAgent;
+
+Constructor for the UserAgent.  Returns a reference to a
+LWP::UserAgent object.
+
+=cut
+
+sub new
+{
+    my($class, $init) = @_;
+    LWP::Debug::trace('()');
+
+    my $self;
+    if (ref $init) {
+	$self = $init->clone;
+    } else {
+	$self = bless {
+		'agent'       => "libwww-perl/$LWP::VERSION",
+		'from'        => undef,
+		'timeout'     => 3*60,
+		'proxy'       => undef,
+		'cookie_jar'  => undef,
+		'use_eval'    => 1,
+                'parse_head'  => 1,
+                'max_size'    => undef,
+		'no_proxy'    => [],
+	}, $class;
+    }
+}
+
+
+=item $ua->simple_request($request, [$arg [, $size]])
+
+This method dispatches a single WWW request on behalf of a user, and
+returns the response received.  The C<$request> should be a reference
+to a C<HTTP::Request> object with values defined for at least the
+method() and url() attributes.
+
+If C<$arg> is a scalar it is taken as a filename where the content of
+the response is stored.
+
+If C<$arg> is a reference to a subroutine, then this routine is called
+as chunks of the content is received.  An optional C<$size> argument
+is taken as a hint for an appropriate chunk size.
+
+If C<$arg> is omitted, then the content is stored in the response
+object itself.
+
+=cut
+
+sub simple_request
+{
+    my($self, $request, $arg, $size) = @_;
+    local($SIG{__DIE__});  # protect agains user defined die handlers
+
+    my($method, $url) = ($request->method, $request->url);
+
+    # Check that we have a METHOD and a URL first
+    return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST, "Method missing")
+	unless $method;
+    return HTTP::Response->new(&HTTP::Status::RC_BAD_REQUEST, "URL missing")
+	unless $url;
+
+    LWP::Debug::trace("$method $url");
+
+    # Locate protocol to use
+    my $scheme = '';
+    my $proxy = $self->_need_proxy($url);
+    if (defined $proxy) {
+	$scheme = $proxy->scheme;
+    } else {
+	$scheme = $url->scheme;
+    }
+    my $protocol;
+    eval {
+	$protocol = LWP::Protocol::create($scheme);
+    };
+    if ($@) {
+	$@ =~ s/\s+at\s+\S+\s+line\s+\d+\.?\s*//;  # remove file/line number
+	return HTTP::Response->new(&HTTP::Status::RC_NOT_IMPLEMENTED, $@)
+    }
+
+    # Extract fields that will be used below
+    my ($agent, $from, $timeout, $cookie_jar,
+        $use_eval, $parse_head, $max_size) =
+      @{$self}{qw(agent from timeout cookie_jar
+                  use_eval parse_head max_size)};
+
+    # Set User-Agent and From headers if they are defined
+    $request->header('User-Agent' => $agent) if $agent;
+    $request->header('From' => $from) if $from;
+    $cookie_jar->add_cookie_header($request) if $cookie_jar;
+
+    # Transfer some attributes to the protocol object
+    $protocol->parse_head($parse_head);
+    $protocol->max_size($max_size);
+    
+    my $response;
+    if ($use_eval) {
+	# we eval, and turn dies into responses below
+	eval {
+	    $response = $protocol->request($request, $proxy,
+					   $arg, $size, $timeout);
+	};
+	if ($@) {
+	    $@ =~ s/\s+at\s+\S+\s+line\s+\d+\.?\s*//;
+	    $response =
+	      HTTP::Response->new(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
+				  $@);
+	}
+    } else {
+	$response = $protocol->request($request, $proxy,
+				       $arg, $size, $timeout);
+	# XXX: Should we die unless $response->is_success ???
+    }
+
+    $response->request($request);  # record request for reference
+    $cookie_jar->extract_cookies($response) if $cookie_jar;
+    $response->header("Client-Date" => HTTP::Date::time2str(time));
+    return $response;
+}
+
+
+=item $ua->request($request, $arg [, $size])
+
+Process a request, including redirects and security.  This method may
+actually send several different simple reqeusts.
+
+The arguments are the same as for C<simple_request()>.
+
+=cut
+
+sub request
+{
+    my($self, $request, $arg, $size, $previous) = @_;
+
+    LWP::Debug::trace('()');
+
+    my $response = $self->simple_request($request, $arg, $size);
+
+    my $code = $response->code;
+    $response->previous($previous) if defined $previous;
+
+    LWP::Debug::debug('Simple result: ' . HTTP::Status::status_message($code));
+
+    if ($code == &HTTP::Status::RC_MOVED_PERMANENTLY or
+	$code == &HTTP::Status::RC_MOVED_TEMPORARILY) {
+
+	# Make a copy of the request and initialize it with the new URI
+	my $referral = $request->clone;
+
+	# And then we update the URL based on the Location:-header.
+	# Some servers erroneously return a relative URL for redirects,
+	# so make it absolute if it not already is.
+	my $referral_uri = (URI::URL->new($response->header('Location'),
+					  $response->base))->abs(undef,1);
+
+	$referral->url($referral_uri);
+
+	return $response unless $self->redirect_ok($referral);
+
+	# Check for loop in the redirects
+	my $count = 0;
+	my $r = $response;
+	while ($r) {
+	    if (++$count > 13 ||
+                $r->request->url->as_string eq $referral_uri->as_string) {
+		$response->header("Client-Warning" =>
+				  "Redirect loop detected");
+		return $response;
+	    }
+	    $r = $r->previous;
+	}
+
+	return $self->request($referral, $arg, $size, $response);
+
+    } elsif ($code == &HTTP::Status::RC_UNAUTHORIZED ||
+	     $code == &HTTP::Status::RC_PROXY_AUTHENTICATION_REQUIRED
+	    )
+    {
+	my $proxy = ($code == &HTTP::Status::RC_PROXY_AUTHENTICATION_REQUIRED);
+	my $ch_header = $proxy ?  "Proxy-Authenticate" : "WWW-Authenticate";
+	my $challenge = $response->header($ch_header);
+	unless (defined $challenge) {
+	    $response->header("Client-Warning" => 
+			      "Missing Authenticate header");
+	    return $response;
+	}
+
+	require HTTP::Headers::Util;
+	$challenge =~ tr/,/;/;  # "," is used to separate auth-params!!
+	($challenge) = HTTP::Headers::Util::split_header_words($challenge);
+	my $scheme = lc(shift(@$challenge));
+	shift(@$challenge); # no value
+	$challenge = { @$challenge };  # make rest into a hash
+	for (keys %$challenge) {       # make sure all keys are lower case
+	    $challenge->{lc $_} = delete $challenge->{$_};
+	}
+
+	unless ($scheme =~ /^([a-z]+(?:-[a-z]+)*)$/) {
+	    $response->header("Client-Warning" => 
+			      "Bad authentication scheme '$scheme'");
+	    return $response;
+	}
+	$scheme = $1;  # untainted now
+	my $class = "LWP::Authen::\u$scheme";
+	$class =~ s/-/_/g;
+	
+	no strict 'refs';
+	unless (defined %{"$class\::"}) {
+	    # try to load it
+	    eval "require $class";
+	    if ($@) {
+		if ($@ =~ /^Can\'t locate/) {
+		    $response->header("Client-Warning" =>
+				      "Unsupport authentication scheme '$scheme'");
+		} else {
+		    $response->header("Client-Warning" => $@);
+		}
+		return $response;
+	    }
+	}
+	return $class->authenticate($self, $proxy, $challenge, $response,
+				    $request, $arg, $size);
+    }
+    return $response;
+}
+
+
+=item $ua->redirect_ok
+
+This method is called by request() before it tries to do any
+redirects.  It should return a true value if the redirect is allowed
+to be performed. Subclasses might want to override this.
+
+The default implementation will return FALSE for POST request and TRUE
+for all others.
+
+=cut
+
+sub redirect_ok
+{
+    # draft-ietf-http-v10-spec-02.ps from www.ics.uci.edu, specify:
+    #
+    # If the 30[12] status code is received in response to a request using
+    # the POST method, the user agent must not automatically redirect the
+    # request unless it can be confirmed by the user, since this might change
+    # the conditions under which the request was issued.
+
+    my($self, $request) = @_;
+    return 0 if $request->method eq "POST";
+    1;
+}
+
+
+=item $ua->credentials($netloc, $realm, $uname, $pass)
+
+Set the user name and password to be used for a realm.  It is often more
+useful to specialize the get_basic_credentials() method instead.
+
+=cut
+
+sub credentials
+{
+    my($self, $netloc, $realm, $uid, $pass) = @_;
+    @{ $self->{'basic_authentication'}{$netloc}{$realm} } = ($uid, $pass);
+}
+
+
+=item $ua->get_basic_credentials($realm, $uri, [$proxy])
+
+This is called by request() to retrieve credentials for a Realm
+protected by Basic Authentication or Digest Authentication.
+
+Should return username and password in a list.  Return undef to abort
+the authentication resolution atempts.
+
+This implementation simply checks a set of pre-stored member
+variables. Subclasses can override this method to e.g. ask the user
+for a username/password.  An example of this can be found in
+C<lwp-request> program distributed with this library.
+
+=cut
+
+sub get_basic_credentials
+{
+    my($self, $realm, $uri, $proxy) = @_;
+    return if $proxy;
+
+    my $netloc = $uri->netloc;
+    if (exists $self->{'basic_authentication'}{$netloc}{$realm}) {
+	return @{ $self->{'basic_authentication'}{$netloc}{$realm} };
+    }
+
+    return (undef, undef);
+}
+
+
+=item $ua->agent([$product_id])
+
+Get/set the product token that is used to identify the user agent on
+the network.  The agent value is sent as the "User-Agent" header in
+the requests. The default agent name is "libwww-perl/#.##", where
+"#.##" is substitued with the version numer of this library.
+
+The user agent string should be one or more simple product identifiers
+with an optional version number separated by the "/" character.
+Examples are:
+
+  $ua->agent('Checkbot/0.4 ' . $ua->agent);
+  $ua->agent('Mozilla/5.0');
+
+=item $ua->from([$email_address])
+
+Get/set the Internet e-mail address for the human user who controls
+the requesting user agent.  The address should be machine-usable, as
+defined in RFC 822.  The from value is send as the "From" header in
+the requests.  There is no default.  Example:
+
+  $ua->from('aas@sn.no');
+
+=item $ua->timeout([$secs])
+
+Get/set the timeout value in seconds. The default timeout() value is
+180 seconds, i.e. 3 minutes.
+
+=item $ua->cookie_jar([$cookies])
+
+Get/set the I<HTTP::Cookies> object to use.  The default is to have no
+cookie_jar, i.e. never automatically add "Cookie" headers to the
+requests.
+
+=item $ua->parse_head([$boolean])
+
+Get/set a value indicating wether we should initialize response
+headers from the E<lt>head> section of HTML documents. The default is
+TRUE.  Do not turn this off, unless you know what you are doing.
+
+=item $ua->max_size([$bytes])
+
+Get/set the size limit for response content.  The default is undef,
+which means that there is not limit.  If the returned response content
+is only partial, because the size limit was exceeded, then a
+"X-Content-Range" header will be added to the response.
+
+=cut
+
+sub timeout    { shift->_elem('timeout',   @_); }
+sub agent      { shift->_elem('agent',     @_); }
+sub from       { shift->_elem('from',      @_); }
+sub cookie_jar { shift->_elem('cookie_jar',@_); }
+sub parse_head { shift->_elem('parse_head',@_); }
+sub max_size   { shift->_elem('max_size',  @_); }
+
+# depreciated
+sub use_eval   { shift->_elem('use_eval',  @_); }
+sub use_alarm
+{
+    Carp::carp("LWP::UserAgent->use_alarm(BOOL) is a no-op")
+	if @_ > 1 && $^W;
+    "";
+}
+
+
+# Declarations of AutoLoaded methods
+sub clone;
+sub is_protocol_supported;
+sub mirror;
+sub proxy;
+sub env_proxy;
+sub no_proxy;
+sub _need_proxy;
+
+
+1;
+__END__
+
+
+=item $ua->clone;
+
+Returns a copy of the LWP::UserAgent object
+
+=cut
+
+
+sub clone
+{
+    my $self = shift;
+    my $copy = bless { %$self }, ref $self;  # copy most fields
+
+    # elements that are references must be handled in a special way
+    $copy->{'no_proxy'} = [ @{$self->{'no_proxy'}} ];  # copy array
+
+    $copy;
+}
+
+
+=item $ua->is_protocol_supported($scheme)
+
+You can use this method to query if the library currently support the
+specified C<scheme>.  The C<scheme> might be a string (like 'http' or
+'ftp') or it might be an URI::URL object reference.
+
+=cut
+
+sub is_protocol_supported
+{
+    my($self, $scheme) = @_;
+    if (ref $scheme) {
+	# assume we got a reference to an URI::URL object
+	$scheme = $scheme->abs->scheme;
+    } else {
+	Carp::croak("Illeal scheme '$scheme' passed to is_protocol_supported")
+	    if $scheme =~ /\W/;
+	$scheme = lc $scheme;
+    }
+    return LWP::Protocol::implementor($scheme);
+}
+
+
+=item $ua->mirror($url, $file)
+
+Get and store a document identified by a URL, using If-Modified-Since,
+and checking of the Content-Length.  Returns a reference to the
+response object.
+
+=cut
+
+sub mirror
+{
+    my($self, $url, $file) = @_;
+
+    LWP::Debug::trace('()');
+    my $request = new HTTP::Request('GET', $url);
+
+    if (-e $file) {
+	my($mtime) = (stat($file))[9];
+	if($mtime) {
+	    $request->header('If-Modified-Since' =>
+			     HTTP::Date::time2str($mtime));
+	}
+    }
+    my $tmpfile = "$file-$$";
+
+    my $response = $self->request($request, $tmpfile);
+    if ($response->is_success) {
+
+	my $file_length = (stat($tmpfile))[7];
+	my($content_length) = $response->header('Content-length');
+
+	if (defined $content_length and $file_length < $content_length) {
+	    unlink($tmpfile);
+	    die "Transfer truncated: " .
+		"only $file_length out of $content_length bytes received\n";
+	} elsif (defined $content_length and $file_length > $content_length) {
+	    unlink($tmpfile);
+	    die "Content-length mismatch: " .
+		"expected $content_length bytes, got $file_length\n";
+	} else {
+	    # OK
+	    if (-e $file) {
+		# Some dosish systems fail to rename if the target exists
+		chmod 0777, $file;
+		unlink $file;
+	    }
+	    rename($tmpfile, $file) or
+		die "Cannot rename '$tmpfile' to '$file': $!\n";
+
+	    if (my $lm = $response->last_modified) {
+		# make sure the file has the same last modification time
+		utime $lm, $lm, $file;
+	    }
+	}
+    } else {
+	unlink($tmpfile);
+    }
+    return $response;
+}
+
+=item $ua->proxy(...)
+
+Set/retrieve proxy URL for a scheme:
+
+ $ua->proxy(['http', 'ftp'], 'http://proxy.sn.no:8001/');
+ $ua->proxy('gopher', 'http://proxy.sn.no:8001/');
+
+The first form specifies that the URL is to be used for proxying of
+access methods listed in the list in the first method argument,
+i.e. 'http' and 'ftp'.
+
+The second form shows a shorthand form for specifying
+proxy URL for a single access scheme.
+
+=cut
+
+sub proxy
+{
+    my($self, $key, $proxy) = @_;
+
+    LWP::Debug::trace("$key, $proxy");
+
+    if (!ref($key)) {   # single scalar passed
+	my $old = $self->{'proxy'}{$key};
+	$self->{'proxy'}{$key} = $proxy;
+	return $old;
+    } elsif (ref($key) eq 'ARRAY') {
+	for(@$key) {    # array passed
+	    $self->{'proxy'}{$_} = $proxy;
+	}
+    }
+    return undef;
+}
+
+=item $ua->env_proxy()
+
+Load proxy settings from *_proxy environment variables.  You might
+specify proxies like this (sh-syntax):
+
+  gopher_proxy=http://proxy.my.place/
+  wais_proxy=http://proxy.my.place/
+  no_proxy="my.place"
+  export gopher_proxy wais_proxy no_proxy
+
+Csh or tcsh users should use the C<setenv> command to define these
+envirionment variables.
+
+=cut
+
+sub env_proxy {
+    my ($self) = @_;
+    my($k,$v);
+    while(($k, $v) = each %ENV) {
+	$k = lc($k);
+	next unless $k =~ /^(.*)_proxy$/;
+	$k = $1;
+	if ($k eq 'no') {
+	    $self->no_proxy(split(/\s*,\s*/, $v));
+	}
+	else {
+	    $self->proxy($k, $v);
+	}
+    }
+}
+
+=item $ua->no_proxy($domain,...)
+
+Do not proxy requests to the given domains.  Calling no_proxy without
+any domains clears the list of domains. Eg:
+
+ $ua->no_proxy('localhost', 'no', ...);
+
+=cut
+
+sub no_proxy {
+    my($self, @no) = @_;
+    if (@no) {
+	push(@{ $self->{'no_proxy'} }, @no);
+    }
+    else {
+	$self->{'no_proxy'} = [];
+    }
+}
+
+
+# Private method which returns the URL of the Proxy configured for this
+# URL, or undefined if none is configured.
+sub _need_proxy
+{
+    my($self, $url) = @_;
+
+    $url = new URI::URL($url) unless ref $url;
+
+    LWP::Debug::trace("($url)");
+
+    # check the list of noproxies
+
+    if (@{ $self->{'no_proxy'} }) {
+	my $host = $url->host;
+	return undef unless defined $host;
+	my $domain;
+	for $domain (@{ $self->{'no_proxy'} }) {
+	    if ($host =~ /$domain$/) {
+		LWP::Debug::trace("no_proxy configured");
+		return undef;
+	    }
+	}
+    }
+
+    # Currently configured per scheme.
+    # Eventually want finer granularity
+
+    my $scheme = $url->scheme;
+    if (exists $self->{'proxy'}{$scheme}) {
+
+	LWP::Debug::debug('Proxied');
+	return new URI::URL($self->{'proxy'}{$scheme});
+    }
+
+    LWP::Debug::debug('Not proxied');
+    undef;
+}
+
+1;
+
+=back
+
+=head1 SEE ALSO
+
+See L<LWP> for a complete overview of libwww-perl5.  See F<lwp-request> and
+F<lwp-mirror> for examples of usage.
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/media.types
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/media.types	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/LWP/media.types	(revision 16561)
@@ -0,0 +1,120 @@
+# $Id$
+#
+# This file defines the mapping from file name extentions to media types.
+# Media types where the subtype does not have the "x-" prefix should be
+# registered by IANA:
+#
+#   ftp://www.isi.edu/in-notes/iana/assignments/media-types/
+#
+# See also: RFC-1590
+
+# Text formats
+text/html			html htm
+text/plain			txt text pm el c h cc hh cxx hxx f90
+text/richtext			rtx
+text/tab-separated-values	tsv
+
+text/x-setext			etx
+text/x-pod			pod			# Perl documentation
+
+# Image formats
+image/png			png
+image/gif			gif			# Compuserve
+image/ief			ief
+image/jpeg			jpeg jpg jpe jfif
+image/tiff			tiff tif
+
+image/x-cmu-raster		ras
+image/x-portable-anymap		pnm
+image/x-portable-bitmap		pbm
+image/x-portable-graymap	pgm
+image/x-portable-pixmap		ppm
+image/x-rgb			rgb			# SGI IRIS
+image/x-xbitmap			xbm			# X Windows
+image/x-xpixmap			xpm			# X Windows
+image/x-xwindowdump		xwd			# X Windows
+image/x-bmp			bmp			# MS Windows
+image/x-targa			tga
+image/x-fits			fts
+
+
+# Audio formats
+audio/basic			au snd
+audio/x-aiff			aif aiff aifc
+audio/x-wav			wav			# MS Windows
+audio/x-mpeg-2			mp2			# MPEG layer 2 audio
+
+# Video formats
+video/mpeg			mpeg mpg mpe
+video/quicktime			qt
+video/x-msvideo			avi
+video/x-sgi-movie		mov
+
+# Message
+message/rfc822			mail
+message/partial
+message/external-body
+message/news			art
+
+# Applications
+application/octet-stream	bin
+application/postscript		ai eps ps
+application/oda			oda
+application/atomicmail
+application/andrew-insert
+application/slate
+application/wita
+application/dec-dx
+application/dca-rtf
+application/activemessage
+application/rtf			rft			# MS Word
+application/applefile
+application/mac-binhex40	hqx
+application/news-message-id
+application/news-transmission
+application/wordperfect5.1
+application/pdf			pdf
+application/zip			zip
+application/macwriteii
+application/msword
+application/remote-printing
+application/mathematica
+application/cybercash
+application/commonground
+application/iges
+application/riscos
+application/eshop
+
+application/x-mif		mif			# FrameMaker
+application/x-maker		fm
+application/x-csh		csh
+application/x-dvi		dvi
+application/x-hdf		hdf
+application/x-latex		latex
+application/x-netcdf		nc cdf
+application/x-sh		sh
+application/x-tcl		tcl
+application/x-perl		pl
+application/x-tex		tex
+application/x-texinfo		texinfo texi
+application/x-troff		t tr roff
+application/x-troff-man		man 1 2 3 4 5 6 7 8
+application/x-troff-me		me
+application/x-troff-ms		ms
+application/x-wais-source	src
+application/x-bcpio		bcpio
+application/x-cpio		cpio
+application/x-gtar		gtar			# GNU tar
+application/x-shar		shar
+application/x-sv4cpio		sv4cpio
+application/x-sv4crc		sv4crc
+application/x-tar		tar
+application/x-ustar		ustar
+
+# Multipart
+multipart/mixed
+multipart/alternative
+multipart/digest
+multipart/parallel
+multipart/appledouble
+multipart/header-set
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Escape.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Escape.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Escape.pm	(revision 16561)
@@ -0,0 +1,119 @@
+#
+# $Id$
+#
+
+package URI::Escape;
+use strict;
+
+=head1 NAME
+
+URI::Escape - Escape and unescape unsafe characters
+
+=head1 SYNOPSIS
+
+ use URI::Escape;
+ $safe = uri_escape("10% is enough\n");
+ $verysafe = uri_escape("foo", "\0-\377");
+ $str  = uri_unescape($safe);
+
+=head1 DESCRIPTION
+
+This module provide functions to escape and unescape URI strings.
+Some characters are regarded as "unsafe" and must be escaped in
+accordance with RFC 1738.  Escaped characters are represented by a
+triplet consisting of the character "%" followed by two hexadecimal
+digits.  The following functions are provided (and exported by
+default):
+
+=over 4
+
+=item uri_escape($string, [$unsafe])
+
+This function replaces all unsafe characters in the $string with their
+escape sequence and return the result.
+
+The uri_escape() function takes an optional second argument that
+overrides the set of characters that are to be escaped.  The set is
+specified as a string that can be used in a regular expression
+character class (between [ ]).  E.g.:
+
+  \x00-\x1f\x7f-\xff          # all control and hi-bit characters
+  a-z                         # all lower case characters
+  ^A-Za-z                     # everything not a letter
+
+The default set of characters to be escaped is:
+
+  \x00-\x20"#%;<>?{}|\\^~`\[\]\x7F-\xFF
+
+=item uri_unescape($string)
+
+Returns a string with all %XX sequences replaced with the actual
+character.
+
+=back
+
+The module can also export the %escapes hash which contains the
+mapping from all characters to the corresponding escape code.
+
+=head1 SEE ALSO
+
+L<URI::URL>
+
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
+use vars qw(%escapes);
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(uri_escape uri_unescape);
+@EXPORT_OK = qw(%escapes);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use Carp ();
+
+# Build a char->hex map
+for (0..255) {
+    $escapes{chr($_)} = sprintf("%%%02X", $_);
+}
+
+my %subst;  # compiled patternes
+
+sub uri_escape
+{
+    my($text, $patn) = @_;
+    return undef unless defined $text;
+    if (defined $patn){
+	unless (exists  $subst{$patn}) {
+	    # Because we can't compile regex we fake it with a cached sub
+	    $subst{$patn} =
+	      eval "sub {\$_[0] =~ s/([$patn])/\$escapes{\$1}/g; }";
+	    Carp::croak("uri_escape: $@") if $@;
+	}
+	&{$subst{$patn}}($text);
+    } else {
+	# Default unsafe characters. (RFC1738 section 2.2)
+	$text =~ s/([\x00-\x20\"#%;<>?{}|\\^~`\[\]\x7F-\xFF])/$escapes{$1}/g;
+    }
+    $text;
+}
+
+sub uri_unescape
+{
+    # Note from RFC1630:  "Sequences which start with a percent sign
+    # but are not followed by two hexadecimal characters are reserved
+    # for future extension"
+    my @copy = @_;
+    for (@copy) { s/%([\dA-Fa-f]{2})/chr(hex($1))/eg; }
+    wantarray ? @copy : $copy[0];
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Heuristic.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Heuristic.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/Heuristic.pm	(revision 16561)
@@ -0,0 +1,191 @@
+package URI::Heuristic;
+
+# $Id$
+
+=head1 NAME
+
+uf_urlstr - Expand URL using heuristics
+
+=head1 SYNOPSIS
+
+ use URI::Heuristic qw(uf_urlstr);
+ $url = uf_urlstr("perl");             # http://www.perl.com
+ $url = uf_urlstr("www.sol.no/sol");   # http://www.sol.no/sol
+ $url = uf_urlstr("aas");              # http://www.aas.no
+ $url = uf_urlstr("ftp.funet.fi");     # ftp://ftp.funet.fi
+ $url = uf_urlstr("/etc/passwd");      # file:/etc/passwd
+
+=head1 DESCRIPTION
+
+This module provide functions that expand strings into real absolute
+URLs using some builtin heuristics.  Strings that already represent
+absolute URLs (i.e. start with a C<scheme:> part) are never modified
+and are returned unchanged.  The main use of these functions are to
+allow abbreviated URLs similar to what many web browsers allow for URLs
+typed in by the user.
+
+The following functions are provided:
+
+=over 4
+
+=item uf_urlstr($str)
+
+The uf_urlstr() function will try to make the string passed as
+argument into a proper absolute URL string.  The "uf_" prefix stands
+for "User Friendly".
+
+=item uf_url($str)
+
+This functions work the same way as uf_urlstr() but it will
+return a C<URI::URL> object.
+
+=back
+
+=head1 ENVIRONMENT
+
+If the hostname portion of a URL does not contain any dots, then
+certain qualified guesses will be made.  These guesses are governed be
+the following two environment variables.
+
+=over 10
+
+=item COUNTRY
+
+This is the two letter country code (ISO 3166) for your location.  If
+the domain name of your host ends with two letters, then it is taken
+to be the default country. See also L<Locale::Country>.
+
+=item URL_GUESS_PATTERN
+
+Contain a space separated list of URL patterns to try.  The string
+"ACME" is for some reason used as a placeholder for the host name in
+the URL provided.  Example:
+
+ URL_GUESS_PATTERN="www.ACME.no www.ACME.se www.ACME.com"
+ export URL_GUESS_PATTERN
+
+Specifying URL_GUESS_PATTERN disables any guessing rules based on
+country.  An empty URL_GUESS_PATTERN disables any guessing that
+involves host name lookups.
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 1997-1998, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+use strict;
+
+use vars qw(@EXPORT_OK $VERSION $MY_COUNTRY %LOCAL_GUESSING $DEBUG);
+
+require Exporter;
+*import = \&Exporter::import;
+@EXPORT_OK = qw(uf_url uf_urlstr);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+eval {
+    require Net::Domain;
+    my $fqdn = Net::Domain::hostfqdn();
+    $MY_COUNTRY = lc($1) if $fqdn =~ /\.([a-zA-Z]{2})$/;
+
+    # Some other heuristics to guess country?  Perhaps looking
+    # at some environment variable (LANG, LC_ALL, ???)
+    $MY_COUNTRY = $ENV{COUNTRY} if exists $ENV{COUNTRY};
+};
+
+%LOCAL_GUESSING =
+(
+ 'us' => [qw(www.ACME.gov www.ACME.mil)],
+ 'uk' => [qw(www.ACME.co.uk www.ACME.org.uk www.ACME.ac.uk)],
+ 'au' => [qw(www.ACME.com.au www.ACME.org.au www.ACME.edu.au)],
+ 'il' => [qw(www.ACME.co.il www.ACME.org.il www.ACME.net.il)],
+ # send corrections and new entries to <aas@sn.no>
+);
+
+
+sub uf_url ($)
+{
+    require URI::URL;
+    URI::URL->new(uf_urlstr($_[0]));
+}
+
+
+sub uf_urlstr ($)
+{
+    local($_) = @_;
+    print STDERR "uf_urlstr: resolving $_\n" if $DEBUG;
+    return unless defined;
+
+    s/^\s+//;
+    s/\s+$//;
+
+    if (/^(www|web|home)\./) {
+	$_ = "http://$_";
+
+    } elsif (/^(ftp|gopher|news|wais|http|https)\./) {
+	$_ = "$1://$_";
+
+    } elsif (m,^/,      ||          # absolute file name
+	     m,^\.\.?/, ||          # relative file name
+	     m,^[a-zA-Z]:[/\\],)    # dosish file name
+    {
+	$_ = "file:$_";
+
+    } elsif (/^\w+([\.\-]\w+)*\@(\w+\.)+\w{2,3}$/) {
+	$_ = "mailto:$_";
+
+    } elsif (!/^[.+\-\w]+:/) {      # no scheme specified
+	if (s/^(\w+(?:\.\w+)*)([\/:\?\#]|$)/$2/) {
+	    my $host = $1;
+
+	    if ($host !~ /\./ && $host ne "localhost") {
+		my @guess;
+		if (exists $ENV{URL_GUESS_PATTERN}) {
+		    @guess = map { s/\bACME\b/$host/; $_ }
+		             split(' ', $ENV{URL_GUESS_PATTERN});
+		} else {
+		    if ($MY_COUNTRY) {
+			my $special = $LOCAL_GUESSING{$MY_COUNTRY};
+			if ($special) {
+			    my @special = @$special;
+			    push(@guess, map { s/\bACME\b/$host/; $_ }
+                                               @special);
+			} else {
+			    push(@guess, "www.$host.$MY_COUNTRY");
+			}
+		    }
+		    push(@guess, map "www.$host.$_",
+			             "com", "org", "net", "edu", "int");
+		}
+
+
+		my $guess;
+		for $guess (@guess) {
+		    print STDERR "uf_urlstr: gethostbyname('$guess')..."
+		      if $DEBUG;
+		    if (gethostbyname($guess)) {
+			print STDERR "yes\n" if $DEBUG;
+			$host = $guess;
+			last;
+		    }
+		    print STDERR "no\n" if $DEBUG;
+		}
+	    }
+	    $_ = "http://$host$_";
+
+	} else {
+	    # pure junk, just return it unchanged...
+
+	}
+    }
+    print STDERR "uf_urlstr: ==> $_\n" if $DEBUG;
+
+    $_;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL.pm	(revision 16561)
@@ -0,0 +1,949 @@
+package URI::URL;
+
+$VERSION = "4.15";   # $Date$
+sub Version { $VERSION; }
+
+require 5.004;
+
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(url);
+
+require AutoLoader;
+*AUTOLOAD = \&AutoLoader::AUTOLOAD;
+
+use Carp ();
+
+# Basic lexical elements, taken from RFC 1738:
+#
+#  safe         = "$" | "-" | "_" | "." | "+"
+#  extra        = "!" | "*" | "'" | "(" | ")" | ","
+#  national     = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
+#  punctuation  = "<" | ">" | "#" | "%" | <">
+#  reserved     = ";" | "/" | "?" | ":" | "@" | "&" | "="
+#  escape       = "%" hex hex
+#  unreserved   = alpha | digit | safe | extra
+#  uchar        = unreserved | escape
+#  xchar        = unreserved | reserved | escape
+
+# draft-fielding-url-syntax-05.txt adds '+' to the reserved chars and
+# takes '~' out
+
+use strict;
+use vars qw($reserved $reserved_no_slash $reserved_no_form $unsafe
+	    $Debug $Strict_URL
+	   );
+
+$reserved          = ";\\/?:\\@&=+#%"; # RFC 1738 reserved pluss '#' and '%'
+$reserved_no_slash = ";?:\\@&=+#%";    # used when escaping path
+$reserved_no_form  = ";\\/?:\\@#%";    # used when escaping params and query
+
+# This is the unsafe characters (excluding those reserved)
+$unsafe   = "\x00-\x20{}|\\\\^\\[\\]`<>\"\x7F-\xFF";
+#$unsafe .= "~";  # according to RFC1738 but not to common practice
+
+$Debug         = 0;     # set to 1 to print URLs on creation
+$Strict_URL    = 0;     # see new()
+
+use overload ( '""' => 'as_string', 'fallback' => 1 );
+
+my %Implementor = (); # mapping from scheme to implementation class
+
+
+# Easy to use constructor
+sub url ($;$)
+{
+    URI::URL->new(@_);
+}
+
+
+# URI::URL objects are implemented as blessed hashes:
+#
+# Each of the URL components (scheme, netloc, user, password, host,
+# port, path, params, query, fragment) are stored under their
+# name. The netloc, path, params and query is stored in quoted
+# (escaped) form.  The others is stored unquoted (unescaped).
+#
+# Netloc is special since it is rendundant (same as
+# "user:password@host:port") and must be kept in sync with those.
+#
+# The '_str' key stores a cached stringified version of the URL
+# (by definition in quoted form).
+# The '_base' key stores the optional base of a relative URL.
+#
+# The '_orig_url' is used while debugging is on.
+#
+# Subclasses may add their own keys but must take great care to
+# avoid names which might be used in later verions of this module.
+
+sub new
+{
+    my($class, $init, $base) = @_;
+
+    my $self;
+    if (ref $init) {
+	$self = $init->clone;
+	$self->base($base) if $base;
+    } else {
+	$init = "" unless defined $init;
+	# RFC 1738 appendix suggest that we just ignore extra whitespace
+	$init =~ s/\s+//g;
+	# Also get rid of any <URL: > wrapper
+	$init =~ s/^<(?:URL:)?(.*)>$/$1/;
+
+	# We need a scheme to determine which class to use
+	my($scheme) = $init =~ m/^([.+\-\w]+):/;
+	if (!$scheme and $base){ # get scheme from base
+	    if (ref $base){ # may be object or just a string
+		$scheme = $base->scheme;
+	    } else {
+		$scheme = $1 if $base =~ m/^([.+\-\w]+):/;
+	    }
+	}
+	unless($scheme){
+	    Carp::croak("Unable to determine scheme for '$init'")
+		if $Strict_URL;
+	    $scheme = 'http';
+	}
+	my $impclass = URI::URL::implementor($scheme);
+	unless ($impclass) {
+	    Carp::croak("URI::URL scheme '$scheme' is not supported")
+		if $Strict_URL;
+	    # use generic as fallback
+	    require URI::URL::_generic;
+	    URI::URL::implementor($scheme, 'URI::URL::_generic');
+	    $impclass = 'URI::URL::_generic';
+	}
+
+	# hand-off to scheme specific implementation sub-class
+	$self->{'_orig_url'} = $init if $Debug;
+	$self = $impclass->new($init, $base);
+    }
+    $self->print_on('STDERR') if $Debug;
+    return $self;
+}
+
+
+sub clone
+{
+    my $self = shift;
+    # this work as long as none of the components are references themselves
+    bless { %$self }, ref $self;
+}
+
+
+sub implementor
+{
+    my($scheme, $impclass) = @_;
+    unless (defined $scheme) {
+	require URI::URL::_generic;
+	return 'URI::URL::_generic';
+    }
+
+    $scheme = lc($scheme);
+    if ($impclass) {
+	$impclass->_init_implementor($scheme);
+	my $old = $Implementor{$scheme};
+	$Implementor{$scheme} = $impclass;
+	return $old;
+    }
+
+    my $ic = $Implementor{$scheme};
+    return $ic if $ic;
+
+    # scheme not yet known, look for internal or
+    # preloaded (with 'use') implementation
+    $ic = "URI::URL::$scheme";  # default location
+    no strict 'refs';
+    # check we actually have one for the scheme:
+    unless (defined @{"${ic}::ISA"}) {
+	# Try to load it
+	eval { require "URI/URL/$scheme.pm"; };
+	die $@ if $@ && $@ !~ /Can\'t locate/;
+	$ic = '' unless defined @{"${ic}::ISA"};
+    }
+    if ($ic) {
+	$ic->_init_implementor($scheme);
+	$Implementor{$scheme} = $ic;
+    }
+    $ic;
+}
+
+
+sub _init_implementor
+{
+    my($class, $scheme) = @_;
+    # Remember that one implementor class may actually
+    # serve to implement several URL schemes.
+
+    if ($] < 5.003_17) {
+	no strict qw(refs);
+	# Setup overloading inheritace - experimental
+	%{"${class}::OVERLOAD"} = %URI::URL::OVERLOAD
+	    unless defined %{"${class}::OVERLOAD"};
+    }
+}
+
+
+# This private method help us implement access to the elements in the
+# URI::URL object hash (%$self).  You can set up access to an element
+# with a routine similar to this one:
+#
+#  sub component { shift->_elem('component', @_); }
+
+sub _elem
+{
+    my $self = shift;
+    my $elem = shift;
+    my $old = $self->{$elem};
+    if (@_) {
+	$self->{$elem} = shift;
+	$self->{'_str'} = '';        # void cached string
+    }
+    $old;
+}
+
+
+# Make all standard methods available to all kinds of URLs.  This allow
+# us to call these without to much worry when URI::URL::strict(0);
+
+sub bad_method;
+
+*netloc       = \&bad_method;
+*user         = \&bad_method;
+*password     = \&bad_method;
+*host         = \&bad_method;
+*port         = \&bad_method;
+*default_port = \&bad_method;
+
+*full_path    = \&bad_method;
+*epath        = \&bad_method;
+*eparams      = \&bad_method;
+*equery       = \&bad_method;
+
+*path         = \&bad_method;
+*path_components = \&bad_method;
+*params       = \&bad_method;
+*query        = \&bad_method;
+*frag         = \&bad_method;
+
+
+#
+# A U T O  L O A D I N G
+#
+# The rest of the methods are autoloaded because they should be less
+# frequently used.  We define stubs here so that inheritance works as
+# it should.
+sub newlocal;
+sub strict;
+sub base;
+sub scheme;
+sub crack;
+sub abs;
+sub rel;
+sub as_string;
+sub eq;
+sub print_on;
+
+# Don't need DESTROY but avoid trying to AUTOLOAD it.
+sub DESTROY { }
+
+1;
+__END__
+
+sub newlocal
+{
+    require URI::URL::file;
+    my $class = shift;
+    URI::URL::file->newlocal(@_);  # pass it on the the file class
+}
+
+sub strict
+{
+    return $Strict_URL unless @_;
+    my $old = $Strict_URL;
+    $Strict_URL = $_[0];
+    $old;
+}
+
+# Access some attributes of a URL object:
+sub base {
+    my $self = shift;
+    my $base  = $self->{'_base'};
+
+    if (@_) { # set
+	my $new_base = shift;
+	$new_base = $new_base->abs if ref($new_base);  # unsure absoluteness
+	$self->{_base} = $new_base;
+    }
+    return unless defined wantarray;
+
+    # The base attribute supports 'lazy' conversion from URL strings
+    # to URL objects. Strings may be stored but when a string is
+    # fetched it will automatically be converted to a URL object.
+    # The main benefit is to make it much cheaper to say:
+    #   new URI::URL $random_url_string, 'http:'
+    if (defined($base) && !ref($base)) {
+	$base = new URI::URL $base;
+	$self->_elem('_base', $base); # set new object
+    }
+    $base;
+}
+
+sub scheme {
+    my $self = shift;
+    my $old = $self->{'scheme'};
+    if (@_) {
+	my $new_scheme = shift;
+	if (defined($new_scheme) && length($new_scheme)) {
+	    # reparse URL with new scheme
+	    my $str = $self->as_string;
+	    $str =~ s/^[\w+\-.]+://;
+	    my $newself = new URI::URL "$new_scheme:$str";
+	    %$self = %$newself;
+	    bless $self, ref($newself);
+	} else {
+	    $self->{'scheme'} = undef;
+	}
+    }
+    $old;
+}
+
+sub crack
+{
+    # should be overridden by subclasses
+    my $self = shift;
+    ($self->scheme,  # 0: scheme
+     undef,          # 1: user
+     undef,          # 2: passwd
+     undef,          # 3: host
+     undef,          # 4: port
+     undef,          # 5: path
+     undef,          # 6: params
+     undef,          # 7: query
+     undef           # 8: fragment
+    )
+}
+
+# These are overridden by _generic (this is just a noop for those schemes that
+# do not wish to be a subclass of URI::URL::_generic)
+sub abs { shift->clone; }
+sub rel { shift->clone; }
+
+# This method should always be overridden in subclasses
+sub as_string {
+    "<URL>";
+}
+
+# Compare two URLs, subclasses will provide a more correct implementation
+sub eq {
+    my($self, $other) = @_;
+    $other = URI::URL->new($other, $self) unless ref $other;
+    ref($self) eq ref($other) &&
+      $self->scheme eq $other->scheme &&
+      $self->as_string eq $other->as_string;  # Case-sensitive
+}
+
+# This is set up as an alias for various methods
+sub bad_method {
+    my $self = shift;
+    my $scheme = $self->scheme;
+    Carp::croak("Illegal method called for $scheme: URL")
+	if $Strict_URL;
+    # Carp::carp("Illegal method called for $scheme: URL")
+    #     if $^W;
+    undef;
+}
+
+sub print_on
+{
+    no strict qw(refs);  # because we use strings as filehandles
+    my $self = shift;
+    my $fh = shift || 'STDERR';
+    my($k, $v);
+    print $fh "Dump of URI::URL $self...\n";
+    foreach $k (sort keys %$self){
+	$v = $self->{$k};
+	$v = 'UNDEF' unless defined $v;
+	print $fh "  $k\t'$v'\n";
+    }
+}
+
+1;
+
+
+#########################################################################
+#### D O C U M E N T A T I O N
+#########################################################################
+
+=head1 NAME
+
+URI::URL - Uniform Resource Locators (absolute and relative)
+
+=head1 SYNOPSIS
+
+ use URI::URL;
+
+ # Constructors
+ $url1 = new URI::URL 'http://www.perl.com/%7Euser/gisle.gif';
+ $url2 = new URI::URL 'gisle.gif', 'http://www.com/%7Euser';
+ $url3 = url 'http://www.sn.no/'; # handy constructor
+ $url4 = $url2->abs;       # get absolute url using base
+ $url5 = $url2->abs('http:/other/path');
+ $url6 = newlocal URI::URL 'test';
+
+ # Stringify URL
+ $str1 = $url->as_string;  # complete escaped URL string
+ $str2 = $url->full_path;  # escaped path+params+query
+ $str3 = "$url";           # use operator overloading
+
+ # Retrieving Generic-RL components:
+ $scheme   = $url->scheme;
+ $netloc   = $url->netloc; # see user,password,host,port below
+ $path     = $url->path;
+ $params   = $url->params;
+ $query    = $url->query;
+ $frag     = $url->frag;
+
+ # Accessing elements in their escaped form
+ $path     = $url->epath;
+ $params   = $url->eparams;
+ $query    = $url->equery;
+
+ # Retrieving Network location (netloc) components:
+ $user     = $url->user;
+ $password = $url->password;
+ $host     = $url->host;
+ $port     = $url->port;   # returns default if not defined
+
+ # Retrieve escaped path components as an array
+ @path     = $url->path_components;
+
+ # HTTP query-string access methods
+ @keywords = $url->keywords;
+ @form     = $url->query_form;
+
+ # All methods above can set the field values, e.g:
+ $url->scheme('http');
+ $url->host('www.w3.org');
+ $url->port($url->default_port);
+ $url->base($url5);                      # use string or object
+ $url->keywords(qw(dog bones));
+
+ # File methods
+ $url = new URI::URL "file:/foo/bar";
+ open(F, $url->local_path) or die;
+
+ # Compare URLs
+ if ($url->eq("http://www.sn.no")) or die;
+
+=head1 DESCRIPTION
+
+This module implements the URI::URL class representing Uniform
+Resource Locators (URL). URLs provide a compact string representation
+for resources available via the Internet. Both absolute (RFC 1738) and
+relative (RFC 1808) URLs are supported.
+
+URI::URL objects are created by calling new(), which takes as argument
+a string representation of the URL or an existing URL object reference
+to be cloned. Specific individual elements can then be accessed via
+the scheme(), user(), password(), host(), port(), path(), params(),
+query() and frag() methods.  In addition escaped versions of the path,
+params and query can be accessed with the epath(), eparams() and
+equery() methods.  Note that some URL schemes will support all these
+methods.
+
+The object constructor new() must be able to determine the scheme for
+the URL.  If a scheme is not specified in the URL itself, it will use
+the scheme specified by the base URL. If no base URL scheme is defined
+then new() will croak if URI::URL::strict(1) has been invoked,
+otherwise I<http> is silently assumed.  Once the scheme has been
+determined new() then uses the implementor() function to determine
+which class implements that scheme.  If no implementor class is
+defined for the scheme then new() will croak if URI::URL::strict(1)
+has been invoked, otherwise the internal generic URL class is assumed.
+
+Internally defined schemes are implemented by the
+URI::URL::I<scheme_name> module.  The URI::URL::implementor() function
+can be used to explicitly set the class used to implement a scheme if
+you want to override this.
+
+
+=head1 HOW AND WHEN TO ESCAPE
+
+
+=over 3
+
+=item This is an edited extract from a URI specification:
+
+The printability requirement has been met by specifying a safe set of
+characters, and a general escaping scheme for encoding "unsafe"
+characters. This "safe" set is suitable, for example, for use in
+electronic mail.  This is the canonical form of a URI.
+
+There is a conflict between the need to be able to represent many
+characters including spaces within a URI directly, and the need to be
+able to use a URI in environments which have limited character sets
+or in which certain characters are prone to corruption. This conflict
+has been resolved by use of an hexadecimal escaping method which may
+be applied to any characters forbidden in a given context. When URLs
+are moved between contexts, the set of characters escaped may be
+enlarged or reduced unambiguously.  The canonical form for URIs has
+all white spaces encoded.
+
+=item Notes:
+
+A URL string I<must>, by definition, consist of escaped
+components. Complete URLs are always escaped.
+
+The components of a URL string must be I<individually> escaped.  Each
+component of a URL may have a separate requirements regarding what
+must be escaped, and those requirements are also dependent on the URL
+scheme.
+
+Never escape an already escaped component string.
+
+=back
+
+This implementation expects an escaped URL string to be passed to
+new() and will return a fully escaped URL string from as_string()
+and full_path().
+
+Individual components can be manipulated in unescaped or escaped
+form. The following methods return/accept unescaped strings:
+
+    scheme                  path
+    user                    params
+    password                query
+    host                    frag
+    port
+
+The following methods return/accept partial I<escaped> strings:
+
+    netloc                  eparams
+    epath                   equery
+
+I<Partial escaped> means that only reserved characters
+(i.e. ':', '@', '/', ';', '?', '=', '&' in addition to '%', '.' and '#')
+needs to be escaped when they are to be treated as normal characters.
+I<Fully escaped> means that all unsafe characters are escaped. Unsafe
+characters are all all control characters (%00-%1F and %7F), all 8-bit
+characters (%80-%FF) as well
+as '{', '}', '|', '\', '^', '[', ']' '`', '"', '<' and '>'.
+Note that the character '~' is B<not> considered
+unsafe by this library as it is common practice to use it to reference
+personal home pages, but it is still unsafe according to RFC 1738.
+
+=head1 ADDING NEW URL SCHEMES
+
+New URL schemes or alternative implementations for existing schemes
+can be added to your own code. To create a new scheme class use code
+like:
+
+   package MYURL::foo;
+   @ISA = (URI::URL::implementor());   # inherit from generic scheme
+
+The 'URI::URL::implementor()' function call with no parameters returns
+the name of the class which implements the generic URL scheme
+behaviour (typically C<URI::URL::_generic>). All hierarchical schemes
+should be derived from this class.
+
+Your class can then define overriding methods (e.g., new(), _parse()
+as required).
+
+To register your new class as the implementor for a specific scheme
+use code like:
+
+   URI::URL::implementor('x-foo', 'MYURL::foo');
+
+Any new URL created for scheme 'x-foo' will be implemented by your
+C<MYURL::foo> class. Existing URLs will not be affected.
+
+
+=head1 FUNCTIONS
+
+=over 3
+
+=item $url = URI::URL->new( $url_string [, $base_url] )
+
+This is the object constructor.  It will create a new URI::URL object,
+initialized from the URL string.
+
+=item $url = URI::URL->newlocal($path);
+
+Returns an URL object that denotes a path within the local filesystem.
+Paths not starting with '/' are interpreted relative to the current
+working directory.  This constructor always return an absolute 'file'
+URL.
+
+=item $url = url($url_string, [, $base_url])
+
+Alternative constructor function.  The url() function is exported by
+the URI::URL module and is easier both to type and read than calling
+C<URI::URL->new> directly.  Useful for constructs like this:
+
+   $h = url($str)->host;
+
+This function is just a wrapper for URI::URL->new.
+
+=item URI::URL::strict($bool)
+
+If strict is true then we croak on errors.  The function returns the
+previous value.
+
+=item URI::URL::implementor([$scheme, [$class]])
+
+Use this function to get or set implementor class for a scheme.
+Returns '' if specified scheme is not supported.  Returns generic URL
+class if no scheme specified.
+
+=back
+
+=head1 METHODS
+
+This section describes the methods available for an URI::URL object.
+Note that some URL schemes will disallow some of these methods and
+will croak if they are used.  Some URL schemes add additional methods
+that are described in the sections to follow.
+
+Attribute access methods marked with (*) can take an optional argument
+to set the value of the attribute, and they always return the old
+value.
+
+=over 3
+
+=item $url->abs([$base])
+
+The abs() method attempts to return a new absolute URI::URL object
+for a given URL.  In order to convert a relative URL into an absolute
+one, a I<base> URL is required. You can associate a default base with a
+URL either by passing a I<base> to the new() constructor when a
+URI::URL is created or using the base() method on the object later.
+Alternatively you can specify a one-off base as a parameter to the
+abs() method.
+
+The rel() method will do the opposite transformation.
+
+Some older parsers used to allow the scheme name to be present in the
+relative URL if it was the same as the base URL scheme.  RFC1808 says
+that this should be avoided, but you can enable this old behaviour by
+setting the $URI::URL::ABS_ALLOW_RELATIVE_SCHEME variable to a TRUE
+value.  The difference is demonstrated by the following examples:
+
+  url("http:foo")->abs("http://host/a/b")     ==>  "http:foo"
+
+  local $URI::URL::ABS_ALLOW_RELATIVE_SCHEME = 1;
+  url("http:foo")->abs("http://host/a/b")     ==>  "http:/host/a/foo"
+
+You can also have the abs() method ignore if there is too many ".."
+segments in the relative URL by setting
+$URI::URL::ABS_REMOTE_LEADING_DOTS to a TRUE value.  The difference is
+demonstrated by the following examples:
+
+  url("../../../foo")->abs("http://host/a/b")   ==> "http://host/../../foo"
+
+  local $URI::URL::ABS_REMOTE_LEADING_DOTS = 1;
+  url("../../../foo")->abs("http://host/a/b")   ==> "http://host/foo"
+
+
+=item $url->as_string
+
+Returns a string representing the URL in its canonical form.  All
+unsafe characters will be escaped.  This method is overloaded as the
+perl "stringify" operator, which means that URLs can be used as
+strings in many contexts.
+
+=item $url->base (*)
+
+Get/set the base URL associated with the current URI::URL object.  The
+base URL matters when you call the abs() method.
+
+=item $url->clone
+
+Returns a copy of the current URI::URL object.
+
+=item $url->crack
+
+Return a 9 element array with the following content:
+
+   0: $url->scheme *)
+   1: $url->user
+   2: $url->password
+   3: $url->host
+   4: $url->port
+   5: $url->epath
+   6: $url->eparams
+   7: $url->equery
+   8: $url->frag
+
+All elements except I<scheme> will be undefined if the corresponding
+URL part is not available.
+
+B<Note:> The scheme (first element) returned by crack will aways be
+defined.  This is different from what the $url->scheme returns, since
+it will return I<undef> for relative URLs.
+
+=item $url->default_port
+
+Returns the default port number for the URL scheme that the URI::URL
+belongs too.
+
+=item $url->eparams (*)
+
+Get/set the URL parameters in escaped form.
+
+=item $url->epath (*)
+
+Get/set the URL path in escaped form.
+
+=item $url->eq($other_url)
+
+Compare two URLs to decide if they match or not.  The rules for how
+comparison is made varies for different parts of the URLs; scheme and
+netloc comparison is case-insensitive, and escaped chars match their
+%XX encoding unless they are "reserved" or "unsafe".
+
+=item $url->equery (*)
+
+Get/set the URL query string in escaped form.
+
+=item $url->full_path
+
+Returns the string "/path;params?query".  This is the string that is
+passed to a remote server in order to access the document.
+
+=item $url->frag (*)
+
+Get/set the fragment (unescaped)
+
+=item $url->host (*)
+
+Get/set the host (unescaped)
+
+=item $url->netloc (*)
+
+Get/set the network location in escaped form.  Setting the network
+location will affect 'user', 'password', 'host' and 'port'.
+
+=item $url->params (*)
+
+Get/set the URL parameters (unescaped)
+
+=item $url->password (*)
+
+Get/set the password (unescaped)
+
+=item $url->path (*)
+
+Get/set the path (unescaped).  This method will croak if any of the
+path components in the return value contain the "/" character.  You
+should use the epath() method to be safe.
+
+=item $url->path_components (*)
+
+Get/set the path using a list of unescaped path components.  The
+return value will loose the distinction beween '.' and '%2E'.  When
+setting a value, a '.' is converted to be a literal '.' and is
+therefore encoded as '%2E'.
+
+=item $url->port (*)
+
+Get/set the network port (unescaped)
+
+=item $url->rel([$base])
+
+Return a relative URL if possible.  This is the opposite of what the
+abs() method does.  For instance:
+
+   url("http://www.math.uio.no/doc/mail/top.html",
+       "http://www.math.uio.no/doc/linux/")->rel
+
+will return a relative URL with path set to "../mail/top.html" and
+with the same base as the original URL.
+
+If the original URL already is relative or the scheme or netloc does
+not match the base, then a copy of the original URL is returned.
+
+
+=item $url->print_on(*FILEHANDLE);
+
+Prints a verbose presentation of the contents of the URL object to
+the specified file handle (default STDERR).  Mainly useful for
+debugging.
+
+=item $url->scheme (*)
+
+Get/set the scheme for the URL.
+
+=item $url->query (*)
+
+Get/set the query string (unescaped).  This method will croak if the
+string returned contains both '+' and '%2B' or '=' together with '%3D'
+or '%26'.  You should use the equery() method to be safe.
+
+=item $url->user (*)
+
+Get/set the URL user name (unescaped)
+
+=back
+
+=head1 HTTP METHODS
+
+For I<http> URLs you may also access the query string using the
+keywords() and the query_form() methods.  Both will croak if the query
+is not of the correct format.  The encodings look like this:
+
+  word1+word2+word3..        # keywords
+  key1=val1&key2=val2...     # query_form
+
+Note: These functions does not return the old value when they are used
+to set a value of the query string.
+
+=over 3
+
+=item $url->keywords (*)
+
+The keywords() method returns a list of unescaped strings.  The method
+can also be used to set the query string by passing in the keywords as
+individual arguments to the method.
+
+=item $url->query_form (*)
+
+The query_form() method return a list of unescaped key/value pairs.
+If you assign the return value to a hash you might loose some values
+if the key is repeated (which it is allowed to do).
+
+This method can also be used to set the query sting of the URL like this:
+
+  $url->query_form(foo => 'bar', foo => 'baz', equal => '=');
+
+If the value part of a key/value pair is a reference to an array, then
+it will be converted to separate key/value pairs for each value.  This
+means that these two calls are equal:
+
+  $url->query_form(foo => 'bar', foo => 'baz');
+  $url->query_form(foo => ['bar', 'baz']);
+
+=back
+
+=head1 FILE METHODS
+
+The I<file> URLs implement the local_path() method that returns a path
+suitable for access to files within the current filesystem.  These
+methods can B<not> be used to set the path of the URL.
+
+=over 3
+
+=item $url->local_path
+
+This method is really just an alias for one of the methods below
+depending on what system you run on.
+
+=item $url->unix_path
+
+Returns a path suitable for use on a Unix system.  This method will
+croak if any of the path segments contains a "/" or a NULL character.
+
+=item $url->dos_path
+
+Returns a path suitable for use on a MS-DOS or MS-Windows system.
+
+=item $url->mac_path
+
+Returns a path suitable for use on a Macintosh system.
+
+=item $url->vms_path
+
+Returns a path suitable for use on a VMS system.  VMS is a trademark
+of Digital.
+
+=back
+
+=head1 GOPHER METHODS
+
+The methods access the parts that are specific for the gopher URLs.
+These methods access different parts of the $url->path.
+
+=over 3
+
+=item $url->gtype (*)
+
+=item $url->selector (*)
+
+=item $url->search (*)
+
+=item $url->string (*)
+
+=back
+
+=head1 NEWS METHODS
+
+=over 3
+
+=item $url->group (*)
+
+=item $url->article (*)
+
+=back
+
+=head1 WAIS METHODS
+
+The methods access the parts that are specific for the wais URLs.
+These methods access different parts of the $url->path.
+
+=over 3
+
+=item $url->database (*)
+
+=item $url->wtype (*)
+
+=item $url->wpath (*)
+
+=back
+
+=head1 MAILTO METHODS
+
+=over 3
+
+=item $url->address (*)
+
+The mail address can also be accessed with the netloc() method.
+
+=back
+
+
+=head1 WHAT A URL IS NOT
+
+URL objects do not, and should not, know how to 'get' or 'put' the
+resources they specify locations for, anymore than a postal address
+'knows' anything about the postal system. The actual access/transfer
+should be achieved by some form of transport agent class (see
+L<LWP::UserAgent>). The agent class can use the URL class, but should
+not be a subclass of it.
+
+=head1 AUTHORS / ACKNOWLEDGMENTS
+
+This module is (distantly) based on the C<wwwurl.pl> code in the
+libwww-perl distribution developed by Roy Fielding
+<fielding@ics.uci.edu>, as part of the Arcadia project at the
+University of California, Irvine, with contributions from Brooks
+Cutter.
+
+Gisle Aas <aas@sn.no>, Tim Bunce <Tim.Bunce@ig.co.uk>, Roy Fielding
+<fielding@ics.uci.edu> and Martijn Koster <m.koster@webcrawler.com>
+(in English alphabetical order) have collaborated on the complete
+rewrite for Perl 5, with input from other people on the libwww-perl
+mailing list.
+
+If you have any suggestions, bug reports, fixes, or enhancements, send
+them to the libwww-perl mailing list at <libwww-perl@ics.uci.edu>.
+
+=head1 COPYRIGHT
+
+Copyright 1995-1997 Gisle Aas.
+Copyright 1995 Martijn Koster.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_generic.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_generic.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_generic.pm	(revision 16561)
@@ -0,0 +1,514 @@
+#####################################################################
+#
+#       Internal pre-defined generic scheme support
+#
+# In this implementation all schemes are subclassed from
+# URI::URL::_generic. This turns out to have reasonable mileage.
+# See also draft-ietf-uri-relative-url-06.txt
+
+package URI::URL::_generic;           # base support for generic-RL's
+require URI::URL;
+@ISA = qw(URI::URL);
+
+use URI::Escape qw(uri_escape uri_unescape %escapes);
+
+
+sub new {                               # inherited by subclasses
+    my($class, $init, $base) = @_;
+    my $url = bless { }, $class;        # create empty object
+    $url->_parse($init);                # parse $init into components
+    $url->base($base) if $base;
+    $url;
+}
+
+
+# Generic-RL parser
+# See draft-ietf-uri-relative-url-06.txt Section 2
+
+sub _parse {
+    my($self, $u, @comps) = @_;
+    return unless defined $u;
+
+    # Deside which components to parse (scheme & path is manatory)
+    @comps = qw(netloc query params frag) unless (@comps);
+    my %parse = map {$_ => 1} @comps;
+
+    # This parsing code is based on
+    #   draft-ietf-uri-relative-url-06.txt Section 2.4
+
+    # 2.4.1
+    $self->{'frag'} = uri_unescape($1)
+      if $parse{'frag'} && $u =~ s/#(.*)$//;
+    # 2.4.2
+    $self->{'scheme'} = lc($1) if $u =~ s/^\s*([\w\+\.\-]+)://;
+    # 2.4.3
+    $self->netloc("$1")	# passing $1 directly fails if netloc is autoloaded
+      if $parse{'netloc'} && $u =~ s!^//([^/]*)!!;
+    # 2.4.4
+    $self->{'query'} = $1
+      if $parse{'query'} && $u =~ s/\?(.*)//;
+    # 2.4.5
+    $self->{'params'} = $1
+      if $parse{'params'} && $u =~ s/;(.*)//;
+
+    # 2.4.6
+    #
+    # RFC 1738 says:
+    #
+    #     Note that the "/" between the host (or port) and the
+    #     url-path is NOT part of the url-path.
+    #
+    # however, RFC 1808, 2.4.6. says:
+    #
+    #    Even though the initial slash is not part of the URL path,
+    #    the parser must remember whether or not it was present so
+    #    that later processes can differentiate between relative
+    #    and absolute paths.  Often this is done by simply storing
+    #    he preceding slash along with the path.
+    #
+    # In version < 4.01 of URI::URL we used to strip the leading
+    # "/" when asked for $self->path().  This created problems for
+    # the consitency of the interface, so now we just consider the
+    # slash to be part of the path and we also make an empty path
+    # default to "/".
+
+    # we don't test for $parse{path} becase it is mandatory
+    $self->{'path'} = $u;
+}
+
+
+# Generic-RL stringify
+#
+sub as_string
+{
+    my $self = shift;
+    return $self->{'_str'} if $self->{'_str'};
+
+    my($scheme, $netloc, $frag) = @{$self}{qw(scheme netloc frag)};
+
+    my $u = $self->full_path(1);  # path+params+query
+
+    # rfc 1808 says:
+    #    Note that the fragment identifier (and the "#" that precedes
+    #    it) is not considered part of the URL.  However, since it is
+    #    commonly used within the same string context as a URL, a parser
+    #    must be able to recognize the fragment when it is present and
+    #    set it aside as part of the parsing process.
+    $u .= "#" . uri_escape($frag, $URI::URL::unsafe) if defined $frag;
+
+    $u = "//$netloc$u" if defined $netloc;
+    $u = "$scheme:$u" if $scheme;
+    # Inline: uri_escape($u, $URI::URL::unsafe);
+    $u =~ s/([$URI::URL::unsafe])/$escapes{$1}/go;
+    $self->{'_str'} = $u;  # set cache and return
+}
+
+# Generic-RL stringify full path "path;params?query"
+#
+sub full_path
+{
+    my($self, $dont_escape)  = @_;
+    my($path, $params, $query) = @{$self}{'path', 'params', 'query'};
+    my $p = '';
+    $p .= $path if defined $path;
+    # see comment in _parse 2.4.6 about the next line
+    $p = "/$p" if defined($self->{netloc}) && $p !~ m:^/:;
+    $p .= ";$params" if defined $params;
+    $p .= "?$query"  if defined $query;
+    return $p if $dont_escape;
+    # Inline: URI::Escape::uri_escape($p, $URI::URL::unsafe);
+    $p =~ s/([$URI::URL::unsafe])/$escapes{$1}/go;
+    $p;
+}
+
+# default_port()
+#
+# subclasses will usually want to override this
+#
+sub default_port { undef; }
+
+
+#####################################################################
+#
+# Methods to handle URL's elements
+
+# These methods always return the current value,
+# so you can use $url->path to read the current value.
+# If a new value is passed, e.g. $url->path('foo'),
+# it also sets the new value, and returns the previous value.
+# Use $url->path(undef) to set the value to undefined.
+
+sub netloc {
+    my $self = shift;
+    my $old = $self->_elem('netloc', @_);
+    return $old unless @_;
+
+    # update fields derived from netloc
+    my $nl = $self->{'netloc'} || '';
+    if ($nl =~ s/^([^:@]*):?(.*?)@//){
+	$self->{'user'}     = uri_unescape($1);
+	$self->{'password'} = uri_unescape($2) if $2 ne '';
+    }
+    if ($nl =~ /^([^:]*):?(\d*)$/){
+	my $port = $2;
+	# Since this happes so frequently, we inline this call:
+	#    my $host = uri_unescape($1);
+	my $host = $1;
+	$host =~ s/%([\dA-Fa-f]{2})/chr(hex($1))/eg;
+	$self->{'host'} = $host;
+	if ($port ne '') {
+	    $self->{'port'} = $port;
+	    if ($self->default_port == $port) {
+		$self->{'netloc'} =~ s/:\d+//;
+	    }
+	} elsif (defined $self->{'netloc'}) {
+	    $self->{'netloc'} =~ s/:$//;  # handle empty port spec
+	}
+    }
+    $self->{'_str'} = '';
+    $old;
+}
+
+
+# A U T O  L O A D E R
+# Don't remove this comment, it keeps AutoSplit happy!!
+# @ISA = qw(AutoLoader)
+#
+# The rest of the methods are only loaded on demand.  Stubs are neccesary
+# for inheritance to work.
+
+#sub netloc;  # because netloc is used by the _parse()
+sub user;
+sub password;
+sub host;
+sub port;
+sub _netloc_elem;
+sub epath;
+sub path;
+sub path_components;
+sub eparams;
+sub params;
+sub equery;
+sub query;
+sub frag;
+sub crack;
+sub abs;
+sub rel;
+sub eq;
+
+1;
+__END__
+
+
+# Fields derived from generic netloc:
+sub user     { shift->_netloc_elem('user',    @_); }
+sub password { shift->_netloc_elem('password',@_); }
+sub host     { shift->_netloc_elem('host',    @_); }
+
+sub port {
+    my $self = shift;
+    my $old = $self->_netloc_elem('port', @_);
+    defined($old) ? $old : $self->default_port;
+}
+
+sub _netloc_elem {
+    my($self, $elem, @val) = @_;
+    my $old = $self->_elem($elem, @val);
+    return $old unless @val;
+
+    # update the 'netloc' element
+    my $nl = '';
+    my $host = $self->{'host'};
+    if (defined $host) {  # can't be any netloc without any host
+	my $user = $self->{'user'};
+	$nl .= uri_escape($user, $URI::URL::reserved) if defined $user;
+	$nl .= ":" . uri_escape($self->{'password'}, $URI::URL::reserved)
+	  if defined($user) and defined($self->{'password'});
+	$nl .= '@' if length $nl;
+	$nl .= uri_escape($host, $URI::URL::reserved);
+	my $port = $self->{'port'};
+	$nl .= ":$port" if defined($port) && $port != $self->default_port;
+    }
+    $self->{'netloc'} = $nl;
+    $self->{'_str'} = '';
+    $old;
+}
+
+sub epath {
+     my $self = shift;
+     my $old = $self->_elem('path', @_);
+     return '/' if !defined($old) || !length($old);
+     return "/$old" if $old !~ m|^/| && defined $self->{'netloc'};
+     $old;
+}
+
+sub path {
+    my $self = shift;
+    my $old = $self->_elem('path',
+		      map uri_escape($_, $URI::URL::reserved_no_slash), @_);
+    return unless defined wantarray;
+    return '/' if !defined($old) || !length($old);
+    Carp::croak("Path components contain '/' (you must call epath)")
+	if $old =~ /%2[fF]/ and !@_;
+    $old = "/$old" if $old !~ m|^/| && defined $self->{'netloc'};
+    return uri_unescape($old);
+}
+
+sub path_components {
+    my $self = shift;
+    my $old = $self->{'path'};
+    $old = '' unless defined $old;
+    $old = "/$old" if $old !~ m|^/| && defined $self->{'netloc'};
+    if (@_) {
+	$self->_elem('path',
+		     join("/", map {uri_escape($_, $URI::URL::reserved)} @_));
+    }
+    map { uri_unescape($_) } split("/", $old, -1);
+}
+
+sub eparams  { shift->_elem('params',  @_); }
+
+sub params {
+    my $self = shift;
+    my $old = $self->_elem('params', map {uri_escape($_,$URI::URL::reserved_no_form)} @_);
+    return uri_unescape($old) if defined $old;
+    undef;
+}
+
+sub equery   { shift->_elem('query',   @_); }
+
+sub query {
+    my $self = shift;
+    my $old = $self->_elem('query', map { uri_escape($_, $URI::URL::reserved_no_form) } @_);
+    if (defined(wantarray) && defined($old)) {
+	if ($old =~ /%(?:26|2[bB]|3[dD])/) {  # contains escaped '=' '&' or '+'
+	    my $mess;
+	    for ($old) {
+		$mess = "Query contains both '+' and '%2B'"
+		  if /\+/ && /%2[bB]/;
+		$mess = "Form query contains escaped '=' or '&'"
+		  if /=/  && /%(?:3[dD]|26)/;
+	    }
+	    if ($mess) {
+		Carp::croak("$mess (you must call equery)");
+	    }
+	}
+	# Now it should be safe to unescape the string without loosing
+	# information
+	return uri_unescape($old);
+    }
+    undef;
+
+}
+
+# No efrag method because the fragment is always stored unescaped
+sub frag     { shift->_elem('frag', @_); }
+
+sub crack
+{
+    my $self = shift;
+    return $self unless wantarray;
+    my @c = @{$self}{qw(scheme user password host port path params query frag)};
+    if (!$c[0]) {
+	# try to determine scheme
+	my $base = $self->base;
+	$c[0] = $base->scheme if $base;
+	$c[0] ||= 'http';  # last resort, default in URI::URL::new
+    }
+    $c[4] ||= $self->default_port;
+    @c;
+}
+
+# Generic-RL: Resolving Relative URL into an Absolute URL
+#
+# Based on RFC1808 section 4
+#
+sub abs
+{
+    my($self, $base, $allow_relative_scheme) = @_;
+    $allow_relative_scheme = $URI::URL::ABS_ALLOW_RELATIVE_SCHEME if @_ < 3;
+    my $embed = $self->clone;
+
+    $base = $self->base unless $base;      # default to default base
+    return $embed unless $base;            # we have no base (step1)
+
+    $base = new URI::URL $base unless ref $base; # make obj if needed
+
+    my($scheme, $host, $path, $params, $query, $frag) =
+	@{$embed}{qw(scheme host path params query frag)};
+
+    # just use base if we are empty             (2a)
+    return $base->clone
+      unless grep(defined($_) && $_ ne '',
+		  $scheme,$host,$port,$path,$params,$query,$frag);
+
+    # if we have a scheme we must already be absolute   (2b),
+    #
+    # but sec. 5.2 also says: Some older parsers allow the scheme name
+    # to be present in a relative URL if it is the same as the base
+    # URL scheme.  This is considered to be a loophole in prior
+    # specifications of the partial URLs and should be avoided by
+    # future parsers.
+    #
+    # The old behavoir can be enabled by passing a TRUE value to the
+    # $allow_relative_scheme parameter.
+    return $embed if $scheme &&
+      (!$allow_relative_scheme || $scheme ne $base->{'scheme'});
+
+    $embed->{'_str'} = '';                      # void cached string
+    $embed->{'scheme'} = $base->{'scheme'};     # (2c)
+
+    return $embed if $embed->{'netloc'};        # (3)
+    $embed->netloc($base->{'netloc'});          # (3)
+
+    return $embed if $path =~ m:^/:;            # (4)
+
+    if ($path eq '') {                          # (5)
+	$embed->{'path'} = $base->{'path'};     # (5)
+
+	return $embed if defined $embed->{'params'}; # (5a)
+	$embed->{'params'} = $base->{'params'};      # (5a)
+
+	return $embed if defined $embed->{'query'};  # (5b)
+	$embed->{'query'} = $base->{'query'};        # (5b)
+
+	return $embed;
+    }
+
+    # (Step 6)  # draft 6 suggests stack based approach
+
+    my $basepath = $base->{'path'};
+    my $relpath  = $embed->{'path'};
+
+    $basepath =~ s!^/!!;
+    $basepath =~ s!/$!/.!;                # prevent empty segment
+    my @path = split('/', $basepath);     # base path into segments
+    pop(@path);                           # remove last segment
+
+    $relpath =~ s!/$!/.!;                 # prevent empty segment
+
+    push(@path, split('/', $relpath));    # append relative segments
+
+    my @newpath = ();
+    my $isdir = 0;
+    my $segment;
+
+    foreach $segment (@path) {            # left to right
+	if ($segment eq '.') {            # ignore "same" directory
+	    $isdir = 1;
+	}
+	elsif ($segment eq '..') {
+	    $isdir = 1;
+	    my $last = pop(@newpath);
+	    if (!defined $last) {         # nothing to pop
+		push(@newpath, $segment); # so must append
+	    }
+	    elsif ($last eq '..') {       # '..' cannot match '..'
+		# so put back again, and append
+		push(@newpath, $last, $segment);
+	    }
+	    #else
+		# it was a component,
+		# keep popped
+	} else {
+	    $isdir = 0;
+	    push(@newpath, $segment);
+	}
+    }
+
+    if ($URI::URL::ABS_REMOTE_LEADING_DOTS) {
+	shift @newpath while @newpath && $newpath[0] =~ /^\.\.?$/;
+    }
+
+    $embed->{'path'} = '/' . join('/', @newpath) .
+	($isdir && @newpath ? '/' : '');
+
+    $embed;
+}
+
+# The oposite of $url->abs.  Return a URL as much relative as possible
+sub rel {
+    my($self, $base) = @_;
+    my $rel = $self->clone;
+    $base = $self->base unless $base;
+    return $rel unless $base;
+    $base = new URI::URL $base unless ref $base;
+    $rel->base($base);
+
+    my($scheme, $netloc, $path) = @{$rel}{qw(scheme netloc path)};
+    if (!defined($scheme) && !defined($netloc)) {
+	# it is already relative
+	return $rel;
+    }
+
+    my($bscheme, $bnetloc, $bpath) = @{$base}{qw(scheme netloc path)};
+    for ($bscheme, $bnetloc, $netloc) { $_ = '' unless defined }
+
+    unless ($scheme eq $bscheme && $netloc eq $bnetloc) {
+	# different location, can't make it relative
+	return $rel;
+    }
+
+    for ($path, $bpath) {  $_ = "/$_" unless m,^/,; }
+
+    # Make it relative by eliminating scheme and netloc
+    $rel->{'scheme'} = undef;
+    $rel->netloc(undef);
+
+    # This loop is based on code from Nicolai Langfeldt <janl@ifi.uio.no>.
+    # First we calculate common initial path components length ($li).
+    my $li = 1;
+    while (1) {
+	my $i = index($path, '/', $li);
+	last if $i < 0 ||
+                $i != index($bpath, '/', $li) ||
+	        substr($path,$li,$i-$li) ne substr($bpath,$li,$i-$li);
+	$li=$i+1;
+    }
+    # then we nuke it from both paths
+    substr($path, 0,$li) = '';
+    substr($bpath,0,$li) = '';
+
+    if ($path eq $bpath && defined($rel->frag) && !defined($rel->equery)) {
+        $rel->epath('');
+    } else {
+        # Add one "../" for each path component left in the base path
+        $path = ('../' x $bpath =~ tr|/|/|) . $path;
+	$path = "./" if $path eq "";
+        $rel->epath($path);
+    }
+
+    $rel;
+}
+
+
+# Compare two URLs
+sub eq {
+    my($self, $other) = @_;
+    local($^W) = 0; # avoid warnings if we compare undef values
+    $other = URI::URL->new($other, $self) unless ref $other;
+
+    # Compare scheme and netloc
+    return 0 if ref($self) ne ref($other);                # must be same class
+    return 0 if $self->scheme ne $other->scheme;          # Always lower case
+    return 0 if lc($self->netloc) ne lc($other->netloc);  # Case-insensitive
+
+    # Compare full_path:
+    # According to <draft-ietf-http-v11-spec-05>:
+    # Characters other than those in the "reserved" and "unsafe" sets
+    # are equivalent to their %XX encodings.
+    my $fp1 = $self->full_path;
+    my $fp2 = $other->full_path;
+    for ($fp1, $fp2) {
+	s,%([\dA-Fa-f]{2}),
+	  my $x = $1;
+	  my $c = chr(hex($x));
+	  $c =~ /^[;\/?:\@&=+\"\#%<>\0-\040\177]/ ? "%\L$x" : $c;
+	,eg;
+    }
+    return 0 if $fp1 ne $fp2;
+    return 0 if $self->frag ne $other->frag;
+    1;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_login.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_login.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/_login.pm	(revision 16561)
@@ -0,0 +1,25 @@
+package URI::URL::_login;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+
+# Generic terminal logins.  This is used as a base class for 'telnet',
+# 'tn3270', and 'rlogin' URL schemes.
+
+
+sub _parse {
+    my($self, $init) = @_;
+    # All we want from _generic is the 'netloc' handling.
+    $self->URI::URL::_generic::_parse($init, 'netloc');
+}
+
+
+*path      = \&URI::URL::bad_method;
+*epath     = \&URI::URL::bad_method;
+*query     = \&URI::URL::bad_method;
+*equery    = \&URI::URL::bad_method;
+*params    = \&URI::URL::bad_method;
+*eparams   = \&URI::URL::bad_method;
+*frag      = \&URI::URL::bad_method;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/data.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/data.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/data.pm	(revision 16561)
@@ -0,0 +1,160 @@
+package URI::URL::data;
+
+# Implements data:-URLs as specified in draft-masinter-url-data-02.txt
+#
+# Abstract
+#
+# A new URL scheme, "data", is defined. It allows inclusion of small
+# data items as "immediate" data, as if it had been included externally.
+#
+# Description
+#
+# Some applications that use URLs also have a need to embed (small)
+# media type data directly inline. This document defines a new URL
+# scheme that would work like 'immediate addressing'. The URLs are of
+# the form:
+#
+#        data:[<mediatype>][;base64],<data>
+#
+
+require URI::URL;
+@ISA = qw(URI::URL);
+
+use URI::Escape;
+use MIME::Base64 ();
+
+# as defined in rfc1522.txt
+my $tspecial  = qq(()<>@,;:/[]?.=);
+my $tokenchar = qq([^\\s\000-\037\177-\377\Q$tspecial\E]);
+
+
+sub new {
+    my($class, $init, $base) = @_;
+
+    my $self = bless { }, $class;
+
+    $self->{'scheme'} = lc($1) if $init =~ s/^\s*([\w\+\.\-]+)://;
+    $self->{'frag'} = $1 if $init =~ s/\#(.*)$//;
+
+    my $type = "";
+    if ($init =~ s/^($tokenchar+\/$tokenchar+)//o) {
+	$type = $1;
+    }
+    while ($init =~ s/^;($tokenchar+)=([^,;]*)//o) {
+	# XXX should we unescape the parst
+	$type .= ";$1=$2";
+    }
+
+    $self->{'media_type'} = $type;
+
+    my $base64;
+    if ($init =~ s/^;base64//i) {
+	$base64 = 1;
+    }
+    
+    $init =~ s/^,//;
+    if ($base64) {
+	$self->{'base64'} = uri_unescape($init);
+    } else {
+	$self->{'data'} = uri_unescape($init);
+    }
+    $self->base($base) if $base;
+    $self;
+}
+
+sub media_type
+{
+    my $self = shift;
+    my $old = $self->{'media_type'};
+    if (@_) {
+	$self->{'media_type'} = shift || "";
+	delete $self->{'_str'};
+    }
+    my($type, $param) = split(/;/, $old, 2);
+    if ($type) {
+	$type = lc $type;
+    } else {
+	$type = "text/plain";
+	$param = "charset=US-ASCII" unless $param;
+    }
+    if (wantarray) {
+	return ($type, $param);
+    } else {
+	return $type;
+    }
+}
+
+sub data
+{
+    my $self = shift;
+    my $old_data;
+    my $old_base64;
+    $old_data = $self->{'data'};
+    $old_base64 = $self->{'base64'};
+    if (@_) {
+	if ($_[1]) { # base64 flag
+	    $self->{'base64'} = $_[0];
+	    delete $self->{'data'};
+	} else {
+	    $self->{'data'} = $_[0];
+	    delete $self->{'base64'};
+	}
+	delete $self->{'_str'};
+    }
+    unless (defined $old_data) {
+	$old_data = MIME::Base64::decode($old_base64);
+	$self->{'data'} = $old_data unless @_;
+    }
+    $old_data;
+}
+
+
+sub crack
+{
+    my $self = shift;
+    ($self->{'scheme'}
+     || 'data',          # scheme
+     undef,              # user
+     undef,              # passwd
+     undef,              # host
+     undef,              # port
+     $self->data,        # path
+     $self->{'media_type'},  # params
+     undef,              # query
+     $self->{'frag'}     # fragment
+    )
+}
+
+sub as_string {
+    my $self = shift;
+    return $self->{'_str'} if $self->{'_str'};
+    my $str = ($self->{'scheme'} || 'data') . ":";
+    $str .= $self->{'media_type'};
+    if (defined $self->{'base64'}) {
+	$str .= ";base64,$self->{'base64'}";
+    } else {
+	my $urlenc = uri_escape($self->{'data'});
+	my $base64 = MIME::Base64::encode($self->{'data'});
+	if (length($base64) + 7 < length($urlenc)) {
+	    $str .= ";base64,$base64";
+	    $self->{'base64'} = $base64;
+	} else {
+	    $str .= ",$urlenc";
+	}
+    }
+    $self->{'_str'} = $str;
+}
+
+sub eq {
+    my($self, $other) = @_;
+    return 0 if ref($self) ne ref($other);
+    return 0 if $self->scheme ne $other->scheme;
+
+    my $mt1 = join(";", $self->media_type);
+    my $mt2 = join(";", $other->media_type);
+    return 0 if $mt1 ne $mt2;
+
+    $self->data eq $other->data;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/file.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/file.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/file.pm	(revision 16561)
@@ -0,0 +1,165 @@
+package URI::URL::file;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+require Carp;
+require Config;
+
+# First we try to determine what kind of system we run on
+my $os = $Config::Config{'osname'};
+OS: {
+    $ostype = 'vms', last if $os eq 'VMS';
+    $ostype = 'dos', last if $os =~ /^(?:os2|mswin32|msdos)$/i;
+    $ostype = 'mac', last if $os eq "Mac";
+    $ostype = 'unix';  # The default
+}
+# NOTE: If you add more types to this list, remember to add a xxx_path
+# method as well.
+
+# This is the BNF found in RFC 1738:
+#
+# fileurl        = "file://" [ host | "localhost" ] "/" fpath
+# fpath          = fsegment *[ "/" fsegment ]
+# fsegment       = *[ uchar | "?" | ":" | "@" | "&" | "=" ]
+# Note that fsegment can contain '?' (query) but not ';' (param)
+
+sub _parse {
+    my($self, $init) = @_;
+    # The file URL can't have query
+    $self->URI::URL::_generic::_parse($init, qw(netloc path params frag));
+}
+
+# sub local_path { ... }
+#
+# Returns a path suitable for use on the local system (we just
+# set up an alias (derived from $ostype) to one of the path methods
+# defined below)
+*local_path = \&{$ostype . "_path"};
+
+*query  = \&URI::URL::bad_method;
+*equery = \&URI::URL::bad_method;
+
+# A U T O  L O A D E R
+# Don't remove this comment, it keeps AutoSplit happy!!
+# @ISA = qw(AutoLoader)
+#
+# These methods are autoloaded as needed
+sub newlocal;
+sub unix_path;
+sub dos_path ;
+sub mac_path ;
+sub vms_path ;
+1;
+__END__
+
+sub newlocal {
+    my($class, $path) = @_;
+
+    Carp::croak("Only implemented for Unix and OS/2 file systems")
+      unless $ostype eq "unix" or $^O =~ /os2|mswin32/i;
+    # XXX: Should implement the same thing for other systems
+
+    my $url = new URI::URL "file:";
+    unless (defined $path and
+    	    ($path =~ m:^/: or 
+	     ($^O eq 'os2' and Cwd::sys_is_absolute($path)) or
+	     ($^O eq 'MSWin32' and $path =~ m<^[A-Za-z]:[\\/]|^[\\/]{2}>))) {
+	require Cwd;
+	my $cwd = Cwd::fastcwd();
+	$cwd =~ s:/?$:/:; # force trailing slash on dir
+	$path = (defined $path) ? $cwd . $path : $cwd;
+    }
+    $url->path($path);
+    $url;
+}
+
+sub unix_path
+{
+    my $self = shift;
+    my @p;
+    for ($self->path_components) {
+	Carp::croak("Path component contains '/' or '\0'") if m|[\0/]|;
+	if (@p) {
+	    next unless length $_;   # skip empty path segments
+	    next if $_ eq '.';       # skip these too
+	    if ($_ eq '..' && $p[-1] ne '..') {  # go up one level
+		pop(@p) if $p[-1] ne '';
+		next;
+	    }
+	}
+	push(@p, $_);
+    }
+    shift(@p) if @p > 1 && $p[0] eq '.';   # './' rendundant if there is more
+    return '/' if !@p || (@p == 1 && $p[0] eq '');
+    join('/', @p);
+}
+
+sub dos_path
+{
+    my $self = shift;
+    my @p;
+    for ($self->path_components) {
+	Carp::croak("Path component contains '/' or '\\'") if m|[/\\]|;
+	push(@p, uc $_);
+    }
+    my $p = join("\\", @p);
+    $p =~ s/^\\([A-Z]:)/$1/;  # Fix drive letter specification
+    $p;
+}
+
+sub mac_path
+{
+    my $self = shift;
+    my @p;
+    for ($self->path_components) {
+	Carp::croak("Path component contains ':'") if /:/;
+	# XXX: Should probably want to do something about ".." and "."
+	# path segments.  I don't know how these are represented in
+	# the Machintosh file system.  If these are valid file names
+	# then we should split the path ourself, as $u->path_components
+	# loose the distinction between '.' and '%2E'.
+	push(@p, $_);
+    }
+    if (@p && $p[0] eq '') {
+	shift @p;
+    } else {
+	unshift(@p, '');
+    }
+    join(':', @p);
+}
+
+sub vms_path
+{
+    # ????? Can some VMS people please redo this function ??????
+
+    # This is implemented based on what RFC1738 (sec 3.10) says in the
+    # VMS file example:
+    #
+    #  DISK$USER:[MY.NOTES]NOTE123456.TXT
+    #
+    #      that might become
+    #
+    #  file:/disk$user/my/notes/note12345.txt
+    #
+    # BEWARE: I don't have a VMS machine myself so this is pure guesswork!!!
+
+    my $self = shift;
+    my @p = $self->path_components;
+    my $abs = 0;
+    if (@p && $p[0] eq '') {
+	shift @p;
+	$abs = 1;
+    }
+    # First I assume there must be a dollar in a disk spesification
+    my $p = '';
+    $p = uc(shift(@p)) . ":"  if @p && $p[0] =~ /\$/;
+    my $file = pop(@p);
+    $p .= "[" . join(".", map{uc($_)} @p) . "]" if @p;
+    $p .= uc $file;
+    # XXX: How is an absolute path different from a relative one??
+    $p =~ s/\[/[./ unless $abs;
+    # XXX: How is a directory denoted??
+    $p;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/finger.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/finger.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/finger.pm	(revision 16561)
@@ -0,0 +1,6 @@
+package URI::URL::finger;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+sub default_port { 79 }
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/ftp.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/ftp.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/ftp.pm	(revision 16561)
@@ -0,0 +1,76 @@
+package URI::URL::ftp;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+sub default_port { 21 }
+
+sub _parse {
+    my($self, $init) = @_;
+    # The ftp URLs can't have any query string
+    $self->URI::URL::_generic::_parse($init, qw(netloc path params frag));
+    1;
+}
+
+
+sub user
+{
+    my($self, @val) = @_;
+    my $old = $self->SUPER::user(@val);
+    defined $old ? $old : "anonymous";
+}
+
+BEGIN {
+    $whoami = undef;
+    $fqdn   = undef;
+}
+
+sub password
+{
+    my($self, @val) = @_;
+    my $old = $self->SUPER::password(@val);
+    unless (defined $old) {
+	my $user = $self->user;
+	if ($user eq 'anonymous' || $user eq 'ftp') {
+	    # anonymous ftp login password
+	    unless (defined $fqdn) {
+		eval {
+		    require Net::Domain;
+		    $fqdn = Net::Domain::hostfqdn();
+		};
+		if ($@) {
+		    $fqdn = '';
+		}
+	    }
+	    unless (defined $whoami) {
+		$whoami = $ENV{USER} || $ENV{LOGNAME} || $ENV{USERNAME};
+		unless ($whoami) {
+		    if ($^O eq 'MSWin32') { $whoami = Win32::LoginName() }
+		    else {
+		        $whoami = getlogin || getpwuid($<) || 'unknown';
+		    }
+		}
+	    }
+	    $old = "$whoami\@$fqdn";
+	} else {
+	    $old = "";
+	}
+    }
+    $old;
+}
+
+sub crack
+{
+    my $self = shift;
+    my @c = $self->SUPER::crack;
+    if ($c[3] && !$c[1]) {
+	# hostname defined, but user is undefined
+	$c[1] = $self->user;
+	$c[2] = $self->password;
+    }
+    @c;
+}
+
+*query  = \&URI::URL::bad_method;
+*equery = \&URI::URL::bad_method;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/gopher.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/gopher.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/gopher.pm	(revision 16561)
@@ -0,0 +1,82 @@
+package URI::URL::gopher;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+use URI::Escape qw(uri_unescape);
+
+sub default_port { 70 }
+
+sub _parse {
+    my($self, $init)   = @_;
+    $self->URI::URL::_generic::_parse($init, qw(netloc path));
+    $self->_parse_gopherpath;
+}
+
+sub path {
+    my $self = shift;
+    my $old = $self->URI::URL::_generic::path(@_);
+    return $old unless @_;
+    $self->_parse_gopherpath;
+    $old;
+}
+
+sub epath {
+    my $self = shift;
+    my $old = $self->URI::URL::_generic::epath(@_);
+    return $old unless @_;
+    $self->_parse_gopherpath;
+    $old;
+}
+
+sub _parse_gopherpath {
+    my $self = shift;
+    my $p = $self->{'path'};
+    # not according to RFC1738, but many popular browsers accept
+    # gopher URLs with a '?' before the search string.
+    $p =~ s/\?/\t/;
+    $p = uri_unescape($p);
+
+    if (defined($p) && $p ne '/' && $p =~ s!^/?(.)!!) {
+	$self->{'gtype'} = $1;
+    } else {
+	$self->{'gtype'} = "1";
+	$p = "";
+    }
+
+    delete $self->{'selector'};
+    delete $self->{'search'};
+    delete $self->{'string'};
+
+    my @parts = split(/\t/, $p, 3);
+    $self->{'selector'} = shift @parts if @parts;
+    $self->{'search'}   = shift @parts if @parts;
+    $self->{'string'}   = shift @parts if @parts;
+}
+
+
+sub gtype    { shift->_path_elem('gtype',    @_); }
+sub selector { shift->_path_elem('selector', @_); }
+sub search   { shift->_path_elem('search',   @_); }
+sub string   { shift->_path_elem('string',   @_); }
+
+sub _path_elem {
+    my($self, $elem, @val) = @_;
+    my $old = $self->_elem($elem, @val);
+    return $old unless @val;
+
+    # construct new path based on elements
+    my $path = "/$self->{'gtype'}";
+    $path .= "$self->{'selector'}" if defined $self->{'selector'};
+    $path .= "\t$self->{'search'}" if defined $self->{'search'};
+    $path .= "\t$self->{'string'}" if defined $self->{'string'};
+    $self->{'path'} = $path;
+
+    $old;
+}
+
+*params  = \&URI::URL::bad_method;
+*qparams = \&URI::URL::bad_method;
+*query   = \&URI::URL::bad_method;
+*equery  = \&URI::URL::bad_method;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/http.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/http.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/http.pm	(revision 16561)
@@ -0,0 +1,57 @@
+package URI::URL::http;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+sub default_port { 80 }
+
+require Carp;
+
+# @ISA = qw(AutoLoader)      # This comment is needed by AutoSplit.
+sub keywords;
+sub query_form;
+1;
+__END__
+
+# Handle ...?dog+bones type of query
+sub keywords {
+    my $self = shift;
+    $old = $self->{'query'};
+    if (@_) {
+	# Try to set query string
+	$self->equery(join('+', map { URI::Escape::uri_escape($_, $URI::URL::reserved) } @_));
+    }
+    return if !defined($old) || !defined(wantarray);
+
+    Carp::croak("Query is not keywords") if $old =~ /=/;
+    map { URI::Escape::uri_unescape($_) } split(/\+/, $old);
+}
+
+# Handle ...?foo=bar&bar=foo type of query
+sub query_form {
+    my $self = shift;
+    $old = $self->{'query'};
+    if (@_) {
+	# Try to set query string
+	my @query;
+	my($key,$vals);
+        my $esc = $URI::URL::reserved . $URI::URL::unsafe;
+	while (($key,$vals) = splice(@_, 0, 2)) {
+	    $key = '' unless defined $key;
+	    $key =  URI::Escape::uri_escape($key, $esc);
+	    $vals = [$vals] unless ref($vals) eq 'ARRAY';
+	    my $val;
+	    for $val (@$vals) {
+		$val = '' unless defined $val;
+		$val = URI::Escape::uri_escape($val, $esc);
+		push(@query, "$key=$val");
+	    }
+	}
+	$self->equery(join('&', @query));
+    }
+    return if !defined($old) || length($old) == 0 || !defined(wantarray);
+    Carp::croak("Query is not a form") unless $old =~ /=/;
+    map { s/\+/ /g; URI::Escape::uri_unescape($_) }
+	 map { /=/ ? split(/=/, $_, 2) : ($_ => '')} split(/&/, $old);
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/https.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/https.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/https.pm	(revision 16561)
@@ -0,0 +1,6 @@
+package URI::URL::https;
+require URI::URL::http;
+@ISA = qw(URI::URL::http);
+
+sub default_port { 443 }
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/mailto.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/mailto.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/mailto.pm	(revision 16561)
@@ -0,0 +1,78 @@
+package URI::URL::mailto;
+require URI::URL;
+@ISA = qw(URI::URL);
+
+use URI::Escape;
+
+sub new {
+    my($class, $init, $base) = @_;
+
+    my $self = bless { }, $class;
+    $self->{'scheme'} = lc($1) if $init =~ s/^\s*([\w\+\.\-]+)://;
+    $self->{'address'} = uri_unescape($init);
+    $self->base($base) if $base;
+    $self;
+}
+
+sub address { shift->_elem('address', @_); }
+
+# can use these as aliases
+*encoded822addr = \&address;   # URI::URL v3 compatibility
+*netloc         = \&address;
+
+sub user {
+    my $self = shift;
+    $old = $self->{'address'};
+    if (@_) {
+	my $new = $old;
+	$new =~ s/.*\@?/$_[0]\@/;
+	$self->{'address'} = $new;
+    }
+    $old =~ s/\@.*//;
+    $old;
+}
+
+sub host {
+    my $self = shift;
+    $old = $self->{'address'};
+    if (@_) {
+	my $new = $old;
+	$new =~ s/\@.*/\@$_[0]/;
+	$self->{'address'} = $new;
+    }
+    $old =~ s/.*\@//;
+    $old;
+}
+
+sub crack
+{
+    my $self = shift;
+    ('mailto',           # scheme
+     $self->user,        # user
+     undef,              # passwd
+     $self->host,        # host
+     undef,              # port
+     $self->{'address'}, # path
+     undef,              # params
+     undef,              # query
+     undef               # fragment
+    )
+}
+
+sub as_string {
+    my $self = shift;
+    my $str = ($self->{'scheme'} || "mailto") . ":";
+    $str .= uri_escape($self->{'address'}) if defined $self->{'address'};
+    $str;
+}
+
+sub eq {
+    my($self, $other) = @_;
+    $other = URI::URL->new($other) unless ref $other;
+
+    # Mail adresses are case-insensitive
+    $self->scheme eq $other->scheme &&
+      lc($self->{'address'}) eq lc($other->{'address'});
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/news.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/news.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/news.pm	(revision 16561)
@@ -0,0 +1,73 @@
+package URI::URL::news;
+require URI::URL;
+@ISA = qw(URI::URL);
+
+use URI::Escape;
+
+sub new {
+    my($class, $init, $base) = @_;
+    my $self = bless { }, $class;
+    $self->{'scheme'}  = lc($1) if $init =~ s/^\s*([\w\+\.\-]+)://;
+    $self->groupart(uri_unescape($init));
+    $self->base($base) if $base;
+    $self;
+}
+
+sub groupart {
+    my $self = shift;
+    my $old = $self->{'path'};
+    if (@_) {
+	my $p = shift;
+	if (defined $p && $p =~ /\@/) {
+	    # it is an message id
+	    $p =~ s/^<(.*)>$/$1/;  # "<" and ">" should not be part of it
+	}
+	$self->{'path'} = $p;
+    }
+    $old;
+}
+
+*path = \&groupart;
+
+sub article   {
+    my $self = shift;
+    Carp::croak("Illegal article id name (does not contain '\@')")
+      if @_ && $_[0] !~ /\@/;
+    my $old = $self->groupart(@_);
+    return undef if $old !~ /\@/;
+    $old;
+}
+
+
+sub group {
+    my $self = shift;
+    Carp::croak("Illegal group name (contains '\@')")
+      if @_ && $_[0] =~ /\@/;
+    my $old = $self->groupart(@_);
+    return undef if $old =~ /\@/;
+    $old;
+}
+
+sub crack
+{
+    my $self = shift;
+    ('news',             # scheme
+     undef,              # user
+     undef,              # passwd
+     undef,              # host
+     undef,              # port
+     $self->{'path'},    # path
+     undef,              # params
+     undef,              # query
+     undef               # fragment
+    )
+}
+
+
+sub as_string {
+    my $self = shift;
+    my $scheme = $self->{'scheme'} || "news";
+    "$scheme:" . uri_escape($self->{'path'});
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/nntp.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/nntp.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/nntp.pm	(revision 16561)
@@ -0,0 +1,44 @@
+package URI::URL::nntp;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+use URI::Escape;
+
+sub default_port { 119 }
+
+sub _parse {
+    my($self, $init) = @_;
+    $self->URI::URL::_generic::_parse($init, qw(netloc path frag));
+
+    my @parts = $self->path_components;
+    shift @parts if @parts && $parts[0] eq '';
+
+    $self->{'group'} = uri_unescape($parts[0]);
+    $self->{'digits'}= uri_unescape($parts[1]);
+}
+
+sub group   { shift->_elem('group',  @_); }
+sub digits  { shift->_elem('digits', @_); }
+sub article { shift->_elem('digits', @_); }
+
+sub as_string {
+    my $self = shift;
+    my $str = "$self->{'scheme'}:";
+    $str .= "//$self->{'netloc'}" if defined $self->{'netloc'};
+    $str .= "/" . uri_escape($self->{'group'}) . "/" .
+		  uri_escape($self->{'digits'});
+    $str;
+}
+
+# Standard methods are not legal for nntp URLs
+*path      = \&URI::URL::bad_method;
+*epath     = \&URI::URL::bad_method;
+*query     = \&URI::URL::bad_method;
+*equery    = \&URI::URL::bad_method;
+*params    = \&URI::URL::bad_method;
+*eparams   = \&URI::URL::bad_method;
+*frag      = \&URI::URL::bad_method;
+*user      = \&URI::URL::bad_method;
+*password  = \&URI::URL::bad_method;
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/prospero.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/prospero.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/prospero.pm	(revision 16561)
@@ -0,0 +1,27 @@
+package URI::URL::prospero;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+# RFC 1738 says:
+#
+#   A prospero URLs takes the form:
+#
+#      prospero://<host>:<port>/<hsoname>;<field>=<value>
+#
+#   where <host> and <port> are as described in Section 3.1. If :<port>
+#   is omitted, the port defaults to 1525. No username or password is
+#   allowed.
+#
+#   The <hsoname> is the host-specific object name in the Prospero
+#   protocol, suitably encoded.  This name is opaque and interpreted by
+#   the Prospero server.  The semicolon ";" is reserved and may not
+#   appear without quoting in the <hsoname>.
+
+sub default_port { 1525 }       # says rfc1738, section 3.11
+
+sub _parse {
+    my($self, $init) = @_;
+    $self->URI::URL::_generic::_parse($init, qw(netloc path params frag));
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/rlogin.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/rlogin.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/rlogin.pm	(revision 16561)
@@ -0,0 +1,7 @@
+package URI::URL::rlogin;
+require URI::URL::_login;
+@ISA = qw(URI::URL::_login);
+
+sub default_port { 513 }
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/telnet.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/telnet.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/telnet.pm	(revision 16561)
@@ -0,0 +1,7 @@
+package URI::URL::telnet;
+require URI::URL::_login;
+@ISA = qw(URI::URL::_login);
+
+sub default_port { 23 }
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/tn3270.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/tn3270.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/tn3270.pm	(revision 16561)
@@ -0,0 +1,7 @@
+package URI::URL::tn3270;
+require URI::URL::_login;
+@ISA = qw(URI::URL::_login);
+
+# sub default_port { 0 }
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/wais.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/wais.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/wais.pm	(revision 16561)
@@ -0,0 +1,50 @@
+package URI::URL::wais;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+use URI::Escape;
+
+# RFC 1738 says:
+#
+#   A WAIS URL takes one of the following forms:
+#
+#     wais://<host>:<port>/<database>
+#     wais://<host>:<port>/<database>?<search>
+#     wais://<host>:<port>/<database>/<wtype>/<wpath>
+#
+#   where <host> and <port> are as described in Section 3.1. If :<port>
+#   is omitted, the port defaults to 210.  The first form designates a
+#   WAIS database that is available for searching. The second form
+#   designates a particular search.  <database> is the name of the WAIS
+#   database being queried.
+
+
+sub default_port { 210 }
+
+sub _parse {
+    my($self, $init) = @_;
+    $self->URI::URL::_generic::_parse($init, qw(netloc path query frag));
+}
+
+# Set the path component with the specified number
+sub _path_comp
+{
+    my $self = shift;
+    my $no   = shift;
+    my @p = $self->path_components;
+    shift(@p) if @p && $p[0] eq '';
+    my $old = $p[$no];
+    if (@_) {
+	$p[$no] = $_[0];
+	$self->path_components(@p);
+    }
+    $old;
+}
+
+sub database { shift->_path_comp(0, @_); }
+sub wtype    { shift->_path_comp(1, @_); }
+sub wpath    { shift->_path_comp(2, @_); }
+
+*params   = \&URI::URL::bad_method;
+*eparams  = \&URI::URL::bad_method;
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/webster.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/webster.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/webster.pm	(revision 16561)
@@ -0,0 +1,6 @@
+package URI::URL::webster;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+sub default_port { 765 }
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/whois.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/whois.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/URI/URL/whois.pm	(revision 16561)
@@ -0,0 +1,6 @@
+package URI::URL::whois;
+require URI::URL::_generic;
+@ISA = qw(URI::URL::_generic);
+
+sub default_port { 43 }
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules.pm	(revision 16561)
@@ -0,0 +1,364 @@
+# $Id$
+
+package WWW::RobotRules;
+
+=head1 NAME
+
+WWW::RobotsRules - Parse robots.txt files
+
+=head1 SYNOPSIS
+
+ require WWW::RobotRules;
+ my $robotsrules = new WWW::RobotRules 'MOMspider/1.0';
+
+ use LWP::Simple qw(get);
+
+ $url = "http://some.place/robots.txt";
+ my $robots_txt = get $url;
+ $robotsrules->parse($url, $robots_txt);
+
+ $url = "http://some.other.place/robots.txt";
+ my $robots_txt = get $url;
+ $robotsrules->parse($url, $robots_txt);
+
+ # Now we are able to check if a URL is valid for those servers that
+ # we have obtained and parsed "robots.txt" files for.
+ if($robotsrules->allowed($url)) {
+     $c = get $url;
+     ...
+ }
+
+=head1 DESCRIPTION
+
+This module parses a F<robots.txt> file as specified in
+"A Standard for Robot Exclusion", described in
+<URL:http://info.webcrawler.com/mak/projects/robots/norobots.html>
+Webmasters can use the F<robots.txt> file to disallow conforming
+robots access to parts of their WWW server.
+
+The parsed file is kept in the WWW::RobotRules object, and this object
+provide methods to check if access to a given URL is prohibited.  The
+same WWW::RobotRules object can parse multiple F<robots.txt> files.
+
+The following methods are provided:
+
+=over 4
+
+=cut
+
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+sub Version { $VERSION; }
+
+
+use URI::URL ();
+use strict;
+
+
+=item $rules = new WWW::RobotRules 'MOMspider/1.0'
+
+This is the constructor for WWW::RobotRules objects.  The first 
+argument given to new() is the name of the robot. 
+
+=cut
+
+sub new {
+    my($class, $ua) = @_;
+
+    # This ugly hack is needed to ensure backwards compatability.
+    # The "WWW::RobotRules" class is now really abstract.
+    $class = "WWW::RobotRules::InCore" if $class eq "WWW::RobotRules";
+
+    my $self = bless { }, $class;
+    $self->agent($ua);
+    $self;
+}
+
+
+=item $rules->parse($url, $content, $fresh_until)
+
+The parse() method takes as arguments the URL that was used to
+retrieve the F</robots.txt> file, and the contents of the file.
+
+=cut
+
+sub parse {
+    my($self, $url, $txt, $fresh_until) = @_;
+
+    $url = new URI::URL $url unless ref($url);	# make it URL
+    my $netloc = $url->netloc;
+
+    $self->clear_rules($netloc);
+    $self->fresh_until($netloc, $fresh_until || (time + 365*24*3600));
+
+    my $ua;
+    my $is_me = 0;		# 1 iff this record is for me
+    my $is_anon = 0;		# 1 iff this record is for *
+    my @me_disallowed = ();	# rules disallowed for me
+    my @anon_disallowed = ();	# rules disallowed for *
+
+    # blank lines are significant, so turn CRLF into LF to avoid generating
+    # false ones
+    $txt =~ s/\015\012/\012/g;
+
+    # split at \012 (LF) or \015 (CR) (Mac text files have just CR for EOL)
+    for(split(/[\012\015]/, $txt)) {
+
+	# Lines containing only a comment are discarded completely, and
+        # therefore do not indicate a record boundary.
+	next if /^\s*\#/;
+
+	s/\s*\#.*//;        # remove comments at end-of-line
+
+	if (/^\s*$/) {	    # blank line
+	    last if $is_me; # That was our record. No need to read the rest.
+	    $is_anon = 0;
+	}
+        elsif (/^User-Agent:\s*(.*)/i) {
+	    $ua = $1;
+	    $ua =~ s/\s+$//;
+	    if ($is_me) {
+		# This record already had a User-agent that
+		# we matched, so just continue.
+	    }
+	    elsif ($ua eq '*') {
+		$is_anon = 1;
+	    }
+	    elsif($self->is_me($ua)) {
+		$is_me = 1;
+	    }
+	}
+	elsif (/^Disallow:\s*(.*)/i) {
+	    unless (defined $ua) {
+		warn "RobotRules: Disallow without preceding User-agent\n";
+		$is_anon = 1;  # assume that User-agent: * was intended
+	    }
+	    my $disallow = $1;
+	    $disallow =~ s/\s+$//;
+	    if (length $disallow) {
+		$disallow = URI::URL->new($disallow, $url)->full_path;
+	    }
+
+	    if ($is_me) {
+		push(@me_disallowed, $disallow);
+	    }
+	    elsif ($is_anon) {
+		push(@anon_disallowed, $disallow);
+	    }
+	}
+	else {
+	    warn "RobotRules: Unexpected line: $_\n";
+	}
+    }
+
+    if ($is_me) {
+	$self->push_rules($netloc, @me_disallowed);
+    } else {
+	$self->push_rules($netloc, @anon_disallowed);
+    }
+}
+
+# is_me()
+#
+# Returns TRUE if the given name matches the
+# name of this robot
+#
+sub is_me {
+    my($self, $ua) = @_;
+    my $me = $self->agent;
+    return index(lc($ua), lc($me)) >= 0;
+}
+
+=item $rules->allowed($url)
+
+Returns TRUE if this robot is allowed to retrieve this URL.
+
+=cut
+
+sub allowed {
+    my($self, $url) = @_;
+    $url = URI::URL->new($url) unless ref $url;	# make it URL
+
+    my $netloc = $url->netloc;
+
+    my $fresh_until = $self->fresh_until($netloc);
+    return -1 if !defined($fresh_until) || $fresh_until < time;
+
+    my $str = $url->full_path;
+    my $rule;
+    for $rule ($self->rules($netloc)) {
+	return 1 unless length $rule;
+	return 0 if index($str, $rule) == 0;
+    }
+    return 1;
+}
+
+# The following methods must be provided by the subclass.
+sub agent;
+sub visit;
+sub no_visits;
+sub last_visits;
+sub fresh_until;
+sub push_rules;
+sub clear_rules;
+sub rules;
+sub dump;
+
+package WWW::RobotRules::InCore;
+
+use vars qw(@ISA);
+@ISA = qw(WWW::RobotRules);
+
+=item $rules->agent([$name])
+
+Get/set the agent name. NOTE: Changing the agent name will clear the robots.txt
+rules and expire times out of the cache.
+
+=cut
+
+sub agent {
+    my ($self, $name) = @_;
+    my $old = $self->{'ua'};
+    if ($name) {
+	delete $self->{'loc'};   # all old info is now stale
+	$name =~ s!/?\s*\d+.\d+\s*$!!;  # loose version
+	$self->{'ua'}=$name;
+    }
+    $old;
+}
+
+sub visit {
+    my($self, $netloc, $time) = @_;
+    $time ||= time;
+    $self->{'loc'}{$netloc}{'last'} = $time;
+    
+    my $count = \$self->{'loc'}{$netloc}{'count'};
+    if (!defined $$count) {
+	$$count = 1;
+    } else {
+	$$count++;
+    }
+}
+
+sub no_visits {
+    my ($self, $netloc) = @_;
+    $self->{'loc'}{$netloc}{'count'};
+}
+
+sub last_visit {
+    my ($self, $netloc) = @_;
+    $self->{'loc'}{$netloc}{'last'};
+}
+
+sub fresh_until {
+    my ($self, $netloc, $fresh_until) = @_;
+    my $old = $self->{'loc'}{$netloc}{'fresh'};
+    if (defined $fresh_until) {
+	$self->{'loc'}{$netloc}{'fresh'} = $fresh_until;
+    }
+    $old;
+}
+
+sub push_rules {
+    my($self, $netloc, @rules) = @_;
+    push (@{$self->{'loc'}{$netloc}{'rules'}}, @rules);
+}
+
+sub clear_rules {
+    my($self, $netloc) = @_;
+    delete $self->{'loc'}{$netloc}{'rules'};
+}
+
+sub rules {
+    my($self, $netloc) = @_;
+    if (defined $self->{'loc'}{$netloc}{'rules'}) {
+	return @{$self->{'loc'}{$netloc}{'rules'}};
+    } else {
+	return ();
+    }
+}
+
+sub dump
+{
+    my $self = shift;
+    for (keys %$self) {
+	next if $_ eq 'loc';
+	print "$_ = $self->{$_}\n";
+    }
+    for (keys %{$self->{'loc'}}) {
+	my @rules = $self->rules($_);
+	print "$_: ", join("; ", @rules), "\n";
+	
+    }
+}
+
+1;
+
+__END__
+
+=back
+
+=head1 ROBOTS.TXT
+
+The format and semantics of the "/robots.txt" file are as follows
+(this is an edited abstract of
+<URL:http://info.webcrawler.com/mak/projects/robots/norobots.html>):
+
+The file consists of one or more records separated by one or more
+blank lines. Each record contains lines of the form
+
+  <field-name>: <value>
+
+The field name is case insensitive.  Text after the '#' character on a
+line is ignored during parsing.  This is used for comments.  The
+following <field-names> can be used:
+
+=over 3
+
+=item User-Agent
+
+The value of this field is the name of the robot the record is
+describing access policy for.  If more than one I<User-Agent> field is
+present the record describes an identical access policy for more than
+one robot. At least one field needs to be present per record.  If the
+value is '*', the record describes the default access policy for any
+robot that has not not matched any of the other records.
+
+=item Disallow
+
+The value of this field specifies a partial URL that is not to be
+visited. This can be a full path, or a partial path; any URL that
+starts with this value will not be retrieved
+
+=back
+
+=head1 ROBOTS.TXT EXAMPLES
+
+The following example "/robots.txt" file specifies that no robots
+should visit any URL starting with "/cyberworld/map/" or "/tmp/":
+
+  User-agent: *
+  Disallow: /cyberworld/map/ # This is an infinite virtual URL space
+  Disallow: /tmp/ # these will soon disappear
+
+This example "/robots.txt" file specifies that no robots should visit
+any URL starting with "/cyberworld/map/", except the robot called
+"cybermapper":
+
+  User-agent: *
+  Disallow: /cyberworld/map/ # This is an infinite virtual URL space
+
+  # Cybermapper knows where to go.
+  User-agent: cybermapper
+  Disallow:
+
+This example indicates that no robots should visit this site further:
+
+  # go away
+  User-agent: *
+  Disallow: /
+
+=head1 SEE ALSO
+
+L<LWP::RobotUA>, L<WWW::RobotRules::AnyDBM_File>
+
+=cut
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules/AnyDBM_File.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules/AnyDBM_File.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lib/WWW/RobotRules/AnyDBM_File.pm	(revision 16561)
@@ -0,0 +1,170 @@
+# $Id$
+
+package WWW::RobotRules::AnyDBM_File;
+
+require  WWW::RobotRules;
+@ISA = qw(WWW::RobotRules);
+$VERSION = sprintf("%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/);
+
+use Carp ();
+use AnyDBM_File;
+use Fcntl;
+use strict;
+
+=head1 NAME
+
+WWW::RobotRules::AnyDBM_File - Persistent RobotRules
+
+=head1 SYNOPSIS
+
+ require WWW::RobotRules::AnyDBM_File;
+ require LWP::RobotUA;
+
+ # Create a robot useragent that uses a diskcaching RobotRules
+ my $rules = new WWW::RobotRules::AnyDBM_File 'my-robot/1.0', 'cachefile';
+ my $ua = new WWW::RobotUA 'my-robot/1.0', 'me@foo.com', $rules;
+
+ # Then just use $ua as usual
+ $res = $ua->request($req);
+
+=head1 DESCRIPTION
+
+This is a subclass of I<WWW::RobotRules> that uses the AnyDBM_File
+package to implement persistent diskcaching of F<robots.txt> and host
+visit information.
+
+The constructor (the new() method) takes an extra argument specifying
+the name of the DBM file to use.  If the DBM file already exists, then
+you can specify undef as agent name as the name can be obtained from
+the DBM database.
+
+=cut
+
+sub new 
+{ 
+  my ($class, $ua, $file) = @_;
+  Carp::croak('WWW::RobotRules::AnyDBM_File filename required') unless $file;
+
+  my $self = bless { }, $class;
+  $self->{'filename'} = $file;
+  tie %{$self->{'dbm'}}, 'AnyDBM_File', $file, O_CREAT|O_RDWR, 0640
+    or Carp::croak("Can't open $file: $!");
+  
+  if ($ua) {
+      $self->agent($ua);
+  } else {
+      # Try to obtain name from DBM file
+      $ua = $self->{'dbm'}{"|ua-name|"};
+      Carp::croak("No agent name specified") unless $ua;
+  }
+
+  $self;
+}
+
+sub agent {
+    my($self, $newname) = @_;
+    my $old = $self->{'dbm'}{"|ua-name|"};
+    if (defined $newname) {
+	$newname =~ s!/?\s*\d+.\d+\s*$!!;  # loose version
+	unless ($old && $old eq $newname) {
+	# Old info is now stale.
+	    my $file = $self->{'filename'};
+	    untie %{$self->{'dbm'}};
+	    tie %{$self->{'dbm'}}, 'AnyDBM_File', $file, O_TRUNC|O_RDWR, 0640;
+	    $self->{'dbm'}{"|ua-name|"} = $newname;
+	}
+    }
+    $old;
+}
+
+sub no_visits {
+    my ($self, $netloc) = @_;
+    my $t = $self->{'dbm'}{"$netloc|vis"};
+    return 0 unless $t;
+    (split(/;\s*/, $t))[0];
+}
+
+sub last_visit {
+    my ($self, $netloc) = @_;
+    my $t = $self->{'dbm'}{"$netloc|vis"};
+    return undef unless $t;
+    (split(/;\s*/, $t))[1];
+}
+
+sub fresh_until {
+    my ($self, $netloc, $fresh) = @_;
+    my $old = $self->{'dbm'}{"$netloc|exp"};
+    if ($old) {
+	$old =~ s/;.*//;  # remove cleartext
+    }
+    if (defined $fresh) {
+	$fresh .= "; " . localtime($fresh);
+	$self->{'dbm'}{"$netloc|exp"} = $fresh;
+    }
+    $old;
+}
+
+sub visit {
+    my($self, $netloc, $time) = @_;
+    $time ||= time;
+
+    my $count = 0;
+    my $old = $self->{'dbm'}{"$netloc|vis"};
+    if ($old) {
+	my $last;
+	($count,$last) = split(/;\s*/, $old);
+	$time = $last if $last > $time;
+    }
+    $count++;
+    $self->{'dbm'}{"$netloc|vis"} = "$count; $time; " . localtime($time);
+}
+
+sub push_rules {
+    my($self, $netloc, @rules) = @_;
+    my $cnt = 1;
+    $cnt++ while $self->{'dbm'}{"$netloc|r$cnt"};
+
+    foreach (@rules) {
+	$self->{'dbm'}{"$netloc|r$cnt"} = $_;
+	$cnt++;
+    }
+}
+
+sub clear_rules {
+    my($self, $netloc) = @_;
+    my $cnt = 1;
+    while ($self->{'dbm'}{"$netloc|r$cnt"}) {
+	delete $self->{'dbm'}{"$netloc|r$cnt"};
+	$cnt++;
+    }
+}
+
+sub rules {
+    my($self, $netloc) = @_;
+    my @rules = ();
+    my $cnt = 1;
+    while (1) {
+	my $rule = $self->{'dbm'}{"$netloc|r$cnt"};
+	last unless $rule;
+	push(@rules, $rule);
+	$cnt++;
+    }
+    @rules;
+}
+
+sub dump
+{
+}
+
+1;
+
+=head1 SEE ALSO
+
+L<WWW::RobotRules>, L<LWP::RobotUA>
+
+=head1 AUTHORS
+
+Hakan Ardo E<lt>hakan@munin.ub2.lu.se>, Gisle Aas E<lt>aas@sn.no>
+
+=cut
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lwpcook.pod
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lwpcook.pod	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/lwpcook.pod	(revision 16561)
@@ -0,0 +1,343 @@
+=head1 NAME
+
+lwpcook - libwww-perl cookbook
+
+=head1 DESCRIPTION
+
+This document contain some examples that show typical usage of the
+libwww-perl library.  You should consult the documentation for the
+individual modules for more detail.
+
+All examples should be runnable programs. You can, in most cases, test
+the code sections by piping the program text directly to perl.
+
+
+
+=head1 GET
+
+It is very easy to use this library to just fetch documents from the
+net.  The LWP::Simple module provides the get() function that return
+the document specified by its URL argument:
+
+  use LWP::Simple;
+  $doc = get 'http://www.sn.no/libwww-perl/';
+
+or, as a perl one-liner using the getprint() function:
+
+  perl -MLWP::Simple -e 'getprint "http://www.sn.no/libwww-perl/"'
+
+or, how about fetching the latest perl by running this command:
+
+  perl -MLWP::Simple -e '
+    getstore "ftp://ftp.sunet.se/pub/lang/perl/CPAN/src/latest.tar.gz",
+             "perl.tar.gz"'
+
+You will probably first want to find a CPAN site closer to you by
+running something like the following command:
+
+  perl -MLWP::Simple -e 'getprint "http://www.perl.com/perl/CPAN/CPAN.html"'
+
+Enough of this simple stuff!  The LWP object oriented interface gives
+you more control over the request sent to the server.  Using this
+interface you have full control over headers sent and how you want to
+handle the response returned.
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+  $ua->agent("$0/0.1 " . $ua->agent);
+  # $ua->agent("Mozilla/8.0") # pretend we are very capable browser
+
+  $req = new HTTP::Request 'GET' => 'http://www.sn.no/libwww-perl';
+  $req->header('Accept' => 'text/html');
+
+  # send request
+  $res = $ua->request($req);
+
+  # check the outcome
+  if ($res->is_success) {
+     print $res->content;
+  } else {
+     print "Error: " . $res->status_line . "\n";
+  }
+
+The lwp-request program (alias GET) that is distributed with the
+library can also be used to fetch documents from WWW servers.
+  
+
+
+=head1 HEAD
+
+If you just want to check if a document is present (i.e. the URL is
+valid) try to run code that looks like this:
+
+  use LWP::Simple;
+
+  if (head($url)) {
+     # ok document exists
+  }
+
+The head() function really returns a list of meta-information about
+the document.  The first three values of the list returned are the
+document type, the size of the document, and the age of the document.
+
+More control over the request or access to all header values returned
+require that you use the object oriented interface described for GET
+above.  Just s/GET/HEAD/g.
+
+
+=head1 POST
+
+There is no simple procedural interface for posting data to a WWW server.  You
+must use the object oriented interface for this. The most common POST
+operation is to access a WWW form application:
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+
+  my $req = new HTTP::Request 'POST','http://www.perl.com/cgi-bin/BugGlimpse';
+  $req->content_type('application/x-www-form-urlencoded');
+  $req->content('match=www&errors=0');
+
+  my $res = $ua->request($req);
+  print $res->as_string;
+
+Lazy people use the HTTP::Request::Common module to set up a suitable
+POST request message (it handles all the escaping issues) and has a
+suitable default for the content_type:
+
+  use HTTP::Request::Common qw(POST);
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+
+  my $req = POST 'http://www.perl.com/cgi-bin/BugGlimpse',
+                [ search => 'www', errors => 0 ];
+
+  print $ua->request($req)->as_string;
+
+The lwp-request program (alias POST) that is distributed with the
+library can also be used for posting data.
+
+
+
+=head1 PROXIES
+
+Some sites use proxies to go through fire wall machines, or just as
+cache in order to improve performance.  Proxies can also be used for
+accessing resources through protocols not supported directly (or
+supported badly :-) by the libwww-perl library.
+
+You should initialize your proxy setting before you start sending
+requests:
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+  $ua->env_proxy; # initialize from environment variables
+  # or
+  $ua->proxy(ftp  => 'http://proxy.myorg.com');
+  $ua->proxy(wais => 'http://proxy.myorg.com');
+  $ua->no_proxy(qw(no se fi));
+
+  my $req = new HTTP::Request 'wais://xxx.com/';
+  print $ua->request($req)->as_string;
+
+The LWP::Simple interface will call env_proxy() for you automatically.
+Applications that use the $ua->env_proxy() method will normally not
+use the $ua->proxy() and $ua->no_proxy() methods.
+
+Some proxies also require that you send it a username/password in
+order to let requests through.  You should be able to add the
+required header, with something like this:
+
+ use LWP::UserAgent;
+
+ $ua = new LWP::UserAgent;
+ $ua->proxy(['http', 'ftp'] => 'http://proxy.myorg.com');
+
+ $req = new HTTP::Request 'GET',"http://www.perl.com";
+ $req->proxy_authorization_basic("proxy_user", "proxy_password");
+
+ $res = $ua->request($req);
+ print $res->content if $res->is_success;
+
+Replace C<proxy.myorg.com>, C<proxy_user> and
+C<proxy_password> with something suitable for your site.
+
+
+=head1 ACCESS TO PROTECTED DOCUMENTS
+
+Documents protected by basic authorization can easily be accessed
+like this:
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+  $req = new HTTP::Request GET => 'http://www.sn.no/secret/';
+  $req->authorization_basic('aas', 'mypassword');
+  print $ua->request($req)->as_string;
+
+The other alternative is to provide a subclass of I<LWP::UserAgent> that
+overrides the get_basic_credentials() method. Study the I<lwp-request>
+program for an example of this.
+
+
+=head1 MIRRORING
+
+If you want to mirror documents from a WWW server, then try to run
+code similar to this at regular intervals:
+
+  use LWP::Simple;
+
+  %mirrors = (
+     'http://www.sn.no/'             => 'sn.html',
+     'http://www.perl.com/'          => 'perl.html',
+     'http://www.sn.no/libwww-perl/' => 'lwp.html',
+     'gopher://gopher.sn.no/'        => 'gopher.html',
+  );
+
+  while (($url, $localfile) = each(%mirrors)) {
+     mirror($url, $localfile);
+  }
+
+Or, as a perl one-liner:
+
+  perl -MLWP::Simple -e 'mirror("http://www.perl.com/", "perl.html")';
+
+The document will not be transfered unless it has been updated.
+
+
+
+=head1 LARGE DOCUMENTS
+
+If the document you want to fetch is too large to be kept in memory,
+then you have two alternatives.  You can instruct the library to write
+the document content to a file (second $ua->request() argument is a file
+name):
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+
+  my $req = new HTTP::Request 'GET',
+                'http://www.sn.no/~aas/perl/www/libwww-perl-5.00.tar.gz';
+  $res = $ua->request($req, "libwww-perl.tar.gz");
+  if ($res->is_success) {
+     print "ok\n";
+  }
+
+Or you can process the document as it arrives (second $ua->request()
+argument is a code reference):
+
+  use LWP::UserAgent;
+  $ua = new LWP::UserAgent;
+  $URL = 'ftp://ftp.unit.no/pub/rfc/rfc-index.txt';
+
+  my $expected_length;
+  my $bytes_received = 0;
+  $ua->request(HTTP::Request->new('GET', $URL),
+               sub {
+                   my($chunk, $res) = @_;
+                   $bytes_received += length($chunk);
+	           unless (defined $expected_length) {
+	              $expected_length = $res->content_length || 0;
+                   }
+		   if ($expected_length) {
+		        printf STDERR "%d%% - ",
+	                          100 * $bytes_received / $expected_length;
+                   }
+	           print STDERR "$bytes_received bytes received\n";
+
+                   # XXX Should really do something with the chunk itself
+	           # print $chunk;
+               });
+
+
+
+=head1 HTML FORMATTING
+
+It is easy to convert HTML code to "readable" text.
+
+  use LWP::Simple;
+  use HTML::Parse;
+  print parse_html(get 'http://www.sn.no/libwww-perl/')->format;
+
+
+
+=head1 PARSE URLS
+
+To access individual elements of a URL, try this:
+
+  use URI::URL;
+  $host = url("http://www.sn.no/")->host;
+
+or
+
+  use URI::URL;
+  $u = url("ftp://ftp.sn.no/test/aas;type=i");
+  print "Protocol scheme is ", $u->scheme, "\n";
+  print "Host is ", $u->host, " at port ", $u->port, "\n";
+
+or even
+
+  use URI::URL;
+  my($host,$port) = (url("ftp://ftp.sn.no/test/aas;type=i")->crack)[3,4];
+
+
+=head1 EXPAND RELATIVE URLS
+
+This code reads URLs and print expanded version.
+
+  use URI::URL;
+  $BASE = "http://www.sn.no/some/place?query";
+  while (<>) {
+     print url($_, $BASE)->abs->as_string, "\n";
+  }
+
+We can expand URLs in an HTML document by using the parser to build a
+tree that we then traverse:
+
+  %link_elements =
+  (
+   'a'    => 'href',
+   'img'  => 'src',
+   'form' => 'action',
+   'link' => 'href',
+  );
+
+  use HTML::Parse;
+  use URI::URL;
+
+  $BASE = "http://somewhere/root/";
+  $h = parse_htmlfile("xxx.html");
+  $h->traverse(\&expand_urls, 1);
+
+  print $h->as_HTML;
+
+  sub expand_urls
+  {
+     my($e, $start) = @_;
+     return 1 unless $start;
+     my $attr = $link_elements{$e->tag};
+     return 1 unless defined $attr;
+     my $url = $e->attr($attr);
+     return 1 unless defined $url;
+     $e->attr($attr, url($url, $BASE)->abs->as_string);
+  }
+
+
+
+=head1 BASE URL
+
+If you want to resolve relative links in a page you will have to
+determine which base URL to use.  The HTTP::Response objects now has a
+base() method.
+
+  $BASE = $res->base;
+
+
+
+=head1 COPYRIGHT
+
+Copyright 1996-1997, Gisle Aas
+
+This library is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/README
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/README	(revision 16561)
@@ -0,0 +1,24 @@
+Self test suite for the libwww-perl library
+-------------------------------------------
+
+Tests are invoked by running the ./TEST script, but usually you run
+the tests with "make test" at the top libwww-perl directory.  Use -v
+option for verbose tests.  You might run an individual test like this:
+
+  ./TEST -v base/date
+
+or all tests in a directory like this
+
+  ./TEST base
+
+You enable network tests by creating the "net/config.pl" file.  A good
+start is to make a copy of the "net/config.pl.dist" file.  For network
+tests you should also make a link from the cgi-bin directory of your
+http server to the "net/cgi-bin" directory.  You might also have to
+check that the interpreter line (#!.../perl) in the scripts is ok for
+your system.  The following setup works for my site:
+
+  ln -s `pwd`/net/cgi-bin ~www/cgi-bin/lwp
+  cp net/config.pl.dist net/config.pl
+  emacs net/config.pl                        # fix if necessary
+  ./TEST net
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/TEST
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/TEST	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/TEST	(revision 16561)
@@ -0,0 +1,39 @@
+#!/usr/local/bin/perl
+
+# This script is run Test::Harness on the tests found under the
+# "t" directory.
+
+# First we check if we already are within the "t" directory
+unless (-d "base") {
+    # try to move into test directory
+    chdir "t" or die "Can't chdir: $!";
+
+    # fix all relative library locations
+    foreach (@INC) {
+	$_ = "../$_" unless m,^/,;
+    }
+}
+# Pick up the library files from the ../blib directory
+unshift(@INC, "../blib/lib", "../blib/arch");
+#print "@INC\n";
+
+
+use Test::Harness;
+$Test::Harness::verbose = shift
+  if $ARGV[0] =~ /^\d+$/ || $ARGV[0] eq "-v";
+
+if (@ARGV) {
+    for (@ARGV) {
+	if (-d $_) {
+	    push(@tests, <$_/*.t>);
+	} else {
+            $_ .= ".t" unless /\.t$/;
+	    push(@tests, $_);
+	}
+    }
+} else {
+    @tests = (<base/*.t>, <uri/*.t>, <robot/*.t>, <local/*.t>);
+    push(@tests, <net/*.t>) if -f "net/config.pl";
+}
+
+runtests @tests;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/common-req.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/common-req.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/common-req.t	(revision 16561)
@@ -0,0 +1,193 @@
+print "1..19\n";
+
+use HTTP::Request::Common;
+
+$r = GET 'http://www.sn.no/';
+print $r->as_string;
+
+print "not " unless $r->method eq "GET" and $r->url eq "http://www.sn.no/";
+print "ok 1\n";
+
+$r = HEAD "http://www.sn.no/",
+     If_Match => 'abc',
+     From => 'aas@sn.no';
+print $r->as_string;
+
+print "not " unless $r->method eq "HEAD" and $r->url->eq("http://www.sn.no");
+print "ok 2\n";
+
+print "not " unless $r->header('If-Match') eq "abc" and $r->header("from") eq "aas\@sn.no";
+print "ok 3\n";
+
+$r = PUT "http://www.sn.no",
+     Content => 'foo';
+print $r->as_string;
+
+print "not " unless $r->method eq "PUT" and $r->url->netloc eq "www.sn.no";
+print "ok 4\n";
+
+print "not " if defined($r->header("Content"));
+print "ok 5\n";
+
+print "not " unless ${$r->content_ref} eq "foo" and
+                    $r->content eq "foo";
+print "ok 6\n";
+
+#--- Test POST requests ---
+
+$r = POST "http://www.sn.no", [foo => 'bar',
+                               baz => [qw(a b c)],
+                               foo => 'zoo=&',
+                               "space " => " + ",
+                              ],
+                              bar => 'foo';
+print $r->as_string;
+
+print "not " unless $r->method eq "POST" and
+                    $r->content_type eq "application/x-www-form-urlencoded" and
+                    $r->content_length == 58 and
+                    $r->header("bar") eq "foo";
+print "ok 7\n";
+
+print "not " unless $r->content eq "foo=bar&baz=a&baz=b&baz=c&foo=zoo%3D%26&space%20=%20%2B%20";
+print "ok 8\n";
+
+$r = POST "mailto:gisle\@aas.no",
+     Subject => "Heisan",
+     Content_Type => "text/plain",
+     Content => "Howdy\n";
+print $r->as_string;
+
+print "not " unless $r->method eq "POST" and
+                    $r->header("Subject") eq "Heisan" and
+                    $r->content eq "Howdy\n" and
+	            $r->content_type eq "text/plain";
+print "ok 9\n";
+
+#
+# POST for File upload
+#
+$file = "test-$$";
+open(FILE, ">$file") or die "Can't create $file: $!";
+print FILE "foo\nbar\nbaz\n";
+close(FILE);
+
+$r = POST 'http://www.perl.org/survey.cgi',
+       Content_Type => 'form-data',
+       Content      => [ name  => 'Gisle Aas',
+                         email => 'gisle@aas.no',
+                         gender => 'm',
+                         born   => '1964',
+                         file   => [$file],
+                       ];
+print $r->as_string;
+
+unlink($file) or warn "Can't unlink $file: $!";
+
+print "not " unless $r->method eq "POST" and
+	            $r->url->path eq "/survey.cgi" and
+                    $r->content_type eq "multipart/form-data" and
+	            $r->header(Content_type) =~ /boundary="?([^"]+)"?/;
+print "ok 10\n";
+$boundary = $1;
+
+$c = $r->content;
+$c =~ s/\r//g;
+@c = split(/--\Q$boundary/, $c);
+print "$c[5]\n";
+
+print "not " unless @c == 7 and $c[6] =~ /^--\n/;  # 5 parts + header & trailer
+print "ok 11\n";
+
+print "not " unless $c[2] =~ /^Content-Disposition:\s*form-data;\s*name="email"/m and
+                    $c[2] =~ /^gisle\@aas.no$/m;
+print "ok 12\n";
+
+print "not " unless $c[5] =~ /^Content-Disposition:\s*form-data;\s*name="file";\s*filename="$file"/m and
+	            $c[5] =~ /^Content-Type:\s*text\/plain$/m and
+	            $c[5] =~ /^foo\nbar\nbaz/m;
+print "ok 13\n";
+
+$r = POST 'http://www.perl.org/survey.cgi',
+      [ file => [ undef, "xxx", Content_type => "text/html", Content => "<h1>Hello, world!</h1>" ]],
+      Content_type => 'multipart/form-data';
+print $r->as_string;
+
+print "not " unless $r->content =~ /^--\S+\015\012Content-Disposition:\s*form-data;\s*name="file";\s*filename="xxx"/m and
+	            $r->content =~ /^Content-Type: text\/html/m and
+	            $r->content =~ /^<h1>Hello, world/m;
+print "ok 14\n";
+
+
+$r = POST 'http://www.perl.org/survey.cgi',
+      Content_type => 'multipart/form-data',
+      Content => [ file => [ undef, undef, Content => "foo"]];
+print $r->as_string;
+
+print "not " if $r->content =~ /filename=/;
+print "ok 15\n";
+
+
+# The POST routine can now also take a hash reference.
+my %hash = (foo => 42, bar => 24);
+$r = POST 'http://www.perl.org/survey.cgi', \%hash;
+print $r->as_string;
+print "not " unless $r->content =~ /foo=42/ &&
+                    $r->content =~ /bar=24/ &&
+                    $r->content_type eq "application/x-www-form-urlencoded" &&
+                    $r->content_length == 13;
+print "ok 16\n";
+
+ 
+#
+# POST for File upload
+#
+use HTTP::Request::Common qw($DYNAMIC_FILE_UPLOAD);
+
+$file = "test-$$";
+open(FILE, ">$file") or die "Can't create $file: $!";
+for (1..1000) {
+   print FILE "a" .. "z";
+}
+close(FILE);
+
+$DYNAMIC_FILE_UPLOAD++;
+$r = POST 'http://www.perl.org/survey.cgi',
+       Content_Type => 'form-data',
+       Content      => [ name  => 'Gisle Aas',
+                         email => 'gisle@aas.no',
+                         gender => 'm',
+                         born   => '1964',
+                         file   => [$file],
+                       ];
+print $r->as_string;
+
+print "not " unless $r->method eq "POST" and
+	            $r->url->path eq "/survey.cgi" and
+                    $r->content_type eq "multipart/form-data" and
+	            $r->header(Content_type) =~ /boundary="?([^"]+)"?/ and
+		    ref($r->content) eq "CODE";
+print "ok 17\n";
+$boundary = $1;
+
+print "not " unless length($boundary) > 10;
+print "ok 18\n";
+
+$code = $r->content;
+my $chunk;
+my @chunks;
+while (defined($chunk = &$code) && length $chunk) {
+   push(@chunks, $chunk);
+}
+
+unlink($file) or warn "Can't unlink $file: $!";
+
+$_ = join("", @chunks);
+
+print int(@chunks), " chunks, total size is ", length($_), " bytes\n";
+
+# should be close to expected size and number of chunks
+print "not " unless abs(@chunks - 15 < 3) and
+                    abs(length($_) - 26589) < 20;
+print "ok 19\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/cookies.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/cookies.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/cookies.t	(revision 16561)
@@ -0,0 +1,515 @@
+print "1..32\n";
+
+#use LWP::Debug '+';
+
+use HTTP::Cookies;
+use HTTP::Request;
+use HTTP::Response;
+
+#-------------------------------------------------------------------
+# First we check that it works for the original example at
+# http://www.netscape.com/newsref/std/cookie_spec.html
+
+# Client requests a document, and receives in the response:
+# 
+#       Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT
+# 
+# When client requests a URL in path "/" on this server, it sends:
+# 
+#       Cookie: CUSTOMER=WILE_E_COYOTE
+# 
+# Client requests a document, and receives in the response:
+# 
+#       Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
+# 
+# When client requests a URL in path "/" on this server, it sends:
+# 
+#       Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
+# 
+# Client receives:
+# 
+#       Set-Cookie: SHIPPING=FEDEX; path=/fo
+# 
+# When client requests a URL in path "/" on this server, it sends:
+# 
+#       Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
+# 
+# When client requests a URL in path "/foo" on this server, it sends:
+# 
+#       Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX
+# 
+# The last Cookie is buggy, because both specifications says that the
+# most specific cookie must be sent first.  SHIPPING=FEDEX is the
+# most specific and should thus be first.
+
+$c = HTTP::Cookies->new;
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/");
+
+$res = HTTP::Response->new(200, "OK");
+$res->request($req);
+$res->header("Set-Cookie" => "CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT");
+$c->extract_cookies($res);
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/");
+$c->add_cookie_header($req);
+
+print "not " unless $req->header("Cookie") eq "CUSTOMER=WILE_E_COYOTE" &&
+                    $req->header("Cookie2") eq "\$Version=1";
+print "ok 1\n";
+
+$res->request($req);
+$res->header("Set-Cookie" => "PART_NUMBER=ROCKET_LAUNCHER_0001; path=/");
+$c->extract_cookies($res);
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/foo/bar");
+$c->add_cookie_header($req);
+
+$h = $req->header("Cookie");
+print "not " unless $h =~ /PART_NUMBER=ROCKET_LAUNCHER_0001/ &&
+                    $h =~ /CUSTOMER=WILE_E_COYOTE/;
+print "ok 2\n";
+
+$res->request($req);
+$res->header("Set-Cookie", "SHIPPING=FEDEX; path=/foo");
+$c->extract_cookies($res);
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/");
+$c->add_cookie_header($req);
+
+$h = $req->header("Cookie");
+print "not " unless $h =~ /PART_NUMBER=ROCKET_LAUNCHER_0001/ &&
+	            $h =~ /CUSTOMER=WILE_E_COYOTE/ &&
+	            $h !~ /SHIPPING=FEDEX/;
+print "ok 3\n";
+
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/foo/");
+$c->add_cookie_header($req);
+
+$h = $req->header("Cookie");
+print "not " unless $h =~ /PART_NUMBER=ROCKET_LAUNCHER_0001/ &&
+	            $h =~ /CUSTOMER=WILE_E_COYOTE/ &&
+		    $h =~ /^SHIPPING=FEDEX;/;
+print "ok 4\n";
+
+print $c->as_string;
+
+
+# Second Example transaction sequence:
+# 
+# Assume all mappings from above have been cleared.
+# 
+# Client receives:
+# 
+#       Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
+# 
+# When client requests a URL in path "/" on this server, it sends:
+# 
+#       Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001
+# 
+# Client receives:
+# 
+#       Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo
+# 
+# When client requests a URL in path "/ammo" on this server, it sends:
+# 
+#       Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001
+# 
+#       NOTE: There are two name/value pairs named "PART_NUMBER" due to
+#       the inheritance of the "/" mapping in addition to the "/ammo" mapping. 
+
+$c = HTTP::Cookies->new;  # clear it
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/");
+$res = HTTP::Response->new(200, "OK");
+$res->request($req);
+$res->header("Set-Cookie", "PART_NUMBER=ROCKET_LAUNCHER_0001; path=/");
+
+$c->extract_cookies($res);
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/");
+$c->add_cookie_header($req);
+
+print "not " unless $req->header("Cookie") eq "PART_NUMBER=ROCKET_LAUNCHER_0001";
+print "ok 5\n";
+
+$res->request($req);
+$res->header("Set-Cookie", "PART_NUMBER=RIDING_ROCKET_0023; path=/ammo");
+$c->extract_cookies($res);
+
+$req = HTTP::Request->new(GET => "http://www.acme.com/ammo");
+$c->add_cookie_header($req);
+
+print "not " unless $req->header("Cookie") =~
+       /^PART_NUMBER=RIDING_ROCKET_0023;\s*PART_NUMBER=ROCKET_LAUNCHER_0001/;
+print "ok 6\n";
+
+print $c->as_string;
+undef($c);
+
+
+#-------------------------------------------------------------------
+# When there are no "Set-Cookie" header, then even responses
+# without any request URLs connected should be allowed.
+
+$c = HTTP::Cookies->new;
+$c->extract_cookies(HTTP::Response->new("200", "OK"));
+print "not " if count_cookies($c) != 0;
+print "ok 7\n";
+
+
+#-------------------------------------------------------------------
+# Then we test with the examples from draft-ietf-http-state-man-mec-03.txt
+#
+# 5.  EXAMPLES
+
+$c = HTTP::Cookies->new;
+
+# 
+# 5.1  Example 1
+# 
+# Most detail of request and response headers has been omitted.  Assume
+# the user agent has no stored cookies.
+# 
+#   1.  User Agent -> Server
+# 
+#       POST /acme/login HTTP/1.1
+#       [form data]
+# 
+#       User identifies self via a form.
+# 
+#   2.  Server -> User Agent
+# 
+#       HTTP/1.1 200 OK
+#       Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
+# 
+#       Cookie reflects user's identity.
+
+$cookie = interact($c, 'http://www.acme.com/acme/login',
+                       'Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"');
+print "not " if $cookie;
+print "ok 8\n";
+
+# 
+#   3.  User Agent -> Server
+# 
+#       POST /acme/pickitem HTTP/1.1
+#       Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
+#       [form data]
+# 
+#       User selects an item for ``shopping basket.''
+# 
+#   4.  Server -> User Agent
+# 
+#       HTTP/1.1 200 OK
+#       Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+#               Path="/acme"
+# 
+#       Shopping basket contains an item.
+
+$cookie = interact($c, 'http://www.acme.com/acme/pickitem',
+		       'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"');
+print "not " unless $cookie =~ m(^\$Version="?1"?; Customer="?WILE_E_COYOTE"?; \$Path="/acme"$);
+print "ok 9\n";
+
+# 
+#   5.  User Agent -> Server
+# 
+#       POST /acme/shipping HTTP/1.1
+#       Cookie: $Version="1";
+#               Customer="WILE_E_COYOTE"; $Path="/acme";
+#               Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+#       [form data]
+# 
+#       User selects shipping method from form.
+# 
+#   6.  Server -> User Agent
+# 
+#       HTTP/1.1 200 OK
+#       Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
+# 
+#       New cookie reflects shipping method.
+
+$cookie = interact($c, "http://www.acme.com/acme/shipping",
+		   'Shipping="FedEx"; Version="1"; Path="/acme"');
+
+print "not " unless $cookie =~ /^\$Version="?1"?;/ &&
+     $cookie =~ /Part_Number="?Rocket_Launcher_0001"?;\s*\$Path="\/acme"/ &&
+     $cookie =~ /Customer="?WILE_E_COYOTE"?;\s*\$Path="\/acme"/;
+print "ok 10\n";
+
+# 
+#   7.  User Agent -> Server
+# 
+#       POST /acme/process HTTP/1.1
+#       Cookie: $Version="1";
+#               Customer="WILE_E_COYOTE"; $Path="/acme";
+#               Part_Number="Rocket_Launcher_0001"; $Path="/acme";
+#               Shipping="FedEx"; $Path="/acme"
+#       [form data]
+# 
+#       User chooses to process order.
+# 
+#   8.  Server -> User Agent
+# 
+#       HTTP/1.1 200 OK
+# 
+#       Transaction is complete.
+
+$cookie = interact($c, "http://www.acme.com/acme/process");
+print "FINAL COOKIE: $cookie\n";
+print "not " unless $cookie =~ /Shipping="?FedEx"?;\s*\$Path="\/acme"/ &&
+                    $cookie =~ /WILE_E_COYOTE/;
+print "ok 11\n";
+
+# 
+# The user agent makes a series of requests on the origin server, after
+# each of which it receives a new cookie.  All the cookies have the same
+# Path attribute and (default) domain.  Because the request URLs all have
+# /acme as a prefix, and that matches the Path attribute, each request
+# contains all the cookies received so far.
+
+print $c->as_string;
+
+
+# 5.2  Example 2
+# 
+# This example illustrates the effect of the Path attribute.  All detail
+# of request and response headers has been omitted.  Assume the user agent
+# has no stored cookies.
+
+$c = HTTP::Cookies->new;
+
+# Imagine the user agent has received, in response to earlier requests,
+# the response headers
+# 
+# Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+#         Path="/acme"
+# 
+# and
+# 
+# Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1";
+#         Path="/acme/ammo"
+
+interact($c, "http://www.acme.com/acme/ammo/specific",
+             'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"',
+             'Part_Number="Riding_Rocket_0023"; Version="1"; Path="/acme/ammo"');
+
+# A subsequent request by the user agent to the (same) server for URLs of
+# the form /acme/ammo/...  would include the following request header:
+# 
+# Cookie: $Version="1";
+#         Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
+#         Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+# 
+# Note that the NAME=VALUE pair for the cookie with the more specific Path
+# attribute, /acme/ammo, comes before the one with the less specific Path
+# attribute, /acme.  Further note that the same cookie name appears more
+# than once.
+
+$cookie = interact($c, "http://www.acme.com/acme/ammo/...");
+print "not " unless $cookie =~ /Riding_Rocket_0023.*Rocket_Launcher_0001/;
+print "ok 12\n";
+
+# A subsequent request by the user agent to the (same) server for a URL of
+# the form /acme/parts/ would include the following request header:
+# 
+# Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+# 
+# Here, the second cookie's Path attribute /acme/ammo is not a prefix of
+# the request URL, /acme/parts/, so the cookie does not get forwarded to
+# the server.
+
+$cookie = interact($c, "http://www.acme.com/acme/parts/");
+print "not " unless $cookie =~ /Rocket_Launcher_0001/ &&
+		    $cookie !~ /Riding_Rocket_0023/;
+print "ok 13\n";
+
+print $c->as_string;
+
+#-----------------------------------------------------------------------
+
+# Test rejection of Set-Cookie2 responses based on domain, path or port
+
+$c = HTTP::Cookies->new;
+
+# illegal domain (no embedded dots)
+$cookie = interact($c, "http://www.acme.com", 'foo=bar; domain=".com"');
+print "not " if count_cookies($c) > 0;
+print "ok 14\n";
+
+# legal domain
+$cookie = interact($c, "http://www.acme.com", 'foo=bar; domain="acme.com"');
+print "not " if count_cookies($c) != 1;
+print "ok 15\n";
+
+# illegal domain (host prefix "www.a" contains a dot)
+$cookie = interact($c, "http://www.a.acme.com", 'foo=bar; domain="acme.com"');
+print "not " if count_cookies($c) != 1;
+print "ok 16\n";
+
+# legal domain
+$cookie = interact($c, "http://www.a.acme.com", 'foo=bar; domain=".a.acme.com"');
+print "not " if count_cookies($c) != 2;
+print "ok 17\n";
+
+# can't use a IP-address as domain
+$cookie = interact($c, "http://125.125.125.125", 'foo=bar; domain="125.125.125"');
+print "not " if count_cookies($c) != 2;
+print "ok 18\n";
+
+# illegal path (must be prefix of request path)
+$cookie = interact($c, "http://www.sol.no", 'foo=bar; domain=".sol.no"; path="/foo"');
+print "not " if count_cookies($c) != 2;
+print "ok 19\n";
+
+# legal path
+$cookie = interact($c, "http://www.sol.no/foo/bar", 'foo=bar; domain=".sol.no"; path="/foo"');
+print "not " if count_cookies($c) != 3;
+print "ok 20\n";
+
+# illegal port (request-port not in list)
+$cookie = interact($c, "http://www.sol.no", 'foo=bar; domain=".sol.no"; port="90,100"');
+print "not " if count_cookies($c) != 3;
+print "ok 21\n";
+
+# legal port
+$cookie = interact($c, "http://www.sol.no", 'foo=bar; domain=".sol.no"; port="90,100, 80,8080"; max-age=100; Comment = "Just kidding! (\"|\\\\) "');
+print "not " if count_cookies($c) != 4;
+print "ok 22\n";
+
+# port attribute without any value (current port)
+$cookie = interact($c, "http://www.sol.no", 'foo9=bar; domain=".sol.no"; port; max-age=100;');
+print "not " if count_cookies($c) != 5;
+print "ok 23\n";
+
+# encoded path
+$cookie = interact($c, "http://www.sol.no/foo/", 'foo8=bar; path="/%66oo"');
+print "not " if count_cookies($c) != 6;
+print "ok 24\n";
+
+my $file = "lwp-cookies-$$.txt";
+$c->save($file);
+$old = $c->as_string;
+undef($c);
+
+$c = HTTP::Cookies->new;
+$c->load($file);
+unlink($file) || warn "Can't unlink $file: $!";
+
+print "not " unless $old eq $c->as_string;
+print "ok 25\n";
+
+undef($c);
+
+#
+# Try some URL encodings of the PATHs
+#
+$c = HTTP::Cookies->new;
+interact($c, "http://www.acme.com/foo%2f%25/%40%40%0Anew%E5/%E5", 'foo  =   bar; version    =   1');
+print $c->as_string;
+
+$cookie = interact($c, "http://www.acme.com/foo%2f%25/@@%0anewå/æøå", "bar=baz; path=\"/foo/\"; version=1");
+print "not " unless $cookie =~ /foo=bar/ && $cookie =~ /^\$version=\"?1\"?/i;
+print "ok 26\n";
+
+$cookie = interact($c, "http://www.acme.com/foo/%25/@@%0anewå/æøå");
+print "not " if $cookie;
+print "ok 27\n";
+
+undef($c);
+
+#
+# Try to use the Netscape cookie file format for saving
+#
+$file = "cookies-$$.txt";
+$c = HTTP::Cookies::Netscape->new(file => $file);
+interact($c, "http://www.acme.com/", "foo1=bar; max-age=100");
+interact($c, "http://www.acme.com/", "foo2=bar; port=\"80\"; max-age=100; Discard; Version=1");
+interact($c, "http://www.acme.com/", "foo3=bar; secure; Version=1");
+$c->save;
+undef($c);
+
+$c = HTTP::Cookies::Netscape->new(file => $file);
+print "not " unless count_cookies($c) == 1;     # 2 of them discarded on save
+print "ok 28\n";
+
+print "not " unless $c->as_string =~ /foo1=bar/;
+print "ok 29\n";
+undef($c);
+unlink($file);
+
+
+#
+# Some additional Netscape cookies test
+#
+$c = HTTP::Cookies->new;
+$req = HTTP::Request->new(POST => "http://foo.bar.acme.com/foo");
+
+# Netscape allows a host part that contains dots
+$res = HTTP::Response->new(200, "OK");
+$res->header(set_cookie => 'Customer=WILE_E_COYOTE; domain=.acme.com');
+$res->request($req);
+$c->extract_cookies($res);
+
+# and that the domain is the same as the host without adding a leading
+# dot to the domain.  Should not quote even if strange chars are used
+# in the cookie value.
+$res = HTTP::Response->new(200, "OK");
+$res->header(set_cookie => 'PART_NUMBER=3,4; domain=foo.bar.acme.com');
+$res->request($req);
+$c->extract_cookies($res);
+
+print $c->as_string;
+
+$req = HTTP::Request->new(POST => "http://foo.bar.acme.com/foo");
+$c->add_cookie_header($req);
+#print $req->as_string;
+print "not " unless $req->header("Cookie") =~ /PART_NUMBER=3,4/ &&
+	            $req->header("Cookie") =~ /Customer=WILE_E_COYOTE/;
+print "ok 30\n";
+
+
+
+# Test handling of local intranet hostnames without a dot
+$c->clear;
+print "---\n";
+#require LWP::Debug;
+#LWP::Debug::level('+');
+
+interact($c, "http://example/", "foo1=bar; PORT; Discard;");
+$_=interact($c, "http://example/", 'foo2=bar; domain=".local"');
+print "not " unless /foo1=bar/;
+print "ok 31\n";
+
+$_=interact($c, "http://example/", 'foo3=bar');
+$_=interact($c, "http://example/");
+print "Cookie: $_\n";
+print "not " unless /foo2=bar/ && count_cookies($c) == 3;
+print "ok 32\n";
+print $c->as_string;
+
+#-------------------------------------------------------------------
+
+sub interact
+{
+    my $c = shift;
+    my $url = shift;
+    my $req = HTTP::Request->new(POST => $url);
+    $c->add_cookie_header($req);
+    my $cookie = $req->header("Cookie");
+    my $res = HTTP::Response->new(200, "OK");
+    $res->request($req);
+    for (@_) { $res->push_header("Set-Cookie2" => $_) }
+    $c->extract_cookies($res);
+    return $cookie;
+}
+
+sub count_cookies
+{
+    my $c = shift;
+    my $no = 0;
+    $c->scan(sub { $no++ });
+    $no;
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/date.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/date.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/date.t	(revision 16561)
@@ -0,0 +1,165 @@
+use HTTP::Date;
+
+print "1..59\n";
+
+$no = 1;
+$| = 1;
+sub ok {
+   print "not " if $_[0];
+   print "ok $no\n";
+   $no++;
+}
+
+# test str2time for supported dates
+my(@tests) =
+(
+ 'Thu Feb  3 00:00:00 GMT 1994',        # ctime format
+ 'Thu Feb  3 00:00:00 1994',            # same as ctime, except no TZ
+
+ 'Thu, 03 Feb 1994 00:00:00 GMT',       # proposed new HTTP format
+ 'Thursday, 03-Feb-94 00:00:00 GMT',    # old rfc850 HTTP format
+ 'Thursday, 03-Feb-1994 00:00:00 GMT',  # broken rfc850 HTTP format
+
+ '03/Feb/1994:00:00:00 0000',   # common logfile format
+ '03/Feb/1994:01:00:00 +0100',  # common logfile format
+ '02/Feb/1994:23:00:00 -0100',  # common logfile format
+
+ '03 Feb 1994 00:00:00 GMT',    # HTTP format (no weekday)
+ '03-Feb-94 00:00:00 GMT',      # old rfc850 (no weekday)
+ '03-Feb-1994 00:00:00 GMT',    # broken rfc850 (no weekday)
+ '03-Feb-1994 00:00 GMT',       # broken rfc850 (no weekday, no seconds)
+ '03-Feb-1994 00:00',           # VMS dir listing format
+
+ '03-Feb-94',    # old rfc850 HTTP format    (no weekday, no time)
+ '03-Feb-1994',  # broken rfc850 HTTP format (no weekday, no time)
+ '03 Feb 1994',  # proposed new HTTP format  (no weekday, no time)
+ '03/Feb/1994',  # common logfile format     (no time, no offset)
+
+ #'Feb  3 00:00',     # Unix 'ls -l' format (can't really test it here)
+ 'Feb  3 1994',       # Unix 'ls -l' format
+
+ "02-03-94  12:00AM", # Windows 'dir' format
+
+ # ISO 8601 formats
+ '1994-02-03 00:00:00 +0000',
+ '1994-02-03',
+ '19940203',
+ '1994-02-03T00:00:00+0000',
+ '1994-02-02T23:00:00-0100',
+ '1994-02-02T23:00:00-01:00',
+ '1994-02-03T00:00:00 Z',
+ '19940203T000000Z',
+ '199402030000',
+
+ # A few tests with extra space at various places
+ '  03/Feb/1994      ',
+ '  03   Feb   1994  0:00  ',
+);
+
+my $time = 760233600;  # assume broken POSIX counting of seconds
+for (@tests) {
+    if (/GMT/i) {
+	$t = str2time($_);
+    } else {
+	$t = str2time($_, "GMT");
+    }
+    $t = "UNDEF" unless defined $t;
+    print "'$_'  =>  $t\n";
+    print $@ if $@;
+    print "not " if $t eq 'UNDEF' || $t != $time;
+    ok;
+}
+
+# test time2str
+die "time2str failed"
+    unless time2str($time) eq 'Thu, 03 Feb 1994 00:00:00 GMT';
+
+# test the 'ls -l' format with missing year$
+# round to nearest minute 3 days ago.
+$time = int((time - 3 * 24*60*60) /60)*60;
+($min, $hr, $mday, $mon) = (localtime $time)[1,2,3,4];
+$mon = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
+$str = sprintf("$mon %02d %02d:%02d", $mday, $hr, $min);
+$t = str2time($str);
+$t = "UNDEF" unless defined $t;
+print "'$str'  =>  $t ($time)\n";
+print "not " if $t != $time;
+ok;
+
+# try some out of bounds date and some garbage.
+for ('03-Feb-1969', '03-Feb-2039',
+     undef, '', 'Garbage',
+     'Mandag 16. September 1996',
+     'Thu Feb  3 00:00:00 CET 1994',
+     'Thu, 03 Feb 1994 00:00:00 CET',
+     'Wednesday, 31-Dec-69 23:59:59 GMT',
+
+     '1980-00-01',
+     '1980-13-01',
+     '1980-01-00',
+     '1980-01-32',
+     '1980-01-01 25:00:00',
+     '1980-01-01 00:61:00',
+     '1980-01-01 00:00:61',
+    )
+{
+    my $bad = 0;
+    eval {
+	if (defined str2time $_) {
+	    print "str2time($_) is not undefined\n";
+	    $bad++;
+	}
+    };
+    print defined($_) ? "'$_'\n" : "undef\n";
+    print $@ if $@;
+    print "not " if $bad;
+    ok;
+}
+
+print "Testing AM/PM gruff...\n";
+
+$t = time2iso(str2time("11-12-96  0:00AM"));print "$t\n";
+ok($t ne "1996-11-12 00:00:00");
+
+$t = time2iso(str2time("11-12-96 12:00AM"));print "$t\n";
+ok($t ne "1996-11-12 00:00:00");
+
+$t = time2iso(str2time("11-12-96  0:00PM"));print "$t\n";
+ok($t ne "1996-11-12 12:00:00");
+
+$t = time2iso(str2time("11-12-96 12:00PM"));print "$t\n";
+ok($t ne "1996-11-12 12:00:00");
+
+
+$t = time2iso(str2time("11-12-96  1:05AM"));print "$t\n";
+ok($t ne "1996-11-12 01:05:00");
+
+$t = time2iso(str2time("11-12-96 12:05AM"));print "$t\n";
+ok($t ne "1996-11-12 00:05:00");
+
+$t = time2iso(str2time("11-12-96  1:05PM"));print "$t\n";
+ok($t ne "1996-11-12 13:05:00");
+
+$t = time2iso(str2time("11-12-96 12:05PM"));print "$t\n";
+ok($t ne "1996-11-12 12:05:00");
+
+
+
+# Test the str2iso routines
+use HTTP::Date qw(time2iso time2isoz);
+
+print "Testing time2iso functions\n";
+
+$a = time2iso;
+$b = time2iso(500000);
+print "LOCAL $a  $b\n";
+$az = time2isoz;
+$bz = time2isoz(500000);
+print "GMT   $az $bz\n";
+
+for ($a,  $b)  { ok if /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$/;  }
+for ($az, $bz) { ok if /^\d{4}-\d\d-\d\d \d\d:\d\d:\d\dZ$/; }
+
+
+
+print "HTTP::Date $HTTP::Date::VERSION tested ok\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-auth.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-auth.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-auth.t	(revision 16561)
@@ -0,0 +1,40 @@
+print "1..4\n";
+
+use HTTP::Response;
+use HTTP::Headers::Auth;
+
+$res = HTTP::Response->new(401);
+$res->push_header(WWW_Authenticate => qq(Foo realm="WallyWorld", foo=bar, Bar realm="WallyWorld2"));
+$res->push_header(WWW_Authenticate => qq(Basic Realm="WallyWorld", foo=bar, bar=baz));
+
+print $res->as_string;
+
+%auth = $res->www_authenticate;
+
+print "not " unless keys(%auth) == 3;
+print "ok 1\n";
+
+print "not " unless $auth{basic}{realm} eq "WallyWorld" &&
+                    $auth{bar}{realm} eq "WallyWorld2";
+print "ok 2\n";
+
+$a = $res->www_authenticate;
+print "not " unless $a eq 'Foo realm="WallyWorld", foo=bar, Bar realm="WallyWorld2", Basic Realm="WallyWorld", foo=bar, bar=baz';
+print "ok 3\n";
+
+$res->www_authenticate("Basic realm=foo1");
+print $res->as_string;
+
+$res->www_authenticate(Basic => {realm => foo2});
+print $res->as_string;
+
+$res->www_authenticate(Basic => [realm => foo3, foo=>33],
+Digest => {nonce=>"bar", foo=>'foo'});
+print $res->as_string;
+
+$_ = $res->as_string;
+
+print "not " unless /WWW-Authenticate: Basic realm="foo3", foo=33/ &&
+                    /WWW-Authenticate: Digest nonce=bar, foo=foo/;
+print "ok 4\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-etag.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-etag.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-etag.t	(revision 16561)
@@ -0,0 +1,28 @@
+print "1..4\n";
+
+require HTTP::Headers::ETag;
+
+$h = HTTP::Headers->new;
+$h->etag("tag1");
+print "not " unless $h->etag eq qq("tag1");
+print "ok 1\n";
+
+$h->etag("w/tag2");
+print "not " unless $h->etag eq qq(W/"tag2");
+print "ok 2\n";
+
+$h->if_match(qq(W/"foo", bar, baz), "bar");
+$h->if_none_match(333);
+
+$h->if_range("tag3");
+print "not " unless $h->if_range eq qq("tag3");
+print "ok 3\n";
+
+$t = time;
+$h->if_range($t);
+print "not " unless $h->if_range == $t;
+print "ok 4\n";
+
+
+print $h->as_string;
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-util.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-util.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers-util.t	(revision 16561)
@@ -0,0 +1,61 @@
+use strict;
+use HTTP::Headers::Util qw(split_header_words join_header_words);
+
+my $extra_tests = 2;
+
+my @s_tests = (
+
+   ["foo"                     => "foo"],
+   ["foo=bar"                 => "foo=bar"],
+   ["   foo   "               => "foo"],
+   ["foo="                    => 'foo=""'],
+   ["foo=bar bar=baz"         => "foo=bar; bar=baz"],
+   ["foo=bar;bar=baz"         => "foo=bar; bar=baz"],
+   ['foo bar baz'             => "foo; bar; baz"],
+   ['foo="\"" bar="\\\\"'     => 'foo="\""; bar="\\\\"'],
+   ['foo,,,bar'               => 'foo, bar'],
+   ['foo=bar,bar=baz'         => 'foo=bar, bar=baz'],
+
+   ['text/html; charset=iso-8859-1' =>
+    'text/html; charset="iso-8859-1"'],
+
+   ['foo="bar"; port="80,81"; discard, bar=baz' =>
+    'foo=bar; port="80,81"; discard, bar=baz'],
+
+   ['Basic realm="\"foo\\\\bar\""' =>
+    'Basic; realm="\"foo\\\\bar\""'],
+);
+
+print "1..", @s_tests + $extra_tests, "\n";
+
+my $testno = 1;
+
+print "split_header_words() tests\n";
+for (@s_tests) {
+   my($arg, $expect) = @$_;
+   my @arg = ref($arg) ? @$arg : $arg;
+
+   my $res = join_header_words(split_header_words(@arg));
+   if ($res ne $expect) {
+       print "\nUnexpected result: '$res'\n";
+       print "         Expected: '$expect'\n";
+       print "  when parsing '", join(", ", @arg), "'\n";
+       eval {
+	   require Data::Dumper;
+           my @p = split_header_words(@arg);
+           print Data::Dumper::Dumper(\@p);
+       };
+       print "not ";
+   }
+   print "ok ", $testno++, "\n";
+}
+
+
+print "Extra tests\n";
+# some extra tests
+print "not " unless join_header_words("foo" => undef, "bar" => "baz")
+                    eq "foo; bar=baz";
+print "ok ", $testno++, "\n";
+
+print "not " unless join_header_words() eq "";
+print "ok ", $testno++, "\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/headers.t	(revision 16561)
@@ -0,0 +1,128 @@
+require HTTP::Headers;
+
+print "1..11\n";
+
+$h = new HTTP::Headers
+	mime_version  => "1.0",
+	content_type  => "text/html";
+
+$h->header(URI => "http://www.oslonett.no/");
+
+if ($h->header("MIME-Version") eq "1.0") {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+if ($h->header('Uri') =~ /^http:/) {
+    print "ok 2\n";
+} else {
+    print "not ok 2\n";
+}
+
+
+$h->header("MY-header" => "foo",
+	   "Date" => "somedate",
+	   "Accept" => ["text/plain", "image/*"],
+	  );
+$h->push_header("accept" => "audio/basic");
+
+if ($h->header("date") eq "somedate") {
+     print "ok 3\n";
+}
+
+@accept = $h->header("accept");
+if (@accept == 3) {
+    print "ok 4\n";
+}
+
+$h->remove_header("uri", "date");
+
+
+$str = $h->as_string;
+print "\nHeader looks like this now:\n$str\n";
+
+$lines = ($str =~ tr/\n/\n/);
+
+if ($lines == 6) {
+    print "ok 5\n";
+} else {
+    print "Header has $lines lines\n";
+    print "not ok 5\n";
+}
+
+$h2 = $h->clone;
+
+$h->header("accept", "*/*");
+$h->remove_header("my-header");
+
+@accept = $h2->header("accept");
+if (@accept == 3) {
+    print "ok 6\n";
+}
+
+@accept = $h->header("accept");
+if (@accept == 1) {
+    print "ok 7\n";
+}
+
+# Check order of headers, but first remove this one
+$h2->remove_header('mime_version');
+
+# and add this general header
+$h2->header(Connection => 'close');
+
+@x = ();
+$h2->scan(sub {push(@x, shift);});
+
+$str = join(";", @x);
+$expected = "Connection;Accept;Accept;Accept;Content-Type;MY-Header";
+
+if ($str eq $expected) {
+    print "ok 8\n";
+} else {
+    print "Headers are '$str',\nexpected    '$expected'\n";
+    print "not ok 8\n";
+}
+
+# Check headers with embedded newlines:
+
+$h = new HTTP::Headers
+	a => "foo\n\n",
+	b => "foo\nbar",
+	c => "foo\n\nbar\n\n",
+	d => "foo\n\tbar";
+$str = $h->as_string("<<\n");
+print "-----\n$str------\n";
+
+print "not " unless $str =~ /^A:\s*foo<<\n
+                              B:\s*foo<<\n
+	                        \s+bar<<\n
+                              C:\s*foo<<\n
+                                \s+bar<<\n
+	                      D:\s*foo<<\n
+                                \t bar<<\n
+                             $/x;
+print "ok 9\n";
+
+
+# Check with FALSE $HTML::Headers::TRANSLATE_UNDERSCORE
+
+local($HTTP::Headers::TRANSLATE_UNDERSCORE);
+$HTTP::Headers::TRANSLATE_UNDERSCORE = undef;  # avoid -w warning
+
+$h = HTTP::Headers->new;
+
+$h->header(abc_abc   => "foo");
+$h->header("abc-abc" => "bar");
+
+#print $h->as_string;
+
+print "not " unless $h->header("ABC_ABC") eq "foo" &&
+                    $h->header("ABC-ABC") eq "bar";
+print "ok 10\n";
+
+$h->remove_header("Abc_Abc");
+print "not " unless !defined($h->header("abc_abc")) &&
+                    $h->header("ABC-ABC") eq "bar";
+print "ok 11\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/listing.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/listing.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/listing.t	(revision 16561)
@@ -0,0 +1,86 @@
+print "1..6\n";
+
+
+use File::Listing;
+
+$dir = <<'EOL';
+total 68
+drwxr-xr-x   4 aas      users        1024 Mar 16 15:47 .
+drwxr-xr-x  11 aas      users        1024 Mar 15 19:22 ..
+drwxr-xr-x   2 aas      users        1024 Mar 16 15:47 CVS
+-rw-r--r--   1 aas      users        2384 Feb 26 21:14 Debug.pm
+-rw-r--r--   1 aas      users        2145 Feb 26 20:09 IO.pm
+-rw-r--r--   1 aas      users        3960 Mar 15 18:05 MediaTypes.pm
+-rw-r--r--   1 aas      users         792 Feb 26 20:12 MemberMixin.pm
+drwxr-xr-x   3 aas      users        1024 Mar 15 18:05 Protocol
+-rw-r--r--   1 aas      users        5613 Feb 26 20:16 Protocol.pm
+-rw-r--r--   1 aas      users        5963 Feb 26 21:27 RobotUA.pm
+-rw-r--r--   1 aas      users        5071 Mar 16 12:25 Simple.pm
+-rw-r--r--   1 aas      users        8817 Mar 15 18:05 Socket.pm
+-rw-r--r--   1 aas      users        2121 Feb  5 14:22 TkIO.pm
+-rw-r--r--   1 aas      users       19628 Mar 15 18:05 UserAgent.pm
+-rw-r--r--   1 aas      users        2841 Feb  5 19:06 media.types
+
+CVS:
+total 5
+drwxr-xr-x   2 aas      users        1024 Mar 16 15:47 .
+drwxr-xr-x   4 aas      users        1024 Mar 16 15:47 ..
+-rw-r--r--   1 aas      users         545 Mar 16 15:47 Entries
+-rw-r--r--   1 aas      users          39 Mar 10 09:05 Repository
+-rw-r--r--   1 aas      users          19 Mar 10 09:05 Root
+
+Protocol:
+total 37
+drwxr-xr-x   3 aas      users        1024 Mar 15 18:05 .
+drwxr-xr-x   4 aas      users        1024 Mar 16 15:47 ..
+drwxr-xr-x   2 aas      users        1024 Mar 15 18:05 CVS
+-rw-r--r--   1 aas      users        4646 Feb 26 20:13 file.pm
+-rw-r--r--   1 aas      users       13006 Mar 15 18:05 ftp.pm
+-rw-r--r--   1 aas      users        5935 Mar  6 10:29 gopher.pm
+-rw-r--r--   1 aas      users        5453 Mar  6 10:29 http.pm
+-rw-r--r--   1 aas      users        2365 Feb 26 20:13 mailto.pm
+
+Protocol/CVS:
+total 5
+drwxr-xr-x   2 aas      users        1024 Mar 15 18:05 .
+drwxr-xr-x   3 aas      users        1024 Mar 15 18:05 ..
+-rw-r--r--   1 aas      users         238 Mar 15 18:05 Entries
+-rw-r--r--   1 aas      users          48 Mar 10 09:05 Repository
+-rw-r--r--   1 aas      users          19 Mar 10 09:05 Root
+EOL
+
+@dir = parse_dir($dir, undef, 'unix');
+
+print int(@dir) ." lines found\n";
+@dir != 25 && print "not ";
+print "ok 1\n";
+
+for (@dir) {
+   ($name, $type, $size, $mtime, $mode) = @$_;
+   $size ||= 0;  # ensure that it is defined
+   printf "%-25s $type %6d  ", $name, $size;
+   print scalar(localtime($mtime));
+   printf "  %06o", $mode;
+   print "\n";
+}
+
+# Pick out the Socket.pm line as the sample we check carefully
+($name, $type, $size, $mtime, $mode) = @{$dir[9]};
+
+$name eq "Socket.pm" || print "not ";
+print "ok 2\n";
+
+$type eq "f" || print "not ";
+print "ok 3\n";
+
+$size == 8817 || print "not ";
+print "ok 4\n";
+
+# Must be careful when checking the time stamps because we don't know
+# which year if this script lives for a long time.
+$timestring = scalar(localtime($mtime));
+$timestring =~ /Mar\s+15\s+18:05/ or print "not ";
+print "ok 5\n";
+
+$mode == 0100644 || print "not ";
+print "ok 6\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/mediatypes.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/mediatypes.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/mediatypes.t	(revision 16561)
@@ -0,0 +1,87 @@
+use LWP::MediaTypes;
+
+require URI::URL;
+
+$url1 = new URI::URL 'http://www/foo/test.gif?search+x#frag';
+$url2 = new URI::URL 'http:test';
+
+$file = "/etc/passwd";
+-r $file or $file = "./README";
+
+@tests =
+(
+ ["/this.dir/file.html" => "text/html",],
+ ["test.gif.htm"        => "text/html",],
+ ["test.txt.gz"         => "text/plain", "gzip"],
+ ["gif.foo"             => "application/octet-stream",],
+ ["lwp-0.03.tar.Z"      => "application/x-tar", "compress"],
+ [$file		        => "text/plain",],
+ ["/random/file"        => "application/octet-stream",],
+ ["/dev/null"	        => "text/plain",],
+ [$url1	        	=> "image/gif",],
+ [$url2	        	=> "application/octet-stream",],
+ ["x.ppm.Z.UU"		=> "image/x-portable-pixmap","compress","x-uuencode",],
+);
+
+$notests = @tests + 3;
+print "1..$notests\n";
+
+if (-f "$ENV{HOME}/.mime.types") {
+   warn "
+The MediaTypes test might fail because you have a private ~/.mime.types file
+If you get a failed test, try to move it away while testing.
+";
+}
+
+
+$testno = 1;
+for (@tests) {
+    ($file, $expectedtype, @expectedEnc) = @$_;
+    $type1 = guess_media_type($file);
+    ($type, @enc) = guess_media_type($file);
+    if ($type1 ne $type) {
+       print "guess_media_type does not return same content-type in scalar and array conext.\n";
+	next;
+    }
+    $type = "undef" unless defined $type;
+    if ($type eq $expectedtype and "@enc" eq "@expectedEnc") {
+	print "ok $testno\n";
+    } else {
+	print "expected '$expectedtype' for '$file', got '$type'\n";
+	print "encoding: expected: '@expectedEnc', got '@enc'\n"
+	  if @expectedEnc || @enc;
+	print "nok ok $testno\n";
+    }
+    $testno++;
+}
+
+@imgSuffix = media_suffix('image/*');
+print "Image suffixes: @imgSuffix\n";
+
+print "\n";
+require HTTP::Response;
+$r = new HTTP::Response 200, "Document follows";
+$r->title("file.tar.gz.uu");
+guess_media_type($r->title, $r);
+print $r->as_string;
+
+print "not " unless $r->content_type eq "application/x-tar";
+print "ok $testno\n"; $testno++;
+
+@enc = $r->header("Content-Encoding");
+print "not " unless "@enc" eq "gzip x-uuencode";
+print "ok $testno\n"; $testno++;
+
+#
+use LWP::MediaTypes qw(add_type add_encoding);
+add_type("x-world/x-vrml", qw(wrl vrml));
+add_encoding("x-gzip" => "gz");
+add_encoding(rot13 => "r13");
+
+@x = guess_media_type("foo.vrml.r13.gz");
+#print "@x\n";
+print "not " unless "@x" eq "x-world/x-vrml rot13 x-gzip";
+print "ok $testno\n"; $testno++;
+
+#print LWP::MediaTypes::_dump();
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/message.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/message.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/message.t	(revision 16561)
@@ -0,0 +1,104 @@
+print "1..15\n";
+
+require HTTP::Request;
+require HTTP::Response;
+
+$req = new HTTP::Request 'GET', "http://www.sn.no/";
+$req->header(
+	"if-modified-since" => "Thu, 03 Feb 1994 00:00:00 GMT",
+	"mime-version"      => "1.0");
+
+$str = $req->as_string;
+
+print $str;
+
+$str =~ /^GET/m || print "not ";
+print "ok 1\n";
+
+$req->header("MIME-Version") eq "1.0" || print "not ";
+print "ok 2\n";
+
+$req->content("gisle");
+$req->add_content(" aas");
+$req->add_content(\ " old interface is depreciated");
+
+${$req->content_ref} =~ s/\s+is\s+depreciated//;
+
+print "Content is: ", $req->content, "\n";
+
+$req->content eq "gisle aas old interface" || print "not ";
+print "ok 3\n";
+
+$req->if_modified_since == 760233600 || print "not ";
+print "ok 4\n";
+
+$time = time;
+
+$req->date($time);
+$timestr = gmtime($time);
+($month) = ($timestr =~ /^\S+\s+(\S+)/);  # extract month;
+
+print "These should represent the same time:\n\t", $req->header('Date'), "\n\t$timestr\n";
+
+$req->header('Date') =~ /\Q$month/ || print "not ";
+print "ok 5\n";
+
+$req->authorization_basic("gisle", "passwd");
+$auth = $req->header("Authorization");
+
+print "$auth\n";
+$auth =~ /Z2lzbGU6cGFzc3dk/ || print "not ";
+print "ok 6\n";
+
+($user, $pass) = $req->authorization_basic;
+($user eq "gisle" && $pass eq "passwd") || print "not ";
+print "ok 7\n";
+
+# Check the response
+$res = new HTTP::Response 200, "This message";
+
+$html = $res->error_as_HTML;
+print $html;
+
+($html =~ /<head>/i && $html =~ /This message/) || print "not ";
+print "ok 8\n";
+
+$res->is_success || print "not ";
+print "ok 9\n";
+
+$res->content_type("text/html;version=3.0");
+$res->content("<html>...</html>\n");
+
+$res2 = $res->clone;
+
+print $res2->as_string;
+
+$res2->header("cOntent-TYPE") eq "text/html;version=3.0" || print "not ";
+print "ok 10\n";
+
+$res2->code == 200 || print "not ";
+print "ok 11\n";
+
+$res2->content =~ />\.\.\.</ || print "not ";
+print "ok 12\n";
+
+# Check the base method:
+
+$res = new HTTP::Response 200, "This message";
+$res->request($req);
+$res->content_type("image/gif");
+
+$res->base eq "http://www.sn.no/" || print "not ";
+print "ok 13\n";
+
+$res->header('Base', 'http://www.sn.no/xxx/');
+
+$res->base eq "http://www.sn.no/xxx/" || print "not ";
+print "ok 14\n";
+
+# Check the AUTLOAD delegate method with regular expressions
+"This string contains text/html" =~ /(\w+\/\w+)/;
+$res->content_type($1);
+
+$res->content_type eq "text/html" || print "not ";
+print "ok 15\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/negotiate.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/negotiate.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/negotiate.t	(revision 16561)
@@ -0,0 +1,105 @@
+print "1..3\n";
+
+use HTTP::Request;
+use HTTP::Negotiate;
+
+
+$no = 1;
+sub ok
+{
+    print "ok " . $no++ . "\n";
+}
+
+sub not_ok
+{
+    print "not ";
+    ok;
+}
+
+
+ #  ID       QS     Content-Type             Encoding     Char-Set      Lang    Size
+ $variants =
+  [
+   ['var1',  0.950, 'text/plain',           ['uuencode',
+					     'compress'], 'iso-8859-2', 'se',    400],
+   ['var2',  1.000, 'text/html;version=2.0', 'gzip',      'iso-8859-1', 'en',   3000],
+   ['var3',  0.333, 'image/gif',            undef,        undef,        undef, 43555],
+ ];
+
+
+# First we try a request with not accept headers
+$request = new HTTP::Request 'GET', 'http://localhost/';
+@a = choose($variants, $request);
+show_res(@a);
+expect(\@a, [['var2' => 1],
+	     ['var1' => 0.95],
+	     ['var3' => 0.333]
+	    ]
+);
+
+
+$a = choose($variants, $request);
+print "The chosen one is '$a'\n";
+if ($a eq 'var2') {
+    ok;
+} else {
+    not_ok;
+}
+
+#------------------
+
+$request = new HTTP::Request 'GET', 'http://localhost/';
+$request->header('Accept', 'text/plain; q=0.55, image/gif; mbx=10000');
+$request->push_header('Accept', 'text/*; q=0.25');
+$request->header('Accept-Language', 'no, en');
+$request->header('Accept-Charset', 'iso-8859-1');
+$request->header('Accept-Encoding', 'gzip');
+
+@a = choose($variants, $request);
+show_res(@a);
+expect(\@a, [['var2' => 0.25],
+	     ['var1' => 0],
+	     ['var3' => 0]
+	    ]
+);
+
+
+
+
+#------------------
+
+sub expect
+{
+    my($res, $exp) = @_;
+    do {
+	$a = shift @$res;
+	$b = shift @$exp;
+	last if defined($a) ne defined($b);
+	if (defined($a)) {
+	    ($va, $qa) = @$a;
+	    ($vb, $qb) = @$b;
+	    if ($va ne $vb) {
+		print "$va == $vb ?\n";
+		not_ok;
+		return;
+	    }
+	    if (abs($qa - $qb) > 0.002) {
+		print "$qa ~= $qb ?\n";
+		not_ok;
+		return;
+	    }
+	}
+
+    } until (!defined($a) || !defined($b));
+    return not_ok if defined($a) ne defined($b);
+    ok;
+}
+
+sub show_res
+{
+    print "-------------\n";
+    for (@_) {
+	printf "%-6s %.3f\n", @$_;
+    }
+    print "-------------\n";
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/response.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/response.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/response.t	(revision 16561)
@@ -0,0 +1,86 @@
+
+# Test extra HTTP::Response methods.  Basic operation is tested in the
+# message.t test suite.
+
+
+print "1..7\n";
+
+
+use HTTP::Date;
+use HTTP::Request;
+use HTTP::Response;
+
+my $time = time;
+
+$req = HTTP::Request->new(GET => 'http://www.sn.no');
+$req->date($time - 30);
+
+$r = new HTTP::Response 200, "OK";
+$r->client_date($time - 20);
+$r->date($time - 25);
+$r->last_modified($time - 5000000);
+$r->request($req);
+
+print $r->as_string;
+
+$current_age = $r->current_age;
+
+if ($current_age < 35  || $current_age > 40) {
+    print "not ";
+}
+print "ok 1\n";
+
+$freshness_lifetime = $r->freshness_lifetime;
+if ($freshness_lifetime < 12 * 3600) {
+    print "not ";
+}
+print "ok 2\n";
+
+$is_fresh = $r->is_fresh;
+
+print "not " unless $is_fresh;
+print "ok 3\n";
+
+print "current_age        = $current_age\n";
+print "freshness_lifetime = $freshness_lifetime\n";
+print "response is ";
+print "not " unless $is_fresh;
+print "fresh\n";
+
+
+print "it will be fresh is ";
+print $freshness_lifetime - $current_age;
+print " more seconds\n";
+
+# OK, now we add an Expires header
+$r->expires($time);
+print $r->as_string;
+
+$freshness_lifetime = $r->freshness_lifetime;
+print "freshness_lifetime = $freshness_lifetime\n";
+print "not " unless $freshness_lifetime == 25;
+print "ok 4\n";
+$r->remove_header('expires');
+
+# Now we try the 'Age' header and the Cache-Contol:
+
+$r->header('Age', 300);
+$r->push_header('Cache-Control', 'junk');
+$r->push_header(Cache_Control => 'max-age = 10');
+
+print $r->as_string;
+
+$current_age = $r->current_age;
+$freshness_lifetime = $r->freshness_lifetime;
+
+print "current_age        = $current_age\n";
+print "freshness_lifetime = $freshness_lifetime\n";
+
+print "not " if $current_age < 300;
+print "ok 5\n";
+
+print "not " if $freshness_lifetime != 10;
+print "ok 6\n";
+
+print "not " unless $r->fresh_until;  # should return something
+print "ok 7\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/status.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/status.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/base/status.t	(revision 16561)
@@ -0,0 +1,32 @@
+use HTTP::Status;
+
+print "1..8\n";
+
+200 == RC_OK || print "not ";
+print "ok 1\n";
+
+is_success(RC_ACCEPTED) || print "not ";
+print "ok 2\n";
+
+is_error(RC_BAD_REQUEST) || print "not ";
+print "ok 3\n";
+
+is_redirect(RC_MOVED_PERMANENTLY) || print "not ";
+print "ok 4\n";
+
+is_success(RC_NOT_FOUND) && print "not ";
+print "ok 5\n";
+
+$mess = status_message(0);
+
+defined $mess && print "not ";
+print "ok 6\n";
+
+$mess = status_message(200);
+
+if ($mess =~ /ok/i) {
+    print "ok 7\n";
+}
+
+is_info(RC_CONTINUE) || print "not ";
+print "ok 8\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/autoload.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/autoload.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/autoload.t	(revision 16561)
@@ -0,0 +1,30 @@
+#
+# See if autoloading of protocol schemes work
+#
+
+print "1..1\n";
+
+require LWP::UserAgent;
+# note no LWP::Protocol::file;
+
+# localhost will not work on win32 (when networking is disabled)
+$url = $^O eq 'MSWin32' ? 'file:.' : 'file://localhost/';
+
+print "Trying to fetch " . (new URI::URL $url)->local_path . " ...\n";
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+$ua->timeout(30);               # timeout in seconds
+#$ua->use_alarm(0);               # don't use alarms
+#$ua->use_eval(0);                # don't eval, just die when thing go wrong
+				# (easier to read while debugging)
+
+my $request = new HTTP::Request('GET', $url);
+
+my $response = $ua->request($request, undef, undef);
+if ($response->is_success) {
+    print "ok 1\n";
+} else {
+    print $response->error_as_HTML;
+    print "not ok 1\n";
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/get.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/get.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/get.t	(revision 16561)
@@ -0,0 +1,85 @@
+#
+# Test retrieving a file with a 'file://' URL,
+#
+
+# First locate some suitable tmp-dir.  We need an absolute path.
+$TMPDIR = undef;
+for ("/tmp/", "/var/tmp", "/usr/tmp", "/local/tmp") {
+    if (open(TEST, ">$_/test-$$")) {
+        close(TEST);
+	unlink("$_/test-$$");
+	$TMPDIR = $_;
+	last;
+    }
+}
+$TMPDIR ||= $ENV{TEMP} if $^O eq 'MSWin32';
+unless ($TMPDIR) {
+   # Can't run any tests
+   print "1..0\n";
+   print "ok 1\n";
+   exit;
+}
+$TMPDIR =~ tr|\\|/|;
+print "1..2\n";
+
+use LWP::Simple;
+require LWP::Protocol::file;
+
+my $orig = "$TMPDIR/lwp-orig-$$";          # local file
+my $copy = "$TMPDIR/lwp-copy-$$"; 	    # downloaded copy
+
+# First we create the original
+open(OUT, ">$orig") or die "Cannot open $orig: $!";
+for (1..100) {
+    print OUT "This is line $_ of $orig\n";
+}
+close(OUT);
+
+
+# Then we make a test using getprint(), so we need to capture stdout
+open (OUT, ">$copy") or die "Cannot open $copy: $!";
+select(OUT);
+
+# do the retrieval
+getprint("file://localhost" . ($orig =~ m|^/| ? $orig : "/$orig"));
+
+close(OUT);
+select(STDOUT);
+
+# read and compare the files
+open(IN, $orig) or die "Cannot open '$orig': $!";
+undef($/);
+$origtext = <IN>;
+close(IN);
+open(IN, $copy) or die "Cannot open '$copy': $!";
+undef($/);
+$copytext = <IN>;
+close(IN);
+
+unlink($copy);
+
+if ($origtext eq $copytext) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+
+# Test getstore() function
+
+getstore("file:$orig", $copy);
+
+# Take a look at the new copy
+open(IN, $copy) or die "Cannot open '$copy': $!";
+undef($/);
+$copytext = <IN>;
+close(IN);
+
+unlink($orig);
+unlink($copy);
+
+if ($origtext eq $copytext) {
+    print "ok 2\n";
+} else {
+    print "not ok 2\n";
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/http.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/http.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/http.t	(revision 16561)
@@ -0,0 +1,319 @@
+$| = 1; # autoflush
+
+require IO::Socket;  # make sure this work before we try to make a HTTP::Daemon
+
+# First we make ourself a daemon in another process
+my $D = shift || '';
+if ($D eq 'daemon') {
+
+    require HTTP::Daemon;
+
+    my $d = new HTTP::Daemon Timeout => 10;
+
+    print "Please to meet you at: <URL:", $d->url, ">\n";
+    open(STDOUT, ">/dev/null");
+
+    while ($c = $d->accept) {
+	$r = $c->get_request;
+	if ($r) {
+	    my $p = ($r->url->path_components)[1];
+	    my $func = lc("httpd_" . $r->method . "_$p");
+	    if (defined &$func) {
+		&$func($c, $r);
+	    } else {
+		$c->send_error(404);
+	    }
+	}
+	$c = undef;  # close connection
+    }
+    print STDERR "HTTP Server terminated\n";
+    exit;
+}
+else {
+    use Config;
+    open(DAEMON, "$Config{'perlpath'} local/http.t daemon |") or die "Can't exec daemon: $!";
+}
+
+print "1..18\n";
+
+
+my $greeting = <DAEMON>;
+$greeting =~ /(<[^>]+>)/;
+
+require URI::URL;
+URI::URL->import;
+my $base = new URI::URL $1;
+
+print "Will access HTTP server at $base\n";
+
+require LWP::UserAgent;
+require HTTP::Request;
+$ua = new LWP::UserAgent;
+$ua->agent("Mozilla/0.01 " . $ua->agent);
+$ua->from('gisle@aas.no');
+
+#----------------------------------------------------------------
+print "Bad request...\n";
+$req = new HTTP::Request GET => url("/not_found", $base);
+$req->header(X_Foo => "Bar");
+$res = $ua->request($req);
+
+print "not " unless $res->is_error
+                and $res->code == 404
+                and $res->message =~ /not\s+found/i;
+print "ok 1\n";
+# we also expect a few headers
+print "not " if !$res->server and !$res->date;
+print "ok 2\n";
+
+#----------------------------------------------------------------
+print "Simple echo...\n";
+sub httpd_get_echo
+{
+    my($c, $req) = @_;
+    $c->send_basic_header(200);
+    print $c "Content-Type: text/plain\015\012";
+    $c->send_crlf;
+    print $c $req->as_string;
+}
+
+$req = new HTTP::Request GET => url("/echo/path_info?query", $base);
+$req->push_header(Accept => 'text/html');
+$req->push_header(Accept => 'text/plain; q=0.9');
+$req->push_header(Accept => 'image/*');
+$req->if_modified_since(time - 300);
+$req->header(Long_text => 'This is a very long header line
+which is broken between
+more than one line.');
+$req->header(X_Foo => "Bar");
+
+$res = $ua->request($req);
+#print $res->as_string;
+
+print "not " unless $res->is_success
+               and  $res->code == 200 && $res->message eq "OK";
+print "ok 3\n";
+
+$_ = $res->content;
+@accept = /^Accept:\s*(.*)/mg;
+
+print "not " unless /^From:\s*gisle\@aas\.no$/m
+                and /^Host:/m
+                and @accept == 3
+	        and /^Accept:\s*text\/html/m
+	        and /^Accept:\s*text\/plain/m
+	        and /^Accept:\s*image\/\*/m
+		and /^If-Modified-Since:\s*\w{3},\s+\d+/m
+                and /^Long-Text:\s*This.*broken between/m
+		and /^X-Foo:\s*Bar$/m
+		and /^User-Agent:\s*Mozilla\/0.01/m;
+print "ok 4\n";
+
+#----------------------------------------------------------------
+print "Send file...\n";
+
+my $file = "test-$$.html";
+open(FILE, ">$file") or die "Can't create $file: $!";
+binmode FILE or die "Can't binmode $file: $!";
+print FILE <<EOT;
+<html><title>En prøve</title>
+<h1>Dette er en testfil</h1>
+Jeg vet ikke hvor stor fila behøver å være heller, men dette
+er sikkert nok i massevis.
+EOT
+close(FILE);
+
+sub httpd_get_file
+{
+    my($c, $r) = @_;
+    my %form = $r->url->query_form;
+    my $file = $form{'name'};
+    $c->send_file_response($file);
+    unlink($file);
+}
+
+$req = new HTTP::Request GET => url("/file?name=$file", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+
+print "not " unless $res->is_success
+                and $res->content_type eq 'text/html'
+		and $res->content_length == 147
+		and $res->title eq 'En prøve'
+		and $res->content =~ /å være/;
+print "ok 5\n";		
+
+
+# A second try on the same file, should fail because we unlink it
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->is_error
+                and $res->code == 404;   # not found
+print "ok 6\n";
+		
+# Then try to list current directory
+$req = new HTTP::Request GET => url("/file?name=.", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->code == 501;   # NYI
+print "ok 7\n";
+
+
+#----------------------------------------------------------------
+print "Check redirect...\n";
+sub httpd_get_redirect
+{
+   my($c) = @_;
+   $c->send_redirect("/echo/redirect");
+}
+
+$req = new HTTP::Request GET => url("/redirect/foo", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+
+print "not " unless $res->is_success
+                and $res->content =~ m|/echo/redirect|;
+print "ok 8\n";
+print "not " unless $res->previous->is_redirect
+                and $res->previous->code == 301;
+print "ok 9\n";
+
+# Let's test a redirect loop too
+sub httpd_get_redirect2 { shift->send_redirect("/redirect3/") }
+sub httpd_get_redirect3 { shift->send_redirect("/redirect4/") }
+sub httpd_get_redirect4 { shift->send_redirect("/redirect5/") }
+sub httpd_get_redirect5 { shift->send_redirect("/redirect6/") }
+sub httpd_get_redirect6 { shift->send_redirect("/redirect2/") }
+
+$req->url(url("/redirect2", $base));
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->is_redirect
+                and $res->header("Client-Warning") =~ /loop detected/i;
+print "ok 10\n";
+$i = 1;
+while ($res->previous) {
+   $i++;
+   $res = $res->previous;
+}
+print "not " unless $i == 6;
+print "ok 11\n";
+
+#----------------------------------------------------------------
+print "Check basic authorization...\n";
+sub httpd_get_basic
+{
+    my($c, $r) = @_;
+    #print STDERR $r->as_string;
+    my($u,$p) = $r->authorization_basic;
+    if (defined($u) && $u eq 'ok 12' && $p eq 'xyzzy') {
+        $c->send_basic_header(200);
+	print $c "Content-Type: text/plain";
+	$c->send_crlf;
+	$c->send_crlf;
+	$c->print("$u\n");
+    } else {
+        $c->send_basic_header(401);
+	$c->print("WWW-Authenticate: Basic realm=\"libwww-perl\"\015\012");
+	$c->send_crlf;
+    }
+}
+
+{
+   package MyUA; @ISA=qw(LWP::UserAgent);
+   sub get_basic_credentials {
+      my($self, $realm, $uri, $proxy) = @_;
+      my $x =  $uri->rel($base);
+      if ($realm eq "libwww-perl" && $uri->rel($base) eq "basic") {
+	  return ("ok 12", "xyzzy");
+      } else {
+          return undef;
+      }
+   }
+}
+$req = new HTTP::Request GET => url("/basic", $base);
+$res = MyUA->new->request($req);
+#print $res->as_string;
+
+print "not " unless $res->is_success;
+print $res->content;
+
+# Let's try with a $ua that does not pass out credentials
+$res = $ua->request($req);
+print "not " unless $res->code == 401;
+print "ok 13\n";
+
+# Let's try to set credentials for this realm
+$ua->credentials($req->url->netloc, "libwww-perl", "ok 12", "xyzzy");
+$res = $ua->request($req);
+print "not " unless $res->is_success;
+print "ok 14\n";
+
+# Then illegal credentials
+$ua->credentials($req->url->netloc, "libwww-perl", "user", "passwd");
+$res = $ua->request($req);
+print "not " unless $res->code == 401;
+print "ok 15\n";
+
+
+#----------------------------------------------------------------
+print "Check proxy...\n";
+sub httpd_get_proxy
+{
+   my($c,$r) = @_;
+   if ($r->method eq "GET" and
+       $r->url->scheme eq "ftp") {
+       $c->send_basic_header(200);
+       $c->send_crlf;
+   } else {
+       $c->send_error;
+   }
+}
+
+$ua->proxy(ftp => $base);
+$req = new HTTP::Request GET => "ftp://ftp.perl.com/proxy";
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->is_success;
+print "ok 16\n";
+
+#----------------------------------------------------------------
+print "Check POSTing...\n";
+sub httpd_post_echo
+{
+   my($c,$r) = @_;
+   $c->send_basic_header;
+   $c->print("Content-Type: text/plain");
+   $c->send_crlf;
+   $c->send_crlf;
+   $c->print($r->as_string);
+}
+
+$req = new HTTP::Request POST => url("/echo/foo", $base);
+$req->content_type("application/x-www-form-urlencoded");
+$req->content("foo=bar&bar=test");
+$res = $ua->request($req);
+#print $res->as_string;
+
+$_ = $res->content;
+print "not " unless $res->is_success
+                and /^Content-Length:\s*16$/mi
+		and /^Content-Type:\s*application\/x-www-form-urlencoded$/mi
+		and /^foo=bar&bar=test/m;
+print "ok 17\n";		
+
+#----------------------------------------------------------------
+print "Terminating server...\n";
+sub httpd_get_quit
+{
+    my($c) = @_;
+    $c->send_error(503, "Bye, bye");
+    exit;  # terminate HTTP server
+}
+
+$req = new HTTP::Request GET => url("/quit", $base);
+$res = $ua->request($req);
+
+print "not " unless $res->code == 503 and $res->content =~ /Bye, bye/;
+print "ok 18\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/protosub.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/protosub.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/local/protosub.t	(revision 16561)
@@ -0,0 +1,73 @@
+#!perl
+
+print "1..6\n";
+
+# This test tries to make a custom protocol implementation by
+# subclassing of LWP::Protocol.
+
+
+use LWP::UserAgent ();
+use LWP::Protocol ();
+
+LWP::Protocol::implementor(http => 'myhttp');
+
+$ua = LWP::UserAgent->new;
+$ua->proxy('ftp' => "http://www.sn.no/");
+
+$req = HTTP::Request->new(GET => 'ftp://foo/');
+$req->header(Cookie => "perl=cool");
+
+$res = $ua->request($req);
+
+print $res->as_string;
+
+print "not " unless $res->code == 200;
+print "ok 5\n";
+print "not " unless $res->content eq "Howdy\n";
+print "ok 6\n";
+exit;
+
+
+#----------------------------------
+package myhttp;
+
+BEGIN {
+   @ISA=qw(LWP::Protocol);
+}
+
+sub new
+{
+    my $class = shift;
+    print "CTOR: $class->new(@_)\n";
+    my($prot) = @_;
+    print "not " unless $prot eq "http";
+    print "ok 1\n";
+    my $self = $class->SUPER::new(@_);
+    for (keys %$self) {
+	print "$_: $self->{$_}\n";
+    }
+    $self;
+}
+
+sub request
+{
+    my $self = shift;
+    print "REQUEST: $self->request(",
+       join(",", (map defined($_)? $_ : "UNDEF", @_)), ")\n";
+
+    my($request, $proxy, $arg, $size, $timeout) = @_;
+    print $request->as_string;
+
+    print "not " unless $proxy eq "http://www.sn.no/";
+    print "ok 2\n";
+    print "not " unless $request->url eq "ftp://foo/";
+    print "ok 3\n";
+    print "not " unless $request->header("cookie") eq "perl=cool";
+    print "ok 4\n";
+
+    my $res = HTTP::Response->new(200 => "OK");
+    $res->content_type("text/plain");
+    $res->date(time);
+    $self->collect_once($arg, $res, "Howdy\n");
+    $res;
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/moved
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/moved	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/moved	(revision 16561)
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo "Location: http://$SERVER_NAME:$SERVER_PORT/"
+echo
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/nph-slowdata
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/nph-slowdata	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/nph-slowdata	(revision 16561)
@@ -0,0 +1,26 @@
+#!/usr/local/bin/perl
+
+# This script outputs some data slowly. It can be used to check that
+# pipelined processing of response content really works.  We use syswrite
+# so that the script will notice when the connection is broken.
+
+out("HTTP/1.0 200 OK\r
+Content-Type: text/plain\r
+\r
+");
+
+for (1..5) {
+    out("The number is now $_\n");
+    sleep(1);
+}
+exit;
+
+
+sub out
+{
+    my $data = shift;
+    my $l = length $data;
+    if (syswrite(STDOUT, $data, $l) != $l) {
+	exit 1;
+    }
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/slowread
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/slowread	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/slowread	(revision 16561)
@@ -0,0 +1,31 @@
+#!/usr/local/bin/perl
+
+# You might post large amount of data to this script.  It will read
+# it slowly.
+
+{ local($!) = 1; print "Content-Type: text/plain\n\n"; }
+
+$len = $ENV{CONTENT_LENGTH};
+
+unless ($len) {
+    system "env";
+    exit;
+}
+
+$size = 20;  # chunk size
+
+$content = '';
+$bytes = 0;
+
+sleep(1);
+while ($len > 0) {
+    $n = sysread(STDIN, $b, $size);
+    last if $n <= 0;
+    $len -= $n;
+    $bytes += $n;
+    $content .= $b;
+    sleep(1);
+}
+print "$bytes bytes read\n";
+
+	
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/test
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/test	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/test	(revision 16561)
@@ -0,0 +1,28 @@
+#!/usr/local/bin/perl
+
+$| = 1;
+print "Content-type: text/plain
+
+";
+
+if (@ARGV) {
+    print "ARGS: ";
+    print join(", ", map { $_ = qq{"$_"} } @ARGV);
+    print "\n\n";
+} else {
+    print "No command line arguments passed to script\n\n";
+}
+
+while (($key,$val) = each %ENV) {
+   print "$key=$val\n";
+}
+
+if ($ENV{CONTENT_LENGTH}) {
+    $len = $ENV{CONTENT_LENGTH};
+    while ($len) {
+	$n = sysread(STDIN, $content, $len, length $content);
+	last unless defined $n;
+	$len -= $n;
+    }
+    print "\nContent\n-------\n$content";
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/timeout
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/timeout	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/cgi-bin/timeout	(revision 16561)
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+sleep 20;
+
+echo "Content-type: text/plain"
+echo
+echo "Test"
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/config.pl.dist
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/config.pl.dist	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/config.pl.dist	(revision 16561)
@@ -0,0 +1,10 @@
+package net;
+
+# Configure these for you local system
+$httpserver  = "localhost:80";
+$cgidir      = "/cgi-bin/lwp";
+
+# Used for proxy test
+$ftp_proxy = "http://localhost/";
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-get.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-get.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-get.t	(revision 16561)
@@ -0,0 +1,43 @@
+#!/usr/local/bin/perl -w
+#
+# Check GET via HTTP.
+#
+
+print "1..2\n";
+
+require "net/config.pl";
+require LWP::Protocol::http;
+require LWP::UserAgent;
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+$netloc = $net::httpserver;
+$script = $net::cgidir . "/test";
+
+$url = new URI::URL("http://$netloc$script?query");
+
+my $request = new HTTP::Request('GET', $url);
+
+print "GET $url\n\n";
+
+my $response = $ua->request($request, undef, undef);
+
+my $str = $response->as_string;
+
+print "$str\n";
+
+if ($response->is_success and $str =~ /^REQUEST_METHOD=GET$/m) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+if ($str =~ /^QUERY_STRING=query$/m) {
+    print "ok 2\n";
+} else {
+    print "not ok 2\n";
+}
+
+# avoid -w warning
+$dummy = $net::httpserver;
+$dummy = $net::cgidir;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-post.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-post.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-post.t	(revision 16561)
@@ -0,0 +1,44 @@
+#!/usr/local/bin/perl -w
+#
+# Check POST via HTTP.
+#
+
+print "1..2\n";
+
+require "net/config.pl";
+require LWP::Protocol::http;
+require LWP::UserAgent;
+
+$netloc = $net::httpserver;
+$script = $net::cgidir . "/test";
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+$url = new URI::URL("http://$netloc$script");
+
+my $form = 'searchtype=Substring';
+
+my $request = new HTTP::Request('POST', $url, undef, $form);
+$request->header('Content-Type', 'application/x-www-form-urlencoded');
+
+my $response = $ua->request($request, undef, undef);
+
+my $str = $response->as_string;
+
+print "$str\n";
+
+if ($response->is_success and $str =~ /^REQUEST_METHOD=POST$/m) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+if ($str =~ /^CONTENT_LENGTH=(\d+)$/m && $1 == length($form)) {
+    print "ok 2\n";
+} else {
+    print "not ok 2\n";
+}
+
+# avoid -w warning
+$dummy = $net::httpserver;
+$dummy = $net::cgidir;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-timeout.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-timeout.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/http-timeout.t	(revision 16561)
@@ -0,0 +1,41 @@
+#
+# Check timeouts via HTTP.
+#
+
+print "1..1\n";
+
+require "net/config.pl";
+require HTTP::Status;
+require LWP::Protocol::http;
+require LWP::UserAgent;
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+$ua->timeout(4);
+
+$netloc = $net::httpserver;
+$script = $net::cgidir . "/timeout";
+
+$url = new URI::URL("http://$netloc$script");
+
+my $request = new HTTP::Request('GET', $url);
+
+print $request->as_string;
+
+my $response = $ua->request($request, undef);
+
+my $str = $response->as_string;
+
+print "$str\n";
+
+if ($response->is_error and
+    $str =~ /timeout/) {
+    print "ok 1\n";
+}
+else {
+    print "nok ok 1\n";
+}
+
+# avoid -w warning
+$dummy = $net::httpserver;
+$dummy = $net::cgidir;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/mirror.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/mirror.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/mirror.t	(revision 16561)
@@ -0,0 +1,34 @@
+#
+# Test mirroring a file
+#
+
+require "net/config.pl";
+require LWP::Protocol::http;
+require LWP::UserAgent;
+require HTTP::Status;
+
+print "1..2\n";
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+my $url = "http://$net::httpserver/";
+my $copy = "lwp-test-$$"; # downloaded copy
+
+my $response = $ua->mirror($url, $copy);
+
+if ($response->code == &HTTP::Status::RC_OK) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+# OK, so now do it again, should get Not-Modified
+$response = $ua->mirror($url, $copy);
+if ($response->code == &HTTP::Status::RC_NOT_MODIFIED) {
+    print "ok 2\n";
+} else {
+    print "nok ok 2\n";
+}
+unlink($copy);
+
+$net::httpserver = $net::httpserver;  # avoid -w warning
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/moved.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/moved.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/moved.t	(revision 16561)
@@ -0,0 +1,34 @@
+#!/usr/local/bin/perl -w
+#
+
+print "1..1\n";
+
+require "net/config.pl";
+require LWP::Debug;
+require LWP::UserAgent;
+
+$url = "http://$net::httpserver$net::cgidir/moved";
+
+#LWP::Debug::level('+trace');
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+$ua->timeout(30);               # timeout in seconds
+
+my $request = new HTTP::Request('GET', $url);
+
+print $request->as_string;
+
+my $response = $ua->request($request, undef, undef);
+
+print $response->as_string, "\n";
+
+if ($response->is_success) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
+
+
+# avoid -w warning
+$dummy = $net::httpserver;
+$dummy = $net::cgidir;
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/proxy.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/proxy.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/net/proxy.t	(revision 16561)
@@ -0,0 +1,36 @@
+#!/usr/local/bin/perl -w
+#
+# Test retrieving a file with a 'ftp://' URL,
+# via a HTTP proxy.
+#
+
+print "1..1\n";
+
+require "net/config.pl";
+unless (defined $net::ftp_proxy) {
+    print "not ok 1\n";
+    exit 0;
+}
+
+require LWP::Debug;
+require LWP::UserAgent;
+
+#LWP::Debug::level('+');
+
+my $ua = new LWP::UserAgent;    # create a useragent to test
+
+$ua->proxy('ftp', $net::ftp_proxy);
+
+my $url = new URI::URL('ftp://ftp.uninett.no/');
+
+my $request = new HTTP::Request('GET', $url);
+
+my $response = $ua->request($request, undef, undef);
+
+my $str = $response->as_string;
+
+if ($response->is_success) {
+    print "ok 1\n";
+} else {
+    print "not ok 1\n";
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules-dbm.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules-dbm.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules-dbm.t	(revision 16561)
@@ -0,0 +1,128 @@
+
+print "1..13\n";
+
+
+use WWW::RobotRules::AnyDBM_File;
+
+$file = "test-$$";
+
+$r = new WWW::RobotRules::AnyDBM_File "myrobot/2.0", $file;
+
+$r->parse("http://www.aas.no/robots.txt", "");
+
+$r->visit("www.aas.no");
+
+print "not " if $r->no_visits("www.aas.no") != 1;
+print "ok 1\n";
+
+
+$r->push_rules("www.sn.no", "/aas", "/per");
+$r->push_rules("www.sn.no", "/god", "/old");
+
+@r = $r->rules("www.sn.no");
+print "Rules: @r\n";
+
+print "not " if "@r" ne "/aas /per /god /old";
+print "ok 2\n";
+
+$r->clear_rules("per");
+$r->clear_rules("www.sn.no");
+
+@r = $r->rules("www.sn.no");
+print "Rules: @r\n";
+
+print "not " if "@r" ne "";
+print "ok 3\n";
+
+$r->visit("www.aas.no", time+10);
+$r->visit("www.sn.no");
+
+print "No visits: ", $r->no_visits("www.aas.no"), "\n";
+print "Last visit: ", $r->last_visit("www.aas.no"), "\n";
+print "Fresh until: ", $r->fresh_until("www.aas.no"), "\n";
+
+print "not " if $r->no_visits("www.aas.no") != 2;
+print "ok 4\n";
+
+print "not " if abs($r->last_visit("www.sn.no") - time) > 2;
+print "ok 5\n";
+
+$r = undef;
+
+# Try to reopen the database without a name specified
+$r = new WWW::RobotRules::AnyDBM_File undef, $file;
+$r->visit("www.aas.no");
+
+print "not " if $r->no_visits("www.aas.no") != 3;
+print "ok 6\n";
+
+print "Agent-Name: ", $r->agent, "\n";
+print "not " if $r->agent ne "myrobot";
+print "ok 7\n";
+
+$r = undef;
+
+print "*** Dump of database ***\n";
+tie(%cat, AnyDBM_File, $file, 0, 0644) or die "Can't tie: $!";
+while (($key,$val) = each(%cat)) {
+    print "$key\t$val\n";
+}
+print "******\n";
+
+untie %cat;
+
+# Try to open database with a different agent name
+$r = new WWW::RobotRules::AnyDBM_File "MOMSpider/2.0", $file;
+
+print "not " if $r->no_visits("www.sn.no");
+print "ok 8\n";
+
+# Try parsing
+$r->parse("http://www.sn.no:8080/robots.txt", <<EOT, (time + 3));
+
+User-Agent: *
+Disallow: /
+
+User-Agent: Momspider
+Disallow: /foo
+Disallow: /bar
+
+EOT
+
+@r = $r->rules("www.sn.no:8080");
+print "not " if "@r" ne "/foo /bar";
+print "ok 9\n";
+
+print "not " if $r->allowed("http://www.sn.no") >= 0;
+print "ok 10\n";
+
+print "not " if $r->allowed("http://www.sn.no:8080/foo/gisle");
+print "ok 11\n";
+
+sleep(5);  # wait until file has expired
+print "not " if $r->allowed("http://www.sn.no:8080/foo/gisle") >= 0;
+print "ok 12\n";
+
+
+$r = undef;
+
+print "*** Dump of database ***\n";
+tie(%cat, AnyDBM_File, $file, 0, 0644) or die "Can't tie: $!";
+while (($key,$val) = each(%cat)) {
+    print "$key\t$val\n";
+}
+print "******\n";
+
+untie %cat;			# Otherwise the next line fails on DOSish
+
+unlink "$file", "$file.pag", "$file.dir", "$file.db";
+
+# Try open a an emty database without specifying a name
+eval { 
+   $r = new WWW::RobotRules::AnyDBM_File undef, $file;
+};
+print $@;
+print "not " unless $@;  # should fail
+print "ok 13\n";
+
+unlink "$file", "$file.pag", "$file.dir", "$file.db";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/rules.t	(revision 16561)
@@ -0,0 +1,138 @@
+#!/local/bin/perl
+
+=head1 NAME
+
+robot-rules.t
+
+=head1 DESCRIPTION
+
+Test a number of different A</robots.txt> files against a number
+of different User-agents.
+
+=cut
+
+require WWW::RobotRules;
+use Carp;
+use strict;
+
+print "1..26\n"; # for Test::Harness
+
+# We test a number of different /robots.txt files,
+#
+
+my $content1 = <<EOM;
+# http://foo/robots.txt
+User-agent: *
+Disallow: /private
+Disallow: http://foo/also_private
+
+User-agent: MOMspider
+Disallow:
+EOM
+
+my $content2 = <<EOM;
+# http://foo/robots.txt
+User-agent: MOMspider
+ # comment which should be ignored
+Disallow: /private
+EOM
+
+my $content3 = <<EOM;
+# http://foo/robots.txt
+EOM
+
+my $content4 = <<EOM;
+# http://foo/robots.txt
+User-agent: *
+Disallow: /private
+
+User-agent: MOMspider
+Disallow: /this
+
+User-agent: Another
+Disallow: /that
+EOM
+
+# and a number of different robots:
+
+my @tests1 = (
+	   [$content1, 'MOMspider' =>
+	    1 => 'http://foo/private' => 1,
+	    2 => 'http://foo/also_private' => 1,
+	   ],
+
+	   [$content1, 'Wubble' =>
+	    3 => 'http://foo/private' => 0,
+	    4 => 'http://foo/also_private' => 0,
+	    5 => 'http://foo/other' => 1,
+	   ],
+
+	   [$content2, 'MOMspider' =>
+	    6 => 'http://foo/private' => 0,
+	    7 => 'http://foo/other' => 1,
+	   ],
+
+	   [$content2, 'Wubble' =>
+	    8  => 'http://foo/private' => 1,
+	    9  => 'http://foo/also_private' => 1,
+	    10 => 'http://foo/other' => 1,
+	   ],
+
+	   [$content3, 'MOMspider' =>
+	    11 => 'http://foo/private' => 1,
+	    12 => 'http://foo/other' => 1,
+	   ],
+
+	   [$content3, 'Wubble' =>
+	    13 => 'http://foo/private' => 1,
+	    14 => 'http://foo/other' => 1,
+	   ],
+
+	   [$content4, 'MOMspider' =>
+	    15 => 'http://foo/private' => 1,
+	    16 => 'http://foo/this' => 0,
+	    17 => 'http://foo/that' => 1,
+	   ],
+
+	   [$content4, 'Another' =>
+	    18 => 'http://foo/private' => 1,
+	    19 => 'http://foo/this' => 1,
+	    20 => 'http://foo/that' => 0,
+	   ],
+
+	   [$content4, 'Wubble' =>
+	    21 => 'http://foo/private' => 0,
+	    22 => 'http://foo/this' => 1,
+	    23 => 'http://foo/that' => 1,
+	   ],
+
+	   [$content4, 'Another/1.0' =>
+	    24 => 'http://foo/private' => 1,
+	    25 => 'http://foo/this' => 1,
+	    26 => 'http://foo/that' => 0,
+	   ],
+
+	   # when adding tests, remember to increase
+	   # the maximum at the top
+
+	  );
+
+my $t;
+
+for $t (@tests1) {
+    my ($content, $ua) = splice(@$t, 0, 2);
+
+    my $robotsrules = new WWW::RobotRules($ua);
+    $robotsrules->parse('http://foo/robots.txt', $content);
+
+    my ($num, $path, $expected);
+    while(($num, $path, $expected) = splice(@$t, 0, 3)) {
+	my $allowed = $robotsrules->allowed($path);
+	$allowed = 1 if $allowed;
+	if($allowed != $expected) {
+	    $robotsrules->dump;
+	    confess "Test Failed: $ua => $path ($allowed != $expected)";
+	}
+	print "ok $num\n";
+    }
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/ua.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/ua.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/robot/ua.t	(revision 16561)
@@ -0,0 +1,135 @@
+$| = 1; # autoflush
+
+require IO::Socket;  # make sure this work before we try to make a HTTP::Daemon
+
+# First we make ourself a daemon in another process
+my $D = shift || '';
+if ($D eq 'daemon') {
+
+    require HTTP::Daemon;
+
+    my $d = new HTTP::Daemon Timeout => 10;
+
+    print "Please to meet you at: <URL:", $d->url, ">\n";
+    open(STDOUT, $^O eq 'MSWin32' ? ">nul" : ">/dev/null");
+
+    while ($c = $d->accept) {
+	$r = $c->get_request;
+	if ($r) {
+	    my $p = ($r->url->path_components)[1];
+	    $p =~ s/\W//g;
+	    my $func = lc("httpd_" . $r->method . "_$p");
+	    #print STDERR "Calling $func...\n";
+            if (defined &$func) {
+		&$func($c, $r);
+	    } else {
+		$c->send_error(404);
+	    }
+	}
+	$c = undef;  # close connection
+    }
+    print STDERR "HTTP Server terminated\n";
+    exit;
+}
+else {
+    use Config;
+    open(DAEMON , "$Config{'perlpath'} robot/ua.t daemon |") or die "Can't exec daemon: $!";
+}
+
+print "1..7\n";
+
+
+$greating = <DAEMON>;
+$greating =~ /(<[^>]+>)/;
+
+require URI::URL;
+URI::URL->import;
+my $base = new URI::URL $1;
+
+print "Will access HTTP server at $base\n";
+
+require LWP::RobotUA;
+require HTTP::Request;
+$ua = new LWP::RobotUA 'lwp-spider/0.1', 'gisle@aas.no';
+$ua->delay(0.05);  # rather quick robot
+
+#----------------------------------------------------------------
+sub httpd_get_robotstxt
+{
+   my($c,$r) = @_;
+   $c->send_basic_header;
+   $c->print("Content-Type: text/plain");
+   $c->send_crlf;
+   $c->send_crlf;
+   $c->print("User-Agent: *
+Disallow: /private
+
+");
+}
+
+sub httpd_get_someplace
+{
+   my($c,$r) = @_;
+   $c->send_basic_header;
+   $c->print("Content-Type: text/plain");
+   $c->send_crlf;
+   $c->send_crlf;
+   $c->print("Okidok\n");
+}
+
+$req = new HTTP::Request GET => url("/someplace", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->is_success;
+print "ok 1\n";
+
+$req = new HTTP::Request GET => url("/private/place", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->code == 403
+                and $res->message =~ /robots.txt/;
+print "ok 2\n";
+
+$req = new HTTP::Request GET => url("/foo", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->code == 404;  # not found
+print "ok 3\n";
+
+# Let the robotua generate "Service unavailable/Retry After response";
+$ua->delay(1);
+$ua->use_sleep(0);
+$req = new HTTP::Request GET => url("/foo", $base);
+$res = $ua->request($req);
+#print $res->as_string;
+print "not " unless $res->code == 503   # Unavailable
+                and $res->header("Retry-After");
+print "ok 4\n";
+
+#----------------------------------------------------------------
+print "Terminating server...\n";
+sub httpd_get_quit
+{
+    my($c) = @_;
+    $c->send_error(503, "Bye, bye");
+    exit;  # terminate HTTP server
+}
+
+$ua->delay(0);
+$req = new HTTP::Request GET => url("/quit", $base);
+$res = $ua->request($req);
+
+print "not " unless $res->code == 503 and $res->content =~ /Bye, bye/;
+print "ok 5\n";
+
+#---------------------------------------------------------------
+$ua->delay(1);
+
+# host_wait() should be around 60s now
+print "not " unless abs($ua->host_wait($base->netloc) - 60) < 5;
+print "ok 6\n";
+
+# Number of visits to this place should be 
+print "not " unless $ua->no_visits($base->netloc) == 4;
+print "ok 7\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/abs-config.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/abs-config.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/abs-config.t	(revision 16561)
@@ -0,0 +1,35 @@
+print "1..6\n";
+
+use URI::URL qw(url);
+
+# Test configuration via some global variables.
+
+$URI::URL::ABS_REMOTE_LEADING_DOTS = 1;
+$URI::URL::ABS_ALLOW_RELATIVE_SCHEME = 1;
+
+$u1 = url("../../../../abc", "http://web/a/b");
+
+print "not " unless $u1->abs->as_string eq "http://web/abc";
+print "ok 1\n";
+
+{
+    local $URI::URL::ABS_REMOTE_LEADING_DOTS;
+    print "not " unless $u1->abs->as_string eq "http://web/../../../abc";
+    print "ok 2\n";
+}
+
+
+$u1 = url("http:../../../../abc", "http://web/a/b");
+print "not " unless $u1->abs->as_string eq "http://web/abc";
+print "ok 3\n";
+
+{
+   local $URI::URL::ABS_ALLOW_RELATIVE_SCHEME;
+   print "not " unless $u1->abs->as_string eq "http:../../../../abc";
+   print "ok 4\n";
+   print "not " unless $u1->abs(undef,1)->as_string eq "http://web/abc";
+   print "ok 5\n";
+}
+
+print "not " unless $u1->abs(undef,0)->as_string eq "http:../../../../abc";
+print "ok 6\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/base.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/base.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/base.t	(revision 16561)
@@ -0,0 +1,955 @@
+#!/local/bin/perl -w
+
+use URI::URL qw(url);
+use URI::Escape qw(uri_escape uri_unescape);
+
+use Carp;
+
+# _expect()
+#
+# Handy low-level object method tester which we insert as a method
+# in the URI::URL class
+#
+sub URI::URL::_expect {
+    my($self, $method, $expect, @args) = @_;
+    my $result = $self->$method(@args);
+    $expect = 'UNDEF' unless defined $expect;
+    $result = 'UNDEF' unless defined $result;
+    return 1 if $expect eq $result;
+    warn "'$self'->$method(@args) = '$result' " .
+		"(expected '$expect')\n";
+    $self->print_on('STDERR');
+    confess "Test Failed";
+}
+
+package main;
+
+# Must ensure that there is no relative paths in @INC because we will
+# chdir in the newlocal tests.
+chomp($pwd = ($^O =~ /mswin32/i ? `cd` : `pwd`));
+for (@INC) {
+    next if m|^/| or $^O =~ /os2|mswin32/i and m|^\w:[\\/]|;
+    print "Turn lib path $_ into $pwd/$_\n";
+    $_ = "$pwd/$_";
+
+}
+
+$| = 1;
+
+print "1..8\n";  # for Test::Harness
+
+# Do basic tests first.
+# Dies if an error has been detected, prints "ok" otherwise.
+
+print "Self tests for URI::URL version $URI::URL::VERSION...\n";
+
+scheme_parse_test();
+print "ok 1\n";
+
+parts_test();
+print "ok 2\n";
+
+escape_test();
+print "ok 3\n";
+
+newlocal_test();
+print "ok 4\n";
+
+absolute_test();
+print "ok 5\n";
+
+eq_test();
+print "ok 6\n";
+
+# Let's test makeing our own things
+URI::URL::strict(0);
+# This should work after URI::URL::strict(0)
+$url = new URI::URL "x-myscheme:something";
+# Since no implementor is registered for 'x-myscheme' then it will
+# be handeled by the URI::URL::_generic class
+$url->_expect('as_string' => 'x-myscheme:something');
+$url->_expect('path' => 'something');
+URI::URL::strict(1);
+
+# Let's try to make our URL subclass
+{
+    package MyURL;
+    @ISA = URI::URL::implementor();
+
+    sub _parse {
+	my($self, $init) = @_;
+	$self->URI::URL::_generic::_parse($init, qw(netloc path));
+    }
+
+    sub foo {
+	my $self = shift;
+	print ref($self)."->foo called for $self\n";
+    }
+}
+# Let's say that it implements the 'x-a+b.c' scheme (alias 'x-foo')
+URI::URL::implementor('x-a+b.c', 'MyURL');
+URI::URL::implementor('x-foo', 'MyURL');
+
+# Now we are ready to try our new URL scheme
+$url = new URI::URL 'x-a+b.c://foo/bar;a?b';
+$url->_expect('as_string', 'x-a+b.c://foo/bar;a?b');
+$url->_expect('path', '/bar;a?b');
+$url->foo;
+$newurl = new URI::URL 'xxx', $url;
+$newurl->foo;
+$url = new URI::URL 'yyy', 'x-foo:';
+$url->foo;
+
+print "ok 7\n";
+
+# Test the new wash&go constructor
+print "not " if url("../foo.html", "http://www.sn.no/a/b")->abs->as_string
+		ne 'http://www.sn.no/foo.html';
+print "ok 8\n";
+
+print "URI::URL version $URI::URL::VERSION ok\n";
+
+exit 0;
+
+
+
+
+#####################################################################
+#
+# scheme_parse_test()
+#
+# test parsing and retrieval methods
+
+sub scheme_parse_test {
+
+    print "scheme_parse_test:\n";
+
+    $tests = {
+	'hTTp://web1.net/a/b/c/welcome#intro'
+	=> {    'scheme'=>'http', 'host'=>'web1.net', 'port'=>80,
+		'path'=>'/a/b/c/welcome', 'frag'=>'intro','query'=>undef,
+		'epath'=>'/a/b/c/welcome', 'equery'=>undef,
+		'params'=>undef, 'eparams'=>undef,
+		'as_string'=>'http://web1.net/a/b/c/welcome#intro',
+		'full_path' => '/a/b/c/welcome' },
+
+	'http://web:1/a?query+text'
+	=> {    'scheme'=>'http', 'host'=>'web', 'port'=>1,
+		'path'=>'/a', 'frag'=>undef, 'query'=>'query+text' },
+
+	'http://web.net/'
+	=> {    'scheme'=>'http', 'host'=>'web.net', 'port'=>80,
+		'path'=>'/', 'frag'=>undef, 'query'=>undef,
+		'full_path' => '/',
+		'as_string' => 'http://web.net/' },
+
+	'http://web.net'
+	=> {    'scheme'=>'http', 'host'=>'web.net', 'port'=>80,
+		'path'=>'/', 'frag'=>undef, 'query'=>undef,
+		'full_path' => '/',
+		'as_string' => 'http://web.net/' },
+
+	'http:0'
+	 => {   'scheme'=>'http', 'path'=>'0', 'query'=>undef,
+		'as_string'=>'http:0', 'full_path'=>'0', },
+
+	'http:/0?0'
+	 => {   'scheme'=>'http', 'path'=>'/0', 'query'=>'0',
+		'as_string'=>'http:/0?0', 'full_path'=>'/0?0', },
+
+	'http://0:0/0/0;0?0#0'
+	 => {   'scheme'=>'http', 'host'=>'0', 'port'=>'0',
+		'path' => '/0/0', 'query'=>'0', 'params'=>'0',
+		'netloc'=>'0:0',
+		'frag'=>0, 'as_string'=>'http://0:0/0/0;0?0#0' },
+
+	'ftp://0%3A:%40@h:0/0?0'
+	=>  {   'scheme'=>'ftp', 'user'=>'0:', 'password'=>'@',
+		'host'=>'h', 'port'=>'0', 'path'=>'/0?0',
+		'query'=>undef, params=>undef,
+		'netloc'=>'0%3A:%40@h:0',
+		'as_string'=>'ftp://0%3A:%40@h:0/0?0' },
+
+	'ftp://usr:pswd@web:1234/a/b;type=i'
+	=> {    'host'=>'web', 'port'=>1234, 'path'=>'/a/b',
+		'user'=>'usr', 'password'=>'pswd',
+		'params'=>'type=i',
+		'as_string'=>'ftp://usr:pswd@web:1234/a/b;type=i' },
+
+	'ftp://host/a/b'
+	=> {    'host'=>'host', 'port'=>21, 'path'=>'/a/b',
+		'user'=>'anonymous',
+		'as_string'=>'ftp://host/a/b' },
+
+	'file://host/fseg/fs?g/fseg'
+	# don't escape ? for file: scheme
+	=> {    'host'=>'host', 'path'=>'/fseg/fs?g/fseg',
+		'as_string'=>'file://host/fseg/fs?g/fseg' },
+
+	'gopher://host'
+	=> {     'gtype'=>'1', 'as_string' => 'gopher://host/', },
+
+	'gopher://host/'
+	=> {     'gtype'=>'1', 'as_string' => 'gopher://host/', },
+
+	'gopher://gopher/2a_selector'
+	=> {    'gtype'=>'2', 'selector'=>'a_selector',
+		'as_string' => 'gopher://gopher/2a_selector', },
+
+	'mailto:libwww-perl@ics.uci.edu'
+	=> {    'address'       => 'libwww-perl@ics.uci.edu',
+		'encoded822addr'=> 'libwww-perl@ics.uci.edu',
+		'user'          => 'libwww-perl',
+		'host'          => 'ics.uci.edu',
+		'as_string'     => 'mailto:libwww-perl@ics.uci.edu', },
+
+	'news:*'
+	=> {    'groupart'=>'*', 'group'=>'*', as_string=>'news:*' },
+	'news:comp.lang.perl'
+	=> {    'group'=>'comp.lang.perl' },
+	'news:perl-faq/module-list-1-794455075@ig.co.uk'
+	=> {    'article'=>
+		    'perl-faq/module-list-1-794455075@ig.co.uk' },
+
+	'nntp://news.com/comp.lang.perl/42'
+	=> {    'group'=>'comp.lang.perl', 'digits'=>42 },
+
+	'telnet://usr:pswd@web:12345/'
+	=> {    'user'=>'usr', 'password'=>'pswd', 'host'=>'web' },
+	'rlogin://aas@a.sn.no'
+	=> {    'user'=>'aas', 'host'=>'a.sn.no' },
+	'tn3270://aas@ibm'
+	=> {    'user'=>'aas', 'host'=>'ibm',
+		'as_string'=>'tn3270://aas@ibm/'},
+
+	'wais://web.net/db'
+	=> { 'database'=>'db' },
+	'wais://web.net/db?query'
+	=> { 'database'=>'db', 'query'=>'query' },
+	'wais://usr:pswd@web.net/db/wt/wp'
+	=> {    'database'=>'db', 'wtype'=>'wt', 'wpath'=>'wp',
+		'password'=>'pswd' },
+    };
+
+    foreach $url_str (sort keys %$tests ){
+	print "Testing '$url_str'\n";
+	my $url = new URI::URL $url_str;
+	my $tests = $tests->{$url_str};
+	while( ($method, $exp) = each %$tests ){
+	    $exp = 'UNDEF' unless defined $exp;
+	    $url->_expect($method, $exp);
+	}
+    }
+}
+
+
+#####################################################################
+#
+# parts_test()          (calls netloc_test test)
+#
+# Test individual component part access functions
+#
+sub parts_test {
+    print "parts_test:\n";
+
+    # test storage part access/edit methods (netloc, user, password,
+    # host and port are tested by &netloc_test)
+
+    $url = new URI::URL 'file://web/orig/path';
+    $url->scheme('http');
+    $url->path('1info');
+    $url->query('key words');
+    $url->frag('this');
+    $url->_expect('as_string' => 'http://web/1info?key%20words#this');
+
+    $url->epath('%2f/%2f');
+    $url->equery('a=%26');
+    $url->_expect('full_path' => '/%2f/%2f?a=%26');
+
+    # At this point it should be impossible to access the members path()
+    # and query() without complaints.
+    eval { my $p = $url->path; print "Path is $p\n"; };
+    die "Path exception failed" unless $@;
+    eval { my $p = $url->query; print "Query is $p\n"; };
+    die "Query exception failed" unless $@;
+
+    # but we should still be able to set it 
+    $url->path("howdy");
+    $url->_expect('as_string' => 'http://web/howdy?a=%26#this');
+
+    # Test the path_components function
+    $url = new URI::URL 'file:%2f/%2f';
+    my $p;
+    $p = join('-', $url->path_components);
+    die "\$url->path_components returns '$p', expected '/-/'"
+      unless $p eq "/-/";
+    $url->host("localhost");
+    $p = join('-', $url->path_components);
+    die "\$url->path_components returns '$p', expected '-/-/'"
+      unless $p eq "-/-/";
+    $url->epath("/foo/bar/");
+    $p = join('-', $url->path_components);
+    die "\$url->path_components returns '$p', expected '-foo-bar-'"
+      unless $p eq "-foo-bar-";
+    $url->path_components("", "/etc", "\0", "..", "øse", "");
+    $url->_expect('full_path' => '/%2Fetc/%00/../%F8se/');
+
+    # Setting undef
+    $url = new URI::URL 'http://web/p;p?q#f';
+    $url->epath(undef);
+    $url->equery(undef);
+    $url->eparams(undef);
+    $url->frag(undef);
+    $url->_expect('as_string' => 'http://web/');
+
+    # Test http query access methods
+    $url->keywords('dog');
+    $url->_expect('as_string' => 'http://web/?dog');
+    $url->keywords(qw(dog bones));
+    $url->_expect('as_string' => 'http://web/?dog+bones');
+    $url->keywords(0,0);
+    $url->_expect('as_string' => 'http://web/?0+0');
+    $url->keywords('dog', 'bones', '#+=');
+    $url->_expect('as_string' => 'http://web/?dog+bones+%23%2B%3D');
+    $a = join(":", $url->keywords);
+    die "\$url->keywords did not work (returned '$a')" unless $a eq 'dog:bones:#+=';
+    # calling query_form is an error
+    eval { my $foo = $url->query_form; };
+    die "\$url->query_form should croak since query contains keywords not a form."
+      unless $@;
+
+    $url->query_form(a => 'foo', b => 'bar');
+    $url->_expect('as_string' => 'http://web/?a=foo&b=bar');
+    my %a = $url->query_form;
+    die "\$url->query_form did not work"
+      unless $a{a} eq 'foo' && $a{b} eq 'bar';
+
+    $url->query_form(a => undef, a => 'foo', '&=' => '&=+');
+    $url->_expect('as_string' => 'http://web/?a=&a=foo&%26%3D=%26%3D%2B');
+
+    my @a = $url->query_form;
+    die "Wrong length" unless @a == 6;
+    die "Bad keys from query_form"
+      unless $a[0] eq 'a' && $a[2] eq 'a' && $a[4] eq '&=';
+    die "Bad values from query_form"
+      unless $a[1] eq '' && $a[3] eq 'foo' && $a[5] eq '&=+';
+
+    # calling keywords is an error
+    eval { my $foo = $url->keywords; };
+    die "\$url->keywords should croak when query is a form"
+      unless $@;
+    # Try this odd one
+    $url->equery('&=&=b&a=&a&a=b=c&&a=b');
+    @a = $url->query_form;
+    #print join(":", @a), "\n";
+    die "Wrong length" unless @a == 16;
+    die "Wrong sequence" unless $a[4]  eq ""  && $a[5]  eq "b" &&
+                                $a[10] eq "a" && $a[11] eq "b=c";
+
+    # Try array ref values in the key value pairs
+    $url->query_form(a => ['foo', 'bar'], b => 'foo', c => ['bar', 'foo']);
+    $url->_expect('as_string', 'http://web/?a=foo&a=bar&b=foo&c=bar&c=foo');
+
+
+    netloc_test();
+    port_test();
+
+    $url->query(undef);
+    $url->_expect('query', undef);
+
+    $url = new URI::URL 'gopher://gopher/';
+    $url->port(33);
+    $url->gtype("3");
+    $url->selector("S");
+    $url->search("query");
+    $url->_expect('as_string', 'gopher://gopher:33/3S%09query');
+
+    $url->epath("45%09a");
+    $url->_expect('gtype' => '4');
+    $url->_expect('selector' => '5');
+    $url->_expect('search' => 'a');
+    $url->_expect('string' => undef);
+    $url->_expect('path' => "/45\ta");
+    $url->path("00\t%09gisle");
+    $url->_expect('search', '%09gisle');
+
+    # Let's test som other URL schemes
+    $url = new URI::URL 'news:';
+    $url->group("comp.lang.perl.misc");
+    $url->_expect('as_string' => 'news:comp.lang.perl.misc');
+    $url->article('<1234@a.sn.no>');
+    $url->_expect('as_string' => 'news:1234@a.sn.no'); # "<" and ">" are gone
+    # This one should be illegal
+    eval { $url->article("no.perl"); };
+    die "This one should really complain" unless $@;
+
+    $url = new URI::URL 'mailto:';
+    $url->user("aas");
+    $url->host("a.sn.no");
+    $url->_expect("as_string" => 'mailto:aas@a.sn.no');
+    $url->address('foo@bar');
+    $url->_expect("host" => 'bar');
+    $url->_expect("user" => 'foo');
+
+    $url = new URI::URL 'wais://host/database/wt/wpath';
+    $url->database('foo');
+    $url->_expect('as_string' => 'wais://host/foo/wt/wpath');
+    $url->wtype('bar');
+    $url->_expect('as_string' => 'wais://host/foo/bar/wpath');
+
+    # Test crack method for various URLs
+    my(@crack, $crack);
+    @crack = URI::URL->new("http://host/path;param?query#frag")->crack;
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "http*UNDEF*UNDEF*host*80*/path*param*query*frag";
+
+    @crack = URI::URL->new("foo/bar", "ftp://aas\@ftp.sn.no/")->crack;
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "ftp*UNDEF*UNDEF*UNDEF*21*foo/bar*UNDEF*UNDEF*UNDEF";
+
+    @crack = URI::URL->new('ftp://u:p@host/q?path')->crack;
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "ftp*u*p*host*21*/q?path*UNDEF*UNDEF*UNDEF";
+
+    @crack = URI::URL->new("ftp://ftp.sn.no/pub")->crack;    # Test anon ftp
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    die "No passwd in anonymous crack" unless $crack[2];
+    $crack[2] = 'passwd';  # easier to test when we know what it is
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "ftp*anonymous*passwd*ftp.sn.no*21*/pub*UNDEF*UNDEF*UNDEF";
+
+    @crack = URI::URL->new('mailto:aas@sn.no')->crack;
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "mailto*aas*UNDEF*sn.no*UNDEF*aas\@sn.no*UNDEF*UNDEF*UNDEF";
+
+    @crack = URI::URL->new('news:comp.lang.perl.misc')->crack;
+    die "Cracked result should be 9 elements" unless @crack == 9;
+    $crack = join("*", map { defined($_) ? $_ : "UNDEF" } @crack);
+    print "Cracked result: $crack\n";
+    die "Bad crack result" unless
+      $crack eq "news*UNDEF*UNDEF*UNDEF*UNDEF*comp.lang.perl.misc*UNDEF*UNDEF*UNDEF";
+}
+
+#
+# netloc_test()
+#
+# Test automatic netloc synchronisation
+#
+sub netloc_test {
+    print "netloc_test:\n";
+
+    my $url = new URI::URL 'ftp://anonymous:p%61ss@håst:12345';
+    $url->_expect('user', 'anonymous');
+    $url->_expect('password', 'pass');
+    $url->_expect('host', 'håst');
+    $url->_expect('port', 12345);
+    # Can't really know how netloc is represented since it is partially escaped
+    #$url->_expect('netloc', 'anonymous:pass@hst:12345');
+    $url->_expect('as_string' => 'ftp://anonymous:p%61ss@h%E5st:12345/');
+
+    # The '0' is sometimes tricky to get right
+    $url->user(0);
+    $url->password(0);
+    $url->host(0);
+    $url->port(0);
+    $url->_expect('netloc' => '0:0@0:0');
+    $url->host(undef);
+    $url->_expect('netloc' => '');
+    $url->host('h');
+    $url->user(undef);
+    $url->_expect('netloc' => 'h:0');
+    $url->user('');
+    $url->_expect('netloc' => ':0@h:0');
+    $url->password('');
+    $url->_expect('netloc' => ':@h:0');
+    $url->user('foo');
+    $url->_expect('netloc' => 'foo:@h:0');
+
+    # Let's try a simple one
+    $url->user('nemo');
+    $url->password('p2');
+    $url->host('hst2');
+    $url->port(2);
+    $url->_expect('netloc' => 'nemo:p2@hst2:2');
+
+    $url->user(undef);
+    $url->password(undef);
+    $url->port(undef);
+    $url->_expect('netloc' => 'hst2');
+    $url->_expect('port' => '21');  # the default ftp port
+
+    $url->port(21);
+    $url->_expect('netloc' => 'hst2');
+
+    # Let's try some reserved chars
+    $url->user("@");
+    $url->password(":-#-;-/-?");
+    $url->_expect('as_string' => 'ftp://%40:%3A-%23-%3B-%2F-%3F@hst2/');
+
+}
+
+#
+# port_test()
+#
+# Test port behaviour
+#
+sub port_test {
+    print "port_test:\n";
+
+    $url = URI::URL->new('http://foo/root/dir/');
+    my $port = $url->port;
+    die "Port undefined" unless defined $port;
+    die "Wrong port $port" unless $port == 80;
+    die "Wrong string" unless $url->as_string eq
+	'http://foo/root/dir/';
+
+    $url->port(8001);
+    $port = $url->port;
+    die "Port undefined" unless defined $port;
+    die "Wrong port $port" unless $port == 8001;
+    die "Wrong string" unless $url->as_string eq
+	'http://foo:8001/root/dir/';
+
+    $url->port(80);
+    $port = $url->port;
+    die "Port undefined" unless defined $port;
+    die "Wrong port $port" unless $port == 80;
+    die "Wrong string" unless $url->as_string eq
+	'http://foo/root/dir/';
+
+    $url->port(8001);
+    $url->port(undef);
+    $port = $url->port;
+    die "Port undefined" unless defined $port;
+    die "Wrong port $port" unless $port == 80;
+    die "Wrong string" unless $url->as_string eq
+	'http://foo/root/dir/';
+}
+
+
+#####################################################################
+#
+# escape_test()
+#
+# escaping functions
+
+sub escape_test {
+    print "escape_test:\n";
+
+    # supply escaped URL
+    $url = new URI::URL 'http://web/this%20has%20spaces';
+    # check component is unescaped
+    $url->_expect('path', '/this has spaces');
+
+    # modify the unescaped form
+    $url->path('this ALSO has spaces');
+    # check whole url is escaped
+    $url->_expect('as_string',
+		  'http://web/this%20ALSO%20has%20spaces');
+
+    $url = new URI::URL uri_escape('http://web/try %?#" those');
+    $url->_expect('as_string',
+		  'http://web/try%20%25%3F%23%22%20those');
+
+    my $all = pack('c*',0..255);
+    my $esc = uri_escape($all);
+    my $new = uri_unescape($esc);
+    die "uri_escape->uri_unescape mismatch" unless $all eq $new;
+    $url->path($all);
+    $url->_expect('full_path' => '/%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20!%22%23$%25%26\'()*%2B,-./0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF');
+
+    # test escaping uses uppercase (preferred by rfc1837)
+    $url = new URI::URL 'file://h/';
+    $url->path(chr(0x7F));
+    $url->_expect('as_string', 'file://h/%7F');
+
+    # reserved characters differ per scheme
+
+    ## XXX is this '?' allowed to be unescaped
+    $url = new URI::URL 'file://h/test?ing';
+    $url->_expect('path', '/test?ing');
+
+    $url = new URI::URL 'file://h/';
+    $url->epath('question?mark');
+    $url->_expect('as_string', 'file://h/question?mark');
+    # XXX Why should this be any different???
+    #     Perhaps we should not expect too much :-)
+    $url->path('question?mark');
+    $url->_expect('as_string', 'file://h/question%3Fmark');
+
+    # See what happens when set different elements to this ugly sting
+    my $reserved = ';/?:@&=#%';
+    $url->path($reserved . "foo");
+    $url->_expect('as_string', 'file://h/%3B/%3F%3A%40%26%3D%23%25foo');
+
+    $url->scheme('http');
+    $url->path('');
+    $url->_expect('as_string', 'http://h/');
+    $url->query($reserved);
+    $url->params($reserved);
+    $url->frag($reserved);
+    $url->_expect('as_string', 'http://h/;%3B%2F%3F%3A%40&=%23%25?%3B%2F%3F%3A%40&=%23%25#;/?:@&=#%');
+
+    $str = $url->as_string;
+    $url = new URI::URL $str;
+    die "URL changed" if $str ne $url->as_string;
+
+    $url = new URI::URL 'ftp:foo';
+    $url->user($reserved);
+    $url->host($reserved);
+    $url->_expect('as_string', 'ftp://%3B%2F%3F%3A%40%26%3D%23%25@%3B%2F%3F%3A%40%26%3D%23%25/foo');
+
+}
+
+
+#####################################################################
+#
+# newlocal_test()
+#
+
+sub newlocal_test {
+    print "newlocal_test:\n";
+    my $pwd = ($^O eq 'MSWin32' ? 'cd' : (-e '/bin/pwd' ? '/bin/pwd' : 'pwd'));
+    my $tmpdir = ($^O eq 'MSWin32' ? $ENV{TEMP} : '/tmp');
+    $tmpdir =~ tr|\\|/|;
+
+    my $savedir = `$pwd`;     # we don't use Cwd.pm because we want to check
+			      # that it get require'd corretly by URL.pm
+    chomp $savedir;
+
+    # cwd
+    chdir($tmpdir) or die $!;
+    my $dir = `$pwd`; $dir =~ tr|\\|/|;
+    chomp $dir; $dir = uri_escape($dir, ':');
+    $url = newlocal URI::URL;
+    $url->_expect('as_string', URI::URL->new("file:$dir/")->as_string);
+
+    print "Local directory is ". $url->local_path . "\n";
+
+    # absolute dir
+    chdir('/') or die $!;
+    $url = newlocal URI::URL '/usr/';
+    $url->_expect('as_string', 'file:/usr/');
+
+    # absolute file
+    $url = newlocal URI::URL '/vmunix';
+    $url->_expect('as_string', 'file:/vmunix');
+
+    # relative file
+    chdir($tmpdir) or die $!;
+    $dir = `$pwd`; $dir =~ tr|\\|/|;
+    chomp $dir; $dir = uri_escape($dir, ':');
+    $url = newlocal URI::URL 'foo';
+    $url->_expect('as_string', "file:$dir/foo");
+
+    # relative dir
+    chdir($tmpdir) or die $!;
+    $dir = `$pwd`; $dir =~ tr|\\|/|;
+    chomp $dir; $dir = uri_escape($dir, ':');
+    $url = newlocal URI::URL 'bar/';
+    $url->_expect('as_string', "file:$dir/bar/");
+
+    # 0
+    chdir('/') or die $!;
+    $dir = `$pwd`; $dir =~ tr|\\|/|;
+    chomp $dir; $dir = uri_escape($dir, ':');
+    $url = newlocal URI::URL '0';
+    $url->_expect('as_string', "file:${dir}0");
+
+    # Test access methods for file URLs
+    $url = new URI::URL 'file:/c:/dos';
+    $url->_expect('dos_path', 'C:\\DOS');
+    $url->_expect('unix_path', '/c:/dos');
+    $url->_expect('vms_path', '[C:]DOS');
+    eval { $url->mac_path; };  # This is not allowed since we have ':' in path
+    die "Mac_path generated when it should fail." unless $@;
+    $url = new URI::URL 'file:/foo/bar';
+    $url->_expect('unix_path', '/foo/bar');
+    $url->_expect('mac_path', 'foo:bar');
+
+    # Some edge cases
+    $url = new URI::URL 'file:';
+    $url->_expect('unix_path', '/');
+    $url = new URI::URL 'file:/';
+    $url->_expect('unix_path', '/');
+    $url = new URI::URL 'file:.';
+    $url->_expect('unix_path', '.');
+    $url = new URI::URL 'file:./foo';
+    $url->_expect('unix_path', 'foo');
+    $url = new URI::URL 'file:0';
+    $url->_expect('unix_path', '0');
+    $url = new URI::URL 'file:../../foo';
+    $url->_expect('unix_path', '../../foo');
+    $url = new URI::URL 'file:foo/../bar';
+    $url->_expect('unix_path', 'bar');
+
+    # Relative files
+    $url = new URI::URL 'file:foo/b%61r/Note.txt';
+    $url->_expect('unix_path', 'foo/bar/Note.txt');
+    $url->_expect('mac_path', ':foo:bar:Note.txt');
+    $url->_expect('dos_path', 'FOO\\BAR\\NOTE.TXT');
+    $url->_expect('vms_path', '[.FOO.BAR]NOTE.TXT');
+
+    # The VMS path found in RFC 1738 (section 3.10)
+    $url = new URI::URL 'file://vms.host.edu/disk$user/my/notes/note12345.txt';
+    $url->_expect('vms_path', 'DISK$USER:[MY.NOTES]NOTE12345.TXT');
+    $url->_expect('mac_path', 'disk$user:my:notes:note12345.txt');
+
+    chdir($savedir) or die $!;
+}
+
+
+#####################################################################
+#
+# absolute_test()
+#
+sub absolute_test {
+
+    print "Test relative/absolute URI::URL parsing:\n";
+
+    # Tests from draft-ietf-uri-relative-url-06.txt
+    # Copied verbatim from the draft, parsed below
+
+    @URI::URL::g::ISA = qw(URI::URL::_generic); # for these tests
+
+    my $base = 'http://a/b/c/d;p?q#f';
+
+    $absolute_tests = <<EOM;
+5.1.  Normal Examples
+
+      g:h        = <URL:g:h>
+      g          = <URL:http://a/b/c/g>
+      ./g        = <URL:http://a/b/c/g>
+      g/         = <URL:http://a/b/c/g/>
+      /g         = <URL:http://a/g>
+      //g        = <URL:http://g>
+      ?y         = <URL:http://a/b/c/d;p?y>
+      g?y        = <URL:http://a/b/c/g?y>
+      g?y/./x    = <URL:http://a/b/c/g?y/./x>
+      #s         = <URL:http://a/b/c/d;p?q#s>
+      g#s        = <URL:http://a/b/c/g#s>
+      g#s/./x    = <URL:http://a/b/c/g#s/./x>
+      g?y#s      = <URL:http://a/b/c/g?y#s>
+      ;x         = <URL:http://a/b/c/d;x>
+      g;x        = <URL:http://a/b/c/g;x>
+      g;x?y#s    = <URL:http://a/b/c/g;x?y#s>
+      .          = <URL:http://a/b/c/>
+      ./         = <URL:http://a/b/c/>
+      ..         = <URL:http://a/b/>
+      ../        = <URL:http://a/b/>
+      ../g       = <URL:http://a/b/g>
+      ../..      = <URL:http://a/>
+      ../../     = <URL:http://a/>
+      ../../g    = <URL:http://a/g>
+
+5.2.  Abnormal Examples
+
+   Although the following abnormal examples are unlikely to occur
+   in normal practice, all URL parsers should be capable of resolving
+   them consistently.  Each example uses the same base as above.
+
+   An empty reference resolves to the complete base URL:
+
+      <>         = <URL:http://a/b/c/d;p?q#f>
+
+   Parsers must be careful in handling the case where there are more
+   relative path ".." segments than there are hierarchical levels in
+   the base URL's path.  Note that the ".." syntax cannot be used to
+   change the <net_loc> of a URL.
+
+     ../../../g = <URL:http://a/../g>
+     ../../../../g = <URL:http://a/../../g>
+
+   Similarly, parsers must avoid treating "." and ".." as special
+   when they are not complete components of a relative path.
+
+      /./g       = <URL:http://a/./g>
+      /../g      = <URL:http://a/../g>
+      g.         = <URL:http://a/b/c/g.>
+      .g         = <URL:http://a/b/c/.g>
+      g..        = <URL:http://a/b/c/g..>
+      ..g        = <URL:http://a/b/c/..g>
+
+   Less likely are cases where the relative URL uses unnecessary or
+   nonsensical forms of the "." and ".." complete path segments.
+
+      ./../g     = <URL:http://a/b/g>
+      ./g/.      = <URL:http://a/b/c/g/>
+      g/./h      = <URL:http://a/b/c/g/h>
+      g/../h     = <URL:http://a/b/c/h>
+
+   Finally, some older parsers allow the scheme name to be present in
+   a relative URL if it is the same as the base URL scheme.  This is
+   considered to be a loophole in prior specifications of partial
+   URLs [1] and should be avoided by future parsers.
+
+      http:g     = <URL:http:g>
+      http:      = <URL:http:>
+EOM
+    # convert text to list like
+    # @absolute_tests = ( ['g:h' => 'g:h'], ...)
+
+    for $line (split("\n", $absolute_tests)) {
+	next unless $line =~ /^\s{6}/;
+	if ($line =~ /^\s+(\S+)\s*=\s*<URL:([^>]*)>/) {
+	    my($rel, $abs) = ($1, $2);
+	    $rel = '' if $rel eq '<>';
+	    push(@absolute_tests, [$rel, $abs]);
+	}
+	else {
+	    warn "illegal line '$line'";
+	}
+    }
+
+    # add some extra ones for good measure
+
+    push(@absolute_tests, ['x/y//../z' => 'http://a/b/c/x/y/z'],
+			  ['1'         => 'http://a/b/c/1'    ],
+			  ['0'         => 'http://a/b/c/0'    ],
+			  ['/0'        => 'http://a/0'        ],
+			  ['%2e/a'     => 'http://a/b/c/%2e/a'],  # %2e is '.'
+			  ['%2e%2e/a'  => 'http://a/b/c/%2e%2e/a'],
+	);
+
+    print "  Relative    +  Base  =>  Expected Absolute URL\n";
+    print "================================================\n";
+    for $test (@absolute_tests) {
+	my($rel, $abs) = @$test;
+	my $abs_url = new URI::URL $abs;
+	my $abs_str = $abs_url->as_string;
+
+	printf("  %-10s  +  $base  =>  %s\n", $rel, $abs);
+	my $u   = new URI::URL $rel, $base;
+	my $got = $u->abs;
+	$got->_expect('as_string', $abs_str);
+    }
+
+    # bug found and fixed in 1.9 by "J.E. Fritz" <FRITZ@gems.vcu.edu>
+    $base = new URI::URL 'http://host/directory/file';
+    my $relative = new URI::URL 'file', $base;
+    my $result = $relative->abs;
+
+    my ($a, $b) = ($base->path, $result->path);
+	die "'$a' and '$b' should be the same" unless $a eq $b;
+
+    # Counter the expectation of least surprise,
+    # section 6 of the draft says the URL should
+    # be canonicalised, rather than making a simple
+    # substitution of the last component.
+    # Better doublecheck someone hasn't "fixed this bug" :-)
+    $base = new URI::URL 'http://host/dir1/../dir2/file';
+    $relative = new URI::URL 'file', $base;
+    $result = $relative->abs;
+    die 'URL not canonicalised' unless $result eq 'http://host/dir2/file';
+
+    print "--------\n";
+    # Test various other kinds of URLs and how they like to be absolutized
+    for (["http://abc/", "news:45664545", "http://abc/"],
+	 ["news:abc",    "http://abc/",   "news:abc"],
+	 ["abc",         "file:/test?aas", "file:/abc"],
+	 ["gopher:",     "",               "gopher:"],
+	 ["?foo",        "http://abc/a",   "http://abc/a?foo"],
+	 ["?foo",        "file:/abc",      "file:/?foo"],
+	 ["#foo",        "http://abc/a",   "http://abc/a#foo"],
+	 ["#foo",        "file:a",         "file:a#foo"],
+	 ["#foo",        "file:/a",         "file:/a#foo"],
+	 ["#foo",        "file:/a",         "file:/a#foo"],
+	 ["#foo",        "file://localhost/a", "file://localhost/a#foo"],
+	 ['123@sn.no',   "news:comp.lang.perl.misc", 'news:123@sn.no'],
+	 ['no.perl',     'news:123@sn.no',           'news:no.perl'],
+	 ['mailto:aas@a.sn.no', "http://www.sn.no/", 'mailto:aas@a.sn.no'],
+
+	 # Test absolutizing with old behaviour.
+	 ['http:foo',     'http://h/a/b',   'http://h/a/foo'],
+	 ['http:/foo',    'http://h/a/b',   'http://h/foo'],
+	 ['http:?foo',    'http://h/a/b',   'http://h/a/b?foo'],
+	 ['http:#foo',    'http://h/a/b',   'http://h/a/b#foo'],
+	 ['http:?foo#bar','http://h/a/b',   'http://h/a/b?foo#bar'],
+	 ['file:/foo',    'http://h/a/b',   'file:/foo'],
+
+	)
+    {
+	my($url, $base, $expected_abs) = @$_;
+	my $rel = new URI::URL $url, $base;
+	my $abs = $rel->abs($base, 1);
+	printf("  %-12s+  $base  =>  %s\n", $rel, $abs);
+	$abs->_expect('as_string', $expected_abs);
+    }
+    print "absolute test ok\n";
+
+    # Test relative function
+    for (
+	 ["http://abc/a",   "http://abc",        "a"],
+	 ["http://abc/a",   "http://abc/b",      "a"],
+	 ["http://abc/a?q", "http://abc/b",      "a?q"],
+	 ["http://abc/a;p", "http://abc/b",      "a;p"],
+	 ["http://abc/a",   "http://abc/a/b/c/", "../../../a"],
+         ["http://abc/a/",  "http://abc/a/",     "./"],
+         ["http://abc/a#f", "http://abc/a",      "#f"],
+
+	 ["file:/etc/motd", "file:/",            "etc/motd"],
+	 ["file:/etc/motd", "file:/etc/passwd",  "motd"],
+	 ["file:/etc/motd", "file:/etc/rc2.d/",  "../motd"],
+	 ["file:/etc/motd", "file:/usr/lib/doc", "../../etc/motd"],
+         ["file:",          "file:/etc/",        "../"],
+         ["file:foo",       "file:/etc/",        "../foo"],
+
+	 ["mailto:aas",     "http://abc",        "mailto:aas"],
+
+	 # Nicolai Langfeldt's original example
+	 ["http://www.math.uio.no/doc/mail/top.html",
+	  "http://www.math.uio.no/doc/linux/", "../mail/top.html"],
+        )
+    {
+	my($abs, $base, $expect) = @$_;
+	printf "url('$abs', '$base')->rel eq '$expect'\n";
+	my $rel = URI::URL->new($abs, $base)->rel;
+	$rel->_expect('as_string', $expect);
+    }
+    print "relative test ok\n";
+}
+
+
+sub eq_test
+{
+    my $u1 = new URI::URL 'http://abc.com:80/~smith/home.html';
+    my $u2 = new URI::URL 'http://ABC.com/%7Esmith/home.html';
+    my $u3 = new URI::URL 'http://ABC.com:/%7esmith/home.html';
+
+    # Test all permutations of these tree
+    $u1->eq($u2) or die "1: $u1 ne $u2";
+    $u1->eq($u3) or die "2: $u1 ne $u3";
+    $u2->eq($u1) or die "3: $u2 ne $u1";
+    $u2->eq($u3) or die "4: $u2 ne $u3";
+    $u3->eq($u1) or die "5: $u3 ne $u1";
+    $u3->eq($u2) or die "6: $u3 ne $u2";
+
+    # Test empty path
+    my $u4 = new URI::URL 'http://www.sn.no';
+    $u4->eq("HTTP://WWW.SN.NO:80/") or die "7: $u4";
+    $u4->eq("http://www.sn.no:81") and die "8: $u4";
+
+    # Test mailto
+    my $u5 = new URI::URL 'mailto:AAS@SN.no';
+    $u5->eq('mailto:aas@sn.no') or die "9: $u5";
+
+    # Test reserved char
+    my $u6 = new URI::URL 'ftp://ftp/%2Fetc';
+    $u6->eq("ftp://ftp/%2fetc") or die "10: $u6";
+    $u6->eq("ftp://ftp://etc") and die "11: $u6";
+}
+
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/heuristic.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/heuristic.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/heuristic.t	(revision 16561)
@@ -0,0 +1,61 @@
+print "1..14\n";
+
+use URI::Heuristic qw(uf_urlstr uf_url);
+$URI::Heuristic::DEBUG++;
+open(STDERR, ">&STDOUT");  # redirect STDERR
+
+print "not " unless uf_urlstr("http://www.sn.no/") eq "http://www.sn.no/";
+print "ok 1\n";
+
+print "not " unless uf_urlstr("/etc/passwd") eq "file:/etc/passwd";
+print "ok 2\n";
+
+print "not " unless uf_urlstr("./foo.txt") eq "file:./foo.txt";
+print "ok 3\n";
+
+print "not " unless uf_urlstr("ftp.aas.no/lwp.tar.gz") eq "ftp://ftp.aas.no/lwp.tar.gz";
+print "ok 4\n";
+
+print "not " unless uf_urlstr("C:\\CONFIG.SYS") eq "file:C:\\CONFIG.SYS";
+print "ok 5\n";
+
+if (gethostbyname("www.netscape.com")) {
+    # DNS probably work, lets run test 6..8
+
+    $URI::Heuristic::MY_COUNTRY = "no";
+    print "not " unless uf_urlstr("perl/camel.gif") eq "http://www.perl.com/camel.gif";
+    print "ok 6\n";
+
+    $URI::Heuristic::MY_COUNTRY = "uk";
+    print "not " unless uf_urlstr("perl/camel.gif") eq "http://www.perl.co.uk/camel.gif";
+    print "ok 7\n";
+   
+    $ENV{URL_GUESS_PATTERN} = "www.ACME.org www.ACME.com";
+    print "not " unless uf_urlstr("perl") eq "http://www.perl.org";
+    print "ok 8\n";
+
+} else {
+    # don't make the inocent worry
+    print "Skipping test 6-8 because DNS does not work\n";
+    for (6..8) { print "ok $_\n"; }
+
+}
+
+$ENV{URL_GUESS_PATTERN} = "";
+print "not " unless uf_urlstr("perl") eq "http://perl";
+print "ok 9\n";
+
+print "not " unless uf_urlstr("http:80") eq "http:80";
+print "ok 10\n";
+
+print "not " unless uf_urlstr("mailto:gisle\@aas.no") eq "mailto:gisle\@aas.no";
+print "ok 11\n";
+
+print "not " unless uf_urlstr("gisle\@aas.no") eq "mailto:gisle\@aas.no";
+print "ok 12\n";
+
+print "not " unless uf_urlstr("Gisle.Aas\@aas.perl.org") eq "mailto:Gisle.Aas\@aas.perl.org";
+print "ok 13\n";
+
+print "not " unless uf_url("gopher.sn.no")->scheme eq "gopher";
+print "ok 14\n";
Index: /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/rel-base.t
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/rel-base.t	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/libwww-perl-5.36/t/uri/rel-base.t	(revision 16561)
@@ -0,0 +1,24 @@
+print "1..3\n";
+
+use URI::URL;
+
+# We used to have problems with URLs that used a base that was
+# not absolute itself.
+
+$u1 = url("/foo/bar", "http://www.acme.com/");
+$u2 = url("../foo/", $u1);
+$u3 = url("zoo/foo", $u2);
+
+$a1 = $u1->abs->as_string;
+$a2 = $u2->abs->as_string;
+$a3 = $u3->abs->as_string;
+
+print "$a1\n$a2\n$a3\n";
+
+print "not " unless $a1 eq "http://www.acme.com/foo/bar";
+print "ok 1\n";
+print "not " unless $a2 eq "http://www.acme.com/foo/";
+print "ok 2\n";
+print "not " unless $a3 eq "http://www.acme.com/foo/zoo/foo";
+print "ok 3\n";
+
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Artistic
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Artistic	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Artistic	(revision 16561)
@@ -0,0 +1,131 @@
+
+
+
+
+			 The "Artistic License"
+
+				Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+	"Package" refers to the collection of files distributed by the
+	Copyright Holder, and derivatives of that collection of files
+	created through textual modification.
+
+	"Standard Version" refers to such a Package if it has not been
+	modified, or has been modified in accordance with the wishes
+	of the Copyright Holder as specified below.
+
+	"Copyright Holder" is whoever is named in the copyright or
+	copyrights for the package.
+
+	"You" is you, if you're thinking about copying or distributing
+	this Package.
+
+	"Reasonable copying fee" is whatever you can justify on the
+	basis of media cost, duplication charges, time of people involved,
+	and so on.  (You will not be required to justify it to the
+	Copyright Holder, but only to the computing community at large
+	as a market that must bear the fee.)
+
+	"Freely Available" means that no fee is charged for the item
+	itself, though there may be fees involved in handling the item.
+	It also means that recipients of the item may redistribute it
+	under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder.  A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+    a) place your modifications in the Public Domain or otherwise make them
+    Freely Available, such as by posting said modifications to Usenet or
+    an equivalent medium, or placing the modifications on a major archive
+    site such as uunet.uu.net, or by allowing the Copyright Holder to include
+    your modifications in the Standard Version of the Package.
+
+    b) use the modified Package only within your corporation or organization.
+
+    c) rename any non-standard executables so the names do not conflict
+    with standard executables, which must also be provided, and provide
+    a separate manual page for each non-standard executable that clearly
+    documents how it differs from the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+    a) distribute a Standard Version of the executables and library files,
+    together with instructions (in the manual page or equivalent) on where
+    to get the Standard Version.
+
+    b) accompany the distribution with the machine-readable source of
+    the Package with your modifications.
+
+    c) give non-standard executables non-standard names, and clearly
+    document the differences in manual pages (or equivalent), together
+    with instructions on where to get the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package.  You may charge any fee you choose for support of this
+Package.  You may not charge a fee for this Package itself.  However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.  You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.  If such scripts or library files are aggregated with this
+Package via the so-called "undump" or "unexec" methods of producing a
+binary executable image, then distribution of such an image shall
+neither be construed as a distribution of this Package nor shall it
+fall under the restrictions of Paragraphs 3 and 4, provided that you do
+not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other
+languages) supplied by you and linked into this Package in order to
+emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution.  Such use shall not be
+construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+				The End
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Changes
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Changes	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Changes	(revision 16561)
@@ -0,0 +1,51 @@
+Fixes in 1.0.6:
+- -f works with binary files
+- w3mir will no longer use 'Referer: (commandline)' when a URL is derived
+  from the commandline/config file.
+- More errormessage fixes
+- Fixed install instructions to warn against LWP 5.40.
+
+Fixes in 1.0.5: 
+- Better error-messaged in -quiet mode 
+- Fixed quoting. Both " and ' quotes in documents are accepted. 
+- Fixed timeout handling. 
+- Fixed broken connection handling. Incomplete documents are 
+  deleted and retried.
+- Fixed URL mangling problem. In 1.0.3 URLs like ``foo.html#bar'' would
+  be rewritten to ``#bar''. This is quite bad and makes a lot of mirrors
+  un-useable. 
+- -v now prints LWPs version as well as w3mirs version
+
+Features/bugs in 1.0.5 
+- As earlier, w3mir does not support cookies. I have received a patch for
+  basic cookie support but have not yet incorporated it due to
+  time-restraints.
+- There actually exists web servers that break the HTTP standard rather
+  thoroughly by not sending a Content-type header (this is mandatory in
+  the standard). W3mir will not discover that a HTML file is HTML in this
+  case and will not extract URLs from it.
+- If you rewrite the ending filename URLs with Apply rules (as in 
+  index.htm -> index.html) and there are links to index.htm#foo the link
+  will be incorrect after mirroring. The solution is to avoid those
+  kinds of rules. This was meant to be fixed in 1.0.3 (which broke it as
+  described above), I will fix it when I get more time. The fix is to
+  remove the #fragment part of URLs when they pass thru the apply
+  machinery. Patches accepted. 
+
+Fixes in 1.0.3: 
+- The -R/remove option to remove files is no longer more destructive
+  than intended.
+- Files with 'unsafe' characters in their filename is now saved as
+  "foo bar" instead of "foo%20bar" 
+- The -B switch works once again. 
+
+Fixes in 1.0.2: 
+- W3mir now only newline converts HTML files, all other files are handled
+  as binary. This should ensure file-integrety even when working with
+   mis-configured servers. 
+
+Fixes in 1.0.1: 
+-fa option and corresponding configuration file directive works again. 
+- Added -nnc option and a corresponding configuration file directive. It
+  disables newline conversions, which could corrupt files the Webserver
+  mis-declares. The HOWTO explains the uses of -nnc 
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL	(revision 16561)
@@ -0,0 +1,117 @@
+This is how you install w3mir on a Unix system. Please see the file
+INSTALL.w32 for install instructions for win32 systems.
+
+INSTALLING PREREQUISITES:
+
+The prerequisites are the perl packages libwww-perl, and MIME-Base64.
+The w3mir install procedure will check for their presence and complain
+if they are not there, so if you are unsure what you have you might
+skip this part for now.
+
+- Get libwww-perl from CPAN 
+  (http://www.perl.com/CPAN/modules/by-module/WWW/).  W3mir does not work
+  with LWP 5.40 and later versions.  Please get an earlier version.
+  It also does not work with the new URI package.  Use the URI package
+  enclosed in the older LWP.
+
+- Unpack it:
+
+    gzip -dc libwww-perl-*.tar.gz | tar xvBf -
+
+- Install it.  W3mir does not require the whole libwww-perl package and
+  all the packages that goes with it to work so you may follow these
+  install instructions instead of the libwww-perl install instructions
+  to get an easier install which will work with w3mir.
+
+    cd libwww-perl-*
+
+  Are you going to install it in the standard perl place (you need to
+  be the system administrator to do this)? If so:
+
+    perl Makefile.PL
+
+  If not (if you are a user):
+
+    perl Makefile.PL PREFIX=/some/dir
+
+  /some/dir could be ~ (as in your home directory).
+
+  IF you are installing an old LWP just for w3mir you can install both
+  the old LWP, the Base64 module and w3mir in a special PREFIX
+  separate from your usual install PREFIX.  This ensures that w3mir will
+  access the older version of LWP and that other software will access
+  the newer version.  If you do this make sure you install all of LWP,
+  Base64 and w3mir with the same PREFIX.
+
+  This step might produce some complaints;  A standard perl
+  installation will typically get messages about missing these
+  modules: IO::Socket, NET::FTP and MD5.  None of these need be
+  installed to run w3mir and so any error messages about these may
+  safely be ignored.
+
+    make
+    make test      (robot/ua and local/http might fail, that's OK)
+    make install
+
+  Now libwww-perl should be installed and working.
+
+- You also have to get the MIME-Base64 package
+  (http://www.perl.com/CPAN/modules/by-module/MIME/)
+
+- Unpack it:
+
+    gzip -dc MIME-Base64-*.tar.gz | tar xvBf -
+
+- Install it:
+
+    cd MIME-Base64-*
+
+  Then, as above, either
+
+    perl Makefile.PL
+
+  or
+
+    perl Makefile.PL PREFIX=/some/dir
+
+  Continue:
+
+    make
+    make test      (it should print 'All tests successful.')
+    make install
+
+  Now MIME-Base64 should be installed and working.
+
+INSTALLING W3MIR:
+
+- Get w3mir (http://www.math.uio.no/~janl/w3mir/ or from CPAN)
+
+- Unpack it:
+
+     gzip -dc w3mir-*.tar.gz | tar xvBf -
+
+- Install it:
+
+     cd w3mir-*
+
+  Are you going to install it in the standard perl place? (You
+  probably need to be the system administrator to do this.)  If so:
+
+    perl Makefile.PL
+
+  If not (if you're a user):
+
+    perl Makefile.PL PREFIX=/some/dir
+
+  /some/dir could be ~ (as in your home directory).
+
+  then
+
+     make
+     make install
+
+w3mir should now be installed correctly.  And if it's installed in
+your path you can even run it.  If you use csh or tcsh you want to run
+'rehash' before the shell will find it.
+
+1999/04/14 - Nicolai Langfeldt
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL.w32
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL.w32	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/INSTALL.w32	(revision 16561)
@@ -0,0 +1,92 @@
+This file contains installation instructions for two different ports
+of Perl to win32.  One port is known as the 'ActivePerl' port, the
+other is known as 'the GS port'.
+
+********************************* ActivePerl **********************************
+
+This was written and submitted by Roland Bauer.  Thanks.  I have
+edited them slightly.
+
+How to install w3mir on Win95/AS-port (April 1999):
+===================================================
+
++ get and install winzip from http://www.winzip.com/
++ get and install ActivePerl (now Build 509) from http://www.activestate.com/
++ get nmake.exe from 
+  ftp://ftp.microsoft.com/Softlib/MSLFILES/nmake15.exe
+  (or set switch make=nmake to make=dmake in Config.pm
+  and get dmake from GS port)
+
++ perl makefile.pl
++ nmake
++ nmake install
+
++ perl w3mir -h
++ perl w3mir http://xx.zz.zz/dir/
+
+
+Caveats:
+========
+
++ archive 1.0.6 from CPAN seems to be corrupted
+
++ &mkdir does not seem to work with dos-paths
+like 'D:/DIR1/DIR2'
+(so it is not possible to save to another harddisk)
+
+*********************************** GS Perl ***********************************
+
+These win32 installation instructions were written by Paul Hsiao and
+slightly edited by me. My thanks to Paul. It was current as of
+22-May-1998.
+
+
+WINZIP DOWNLOAD INSTRUCTIONS
+
+    The WinZip program can be accessed from the "Start" button, if the
+    program came with your system.  This software can be obtained from:
+ 
+                http://www.winzip.com/
+ 
+    Follow the installation instructions for the WinZip software as needed.
+
+ 
+PERL DOWNLOAD INSTRUCTIONS
+ 
+    The w3mir code will need access to version 5.003 or higher of the 
+    Perl interpreter and LWP library extensions in order for it to run. 
+    This software can be obtained from:
+ 
+                ftp://sunsite.uio.no/pub/languages/perl/CPAN/ports/win32/Standard
+ 
+    From this URL, select perl5.00402-bindist04-bc.tar.gz and download
+    to your PC into a folder of your choice.  In the folder, run WinZip
+    on this file.  For MS Windows 95, you need to update the registration: 
+    open C:\autoexec.bat  and add a line
+
+                path %path%;C:\Perl\bin;       
+    (C:\Perl\ is the path of the perl folder)  
+
+
+W3MIR DOWNLOAD AND INSTALLATION INSTRUCTIONS
+
+    You already have w3mir. Right? Unpack the .tar.gz file with
+    winzip.
+ 
+    Open a DOS prompt screen and go to the w3mir folder:
+ 
+        cd C:\w3mir-1.0.3
+ 
+    Type the install command for w3mir:
+ 
+        perl.exe Makefile.PL
+        make
+        make install
+ 
+    To test w3mir, type:
+ 
+    perl w3mir -h
+ 
+    If the screen displays all options (about 25 options), then 
+    w3mir is installed correctly.
+
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/MANIFEST
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/MANIFEST	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/MANIFEST	(revision 16561)
@@ -0,0 +1,15 @@
+Artistic
+Changes
+INSTALL
+INSTALL.w32
+MANIFEST
+Makefile.PL
+README
+example.cfg
+multiscope.cfg
+w3mir.PL
+w3mir-HOWTO.html
+w3mfix.PL
+htmlop.pm
+w3http.pm
+w3pdfuri.pm
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Makefile.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Makefile.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/Makefile.PL	(revision 16561)
@@ -0,0 +1,116 @@
+# -*-perl-*-
+# Makefile.PL for w3mir.
+# Note that the file 'MANIFEST' is quite significant in this context
+#
+# Notes:  Should unpack any tar.gz files in current directory and install
+#   them with identical arguments to Makefile.PL to this invocation.
+#   This is especially for making installation of w3mir easy 
+#   by installing libwww-perl and mime-base64 for the user before
+#   installing w3mir.  Cool eh?  That would require some changes.
+#
+# - janl 18/4/1997
+#
+# 19/04/99 janl: Added 'clean' target on tip from  Lupe Christoph
+#	and LWP max-version check to the LWP detection.
+# 
+
+# First just write the Makefile
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME	=> 'w3mir',
+    VERSION_FROM => 'w3mir.PL', # finds $VERSION
+    EXE_FILES => [ 'w3mir', 'w3mfix' ],
+    clean => { FILES => 'w3mir w3mfix' },
+    dist => { COMPRESS => 'gzip -f9', SUFFIX => 'gz' },
+);
+
+print "\n";
+
+# Find value of INSTALLSITELIB
+
+&read_makefile;
+$islib = resolve_make_var('INSTALLSITELIB');
+
+# Check for prerequisites
+
+$| = 1;
+my $missing_modules = 0;
+
+# libwww-perl
+print "Checking for libwww-perl:";
+
+eval "use lib '$islib'; require LWP;";
+
+if (!defined($LWP::VERSION)) {
+  print ' failed
+
+libwww seems to be missing.
+
+';
+  $missing_modules++;
+  sleep(2);
+} elsif ($LWP::VERSION<5.08 or $LWP::VERSION>=5.40) {
+  print ' failed
+
+libwww is version ',$LWP::VERSION,', I need version 5.08 to 5.39.
+Please see the INSTALL file.
+
+';
+  $missing_modules++;
+  sleep(2);
+} else {  
+  print " ok\n\n";
+}
+
+# MIME::Base64
+print "Checking for MIME::Base64:";
+
+eval "use lib '$islib'; require MIME::Base64;";
+
+if (!defined(&MIME::Base64::encode)) {
+  print ' failed
+
+MIME::Base64 seems to be missing.
+
+';
+  $missing_modules++;
+  sleep(2);
+} else {
+  print " ok\n\n";
+}
+
+
+print 'Please see the INSTALL file for instructions on how to get the
+missing/too old modules.
+
+' if $missing_modules;
+
+
+sub resolve_make_var ($) {
+
+  my($var) = shift @_;
+  my($val) = $make{$var};
+
+#  print "Resolving: ",$var,"=",$val,"\n";
+  
+  while ($val =~ s~\$\((\S+)\)~$make{$1}~g) {}
+#  print "Resolved: $var: $make{$var} -> $val\n";
+  $val;
+}
+
+
+sub read_makefile {
+
+  open(MAKEFILE, 'Makefile') || 
+    die "Could not open Makefile for reading: $!\n";
+
+  while (<MAKEFILE>) {
+    chomp;
+    next unless m/^([A-Z]+)\s*=\s*(\S+)$/;
+    $make{$1}=$2;
+#    print "Makevar: $1 = $2\n";
+  }
+
+  close(MAKEFILE)
+}
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/README
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/README	(revision 16561)
@@ -0,0 +1,133 @@
+PROPAGANDA:
+
+See http://www.math.uio.no/~janl/w3mir/ for propaganda.
+
+--------------------------------------------------------------------------
+Q: Where can I get a new version of w3mir?
+A: The w3mir homepage is at http://www.math.uio.no/~janl/w3mir/.
+   W3mir is also distributed on CPAN: http://www.perl.com/
+
+Q: Are there any mailing lists?
+A: Yes, see below.
+
+Q: Should I subscribe to any of the mailinglists?
+A: Yes, if you use w3mir at all you should subscribe to 
+   w3mir-info@usit.uio.no, send e-mail to janl@math.uio.no to be
+   subscribed.
+
+Q: I found a bug!
+A: See below.
+
+Q: How do it...
+A: There is a w3mir-HOWTO.html file in the distribution.  Read it.
+
+--------------------------------------------------------------------------
+BUGS:
+
+- -lc switch does not work too well.
+
+Please see below for how to report bugs.
+
+--------------------------------------------------------------------------
+FEATURES (NOT bugs):
+
+- URLs with two /es ('//') in the path component does not work
+  as some might expect.  According to my reading of the http/url spec.
+  it is an illegal construct, which is a Good Thing, because I don't
+  know how to handle it if it's legal.
+- If you start at http://foo/bar/ then index.html might be gotten twice.
+- Some documents point to a point above the server root, i.e.,
+  http://some.server/../stuff.html.  Netscape, and other browsers, in
+  defiance of the URL standard documents will change the URL to
+  http://some.server/stuff.html.  W3mir will not.
+
+--------------------------------------------------------------------------
+MAIL LISTS, REPORTING BUGS:
+
+Please send ideas (see todo lists further down please) and bug reports
+to w3mir-core@usit.uio.no, please include URL and command line/config
+file that triggered the bug. w3mir-info@usit.uio.no is mainly used for
+announcing new versions or bugs to w3mir users. You will get more
+information following that list than anywhere else. And it's very low
+volume. To subscribe to these lists email janl@math.uio.no. The
+w3mir-core list is intended for w3mir hackers only.
+
+--------------------------------------------------------------------------
+COPYRIGTHS:
+
+w3mir, w3http.pm, w3pdfuri.pm and htmlop.pm are free but it is
+Copyrighted by the various involved hackers.  If you want to copy,
+hack or distribte w3mir you can do that providing you comply with the
+'Artistic License' enclosed in the w3mir distribution in the file
+named Artistic.
+
+--------------------------------------------------------------------------
+CREDITS:
+
+- Oscar Nierstrasz: Wrote htget
+- Gorm Haug Eriksen: Started w3mir on the foundations of htget,
+	contributed code later.
+- Nicolai Langfeldt: Learning from Oscar and Gorms mistakes, rewrote
+  everything.
+- Chris Szurgot: Adapting to win32, good ideas and code contribs, 
+  Debugging.  And criticism.
+- Rik Faith: Uses w3mir extensively, not shy about complaining and
+  commenting and suggesting.  
+- The libwww-perl author(s) that made adding some new featres
+  ridicolously easy.
+
+--------------------------------------------------------------------------
+TODO LIST:
+
+* TODO, version 1.1:
+
+Some of these are speculative, some others are very useful.
+
+- CSS parsing/support at the same level as HTML
+- Full support for APPLETS/OBJECT tags.
+- Alias rules.  These would enable w3mir to map ukoln.bath.ac.uk and 
+  bubl.bath.ac.uk to www.ukoln.ac.uk and know that the objects contained
+  in these are all the same.  Another use would be to mirror from a mirror
+  instead of the _real_ site, since the original site to which you have
+  references are on a slow link while the mirror is on a fast link.
+- FTP support (easy if through a http style ftp proxy, but is that what
+  we want?)
+- SHTTP/SSL support
+- Add a 3rd argument to URL/Also that denotes the depth of documents to
+  be retrived under each, this would autimatically generate fetch/ignore
+  rules.  How well these work would be dependent on the order of the
+  URL/Also directives though.  Must be documented.
+- Retrive recursively until N-th order links are reached.  This
+  differs siginificantly from directory recursion which we do now.  When
+  this is done w3mir should also know the difference between inline and
+  external links, inline links should always be retrived.  Trivia question:
+  What order is needed to reach every findable document on the web from
+  Yahoo?
+- Integrate with cvs or rcs (or other version controll system) to make
+  retriver able to reproduce mirrored site for any given date.
+- Some text processing:  Adding and removing text/sgml comments when suitable
+  options and tags are found.
+- Put retrival date-stamps as comments in html files, to document the when
+  and how of how this document was retrived.
+- Example: If you're mirroring a site primarily to get to the papers, but
+  the site has n versions of each paper: foo.ps.gz, foo.ps.Z, foo.dvi.gz
+  foo.dvi.Z, foo.tar.gz, foo.zip and you only need one version.  Implement
+  a way to get only one version of documents provided in multipele versions,
+  something like multi axis preference list to get only the most attractive
+  version of the doc.
+- Logging of retrivals to file, need to change every print to a functioncall.
+- Your suggestion here.
+
+* TODO, http related
+- Use Keep-alive.  Then we should probably stop using 30 second pauses
+  between document retrivals.
+- HTTP/1.1?  HTTP/1.1 servers should do keep-alive even with 1.0 requests.
+- Separate quenes for each server, interleave requests.
+- Authentication is only done if challenged now.  If a document in a
+  directory needs authentication it is very likely that all other documents
+  in that directory and subdirectories require authentication.
+
+* If perl gets threads:
+- Make the retrival and analysis engines separate threads, and have each
+  one retrival thread pr. method/server/port and do paralell retrivals.
+- This makes http/1.1 easier too.
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/example.cfg
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/example.cfg	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/example.cfg	(revision 16561)
@@ -0,0 +1,120 @@
+# This is a example w3mir config file.  The file and options are fully
+# documented in the w3mir man page, and a lot is spelled out in the 
+# w3mir-HOWTO.  This file might not be completely updated, but the man
+# page should be.
+#
+# Comment lines start with '#'.  Comments are not allowed at end of
+# lines.  The keywords are not casesensitive, but some settings are,
+# notably fetch/ignore/apply settings, as well as the urls given.
+#
+# Available options: recurse, no-date-check, only-nonexistent, list-urls,
+# lowercase.  It is not default to recurse.  Options must be the
+# first directive to appear in the config file.
+# The options directive needs to be the first directive in the config file
+# and there can only be one.
+Options: recurse
+# Multipele options:
+# Options: recurse, only-nonexistent
+# This directive was previously named 'Fetch-options'
+#
+# Start here:
+URL: http://www.stud.ifi.uio.no/~janl/
+#
+#
+# How much noise to make, one of quiet, brief or chatty.  Default is brief
+#Verbosity: brief
+#
+# Initial referer.  Some servers are very picky....
+#Initial-Referer: http://foo.bar/
+#
+# Headers to disable for anonymitys sake: 
+#Disable-headers: referer, user
+#
+# Nicenes, robotrules can be true or false
+#Robot-Rules: true
+# Default pause between http connections is now 0 seconds 
+# Pause: 0
+# Pause between retries, in seconds.  If w3mir runs as a once-a-day thing
+# this can and should be high.  I suggest at least one hour (3600 seconds).
+#Retry-Pause: 600
+# How many times a failed retrival is retried.
+#Retries: 3
+#
+# URL selection.  Order is significant, first Fetch/Ignore rule overrides
+# later rules.  Supply as many as you want.  If not matched by ignore rules
+# a doc is gotten.  
+#Fetch: *.html
+#
+# Ignore urls we can't copy anyway.  Actually w3mir can copy
+# many cgi things with no problems.  But not always.
+Ignore: *.cgi
+Ignore: *-cgi
+Ignore: *.map
+#
+# RE variants are full perl REs.  Mix freely with the others.  These
+# must be legal perl REs, including the starting m and any
+# options you want, like 'i'.
+#
+#Fetch-RE: m/jpeg/
+#Ignore-RE: m/\.gif$/
+#
+# URL manipulation.  _All_ rules are applied in order.  If lowercasing
+# options is given that is done first.  This must be a legal perl
+# substitution RE.  Including the starting s and any options you want,
+# like 'g' and 'i'.
+#
+# This changes the name of all .gif files to .fif
+#Apply: s/\.gif$/.fif/
+#
+# HTTP Proxy server:
+#HTTP-Proxy: www-cache.uio.no:3128
+#
+# HTTP Proxy options: 
+# - 'revalidate': Force proxy to revalidate cached documents.
+# - 'refresh': Force proxy to get cached documents from server _again_.
+# - 'no-store': Use if the contents of the documents is sensitive.
+# - 'no-pragma': Force use of HTTP/1.1 'Cache-Control:' header.
+# If only 'refresh' is specified the default is to use the 'Pragma: no-cache'
+# HTTP/1.0 header, this can be overruled with the 'no-pragma' option.
+# You can be fairly sure any proxy will understand 'refresh', ONLY HTTP/1.1
+# proxies will understand 'revalidate' and 'no-store'.
+#proxy-options: refresh
+#
+# HTTP Proxy authentication.  The mechanism  is simpleminded, we either need
+# to authenticate ourselves with the proxy or not, and there can only be
+# one user/password tuple, domain/realm information is ignored...
+# HTTP-Proxy-user: foo
+# HTTP-Proxy-passwd: bar
+#
+# What to do with files, one of save, stdout or forget.
+#File-disposition: save
+#
+# Debug level
+#Debug: 0|1|2
+#
+# Umask, if not the default
+#Umask: 077
+#
+# HTML document editing options
+#Header: Mirrored from http://www.ifi.uio.no/~janl/ts/
+#Remove-nomirror: true
+#
+# Authentication:
+#Auth-domain: server:port/realm
+#Auth-user: me
+#Auth-passwd: my_password
+#
+# You can use '*' in place of server:port and/or realm:
+#Auth-domain: */*
+#Auth-user: otherme
+#Auth-passwd: otherpassword
+#
+
+# 'Fixup' is done by the external w3mfix program.  If you want a
+# mirror that's browseable in a filesystem (as opposed to via a http
+# server) just put this in:
+Fixup: run
+
+# Related is this, what is the name of the file the http server serves
+# when a directory URL is requested?  w3mir default is index.html.
+#Index-name: welcome.html
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/htmlop.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/htmlop.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/htmlop.pm	(revision 16561)
@@ -0,0 +1,656 @@
+# -*-perl-*-
+# htmlop.pl: Do operations on html documents.
+$VERSION=0.2.4;
+#
+# Original source from Bjørn Borud, without it I would not have atempted
+# this. In this incarnation it bears no resemblance to Bjørns code.
+# - Nicolai Langfeldt 18/11/95.
+# 
+# htmlop.pl does operations on html files, possebly many at a time.
+# Operations:
+# - Absolitify urls
+# - Relativify urls
+# - Gather list of urls
+# - Callback with url
+# - Canonify document w.r.t. SGML.
+#
+# Authors:
+# - Nicolai Langfeldt (janl@ifi.uio.no)
+# - Chris Szurgot (szurgot@itribe.net)
+#
+# Changes:
+# janl    18/11/95 - Initial version
+# szurgot 09/02/96 - Code in htmlop'process to remove <BASE> Tag if we are 
+#                    returning the form. An unchanged base destroys local 
+#                    fragments.
+# janl    22/02/96 - Added URLSUB functions.  <BASE> will only be removed from
+#		     the returned doc, never from the origninal doc.
+# janl    16/05/96 - Removed I* options and added single NODOC option/modifier
+#		     to replace them.
+# janl	  09/09/96 - Added URLPROC and NREL opcodes, for a better URL
+#		     processing model. -> 0.1.7
+# janl    24/09/96 - Various cosmetics, no longer inserting a !SGML tag,
+#		     only !DOCTYPE and HTML tags. -> 0.1.8
+# janl    11/10/96 - URLs in HTML 3.2 tags are now found.
+# janl     6/11/96 - Added netscape SCRIPT tag.
+# janl    20/11/96 - 'BORDER' is different from 'BORDER=0' -> 0.1.10
+# janl    06/11/96 - Added userdata argument to URLPROC.
+#   			Added SAVEURLS and USESAVED opcodes. -> 0.1.11
+# janl    11/04/97 - Changed to URI::URL and got into strict harness
+# janl	  13/04/97 - Fixed comment processing, extended it to processing
+#			directives. Bug reported by Chris Johnson. -> 0.1.12
+# janl    08/04/97 - Made ISMAP into ISMAP=, bad! -> 0.1.13
+# janl	  23/05/97 - Treating <script> and <style> as verbatim -> 0.1.14
+# janl	  06/06/97 - Canonify: <!SGML tag confused it and !DOCTYPE was mangled
+#			-> 0.1.15
+# janl	  30/07/97 - Now supporting <BASE> tag.
+#		   - Added TAGCALLBACK opcode -> 0.1.16
+# janl    16/10/97 - Empty string must be quoted, reported by Bart Barenburg
+#			-> 0.1.17
+# janl	  01/12/97 - More HTML URL tags/attributes recognized (Greg Lindhorst) 
+# janl	  13/12/97 - Can't delete the whole <BASE> tag.  Netscape has
+#		     extended it for frame use.  So only delete the HREF
+#		     attr -> 0.1.18
+# janl    01/01/98 - Realized applet/object support can't work and changed
+#		     so it's able to work, if conditions are right.
+# janl    04/01/98 - Added html 4.0 tags and attributes.
+# janl    08/02/98 - Hacked for speed.  Went from 43s to 9s on a 170K
+#		     document -> 0.2.  Thanks to Rune Frøysa who taunted me.
+# janl	  09/04/98 - More tolerant about what constitutes a newline -> 0.2.1
+# janl	  09/05/98 - Export %isdir -> 0.2.2
+# janl    13/04/99 - Remove leading /../ sequences in path component in
+#		     ABS code. -> 0.2.3
+# janl    28/05/99 - The code was buggy, now it's not. -> 0.2.4
+
+package htmlop;
+
+use URI::URL;
+
+use strict;
+# Global variables
+use vars qw($ABS $REL $LIST $CANON $URLSUB $NODOC $URLPROC $NREL);
+use vars qw($SAVEURL $USESAVED $TAGCALLBACK $debug %isdir);
+
+# These are for the smartrel routines
+my $url_origin;
+my $doc_top;
+my $doc_top_re;
+my $choped_url_or;
+
+# HTML operation codes for process_html.  The first argument is the
+# html document to do operations on.  It will not be changed.  If
+# NODOC is not passed a new document edited as specified will be
+# returned.  No more than one document will be returned by one
+# invocation of htmlop.
+
+$ABS = 1;			# Absolutify urls. Arg: Origin. The
+                                # ABS function absolutifies URLS,
+                                # assuming they are relative to the
+                                # argument.
+				# If a <BASE> tag is found the origin
+				# given by it will be used instead of
+				# the arguemnt given.
+
+$REL = 2;			# Relativize all urls. Arg: Origin.
+                                # The REL function simply removes the
+                                # argument string from any urls
+                                # matching it.  For this reason the
+                                # origin string is interprated as a
+                                # RE, which may have unexpected
+                                # results, unless RE specials are
+                                # escaped, which they should be.
+
+$LIST = 3;			# List all urls, Ret: urls
+
+$CANON = 4;			# See that missing opening (and
+                                # ending) SGML tags are injected.
+
+$URLSUB = 5;			# Do regular expression substitution
+                                # on urls.  Arg: RE, substitute.
+
+$NODOC = 6;			# Do not return a rebuilt document.
+                                # Saves memory, and time
+
+# NOTE: I SUSPECT URLPROC IS BROKEN IF A BASE TAG APPEARS IN THE TEXT.
+# THE URL PROCESSOR NEEDS TO BE PASSED htmlop::process' IDEA OF WHAT THE
+# BASE URL IS.
+
+$URLPROC = 7;			# Apply function on urls (process
+                                # urls).  Arg: Pointer to function to
+				# apply, userdata.  The function will
+				# be passed the url, modified by any
+				# previous operations and the
+				# userdata.  The function must return
+				# the new url.
+
+$NREL = 8;			# New relativisation function, works
+                                # much better than the old one. Arg:
+                                # Origin, Top.
+
+$SAVEURL = 9;			# Save urls in tag with modified name.
+				# Arg: attribute prefix.
+				# Example: <a href=foo> becomes
+				# <a href=foo w3mir-href=foo> if no other
+				# processing of the url is done.
+
+$USESAVED = 10;			# Use saved urls. Arg: attribute prefix
+
+$TAGCALLBACK = 11;		# Procedure to call for each Tag.
+				# Args: procedure, userdata (one item)
+
+				# Args to procedure: userdata, Base
+				# URL, tag name, reference to array of
+				# URL attributes, reference to hash of
+				# all attributes.  The base url is
+				# derived from the one used in ABS or
+				# the BASE tag.
+
+$debug=0;			# Debugging level in this package
+
+# process_html returns a array.  The first component of the array is
+# the new html document. The rest of the array is the urls.  If a
+# document is not to be returned a empty string is returned. If a url
+# list is not to be returned a empty array is returned.
+
+# HERE BE DRAGONS:
+
+# Where to find URLs in various tags.  The second compoent is a array
+# reference.
+
+my(%urls) = (
+	HEAD    => [ 'PROFILE' ],
+        BLOCKQUOTE => [ 'CITE' ],
+        Q 	=> [ 'CITE' ],
+        INS	=> [ 'CITE' ],
+	DEL	=> [ 'CITE' ],
+	A	=> [ 'HREF' ] ,
+	IMG	=> [ 'SRC' ,'LOWSRC' ,'USEMAP', 'LONGDESC' ] ,
+	EMBED	=> [ 'SRC' ],
+	FRAME	=> [ 'SRC', 'LONGDESC' ],
+        IFRAME  => [ 'SRC', 'LONGDESC' ],
+	BODY	=> [ 'BACKGROUND' ],
+	AREA	=> [ 'HREF' ],
+	LINK	=> [ 'HREF' ],
+
+	# The APPLET and OBJECT tags do not fit into my model for URL
+	# manipulation.  Just looking at CODEBASE might work, if the
+	# URL it names is a browseable directory...
+	APPLET	=> [ 'CODEBASE' ],  # If the codebase dir is browseable
+	OBJECT  => [ 'CODEBASE' ],  # Ditto.  Can't handle DATA attribute now
+
+	INPUT	=> [ 'SRC', 'USEMAP' ],
+	MAP	=> [ 'HREF' ],
+	SCRIPT	=> [ 'SRC', 'FOR' ],# 'FOR's semantics is not defined, the
+				    # attribute is just reserved for possible
+				    # future use...
+	BGSOUND => [ 'SRC' ],
+	FORM	=> [ 'ACTION' ],    # Is this asking for trouble?
+				    # Maybe it should just be absolutized...
+	     			    # On the other hand: It's CGI...
+	);
+
+my(%relative) = (
+	# Identify URL attributes containing urls that are relative to
+	# the named URL attribute.   When processing these they should
+        # be absolitized and then relativized relative to the BASE attribute.
+        # This is just window dressing for now; it is not used for anything.
+
+	# ARCHIVE is really a URI _list_.
+	CODEBASE => [ 'CLASSID', 'DATA', 'CODE', 'ARCHIVE' ],
+        );
+
+%isdir = (
+	# These tags refer to directories:
+	CODEBASE => 1
+	);
+
+# Tags that enclose bits we want to leave absolutely alone because they
+# are not very like HTML, or some such.
+
+# The material between the start and end tags is copied with no
+# processing at all.  The end tag is left to be processed.
+# The endtag match is case insensitive.
+my(%verbatim) = (
+        SCRIPT	=> quotemeta('</SCRIPT>'),	# Embeded scripts
+	STYLE	=> quotemeta('</STYLE>'),	# Embeded stylesheet
+	);
+
+# These are the functions that pick the HTML to pieces.  It will not
+# work esp. good on a random SGML document since the HTML application
+# of SGML has simpler quoting than it might.
+
+sub gettoken {
+  # Get one token from the argument, removing it from the argument.
+  # BUG: There should be whitespace at the end of the examined string.
+  my($c,$token,$i);
+  
+  # Skip whitespace and newlines
+  return '' unless defined(@_) && defined($_[0]);
+  $_[0] =~ s/^[\r\n\s]*//;
+  
+  return '' if ($_[0] eq '');
+  
+  $c = substr($_[0],0,1);
+  substr($_[0],0,1)='';
+  
+  if ($c eq '"' || $c eq "\'") { # Quoted material
+    $i=index($_[0],$c);
+    # End-quote missing, just gobble the rest of the doc
+    $i=length($_[0]) if $i == -1;
+    # Extract and remove token
+    $token=substr($_[0],0,$i);
+    substr($_[0],0,$i+1)='';
+  } elsif ($c eq '=') {
+    $token='=';
+  } else {			# Non-quoted material, ends in whitespace or =
+    $_[0] =~ m/[=\s\n\r]/;
+    $_[0] = $&.$';
+    $token=$c.$`;
+  }
+  # print "Token: '$token'\t\tRest: '",$_[0],"'\n";
+  return $token;
+}
+
+
+sub tagtoken {
+  # Pick the tag to pieces (also knonw as tokens). Return an
+  # associative array of attributes.  The attribute-names are changed
+  # to uppercase.  The attribute-values are left as is.
+  my($tok,$lasttok,%tokens);
+  # Append a space, gettoken needs it - silly? Yes! 
+  # Change it to test on boundrary things rather than ...?
+  $_[0].=' ';
+
+  $lasttok='';
+  while (1) {
+    last if (($tok=uc &gettoken($_[0])) eq '');
+
+    if ($tok eq '=') {
+#      print STDERR " -bad html-" if ($lasttok eq '');
+      $tokens{$lasttok}.=&gettoken($_[0]);
+      print STDERR "STORED: $lasttok = ",$tokens{$lasttok},"\n" if $debug;
+    } else {
+      $tokens{$tok}=undef;
+      $lasttok=$tok;
+    }
+  }
+  return %tokens;
+}
+
+
+sub gettag {
+  # Pick out the following things from the remaining html doc:
+  # Everything leading up to the first tag.  The first tag, and its
+  # contents.  Modify @_ directly to reduce number of copies of
+  # possebly huge documents kept in memory at once.  Return the body,
+  # the tag name, and the attributes (associative array)
+  my(%attr,$tagn,$tagc,$body,$tag,$doc);
+
+  $doc=\$_[0];
+
+  my($start,$end,$length);
+  
+  $start=index($$doc,'<');
+
+  if ($start<$[) {
+    # EOF
+    $body=$$doc;
+    $$doc='';
+    return ($body,'',());
+  }
+
+  $end=index($$doc,'>',$start+1);
+  
+  if ($end<$[) {
+    # This sucks, found no end of the tag...
+    $body=$$doc;
+    $$doc='';
+    return ($body,'',());
+  }
+
+  $length=$end-$start-1;
+
+  $body=substr($$doc,0,$start);
+  $tag=substr($$doc,$start+1,$length);
+
+  # This shortens the string in each itteration, some kind of mechanism
+  # to do it once in a while would speed things up further.  HOWEVER, when
+  # I tried to code this all I got was a _nasty_ memory leak.
+  substr($$doc,0,$end+1)='';
+
+#  print STDERR "------\n";
+  
+#  print STDERR "BODY: /$body/\n";
+  print STDERR "COMPLETE TAG: /$tag/\n" if $debug;
+#  print STDERR "REST: /",substr($$doc,0,20),"/\n";
+
+#  print STDERR "------\n";
+
+  # Examine tag contents
+  if ($tag =~ /^([!?]--)/ || $tag =~ /^(!\w+)/) {
+    # Comment or processing dicective, handle specially
+    $tagn=$&;
+    $tagc=$';
+    return ($body,$tagn,("$tagc",undef));
+  }
+
+  # Everything else
+  ($tagn,$tagc) = split(/[\s\n\r]+/,$tag,2);
+  $tagn="\U$tagn";
+
+  return ($body,$tagn,()) if !defined($tagc);
+  
+  return ($body,$tagn,&tagtoken($tagc));
+}
+
+
+# This is meant for general consumption:
+
+sub process {
+  # Process a html file.  Into one end you put a html file.  Out
+  # of the other end you get something dependent on the operations
+  # you specified.
+  # I cannot gobble my arguments. I need to examine them several times.
+  
+  my($arg,$doc,$i,$retdoc,$canon,$newdoc,@urllist,$Q,$cont);
+  my($origin,$baseurl);
+  
+  $origin=$baseurl='';
+
+  $retdoc=$canon=0;
+  
+  # Get the document from the argument list
+  $doc=shift(@_);
+  
+  $retdoc=1;
+  $i=0;
+  # Argument checking
+  while (defined($arg=$_[$i++])) {
+    if (! ($arg =~ /^\d+$/)) {
+      print STDERR "ERROR IN HTMLOP::process:\n";
+      print STDERR "Args: ",join(',',@_),"\n";
+      print STDERR "ARG: $arg is not a opcode.\n";
+      exit(1);
+    }
+    if ($arg == $ABS) {
+      $baseurl=$origin=$_[$i++];
+    } elsif ($arg == $REL || $arg == $SAVEURL || $arg == $USESAVED) {
+      $i++; # Skip one arg
+    } elsif ($arg == $URLSUB || $arg == $NREL || $arg == $URLPROC ||
+	     $arg == $TAGCALLBACK ) {
+      $i += 2; # Skip two args
+    } elsif ($arg == $LIST) { 
+      # do nothing
+    } elsif ($arg == $CANON) {
+      $canon=1;
+    } elsif ($arg == $NODOC) {
+      $retdoc=0;
+    } else {
+      die "htmlop: Incorrect invocation of html_process\n";
+    }
+  }
+  
+  my($endhtml)=0;	# Have we seen </html>?
+  my($SGML)=1;		# 1: !DOCTYPE not seen, 2: !DOCTYPE seen, -1: it's OK
+
+  # These are used to store the tag components
+  my($textpart);
+  my($tagname);
+  my(%attrval);
+
+  my($wholetag);	# The whole tag, put together again
+  my($moretag);		# TMP storage of tag attributes
+  my($attr);		# Looping thru attributes, misnomer.
+  my($RE);
+  my($subst);
+  my($prefix);
+  my($fun);		# Function to apply to url
+  my($verbatim)='';
+  my($url_o,$path);		# URL object Used in ABS to remove leading ..
+
+  # Welcome to the machine
+
+  $newdoc='';
+  while ($doc ne '') {
+    ($textpart,$tagname,%attrval)=&gettag($doc);
+    
+    # Process the tag
+    { # Need a way to get out of this, last is my friend
+      # 'Canonize'
+      if ($canon) {
+	$endhtml=1 if ($tagname eq '/HTML');
+	if ($SGML!=-1 && $canon && $tagname ne '!--' && $tagname ne '!SGML') {
+	  $wholetag=$tagname;
+	  $moretag=join('',keys %attrval);
+	  $wholetag.=' '.$moretag if ($moretag ne '');
+	  if ($SGML==1) {
+	    $SGML=2;
+	    if ($tagname eq '!DOCTYPE') {
+	      $tagname=$wholetag;
+	      %attrval=();
+	      last;
+	    }
+
+	    # We have no idea what DTD this doc follows so we put in
+	    # something kinda non-comitting
+	    $newdoc.='<!DOCTYPE HTML PUBLIC "html.dtd">'."\n";
+	  }
+	  if ($SGML==2) {
+	    $SGML=-1;
+	    if ($tagname eq 'HTML') {
+	      $tagname=$wholetag;
+	      %attrval=();
+	      last;
+	    }
+	    $newdoc.='<HTML>'."\n";
+	  }
+	}			# sgml!=-1
+      }				#canon
+
+      # Tack on text part before I bail out, if wanted
+      $newdoc.=$textpart if $retdoc;
+
+      if ($tagname eq 'BASE') {
+	if (exists($attrval{'HREF'})) {
+	  if ($origin) {
+	    $baseurl=(url($attrval{'HREF'})->abs($origin,1))->as_string;
+	  } else {
+	    $baseurl=$attrval{'HREF'};
+	  }
+	  # Get rid of the HREF attribute.  Netscape 4.0 puts (other) stuff
+	  # into BASE that is not found in the HTML 4.0 spec.
+	  delete $attrval{'HREF'};
+	  print STDERR "\nBase tag: $baseurl\n" if $debug;
+	}
+      }
+
+      # URL processing
+      $i=0;
+      while (defined($arg=$_[$i])) {
+	if (! ($arg =~ /^\d+$/)) {
+	  print STDERR "Args: ",join(',',@_),"\n";
+	  print STDERR "ARG: $arg ($i) is not a opcode.\n";
+	  exit(1);
+	}
+	$i++;
+	if ($arg == $ABS) {
+	  $origin=$baseurl || $_[$i++];
+	  $i++;
+	  # Want it to be a URL object
+	  $origin=url $origin unless ref $origin;
+	  print STDERR 'ABS: ',$origin->as_string,"\n" if $debug;
+	  next unless defined($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    if (defined($attrval{$attr})) {
+	      # Ugly: Remove leading /../ sequences in path component
+	      $url_o=url($attrval{$attr})->abs($origin,1);
+	      if (defined($path=$url_o->path)) {
+		# mailto: URLs does not have path components
+		$path =~ s~/\.\.(?=/)~~g;
+		$url_o->path($path);
+	      }
+	      $attrval{$attr}=$url_o->as_string;
+	    }
+	  }
+	} elsif ($arg == $REL) {
+	  $origin=$_[$i++];
+	  # Want it to be a string
+	  $origin=$origin->as_string if ref $origin;
+	  print STDERR 'REL: ',$origin,"\n" if $debug;
+	  next unless defined($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    $attrval{$attr} =~ s/^$origin//
+	      if defined($attrval{$attr});
+	  }
+	} elsif ($arg == $URLSUB) {
+	  $RE=$_[$i++];
+	  $subst=$_[$i++];
+	  warn "URLSUB: $RE -> $subst\n" if $debug;
+	  next unless defined($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    $attrval{$attr} =~ s/$RE/$subst/
+	      if defined($attrval{$attr});
+	  }
+	} elsif ($arg == $LIST) {
+	  warn "LIST;\n" if $debug;
+	  next unless exists($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    if (exists($attrval{$attr})) {
+	      $attrval{$attr}.='/'
+		if $isdir{$attr} && $attrval{$attr} =~ m~[^/]$~;
+	      push(@urllist,$attrval{$attr});
+	    }
+	  }
+	} elsif ($arg == $SAVEURL) {
+	  warn "SAVEURL;\n" if $debug;
+	  $prefix=$_[$i++];
+	  next unless exists($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    $attrval{"$prefix$attr"}=$attrval{$attr}
+	      if defined($attrval{$attr});
+	  }
+	} elsif ($arg == $USESAVED) {
+	  warn "USESAVED;\n" if $debug;
+	  $prefix=$_[$i++];
+	  next unless exists($urls{$tagname});
+	  foreach $attr (@{$urls{$tagname}}) {
+	    $attrval{$attr}=$attrval{"$prefix$attr"}
+	      if (defined($attrval{"$prefix$attr"}));
+	    # bug compatability, drop sometime after w3mir 1.0
+	    $attrval{$attr}=$attrval{"$prefix-$attr"}
+	      if (defined($attrval{"$prefix-$attr"}));
+	  }
+	} elsif ($arg == $NODOC) {
+	  warn "NODOC;\n" if $debug;
+	} elsif ($arg == $CANON) {
+	  warn "CANON;\n" if $debug;
+	} elsif ($arg == $URLPROC || $arg == $NREL) {
+	  # Apply a function to all urls.
+	  # NREL = Special case of $URLPROC, apply internal function.
+	  if ($arg == $URLPROC) {
+	    warn "URLPROC;\n" if $debug;
+	    $fun=$_[$i++];
+	    $arg=$_[$i++];
+	  } else {
+	    $fun=\&smartrel;
+	    $arg=undef;
+	    $url_origin=$_[$i++];
+	    $doc_top=$_[$i++];
+	    $doc_top_re=quotemeta $doc_top;
+	    ($choped_url_or=$url_origin) =~ s/^$doc_top_re//;
+	    warn "NREL $url_origin $doc_top\n" if $debug;
+	  }
+
+	  next unless (defined($urls{$tagname}));
+
+	  foreach $attr (@{$urls{$tagname}}) {
+	    $attrval{$attr}=&$fun($attrval{$attr},$arg)
+	      if defined($attrval{$attr});
+	  }
+	} elsif ($arg == $TAGCALLBACK) {
+	  $fun=$_[$i++];
+	  $arg=$_[$i++];
+	  warn "TAGCALLBACK($tagname);\n" if $debug;
+	  &$fun($arg,$baseurl,$tagname, 
+		(defined($urls{$tagname})?($urls{$tagname}):undef),
+		\%attrval);
+	} else {
+	  print STDERR "Internal error. opcode: $arg, i: $i, args: ",
+	  join(',',@_),"\n";
+	}
+      }
+      last;
+    }
+
+    # That ends URL processing
+
+    # Was this a verbatim leadin tag?  If yes, atempt to fish out text
+    # between here and the end tag, (minimal match) and substitute it
+    # with nothing.  The end tag is kept.  And the fished out text is
+    # re-inserted in the result with no changes.
+
+    $verbatim=$1 
+      if defined($verbatim{$tagname}) &&
+	($doc =~ s/^(.*?)($verbatim{$tagname})/$2/is);
+    
+    # Tack on the tag, if wanted.
+    if ($retdoc) {
+      if ($tagname) {
+	$newdoc.='<'.$tagname;
+	foreach $attr (keys %attrval) {
+	  $newdoc.=' '.$attr;
+	  if (defined($cont=$attrval{$attr})) {
+	    $Q='"';
+	    $Q="'" if $cont =~ m/\"/;
+	    $newdoc.='='.$Q.$cont.$Q;
+	  }
+	}
+	$newdoc.='>'.$verbatim;
+	$verbatim='';
+      }
+    }
+    print STDERR "NEW: $newdoc\n" if $debug > 2;
+  }
+  $newdoc.="</HTML>\n" if ($canon && !$endhtml);
+  return ($newdoc,@urllist);
+}
+
+
+sub smartrel {
+  # 'smart' relativisation function, uses .. to refer to things up to
+  # $doc_top level.  Outside that scope it stays absolute.
+  #
+  # The rel function itself is now in libwww-perl
+  
+  my($url_o)=@_;
+
+  return if (!(defined($url_o) && $url_o));
+  
+  $url_o=url $url_o unless ref $url_o;
+
+#  print STDERR "\nsmartrel: $url_o->as_string\n",
+#                 "    from: $url_origin\n",
+#                 "  within: $doc_top\n";
+  
+  # Check if within scope of our doc
+  $url_o = $url_o->rel($url_origin)
+    if $url_o->as_string =~ m/^$doc_top_re/;
+
+  return $url_o->as_string;
+}
+
+
+sub smart_setup {
+  # Setup routine for smartrel.
+
+  ($url_origin,$doc_top) = @_;
+
+  $url_origin=$url_origin->as_string if ref $url_origin;
+  $doc_top=$doc_top->as_string if ref $doc_top;
+
+  $doc_top_re=quotemeta $doc_top;
+
+  ($choped_url_or=$url_origin) =~ s/^$doc_top_re//;
+}
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/install.sh
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/install.sh	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/install.sh	(revision 16561)
@@ -0,0 +1,9 @@
+#!/bin/sh -v
+
+export PERL5LIB=$GSDLHOME/perllib/cpan/lib:$PERL5LIB
+export PATH=$GSDLHOME/perllib/cpan/bin:$PATH
+
+perl Makefile.PL PREFIX=$GSDLHOME/perllib/cpan
+
+make
+make install
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/multiscope.cfg
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/multiscope.cfg	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/multiscope.cfg	(revision 16561)
@@ -0,0 +1,46 @@
+# This file is meant to illustrate multiscope retrival.
+#
+# Available options: recurse, no-date-check, only-nonexistent, list-urls,
+# lowercase.  It is not default to recurse.  options must be the 
+# first directive in the the config file.
+Options: recurse
+# This directive was previously known as 'Fetch-options'
+#
+# This is the two argument form of URL:.  It fetches the first into the second
+URL: http://www.math.uio.no/~janl/ math/janl
+#
+# These says that any documents refered to that lives under these places
+# should be gotten too.  Into the named directories.  Two arguments are
+# required for 'Also:'.
+Also: http://www.math.uio.no/drift/personer/ math/drift
+Also: http://www.stud.ifi.uio.no/~janl/ ifi/janl
+Also: http://www.mi.uib.no/~nicolai/ math-uib/nicolai
+#
+# A more typical use would perhaps be:
+#   URL: http://www.math.uio.no/~janl/ janl
+#   Also: http://www.math.uio.no/images/ images
+# which would retrive the images refered to under
+# http://www.math.uio.no/~janl/ into a directory called images
+#
+# CD here before doing any work
+#cd: janlhomes
+#
+# Default pause is now 0.
+#Pause: 0
+#
+# Ignore urls we can't copy anyway.  Actually w3mir can copy
+# many cgi things with no problems.  But not always.
+Ignore: *.cgi
+Ignore: *-cgi
+Ignore: *.map
+#
+# Run fixup to get a filesystem browseable mirror
+Fixup: run
+# The options above will result in this directory hierarchy under
+# where you started w3mir:
+#
+# w3mir/math/janl		files from http://www.math.uio.no/~janl
+# w3mir/math/drift		from http://www.math.uio.no/drift/personer/
+# w3mir/ifi/janl		from http://www.stud.ifi.uio.no/~janl/
+# w3mir/math-uib/nicolai	from http://www.mi.uib.no/~nicolai/
+#
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3http.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3http.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3http.pm	(revision 16561)
@@ -0,0 +1,765 @@
+# -*- perl -*-
+# w3http.pm	--- send http requests, janl's 12" mix for w3mir
+#
+$VERSION=1.0.22;
+#
+# This implements http/1.0 requests.  We'll have problems with http/0.9
+# This is in no way specific to w3mir.
+#
+# IMPORTANT: The caller should initialize the C locale for some of the
+#   things here to work correctly (specifically the strftime function).
+#
+# This is a rewrite of http.pl by Oscar Nierstrasz; I copied the code he he
+# copied from the camel book.  Some functions written by Gorm Haug Eriksen
+# (gorm@usit.uio.no) has been used as is.
+#
+# Contributors:
+#   Nicolai Langfeldt, janl@ifi.uio.no
+#   Gorm Haug Eriksen, gorm@usit.uio.no
+#   Chris Szurgot, szurgot@itribe.net
+#   Bernhard Weisshuh, bkw@weisshuhn.de
+# Copying and modification is governed by the "Artistic License" enclosed in
+# the w3mir distribution
+#
+# gorm :
+# &w3http::get_last_modified  return the last modified stamp on a file in
+#                         the right format for use with http
+#
+# janl:
+# &http::query: Send a http query.  A completely general function to send a
+#   http query.  Will extract header values, http response code and, optionaly,
+#   convert text files to local linefeed format.
+#
+# Variables to examine after a query
+# $w3http::document: The document returned by the query, if any.
+# $w3http::doclen: The length of the document
+# $w3http::result: The numerical http result code.  It may take these values:
+# - Normal HTTP reply codes
+# -  98: OS error, permanent, errormessage in $!
+# -  99: Other permanent error: see $restext.
+# - 100: Transient error: Timeout/connection broken
+# $w3http::restext: The english(?) HTTP result or w3http generated message
+# $w3http::header: The http header returned.
+# $w3http::plaintexthtml: 1 if this doc is non-content-encoded text/html
+#	(as opposed to content-encoding: compressed content-type: text/html
+#       which needs decompression before we can inspect the html) 
+#	The tests are somewhat longwinded so I do it just once here.
+# %w3http::headval: Associative array of header values
+# $w3http::headval{'CONTENT-TYPE'}: Derived content type, stripped of charset
+#		qualifiers and other distractions.
+# $w3http::xfbytes: Transfered bytes, cumulative.  Document part only.
+# $w3http::headbytes: Bytes of headers received, cumulative.
+#
+# Variables that change http's behaviour/requests:
+# $w3http::agent: User agent, default is basename of $0
+# $w3http::from: Request is from, default is user@host
+# $w3http::version: The http version to use, only 1.0 is known to me.
+# $w3http::timeout: How long to wait for new data to arrive, default is 600sec
+# $w3http::buflen: Network read buffer size, default is 4096.  It might give a
+#	speedup to tune this for specific servers' so it matches their send
+#	size.  This size can be detected if we want to, I think.
+# $w3http::debug: 1 debuging output, 2, more, 3 queries and replies
+# $w3http::verbose: 0: say nothing, 1: print progress info
+# $w3http::convert: Convert text/* documents to local newline convention?
+#	The default is to do it.
+# $w3http::proxyserver: The name of the proxyserver to use.
+# $w3http::proxyport: The port of the proxyserver to use. 0 if no proxyserver.
+# $w3http::proxyuser: If this is set proxy authentication will be used.
+# $w3http::proxypasswd: The password for proxy authentication
+#
+# Things gotten from main:
+# - $main::win32: 1 if win32 restrictions apply to this system
+# - $main::nulldevice: Bit sink file/device on this system.
+#
+# History (european date format dd/mm/yy):
+#     janl ??/??/95 -- Rewrite finished
+#  szurgot ??/??/95 -- Win32 compatability
+#     janl 16/05/96 -- Added SAVEBIN option, based on idea by szurgot
+#  szurgot 03/05/96 -- Corrected typo in check for content-length against
+#                      retreive document length. Added test for zero-length
+#		       documents (Not retreived because not-modified)
+#  szurgot 19/05/96 -- Win32 adaptions, fixes.
+#     janl 19/05/96 -- Chris won an argument, and janl simplified http 
+#		       retrival loop (-> version 1.0.4)
+#     janl 09/09/96 -- Incorporated a patch submited by Michael Kriby -> 1.0.5
+#     janl 16/09/96 -- Support for authorization. -> 1.0.6
+#     janl 27/09/96 -- Support for Accept header, lack pointed out by
+# 			charles@ermine.ox.ac.uk: ... HTTP/1.1 (§14.1) says 
+#			``If no Accept header field is present, then it is
+#			assumed that the client accepts all media types,
+#			earlier versions of the protocol suggest that only
+#			text/plain and text/html will be offered by default.''
+#			This contradicts my memory of a http/1.0 draft.
+#			Also added $ACCEPT option.
+#     janl 20/10/96 -- Now uses HTTP::Date to produce HTTP timestamps -> 1.0.7
+#     janl 27/10/96 -- Didn't use to check if gethostbyname worked -> 1.0.8
+#     janl 02/12/96 -- Forgot a unlink when renaming temporary files.
+#     janl 21/02/97 -- Multipele $ACCEPT options work. -> 1.0.9
+#     janl 19/03/97 -- Now issues Host: header -> 1.0.10
+#     janl 10/04/97 -- Changed from wwwurl to URI::URL, and various related
+#                      changes. -> 1.0.11
+#     janl 09/05/97 -- Microsoft ISS servers are _so_ broken -> 1.0.12
+#                      (don't close the write end of the HTTP socket after
+#			sending a query to them)
+#     janl 12/05/97 -- New version of perl caught some typos, fixed
+#			longstanding bug in the newline conversion bit.
+#			-> 1.0.13
+#     janl 06/06/97 -- Demand Loading of MIME::BASE64 -> 1.0.14
+#     janl 01/12/97 -- FAT filesystems drops LSB of modtime.  Patch from
+#			Greg Lindhorst (gregli@microsoft.com)
+#                   -- whoami does not exist on win32, hardwire a default
+#			value (unknown) (also Greg L.) -> 1.0.15
+#     janl 01/22/98 -- Proxy authentication as outlined by Christian Geuer
+#     janl 02/20/98 -- Complex 'content-type' headers handled. -> 1.0.17
+#     janl 04/20/98 -- Only newline convert text/html, everything else is
+#			handled as binary. -> 1.0.18
+#     janl 12/05/98 -- Store tmpfile in its final destination directory
+#			avoiding asking movefile move it across filesystems.
+#			-> 1.0.19
+#     janl 01/08/98 -- Timeout fix from Michael Gusev, also flag short doc
+#			as error.
+#     janl 24/09/98 -- Better error handling -> 1.0.20
+#     bkw  17/12/98 -- Fixed problem with tempfile-generation when
+#                      running in forget-mode (-f)
+#     janl 05/01/99 -- Referer: dropped if argument not true -> 1.0.21
+#     janl 13/04/99 -- Added workaround for broken win32 perl resolving.
+
+package w3http;
+
+require 5.002;
+use Socket;
+use HTTP::Date;
+use Sys::Hostname;
+use URI::URL;
+
+# Suplementary libwww-perl:
+sub URI::URL::_generic::basename {
+  my $self = shift;
+  my @p = $self->path_components;
+  my $old = $p[-1];
+  if (@_) {
+    splice(@p, -1, 1, shift);
+    $self->path_components(@p)
+  }
+  $old;
+}
+
+
+END { 
+  # Remove tmp file and such in here.  That means that main:: gotta catch
+  # interrupt signals and exit on them, so ENDs are executed.
+}
+
+use strict;
+# Global variables, we want to share them:
+use vars qw($GET $HEAD $GETURL $HEADURL $IFMOD $IFMODF $AUTHORIZ $REFERER);
+use vars qw($SAVEBIN $ACCEPT $NOUSER $FREEHEAD $agent $version $timeout);
+use vars qw($debug $convert $proxyserver $proxyport $xfbytes $headbytes);
+use vars qw($verbose $result $restext $header $document);
+use vars qw($plaintexthtml %headval $progress $doclen $proxyuser);
+use vars qw($proxypasswd);
+
+my $hasAlarm;   # Win32 does not have any alarm
+my $chime;	# Has the alarm gone off yet?
+my %address;	# My own DNS cache
+my $savALRM;	# Saved ALRM handler
+my $savPIPE;	# Saved PIPE handler
+
+# The main:: program should detect if we're running on win32 or not,
+# somehow
+if ($main::win32) {
+  warn "win32\n";
+  # Compensate for lacks of win32 perl. 
+  $hasAlarm=0;
+  # Seems to be unavailable in win32/perl5.001.  It has to be in 5.003!
+#  eval "sub sockaddr_in {
+#	($port, $thataddr) = @_;
+#	$sockaddr = 'S n a4 x8';
+#	return pack($sockaddr, &AF_INET, $port, $thataddr);
+#    }";
+} else {
+  $hasAlarm=1;
+}
+
+
+# Find out some things
+my $thishost = hostname();
+my $proto = getprotobyname("tcp");
+
+(my $name, undef) = gethostbyname($thishost);
+chomp(my $user = $ENV{'LOGNAME'} || $ENV{'USER'} || `whoami` || 'unknown');
+my $from   = "$user\@$name";
+
+my $nl = "\r\n";
+# Default values, change by assignment in using-program.
+$agent  = $0; $agent =~ s~.*/~~; # Basename 
+$version= "1.0";
+$timeout= 600;			# Timeout while waiting for data/connection
+my $buflen = 4096;		# recv buffer length
+$debug = 0;			# Debuging output?
+$convert = 1;			# Convert newlines of text docs to local format
+$proxyserver='';		# Proxy server.
+$proxyport=0;			# Proxy server port. 0 if no proxy.
+$proxyuser='';			# Username for proxy authentication
+$proxypasswd='';		# Password for proxy authentication
+$xfbytes=0;			# 0 bytes transfered, cumulative
+$headbytes=0;			# 0 bytes of headers, cumulative
+$doclen=0;			# 0 bytes in doc, pr. document
+my $tmpfile="w3mir$$.tmp";	# Temporary filename
+$verbose=0;			# Verbosenes, 0: silent, 1: progress info
+
+# Query opcodes
+$GET = 1;			# GET query. Arg: host,port,path
+$HEAD = 2;			# HEAD query. Arg: host,port,path
+$GETURL = 3;			# GET query. Arg: url
+$HEADURL = 4;			# HEAD query. Arg: url
+# Here we lack PUT, which is not implemented
+# Modify query thus:
+$IFMOD = 101;			# If-modified after: Arg: HTTP-date-str
+$IFMODF = 102;			# If-modified after file: Arg: local-file-name
+$AUTHORIZ= 103;			# Basic authorization. Arg: 'user:password'
+$REFERER = 104;			# Referer: Arg: Referer 
+$SAVEBIN = 105;			# Write binary files to disk. Arg: File name
+				# If this opcode is used then main must provide
+				# a &main::movefile(oldname,newname) procedure
+				# that handles moving the tmp file to the
+				# final name/location.
+$ACCEPT  = 106;			# Accept header value: Arg: value
+$NOUSER  = 107;			# Don't insert user header.  Arg: none
+$FREEHEAD= 999;			# Freeform header, one line.  Arg: header
+
+sub query {
+  # Build and send a HTTP query.  And also receive response - janl 95/09/18
+  #
+  # Return codes: 0 if it didn't work.  1 if it did work.
+  # HTTP style result code in w3http::$result and message in w3http::$restext
+  
+  # We do next to no argument type checking btw.
+  
+  my($host,$port,$request,$query,$method,$inp,$linp,$saveto,$save,$arg);
+  my($start,$wantbytes,$thataddr,$err,$headb,$tmpf,$ldoc,$nouser,$q,$accept);
+  my($origreq,$req_o,$plaintext);
+  
+  # Something ought to be said
+  $result=99;
+  $restext='w3http: internal error';
+  $nouser=0;
+  
+  if ($version ne '1.0') {
+    warn "Unknown HTTP version $version, no request sent\n";
+    return 0;
+  }
+  
+  $accept=$saveto=$query='';
+  
+  # Find out what to ask for
+  
+  while (defined($arg=shift)) {
+    if ($arg == $GET) {
+      $host=shift;
+      $port=shift;
+      $request=shift;
+      $req_o=url 'http://'.$host.':'.$port.$request;
+      if ($proxyport) {
+	$query.='GET http://'.$req_o->as_string;
+      } else {
+	$query.='GET '.$req_o->epath;
+      }
+      $query.=' HTTP/'.$version.$nl;
+    } elsif ($arg == $HEAD) {
+      $host=shift;
+      $port=shift;
+      $request=shift;
+      $req_o=url 'http://'.$host.':'.$port.$request;
+      if ($proxyport) {
+	$query.='HEAD '.$req_o->as_string;
+      } else {
+	$query.='HEAD '.$req_o->epath;
+      }
+      $query.=' HTTP/'.$version.$nl;
+    } elsif ($arg == $GETURL) {
+      $req_o=shift;
+      $req_o=url $req_o unless ref $req_o;
+      ($method,undef,undef,$host,$port,$request,undef,$q) = $req_o->crack;
+      if ($proxyport) {
+	$query.='GET '.$req_o->as_string;
+      } else {
+	$q=$req_o->equery;
+	$query.='GET '.$request.($q?"?$q":'');
+      }
+      $query.=' HTTP/'.$version.$nl;
+    } elsif ($arg == $HEADURL) {
+      $req_o=shift;
+      $req_o=url $req_o unless ref $req_o;
+      if ($proxyport) {
+	$query.='HEAD '.$req_o->as_string;
+      } else {
+	$q=$req_o->equery;
+	$query.='HEAD '.$req_o->epath.($q?"?$q":'');
+      }
+      $query.=' HTTP/'.$version.$nl;
+    } elsif ($arg == $IFMOD) {
+      $query.='If-Modified-Since: '.(shift).$nl;
+    } elsif ($arg == $IFMODF) {
+      $query.='If-Modified-Since: '.&last_modified(shift).$nl;
+    } elsif ($arg == $AUTHORIZ) {
+      # Demand-load MIME::Base64
+      if (!defined(&MIME::Base64::encode)) {
+	eval "use MIME::Base64;";
+	die "w3http: Could not load MIME::Base64 module necessary for authentication\n"
+	  unless defined(&MIME::Base64::encode);
+      }
+      $query.='Authorization: Basic '.MIME::Base64::encode(shift,'').$nl;
+    } elsif ($arg == $REFERER) {
+      my($referer)=shift;
+      $query.='Referer: '.$referer.$nl if $referer;
+    } elsif ($arg == $SAVEBIN) {
+      $saveto=shift;
+    } elsif ($arg == $ACCEPT) {
+      $accept.='Accept: '.(shift).$nl;
+    } elsif ($arg == $NOUSER) {
+      $nouser=1;
+    } elsif ($arg == $FREEHEAD) {
+      $query.=(shift).$nl;
+    } else {
+      warn "Unknown http query opcode: $arg\n";
+    } 
+    # Insert the last parts of the query:
+  }
+  
+  $query.='Host: '.$req_o->netloc.$nl;
+  $query.='From: '.$from.$nl unless $nouser;
+
+  $accept='Accept: */*'.$nl unless $accept;
+
+  if ($proxyport) {
+    # Use proxy instead of originserver
+    $host=$proxyserver;
+    $port=$proxyport;
+
+    # Add authentication stuff to query
+    if ($proxyuser) {
+      # Demand-load MIME::Base64
+      if (!defined(&MIME::Base64::encode)) {
+	eval "use MIME::Base64;";
+	die "w3http: Could not load MIME::Base64 module necessary for authentication\n"
+	  unless defined(&MIME::Base64::encode);
+      }
+      
+      $query.='Proxy-Authorization: Basic '.
+	MIME::Base64::encode($proxyuser.':'.$proxypasswd);
+
+      print STDERR "\nProxyuser: [$proxyuser]\nProxypasswd: [$proxypasswd]\n"
+	if $debug>=2;
+    }
+  }
+  
+  $query.='User-Agent: '.$agent.$nl.$accept.$nl;
+  
+  # If we're using proxy then set up things...
+  print STDERR "\nQUERY:\n",$query,"---\n" if $debug>=2;
+
+  # win32 fix: this should be added in case of troubles with
+  # gethostbyname. possible reason: nameserver down?
+  if ($host =~ /^\d+(\.\d+){3}$/) {
+    # in case gethostbyname will not work ... ;-)
+    $address{$host} = pack 'C4', (split /\./, $host);
+  }
+  
+  # Find out who to ask, check if we know already
+  if (exists($address{$host})) {
+    # We know
+    $thataddr=$address{$host};
+  } else {
+    # Cache miss, get and remember.
+    (my $fqdn, undef, undef, undef, $thataddr) = gethostbyname($host);
+    # Hostname lookup failure?  Cache even misses.
+    if (defined($fqdn)) {
+      print STDERR "Lookup of $host:\nFQDN: $fqdn\n"
+	if $debug;
+      $address{$host}=$thataddr;
+      $address{$fqdn}=$thataddr if $fqdn ne $host;
+    } else {
+      $thataddr=$address{$host}=undef;
+    }
+  }    
+
+  # Check if lookup failure, return
+  if (!defined($thataddr)) {
+    $restext='Host lookup failure';
+    return;
+  }
+
+  $port=80 unless defined($port) && $port;
+
+  # When connected we might receive SIGPIPE.  I'm not sure if the
+  # default behaviour of dying is beneficial in that case.  If we get
+  # alarm a timeout has expired.
+  $savPIPE = $SIG{'PIPE'};
+  $savALRM = $SIG{'ALRM'};
+
+  $chime=0;			# There has been no alarm yet
+  $SIG{'ALRM'} = \&timeout;
+  $SIG{'PIPE'} = \&ignore;
+
+  # Close the socket, just in case, and ignore error returns
+  close(FS);
+  
+  socket(FS, AF_INET, SOCK_STREAM, $proto) or return &oserror;
+  warn "Got my socks on\n" if $debug;
+  
+  my $paddr = sockaddr_in($port, $thataddr);
+  connect(FS, $paddr) or return &oserror;
+  warn "Connected\n" if $debug;
+  
+  # Arrange timeout
+  alarm($timeout) if $hasAlarm;
+  
+  # We have, in fact, received SIGPIPE on this line:
+  send(FS,$query,0) or return &oserror;
+
+  if ($chime) {
+    $result=100;
+    $restext='timeout sending query';
+    return &resetsign;
+  }
+  
+  $header='';
+  $document='';
+  $inp=' 'x$buflen;
+  $doclen=$chime=$plaintext=$plaintexthtml=$save=0;
+
+  # Breaks some M$ ISS servers:
+  # shutdown(FS,1);  # Half-close socket, sending now not allowed
+  
+  print STDERR ", receiving header" if $verbose>0;
+  
+  # Retrive HTTP response HEADER.  Why do I use recv and not <FS>?
+  # Because then the timeout can work correctly!
+  while (1) {
+    # Set up alarm to ensure recv returns within a reasonable timeframe
+    alarm($timeout) if $hasAlarm;
+    $err = recv(FS,$inp,$buflen,0);
+    # recv returned, cancel alarm.
+    alarm(0) if $hasAlarm;
+    
+    # If there has been a timeout, then we quit now.  The recv man page
+    # does not seem to allow recv to return the bytes received up to
+    # the timeout.
+    if ($chime) {
+      $result=100;  
+      $restext='timeout fetching document';
+      $!=0;
+      if ($save) {
+	unlink($tmpf) || 
+	  warn "Could not unlink $tmpf: $!\n";
+      }
+      return &resetsign;
+    }
+    
+    # recv returnes the undefined value on error
+    if (!defined($err)) {
+      warn "Error in recv: $!\n";
+      last;
+    }
+    
+    $linp=length($inp);
+    
+    # If the returned input was 0 in length then we've gotten to the 
+    # end of the response.
+    last unless $linp;
+    
+    # Accounting
+    $xfbytes += $linp;
+    $doclen += $linp;
+    
+    # Accumulate input
+    $header.=$inp;
+
+    # eof(SOCKET) has strange semantics it seems
+    # last if eof(FS);
+    
+    # Check if header is complete
+    last if ($header =~ m/(\r?\n\r?\n)/);
+  }
+  
+  if (length($header)==0) {
+    $restext='the HTTP reply header is empty!';
+    return &resetsign;
+  }
+  
+  if ($header =~ m/(\r?\n\r?\n)/) {
+    $header=$`;
+    $document=$';
+  } else {
+    $header=$document;
+  }
+  
+  # Adjust accounting
+  $headb = length($header)+length($1);
+  $headbytes += $headb;
+  $xfbytes -= $headb;
+  $doclen -= $headb;
+  
+  # Pick headers to pieces
+  ($result,$restext,%headval)=&analyze_header($header);
+
+  print STDERR "REPLY:\n",$header,"\n---\n" if $debug>=2;
+
+  # Check if the document is a non-encoded text document. The contents
+  # could be (x-)?compress or (x-)gzip coded (compressed in other
+  # words).
+  
+  $plaintext=defined($headval{'CONTENT-TYPE'}) &&
+    (substr($headval{'CONTENT-TYPE'},0,5) eq 'text/' || 0) &&
+      !defined($headval{'content-encoding'});
+  $plaintexthtml=$plaintext && 
+    ($headval{'CONTENT-TYPE'} eq 'text/html');
+  
+  if ($result==200) {
+    
+    # Save this to a file, or not?  Never save html files.
+    if ($saveto && !$plaintexthtml) {
+      # We're going to save this document directly into a file.  This
+      # stresses the VM less when getting the large binares so often
+      # found at cool sites.
+      $save=1;
+
+      # Find a temporary filename
+      $tmpf=url "file:$saveto";
+      $tmpf->basename($tmpfile);
+      $tmpf=$tmpf->unix_path;
+      
+      # Find suitable final filename, one with no URL escapes
+      $saveto=(url "file:$saveto")->unix_path;
+
+      # If output to stdout then send it directly there rather than
+      # using disk unnecesarily.
+      $tmpf='-' if ($saveto eq '-');
+
+      # If output is nulldevice (running -f), use it also for tmpfile,
+      # since it would otherwise try to create it in /dev under unix.
+      $tmpf=$main::nulldevice if ($saveto eq $main::nulldevice);
+      
+      warn "USING TMPFILE: $tmpf\n" if $debug;
+      
+      open(SAVE,">$tmpf") || 
+	die "Could not open tmp file: $tmpf: $!\n";
+      binmode SAVE;		# It's a binary file...
+    }
+    
+    if ($verbose>0) {
+      print STDERR ", document";
+      print STDERR "->disk" if $save;
+    }
+    
+    # Now retrive document itself.  Se comments in header loop
+    $start=time;
+    $wantbytes = defined($headval{'content-length'})?
+      $headval{'content-length'}:0;
+    
+    $ldoc=length($document);
+    
+    while (1) {
+      alarm($timeout) if $hasAlarm;
+      recv(FS,$inp,$buflen,0);
+      alarm(0) if $hasAlarm;
+      
+      if ($chime) {
+	$result=100;
+	$restext='timeout fetching document';
+	$!=0;
+	if ($save) {
+	  unlink($tmpf) || 
+	    warn "Could not unlink $tmpf: $!\n";
+	}
+	return &resetsign;
+      }
+      
+      $linp=length($inp);
+      
+      last unless $linp || $ldoc;
+      $ldoc = 0;
+      
+      $xfbytes += $linp;
+      $doclen += $linp;
+      
+      if ($verbose>0 && time-$start>5) {
+	# Write progress info ... 
+	if ($wantbytes) {
+	  $progress = sprintf " %3d%%", $doclen/$wantbytes*100;
+	} else {
+	  $progress = sprintf " %d", $doclen;
+	}
+	print STDERR $progress, "\ch"x(length($progress));
+	# ...every 5 seconds
+	$start=time;
+      }
+      
+      $document.=$inp;
+      
+      if ($save) {
+	$err = print SAVE $document;
+	die "Error writing $tmpf: $!\n" unless $err;
+	$document='';
+      }
+
+      # The eof test seems to work very oddly for sockets.
+      # last if eof(FS);
+    }
+
+    close(FS);  # Close socket completely
+    
+    print STDERR "DOCUMENT:\n----\n",$document,"\n----\n" if $debug>=255;
+  
+    if ($wantbytes &&
+	$wantbytes != $doclen) {
+      $result=100;
+      $restext='transfer error; too many bytes in document';
+      $restext='document was incomplete' if ($wantbytes > $doclen) ;
+      print STDERR "SHORT DOCUMENT" if $debug>=16;
+      if ($save) {
+	unlink($tmpf) || warn "Could not unlink $tmpf: $!\n";
+      }
+      return &resetsign;
+    }
+    
+    # warn "XFB: $xfbytes, DL: $doclen\n";
+    if ($save) {
+      close(SAVE);
+      &main::movefile($tmpf,$saveto);
+    }
+    
+    # If this is a non-encoded text file and we're supposed to convert
+    # foreign newlines then we do it. It would be faster to do this
+    # with each chunk of input in the input loop, but this gives us
+    # two problems:
+    # - A \r\n newline could be split into two chunks.  Thus escaping
+    #   newline conversion.
+    # - It messes up the received bytes accounting rather badly.
+    #
+    # This used to be a test for $plaintext, the problem is that too
+    # many documents were typed as text/plain and so we corrupted
+    # binary files. This is bad. So now we're more paranoid about it:
+    # Only HTML gets converted.
+    if ($convert && $plaintexthtml) {
+      # Change non unix newlines to unix newlines. bare \r is known
+      # from macintosh (they hadta be different didn't they?), \r\n is
+      # known as 'network format' and from numerous systems, among
+      # them ms-dos.
+      $document =~ s~\r~\n~g unless $document =~ s~\r\n~\n~g;
+      warn "Newlines converted(?)\n" if $debug;
+    }
+    
+  }				# if $result == 200
+  
+  &resetsign;
+  return 1;
+}
+
+
+sub analyze_header {
+  my($header)=@_;
+  my($result,$restext,%headval,$hdln,$key,$value);
+  
+  # Summary of the http spec on headers (with my comments):
+  # - Each header line ends in CRLF (or just LF, or maybe even just CR,
+  #   anyways, it's easier if all is LF).
+  $header =~ s/\r/\n/mg unless $header =~ s/\r\n/\n/mg;
+  # - If a line starts with space then it's a continuation of the previous
+  #   line (these I fold into one line).
+  $header =~ s/\n\s/ /mg;
+  # - The header field names are case insensitive (so I convert them to
+  #   lowercase)
+  # - A field may appear twice, that is equivalent to listing the values
+  #   in a comma separated list (so I fold them into a comma separated list)n
+  # - The field name and the field value are separated by ': '
+  ($result,$restext) = $header =~ m~^HTTP/\d\.\d (\d\d\d) (.*)~;
+  # Shave off http result code from the header
+  $header =~ s~^.*\n~~;
+  
+  warn "Header:\n$header\n---\n" if $debug>=3;
+  
+  warn "Result: $result, Text: $restext\n" if $debug>=2;
+  
+  %headval=();
+  
+  foreach $hdln (split(/\r?\n/m,$header)) {
+    ($key,$value)=split(': ',$hdln,2);
+    $key="\L$key";
+    # Strip leading&trailing space off the reply, some servers use
+    # copious space after.
+    $value =~ s/^\s+|\s+$//g;
+    print STDERR "K: '$key', V: '$value'\n" if $debug>=2;
+    if (defined($headval{$key})) {
+      $headval{$key}.=", ".$value;
+    } else {
+      $headval{$key}=$value;
+    }
+  }
+
+  # See if there are any type parameters in the content-type header
+  # and if so remove them.
+  if (defined($headval{'content-type'})) {
+    my $val=$headval{'content-type'};
+    ($val,undef)=split(';',$val,2) if ($val =~ /;/);
+    $headval{'CONTENT-TYPE'}=$val;
+  }
+  
+  return ($result,$restext,%headval);
+}
+
+
+sub last_modified {
+  # will return the last modified time for a local file as a HTTP
+  # timestamp.
+  
+  my(@tmp) = stat($_[0]);	# file doesn't exist ok to fetch
+
+  # FAT file systems strip the LSB of the file time.  Add it back in
+  # here before asking the server about a modified file.  The only way
+  # this can fail is if the newer server file was saved one second
+  # after the first version (very unlikely).  This isn't needed for
+  # NTFS file systems, but there is no good portable Perl way to
+  # determine the file system type.
+  $tmp[9] = $tmp[9] | 1 if ( $main::win32 );
+
+  # now we got the last modified in a 32 bit integer.  time to convert
+  # it and return
+  return time2str($tmp[9]);
+}
+
+
+sub timeout {
+  # Set timeout flag.  The using procedure has to set other result codes.
+  $chime=1;			# When this is 1 then the alarm has gone off
+  print STDERR "TIMEOUT!!!!\n" if $debug>=16;
+}
+
+
+sub ignore {
+  warn "I got SIGPIPE, ignoring it...\n";
+}
+
+
+sub resetsign {
+  return 0 if !defined($savALRM);
+  $SIG{'ALRM'}=$savALRM;
+  undef $savALRM;
+#  $SIG{'PIPE'}=$savPIPE;
+  return 0;
+}
+
+
+sub oserror {
+  
+  resetsign;
+
+  $result=98;
+  $restext='w3http: OS error';
+  return 0;
+
+}
+
+
+1;
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mfix.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mfix.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mfix.PL	(revision 16561)
@@ -0,0 +1,1070 @@
+# -*-perl-*-
+
+use Config;
+
+&read_makefile;
+$fullperl = resolve_make_var('FULLPERL') || $Config{'perlpath'};
+$islib = resolve_make_var('INSTALLSITELIB');
+
+$name = $0;
+$name =~ s~^.*/~~;
+$name =~ s~.PL$~~;
+
+open(OUT,"> $name") || 
+  die "Could open $name for writing: $!\n";
+
+print "writing $name\n";
+
+while (<DATA>) {
+  if (m~^\#!/.*/perl.*$~o) {
+    # This substitutes the path perl was installed at on this system
+    # _and_ removed any (-w) options.
+    print OUT "#!",$fullperl,$1,"\n";
+    next;
+  }
+  if (/^use lib/o) {
+    # This substitutes the actuall library install path
+    print OUT "use lib '$islib';\n";
+    next;
+  }
+  print OUT;
+}
+
+close(OUT);
+
+# Make it executable too, and writeable
+chmod 0755, $name;
+
+#### The library
+
+sub resolve_make_var ($) {
+
+  my($var) = shift @_;
+  my($val) = $make{$var};
+
+#  print "Resolving: ",$var,"=",$val,"\n";
+  
+  while ($val =~ s~\$\((\S+)\)~$make{$1}~g) {}
+#  print "Resolved: $var: $make{$var} -> $val\n";
+  $val;
+}
+
+
+sub read_makefile {
+
+  open(MAKEFILE, 'Makefile') || 
+    die "Could not open Makefile for reading: $!\n";
+
+  while (<MAKEFILE>) {
+    chomp;
+    next unless m/^([A-Z]+)\s*=\s*(\S+)$/;
+    $make{$1}=$2;
+#    print "Makevar: $1 = $2\n";
+  }
+
+  close(MAKEFILE)
+}
+
+__END__
+#!/local/bin/perl5 -w
+# Perl 5.002 or later.  w3mir is mostly tested with perl 5.004
+#
+# You might want to change or comment out this:
+use lib '/hom/janl/lib/perl';
+#
+# Can perform the following fixes:
+# - Rewrite redirected to URLs
+# - change .../ into .../index.html (or .../Welcome.html)
+# - change external links to point to some helpfull .html file ---- NOT IMPLEMENTED
+# - change links to documents not retrived to point to some helpfull
+#   .html file --- NOT IMPLEMENTED
+# - After adding a 'Also' directive we can edit the urls pointing to within
+#   the new space of the retrival scope, making the pointers consistent.
+
+# Method: 
+# 1 Gather list of all URLs to be rewritten:
+#   - Redirects: Just read the .redirs file
+#   - .../ into .../index.html: All can be found in the .referers file
+#     Remember to rewrite missing / redirects too...
+#   - external links: in the .referers file too
+#   - non-retrived documents: What files should be here (according to
+#	.referers file) but are not?
+# 2 Gather list of all documents needing editing
+# 3 Edit them
+#
+# 09/05/98 janl		- Only rewrite .../ to .../$indexname if the attribute
+#			  does not refer to a directory. -> 0.6.2
+# 12/05/98 janl		- Use ->local_path to determine filename -> 0.6.3
+# 22/05/98 janl		- .../#foo was not being rewritten to 
+#			  .../$indexname#foo -> 0.6.4
+#
+require 5.002;
+
+use vars qw($win32);
+
+# To figure out what kind of system this is
+BEGIN {
+  use Config;
+  $win32 = ( $Config{'osname'} eq 'MSWin32' ); 
+}
+
+use Carp;
+use htmlop;
+use URI::URL;
+use URI::Escape;
+
+use strict;
+
+my $VERSION;
+$VERSION='0.6.5';
+
+my $debug=0;			# Not debugging
+my $verbose=0;
+
+my $indexname='index.html';
+my $chdirto='';			# Place to chdir to after reading
+                                # config file
+
+my $infoloss=0;			# 1 if any URL translations (which
+                                # cause information loss) are in
+                                # effect.  If this is true we use the
+                                # SAVEURL operation.  What to get, and
+                                # not.  Text of user supplied
+                                # fetch/ignore rules
+
+my $doindex=1;			# append $indexname to /$ ?
+
+my $editthis='';		# Edit references matching this expression.
+
+my $files=0;			# How many files have I edited?
+my $rc='';
+
+my $rule_text="# User defined fetch/ignore rules\n";
+# Code ref to the rule procedure
+my $rule_code;
+
+# Code to prefix and postfix the generated code.  Prefix should make
+# $_ contain the url to match.  Postfix should return 1, the default
+# is to get the url/file.
+my $rule_prefix='$rule_code = sub { local($_) = shift;'."\n";
+my $rule_postfix=' return 1; } ';
+
+# Scope tests generated by URL/Also directives in cfg. The scope code
+# is just like the rule code, but used for program generated
+# fetch/ignore rules related to multiscope retrival.
+my $scope_fetch="# Automatic fetch rules for multiscope retrival\n";
+my $scope_ignore="# Automatic ignore rules for multiscope retrival\n";
+my $scope_code;
+
+my $scope_prefix='$scope_code = sub { local($_) = shift;'."\n";
+my $scope_postfix=' return 0; } ';
+
+# Function to apply to urls, se rule comments.
+my $user_apply_code;	# User specified apply code
+my $apply_code;		# w3mirs apply code
+my $apply_prefix='$apply_code = sub { local($_) = @_;'."\n";
+my $apply_lc=' $_ = lc $_; ';
+my $apply_postfix=' return $_; } ';
+my @user_apply;		# List of users apply rules.
+my @internal_apply;	# List of w3mirs apply rules.
+
+
+my $iinline='';			# inline RE code to make RE caseinsensitive 
+my $ipost='';			# RE postfix to make it caseinsensitive
+my $lc=0;			# Convert urls/filenames to lowercase?
+my $abs=0;			# Absolutify URLs?
+my $fixrc='';			# Name of w3mfix config file
+my $fixup=0;			# Do things needed to run fixup
+my $r=0;			# Recurse? no recursion = absolutify links
+my %rum_referers=();		# Array of referers, key: rum_url
+my %rum_redirected=();		# Array of redirected url: key: original url
+my %lf_edited=();		# Edited this file yet?
+my $list;			# List url on STDOUT?
+
+my %stat=();			# stat($lf_url): 'd' for dir, 'f' for others
+
+# ######################### Libwww-perl addons:
+
+sub URI::URL::_generic::unix_path {
+  my $self = (shift)->clone;
+
+  $self->frag(undef);
+  $self->scheme("file");
+  $self->unix_path;
+}
+
+
+sub URI::URL::_generic::basename {
+  my $self = shift;
+  my @p = $self->path_components;
+  my $old = $p[-1];
+  if (@_) {
+    splice(@p, -1, 1, shift);
+    $self->path_components(@p)
+  }
+  $old;
+}
+
+
+sub URI::URL::_generic::path_components {
+    my $self = shift;
+    my $old = $self->{'path'};
+    $old = '' unless defined $old;
+    $old = "/$old" if $old !~ m|^/| && defined $self->{'netloc'};
+    if (@_) {
+	$self->_elem('path',
+		     join("/", map { uri_escape($_,$URI::URL::reserved)
+				   } @_));
+    }
+    map { uri_unescape($_) } split("/", $old, -1);
+}
+
+
+# ######################### Configuration/argument parsing
+
+sub parse_args {
+  my $f;
+  my $i;
+
+  $i=0;
+
+  while ($f=shift) {
+    $i++;
+    # This is a demonstration against Getopts::Long.
+    if ($f =~ s/^-+//) {
+      $verbose=-1,next if $f eq 'q'; 		# Quiet
+      $verbose=1,next if $f eq 'c';		# Chatty
+      die "w3mfix version $VERSION\n" if $f eq 'v';	# Version
+      die "rtfm\n" if ($f eq 'help' || $f eq 'h' || $f eq '?');
+
+      if ($f eq 'editref') {
+	die "Sorry, can only have one -editref pr. run\n"
+	  if $editthis;
+	$editthis=quotemeta(shift);
+	next;
+      }
+
+      if ($f eq 'd') {	# Debugging level
+	$f=shift;
+	unless (($debug = $f) > 0) {
+	  die "w3mfix: debug level must be a number greater than zero.\n";
+	}
+	next;
+      }
+
+      # Those were all the options...
+      warn "w3mfix: Unknown option: -$f.  Use -h for usage info.\n";
+      exit(1);
+    } else {
+      # If we get this far then ... it's a configuration file name:
+      $rc = $f;
+      die "w3mfix: Got a non-option argument that wasn't the name of a\n".
+	"(configuration) file either\n"
+	  unless -f $f;
+    }
+  }
+}
+
+
+sub parse_cfg_file {
+  # Read the configuration file.  Aborts on errors.
+  # Ignores w3mir options w3mfix does not need itself.
+
+  my ( $file ) = @_ ;
+  my ($key, $value, $authserver,$authrealm,$authuser,$authpasswd);
+  my $i;
+
+  die "w3mfix: config file $file is not a file.\n" unless -f $file;
+  open(CFGF, $file) || die "Could not open config file $file: $!\n";
+
+  # print STDERR "Reading $file\n";
+
+  $i=0;
+
+  while (<CFGF>) {
+    # Trim off various junk
+    chomp;
+    s/^#.*//;
+    s/^\s+|\s$//g;
+    # Anything left?
+    next if $_ eq '';
+    # Examine remains
+    $i++;
+    ($key, $value) = split(/\s*:\s*/,$_,2);
+    $key = lc $key;
+
+    # These are no-ops in w3mfix
+    next if ( $key eq 'initial-referer' );
+    next if ( $key eq 'header' );
+    next if ( $key eq 'pause' );
+    next if ( $key eq 'retry-pause' );
+    next if ( $key eq 'retries' );
+    next if ( $key eq 'robot-rules' );
+    next if ( $key eq 'remove-nomirror' );
+    next if ( $key eq 'file-disposition' );
+    next if ( $key eq 'http-proxy' );
+    next if ( $key eq 'proxy-options' );
+    next if ( $key eq 'auth-domain' );
+    next if ( $key eq 'auth-user' );
+    next if ( $key eq 'auth-passwd' );
+    next if ( $key eq 'disable-headers' );
+    next if ( $key eq 'agent' );
+
+    $debug=numeric($value),next if ( $key eq 'debug' );
+    umask(numeric($value)),next if ( $key eq 'umask' );
+    $indexname=$value,next if ($key eq 'index-name');
+    $verbose=nway($value,'quiet','brief','chatty')-1,next
+      if ( $key eq 'verbosity' );
+
+    if ( $key eq 'cd' ) {
+      $chdirto=$value;
+      next;
+    }
+
+    if ($key eq 'url') {
+      my ($rum_url_o,$lf_dir,$rum_reurl,$rum_rebase);
+
+      # A two argument URL: line?
+      if ($value =~ m/^(.+)\s+(.+)/i) {
+	# Two arguments.
+	$rum_url_o=url $1;
+	# The last is a directory, it must end in /
+	$lf_dir=$2;
+	$lf_dir.='/' unless $lf_dir =~ m~/$~;
+
+	# The first is a URL, make it more canonical, find the base.
+	# The namespace confusion in this section is correct.(??)
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+	# print "URL: ",$rum_url_o->as_string,"\n";
+	# print "Base: $rum_rebase\n";
+
+	# Translate from rum space to lf space:
+	push(@internal_apply,"s/^".$rum_rebase."/".quotemeta($lf_dir)."/");
+
+	# That translation could lead to information loss.
+	$infoloss=1;
+
+	# Fetch rules tests the rum_url_o->as_string.  Fetch whatever
+	# matches the base.
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+
+	# Ignore whatever did not match the base.
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+
+      } else {
+	# $rum_url_o=root_quene($value);
+
+	$rum_url_o=url $value;
+
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+	# Translate from rum space to lf space:
+	push(@internal_apply,"s/^".$rum_rebase."//");
+
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+      }
+      next;
+    }
+
+    if ($key eq 'also' || $key eq 'also-quene') {
+      if ($value =~ m/^(.+)\s+(.+)/i) {
+	my ($rum_url_o,$lf_dir,$rum_reurl,$rum_rebase);
+	# Two arguments.
+	$rum_url_o=url $1;
+	$rum_url_o->host(lc $rum_url_o->host);
+	# The last is a directory, it must end in /
+	$lf_dir=$2;
+	$lf_dir.='/' unless $lf_dir =~ m~/$~;
+
+	# The first is a URL, find the base
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+	# Ok, now we can transform and select stuff the right way
+	push(@internal_apply,"s/^".$rum_rebase."/".quotemeta($lf_dir)."/");
+	$infoloss=1;
+
+	# Fetch rules tests the rum_url_o->as_string.  Fetch whatever
+	# matches the base.
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+
+	# Ignore whatever did not match the base.  This cures problem
+	# with '..' from base in in rum space pointing within the the
+	# scope in ra space.  We introduced a extra level (or more) of
+	# directories with the apply above.  Must do same with 'Also:'
+	# directives.
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+      } else {
+	die "Also: requires 2 arguments\n";
+      }
+      next;
+    }
+
+    if ($key eq 'quene') {
+      # root_quene($value);
+      next;
+    }
+
+    if ($key eq 'ignore-re' || $key eq 'fetch-re') {
+      # Check that it's a re, better that I am strict than for perl to
+      # make compilation errors.
+      unless ($value =~ /^m(.).*\1[gimosx]*$/) {
+	print STDERR "w3mfix: $value is not a recognized regular expression\n";
+	exit 1;
+      }
+    }
+
+    if ($key eq 'fetch' || $key eq 'fetch-re') {
+      my $expr=$value;
+      $expr = wild_re($expr).$ipost if ($key eq 'fetch');
+      $rule_text.=' return 1 if '.$expr.";\n";
+      next;
+    }
+
+    if ($key eq 'ignore' || $key eq 'ignore-re') {
+      my $expr=$value;
+      $expr = wild_re($expr).$ipost if ($key eq 'ignore');
+      $rule_text.=' return 0 if '.$expr.";\n";
+      next;
+    }
+
+
+    if ($key eq 'apply') {
+      unless ($value =~ /^s(.).*\1.*\1[gimosxe]*$/) {
+	print STDERR
+	  "w3mfix: '$value' is not a recognized regular expression\n";
+	exit 1;
+      }
+      push(@user_apply,$value) ;
+      $infoloss=1;
+      next;
+    }
+
+    if ($key eq 'options') {
+      
+      my($val,$nval);
+      foreach $val (split(/\s*,\s*/,lc $value)) {
+	if ($i==1) {
+	  $nval=nway($val,'recurse','no-date-check','only-nonexistent',
+		     'list-urls','lowercase','remove','batch','read-urls',
+		     'abs');
+	  $r=1,next if $nval==0;
+	  next if $nval==1;
+	  next if $nval==2;
+	  $list=1,next if $nval==3;
+	  if ($nval==4) {
+	    $lc=1;
+	    $iinline=($lc?"(?i)":"");
+	    $ipost=($lc?"i":"");
+	    next ;
+	  }
+	  next if $nval==5;
+	  next if $nval==6;
+	  next if $nval==7;
+	  $abs=1,next if $nval==8;
+        } else {
+	  die "w3mfix: options must be the first directive in the config file.\n";
+	}
+      }
+    }
+
+    if ($key eq 'fixup') {
+
+#      chomp($fixrc=`pwd` || '.');
+#      $fixrc.="/$file";
+#      warn "Fixrc: $fixrc\n";
+#      $fixup=1;
+
+      my($val,$nval);
+      foreach $val (split(/\s*,\s*/,lc $value)) {
+	$nval=nway($val,'on','run','noindex');
+	next if $nval==0;
+	next if $nval==1;
+	$doindex=0 if $nval==2;
+	# Ignore everyting else
+      }
+    }
+
+  }
+  close(CFGF);
+}
+
+sub wild_re {
+  # Here we translate unix wildcard subset to to perlre
+  local($_) = shift;
+  s#\*#\.\*#;
+  s#\?#\.#;
+  s#([\/\(\)\\\|\{\}\+)\$\^])#\\$1#g;
+  return $_ = '/'.$_.'/';
+}
+
+
+sub numeric {
+  # Check if argument is numeric?
+  my ( $number ) = @_ ;
+  return oct($number) if ($number =~ /\d+/ || $number =~ /\d+.\d+/);
+  die "Expected a number, got \"$number\"\n";
+}
+
+
+sub boolean {
+  my ( $boolean ) = @_ ;
+
+  $boolean = lc $boolean;
+
+  return 0 if ($boolean eq 'false' || $boolean eq 'off' || $boolean eq '0');
+  return 1 if ($boolean eq 'true' || $boolean eq 'on' || $boolean eq '1');
+  die "Expected a boolean, got \"$boolean\"\n";
+}
+
+
+sub nway {
+  my ( $value ) = shift;
+  my ( @values ) = @_;
+  my ( $val ) = 0;
+
+  $value = lc $value;
+  while (@_) {
+    return $val if $value eq shift;
+    $val++;
+  }
+  die "Expected one of ".join(", ",@values).", got \"$value\"\n";
+}
+
+
+sub stat {
+
+  my $file = shift;
+
+  if (exists($stat{$file})) {
+    print STDERR "++Cache hit: $file\n" if $debug;
+  } else {
+    stat($file);
+    if (-e _) {
+      $stat{$file}=(-d _)?'d':'f';
+    } else {
+      $stat{$file}='n';
+    }
+    print STDERR "--Cache miss: $file\n" if $debug;
+  }
+  return $stat{$file};
+}
+
+# ######################## Read 'state' files ##############################
+
+sub read_state {
+
+  my $reffile='.referers';
+  my $refered;
+  my @referers;
+
+  $reffile="referers" if $win32;
+
+  print STDERR "reading $reffile\n" if $verbose>0;
+  open(REFERERS,"< $reffile") ||
+    die "Could not open $reffile for reading: $!\n";
+
+  while (<REFERERS>) {
+    chomp;
+    ($refered,undef,@referers) = split(/\s+/);
+    $rum_referers{$refered}= [ @referers ];
+    # print STDERR $refered," <- ",join(' and ',@referers),"\n";
+  }
+
+  close(REFERERS);
+
+
+  # Read redirection report
+
+  my $redirfile='.redirs';
+  my $wrong;
+  my $right;
+  my $tmp;
+
+  $redirfile="redirs" if $win32;
+
+  print STDERR "reading $redirfile\n" if $verbose>0;
+  open(REDIRS,"< $redirfile") ||
+    die "Could not open $redirfile for reading: $!\n";
+
+  while (<REDIRS>) {
+    chomp;
+    ($wrong,undef,$right) = split(/\s+/);
+
+    $rum_redirected{$wrong}=$right;
+  }
+
+  close(REDIRS);
+}
+
+# ######################### Process every single tag ########################
+
+sub process_tag {
+  # Process a tag in html file
+  my $lf_referer = shift;
+  my $base_url = shift;
+  my $tag_name = shift;
+  my $url_attrs = shift;
+
+  # Retrun quickly if no URL attributes
+  return unless defined($url_attrs);
+
+  my $attrs = shift;
+
+  # Information loss through apply or processing in this procedure?
+  my $il = $infoloss; 
+  my $redirs;
+  my $stat;
+
+  my $rum_url;	# The absolute URL
+  my $lf_url;	# The local filesystem url
+  my $lf_url_o; # ... and it's object
+  my $key;
+  my $orig_rum_url;
+#  my $debug = 1;
+
+#  print STDERR "\nProcess Tag: $tag_name, URL attributes: ", join(', ',@{$url_attrs}),"\nOrigin:",$base_url,"\n"; # if $debug>2;
+
+  substr($lf_referer,0,0)='./' unless substr($lf_referer,0,1) eq '/';
+
+  foreach $key (@{$url_attrs}) {
+    if (defined($$attrs{$key})) {
+      $orig_rum_url=$rum_url=$$attrs{$key};
+
+      print STDERR "\n$key = $rum_url\n" if $debug;
+
+      # Apply redirects:
+      $redirs=0;
+      if (exists($rum_redirected{$rum_url})) {
+	$il=1;
+	while (exists($rum_redirected{$rum_url})) {
+	  die "Too many redirects in a row\n" if $redirs++>32;
+	  print STDERR "$rum_url -> ".$rum_redirected{$rum_url}."\n"
+	    if $debug;
+	  $rum_url=$rum_redirected{$rum_url};
+	}
+      }
+
+      # Apply program/user apply rules
+      $lf_url=apply($rum_url);
+
+      if (defined($lf_url)) {
+	# Apply directory/file check here
+	$stat=&stat($lf_url);
+
+	if ($stat eq 'f' && $lf_url =~ m~/$~) {
+	  # It's a file, remove trailing /
+	  print STDERR "****** File / fixup of $lf_url\n" if $debug;
+	  $il=1;
+	  substr($lf_url,length($lf_url)) = '';
+	} elsif ($stat eq 'd' && !($lf_url =~ m~/$~) ) {
+	  # It's a directory, add a trailing /
+	  print STDERR "****** Directory / fixup of $lf_url\n" if $debug;
+	  $il=1;
+	  $lf_url .= '/';
+	}
+
+	substr($lf_url,0,0)='./' unless substr($lf_url,0,1) eq '/';
+
+	$lf_url_o=url $lf_url;
+	my $tmp=$lf_url_o->clone;
+	$tmp->basename($indexname);
+
+	if ( $doindex && $lf_url_o->basename eq "" && 
+	     &stat($tmp->unix_path) eq 'f' &&
+	     !$htmlop::isdir{$key}) {
+	  $lf_url_o=$tmp;
+	  $il=1;
+	  print STDERR "indexname adjusted to ",$lf_url_o->as_string,"\n"
+	    if $debug;
+	}
+		 
+	# Save new value in the hash, make it a file url to get 'rel' working
+	$lf_url_o->scheme('file');
+	$$attrs{$key} = ($lf_url_o->rel("file:".$lf_referer))->as_string;
+
+	printf STDERR "Saved ".$$attrs{$key}."\n" if $debug;
+
+	# If there is potential information loss save the old value too
+	$$attrs{"W3MIR".$key}=$orig_rum_url if $il;
+
+      } elsif ($redirs>0) {
+	$$attrs{$key}=$rum_url;
+	print STDERR "Saved ".$$attrs{$key}."\n" if $debug;
+	$$attrs{"W3MIR".$key}=$orig_rum_url;
+      }
+    }
+  }
+}
+
+
+# ###################### Edit the URLs in one file... ########################
+
+sub edit_html_file {
+  # Check if it's a html file.  I know this tag is in all html
+  # files, because w3mir put it there.
+
+  my($lf_url)=shift;
+  my($rum_url)=shift;
+
+  # Figure out the filename for our local filesystem.
+  $lf_url.=$indexname if $lf_url =~ m~/$~ || $lf_url eq '';
+
+  # Stuff in need of unquoting?
+  $lf_url = (url "file:$lf_url")->local_path if $lf_url =~ /\%\d\d/;
+
+  if (exists($lf_edited{$lf_url})) {
+    print STDERR "Already edited $lf_url\n" if $debug;
+    return ;
+  }
+
+  $lf_edited{$lf_url}=1;
+
+  my $page;
+  my $newpage;
+  my $read;
+  my $atime;
+  my $mtime;
+
+  # dev  uno   mode  nlink uid   gid   rdev  size  atime mtime
+  (undef,undef,undef,undef,undef,undef,undef,undef,$atime,$mtime)
+    = stat($lf_url);
+
+  if (!open(TMPF,"< $lf_url\n")) {
+    warn "Cannot read $lf_url: $!\n" if $verbose>=0;
+    return;
+  }
+
+  $read=sysread(TMPF,$page,10240,0);
+  close(TMPF);
+
+  if (! $page =~ /<HTML/i) {
+    print STDERR "$lf_url is not html\n" if $verbose>0;
+    return ;
+  }
+
+  $files++;
+
+  print STDERR "w3mfix: $lf_url" if $verbose>=0;
+  
+  print STDERR "$lf_url is a html file\n" if $debug;
+
+  print STDERR " reading" if $verbose>0;
+  
+  open(TMPF,$lf_url) || 
+    die "Could not open $lf_url for reading: $!\n";
+  # read the whole file.
+  {
+    local($/)=undef;
+    $page = <TMPF>;
+  }
+  close(TMPF);
+
+  print STDERR " ",length($page)," bytes" if $verbose>0;
+
+  # It's a html document
+
+  print STDERR ", editing" if $verbose>0;
+
+  ($newpage,undef) = 
+    &htmlop::process($page, # $htmlop::NODOC,
+		     $htmlop::ABS,$rum_url,
+		     $htmlop::USESAVED,'W3MIR',
+		     $htmlop::TAGCALLBACK,\&process_tag,$lf_url);
+
+  open(TMPF,">$lf_url") ||
+    die "\nCould not open $lf_url for writing: $!\n";
+  
+  print STDERR ", saving" if $verbose>0;
+
+  if (length($newpage)) {
+    # This is ODD: close does not seem to flush the buffers.  So we
+    # force the issue.
+    local($|)=1;
+    print TMPF $newpage || 
+      die "\nCould not write to $lf_url (disk full?): $!\n";
+  }
+
+  close(TMPF) || 
+    die "\nCould not close $lf_url after writing: $!\n";
+
+  # Set times back to what they were.
+  utime $atime,$mtime,$lf_url;
+
+  print STDERR ".\n" if $verbose>=0;
+}
+
+# ############################### Scope test
+
+sub want_this {
+  # Find out if we want the url passed.  Just pass it on to the
+  # generated functions.
+  my($rum_url)=shift;
+
+  # Does scope rule want this?
+  return &$scope_code($rum_url) &&
+    # Does user rule want this too?
+    &$rule_code($rum_url) 
+  
+}
+
+# ############################### Apply the apply rules
+
+sub user_apply {
+  # Apply the user apply rules
+  return &$user_apply_code(shift);
+}
+
+
+sub internal_apply {
+  # Apply the w3mir generated apply rules
+  return &$apply_code(shift);
+}
+
+
+sub apply {
+  # Apply the user apply rules.  Then if URL is wanted return result of
+  # w3mir apply rules.  Return the undefined value otherwise.
+
+  my $url = user_apply(shift);
+
+  return undef unless want_this($url);
+
+  internal_apply($url);
+}
+
+# ############################### Decide what URLs to edit
+
+sub edit_as_needed {
+  my $rum_redirected;
+  my $rum_url;
+  my $o_rum_url;
+  my $rum_referer;
+  my $lf_url;
+  my $foo;
+  my $redirs;
+
+  if ($editthis) {
+
+    # Find the URLs that match $edithis
+    foreach $o_rum_url (keys %rum_referers) {
+      # Work on them if they (now) fall within the scope of retrival
+      $redirs=0;
+      $rum_url=$o_rum_url;
+      if (exists($rum_redirected{$o_rum_url})) {
+	while (exists($rum_redirected{$rum_url})) {
+	  die "Too many redirects in a row\n" if $redirs++>32;
+	  print STDERR "$rum_url -> ".$rum_redirected{$rum_url}."\n"
+	    if $debug;
+	  $rum_url=$rum_redirected{$rum_url};
+	}
+      }
+      next unless $rum_url =~ /$editthis/io;
+      next unless want_this($rum_url);
+      # Find and edit the documents containing references to $o_rum_url
+      foreach $rum_referer (@{$rum_referers{$o_rum_url}}) {
+	next if $rum_referer eq '(commandline)';
+	$lf_url=apply($rum_referer);
+	next unless defined($lf_url);
+	edit_html_file($lf_url,$rum_referer);
+      }
+    }
+    
+    # Don't do anything else when invoked thus
+    return;
+  }
+
+  if ($doindex) {
+    # Edit everything that refers anything with trailing /
+    foreach $rum_url (grep(/\/$/,keys %rum_referers)) {
+      foreach $rum_referer (@{$rum_referers{$rum_url}}) {
+	next if $rum_referer eq '(commandline)';
+	$lf_url=apply($rum_referer);
+	next unless defined($lf_url);
+	edit_html_file($lf_url,$rum_referer);
+      }
+    }
+  }
+
+  # Edit only redirected stuff
+  foreach $rum_redirected (keys %rum_redirected) {
+    # print "Redirected $rum_redirected\n";
+    foreach $rum_url (@{$rum_referers{$rum_redirected}}) {
+      # print "- Found in $rum_url\n";
+      $lf_url=apply($rum_url);
+      next unless defined($lf_url);
+      edit_html_file($lf_url,$rum_url);
+    }
+  }
+}
+
+# ############################### 'main'
+
+&parse_args(@ARGV);
+
+if (!$rc) {
+  $rc='.w3mirc';
+  $rc='w3mir.ini' if $win32;
+}
+
+print STDERR "w3mfix: rc file: $rc\n" if $verbose>0;
+
+&parse_cfg_file($rc);
+
+# $chdirto is ignored, w3mir already did it
+
+# Compile second order code
+
+# - The rum scope tests
+my $full_rules=$scope_prefix.$scope_fetch.$scope_ignore.$scope_postfix;
+eval $full_rules;
+
+# warn "Scope rules:\n-------------\n$full_rules\n---------------\n";
+
+die "Program generated rules did not compile.  The code is:\n----\n".
+  $full_rules."\n----\n"
+  if !defined($scope_code);
+
+$full_rules=$rule_prefix.$rule_text.$rule_postfix;
+eval $full_rules;
+
+# warn "User rules:\n-------------\n$full_rules\n---------------\n";
+
+# - The user specified rum tests
+die "Ignore/Fetch rules did not compile.  The code is:\n----\n".
+  $full_rules."\n----\n"
+  if !defined($rule_code);
+
+# - The user specified apply rules
+
+my $full_apply=$apply_prefix.($lc?$apply_lc:'').
+  join($ipost.";\n",@user_apply).(($#user_apply>=0)?$ipost:"").";\n".
+  $apply_postfix;
+eval $full_apply;
+
+die "User apply rules did not compile. The code is:
+----
+".$full_apply."
+----\n" if !defined($apply_code);
+
+$user_apply_code=$apply_code;
+
+# - The w3mir generated apply rules
+
+$full_apply=$apply_prefix.($lc?$apply_lc:'').
+  join($ipost.";\n",@internal_apply).(($#internal_apply>=0)?$ipost:"").";\n".
+  $apply_postfix;
+eval $full_apply;
+
+die "Internal apply rules did not compile.  The code is:
+----
+".$full_apply."
+----\n" if !defined($apply_code);
+
+&read_state;
+
+&edit_as_needed;
+
+exit 0;
+__END__
+# -*- perl -*-  There must be a blank line here:
+
+=head1 NAME
+
+w3mfix - fixup program for w3mir
+
+=head1 SYNOPSIS
+
+B<w3mfix> [B<options>] [B<configuration-file>]
+
+=head1 DESCRIPTION
+
+B<w3mfix> is the companion program to L<w3mir>.  It can be used for
+several URL editing operations usefull in different situations.  
+
+When starting B<w3mfix> will read it's configuration file.  It's name is 
+either .w3mirc (w3mir.ini on win32) or specified on the commandline.
+
+B<w3mfix> is controlled by the 'Fixup' directive of the configuration
+file (described in the L<w3mir> documentation).  B<w3mfix> is also
+affected by 'Index-name' and the one special commandline option it
+knows, as well as the directives/options controlling verbosity and
+debugging information.
+
+=head1 DESCRIPTION
+
+B<w3mfix> can rewrite URLs in these ways:
+
+=over 4
+
+=item * Rewrite URLs that resutled in redirects to point to the place
+redirected to.  This is needed in all cases and will always be fixed
+by B<w3mfix>.
+
+=item * Change URLs ending in .../ into .../index.html (or
+.../Welcome.html).  This is, probably, not needed when the mirror is
+meant do be used with a web-server.  It is usefull for browsing
+directly from disk or CDROM, but in this case it's, most often,
+required.  To disable this specify the I<noindex> option with the
+'Fixup' directive.
+
+The default is to transform URLs ending in .../ into .../index.html.
+
+To controll the name of the index file use the I<Index-name> directive
+as documented in L<w3mir>
+
+=item * Change URL links to documents outside the mirror to point to
+some local document.  Could be usefull if the mirror is destined for a
+CDROM to be used on a unconnected machine.
+
+THIS IS NOT YET IMPLEMENTED
+
+=item * Change URL links to documents that L<w3mir> was
+unable/forbidden to retrive to point to some local document.  Pointing
+these to a nice informative document is probably better than random
+error messages from the browser.
+
+THIS IS NOT YET IMPLEMENTED
+
+=item * And, least, but far from last, B<w3mfix> can be used to
+prepare an established mirror for enlargement.
+
+This feature is used thus: Add the new site or subsite to be mirrored
+on the configuration file (by adding B<Also:> and B<Also-quene:>
+directives).  Then run B<w3mfix> with the B<-editref> option.  When
+the B<-editref> option is specified B<w3mfix> will not perform any
+other editing tasks.
+
+E.g.; To add I<http://www.yahoo.com/Science/Artificial_Life/> to your
+mirror add something like
+
+	  Also: http://www.yahoo.com/Science/Artificial_Life/ yahoo
+
+to the configuration file, then run w3mfix:
+
+	  w3mfix -editref www.yahoo.com/Science/Artificial_Life
+
+This will cause all references to
+I<www.yahoo.com/Science/Artificial_Life> (and under) to be edited so they
+point to within the mirror.  After B<w3mfix> has finished you can run
+L<w3mir> in the normal manner.
+
+=back
+
+=head1 BUGS
+
+Naah.
+
+=head1 SEE ALSO
+
+L<w3mir>
+
+=head1 AUTHORS
+
+B<w3mir>s authors can be reached at I<w3mir-core@usit.uio.no>.
+B<w3mir>s home page is at http://www.math.uio.no/~janl/w3mir/
+
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir-HOWTO.html
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir-HOWTO.html	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir-HOWTO.html	(revision 16561)
@@ -0,0 +1,943 @@
+<!doctype html public "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>W3MIR HOWTO</title>
+<style type="text/css">
+<!--
+  body { background-color: white }
+  h1, h2, h3, b { font-family: sans-serif }
+  .red { color: red }
+-->
+</style>
+<body>
+<h1>W3MIR HOWTO</h1>
+
+<p><b>Corresponding to w3mir version 1.0.2 and above</b>
+
+<p>W3mir is an all purpose WWW copying and mirroring program.  Its
+main focus is copying complete directory structures keeping your copy
+browseable through a web server, or directly off a disk or CDROM if
+you want.  W3mir will fix URLs that are redirected and everything else
+that needs to be fixed to make your copy browseable.  But it also does
+odd jobs, retrieving single documents, batch getting several documents
+and more.  You may tell w3mir not to change anything in the retrieved
+documents.  W3mir has been in development quite a long time so you
+find options to do a lot of things needed when copying things off the
+web.
+
+<p>With w3mir you may copy the entire contents a web server.  Or just
+a directory hierarchy, or several related hierarchies off as many
+servers as you like.  They don't even have to be related.
+
+<p>W3mir supports HTML4, and has partial support for CSS, Java,
+ActiveX and Adobe Acrobat (PDF) files.  And it works on Win32
+machines.
+
+<p><b>Warning:</b> W3mir enables you to copy a lot of things off the
+Web, but remember, the things you retrieve might be copyrighted and
+the copy you make with w3mir might in fact be illegal to make and
+posses.
+
+<hr>
+
+<h2><a name="contents">Contents</a></h2>
+
+<p><a href="#intro">README</a> (You want to read this!  <b
+class="red">Really!</b>)
+
+<p><b>How do I...</b>
+<ol>
+  <li><p><a href="#copy">copy a file?</a>
+  <li><p><a href="#recurse">copy a directory hierarchy?</a>
+  <li><p><a href="#resources">copy the needed resource files from another
+    directory hierarchy?</a>
+  <li><p><a href="#ignore">avoid copying files I don't want or copy only
+    files I want?</a>
+  <li><p><a href="#rm">remove the files that are no longer on the
+    original site from the mirror?</a>
+  <li><p><a href="#depth">limit how deep w3mir will recurse?</a>
+  <li><p><a href="#memory">limit w3mirs memory usage?</a>
+  <li><p><a href="#multi">copy files from multiple sites?</a>
+  <li><p><a href="#alias">copy files from one server with several names?</a>
+  <li><p><a href="#aborted">restart a mirror process after stopping it
+    prematurely?</a>
+  <li><p><a href="#enlarge">enlarge or prune an established mirror?</a>
+  <li><p><a href="#cat">'cat' a file?</a>
+  <li><p><a href="#list">list URLs in a document?</a>
+  <li><p><a href="#robots">disable robots.txt obedience?</a>
+  <li><p><a href="#corrupt">stop w3mir from corrupting binary files?</a>
+  <li><p><a href="#auth">copy a site that wants user-name and password?</a>
+  <li><p><a href="#mauth">access a site that wants several different
+    user-names and passwords?</a>
+  <li><p><a href="#proxy">use a proxy server?</a>
+  <li><p><a href="#pauth">authenticate myself to a proxy server?</a>
+  <li><p><a href="#proxytweak">ensure that the proxy server ...?</a>
+  <li><p><a href="#batchget">batch get files with w3mir?</a>
+  <li><p><a href="#cgi">handle CGI?</a>
+  <li><p><a href="#imap">handle server side image-maps?</a>
+  <li><p><a href="#java">handle Java and ActiveX?</a>
+  <li><p><a href="#script">handle java-script and other script languages?</a>
+  <li><p><a href="#css">handle the other things with 'partial support'?</a>
+  <li><p><a href="#anon">keep my identity secret?</a>
+  <li><p><a href="#ns">pretend that I'm using Netscape, Internet
+    Explorer or Lynx?</a>
+  <li><p><a href="#other">do other things?</a>
+</ol>
+
+<hr>
+
+<h2><a name="intro">README</a></h2>
+
+<p>W3mir may be used in two, main, ways:
+
+<ul>
+  <li><p>To copy something random once.
+  <li><p>To keep a local mirror of some remote site
+</ul>
+
+<p>To copy something random once there is a high likeliness you can
+just start w3mir with some simple options and it will do the job you
+want it to.  Providing that the remote site is not too complex and
+your expectations of the copy aren't high :-) This is what wget, the
+gnu w3 mirroring program, does and is good at.
+
+<h3>Configuration file</h3>
+
+<p>Once you want to keep a copy of a remote site up-to-date over time,
+mirror something with server side image-maps, redirects or
+authentication you have to write a configuration file for w3mir.  This
+is what w3mir is good at, compared to wget.  Writing the file is not
+hard, and there are two example files in the w3mir distribution.  It
+will also be explained here.  The configuration file is typically
+called <tt>.w3mirc</tt> (<tt>w3mir.ini</tt> on win32 machines), and
+can be written with a simple text editor.  It is kept in the top
+directory of the mirror, where w3mir will find it when it starts.
+Please refer to the <a href="#contents">contents</a> for how to handle
+a specific problem with a configuration file.
+
+<hr>
+
+<h2>The answers:</h2>
+
+<hr>
+
+<h3><a name="copy">How do I copy a file?</a></h3>
+
+<p>To copy the top page off www.starwars.com:
+
+<p><tt>w3mir http://www.starwars.com/</tt>
+
+<p><b>Note:</b> it is <em>important</em> that you give the trailing
+slash for server names and directories.
+
+<hr>
+
+<h3><a name="recurse">How to I copy a directory hierarchy?</a></h3>
+
+<p>To copy the entire stuff about episode I from www.starwars.com
+which is stored in <tt>http://www.starwars.com/episode-i/</tt> (I don't
+recommend this, it's quite a lot of data):
+
+<p><tt>w3mir -r http://www.starwars.com/episode-i/</tt>
+
+<p>The corresponding configuration file is simple:
+
+<pre>
+Options: recurse
+URL: http://www.starwars.com/episode-i/
+Fixup: run
+</pre>
+
+<p>The <tt>-r</tt> option makes w3mir recurse down from the starting
+point.  It will only copy all the documents under
+http://www.starwars.com/episode-i/ that it sees referenced from those
+same documents.  W3mir will <em>not</em> retrieve documents from
+http://www.starwars.com/ because it is considered to be 'over' the
+starting point.
+
+<p>The command-line will get you a copy that is definitely browseable
+via a WEB server, and possibly browseable directly from a CDROM or
+hard-disk.  To ensure that it is browseable from CDROM and disk you
+need to use a configuration file with the <tt>Fixup: run</tt> line in.
+It causes w3mir to edit anything that needs editing after the mirror
+has completed, including fixing URLs that caused redirects.  The dirty
+work is done by w3mirs helper program w3mfix.  The directive will
+cause w3mfix to be run each time w3mir completes the mirror.
+
+<p><b>Note:</b> it is <em>important</em> that you give the trailing
+slash after the directory name.  Specifying
+<tt>http://www.starwars.com/episode-i</tt> and
+<tt>http://www.starwars.com/episode-i/</tt> is quite different in
+w3mirs eyes.  In the former case episode-i is considered to be a
+document within the / (top) directory of www.starwars.com and w3mir
+will recurse from /, which is a lot more than you wanted.  In the
+latter case w3mir understands that episode-i is a directory and will
+consider that directory to be the staring point, which is what you
+wanted.
+
+<hr>
+
+<h3><a name="resources">How do I copy the needed resource files from
+another directory hierarchy?</a></h3>
+
+<p>Some sites store their documents in one place, and puts their
+banners, icons and such in a separate directory called
+<tt>/images</tt>, <tt>/banners</tt>, <tt>/icons</tt>,
+<tt>/resources</tt> or some such.  Unless you retrieve these as well as
+the documents things will probably not be too colorful.  So, imagine
+that the starwars site stored all the images in one holding directory
+called <tt>/imagery</tt> and you want to copy all the stuff in it that
+the episode-i pages need.  Then you do this:
+
+<pre>
+Options: recurse
+URL: http://www.starwars.com/episode-i/ episode-i
+Also: http://www.starwars.com/imagery/ imagery
+Fixup: run
+</pre>
+
+<p>There are two changes here compared to the simpler file we started
+with: There is an extra argument at the end of the URL directive.  It
+tells w3mir to store everything gotten from
+<tt>http://www.starwars.com/episode-i/</tt> in the subdirectory
+<tt>episode-i</tt>.  The directory can be omitted, but I think its
+neater this way.  Then the new directive 'Also:'.  It tells w3mir that
+you also want whatever the documents under
+<tt>http://www.starwars.com/episode-i/</tt> references under
+<tt>http://www.starwars.com/imagery/</tt>.
+
+<p><b>Note:</b> this will only get stuff that was used by the
+documents under <tt>http://www.starwars.com/episode-i/</tt>, anything
+stored under <tt>http://www.starwars.com/imagery/</tt> which is not
+used will not be retrieved. If you want everything under
+<tt>imagery</tt> to be retrived use the <tt>Also-quene:</tt>
+directive.
+
+<hr>
+
+<h3><a name="ignore">How do I avoid copying files I don't want or copy
+only files I want?</a></h3>
+
+<p>To control what files w3mir copies you can use the
+<tt>Ignore:</tt>, <tt>Fetch:</tt>, <tt>Ignore-RE:</tt> and
+<tt>Fetch-RE:</tt> directives in the configuration file.  The embeded
+references to any file you chose to ignore, i.e., not copy, will point
+at the original site, <em>not</em> to the mirror.  This means that the
+mirror user may still get ahold of the file from the original source
+by simply clicking if she so desires.
+
+<p>If a site contains huge .wav audio files that you are not
+interested in you put
+
+<pre>
+Ignore: *.wav
+</pre>
+
+<p>in the configuration file.  You may ignore as many different
+filename patterns as you want.  If you are mirroring a site you want
+very few, specific files from, say all HTML (named
+<em>something</em><tt>.html</tt>) and all Mpeg video files (named
+<em>something</em><tt>.mpg</tt>) you can write this:
+
+<pre>
+Fetch: *.html
+Fetch: *.mpg
+Ignore: *
+</pre>
+
+<p>W3mir will test each filename against each Fetch/Ignore rule in
+sequence.  A html file will match the first line and be fetched.  Any
+mpg file will match the second line and be fetched.  All other files
+will match the third line, and be ignored.  This last line is needed
+because the default is to get any files which are not ignored.  By
+arranging fetch and ignore rules carefully you may retrieve exactly
+the filename patterns you want and not retrieve anything else.
+
+<p>If you decide you also want all Mpeg Layer 3 audio files
+(<em>something.</em><tt>mp3</tt>) from the site, after the mirror has
+been established.  Then you add this:
+
+<pre>
+Fetch: *.mp3
+</pre>
+
+<p>as the third line, making the <tt>Ignore: *</tt> line the forth and
+last.  Then you must fix all references to .mp3 files within the
+mirror by running w3mfix thus:
+
+<pre>
+w3mfix -editref .mp3
+</pre>
+
+<p>which will edit all references to .mp3 files, pointing them the
+right place, on your disk. Ditto when you remove a fetch rule, or add
+or remove an ignore rule. See the answer about <a
+href="#enlarge">enlarging and pruning</a> mirrors for more examples of
+using <tt>w3mfix -editme ...</tt>
+
+<p><b>Note:</b> when retrieving only a very limited set of files, as
+in the example above, you <em>must</em> retrieve the html files,
+because how else will w3mir find URLs of files to retrieve? Only html
+files contain links to other files.
+
+<p>Similarly, you may chose to not mirror whole branches of the
+original site.  If you for example mirror my home-pages, and you decide
+not to mirror the comics pages you can put
+
+<pre>
+Ignore: /ts/
+</pre>
+
+<p>or more precisely
+
+<pre>
+Ignore: http://www.ifi.uio.no/~janl/ts/
+</pre>
+
+<p>in the configuration file.  If you do this after having established
+the mirror you use w3mfix to fix the references:
+
+<pre>
+w3mfix -editref /ts/
+</pre>
+
+<p><tt>Fetch:</tt> and <tt>Ignore:</tt> rules can only use a very
+limited subset of the Unix wild-cards.  w3mir understands only '?',
+'*', and '[a-z]' ranges.
+
+<p><tt>Ignore-RE:</tt> and <tt>Fetch-RE:</tt> are the same as
+<tt>Fetch:</tt> and <tt>Ignore:</tt> except that they give you access
+to the full power of Regular Expressions to make rules for that to get
+or not to get. They support perls superset of the normal Unix regular
+expression syntax.  They must be completely specified, including the
+prefixed m, a delimiter of your choice (except the paired delimiters:
+parenthesis, brackets and braces), and any of the RE modifiers. I.e.,
+
+<pre>
+Ignore-RE: m/.gif$/i
+</pre>
+
+<p>or
+
+<pre>
+Ignore-RE: m~/.*/.*/.*/~
+</pre>
+
+<p>and so on.  "#" cannot be used as delimiter as it is the comment
+character in the configuration file.
+
+<p>There are some traps when using <tt>Ignore-RE</tt> and
+<tt>Fetch-RE</tt>, please see their documentation in <tt>mandoc
+w3mir</tt> for a more complete explanation.
+
+<hr>
+
+<h3><a name="depth">How do I limit how deep w3mir will recurse?</a></h3>
+
+<p>W3mir has no explicit mechanism to limit the depth of recursion,
+but the same result can be achieved with a simple <tt>Ignore</tt> rule:
+
+<pre>
+Ignore: /*/*/*/*/*/*/
+</pre>
+
+<p>This will ignore any URLs that contain at least 7 slashes ("/").
+Note that a URL contains three slashes that does not have anything to
+do with depth:
+
+<pre>
+http://www.ifi.uio.no/
+</pre>
+
+<p>so only the surplus slashes are used for depth in this match.  In the
+example above the limit is 4 levels from the top.  The
+<tt>Ignore:</tt> rule that is used to limit recursion depth must be
+listed before any <tt>Fetch:</tt> rules to be effective.
+
+<hr>
+
+<h3><a name="memory">How do I limit w3mirs memory usage?</a></h3>
+
+<p>In a mirror consisting of <em>many</em> files, such as a archive of
+an active mailinglist w3mir will build a very large referer table, in
+part for w3mir to use in the <tt>Referer:</tt> header and in part for
+w3mfix to use in fixing references.
+
+<p>If you disable both the <tt>Referer:</tt> header and don't use
+w3mfix w3mir will not build a referer table.  You do this in the
+configuration file:
+
+<pre>
+Disable-headers: referer
+Fixup: off
+</pre>
+
+<p>Please note the potential problems of turning off fixup described
+earlier in this howto.  There are normaly no problems associated with
+simple sites, but if there are redirects fixup <em>is</em> needed for
+a consistent mirror.
+
+<hr>
+
+<h3><a name="rm">How do I remove the files are no longer on the
+original site from the mirror?</a></h3>
+
+<p>Over time the site you mirror will add files, and quite possibly
+remove files.  Or you might introduce new <tt>Ignore:</tt> rules after
+establishing the mirror that reduces the files wanted in the mirror.
+
+<p>By default w3mir will not delete such old files, some people might
+want to keep the files even if they are removed from the original
+site.  To remove the old/unwanted files you add 'remove' to the
+<tt>Options:</tt> line.
+
+<hr>
+
+<h3><a name="multi">How do I copy files from multiple sites?</a></h3>
+
+<p>In the answer to the previous question we see how to mirror several
+related sites.  For example, say you want to mirror all my home-pages
+into one mirror:
+
+<pre>
+Option: recurse
+URL: http://www.math.uio.no/~janl/ math/janl
+Also: http://www.math.uio.no/drift/personer/ math/drift
+Also: http://www.ifi.uio.no/~janl/ ifi/janl
+Also: http://www.mi.uib.no/~nicolai/ math-uib/nicolai
+</pre>
+
+<p>As in the previous example this will only get documents that are
+referenced.  Any documents that are stored at these location but to
+which w3mir finds no references will not be retrieved.  So this will
+fail if the sites are not in any way related, or if you wanted
+<em>everything</em> stored at each site.
+
+<p>To mirror unrelated sites, or get it all you may specify that the
+given URL should be considered a starting-point as well:
+
+<pre>
+Also-quene: http://www.math.uio.no/drift/personer/ math/drift
+</pre>
+
+<p>and, if you want to add an additional starting-point within a already
+named site:
+
+<pre>
+Quene: http://www.math.uio.no/drift/personer/foo.html
+</pre>
+
+<p>Armed with that you should be able to get pretty much anything you
+like.
+
+<hr>
+
+<h3><a name="alias">How do I copy files from one server with several
+names?</a></h3>
+
+<p>Simple, the same way you mirror several servers with different
+names. The math department at University of Oslo has a web server
+known under two names: math-www.uio.no and www.math.uio.no, and both
+names are used in documents stored on it. To copy the whole server,
+one time only, give these URL and Also lines:
+
+<pre>
+URL: http://www.math.uio.no/ .
+Also: http://math-www.uio.no/ .
+</pre>
+
+<p>Note the period/dot (.) at the end of each line.  It means that
+w3mir will store the files in the current directory, i.e. documents
+from both servers will be stored in the same place.  But since w3mir
+asks to only get documents that are newer than the ones it already has
+any document gotten from the server under the www.math.uio.no name
+will not be gotten from the math-www.uio.no name as well.  ... w3mir
+will ask for the document, but the server will tell w3mir that its
+copy is current and there will be no additional transfer of the
+document.
+
+<hr>
+
+<h3><a name="enlarge">How do I enlarge or prune an established
+mirror?</a></h3>
+
+<p>This only works if you use a configuration file.
+
+<p>If you want to add a site or directory to a mirror you simply add
+the needed <tt>Also:</tt> or <tt>Also-Quene:</tt> to the configuration
+file and then you run w3mfix manually, with the -editref option.  If,
+you for example have established a mirror of my home-pages, but want to
+add my wife's home-page you add this
+
+<pre>
+Also: http://www.ifi.uio.no/~annen/ ifi/annen
+</pre>
+
+<p>to the configuration shown earlier.  Then you run w3mfix, and you want
+it to fix all URLs referencing her home-page, the distinguishing
+characteristic is the name 'annen':
+
+<pre>
+w3mfix -editref annen
+</pre>
+
+<p>but 
+
+<pre>
+w3mirx -editref http://www.ifi.uio.no/~annen/
+</pre>
+
+<p>would work too, but it's a lot more to type.  This fixes all the
+references to her home-page so that they point to the mirror instead of
+the original pages.
+
+<p>To prune (cut out something) a mirror you do the same.  Make the
+change in the configuration file and run 'w3mfix -editme ...' to fix
+the references to that which you removed.
+
+<hr>
+
+<h3><a name="cat">How do I 'cat' a file?</a></h3>
+
+<p>W3mir will output the fetched document to its standard output
+(normally your screen/window) if you specify the '-s' command line
+option.  The corresponding configuration file directive is 
+
+<pre>
+File-Disposition: stdout
+</pre>
+
+<hr>
+
+<h3><a name="list">How do I list URLs in a document?</a></h3>
+
+<p>To list the URLs in http://www.math.uio.no/:
+
+<pre>
+w3mir -q -f -l http://www.math.uio.no/
+</pre>
+
+<p>The <tt>-q</tt> switch causes w3mir to produce no other output
+which would disturb the URL listing.  The <tt>-f</tt> switch tells
+w3mir to forget the document once it has been analyzed, i.e., not save
+it on disk.  And finally, the <tt>-l</tt> switch makes w3mir list the
+URLs in the document.  You may combine <tt>-l</tt> with <tt>-r</tt>
+and you need not use it with <tt>-f</tt>.
+
+<p>In the configuration file you put <tt>list</tt> on the
+<tt>Options:</tt> line.
+
+<hr>
+
+<h3><a name="aborted">How to I restart a mirror process after stopping
+it prematurely?</a></h3>
+
+<p>You may just rerun the same command once more.  But that makes
+w3mir request all the documents you have already once more to see if a
+more recent version is available on the server.  You can save time by
+using the <tt>-fs</tt> (Fetch Some) option.  This makes w3mir only
+request documents it does not find on your disk.  E.g.:
+
+<p><tt>w3mir -fs -r http://www.starwars.com/</tt>
+
+<p>This is not something you would normally put in the configuration
+file, but you can, by adding 'only-nonexistent' on the 'Options:' line.
+
+<hr>
+
+<h3><a name="robots">How do I disable robots.txt obedience?</a></h3>
+
+<p>Normally w3mir will read and obey each sites robots.txt file,
+because w3mir wants to be a nice tool. However robots.txt was designed
+with something slightly different than the normal use of w3mir in
+mind, so if you want w3mir to disregard the robot rules you can use
+<tt>-drr</tt> (Disable Robot Rules) on the command-line, or the line
+
+<pre>
+Robot-Rules: off
+</pre>
+
+<p>in the configuration file.  The robot exclusion standard is
+described in <a
+href="http://info.webcrawler.com/mak/projects/robots/norobots.html">http://info.webcrawler.com/mak/projects/robots/norobots.htm</a>.
+
+<hr>
+
+<h3><a name="corrupt">How do I stop w3mir from corrupting binary
+files?</a></h3>
+     
+<p>During the normal course of events w3mir converts the newline
+format of fetched HTML documents to your systems native newline
+format. On Unix a newline consists of a single ASCII LF character, on
+Macintoshes it's a single ASCII CR character and on Dos/Windows it's a
+ASCII CR/LF pair. W3mir understands all these and all HTML files are
+saved in the format your operating system prefers.
+
+<p>If, and this is very unlikely, a web server identifies a binary
+file as HTML w3mir will very likely corrupt the file. If you discover
+a file which is obviously ruined in the mirror, but is not ruined when
+you view it on the original site do this:
+
+<ol>
+
+<li>Notify the webmaster on the original site that the file has the
+wrong MIME type
+
+<li>Use the <tt>-nnc</tt> (No Newline Conversion) option on the
+command line, or
+
+<pre>
+Options: no-newline-conv
+</pre>
+
+in the configuration file.
+
+<li>Remove the corrupt file(s).
+
+<li>Run "<tt>w3mir -fs</tt>...", to fetch only the deleted file(s)
+again.
+
+</ol>
+
+<hr>
+
+<h3><a name="auth">How do I copy a site that wants user-name and
+password?</a></h3>
+
+<p>This can only be done with a configuration file.  Being able to
+give this on the command-line would give the user-name and password away
+to other users of the system, so the ability to give authentication
+information that way has not been put in w3mir.
+
+<p>In the configuration file you put:
+
+<pre>
+Auth-domain: */*
+Auth-user: me
+Auth-passwd: my-password
+</pre>
+
+<p>This will cause w3mir to give the user-name and password each time
+the server asks.  There is no way to make w3mir give the user-name and
+password each time no matter if the server asks or not.
+
+<hr>
+
+<h3><a name="mauth">How do I access a site that wants several
+different user-names and passwords?</a></h3>
+
+<p>If you have several user-names and passwords across
+the server(s) that are copied you need a slightly more advanced
+version of this that associates each user-name/password with a
+authentication "domain".  "Domain" is a HTTP concept.  It is simply a
+grouping of files and documents within a "realm".  One file or a whole
+directory hierarchy can belong to a realm.  One server may have many
+realms.  A user may have separate passwords for each realm, or the
+same password for all the realms the user has access to.  A
+combination of a server name, server port and a realm is called a
+domain.
+
+<pre>
+Auth-domain: theserver:theport/therealm
+Auth-user: me
+Auth-passwd: my-password
+
+Auth-domain: theserver:theport/otherrealm
+Auth-user: other-me
+Auth-password: other-password
+</pre>
+
+W3mir will tell you what the name of the realm is if it is unable to
+authenticate itself with the server. You may also use '*' as the realm
+name if you only copy documents from one realm on that server.
+
+<hr>
+
+<h3><a name="proxy">How do I use a proxy server?</a></h3>
+
+<p>On some secured sites you have to access the Internet through proxy
+servers to get out of the internal network.
+
+<p>A proxy server has a host name, and a port you must use. On the
+command line you simply specify <tt>-P proxy-host-name:proxy-port</tt>. In
+the configuration file you put this:
+
+<pre>
+HTTP-Proxy: proxy-host-name:proxyport
+</pre>
+
+<p>The main advantage of working through proxy servers other than
+security is that you take advantage of any caching the proxy server
+which can speed up retrievals enormously.
+
+<p>Another use of the proxy option is to "prime" the proxy servers
+cache.  I.e. you can use w3mir to fetch the documents through the proxy
+server to ensure that the documents are cached there later when you
+want to read them with your browser.  If you also specify 
+
+<pre>
+File-Disposition: forget
+</pre>
+
+<p>it won't even use any space on your disk, w3mir will just process
+the documents looking for URLs and then <em>not</em> save them.
+
+<hr>
+
+<h3><a name="pauth">How do I authenticate myself to a proxy
+server?</a></h3>
+
+<p>Some proxy servers demands a user-name and password to let you use
+them.  W3mir does not support the domain concept in connection with
+proxy authentication because the author cannot imagine that it will be
+needed.  You need to put this in your configuration file:
+
+<pre>
+HTTP-Proxy-user: proxy-username
+HTTP-Proxy-passwd: proxy-password
+</pre>
+
+<hr>
+
+<h3><a name="proxytweak">How do I ensure that the proxy server
+...?</a></h3>
+
+<p>HTTP/1.0 proxy servers may be told to not use its current copy of
+a document if you specify the <tt>-pflush</tt> command-line option.  Or 
+
+<pre>
+Proxy-Options: refresh
+</pre>
+
+<p>in the configuration file.  This is useful if the proxy has an old
+copy of some document and does not realize that a newer version exists
+on the origin site.  W3mir uses the HTTP/1.0 version of this command
+by default.  You can force w3mir to use the HTTP/1.1 version by adding
+<tt>no-pragma</tt> to the line.  If you do this it will not work at
+all as you want unless the server knows the HTTP/1.1 protocol.
+
+
+<p>HTTP/1.1 proxy servers can be manipulated in a few more ways.  The
+configuration file <tt>Proxy-Options:</tt> directive also takes
+<tt>revalidate</tt> and <tt>no-store</tt> options.  The former tells
+the proxy server to check if there is any newer version available.
+This is, in principle, more network friendly than the <tt>refresh</tt>
+option since it will only cause a copy if there is a newer file
+available.  The <tt>no-store</tt> option tells the proxy server to not
+store the documents you transfer.  This might be useful if the
+documents are 'sensitive' or something like that, but if the proxy
+server does not understand HTTP/1.1 it will not obey this option, and
+it might store the document anyway because the functionality is not
+implemented, so you should not count on this to work.
+
+<hr>
+
+<h3><a name="batchget">How do I batch get files with w3mir?</a></h3>
+
+<p>Normally when fetching files w3mir will process each html (and PDF)
+file to find URLs in them for further retrievals.  This is
+time-consuming, and not always wanted.  Sometimes you simply want to
+get a file, or more, and save it, untouched:
+
+<pre>
+w3mir -B http://www.starwars.com/ http://www.ifi.uio.no/~janl/
+</pre>
+
+<p>There is a companion switch for <tt>-B</tt>, namely <tt>-I</tt>, it
+makes w3mir read URLs from its standard input, one pr. line.  Thus you
+can use w3mir in a pipe to batch get several files whose URLs you find
+in some way.  This is a stupid example:
+
+<pre>
+w3mir -q -l -f http://www.ifi.uio.no/ | w3mir -I -B
+</pre>
+
+<p><tt>-B</tt> may also be used with <tt>-r</tt>, but the only effect
+it will have then is to save the html files unchanged on disk, because
+to recurse w3mir <em>has</em> to examine all the html the documents
+for URLs.
+
+<p><b>Please note</b> that using <tt>-B</tt> combined with <tt>-r</tt>
+for mirroring will probably lead to a unstable mirror, because w3mir
+does not get a chance to manipulate the URLs in the documents as it
+needs to be able to maintain a mirror later, and most important of
+all, w3mir needs all html files to contain a &lt;HTML&gt; tag to be
+able to recognize a HTML file as a HTML file. When running with the
+<tt>-B</tt> switch w3mir will not ensure the presence of this and thus
+we must rely on the original documents author to be nice. This is a
+bad bet. In other words, <b>don't use <tt>-B</tt> for recursive
+mirroring</b>, only for batch copying/mirroring of single documents.
+
+<hr>
+
+<h3><a name="cgi">How do I handle CGI?</a></h3>
+
+<p>There is no way w3mir can duplicate the process that happens on the
+Web server when it comes to CGI.  For some CGI programs w3mir can
+simply copy the output and store on disk.  For other CGI programs this
+is not possible, and the only way out is to make w3mir not get the
+involved files using Ignore rules in the configuration file.  These
+will avoid a lot of cgi programs:
+
+<pre>
+Ignore: *.cgi
+Ignore: *-cgi
+</pre>
+
+<p>You might have to add other/more rules for some sites if they have
+other naming conventions or if it's simply impossible to tell from the
+file-name if it's a CGI or not.
+
+<p>When you add ignore rules this causes two things:
+
+<ol>
+<li><p>W3mir will not retrieve documents matching the rules
+<li><p>W3mir will make all references to matching documents point to
+  the site you mirrored from instead of pointing to a non-existent
+  file in the mirror.
+</ol>
+
+<hr>
+
+<h3><a name="imap">How do I handle server side image-maps?</a></h3>
+
+<p>Server side image-maps is yet another thing it's impossible for
+w3mir to relate to.  w3mir simply cannot handle them.  Put ignore
+rules in the configuration file:
+
+<pre>
+Ignore: *.map
+</pre>
+
+<p>W3mir has full support for client side image-maps though.
+
+<hr>
+
+<h3><a name="java">How do I handle Java and ActiveX?</a></h3>
+
+<p>Java and Active X objects are are included in html pages with a
+<tt>&lt;OBJECT&gt;</tt> or <tt>&lt;APPLET&gt;</tt> tag.  W3mir can
+handle these on one condition: The CODEBASE attribute names the
+directory where the program stores its resources (such as
+subprograms, graphic files, sound, text, and so on) and w3mir must
+have read access to this directory.  Otherwise w3mir is without hope,
+it's impossible to extract the name of the resources the program needs
+in any reliable way.
+
+<p>HTML4 supports a attribute that enumerates the resources the
+program needs, w3mir is not able to use this yet.
+
+<hr>
+
+<h3><a name="script">How do I handle java-script and other script
+languages?</a></h3>
+
+<p>W3mir does its best to pass scripts (java-script, perl-script,
+etc...) embedded in the HTML undamaged.  It cannot, however, extract
+any URLs the script generates and the browser would cause the document
+to refer to or embed in a page. 
+
+<p>It will however work if the script generates relative references
+and there is some other way for w3mir to access the referenced file in
+some other manner.  Or if the script generates absolute references and
+the person browsing the mirror has access to the site named, then the
+user will be able to browse the referenced documents via that other
+server.
+
+<hr>
+
+<h3><a name="css">How to I handle the other things with 'partial
+support'</a></h3>
+
+<p>W3mir has partial support for CSS.  This means that
+<tt>&lt;style&gt</tt> tags and the enclosed style data are passed
+undamaged by w3mir.  W3mir will also retrieve the external CSSes named
+in HTML documents.  But w3mir will <em>not</em> (yet) analyze the
+CSSes data to find URLs of other resources (such as fonts) named in
+these.
+
+<p>W3mir also has partial support for Adobe Acrobat (PDF) files.  This
+means that w3mir can extract URLs from PDF files, and get the named
+documents if you want them.  But w3mir cannot edit those URLs so that
+the PDF files point to the mirror instead of wherever on the original
+site they were pointing.  If the PDF files contain absolute URLs they
+will continue pointing to where they were pointing before.  However,
+if the PDF files contain relative references things will work out.
+
+<p>The reason that URLs in PDF files cannot be edited is that they are
+binary and contain byte pointers.  If the URLs length is changed the
+byte pointers will point to the wrong place in the document.  Writing
+code to correct these pointers would be quite complex.  But if you
+write it I will use it.
+
+<hr>
+
+<h3><a name="anon">How do I keep my identity secret?</a></h3>
+
+<p>The HTTP protocol has a header, <tt>User:</tt> which is recommended
+to use by robots, such as w3mir.  Another way to track you is looking
+at the 'Referer:' header w3mir gives in HTTP requests.  Both can be
+disabled:
+
+<pre>
+Disable-headers: referer, user
+</pre>
+
+<p>If you in addition use a proxy server that many other users use
+there is little probability you can be tracked (easily) by the server
+you are copying things from.  You are however much easier to track
+from the logs in the proxy server.  And a court order is quite likely
+to get you tracked in spite of any precautions you take.  
+
+<p>W3mir does not support cookies and thus you cannot be tracked with
+the help of that mechanism.
+
+<hr>
+
+<h3><a name="ns">How do I pretend that I'm using Netscape, Internet
+Explorer or Lynx?</a></h3>
+
+<p>Some web sites give you different documents when you ask for a
+specific URL based on what browser you use, or even what OS you appear
+to be using.  w3mir identifies itself with a string that looks like
+this:
+
+<p><tt>w3mir/<em>version</em>-<em>release-date</em></tt>
+
+<p>Netscape identifies itself with strings that look something like
+this:
+
+<p><tt>Mozilla/3.01 (X11; I; Linux 2.0.30 i586)</tt>
+
+<p>and Internet Explorer says it's something like this:
+
+<p><tt>Mozilla/2.0 (compatible; MSIE 3.02; Windows NT)</tt>
+
+<p>and Lynx says something like this
+
+<p><tt>Lynx/2.6  libwww-FM/2.14</tt>
+
+<p>You can change w3mirs identification with <tt>-agent 'string'</tt>
+on the command line.  In the configuration file you put
+
+<pre>
+Agent: Mozilla/3.01 (X11; I; Linux 2.0.30 i586)
+</pre>
+
+<p>to pretend w3mir is netscape 3.01.
+
+<hr>
+
+<h3><a name="other">How do I do other things?</a></h3>
+
+<p>This document is by no means a complete list of the things you can
+do with w3mir.  The w3mir man page (<tt>man w3mir</tt> or <tt>perldoc
+w3mir</tt> lists more things, and goes into more detail of how things
+work so you can use the knowledge to do neat things.  There are
+several things mentioned only in the man-page that helps you with
+tricky multi-server mirroring, and gives you better control of what to
+get and not to get and under what name to save it on disk.  And a
+couple of other things...
+
+<hr>
+<address>Nicolai Langfeldt 9/7/1998</address>
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir.PL
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir.PL	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3mir.PL	(revision 16561)
@@ -0,0 +1,3291 @@
+# -*-perl-*-
+
+use Config;
+
+&read_makefile;
+$fullperl = resolve_make_var('FULLPERL') || $Config{'perlpath'};
+$islib = resolve_make_var('INSTALLSITELIB');
+
+$name = $0;
+$name =~ s~^.*/~~;
+$name =~ s~.PL$~~;
+
+open(OUT,"> $name") || 
+  die "Could open $name for writing: $!\n";
+
+print "writing $name\n";
+
+while (<DATA>) {
+  if (m~^\#!/.*/perl.*$~o) {
+    # This substitutes the path perl was installed at on this system
+    # _and_ removed any (-w) options.
+    print OUT "#!",$fullperl,$1,"\n";
+    next;
+  }
+  if (/^use lib/o) {
+    # This substitutes the actuall library install path
+    print OUT "use lib '$islib';\n";
+    next;
+  }
+  print OUT;
+}
+
+close(OUT);
+
+# Make it executable too, and writeable
+chmod 0755, $name;
+
+#### The library
+
+sub resolve_make_var ($) {
+
+  my($var) = shift @_;
+  my($val) = $make{$var};
+
+#  print "Resolving: ",$var,"=",$val,"\n";
+  
+  while ($val =~ s~\$\((\S+)\)~$make{$1}~g) {}
+#  print "Resolved: $var: $make{$var} -> $val\n";
+  $val;
+}
+
+
+sub read_makefile {
+
+  open(MAKEFILE, 'Makefile') || 
+    die "Could not open Makefile for reading: $!\n";
+
+  while (<MAKEFILE>) {
+    chomp;
+    next unless m/^([A-Z]+)\s*=\s*(\S+)$/;
+    $make{$1}=$2;
+#    print "Makevar: $1 = $2\n";
+  }
+
+  close(MAKEFILE)
+}
+
+__END__
+#!/local/bin/perl5 -w
+# Perl 5.002 or later.  w3mir is mostly tested with perl 5.004
+#
+# You might want to change or comment out this:
+use lib '/hom/janl/lib/perl';
+#
+# Once upon a long time ago this was Oscar Nierstrasz's
+# <oscar@cui.unige.ch> htget script.
+#
+# Retrieves HTML pages, creating local copies in the _current_ 
+# directory. The script will check for the last-modified stamp on the
+# document, and will not fetch it if the document isn't changed.
+#
+# Bug list is in w3mir-README.
+#
+# Test cases for janl to use:
+#   w3mir -r -fs http://www.eff.org/ - infinite recursion!
+#     --- but cursory examination seems to indicate confused server...
+#   http://java.sun.com/progGuide/index.html check out the img things.
+#
+# Copyright Holders:
+#   Nicolai Langfeldt, janl@ifi.uio.no
+#   Gorm Haug Eriksen, gorm@usit.uio.no
+#   Chris Szurgot, szurgot@itribe.net
+#   Ed Jordan, ed@olympus.itl.net
+#   Alex Knowles, aknowles@avs.com aka ark.
+# Copying and modification is governed by the "Artistic License" enclosed in
+# the w3mir distribution
+#
+# History (European format date: dd/mm/yy):
+#  oscar 25/03/94 -- added -s option to send output to stdout
+#  oscar 28/03/94 -- made HTTP 1.0 the default
+#  oscar 30/05/94 -- special handling of directory URLs missing a trailing "/"
+#   gorm 20/02/95 -- added mirror capacity + fixed a couple of bugs
+#   janl 28/03/95 -- added a working commandline parser.
+#   janl 18/09/95 -- Changed to use a net http library.  Removed dependency of
+#		     url.pl.
+#   janl 19/09/95 -- Extensive rewrite. Simplified a lot, works better. 
+#		     HTML files are now saved in a new and improved manner,
+#		     which means they can be recognized as such w/o fancy
+#		     filename extention type rules.
+# szurgot 27/01/96-- Added "Plaintextmode" wrapper to binmode PAGE. 
+#                    binmode page is required under Win32, but broke modified
+#                    checking
+#                 -- Minor change added ; to "# '" strings for Emacs cperl-mode
+# szurgot 07/02/96-- When reading in local file for checking of URLs changed
+#                    local ($/) =0; to equal undef;
+#   janl 08/02/96 -- Added szurgot's changes and changed them :-)
+# szurgot 09/02/96-- Added code to strip /#.*$/ from urls when reading from 
+#                    local file
+#                 -- Added hasAlarm variable to w3http.pl. Set to 1 if you have
+#                    alarm(). 0 otherwise.
+#                 -- Moved code setting up the valid extensions list into the
+#                    args processing where it belonged
+#   janl 20/02/96 -- Added szurgot changes again.
+#		  -- Make timeout code work.
+#   		  -- and made another win32 test.
+#   janl 19/03/96 -- Worked through the code for handling not-modified 
+#    		     documents, it was a bit shabby after htmlop was intro'ed.
+#   janl 20/03/96 -- -l fix
+#   janl 23/04/96 -- Added -fs by request (by Rik Faith)
+#   janl 16/05/96 -- Made -R mandatory, added use and support for
+#			w3http::SAVEBIN
+# szurgot 19/05/96-- Win95 adaptions.
+#   janl 19/05/96 -- -C did not exactly work as expected.  Thanks to Petr
+#		     Novak for bug descriptions.
+#   janl 19/05/96 -- Changed logic for @didntget, @got and so on to use
+#		     @queue and %urlstat.
+#   janl 09/09/96 -- Removed -R switch.
+#   janl 14/09/96 -- Added ir (initial referer) switch
+#   janl 21/09/96 -- Made retry code saner.  There probably needs to be a
+#                    sleep before retry comencing switch.  When no tty is
+#                    present it should be fairly long.
+#   gorm 15/09/96 -- Added cr (check robot) switch. Default to 1 (on)
+#   janl 22/09/96 -- Modified gorms patch to use WWW::RobotRules.  Changed
+#		     robot switch to be consistent with current w3mir
+#                    practice.
+#   janl 27/09/96 -- Spelling corrections from charles.curran@ox.ac.uk
+#                 -- Folded in manual diffs from ark.
+#    ark 24/09/96 -- Simple facilities to edit the incomming file(s)
+#   janl 27/09/96 -- Added switch to enable <!--NOMIRROR--> editing and
+#		     foolproofed ark's patch a bit.
+#   janl 02/10/96 -- Added -umask switch.
+#		  -- Redirected documents did not have a meaningful referer
+#		     value (it was undefined).
+#		  -- Got w3mir into strict discipline, found some typos...
+#   janl 20/10/96 -- Mtime is preserved
+#   janl 21/10/96 -- -lc switch added.  Mtime preservation works better.
+#   janl 06/11/96 -- Treat 301 like 302.
+#   janl 02/12/96 -- Added config file code, fetch/ignore rules, apply
+#   janl 04/12/96 -- Better checking of config input.
+#   janl 06/12/96 -- Putting together the URL selection/editing brains.
+#   janl 07/12/96 -- Checking out some bugs.  Adding multiscope options.
+#   janl 12/12/96 -- Adding to and defeaturing the multiscope options.
+#   janl 13/12/96 -- Continuing work in multiscope stuff
+#                 -- Unreferenced file and empty directory removal works.
+#   janl 19/02/97 -- Can extract urls from adobe acrobat pdf files :-)
+#		     Important: It does _not_ edit urls, so they still
+#		     point at the original site(s).
+#   janl 21/02/97 -- Fix -lc bug related to case and the apply things.
+#		  -- only use SAVEURL if needed
+#   janl 11/03/97 -- Finish work on SAVEURL conditional.
+#		  -- Fixed directory removal code.
+#                 -- parse_args did not abort when unknown option/argument
+#		     was specified.
+#   janl 12/03/97 -- Made test case for -lc.  Didn't work.  Fixed it.  I think.
+#		     Realized we have bug w.r.t. hostname caseing.
+#   janl 13/03/97 -- All redirected to URLs within scope are now queued.  
+#		     That should make the mirror more complete, but it won't
+#		     help browsability when it comes to the redirected doc.
+#		  -- Moved robot retrival to the inside of the mirror loop
+#		     since we now possebly mirror several sites.
+#		  -- Changed 'fetch-options' to 'options'.
+#                 -- Added 'proxy-options'/-pflush to controll proxy server(s).
+#   janl 09/04/97 -- Started using URI::URL.
+#   janl 11/04/97 -- Debugging and using URI::URL more correctly various places
+#   janl 09/05/97 -- Added --agent switch
+#   janl 12/05/97 -- Simplified scope checks for root URL, changed URL 'apply'
+#		     processing.
+#		  -- Small output formating fix in the robot rules code.
+#		  -- Version is now 0.99
+#   janl 14/05/97 -- htmpop no-longer puts '<!DOCTYPE...' into doc, so check 
+#		     for '<HTML' instead
+#   janl 11/06/97 -- Made :port optional in server part of auth-domain.
+#			Always removing :80 from server part to match netloc.
+#   janl 22/07/97 -- More debugging of rewrite for new features -B, -I.
+#   janl 01/08/97 -- Fixed bug in RE quoting for Ignore/Fetch
+#   janl 04/08/97 -- s/writepage/write_page/g
+#   janl 07/09/97 -- 0.99b1 is released
+#   janl 19/09/97 -- Kaj Hejer discovers omissions in non-html-url-mining code.
+#                 -- 0.99b2 is released
+#   janl 24/09/97 -- Matt Chapman found bug in realm-name extraction.
+#   janl 10/10/97 -- Referer: header supression supressed User: header instead
+#		  -- Added fixup handling, writes .redirs and .referers
+#		     (no dot in win32)
+#  		  -- Read .w3mirc (w3mir.ini on win32) if present
+#		  -- Stop file removal code from removing these files
+#   janl 16/10/97 -- process_tag was mangling url attributes in tags with more
+#		     than one of them.  Problem found by Robert L. Binkley
+#   janl 04/12/97 -- Fixed problem with authentication, misplaced +
+#		  -- default inter-docuent pause is 0.  I figure it's better
+#		     to keep one httpd occupied in a steady stream than to
+#		     wait for it to die before we talk to it again.
+#   janl 13/12/97 -- The arguments to index.html in the form of index.html/foo
+#		     handling code was incomplete.  To make it complete would
+#		     have been hard, so it was removed.
+#		  -- If a URL changes from file to directory or vice versa
+#		     this is now handled.
+#   janl 11/01/98 -- PDF files with no URLs does not cause warnings now.
+#		  -- Close REFERERS and REDIRECTS before calling w3mfix
+#   janl 22/01/98 -- Proxy authentication as outlined by Christian Geuer
+#   janl 04/02/98 -- Version 1pre1
+#   janl 18/02/98 -- Fixed wild_re after tip by Prentiss Riddle.
+#		  -- Version 1pre2
+#   janl 20/02/98 -- w3http updated to handle complex content-types.
+#		  -- Fix wild_re more, bug noted by James Dumser
+#		  -- 1.0pre3
+#   janl 18/03/98 -- Version 1.0 is released
+#   janl 09/04/98 -- Added feature so user can disable newline conversion.
+#   janl 20/04/98 -- Only convert newlines in HTML files. -> 1.0.2
+#   janl 09/05/98 -- More carefull clean_disk code.
+#		  -- Check if the redirected URL was a root url, if so
+#		     issue a warning and exit.
+#   janl 12/05/98 -- use ->unix_path instead of ->as_string to derive local
+#		     filename.
+#   janl 25/05/98 -- -B didn't work too well.
+#   janl 09/07/98 -- Redirect to fragment broke us, less broken now -> 1.0.4
+#   janl 24/09/98 -- Better errormessages on errors -> 1.0.5
+#   janl 21/11/98 -- Fix errormessages better.
+#   janl 05/01/99 -- Drop 'Referer: (commandline)'
+#   janl 13/04/99 -- Add initial referer to root urls in batch mode.
+#
+# Variable name discipline:
+# - remote, umodified URL.  Variables prefixed 'rum_'
+# - local, filesystem.  Variables prefixed 'lf_'.
+# Use these prefixes so we know what we're working with at all times.
+# Also, URL objects are postfixed _o
+#
+# The apply rules and scope rules work this way:
+# - First apply the user rules to the remote url.
+# - Check if document is within scope after this.
+# - Then apply w3mir's rules to the result.  This results is the local,
+#   filesystem, name.
+#
+# We use features introduced in 5.002.
+require 5.002;
+
+# win32 and $nulldevice need to be globals, other modules use them.
+use vars qw($win32 $nulldevice);
+
+# To figure out what kind of system this is
+BEGIN {
+  use Config;
+  $win32 = ( $Config{'osname'} eq 'MSWin32' ); 
+}
+# More ways to die:
+use Carp;
+# Http module:
+use w3http;
+# html url extraction and manupulation:
+use htmlop;
+# Extract urls from adobe acrobat pdf files:
+use w3pdfuri;
+# Date computer:
+use HTTP::Date;
+# URLs:
+use URI::URL;
+# For flush method
+use FileHandle;
+
+# Full discipline:
+use strict;
+
+# Set params in the http package, HTTP protocol version:
+$w3http::version="1.0";
+
+# The defaults should be for a robotic http agent on good behaviour.
+my $debug=0;			# Debug level
+my $verbose=0;			# Verbosity level, -1 = quiet, 0 = normal, 1...
+my $pause=0;			# Pause between http requests
+my $retryPause=600;		# Pause between retries. 10 minutes.
+my $retry=3;			# Max 3 stabs pr. url.
+my $r=0;			# Recurse? no recursion = absolutify links
+my $remove=0;			# Remove files that are not there?
+my $s=0;			# 0: save on disk 1: stdout 2: just forget 'em
+my $useauth=0;			# Use authorization
+my %authdata;			# Authorization data
+my $check_robottxt = 1;		# Check robots.txt
+my $do_referer = 1;		# Send referers header
+my $do_user = 1;		# Send user header
+my $cache_header = '';		# The cache-control/pragma: no-cache header
+my $using_proxy = 0;		# Using proxy server or not?
+my $batch=0;			# Batch get URLs?
+my $read_urls=0;		# Get urls from STDIN?
+my $abs=0;			# Absolutify URLs?
+my $immediate_redir=0;		# Immediately follow a redirect?
+my @root_urls;			# This is where we start, the root documents
+my @root_dirs;			# The corresponding directories.  for remove
+my $chdirto='';			# Place to chdir to after reading config file
+my %nodelete=();		# Files that should not be deleted
+my $numarg=0;			# Number of arguments accepted.
+
+# Fixup related things
+my $fixrc='';			# Name of w3mfix config file
+my $fixup=1;			# Do things needed to run fixup
+my $runfix=0;			# Run w3mfix for user?
+my $fixopen=0;			# Fixup files open?
+
+my $indexname='index.html';
+
+my $VERSION;
+$VERSION='1.0.8';
+$w3http::agent = my $w3mir_agent = "w3mir/$VERSION-1999-05-28";
+my $iref='';			# Initial referer.  Must evaluate to false
+
+# Derived settings	
+my $mine_urls=0;		# Mine URLs from documents?
+my $process_urls=0;		# Perform (URL) processing of documents?
+
+# Queue of urls to get.
+my @rum_queue = ();
+my @urls = ();
+# URL status map.
+my %rum_urlstat = ();
+# Status codes:
+my $QUEUED   = 0;		# Queued but not gotten yet.
+my $TERROR   = 100;		# Transient error, retry later
+my $HLERR    = 101;		# Permanent error, give up
+my $GOTIT    = 200;		# Gotten. Note similarity to http result code
+my $NOTMOD   = 304;		# Not modified.
+# Negative codes for nonexistent files, easier to check.
+my $NEVERMIND= -1;		# Don't want it
+my $REDIR    = -302;		# Does not exist, redirected
+my $ENOTFND  = -404;		# Does not exist.
+my $OTHERERR = -600;		# Some other error happened
+my $FROBOTS  = -601;		# Forbidden by robots.txt rule
+
+# Directory/files survey:
+my %lf_file;		# What files are present in FS?  Disposition?  One of:
+my $FILEDEL=0;		# Delete file
+my $FILEHERE=1;		# File present in filesystem only
+my $FILETHERE=2;	# File present on server too.
+my %lf_dir;		# Number of files/dirs in dir.  If 0 dir is 
+			# eligible for deletion.
+
+my %fiddled=();		# If a file becomes a directory or a directory
+                        # becomes a file it is considered fiddled and
+                        # w3mir will not fiddle with it again in this
+                        # run.
+
+# Bitbucket device, very OS dependent.
+$nulldevice='/dev/null';
+$nulldevice='nul:' if ($win32);
+
+# What to get, and not.
+# Text of user supplied fetch/ignore rules
+my $rule_text=" # User defined fetch/ignore rules\n";
+# Code ref to the rule procedure
+my $rule_code;
+
+# Code to prefix and postfix the generated code.  Prefix should make
+# $_ contain the url to match.  Postfix should return 1, the default
+# is to get the url/file.
+my $rule_prefix='$rule_code = sub { local($_) = shift;'."\n";
+my $rule_postfix=" return 1;\n}";
+
+# Scope tests generated by URL/Also directives in cfg. The scope code
+# is just like the rule code, but used for program generated
+# fetch/ignore rules related to multiscope retrival.
+my $scope_fetch=" # Automatic fetch rules for multiscope retrival\n";
+my $scope_ignore=" # Automatic ignore rules for multiscope retrival\n";
+my $scope_code;
+
+my $scope_prefix='$scope_code = sub { local($_) = shift;'."\n";
+my $scope_postfix=" return 0;\n}";
+
+# Function to apply to urls, se rule comments.
+my $user_apply_code;	# User specified apply code
+my $apply_code;		# w3mirs apply code
+my $apply_prefix='$apply_code = sub { local($_) = @_;'."\n";
+my $apply_lc=' $_ = lc $_; ';
+my $apply_postfix='  return $_;'."\n}";
+my @user_apply;		# List of users apply rules.
+my @internal_apply;	# List of w3mirs apply rules.
+
+my $infoloss=0;		# 1 if any URL translations (which cause
+                        # information loss) are in effect.  If this is
+                        # true we use the SAVEURL operation.
+my $list;		# List url on STDOUT?
+my $edit;		# Edit doc? Remove <!--NOMIRROR>...<!--/NOMIRROR-->
+my $header;		# Text to insert in header
+my $lc=0;		# Convert urls/filenames to lowercase?
+my $fetch=0;		# What to fetch: -1: Some, 0: not modified 1: all
+my $convertnl=1;	# Convert newlines?
+
+# Non text/html formats we can extract urls from.  Function must take one
+# argument: the filename.
+my %knownformats = ( 'application/pdf', \&w3pdfuri::list,
+		     'application/x-pdf', \&w3pdfuri::list,
+		   );
+
+# Known 'magic numbers' of the known formats.  The value is used as
+# key in %knownformats.  the key part is a exact match for the
+# following <string> beginning at the first byte of the file.
+# This should probably be made more flexible, but not until we need it.
+
+my %knownmagic = ( '%PDF-', 'application/pdf' );
+
+my $iinline='';	# inline RE code to make RE caseinsensitive 
+my $ipost='';	# RE postfix to make it caseinsensitive
+
+usage() unless parse_args(@ARGV);
+
+{
+  my $w3mirc='.w3mirc';
+  
+  $w3mirc='w3mir.ini' if $win32;
+  
+  if (-f $w3mirc) {
+    parse_cfg_file($w3mirc);
+    $nodelete{$w3mirc}=1;
+  }
+}
+
+# Check arguments and options
+if ($#root_urls>=0) {
+  # OK
+} else {
+  print "URLs: $#rum_queue\n";
+  usage("No URLs given");
+}
+
+# Are we converting newlines today?
+$w3http::convert=0 unless $convertnl;
+
+if ($chdirto) {
+  &mkdir($chdirto.'/this-is-not-created-odd-or-what');
+  chdir($chdirto) || 
+    die "w3mir: Can't change working directory to '$chdirto': $!\n";
+}
+
+$SIG{'INT'}=sub { print STDERR "\nCaught SIGINT!\n"; exit 1; };
+$SIG{'QUIT'}=sub { print STDERR "\nCaught SIGQUIT!\n"; exit 1; };
+$SIG{'HUP'}=sub { print STDERR "\nCaught SIGHUP!\n"; exit 1; };
+
+&open_fixup if $fixup;
+
+# Derive how much document processing we should do.
+$mine_urls=( $r || $list );
+$process_urls=(!$batch && !$edit && !$header);
+# $abs can be set explicitly with -abs, and implicitly if not recursing
+$abs = 1 unless $r;
+print "Absolute references\n" if $abs && $debug;
+
+# Cache_controll specified but proxy not in use?
+die "w3mir: If you want to control a cache, use a proxy server!\n"
+  if ($cache_header && !$using_proxy);
+
+# Compile the second order code
+
+# - The rum scope tests
+my $full_rules=$scope_prefix.$scope_fetch.$scope_ignore.$scope_postfix;
+# warn "Scope rules:\n-------------\n$full_rules\n---------------\n";
+eval $full_rules;
+
+die "w3mir: Program generated rules did not compile.\nPlease report to w3mir-core\@usit.uio.no.  The code is:\n----\n".
+  $full_rules."\n----\n"
+  if !defined($scope_code);
+
+$full_rules=$rule_prefix.$rule_text.$rule_postfix;
+# warn "Fetch rules:\n-------------\n$full_rules\n---------------\n";
+eval $full_rules;
+
+# - The user specified rum tests
+die "w3mir: Ignore/Fetch rules did not compile.\nPlease report to w3mir-core\@usit.uio.no.  The code is:\n----\n".
+  $full_rules."\n----\n"
+  if !defined($rule_code);
+
+# - The user specified apply rules
+
+my $full_apply=$apply_prefix.($lc?$apply_lc:'').
+  join($ipost.";\n",@user_apply).(($#user_apply>=0)?$ipost:"").";\n".
+  $apply_postfix;
+eval $full_apply;
+
+die "w3mir: User apply rules did not compile.\nPlease report to w3mir-core\@usit.uio.no. The code is:
+----
+".$full_apply."
+----\n" if !defined($apply_code);
+
+$user_apply_code=$apply_code;
+
+# - The w3mir generated apply rules
+
+$full_apply=$apply_prefix.($lc?$apply_lc:'').
+  join($ipost.";\n",@internal_apply).(($#internal_apply>=0)?$ipost:"").";\n".
+  $apply_postfix;
+eval $full_apply;
+
+die "Internal apply rules did not compile.  The code is:
+----
+".$full_apply."
+----\n" if !defined($apply_code);
+
+# - Information loss via -lc?  There are other sources as well.
+$infoloss=1 if $lc;
+
+warn "Infoloss is $infoloss\n" if $debug;
+
+# More setup:
+
+$w3http::debug=$debug;
+
+$w3http::verbose=$verbose;
+
+my %rum_referers=();	# Array of referers, key: rum_url
+my $Robot_Blob;		# WWW::RobotsRules object, decides if rum_url is 
+			# forbidden to access for us.
+my $rum_url_o;		# rum url, mostly the current, the one we're getting
+my %gotrobots;		# Did I get robots.txt from site? key: url->netloc
+my($authuser,$authpass);# Username and password for authentication with server
+my @rum_newurls;	# List of rum_urls in document
+
+if ($check_robottxt) {
+  # Eval is only way to defer loading of module until we know it's needed?
+  eval 'use WWW::RobotRules;';
+
+  die "Could not load WWW::RobotRules, try -drr switch\n"
+    unless defined(&WWW::RobotRules::parse);
+
+  $Robot_Blob = new WWW::RobotRules $w3mir_agent;
+}
+
+# We have several main-modes of operation.  Here we select one
+if ($r) {
+
+  die "w3mir: No URLs?  Try 'w3mir -h' for help.\n"
+    if $#root_urls==-1;
+
+  warn "Recursive retrival comencing\n" if $debug;
+
+  die "w3mir: Sorry, you cannot combine -r/recurse with -I/read_urls\n"
+    if $read_urls;
+
+  # Recursive
+  my $url;
+  foreach $url (@root_urls) {
+    warn "Root url dequeued: $url\n" if $debug;
+    if (want_this($url)) {
+      queue($url);
+      &add_referer($url,$iref);
+    } else {
+      die "w3mir: Inconsistent configuration: Specified $url is not inside retrival scope\n";
+    }
+  }
+  mirror();
+
+} else {
+  if ($batch) {
+    warn "Batch retrival commencing\n" if $debug;
+    # Batch get
+    if ($read_urls) { 
+      # Get URLs from <STDIN>
+      while (<STDIN>) {
+	chomp;
+	&add_referer($_,$iref);
+	batch_get($_);
+      }
+    } else {
+      # Get URLs from commandline
+      my $url;
+      foreach $url (@root_urls) {
+	&add_referer($url,$iref);
+      }
+      foreach $url (@root_urls) {
+	batch_get($url);
+      }
+    }
+  } else {
+    warn "Single url retrival commencing\n" if $debug;
+
+    # A single URL, with all processing on
+    die "w3mir: You specified several URLs and not -B/batch\n"
+      if $#root_urls>0;
+    queue($root_urls[0]);
+    &add_referer($root_urls[0],$iref);
+    mirror();
+  }
+}
+
+&close_fixup if $fixup;
+
+# This should clean up files:
+&clean_disk if $remove;
+
+warn "w3mir: That's all (".$w3http::xfbytes.'+',$w3http::headbytes.
+  " bytes of it).\n" unless $verbose<0;
+
+if ($runfix) {
+  eval 'use Config;';
+  warn "Running w3mfix\n";
+  if ($win32) {
+    system($Config{'perlpath'}." w3mfix $fixrc");
+  } else {
+    system("w3mfix $fixrc");
+  }
+}
+
+exit 0;
+
+sub get_document {
+  # Get one document by HTTP ($1/rum_url_o).  Save in given filename ($2).
+  # Possebly returning references found in the document.  Caller must
+  # set up referer array, check wantedness and everything else.  We
+  # handle authentication here though.
+
+  my($rum_url_o)=shift;
+  my($lf_url)=shift;
+  croak("\$rum_url_o is empty") if !defined($rum_url_o) || !$rum_url_o;
+  croak("$lf_url is empty") if !defined($lf_url) || !$lf_url;
+
+  # Make sure it's an object
+  $rum_url_o = url $rum_url_o 
+    unless ref $rum_url_o;
+  
+  # Derive a filename from the url, the filename contains no URL-quoting
+  my($lf_name) = (url "file:$lf_url")->unix_path;
+
+  # Make all intermediate directories
+  &mkdir($lf_name) if $s==0;
+
+  my($rum_as_string) = $rum_url_o->as_string;
+
+  print STDERR "GET_DOCUMENT: '",$rum_as_string,"' -> '",$lf_name,"'\n"
+    if $debug;
+
+  my $hostport;
+  my $www_auth='';	# Value of that http reply header
+  my $page_ref;
+  my @rum_newurls;	# List of URLs extracted
+  my $url_extractor;
+  my $do_query;		# Do query or not?
+
+  if (defined($rum_urlstat{$rum_as_string}) && 
+      $rum_urlstat{$rum_as_string}>0) {
+    warn "w3mir: Internal error, ".$rum_as_string.
+      " queued several times\n";
+    next;
+  }
+
+  # Goto here if we want to retry b/c of authentication
+  try_again:
+
+  # Start building the extra http::query arguments again
+  my @EXTRASTUFF=();
+
+  # We'll start by assuming that we're doing the query.
+  $do_query=1;
+    
+  # If we're not checking the timestamp, or the file does not exist
+  # then we get the file unconditionally.  Otherwise we only want it
+  # if it's updated.
+
+  if ($fetch==1) {
+    # Nothing do do?
+  } else {
+    if (-f $lf_name) {
+      if ($fetch==-1) {
+	print STDERR "w3mir: ".($infoloss?$rum_as_string:$lf_name).
+	  ", already have it" if $verbose>=0;
+	if (!$mine_urls) {
+	  # If -fs and the file exists and we don't need to mine URLs
+	  # we're finished!
+	  warn "Already have it, no mining, returning!\n" if $debug;
+	  print STDERR "\n" if $verbose>=0;
+	  return;
+	}
+	$w3http::result=1304;	# Pretend it was 'not modified'
+	$do_query=0;
+      } else {
+	push(@EXTRASTUFF,$w3http::IFMODF,$lf_name);
+      }
+    }
+  }
+
+  if ($do_query) {
+
+    # Does the server want authorization for this file?  $www_auth is
+    # only set if authentication was requested the first time around.
+
+    # For testing:
+    # $www_auth='Basic realm="foo"';
+
+    if ($www_auth) {
+      my($authdata,$method,$realm);
+
+      ($method,$realm)= $www_auth =~ m/^(\S+)\s+realm=\"([^\"]+)\"/i;
+      $method=lc $method;
+      $realm=lc $realm;
+      die "w3mir: '$method' authentication needed, don't know that.\n"
+	if ($method ne 'basic');
+      
+      $hostport = $rum_url_o->netloc;
+      $authdata=$authdata{$hostport}{$realm} || $authdata{$hostport}{'*'} ||
+	$authdata{'*'}{$realm} || $authdata{'*'}{'*'};
+
+      if ($authdata) {
+	push(@EXTRASTUFF,$w3http::AUTHORIZ,$authdata);
+      } else {
+	print STDERR "w3mir: No authorization data for $hostport/$realm\n";
+	$rum_urlstat{$rum_as_string}=$NEVERMIND;
+	next;
+      }
+    }
+    
+    push(@EXTRASTUFF,$w3http::FREEHEAD,$cache_header)
+      if ($cache_header);
+
+    # Insert referer header data if at all
+    push(@EXTRASTUFF,$w3http::REFERER,$rum_referers{$rum_as_string}[0])
+      if ($do_referer && exists($rum_referers{$rum_as_string}));
+
+    push(@EXTRASTUFF,$w3http::NOUSER)
+      unless ($do_user);
+
+    # YES, $lf_url is right, w3http::query handles this like an url so
+    # the quoting must all be in place.
+    my $binfile=$lf_url;
+    $binfile='-' if $s==1;
+    $binfile=$nulldevice if $s==2;
+
+    if ($pause) {
+      print STDERR "w3mir: sleeping\n" if $verbose>0;
+      sleep($pause);
+    }
+
+    print STDERR "w3mir: ".($infoloss?$rum_as_string:$lf_name) 
+      unless $verbose<0;
+    print STDERR "\nFile: $lf_name\n" if $debug;
+
+    &w3http::query($w3http::GETURL,$rum_as_string,
+		   $w3http::SAVEBIN,$binfile,
+		   @EXTRASTUFF);
+
+    print STDERR "w3http::result: '",$w3http::result,
+    "' doc size: ", length($w3http::document),
+    " doc type: -",$w3http::headval{'CONTENT-TYPE'},
+    "- plaintexthtml: ",$w3http::plaintexthtml,"\n"
+      if $debug;
+
+    print "Result: ",$w3http::result," Recurse: $r, html: ",
+    $w3http::plaintexthtml,"\n" 
+      if $debug;
+
+  } # if $do_query
+    
+  if ($w3http::result==200) { # 200 OK 
+    $rum_urlstat{$rum_as_string}=$GOTIT;
+
+    if ($mine_urls || $process_urls) {
+
+      if ($w3http::plaintexthtml) {
+	# Only do URL manipulations if this is a html document with no
+	# special content-encoding.  We do not handle encodings, yet.
+
+	my $page;
+
+	print STDERR ($process_urls)?", processing":", url mining"
+	  if $verbose>0;
+	  
+	print STDERR "\nurl:'$lf_url'\n" 
+	  if $debug;
+
+	print "\nMining URLs: $mine_urls, Process: $process_urls\n" 
+	  if $debug;
+	    
+	($page,@rum_newurls) = 
+	  &htmlop::process($w3http::document,
+			   # Only get a new document if wanted
+			   $process_urls?():($htmlop::NODOC),
+			   $htmlop::CANON,
+			   $htmlop::ABS,$rum_url_o,
+			   # Only list urls if wanted
+			   $mine_urls?($htmlop::LIST):(),
+
+			   # If user wants absolute URLs do not
+			   # relativize them
+
+			   $abs?
+			   ():
+			   (
+			    $htmlop::TAGCALLBACK,\&process_tag,$lf_url,
+			    )
+			  );
+
+#	print "URL: ",join("\nURL: ",@rum_newurls),"\n";
+
+	if ($process_urls) {
+	  $page_ref=\$page;
+	  $w3http::document='';
+	} else {
+	  $page_ref=\$w3http::document;
+	}
+
+      } elsif ($s == 0 &&
+	       ($url_extractor = 
+		$knownformats{$w3http::headval{'CONTENT-TYPE'}})) {
+
+	# The knownformats extractors only work on disk files so write
+	# doc to disk if not there already (non-html text will not be)
+	write_page($lf_name,$w3http::document,1);
+
+	# Now we try our hand at fetching URIs from non-html files.
+	print STDERR ", mining URLs" if $verbose>=1;
+	@rum_newurls = &$url_extractor($lf_name);
+	# warn "URLs from PDF: ",join(', ',@rum_newurls),"\n";
+      }
+      
+      
+    } # if ($mine_urls || $process_urls)
+
+#    print "page_ref defined: ",defined($page_ref),"\n";
+#    print "plaintext: ",$w3http::plaintext,"\n";
+
+    $page_ref=\$w3http::document 
+      if !defined($page_ref) && $w3http::plaintexthtml;
+
+    if ($w3http::plaintexthtml) {
+      # ark: this is where I want to do my changes to the page strip
+      # out the <!--NOMIRROR-->...<!--/NOMIRROR--> Stuff.
+      $$page_ref=~ s/<(!--)?\s*NO\s*MIRROR\s*(--)?>[^\000]*?<(!--)?\s*\/NO\s*MIRROR\s*(--)?>//g 
+	if $edit;
+	  
+      if ($header) {
+	# ark: insert a header string at the start of the page
+	my $mirrorstr=$header;
+	$mirrorstr =~ s/\$url/$rum_as_string/g;
+	insert_at_start( $mirrorstr, $page_ref );
+      }
+    }
+
+    write_page($lf_name,$page_ref,0);
+
+    # print "New urls: ",join("\n",@rum_newurls),"\n";
+
+    return @rum_newurls;
+  }
+
+  if ($w3http::result==304 || #  304 Not modified
+      $w3http::result==1304) { # 1304 Have it
+
+    { 
+      # last = out of nesting
+
+      my $rum_urlstat;
+      my $rum_newurls;
+      
+      @rum_newurls=();
+      
+      print STDERR ", not modified"
+	if $verbose>=0 && $w3http::result==304;
+      
+      $rum_urlstat{$rum_as_string}=$NOTMOD;
+
+      last unless $mine_urls;
+      
+      $rum_newurls=get_references($lf_name);
+
+      # print "New urls: ",ref($rum_newurls),"\n";
+      
+      if (!ref($rum_newurls)) {
+	last;
+      } elsif (ref($rum_newurls) eq 'SCALAR') {
+	$page_ref=$rum_newurls;
+      } elsif (ref($rum_newurls) eq 'ARRAY') {
+	@rum_newurls=@$rum_newurls;
+	last;
+      } else {
+	die "\nw3mir: internal error: Unknown return type from get_references\n";
+      }
+      
+      # Check if it's a html file.  I know this tag is in all html
+      # files, because I put it there as I pull them in.
+      last unless $$page_ref =~ /<HTML/i;
+      
+      warn "$lf_name is a html file\n" if $debug;
+      
+      # It's a html document
+      print STDERR ", mining URLs" if $verbose>=1;
+      
+      # This will give us a list of absolute urls
+      (undef,@rum_newurls) = 
+	&htmlop::process($$page_ref,$htmlop::NODOC,
+			 $htmlop::ABS,$rum_as_string,
+			 $htmlop::USESAVED,'W3MIR',
+			 $htmlop::LIST);
+    }
+
+    print STDERR "\n" if $verbose>=0;
+    return @rum_newurls;
+  }
+	
+  if ($w3http::result==302 || $w3http::result==301) { # Redirect
+    # Cern and NCSA httpd sends 302 'redirect' if a ending / is
+    # forgotten on a url.  More recent httpds send 301 'permanent
+    # redirect' in this case.  Here we check if the difference in URLs
+    # is just a / and if so push the url again with the / added.  This
+    # code only works if the http server has the right idea about its
+    # own name.
+    #
+    # 18/3/97: Added code to queue redirected-to-URLs that are within
+    # the scope of the retrival.
+    my $new_rum_url;
+
+    $rum_urlstat{$rum_as_string}=$REDIR;
+
+    # Absolutify the new url, it might be relative to the requested
+    # document.  That's a ugly wart on some servers/admins.
+    $new_rum_url=url $w3http::headval{'location'};
+    $new_rum_url=$new_rum_url->abs($rum_url_o);
+
+    print REDIRS $rum_as_string,' -> ',$new_rum_url->as_string,"\n"
+      if $fixup;
+
+    if ($immediate_redir) {
+      print STDERR " =>> ",$new_rum_url->as_string,", getting that instead\n";
+      return get_document($new_rum_url,$lf_url);
+    }
+
+    # Some redirect to a fragment of another doc...
+    $new_rum_url->frag(undef);
+    $new_rum_url=$new_rum_url->as_string;
+
+    if ($rum_as_string.'/' eq $new_rum_url) {
+      if (grep { $rum_as_string eq $_; } @root_urls) {
+	print STDERR "\nw3mir: missing / in a start URL detected.  Please fix commandline/config file.\n";
+	exit(1);
+      }
+      print STDERR ", missing /\n";
+      queue($new_rum_url);
+      # Initialize referer to something meaningful
+      $rum_referers{$new_rum_url}=$rum_referers{$rum_as_string};
+    } else {
+      print STDERR " =>> $new_rum_url";
+      if (want_this($new_rum_url)) {
+	print STDERR ", getting that\n";
+	queue($new_rum_url);
+	$rum_referers{$new_rum_url}=$rum_referers{$rum_as_string};
+      } else {
+	print STDERR ", don't want it\n";
+      }
+    }
+    return ();
+  }
+
+  if ($w3http::result==403 || # Forbidden
+      $w3http::result==404 || # Not found
+      $w3http::result==406 || # Not Acceptable, hmm, belongs here?
+      $w3http::result==410) { # Gone - no forwarding address known
+
+    $rum_urlstat{$rum_as_string}=$ENOTFND;
+    &handleerror;
+    print STDERR "Was refered from: ",
+      join(',',@{$rum_referers{$rum_as_string}}),
+      "\n" if defined(@{$rum_referers{$rum_as_string}});
+    return ();
+  }
+
+  if ($w3http::result==407) {
+    # Proxy authentication requested
+    die "Proxy server requests authentication but failed to return the\n".
+      "REQUIRED Proxy-Authenticate header for this condition\n"
+	unless exists($w3http::headval{'proxy-authenticate'});
+
+    die "Proxy authentication is required for ".$w3http::headval{'proxy-authenticate'}."\n";
+  }
+
+  if ($w3http::result==401) {
+    # A www-authenticate reply header should acompany a 401 message.
+    if (!exists($w3http::headval{'www-authenticate'})) {
+      warn "w3mir: Server indicated authentication failure but gave no www-authenticate reply\n";
+      $rum_urlstat{$rum_as_string}=$NEVERMIND;
+    } else {
+      # Unauthorized
+      if ($www_auth) {
+	# Failed when authorization data was supplied.
+	$rum_urlstat{$rum_as_string}=$NEVERMIND;
+	print STDERR ", authorization failed data needed for ",
+	$w3http::headval{'www-authenticate'},"\n"
+	  if ($verbose>=0);
+      } else {
+	if ($useauth) {
+	  # First time failure, send back and retry at once with some known
+	  # user/passwd.
+	  $www_auth=$w3http::headval{'www-authenticate'};
+	  print STDERR ", retrying with authorization\n" unless $verbose<0;
+	  goto try_again;
+	} else {
+	  print ", authorization needed: ",
+	    $w3http::headval{'www-authenticate'},"\n";
+	  $rum_urlstat{$rum_as_string}=$NEVERMIND;
+	}
+      }
+    }
+    return ();
+  }
+   
+  # Something else.  
+  &handleerror;
+}
+
+
+sub robot_check {
+  # Check if URL is allowed by robots.txt, if we respect it at all
+  # that is.  Return 1 it allowed, 0 otherwise.
+
+  my($rum_url_o)=shift;
+  my $hostport;
+
+  if ($check_robottxt) {
+    
+    $hostport = $rum_url_o->netloc;
+    if (!exists($gotrobots{$hostport})) {
+      # Get robots.txt from the server
+      $gotrobots{$hostport}=1;
+      my $robourl="http://$hostport/robots.txt";
+      print STDERR "w3mir: $robourl" if ($verbose>=0);
+      &w3http::query($w3http::GETURL,$robourl);
+      $w3http::document='' if ($w3http::result != 200);
+      print STDERR ", processing" if $verbose>=1;
+      print STDERR "\n" if ($verbose>=0);
+      $Robot_Blob->parse($robourl,$w3http::document);
+    }
+
+    if (!$Robot_Blob->allowed($rum_url_o->as_string)) {
+      # It is forbidden
+      $rum_urlstat{$rum_url_o->as_string}=$FROBOTS;
+      warn "w3mir: ",$rum_url_o->as_string,": forbidden by robots.txt\n";
+      return 0;
+    }
+  }
+  return 1;
+}
+
+
+
+sub batch_get {
+  # Batch get _one_ document.
+  my $rum_url=shift;
+  my $lf_url;
+
+  $rum_url_o = url $rum_url;
+
+  return unless robot_check($rum_url_o);
+
+  ($lf_url=$rum_url) =~ s~.*/~~;
+  if (!defined($lf_url) || $lf_url eq '') {
+    ($lf_url=$rum_url) =~ s~/$~~;
+    $lf_url =~ s~.*/~~;
+    $lf_url .= "-$indexname";
+  }
+
+  warn "Batch get: $rum_url -> $lf_url\n" if $debug;
+
+  $immediate_redir=1; # Do follow redirects immediately
+
+  get_document($rum_url,$lf_url);
+}
+
+
+
+sub mirror {
+  # Mirror (or get) the requested url(s). Possibly recursively.
+  # Working from whatever cwd is at invocation we'll retrieve all
+  # files under it in the file hierarchy.
+  
+  my $rum_url;	# URL of the document we're getting now, defined at main level
+  my $lf_url;	# rum_url after apply - and 
+  my $new_lf_url;
+  my @new_rum_urls;
+  my $rum_ref;
+  
+  while (defined($rum_url = pop(@rum_queue))) {
+
+    warn "mirror: Poped $rum_url from queue\n" if $debug;
+
+    # Unwanted URLs should not be queued
+    die "Found url $rum_url that I don't want in queue!\n" 
+      unless defined($lf_url=apply($rum_url));
+
+    $rum_url_o = url $rum_url;
+
+    next unless robot_check($rum_url_o);
+
+    # Figure out the filename for our local filesystem.
+    $lf_url.=$indexname if $lf_url =~ m~/$~ || $lf_url eq '';
+
+    @new_rum_urls = get_document($rum_url_o,$lf_url);
+
+    print join("\n",@new_rum_urls),"\n" if ($list);
+
+    if ($r) {
+      foreach $rum_ref (@new_rum_urls) {
+	# warn "Recursive url: $rum_ref\n";
+	$new_lf_url=apply($rum_ref);
+	next unless $new_lf_url;
+
+	# warn "Want it\n";
+	$rum_ref =~ s/\#.*$//s;		# Clip off section marks
+	
+	add_referer($rum_ref,$rum_url_o->as_string);
+	queue($rum_ref);
+      }
+    }
+
+    @new_rum_urls=();
+
+    # Is the URL queue empty? Are there outstanding retries?  Refill
+    # the queue from the retry list.
+    if ($#rum_queue<0 && $retry-->0) {
+      foreach $rum_url_o (keys %rum_urlstat) {
+	$rum_url_o = url $rum_url_o;
+	if ($rum_urlstat{$rum_url_o->as_string}==100) {
+	  push(@rum_queue,$rum_url_o->as_string);
+	  $rum_urlstat{$rum_url_o->as_string}=0;
+	}
+      }
+      if ($#rum_queue>=0) {
+	warn "w3mir: Sleeping before retrying.  $retry more times left\n"
+	  if $verbose>=0;
+	sleep($retryPause);
+      }
+    }
+
+  }
+}
+
+
+sub get_references {
+  # Get references from a non-html-on-disk file.  Return references if
+  # we know how to find them.  Return reference do the complete page
+  # if it's html.  Return single numerical 0 if unknown format.
+
+  my($lf_url)=shift;
+  my($urlextractor)=shift;
+
+  my $read; # Buffer of stuff read from file to check filetype
+  my $magic;
+  my $url_extractor;
+  my $rum_ref;
+  my $page;
+
+  warn "w3mir: Looking at local $lf_url\n" if $debug;
+	
+  # Open file and read the first 10kilobytes for file-type-test
+  # purposes.
+  if (!open(TMPF,$lf_url)) {
+    warn "Unable to open $lf_url for reading: $!\n";
+    last;
+  }
+	
+  $page=' 'x10240;
+  $read=sysread(TMPF,$page,length($page),0);
+  close(TMPF);
+	
+  die "Error reading $lf_url: $!\n" if (!defined($read));
+
+  if (!defined($url_extractor)) {
+    $url_extractor=0;
+
+    # Check file against list of magic numbers.
+    foreach $magic (keys %knownmagic) {
+      if (substr($page,0,length($magic)) eq $magic) {
+	$url_extractor = $knownformats{$knownmagic{$magic}};
+	last;
+      }
+    }
+  }
+
+  # Found a extraction method, apply.
+  if ($url_extractor) {
+    print STDERR ", mining URLs" if $verbose>=1;
+    return [&$url_extractor($lf_url)];
+  }
+
+  if ($page =~ /<HTML/i) {
+    open(TMPF,$lf_url) || 
+      die "Could not open $lf_url for reading: $!\n";
+    # read the whole file.
+    local($/)=undef;
+    $page = <TMPF>;
+    close(TMPF);
+    return \$page;
+  }
+
+  return 0;
+}
+
+
+sub open_fixup {
+  # Open the referers and redirects files
+
+  my $reffile='.referers';
+  my $redirfile='.redirs';
+
+  if ($win32) {
+    $reffile="referers";
+    $redirfile="redirs";
+  }
+
+  $nodelete{$reffile} = $nodelete{$redirfile} = 1;
+
+  open(REDIRS,"> $redirfile") || 
+    die "Could not open $redirfile for writing: $!\n";
+
+  autoflush REDIRS 1;
+
+  open(REFERERS,"> $reffile") ||
+    die "Could not open $reffile for writing: $!\n";
+
+  $fixopen=1;
+  eval 'END { close_fixup; 0; }';
+}
+
+
+sub close_fixup {
+  # Close the fixup data files.  In the case of the referer file also
+  # write the entire content
+
+  return unless $fixopen;
+
+  my $referer;
+
+  foreach $referer (keys %rum_referers) {
+    print REFERERS $referer," <- ",join(' ',@{$rum_referers{$referer}}),"\n";
+  }
+
+  close(REFERERS) || warn "Error closing referers file: $!\n";
+  close(REDIRS) || warn "Error closing redirects file: $!\n";
+  $fixopen=0;
+}
+
+
+sub clean_disk {
+  # This procedure removes files that are not present on the server(s)
+  # anymore.
+  
+  # - To avoid removing files that were not fetched due to network
+  #   problems we only do blanket removal IFF all documents were
+  #   fetched w/o problems, eventually.  
+  # - In any case we can remove files the server said were not found
+
+  # The strategy has three main parts:
+  # 1. Find all files we have
+  # 2. Find what files we ought to have
+  # 3. Remove the difference
+  
+  my $complete_retrival=1;	# Flag saying IFF all documents were fetched
+  my $urlstat;			# Tmp storage
+  my $rum_url;
+  my $lf_url;
+  my $lf_dir;
+  my $dirs_to_remove;
+
+  # For fileremoval code
+  eval "use File::Find;" unless defined(&find);
+
+  die "w3mir: Could not load File::Find module.  Don't use -R switch.\n"
+    unless defined(&find);
+  
+  # This to shut up -w
+  $lf_dir=$File::Find::dir;
+
+  # ***** 1. Find out what files we have *****
+  #
+  # This does two things:  For each file or directory found:
+  # - Increases entry count for the container directory
+  # - If it's a file: $lf_file{relative_path}=$FILEHERE;
+
+  chop(@root_dirs);
+  print STDERR "Looking in: ",join(", ",@root_dirs),"\n" if $debug;
+
+  find(\&find_files,@root_dirs);
+
+  # ***** 2. Find out what files we ought to have *****
+  #
+  # First we loop over %rum_urlstat to determine what files are not
+  # present on the server(s).
+  foreach $rum_url (keys %rum_urlstat) {
+    # Figure out name of local file from rum_url
+    next unless defined($lf_url=apply($rum_url));
+
+    $lf_url.=$indexname if $lf_url =~ m~/$~ || $lf_url eq '';
+
+    # find prefixes ./, we must too.
+    $lf_url="./".$lf_url unless substr($lf_url,0,1) eq '/';
+
+    # Ignore if file does not exist here.
+    next unless exists($lf_file{$lf_url});
+    
+    # The apply rules can map several remote files to same local
+    # file.  If we decided to keep file already we stay with that.
+    next if $lf_file{$lf_url}==$FILETHERE;
+    
+    $urlstat=$rum_urlstat{$rum_url};
+
+    # Figure out the status code.
+    if ($urlstat==$GOTIT || $urlstat==$NOTMOD) {
+      # Present on server.  Keep.
+      $lf_file{$lf_url}=$FILETHERE;
+      next;
+    } elsif ($urlstat==$ENOTFND || $urlstat==$NEVERMIND ) {
+      # One of: not on server, can't get, don't want, access forbiden:
+      # Schedule for removal.
+      $lf_file{$lf_url}=$FILEDEL if exists($lf_file{$lf_url});
+      next;
+    } elsif ($urlstat==$OTHERERR || $urlstat==$TERROR) {
+      # Some error occured transfering.
+      $complete_retrival=0;	# The retrival was not complete. Delete less
+    } elsif ($urlstat==$QUEUED) {
+      warn "w3mir: Internal inconsistency, $rum_url marked as queued after retrival terminated\n";
+      $complete_retrival=0;	# Fishy. Be conservative about removing
+    } else {
+      $complete_retrival=0;
+      warn "w3mir: Warning: $rum_url is marked as $urlstat.\n".
+	"w3mir: Please report to w3mir-core\@usit.uio.no.\n";
+    }
+  }				# foreach %rum_urlstat
+
+  # ***** 3. Remove the difference *****
+
+  # Loop over all found files:
+  # - Should we have this file?
+  # - If not: Remove file and decrease directory entry count
+  # Loop as long as there are directories with 0 entry count:
+  # - Loop over all directories with 0 entry count:
+  #   - Remove directory
+  #   - Decrease entry count of parent
+  
+  warn "w3mir: Some error occured, conservative file removal\n"
+    if !$complete_retrival && $verbose>=0;
+
+  # Remove all files we don't want removed from list of files present:
+  foreach $lf_url (keys %nodelete) {
+    print STDERR "Not deleting: $lf_url\n" if $verbose>=1;
+    delete $lf_file{$lf_url} || delete $lf_file{'./'.$lf_url};
+  }
+  
+  # Remove files
+  foreach $lf_url (keys %lf_file) {
+    if (($complete_retrival && $lf_file{$lf_url}==$FILEHERE) ||
+	($lf_file{$lf_url} == $FILEDEL)) {
+      if (unlink $lf_url) {
+	($lf_dir)= $lf_url =~ m~^(.+)/~;
+	$lf_dir{$lf_dir}--;
+	$dirs_to_remove=1 if ($lf_dir{$lf_dir}==0);
+	warn "w3mir: removed file $lf_url\n" if $verbose>=0;
+      } else {
+	warn "w3mir: removal of file $lf_url failed: $!\n";
+      }
+    }
+  }
+  
+  # Remove empty directories
+  while ($dirs_to_remove) {
+    $dirs_to_remove=0;
+    foreach $lf_url (keys %lf_dir) {
+      next if $lf_url eq '.';
+      if ($lf_dir{$lf_url}==0) {
+	if (rmdir($lf_url)) {
+	  warn "w3mir: removed directory $lf_dir\n" if $verbose>=0;
+	  delete $lf_dir{$lf_url};
+	  ($lf_dir)= $lf_url =~ m~^(.+)/~;
+	  $lf_dir{$lf_dir}--;
+	  $dirs_to_remove=1 if ($lf_dir{$lf_dir}==0);
+	} else {
+	  warn "w3mir: removal of directory $lf_dir failed: $!\n";
+	}
+      }
+    }
+  }
+}
+
+
+sub find_files {
+  # This is called by the find procedure for every file/dir found.
+  
+  # This builds two hashes:
+  # lf_file{<file>}: 1: file exists
+  # lf_dir{<dir>): Number of files in directory.
+  
+  lstat($_);
+
+  $lf_dir{$File::Find::dir}++;
+
+  if (-f _) {
+    $lf_file{$File::Find::name}=$FILEHERE;
+  } elsif (-d _) {
+    # null
+    # Bug: If an empty directory exists it will not be removed
+  } else {
+    warn "w3mir: File $File::Find::name has unknown type.  Ignoring.\n";
+  }
+  return 0;
+  
+}
+
+
+sub handleerror {
+  # Handle error status of last http connection, will set the rum_urlstat
+  # appropriately and print a error message.
+
+  my $msg;
+  
+  if ($verbose<0) {
+    $msg="w3mir: ".$rum_url_o->as_string.": ";
+  } else {
+    $msg=": ";
+  }
+
+  if ($w3http::result == 98) {
+    # OS/Network error
+    $msg .= "$!";
+    $rum_urlstat{$rum_url_o->as_string}=$OTHERERR;
+  } elsif ($w3http::result == 100) {
+    # Some kind of error connecting or sending request
+    $msg .= $w3http::restext || "Timeout";
+    $rum_urlstat{$rum_url_o->as_string}=$TERROR;
+  } else {
+    # Other HTTP error
+    $rum_urlstat{$rum_url_o->as_string}=$OTHERERR;
+    $msg .= " ".$w3http::result." ".$w3http::restext;
+    $msg .= " =>> ".$w3http::headval{'location'}
+    if (defined($w3http::headval{'location'}));
+  }
+  print STDERR "$msg\n";
+}
+
+
+sub queue {
+  # Queue given url if appropriate and create a status entry for it
+  my($rum_url_o)=url $_[0];
+
+  croak("BUG: undefined \$rum_url_o")
+    if !defined($rum_url_o);
+
+  croak("BUG: undefined \$rum_url_o->as_string")
+    if !defined($rum_url_o->as_string);
+
+  croak("BUG: ".$rum_url_o->as_string." (fragnent) queued")
+    if $rum_url_o->as_string =~ /\#/;
+  
+  return if exists($rum_urlstat{$rum_url_o->as_string});
+  return unless want_this($rum_url_o->as_string);
+
+  warn "QUEUED: ",$rum_url_o->as_string,"\n" if $debug;
+  
+  # Note lack of scope checks.
+  $rum_urlstat{$rum_url_o->as_string}=$QUEUED;
+  push(@rum_queue,$rum_url_o->as_string);
+}
+
+
+sub root_queue {
+  # Queue function for root urls and directories.  One or the other might
+  # be boolean false, in that case, don't queue it.
+
+  my $root_url_o;
+
+  my($root_url)=shift;
+  my($root_dir)=shift;
+
+  die "w3mir: No fragments in start URLs :".$root_url."\n"
+    if $root_url =~ /\#/;
+
+  if ($root_dir) {
+    print "Root dir: $root_dir\n" if $debug;
+    $root_dir="./$root_dir" unless substr($root_dir,0,1) eq '/' or
+      substr($root_dir,0,2) eq './';
+    push(@root_dirs,$root_dir);
+  }
+
+
+  if ($root_url) {
+    $root_url_o=url $root_url;
+
+    # URL canonification, or what we do of it at least.
+    $root_url_o->host($root_url_o->host);
+
+    warn "Root queue: ".$root_url_o->as_string."\n" if $debug;
+
+    push(@root_urls,$root_url_o->as_string); 
+
+    return $root_url_o;
+ }
+
+}
+
+
+sub write_page {
+  # write a retrieved page to wherever it's supposed to be written.
+  # Added difficulty: all files but plaintext files have already been
+  # written to disk in w3http.
+  
+  # $s == 0 save to disk
+  # $s == 1 dump to stdout
+  # $s == 2 forget
+
+  my($lf_name,$page_ref,$silent) = @_;
+  my($verb);
+
+  if ($silent) {
+    $verb=-1;
+  } else {
+    $verb=$verbose;
+  }
+
+#  confess("\n\$page_ref undefined") if !defined($page_ref);
+  
+  if ($w3http::plaintexthtml) {
+    # I have it in memory
+    if ($s==0) {
+      print STDERR ", saving" if $verb>0;
+
+      while (-d $lf_name) {
+	# This will run once, maybe twice, $fiddled will be canged the
+	# first time
+	if (exists($fiddled{$lf_name})) {
+	  warn "Cannot save $lf_name, there is a directory in the way\n";
+	  return;
+	}
+
+	$fiddled{$lf_name}=1;
+
+	rm_rf($lf_name);
+	print STDERR "w3mir: $lf_name" if $verbose>=0;
+      }
+
+      if (!open(PAGE,">$lf_name")) {
+	warn "\nw3mir: can't open $lf_name for writing: $!\n";
+	return;
+      }
+      if (!$convertnl) {
+	binmode PAGE;
+	warn "BINMODE\n" if $debug;
+      }
+      if ($$page_ref ne '') {
+	print PAGE $$page_ref || die "w3mir: Error writing $lf_name: $!\n";
+      }
+      close(PAGE) || die "w3mir: Error closing $lf_name: $!\n";
+      print STDERR ": ", length($$page_ref), " bytes\n"
+	if $verb>=0;
+      setmtime($lf_name,$w3http::headval{'last-modified'})
+	if exists($w3http::headval{'last-modified'});
+    } elsif ($s==1) {
+      print $$page_ref ;
+    } elsif ($s==2) {
+      print STDERR ", got and forgot it.\n" unless $verb<0;
+    }
+  } else {
+    # Already written by http module, just emit a message if wanted
+    if ($s==0) {
+      print STDERR ": ",$w3http::doclen," bytes\n"
+	if $verb>=0;
+      setmtime($lf_name,$w3http::headval{'last-modified'})
+	  if exists($w3http::headval{'last-modified'});
+    } elsif ($s==2) {
+      print STDERR ", got and forgot it.\n" if $verb>=0;
+    }
+  }
+}
+
+
+sub setmtime {
+  # Set mtime of the given file
+  my($file,$time)=@_;
+  my($tm_sec,$tm_min,$tm_hour,$tm_mday,$tm_mon,$tm_year,$tm_wday,$tm_yday,
+     $tm_isdst,$tics);
+
+  $tm_isdst=0;
+  $tm_yday=-1;
+
+  carp("\$time is undefined"),return if !defined($time);
+
+  $tics=str2time($time);
+  utime(time, $tics, $file) || 
+      warn "Could not change mtime of $file: $!\n";
+}
+
+
+sub movefile {
+  # Rename a file.  Note that copy is not a good alternative, since
+  # copying over NFS is something we want to Avoid.
+
+  # Returns 0 if failure and 1 in case of sucess.
+
+  (my $old,my $new) = @_;
+
+  # Remove anything that might have the name already.
+  if (-d $new) {
+    print STDERR "\n" if $verbose>=0;
+    rm_rf($new);
+    $fiddled{$new}=1;
+    print STDERR "w3mir: $new" if $verbose>=0;
+  } elsif (-e $new) {
+    $fiddled{$new}=1;
+    if (unlink($new)) {
+      print STDERR "\nw3mir: removed $new\nw3mir: $new"
+	if $verbose>=0;
+    } else {
+      return 0;
+    }
+
+  }
+
+  if ($new ne '-' && $new ne $nulldevice) {
+    warn "MOVING $old -> $new\n" if $debug;
+    rename($old,$new) || 
+      warn "Could not rename $old to $new: $!\n",return 0;
+  }
+  return 1;
+}
+
+
+sub mkdir {
+  # Make all intermediate directories needed for a file, the file name
+  # is expected to be included in the argument!
+
+  # Reasons for not using File::Path::mkpath:
+  # - I already wrote this.
+  # - I get to be able to produce as good and precise errormessages as
+  #   unix and perl will allow me.  mkpath will not.
+  # - It's easier to find out if it worked or not.
+  
+  my($file) = @_;
+  my(@dirs) = split("/",$file);
+  my $path;
+  my $dir;
+  my $moved=0;
+
+  if (!$dirs[0]) {
+    shift @dirs;
+    $path='';
+  } else {
+    $path = '.';
+  }
+
+  # This removes the last element of the array, it's meant to shave
+  # off the file name leaving only the directory name, as a
+  # convenience, for the caller.
+  pop @dirs;
+  foreach $dir (@dirs) {
+    $path .= "/$dir";
+    stat($path);
+    # only make if it isn't already there
+    next if -d _;
+
+    while (!-d _) {
+      if (exists($fiddled{$path})) {
+	warn "Cannot make directory $path, there is a file in the way.\n";
+	return;
+      }
+
+      $fiddled{$path}=1;
+
+      if (!-e _) {
+	mkdir($path,0777);
+	last;
+      }
+
+      if (unlink($path)) {
+	warn "w3mir: removed file $path\n" if $verbose>=0;
+      } else {
+	warn "Unable to remove $path: $!\n";
+	next;
+      }
+    
+      warn "mkdir $path\n" if $debug;
+      mkdir($path,0777) ||
+	warn "Unable to create directory $path: $!\n";
+
+      stat($path);
+    }
+  }
+}
+
+
+sub add_referer {
+  # Add a referer to the list of referers of a document.  Unless it's
+  # already there.
+  # Don't mail me if you (only) think this is a bit like a toungetwiser:
+
+  # Don't remember referers if BOTH fixup and referer header is disabled.
+  return if $fixup==0 && $do_referer==0;
+
+  my($rum_referee,$rum_referer) = @_ ;
+  my $re_rum_referer;
+
+  if (exists($rum_referers{$rum_referee})) {
+    $re_rum_referer=quotemeta $rum_referer;
+    if (!grep(m/^$re_rum_referer$/,@{$rum_referers{$rum_referee}})) {
+      push(@{$rum_referers{$rum_referee}},$rum_referer);
+      # warn "$rum_referee <- $rum_referer pushed\n";
+    } else {
+      # warn "$rum_referee <- $rum_referer NOT pushed\n";
+    }
+  } else {
+    $rum_referers{$rum_referee}=[$rum_referer];
+    # warn "$rum_referee <- $rum_referer pushed\n";
+  }
+}
+
+
+sub user_apply {
+  # Apply the user apply rules
+
+  return &$user_apply_code(shift);
+
+# Debug version:
+#  my ($foo,$bar);
+#  $foo=shift;
+#  $bar=&$apply_code($foo);
+#  print STDERR "Apply: $foo -> $bar\n";
+#  return $bar;
+}
+
+sub internal_apply {
+  # Apply the w3mir generated apply rules
+
+  return &$apply_code(shift);
+}
+
+
+sub apply {
+  # Apply the user apply rules.  Then if URL is wanted return result of
+  # w3mir apply rules.  Return the undefined value otherwise.
+
+  my $url = user_apply(shift);
+
+  return undef unless want_this($url);
+
+  internal_apply($url);
+}
+
+
+sub want_this {
+  # Find out if we want the url passed.  Just pass it on to the
+  # generated functions.
+  my($rum_url)=shift;
+
+  # What about robot rules?
+
+  # Does scope rule want this?
+  return &$scope_code($rum_url) &&
+    # Does user rule want this too?
+    &$rule_code($rum_url) 
+  
+}
+
+
+sub process_tag {
+  # Process a tag in html file
+  my $lf_referer = shift;	# User argument
+  my $base_url = shift;		# Not used... why not?
+  my $tag_name = shift;
+  my $url_attrs = shift;
+
+  # Retrun quickly if no URL attributes
+  return unless defined($url_attrs);
+
+  my $attrs = shift;
+
+  my $rum_url;	# The absolute URL
+  my $lf_url;	# The local filesystem url
+  my $lf_url_o; # ... and it's object
+  my $key;
+
+  print STDERR "\nProcess Tag: $tag_name, URL attributes: ", 
+  join(', ',@{$url_attrs}),"\nbase_url: ",$base_url,"\nlf_referer: ",
+  $lf_referer,"\n" 
+    if $debug>2;
+
+  $lf_referer =~ s~^/~~;
+  $lf_referer = "file:/$lf_referer";
+
+  foreach $key (@{$url_attrs}) {
+    if (defined($$attrs{$key})) {
+      $rum_url=$$attrs{$key};
+      printf STDERR "$key = $rum_url\n" if $debug;
+      $lf_url=apply($rum_url);
+      if (defined($lf_url)) {
+
+	printf STDERR "Transformed to $lf_url\n" if $debug>2;
+
+	$lf_url =~ s~^/~~;  # Remove leading / to avoid doubeling
+	$lf_url_o=url "file:/$lf_url";
+
+	# Save new value in the hash
+	$$attrs{$key}=($lf_url_o->rel($lf_referer))->as_string;
+	print STDERR "New value: ",$$attrs{$key},"\n" if $debug>2;
+
+	# If there is potential information loss save the old value too
+	$$attrs{"W3MIR".$key}=$rum_url if $infoloss;
+      }
+    }
+  }
+}
+
+
+sub version {
+  eval 'require LWP;';
+  print $w3mir_agent,"\n";
+  print "LWP version ",$LWP::VERSION,"\n" if defined $LWP::VERSION;
+  print "Perl version: ",$],"\n";
+  exit(0);
+}
+
+
+sub parse_args {
+  my $f;
+  my $i;
+
+  $i=0;
+
+  while ($f=shift) {
+    $i++;
+    $numarg++;
+    # This is a demonstration against Getopts::Long.
+    if ($f =~ s/^-+//) {
+      $s=1,next if $f eq 's';			# Stdout
+      $r=1,next if $f eq 'r';			# Recurse
+      $fetch=1,next if $f eq 'fa';		# Fetch all, no date test
+      $fetch=-1,next if $f eq 'fs';	# Fetch those we don't already have.
+      $verbose=-1,next if $f eq 'q'; 		# Quiet
+      $verbose=1,next if $f eq 'c';		# Chatty
+      &version,next if $f eq 'v';		# Version
+      $pause=shift,next if $f eq 'p';		# Pause between requests
+      $retryPause=shift,next if $f eq 'rp';	# Pause between retries.
+      $s=2,$convertnl=0,next if $f eq 'f';	# Forget
+      $retry=shift,next if $f eq 't';		# reTry
+      $list=1,next if $f eq 'l';		# List urls
+      $iref=shift,next if $f eq 'ir';		# Initial referer
+      $check_robottxt = 0,next if $f eq 'drr';	# Disable robots.txt rules.
+      umask(oct(shift)),next if $f eq 'umask';
+      parse_cfg_file(shift),next if $f eq 'cfgfile';
+      usage(),exit 0 if ($f eq 'help' || $f eq 'h' || $f eq '?');
+      $remove=1,next if $f eq 'R';
+      $cache_header = 'Pragma: no-cache',next if $f eq 'pflush';
+      $w3http::agent=$w3mir_agent=shift,next if $f eq 'agent';
+      $abs=1,next if $f eq 'abs';
+      $convertnl=0,$batch=1,next if $f eq 'B';
+      $read_urls = 1,next if $f eq 'I';
+      $convertnl=0,next if $f eq 'nnc';
+
+      if ($f eq 'lc') {
+	if ($i == 1) {
+	  $lc=1;
+	  $iinline=($lc?"(?i)":"");
+	  $ipost=($lc?"i":"");
+	  next;
+	} else {
+	  die "w3mir: -lc must be the first argument on the commandline.\n";
+	}
+      }
+
+      if ($f eq 'P') {				# Proxy
+	($w3http::proxyserver,$w3http::proxyport)= 
+	  shift =~ /([^:]+):?(\d+)?/;
+	$w3http::proxyport=80 unless $w3http::proxyport;
+	$using_proxy=1;
+	next;
+      }
+    
+      if ($f eq 'd') {		# Debugging level
+	$f=shift;
+	unless (($debug = $f) > 0) {
+	  die "w3mir: debug level must be a number greater than zero.\n";
+	}
+	next;
+      }
+
+      # Those were all the options...
+      warn "w3mir: Unknown option: -$f.  Use -h for usage info.\n";
+      exit(1);
+      
+    } elsif ($f =~ /^http:/) {
+      my ($rum_url_o,$rum_reurl,$rum_rebase,$server);
+
+      $rum_url_o=root_queue($f,'./');
+
+      $rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+      push(@internal_apply,"s/^".$rum_rebase."//");
+      $scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+      $scope_ignore.="return 0 if m/^".
+	quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+
+    } else {
+      # If we get this far then the commandline is broken
+      warn "Unknown commandline argument: $f.  Use -h for usage info.\n";
+      $numarg--;
+      exit(1);
+    }
+  }
+  return 1;
+}
+
+
+sub parse_cfg_file {
+  # Read the configuration file.  Aborts on errors.  Not good to
+  # mirror something using the wrong config.
+
+  my ( $file ) = @_ ;
+  my ($key, $value, $authserver,$authrealm,$authuser,$authpasswd);
+  my $i;
+
+  die "w3mir: config file $file is not a file.\n" unless -f $file;
+  open(CFGF, $file) || die "Could not open config file $file: $!\n";
+
+  $i=0;
+
+  while (<CFGF>) {
+    # Trim off various junk
+    chomp;
+    s/^#.*//;
+    s/^\s+|\s$//g;
+    # Anything left?
+    next if $_ eq '';
+    # Examine remains
+    $i++;
+    $numarg++;
+
+    ($key, $value) = split(/\s*:\s*/,$_,2);
+    $key = lc $key;
+
+    $iref=$value,next if ( $key eq 'initial-referer' );
+    $header=$value,next if ( $key eq 'header' );
+    $pause=numeric($value),next if ( $key eq 'pause' );
+    $retryPause=numeric($value),next if ( $key eq 'retry-pause' );
+    $debug=numeric($value),next if ( $key eq 'debug' );
+    $retry=numeric($value),next if ( $key eq 'retries' );
+    umask(numeric($value)),next if ( $key eq 'umask' );
+    $check_robottxt=boolean($value),next if ( $key eq 'robot-rules' );
+    $edit=boolean($value),next if ($key eq 'remove-nomirror');
+    $indexname=$value,next if ($key eq 'index-name');
+    $s=nway($value,'save','stdout','forget'),next 
+      if ( $key eq 'file-disposition' );
+    $verbose=nway($value,'quiet','brief','chatty')-1,next
+      if ( $key eq 'verbosity' );
+    $w3http::proxyuser=$value,next if $key eq 'http-proxy-user';
+    $w3http::proxypasswd=$value,next if $key eq 'http-proxy-passwd';
+
+    if ( $key eq 'cd' ) {
+      $chdirto=$value;
+      warn "Use of 'cd' is discouraged\n" unless $verbose==-1;
+      next;
+    }
+    
+    if ($key eq 'http-proxy') {
+      ($w3http::proxyserver,$w3http::proxyport)= 
+	$value =~ /([^:]+):?(\d+)?/;
+      $w3http::proxyport=80 unless $w3http::proxyport;
+      $using_proxy=1;
+      next;
+    }
+
+    if ($key eq 'proxy-options') {
+      my($val,$nval,@popts,$pragma);
+      $pragma=1;
+      foreach $val (split(/\s*,\*/,lc $value)) {
+	$nval=nway($val,'no-pragma','revalidate','refresh','no-store',);
+	# Force use of Cache-control: header
+	$pragma=0 if ($nval==0);
+	# use to force proxy to revalidate
+	$pragma=0,push(@popts,'max-age=0') if ($nval==1);
+	# use to force proxy to refresh
+	push(@popts,'no-cache') if ($nval==2);
+	# use if information transfered is sensitive
+	$pragma=0,push(@popts,'no-store') if ($nval==3);
+      }
+      $cache_header=($pragma?'Pragma: ':'Cache-control: ').join(', ',@popts);
+      next;
+    }
+
+
+    if ($key eq 'url') {
+      my ($rum_url_o,$lf_dir,$rum_reurl,$rum_rebase);
+
+      # A two argument URL: line?
+      if ($value =~ m/^(.+)\s+(.+)/i) {
+	# Two arguments.
+	# The last is a directory, it must end in /
+	$lf_dir=$2;
+	$lf_dir.='/' unless $lf_dir =~ m~/$~;
+
+	$rum_url_o=root_queue($1,$lf_dir);
+
+	# The first is a URL, make it more canonical, find the base.
+	# The namespace confusion in this section is correct.(??)
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+	# print "URL: ",$rum_url_o->as_string,"\n";
+	# print "Base: $rum_rebase\n";
+
+	# Translate from rum space to lf space:
+	push(@internal_apply,"s/^".$rum_rebase."/".quotemeta($lf_dir)."/");
+
+	# That translation could lead to information loss.
+	$infoloss=1;
+
+	# Fetch rules tests the rum_url_o->as_string.  Fetch whatever
+	# matches the base.
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+
+	# Ignore whatever did not match the base.
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+
+      } else {
+	$rum_url_o=root_queue($value,'./');
+
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+	# Translate from rum space to lf space:
+	push(@internal_apply,"s/^".$rum_rebase."//");
+
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+      }
+      next;
+    }
+
+    if ($key eq 'also-quene') {
+      print STDERR 
+	"Found 'also-quene' keyword, please replace with 'also-queue'\n";
+      $key='also-queue';
+    }
+
+    if ($key eq 'also' || $key eq 'also-queue') {
+      if ($value =~ m/^(.+)\s+(.+)/i) {
+	my ($rum_url_o,$lf_dir,$rum_reurl,$rum_rebase);
+	# Two arguments.
+	# The last is a directory, it must end in /
+	# print STDERR "URL ",$1," DIR ",$2,"\n";
+	$lf_dir=$2;
+	$lf_dir.='/' unless $lf_dir =~ m~/$~;
+
+	if ($key eq 'also-queue') {
+	  $rum_url_o=root_queue($1,$lf_dir);
+	} else {
+	  root_queue("",$lf_dir);
+	  $rum_url_o=url $1;
+	  $rum_url_o->host(lc $rum_url_o->host);
+	}
+
+	# The first is a URL, find the base
+	$rum_rebase = quotemeta( ($rum_url_o->as_string =~ m~^(.*/)~)[0] );
+
+#	print "URL: $rum_url_o->as_string\n";
+#	print "Base: $rum_rebase\n";
+#	print "Server: $server\n";
+
+	# Ok, now we can transform and select stuff the right way
+	push(@internal_apply,"s/^".$rum_rebase."/".quotemeta($lf_dir)."/");
+	$infoloss=1;
+
+	# Fetch rules tests the rum_url_o->as_string.  Fetch whatever
+	# matches the base.
+	$scope_fetch.="return 1 if m/^".$rum_rebase."/".$ipost.";\n";
+
+	# Ignore whatever did not match the base.  This cures problem
+	# with '..' from base in in rum space pointing within the the
+	# scope in ra space.  We introduced a extra level (or more) of
+	# directories with the apply above.  Must do same with 'Also:'
+	# directives.
+	$scope_ignore.="return 0 if m/^".
+	  quotemeta("http://".$rum_url_o->netloc."/")."/".$ipost.";\n";
+      } else {
+	die "Also: requires 2 arguments\n";
+      }
+      next;
+    }
+
+    if ($key eq 'quene') {
+      print STDERR "Found 'quene' keyword, please replace with 'queue'\n";
+      $key='queue';
+    }
+
+    if ($key eq 'queue') {
+      root_queue($value,"");
+      next;
+    }
+
+    if ($key eq 'ignore-re' || $key eq 'fetch-re') {
+      # Check that it's a re, better that I am strict than for perl to
+      # make compilation errors.
+      unless ($value =~ /^m(.).*\1[gimosx]*$/) {
+	print STDERR "w3mir: $value is not a recognized regular expression\n";
+	exit 1;
+      }
+      # Fall-through to next cases!
+    }
+
+    if ($key eq 'fetch' || $key eq 'fetch-re') {
+      my $expr=$value;
+      $expr = wild_re($expr).$ipost if ($key eq 'fetch');
+      $rule_text.=' return 1 if '.$expr.";\n";
+      next;
+    }
+
+    if ($key eq 'ignore' || $key eq 'ignore-re') {
+      my $expr=$value;
+      $expr = wild_re($expr).$ipost if ($key eq 'ignore');
+      # print STDERR "Ignore expression: $expr\n";
+      $rule_text.=' return 0 if '.$expr.";\n";
+      next;
+    }
+
+
+    if ($key eq 'apply') {
+      unless ($value =~ /^s(.).*\1.*\1[gimosxe]*$/) {
+	print STDERR
+	  "w3mir: '$value' is not a recognized regular expression\n";
+	exit 1;
+      }
+      push(@user_apply,$value) ;
+      $infoloss=1;
+      next;
+    }
+
+    if ($key eq 'agent') {
+      $w3http::agent=$w3mir_agent=$value;
+      next;
+    }
+
+    # The authorization stuff:
+    if ($key eq 'auth-domain') {
+      $useauth=1;
+      ($authserver, $authrealm) = split('/',$value,2);
+      die "w3mir: server part of auth-domain has format server[:port]\n"
+	unless $authserver =~ /^(\S+(:\d+)?)$|^\*$/;
+      $authserver =~ s/:80$//;
+      die "w3mir: auth-domain '$value' is not valid\n"
+	if !defined($authserver) || !defined($authrealm);
+      $authrealm=lc $authrealm;
+    }
+
+    $authuser=$value if ($key eq 'auth-user');
+    $authpasswd=$value if ($key eq 'auth-passwd');
+
+    # Got a full authentication spec?
+    if ($authserver && $authrealm && $authuser && $authpasswd) {
+      $authdata{$authserver}{$authrealm}=$authuser.":".$authpasswd;
+      print "Authentication for $authserver/$authrealm is ".
+	"$authuser/$authpasswd\n" if $verbose>=0;
+      # exit;
+      # Invalidate tmp vars
+      $authserver=$authrealm=$authuser=$authpasswd=undef;
+      next;
+    }
+
+    next if $key eq 'auth-user' || $key eq 'auth-passwd' || 
+      $key eq 'auth-domain';
+
+    if ($key eq 'fetch-options') {
+      warn "w3mir: The 'fetch-options' directive has been renamed to 'options'\nw3mir: Please change your configuration file.\n";
+      $key='options';
+      # Fall through to 'options'!
+    }
+
+    if ($key eq 'options') {
+      
+      my($val,$nval);
+      foreach $val (split(/\s*,\s*/,lc $value)) {
+	if ($i==1) {
+	  $nval=nway($val,'recurse','no-date-check','only-nonexistent',
+		     'list-urls','lowercase','remove','batch','read-urls',
+		     'abs','no-newline-conv');
+	  $r=1,next if $nval==0;
+	  $fetch=1,next if $nval==1;
+	  $fetch=-1,next if $nval==2;
+	  $list=1,next if $nval==3;
+	  if ($nval==4) {
+	    $lc=1;
+	    $iinline=($lc?"(?i)":"");
+	    $ipost=($lc?"i":"");
+	    next ;
+	  }
+	  $remove=1,next if $nval==5;
+	  $convertnl=0,$batch=1,next if $nval==6;
+	  $read_urls=1,next if $nval==7;
+	  $abs=1,next if $nval==8;
+	  $convertnl=0,next if $nval==9;
+        } else {
+	    die "w3mir: options must be the first directive in the config file.\n";
+	}
+      }
+      next;
+    }
+
+    if ($key eq 'disable-headers') {
+      my($val,$nval);
+      foreach $val (split(/\s*,\s*/,lc $value)) {
+	$nval=nway($val,'referer','user');
+	$do_referer=0,next if $nval==0;
+	$do_user=0,next if $nval==1;
+      }
+      next;
+    }
+
+
+    if ($key eq 'fixup') {
+
+      $fixrc="$file";
+      # warn "Fixrc: $fixrc\n";
+
+      my($val,$nval);
+      foreach $val (split(/\s*,\s*/,lc $value)) {
+	$nval=nway($val,'on','run','noindex','off');
+	$runfix=1,next if $nval==1;
+	# Disable fixup
+	$fixup=0,next if $nval==3;
+	# Ignore everyting else
+      }
+      next;
+    }
+
+    die "w3mir: Unrecognized directive ('$key') in config file $file at line $.\n";
+
+  }
+  close(CFGF);
+
+  if (defined($w3http::proxypasswd) && $w3http::proxyuser) {
+    warn "Proxy authentication: ".$w3http::proxyuser.":".
+      $w3http::proxypasswd."\n" if $verbose>=0;
+  }
+
+}
+
+
+sub wild_re {
+  # Here we translate unix wildcard subset to to perlre
+  local($_) = shift;
+
+  # Quote anything that's RE and not wildcard: / ( ) \ | { } + $ ^
+  s~([\/\(\)\\\|\{\}\+)\$\^])~\\$1~g;
+  # . -> \.
+  s~\.~\\.~g;
+  # * -> .*
+  s~\*~\.\*~g;
+  # ? -> .
+  s~\?~\.~g;
+
+  # print STDERR "wild_re: $_\n";
+
+  return $_ = '/'.$_.'/';
+}
+
+
+sub numeric {
+  # Check if argument is numeric?
+  my ( $number ) = @_ ;
+  return oct($number) if ($number =~ /\d+/ || $number =~ /\d+.\d+/);
+  die "Expected a number, got \"$number\"\n";
+}
+
+
+sub boolean {
+  my ( $boolean ) = @_ ;
+
+  $boolean = lc $boolean;
+
+  return 0 if ($boolean eq 'false' || $boolean eq 'off' || $boolean eq '0');
+  return 1 if ($boolean eq 'true' || $boolean eq 'on' || $boolean eq '1');
+  die "Expected a boolean, got \"$boolean\"\n";
+}
+
+
+sub nway {
+  my ( $value ) = shift;
+  my ( @values ) = @_;
+  my ( $val ) = 0;
+
+  $value = lc $value;
+  while (@_) {
+    return $val if $value eq shift;
+    $val++;
+  }
+  die "Expected one of ".join(", ",@values).", got \"$value\"\n";
+}
+
+
+sub insert_at_start {
+  # ark: inserts the first arg at the top of the html in the second arg
+  # janl: The second arg must be a reference to a scalar.
+  my( $str, $text_ref ) = @_;
+  my( @possible ) =("<BODY.*?>", "</HEAD.*?>", "</TITLE.*?>", "<HTML.*?>" );
+  my( $f, $done );
+  
+  $done=0;
+  @_=@possible;
+  
+  while( $done!=1 && ($f=shift) ){
+    print "Searching for: $f\n";
+    if( $$text_ref =~ /$f/i ){
+      print "found it!\n";
+      $$text_ref =~ s/($f)/$1\n$str/i;
+      $done=1;
+    }
+  }
+}
+
+
+
+sub rm_rf {
+  # Recursively remove directories and other files
+  # File::Path::rmtree does a similar thing but the messages are wrong
+
+  my($remove)=shift;
+
+  eval "use File::Find;" unless defined(&finddepth);
+
+  die "w3mir: Could not load File::Find module when trying to remove $remove\n"
+    unless defined(&find);
+
+  finddepth(\&remove_everything,$remove);
+
+  if (rmdir($remove)) {
+    print STDERR "\nw3mir: removed directory $remove\n" if $verbose>=0;
+  } else {
+    print STDERR "w3mir: could not remove $remove: $!\n";
+  }
+}
+
+
+sub remove_everything {
+  # This does the removal
+  ((-d && rmdir($_)) || unlink($_)) && $verbose>=0 &&
+    print STDERR "w3mir: removed $File::Find::name\n";
+}
+
+
+
+sub usage {
+  my($message)=shift @_;
+
+  print STDERR "w3mir: $message\n" if $message;
+  
+  die 'w3mir: usage: w3mir [options] <single-http-url>
+          or: w3mir -B [-I] [options] [<http-urls>]
+
+    Options :
+	-agent <agent>	- Set the agent name.  Default is w3mir
+	-abs		- Force all URLs to be absolute.
+	-B		- Batch-get documents.
+	-I		- The URLs to get are read from standard input.
+	-c		- be more Chatty.
+	-cfgfile <file> - Read config from file
+	-d <debug-level>- set debug level to 1 or 2
+	-drr		- Disable robots.txt rules.
+	-f		- Forget all files, nothing is saved to disk.
+	-fa		- Fetch All, will not check timestamps.
+	-fs		- Fetch Some, do not fetch the files we already have.
+	-ir <referer>	- Initial referer.  For picky servers.
+	-l		- List URLs in the documents retrived.
+	-lc		- Convert all URLs (and filenames) to lowercase.
+				This does not work reliably.
+	-p <n>		- Pause n seconds before retriving each doc.
+	-q		- Quiet, error-messages only
+        -rp <n>		- Retry Pause in seconds.
+	-P <server:port>- Use host/port for proxy http requests
+        -pflush		- Flush proxy server.
+	-r		- Recursive mirroring.
+        -R		- Remove files not referenced or not present on server.
+	-s		- Send output to stdout instead of file
+	-t <n>		- How many times to (re)try getting a failed doc?
+        -umask <umask>  - Set umask for mirroring, must be usual octal format.
+	-nnc		- No Newline Conversion.  Disable newline conversions.
+	-v		- Show w3mir version.
+';
+}
+__END__
+# -*- perl -*- There must be a blank line here
+
+=head1 NAME
+
+w3mir - all purpose HTTP-copying and mirroring tool
+
+=head1 SYNOPSIS
+
+B<w3mir> [B<options>] [I<HTTP-URL>]
+
+B<w3mir> B<-B> [B<options>] <I<HTTP-URLS>>
+
+B<w3mir> is a all purpose HTTP copying and mirroring tool.  The
+main focus of B<w3mir> is to create and maintain a browsable copy of
+one, or several, remote WWW site(s).
+
+Used to the max w3mir can retrive the contents of several related
+sites and leave the mirror browseable via a local web server, or from
+a filesystem, such as directly from a CDROM.  
+
+B<w3mir> has options for all operations that are simple enough for
+options.  For authentication and passwords, multiple site retrievals
+and such you will have to resort to a L</CONFIGURATION-FILE>.  If
+browsing from a filesystem references ending in '/' needs to be
+rewritten to end in '/index.html', and in any case, if there are URLs
+that are redirected will need to be changed to make the mirror
+browseable, see the documentation of B<Fixup> in the
+L</CONFIGURATION-FILE> secton.
+
+B<w3mir>s default behavior is to do as little as possible and to be as
+nice as possible to the server(s) it is getting documents from.  You
+will need to read through the options list to make B<w3mir> do more
+complex, and, useful things.  Most of the things B<w3mir> can do is
+also documented in the w3mir-HOWTO which is available at the B<w3mir>
+home-page (F<http://www.math.uio.no/~janl/w3mir/>) as well as in the
+w3mir distribution bundle.
+
+=head1 DESCRIPTION
+
+You may specify many options and one HTTP-URL on the w3mir
+command line.
+
+A single HTTP URL I<must> be specified either on the command line or
+in a B<URL> directive in a configuration file.  If the URL refers to a
+directory it I<must> end with a "/", otherwise you might get surprised
+at what gets retrieved (e.g. rather more than you expect).
+
+Options must be prefixed with at least one - as shown below, you can
+use more if you want to. B<-cfgfile> is equivalent to B<--cfgfile> or
+even B<------cfgfile>.  Options cannot be I<clustered>, i.e., B<-r -R>
+is not equivalent to B<-rR>.
+
+=over 4
+
+=item B<-h> | B<-help> | B<-?>
+
+prints a brief summary of all command line options and exits.
+
+=item B<-cfgfile> F<file>
+
+Makes B<w3mir> read the given configuration file.  See the next section
+for how to write such a file.
+
+=item B<-r>
+
+Puts B<w3mir> into recursive mode.  The default is to fetch only one
+document and then quit.  'I<recursive>' mode means that all the
+documents linked to the given document that are fetched, and all they
+link to in turn and so on.  But only I<Iff> they are in the same
+directory or under the same directory as the start document.  Any
+document that is in or under the starting documents directory is said
+to be within the I<scope of retrieval>.
+
+=item B<-fa>
+
+Fetch All.  Normally B<w3mir> will only get the document if it has been
+updated since the last time it was fetched.  This switch turns that
+check off.
+
+=item B<-fs>
+
+Fetch Some.  Not the opposite of B<-fa>, but rather, fetch the ones we
+don't have already.  This is handy to restart copying of a site
+incompletely copied by earlier, interrupted, runs of B<w3mir>.
+
+=item B<-p> I<n>
+
+Pause for I<n> seconds between getting each document.  The default is
+30 seconds.
+
+=item B<-rp> I<n>
+
+Retry Pause, in seconds.  When B<w3mir> fails to get a document for some
+technical reason (timeout mainly) the document will be queued for a
+later retry.  The retry pause is how long B<w3mir> waits between
+finishing a mirror pass before starting a new one to get the still
+missing documents.  This should be a long time, so network conditions
+have a chance to get better.  The default is 600 seconds (10 minutes),
+which might be a bit too short, for batch running B<w3mir> I would
+suggest an hour (3600 seconds) or more.
+
+=item B<-t> I<n>
+
+Number of reTries.  If B<w3mir> cannot get all the documents by the
+I<n>th retry B<w3mir> gives up.  The default is 3.
+
+=item B<-drr>
+
+Disable Robot Rules.  The robot exclusion standard is described in
+http://info.webcrawler.com/mak/projects/robots/norobots.html.  By
+default B<w3mir> honors this standard.  This option causes B<w3mir> to
+ignore it.
+
+=item B<-nnc>
+
+No Newline Conversion.  Normally w3mir converts the newline format of
+all files that the web server says is a text file.  However, not all
+web servers are reliable, and so binary files may become corrupted due
+to the newline conversion w3mir performs.  Use this option to stop
+w3mir from converting newlines.  This also causes the file to be
+regarded as binary when written to disk, to disable the implicit
+newline conversion when saving text files on most non-Unix systems.
+
+This will probably be on by default in version 1.1 of w3mir, but not
+in version 1.0.
+
+=item B<-R>
+
+Remove files.  Normally B<w3mir> will not remove files that are no
+longer on the server/part of the retrieved web of files.  When this
+option is specified all files no longer needed or found on the servers
+will be removed.  If B<w3mir> fails to get a document for I<any> other
+reason the file will not be removed.
+
+=item B<-B>
+
+Batch fetch documents whose URLs are given on the commandline.
+
+In combination with the B<-r> and/or B<-l> switch all HTML and PDF
+documents will be mined for URLs, but the documents will be saved on
+disk unchanged.  When used with the B<-r> switch only one single URL
+is allowed.  When not used with the B<-r> switch no HTML/URL
+processing will be performed at all.  When the B<-B> switch is used
+with B<-r> w3mir will not do repeated mirrorings reliably since the
+changes w3mir needs to do, in the documents, to work reliably are not
+done.  In any case it's best not to use B<-R> in combination with
+B<-B> since that can result in deleting rather more documents than
+expected.  Hwowever, if the person writing the documents being copied
+is good about making references relative and placing the <HTML> tag at
+the beginning of documents there is a fair chance that things will
+work even so.  But I wouln't bet on it.  It will, however, work
+reliably for repeated mirroring if the B<-r> switch is not used.
+
+When the B<-B> switch is specified redirects for a given document will
+be followed no matter where they point.  The redirected-to document
+will be retrieved in the place of the original document.  This is a
+potential weakness, since w3mir can be directed to fetch any document
+anywhere on the web.
+
+Unless used with B<-r> all retrived files will be stored in one
+directory using the remote filename as the local filename.  I.e.,
+F<http://foo/bar/gazonk.html> will be saved as F<gazonk.html>.
+F<http://foo/bar/> will be saved as F<bar-index.html> so as to avoid
+name colitions for the common case of URLs ending in /.
+
+=item B<-I>
+
+This switch can only be used with the B<-B> switch, and only after it
+on the commandline or configuration file.  When given w3mir will get
+URLs from standard input (i.e., w3mir can be used as the end of a pipe
+that produces URLs.)  There should only be one URL pr. line of input.
+
+=item B<-q>
+
+Quiet.  Turns off all informational messages, only errors will be
+output.
+
+=item B<-c>
+
+Chatty.  B<w3mir> will output more progress information.  This can be
+used if you're watching B<w3mir> work.
+
+=item B<-v>
+
+Version.  Output B<w3mir>s version.
+
+=item B<-s>
+
+Copy the given document(s) to STDOUT.
+
+=item B<-f>
+
+Forget.  The retrieved documents are not saved on disk, they are just
+forgotten.  This can be used to prime the cache in proxy servers, or
+not save documents you just want to list the URLs in (see B<-l>).
+
+=item B<-l>
+
+List the URLs referred to in the retrieved document(s) on STDOUT.
+
+=item B<-umask> I<n>
+
+Sets the umask, i.e., the permission bits of all retrieved files.  The
+number is taken as octal unless it starts with a 0x, in which case
+it's taken as hexadecimal.  No matter what you set this to make sure
+you get write as well as read access to created files and directories.
+
+Typical values are: 
+
+=over 8
+
+=item 022
+
+let everyone read the files (and directories), only you can change
+them. 
+
+=item 027
+
+you and everyone in the same file-group as you can read, only you can
+change them.
+
+=item 077
+
+only you can read the files, only you can change them.
+
+=item 0
+
+everyone can read, write and change everything.
+
+=back
+
+The default is whatever was set when B<w3mir> was invoked.  022 is a
+reasonable value.
+
+This option has no meaning, or effect, on Win32 platforms.
+
+=item B<-P> I<server:port>
+
+Use the given server and port is a HTTP proxy server.  If no port is
+given port 80 is assumed (this is the normal HTTP port).  This is
+useful if you are inside a firewall, or use a proxy server to save
+bandwidth.
+
+=item B<-pflush>
+
+Proxy flush, force the proxy server to flush it's cache and re-get the
+document from the source.  The I<Pragma: no-cache> HTTP/1.0 header is
+used to implement this.
+
+=item B<-ir> I<referrer>
+
+Initial Referrer.  Set the referrer of the first retrieved document.
+Some servers are reluctant to serve certain documents unless this is
+set right.
+
+=item B<-agent> I<agent>
+
+Set the HTTP User-Agent fields value.  Some servers will serve
+different documents according to the WWW browsers capabilities.
+B<w3mir> normally has B<w3mir>/I<version> in this header field.
+Netscape uses things like B<Mozilla/3.01 (X11; I; Linux 2.0.30 i586)>
+and MSIE uses things like B<Mozilla/2.0 (compatible; MSIE 3.02;
+Windows NT)> (remember to enclose agent strings with spaces in with
+double quotes ("))
+
+=item B<-lc>
+
+Lower Case URLs. Some OSes, like W95 and NT, are not case sensitive
+when it comes to filenames.  Thus web masters using such OSes can case
+filenames differently in different places (apps.html, Apps.html,
+APPS.HTML).  If you mirror to a Unix machine this can result in one
+file on the server becoming many in the mirror.  This option
+lowercases all filenames so the mirror corresponds better with the
+server.
+
+If given it must be the first option on the command line.
+
+This option does not work perfectly.  Most especially for mixed case
+host-names.
+
+=item B<-d> I<n>
+
+Set the debug level.  A debug level higher than 0 will produce lots of
+extra output for debugging purposes.
+
+=item B<-abs>
+
+Force all URLs to be absolute.  If you retrive
+F<http://www.ifi.uio.no/~janl/index.html> and it references foo.html
+the referense is absolutified into
+F<http://www.ifi.uio.no/~janl/foo.html>.  In other words, you get
+absolute references to the origin site if you use this option.
+
+=back
+
+=head1 CONFIGURATION-FILE
+
+Most things can be mirrored with a (long) command line.  But multi
+server mirroring, authentication and some other things are only
+available through a configuration file.  A configuration file can
+either be specified with the B<-cfgfile> switch, but w3mir also looks
+for .w3mirc (w3mir.ini on Win32 platforms) in the directory where
+w3mir is started from.
+
+The configuration file consists of lines of comments and directives.
+A directive consists of a keyword followed by a colon (:) and then one
+or several arguments.
+
+ # This is a comment.  And the next line is a directive:
+ Options: recurse, remove
+
+A comment can only start at the beginning of a line.  The directive
+keywords are not case-sensitive, but the arguments I<might> be.
+
+=over 4
+
+=item Options: I<recurse> | I<no-date-check> | I<only-nonexistent> | I<list-urls> | I<lowercase> | I<remove> | I<batch> | I<input-urls> | I<no-newline-conv>
+
+This must be the first directive in a configuration file.  
+
+=over 8
+
+=item I<recurse>
+
+see B<-r> switch.
+
+=item I<no-date-check>
+
+see B<-fa> switch.
+
+=item I<only-nonexistent>
+
+see B<-fs> switch.
+
+=item I<list-urls>
+
+see B<-l> option.
+
+=item I<lowercase>
+
+see B<-lc> option. 
+
+=item I<remove>
+
+see B<-R> option.
+
+=item I<batch>
+
+see B<-B> option.
+
+=item I<input-urls>
+
+see B<-I> option.
+
+=item I<no-newline-conv>
+
+see B<-nnc> option.
+
+=back
+
+=item URL: I<HTTP-URL> [I<target-directory>]
+
+The URL directive may only appear once in any configuration file.
+
+Without the optional target directory argument it corresponds directly
+to the I<single-HTTP-URL> argument on the command line.  
+
+If the optional target directory is given all documents from under the
+given URL will be stored in that directory, and under.  The target
+directory is most likely only specified if the B<Also> directive is
+also specified.
+
+If the URL given refers to a directory it I<must> end in a "/",
+otherwise you might get quite surprised at what gets retrieved.
+
+Either one URL: directive or the single-HTTP-URL at the command-line
+I<must> be given.
+
+=item Also: I<HTTP-URL directory>
+
+This directive is only meaningful if the I<recurse> (or B<-r>)
+option is given.
+
+The directive enlarges the scope of a recursive retrieval to contain
+the given HTTP-URL and all documents in the same directory or under.
+Any documents retrieved because of this directive will be stored in the
+given directory of the mirror.
+
+In practice this means that if the documents to be retrieved are stored
+on several servers, or in several hierarchies on one server or any
+combination of those.  Then the B<Also> directive ensures that we get
+everything into one single mirror.
+
+This also means that if you're retrieving
+
+  URL: http://www.foo.org/gazonk/
+
+but it has inline icons or images stored in http://www.foo.org/icons/
+which you will also want to get, then that will be retrieved as well by
+entering
+
+  Also: http://www.foo.org/icons/ icons
+
+As with the URL directive, if the URL refers to a directory it I<must>
+end in a "/".
+
+Another use for it is when mirroring sites that have several names
+that all refer to the same (logical) server:
+
+  URL: http://www.midifest.com/
+  Also: http://midifest.com/ .
+
+At this point in time B<w3mir> has no mechanism to easily enlarge the
+scope of a mirror after it has been established.  That means that you
+should survey the documents you are going to retrieve to find out what
+icons, graphics and other things they refer to that you want.  And
+what other sites you might like to retrieve.  If you find out that
+something is missing you will have to delete the whole mirror, add the
+needed B<Also> directives and then reestablish the mirror.  This lack
+of flexibility in what to retrieve will be addressed at a later date.
+
+See also the B<Also-quene> directive.
+
+=item Also-quene: I<HTTP-URL directory>
+
+This is like Also, except that the URL itself is also quened.  The
+Also directive will not cause any documents to be retrived UNLESS they
+are referenced by some other document w3mir has already retrived.
+
+=item Quene: I<HTTP-URL>
+
+This is quenes the URL for retrival, but does not enlarge the scope of
+the retrival.  If the URL is outside the scope of retrival it will not
+be retrived anyway.
+
+The observant reader will see that B<Also-quene> is like B<Also>
+combined with B<Quene>.
+
+=item Initial-referer: I<referer>
+
+see B<-ir> option.
+
+=item Ignore: F<wildcard>
+
+=item Fetch: F<wildcard>
+
+=item Ignore-RE: F<regular-expression>
+
+=item Fetch-RE: F<regular-expression>
+
+These four are used to set up rules about which documents, within the
+scope of retrieval, should be gotten and which not.  The default is to
+get I<anything> that is within the scope of retrieval.  That may not
+be practical though.  This goes for CGI scripts, and especially server
+side image maps and other things that are executed/evaluated on the
+server.  There might be other things you want unfetched as well.
+
+B<w3mir> stores the I<Ignore>/I<Fetch> rules in a list.  When a
+document is considered for retrieval the URL is checked against the
+list in the same order that the rules appeared in the configuration
+file.  If the URL matches any rule the search stops at once.  If it
+matched a I<Ignore> rule the document is not fetched and any URLs in
+other documents pointing to it will point to the document at the
+original server (not inside the mirror).  If it matched a I<Fetch>
+rule the document is gotten.  If not matched by any ruøes the document
+is gotten.
+
+The F<wildcard>s are a very limited subset of Unix-wildcards.
+B<w3mir> understands only 'I<?>', 'I<*>', and 'I<[x-y]>' ranges.
+
+The F<perl-regular-expression> is perls superset of the normal Unix
+regular expression syntax.  They must be completely specified,
+including the prefixed m, a delimiter of your choice (except the
+paired delimiters: parenthesis, brackets and braces), and any of the
+RE modifiers. E.g.,
+
+  Ignore-RE: m/.gif$/i
+
+or
+
+  Ignore-RE: m~/.*/.*/.*/~
+
+and so on.  "#" cannot be used as delimiter as it is the comment
+character in the configuration file.  This also has the bad
+side-effect of making you unable to match fragment names (#foobar)
+directly.  Fortunately perl allows writing ``#'' as ``\043''.
+
+You must be very carefull of using the RE anchors (``^'' and ``$''
+with the RE versions of these and the I<Apply> directive. Given the
+rules:
+
+  Fetch-RE: m/foobar.cgi$/
+  Ignore: *.cgi
+
+the all files called ``foobar.cgi'' will be fetched. However, if the
+file is referenced as ``foobar.cgi?query=mp3'' it will I<not> be
+fetched since the ``$'' anchor will prevent it from matching the
+I<Fetch-RE> directive and then it will match the I<Ignore> directive
+instead. If you want to match ``foobar.cgi'' but not ``foobar.cgifu''
+you can use perls ``\b'' character class which matches a word
+boundrary:
+
+  Fetch-RE: m/foobar.cgi\b/
+  Ignore: *.cgi
+
+which will get ``foobar.cgi'' as well as ``foobar.cgi?query=mp3'' but
+not ``foobar.cgifu''. BUT, you must keep in mind that a lot of
+diffetent characters make a word boundrary, maybe something more
+subtle is needed.
+
+=item Apply: I<regular-expression>
+
+This is used to change a URL into another URL.  It is a potentially
+I<very> powerful feature, and it also provides ample chance for you to
+shoot your own foot. The whole aparatus is somewhat tenative, if you
+find there is a need for changes in how Apply rules work please
+E-mail. If you are going to use this feature please read the
+documentation for I<Fetch-RE> and I<Ignore-RE> first.
+
+The B<Apply> expressions are applied, in sequence, to the URLs in
+their absolute form. I.e., with the whole
+http://host:port/dir/ec/tory/file URL. It is only after this B<w3mir>
+checks if a document is within the scope of retrieval or not. That
+means that B<Apply> rules can be used to change certain URLs to fall
+inside the scope of retrieval, and vice versa.
+
+The I<regular-expression> is perls superset of the usual Unix regular
+expressions for substitution.  As with I<Fetch> and I<Ignore> rules it
+must be specified fully, with the I<s> and delimiting character.  It
+has the same restrictions with regards to delimiters. E.g.,
+
+  Apply: s~/foo/~/bar/~i
+
+to translate the path element I<foo> to I<bar> in all URLs.
+
+"#" cannot be used as delimiter as it is the comment character in the
+configuration file.
+
+Please note that w3mir expects that URLs identifying 'directories'
+keep idenfifying directories after application of Apply rules.  Ditto
+for files.
+
+=item Agent: I<agent>
+
+see B<-agent> option.
+
+=item Pause: I<n>
+
+see B<-p> option.
+
+=item Retry-Pause: I<n>
+
+see B<-rp> option.
+
+=item Retries: I<n>
+
+see B<-t> option.
+
+=item debug: I<n>
+
+see B<-d> option.
+
+=item umask I<n>
+
+see B<-umask> option.
+
+=item Robot-Rules: I<on> | I<off>
+
+Turn robot rules on of off.  See B<-drr> option.
+
+=item Remove-Nomirror: I<on> | I<off>
+
+If this is enabled sections between two consecutive
+
+  <!--NO MIRROR-->
+
+comments in a mirrored document will be removed.  This editing is
+performed even if batch getting is specified.
+
+=item Header: I<html/text>
+
+Insert this I<complete> html/text into the start of the document.
+This will be done even if batch is specified.
+
+=item File-Disposition: I<save> | I<stdout> | I<forget>
+
+What to do with a retrieved file.  The I<save> alternative is default.
+The two others correspond to the B<-s> and B<-f> options.  Only one
+may be specified.
+
+=item Verbosity: I<quiet> | I<brief> | I<chatty>
+
+How much B<w3mir> informs you of it's progress.  I<Brief> is the
+default.  The two others correspond to the B<-q> and B<-c> switches.
+
+=item Cd: I<directory>
+
+Change to given directory before starting work.  If it does not exist
+it will be quietly created.  Using this option breaks the 'fixup'
+code so consider not using it, ever.
+
+=item HTTP-Proxy: I<server:port>
+
+see the B<-P> switch.
+
+=item HTTP-Proxy-user: I<username>
+
+=item HTTP-Proxy-passwd: I<password>
+
+These two are is used to activate authentication with the proxy
+server.  L<w3mir> only supports I<basic> proxy autentication, and is
+quite simpleminded about it, if proxy authentication is on L<w3mir>
+will always give it to the proxy.  The domain concept is not supported
+with proxy-authentication.
+
+=item Proxy-Options: I<no-pragma> | I<revalidate> | I<refresh> | I<no-store>
+
+Set proxy options.  There are two ways to pass proxy options, HTTP/1.0
+compatible and HTTP/1.1 compatible.  Newer proxy-servers will
+understand the 1.1 way as well as 1.0.  With old proxy-servers only
+the 1.0 way will work.  L<w3mir> will prefer the 1.0 way.
+
+The only 1.0 compatible proxy-option is I<refresh>, it corresponds to
+the B<-pflush> option and forces the proxy server to pass the request
+to a upstream server to retrieve a I<fresh> copy of the document. 
+
+The I<no-pragma> option forces w3mir to use the HTTP/1.1 proxy
+control header, use this only with servers you know to be new,
+otherwise it won't work at all.  Use of any option but I<refresh> will
+also cause HTTP/1.1 to be used.  
+
+I<revalidate> forces the proxy server to contact the upstream server
+to validate that it has a fresh copy of the document.  This is nicer
+to the net than I<refresh> option which forces re-get of the document
+no matter if the server has a fresh copy already.
+
+I<no-store> forbids the proxy from storing the document in other than
+in transient storage.  This can be used when transferring sensitive
+documents, but is by no means any warranty that the document can't be
+found on any storage device on the proxy-server after the transfer.
+Cryptography, if legal in your contry, is the solution if you want the
+contents to be secret.
+
+I<refresh> corresponds to the HTTP/1.0 header I<Pragma: no-cache> or
+the identical HTTP/1.1 I<Cache-control> option.  I<revalidate> and
+I<no-store> corresponds to I<max-age=0> and I<no-store> respectively.
+
+=item Authorization
+
+B<w3mir> supports only the I<basic> authentication of HTTP/1.0.  This
+method can assign a password to a given user/server/I<realm>.  The
+"user" is your user-name on the server.  The "server" is the server.
+The I<realm> is a HTTP concept.  It is simply a grouping of files and
+documents.  One file or a whole directory hierarchy can belong to a
+realm.  One server may have many realms.  A user may have separate
+passwords for each realm, or the same password for all the realms the
+user has access to.  A combination of a server and a realm is called a
+I<domain>.
+
+=over 8
+
+=item Auth-Domain: I<server:port/realm>
+
+Give the server and port, and the belonging realm (making a domain)
+that the following authentication data holds for.  You may specify "*"
+wildcard for either of I<server:port> and I<realm>, this will work
+well if you only have one usernme and password on all the servers
+mirrored.
+
+=item Auth-User: I<user>
+
+Your user-name.
+
+=item Auth-Passwd: I<password>
+
+Your password.
+
+=back
+
+These three directives may be repeated, in clusters, as many times as
+needed to give the necessary authentication information
+
+=item Disable-Headers: I<referer> | I<user>
+
+Stop B<w3mir> from sending the given headers.  This can be used for
+anonymity, making your retrievals harder to track.  It will be even
+harder if you specify a generic B<Agent>, like Netscape.
+
+=item Fixup: I<...>
+
+This directive controls some aspects of the separate program w3mfix.
+w3mfix uses the same configuration file as w3mir since it needs a lot
+of the information in the B<w3mir> configuration file to do it's work
+correctly.  B<w3mfix> is used to make mirrors more browseable on
+filesystems (disk or CDROM), and to fix redirected URLs and some other
+URL editing.  If you want a mirror to be browseable of disk or CDROM
+you almost certainly need to run w3mfix.  In many cases it is not
+necessary when you run a mirror to be used through a WWW server.
+
+To make B<w3mir> write the data files B<w3mfix> needs, and do nothing
+else, simply put
+
+=over 8
+
+  Fixup: on
+
+=back
+
+in the configuration file.  To make B<w3mir> run B<w3mfix>
+automatically after each time B<w3mir> has completed a mirror run
+specify
+
+=over 8
+
+  Fixup: run
+
+=back
+
+L<w3mfix> is documented in a separate man page in a effort to not
+prolong I<this> manpage unnecessarily.
+
+=item Index-name: I<name-of-index-file>
+
+When retriving URLs ending in '/' w3mir needs to append a filename to
+store it localy.  The default value for this is 'index.html' (this is
+the most used, its use originated in the NCSA HTTPD as far as I know).
+Some WWW servers use the filename 'Welcome.html' or 'welcome.html'
+instead (this was the default in the old CERN HTTPD).  And servers
+running on limited OSes frequently use 'index.htm'.  To keep things
+consistent and sane w3mir and the server should use the same name.
+Put
+
+  Index-name: welcome.html
+
+when mirroring from a site that uses that convention.
+
+When doing a multiserver retrival where the servers use two or more
+different names for this you should use B<Apply> rules to make the
+names consistent within the mirror.
+
+When making a mirror for use with a WWW server, the mirror should use
+the same name as the new server for this, to acomplish that
+B<Index-name> should be combined with B<Apply>.
+
+Here is an example of use in the to latter cases when Welcome.html is
+the prefered I<index> name:
+
+  Index-name: Welcome.html
+  Apply: s~/index.html$~/Welcome.html~
+
+Similarly, if index.html is the prefered I<index> name.
+
+  Apply: s~/Welcome.html~/index.html~
+
+I<Index-name> is not needed since index.html is the default index name.
+
+=back
+
+=head1 EXAMPLES
+
+=over 4
+
+=item * Just get the latest Dr-Fun if it has been changed since the last
+time
+
+ w3mir http://sunsite.unc.edu/Dave/Dr-Fun/latest.jpg
+
+=item * Recursively fetch everything on the Star Wars site, remove
+what is no longer at the server from the mirror:
+
+ w3mir -R -r http://www.starwars.com/
+
+=item * Fetch the contents of the Sega site through a proxy, pausing
+for 30 seconds between each document
+
+ w3mir -r -p 30 -P www.foo.org:4321 http://www.sega.com/
+
+=item * Do everything according to F<w3mir.cfg>
+
+ w3mir -cfgfile w3mir.cfg
+
+=item * A simple configuration file
+
+ # Remember, options first, as many as you like, comma separated
+ Options: recurse, remove
+ #
+ # Start here:
+ URL: http://www.starwars.com/
+ #
+ # Speed things up
+ Pause: 0
+ #
+ # Don't get junk
+ Ignore: *.cgi
+ Ignore: *-cgi
+ Ignore: *.map
+ #
+ # Proxy:
+ HTTP-Proxy: www.foo.org:4321
+ #
+ # You _should_ cd away from the directory where the config file is.
+ cd: starwars
+ #
+ # Authentication:
+ Auth-domain: server:port/realm
+ Auth-user: me
+ Auth-passwd: my_password
+ #
+ # You can use '*' in place of server:port and/or realm:
+ Auth-domain: */*
+ Auth-user: otherme
+ Auth-user: otherpassword
+
+=item Also:
+
+ # Retrive all of janl's home pages:
+ Options: recurse
+ #
+ # This is the two argument form of URL:.  It fetches the first into the second
+ URL: http://www.math.uio.no/~janl/ math/janl
+ #
+ # These says that any documents refered to that lives under these places
+ # should be gotten too.  Into the named directories.  Two arguments are
+ # required for 'Also:'.
+ Also: http://www.math.uio.no/drift/personer/ math/drift
+ Also: http://www.ifi.uio.no/~janl/ ifi/janl
+ Also: http://www.mi.uib.no/~nicolai/ math-uib/nicolai
+ #
+ # The options above will result in this directory hierarchy under
+ # where you started w3mir:
+ # w3mir/math/janl		files from http://www.math.uio.no/~janl
+ # w3mir/math/drift		from http://www.math.uio.no/drift/personer/
+ # w3mir/ifi/janl		from http://www.ifi.uio.no/~janl/
+ # w3mir/math-uib/nicolai	from http://www.mi.uib.no/~nicolai/
+
+=item Ignore-RE and Fetch-RE
+
+ # Get only jpeg/jpg files, no gifs
+ Fetch-RE: m/\.jp(e)?g$/
+ Ignore-RE: m/\.gif$/
+
+=item Apply
+
+As I said earlier, B<Apply> has not been used for Real Work yet, that
+I know of.  But B<Apply> I<could>, be used to map all web servers at
+the university of Oslo inside the scope of retrieval very easily:
+
+  # Start at the main server
+  URL: http://www.uio.no/
+  # Change http://*.uio.no and http://129.240.* to be a subdirectory
+  # of http://www.uio.no/.
+  Apply: s~^http://(.*\.uio\.no(?:\d+)?)/~http://www.uio.no/$1/~i
+  Apply: s~^http://(129\.240\.[^:]*(?:\d+)?)/~http://www.uio.no/$1/~i
+
+
+=back
+
+There are two rather extensive example files in the B<w3mir> distribution.
+
+=head1 BUGS
+
+=over 4
+
+=item The -lc switch does not work too well.
+
+=back
+
+=head1 FEATURES
+
+These are not bugs.
+
+=over 4
+
+=item URLs with two /es ('//') in the path component does not work as
+some might expect.  According to my reading of the URL spec. it is an
+illegal construct, which is a Good Thing, because I don't know how to
+handle it if it's legal.
+
+=item If you start at http://foo/bar/ then index.html might be gotten
+twice.
+
+=item Some documents point to a point above the server root, i.e.,
+http://some.server/../stuff.html.  Netscape, and other browsers, in
+defiance of the URL standard documents will change the URL to
+http://some.server/stuff.html.  W3mir will not.
+
+=item Authentication is I<only> tried if the server requests it.  This
+might lead to a lot of extra connections going up and down, but that's
+the way it's gotta work for now.
+
+=back
+
+=head1 SEE ALSO
+
+L<w3mfix>
+
+=head1 AUTHORS
+
+B<w3mir>s authors can be reached at I<w3mir-core@usit.uio.no>.
+B<w3mir>s home page is at http://www.math.uio.no/~janl/w3mir/
Index: /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3pdfuri.pm
===================================================================
--- /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3pdfuri.pm	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/w3mir/w3mir-1.0.8/w3pdfuri.pm	(revision 16561)
@@ -0,0 +1,64 @@
+# -*- perl -*-
+#
+# w3pdfuri.pm -- extract uris from a adobe acrobat pdf file.
+#     version 0.1
+$VERSION=0.1;
+#
+# Assumptions:
+# - File starts with %PDF
+# - All URLS match this RE: ^\/URI\s+\(([^)]*)\)\s*$  (found by inspection of
+#   one (1) pdf file (the reader.pdf in the acrobat distribution).
+# - pdf files are potentially very large, so we read it from disk
+#   record by record.
+# - Record separator is ^M
+#
+# History:
+# - 19/02/97 janl: Version 0.1, seems to work with PDF-1.2
+#
+
+package w3pdfuri;
+
+use strict;
+
+sub list ($) {
+  my ($file) = @_;
+
+  my @urls=();
+  local($/)="\r";
+
+  unless (open(PDF,"< $file")) {
+    warn "Could not open $file for input: $!\n";
+    return;
+  }
+
+  $_ = <PDF>;
+  unless (/^%PDF-/) {
+    warn "$file is not a PDF file.\n";
+    close(PDF);
+    return;
+  }
+  
+  while (<PDF>) {
+    chomp;
+    push(@urls,$1) if (m~^/URI\s+\(([^)]*)\)\s*$~);
+  }
+
+  close(PDF);
+
+  return @urls;
+}
+
+1;
+
+__END__
+
+# Test code
+print "reader.pdf: ",
+  join(",",list("/local/lib/acrobat/Reader/help/reader.pdf")),"\n";
+print "Acrobat.pdf: ",
+  join(",",list("/local/lib/acrobat/Reader/Acrobat.pdf")),"\n";
+print "License.pdf: ",
+  join(",",list("/local/lib/acrobat/Reader/License.pdf")),"\n";
+print "MapTypes.pdf: ",
+  join(",",list("/local/lib/acrobat/Reader/MapTypes.pdf")),"\n";
+list("/etc/services");
Index: /gsdl/trunk/gs2build/packages/wget/README
===================================================================
--- /gsdl/trunk/gs2build/packages/wget/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/wget/README	(revision 16561)
@@ -0,0 +1,35 @@
+wget - web site mirroring software
+
+This directory contains a version of the GNU wget program that was written by
+Hrvoje Niksic and others and distributed under the GNU General Public Licence
+
+The GNU wget homepage is http://www.cg.tuwien.ac.at/~prikryl/wget.html
+
+A couple of very small changes were made to this package by Stefan Boddie
+(sjboddie@cs.waikato.ac.nz). The USE_STDARG preprocessor definition was
+added to the VC++ makefile as it appeared to need it to compile on either
+VC++ 4.2 or VC++ 6.0. A change was also made to prevent backslashes from
+mistakenly being converted to "@5c" on windows.
+
+2003/11/27 - sjboddie@cs.waikato.ac.nz made a few more small changes to fix
+h_errno bug preventing code from compiling on latest versions of GCC.
+
+2004/06/30 - kjdon@cs.waikato.ac.nz replaced version 1.5.3 with version 1.9.
+It compiled as is under Windows XP, using VC++ 6.0, under Linux using GCC 3.2.3
+and GCC 2.95.3. So I haven't made any changes. 
+Under Windows, I haven't compiled it with openssl, so it doesn't support
+https://. OpenSSL comes with a warning about it being illegal in some countries
+to distribute cryptographic technology, so I haven't used it. Look at
+wget-1.9/windows/README for Windows compiling instructions if you want to add
+it in.
+
+2008/07/03 - oranfry@cs.waikato.ac.nz replaced version 1.9 with version 1.11.4.
+Version 1.9 didn't compile statically with the new libraries which come with
+fedora, so upgrading. Had to apply the patch at
+http://marc.info/?l=wget&m=115131792408685&w=2 to get it to compile under
+windows(VC++ 6.0), and those changes are in the tar.gz file in the repository.
+Again, no ssl in windows wget. The packages/configure and packages/Makefile
+files perform the same operations on wget as before, just the directory name
+has changed from wget/wget-1.9 to wget/wget-1.11.4.
+
+
Index: /gsdl/trunk/gs2build/packages/wv/README
===================================================================
--- /gsdl/trunk/gs2build/packages/wv/README	(revision 16561)
+++ /gsdl/trunk/gs2build/packages/wv/README	(revision 16561)
@@ -0,0 +1,16 @@
+wv - Microsoft Word converter
+
+This directory contains a version of the wv program.  It was originally
+written by Caolan McNamara and distributed under the GNU General Public
+License.  wv is now maintained by Dom Lachowicz and Martin Vermeer.  wv
+incorporates code from a variety of GPL programs from other sources.
+Thank you all.
+
+The wv homepage is http://wvware.sourceforge.net/
+
+We have included the source code here.  It is a slimmed-down version
+of wv version 0.7.1. It should handle documents created with Word 2 up
+to Word 2000. We have tested it on Linux (slackware 7), darwin (OS X
+10.1.3), cygwin, and solaris 2.8 (via sourceforge's compile farm).
+
+John McPherson -- Apr 2002
Index: /gsdl/trunk/gs2build/src/hashfile/Makefile.in
===================================================================
--- /gsdl/trunk/gs2build/src/hashfile/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2build/src/hashfile/Makefile.in	(revision 16561)
@@ -0,0 +1,79 @@
+###########################################################################
+#
+# Makefile -- src/hashfile
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = 
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	hashfile.cpp
+
+OBJECTS = \
+	hashfile.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = hashfile
+
+
+# Default target: make the hashfile executable
+all: $(EXECUTABLE)
+
+hashfile: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2build/src/hashfile/hashfile.cpp
===================================================================
--- /gsdl/trunk/gs2build/src/hashfile/hashfile.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/src/hashfile/hashfile.cpp	(revision 16561)
@@ -0,0 +1,263 @@
+/**********************************************************************
+ *
+ * hashfile.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.
+ *
+ *********************************************************************/
+
+#include <stdio.h>
+
+#define MAXNUMLEN  100   // bytes
+
+
+// The number class is used to store very long unsigned integers.
+// The least significant of each number is stored first. Numbers can 
+// be no longer than MAXNUMLEN bytes long. A number with length=0
+// is considered to be equal to 0. There should be no extra zeros
+// at the front of the number.
+
+struct number {
+  unsigned int len;
+  unsigned char num[MAXNUMLEN];
+
+  number () {len = 0;}
+};
+
+
+// evaluates a = a + b
+// a and b can be the same number
+void my_inc (number &a, number &b) {
+  unsigned int carry = 0;
+  unsigned int num = 0;
+  unsigned int i = 0;
+
+  while ((carry || (i < a.len) || (i < b.len)) && (i < MAXNUMLEN)) {
+    num = carry;
+    if (i < a.len) num += a.num[i];
+    if (i < b.len) num += b.num[i];
+
+    if (num >= 256) {
+      num -= 256; // assumes that each number in the list < 256
+      carry = 1;
+    } else {
+      carry = 0;
+    }
+    a.num[i] = (unsigned char)num;
+
+    ++i;
+  }
+
+  a.len = i;
+}
+
+
+// evaluates a = a - b if a >= b
+// returns true if the subtraction was possible
+int my_ifpos_dec (number &a, number &b) {
+  int carry = 0;
+  int num = 0;
+  int i = 0;
+  unsigned int len = 0; // the last non-zero digit
+
+  // make sure a >= b
+  if (b.len > a.len) return 0;
+  if (b.len == a.len) {
+    i = a.len - 1;
+    while (i >= 0) {
+      if (a.num[i] > b.num[i]) break;
+      if (a.num[i] < b.num[i]) return 0;
+      --i;
+    }
+  }
+
+  unsigned int j = 0;
+  len = 0;
+  while ((j < a.len) || (j < b.len)) {
+    num = -carry;
+    if (j < a.len) num += a.num[j];
+    if (j < b.len) num -= b.num[j];
+
+    if (num < 0) {
+      num += 256; // assumes each number in the list < 256
+      carry = 1;
+    } else {
+      carry = 0;
+    }
+
+    a.num[j] = (unsigned char) num;
+    
+    ++j;
+    if (num != 0) len = j;
+  }
+
+  a.len = len;
+
+  if (carry) {
+    // panic, this should NOT happen
+    fprintf (stderr, "panic: error in my_ifpos_dec\n");
+  }
+
+  return 1;
+}
+
+
+char *my_convert_num (number &a) {
+  char *result = new char[(a.len*2)+1];
+  int i,len;
+  char convert[16] = {'0','1','2','3','4','5','6','7',
+		      '8','9','a','b','c','d','e','f'};
+
+  // zero might be a special case
+  if (a.len == 0) {
+    result[0] = '0';
+    result[1] = '\0';
+    return result;
+  }
+
+  for (i=a.len-1, len=0; i >= 0; --i) {
+    result[len++] = convert[a.num[i]/16];
+    result[len++] = convert[a.num[i]%16];
+  }
+  result[len] = '\0';
+
+  return result;
+}
+
+
+
+// create a hash string from the contents of a file.
+//
+// The file is treated as a large base 256 number (each char is a digit),
+// and the hash value is the remainder when this number is divided by a
+// very large prime.
+//
+// PROBLEM: This is a flawed hash function because the rightmost (highest)
+// value in remainder is more likely to be a "1" than it is to be any other
+// number.  
+//
+// EVIDENCE: About 50% of the files in any GSDL directory have a hash code
+// that starts with 01* which implies (based on the my_convert_num function)
+// that the rightmost "digit" of the remainder = z such that (z % 16 = 0)
+// and (z / 16 = 1) which means (z = 1).
+//
+// MAJOR REASON: suppose our prime number was 19.  Then, if we have a 
+// reasonably random distribution of numbers for which we are going to 
+// calculate (N % 19), we expect to get a roughly uniform distribution
+// of remainders where the possible values are 0, 1, 2, 3... 18.  The
+// problem is that 9 out of the 19 possible values (10,11... 18) start
+// with the digit 1.  Thus our hascode will start with "01".
+//
+// ANOTHER PROBLEM: Characters in the file are read one at a time; after each 
+// one is read it is prepended to the remainder, then the remainder is
+// recalculated on the string thus far seen.  I am sure the math here 
+// is wrong - if I try calculating (111 mod 7) by the same algorithm, it
+// simply does nor work.
+//
+// ANOTHER POSSIBLE PROLEM: Each character from the file is read into 
+// remainder at the most significant end, and when that character
+// is a zero you get a number like "01" which would be considered 
+// larger than a number like "8" because it is longer (two  digits
+// instead of 1).  
+//
+// These comments added by Gordon Paynter (gwp@cs.waikato.ac.nz) in
+// June 2000.  I didn't write any code, however.
+
+char *hashfile (char *filename) {
+  FILE *infile = (FILE *)NULL;
+  int i;
+
+  number primepow[8];
+  number pow;
+  number remainder;
+  unsigned char c;
+
+  // our prime number (618970019642690137449562111)
+  pow.num[0] = 255;  pow.num[1] = 255;
+  pow.num[2] = 255;  pow.num[3] = 255;
+  pow.num[4] = 255;  pow.num[5] = 255;
+  pow.num[6] = 255;  pow.num[7] = 255;
+  pow.num[8] = 255;  pow.num[9] = 255;
+  pow.num[10] = 255; pow.num[11] = 1;
+  pow.len = 12;
+
+  // calculate 8 multiples of the prime number.
+  // These are used to find the remainder using only subtraction operations
+  for (i=0; i<8; ++i) {
+    primepow[i] = pow;
+    my_inc (pow, pow);
+  }
+
+  // The "remainder" after division by the prime.  Our result.
+  remainder.len = 0;
+
+  // open the file
+  infile = fopen (filename, "rb");
+  if (infile == NULL) {
+    return (char *)NULL;
+  }
+  c = (unsigned char)fgetc(infile);
+  
+  while (!feof (infile)) {
+
+    // make sure the remainder has not grown too large
+    if (remainder.len == MAXNUMLEN-1) {
+      fprintf (stderr, "ERROR - number overflow\n");
+      return (char *)NULL;
+    }
+
+    // remainder = remainder * 256 + c
+    for (i=remainder.len; i>0; --i) {
+      remainder.num[i] = remainder.num[i-1];
+    }
+    remainder.num[0] = c;
+    if (remainder.len > 0 || c != 0) remainder.len = remainder.len+1;
+    
+    // remainder = (remainder % large-prime-number)
+    for (i=7; i>=0; --i) {
+      my_ifpos_dec (remainder, primepow[i]);
+    }
+
+    // read a new character from the file
+    c = (unsigned char)fgetc(infile);
+  }
+
+  fclose (infile);
+
+  return my_convert_num (remainder);
+}
+
+
+int main (int argc, char *argv[]) {
+  int i;
+  char *result;
+
+  for (i = 1; i < argc; ++i) {
+    result = hashfile (argv[i]);
+    if (result == NULL) {
+      printf ("ERROR - could not process %s\n", argv[i]);
+    } else {
+      printf ("%s: %s\n", argv[i], result);
+      delete [] result;
+    }
+  }
+
+  return 0;
+}
Index: /gsdl/trunk/gs2build/src/hashfile/win32.mak
===================================================================
--- /gsdl/trunk/gs2build/src/hashfile/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2build/src/hashfile/win32.mak	(revision 16561)
@@ -0,0 +1,76 @@
+###########################################################################
+#
+# win32 makefile -- hashfile
+# 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  The 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.
+#
+###########################################################################
+ 
+GSDLHOME = ..\..
+GSDL_VC4 = 0
+
+AR = lib
+CC = cl
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+DEFS = -DHAVE_CONFIG_H -D__WIN32__ -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"$(GSDLHOME)\src\hashfile"
+LIBS =
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+ 
+HEADERS = 
+
+SOURCES = hashfile.cpp
+
+OBJECTS = hashfile$o
+
+EXEC = hashfile$e
+ 
+all : $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *$o
+	del $(EXEC)
+
+HASHFILE_OBJS = hashfile$o
+
+hashfile$e: $(HASHFILE_OBJS) $(LIBS)
+	    $(LINK) $(HASHFILE_OBJS) $(LIBS)
Index: /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLT.java
===================================================================
--- /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLT.java	(revision 16561)
+++ /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLT.java	(revision 16561)
@@ -0,0 +1,373 @@
+package org.nzdl.gsdl;
+
+import java.io.*;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import javax.xml.parsers.*;
+import javax.xml.transform.dom.*;
+import org.w3c.dom.*;
+
+
+
+/**
+ *  Use the TraX interface to perform a transformation in the simplest manner possible
+ *  (3 statements).
+ */
+public class ApplyXSLT
+{
+
+    public static final String DOC_START = new String ("<?DocStart?>"); 
+    public static final String DOC_END = new String ("<?DocEnd?>"); 
+    public static final String INPUT_END = new String ("<?Done?>"); 
+
+    private static final String RECORD_ELEMENT = "record";
+    private static final String CONTROLFIELD_ELEMENT = "controlfield";
+    private static final String SUBFIELD_ELEMENT = "subfield";
+    private static final String LEADER_ELEMENT = "leader";
+
+    private final int BEFORE_READING = 0;
+    private final int IS_READING = 1;
+    private String xsl_file;
+    private String mapping_file;
+
+    public ApplyXSLT(){}
+   
+    public ApplyXSLT(String xsl_file)
+    {
+	this.xsl_file = xsl_file; 
+    }
+
+    public ApplyXSLT(String xsl_file,String mapping_file){
+	this.xsl_file = xsl_file; 
+	this.mapping_file = mapping_file;
+    }
+
+    private boolean process()
+    {
+	try{
+
+	// Use System InputStream to receive piped data from the perl program
+	InputStreamReader ir = new InputStreamReader(System.in, "UTF8");
+	BufferedReader br = new BufferedReader(ir);
+	
+	int system_status = BEFORE_READING;
+ 	String a_doc = new String();
+	String output_file = new String();
+
+
+	    while (br.ready()) {
+		                   
+		String this_line = br.readLine();
+		if(system_status == BEFORE_READING){
+		    if(this_line.compareTo(DOC_START) == 0){
+			output_file = br.readLine(); // read the next line as the output file name
+			system_status = IS_READING;
+			a_doc = "";
+		    }
+		    else if(this_line.compareTo(INPUT_END) == 0){
+			return true;
+		    }
+		    else{
+			System.err.println("Undefined process status:" + this_line);
+			system_status = BEFORE_READING;
+		    }
+		    
+		}
+		else if(system_status == IS_READING){
+		    if(this_line.compareTo(DOC_END) == 0){
+			boolean result = false;
+			if (mapping_file !=null && !mapping_file.equals("")){
+			    result = translateXMLWithMapping(a_doc,output_file);			    
+			}
+			else{
+			    result = translateXML(a_doc,output_file); 
+			}
+			
+			if (!result){
+			    System.err.println("Translation Failed!!");
+			    return false;
+			}
+			
+			system_status = BEFORE_READING;
+			
+		    }
+		    else{
+			a_doc = a_doc + this_line + "\n";
+			
+		    }
+		}
+		else{		  
+		    System.err.println ("Undefined system status in ApplyXSLT.java main().");
+		    System.exit(-1);
+		    }
+		
+	    }
+	}catch (Exception e)
+	    {
+		System.err.println("Receiving piped data error!" + e.toString());
+	    }
+				
+	return false;
+    }
+    
+    
+    private boolean translateXML(String full_doc, String output_file) 
+	throws IOException,TransformerException, TransformerConfigurationException, FileNotFoundException
+    {
+	
+ 	StringReader str = new StringReader(full_doc) ;
+	
+	TransformerFactory tFactory = TransformerFactory.newInstance();
+   	Transformer transformer = tFactory.newTransformer(new StreamSource(xsl_file));
+   	transformer.transform(new StreamSource(str), new StreamResult(new FileOutputStream(output_file)));
+	return true;
+    }
+
+    private boolean translateXMLWithMapping(String full_doc, String output_file) 
+	throws IOException,TransformerException, TransformerConfigurationException, FileNotFoundException
+    {	
+	StringReader str = new StringReader(full_doc) ;
+
+	try{
+	    TransformerFactory tFactory = TransformerFactory.newInstance();
+	    Transformer transformer = tFactory.newTransformer(new StreamSource(xsl_file));
+	    
+	    Document mapping_doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(mapping_file);
+	    Element mapping =mapping_doc.getDocumentElement(); 
+	    
+	    transformer.setParameter("mapping",mapping);
+	    
+	    Document output_doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+	    
+	    transformer.transform(new StreamSource(str), new DOMResult(output_doc));
+	    
+	    calculateRecordsLength(output_doc);
+	   
+	    transformer = tFactory.newTransformer();
+	   
+	    transformer.transform(new DOMSource(output_doc), new StreamResult(new FileOutputStream(output_file)));
+
+	}
+	catch(Exception e){
+	    e.printStackTrace();
+	    return false;
+	}
+
+	return true;
+    }
+
+    private void calculateRecordsLength(Document output_doc){
+	NodeList records = output_doc.getDocumentElement().getElementsByTagName(RECORD_ELEMENT);
+
+	for(int i=0;i<records.getLength();i++){
+	    Element record = (Element)records.item(i);
+	    calculateRecordLength(record);
+	}
+    }
+
+    private void calculateRecordLength(Element record){
+	int total_length =0;	
+	NodeList controlfileds = record.getElementsByTagName(CONTROLFIELD_ELEMENT);
+	for(int i=0;i<controlfileds.getLength();i++){
+	    Element controlfiled = (Element)controlfileds.item(i);
+	    total_length +=getElementTextValue(controlfiled).length();
+	}
+	
+	NodeList subfileds = record.getElementsByTagName(SUBFIELD_ELEMENT);
+	for(int i=0;i<subfileds.getLength();i++){
+	    Element subfiled = (Element)subfileds.item(i);
+	    total_length +=getElementTextValue(subfiled).length();
+	}
+
+	String record_length = total_length+"";
+	//fill in a extra digit as record length needs to be five characters long
+	if (total_length < 10000){
+	    record_length = "0"+record_length;
+	    if (total_length < 1000){
+		record_length = "0"+record_length;	
+	    }
+	    if (total_length < 100){
+		record_length = "0"+record_length;	
+	    }
+	    if (total_length < 10){
+		 record_length = "0"+record_length;	
+	    }
+	   
+	}
+
+	NodeList leaders = record.getElementsByTagName(LEADER_ELEMENT);	
+	
+	//only one leader element
+        if (leaders.getLength() >0){
+	    Element leader_element = (Element)leaders.item(0);
+	    removeFirstTextNode(leader_element);
+	    leader_element.insertBefore(leader_element.getOwnerDocument().createTextNode(record_length),leader_element.getFirstChild()); 
+	}
+
+    }
+
+    private void removeFirstTextNode(Element element){
+	//remove the first text node
+	NodeList children_nodelist = element.getChildNodes();
+	for (int i = 0; i < children_nodelist.getLength(); i++) {
+	    Node child_node = children_nodelist.item(i);
+	    if (child_node.getNodeType() == Node.TEXT_NODE) {
+		element.removeChild(child_node);
+		return;
+	    }
+	}
+
+    }
+
+    private String getElementTextValue(Element element)
+    {
+	String text ="";	
+
+	// Find the node child
+	NodeList children_nodelist = element.getChildNodes();
+	for (int i = 0; i < children_nodelist.getLength(); i++) {
+	    Node child_node = children_nodelist.item(i);
+	    if (child_node.getNodeType() == Node.TEXT_NODE) {
+		text +=child_node.getNodeValue();
+	    }
+	}
+
+       	return text;
+    }
+ 
+
+    private void setMappingVariable(Document style_doc){
+	Node child = style_doc.getDocumentElement().getFirstChild();
+	while(child != null) {
+	    String name = child.getNodeName();
+	    if (name.equals("xsl:variable")) {
+		Element variable_element = (Element)child;
+		if ( variable_element.getAttribute("name").trim().equals("mapping")){
+		    variable_element.setAttribute("select","document('"+mapping_file+"')/Mapping");
+		}
+	    }
+	    child = child.getNextSibling();
+	}
+	
+    } 
+
+
+    private void translate(String xml_file, String xsl_file, String output_file)throws IOException,TransformerException, TransformerConfigurationException, FileNotFoundException, IOException{             
+         
+      	TransformerFactory tFactory = TransformerFactory.newInstance();
+   	Transformer transformer = tFactory.newTransformer(new StreamSource(xsl_file));
+           
+	OutputStreamWriter output = null;  
+	if (output_file.equals("")) {
+             output = new OutputStreamWriter(System.out, "UTF-8");
+	}
+	else{
+            output = new OutputStreamWriter(new FileOutputStream(output_file), "UTF-8"); 
+	}
+       
+        transformer.transform(new StreamSource(new File(xml_file)),new StreamResult(output));
+        
+    }
+
+     static public String replaceAll(String source_string, String match_regexp, String replace_string)
+    {
+	return source_string.replaceAll(match_regexp, replace_string);
+    }
+
+
+    public static void main(String[] args) 
+    { 
+
+        String xml_file="";
+        String xsl_file="";
+        String mapping_file="";
+        String output_file="";
+      
+	// Checking Arguments
+	if(args.length < 1)
+	    {
+	       printUsage(); 
+	    }           
+
+	for (int i=0;i<args.length;i++){
+	    if (args[i].equals("-m") && i+1 < args.length && !args[i+1].startsWith("-")){
+		 mapping_file = args[++i];
+         	 checkFile(mapping_file.replaceAll("file:///",""));
+	    }
+	    else if (args[i].equals("-x") && i+1 < args.length && !args[i+1].startsWith("-")){
+               xml_file = args[++i]; 
+               checkFile(xml_file.replaceAll("file:///",""));
+	    }
+	    else if(args[i].equals("-t") && i+1 < args.length && !args[i+1].startsWith("-")){
+                xsl_file = args[++i];
+	         checkFile( xsl_file.replaceAll("file:///","")); 
+	    }
+            else if(args[i].equals("-o") && i+1 < args.length && !args[i+1].startsWith("-")){
+                output_file = args[++i];
+	    
+	    }
+	    else if(args[i].equals("-h")){
+                printUsage();
+	    }
+	    else{
+                printUsage();
+	    }
+		  
+	}
+
+         
+	ApplyXSLT core = null;
+	
+ 	if (xml_file.equals("") && !xsl_file.equals("")){//read from pipe line
+	    if (mapping_file.equals("")){ 
+           	 core = new ApplyXSLT(xsl_file);
+	    }
+	    else{
+                core = new ApplyXSLT(xsl_file,mapping_file);  
+	    }
+            
+	    if (core != null){
+		core.process();
+	    }
+	    else{
+		printUsage(); 
+	    }
+ 	}
+ 	else if(!xml_file.equals("") && !xsl_file.equals("")){
+	    core = new ApplyXSLT();
+            try {
+		core.translate(xml_file,xsl_file,output_file); 
+	    }  
+	    catch(Exception e){e.printStackTrace();}
+ 	} 
+	else{
+	    printUsage();    
+	}
+	
+    }
+
+    private static void checkFile(String filename){
+         File file = new File(filename);
+	 if (!file.exists()){
+	     System.out.println("Error: "+filename+" doesn't exist!");
+	     System.exit(-1);
+	 }
+    }
+  
+    private  static void printUsage(){
+	System.out.println("Usage:ApplyXSLT  -x File -t File [-m File] [-o File]");
+        System.out.println("\t-x spacifies the xml file (Note: optional for piped xml data)");
+        System.out.println("\t-t spacifies the xsl file");
+    	System.out.println("\t-m specifies the mapping file (for MARCXMLPlugout.pm only)");
+        System.out.println("\t-o specifies the output file name (output to screen if this option is absent)");
+        System.exit(-1);
+    }
+}
+
+
Index: /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLTUtil.java
===================================================================
--- /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLTUtil.java	(revision 16561)
+++ /gsdl/trunk/gs2build/src/java/org/nzdl/gsdl/ApplyXSLTUtil.java	(revision 16561)
@@ -0,0 +1,8 @@
+package org.nzdl.gsdl;
+
+public class ApplyXSLTUtil {
+    
+    public static String toLowerCase(String value){
+	return value.toLowerCase();
+    } 
+}
Index: /gsdl/trunk/gs2build/src/phind/generate/.cvsignore
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/.cvsignore	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/.cvsignore	(revision 16561)
@@ -0,0 +1,4 @@
+Makefile
+suffix
+suffix2
+*.o
Index: /gsdl/trunk/gs2build/src/phind/generate/Makefile.in
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/Makefile.in	(revision 16561)
@@ -0,0 +1,82 @@
+###########################################################################
+#
+# Makefile -- src/phind/generate
+#
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = 
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	phrase.cpp \
+	suffix.cpp
+
+OBJECTS = \
+	phrase.o \
+	suffix.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = suffix
+
+
+# Default target: make the suffix executable
+all: $(EXECUTABLE)
+
+suffix: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2build/src/phind/generate/check.h
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/check.h	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/check.h	(revision 16561)
@@ -0,0 +1,130 @@
+// check.h -- manipulate the suffixCheck array
+//
+// The suffixCheck array is used to prevent a phrase being added to
+// the results of getExpansions.  It works by declaring an array that
+// has one bit for each entry in the suffix array, which is cleared at
+// the start of getExpansions.  As getExpansions adds each phrase to
+// the result set, we set the bits in the suffixCheck array that
+// correspond to the positions of of the phrase in the suffixArray.
+
+// (The point is that getExpansions can later check whether a phrase
+// or its prefix has been seen before by looking up the bit
+// corresponding to its first occurence in the suffixCheck array: if
+// the bit is set, a subphrase of this phrase is already in the
+// results already, and the current phrase can be ignored.)
+
+#include "suffix.h"
+
+// The suffixCheck array
+#if defined (__WIN32__)
+typedef unsigned char check;
+#else
+#include <inttypes.h>
+typedef uint8_t check;
+#endif
+
+check *suffixCheck;
+
+// The length of the suffixCheck array
+cellcount checkLength;
+
+// Some useful bitmask constants 
+const check ALLBITS        = 255;
+const check LEFTMOSTBIT    = static_cast<check>(ALLBITS << 7);
+const check LEFTMOSTBITS[] = {static_cast<check>(ALLBITS << 8), 
+			      static_cast<check>(ALLBITS << 7), 
+			      static_cast<check>(ALLBITS << 6), 
+			      static_cast<check>(ALLBITS << 5), 
+			      static_cast<check>(ALLBITS << 4),
+			      static_cast<check>(ALLBITS << 3), 
+			      static_cast<check>(ALLBITS << 2), 
+			      static_cast<check>(ALLBITS << 1),  
+			      static_cast<check>(ALLBITS << 0)};
+
+
+// Allocate the initial memory for suffixCheck
+inline void allocateSuffixCheck(cellcount number_of_symbols) 
+{
+  checkLength = number_of_symbols / (sizeof(check) * 8) + 1;
+  suffixCheck = new check[checkLength];
+  if (suffixCheck == NULL) {
+    cerr << "Suffix error: out of memory allocating suffixCheck array" << endl; 
+    exit(2);
+  }
+  memset(suffixCheck, 0, sizeof(check) * checkLength);
+}
+
+
+// Set all bits in suffixCheck to 0
+inline void clearSuffixCheck() 
+{
+  memset(suffixCheck, 0, sizeof(check) * checkLength);
+}
+
+
+// Get the value of a particular bit in suffixCheck
+inline int getSuffixCheck(cellindex position) 
+{
+  cellindex cell = position >> 3;
+  check remainder = position & 0x07; // the last 3 bits
+  if (suffixCheck[cell] & (LEFTMOSTBIT >> remainder)) {
+    return 1;
+  }
+  return 0;
+}                                                                               
+
+// Set the value of a bit in suffixCheck to 1
+inline void setSuffixCheck(cellindex suff) 
+{
+  cellindex cell = suff >> 3;
+  check remainder = suff & 0x07u; // the last 3 bits
+  suffixCheck[cell] |= (LEFTMOSTBIT >> remainder);
+}  
+
+
+// Set the bits in a cell in the suffixArray.
+// Bits are indexed 0-7.
+inline void setCellBits(cellindex cell_offset, unsigned first_bit, unsigned last_bit) 
+{
+  unsigned number_of_bits = last_bit - first_bit + 1;
+  check mask = (LEFTMOSTBITS[number_of_bits] >> first_bit);
+  suffixCheck[cell_offset] |= mask;
+}
+
+
+inline void setSuffixCheck(cellindex first, cellindex last) 
+{
+  // Find the first and last cells in which bits are set
+  cellindex first_cell = first >> 3;
+  cellindex last_cell = last >> 3;
+
+  // If all the values are in the same cell, set them
+  if (first_cell == last_cell) {
+    setCellBits(first_cell, (first & 0x07u), (last & 0x07u));
+    return;
+  }
+
+  // Set the bits in the first and last cells
+  setCellBits(first_cell, (first & 0x07u), 7);
+  setCellBits(last_cell, 0, (last & 0x07u));
+
+  // Set the bits in the intermediate cells
+  ++first_cell;
+  if(last_cell > first_cell)
+    memset(suffixCheck + first_cell, ALLBITS, 
+	   (last_cell - first_cell) * sizeof(check));
+}
+
+
+// Print the suffixCheck array (for debugging)
+void printSuffixCheck() 
+{
+  for (cellcount i = 0; i < checkLength; ++i) {
+    cout << "cell " << i << "  \t";
+    cout << "(" << (i * 8) << "-" << (i * 8 + 7) << ")    \t";
+    for (cellindex j = 0; j < 8; ++j)
+      cout << getSuffixCheck(i * 8 + j);
+    cout << " (" << (unsigned int) suffixCheck[i] << ")\n";
+  }
+}
+
Index: /gsdl/trunk/gs2build/src/phind/generate/phrase.cpp
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/phrase.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/phrase.cpp	(revision 16561)
@@ -0,0 +1,853 @@
+/**********************************************************************
+ *
+ * phrase.cpp -- implementation of the phrase object used by suffix.cpp
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+#include <assert.h>
+#include <stdio.h>
+
+#if defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#else
+#  include <vector>
+#endif
+
+#if defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#include "suffix.h"
+#include "phrase.h"
+
+
+// Phrase constructor functions
+
+Phrase::Phrase(symbol *words, cellcount size, int direction) {
+  empty();
+  length = size;
+
+  if (direction == SUFFIX) {
+     forward = words;
+     back = forward + size - 1;
+  } else {
+    back = words;
+    forward = back - size + 1;
+  }
+}
+
+
+Phrase::Phrase() {
+  empty();
+}
+
+
+Phrase::Phrase(const Phrase &p) {
+  forward = p.forward;
+  back = p.back;
+  length = p.length;
+
+  suffixFound = p.suffixFound;
+  prefixFound = p.prefixFound;
+
+  firstSuffix = p.firstSuffix;
+  lastSuffix  = p.lastSuffix;
+  firstSuffixIndex = p.firstSuffixIndex;
+  lastSuffixIndex  = p.lastSuffixIndex;
+  suffixFrequency  = p.suffixFrequency;
+
+  firstPrefix = p.firstPrefix;
+  lastPrefix  = p.lastPrefix;
+  firstPrefixIndex = p.firstPrefixIndex;
+  lastPrefixIndex  = p.lastPrefixIndex;
+  prefixFrequency  = p.prefixFrequency;
+
+  uniqueSuffixExtension = p.uniqueSuffixExtension;
+  uniquePrefixExtension = p.uniquePrefixExtension;
+}
+
+
+// Empty the contents of a phrase
+
+int Phrase::empty() {
+
+  forward = back = NULL;
+  length = 0;
+
+  suffixFound = prefixFound = 0;
+  firstSuffix = firstPrefix = NULL;
+  lastSuffix = lastPrefix = NULL;
+
+  firstSuffixIndex = lastSuffixIndex = suffixFrequency = 0;
+  firstPrefixIndex = lastPrefixIndex = prefixFrequency = 0;
+
+  uniqueSuffixExtension = uniquePrefixExtension = -1;
+
+  return 0;
+}
+
+// Remove the details of the location where a suffix/prefix
+// is found in the array.
+
+int Phrase::clearSuffix() {
+  suffixFound = 0;
+  firstSuffix = lastSuffix = NULL;
+  firstSuffixIndex = lastSuffixIndex = suffixFrequency = 0;
+  uniqueSuffixExtension = -1;
+  return 0;
+}
+
+int Phrase::clearPrefix() {
+  prefixFound = 0;
+  firstPrefix = lastPrefix = NULL;
+  firstPrefixIndex = lastPrefixIndex = prefixFrequency = 0;
+  uniquePrefixExtension = -1;
+  return 0;
+}
+
+// Increase the length of s phrase
+//
+// We increase the length of a string "in place".  When we expand
+// the suffix, however, we invalidate the prefix data, and vice-versa.
+
+int Phrase::increaseSuffixLength(cellcount l) {
+  length = l;
+  clearPrefix();
+  back = forward + l - 1;
+  return 0;
+}
+
+int Phrase::increasePrefixLength(cellcount l) {
+  length = l;
+  clearSuffix();
+  forward = back - l + 1;
+  return 0;
+}
+
+// Shorten a phrase by one symbol
+int Phrase::shortenByOneAtSuffix() {
+  --length;
+  --back;
+  if (phraseMode==STOPWORDS) {
+    while (*back >=firstStopSymbol && *back <= lastStopSymbol) {
+      --length;
+      --back;
+    }
+  }  
+  clearSuffix();
+  clearPrefix();
+  return 0;
+}
+
+int Phrase::shortenByOneAtPrefix() {
+  --length;
+  ++forward;
+  if (phraseMode==STOPWORDS) {
+    while (*forward >=firstStopSymbol && *forward <= lastStopSymbol) {
+      --length;
+      ++forward;
+    }
+  }
+  clearSuffix();
+  clearPrefix();
+  return 0;
+}
+
+
+// Output a phrase to a stream
+ostream &operator<<(ostream &stream, const Phrase &phrase)
+{
+  assert(phrase.forward);
+  symbol *s = phrase.forward;
+
+  stream << "s" << *s++;
+  for (cellcount i = 1; i < phrase.length; ++i)
+    stream << " s" << *s++;
+
+  return stream;
+}
+
+
+// Convert the phrase to a string
+// Note thgat you have to delete the memory yourself.
+char *Phrase::toString() 
+{
+  assert(forward);
+
+  char *str;
+  str = new char[length*20];
+  symbol *s = forward;
+  sprintf(str, "s%d", *s++);
+
+  for (cellcount i = 1; i < length; ++i) {
+    sprintf(str, "%s s%d", str, *s++);
+  }
+
+  return str;
+}
+
+
+// Compare a phrase to an another array of symbols
+//
+// Given an array of words with a specified length, 
+// compare those words to this phrase.
+//
+// Return 0 if the two phrases are the same over length length
+// Return 1 if the phrase is "greater" than tyhe words, or
+// Return -1 if the phrase is less.
+
+int Phrase::compareSuffix(symbol *words, cellcount length) {
+  assert(forward);
+  symbol *p = forward;
+  
+  for (cellcount i = 0; i < length; ++i) {
+    if (*p > *words) {
+      return 1;
+    } else if (*p < *words) {
+      return -1;
+    } else {
+      *p++;
+      *words++;
+    }
+  }
+
+  return 0;
+
+}
+
+int Phrase::comparePrefix(symbol *words, cellcount length) {
+  
+  assert(back);
+  symbol *p = back;
+  
+  for (cellcount i = 0; i < length; ++i) {
+    if (*p > *words) {
+      return -1;
+    } else if (*p < *words) {
+      return 1;
+    } else {
+      *p--;
+      *words--;
+    }
+  }
+
+  return 0;
+
+}
+
+
+// Does the phrase have a unique suffix/prefix extension?
+//
+// For suffix:
+//   If uniqueSuffixExtensions is 1, then phrase has some unique expansion. 
+//   If it is 0 then it does not.  If it is -1 then we don't know, and have
+//   to calculate it from the suffix array.  Same goes for prefixes.
+
+int Phrase::hasUniqueSuffixExtension() {
+
+  // if we don't know, then work it out
+  if (uniqueSuffixExtension == -1) {
+
+    ensureSuffixFound();
+
+    // pointers to the phrase's first and last occurances in te suffixArray
+    symbol *fst = firstSuffix + length;
+    symbol *lst = lastSuffix + length;
+
+    // in ANYPHRASE mode, simply check the next symbol
+    if (phraseMode == ANYPHRASE) {
+      if ((*fst == *lst) && (*fst > LASTDELIMITER)) {
+	uniqueSuffixExtension = 1;
+      } else {
+	uniqueSuffixExtension = 0;
+      }
+    }
+    // in STOPWORDS mode, make sure there is a unique next content symbol
+    else {
+      uniqueSuffixExtension = 0;
+      while ((*fst == *lst) && (*fst > LASTDELIMITER)) {
+	if (*fst >= firstContentSymbol) {
+	  uniqueSuffixExtension = 1;
+	  break;
+	}
+	++fst;
+	++lst;
+      }
+    }
+  }
+
+  return uniqueSuffixExtension;
+}
+
+int Phrase::hasUniquePrefixExtension() {
+
+  // if we don't know, then work it out
+  if (uniquePrefixExtension == -1) {
+
+    ensurePrefixFound();
+
+    // pointers to the phrase's first and last occurances in the prefixArray
+    symbol *fst = firstPrefix - length;
+    symbol *lst = lastPrefix - length;
+
+    // in ANYPHRASE mode, simply check the next symbol
+    if (phraseMode == ANYPHRASE) {
+      if ((*fst == *lst) && (*fst > LASTDELIMITER)) {
+	uniquePrefixExtension = 1;
+      } else {
+	uniquePrefixExtension = 0;
+      }
+    }
+    // in STOPWORDS mode, make sure there is a unique next content symbol
+    else {
+      uniquePrefixExtension = 0;
+      while ((*fst == *lst) && (*fst > LASTDELIMITER)) {
+	if (*fst >= firstContentSymbol) {
+	  uniquePrefixExtension = 1;
+	  break;
+	}
+	--fst;
+	--lst;
+      }
+    }
+  }
+
+  return uniquePrefixExtension;
+}
+
+
+// Expand a phrase with a unique suffix/prefix by 1 symbol
+//
+// Note that in STOPWORDS mode a "unique extension" means a unique
+// extension that ends on a content symbol.  Although we may extend 
+// such a phrase by 2 or more symbols, we will only extend it by a
+// single content symbol.
+
+int Phrase::expandUniqueSuffixExtensionByOne() {
+  assert(suffixFound);
+  assert(uniqueSuffixExtension == 1);
+
+  // in ANYPHRASE mode, expand the phrase by one symbol
+  if (phraseMode == ANYPHRASE) {
+    increaseSuffixLength(length + 1);
+  }
+  // in STOPWORDS mode, expand the phrase up to the next content symbol
+  else {
+    symbol *next = firstSuffix + length;
+    cellcount newlength = length + 1;
+
+    while (*next < firstContentSymbol) {
+      ++next;
+      ++newlength;
+    }
+    increaseSuffixLength(newlength);
+  }
+
+  uniqueSuffixExtension = -1;
+  return 0;
+}
+
+int Phrase::expandUniquePrefixExtensionByOne() {
+  assert(prefixFound);
+  assert(uniquePrefixExtension == 1);
+
+  // in ANYPHRASE mode, expand the phrase by one symbol
+  if (phraseMode == ANYPHRASE) {
+    increasePrefixLength(length + 1);
+  }
+  // in STOPWORDS mode, expand the phrase to the next content symbol
+  else {
+    symbol *next = firstPrefix - length;
+    cellcount newlength = length + 1;
+    while (*next < firstContentSymbol) {
+      --next;
+      ++newlength;
+    }
+    increasePrefixLength(newlength);
+
+  }
+
+  uniquePrefixExtension = -1;
+  return 0;
+}
+
+
+// Find the phrase in a suffixArray
+//
+// Given a suffix array, find the first and last occurances of the phrase.
+// The begin and end parameters identify which cells to search (inclusive). 
+
+int Phrase::findFirstAndLastSuffix() {
+  
+  return findFirstAndLastSuffix(0, inputLength-1);
+}
+
+int Phrase::findFirstAndLastSuffix(cellindex begin, cellindex end) {
+
+  // First find *any* occurance of the phrase
+  assert(begin <= end);
+
+  // if we're only searching one cell, it is very easy
+  if (begin == end) {
+    assert(compareSuffix(suffixArray[begin], length) == 0);
+    firstSuffix = lastSuffix = suffixArray[begin];
+    firstSuffixIndex = lastSuffixIndex = begin;
+    suffixFrequency = 1;
+    suffixFound = 1;
+    return 0;
+  }
+
+  cellindex c;
+  int cmp;
+
+  do {
+    c = (end + begin) / 2;
+    cmp = compareSuffix(suffixArray[c], length);
+    if (cmp == 1) {
+      // target phrase is lower than phrase at suffixArray[c]
+      begin = c + 1;
+    } else if (cmp == -1) {
+      // target phrase is higher than phrase at suffixArray[c]
+      end = c - 1;
+    }
+  } while (cmp != 0);
+    
+  cellindex lastbegin = c;
+  cellindex lastend = end;
+
+  // Next find the first occurance of the phrase.
+  // We know that the first occurance must be between the 
+  // current value of begin and the current value of c.
+  end = c;
+
+  do {
+    if (begin == end) {
+      c = begin;
+      cmp = 0;
+    } else {
+      c = (begin + end) / 2;
+      cmp = compareSuffix(suffixArray[c], length);
+      if (cmp == 1) {
+	// target phrase is lower than phrase at suffixArray[c]
+	begin = c + 1;
+      } else {
+	assert(cmp == 0);
+	// target phrase is the same as the phrase at suffixArray[c].  However,
+	// to find the first occurance, suffixArray[c] must be the same as the 
+	// phrase, but suffixArray[c-1] must be different.
+	if (c>0) {
+	  cmp = compareSuffix(suffixArray[c-1], length);
+	  if (cmp == 0) {
+	    end = c - 1;
+	    assert(end >= begin);
+	    cmp = 1;
+	  } else {
+	    cmp = 0;
+	  }
+	}
+      }
+    }
+  } while (cmp != 0);
+
+  // we have found the first location
+  firstSuffixIndex = c;
+  firstSuffix = suffixArray[c];
+  
+  // Next find the last occurance of the phrase.
+  // We previously stored some bounds for its loccation.
+  begin = lastbegin;
+  end = lastend;
+  
+  do {
+    if (begin == end) {
+      c = begin;
+      cmp = 0;
+    } else {
+      c = (begin + end) / 2;
+      cmp = compareSuffix(suffixArray[c], length);
+      if (cmp == -1) {
+	// target phrase is greater than phrase at suffixArray[c]
+	end = c - 1;
+      } else {
+	assert(cmp == 0);
+	// target phrase is the same as the phrase at suffixArray[c].  However,
+	// to find the last occurance, one of two additional condiditons must be met:
+	// either c must be the very last cell in the array, or
+	// suffixArray[c+1] must be different from phrase
+	if (c < inputLength-1) {
+	  cmp = compareSuffix(suffixArray[c+1], length);
+	  if (cmp == 0) {
+	    begin = c + 1;
+	    cmp = -1;
+	  } else {
+	    cmp = 0;
+	  }
+	} 
+      }
+    }
+  } while (cmp != 0);
+  
+  lastSuffixIndex = c;
+  lastSuffix = suffixArray[c];
+  suffixFrequency = lastSuffixIndex - firstSuffixIndex + 1;
+  suffixFound = 1;
+
+  return 0;
+}
+
+
+// Find the phrase in a prefix array
+//
+// Given a prefix array, find the first and last occurances of the phrase.
+// The begin and end parameters identify which cells to search (inclusive). 
+
+int Phrase::findFirstAndLastPrefix() {
+  return findFirstAndLastPrefix(0, inputLength-1);
+}
+
+int Phrase::findFirstAndLastPrefix(cellindex begin, cellindex end) {
+
+  // First find *any* occurance of the phrase
+  assert(begin <= end);
+
+  // if we're only searching one cell, it is very easy
+  if (begin == end) {
+    assert(comparePrefix(prefixArray[begin], length) == 0);
+    firstPrefix = lastPrefix = prefixArray[begin];
+    firstPrefixIndex = lastPrefixIndex = begin;
+    prefixFrequency = 1;
+    prefixFound = 1;
+    return 0;
+  }
+
+  cellindex c;
+  int cmp;
+
+  do {
+    c = (end + begin) / 2;
+    cmp = comparePrefix(prefixArray[c], length);
+    if (cmp == 1) {
+      begin = c + 1;
+    } else if (cmp == -1) {
+      end = c - 1;
+    }
+  } while (cmp != 0);
+    
+  cellindex lastbegin = c;
+  cellindex lastend = end;
+
+  // Next find the first occurance of the phrase.
+  // We know that the first occurance must be between the 
+  // current value of begin and the cureent value of c.
+  end = c;
+
+  do {
+    if (begin == end) {
+      c = begin;
+      cmp = 0;
+    } else {
+      c = (begin + end) / 2;
+      cmp = comparePrefix(prefixArray[c], length);
+      if (cmp == 1) {
+	// target phrase is lower than phrase at prefixArray[c]
+	begin = c + 1;
+      } else {
+	assert(cmp == 0);
+	// target phrase is the same as the phrase at prefixArray[c].  However, to
+	// find the first occurance, one of two additional condiditons must be met:
+	// either  c == the first cell in prefix array
+	// or      prefixArray[c-1] != phrase.
+	if (c > 0) {
+	  cmp = comparePrefix(prefixArray[c-1], length);
+	  if (cmp == 0) {
+	    end = c - 1;
+	    assert(end >= begin);
+	    cmp = 1;
+	  } else {
+	    cmp = 0;
+	  }
+	}
+      }
+    }
+  } while (cmp != 0);
+
+  // we have found the first location
+  firstPrefixIndex = c;
+  firstPrefix = prefixArray[c];
+
+  // Next find the last occurance of the phrase.
+  // We previously stored some bounds for its loccation.
+  begin = lastbegin;
+  end = lastend;
+  
+  do {
+    if (begin == end) {
+      c = begin;
+      cmp = 0;
+    } else {
+      c = (begin + end) / 2;
+      cmp = comparePrefix(prefixArray[c], length);
+      if (cmp == -1) {
+	// target phrase is greater than phrase at prefixArray[c]
+	end = c - 1;
+      } else {
+	assert(cmp == 0);
+	// target phrase is the same as the phrase at prefixArray[c].  However,
+	// to find the last occurance, prefixArray[c] must be the same as the 
+	// phrase, but prefixArray[c+1] must be different.
+	cmp = comparePrefix(prefixArray[c+1], length);
+	if (cmp == 0) {
+	  begin = c + 1;
+	  cmp = -1;
+	} else {
+	  cmp = 0;
+	}
+      } 
+    }
+  } while (cmp != 0);
+  
+  lastPrefixIndex = c;
+  lastPrefix = prefixArray[c];
+  prefixFrequency = lastPrefixIndex - firstPrefixIndex + 1;
+  prefixFound = 1;
+
+  return 0;
+}
+
+
+// Calculate a set of initial suffix/prefix candidates
+//
+// Expand the phrase to find the initial 
+// set of candidates that may be extensions of that phrase,
+// and add them to the end of the results vector
+
+void Phrase::initialSuffixCandidates(vector<Phrase> &results) {
+
+  ensureSuffixFound();
+  Phrase next;
+  cellindex i = firstSuffixIndex;
+
+  // Find all the expansions of Phrase p
+  while (i <= lastSuffixIndex) {
+
+    // create new phrase exactly one longer than the current one
+    next = newPhraseShortestSuffixExpansion(i);
+
+    // If the expansion occurs more than once and is not delimited, expand it
+    if ((*(next.back) > LASTDELIMITER) && (next.suffixFrequency >= minOccurs)) {
+      next.expandWhileUniqueSuffixExtension();
+      results.push_back(next);
+    }
+    
+    // Move onto the next expansion
+    i = next.lastSuffixIndex + 1;
+  }
+}
+
+
+void Phrase::initialPrefixCandidates(vector<Phrase> &results) {
+
+  ensurePrefixFound();
+  Phrase next;
+  cellindex i = firstPrefixIndex;
+
+  // Find all the expansions of Phrase p
+  while (i <= lastPrefixIndex) {
+
+    // create new phrase exactly one longer than the current one
+    next = newPhraseShortestPrefixExpansion(i);
+
+    // If the expansion occurs more than once and is not delimited, expand it
+    if ((*(next.forward) > LASTDELIMITER) && (next.prefixFrequency >= minOccurs)) {
+      next.expandWhileUniquePrefixExtension();
+      results.push_back(next);
+    }
+    
+    // Move onto the next expansion
+    i = next.lastPrefixIndex + 1;
+  }
+}
+
+
+// Create a new phrase that is longer then the current one, but as short
+// as possible.  In ANYPHRASE mode this means the new phrase is exactly
+// one symbol longer than the current one, but in STOPWORD mode the expansion
+// must introduce a new content symbol
+//
+// Note that we never expand across delimiters; if we encounter a delimiter
+// then this becomes the "end" symbol of the phrase.  The calling function 
+// should check for this condition.
+
+Phrase Phrase::newPhraseShortestSuffixExpansion(cellindex i) {
+  
+  // create a new phrase exactly one symbol longer than the current one
+  Phrase p(suffixArray[i], length + 1, SUFFIX);
+
+  if (phraseMode == STOPWORDS) {
+    // in STOPWORDS mode we must introduce a new content symbol
+    while ((*(p.back) >= firstStopSymbol) &&
+	   (*(p.back) <= lastStopSymbol)) {
+      p.increaseSuffixLength(p.length + 1);
+    }
+  }
+
+  p.findFirstAndLastSuffix(i, lastSuffixIndex);
+  return p;
+}
+
+Phrase Phrase::newPhraseShortestPrefixExpansion(cellindex i) {
+  
+  // create a new phrase exactly one symbol longer than the current one
+  Phrase p(prefixArray[i], length + 1, PREFIX);
+
+  if (phraseMode == STOPWORDS) {
+    // in STOPWORDS mode we must introduce a new content symbol
+    while ((*(p.forward) >= firstStopSymbol) &&
+	   (*(p.forward) <= lastStopSymbol)) {
+      p.increasePrefixLength(p.length + 1);
+    }
+  }
+
+  p.findFirstAndLastPrefix(i, lastPrefixIndex);
+  return p;
+}
+
+
+// Expand a phrase until it no longer has a unique suffix extension
+// 
+// If the phrase only occurs once in the suffix array, do nothing.
+//
+// If we are in stopwords mode then we expand if there is a unique 
+// suffix expansion and this expansion is a content word.  We do this
+// with the content_len variable, which holds the length of the longest
+// phrase ending in a content word. 
+
+int Phrase::expandWhileUniqueSuffixExtension() {
+  ensureSuffixFound();
+
+  // if the phrase occurs only once, do nothing
+  if (suffixFrequency < minOccurs)
+    return 0;
+
+  // if we already know there's no unique suffix extension, do nothing
+  if (uniqueSuffixExtension == 0)
+    return 0;
+
+  // count the length over which the cells are the same
+  // and no delimiter is crossed.
+  symbol *fst = firstSuffix;
+  symbol *lst = lastSuffix;
+  cellindex len = 0;
+  cellindex content_len = 0;
+
+  // Calculate the length over which the phrases match
+  while((*fst == *lst) && (*fst > LASTDELIMITER)) {
+    ++len;
+    if (*fst > lastStopSymbol) content_len = len;
+    ++fst;
+    ++lst;
+  }
+
+  // in ANYPHRASE mode, expand the phrase for as long as possible
+  if (phraseMode == ANYPHRASE) {
+    if (len > length) {
+      increaseSuffixLength(len);
+    }
+  } 
+  // in STOPWORDS mode, expand only as far as the last contentword
+  else {
+    if (content_len > length) {
+      increaseSuffixLength(content_len);
+    }
+  }
+
+  uniqueSuffixExtension = 0;
+  return 0;
+}
+
+int Phrase::expandWhileUniquePrefixExtension() {
+  ensurePrefixFound();
+
+  // if the phrase occurs only once, do nothing
+  if (prefixFrequency < minOccurs)
+    return 0;
+
+  // if we already know there's no unique extension, do nothing
+  if (uniquePrefixExtension == 0)
+    return 0;
+
+  // count the length over which the cells are the same
+  // and no delimiter is crossed.
+  symbol *fst = firstPrefix;
+  symbol *lst = lastPrefix;
+  cellindex len = 0;
+  cellindex content_len = 0;
+
+  // Calculate the length over which the phrases match
+  while((*fst == *lst) && (*fst > LASTDELIMITER)) {
+    ++len;
+    if (*fst > lastStopSymbol) content_len = len;
+    --fst;
+    --lst;
+  }
+
+  // in ANYPHRASE mode, expand the phrase for as long as possible
+  if (phraseMode == ANYPHRASE) {
+    if (len > length) {
+      increasePrefixLength(len);
+    }
+  }
+  // in STOPWORDS mode, expand only as far as the last contentword
+  else {
+    if (content_len > length) {
+      increasePrefixLength(content_len);
+    }
+  }
+
+  uniquePrefixExtension = 0;
+  return 0;
+}
+
+// Compare the length of two phrases
+//
+// Given two phrases, return true if the first is shorter/longer,
+// otherwise return false.  For use in various sort functions.
+
+bool isShorter(Phrase p1, Phrase p2) {
+  if (p1.length < p2.length) {
+    return true;
+  }
+  return false;
+}
+
+bool isLonger(Phrase p1, Phrase p2) {
+  if (p1.length > p2.length) {
+    return true;
+  }
+  return false;
+}
+
+
Index: /gsdl/trunk/gs2build/src/phind/generate/phrase.h
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/phrase.h	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/phrase.h	(revision 16561)
@@ -0,0 +1,162 @@
+/**********************************************************************
+ *
+ * phrase.h -- definition of the phrase object used by suffix.cpp
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+#ifndef PHRASE_H
+#define PHRASE_H
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+#if defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#else
+#  include <vector>
+#endif
+
+#include "suffix.h"
+
+class Phrase {
+
+public:
+
+  // The phrase itself is stored with two pointers: forward points to
+  // its first cell, back points to its last.  The length is always
+  // stored in length.  If one of these is set, all must be set, and
+  // it must be true that (forward + length - 1) = back.
+  symbol *forward;
+  symbol *back;
+  cellcount length;
+  
+  // Phrase location in the suffix array
+  int suffixFound;
+  symbol *firstSuffix;
+  symbol *lastSuffix;
+  cellindex firstSuffixIndex;
+  cellindex lastSuffixIndex;
+  cellcount suffixFrequency;
+
+  // Phrase location in the prefix array
+  int prefixFound;
+  symbol* firstPrefix;
+  symbol* lastPrefix;
+  cellindex firstPrefixIndex;
+  cellindex lastPrefixIndex;
+  cellcount prefixFrequency;
+
+  // Constructor functions
+  Phrase();
+  Phrase(const Phrase &p);
+
+  // A "partial" constructor: the first argument is an array of words,
+  // second is its length, third is the direction (SUFFIX or PREFIX)
+  // in which the words should be read (defaults to SUFFIX).
+  Phrase(symbol *words, cellcount size, int direction);
+
+  // Represent the phrase as an arracy of characters
+  // You will have to call "delete []" on the array returned. 
+  char *toString();
+
+  // Find an initial set of candidate phrases in the suffix/prefix array
+  void initialSuffixCandidates(vector<Phrase> &results);
+  void initialPrefixCandidates(vector<Phrase> &results);
+  
+  // Does the phrase have a unique extension?
+  int hasUniqueSuffixExtension();
+  int hasUniquePrefixExtension();
+  
+  // Extend a phrase by exactly one symbol
+  int expandUniquePrefixExtensionByOne();
+  int expandUniqueSuffixExtensionByOne();
+
+  // Extend a phrase until it no longer has a unique extanesion
+  int expandWhileUniqueSuffixExtension();
+  int expandWhileUniquePrefixExtension();
+
+  // Shorten a phrase by one symbol
+  int shortenByOneAtSuffix();
+  int shortenByOneAtPrefix();
+
+  // Find the phrase in the suffix/prefix array
+  int findFirstAndLastSuffix();
+  int findFirstAndLastPrefix();
+  int findFirstAndLastSuffix(cellindex begin, cellindex end);
+  int findFirstAndLastPrefix(cellindex begin, cellindex end);
+
+  // Make sure the phrase location in the suffix/prefix array is known
+  inline void ensureSuffixFound() {
+    if (!suffixFound)
+      findFirstAndLastSuffix();
+  }
+  inline void ensurePrefixFound() {
+    if (!prefixFound)
+      findFirstAndLastPrefix();
+  }
+
+  // Output a phrase to a stream
+  friend ostream &operator<<(ostream &stream, const Phrase &phrase);
+
+  int uniqueSuffixExtension;
+  int uniquePrefixExtension;
+
+private:
+
+  // Does the phrase have a unique suffix/prefix extension?
+  // if yes, then 1; if no then 0; if unknown then -1;
+
+  // reset a phrase
+  int empty();
+
+  // reset phrase information relating to location in suffix/prefix array
+  int clearSuffix();
+  int clearPrefix();
+
+  // increase the length of a phrase "in place"
+  int increaseSuffixLength(cellcount l);
+  int increasePrefixLength(cellcount l);
+    
+  // Compare the phrase to a given array of symbols
+  int compareSuffix(symbol *words, cellcount length);
+  int comparePrefix(symbol *words, cellcount length);
+
+  // Create a new phrase that is longer than this one, yet as short as possible.
+  Phrase newPhraseShortestSuffixExpansion(cellindex i);
+  Phrase newPhraseShortestPrefixExpansion(cellindex i);
+
+};
+
+
+bool isShorter(Phrase p1, Phrase p2);
+bool isLonger(Phrase p1, Phrase p2);
+
+#endif
+
Index: /gsdl/trunk/gs2build/src/phind/generate/suffix.cpp
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/suffix.cpp	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/suffix.cpp	(revision 16561)
@@ -0,0 +1,1094 @@
+/**********************************************************************
+ *
+ * Suffix.cpp -- Extract the repeated phrases in the input with suffix
+ *               and prefix arrays (cgn & gwp's simpler algorithm,
+ *               and kjm's improvements).
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(GSDL_USE_IOS_H)
+#  include <fstream.h>
+#  include <iostream.h>
+#else
+#  include <fstream>
+#  include <iostream>
+#endif
+
+#if defined(GSDL_USE_STL_H)
+#  if defined(GSDL_USE_ALGO_H)
+#    include <algo.h>
+#  else
+#    include <algorithm.h>
+#  endif
+#  include <vector.h>
+#else
+#  include <algorithm>
+#  include <vector>
+#endif
+
+#include "suffix.h"
+#include "phrase.h"
+#include "check.h"
+
+// Global variables declared in suffix.h
+cellcount inputLength;
+
+symbol   *symbols;
+symbol  **suffixArray;
+symbol  **prefixArray;
+
+// How many documents are in this collection?
+cellcount numberOfDocuments;
+symbol  **documentArray;
+
+
+// Do we accept any phrase, or do we eliminate those ending with stopwords ?
+int phraseMode = ANYPHRASE; //STOPWORDS;
+// What is the minimum phrase frequency for a phrase to be included in the hierarchy
+int minOccurs = 2;
+
+// The filestem of the collection's phindex directory
+char collection[FILENAME_MAX];
+
+
+// The ranges of the stopword and content-word symbols for the collection
+symbol firstStopSymbol = 0;
+symbol lastStopSymbol = 0;
+symbol firstContentSymbol = 0;
+symbol lastContentSymbol = 0;
+
+
+// Some useful comparison functions, defined below.
+int suffixCompare(const void *, const void *);
+int prefixCompare(const void *, const void *);
+int pointerCompare(const void *, const void *);
+
+                                           
+// Functions for implementing "phrase memory".  These let us "remember"
+// each phrase that we've expanded without using too much memory.
+void initialisePhraseMemory();
+void rememberThisPhrase(cellindex index, cellcount length);
+bool isPhraseStored(cellindex index, cellcount length);
+void deletePhraseMemory();
+
+
+// how much output do we want?
+int verbosity = 1;
+
+
+// Get a phrase's expansions
+//
+// Get the set of "minimal", "maximal", non-unique expansions of a
+// phrase p, using the simpler algorithm that Craig and Gordon came up
+// with at Google.
+//
+// Returns a vector of Expansions.
+
+void getExpansions(Phrase &p, vector<Phrase> &results) {
+
+  // 1. Get the initial candidates
+  vector<Phrase> candidates;
+  p.initialSuffixCandidates(candidates);
+  int suffcands = candidates.size();
+  p.initialPrefixCandidates(candidates);
+
+  if (candidates.size() == 0)
+    return;
+
+  vector<Phrase>::iterator i;
+  for (i = candidates.begin(); i != candidates.end(), suffcands>0; ++i, --suffcands) {
+    i->expandWhileUniquePrefixExtension();
+    i->ensureSuffixFound();
+  }
+  for (i; i != candidates.end(); ++i) {
+    i->expandWhileUniqueSuffixExtension();
+  }
+
+  // 3. Ensure minimality: ignore phrases whose subphrases are also found
+  results.clear();
+
+  // Initialise the candidates, check array, and various variables.
+  sort(candidates.begin(), candidates.end(), isShorter);
+  unsigned minimum_length = candidates.begin()->length;
+  clearSuffixCheck();
+  
+  // Try to add each candidate to the results set, ignoring the non-minimal
+  for (vector<Phrase>::iterator candidate = candidates.begin(); 
+       candidate != candidates.end(); ++candidate) {
+
+    // Make a copy of candidate to mutilate while performing sub-phrase checks
+    Phrase temp_phrase(*candidate);
+    bool shorter_found = false;
+    
+    // Check for shorter and shorter versions of the temporary phrase
+    while (temp_phrase.length >= minimum_length && !shorter_found) {
+      temp_phrase.ensureSuffixFound();
+      if (getSuffixCheck(temp_phrase.firstSuffixIndex)==0)
+	temp_phrase.shortenByOneAtPrefix();
+      else
+	shorter_found = true;
+
+      // Possible efficiency here: we can finish if the prefix of c
+      // and temp_phrase are the same for candidate->length symbols.
+    }
+      
+    // If no shorter phrase is found, use this one
+    if (!shorter_found) {
+      results.push_back(*candidate);
+      candidate->ensureSuffixFound();
+      setSuffixCheck(candidate->firstSuffixIndex, candidate->lastSuffixIndex);
+    }
+  }
+}
+
+
+// suffixCompare
+//
+// Compare two pointers into a suffix array.  We use this in the 
+// qsort function, so the input are pointers to pointers.  
+//
+// Return -1 if (a < b), otherwise (a > b) so return +1, 
+
+int suffixCompare(const void *cpa, const void *cpb) {
+
+  // Cast then dereference pointers to suffix array elements
+  symbol *pa = (symbol *) cpa;
+  symbol *pb = (symbol *) cpb;
+  pa = (symbol *) *pa;
+  pb = (symbol *) *pb;
+
+  // If the two elements are the same, examine the next one
+  while (*pa == *pb) {
+    *pa++;
+    *pb++;
+  }
+
+  // Make the copmparison and return
+  if ( *pa < *pb) {
+    return -1;
+  } else {
+    return +1;
+  }
+}
+
+
+// prefixCompare
+//
+// Compare two pointers into a prefix array.  We use this in the 
+// qsort function, so the input are pointers to pointers.  
+//
+// Return -1 if (a > b), otherwise (a < b) so return +1, 
+
+int prefixCompare(const void *cpa, const void *cpb) {
+
+  // Cast then dereference pointers to prefix array elements
+  symbol *pa = (symbol *) cpa;
+  symbol *pb = (symbol *) cpb;
+  pa = (symbol *) *pa;
+  pb = (symbol *) *pb;
+
+  // If the two elements are the same, examine the next one
+  while (*pa == *pb) {
+    *pa--;
+    *pb--;
+  }
+
+  // Make the copmparison and return
+  if ( *pa > *pb) {
+    return -1;
+  } else {
+    return +1;
+  }
+}
+
+// simpleCompare
+//
+// Compare two pointers based on the memory location they point to.
+
+int pointerCompare( const void *pa, const void *pb ) {
+
+  symbol **a = (symbol **) pa;
+  symbol **b = (symbol **) pb;
+
+  if (*a < *b) {
+    return  -1;
+  } else if (*a > *b) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+
+// Read the clauses.numbers file into the "symbols" array.
+//
+// Each number in the file is a symbol number; it is essential that 
+// the first symbol (and no others) be COLLECTIONSTART and the last
+// symbol (and no others) be COLLECTIONEND.
+//
+// Return the number of numbers in the array.
+
+int readNumbers() {
+
+  char filename[FILENAME_MAX];
+  sprintf(filename, "%s/clauses.numbers", collection);
+  if (verbosity) {
+    cout << "Reading numbers file: " << filename << endl;
+  }
+
+  // Open the numbers file
+  ifstream inFile1(filename, ios::in);
+  if (!inFile1) {
+    cerr << "File " << filename << " could not be opened\n";
+    exit(1);
+  }
+
+  // Count the number of symbols
+  inputLength = 0;
+  symbol word;
+  while (inFile1 >> word) {
+    ++inputLength;
+  }
+  inFile1.close();
+
+  // Allocate the symbbols array
+  if (verbosity > 1) {
+    cout << "Allocating symbol arrays for " << inputLength << " symbols" << endl;
+  }
+  symbols = new symbol[inputLength];
+  if (symbols == NULL) {
+    cerr << "Suffix error: not enough memory to hold " << inputLength
+	 << " symbols." << endl;
+    exit(2);
+  }
+
+  // Read the numbers file into the numbers array
+  if (verbosity > 2) {
+    cout << "Reading the numbers" << endl;
+  }
+
+  ifstream inFile2(filename, ios::in);
+  if (!inFile2) {
+    cerr << "File " << filename << " could not be opened\n";
+    exit(1);
+  }
+  cellcount next = 0;
+  numberOfDocuments = 0;
+  while (inFile2 >> word) {
+    symbols[next++] = word;
+    if (word == DOCUMENTSTART) {
+      ++numberOfDocuments;
+    }
+  }
+  inFile2.close();
+  
+  // Make sure the numbers file is intact
+  assert(symbols[0] == COLLECTIONSTART);
+  assert(symbols[next-1] == COLLECTIONEND);
+
+  return inputLength;
+}
+
+
+
+// Get Document Occurrance statistics
+//
+// Given a phrase, what documents does it occur in?
+
+cellcount getDocumentOccurrances(const Phrase &p, cellcount *frequency) {
+
+  // cout << "searching for \""<< p << "\" in documents " 
+  //      << 0 << "-" << numberOfDocuments - 1 << endl;
+
+  // The number of documents in which this phrase occurs
+  cellcount df = 0;
+
+  // Initialise the document frequency array
+  //  for (cellindex i = 0; i < numberOfDocuments; ++i) {
+  // frequency[i] = 0;
+  //}
+  memset(frequency, 0, sizeof(cellcount)*numberOfDocuments);
+
+  // variables used to facilitate the search
+  cellindex begin;
+  cellindex end;
+  cellindex d;
+  symbol *target;
+  bool found;
+
+  // search for the document in which each occurence of the phrase is found
+  for (cellcount j = p.firstSuffixIndex; j <= p.lastSuffixIndex; ++j) {
+    
+    // cout << "looking for phrase at suffixArray[" << j << "]\n";
+    
+    target = suffixArray[j];
+    begin = 0;
+    end = numberOfDocuments - 1;
+    found = false;
+
+    // Search for the occurence of a document delimiter that target
+    // occurs immediately after.  
+    // We do this by performing a binary chop search on documentArray.
+    while (!found) {
+
+      // cout << "searching for " << (cellindex) target << " in "
+      //      << begin << " - " << end << endl;
+
+      assert (begin <= end);
+
+      // If the beginning and end of the interval are the same,
+      // then we've found the correct document
+      if (begin == end) {
+	if (frequency[begin] == 0) {
+	  ++df;
+	}
+	++frequency[begin];
+	found = true;
+      }
+
+      // Otherwise, examine a new document midway through the begin-end
+      // interval and see if it is the one.
+      else {
+	d = (begin + end) / 2;
+	if (target > documentArray[d]) {
+	  // If target addrss is greater than this, but thisi sthe last document,
+	  // then this must be the one we want.  Or, if target is greater than
+	  // this one but less then the next, this must be the one we wnat.
+	  if ((d == numberOfDocuments - 1) || (target < documentArray[d+1])) {
+	    if (frequency[d] == 0) {
+	      ++df;
+	    }
+	    ++frequency[d];
+	    found = true;
+	  } else {	
+	    // otherwise we know to search later in the document set
+	    begin = d + 1;
+	  }
+	} else {
+	  // search earlier in the document set
+	  end = d - 1;
+	}
+      }
+    }
+  }
+  return df;
+}
+
+
+
+
+
+
+// phraseExpansionMemory : Which phrases have we expanded?
+//
+// A set of utilities for keeping track of which phrases we have expanded.
+// We don't want to expand a phrase more than once, after all.
+//
+// This REALLY ought to be in its own class, but it works so that's okay.
+//
+// Phrases are identified by their firstSuffixPosition and length.
+//
+// Functions provided are:
+//       void initialisePhraseMemory()
+//       void rememberThisPhrase(index, length)
+//       bool isPhraseStored(index, length)
+//       void deletePhraseMemory()
+//
+// Internally, we will have two separate cases:
+//
+// Phrases of length 1-8: 
+//       unsigned char phraseMemory[inputLength]
+// is an array where each cell "remembers" the corresponding index in the 
+// suffixArray, and each of the 8 bits of the cell correspond to the phrases 
+// of length 1, 2... 8.  
+// Eventually, we will make this disk-based (i.e. store the array in a file).
+//
+// Phrases of length 9+:
+//       file hashTableFile
+//       file listOfEntries
+// The first file is a hash table; each phrase maps to one of its cells, which
+// contains either 0 (empty, no occurence) or a number which is an entry number 
+// in the second file.  This file contains a "list" of entries.  Each consists of 
+// three numbers: the suffixArray index of the phrase, the length of the phrase,
+// and the entry number of the next phrase with the same hash.
+//
+
+
+unsigned char *phraseMemory;
+
+void initialiseLongPhraseMemory();
+void rememberThisLongPhrase(cellindex index, cellcount length);
+bool isLongPhraseStored(cellindex index, cellcount length);
+void deleteLongPhraseMemory();
+
+
+void initialisePhraseMemory() {
+
+  phraseMemory = new unsigned char[inputLength];
+
+  // to begin with, everything is empty
+  //  for (cellcount i = 0; i < inputLength; ++i) {
+  // phraseMemory[i] = 0;
+  //}
+  memset(phraseMemory, 0, sizeof(char)*inputLength);
+  
+  // intialise the hashTable of long phrases
+  initialiseLongPhraseMemory();
+
+}
+
+void rememberThisPhrase(cellindex index, cellcount length) {
+
+  // if the phrase is very long, use the file-based system
+  if (length > 8) {
+    rememberThisLongPhrase(index, length);
+    return;
+  }
+
+  // create a char with just the bit corresponding to length set
+  unsigned char newbit = 1;
+  for (cellcount i = 1; i < length; ++i) {
+    newbit <<= 1;
+  }
+
+  // set that bit in the memory array at position index
+  phraseMemory[index] |= newbit;
+}
+
+
+bool isPhraseStored(cellindex index, cellcount length) {
+
+  // if the phrase is very long, use the file-based system
+  if (length > 8) {
+    return isLongPhraseStored(index, length);
+  }
+
+  // create a char with just the bit corresponding to length set
+  unsigned char newbit = 1;
+  for (cellcount i = 1; i < length; ++i) {
+    newbit <<= 1;
+  }
+
+  // retrurn true if that bit is set in memory arrayat position index
+  return (phraseMemory[index] & newbit);
+}
+
+void deletePhraseMemory() {
+  delete []phraseMemory;
+  deleteLongPhraseMemory();
+}
+
+
+
+// Files etc used to store "long" equavlents of the above
+
+fstream hashTableFile;
+char    hashTableFileName[FILENAME_MAX];
+fstream listOfEntries;
+char    listOfEntriesName[FILENAME_MAX];
+cellindex nextEntryNumber;
+
+const cellcount bigPrime = 7919;
+
+
+void initialiseLongPhraseMemory() {
+
+  cellindex example = 0;
+
+  sprintf(hashTableFileName, "%s/hashTable", collection);
+  sprintf(listOfEntriesName, "%s/hashLists", collection);
+
+
+  // create the new hashtable
+  if (verbosity > 1) {
+    cout << "Initialising hashTable: " << hashTableFileName << endl;
+  }
+  hashTableFile.open(hashTableFileName, ios::in | ios::out);
+  for (cellcount i = 0; i < bigPrime; ++i) {
+    hashTableFile.write((char *) &example, sizeof(example));
+  }
+
+  // create the list of phrases
+  if (verbosity > 1) {
+    cout << "Initialising list of hashtable entries: " << listOfEntriesName << endl;
+  }
+  listOfEntries.open(listOfEntriesName, ios::in | ios::out);
+  listOfEntries.write((char *) &example, sizeof(example));
+  listOfEntries.write((char *) &example, sizeof(example));
+  listOfEntries.write((char *) &example, sizeof(example));
+  nextEntryNumber = 1;
+}
+
+
+void rememberThisLongPhrase(cellindex index, cellcount length) {
+
+  // cout << "rememberThisLongPhrase(" << index << ", " << length << ")\n";
+
+  cellindex hashOffset = ((index + length) % bigPrime) * sizeof(cellindex);
+  cellindex pointer;
+  cellindex zero = 0;
+  cellindex readp = 0;
+  cellindex readi = 0;
+  cellindex readl = 0;
+
+  hashTableFile.seekg(hashOffset);
+  hashTableFile.read((char *) &pointer, sizeof(cellindex));
+
+  if (pointer == 0) {
+    // There is no entry at all in the hash table for this entry
+    // so create one
+
+    pointer = nextEntryNumber++;
+    hashTableFile.seekg(hashOffset);
+    hashTableFile.write((char *) &pointer, sizeof(cellindex));
+        
+    listOfEntries.seekp(pointer * sizeof(cellindex) * 3);
+    listOfEntries.write((char *) &zero, sizeof(cellindex));
+    listOfEntries.write((char *) &index, sizeof(cellindex));
+    listOfEntries.write((char *) &length, sizeof(cellindex));
+
+  } else {
+    // There is a list starting at this hash value, so the phrase may
+    // be already remembered, or it might need to be appended
+    
+    while (pointer != 0) {
+      // Read the entry pointed to by pointer
+      listOfEntries.seekg(pointer * sizeof(cellindex) * 3);
+      listOfEntries.read((char *) &readp, sizeof(cellindex));
+      listOfEntries.read((char *) &readi, sizeof(cellindex));
+      listOfEntries.read((char *) &readl, sizeof(cellindex));
+
+      // cout << "read " << pointer << ", " << readp << ", " << readi << ", " << readl << endl;
+
+      if ((readi == index) && (readl = length)) {
+	// we've found that we've already stored it
+	return;
+      } else if (readp == 0) {
+	// we're reached the end of the list.  Add a new entry.
+	listOfEntries.seekp(pointer * sizeof(cellindex) * 3);
+	listOfEntries.write((char *) &nextEntryNumber, sizeof(cellindex));
+	pointer = nextEntryNumber++;
+
+	listOfEntries.seekp(pointer * sizeof(cellindex) * 3);
+	listOfEntries.write((char *) &zero, sizeof(cellindex));
+	listOfEntries.write((char *) &index, sizeof(cellindex));
+	listOfEntries.write((char *) &length, sizeof(cellindex));
+	return;
+      } else {
+	// go on to the next node
+	pointer = readp;
+      }
+    }
+  }
+
+
+}
+
+
+bool isLongPhraseStored(cellindex index, cellcount length) {
+
+  // cout << "isLongPhraseExpanded(" << index << ", " << length << ")\n";
+
+  cellindex hashOffset = ((index + length) % bigPrime) * sizeof(cellindex);
+  cellindex pointer;
+  cellindex readp = 0;
+  cellindex readi = 0;
+  cellindex readl = 0;
+
+  // Find the phrase in the hashFile
+  hashTableFile.seekg(hashOffset);
+  hashTableFile.read((char *) &pointer, sizeof(cellindex));
+
+  if (pointer == 0) {
+    // There is no entry at all in the hash table for this entry
+    // so nothing is stored
+    return false;
+
+  } else {
+    // There is a list starting at this hash value, so the phrase may
+    // be already remembered in that list
+    while (pointer != 0) {
+      // Read the entry pointed to by pointer
+      listOfEntries.seekg(pointer * sizeof(cellindex) * 3);
+      listOfEntries.read((char *) &readp, sizeof(cellindex));
+      listOfEntries.read((char *) &readi, sizeof(cellindex));
+      listOfEntries.read((char *) &readl, sizeof(cellindex));
+
+      if ((readi == index) && (readl = length)) {
+	// we've found the phrase stored here
+	return true;
+      } else {
+	// go on to the next node
+	pointer = readp;
+      }
+    }
+  }
+  return false;
+}
+
+
+void deleteLongPhraseMemory() {
+  // remove the hash & other files
+
+  hashTableFile.close();
+  listOfEntries.close();
+  remove(hashTableFileName);
+  remove(listOfEntriesName);
+
+}
+
+
+// Read the collection statistics file
+//
+void readStatistics() {
+
+  // open the statistics file
+  char filename[FILENAME_MAX];
+  sprintf(filename, "%s/clauses.stats", collection);
+
+  // Open the file
+  ifstream inFile(filename, ios::in);
+  if (!inFile) {
+    cerr << "File " << filename << " could not be opened\n";
+    exit(1);
+  }
+
+  // Read the numbers file into the numbers array
+  char key[1000];
+  symbol value;
+  while (inFile >> key >> value){
+    if (strcmp(key, "first_stopword") == 0) {
+      firstStopSymbol = value;
+    } else if (strcmp(key, "last_stopword") == 0) {
+      lastStopSymbol = value;
+    } else if (strcmp(key, "first_contentword") == 0) {
+      firstContentSymbol = value;
+    } else if (strcmp(key, "last_contentword") == 0) {
+      lastContentSymbol = value;
+    }
+  }
+  inFile.close();
+
+  // Make sure we have the information we need
+  if (!(firstStopSymbol && lastStopSymbol && firstContentSymbol && lastContentSymbol)) {
+    cerr << "Statistics file incomplete" << endl;
+    exit(1);
+  }
+}
+
+cellcount getContentCount(symbol firstContent) {
+
+  cellcount content=0;
+  for (cellcount i=0; i<inputLength; ++i) {
+    if (symbols[i]>=firstContent) ++content;
+  }
+
+  return content;
+}
+
+
+int main (int argc, char * argv[]) {
+
+  // Command-line arguments
+  // argv[1] is the phindex directory
+  // argv[2] is the mode, where 1 is stopword mode 
+  // argv[3] is the min_occurs, - minimum occurrence frequency for a phrase to be included in the hierarchy
+  // argv[4] is opitonal verbosity
+  if (argc < 4) {
+    cerr << "Usage: " << argv[0] << " phind-directory mode min-phrase-freq [verbosity]" << endl;
+    exit(1);
+  }
+
+  // collection directory
+  strcpy(collection, argv[1]);
+
+  // mode parameter
+  phraseMode = atoi(argv[2]);
+  assert((phraseMode == STOPWORDS) || (phraseMode == ANYPHRASE));
+
+  minOccurs = atoi(argv[3]);
+  assert((minOccurs > 0));
+
+  // optional verbosity parameter
+  if (argc == 5) {
+    verbosity = atoi(argv[4]);
+    assert (verbosity >= 0);
+  }
+
+  if (verbosity) {
+    cout << "suffix: the phrase extraction program" << endl;
+  }
+  if (verbosity > 1) {
+    if (phraseMode == STOPWORDS) {
+      cout << "Stopwords mode: no phrase may begin or end with a stopword" << endl;
+    } else {
+      cout << "AllPhrase mode: extract every phrase that occurs more than once" << endl;
+    }
+  }
+
+  // Read the statistics file 
+  readStatistics();
+  // Read the numbers file
+  readNumbers();
+  
+  if (numberOfDocuments == 0) {
+    cerr << "There are no documents in this collection!" << endl;
+    exit(0); // not necessarily an error...
+  }
+
+  symbol firstContent;
+  if (phraseMode==STOPWORDS) firstContent=firstContentSymbol;
+  else firstContent = firstStopSymbol;
+
+  // Allocate memory for the suffix & prefix arrays
+  cellcount contentLength = 0;
+  contentLength = getContentCount(firstContent);
+  suffixArray = new symbol *[contentLength];
+  prefixArray = new symbol *[contentLength];
+  if (prefixArray == NULL) {
+    cerr << "Suffix: not enough memory to hold " << inputLength << " symbols." << endl;
+    exit(2);
+  }
+  allocateSuffixCheck(contentLength);
+  // Initialise prefix and suffix arrays, only use the needed suffixes
+  for (cellcount j = 0, here = 0; j < inputLength; ++j) {
+    if (symbols[j]>=firstContent) {
+      suffixArray[here] = &symbols[j];
+      prefixArray[here] = &symbols[j];
+      ++here;
+    }    
+  }
+  qsort(suffixArray, contentLength, sizeof(symbol *), suffixCompare);
+  qsort(prefixArray, contentLength, sizeof(symbol *), prefixCompare);
+  // Create the document arrays
+  if (verbosity > 1) {
+    cout << "Allocating document arrays for " << numberOfDocuments << " documents" << endl;
+  }
+
+  // The document frequecy array is used to count the number of times
+  // each phrase occurs in each document.  The number of documents in
+  // which a phrase occurs is stored in df.
+  frequency *documentFrequency = new frequency[numberOfDocuments];
+  frequency df;
+
+  // documentArray will be searched in order to discover which document
+  // each phrase occurs in.
+  documentArray = new symbol *[numberOfDocuments];  
+  if (documentArray == NULL) {
+    cerr << "Suffix: out of memory allocating document arrays." << endl;
+    exit(2);
+  }  
+
+  // just scan through the input text to find the doc starts
+  cellindex d = 0;
+  for (cellindex i=0; i<inputLength; ++i) {
+    if (symbols[i] == DOCUMENTSTART) {
+      documentArray[d] = &symbols[i];
+      ++d;
+    }
+  }
+
+  // the phrases stuff is expecting inputLength to be the length of the
+  // suffix array, so change it.
+  inputLength = contentLength;
+
+  // Extract phrases
+  //
+  // We will make several passesover the data, in each case considering
+  // a set of input phrases and generating a set of output phrases, which
+  // we will expancd in later passes.
+  //
+  // The input phrases in the first pass will be the vocabulary.
+  // In later passes, the input phrases will be the output phrases of the
+  // previous pass.
+  //
+  // In each pass we will consider each input phrase in turn.  If we
+  // have seen it before, we will ignore it.  Otherwise, we will expand
+  // it and add its expansions to the set of output phrases.
+
+  cout <<"\ngenerating the phrase hierarchy\n\n";
+  
+  // Store the phrase data in the phrases file
+  char phraseDataName[FILENAME_MAX];
+  sprintf(phraseDataName, "%s/phrases", collection);
+  ofstream phraseData(phraseDataName, ios::out);
+  if (!phraseData) {
+    cout << "File " << phraseDataName << " could not be opened\n";
+    exit(1);
+  }
+
+  // Count the number of phrases output
+  unsigned long int phraseCounter = 0;
+
+  // Set up the phrase expansion memory.
+  // We need this so that we don't expand a phrase more than once
+  initialisePhraseMemory();
+
+  // The current pass numebr
+  int phrasePass = 1;
+
+
+  // PASS NUMBER 1
+  if (verbosity > 1) {
+    cout << "Starting pass " << phrasePass << endl;
+  }
+
+  ofstream outPhrase;
+  char     outPhraseName[FILENAME_MAX];
+  unsigned long int outPhraseCounter = 0;
+
+  // On the first pass, simply work through the vocabulary
+  sprintf(outPhraseName, "%s/outPhrase.1", collection);
+  outPhrase.open(outPhraseName, ios::out);
+  if (!outPhrase) {
+    cerr << "File " << outPhraseName << " could not be opened\n";
+    exit(1);
+  }
+
+  // Iterate over the different symbols by working through the suffix array
+  vector<Phrase> result;
+  cellindex ij = 0;
+  char *tmpString;
+
+  Phrase p;
+  while (ij < inputLength) {
+
+    // make a new phrase of length 1
+    p = Phrase(suffixArray[ij], 1, SUFFIX);
+    p.findFirstAndLastSuffix(ij, inputLength-1);
+
+    // We ignore this symbol if it occurs only once, if it is a delimiter,
+    // of if we are in stopwords mode and it is a stopword
+    // - in this new version, only need to check freq
+    // We could imagine a new mode/command-line option, which is like 
+    // STOPWORDS but without this restrictrion.  This would let you browse
+    // from "the" to "the AGRIS" for example, but not from "AGRIS" to
+    // "the AGRIS" (where the is a stopword and AGRIS a content word).
+    // The system used to work like this; it is easy to implement, but
+    // it explodes the size of the indexes.  So: would it be useful?  
+    if (p.suffixFrequency >= minOccurs) {
+      // Get minimal expansions of the phrase
+      getExpansions(p, result);
+      
+      if (!result.empty()) {
+	
+	// Remember that we have expanded this phrase
+	rememberThisPhrase(ij, 1);
+
+	// write the phrase text
+	phraseData << ij << "-1:" << p << ":" << p.suffixFrequency << ":"
+		   << result.size() << ":";
+
+	// write the results
+	for (cellcount k = 0; k < result.size(); ++k) {
+	  if (k) {
+	    phraseData << ",";
+	  }
+	  phraseData << result[k].firstSuffixIndex << "-" << result[k].length;
+	  outPhrase << result[k].firstSuffixIndex << " " << result[k].length << endl;
+	  ++outPhraseCounter;
+	}
+	result.clear();
+	
+	// Write the documents in which this phrase occurs
+	df = getDocumentOccurrances(p, documentFrequency);
+	phraseData << ":" << df << ":";
+
+	// write the documents
+	for (cellcount m = 0, first = 1; m < numberOfDocuments; ++m) {
+	  if (documentFrequency[m]) {
+	    if (first) {
+	      first = 0;
+	    } else {
+	      phraseData << ";";
+	    }
+	    // Output the document number.  Note that here we've numbered the 
+	    // N documents from 0 to N-1, but later they'll be 1-N.  Thus we
+	    // add 1 to the document id when we output it.
+	    phraseData << "d" << (m+1);
+	    // Next, output the frequency with which the document occurs, but
+	    // only if it is > 1.
+	    if (documentFrequency[m] > 1) {
+	      phraseData << "," << documentFrequency[m];
+	    }
+	  }
+	}
+
+	phraseData << endl;
+	++phraseCounter;
+
+	// feedback 
+	if (verbosity) {
+	  if (phraseCounter % 1000 == 0) {
+	    cout << "phrase " << phraseCounter << ": "
+		 << "cell " << p.firstSuffixIndex << " - " << p << endl;
+	  }
+	}
+      }
+    }
+   ij = p.lastSuffixIndex + 1;
+  }
+  outPhrase.close();
+
+  // REMAINING PASSES
+  // The previous outPhrase file forms the input to each new pass
+  cellcount start, length;
+  while (outPhraseCounter > 0) {
+
+    // Start a new pass
+    ++phrasePass;
+    if (verbosity) {
+      cout << "Starting pass " << phrasePass << endl;
+    }
+
+    // Open the input file
+    char inPhraseName[FILENAME_MAX];
+    sprintf(inPhraseName, "%s/outPhrase.%d", collection, phrasePass - 1);
+    ifstream inPhrase (inPhraseName, ios::in);
+    if (!inPhrase) {
+      cerr << "File " << inPhraseName << " could not be opened\n";
+      exit(1);
+    }
+
+    // Open the output file
+    sprintf(outPhraseName, "%s/outPhrase.%d", collection, phrasePass);
+    outPhrase.open(outPhraseName, ios::out);
+    if (!outPhrase) {
+      cerr << "File " << outPhraseName << " could not be opened\n";
+      exit(1);
+    }
+    outPhraseCounter = 0;
+
+    // Process each phrase
+    while(inPhrase >> start >> length) {
+
+      // Ignore the phrase if we have expanded it before
+      if (isPhraseStored(start, length))
+	continue;
+
+      // Remember that we have examined this phrase
+      rememberThisPhrase(start, length);
+
+      // Find the phrase in the suffixarray
+      p = Phrase(suffixArray[start], length, SUFFIX);
+      p.findFirstAndLastSuffix(start, inputLength-1);
+
+      // Ignore the phrase if it only occurs once
+      if (p.suffixFrequency < minOccurs)
+	continue;
+
+      // Write the phrase text;
+      phraseData << start << "-" << length << ":" << p << ":" << p.suffixFrequency << ":";
+	
+      // Expand the phrase, if it is fewer than 8 words long
+      if (length <= 8) {
+
+	// Get the minimal expansions for this phrase
+	getExpansions(p, result);
+      
+	// write the results
+	phraseData << result.size() << ":";
+
+	for (cellcount i = 0; i < result.size(); ++i) {
+	  if (i) {
+	    phraseData << ",";
+	  }
+	  phraseData << result[i].firstSuffixIndex << "-" << result[i].length;
+	  outPhrase << result[i].firstSuffixIndex << " " << result[i].length << endl;
+	  ++outPhraseCounter;
+	}
+	result.clear();
+	
+      } else {
+	// phrase is too long to expand further
+	phraseData << "0:";
+      }
+
+      // Write the documents in which this phrase occurs
+      df = getDocumentOccurrances(p, documentFrequency);
+      phraseData << ":" << df << ":";
+
+      // write the documents
+      for (cellcount i = 0, first = 1; i < numberOfDocuments; ++i) {
+	if (documentFrequency[i]) {
+	  if (first) {
+	    first = 0;
+	  } else {
+	    phraseData << ";";
+	  }
+	  // Output the document number.  Note that here we've numbered the 
+	  // N documents from 0 to N-1, but later they'll be 1-N.  Thus we
+	  // add 1 to the document id when we output it.
+	  phraseData << "d" << (i+1);
+	  // Next, output the frequency with which the document occurs, but
+	  // only if it is > 1.
+	  if (documentFrequency[i] > 1) {
+	    phraseData << "," << documentFrequency[i];
+	  }
+	}
+      }
+      
+      phraseData << endl;
+      ++phraseCounter;
+
+      // feedback 
+      if (verbosity) {
+	if (phraseCounter % 1000 == 0) {
+	  cout << "phrase " << phraseCounter << ": "<< "start " << start 
+	       << ", length " << length << " - " << p << endl;
+	}
+      }
+
+    }
+
+    inPhrase.close();
+    outPhrase.close();
+  }
+    
+  phraseData.close();
+  deletePhraseMemory();
+
+  delete [] documentFrequency;
+  delete [] symbols;
+  delete [] suffixArray;
+  delete [] prefixArray;
+  delete [] suffixCheck;
+  delete [] documentArray;
+
+
+  
+  cout << endl << "Done: " << phraseCounter << " phrases in " << phraseDataName << endl;
+  return 0;
+}
+
+
+
+
+
Index: /gsdl/trunk/gs2build/src/phind/generate/suffix.h
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/suffix.h	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/suffix.h	(revision 16561)
@@ -0,0 +1,89 @@
+/**********************************************************************
+ *
+ * suffix.h -- definitions used in suffix.cpp
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+#ifndef SUFFIX_H
+#define SUFFIX_H
+
+// Types
+
+// The input words stored as an array of type symbol.
+// Required range: 0 - vocabulary size (typically hundreds of thousands)
+typedef unsigned int symbol;
+
+// The cellindex and cellcount types are used when we store indexes
+// to and ranges of cells in large arrays.
+// Required Range: 0 - length of input (typically millions)
+typedef unsigned int cellindex;
+typedef unsigned int cellcount;
+
+// The frequency type is used when we want to store the frequency with 
+// which a phrase (or some other thing) occurs
+// Required range: 0 - frequency of most common symbol (often "the")
+typedef unsigned int frequency;
+
+// Global variables 
+
+// The symbol array holds the input words
+extern cellcount symbol_array_length;
+extern symbol   *symbols;
+
+// The number of words read is storesd in inputLength.
+extern cellcount inputLength;
+
+// Suffix and prefix arrays are used to extract phrases
+extern symbol  **suffixArray;
+extern symbol  **prefixArray;
+
+// Collection-specific information about the first stopword/content symbols
+extern symbol firstStopSymbol;
+extern symbol lastStopSymbol;
+extern symbol firstContentSymbol;
+extern symbol lastContentSymbol;
+
+
+// Are we allowed to terminate a phrase on a stopword?
+extern int phraseMode;
+extern int minOccurs;
+
+#define ANYPHRASE 0
+#define STOPWORDS 1
+
+
+// Direction values
+#define SUFFIX 0
+#define PREFIX 1
+
+
+// Special symbol values
+#define COLLECTIONSTART 1
+#define COLLECTIONEND 2
+#define DOCUMENTSTART 3
+#define PHRASELIMIT 4
+
+#define LASTDELIMITER 4
+
+#endif
Index: /gsdl/trunk/gs2build/src/phind/generate/symbol-names.pl
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/symbol-names.pl	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/symbol-names.pl	(revision 16561)
@@ -0,0 +1,57 @@
+#! /usr/bin/perl -w
+
+###########################################################################
+#
+# symbol-names.pl -- Diagnostic tool that substitutes symbols names for 
+#                    symbol numbers in the output of the suffix program. 
+#
+# Copyright (C) 2000 Gordon Paynter
+#
+# 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.
+#
+###########################################################################
+
+$collection = shift(@ARGV);;
+die "Usage: symbol-names.pl collection [file]\n" unless ($collection);
+
+$vocab = $ENV{'GSDLHOME'} . "/collect/$collection/index/phind1/clauses.vocab";
+if (-e "$vocab") {
+    print "Using vocabulary from: $vocab\n";
+} else {
+    $vocab = $ENV{'GSDLHOME'} . "/collect/$collection/building/phind1/clauses.vocab";
+    if (-e "$vocab") {
+	print "Using vocabulary from: $vocab\n";
+    } else {
+	die "Usage: symbol-names.pl collection [file]\n" unless (-e "$vocab");
+    }
+}
+
+open(V, "<$vocab");
+my $n = 1;
+
+while(<V>) {
+    chomp;
+    $symbol[$n++] = $_;
+}
+
+while(<>) {
+    s/s(\d+)/$symbol[$1]/g;
+    print;
+}
+
Index: /gsdl/trunk/gs2build/src/phind/generate/win32.mak
===================================================================
--- /gsdl/trunk/gs2build/src/phind/generate/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2build/src/phind/generate/win32.mak	(revision 16561)
@@ -0,0 +1,76 @@
+###########################################################################
+#
+# win32 makefile -- src/phind/generate
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2001  The 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.
+#
+###########################################################################
+
+GSDLHOME = ..\..\..
+GSDL_VC4 = 0
+STLPATH = "$(GSDLHOME)\packages\windows\stlport\stlport"
+
+AR = lib
+CC = cl
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+LDFLAGS =
+DEFS = -D__WIN32__
+INCLUDES =
+!IF $(GSDL_VC4)
+DEFS = $(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = $(INCLUDES) -I$(STLPATH)
+!ENDIF
+LIBS=
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+HEADERS = suffix.h phrase.h
+SOURCES = suffix.cpp phrase.cpp
+OBJECTS = suffix.obj phrase.obj
+EXEC    = suffix.exe
+
+all: $(EXEC)
+
+install:
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *.obj
+	del $(EXEC)
+
+suffix.exe: $(OBJECTS)
+	    $(LINK) $(OBJECTS) $(LIBS) 
Index: /gsdl/trunk/gs2common/indexers/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/Makefile.in	(revision 16561)
@@ -0,0 +1,115 @@
+###########################################################################
+#
+# Makefile -- Makefile for indexers used by Greenstone
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2007 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.
+#
+###########################################################################
+
+
+# INDEXERDIRS contains the packages that we want to make and install.  It
+# is assumed that each package will have at least four rules: all, install,
+# clean, and distclean.
+
+ENABLE_ACCENTFOLD=@ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+UNAC=packages/unac
+else
+UNAC=
+endif
+
+INDEXERDIRS = $(UNAC) mg mgpp lucene-gs 
+
+all install clean:
+	for odir in $(INDEXERDIRS); do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+
+nojava:
+	for odir in $(UNAC); do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) all) || exit 1; \
+        done
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+
+install-nojava:
+	for odir in $(UNAC); do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) install) || exit 1; \
+        done
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+
+javaonly:
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+	for odir in lucene-gs; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) all) || exit 1; \
+        done
+
+install-javaonly:
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+	for odir in lucene-gs; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) install) || exit 1; \
+        done
+
+# make everything except jni stuff
+gs2:
+	for odir in $(UNAC) lucene-gs; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) all) || exit 1; \
+        done
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) nojava) || exit 1; \
+        done
+
+install-gs2:
+	for odir in $(UNAC) lucene-gs; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) install) || exit 1; \
+        done
+	for odir in mg mgpp; do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) install-nojava) || exit 1; \
+        done
+
+
+distclean:
+	for odir in $(INDEXERDIRS); do \
+          echo making $@ in $$odir; \
+          (cd $$odir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+        done
+	rm -f Makefile config.status config.log
+
Index: /gsdl/trunk/gs2common/indexers/README.txt
===================================================================
--- /gsdl/trunk/gs2common/indexers/README.txt	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/README.txt	(revision 16561)
@@ -0,0 +1,1 @@
+A temporary readme file for new indexers project for greenstone.
Index: /gsdl/trunk/gs2common/indexers/config.guess
===================================================================
--- /gsdl/trunk/gs2common/indexers/config.guess	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/config.guess	(revision 16561)
@@ -0,0 +1,1298 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-07-12'
+
+# This file 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int dummy(){}" > $dummy.c ;
+	for c in cc gcc c89 ; do
+	  ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+	  if test $? = 0 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	rm -f $dummy.c $dummy.o $dummy.rel ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# Netbsd (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	# Determine the machine/vendor (is the vendor relevant).
+	case "${UNAME_MACHINE}" in
+	    amiga) machine=m68k-unknown ;;
+	    arm32) machine=arm-unknown ;;
+	    atari*) machine=m68k-atari ;;
+	    sun3*) machine=m68k-sun ;;
+	    mac68k) machine=m68k-apple ;;
+	    macppc) machine=powerpc-apple ;;
+	    hp3[0-9][05]) machine=m68k-hp ;;
+	    ibmrt|romp-ibm) machine=romp-ibm ;;
+	    *) machine=${UNAME_MACHINE}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE}" in
+	    i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	if test $UNAME_RELEASE = "V4.0"; then
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+	fi
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	cat <<EOF >$dummy.s
+	.data
+\$Lformat:
+	.byte 37,100,45,37,120,10,0	# "%d-%x\n"
+
+	.text
+	.globl main
+	.align 4
+	.ent main
+main:
+	.frame \$30,16,\$26,0
+	ldgp \$29,0(\$27)
+	.prologue 1
+	.long 0x47e03d80 # implver \$0
+	lda \$2,-1
+	.long 0x47e20c21 # amask \$2,\$1
+	lda \$16,\$Lformat
+	mov \$0,\$17
+	not \$1,\$18
+	jsr \$26,printf
+	ldgp \$29,0(\$26)
+	mov 0,\$16
+	jsr \$26,exit
+	.end main
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+	if test "$?" = 0 ; then
+		case `./$dummy` in
+			0-0)
+				UNAME_MACHINE="alpha"
+				;;
+			1-0)
+				UNAME_MACHINE="alphaev5"
+				;;
+			1-1)
+				UNAME_MACHINE="alphaev56"
+				;;
+			1-101)
+				UNAME_MACHINE="alphapca56"
+				;;
+			2-303)
+				UNAME_MACHINE="alphaev6"
+				;;
+			2-307)
+				UNAME_MACHINE="alphaev67"
+				;;
+		esac
+	fi
+	rm -f $dummy.s $dummy
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    arc64:OpenBSD:*:*)
+	echo mips64el-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    arc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hkmips:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    pmax:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy \
+	  && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		eval $set_cc_for_build
+		$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+		rm -f $dummy.c $dummy
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+              case "${HPUX_REV}" in
+                11.[0-9][0-9])
+                  if [ -x /usr/bin/getconf ]; then
+                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                        esac ;;
+                    esac
+                  fi ;;
+              esac
+              if [ "${HP_ARCH}" = "" ]; then
+              sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+	eval $set_cc_for_build
+	(CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+	if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+	rm -f $dummy.c $dummy
+	fi ;;
+	esac
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    hppa*:OpenBSD:*:*)
+	echo hppa-unknown-openbsd
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+	echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3D:*:*:*)
+	echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY-2:*:*:*)
+	echo cray2-cray-unicos
+        exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i386-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+	  big)    echo mips-unknown-linux-gnu && exit 0 ;;
+	  little) echo mipsel-unknown-linux-gnu && exit 0 ;;
+	esac
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev67 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	ld_supported_targets=`cd /; ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;		
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-pc-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-pc-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    M68*:*:R3V[567]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                           # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	echo `uname -p`-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	if test "${UNAME_MACHINE}" = "x86pc"; then
+		UNAME_MACHINE=pc
+	fi
+	echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-[KW]:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+eval $set_cc_for_build
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/config.sub
===================================================================
--- /gsdl/trunk/gs2common/indexers/config.sub	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/config.sub	(revision 16561)
@@ -0,0 +1,1375 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-06-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \
+	        | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \
+		| pyramid | mn10200 | mn10300 | tron | a29k \
+		| 580 | i960 | h8300 \
+		| x86 | ppcbe | mipsbe | mipsle | shbe | shle \
+		| hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+		| hppa64 \
+		| alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+		| alphaev6[78] \
+		| we32k | ns16k | clipper | i370 | sh | sh[34] \
+		| powerpc | powerpcle \
+		| 1750a | dsp16xx | pdp10 | pdp11 \
+		| mips16 | mips64 | mipsel | mips64el \
+		| mips64orion | mips64orionel | mipstx39 | mipstx39el \
+		| mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+		| mips64vr5000 | mips64vr5000el | mcore | s390 | s390x \
+		| sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
+		| v850 | c4x \
+		| thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
+		| pj | pjl | h8500 | z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	# FIXME: clean up the formatting here.
+	vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+	      | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \
+	      | arm-*  | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \
+	      | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+	      | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+	      | xmp-* | ymp-* \
+	      | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \
+	      | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+	      | hppa2.0n-* | hppa64-* \
+	      | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+	      | alphaev6[78]-* \
+	      | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+	      | clipper-* | orion-* \
+	      | sparclite-* | pdp10-* | pdp11-* | sh-* | sh[34]-* | sh[34]eb-* \
+	      | powerpc-* | powerpcle-* | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \
+	      | mips16-* | mips64-* | mipsel-* \
+	      | mips64el-* | mips64orion-* | mips64orionel-* \
+	      | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+	      | mipstx39-* | mipstx39el-* | mcore-* \
+	      | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
+	      | [cjt]90-* \
+	      | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+	      | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \
+	      | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	cray2)
+		basic_machine=cray2-cray
+		os=-unicos
+		;;
+	[cjt]90)
+		basic_machine=${basic_machine}-cray
+		os=-unicos
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mipsel*-linux*)
+		basic_machine=mipsel-unknown
+		os=-linux-gnu
+		;;
+	mips*-linux*)
+		basic_machine=mips-unknown
+		os=-linux-gnu
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	mmix*)
+		basic_machine=mmix-knuth
+		os=-mmixware
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+        pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2)
+		basic_machine=i686-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+	        ;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+	        ;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sparclite-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=t3e-cray
+		os=-unicos
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	windows32)
+		basic_machine=i386-pc
+		os=-windows32-msvcrt
+		;;
+	xmp)
+		basic_machine=xmp-cray
+		os=-unicos
+		;;
+        xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	mips)
+		if [ x$os = x-linux-gnu ]; then
+			basic_machine=mips-unknown
+		else
+			basic_machine=mips-mips
+		fi
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+        cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	c4x*)
+		basic_machine=c4x-none
+		os=-coff
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto*)
+		os=-nto-qnx
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-ns2 )
+	        os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+	        os=-mint
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	pdp10-*)
+		os=-tops20
+		;;
+        pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+        *-gould)
+		os=-sysv
+		;;
+        *-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+        *-sgi)
+		os=-irix
+		;;
+        *-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-vxsim* | -vxworks*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/configure
===================================================================
--- /gsdl/trunk/gs2common/indexers/configure	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/configure	(revision 16561)
@@ -0,0 +1,2375 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="mgpp/text/MGQuery.cpp"
+ac_subdirs_all="$ac_subdirs_all packages/unac"
+ac_subdirs_all="$ac_subdirs_all mg mgpp"
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS ENABLE_ACCENTFOLD COMPAT32BITFLAGS subdirs LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-accentfold    Disable Accent Folding support
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-unac=dir       Use specified unac directory
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-accentfolding or --disable-accentfolding was given.
+if test "${enable_accentfolding+set}" = set; then
+  enableval="$enable_accentfolding"
+  ENABLE_ACCENTFOLD=0
+else
+  ENABLE_ACCENTFOLD=1
+fi;
+cat >>confdefs.h <<_ACEOF
+#define ENABLE_ACCENTFOLD $ENABLE_ACCENTFOLD
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define COMPAT32BITFLAGS $COMPAT32BITFLAGS
+_ACEOF
+
+
+
+
+
+# Check whether --with-unac or --without-unac was given.
+if test "${with_unac+set}" = set; then
+  withval="$with_unac"
+  UNAC_DIR=$withval
+fi;
+
+if test $ENABLE_ACCENTFOLD = 1; then
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+
+
+subdirs="$subdirs packages/unac"
+
+fi
+
+
+
+subdirs="$subdirs mg mgpp"
+
+
+          ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[	 `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output.  A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@ENABLE_ACCENTFOLD@,$ENABLE_ACCENTFOLD,;t t
+s,@COMPAT32BITFLAGS@,$COMPAT32BITFLAGS,;t t
+s,@subdirs@,$subdirs,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+#
+# CONFIG_SUBDIRS section.
+#
+if test "$no_recursion" != yes; then
+
+  # Remove --cache-file and --srcdir arguments so they do not pile up.
+  ac_sub_configure_args=
+  ac_prev=
+  for ac_arg in $ac_configure_args; do
+    if test -n "$ac_prev"; then
+      ac_prev=
+      continue
+    fi
+    case $ac_arg in
+    -cache-file | --cache-file | --cache-fil | --cache-fi \
+    | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+      ac_prev=cache_file ;;
+    -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+    | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \
+    | --c=*)
+      ;;
+    --config-cache | -C)
+      ;;
+    -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+      ac_prev=srcdir ;;
+    -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+      ;;
+    -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+      ac_prev=prefix ;;
+    -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+      ;;
+    *) ac_sub_configure_args="$ac_sub_configure_args $ac_arg" ;;
+    esac
+  done
+
+  # Always prepend --prefix to ensure using the same prefix
+  # in subdir configurations.
+  ac_sub_configure_args="--prefix=$prefix $ac_sub_configure_args"
+
+  ac_popdir=`pwd`
+  for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue
+
+    # Do not complain, so a configure script can configure whichever
+    # parts of a large source tree are present.
+    test -d $srcdir/$ac_dir || continue
+
+    { echo "$as_me:$LINENO: configuring in $ac_dir" >&5
+echo "$as_me: configuring in $ac_dir" >&6;}
+    { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+    cd $ac_dir
+
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      ac_sub_configure="$SHELL '$ac_srcdir/configure.gnu'"
+    elif test -f $ac_srcdir/configure; then
+      ac_sub_configure="$SHELL '$ac_srcdir/configure'"
+    elif test -f $ac_srcdir/configure.in; then
+      ac_sub_configure=$ac_configure
+    else
+      { echo "$as_me:$LINENO: WARNING: no configuration information is in $ac_dir" >&5
+echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;}
+      ac_sub_configure=
+    fi
+
+    # The recursion is here.
+    if test -n "$ac_sub_configure"; then
+      # Make the cache file name correct relative to the subdirectory.
+      case $cache_file in
+      [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;;
+      *) # Relative path.
+	ac_sub_cache_file=$ac_top_builddir$cache_file ;;
+      esac
+
+      { echo "$as_me:$LINENO: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5
+echo "$as_me: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;}
+      # The eval makes quoting arguments work.
+      eval $ac_sub_configure $ac_sub_configure_args \
+	   --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir ||
+	{ { echo "$as_me:$LINENO: error: $ac_sub_configure failed for $ac_dir" >&5
+echo "$as_me: error: $ac_sub_configure failed for $ac_dir" >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+
+    cd $ac_popdir
+  done
+fi
+
+
+
Index: /gsdl/trunk/gs2common/indexers/configure.ac
===================================================================
--- /gsdl/trunk/gs2common/indexers/configure.ac	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/configure.ac	(revision 16561)
@@ -0,0 +1,27 @@
+dnl
+dnl Disable accent folding
+dnl
+
+AC_INIT(mgpp/text/MGQuery.cpp)
+AC_ARG_ENABLE(accentfolding, [  --disable-accentfold    Disable Accent Folding support], ENABLE_ACCENTFOLD=0, ENABLE_ACCENTFOLD=1)
+AC_DEFINE_UNQUOTED(ENABLE_ACCENTFOLD, $ENABLE_ACCENTFOLD)
+AC_SUBST(ENABLE_ACCENTFOLD)
+
+AC_DEFINE_UNQUOTED(COMPAT32BITFLAGS, $COMPAT32BITFLAGS)
+AC_SUBST(COMPAT32BITFLAGS)
+
+
+dnl
+dnl Specify location of unac - if specified, will be passed to cascading configures
+dnl
+AC_ARG_WITH(unac, [  --with-unac=dir       Use specified unac directory], UNAC_DIR=$withval)
+
+if test $ENABLE_ACCENTFOLD = 1; then
+AC_CONFIG_SUBDIRS(packages/unac)
+fi
+
+AC_CONFIG_SUBDIRS(mg mgpp)
+
+AC_OUTPUT(Makefile)
+
+
Index: /gsdl/trunk/gs2common/indexers/install-sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/install-sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/install-sh	(revision 16561)
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/Makefile
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/Makefile	(revision 16561)
@@ -0,0 +1,21 @@
+LUCENE_JAR = ./lib/lucene-core-2.3.2.jar
+CLASSPATH = $(LUCENE_JAR):classes
+
+all: compile jar
+
+compile:
+	mkdir -p classes
+	javac -classpath $(CLASSPATH):. -d classes -sourcepath src/ src/org/greenstone/LuceneWrapper/*.java
+
+jar:
+	cd classes && jar xf ../$(LUCENE_JAR)
+	jar cvf LuceneWrapper.jar -C classes .
+
+install:
+
+clean:
+	/bin/rm -f classes/org/greenstone/LuceneWrapper/*.class
+
+distclean: clean
+	if test -e classes; then /bin/rm -r classes; fi
+	if test -e LuceneWrapper.jar; then /bin/rm -r LuceneWrapper.jar; fi
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/lib/lucene-license.txt
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/lib/lucene-license.txt	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/lib/lucene-license.txt	(revision 16561)
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2IndexModifier.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2IndexModifier.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2IndexModifier.java	(revision 16561)
@@ -0,0 +1,132 @@
+/** @file GS2IndexModifier.java
+ *
+ *  Extends the standard IndexModifier so that a use can retrieve documents and
+ *  the underlying IndexReader and IndexWriter. 
+ *
+ *  Addresses the problem that the Lucene standard IndexModifier can't actually
+ *  return a specified Document object, nor does it expose the IndexReader and
+ *  IndexWriter so the user can access their methods instead.
+ *
+ *  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.
+ *
+ *  Copyright (c) 2006 DL Consulting Ltd., New Zealand
+ */
+
+package org.greenstone.LuceneWrapper;
+
+import java.io.IOException;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexModifier;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermPositions;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.IndexSearcher;
+
+/**
+ */
+public class GS2IndexModifier
+    extends IndexModifier
+{
+    /** **/
+    private boolean debug = true;
+
+    /**
+     */
+    public GS2IndexModifier (String dir_name, Analyzer analyzer)
+        throws IOException
+    {
+        super(dir_name, analyzer, false);
+    }
+    /** GS2IndexModifier() **/
+
+    /**
+     */
+    protected void debug(String message)
+    {
+        if(debug)
+            {
+                System.err.println(message);
+            }
+    }
+    /** debug() **/
+
+    /**
+     */
+    public Document document(int n)
+        throws IOException
+    {
+        // Ensure the reader exists
+        createIndexReader();
+        return indexReader.document(n);
+    }
+    /** document() **/
+
+    /**
+     */
+    public int getDocNumByNodeID(int node_id)
+        throws IOException
+    {
+        debug("GS2IndexModifier.getDocument(" + node_id + ")");
+        int doc_num = -1;
+        // Create a new term to encapsulate this node id
+        Term term = new Term("nodeID", String.valueOf(node_id));
+        debug("Searching using term: " + term.toString());
+        // Ensure the indexReader exists
+        createIndexReader();
+        // We can return an enumeration of docNums which contains this term
+        TermPositions term_positions = indexReader.termPositions(term);
+        // If the size of term_positions is not 1 then somethings gone wrong in
+        // that the original terms did not uniquely identify a document.
+        if (term_positions.next())
+            {
+                doc_num = term_positions.doc();
+            }
+        else
+            {
+                debug("Term doesn't exists in index");
+            }
+        term_positions.close();
+        // To the garbage collection with thee
+        term_positions = null;
+        term = null;
+        // Done
+        return doc_num;
+    }
+    /** getDocumentByNodeID() **/
+
+    /**
+     */
+    public IndexReader getIndexReader()
+    {
+        return indexReader;
+    }
+    /** getIndexReader() **/
+
+    /**
+     */
+    public IndexWriter getIndexWriter()
+    {
+        return indexWriter;
+    }
+    /** getIndexWriter() **/
+}
+/** GS2IndexModifier **/
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneDelete.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneDelete.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneDelete.java	(revision 16561)
@@ -0,0 +1,169 @@
+/** @file GS2LuceneDelete.java
+ *
+ *  Provides a wrapper to the document deleting features of Lucene. 
+ *
+ *  This java application makes use of the existing Lucene class IndexModifier 
+ *  to access and make changes to the information stored about documents in a
+ *  Lucene database. This is an essential component of the IncrementalBuilder
+ *  PERL module, and endevours to make editing the text and metadata of 
+ *  documents without having to rebuild the entire collection a reality (in
+ *  other words, true incremental/dynamic building).
+ *
+ *  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.
+ *
+ *  Copyright (c) 2006 DL Consulting Ltd., New Zealand
+ */
+
+package org.greenstone.LuceneWrapper;
+
+import java.io.IOException;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+
+
+/** Contains methods for deleting a document that has previously been indexed
+ *  into a Lucene database.
+ *  @author John Thompson, DL Consulting Ltd. (unless stated otherwise)
+ */
+public class GS2LuceneDelete
+{
+    /** This is the main entry point to the deletor and is responsible for 
+     *  parsing the arguments and creating an instance of the deletor class.
+     *
+     *  @param  args The arguments passed into the application as a string
+     *               array
+     *  @return An integer describing the exit state of the application
+     *  @throws Exception on any fatal error state
+     */
+    static public void main (String args[])
+        throws Exception
+    {
+        // Parse arguments
+        String index_path = "";
+        int node_id = -1;
+
+        for (int i = 0; i < args.length; i += 2)
+        {
+	    if (args[i].equals("--index"))
+	    {
+		index_path = args[i + 1];
+	    }
+	    else if (args[i].equals("--nodeid"))
+	    {
+		node_id = Integer.parseInt(args[i + 1]);
+	    }
+	    else 
+	    {
+		System.out.println("Error! Unknown argument: " + args[i]);
+		GS2LuceneDelete.printUsage();
+		System.exit(0);
+	    }
+	}
+
+        // Check arguments
+        if (index_path.equals(""))
+        {
+	    System.out.println("Error! Missing index path");
+	    GS2LuceneDelete.printUsage();
+	    System.exit(0);
+	}
+        if (node_id == -1)
+        {
+	    System.out.println("Error! Missing or invalid Node ID");
+	    GS2LuceneDelete.printUsage();
+	    System.exit(0);
+	}
+
+        // Instantiate deletor, and perform the delete
+        GS2LuceneDelete deletor = new GS2LuceneDelete(index_path);
+        deletor.deleteDocument(node_id);
+        deletor.destroy();
+        deletor = null;
+    }
+
+
+    /** Display program usage message.
+     */
+    static public void printUsage()
+    {
+        System.out.println("usage: GS2LuceneDelete --index <path> --nodeid <int>");
+        System.out.println("");
+        System.out.println("where:");
+        System.out.println("  index    - is the full path to the directory containing the directory");
+        System.out.println("             to edit, including the level (ie didx, sidx)");
+        System.out.println("  nodeid   - the unique identifier of the document to delete. This is the");
+        System.out.println("             same as the docnum in the GDBM");
+        System.out.println("");
+    }
+
+
+    /** **/
+    private boolean debug = true;
+
+    /** **/
+    private IndexWriter index_writer = null;
+
+
+    /** Constructor which takes the path to the Lucene index to be edited.
+     *
+     *  @param  index_path The full path to the index directory as a String
+     */
+    public GS2LuceneDelete(String index_path)
+        throws IOException
+    {
+	index_writer = new IndexWriter(index_path, new StandardAnalyzer());
+    }
+
+
+    /** When called prints a debug message but only if debugging is enabled.
+     */
+    public void debug(String message)
+    {
+        if (debug)
+        {
+	    System.err.println(message);
+	}
+    }
+
+
+    /** Destructor which unallocates connection to Lucene.
+     */
+    public void destroy()
+        throws IOException
+    {
+	index_writer.close();
+	index_writer = null;
+    }
+
+
+    /** Delete the indicated document from the Lucene index. This process is
+     *  very similar to the initial step of index editing.
+     *
+     *  @param  node_id   The unique identifier of a Lucene document as an
+     *                    integer
+     */
+    public void deleteDocument(int node_id)
+        throws IOException
+    {
+        debug("GS2LuceneDelete.deleteDocument(" + node_id + ")");
+        debug("- Initial number of documents in index: " + index_writer.docCount());
+	index_writer.deleteDocuments(new Term("nodeid", "" + node_id));
+        debug("- Final number of documents in index: " + index_writer.docCount());
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneEditor.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneEditor.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneEditor.java	(revision 16561)
@@ -0,0 +1,358 @@
+/** @file GS2LuceneEditor.java
+ *
+ *  Provides a wrapper to the index/document editing features of Lucene. 
+ *
+ *  This java application makes use of the existing Lucene class IndexModifier 
+ *  to access and make changes to the information stored about documents in a
+ *  Lucene database. This is an essential component of the IncrementalBuilder
+ *  PERL module, and endevours to make editing the text and metadata of 
+ *  documents without having to rebuild the entire collection a reality (in
+ *  other words, true incremental/dynamic building).
+ *
+ *  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.
+ *
+ *  Copyright (c) 2006 DL Consulting Ltd., New Zealand
+ */
+
+package org.greenstone.LuceneWrapper;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+
+
+/** Contains methods for modifying a document that has previously been indexed
+ *  into a Lucene database.
+ *  @author John Thompson, DL Consulting Ltd.
+ */
+public class GS2LuceneEditor
+{
+    /** This is the main entry point to the editor and is responsible for 
+     *  parsing the arguments and creating an instance of the editor class.
+     *
+     *  @param  args The arguments passed into the application as a string
+     *               array
+     *  @return An integer describing the exit state of the application
+     *  @throws Exception on any fatal error state
+     *
+     *  @author John Thompson, DL Consulting Ltd.
+     */
+    static public void main (String args[])
+        throws Exception
+    {
+        // Parse arguments
+        int node_id = -1;
+        String field = "";
+        String index_path = "";
+        String new_value = "";
+        String old_value = "";
+
+        for (int i = 0; i < args.length; i += 2)
+            {
+                if (args[i].equals("--index"))
+                    {
+                        index_path = args[i + 1];
+                    }
+                else if (args[i].equals("--nodeid"))
+                    {
+                        String temp = args[i + 1];
+                        node_id = Integer.parseInt(temp);
+                        temp = null; // Off to the gc with you!
+                    }
+                else if (args[i].equals("--field"))
+                    {
+                        field = args[i + 1];
+                    }
+                else if (args[i].equals("--oldvalue"))
+                    {
+                        old_value = args[i + 1];
+                    }
+                else if (args[i].equals("--newvalue"))
+                    {
+                        new_value = args[i + 1];
+                    }
+                else 
+                    {
+                        System.out.println("Error! Unknown argument: " + args[i]);
+                        GS2LuceneEditor.printUsage();
+                    }
+            }
+
+        // Check arguments
+        if(index_path.equals(""))
+            {
+                System.out.println("Error! Missing index path");
+                GS2LuceneEditor.printUsage();
+            }
+        if(field.equals(""))
+            {
+                System.out.println("Error! Missing field");
+                GS2LuceneEditor.printUsage();
+            }
+        if(node_id == -1)
+            {
+                System.out.println("Error! Missing or invalid Node ID");
+                GS2LuceneEditor.printUsage();
+            }
+        if(old_value.equals("") && new_value.equals(""))
+            {
+                System.out.println("Error! No modification requested");
+                GS2LuceneEditor.printUsage(); 
+            }
+
+
+        // Instantiate editor, and perform the edit
+        GS2LuceneEditor editor = new GS2LuceneEditor(index_path);
+        editor.editIndex(node_id, field, old_value, new_value);
+        editor.destroy();
+        editor = null;
+    }
+    /** main() **/
+
+    /** **/
+    private boolean debug = true;
+
+    /** **/
+    private GS2IndexModifier index_modifier;
+
+    /** Constructor which takes the path to the Lucene index to be edited.
+     *
+     *  @param  index_path The full path to the index directory as a String
+     *
+     *  @author John Thompson, DL Consulting Ltd.
+     */
+    public GS2LuceneEditor(String index_path)
+        throws IOException
+    {
+        Analyzer analyzer = new StandardAnalyzer();
+        // create an index in /tmp/index, overwriting an existing one:
+        index_modifier = new GS2IndexModifier(index_path, analyzer);
+    }
+    /** GS2LuceneEditor **/
+
+    /**
+     */
+    public void debug(String message)
+    {
+        if(debug)
+            {
+                System.err.println(message);
+            }
+    }
+    /** debug() **/
+
+    /** Destructor which unallocates connection to Lucene.
+     */
+    public void destroy()
+        throws IOException
+    {
+        index_modifier.close();
+        index_modifier = null;
+    }
+
+    /** Make an edit to a Lucene index.
+     *
+     *  @param  oid       The unique identifier of a Lucene document as an
+     *                    integer
+     *  @param  field     The field to be modified as a String
+     *  @param  old_value The existing value to be changed or removed as a 
+     *                    String
+     *  @param  old_value The replacement value to be changed or added as a 
+     *                    String
+     *
+     *  @author John Thompson, DL Consulting Ltd.
+     */
+    public void editIndex(int node_id, String field, String old_value, String new_value)
+        throws IOException
+    {
+        debug("GS2LuceneEditor.editIndex(" + node_id + ",'" + field + "','" + old_value + "','" + new_value + "')");
+        debug("- Initial number of documents in index: " + index_modifier.docCount());
+        // Retrieve the document requested
+        int doc_num = index_modifier.getDocNumByNodeID(node_id);
+        if (doc_num != -1)
+            {
+                debug("* Found document #" + doc_num);
+                // Retrieve the actual document
+                Document document = index_modifier.document(doc_num);
+                // Remove the document from the index before modifying
+                index_modifier.deleteDocument(doc_num);
+                debug("* Removed document from index prior to editing");
+                // Retrieve the requested fields values, and turn it into a 
+                // vector
+                debug("* Modifying the value of the field: " + field);
+                doEdit(document, field, old_value, new_value);
+
+                // We have to do a similar modification to the ZZ field
+                // too
+                debug("* Modifying the value of the field: ZZ");
+                doEdit(document, "ZZ", old_value, new_value);
+
+                // Re-index document
+                index_modifier.addDocument(document);
+                debug("* Reindexing modified document");
+            }
+        else
+            {
+                debug("- No such document!");
+                Document document = new Document();
+
+                // Retrieve the requested fields values, and turn it into a 
+                // vector
+                debug("* Adding the value to the field: " + field);
+                doEdit(document, field, old_value, new_value);
+
+                // We have to do a similar modification to the ZZ field
+                // too
+                debug("* Adding the value to the field: ZZ");
+                doEdit(document, "ZZ", old_value, new_value);
+
+                // We also have to initialize the nodeId value
+                document.add(new Field("nodeID", String.valueOf(node_id), Field.Store.YES, Field.Index.TOKENIZED));
+
+                // Re-index document
+                index_modifier.addDocument(document);
+                debug("* Indexing new document");
+            }
+
+
+    }
+    /** editIndex() **/
+
+    /**
+     */
+    protected void doEdit(Document document, String field, String old_value, String new_value)
+    {
+        if (debug)
+            {
+                debug("GS2LuceneEditor.doEdit(Document, \"" + field + "\", \"" + old_value + "\", \"" + new_value + "\")");
+            }
+
+        String values_raw[] = document.getValues(field);
+        if(values_raw != null)
+            {
+                Vector values = new Vector(Arrays.asList(values_raw));
+                // Remove all the values for this field (no other safe way to
+                // do this
+                document.removeFields(field);
+                // DEBUG
+                if (debug)
+                    {
+                        debug("- Before modification:");
+                        for(int i = 0; i < values.size(); i++)
+                            {
+                                debug("\t" + field + "[" + i + "]: " + values.get(i));
+                            }
+                    }
+                // If old_value is set, remove it from the values array
+                if(!old_value.equals(""))
+                    {
+                        // Remove all occurances of this metadata - this means
+                        // it becomes a bit dangerous to have multiple pieces
+                        // of metadata with exactly the same metadata - but
+                        // this is only for indexing purposes so its not so
+                        // bad.
+                        while(values.contains(old_value))
+                            {
+                                values.remove(old_value);
+                            }
+                    }                
+                // If new_value is set, add it to the values array
+                if(!new_value.equals("") && !values.contains(new_value))
+                    {
+                        values.add(new_value);
+                    }
+                // DEBUG
+                if(debug)
+                    {
+                        debug("- After modification:");
+                        for(int i = 0; i < values.size(); i++)
+                            {
+                                debug("\t" + field + "[" + i + "]: " + values.get(i));
+                            }
+                    }
+                // Add all the values for this field
+                for(int i = 0; i < values.size(); i++)
+                    {
+                        document.add(new Field(field, (String)values.get(i), Field.Store.YES, Field.Index.TOKENIZED));
+                    }
+                values.clear();
+                values = null;
+            }
+        // We may be adding a value to a field that current has no values
+        else if (!new_value.equals(""))
+            {
+                Vector values = new Vector();
+                values.add(new_value);
+                // DEBUG
+                if(debug)
+                    {
+                        debug("- Brand spanking new values:");
+                        for(int i = 0; i < values.size(); i++)
+                            {
+                                debug("\t" + field + "[" + i + "]: " + values.get(i));
+                            }
+                    }
+                // Add all the values for this field
+                for(int i = 0; i < values.size(); i++)
+                    {
+                        document.add(new Field(field, (String)values.get(i), Field.Store.YES, Field.Index.TOKENIZED));
+                    }
+                values.clear();
+                values = null;
+            }
+        // Can't do a removal unless something exists
+        else
+            {
+                debug("- No such field for this document: " + field);
+            }
+        values_raw = null;
+    }
+    /** doEdit() **/
+
+    /**
+     */
+    static public void printUsage()
+    {
+        System.out.println("usage: GS2LuceneEditor --index <path> --nodeid <int> --field <string>");
+        System.out.println("                      [--oldvalue <string>] [--newvalue <string>]");
+        System.out.println("");
+        System.out.println("where:");
+        System.out.println("  index    - is the full path to the directory containing the directory");
+        System.out.println("             to edit, including the level (ie didx, sidx)");
+        System.out.println("  nodeid   - the unique identifier of the document to change. This is the");
+        System.out.println("             same as the docnum in the GDBM");
+        System.out.println("  field    - the two letter code of the metadata field to edit. These can");
+        System.out.println("             found in the build.cfg file. ZZ is not a valid field as it");
+        System.out.println("             is handled as a special case");
+        System.out.println("  oldvalue - the current value of the metadata field if it is to be");
+        System.out.println("             replaced or removed");
+        System.out.println("  newvalue - the new value for the metadata field if it is to be replaced");
+        System.out.println("             or added");
+        System.out.println("");
+        System.exit(0);
+    }
+    /** printUsage() **/
+
+}
+/** class GS2LuceneEditor **/
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneIndexer.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneIndexer.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneIndexer.java	(revision 16561)
@@ -0,0 +1,401 @@
+/**********************************************************************
+ *
+ * GS2LuceneIndexer.java 
+ *
+ * Copyright 2004 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.
+ *
+ *********************************************************************/
+
+package org.greenstone.LuceneWrapper;
+
+
+import java.io.*;
+import java.util.Vector;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+import java.util.Stack;
+import java.io.FileInputStream;
+import java.io.File;
+import java.io.StringReader;
+import java.net.URL;
+
+
+/**
+ * class for indexing XML generated by lucenebuildproc.pm
+  */
+
+public class GS2LuceneIndexer {
+
+    public static void main (String args[]) throws Exception 
+    { 
+
+	int verbosity = 1;
+	// Default is to edit the existing index
+	boolean create_new_index = false;
+
+	Vector filtered_args = new Vector();
+
+	int argc = args.length;
+	int i = 0;
+	while (i<argc) {
+	    if (args[i].startsWith("-")) {
+
+		// -removeold causes the existing index to be overwritten
+		if (args[i].equals("-removeold")) {
+		    create_new_index = true;
+		}
+
+		// -verbosity [num]
+		else if (args[i].equals("-verbosity")) {
+		    i++;
+		    if (i<argc) {
+			verbosity = Integer.parseInt(args[i]);
+		    }
+		}
+		else {
+		    System.out.println("Unrecognised option: " + args[i]);
+		}
+	    }
+	    else {
+		filtered_args.add((Object)args[i]);
+	    }
+	    i++;
+	}
+
+	if (filtered_args.size() != 3) {
+	    System.out.println("Usage: java GS2LuceneIndexer [-removeold|-verbosity [num]] doc-tag-level building_dir index");
+	    return;
+	}
+ 
+	String doc_tag_level     = (String)filtered_args.get(0);
+	String building_dirname  = (String)filtered_args.get(1);
+	String index_dirname     = (String)filtered_args.get(2);
+
+	String import_dirname = building_dirname + File.separator + "text";
+
+	File import_dir   = new File(import_dirname);
+	File building_dir = new File(building_dirname);
+
+	if (!import_dir.exists()) {
+	    System.out.println("Couldn't find import directory: "+import_dirname);
+	    return;
+	}
+
+	File idx_dir = new File(building_dir.getPath()+File.separator+index_dirname+File.separator);
+	idx_dir.mkdir();
+	
+	// Set up indexer
+	Indexer indexer = new Indexer(doc_tag_level, idx_dir, create_new_index);
+
+	// Read from stdin the files to process
+	try {
+	    InputStreamReader isr = new InputStreamReader(System.in, "UTF-8");
+	    BufferedReader brin = new BufferedReader(isr);
+
+	    StringBuffer xml_text = new StringBuffer(1024);
+	    String line = null;
+	    while ((line = brin.readLine()) != null) {
+		xml_text.append(line);
+		if (line.startsWith("</Doc>")) {
+		    indexer.index(xml_text.toString());
+		    xml_text = new StringBuffer(1024);
+		}
+	    }
+
+	    brin.close();
+	    isr.close();
+
+	} catch (IOException e) {
+	    System.err.println("Error: unable to read from stdin");
+	    e.printStackTrace();
+	}
+
+	indexer.finish();
+    }
+
+
+    static public class Indexer extends DefaultHandler 
+    {
+	IndexWriter writer_   = null;
+	SAXParser sax_parser_ = null;
+	String doc_tag_level_ = null;
+
+	Stack stack_ = null;
+	String path_ = "";
+
+	Document current_doc_ = null;
+	String current_node_ = "";
+	String current_doc_oid_ = "";
+	String indexable_current_node_ = "";
+	String current_contents_ = "";
+
+	protected String file_id_ = null;
+
+	/** pass in true if want to create a new index, false if want to use the existing one */
+	public Indexer (String doc_tag_level, File index_dir, boolean create) 
+	{
+	    doc_tag_level_ = doc_tag_level;
+
+	    try {
+		stack_ = new Stack();
+		SAXParserFactory sax_factory = SAXParserFactory.newInstance();
+		sax_parser_ = sax_factory.newSAXParser();
+
+		XMLReader reader = sax_parser_.getXMLReader(); 
+		reader.setFeature("http://xml.org/sax/features/validation", false);
+
+		writer_ = new IndexWriter(index_dir.getPath(), new StandardAnalyzer(), create);
+		// by default, will only index 10,000 words per document
+		// Can throw out_of_memory errors
+		writer_.setMaxFieldLength(Integer.MAX_VALUE);
+		if (create) {
+		    writer_.optimize();
+		}
+	    }
+	    catch (Exception e) {
+		// We need to know if creating/opening the index fails
+		e.printStackTrace();
+	    }
+	}
+
+	/** index one document */
+	public void index (String file_id, File file) 
+	{
+	    file_id_ = file_id;
+	    path_ = "";
+	    String base_path = file.getPath();
+	    base_path = base_path.substring(0, base_path.lastIndexOf(File.separatorChar));
+
+	    try {	    
+		sax_parser_.parse(new InputSource(new FileInputStream(file)), this);
+	    }
+	    catch (Exception e) {
+		println("parse error:");
+		e.printStackTrace();
+	    }
+	}
+
+	/** index one document stored as string*/
+	public void index (String xml_text) 
+	{
+	    file_id_ = "<xml doc on stdin>";
+	    path_ = "";
+
+	    try {
+		sax_parser_.parse(new InputSource(new StringReader(xml_text)), this);
+	    }
+	    catch (Exception e) {
+		println("parse error:");
+		e.printStackTrace();
+	    }
+	}
+    
+	public void finish() 
+	{
+	    /** optimise the index */
+	    try {
+		writer_.optimize();
+		writer_.close();
+	    } 
+	    catch (Exception e) {
+	    }
+	}
+
+	protected void print(String s) 
+	{ 
+	    System.out.print(s); 
+	}
+
+	protected void println(String s) 
+	{ 
+	    System.out.println(s); 
+	}
+
+	public void startDocument() throws SAXException 
+	{
+	    println("Starting to index " + file_id_);
+	    print("[");
+	}
+
+	public void endDocument() throws SAXException 
+	{
+	    println("]");
+	    println("... indexing finished.");
+	}
+
+	public void startElement(String uri, String localName, String qName, Attributes atts) 
+	    throws SAXException 
+	{
+	    path_ = appendPathLink(path_, qName, atts);
+
+	    if (qName.equals(doc_tag_level_)) {
+		pushOnStack(); // start new doc
+		current_node_ = qName;
+
+		String node_id = atts.getValue("gs2:id");
+		print(" " + qName + ": " + node_id );
+		current_doc_.add(new Field("nodeID", node_id, Field.Store.YES, Field.Index.UN_TOKENIZED));
+
+		current_doc_oid_ = atts.getValue("gs2:docOID");
+		current_doc_.add(new Field("docOID", current_doc_oid_, Field.Store.YES, Field.Index.UN_TOKENIZED));
+	    }
+
+	    if (isIndexable(atts)) {
+		indexable_current_node_ = qName;	
+	    }
+	    else {
+		indexable_current_node_ = "";
+	    }
+	}
+
+	public static boolean isIndexable(Attributes atts) 
+	{
+	    boolean is_indexable = false;
+
+	    String index = atts.getValue("index");
+	    if (index!=null) {
+		if (index.equals("1")) {
+		    is_indexable = true;
+		}
+	    }
+	    return is_indexable;
+	}
+
+	public void endElement(String uri, String localName, String qName) throws SAXException 
+	{
+	    if (qName.equals(indexable_current_node_))
+		{
+		    current_doc_.add(new Field(qName, current_contents_, Field.Store.NO, Field.Index.TOKENIZED, Field.TermVector.YES));
+		    // We only need the term vector for the TX field
+		    if (!qName.equals("TX"))
+			{
+			    current_doc_.add(new Field("by" + qName, current_contents_, Field.Store.NO, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
+			}
+
+		    current_contents_ = "";
+		}
+
+	    if (qName.equals(doc_tag_level_)) {
+		try {
+		    writer_.updateDocument(new Term("docOID", current_doc_oid_), current_doc_);
+		} 
+		catch (java.io.IOException e) {
+		    e.printStackTrace();
+		}
+		popOffStack(); // end document
+	    }
+	
+	    path_ = removePathLink(path_);
+	}
+
+	public void characters(char ch[], int start, int length) throws SAXException 
+	{
+	    String data = new String(ch, start, length).trim();
+	    if (data.length() > 0 ) {
+		current_contents_ += data;
+	    }
+	}
+    
+	protected String appendPathLink(String path, String qName, Attributes atts) 
+	{
+
+	    path = path + "/"+qName;
+	    if (atts.getLength()>0) {
+		String id = atts.getValue("gs2:id");
+		if (id != null) {
+		    path +=  "[@gs2:id='"+id+"']";
+		}
+		else {
+		    id = atts.getValue("gs3:id");
+		    if (id != null) {
+			path +=  "[@gs3:id='"+id+"']";
+		    }
+		}
+	    }
+	    return path;
+	}
+
+	protected String removePathLink(String path) 
+	{
+
+	    int i=path.lastIndexOf('/');
+	    if (i==-1) {
+		path="";
+	    } else {
+		path = path.substring(0, i);
+	    }
+	    return path;
+	}
+
+
+	/** these are what we save on the stack */
+	private class MyDocument 
+	{
+	    public Document doc = null;
+	    public String contents = null;
+	    public String tagname = "";
+	
+	}
+
+
+	protected void pushOnStack() 
+	{
+	    if (current_doc_ != null) {
+		MyDocument save = new MyDocument();
+		save.doc = current_doc_;
+		save.contents = current_contents_;
+		save.tagname = current_node_;
+		stack_.push(save);
+	    }
+	    current_doc_ = new Document();
+	    current_contents_ = "";
+	    current_node_ = "";
+	}
+
+	protected void popOffStack() 
+	{
+	    if (!stack_.empty()) {
+		MyDocument saved = (MyDocument)stack_.pop();
+		current_doc_ = saved.doc;
+		current_contents_ = saved.contents;
+		current_node_ = saved.tagname;
+	    } else {
+		current_doc_ = new Document();
+		current_contents_ = "";
+		current_node_ = "";
+	    }
+	}
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneQuery.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneQuery.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/GS2LuceneQuery.java	(revision 16561)
@@ -0,0 +1,656 @@
+/**********************************************************************
+ *
+ * GS2LuceneQuery.java 
+ *
+ * Copyright 2004 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.
+ *
+ *********************************************************************/
+package org.greenstone.LuceneWrapper;
+
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.BooleanQuery.TooManyClauses;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.Hit;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.RangeFilter;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopFieldDocs;
+
+
+public class GS2LuceneQuery
+{
+
+
+    static private String TEXTFIELD = "TX";
+
+    // Use the standard set of English stop words by default
+    static private String[] stop_words = StandardAnalyzer.STOP_WORDS;
+
+    private String full_indexdir="";
+    private String default_conjunction_operator = "OR";
+    private String fuzziness = null;
+    private String sort_field = null;
+    private Sort sorter=new Sort();
+    private String filter_string = null;
+    private Filter filter = null;
+    private int start_results=1;
+    private int end_results=Integer.MAX_VALUE;
+
+    private QueryParser query_parser = null;
+    private QueryParser query_parser_no_stop_words = null;
+    private Searcher searcher = null;
+    private IndexReader reader = null;
+
+    static private PrintWriter utf8out = null;
+
+    static
+    {
+	try {
+	    OutputStreamWriter osw = new OutputStreamWriter(System.out, "UTF-8");
+	    utf8out = new PrintWriter(osw, true);
+	}
+        catch (UnsupportedEncodingException e) { 
+	    System.out.println(e); 
+	}
+    }
+
+    
+    public GS2LuceneQuery() {
+
+	// Create one query parser with the standard set of stop words, and one with none
+
+	query_parser = new QueryParser(TEXTFIELD, new StandardAnalyzer(stop_words));
+    	query_parser_no_stop_words = new QueryParser(TEXTFIELD, new StandardAnalyzer(new String[] { }));
+    }
+    
+    
+    public boolean initialise() {
+
+    	if (full_indexdir==null || full_indexdir.length()==-1){
+	    utf8out.println("Index directory is not indicated ");
+	    utf8out.flush();
+	    return false;
+    	}
+        try {
+    	    searcher = new IndexSearcher(full_indexdir);
+    	    reader = ((IndexSearcher) searcher).getIndexReader();
+	    
+	}
+	catch (IOException exception) {
+    	    exception.printStackTrace();
+	    return false;
+    	}
+	return true;
+
+    }
+    
+    public LuceneQueryResult runQuery(String query_string) {
+	
+	if (query_string == null || query_string.equals("")) {
+	    utf8out.println("The query word is not indicated ");
+	    utf8out.flush();
+	    return null;
+	}
+
+ 	LuceneQueryResult lucene_query_result=new LuceneQueryResult();
+	lucene_query_result.clear();
+	    
+	try {    	        
+	    Query query_including_stop_words = query_parser_no_stop_words.parse(query_string);
+	    query_including_stop_words = query_including_stop_words.rewrite(reader);
+		
+	    Query query = parseQuery(reader, query_parser, query_string, fuzziness);
+	    query = query.rewrite(reader);
+		
+	    // Get the list of expanded query terms and their frequencies 
+	    // num docs matching, and total frequency	    
+	    HashSet terms = new HashSet();
+	    query.extractTerms(terms);
+	    
+	    Iterator iter = terms.iterator();
+	    while (iter.hasNext()) {
+		    
+		Term term = (Term) iter.next();
+		
+		// Get the term frequency over all the documents
+		TermDocs term_docs = reader.termDocs(term);
+		int term_freq = term_docs.freq();
+		int match_docs = 0;
+		if (term_freq != 0) match_docs++;
+		while (term_docs.next()) {
+		    term_freq += term_docs.freq();
+		    if (term_docs.freq()!= 0) {
+			match_docs++;
+		    }
+		}
+
+		// Create a term 
+		lucene_query_result.addTerm(term.text(), term.field(), match_docs, term_freq);
+	    }
+	
+	    // Get the list of stop words removed from the query
+	    HashSet terms_including_stop_words = new HashSet();
+	    query_including_stop_words.extractTerms(terms_including_stop_words);
+	    Iterator terms_including_stop_words_iter = terms_including_stop_words.iterator();
+	    while (terms_including_stop_words_iter.hasNext()) {
+		Term term = (Term) terms_including_stop_words_iter.next();
+		if (!terms.contains(term)) {
+		    lucene_query_result.addStopWord(term.text());
+		}
+	    }
+	    
+	    // do the query
+	    // Simple case for getting all the matching documents
+	    if (end_results == Integer.MAX_VALUE) {
+		// Perform the query (filter and sorter may be null)
+		Hits hits = searcher.search(query, filter, sorter);
+		lucene_query_result.setTotalDocs(hits.length());
+
+		// Output the matching documents
+		lucene_query_result.setStartResults(start_results);
+		lucene_query_result.setEndResults(hits.length());
+
+		for (int i = start_results; i <= hits.length(); i++) {
+		    Document doc = hits.doc(i - 1);
+		    lucene_query_result.addDoc(doc.get("nodeID").trim(), hits.score(i-1));
+		}
+	    }
+
+	    // Slightly more complicated case for returning a subset of the matching documents
+	    else {
+		// Perform the query (filter may be null)
+		TopFieldDocs hits = searcher.search(query, filter, end_results, sorter);
+		lucene_query_result.setTotalDocs(hits.totalHits);
+		
+		lucene_query_result.setStartResults(start_results);
+		lucene_query_result.setEndResults(end_results < hits.scoreDocs.length ? end_results: hits.scoreDocs.length);
+
+		// Output the matching documents
+		for (int i = start_results; (i <= hits.scoreDocs.length && i <= end_results); i++) {
+		    Document doc = reader.document(hits.scoreDocs[i - 1].doc);
+		    lucene_query_result.addDoc(doc.get("nodeID").trim(), hits.scoreDocs[i-1].score);
+		}
+	    }
+	}
+	
+	catch (ParseException parse_exception) {
+	    lucene_query_result.setError(LuceneQueryResult.PARSE_ERROR);
+	}
+	catch (TooManyClauses too_many_clauses_exception) {
+	    lucene_query_result.setError(LuceneQueryResult.TOO_MANY_CLAUSES_ERROR);
+	}
+	catch (IOException exception) {
+	    lucene_query_result.setError(LuceneQueryResult.IO_ERROR);
+	    exception.printStackTrace();
+	}
+	catch (Exception exception) {
+	    lucene_query_result.setError(LuceneQueryResult.OTHER_ERROR);
+	    exception.printStackTrace();
+	}
+	return lucene_query_result;
+    }
+
+    public void setDefaultConjunctionOperator(String default_conjunction_operator) {
+	this.default_conjunction_operator = default_conjunction_operator.toUpperCase();
+	if (default_conjunction_operator.equals("AND")) {
+	    query_parser.setDefaultOperator(query_parser.AND_OPERATOR);
+	    query_parser_no_stop_words.setDefaultOperator(query_parser.AND_OPERATOR);
+	} else { // default is OR
+	    query_parser.setDefaultOperator(query_parser.OR_OPERATOR);
+	    query_parser_no_stop_words.setDefaultOperator(query_parser.OR_OPERATOR);
+	}
+    }
+    
+    public String getDefaultConjunctionOperator() {
+	return this.default_conjunction_operator;
+    }
+    
+    public void setEndResults(int end_results) {
+	this.end_results = end_results;
+    }
+    public int getEndResults() {
+	return this.end_results;
+    }
+        
+    public void setFilterString(String filter_string) {
+	this.filter_string = filter_string;
+	this.filter = parseFilterString(filter_string);
+    }
+    public String getFilterString() {
+	return this.filter_string ;
+    }
+    
+    public Filter getFilter() {
+	return this.filter;
+    }
+
+    public void setIndexDir(String full_indexdir) {
+	this.full_indexdir = full_indexdir;
+    }
+    
+    public void setFuzziness(String fuzziness) {
+	this.fuzziness = fuzziness;
+    }
+    public String getFuzziness() {
+	return this.fuzziness;
+    }
+    
+    public void setSortField(String sort_field) {
+	this.sort_field = sort_field;
+	if (sort_field == null) {
+	    this.sorter = new Sort();
+	} else {
+	    this.sorter = new Sort(sort_field);
+	}
+    }
+    public String getSortField() {
+	return this.sort_field;
+    }
+        
+    public void setStartResults(int start_results) {
+	if (start_results < 1) {
+	    start_results = 1;
+	}
+	this.start_results = start_results;
+    }
+    public int getStartResults() {
+	return this.start_results;
+    }
+        
+    public void cleanUp() {
+	try {
+	    if (searcher != null) {
+		searcher.close();
+	    }
+	} catch (IOException exception) {
+	    exception.printStackTrace();
+	}
+    }
+
+    private Query parseQuery(IndexReader reader, QueryParser query_parser, String query_string, String fuzziness)
+	throws java.io.IOException, org.apache.lucene.queryParser.ParseException
+    {
+	// Split query string into the search terms and the filter terms
+	// * The first +(...) term contains the search terms so count
+	//   up '(' and stop when we finish matching ')'
+	int offset = 0;
+	int paren_count = 0;
+	boolean seen_paren = false;
+	while (offset < query_string.length() && (!seen_paren || paren_count > 0)) {
+	    if (query_string.charAt(offset) == '(') {
+		paren_count++;
+		seen_paren = true;
+	    }
+	    if (query_string.charAt(offset) == ')') {
+		paren_count--;
+	    }
+	    offset++;
+	}
+	String query_prefix = query_string.substring(0, offset);
+	String query_suffix = query_string.substring(offset);
+	
+	///ystem.err.println("Prefix: " + query_prefix);
+	///ystem.err.println("Suffix: " + query_suffix);
+	
+	Query query = query_parser.parse(query_prefix);
+	query = query.rewrite(reader);
+	
+	// If this is a fuzzy search, then we need to add the fuzzy
+	// flag to each of the query terms
+	if (fuzziness != null && query.toString().length() > 0) {
+	    
+	    // Revert the query to a string
+	    System.err.println("Rewritten query: " + query.toString());
+	    // Search through the string for TX:<term> query terms
+	    // and append the ~ operator. Note that this search will
+	    // not change phrase searches (TX:"<term> <term>") as
+	    // fuzzy searching is not possible for these entries.
+	    // Yahoo! Time for a state machine!
+	    StringBuffer mutable_query_string = new StringBuffer(query.toString());
+	    int o = 0; // Offset
+	    // 0 = BASE, 1 = SEEN_T, 2 = SEEN_TX, 3 = SEEN_TX:
+	    int s = 0; // State
+	    while(o < mutable_query_string.length()) {
+		char c = mutable_query_string.charAt(o);
+		if (s == 0 && c == TEXTFIELD.charAt(0)) {
+		    ///ystem.err.println("Found T!");
+		    s = 1;
+		}
+		else if (s == 1) {
+		    if (c == TEXTFIELD.charAt(1)) {
+			///ystem.err.println("Found X!");
+			s = 2;
+		    }
+		    else {
+			s = 0; // Reset
+		    }
+		}
+		else if (s == 2) {
+		    if (c == ':') {
+			///ystem.err.println("Found TX:!");
+			s = 3;
+		    }
+		    else {
+			s = 0; // Reset
+		    }
+		}
+		else if (s == 3) {
+		    // Don't process phrases
+		    if (c == '"') {
+			///ystem.err.println("Stupid phrase...");
+			s = 0; // Reset
+		    }
+		    // Found the end of the term... add the
+		    // fuzzy search indicator
+		    // Nor outside the scope of parentheses
+		    else if (Character.isWhitespace(c) || c == ')') {
+			///ystem.err.println("Yahoo! Found fuzzy term.");
+			mutable_query_string.insert(o, '~' + fuzziness);
+			o++;
+			s = 0; // Reset
+		    }
+		}
+		o++;
+	    }
+	    // If we were in the state of looking for the end of a
+	    // term - then we just found it!
+	    if (s == 3) {
+		    
+		mutable_query_string.append('~' + fuzziness);
+	    }
+	    // Reparse the query
+	    ///ystem.err.println("Fuzzy query: " + mutable_query_string.toString() + query_suffix);
+	    query = query_parser.parse(mutable_query_string.toString() + query_suffix);
+	}
+	else {
+	    query = query_parser.parse(query_prefix + query_suffix);
+	}
+	
+	return query;
+    }
+
+    private Filter parseFilterString(String filter_string)
+    {
+	Filter result = null;
+	Pattern pattern = Pattern.compile("\\s*\\+(\\w+)\\:([\\{\\[])(\\d+)\\s+TO\\s+(\\d+)([\\}\\]])\\s*");
+	Matcher matcher = pattern.matcher(filter_string);
+	if (matcher.matches()) {
+	    String field_name = matcher.group(1);
+	    boolean include_lower = matcher.group(2).equals("[");
+	    String lower_term = matcher.group(3);
+	    String upper_term = matcher.group(4);
+	    boolean include_upper = matcher.group(5).equals("]");
+	    result = new RangeFilter(field_name, lower_term, upper_term, include_lower, include_upper);
+	}
+	else {
+	    System.err.println("Error: Could not understand filter string \"" + filter_string + "\"");
+	}
+	return result;
+    }
+
+
+    protected void finalize() throws Throwable 
+    {
+	try {
+	    utf8out.flush(); 
+	} finally {
+	    super.finalize();
+	}
+    }
+
+    
+    /** command line program and auxiliary methods */
+
+    // Fairly self-explanatory I should hope
+    static private boolean query_result_caching_enabled = false;
+
+
+    static public void main (String args[])
+    {
+
+
+	if (args.length == 0) {
+	    System.out.println("Usage: GS2LuceneQuery <index directory> [-fuzziness value] [-filter filter_string] [-sort sort_field] [-dco AND|OR] [-startresults number -endresults number] [query]");
+	    return;
+	}
+
+	try {
+	    String index_directory = args[0];
+	    
+	    GS2LuceneQuery queryer = new GS2LuceneQuery();
+	    queryer.setIndexDir(index_directory);
+
+	    // Prepare the index cache directory, if query result caching is enabled
+	    if (query_result_caching_enabled) {
+		// Make the index cache directory if it doesn't already exist
+		File index_cache_directory = new File(index_directory, "cache");
+		if (!index_cache_directory.exists()) {
+		    index_cache_directory.mkdir();
+		}
+
+		// Disable caching if the index cache directory isn't available
+		if (!index_cache_directory.exists() || !index_cache_directory.isDirectory()) {
+		    query_result_caching_enabled = false;
+		}
+	    }
+
+	    String query_string = null;
+
+	    // Parse the command-line arguments
+            for (int i = 1; i < args.length; i++) {
+		if (args[i].equals("-sort")) {
+		    i++;
+		    queryer.setSortField(args[i]);
+		}
+		else if (args[i].equals("-filter")) {
+		    i++;
+		    queryer.setFilterString(args[i]);
+		}
+		else if (args[i].equals("-dco")) {
+		    i++;
+		    queryer.setDefaultConjunctionOperator(args[i]);
+		}
+		else if (args[i].equals("-fuzziness")) {
+		    i++;
+		    queryer.setFuzziness(args[i]);
+		}
+		else if (args[i].equals("-startresults")) {
+		    i++;
+		    if (args[i].matches("\\d+")) {
+			queryer.setStartResults(Integer.parseInt(args[i]));
+		    }
+		}
+		else if (args[i].equals("-endresults")) {
+		    i++;
+		    if (args[i].matches("\\d+")) {
+			queryer.setEndResults(Integer.parseInt(args[i]));
+		    }
+		}
+		else {
+		    query_string = args[i];
+		}
+	    }
+	    
+	    if (!queryer.initialise()) {
+		return;
+	    }
+	    
+	    // The query string has been specified as a command-line argument
+	    if (query_string != null) {
+		runQueryCaching(index_directory, queryer, query_string);
+	    }
+
+	    // Read queries from STDIN
+	    else {
+		BufferedReader in = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
+		while (true) {
+		    // Read the query from STDIN
+		    query_string = in.readLine();
+		    if (query_string == null || query_string.length() == -1) {
+			break;
+		    }
+
+		    runQueryCaching(index_directory, queryer, query_string);
+		    
+		}
+	    }
+	    queryer.cleanUp();
+	}
+ 	catch (IOException exception) {
+ 	    exception.printStackTrace();
+ 	}
+    }
+
+    private static void runQueryCaching(String index_directory, GS2LuceneQuery queryer, String query_string) 
+	throws IOException
+    {
+	StringBuffer query_results_xml = new StringBuffer();
+
+	// Check if this query result has been cached from a previous search (if it's enabled)
+	File query_result_cache_file = null;
+	if (query_result_caching_enabled) {
+	    // Generate the cache file name from the query options
+	    String query_result_cache_file_name = query_string + "-";
+	    String fuzziness = queryer.getFuzziness();
+	    query_result_cache_file_name += ((fuzziness != null) ? fuzziness : "") + "-";
+	    String filter_string = queryer.getFilterString();
+	    query_result_cache_file_name += ((filter_string != null) ? filter_string : "") + "-";
+	    String sort_string = queryer.getSortField();
+	    query_result_cache_file_name += ((sort_string != null) ? sort_string : "") + "-";
+	    String default_conjunction_operator = queryer.getDefaultConjunctionOperator();
+	    query_result_cache_file_name += default_conjunction_operator + "-";
+	    int start_results = queryer.getStartResults();
+	    int end_results = queryer.getEndResults();
+	    query_result_cache_file_name += start_results + "-" + end_results;
+	    query_result_cache_file_name = fileSafe(query_result_cache_file_name);
+
+	    // If the query result cache file exists, just return its contents and we're done
+	    File index_cache_directory = new File(index_directory, "cache");
+	    query_result_cache_file = new File(index_cache_directory, query_result_cache_file_name);
+	    if (query_result_cache_file.exists() && query_result_cache_file.isFile()) {
+		FileInputStream fis = new FileInputStream(query_result_cache_file);
+		InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
+		BufferedReader buffered_reader = new BufferedReader(isr);
+		String line = "";
+		while ((line = buffered_reader.readLine()) != null) {
+		    query_results_xml.append(line + "\n");
+		}
+		String query_results_xml_string = query_results_xml.toString();
+		query_results_xml_string = query_results_xml_string.replaceFirst("cached=\"false\"", "cached=\"true\"");
+
+		utf8out.print(query_results_xml_string);
+		utf8out.flush();
+
+		return;
+	    }
+	}
+	
+	// not cached
+	query_results_xml.append("<ResultSet cached=\"false\">\n");
+	query_results_xml.append("<QueryString>" + LuceneQueryResult.xmlSafe(query_string) + "</QueryString>\n");
+	Filter filter = queryer.getFilter();
+	if (filter != null) {
+	    query_results_xml.append("<FilterString>" + filter.toString() + "</FilterString>\n");
+	}
+	
+	LuceneQueryResult query_result = queryer.runQuery(query_string);
+	if (query_result == null) {
+	    System.err.println("Couldn't run the query");
+	    return;
+	}
+	
+	if (query_result.getError() != LuceneQueryResult.NO_ERROR) {
+	    query_results_xml.append("<Error type=\""+query_result.getErrorString()+"\" />\n");
+	} else {
+	    query_results_xml.append(query_result.getXMLString());
+	}
+	query_results_xml.append("</ResultSet>\n");
+
+	utf8out.print(query_results_xml);
+	utf8out.flush();
+
+	try {
+	    /*
+	    Writer output = null;
+	    File file = new File("/tmp/lucenequery.txt");
+	    output = new BufferedWriter(new FileWriter(file,"UTF-8"));
+	    output.write(query_results_xml.toString());
+	    output.close();
+	    */
+
+	    FileOutputStream fos = new FileOutputStream("/tmp/lucenequery.txt");
+	   
+	    OutputStreamWriter osw2 = new OutputStreamWriter(fos, "UTF-8");
+
+	    osw2.write("Query string = " + query_string + "\n");
+	    osw2.write(query_results_xml.toString());
+	    osw2.close();
+	}
+	catch (Exception e) {
+	    e.printStackTrace();
+	}
+
+
+
+	// Cache this query result, if desired
+	if (query_result_caching_enabled) {
+	    FileWriter query_result_cache_file_writer = new FileWriter(query_result_cache_file);
+	    query_result_cache_file_writer.write(query_results_xml.toString());
+	    query_result_cache_file_writer.close();
+	}
+    }
+    
+    private static String fileSafe(String text)
+    {
+	StringBuffer file_safe_text = new StringBuffer();
+	for (int i = 0; i < text.length(); i++) {
+	    char character = text.charAt(i);
+	    if ((character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || (character >= '0' && character <= '9') || character == '-') {
+		file_safe_text.append(character);
+	    }
+	    else {
+		file_safe_text.append('%');
+		file_safe_text.append((int) character);
+	    }
+	}
+	return file_safe_text.toString();
+    }
+
+    
+}
+
+
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/LuceneQueryResult.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/LuceneQueryResult.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/src/org/greenstone/LuceneWrapper/LuceneQueryResult.java	(revision 16561)
@@ -0,0 +1,256 @@
+/**********************************************************************
+ *
+ * LuceneQueryResult.java 
+ *
+ * Copyright 2007 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.
+ *
+ *********************************************************************/
+
+package org.greenstone.LuceneWrapper;
+
+import java.util.Vector;
+
+/** a QueryResult class for a lucene search
+ *
+ */
+public class LuceneQueryResult {
+    
+    public static final int NO_ERROR = 0;
+    public static final int PARSE_ERROR = 1;
+    public static final int TOO_MANY_CLAUSES_ERROR = 2;
+    public static final int IO_ERROR = 3;
+    public static final int OTHER_ERROR = 4;
+    
+    /** the list of DocInfo */
+    protected Vector docs_=null;
+    /** the list of TermInfo */
+    protected Vector terms_=null;
+    /** the list of stopwords found in the query */
+    protected Vector stopwords_ = null;
+    /** the total number of docs found - not necessarily the size of docs_*/
+    protected int total_num_docs_=0;
+    /** the start result number if we are retrieving only a portion of the results */
+    protected int start_results_ = 0;
+    /** the end result number if we are retrieving only a portion of the results */
+    protected int end_results_ = 0;
+    /** whether an error has occurred and what kind it is*/
+    protected int error_ = NO_ERROR;
+
+    LuceneQueryResult() {
+	docs_ = new Vector();
+	terms_ = new Vector();
+	stopwords_ = new Vector();
+    }
+    
+    /** clear the info from the last query - should be called before setting any new docs/terms */
+    public void clear() {
+	total_num_docs_=0;
+	docs_.clear();
+	terms_.clear();
+	stopwords_.clear();
+	error_ = NO_ERROR;
+    }
+
+    /** returns the result as a String - useful for printing out results */
+    public String toString() {
+	
+	String result = "";
+	result += "docs (ranks): ";
+	for (int i=0; i<docs_.size(); i++) {
+	    result += ((DocInfo)docs_.elementAt(i)).toString()+", ";
+	}
+	result += "\nterms: ";
+	for (int i=0; i<terms_.size(); i++) {
+	    result += ((TermInfo)terms_.elementAt(i)).toString()+", ";
+	}
+	result += "\nactual number of docs found = "+total_num_docs_;
+	
+	return result;
+    }
+    /** a shorter representation - just terms and total docs - not the 
+	individual docnums and ranks */
+    public String toShortString() {
+	String result = "";
+	result += "\nterms: ";
+	for (int i=0; i<terms_.size(); i++) {
+	    result += ((TermInfo)terms_.elementAt(i)).toString()+", ";
+	}
+	result += "\nactual number of docs found = "+total_num_docs_;
+	return result;
+    }
+    
+    public void setTotalDocs(int num) {
+	total_num_docs_=num;
+    }
+    
+    public void setStartResults(int start) {
+	start_results_ = start;
+    }
+
+    public void setEndResults(int end) {
+	end_results_ = end;
+    }
+
+    public void addDoc(String id, float rank)
+    {
+	docs_.add(new DocInfo(id, rank));
+    }
+    
+    public void addTerm(String term, String field, int match, int freq) {
+	TermInfo ti = new TermInfo();
+	ti.term_=term;
+	ti.field_=field;
+	ti.match_docs_=match;
+	ti.term_freq_=freq;
+	terms_.add(ti);
+    }
+    public void addStopWord(String stopword) {
+	stopwords_.add(stopword);
+    }
+    public Vector getDocs() {
+	return docs_;
+    }
+    
+    public int getError() {
+	return error_;
+    }
+    
+    public String getErrorString() {
+	if (error_ == PARSE_ERROR) {
+	    return "PARSE_EXCEPTION";
+	}
+	if (error_ == TOO_MANY_CLAUSES_ERROR) {
+	    return "TOO_MANY_CLAUSES";
+	}
+	if (error_ == IO_ERROR) {
+	    return "IO_ERROR";
+	}
+	if (error_ == NO_ERROR) {
+	    return "NO_ERROR";
+	}
+	return "UNKNOWN";
+    }
+
+    public Vector getTerms() {
+	return terms_;
+    }
+    
+    public Vector getStopWords() {
+	return stopwords_;
+    }
+    public int getTotalDocs() {
+	return total_num_docs_;
+    }
+    
+    public void setError(int error) {
+	error_ = error;
+    }
+    
+    public String getXMLString() {
+	StringBuffer buffer = new StringBuffer();
+
+	// terms
+	buffer.append("<QueryTermsInfo num=\"" + terms_.size() + "\"/>\n");
+	for (int i=0; i<terms_.size(); i++) {
+	    buffer.append(((TermInfo)terms_.elementAt(i)).toXMLString()+"\n");
+	}
+
+	// stopwords
+	for (int i=0; i<stopwords_.size(); i++) {
+	    buffer.append("<StopWord value=\"" + (String)stopwords_.elementAt(i)+"\" />\n");
+	}
+	
+	// results
+	buffer.append("<MatchingDocsInfo num=\"" + total_num_docs_ + "\"/>\n");
+	buffer.append("<StartResults num=\"" + start_results_ + "\"/>\n");
+	buffer.append("<EndResults num=\"" + end_results_ + "\"/>\n");
+	
+	for (int i=0; i< docs_.size(); i++) {
+	    buffer.append(((DocInfo)docs_.elementAt(i)).toXMLString()+"\n");
+	}
+
+	return buffer.toString();
+    }
+
+ 
+    public class TermInfo {
+	
+	/** the term itself */
+	public String term_=null;
+	/** the field for which this term was queried */
+	public String field_=null;
+	/** the number of documents containing this term */
+	public int match_docs_=0;
+	/** overall term freq for this term */
+	public int term_freq_=0;
+	
+	public TermInfo() {
+	}
+	
+	/** output the class as a string */
+	public String toString() {
+	    String result="";
+	    result +="<"+field_+">\""+term_+" docs("+match_docs_;
+	    result +=")freq("+term_freq_+")";
+	    return result;
+	}
+
+	/** output as an XML element */
+	public String toXMLString() {
+	    return "<Term value=\"" + xmlSafe(term_) + "\" field=\"" + field_ + "\" freq=\"" + term_freq_ + "\" />";
+	}
+    }
+
+
+    public class DocInfo
+    {
+	public String id_ = "";
+	public float rank_ = 0;
+
+	public DocInfo (String id, float rank)
+	{
+	    id_ = id;
+	    rank_ = rank;
+	}
+
+	public String toString()
+	{
+	    return "" + id_ + " (" + rank_ + ")";
+	}
+
+	public String toXMLString()
+	{
+	    return "<Match id=\"" + id_ + "\" rank=\"" + rank_ + "\" />";
+	}
+    }
+
+
+    // where should this go???
+    public static String xmlSafe(String text) {
+	text = text.replaceAll("&","&amp;amp;");
+	text = text.replaceAll("<","&amp;lt;");
+	text = text.replaceAll(">","&amp;gt;");
+	text = text.replaceAll("'","&amp;#039;");
+	text = text.replaceAll("\\\"","&amp;quot;");
+	return text;
+    }
+ 
+}
Index: /gsdl/trunk/gs2common/indexers/lucene-gs/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/lucene-gs/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/lucene-gs/winMake.bat	(revision 16561)
@@ -0,0 +1,50 @@
+@echo off
+
+set LUCENEHOME=.
+
+rem ---- Where to put class files ----
+set JAVACLASSDIR=classes
+
+rem ---- Name and location of java programs ----
+set JAVAC="%JAVA_HOME%\bin\javac"
+set JAVAH="%JAVA_HOME%\bin\javah"
+set JAVA="%JAVA_HOME%\bin\java"
+set JAVADOC="%JAVA_HOME%\bin\javadoc"
+set JAR="%JAVA_HOME%\bin\jar"
+
+set JAVACOPTIONS=-deprecation -g -O
+
+set LUCENE_JAR=.\lib\lucene-core-2.3.2.jar
+set MYCLASSPATH=%LUCENE_JAR%;%JAVACLASSDIR%
+
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto all
+if ""%1"" == ""install"" goto install
+if ""%1"" == ""clean"" goto clean
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:all
+	if not exist %JAVACLASSDIR% mkdir %JAVACLASSDIR%
+	echo Compiling Greenstone Lucene Java classes ...
+	%JAVAC% -d %JAVACLASSDIR% -classpath %MYCLASSPATH% %JAVACOPTIONS% -sourcepath src src\org\greenstone\LuceneWrapper\*.java
+	cd %JAVACLASSDIR%
+	%JAR% xf ..\%LUCENE_JAR%
+	cd ..
+	%JAR% cf LuceneWrapper.jar -C %JAVACLASSDIR% .
+	goto done
+
+:install
+	goto done
+
+:clean
+	echo Cleaning up Greenstone Lucene ...
+	if exist %JAVACLASSDIR% rmdir /S /Q %JAVACLASSDIR%
+        if exist LuceneWrapper.jar del LuceneWrapper.jar
+	goto done 
+
+:done
+
Index: /gsdl/trunk/gs2common/indexers/mg/COPYING
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/COPYING	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/COPYING	(revision 16561)
@@ -0,0 +1,344 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /gsdl/trunk/gs2common/indexers/mg/INSTALL
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/INSTALL	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/INSTALL	(revision 16561)
@@ -0,0 +1,176 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
Index: /gsdl/trunk/gs2common/indexers/mg/INSTALL.mg
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/INSTALL.mg	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/INSTALL.mg	(revision 16561)
@@ -0,0 +1,38 @@
+                     BUILDING AND INSTALLATION OF MG
+                     ===============================
+ 
+This file explains how to build the mg system.
+ 
+(1) Follow the general GNU install instructions in the INSTALL file.
+    NOTE: this procedure has changed from version mg-1.1 which did
+    not use a GNU configure script.
+    e.g.
+      cd ~/mg-1.2
+      ./configure --prefix=`pwd`
+      make
+      make check
+      make install
+    Report portability problems to tes@kbs.citri.edu.au .
+ 
+(2) You will need to set the environment variable MGDATA which 
+    indicates where the mg data files will be created. You will also
+    need to create the directory you specify.
+ 
+    e.g.
+        setenv MGDATA ~/mgdata
+	mkdir ~/mgdata
+
+(4) You may also need to set the environment variable MGSAMPLE which
+    is used by mg_get and indicates where the sample data included
+    with this package is located.
+    If this is not specified it defaults to ``./SampleData''
+
+(4) Included with mg is an X11 interface to mgquery called xmg. 
+    Xmg is a ``wish'' script which uses the Tcl/Tk packages available
+    by anonymous ftp from allspice.berkeley.edu [128.32.150.27].
+ 
+(5) Another environment variable that you may wish to set is
+    MGIMAGEVIEWER, this variable sets the image viewer to be used
+    to display images. The image viewer must take the image from stdin.
+    If this is not specified it defaults to ``xv -''.
+ 
Index: /gsdl/trunk/gs2common/indexers/mg/MODIFICATIONS
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/MODIFICATIONS	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/MODIFICATIONS	(revision 16561)
@@ -0,0 +1,1023 @@
+TITLE
+  Parsing of Long Words
+APPLICATION
+ mg-1, mg-2
+TYPE
+  bug
+REPORT
+  tim@cosc.canterbury.ac.nz - May 11th 1994
+FIX
+  tes@kbs.citri.edu.au - August 9th 1994
+CLAIM
+  Mg didn't handle long words properly; it crashed.
+PROBLEM
+  Invf passes calls PARSE_LONG_WORD [words.h] which uses a limit of
+  MAXLONGWORD on iterating thru the string and storing into 
+  a word. MAXLONGWORD = 8192.
+  However, mg strings generally store the length in the first
+  byte limiting them to 255 characters. The word which was passed
+  to PARSE_LONG_WORD was an allocated string of MAXSTEMLEN = 255,
+  which is as large as we should get anyway. Thus when accessing
+  a larger word than 255 chars, PARSE_LONG_WORD would allow it
+  (less than 8192) and would try storing beyond the array limit.
+SOLUTION
+  The author can't remember why PARSE_LONG_WORD was used and what
+  the significance of MAXLONGWORD = 8192 is.
+  So PARSE_LONG_WORD has been changed to PARSE_STEM_WORD which
+  uses MAXSTEMLEN as its limit.
+FILES
+ *   words.h
+ *   invf.pass1.c
+ *   invf.pass2.c
+ *   ivf.pass1.c
+ *   ivf.pass2.c
+ *   query.ranked.c
+*************************************************************
+TITLE
+  Use of Lovins stemmer
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  local - 1994
+FIX
+  linh@kbs.citri.edu.au - 1994
+CLAIM
+  Stemming was done naively.
+PROBLEM
+  Only a few types of words and their endings
+  were considered.
+SOLUTION
+  Replacement with a more elaborate "known" stemmer by Lovins.
+  The algorithm is described in:
+    J.B. Lovins, "Development of a Stemming Algorithm",
+    Mechanical Translation and Computational Linguistics, Vol 11,1968.
+FILES
+ *   stem.c
+ *   stem.h
+*************************************************************
+TITLE
+  Different term parsing
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - 23 Aug 1994
+FIX
+  tes@kbs.citri.edu.au - 23 Aug 1994
+CLAIM
+  Boolean queries did not extract words/terms using the
+  same method as is done at inverted-file creation and
+  as is used for rank query parsing.
+PROBLEM
+  The hand-written lex. analyser, query_lex, which is called by
+  the boolean query parser was not calling a common
+  word-extraction routine as used by the rest of mg. 
+  This would be ok if the code did the same things - but they didn't.
+  Query_lex, for instance, did NOT place any limit on the
+  number of digits in a term.
+  Of even more concern, it would allow arbitrary sized words
+  although it used Pascal style strings which store the length
+  in the first byte and can therefore only be 255 characters in length.
+SOLUTION
+  Query_lex in "query.bool.y", was modified to call the routine
+  PARSE_STEM_WORD which is also used by text-inversion routines and
+  ranking query routines.
+  Now all terms are extracted by the same routine.
+  To do this, the end of the line buffer had to be noted as
+  PARSE_STEM_WORD requires a pointer to the end - which is the
+  safe thing to do (don't want to run over the end).
+  This meant I had to find the length of the query line buffer.
+  This was allocated in the file "read_line.c" by the routine,
+  "readline". Its size was the literal number 1024. 
+  This was changed to a constant and placed in "read_line.h".
+  The definition for PARSE_STEM_WORD can be found in "words.h".
+FILES
+ *   query.bool.y
+ *   query.bool.c (by bison)
+ *   read_line.c
+ *   read_line.h
+*************************************************************
+TITLE
+  Highlighting of query terms
+APPLICATION
+  mg-1
+TYPE
+  extend
+REPORT
+  tes@kbs.citri.edu.au - Aug 94
+FIX
+  tes@kbs.citri.edu.au - Sep 94
+CLAIM
+  Difficult to feel happy that the query-result returned is
+  satisfying the query - need to look hard to find the queried words.
+  Need to show words in results using some highlighting method.
+PROBLEM
+  No highlighting of query terms in results.
+SOLUTION
+  Mgquery was previously outputting the decompressed text to a pager
+  such as "less(1)" or "more(1)".
+  (Except when redirected or piped elsewhere :)
+  So what was needed was some sort of highlight pager that instead of
+  displaying the text would also use some means for highlighting the
+  stemmed query words. 
+  Two common forms of highlighting were chosen: underline and bolding.
+  These are supported by "less(1)" and possibly by "more(1)" by
+  using the backspace character.
+  A highlight pager will also need to know which words need to be
+  highlighted. Therefore, the code was modified to build up a
+  string of the stemmed query words for passing to the highlight pager.
+  Design Options:
+  ---------------
+    * Could do text filtering in mgquery before passing out to pager.
+      Instead I pipe to a separate process, the "hilite_words" pager,
+      which filters and pipes into less/more.
+    * Could do different highlighting or a combination.
+    * Could use a different structure for storing the query words other
+      than the hash-table I used.
+FILES
+ *   Makefile - to include hilite_words target
+ *   mg_hilite_words.c
+ *   mgquery.c
+ *   mgquery.1
+ *   query.bool.y
+ *   query.ranked.c
+ *   environment.c
+ *   environment.h
+ *   backend.h
+*************************************************************
+TITLE
+  Mg_compression_dict did premature free
+APPLICATION
+  mg-1
+TYPE
+  bug 
+REPORT
+  R.Sosic@cit.gu.edu.au - 23 Sep 94
+FIX
+  R.Sosic@cit.gu.edu.au - 23 Sep 94
+CLAIM
+  mg_compression_dict dumped core in 
+	file: mg_compression_dict.c
+	function: Write_data
+	line: int codelen = hd->clens[i];
+PROBLEM
+  Huffman data, hd, was freed *before* it was accessed again. 
+SOLUTION
+  The freeing of hd has been moved to after all accesses
+  (just before returning).
+FILES
+ *   mg_compression_dict.c
+*************************************************************
+TITLE
+  Boolean tree optimising rewrite
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  sosic@kurango.cit.gu.edu.au - 23 Sep 94
+FIX
+  tes@kbs.citri.edu.au - Oct 94
+CLAIM
+  "I am still getting core dump in "and" queries in mgquery, 
+   where the first word does not exist, but the second one does."
+PROBLEM
+  Having freed a particular node, it tried to refree it and 
+  access one of its fields.
+
+  I.e. code-fragment...
+
+       FreeNode(curr); /* where curr = CHILD(base) for 1st term in list */
+       FreeNodes(next);
+       FreeNodes(CHILD(base)); 
+              /* but CHILD(base) has already been freed above */
+              /* if the node was the first one in the list */
+
+SOLUTION
+  A number of things in the code seemed a bit dubious to me.
+  So I have rewritten the boolean optimising stage and abstracted out
+  the various stages - each file starts with "bool".
+  Boolean query optimising seems to be a tricky problem. 
+  It is not clear that putting an expression into a certain form will
+  actually simplify it and whether simplification means faster querying.
+  I have converted a given boolean expression into DNF 
+  (Disjunctive Normal Form).  "And not" nodes, which are readily apparent 
+  in DNF, are converted to "diff" nodes.  I have only applied the idempotency 
+  laws involving TRUE and FALSE, and not the ones requiring matching of 
+  expressions - it is a potentially more complicated problem.
+  The optimiser has been tested by playing with "bool_tester", and if you are
+  having a crash or problem in a boolean query it would be worth testing the
+  query on the "bool_tester." The token "*" stands for TRUE (or all documents) 
+  and the token "_" stands for FALSE (or no documents). This should show the 
+  expression before and after optimisation in an ascii tree bracketting format.
+FILES
+ *   bool_tree.c
+ *   bool_parser.y
+ *   bool_optimiser.c
+ *   bool_query.c 
+ *   bool_tester.c
+ *   term_lists.c
+*************************************************************
+TITLE
+  Mgtic pixel placement
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  Bruce McKenzie - bruce@cosc.canterbury.ac.nz (21st Oct 1994)
+FIX
+  singlis@cs.waikato.ac.nz
+CLAIM
+  mgtic crashed on certain files.
+PROBLEM
+  Placing pixels outside of bitmap.
+SOLUTION
+  Changed the putpixel routine to truncate at borders of the image.
+FILES
+ *   mgtic.c
+*************************************************************
+TITLE
+  Improved boolean tree optimising
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  tes@kbs.citri.edu.au - 12/Dec/94
+FIX
+  tes@kbs.citri.edu.au - 21/Dec/94, 14/Mar/95
+CLAIM
+  Optimising by conversion to DNF is not necessarily such
+  a good idea - can actually slow things down.
+PROBLEM
+  The distributive law used in converting to DNF
+  duplicates expressions.
+SOLUTION
+  Introduce a query environment variable, optimise_type = 0 | 1 | 2.
+  Type 0 does nothing to the parse tree. 
+  Type 2 does the DNF conversion.
+  Type 1 is the new default and does the following...
+    Do simple tree rearrangement like flattening. 
+    Optimise for CNF queries.
+FILES
+ *   bool_query.c, .h
+ *   bool_optimiser.c
+ *   environment.c
+ *   invf_get.c
+ *   bool_tree.c, .h
+ *   bool_tester.c
+ *   lists.h
+*************************************************************
+TITLE
+  Similarity variants
+APPLICATION
+  mg-2
+TYPE
+  extend
+REPORT
+  jz@kbs.citri.edu.au/alistair - June 1994
+FIX
+  tes@kbs.citri.edu.au - July 1994 .. Feb 1995
+CLAIM
+  Can only use one type of similarity measure - the
+  standard cosine measure.
+PROBLEM
+  See CITRI/TR-95-3 for more details.
+  The standard measure can be broken up into 7 components.
+  The 7 components are  
+  Each one of these components has a number of alternatives.
+  The overall measure, S_qd, can also be altered.
+  Thus the particular similarity measure used can be specified
+  by an 8 dimensional vector.
+  What is desired is to be able to specify to mgquery an option
+  and a 8-digit string representing this vector (assuming that
+  any one alternative can have at most 9 (not using zero) variants).
+SOLUTION
+  The programs which had to be modified were:
+  (i) mgquery,
+  (ii) mg_weights_build.
+  The other mg programs in existence store the text, indexing info,
+  and the basic statistics such as N, n, ft, fdt.  
+  Other programs which had to be created were:
+  (i) mg_fmd_build,
+  (ii) mg_wt_build.
+  Mg_fmd_build will create the file to store the f_md statistic, 
+  where f_md is the largest (maximum) f_dt of any term in document, d.
+  Mg_wt_build will create the file to store the w_t primitive.
+  It only creates this for the w_t variants 6-9 which would require
+  extra passes of invf at query time if they were not stored here.
+  For details on similarity changes for mgquery and mg_weights_build,
+  please see the other modification entries.  
+FILES
+ *   mg_fmd_build.c
+ *   mg_wt_build.c
+ *   build_lib.c, build_lib.h
+*************************************************************
+TITLE
+  Similarity variants for mgquery
+APPLICATION
+  mg-2
+TYPE
+  extend
+REPORT
+  jz@kbs.citri.edu.au/alistair - June 1994
+FIX
+  tes@kbs.citri.edu.au - July 1994 .. Feb 1995
+CLAIM
+  "mgquery" needs to be altered to allow modification of
+  the similarity measure. 
+PROBLEM
+  See CITRI/TR-95-3 for more details.
+SOLUTION
+
+  Most of the similarity measures, Sqd, are of the 
+  form:   Aqd
+         -----
+          Bqd
+  where Bqd is an expression involving Wd and possibly Wq,
+  where Aqd is a sum over the common document/query terms
+  of w_qt and w_dt.
+  Building of Aqd
+  ===============
+  The calculation of Aqd is done in the file build_Aqd.c .
+  The functions for doing this used to be in the file invf_get.c .
+  Build_Aqd.c contains 4 different functions for building Aqd, each
+  of them building a different data structure:
+    (i) Array, (ii) Splay Tree, (iii) Hash Table, (iv) List Table.
+  Each of these routines seems to have been construction by modifications
+  to duplicated code. This is often the easiest way to construct variants
+  but is quite difficult to maintain consistency. 
+  As the aim of the exercise was to try out different sim. measures for
+  retrieval effectiveness, I only modified the code that constructed
+  an array. This routine was called "CosineDecode"; I changed it to
+  "build_Aqd_Array." This change reflects the fact that we are only 
+  calculating Aqd and this need not be used for the Cosine measure.
+  The other routines: "CosineDecodeSplay," "CosineDecodeHash," and
+  "CosineDecodeList" have been left unaltered - they need to be updated
+  in the future which would be best be done by abstracting out common code.
+  By the stage of building Aqd, the query terms have been looked up in
+  the inverted file dictionary and put into a list.
+  This list of common terms is traversed to lookup the corresponding
+  invf entries. Before the invf entry is processed, all query and term
+  relevant statistics and primitive quantities are calculated.
+  For example, fqt, ft, wt, rqt, wqt, Wq-partial-sum.
+  To save unnecessary calculations, there is a test for each value
+  to see whether it is needed e.g. "if (sim->variant_needs & NEEDS_wt) ... ".  
+  Aside: Variant Needs
+  --------------------
+  The idea behind the "variant_needs" field is to be able to have all
+  the code in the one place for each possible variant and this code would
+  get the information at the correct time/place only when it is needed.
+  The overhead is a "bit-and" and "test" for each component.
+  The important concern though, is that the "variant_needs" must be
+  accurate i.e. it should be carefully maintained.
+  Each possible need is stored as a bit position in a constant/macro
+  of the form "NEEDS_component" e.g. NEEDS_wt, NEEDS_nt 
+  More recently (Jan/95), I have found that the _need_ing of a component
+  may be relevant to a particular purpose, that is, it may be needed in
+  one situation and not another. This was the case for Wd. 
+  For rdt#6, Wd was needed, however, it might not be needed for Bqd, the
+  denominator of Sqd. So I changed from NEEDS_Wd to NEEDS_Wd_for_Sqd and
+  NEEDS_Wd_for_rdt. 
+  --------------------
+  To handle the different calculations of the variants, I wrote macros
+  based around a switch statement. All these macros are stored in the file
+  "similarity.h" . The point of doing this is to centralise most of the changes
+  and cut down on the number of files which have to be altered if a new
+  way of calculating a primitive is to be added.  
+  This is achieved by having a data record called, "Similarity_variant" whose
+  fields includes all the statistics and the similarity primitives.
+  So the standard procedure is to see if something is needed and if so, then
+  extract it from an mg file or calculate it using a macro - most of the
+  input and output to the macro is done via the "Similarity_variant" structure.
+  As well as calculating Aqd, it is also a convenient place to calculate Wq.
+  Previously, for Cosine measure, Wq was not needed because each Aqd was directly
+  divided by Wq and thus would not change the ordering. However, there are
+  some Sqd's which divide by a sum involving Wq and thus need Wq. 
+  Calculating Sqd and Ordering Documents
+  ====================================== 
+  The file query.ranked.c contains the code for calculating Sqd using the
+  approximate Wd in order to do the ranking of the documents.
+  The mg-1 file was cleaned up and modified slightly.
+  All the heap data structures and routines were taken out and placed in 
+  their own file, heap_weights.c/.h .
+  The major components/steps in the ranking process were abstracted into
+  macros and functions:
+    calc_MaxParas
+    insert_heap_entry
+    insert_greater_heap_entry
+    approx_guided_insert
+    fill_initial_heap
+    add_heap_remainders
+    change_heap_to_exact
+    add_remainder_exact_weights
+    Make_Exact_Root
+    build_doc_list
+  Aside: Zeroing of an Aqd Element
+  -------------------------
+  One interesting change concerned the use of zeroing out an Aqd element
+  so as to mark it as being used in the heap.
+  In the heap, the approx. Sqd is stored based on Aqd/Wd-approx.
+  Later when Aqd is required, it can be extracted from the approx. Sqd
+  by muliplication of Wd-approx i.e. Aqd = Sqd-approx * Wd-approx.
+  This, however, is not always possible for the various Sqd variants.
+  So, instead of zeroing out Aqd, I decided just to make it negative.
+  -------------------------
+  In some Sqd cases, Wd and Wd-approx. are not required. In which case,
+  step 3 which calls on "change_heap_to_exact", "Heap_Build" and
+  "add_remainder_exact_weights" is not required.
+ 
+FILES
+ *    build_Aqd.c
+ *    query.ranked.c
+ *    similarity.c/.h (in libmg)
+ *    heap_weights.c/.h
+ *    backend.c/.h
+*************************************************************
+TITLE
+  Similarity variants for mg_weights_build
+APPLICATION
+  mg-2
+TYPE
+  extend
+REPORT
+  jz@kbs.citri.edu.au/alistair - June 1994
+FIX
+  tes@kbs.citri.edu.au - July 1994 .. Feb 1995
+CLAIM
+  "mg_weights_build" needs to be altered to allow modification of
+  the similarity measure.
+PROBLEM
+  See CITRI/TR-95-3 for more details.
+SOLUTION
+  The weight files which are generated for a particular similarity
+  measure have their names extended by a suffix.
+  In the case of Wd#1 no weights are generated.
+  In the case of the standard cosine weights, Wd#2, a 3 letter suffix
+  is used to represent .
+  In the case of the other Wd variants, a one letter suffix is used
+  to represent which Wd variant it is.
+  In each case, the variant input (e.g. -q 22222222) should be the whole
+  similarity variant string and the relevant fields will be extracted out.
+  This is done for consistency in code and interface. 
+  The code is fairly similar to the original.
+  A dependency check has been added so that the dates of files and the
+  type of needed files is verified before building.
+  The dependencies include, invf dictionary, invf index, invf, fmd and wt files.
+  The major change here, is the possible use of fmd and/or wt files.
+  Later when I was having to write mg_fmd_build and mg_wt_build,
+  I decided to abstract out some macros, namely:
+    Get_ft, Get_ft_Ft, loop_invf_entry,
+  which were put into "build_lib.c" .
+  "loop_invf_entry" takes a function/macro name as a parameter and applies
+  it to the sim record (with field fdt set), current doc number and modifies
+  the return value. 
+FILES
+ *   mg_weights_build.c
+ *   build_lib.c
+*************************************************************
+TITLE
+  Mgstat with non-existent files
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  tes@kbs.citri.edu.au - 10 Aug 1994
+CLAIM
+  NaNs and Infinites would be printed out by mgstat
+  if unable to open .text or .text.dict file.
+PROBLEM
+  The NaNs etc. were output in the column stating
+  the percentage size of the file compared with the
+  number of input bytes of the source text data.
+  If it couldn't read the .text file with its
+  header describing the number of source text bytes, then
+  in working out the percentage it would divide by zero.
+  Also due to some bad control flow, it wouldn't attempt to
+  open the .text file if it failed when opening 
+  the .text.dict file.
+SOLUTION
+  Only printout the percentage if we can read the header
+  from the .text file.
+  Read in text header irrespective of text dictionary file.
+FILES
+ *   mgstat.c
+*************************************************************
+TITLE
+  Boolean tree optimisations
+APPLICATION
+  mg-2
+TYPE
+  extend
+REPORT
+  (i) tes@kbs.citri.edu.au - 28/Sep/94
+  (ii) tes@kbs.citri.edu.au - 12/Dec/94
+FIX
+  (i) tes@kbs.citri.edu.au - 18/Oct/94
+  (ii) tes@kbs.citri.edu.au - 21/Dec/94
+CLAIM
+  The initial prompt for investigating the optimisation of
+  boolean queries is noted in the mg-1 mod14.txt. 
+  The code for optimising seemed to have a number of faults.
+PROBLEM
+  Boolean optimisation was unreliable.
+SOLUTION
+  Initially (in case (i) above, see mg-1/mod14.txt), I rewrote 
+  all the boolean tree and optimising code. I converted the boolean
+  expression into DNF. I did this after reading some notes about 
+  the steps involved in optimisation and they suggested standardising
+  in some normal form. I thought that DNF would be appropriate so that
+  all the terms are converted to be part of "and" expressions and be
+  evaluated quickly using skipping.
+  This, however, can suffer quite badly if the distributive law is
+  applied to often and the query expands in size. If there was
+  some sort of cacheing of invf entries, then it might not be so
+  bad otherwise there is quite an overhead on reading the same
+  invf entry more than once.
+
+  As it happens, CNF queries are reasonably common, where the user
+  queries with a conjunction of disjunctions of similes:
+    e.g. (car | automobile | vehicle) & (fast | quick | speedy)
+  This sort of CNF query expands a hell of a lot !
+  So after speaking with Justin who wanted to benchmark Atlas with Mg on
+  these sort of queries, I looked up the MG book for other ideas.
+
+  The method that I implemented was the following:
+
+  -----------------------------------------------------------------
+  Steps of tree modifications:
+    Gets literals by pushing the nots down, detecting T/F at leaves
+    and collapsing the tree by detecting  'and' of 'and's and 'or' of
+    'or's.
+    Next it looks at the or nodes and if all the children are terms
+    then it marks the or-node as such.
+    Finally, the or-term-nodes are sorted by using the sum of their
+    ft's for comparison.
+  Steps at query evaluation:
+    If it comes across an 'and' of 'or-terms' then the evaluation is
+    done noting the distributive law.
+    I.e. a & (b | c | d) = (a & b) | (a & c) | (a & d)
+    Assuming 'a' is the c-set of documents.
+    All of 'a' is tested against 'b' and matching ones are marked.
+    Next, all the unmarked members of 'a' are tested against 'c'.
+    Likewise for 'd'.
+    Now all the marked members of our c-set are kept.
+    When we do the testing, we can use the skipping in the invf entries.
+  -----------------------------------------------------------------
+
+
+  After doing this, I added the choice of which type of optimisation
+  the user wanted by adding query-environment-variable, "optimise_type".
+  Type 0 = no parse tree modification. 
+  Type 1 = Or-term recognition and CNF query evaluation optimisation. 
+  Type 2 = Put into DNF form. [generally not recommended]
+FILES
+ *   bool_tree.c
+ *   bool_parser.y
+ *   bool_optimiser.c
+ *   bool_query.c 
+ *   bool_tester.c
+ *   term_lists.c
+ *   query_env.c
+ *   invf_get.c [GetDocsOp]
+*************************************************************
+TITLE
+  nonexistent HOME bug  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  bug 
+REPORT
+  cgn@totara.cs.waikato.ac.nz - 2/May/95
+FIX
+  tes@kbs.citri.edu.au - 2/May/95
+CLAIM
+"The big problem was that mgquery crashes when the HOME environment
+ variable is not set, which is the case when it is run by the www server."
+ [...] "I expect it happens when looking for $HOME/.mgrc."
+PROBLEM
+  The result of getenv("HOME")" was used directly in
+  a sprintf call. If the environment variable HOME
+  was not in existence then null would be used.
+  In some C libraries sprintf will convert the 0
+  string into the string "(null)" on others it will core dump.
+  (For example, Solaris seems to core dump, sunos 4 seems ok).
+SOLUTION
+  The result from getenv("HOME")" is tested before
+  being used.
+FILES
+ *   commands.c
+*************************************************************
+TITLE
+  mgquery collection name preference  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  improve
+REPORT
+  cgn@totara.cs.waikato.ac.nz - 2/May/95
+FIX
+  tes@kbs.citri.edu.au - 4/May/95
+CLAIM
+ Surely something must override mquery's preference for ./bib. 
+ If MGDATA is set correctly, I think it should prefer that collection, 
+ and -d should definitely override it. 
+ I could always say -d . if I really wanted ./bib.
+PROBLEM
+Currently the priority is:
+1.  Check if ./name is a directory,
+     If so then use it as the collection directory.
+2.  Check if ./name.text is a file,
+     If so then use ./ as the collection directory.
+3.  Check if mgdir/name is a directory,
+     If so then use mgdir/name as the collection directory.
+4.  Otherwise,
+     Use mgdir/name as the database file prefix.
+     This would be the case if one used "-f alice/alice".
+     However, one would then not specify a final name argument
+     and we'd never get here. Go figure ???
+SOLUTION
+Moved step 3 to the top instead.
+FILES
+ *   mgquery.c [search_for_collection()]
+*************************************************************
+TITLE
+  Printout of query terms
+APPLICATION
+  mg-1, mg-2
+TYPE
+  extend
+REPORT
+  tes@kbs.citri.edu.au - April 95
+FIX
+  tes@kbs.citri.edu.au - April 95
+CLAIM
+  No easy way to find out the parsed and stemmed words
+  used in the query. Would like to know these words
+  so I can call a separate highlighting program to 
+  highlight these words.
+PROBLEM
+  No facility available.
+SOLUTION
+  A ".queryterms" mgquery command was added which lists
+  out the parsed/stemmed queryterms of the last query.
+FILES
+ *    commands.c (added CmdQueryTerms)
+*************************************************************
+TITLE
+  mg_getrc  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  extend
+REPORT
+  bruce@cosc.canterbury.ac.nz - 2/May/95
+FIX
+   - 
+CLAIM
+  Repeated code had to be written for different named
+  gets but really the same type of parsing required.
+  E.g. one might want to use a standard method for inserting
+  ^Bs between paragraphs for different books. One doesn't
+  want to write duplicate code for each different named book,
+  rather note that each book should be filtered "book" style.
+PROBLEM
+  There was no way of abstracting out types of filters from
+  the name of an instance of a collection.
+SOLUTION
+  Allow information to be given with <name, type, files>.
+  This extra info can be provided in a mg_getrc file.
+  See man page for mg_get for details.
+FILES
+ *    mg_get.sh
+*************************************************************
+TITLE
+  TREC DocNo file   
+APPLICATION
+  mg-2
+TYPE
+  improve
+REPORT
+  tes@kbs.citri.edu.au - 26/May/95
+FIX
+  tes@kbs.citri.edu.au - 26/May/95
+CLAIM
+  MG has problems dealing with trec docnos for trec disk 3.
+PROBLEM
+  Trec DocNos file didn't have a wide enough field
+  to handle disk 3. 
+SOLUTION
+  Allow different width fields for file.
+  It is still fixed width but a number in the header
+  says how wide the field is.
+FILES
+ *   passes/mg.special.c
+ *   query/mgquery.c
+*************************************************************
+TITLE
+  Boolean optimiser #1 with `!'  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  bug 
+REPORT
+  triffid@iconz.co.nz - 20/7/95
+FIX
+  tes@kbs.citri.edu.au - 27/7/95
+CLAIM
+  Complained about not-nodes. 
+  e.g. complained about "croquet & !hedgehog"
+PROBLEM
+  Boolean optimiser type#1 didn't convert
+  "and not"s into diff nodes.
+SOLUTION
+  Added code to convert '&!' to '-'.
+FILES
+ *   mg/bool_optimiser.c [mg-1]
+ *   query/bool_optimiser.c [mg-2]
+*************************************************************
+TITLE
+   Autoconfiguring mg-1  
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  many people - 94/95
+FIX
+  tes@kbs.citri.edu.au - Aug/95
+CLAIM
+  Portability is limited by setting up c-macros just for particular
+  machines and operating systems.
+  People had to make changes for HP, Next, Linux, Dec Alpha, ...
+PROBLEM
+  Porting was only targetting at the machines that the author had
+  access to.
+SOLUTION
+  Use GNU's autoconfigure program.
+  This allows checking of the systems features/characteristics.
+  It also allows some checking for specific machines/OS - although
+  I have not utilised this option.
+  I used GNU's tar-1.11.8 as an example to base my changes on.
+  I also used autoscan to generate the initial "configure.in".
+  The "Makefile.in"s were done very similarly to GNU tar's.
+  The "config.h" and "sysfuncs.h" files were scrapped and 
+  rewritten. The new "config.h" file is generated by the configure
+  script - it contains all the #define's for the system features.
+  The "sysfuncs.h" file wraps up a number of system headers. 
+  For example, some systems use , while some use ;
+  which one is included is decided in "sysfuncs.h".
+  I have also used GNU tar's use of ansi2knr in its Makefiles.
+  This should hopefully allow the package to work on a system with
+  only a K&R C compiler.
+  However, there are probably problems with what I have done.
+  I am concerned about <stdarg.h> for example.
+  I also noticed that "ansi2knr" require function definitions as
+  the GNU coding style recommends ie. with function name the first
+  string on the line. This prompted me to run all the package's code
+  thru GNU's indent.
+  Setting up the configure changes is difficult. It really seems
+  necessary to try the package out on many target machines so one
+  can know what is necessary.
+  A simple check target for the main Makefile has been written.
+  It is used to see if the installation is working - it does
+  not test much of the functionality of mg.
+  It does cmp's on data files and diff's on query/result files.
+FILES
+  Most of the files in the distribution.
+*************************************************************
+TITLE
+  Consistent use of stderr
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  tes@kbs.citri.edu.au - 11 August 1994
+CLAIM
+  Inconsistent use of stdout/stderr in usage messages.
+PROBLEM
+  Sometimes used "printf" and sometimes used "fprintf(stderr"
+  in usage messages.
+SOLUTION
+  All should now use "fprintf(stderr" in usage messages.
+FILES
+ *   mg_compression_dict.c
+ *   mg_compression_dict.1
+ *   mg_fast_comp_dict.c
+ *   mg_fast_comp_dict.1
+ *   mg_invf_dict.c
+ *   mg_invf_dict.1
+ *   mg_invf_dump.c
+ *   mg_invf_dump.1
+ *   mg_invf_rebuild.c
+ *   mg_invf_rebuild.1
+ *   mg_perf_hash_build.c
+ *   mg_perf_hash_build.1
+ *   mg_text_estimate.c
+ *   mg_text_estimate.1
+ *   mg_weights_build.c
+ *   mg_weights_build.1
+*************************************************************
+TITLE
+  xmg bug
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  cgn@cpsc.ucalgary.ca - 22 April 1994
+FIX
+  cgn@cpsc.ucalgary.ca - 22 April 1994
+CLAIM
+  "Serious problem in xmg, which I fear occurs whenever a query
+   doesn't return anything."
+PROBLEM
+  ??
+SOLUTION
+  [xmg.sh 201] set rank 0
+FILES
+ *   xmg.sh
+*************************************************************
+TITLE
+  Unnecessary loading of text
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - ?? August 1994
+FIX
+  tes@kbs.citri.edu.au - 12 August 1994
+CLAIM
+  Mg was loading and uncompressing text when the
+  query did not require the text.
+PROBLEM
+  There was no test for the query mode
+  before loading and uncompressing the text.
+SOLUTION
+  Only load/uncompress text if query mode
+  is for text, headers or silent(for timing).
+FILES
+ *   mgquery.c
+*************************************************************
+TITLE
+  Man page errors
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  beebe@math.utah.edu - 16 May 1994
+CLAIM
+  Man page errors.
+PROBLEM
+  See below.
+SOLUTION
+ "The mg_make_fast_dict.1 file has been renamed mg_fast_comp_dict.1,
+  and all mg_make_fast_dict strings changed to mg_fast_comp_dict in all
+  man pages.
+  A large number of errors of spelling, typography, spacing, fonts,
+  grammar, omitted words, slang, punctuation, missing man page
+  cross-references, and man-page style have been corrected."
+FILES
+ *    mg_compression_dict.1
+ *    mg_fast_comp_dict.1
+ *    mg_get.1
+ *    mg_invf_dict.1
+ *    mg_invf_dump.1
+ *    mg_invf_rebuild.1
+ *    mg_passes.1
+ *    mg_perf_hash_build.1
+ *    mg_text_estimate.1
+ *    mg_weights_build.1
+ *    mgbilevel.1
+ *    mgbuild.1
+ *    mgdictlist.1
+ *    mgfelics.1
+ *    mgquery.1
+ *    mgstat.1
+ *    mgtic.1
+ *    mgticbuild.1
+ *    mgticdump.1
+ *    mgticprune.1
+ *    mgticstat.1
+ *    xmg.1
+*************************************************************
+TITLE
+  Man page overview
+APPLICATION
+  mg-1
+TYPE
+  extend
+REPORT
+  beebe@math.utah.edu - 
+FIX
+  tes@kbs.citri.edu.au - 17 August 1994
+CLAIM
+  "Write new mg.1 file to give a brief overview of mg, with samples
+   of how to use it.  Otherwise, users are likely to be completely
+   overwhelmed by the number of programs (about 20) which might need to
+   be used, when in reality, only 2 or 3 are likely to be run by end
+   users."
+SOLUTION
+  It was thought that mg.1, written by Nelson Beebe, was very useful
+  but a bit too comprehensive for an introduction.
+  Therefore, two man files, mgintro.1 and mgintro++.1 were written
+  with the basic stuff in mgintro.1 and slightly more advanced stuff
+  in mgintro++.1 . 
+FILES
+ *   mg.1
+ *   mgintro.1
+ *   mgintro++.1 
+*************************************************************
+TITLE
+  Parse errors not bus errors
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tim@cosc.canterbury.ac.nz - 2 Jun 94
+FIX
+  tes@kbs.citri.edu.au - 19 Aug 94
+CLAIM
+  "These two queries 
+   (which I typed in before I knew what I was doing!!)
+      > The Queen of Hearts, she made some tarts
+      > "The Queen of Hearts" and "she made some tarts"
+      produced the following result:
+      mgquery : parse error
+      Bus error
+  "
+PROBLEM
+  What is expected to happen under boolean querying:
+    Query1:
+    > The Queen of Hearts, she made some tarts
+    will produce a parse error due to the comma which
+    is not a valid TERM.
+    Query2:
+    > "The Queen of Hearts" and "she made some tarts"
+    will store a post-processing string
+    of ''The Queen of Hearts" and "she made some tarts'' and
+    will have a main boolean query of the empty string.
+    This is because the postprocessing string takes in
+    everything between the first quote and the last one.
+    An empty string is illegal for the boolean grammar and
+    hence a parse error.
+  The problem stems from the fact that the processing of
+  the parse tree is carried out, even though we have a 
+  parse error. In the case of using an empty string to build
+  a parse tree, it is likely to leave the parse tree undefined.
+SOLUTION
+  As soon as we find out that there is a parse-error,
+  we abandon any processing of the parse tree.
+FILES
+ *   query.bool.y
+ *   query.bool.c (generated from query.bool.y)
+*************************************************************
+TITLE
+  Perfect hashing on small vocab
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - July 1994
+FIX
+  alistair@cs.mu.oz.au - July 1994
+CLAIM
+  Mg could not handle small collections in the case
+  where there was only a small number of unique words.
+  The perfect hash function would report an error.
+PROBLEM
+  Rounding of the arithmetic during the calculation of the
+  parameters of the perfect hash function was resulting in a
+  combination of values such that the probability of a hash
+  function being found was very small. This led to the limit
+  on the generation loop being exceeded, and eventual
+  failure.
+SOLUTION
+  By using ceiling rather than floor when converting from a
+  floating point value to an integer parameter, the arithmetic
+  is now correct for all lexicon sizes, and the probability of
+  each iteration successfully generating a hash function is
+  sufficiently great that with _very_ high probability the
+  execution loop counter will not be exceeded unless there
+  genuinely is no hash function (for example, if the lexicon
+  contains two words the same there cannot be  a hash
+  function).
+FILES
+ *   perf_hash.c
+*************************************************************
Index: /gsdl/trunk/gs2common/indexers/mg/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/Makefile.in	(revision 16561)
@@ -0,0 +1,180 @@
+# Main Makefile for MG
+# Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+ 
+# 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, 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.
+ 
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+@SET_MAKE@
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+srcdir = @srcdir@
+ 
+
+CC = @CC@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -fpic 
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@ 
+LIBS = @LIBS@  
+TOUCH = echo timestamp >
+ 
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+libexecdir = $(exec_prefix)/libexec
+ 
+MDEFINES = 
+#CC='$(CC)' CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \
+#prefix='$(prefix)' exec_prefix='$(exec_prefix)' \
+#bindir='$(bindir)' libexecdir='$(libexecdir)' \
+#infodir='$(infodir)' infodir='$(infodir)' libexecdir='$(libexecdir)'
+
+#SUBDIRS = lib src/text src/images src/scripts 
+SUBDIRS = lib src/text
+JAVADIRS = java/org/greenstone/mg 
+JNIDIRS = jni
+.SUFFIXES:
+
+all install uninstall execinstall execuninstall maninstall manuninstall: config.h
+	for subdir in $(SUBDIRS) $(JAVADIRS) $(JNIDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+	done
+
+nojava: 
+	for subdir in $(SUBDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) all) || exit 1; \
+	done
+
+install-nojava: 
+	for subdir in $(SUBDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) install) || exit 1; \
+	done
+
+javaonly:
+	for subdir in $(JAVADIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) all) || exit 1; \
+	done
+
+install-javaonly:
+	for subdir in $(JAVADIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) install) || exit 1; \
+	done
+
+
+check: all
+	echo making $@ in ./test directory
+	(cd ./test && $(MAKE) all) || exit 1
+	./test/testmg `pwd`
+
+mostlyclean: mostlyclean-recursive mostlyclean-local
+
+clean: clean-recursive clean-local
+
+distclean: distclean-recursive distclean-local
+
+maintainer-clean: maintainer-clean-recursive maintainer-clean-local
+	rm -f config.status
+
+mostlyclean-recursive clean-recursive distclean-recursive maintainer-clean-recursive:
+	for subdir in $(SUBDIRS) $(JAVADIRS) $(JNIDIRS); do \
+	  target=`echo $@ | sed 's/-recursive//'`; \
+	  echo making $$target in $$subdir; \
+	  (cd $$subdir && $(MAKE) $$target) || exit 1; \
+	done
+
+mostlyclean-local:
+
+clean-local: mostlyclean-local
+
+distclean-local: clean-local
+	rm -f Makefile stamp-h config.status config.h config.log
+
+maintainer-clean-local: distclean-local
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+distdir = $(PACKAGE)-$(VERSION)
+
+distname = $(distdir)
+
+DISTFILES = \
+INSTALL.mg INSTALL README README.port COPYING MODIFICATIONS about_mg.html \
+Makefile.in acconfig.h aclocal.m4 configure.in install-sh mkinstalldirs \
+config.guess config.sub \
+config.h.in configure stamp-h.in sysfuncs.h
+
+dist: $(DISTFILES)
+#-----------------------------------------------------------------
+	#create distribution directory
+	-rm -rf $(distdir)
+	mkdir $(distdir)
+#
+	#link in local distribution files
+	ln $(DISTFILES) $(distdir)
+#
+	#link in SampleData
+	mkdir $(distdir)/SampleData
+	(cd SampleData; $(MAKE) "distdir=../$(distdir)/SampleData" dist)
+	# link in davinci stuff
+	mkdir $(distdir)/SampleData/davinci
+	(cd SampleData/davinci; \
+	$(MAKE) "distdir=../../$(distdir)/SampleData/davinci" dist)
+	# link in bible stuff
+	mkdir $(distdir)/SampleData/bible
+	(cd SampleData/bible; \
+	$(MAKE) "distdir=../../$(distdir)/SampleData/bible" dist)
+#
+	#link in tests
+	mkdir $(distdir)/test
+	(cd test; $(MAKE) "distdir=../$(distdir)/test" dist)
+#
+	#link in docs directory
+	mkdir $(distdir)/docs
+	(cd docs; $(MAKE) "distdir=../$(distdir)/docs" dist)
+#
+	#link in lib directory
+	mkdir $(distdir)/lib
+	(cd lib; $(MAKE) "distdir=../$(distdir)/lib" dist)
+#
+	#link in src distribution files
+	mkdir $(distdir)/src
+	for dir in src/text src/images src/scripts; do \
+	  echo makeing $@ in $$dir ; \
+	  mkdir $(distdir)/$$dir; \
+	  (cd $$dir; $(MAKE) "distdir=../../$(distdir)/$$dir" $@) ; \
+	done
+#
+	# archive and compress
+	tar -chf - $(distdir) | gzip -9 > $(distname).tar.gz
+	tar -chf - $(distdir) | compress > $(distname).tar.Z
+#
+	# cleanup
+	rm -rf $(distdir)
+#-----------------------------------------------------------------
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mg/README
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/README	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/README	(revision 16561)
@@ -0,0 +1,156 @@
+                 MG INFORMATION RETRIEVAL SYSTEM
+                 ===============================
+
+The MG system is a suite of programs for compressing and indexing text
+and images. Most of the functionality implemented in the suite is as
+described in the book ``Managing Gigabytes:  Compressing and Indexing
+Documents and Images'', I.H. Witten, A.  Moffat, and T.C. Bell; Van
+Nostrand Reinhold, New York, 1994, ISBN 0-442-01863-0; US $54.95; call
+1 (800) 544-0550 to order.
+
+These features include:
+
+-- text compression using a Huffman-coded semi-static word-based scheme
+-- two-level context-based compression of bi-level images
+-- FELICS lossless compression of gray-scale images
+-- combined lossy/lossless compression for textual images
+-- indexing algorithms for large volumes of text in limited main memory
+-- index compression
+-- a retrieval system that processes Boolean and ranked queries
+-- an X windows interface to the retrieval system
+
+As one example, a collection of 2 Gb of text (1,700,000 documents) can
+be indexed (on a SPARC 10 Model 512) in about four hours and compressed
+in a further four hours to make a database that in total occupies less
+than 800 Mb, or 40% of the original size. This includes a full index to
+every word and number in the original text. Boolean queries such as
+``managing AND gigabytes'' run in a few seconds, and ranked queries of
+30--50 terms are evaluated in 10--30 seconds.
+
+Details of these methods and further performance results appear in the
+MG book.
+
+The MG system comes with ABSOLUTELY NO WARRANTY; for details see the
+file COPYING.
+
+Instructions on how to build and install mg are in the file INSTALL.
+
+
+** CHANGES FROM BOOK
+
+For copyright reasons the stemmer used in this distribution of MG is
+not the same as the one illustrated in Figure 3.8 on page 108 of the MG
+book.  This means that the numbers generated by the command ``mgstat
+alice'' will not match those numbers in Figure A.1 on page 394.
+Another stemmer was initially written as a simple stopgap for version
+1.0.  That stemmer has been replaced by a stemmer based on the Lovin's
+stemming algorithm for mg-1.1.
+
+The output format of ``mgstat'' has changed since Figure A.1 (page 394)
+was prepared. The same information is displayed but formatted
+differently.
+
+** MG VERSIONS
+
+The current version is mg-1.2, September 1995. The changes from earlier 
+versions are listed in the file MODIFICATIONS. This can be accessed
+with mg by building a database using ``mgbuild mods'' and can also be
+accessed from the mg web page (see below). 
+
+The mg-1.2 extensions include:
+
+-- Source modifications for use of GNU's autoconf.
+
+The mg-1.1 extensions include:
+
+-- A new highlighting mode.
+   The output mode ``hilite'' will highlight the query terms in the
+   retrieved text documents. The variable ``hilite_style'' can be set
+   to ``bold'' or ``underline''. It works best with the pager
+   ``less''.  A .mgrc to use would include:
+	.set pager less
+	.set mode hilite
+	.set hilite_style bold
+
+-- A web site containing manual pages, documentation, and a
+   mgquery demo page (utilising cgi scripts). 
+   See: http://www.kbs.citri.edu.au/mg
+   One of these pages ``about_mg.html'' is included in this
+   distribution.
+
+-- A revised mg_get script which uses a .mg_getrc file to map
+   specific collection names to filter types.  (Modifications by Bruce
+   McKenzie). See mg_get.1 for more details.
+
+-- Code to perform merging of existing databases. This code
+   was created by Shane Hudson and is documented in the mgmerge.README
+   file found in the docs subdirectory.  This code is maintained by
+   Shane Hudson (shane@cosc.canterbury.ac.nz).
+
+-- Revised man pages, including some new entries (thanks to Nelson
+   Beebe). See mg.1, mgintro.1, mgintro++.1.
+
+-- A real (rather than toy) stemmer.
+
+** PORTABILITY
+
+Please refer to "README.port".
+
+** CREDITS
+ 
+The MG development is largely the result of research collaboration
+between:
+
+        Tim C. Bell            <tim@cosc.canterbury.ac.nz>
+        Ian Witten             <ihw@cs.waikato.ac.nz>
+        Alistair Moffat        <alistair@cs.mu.oz.au>
+        Justin Zobel           <jz@cs.rmit.oz.au>
+
+The bulk of the programming work has been carried out by:
+
+        Stuart Inglis          <singlis@cs.waikato.ac.nz>
+        Craig Nevill-Manning   <cgn@cs.waikato.ac.nz>
+        Neil Sharman           <neil@kbs.citri.edu.au>
+        Tim Shimmin            <tes@kbs.citri.edu.au>
+
+In addition to these, the following people have contributed to the
+development of the MG software:
+
+        Lachlan Andrew         <lha@kbs.citri.edu.au>
+        Gary Eddy              <gary@cs.mu.oz.au>
+        Hugh Emberson          <hugh@cosc.canterbury.ac.nz>
+        Kerry Guise            <kerry@cs.waikato.ac.nz>
+	Shane Hudson           <shane@cosc.canterbury.ac.nz>
+        Linh Huynh             <linh@kbs.citri.edu.au>
+        Bohdan S. Majewski     <bohdan@cs.uq.oz.au>
+        Bruce McKenzie         <bruce@cosc.canterbury.ac.nz>
+
+In addition to these, the following people have submitted bug reports
+and suggestions/fixes:
+
+        Rex Barzee             <rex@python.cs.byu.edu>
+        Tim A.H. Bell          <bhat@cs.mu.oz.au>
+        Tim C. Bell            <tim@cosc.canterbury.ac.nz>
+        Nelson Beebe           <beebe@math.utah.edu>
+	Rodney Brown           <RBrown@cmutual.com.au>
+        Rok Sosic              <R.Sosic@cit.gu.edu.au>
+        Carl Staelin           <staelin@cello.hpl.hp.com>
+
+Development of the MG system was supported by the Australian Research
+Council; the Universities of Melbourne, Waikato, Canterbury, and
+Calgary; RMIT; and the Collaborative Information Technology Research
+Institute (Melbourne).
+
+** BUG REPORTS
+
+Send bug reports to <alistair@cs.mu.oz.au> and <tes@kbs.citri.edu.au>.
+Back-traces from gdb are always welcome but not mandatory :-)
+
+** FURTHER READING
+
+A bibliography of MG related research work appears in the files
+MG.Bibliography.ps and MG.Bibliography.bib on the ftp site and
+is accessible through the Web page.
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/README.port
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/README.port	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/README.port	(revision 16561)
@@ -0,0 +1,125 @@
+P O R T A B I L I T Y
+=====================
+
+The main development of MG is done under Solaris on a Sun sparc.
+However, we would also like MG to be reasonably portable.
+Therefore, MG-1.2 now uses a configure stript generated by GNU's autoconf.
+Below, is a list of the machines that the s/ware has been ported to. 
+Most only need the configure script and some require manual modifications 
+mentioned in the "Problems" field.
+
+------------------------------------------------------------------------- 
+** Systems which have successfully been built.
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Sun
+System:		SunOS 4.1.2 8 sun4c
+Compiler:	gcc version 2.3.3
+Problems:	none
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Sun
+System:		SunOS 4.1.2_DB 14 sun4c
+Compiler:	gcc version 2.3.3
+Problems:	none
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Sun
+System:		SunOS 4.1.3_U1 1 sun4c
+Compiler:	gcc version 2.5.7
+Problems:	none
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Sun
+System:		SunOS 5.3 Generic_Patch sun4m sparc
+Compiler:	gcc version 2.4.3
+Problems:	none
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Silicon Graphics
+System:		IRIX 5.2 02282014 IP7
+Compiler:	gcc version 2.6.3
+Problems:	none
+
+Tester:		tes@kbs.citri.edu.au
+Date:
+Machine:	Sun
+System:		SunOS 5.4 Generic_101945-27 sun4c sparc
+Compiler:	cc: SC3.0.1 13 Jul 1994
+Problems:	none
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:
+System:		SCO 3.2v4.1
+Compiler:	gcc 2.6.1
+Problems:	requires RANLIB=true, use of tcsh
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:	HP
+System:		HP-UX A.09.04 D 9000/837 415303291 64-user license
+Compiler:	HP92453-01 A.09.73 HP C Compiler
+Problems:
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:	Data General
+System:		dgux 5.4R3.00 generic AViiON mc88110 
+Compiler:	gcc 2.4.5 (dg supplied)
+Problems:	none
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:	Pyramid MIPS R3000 box
+System:		
+Compiler:	cc (vendor supplied)
+Problems:	none
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:	IBM RS6000
+System:		AIX 3.2
+Compiler:	AIX XL C Compiler/6000
+Problems:	none
+
+Tester:		RBrown@cmutual.com.au
+Date:
+Machine:	Linux 1.2.9
+System:		
+Compiler:	gcc 2.6.3
+Problems:	none
+
+-----------------------------------------------------------------------
+** Systems which have NOT successfully been built.
+
+Tester:		RBrown@cmutual.com.au, 
+		Lloyd.Parkes@vuw.ac.nz
+Date:		Oct, Nov, 1995
+Machine:	Dec Alpha OSF/1
+System:
+Compiler:	cc
+Problems: 	Many - see below.
+
+* Problems with Dec Alpha
+  - 64 bit length longs as opposed to 32 bit longs.
+  - Lloyd.Parkes@vuw.ac.nz says that there is some bit encoding problems 
+    but the decoding seems to work.
+  - RBrown@cmutual.com.au says that it will compile but will core dump
+    when  building the test database.
+  - tes.kbs.citri.edu.au says that he needs to look into the repercussions
+    of 64 bit longs, however, he does not have access to such a machine.
+		
+-----------------------------------------------------------------------
+** Other systems. 
+
+Systems other than the above may have installation problems.
+If this is the case, please send me (tes@kbs.citri.edu.au) a log of the output
+and I will modify the configuration files in an effort to fix the problems.
+
+-----------------------------------------------------------------------
Index: /gsdl/trunk/gs2common/indexers/mg/SampleData/MODIFICATIONS
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/SampleData/MODIFICATIONS	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/SampleData/MODIFICATIONS	(revision 16561)
@@ -0,0 +1,664 @@
+TITLE
+  Parsing of Long Words
+APPLICATION
+ mg-1, mg-2
+TYPE
+  bug
+REPORT
+  tim@cosc.canterbury.ac.nz - May 11th 1994
+FIX
+  tes@kbs.citri.edu.au - August 9th 1994
+CLAIM
+  Mg didn't handle long words properly; it crashed.
+PROBLEM
+  Invf passes calls PARSE_LONG_WORD [words.h] which uses a limit of
+  MAXLONGWORD on iterating thru the string and storing into 
+  a word. MAXLONGWORD = 8192.
+  However, mg strings generally store the length in the first
+  byte limiting them to 255 characters. The word which was passed
+  to PARSE_LONG_WORD was an allocated string of MAXSTEMLEN = 255,
+  which is as large as we should get anyway. Thus when accessing
+  a larger word than 255 chars, PARSE_LONG_WORD would allow it
+  (less than 8192) and would try storing beyond the array limit.
+SOLUTION
+  The author can't remember why PARSE_LONG_WORD was used and what
+  the significance of MAXLONGWORD = 8192 is.
+  So PARSE_LONG_WORD has been changed to PARSE_STEM_WORD which
+  uses MAXSTEMLEN as its limit.
+FILES
+ *   words.h
+ *   invf.pass1.c
+ *   invf.pass2.c
+ *   ivf.pass1.c
+ *   ivf.pass2.c
+ *   query.ranked.c
+*************************************************************
+TITLE
+  Use of Lovins stemmer
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  local - 1994
+FIX
+  linh@kbs.citri.edu.au - 1994
+CLAIM
+  Stemming was done naively.
+PROBLEM
+  Only a few types of words and their endings
+  were considered.
+SOLUTION
+  Replacement with a more elaborate "known" stemmer by Lovins.
+  The algorithm is described in:
+    J.B. Lovins, "Development of a Stemming Algorithm",
+    Mechanical Translation and Computational Linguistics, Vol 11,1968.
+FILES
+ *   stem.c
+ *   stem.h
+*************************************************************
+TITLE
+  Different term parsing
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - 23 Aug 1994
+FIX
+  tes@kbs.citri.edu.au - 23 Aug 1994
+CLAIM
+  Boolean queries did not extract words/terms using the
+  same method as is done at inverted-file creation and
+  as is used for rank query parsing.
+PROBLEM
+  The hand-written lex. analyser, query_lex, which is called by
+  the boolean query parser was not calling a common
+  word-extraction routine as used by the rest of mg. 
+  This would be ok if the code did the same things - but they didn't.
+  Query_lex, for instance, did NOT place any limit on the
+  number of digits in a term.
+  Of even more concern, it would allow arbitrary sized words
+  although it used Pascal style strings which store the length
+  in the first byte and can therefore only be 255 characters in length.
+SOLUTION
+  Query_lex in "query.bool.y", was modified to call the routine
+  PARSE_STEM_WORD which is also used by text-inversion routines and
+  ranking query routines.
+  Now all terms are extracted by the same routine.
+  To do this, the end of the line buffer had to be noted as
+  PARSE_STEM_WORD requires a pointer to the end - which is the
+  safe thing to do (don't want to run over the end).
+  This meant I had to find the length of the query line buffer.
+  This was allocated in the file "read_line.c" by the routine,
+  "readline". Its size was the literal number 1024. 
+  This was changed to a constant and placed in "read_line.h".
+  The definition for PARSE_STEM_WORD can be found in "words.h".
+FILES
+ *   query.bool.y
+ *   query.bool.c (by bison)
+ *   read_line.c
+ *   read_line.h
+*************************************************************
+TITLE
+  Highlighting of query terms
+APPLICATION
+  mg-1
+TYPE
+  extend
+REPORT
+  tes@kbs.citri.edu.au - Aug 94
+FIX
+  tes@kbs.citri.edu.au - Sep 94
+CLAIM
+  Difficult to feel happy that the query-result returned is
+  satisfying the query - need to look hard to find the queried words.
+  Need to show words in results using some highlighting method.
+PROBLEM
+  No highlighting of query terms in results.
+SOLUTION
+  Mgquery was previously outputting the decompressed text to a pager
+  such as "less(1)" or "more(1)".
+  (Except when redirected or piped elsewhere :)
+  So what was needed was some sort of highlight pager that instead of
+  displaying the text would also use some means for highlighting the
+  stemmed query words. 
+  Two common forms of highlighting were chosen: underline and bolding.
+  These are supported by "less(1)" and possibly by "more(1)" by
+  using the backspace character.
+  A highlight pager will also need to know which words need to be
+  highlighted. Therefore, the code was modified to build up a
+  string of the stemmed query words for passing to the highlight pager.
+  Design Options:
+  ---------------
+    * Could do text filtering in mgquery before passing out to pager.
+      Instead I pipe to a separate process, the "hilite_words" pager,
+      which filters and pipes into less/more.
+    * Could do different highlighting or a combination.
+    * Could use a different structure for storing the query words other
+      than the hash-table I used.
+FILES
+ *   Makefile - to include hilite_words target
+ *   mg_hilite_words.c
+ *   mgquery.c
+ *   mgquery.1
+ *   query.bool.y
+ *   query.ranked.c
+ *   environment.c
+ *   environment.h
+ *   backend.h
+*************************************************************
+TITLE
+  Mg_compression_dict did premature free
+APPLICATION
+  mg-1
+TYPE
+  bug 
+REPORT
+  R.Sosic@cit.gu.edu.au - 23 Sep 94
+FIX
+  R.Sosic@cit.gu.edu.au - 23 Sep 94
+CLAIM
+  mg_compression_dict dumped core in 
+	file: mg_compression_dict.c
+	function: Write_data
+	line: int codelen = hd->clens[i];
+PROBLEM
+  Huffman data, hd, was freed *before* it was accessed again. 
+SOLUTION
+  The freeing of hd has been moved to after all accesses
+  (just before returning).
+FILES
+ *   mg_compression_dict.c
+*************************************************************
+TITLE
+  Boolean tree optimising rewrite
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  sosic@kurango.cit.gu.edu.au - 23 Sep 94
+FIX
+  tes@kbs.citri.edu.au - Oct 94
+CLAIM
+  "I am still getting core dump in "and" queries in mgquery, 
+   where the first word does not exist, but the second one does."
+PROBLEM
+  Having freed a particular node, it tried to refree it and 
+  access one of its fields.
+
+  I.e. code-fragment...
+
+       FreeNode(curr); /* where curr = CHILD(base) for 1st term in list */
+       FreeNodes(next);
+       FreeNodes(CHILD(base)); 
+              /* but CHILD(base) has already been freed above */
+              /* if the node was the first one in the list */
+
+SOLUTION
+  A number of things in the code seemed a bit dubious to me.
+  So I have rewritten the boolean optimising stage and abstracted out
+  the various stages - each file starts with "bool".
+  Boolean query optimising seems to be a tricky problem. 
+  It is not clear that putting an expression into a certain form will
+  actually simplify it and whether simplification means faster querying.
+  I have converted a given boolean expression into DNF 
+  (Disjunctive Normal Form).  "And not" nodes, which are readily apparent 
+  in DNF, are converted to "diff" nodes.  I have only applied the idempotency 
+  laws involving TRUE and FALSE, and not the ones requiring matching of 
+  expressions - it is a potentially more complicated problem.
+  The optimiser has been tested by playing with "bool_tester", and if you are
+  having a crash or problem in a boolean query it would be worth testing the
+  query on the "bool_tester." The token "*" stands for TRUE (or all documents) 
+  and the token "_" stands for FALSE (or no documents). This should show the 
+  expression before and after optimisation in an ascii tree bracketting format.
+FILES
+ *   bool_tree.c
+ *   bool_parser.y
+ *   bool_optimiser.c
+ *   bool_query.c 
+ *   bool_tester.c
+ *   term_lists.c
+*************************************************************
+TITLE
+  Mgtic pixel placement
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  Bruce McKenzie - bruce@cosc.canterbury.ac.nz (21st Oct 1994)
+FIX
+  singlis@cs.waikato.ac.nz
+CLAIM
+  mgtic crashed on certain files.
+PROBLEM
+  Placing pixels outside of bitmap.
+SOLUTION
+  Changed the putpixel routine to truncate at borders of the image.
+FILES
+ *   mgtic.c
+*************************************************************
+TITLE
+  Improved boolean tree optimising
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  tes@kbs.citri.edu.au - 12/Dec/94
+FIX
+  tes@kbs.citri.edu.au - 21/Dec/94, 14/Mar/95
+CLAIM
+  Optimising by conversion to DNF is not necessarily such
+  a good idea - can actually slow things down.
+PROBLEM
+  The distributive law used in converting to DNF
+  duplicates expressions.
+SOLUTION
+  Introduce a query environment variable, optimise_type = 0 | 1 | 2.
+  Type 0 does nothing to the parse tree. 
+  Type 2 does the DNF conversion.
+  Type 1 is the new default and does the following...
+    Do simple tree rearrangement like flattening. 
+    Optimise for CNF queries.
+FILES
+ *   bool_query.c, .h
+ *   bool_optimiser.c
+ *   environment.c
+ *   invf_get.c
+ *   bool_tree.c, .h
+ *   bool_tester.c
+ *   lists.h
+*************************************************************
+TITLE
+  Mgstat with non-existent files
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  tes@kbs.citri.edu.au - 10 Aug 1994
+CLAIM
+  NaNs and Infinites would be printed out by mgstat
+  if unable to open .text or .text.dict file.
+PROBLEM
+  The NaNs etc. were output in the column stating
+  the percentage size of the file compared with the
+  number of input bytes of the source text data.
+  If it couldn't read the .text file with its
+  header describing the number of source text bytes, then
+  in working out the percentage it would divide by zero.
+  Also due to some bad control flow, it wouldn't attempt to
+  open the .text file if it failed when opening 
+  the .text.dict file.
+SOLUTION
+  Only printout the percentage if we can read the header
+  from the .text file.
+  Read in text header irrespective of text dictionary file.
+FILES
+ *   mgstat.c
+*************************************************************
+TITLE
+  nonexistent HOME bug  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  bug 
+REPORT
+  cgn@totara.cs.waikato.ac.nz - 2/May/95
+FIX
+  tes@kbs.citri.edu.au - 2/May/95
+CLAIM
+"The big problem was that mgquery crashes when the HOME environment
+ variable is not set, which is the case when it is run by the www server."
+ [...] "I expect it happens when looking for $HOME/.mgrc."
+PROBLEM
+  The result of getenv("HOME")" was used directly in
+  a sprintf call. If the environment variable HOME
+  was not in existence then null would be used.
+  In some C libraries sprintf will convert the 0
+  string into the string "(null)" on others it will core dump.
+  (For example, Solaris seems to core dump, sunos 4 seems ok).
+SOLUTION
+  The result from getenv("HOME")" is tested before
+  being used.
+FILES
+ *   commands.c
+*************************************************************
+TITLE
+  mgquery collection name preference  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  improve
+REPORT
+  cgn@totara.cs.waikato.ac.nz - 2/May/95
+FIX
+  tes@kbs.citri.edu.au - 4/May/95
+CLAIM
+ Surely something must override mquery's preference for ./bib. 
+ If MGDATA is set correctly, I think it should prefer that collection, 
+ and -d should definitely override it. 
+ I could always say -d . if I really wanted ./bib.
+PROBLEM
+Currently the priority is:
+1.  Check if ./name is a directory,
+     If so then use it as the collection directory.
+2.  Check if ./name.text is a file,
+     If so then use ./ as the collection directory.
+3.  Check if mgdir/name is a directory,
+     If so then use mgdir/name as the collection directory.
+4.  Otherwise,
+     Use mgdir/name as the database file prefix.
+     This would be the case if one used "-f alice/alice".
+     However, one would then not specify a final name argument
+     and we'd never get here. Go figure ???
+SOLUTION
+Moved step 3 to the top instead.
+FILES
+ *   mgquery.c [search_for_collection()]
+*************************************************************
+TITLE
+  Printout of query terms
+APPLICATION
+  mg-1, mg-2
+TYPE
+  extend
+REPORT
+  tes@kbs.citri.edu.au - April 95
+FIX
+  tes@kbs.citri.edu.au - April 95
+CLAIM
+  No easy way to find out the parsed and stemmed words
+  used in the query. Would like to know these words
+  so I can call a separate highlighting program to 
+  highlight these words.
+PROBLEM
+  No facility available.
+SOLUTION
+  A ".queryterms" mgquery command was added which lists
+  out the parsed/stemmed queryterms of the last query.
+FILES
+ *    commands.c (added CmdQueryTerms)
+*************************************************************
+TITLE
+  mg_getrc  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  extend
+REPORT
+  bruce@cosc.canterbury.ac.nz - 2/May/95
+FIX
+   - 
+CLAIM
+  Repeated code had to be written for different named
+  gets but really the same type of parsing required.
+  E.g. one might want to use a standard method for inserting
+  ^Bs between paragraphs for different books. One doesn't
+  want to write duplicate code for each different named book,
+  rather note that each book should be filtered "book" style.
+PROBLEM
+  There was no way of abstracting out types of filters from
+  the name of an instance of a collection.
+SOLUTION
+  Allow information to be given with <name, type, files>.
+  This extra info can be provided in a mg_getrc file.
+  See man page for mg_get for details.
+FILES
+ *    mg_get.sh
+*************************************************************
+TITLE
+  Boolean optimiser #1 with `!'  
+APPLICATION
+  mg-1, mg-2
+TYPE
+  bug 
+REPORT
+  triffid@iconz.co.nz - 20/7/95
+FIX
+  tes@kbs.citri.edu.au - 27/7/95
+CLAIM
+  Complained about not-nodes. 
+  e.g. complained about "croquet & !hedgehog"
+PROBLEM
+  Boolean optimiser type#1 didn't convert
+  "and not"s into diff nodes.
+SOLUTION
+  Added code to convert '&!' to '-'.
+FILES
+ *   mg/bool_optimiser.c [mg-1]
+ *   query/bool_optimiser.c [mg-2]
+*************************************************************
+TITLE
+  Consistent use of stderr
+APPLICATION
+  mg-1
+TYPE
+  improve
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  tes@kbs.citri.edu.au - 11 August 1994
+CLAIM
+  Inconsistent use of stdout/stderr in usage messages.
+PROBLEM
+  Sometimes used "printf" and sometimes used "fprintf(stderr"
+  in usage messages.
+SOLUTION
+  All should now use "fprintf(stderr" in usage messages.
+FILES
+ *   mg_compression_dict.c
+ *   mg_compression_dict.1
+ *   mg_fast_comp_dict.c
+ *   mg_fast_comp_dict.1
+ *   mg_invf_dict.c
+ *   mg_invf_dict.1
+ *   mg_invf_dump.c
+ *   mg_invf_dump.1
+ *   mg_invf_rebuild.c
+ *   mg_invf_rebuild.1
+ *   mg_perf_hash_build.c
+ *   mg_perf_hash_build.1
+ *   mg_text_estimate.c
+ *   mg_text_estimate.1
+ *   mg_weights_build.c
+ *   mg_weights_build.1
+*************************************************************
+TITLE
+  xmg bug
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  cgn@cpsc.ucalgary.ca - 22 April 1994
+FIX
+  cgn@cpsc.ucalgary.ca - 22 April 1994
+CLAIM
+  "Serious problem in xmg, which I fear occurs whenever a query
+   doesn't return anything."
+PROBLEM
+  ??
+SOLUTION
+  [xmg.sh 201] set rank 0
+FILES
+ *   xmg.sh
+*************************************************************
+TITLE
+  Unnecessary loading of text
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - ?? August 1994
+FIX
+  tes@kbs.citri.edu.au - 12 August 1994
+CLAIM
+  Mg was loading and uncompressing text when the
+  query did not require the text.
+PROBLEM
+  There was no test for the query mode
+  before loading and uncompressing the text.
+SOLUTION
+  Only load/uncompress text if query mode
+  is for text, headers or silent(for timing).
+FILES
+ *   mgquery.c
+*************************************************************
+TITLE
+  Man page errors
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  beebe@math.utah.edu - 16 May 1994
+FIX
+  beebe@math.utah.edu - 16 May 1994
+CLAIM
+  Man page errors.
+PROBLEM
+  See below.
+SOLUTION
+ "The mg_make_fast_dict.1 file has been renamed mg_fast_comp_dict.1,
+  and all mg_make_fast_dict strings changed to mg_fast_comp_dict in all
+  man pages.
+  A large number of errors of spelling, typography, spacing, fonts,
+  grammar, omitted words, slang, punctuation, missing man page
+  cross-references, and man-page style have been corrected."
+FILES
+ *    mg_compression_dict.1
+ *    mg_fast_comp_dict.1
+ *    mg_get.1
+ *    mg_invf_dict.1
+ *    mg_invf_dump.1
+ *    mg_invf_rebuild.1
+ *    mg_passes.1
+ *    mg_perf_hash_build.1
+ *    mg_text_estimate.1
+ *    mg_weights_build.1
+ *    mgbilevel.1
+ *    mgbuild.1
+ *    mgdictlist.1
+ *    mgfelics.1
+ *    mgquery.1
+ *    mgstat.1
+ *    mgtic.1
+ *    mgticbuild.1
+ *    mgticdump.1
+ *    mgticprune.1
+ *    mgticstat.1
+ *    xmg.1
+*************************************************************
+TITLE
+  Man page overview
+APPLICATION
+  mg-1
+TYPE
+  extend
+REPORT
+  beebe@math.utah.edu - 
+FIX
+  tes@kbs.citri.edu.au - 17 August 1994
+CLAIM
+  "Write new mg.1 file to give a brief overview of mg, with samples
+   of how to use it.  Otherwise, users are likely to be completely
+   overwhelmed by the number of programs (about 20) which might need to
+   be used, when in reality, only 2 or 3 are likely to be run by end
+   users."
+SOLUTION
+  It was thought that mg.1, written by Nelson Beebe, was very useful
+  but a bit too comprehensive for an introduction.
+  Therefore, two man files, mgintro.1 and mgintro++.1 were written
+  with the basic stuff in mgintro.1 and slightly more advanced stuff
+  in mgintro++.1 . 
+FILES
+ *   mg.1
+ *   mgintro.1
+ *   mgintro++.1 
+*************************************************************
+TITLE
+  Parse errors not bus errors
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tim@cosc.canterbury.ac.nz - 2 Jun 94
+FIX
+  tes@kbs.citri.edu.au - 19 Aug 94
+CLAIM
+  "These two queries 
+   (which I typed in before I knew what I was doing!!)
+      > The Queen of Hearts, she made some tarts
+      > "The Queen of Hearts" and "she made some tarts"
+      produced the following result:
+      mgquery : parse error
+      Bus error
+  "
+PROBLEM
+  What is expected to happen under boolean querying:
+    Query1:
+    > The Queen of Hearts, she made some tarts
+    will produce a parse error due to the comma which
+    is not a valid TERM.
+    Query2:
+    > "The Queen of Hearts" and "she made some tarts"
+    will store a post-processing string
+    of ''The Queen of Hearts" and "she made some tarts'' and
+    will have a main boolean query of the empty string.
+    This is because the postprocessing string takes in
+    everything between the first quote and the last one.
+    An empty string is illegal for the boolean grammar and
+    hence a parse error.
+  The problem stems from the fact that the processing of
+  the parse tree is carried out, even though we have a 
+  parse error. In the case of using an empty string to build
+  a parse tree, it is likely to leave the parse tree undefined.
+SOLUTION
+  As soon as we find out that there is a parse-error,
+  we abandon any processing of the parse tree.
+FILES
+ *   query.bool.y
+ *   query.bool.c (generated from query.bool.y)
+*************************************************************
+TITLE
+  Perfect hashing on small vocab
+APPLICATION
+  mg-1
+TYPE
+  bug
+REPORT
+  tes@kbs.citri.edu.au - July 1994
+FIX
+  alistair@cs.mu.oz.au - July 1994
+CLAIM
+  Mg could not handle small collections in the case
+  where there was only a small number of unique words.
+  The perfect hash function would report an error.
+PROBLEM
+  Rounding of the arithmetic during the calculation of the
+  parameters of the perfect hash function was resulting in a
+  combination of values such that the probability of a hash
+  function being found was very small. This led to the limit
+  on the generation loop being exceeded, and eventual
+  failure.
+SOLUTION
+  By using ceiling rather than floor when converting from a
+  floating point value to an integer parameter, the arithmetic
+  is now correct for all lexicon sizes, and the probability of
+  each iteration successfully generating a hash function is
+  sufficiently great that with _very_ high probability the
+  execution loop counter will not be exceeded unless there
+  genuinely is no hash function (for example, if the lexicon
+  contains two words the same there cannot be  a hash
+  function).
+FILES
+ *   perf_hash.c
+*************************************************************
Index: /gsdl/trunk/gs2common/indexers/mg/SampleData/Makefile
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/SampleData/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/SampleData/Makefile	(revision 16561)
@@ -0,0 +1,4 @@
+DISTFILES = Makefile alice13a.txt.Z MODIFICATIONS 
+
+dist:
+	ln $(DISTFILES) $(distdir)
Index: /gsdl/trunk/gs2common/indexers/mg/SampleData/alice13a.txt.Z
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/SampleData/alice13a.txt.Z	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/SampleData/alice13a.txt.Z	(revision 16561)
@@ -0,0 +1,2293 @@
+ 
+
+EÊ%EPq€
+"NrD'D@©âdÈERL)DÊ$ Š )Yä
+K
+`I³fMcRÄÈ#FÌ
+ Èy£ŠÌ:
+ëÐ)ãFL9g@ 	3D0dÊ#GN7gÊŽaJ§ê4tÐh¬ãfÌWeæá:F­7r@(H«L9tä€£4Í7 ÞYÛö­žsåÔQÑÀÓÁd°W9y@,ÅCÇE@'o¶9\&ô_sÞ°Ñ¶
+bB
+%z9éÒŠOÏ¶È
+2ÃÐ1ìŠêÔtÞè	TW1\Ã0v(¶ÜáÊ¥«(O
+y8dÀ
+2µ
+0iÎža"	6Ä_
+ Žà
+|òhŒöÚSÉQÆ
+\Á_
+oÔaU¡Áñ`  ¢tqÔVnäqOYžUUl
+U[ÇPføo F[uteÛpzÁDUVH¡üñegaÜbÌår
+XUsÔ1Öm¹¡Ñ
+ÜR#ªöOUµ%X^\þUb[Zý( !¡rÌU
+×qv
+FÍ¥eXvÔPnh&Z€Í1
+£e[JØ'ø}	[Yå5Æpä!Øh UŠ^7öØ_iäõãRgFG
+üñ%á]¹§ÆQG­¶Ú}sTÅW"v«tðwšnÚUkhU×WaÝVæyœqb§ÂF\Ã]¡
+$Uæ<Þâb_mkgÝV%lnÁ_l Üáí[§
+yâ³i^¶¡š²}Ÿ1V»Z¡ªVÂU\Á_¹rn(§Ã.F®¢çj:š_ZXzŠõÚUn°)ÇpIší
+²qÛlÝp_
+cX^5ó9GcmóS/n%ÆvžšF
+ þ8udUã
+iŽšyŠ\Ò¥QeTiuÕÙ+š³ÐñÆÕYoE6Ëß v€¡XøÝt
+!
+@
+[ÇvŽA
+b 'L:Îä_.ù.
+¶iš NyífiH|±UÔG(ÅSPÝ>èw!öØx©5Õ
+
+ä9ažq§N}s¿Ÿ§VUDµ
+dïú|ÅýøÆ_ŠýôS6ÕI
+QÄ	SXDQQEHõ'áWxF&Âd:kgÂ	œf\ÊÇÈŸ
+H(à&@Ä	IšB@`*0aR!e*$A 
+f°>
+ÒCÐ	Ob°
+Ç@<	Ž¡XÃ!¶ï$AÂC€ÐÔ°FÔ¹Ãì-sthd£
+B1TÀO01ŒFN9Ã"
+)ß
+Ír$tE
+o9m
+ì`c'ªé%&bÌR±ã5ðç+u\ÌT
+×F
+Ù¬eBãcÆ9æ,ŠrÀ¡
+eøämn7­\í
+k
+ÕÈªQš*ò
+X¬¥!
+Â=©:).¬Þ÷v·4¡
+?`@dÁŠâ#gñkZYvNiÈè%-bRÈš­á,Ø¬¥ºòKAê0ÃÔa~`M@d
+©Ñä«R=²êGQÑÓ
+§hÝ42ØÇh$ÏÕAhüQ°¶'
+
+CÛ
+ÍJãÄ6	eh	:š
+)àÏÊà«kò	pë§Å-|Ö
+øC
+Zð-=í `z^ÒÂPÌÔ¡JZQÀ¢ÞÇšxE·ç\ZEþe]s)Ó¥¿1
+
+`Ð€)\QDàTº
+Éš<Ó+è\pØ¥}òÔ+
+÷Èš
+RÈ$t9¬aoi3w ŒŒøå6Ü¬ä^DVÝu"zmÃäÔ!€,ªcjXÄzU
+X
+DË€ŽÈ
+
+f@æÔ®  «=Nd^ÛDMJÉ8NyÉB`M `€TåS8µÉ4Ë~o+fX
+DšÚ21cCèH±=ŠE9°áS6[UÝ0ôM@
+Xn]9÷ZånÙòÛÂ¢2Vmn§ I)UÀµëÙ¯¶·
+
+ð%XA ÂI@ð0Øý¡Bý\)(ø		6PP0%
+
+ØÞÊda÷HÝ
+bÐ0ÝuÕq?Äé&lâŽq¥Feµ\f+€Tò$¯>T=MÄ×EJAqÉŠãê·
+ÀÒ\Ž¢
+¯¬Ús]IµC!;J9-ðÐºµãAqY9!mVÙñ¡X£Ö>É0áãUvµÖ­
+¹Y
+Uèº\åÚ2Ä·/K¶¬¬*[ë×K±)?×CpÐ
+rZŠ
+Ž
+e¯þmÆK*ªUEdË[Ä|¥GF¶YÌÔ2Å+±TN.ÕPÜdªQ2šõLÃ=Ûš1{5M§r tAçZŠ7&ŠaÒV}A>Y¢æbÁf
+3ü7KD£²hØÂ&ã,46QËlBÉÄø¢MØ²âGoÆ«*œ
+äWÉÕVÃ6=2{k3`P5ÖšÀîŽqJ÷ì2R©|ZBoëÒ{×è¢Y	å¶CmÊŒÎÔ|šqæ
+rPÂapiãE?vÑ9ÄèXåÏ¹ùc¬Í2$
+Íe)MÐÏÚãe*­Öæ=²ûÆá-Õ2±;×ç0u³a7SnG©¶°Vp¬€8
+ðž2¹ÍZ®»ËŠÑ©
+èÐÒGHa6êW)ÀÒùª+aÛMq¥wŒ®r°yY£Ñzqd7ÅÅÌí
+Æ`ÚenSgéÂáj\in©Ùy²!ksÈ/ìšäÕSüiöº
+
+¶$âqú2pRaQ=OÁRïäyt®vš¡³f
+ŽX3Ÿ*
+õU+(³äBÂF
+S,0<A
+9Pòó»{u©«¹ fY÷Ð­
+."ðBT,pØã
+zXº>)
+EICiUt|y±PÕY²TåÓF'kWCzF08me·,µ€#$Ì×xÞâQ '¢WtA5sÆÁ,xWr`à.ÅÁ[{²]ÛTF'³YmqÈº6U#fà^4b^üza[sptÅXQqY^R%
+ÇOjvŽµEö"^Ñ7|'ð1ÍSJQ]3Žuh gBxSBã' dxHÈtžc
+PY#-!&ø<QEÑi
+ØPÐä(@¡,÷¡n¶rPeà)P$?ASÄ÷fSÔ.ACyZÑãEW5eòLAŽB^!uÅ&5úÂ)ýq}ã@ÇV{òË1ìôzøA^16Ã¡ªh\¹ET.
+nâzlÒYUdF$²R{È'ÃÑ@H1pRJÓohSáô4"f6²xMtp-ÐÖÀPÂ2eükRásp#gJFO!%¹)Urà#+F5Ù±(ÇuAbl0yÃ¥æõhþ§
+x¥WÏñÄp;Åh;g..¥D5ôb.äL-Á|€5­DE4(³
+s}ÎAbIå×SIeò4®a\O`Ðfå\¥
+'°%WSKUŽ*Ü3wbFíx-w­èJÇâKb#TyÁ2a£I@Zñ
+"ÓŸe"­5uH!ËAóP`¡b£e°£~x­±m)+Ê4"7CÈ!(!A"VPiôÑn0
+Åu»vÙå^¶žWùÁ]
+AQGUth
+æ¢)ävYh
+S-e2wm×^oRw<÷IšXà(%©TgŽ%z÷Æ\Ž
+)A"ip6AhR?²íøqKÊ±Gg*è).Q?NÂ|6ùøÔ+
+R{±^/ù\¥J!"]îU«wxR °-p-BHÆÑsVTJ
+óvI_Qó§ÂáÎ%,+yÃ)å"M'5ü¡ñ	­c/Z0 "bÇè³œP"VPáb×ÒUr±Qé4
+Ò-íØmC°?VYà?A#crÇqCQ¬ÂPø!ŽEØB&]ø¥Ehøihñ8 A bªož2ÿGtXjl{¶ÿè
+hfs°T`A[šQ¢t §
+Ðk
+
+ú§óIr0³W[Sñ1Vºò£iÌ8RâíÄ>x.(ªŽÕsåHoØ(kIo00©a±%O%T€.õÓDð#azj65%Yiö-)ÚŽ§18µ§.è[œZ2ø|â¥ŠÅš1dyzÊWb<z±GO39Ÿ¶èp®2-°È«Ÿªm­Å¥q¡
+iÇO®WgbxªR!¹d6Ô['-8èÂŠ~
+ ,fC\Ø:Ó€
+ÉLQ	n5²Ðt!Ù²¡Ã(µ
+}¢
+Uá~ž¡²ÛIr kz®g àl
+à%es.§g9kYbWwqW¢®v
+2¯°±Sãcz^­+]ØQ?À«<ª²bµ*Û
+%kîê\R{cW[µHAµœbJÑw­(2Ç('
+`,q(J(d ·Æ*¯Êah+R ÒÃ!Ø(ÛØoái&zpÐyžiÈXgÀ8sáWPögžJlÊ€O©=Óú«GŽ
+Ãl97 šqü±ºhÐ+b
+€8vJÓÂÂyÁ²wµœÈ_úvUz^¯%œöñZ
+WPn5
+%ÚÁY~šq¡'bszÛî5WXó%g©pž!",V­Ë'[cÉrT»ERi"pGûÇ'×¿/qîS}÷Ÿ:ÕPsõj%#m4}žŸZ5_Zs5Ÿ=÷üq¿ Ñ-ò¯4âH+kñR&¹¥VtÀ8ÍYÛvk2ørþys
+¹
+CieÂY=6@["r{lB^u£3ª%¯¡ŽÇyñÙ-àÓÂ·	*\
+«Ú[_ó!ŠT5Jy7UGÅš	Å
+
+¡%ì¹äÁaA©[SæÜ§c5Z<b³4bö2©l U;W³G&Uv*ö!Sm|ÃP9;	qõ.1
+x±%s¥­1·e,ry1é^12f¶_IÇtésN±uýråqÌgO¬wGUEBå+œòÂmŒôJb:IqÂ'Zd`oüTEfó7P0ëR¯¹ÈëFÜfºjªîóœ·œZÑœI{Éuz2h !eÐP.¢X.â^W!6ò³qåÉ%GÃU¡ª')-HA)f5G¡ñ
+B³uÝ¢gÍ#bž¶±9>Ùo"2±Üµ}2B1
+XAåÊàÓ|t[u(d¯E(§»ŒfÃ§[Pá"²XZÑÑ'œwsp·Ç[Ñ^)jhVèãjS}ÐÏ2
+RŒCENc­(&Ë3ÏPSÏŒW#7ÇAR"z²d>gÃgF9eçqü\<§ù¬iÖ
+>º¶u^Ã"E¶,dÇm§²Ao  Ô¥%Ö>ýcF°Û~ØVÂ]"#tj¯õÎöÕeÇ'2bwaq(7"¥N  ìþžD
+È{âTmÑØôâž¥bÙwv&È
+Ûy²
+#Y3{³¡·uÇ{\°7RXx2rÇÎÖf÷Ù\JÄD5	WKÍ3/žVN!zB
+ÿ©)7×&)Mku*²Qq$çÅÞÚ{ÕeÂál{ô/¥ÜÅÚÖYL"fà÷¢L«àá³ys¥¬ÂÖtF3ØžLVý¬¹uÐ
+
+MÙNHÁŽŽm2
+«%Æö¯€BX¡ÊÆbi
+«
+éRŽ%FžãX¢Šyž¡
+e/:¬¹ÓÖ[F"«!nKÎSY- íÊ"-ÄuÊ>©TÅºÕhÑ[yÁS³FØoEQö²Xø$Åâ,Çg,êheã!Í2Qr«ü^bqÇ¹å$,nSøQTžXEø
+X2
+«^³
+cfpÂ%Ð-,%*ËÈ'*]Øû©ª*Žbf&Õ¡Á³âFMÔ²žsJéÆRlMÄA5äsPäsð'ê®÷ÚûÙŠÔ#M3;ßÔ ¡fT éhó{K1)qìá¢%lþf!šLhÍPl²éBÓž¡ìõ×÷ášºµ}"¥= U!µRà?Öó£N
+aåh7r+ÉÂìiâi>MRÊ|ïyG#îuQ*Wi%µAVcÁí\iÄk5éA%¿5ìPV÷ºf¢Ê+±zòœÓ¢9š³ø1§#
+vÖTPOó°ÊQ ŽL$µGfì&å," 
+)äH'_;GË·Kïõ=þÈ>­âÑAvRÙfuq6+rcž
+¬ç~[ÉÇõLeb°F5l¡ëT!§x9[Fáï­zËÔ`L6
+®m ®ÎèEëÀÔµ¢)d0Eæs%ÂàÙû·Tü¹W¶mH1€|6ž$!,ý¥Î9ÙÏ5ÔLXìVy¥/,Ò
++#h`A5)[¡díÚ£
+ôbØ.""\SÕ\¬£5ÝIÊcp©IÆäb
+8ÝiAÊ
+îtX}µÍÄHôÁí:Ë«"Ó.×8
+í÷.Ú3&°Êå·d
+¿©üÕß¯q5Ù?O(ú
+±ýÝçýû¡æÅª!VnO²Ø
+Ðí±È0dI>#²Kêñ`¿°ÒÒÔâ4­€XJÀÉâÇx
+Q.C]x
+ ¡]}þ ]Ôâc¥Åq¶WÞÛ»&A¢£õ
+J,C9Êå±=
+JÍ!1`ä«("PRÉ* &DA0gB
+W
+JÁT¶©`Ž_
+rÁÉ`Ô^0
+Áw¶*vèQkhO\ÐÍC,2(Â	!É"tG%û qãë§Je3«iù5áO@	hlóË^899ØÚb
+«	ò(²d"LUú
+,ªõgE\ 1{h·É?ŠúT^yÄi»D7ª×)ùm²í}9(!Â02`gÇ°Í
+]43s0öüß!*Õb\
+V}6ýÀ+H°£±Íöìž
+°"ë±7óáJJ«o§(ËU¶zÁíîà·[?µ8ÄÀŽ¢áùh#ì
+D7Rx]àÝì]5
+$ÕUË-°1
+üNÆc£Fj
+\ÊSEDàÂ)+`×s¡Fd#V
+*ÁO*ÎîYŸD7øR:[$ËPË÷"ÌA5¢pÑÀÈT*Dk#«ÁS 9Š`ÄN_0
+ç#
+ž{O [á
+
+»üŠrX(
+âEÉ ×Â_Õ[r(e¢©X3£èßhÓc~ÊnëH:Zšá6
+Ó)Æyx¡é
+T)	h@
+Ç
+b§nèâq·Á	:²Q%^Ý\
+h¡±FD÷\ä
+@)Õ.4,gøÄB
+)G=Ä=â@Äo
+/S%6AÓ
+H|ÈÓA5?6ÔlÎurTyàøy±­C×:É
+I'ã<Œ#ï€ÙOZ!+æWEËûá'ÊDÚ=64d
+pÑ­=á'òKñXIÆþ¢wHZr£<€jä*B Ø8 V(
+kEX$ôM @ê'nÀwž °ÆÂÎ~Q¢oxÇÅpQ:y!
+ÃLÀDq.ÊßiÈZÜ
+â`.Z)Ql­ %eNÅF3ñðÞ¹ð=¢ô
+p1Ú»ZC\£1X<à
+*ZW£=ì1"
+Ll#'Ðd`HÓÈ¢DÔ8w}b(è-%s
+ÈUÒŠ9`»
+yÉÑá5BsäÄ]pQáFS#OÐ0eà8	D0`²ðNtŒ1,
+~8RÈ]ã·C&eBKþcÁ.É²ºn
+¡â+ÊÀÆÝlñ7>¢eH.í0P€ùæÀ'9
+
+VŽÃ0Ù<É{ÄDÎÁáœºáe
+ìb$¬âRJ
+ÞJ@¥±©69Æû¢1á5ç¶¥¬nÖôBáJ3)Þ
+(°ô5éskïßŒmôF	º.!øè
+ô»ÖÇ`YÇ€æmGn5n)})_š+ÆWd	eÇœ
+?+Zk@ØòÅiK7ñœhj·å)l§ðà'\^0
+EŠÁÓòkKÃÙÜÂ§xÝ`2YE$c
+¥,¹íGüa
+s Rþ5f2Ü[Ä"­k;~<mf1Z6h@¢ÉY²Õ*°1&4ÌÕe/Š§ ñà(D¬(ËÄpA­Eè
+Üå{ÊEÒ1J 1ðšDô'¢RtñÐŽÚ€ÌZã#­ð
+²¡É$Þ£}ìGÅ¡­<ÌD^èóq?ôD[ª7§áòõS
+Ó	
+C)
+%€ûQáZx90ÇÆÜhABi(ÂØ6àK²Ç!àeF_òQôâl (nI
+Ì6ÞÄáz+25Q0ð!I1G0K(Á £[è³A0:Kç4iP}ŽÓ©Å`ëDªÓuÁ×:g§ítbÄ2œ30RýaUÞyª@Hã
+HwòÀÀž3áPœ(	Mž5ñ:Ê
+¡ÈEvÐß!ú`>3(À#Zp
+±÷Ò'áÎ#é
+"lÀ/ª^Ì¢åæNR-Áì[V·Bž1Då
+:.¯¶
+ T ÌÎÑ1h¡ÈÅÌVbnÚY2OÇGÄå7édnãÏ²Ô;ÂÂ,bzK
+oÝ
+TÍ@
+M5Î*tÁF
+ØñJâÅ_jìñ1àÀUa+#Æ
+ÆLÓÂ
+Ê®0²Öþ7â
+$/päÖ±áÈŠñAsÖx?,JY€Ú9ÃâÊóG6©$û2ãXÇHjX Ò^h}_°l¥Z²ã(BTtòYp;7âÔ pT
+&]Ã 0³â9ÿdä°Ôð
+h6²
+Ýš
+0i
+Ôb×MØû°øÚ)i{Ô
+ÐðI®¥øTc€!1ËÒ{ZÔ<òåÊgUŠØË0b[ÄÑçé®UY:©æ	6I"2yE/gè&-Ôü`,H&Ö°±ø
+¥-ðy$ÈvÅõ8
+fôTrÚ§Mxñc~Ô{ 0 2L(9
+H)	®	6%â	JÉÓ F" )0P§@D-%ÀSùA?8ÃÀ/TÅz%U°
+h--¬ŠÍê ,
+J ésbÆä@8IÒ3Z³þ$c
+zbãÈ¥?¢>{
+l€q	°Ñá&¡¥DéìÜKÀØ¢F£lJí¶ç
+Ã¢e,f/ã©»É{x[ÂMX>
+9Š,è«zÖ± ³DWXVØëôRFÉP âÖ©3_Ó¬PEÊ©³¬,+>µ¡|NáLLÑ÷hlÄäžÁÍõHKuÜ
+IRelaÒŠÜšTÕô
+-iéí
+i2ñ¢,+Ö
+ãà±Øx"uõmâ%!Á@³QA2­6EîDºòm}è=0±qÑªLb3¡åjŽóYkFñož(Á ÅeábüÁBÑ>éÀz[pD[È8»~žÞÈg'°*L#îãTéð
+I¿p¢£4K&2X1â
+å¬!Sµ'ò-Caôj3R
+Ù
+w^+s
+#wÒø!B}
+)H*ö3
+%Ð9,ö&pÏ§)-9M,
+&Ý`>03Åtg y{rqSR4èT®bn)Ž¡Ç$Ki»xuì
+
+I#f¬ð>µ¡|ðê\ªÈUlÁ$Ùð¢0
+
+¿sv`Ÿ¶Ô7º
+EÛ#]ŠšD+{Ž
+aVpý«BýK>fR%l |¡¡Ã&öåYå/U$Ââ±§ùŒ€87ëvsÑ+Ê·Ýô
+1%÷äBB!rà;O=ëk
+Ç2
+@§NéÀÁpfMjÞ.Ø.ÆPTëZSŒ±ŽÙÊ[í
+«÷H¹©"ã-®Ús_Clø€f&s<)OïË¹ útjÚ0cÇ/ëcÖËÌ[
+
+ø:i)ITgÀI­È`fK@ènzkÀÆº¢%E
+)¶5 $ÂŠ€\Ð-wH³t48J#à3V8.ûÆ¶Î·š}ÆÒ€heÕ*è%
+^ÝqÌ$ç4&Bb×rÄ 8gšyd!tO5ð øÄU
+m€Ó°År
+=ZPèTv\3
+¹ë: @o/á\ÈPjÈ©ó.Ùhhè]&/ÁTÔ§øEF
+Ðz öDÈ«
+;8'éž3o1%»
+Å)nR®òË?ô4ŸÐ§i
+ŽIVWúR"¡Y$ý]2¶PºÆéj©LT"R¡
+
+
+0¥
+6\läËÜRÎÄÎÄhÇÜ¬e(
+çÈÔÂž ·©0Œ$eKŽâ"ÛŽøB\Ê­Ü¢à^BPÎœ€¶×xòõKIvÙÖDS\þ£-B .,
+ç¢äé<·?¬ÑÞká¶QÎè"]û£­qø
+GNO]š}»oÓÔ}·¿§@:I²ÂÁÍ€ãµ£	©)Ò@ßÐE¥Zº)£}ÍÚXH°ÜÚ$[åw
+[O±ÍÞû{]Û¢Æaù9°hkÄGzÁ.°Î6úhÀAY(E3šÄ@"%r`ŒUN¶<À8ïÆZŸG L:&8P`"ê×ª1%à
+òàðÆöGM
+8Ç#ÇB;âüÝúÑŸ Åo|¢å4iQ>xKŒKPfj\
+ÃŒÐyEò-Br£@ÐÝOYù¯
+é`M8Êµ
+Í7þ]Ç'o÷š ¯Rì
+Oáº¢BBjèt¯9]!!Þ'ÞQ?{ÑfŸm Æ¿~10(a ·³aÀ¡t]*£HG
+Ñ$-D
+ïÎöª0¬
+ù/Hj6t6Iöa(èÇ× I~SI
+
+U$Bñ~ÙÂ+(ª»ÓöñC*}ªŽÑ-dÑ1h*ñm:OàeMUèÚdÊ?"/îö4-lÐgî5|0pI%×Ð%šçf±MžÚÃ>:ãŠv6\Êñ©Æ3 È]hAwßYL`ÑÚ€Vz×·£,€É1S6¥îË}8Œíìm±²xñýÊ£1êZ¡	'(
+dhjY9Pì
+-(
+bfÃ÷kØeH%¢ë¬
+mA»\4P
+'
+òdi/-@
+ß»ÓŽÛÁè
+'õH»ÕYå·e×1­#C^Øš-±>*÷ò*dgÉJD5k«ç«ê©EX\s2º6°Å§È°xE7êH¶Ð
+¹yMÝÖµ=Iå^&a\]ªq
+ÛÜ­ÀsUU#xa·cZS~«Èc9
+'%á€£­áÔÅàHò9füdNÛ{QÚbl@.=Zfê¯õ
+ÙSÅ;"pb3 â¹2QE[òÎB»B}ÏdßsO÷FB
+Exûnc"±`7Ö8H	Û"Ò±E «îð"fV9
+
+ÂMaR4ï¥Y³÷(©üÐÑØ
+ûË)T0bä.ëí-ôÑRŠH`l+ºGøB$^ÇLzbŠ¢©"d/jÚŒ
+(UYFÖ$í)²LªðK@&#
+oüºÊç7k¹Wp4$š¬æ,GyªóõÉtàKÚPLXT?ç¡ÒphQÃBâRŒÂGÁ*	ÅÝ(*[`q¥tÁ€[
+Ìdû^ž/
+ xSOÒ3J¬f01
+hýâÆ¶[I}Øóä Š'=ÕÑnË:F³âÁD[#üã57("
++o¬J®}ê§?§£)böãQ§9Rô<áLEþ\(³º£A×Û}ê
+ÀºÈéŠ^&i§E[ªPœê¡
+l'ÌÇ¹)°ß
+u`ÃÅA©Y2Ù*o"qûh»ª)á(Ï%XiïVy¹&+ëIl[¡,Ë
+"ß	'²ÒZÝ¿
+V581B(+LcOQZ°³mYio"ßú}·ã
+k¥]áP\£ÖIk§K«ÁX
+w>dÔ$¯%r53Øµõ\ìÓÐ C¹xµN
+Æ]b6¡+ù5­±MãåâŽªÝ(
+XËÛo€]ÜØžoê0á°ØºQŠNóYç$3
+PÙ|2jñ.e"ÛÅù?ÅrùD¯sQQx
+²QÁÑê9.Â	AÂ'ègyÊWãþ
+€Ù<nÇ:W-Ü^
+r-xñ\UQ#V[]¿pÔHÁ¹
+Mi áhã}8³À1i¹Ëñ#R9É¹ð5€É0ªLâbjQT&@ÿþ¬ÍSÑ-8ÐÈÕ²b	L4gNÚžm,ÚI?¢éTS
+é+È-ì,ºg]ÐU3
+dØ@!åŸMcÈÃµã3þ­>4RpNsè*êðÆYÐ°Õª»Tszi}3.6¢t©Ú!ÜJ$W	Ù/ÀÄÛ× »Í-º=ï,¥ÊTÚžIäëŒîyËè[¿¡î*X®k8Ä¡$ÿÊžgs55©AQ¶6åv4$­1ÓçBa7ÈHrpP4Ðí!JŽe[Ç+jŸ±Ð©Ë9ZwÕŠ%Ò6î×~Ì,k¥ª°_
+×úà
+BÊØàžj"âÄá©²îjÕïM(÷9ìâ{
+8ÑÓqûH1R
+_Î
+¹~Ä<ÎêñYÕœTâº K[U9yoWGÕ\D<±Zà\ßxSä|c¶qx
+=1r¢··{¢Ü¶ÅeË±KNØæñA·ÊÝ!lÇE<&
+zpØ¹v|_€ò[T!wÞÏð}"éÚFÀ5)P ÛËo=ËU
+Gµž¥ãìRd%ŒØTÆn,œ€'Wh}«={/F¬ãíš¶phÒx%ocŒG+êÔ¢g\51gíË&_Â÷õ­«ÂCqÈ
+^%ÂŽtÃ9ÔÔcÙpSA§HíÓÀ,ò×Ûà²íç 
+ßžFj
+±Z^0ÂRò
+%  Ïp® Ò
+ÝÞ
+;£· +wÍê{Ù#ª%Ug«ôÌÃvåkDº=^s6
+*E[\¢7D'îfAVÐ€ÄšnW¡O0¥ÐŒfe×á[.Šp°TØ
+Áæ
+]-ÖÃP ÎÎ
+KÈŠ<?Ïü$æ¡èÎþ(Xèè€|ºjäó`(ÐBiD/¬û (åõ|Á
+À
+ä³
+¡3AkÆM~
+²!%D
+H[}â±',îÂôXT
+x³0dÛ§Z.pC¶R^Ás¬&
+Omú0©ôð|àÏ*
+4Hµ*ÜŸòŒY3ËžNÌ³*¬Zú
+ºuCn£Pâ	3sß\±Q|²
+S·"Æ¬ 
+¿œq¯Z³ÂŠ,€À»|Ã~QJ\hK
+ô$"Åèë¯/[,F³liÜ
+PXc€&Ö«{,X]ávòž~:FÖÄYjiÐ6¯qëÉ¡-;ãtÛûúõèrÚThäé&&LTDdÁÊÞ#÷¡ÇEªp^©FÒJ- 9ÜÎÇ¥^ÓB=5ÄGÓÉâÆê|Ü-ŒàÅ|JÅzC«}¹nÁ¢ÂP=F
+3'
+{ÔÎ
+
+vLO°õhFX)ñ5Ÿ÷é2+íù#ß~ÔÏ
+?
+(^.:vùVag0îtŠ÷±œŠ7š#ëÍÅ7BÓvÔuTð.âë4ŸÂÔê7»§š*]8DÓ`ŸnÅ-Ø%ÄÀî ŽÅ¡sBt$¥Df©
+'¯dªÅ8J`Ð37?ØÂ
+kb"rëðbRUÒY¢2IÒ7&FjB*Jì
+4Çz¶[²ò^ûÚÕgA¥Ê_ùñ!LÚÄvòÁ"CtòH¯!®$°äÜLü
+¹Ô$Œ)ï?6ì+Ð-³=eLÿ·s!¡hu|è8"~ï°·CÆGäúì³9},"_5ì(9op
+8Ò7mÒL€ž@ÈT
+nšÍLYî«wdQ¡G-¢D·Ô¯cz¢Ð\yDùzÜg©jB3F©y1As×5ògèÝ)š±Ìß°žÂäÎ	ÔÆ6PßådI;°Ð±pÍ
+XöÙid[rß²\ÆjcA&Î{uaÅ¶($ ð¥R`@ÃW%®
+HèïœÌj]±'­T¥ï0VlBÑÔµ
+q2Ï
+þŸE+v^Mù	;
+<W1ëw±lè©"b1>MØRÚà|c5 æ
+â·Ç¥%¡k¯xé3fÈ»fÇ ò»5-ËÜ5øžšSäÖgÞ}ýeŒèÛ/¡_Wçóz
+hEÔ¢§gqNÌT;)&úÅžã(ÊýïÜìn"<x6
+ºüŒë?ÀyËoÑ
+rœ Û£Å äébCÞ.ÇŸµÀ®.Œ]G&Tîßã|4FÕŽË0ûV1uáìD=<QJ
+ÊeŸLR¹
+Üh¢¯+ZÿJS+DÕ"
+i
+×$ò¬:)Ôéàï.qŸ­w|"sD&)Za5àûÜëÛÃugÉ/+FDKQøÔQhsxc9Ô%
+zÊ§zY_Çp°G³MŽÌ2Ë¶AÔtËlÖ±5+ÌäP	ŸÓ<	O_Ä1ÉA×Ðx»ÇŽ4
+X`Á<*ž
+·çX1²?svž 
+á
+aÏlÃ²
+a_w²ÂÕ/Û_8DåIä~<
+À@Õ
+|ÌÉT·\C>áRŽ1­J)Ñ@
+`ožÃñgdm÷R[`1(¡#sŽk2×Ô·(T(Ø+aãš3ïG$	;IÏaÅÔØÆ!*{
+ÁGÌÐsÑ
+@£a_(¥^RÕ}µiÆÝµåRô èál€djRó"}jØYŽ¡`6
+ËÇ`ù><Ã	HáyËô'nüsP0kZü§gyØá*~~ÑA±x4ÉUCÛq€Ùl6Ù 
+oûboPSðc%=Ø{$¿\5¡
+d/mÆrµIBO²{ak3)âžrÓað<bÐÁïbKl	ºÍùòpkeuÅ/²Í
+ôã5q`1ŠèÊB2>ø;Ø®°Qž7 
+ÄõíFÈÄ "š#eÏP@
+íIÄI%_LÀðôo«]W''0ÛÅ(Ç÷^
+rTý>÷
+µ³üL€ 7
+1NáL@ôÿ
+1ÌÖÅF`_
+S8ähä PÌb,=Q -WÍW#`
+4JD~ÿál/Y
+â© ?×ÞØ,;'-Ø;
+°ä<
+·³P0*Sµá ÌhÒÏõ^õÂâÒá	
+ZýD,ÙB£eä"8V>(|B9wÎa9à~£÷è?=8ôK	Qiy
+Vå
+¥åjÍ\Ÿ"U€ð¥`mÆSl
+LÅÄÛ-Pt *	Á
+`ÉQõEPË¥¥dÀAÈ·(4Oô 
+(š6ŽaÑpaqWÀ* n)Èì
+
+Â÷à·xŠÑàíiCáßç2çÂÏt-H
+Â%ã!)qœÊm0ÈqœTÖõž!C!
+úhgËw1\&MW÷°(
+4!Ž#BH¬`F
+	dagø&G¡¡[pÍÌ&Z@JCÑZ/ôŸ&[
+kK&DùÊ
+[åÃVs£
+=€@
+d1Altr@¡g;p
+ýM;B²AWÅq<îYSÇ'š*H¬kRyžVÔaÔ
+ÿa
+7@éQ¶pðéOm=Zsã©cXámÒ#þš7r
+!#Ul
+xÒ$äÐn§þ)qÖÜÕ$£@hÏÜgIpxApÒñv{
+öÙJGÿQE<ùÊÌÔUp
+qØ
+bÅ]£ÏðnßÕž0.µ#Ü-KY
+!>âÓœVß\gØæ|Á
+' ímfáry)<sR6xN§[»ÎA
+Kõ@LíEaŸÍ \©`2 \Ê
+Ä¢p¢Ž8ÃŒ7u7ÁÆ$!ÅŽÔ+œHÒwàH²&ú-
+ÞÕ÷wx>@aPÓ^¬,õ 7ÄäXcAòð¬x
+!s}XÐ¹È	_ÈäÅ
+åFÃ9Zä¡²¬bAœ¡gOEKä§Ad Ü'^È
+:F®Bš$ñàå	\ÄP1aš®`0ÎðFH
+à_HZlÐ ÇQõSŸ
+VEýGRXn]óuÑS
+>€q WîŽW9E&FGe¬ùTÛIÆž}¬
+}ÃÝ1Ü-cX1öKŒàqèr
+N³x
+è
+7 }È¶Šý
+{ID^
+wèÀU
+gÆQ<ŒB¹ØþÝ\Z
+r\ 1ß²d¶hÙUXŽ8rOátQ`
+Ô%`G`FP6jhcžuË¯ÝmÀÛ @#üf.b
+IÅ[H^€a©WyLOŸ#<D^!Tdp
+
+¿y¬'€«I
+^Èåµ!
+¢!Qc·ÙLpR×"9©AÛ*è	áOçBþqgÜÌRH,õ
+¯çØü
+Å¹pŽ
+oÙ
+8Í!<â 
+
+íÓèdo_"ÇT&ï
+0ŸeK~EÁmZ	è-v
+ÐX+èskÅsP
+ÔÂiÒ­<`sà2ž°jzà/BUjÄŽ%
+$°á5ÑVKà.ëau*þã$xYã^4À'ü
+x°A5šB×ä
+T 
+
+iÖÒ!J(	ÏÙld¥Ã7];]LA1@9]ßÇgtBãÕ4HÅ÷àHØ^An3úÞGŒ?M
+N<áÚçd
+eÚŸèb³Íã@i#äÁ"(||Ú,³Q|
+CÆ
+`ý;(Æ}`<2dò3InÇµ<ä[pâOÀ àeâ
+ø7Jh¡^sP· Óã ÷¥=-	äñËOtÈHìÜrìZíhIa3ÓÖkUA¡à vá 1Ð
+^E',Ø[Ã¥$ô
+ïSÕtÙ47œD9A3
+xfÊT7aDUl
+fÄÿ·šJy!¡d±86÷YP¹{qY
+yA+Tü±AÂ§PR¯\sKÆœÈÓ'Ñáét>òIÂ
+.zFí58@øc=ö¬î_s+CL!TFC "a£Fpô9l¡&æD~®R?ä¯5 IHŽT× C
+(BA KŸñä<É¬MNÂâÁePD#ÃY­Éàž
+gÉ>A5däáCõc,Ú4*Ýmiã\ `Åo4\èL÷wF"Cq
+=H[·=@
+_U0ðÈÃÈ¡3WG£Á|ÄêQ@eÄ2}
+"\åHŠ
+Ø¥¡šI¢#Cä© O3$¡$Ê#ê(ýQâ!&kãDÑ2ßEÊæQ>¢
+BIRô"ÕÖhÔ+
+$®ATªMZÎ¢ñ4J*Híp-!%~ 5
+^ ¹DRÕ9iqì\!Ê3!'ÐäÄEŽd%
+IxZ6ÃY90heQ³
+®
+a(TgxÎÊ%È)%P
+&üEhbpæúm#hQt¡fî0FGàZ2ÃMjäÖE±7šiÍÆè2ÊM'A08&ò"úÚ0Ì
+¢[08²Q}bvqK"MtEQB
+öAv× akHRÅ@EBaxä*É`éjUQ]%
+Tà:Î£D)ìHc#ïÈ#üA
+2ÁNr
+Ì±@58 è1w@Fyå1/þÄ C±(@ÛØ!ÂÜgSBíòKn3Ïä35	}¡ŒeçÕE'äögpRW9v°KDÈi$ILÔ£¡{
+@C6ŽÅ-^wMVB¬ ¬Ü1³_Ô°2Ðs ²TºC
+1òËø~¯ñh2Ž#C@Aè]TUpí£2Üi
+4ß3yb(&N{DQí
+ÒämåÖ r
+;k%I¥'ŠVYz>v£¬&
+ÖwžÙñ`r$*Ö'xx3¹¥	Hq
+ó§ñ¬Ÿ
+^yXâ`MûERòàQ=	B0Dº%f	>Ôð«Xvs,Vfb$h¿Pa±~Ïsü!$þÃ[Ô_ HbÞ
+AB©°ÕàQÇå`Š'fDI 1Dtd.B¬)©|"Dà?jÊvc Â
+°I©Š+È±­	FèuUÀp@dÎ`ŸŠ
+!û­vNB¶iCÐÓaâyíCNiÁQD"%0<¹©ù
+?I¶Iq¹8¯	75®
+`ôdS(m*u\¡ÖlÒ=
+ LÞS!äÀÌÀÙ>ìvúà5ãn.àéP9	[0q
+ÂÖjôFÄØøo*¯€Cnvî&òêç@ fQáqê6ÙÆ)ä'¯°o>A¿jOÒé<š«	L6§ËYs>Ë3Ãs_øt/Gt.ÉÑÓé)Š-çað1ÇæØZ`#;'c
+WÀQÚ
+&)'R
+²
+òòTmÒB]
+cI$>YåphgŸCR`ê©ÐE&»\šNAÀÐ[u!z§AK8üOZ÷bW0
+aN²
+ž×)cÝ$·`yi%éåq°T(­vn4ñ!7ÛåYr
+Í]Ó!ÿ
+(1Ê"çE
+,BE1
+ä=0ò
+¬q_9;ÅßyzlQÙî
+:RBe¢ªâ6çiw¥]±|T\ |ú-Ë\ Ñž rõwÉ ¢p¥°aç%Ù°Ü|EÑW/øf€)guK8&©ùGÂÜ¥}ëß ³QWmeMØ#@€kÛ€vá+À2ÆŸØ_Š	I#B¶dÚÉÏ1¬È
+r-Øydm§1,
+8£Íè
+dÁíò('&Æ.'_ð2;CÜUÖ#ïÈ-jÊG°
+n}šhÐ62WNSÜÚ* Hoê3XbÔ#MnH">
+4mØ,SH>TðsõXô·Åøv'J5Òá,ßòcv¬Ô&±ð/Ù²E+c·¹(aÀÄÈs	mÄº7h@ÄQùä+X
+hcJ)ZA¥Qž£'r
+Ëeb`)ß§ižEg·F¯qÛ	¬œÙ!DXç2è4TGâô
+fpPp¢	ã2ãñoII!#íP¯ïõÄET "
+C2ÕapÆ'¬e%±T&
+©ÞSp»°ÁzŸÁ¡qTþ
+(ŒK7õ]a+ÿ}ÇzåwYZaGÍçRxbœG^×^b@àI¢íšg·ó¬r`dJÔ
+Í
+4°zòÒ
+
+?š÷#(UÍÞA(¥ådPu¶ K_)ur Ô`?ßä0O\Dt?Zd€ZT'L€Qú(g@LYàœà
+ÖplºÑQÏUÂØíb[€Ù0±qAÅ­ØP
+ÍÀõ€wùN2Ô0(I€3!rQZGš
+gú
+Â¥.'ŽesÈDWFešIiÚÁºÝ+ÆZòg~q
+?C.ÂX0
+ldY
+¢
+$'u
+ò	jœ/Äq§L<Ý%žeL^¬ÁoHnÂÄ60Éïbí]tcq#Òâš¡Y°	-O)Zö&ÇÒ¹p-î5Ra^á4£ÕQ£
+Õ
+Y
+Ðµ+îséœb+}%¶i 0
+ÚÍÎ#,8>UÌ!9oyÁæ§Ðý&j hÒüeVÇÂ@h"ÆÍQzš
+º{šãÜ3lxfãCE@øYLX}õÜW
+c×JD*
+	VÒÚ
+4²q&5£à)XrýÆ¥Kü[ÍÂ¡7ôÅàRj€Åæ€<b(µ1ñ ðXŒØAèYSÀ)ÒiA&&šS#`V1VaE3ÊÎ2²ønßBÁ+ ìÙ1ÕÐÆÕ×Õ'é%ÊÃ
+oG0_
+})Í­³<Š
+nDD®LVëŠ<Y
+Æ[¬ŠEx÷æýš$ÉwõËü`ÌçLÌD)Ÿ4E1bsÐö,
+úæõ}ÂyÖDŒV'H
+D°©Ì2ÄÌ¡yd#³[%
+S&FÜ `ùÔki5Í5>lg lQouìâ%+ýmÝÍa,Æ9#ødûâ#h[AEœàz2Q;¢»œ!{`cznÂ§
+×ÐS!@ÕÝ±:VöÔd^X$GXX`SéP¢Yæj"ºSrX²Ë¬Â8éYvâ9ÐU§^¡
+étWÏ`ÊÂP¶Å­P5zé:ò!°ŠÀmÊKjÈÿŽ$NÀ¹!w¢Ð­¹6²šŒÂ^w6D
+;F
+m
+
+§ŸC·ÚÙÄøü'bIwXIš{$vtù95õòGÞš,0{m#Â©8xÉî
+Egµ3ÈÒ5ØH¯¯97
+¹°éFZñKu7ÌYø$-eï
+óaÛÛe
+µ°õ>?V@S4Péké©šG&çú
+~ŠIxÐ°ãgógâÖçuJÍX7ÄkÖJbÑQ³(ÜJÕÊ|BS+`$¡	aÉ5¬ Šø!-D;Áð
+
+³³žt6Åë
+òFÔ##UUwGñ1i
+GÕÍã¬){[UF5«¡Á³¬¶q¬ fSr~
+ÅK
+ÑÍª«
+ÏXn9"a_1U
+ CLp¶±~G®Õ)
+q#ÍÒG"C×Z EnÎ¯¬ïÛ	E*Ìùõ4M×ñ®6]¶J%õDRÓRzIeß[£7)q"À	hàŽ
+WÆ
+BK9*4L%ó
+
++#=\}zŽ%-GïÐ$
+c! \ádbOQ²ó<%JÀi*LáRÀr2då&êöu!À]59k\¥ãùÊyŒË135$Œf
+ÁÍaÊêå:§ªO@1¥Ìàê"ÔÖY4(YÅ-ô^U£ÄD-XBN€ÔÔrÆJ&ÑøA·1(ã¡Ýr*?#
+
+ÍÇ0Í#VÎ2j56ºhÔžÒ©nÊ
+
+byj{êxi
+SO#MöIñ$Æ#ØécËÇ`Ï`§
+þØ@.ÃG`ŽÏI:!ê a|J'\ñ
+jÂ{4GbÈèaïà;DÀíèµ¢Ò(*$x`99HRtwÌr²T£ŸA8)ŽM0Ÿ
+m¬W8uh
+³Öw	Å³C
+BËò2\<åK
+Ð¢
+ciŽ)ñŠKé
+sÊÒ&*X±ÁÊjÇ-Mmª/
+5:©£{N[Ü|ã:Øg4FÓ
+ÕŸÕ
+;
+u©7î ¶*.\EmÓe`ø0+Ý8°æŸ7Hš;.qZNŸL6
+I68 íÌð6gcnkæ`ú¯3SQrnîåN*AônnX"×-*)AM`AeÇÍ3ÿ,ëæªq@iPIU8¢hz8Êe×Ôb0íÈþ0+î(óÙPF(2?²
+ŠÑÜ²ÌÁ0pLŸÁšñLÇZüCËÁlÙQð1Y[øçÙIŽÝìm9­ßlqÆ®U¥ÒcA~*
+cd,Ch7xØÑŠ[«Ñ}Zâ"Y#%×ÐÉpj
+
+É(°WœÍ2ŽcšúÌ8
+rŽ íé 
+,.<	oÒUf	(í ]nÍ
+8@xæ@I
+
+ËLÇP°ÛÓVÝüSEu
+lÂóª±2pžËm¢y<N_Ù5
+Ç
+Þ€,Í WÀëætsè
+¶Ê¬1/UwšR©ªömH
+ìY€?áà4µu,Õ~Ø
+NÂ3ÓŸi&FrQÈ±eR]böeLR©HiçdY@¡L«$/Ç
+O*DÂb>íÚç$DÄ©qÃ 9 gótüÑŒM£2š7·sÁç'æÈ²UAÅ+!·
+_Xk]Ñ	7ÊmÓî'I/D]0oú
+ŸÐ¥kYoPoãŠó¡^}oí­p#à)]¿'&í5§ÊQ
+ú×V¶-ÕæûhQ-lZy© YAG',zAßû9bwÓÓÔ
+3Ešé\à!­WyR4M!·üHžãmâz¬¬T_GX,
+¬J¬UVŠC¹tÜ
+1ã¹\í©ëÄ£]Ä5µy;_°Ù
+"GÅ©7(
+Ëç.œ|]àLÉ6(b
+¡B5P)Ól
+¹çÞ¶@0n|A.bI"KH)n`Àl*"¢³Ñål4NÒ[°XoæÂ[Š±pQâÆÜ¡h5gÄL¬€3UJ%Y
+_lh[º4ºð51çæF{lAàÅ88E·Àµ&'Cý
+	ÌŒf¬¢^³#e
+åÙ/
+êùb éŠ	«µ¬n[n¬(u¹
+Ò @mŠžzApë-êEÎÃZÒ¡Ó=y£
+qñVÉŠi2íZ2³3Â~
+¬6}Q/Ùèr`.7y1iŸÂ^Uhš7wàÀÓ/ª¬¥
+Øu»2Óºô!ñFÒ1§iPiuRénñî³jjvhb«s
+Ó¹ÒV
+Ûàú[OÈ@Öø°¡\>|2ÖÅÑæYþÅ(RŽÄ<~×5£Þ
+¥_Ù¡Né­Öå&juEMó4AR!SëMá»Á}Ñ©¬µb¡úa.ñRÿ
+ìÆÔy;ÚîVMi&ž£EÏè°wÑªÊn\
+Hóñ;UQÕÐŽ
+i&É\`!eÙÔb$Öì(©²]ÃÑoöAç#µ .ØÀ?ŸC×iý±Z	5*#CDÆ~z
+1uøýZù«¡Àµ
+`§-
+7Ýrj&¶
+YLÅÇËsDD:J¹
+ÙèYJ
+ŸYøq­ÍÏÇ¢Óc{æ{ç.
+bÄ8m)gë\>®Cê³\Z²ŽT£øB±Å[·. )ìSSEIäÝø În
+Î¡|ÈmjÁÓ
+Öß}(1¡Ã^P¢¶H%TÛHÒEý&(>rFMÚÀbr\×NQrŽ"ÙÐëkQv
+
+Vª³ÎŒ×YTÒ'g<1¥úc£©b|
+X©âZ×ŒDÆþ¢t{%eðJÑ§Ò	ð§p!ŒL	€çõ*þï(cfÀh/{ïZ×¢êÇ'!h|Êë3Õ(d[ûÇNÑ
+
+O
+I`uû
+8ó8Öå.)&ðâ"¿&ãæú0Nxoä	 U5çTµðé3(µ¡(Çg4@
+ú	ý("$]à©D"lÐÂÖªC
+÷)¬eé
+ì±ÖB.ò$¶þîIÏ
+ Q
+ÌhQ§ú0
+z
+üû\
+ü'jqÈÆ²çºt¢
+¹,HÃ%Ö
+}ùjÑº
+ë0Ä»±ÚÕÔ°¢*Èè~rt
+Êq&î¥CÌlGV:1hbßèO°ßŸ4VÝ¿UüúÕ-p(Y·h
+?ð!á-ÂÆWŽR¶T
+HÎð!šC§tÒ
+í'ä
+dó@cS)A¹PjkÇækÓ®/Úôté
+µÂ
+Á/šJLBÝë[5×À¶ïP°ÎÚ°\TéÙlõ£:¿LÝùÅ[8!
+²ævAÄÉÞ2z
+ÊÂ&àÔè·,^|ÛŸµ¯þ&eô(j6ø=D@òÅhÞWVF¬ËL5ÌSà­1d¢¡}c¬Cç%ŒnÈ:Áìpyß
+€ýº1à}E
+ÉÀª
+®4ñž%°H÷+cô'Ù§²À=ÿF¬æJ¶¥þÊÀÑD>?@;2ëÒ·*Þ ðÊMu^ð gL<ÖÃíÈRÌŒÚÞ,êšH;b»¬phýyYÊOA;âò,ZÁWìÌ}MÚ
+}¿ÕÜB.9L
+±0
+RÒßrIÖÒÚ ?KXšÞdA¢¢
+²áVÌHÅ¯
+L8jÜfÉ"àEŠ
+MµöÄ,íÈ)b ¯ëÔZas
+|qÄW~q±Ù·EÁDü
+Za
+ãh)Àø»
+9aÓ{ªTŒDD®ÍuQR7zu×­ÁmìŒÃ'~òZP%éÚ0lc'Ž!@T
+úüÆÙœ¡wŠâ+èÓšÉ
+PÚ0®Â¥=Ï^+fe^ëé&YTÜSNp,Ñáµ­Á§CkÉ=R¬2lêÐæ$&œ	ÅÐOª¥R[Ã^IŠ«0åz{lR
+DÛ?YP,ÊzÙG"
+óc PiîRíF5Y¹PòÇ:Æ_»Tl:o>ó³6B$cÞ1vU%o=Äñt\jB±YC©oG_|ªvþÈö·(ÈHŽ),ß
+ûJA~±ŒÝ(MrH
+r9wSz0ñ%ßÐS²øcKÙš_€SèÆ«J·Ö6+geCŸ 7µŽÉj]WÞ4^tYqŽúþ5Z¢?RÊ¡ó
+$>qx«ú
+º»ã/±íí§ ° Ý­W¶AX`Ó1pÅ>jkùßap
+b]7ïùbpT(ÏITVT£õB;B
+nÃÌZ6hÅFb:8&H
+Ì .ÅÐþ^Ú 
+äPŸXBþŒÝPóÚÎzË²~Ÿ>k¬É&
+I3â(%uðv(9Yœm;¿
+Y¡W=Q0«¡*(.ëõÓZcÔÈf4
+®p
+ÐèEâSŽN¿8¬!yÍyA 4~-À°ÂtxäqT@\ lÞ·ÀiÊL1ð2€gÜ
+Ði
+êS+ß0¹æxr%ú0llV.FX
+2V")è=ÈÎ
+€­q)Zþ À3è)rÁ6Ò¿bÀ³ŠR&Â­
+_ÄI>7üŠyèÇj
+ØÃÈdê0EÄ÷$¶!`k»Þ+0ÖD^žxÝ§Ç«v
+Q[SAE\`v¯hBù4@PµÊ[°0*;Ã
+4CÌuJñFÀÎ¥ÂïDæÂËç1ÑäcŽqXgî$
+Ž|/ß¢0"&œÈ9lFx»xCÁÜ@b°cìãeÀ;k ÌÃE_ 
+,2ôî,Ë
+òÜœùpë4Â4ÐÂVq±5$ß/uÅsneU¥`«æÊÀå/
+ô(hN³yçýJ]¬ð)Ò¿°aa3­0¬â»Ú
+1¹ê7Ç@QÍ×
+QÎ@EP3°Wg&]5¯KN/ß*ÐY1mTë{¬>²D
+·KeâGŠ$ËÁ4na¿dFËc¬µcÚj×ÇjjãdÁHš±Ð¿
+ÑP4JÔ5£RÔ`þYGºZÓÍÜ7/Fu¹ÏB¬lÚXZeÂÏÊã®oëK¢©Uþ*Ü¬ÙªÛ	#+Í!y±EBI¡sÕÓKõEè©&G¢µ[ý)žØTçý
+
+)È 1àCJ2RŸjqÔè¡Çb
+úÐÂ*Îüq³¬Ã FaII
+
+ÏH8B
+°äI¹œNÞÃeþsDßq/ìBæ8ØÐ,7OÇÃT!T0
+ÏACë«¹
+^.9æ4ÍB[OâôÚ
+Ófâ±AÔILn¯§ðwêÇö¬qòæP&<ïx$$Î16µ	h¯N}C6ŽBª¬/CBeÍî&ü¬LËªåU
+%Ö.T0-X8¢NµqŠcçÓ¢
+Ö4eÚbx!þéÒ_AÐOþ¡­J¹È©niøl]VÁ€d¥_=
+ÆâÃÖèÔÕ5Nñ"Þv
+wIûO0ÌZ"Y ð®Qì0{zLlÙEêÂV3,
+µ,}ãÍe£
+ÿf)UG
+ Wt¹0É-fQo\
+0o«XaPuF*;îðrš2a,o&€ß¢l­®µá]4SÏ*
+;m·,qUÃ[Ëá?dqL¯KÖÔ>D=¬U&.¶KVÚL©Ÿâ²@ótÚDœú|Í'Ýâ(äªµ©¥NTšRvÔ²LGBèAïÊ.ÈÇÄ²ì5U1Ugï
+»R·ïLí `ËõBy¯ŸdR
+ùÈûè=Üð®É	_3(BQNv¡M
+q*eÀW/Ôóÿ!ŸÒTF)¥¯&}äÞ¶¯y»Ïë{.
+ M	%AZ ¯*z9ŸŽ«œ°Ø°2ò#Ž
+^ 'Óïïæ
+|9E_£
+šæE>Wu
+"vÎVÑ.€
+#Á4 stÁQj
+wt§æ+GšP§
+/êäµ(ä|,5±Ì6
+7g|9ÎXè5¥/¡¡í¥eÛ
+Ñª€z¹uÃ¢dßÜô9dGÙ°^.E*Â
+ ]ö/ú»HRð}Æžs]Ú	"`;]77rõJŽÈX7ZJtÔœìÌ<ÖÓyK»Put
+Q
+So×f
+Çs³3ÕE{Mysµ1Q
+ÈrLI>:
+Þ@o€`ÃiLÈÛô&
+ª»¡.':"÷(sF»mÏ&§¥º9ûÊÉê ÄàØ I)
+µ+þ:XZ~oLN!éDyô]mÆÛÍ[
+¥ÈÞÌ7_Ðåòº·527MK©qšO I®éÌ§xPÙ	0mv°ÛÑWùµOÒ*nÚa/<µ3¡*kÒŽ;ZÔkmÚÃKÝ°GÁÎÞé]î tì%« òI±2Y09®Êõb
+ª"€jB\ÒÂ%ÊbºZºÚX±I²Õ¥Eââ,Ò7¯«ÊZR
+7èŽ#¯HÛ8Å<œÔ¢é¢®nLAãv-Þ"ý£æ2(ÀXÞ.ÑÅ²
+£4ÉyR'j$Ž.Ð?µsùà%>×cºµÓ¡ü
+ÒLc?a@â`FyŽèëX
+êËåB?äÆœ£hŠ$kXP²£ŸeçI{¶16Y*K7³ÔÊòAà^)ãÝÚzæ$óÖre0õMeÜÓ²ŒfâKÅýôŸ³-õ5(F]LwÌ*Õ
+6óè<%+ÕA#¹UÏÊ¬11Cž*hóHþË€
+s
+¥ËQð
+³ÉÇÉF:\CÂýjÀÉË>æÅùl&UÒ¬*q*ë¯)êBq0J'ÑÈLF¡ËqÆËp!%òI±¬Hép
+¬K~£,ûÎÇ7Ý¹5
+«èkXñ43Uú§ºSß=þ§êJð;1*à-Dà]R'²ûjòSPu£
+}·*
+!èÓY@Â²^n)
+WAIqnPÄFNU)
+1(YE4Þ·Õ-È,
+64*ñ«®.'ckÙûÉß a	ÚL²¹à<>Fáw2èE°Pø1Cz?Þ¬è}¢pŠh_mIHÿ€Ç×.1Á
+@ŒÅ2Ö
+ž1G
+p»š"þìµ
+ | Ädd?o¡a€YnåyBöpM:¬|ÌêÇiGVè¿YÖ7Žª_Œø#ð×5KLtpH,âg1¥
+ß§7å|§
+Ê
+ºøK	ëêU¯šÅ÷Ðª ÉL °0,;oZi¡ÔÈÄbûê8+CF
+Ló(qæzÂûZévjÇ·å
+²A¹Wyh^
+SWÒú\ª]¿ÊÚwO+ÈµÁ÷M«eD
+Óîw'ø{38q75ÝÍaÅÈÎ*B0¡PresJFŒ >>_^³Â`Ç`ýøò9ySÝ>øçØ,Ia'Š ]rË XFÿ·]PÍ
+(š·-daÑÆlÁJt Á¹ö0%?ÃÇö¡[êÐh.ø·G>**-â@P0v»èzÏû%(¡Ø
+Én"Þ×þ
+v×á4-íàª^Ë w8"¥¯
+×unsâNLÒyÐ²ä^ÄËZBßáâEïV&è;g.ù÷àªwÅ°Ñ
+œuIè
+LÂ·§@|3k(ÓÔêªiZ@Ö±¥kÄñÉsTßº5ÄYhuPÝz	£
+ËÀñv?{·OÃÝ€X ÿÑ"óµG
+óô©rÙú}ëßShGdqYÉ0ÇÎ
+%à*S¹8'ß]5"L{lkfÒåRXÛýó;ŽÙ03Ü;8ÕxiÂBÎhyÚ ËYV9FYôÛÎmŒ¶Ugói
+SžÂõ¢ŒÖE2.GãRé·WÆgn	úH!µó%
+©	'fÁö)ÎaÛê"ã ïäÓÓÓDš5UÜµØKq(þ>£B9
+®zûmßñU|R	 1šg}vdÕ0ñ,gC²Ð 
+*ùP6$£úISFÜAÍÇfYÒ­ñ©w¯|c4©LØâ&î^C¥6H®Ñ##éÄŽ$ÄD
+5pÍ&VçS\ªDF*rrÈÞ|Ø]o¿œ Y_wUÛd
+ŒÛMYžµº
+ÈÂg®}yZs/àÙ(!¢6ÀåÄ­âßáÌ7$
+L	âÁx1Ðâ{n^ÕÀO_þ-^ÙØK7Ôä
+M
+ôÐž7ÌÄ4!Þ{,Õð7/e>;Þ³žwãdB­<
+!OÀ ð?(¡AÀTàA¯UÀ WHj¬Ñs-ÂLäÓdMg©d
+b4á9V±Ò(Žvéïm<u
+fp2Ç/Áð÷ðF@A0H3ü4,mÇâjù_­ã·àäÉaI
+ªYCÐÎÙÁ2Ùè5^^)sÏ
+Zª{H
+¢£2Š­[¢ÝTà¡d
+±"»à©ùÅ@°n	0€ºœ²kµAx)ß^w
+CºMëø	ED"=tGUœ5aéàÜYHRæ>>ÞfaÂÁPD1r·Ò±Œšhãd^Õú#TéAdLg]lãHåšPÎëú¥È9
+ÅÍq{bº{ùÙÄ£k>:BQ.	3v2QìAdb×pËPD8un.¹
+ÀÚŸ	'¡ršhE&ìähé!k`»Öçæ/Y3šæL ŒÜÛg	ã
+
+ÀŠI)©
+§Ãâ³ë®JÅ8KÇÒ~$"
+4<í±%ÇŽFb@UPs8ÐRg¢;ÂhÕÈkpµRdAïEbè$
+Å
+ëVº18ç¯ÃmjPè:Ïú§v&¢:,.­Gw+Ã9Ö)D¬ÛüEð|ywÅà³òIq«¹º¢zZQ0a}
+e¬©5Htpøº@p¬··ñ-ôR®£-°·Ñ:þw(=4cå°
+[²©€W'äE(ÈBÚKµúÃSŽÒŸÏÅ3\ty×Ü`Á­Tf2(m|Pc;{í#(@Màpë ³6}öè+EÁþ¢ìi$Œ
+
+lCÐ±®të?ÅR|HÈËÅ±;ÊRÂxú43
+a¬ä-
++ ì[0vá¿Í#
+Ë7,}
+ÈËË.±
+£
+ua¥ì<Ö6Ò¬,-ôKø4ËkP³ÃNÆèIéÐÖeÕdzp
+ŠüµaqE7ñ0ëK@&M{é!0ÇËc
+È©ÄÝ×2Ì­Ånäí,[6;ô	Éái9|ŸÇ]×aPŽÄñþD	áÀ8¶X
+Å:3ÒÚãN/iãÅ
+ÞL
+>X-ãBgŸíxöå1z'¬%E*è,ãÅ¢_Å³^Ž<æsñÔ_tÝWø9®cè?f95õËâáATþt1xç*íw3/áÿÛ#Ùjßù`Ù· ¬Š[4À§Ö×wÉæÂ
+Ÿ;'ÀB°ì±ïÊ»~<«Àbmx\:ÓBnjAõ ùGþ
+No^³¿YÐ%#HŠYþM¡?kÔ
+îyòz»ðc]µ
+Ã¶c 
+èi;0Ô
+vxŠG
+J­ÄMîÏMxUn={\ÆuuSY)45ï]Šî¥Åñº÷ûåuçÙ
+Â×us<ÄEúŸ
+ÃR:ch|©Æ'-Y.ûÓÅ`ºZåg²PÝò«;em%U¹ÃÓµL4v·Ø5Ž_û!IñE»J¶ðg[jvçL*&
+>BZÆÃÂOñ\ºŽtÅ­÷_!
+³œmnÞa¹kS§\aù;Ãc; O
+a®
+\ºtu|¥-iÂT ÉÅœ»Ä
+k|óñ«·ÚPžÞ·ù=,{ó)F
+@©9¿F8I§FRÕö¢27bS$
+ÈLUEt¿uI,äÑ$ç¥ù)ZWœÆÎÁa?
+Ûwé×üâžçŒŸ3ÎÍŽNI9ñÆ»€=ËD¯N)J)ÛÔŠ«k]-&eì~3X<mk[J8ÿœ1Hk)4gHD%ºpk°È;ÞÂ_Ýç7áH·]Ûø¢Kr¬ALšE(Ù MæóÓ448ÔIM
+N27}
+ìZ8ã®G]ŽhoYd%#Ô_2`Öî|âÇ<ê£áòE°Í`Ë	:6M&Î\ììÆB¶{ŠoYs/Êž7Ø
+KžJ¢ÍJ'4b¬È³²ô
+Ö°96AèN€,Ž«7Ì€fsÇ E÷@­îcú%GÊD€P9· P&ö|B¢±kß
+©o£UÄ;üâÐÑò;~iqtÑ[c*5cÊËXàTàKµé¬rt­ùûÎ@Ä°v.Ï«=µó(¿çºVë6hq®õžy.AìXV<
+bi
+i
+œGKv7B9A&êJÅF1°,ËsÐXR`Ünïè·w×÷)ž1ZL
+A÷0N;® í¯U¿²)œ±ÆðB8³6¡_šš[lÉQ¯]iLÐÎ%ÁbÒuî]v*t;Yø²SákAwí€á{ø>oý§¬Ó
+/âN§oÏ`#Òž°¬yÃÌc>IKûŽ$ÖË
+ïûÜk¥Ú ÄõTÃú$³Ók[¬{k© =§t5m[yo;r­%Ã¹-wyªl0GaÒÐñ­<Å¢PÎR2ÑÎl[&¬þPê|¡ó\0>€?®.¢-*mÏ2OgÁÒžÈK|Wyx©Å
+vÁæÏ£yÄáØBçèðÎŠž
+_?$¥¶OG>¢ÎPê"á\Œ"Âf·gá ¶i ÷Þ*Ù<Rq£TbšÝfà`}ùclæ1
+ÁtžÞÛi·{sfï
+ñÂ7 
+ñ
+LÂœäŠðoé¶Ì'€àHúDïÅV©àcG®ÐÏñ¬HjÐôªm4Fqmµf^PÎVNUæ~âœòVC¯ø«õ¥Á\4€ù!YúR¢ìä°
+ÂæÇÒ7D]7x46Ÿ|ËÀDµÊÙORŽøY<-%<FmÑÅö
+Z¯/Ã#ÂªïÍÓÀŸ€T.,€L$I
+?9§Éñ	Ð#QP
+;àÒ¡>Šµ÷f]
+ùAÄÒ&7Çøw±I~Ï~¿cræaV
+Ênœãï¡$šå>`ñ!üß6RQ
+¹æÈøÓOŽ
+@¯hr}ÕlBìxCôJ²;ïfdóóÙÎœJÃ?Å§5Òó~õçÎ©ÉXRÓ"È|VÈCd^¥ýÁVHé³ð}òá*
+Ó~Ïm
+ylNÉ&Q(V&çm¿Äöš?Òç!á­LI9#¢£ Žkn<ý^ÏxG5sRÑºâãP6ßäüe+æïÊ²ÞÕ$sK0®
+cGŸyšïÙ7>¿ÛŒUá%GìþbŠ8øü?ò
+Z¢É¢
+V¹}gT`ÁÏÞ#õ¿Ï(m+,
+`þÒ×Ib€áñåÔçÆÔ¯4Äz
+üfnù£Ö²&·à\ÆôcGdvíÞãÂ
+ÏpðTGPWðŠËxÿ·Ä¿&ŒfÆ
+èãa4Q
+#}7va/°K:F6&¡§7 uÚ?Ö
+(ÁíÐá-CZZ­ÉŸSäiè(3²Ãå€T3oÐFu©éZGhÈ-$v
+
+E^orýcØFcÙ­UÁËÆÈÓ2 ú8^AŽÛÐçwôÓó¯~ü±÷ïÒûRÌÙ}åzè3rGÐâZé¯ÇÀ9÷¢ÓýöNáKßÏ`Õ.r@rm' ÜðúA
+¹SL2Xpà_â ÕÓî1ôÂÖÞÍÄ©"«=hà
+BI¿áww	ö
+B0jõ
+¯\ät±9
+mg6ùãWð:È]ú
+.
+K,
+×
+­tcê!î
+R_:C Aš3°ñž(²ïMÆF[D
+,ú&€?ÖZ­K:rIP×ð-À¶
+ÏuMø]¬£.0~C
+Ð
+µC<ÿÎß§À¡¯DÐJž
+
+Al[èhÆÇ¥	J­
+$áÅËGhSýû8U<Ä¶c >ËT5x
+É'økG¿«+ïõ
+2l^,£#úúó4Í$iœË8Çû@Øã7c&#"Z`yØÌo(Å.¡æ+H×ÆjÖVh!!ÍÉóî,š
+ðëÇ8	²-FOPÔÉHAAÒ,(ýÉÞkItP#.!7Úä#ò
+Â+QR²j})øÑÊxô³1X®?©q/²Sc¡ÔRI?nàfç3eÃ=ENÐ?Uz ±&¶F,ÀÂJN,2Fz5và
+wšcó@`#£K ÍHVIû&ùâ«ÄôQj{Ô.Œš+ÅO±"ë>(þìrÚ  -ã
+
+L%|RdpAÉ"åü£B`è*j&lAäÃ%%r+É
+
+i&¡n±¡ÓV$ôI¯eë=SbJÄ€BIÑÍ§ePÂ|5š71³ÊZßÁ@"2
+K"ÏS°]#ZfŒ¿ÐKÒ.äwkÔÄ}!ÁXÀ[]\÷e€÷g±æÉ¿tKê>Gþ)D}ãk!
+`$UBOÇø3¡"07
+p¡)JT
+÷gë¹·yçø$®q£¢%©}Bè2Ÿ¯-ï¡5Ù9ð@K,Ðo*&¶
+Ô[º÷uW
+=Ø"øè{wóaþôm¹MÈþr3ÛýÆ¡§t»W¬\ª`6EîE2åÐ}2AÒBð
+wC€Ñ¢6
+R;ªš
+Ô8«{äfs
+­ŽÕb. ,é€Lpm~­úA{êÁÔ¯ÈáPJ2
+à
+ÑÊ¹`Ži
+
+O,TVÃH,¬GX¬£ö¢JŽ³¡IþüRl.é
+
+0¡OòÌ±¹È=ùtÊâ7öµ50ïÂ
+
+Dîµ
+DHm­{ØSÐVúSc »:=Ý oM
+£MH±xsæP
+ÃeÈ¢ÈE^NÃ×Y$ÁqÓô+Ze 
+xåb%ô»u*ÂhêŽõK1
+sŽxTÄ?M GõU¬<SÈ%B±Žz	ÂŠÄjwX×aÈ×Cg|;8ÎLêGb`Ûšý!ií!Y­¥9°x°ß\
+]ÔV
+KÊøÑè	ŽG§Å4£5Tõ;Ò$äÑ²KaŽõ
+r4+OŸ"6Þao=Lza
+TëšQg8QÐ
+^*R/êEBlk{¹Âgá+'Çtý¡ø;X~ËH(_FÊ8 HMÉÂ€ABÁ
+«út$Ì¥Üà¥ßpÕFL	nF qÅiìÜW]è>H<¢ô c ðžAnÔK,í8é[kF'iÝ!À£G+Tè	|gCÐî
+_âÖ«jui.	K 4ô!y-61þÁÖCÅ!RòæšfkfÆ ¥ia ,Á|,<'ËdA!
+1àqØ `]%ñÀù
+C"u3÷â
+YRu¬ß€;.²V¹Âó¯õÃ€èJÐjÈ¶á8@6ô
+Ô~_C9ãGhàq×ÐÒÀAÐ®ŽqKw
+zú»ÌâCÚóhü7ê'.¶¯©*ØÉ÷
+Cb1ÝfÌÇ¹ ÔÜBì<4œÈÝª4â	ä!ñä7â
+0P$#š7ü¶ª0Ã~ÖÌPYèõp¹Ô@Û¬ÉáÕ-±8
+
+k!l!È2pòÝ°Z`¯ ŠÞ©çzwëâ³ÉÐR
++	^Æ"m¬³Êâ^Æ¹¿æKÛê7R.
+â
+¢;1<ÍdY'$
+86LÌÂµBîEŽá#4b:­
+(ØDJÓèò
+,`ç
+§<0æTEµµ\óa1@s4PÂÆY³é	Vî¢TÝ«Ùd\º^
+¯BNð^Z+höL
+N4®gåžðHÊG ÉEJ§EBtŸP,&&E
+4dÀFÄoëp­
+Iý§
+Ôì.Œ"f3HAÂvè(ª2Ð>Q³4ÁÊ |juæhoSÏ5ÃÂÎX;9@à!$ÅÁõô²`dh$~\á@»IU4Y
+ãi%þ)@ý )ªqq/9ÏŽ
+DÂcmôNAë!_
+{xWê)PŠÔÜ
+ñ"43iŒïp>HÂ¶÷ål~bo@æh-T8lïé7Žz5íG"
+'ÁÂ¢uûè³š
+¹n$ZËV+  \áOL5`
+Ä¢ÚG#r7×»ž`eN­¶
+sm±êHrñ¡rër>ÀDyÝãÄAôÈ7e
+YÖ.qV@ÔXÌqÃpp,ÛÐ
+¬U	häá*
+œþ^
+kÕNÂaQÈP@Õ²ÄGä) Ä÷
+Z!;TGÎØ3"ÊžAZãøØÚŽiÊ*ÞµæBÓ8,ïDi
+°HÿÔ»B
+ðÔ
+áèÞDÐÙ
+$WÎa¢qD¶øS,xkÑO2{C*ÄÃÄ·(÷E$
+&Úv<1(Oîû)ð5Ë-ç9L#I9ì 
+ÃÙ'œú8
+Ð
+
+VV
+ñÉXùXÖeýAÎHÃlø¢HŸ%yáÆbù'È%Mœz
+3OQDõÕÒÜz@6(ÔGÈÆÊ
+%žÌ¯Ôn.e8ð%*Ú"2ÍÄ5ç0J*Û¹=*zt
+©E±xæª%¹ADf·
+ú©È\j#
+Â
+íÃ·¡ÁFŽz_xÏÓ-°aÀÔÓ=gG
+É`Õ³,H1i=×_îv»Ò.Ð
+uf6Å¬aW±V
+â­
+õE\*k©À×VYtæ
+5©§
+2¯h@ôùT¢]Ÿ!ãPFÃGX
+®|ÐxþSáføp|¢DŸWàìv 7ªÜž°p[Óð
+T5_TwB.#¿ÐR ãs+^Qv
+
+@Q>€'l5
+Ù
+Ä'T(X;è%t«ŠÎLïÏVÓÍüÑç 
+1'ªîEÉJC
+Þ¶êú
+p:m
+é"l@ÂXð.Pæ:Y
+ãlŒšgðcü
+$¹6
+KâÆR[-2ØÜDcÍ}À<T<¹&](
+	ââ	€ÕTT2E
+_œZ
+*!XµbÁ/a+t|ÊCHÉÁùµ\<n)D94âŒTçSÇx(Ø>8TpN€Ëûÿ
+4nY@IóG~ÃAªheY%?g 3]lH
+ÛŒþÝéÙqÿ3èù.
+Ù"ÆÎ
+g¬aŒ)6lENoºÝI2H|:0æüÀ,aÙ%bgÀ¬É×l¯~×oØüNö¢ø hÁØäË
+¬©6qªPT|oGAGÙèµX.9	KB©B
+a°}25­5Î5åÕùøèOú
+ôž¢ñ`p¥rtõLØ=
+=Áy	XNÑ
+jD%}jÆRå»S0<ÜEãÖ°YP§ÑÅ·Æj1Î
+š0rŸD%îÌ
+ïÐ"»hEá<¹8éÏ|A,d©zµ]_èGØæÅ-ÈA9ÚF&K·âÃ
+ÀEHE:O±TLø­ÏÞ#zÅJI£XfG€ÜT#ÌÀï¬w5¡èYü=
+tÜŸqÑúÏDn:héf]Öè~ÄzCHDp
+ôÅü¢ÿÂÑ!û
+ÖÞ
+ã=ËIÁg2Í áP^­ÁwÅ"B!b<·±eCÛ
+	A¿
+R6XCîk
+
+ošÜÆÅñwT4D=	
+r"dÐÂsXN;ÑŸ§UHß fDg?££«#Qlžðàð¢-mNýaE
+t_P#GIapsøHq@ ÖŸ
+hÑxâAà#^
+ÙpÎSP­Jc(xtUæ·éE,t3:V=¶{VE
+C¡®üÉU@Bà
+P <ž16KŽ~"ÉÑÊ*W\
+ {
+ QŠi\?€c
+ Î	ò
+Z
+P3
+3ÇŠI)¥e@žf)$,"FwÈÏcBt4ŒGÈCa/5pøY#õ Z	u®V¿] 6:.^5~°W$dÂ
+y:§
+DÎ5+J¡Ö&Ù
+úLB_Õ
+)åéâ9=Ñðæ
+JxZÁÉú%2Åj'#än \Ì_»lÁsÆñ9°H`ÏÞAAQGdpØí,Rb-ÇdÞ£÷I@ Ñ¿
+£q@ñ_ì¶è<²<ö=b¡'µxÞi¯DãåÄ|'À	
+	ªcáŠf[2/.'Ðô%Z­ á
+|"r!7f[°Æ:IënÓ4\Á5ª²8TþËL¡ücÀm'FdKÂÂ  ÒwIhd
+,.
+£±Nî	ïCl¢è;PiÎòc1²7Ø	6wU"'<*=[æcµÏ
+ÃÅ66ð]-ÀV7íùgq³Ëò'ýZ6GnH]'æg
+û";ßcã±S ê=(#Ô¢à°ÿ·³³ÒÀ"þÓÄh«	ÁÌ21ÁÓÞþÏ£¡@œ<€1«ÜN.0KºÒNß³SªÉ AU,FãÃ=Ä685é6N+2Xåb¶ôÍ"K;ªmã@\à³ñeq nè
+ÄfÙ®C¢Šµcá¶ç	È/* *7	qÜR
+$èÕD%š8ï"öÖ¢w#+<#€EdZ\
+Xí ÎT-i²2"/v1E¬Ø.8ø"É:1`tw"#
+ù-r&L!L
+@d$3h0TÅ]¶b¯DaŸÃT[ìíb<Œ 
+
+ÊTâR~
+J ÆàÚñÈ7Ø{= öNå@ØfÌÙ œ2B l62árÈêÓðdgC·ÃšxÂß«LxŸ0g'âtàU	-^l¬;CXa4±íŒð*	/RHögíh
+Ä6¢(G+vC¹Ž
+! ÉWñõCr®Å!:a7eÂid¡
+ð
+4¯îÈ%FŸÌ"(
+ÁôŸ
+c"7Ihoõ8aà 
+I¹
+¿glÅeºÛ€%EreBHšš'ŒçF(ìÄ7á-Å4i6NýD%KÑa?qV'³zÍÎ*~
+æ÷D0ø¬nP>d_ê»ô
+ÌÉüoN
+10cLD&~
+ÇEóÒ%ÁNð\šªÕ6Î
+ÂÌ¥l;'rO
+c06X:äòúK%
+-
+ÒI-ÉPö|äÙ  Æž¿§Íp=\vË#ÛgµsÚ$iLsÚ2£àìÈâcÃhøF \^ÒÂÝrTžÄ-&,cybpë;~=â>Í€ãÒœk3|¢DëÈ¥\®ÀB¡
+( à_çX0Sae
+7
+ï#ï³PTïg
+ÅN?1P ÊY$¬>kV!Gwê
+¶,Â#J PWuÊ±=nÜi#ªSi!òCB¢ADà5à±×ÍëpèX0eÈ4T#%J&W7ÈZM.ÑŒ*°²
+$:)á\õ!ÿ¬,ö1bŽèOTè8»¡&ÜÛµGH8Ÿô>gPÓòfq[è$v>
+;à)€
+Y)6pe%d¬ÚHÍÛew§
+¢Pj¬¥óÏBÂD¹~UDðžÍÃHä"Îq¡8èF'ï3pèBàüµ(¥ÌÇD©;øV\ýB}E`8 £ì
+Œª>%µL
+¹
+RßPeùIÊ1t
+â	Š¹çGìJ
+5g€¹ÆÎL{H#Pb ïn)«3²BVñžCÖkµÙÚ29ÂöG³¬	^ž&Ñ­Àé¹­<¬}ãÈLËí:cåã'ÔmVüªåÙìf
+sDxb^J­å© 6÷Qx{n*6ÁÝ¢÷WXhé$|4šüõ'29é>åº D,®ôÚ ø~®!£Ã²+6XÙòÔ+K
+õ 9PB@ÝÈÐI
+9©Ë24PvsÆ\iñØøVGäj'YŠŒ$ÍâÜä¿q³Å€z£òÄ©êÌj`°
+Ä[[sí£.øÁ ä2F$K
+ï8.lk¶RºÃuÈ6VTCYH<ò²%(G©B­+Ók-³ÄË<ž3ùM
+zFJ»Í_£Ecm	Š
+ì©ŽÐ¡Y£«.JS,
+Jô#nžºó¶?ŒÇÞbåòþteäsÐ1~9öÁªI9!Ó0øèdÃÄþ!Êæ°º}07Š1â
+É
+7ÕX
+ô<d2ãßdØÖÆù¢ð9Ž·	sdré)p
+B]#[pïfTAU¢ìÊ³q€g€2añµå®g@£¥,"1SùJtªX(2|
+dJDù
+
+¥KÁ!ÑÙd)§/ Èäª¡
+!3rÅ>`ŸIáq$Ç£È}b]z¯4]
+óYc
+÷²MŸO¡ewšEàÚ@8rãcQäy(
+­Ô@à#ít3i)£÷?æû.¡è«Z÷³Qd²þ@<i	Ât
+äÓµxüµìì;Àeh¬äHÉûŽ4øYÇuðÒh&	0IvÀMñ§ðcCG<;Yòõu§üDö(K'	,h	Y<- F
+h$¯}?Œ²$
+ÈÀÓ§Ë¡Z
+wj €FPjU;ø±xo®%cÌóÊÈ¢xú8zà
+ªÝ²dY#hVMŒj+ÀSa¢±±Œ,BØ@
+ÄdÇÐn«%Ø_i0%)|
+{£ &Ð0zÓÄÁ#
+åü·Ã
+äôeJ
+?°¶Z°T
+Ã`DáÌe)$öÀÐd1Ð7ËÓð€N
+CJ^ ïE$óÞ%bVILC¯ñ­~ÃKŒE
+afÙ@\YSRvÀ9
+§òà÷Óeêœ¶pi+L3ç¡é9šKÎgg'ÇDŒ}å7n<c¯
+
+î©Ñn°ü¥1ó00@
+îÌHë¹èHõ8dz#ú¢bK
+ì+­9ôÆá
+LÈÅØWæ^<	
+_È *ÀŽk¬
+èA8°h Å«©»Ì2Ç@€KCáaÃÀ?_nºæ	!µÕ÷ËÀÔ]él($
++
+
+CÄ¹
+CÐÎLôùCD¶ºJ³ÇGyÜœã7Cif§GËãMì<ar%X¢
+òÐªZ(ÕqŠÇsn>/iè@
+Á Ç7RÆáMî,ÎÜ32<hÄ[æÍ"24QLÂa
+é[xf 4°µóV3o¬ãÀúå<Üön- )*ÊrÊ¢!ZdíüSÐñìÆÚFÙòYŽÜLšJË"ã%JÚöq
+&-^þÌÂoÄ!µiR5§:,Ó.«)Í
+Þ#Å¬ëÉ£R¡©sM.è¶ØK;FŒ°5çÝ&!ÚeÖw4
+?
+ "Ó€`ü?d
+r@_Ï!(hÁÐkŽÎ§Ä
+eBF¹ª¹¥xæICd
+ïK*@*a-×@ÂE
+*T!ÖQXNëRŒC
+
+8ZzCfßfé"$
+µ57#äþHÿqÁTe}ñ¬°&Ç 6gsÉÏPADÚ
+ÉŠ%!Gp0lÎÁÚîÉŠ5¡	7#rvir£œ€y×1ªŽPªØÝðDûYíd	'j¿A°Ë`a°ïP0ÊçBUs8Qü°$À[JF(	 9=\Wt¥€ŒïC¥T[L gÄMß*4£Ž€D]
+S'ßEò¥o Â£*èÿJS¿ÏÚÅ~I\Ä#Ð
+ÔF"[¡Ñd\>4"C¶¡(L)Q%Má
+²Þëhª5Èg€oCL'îÐom*8¬ñÍM²c
+l>^Ñ+ÙÂXb[7x,2 S*É°¬3\Cä
+ŠÔ@Ì
+áÎ`/;"`ûåÃsæg÷;Ñ"ÂbÑ/0ö2F·'[LáæŠ)E`h44{äãÂÔ ò9RB!Ñûf~ùîÑ³<
+ bÂéyüòiæÞ@d
+ÈÃž|S/«+g¡PÇ44Ð)é«M S°$6
+O§INâ6 <
+­`ÄªÛEc"µÎš%àKNd$€@N(Ž:Û
+ËÍs[¹ÓžÔ
+Ô|S&_¡V"(hÝò$ø%Hçp¡
+°ÿDÛq
+ŠÃS +Ÿ$ÂZ 
+Öwµøh4µ
+Æ©€9. f
+ZµÅÊ,)H%ÎO§Ã¹-Dú{o
+
+Ú®%¹ŸzqøÀEÔA^dqÉba
+øyè&R^IÚG
+Lïo8Ä:5ë²É=[D?$ZÊrhGÒ
+n«Àà è¬ìýRÆàOS
+âwiKãŒ¯"_»ÑttbÁ2Öé*ŽÎMàÊŒåá¹JÜ€<l1Î7¯@u·Ú¬$|ÛhJmœjÄ¶tû»7<ÂpÞð0Õšåèà9ÒDµÑõSä
+rzOþÑS÷h[äs6oõb
+wh8
+~4&`=9UÄñOaòû,
+?sñ}?aó÷
+»l|
+"å¢Ü'Ó ³Ìrø1ÒPŠª×gÒn4 °æ¬V°&5RîWÊPJÙà;0Š|Ïâ£0DOžuŒ'ÊE
+ÏÔ!Q<(ÝL$ÆÑhtÒ/R¥¡Î Fä2=ìÍ®]ÓêÛðµ
+IŸíî<*ŽÚØgôOV±
+¡öŸ°ÈDÃsQhDd |ügbà¶JÜÇH<Pz	Tæ/àír/@§A=â£:ªã9\]3òÝMufYØÀÜkÄÙHfyMqW=&Uëlm
+ÿsŒ4)¥À5YëüµdŠÌUóÊµFÄ]dI¢V[Î[&Ûy
+ü¬nb»º1¯åo¢w%ºÏ±.£èBiòéóèj¥¥éË?M>¹râÊo(Ýö²à©õðtœÇ|0 ßPuÅïMÕOÐåhÑô;
+Ä3×uš¢žŒ©_ ýIZü§ jeWßš'^
+ywìÂË1¢!$¶=LNKÈ¥*0
+
+­ÔB§
+"=ÏÙ©šÆT­Ðº0P®äæ
+¿B
+ÌBQµÐ7Â-Ôú4!·Ðf¡ÎZhž!
+ÿd
+¡Ú ç24øe<ªj¡<öÅ6NLL@ùxæ(e¡@,œ²¶Àþ>¡Ë^èýçÐZýÊÜP7S@GØpÑ
+&9y
+ŠX\þÓrd
+ÙõØ0(AÑ|VÊàH¬^%,°PIÌàxsD/y¹ŠGzNàùZZRCÐsK\À[§TÓnŠ&
+â%(RÏH9Q9ÁO
+Å3myþhçyð*}\%JãWIÎ@Mê&ÈXÎ"k­ÃBŠ3(BUqºuïµ
+ÿóÅàöý£ÞnÇTÊd±Y€YÜhÕ
+×@d©lª/çYÛIwÈJûÐ{}Iä;
+d5áiQ
+î¢bF
+A'oÁÊ7L1pb@
+ÄËúãÿÔhJ¶þL@O
+Ào3¡ª"H,
+{QûIy=B0Åæ	*ÀÚ4"bHFìòh0ZþT 5Ê³<q@}Púß#
+pfeÄBÁhô>(
+(Ðíæ2`³sÅX,S·6°Å>ÅZ$<@ß
+<Îšþ)gÛ£w¬v
+Û²	Å)3?_ÐG[éz
+,@Ã7Gå§­Dæ%Ÿ	,ö¢ëÀdfo1äêrš×H1I>Wö`yf
+I\	·aÑÖ§l1åçðÏÄ£ŸªÒá¥œU°E³6ÅàÐÆØt?ô<=PæÂpUÌŠ@^¢	J0üI!X5}¿}NøÃVT2(üa
+3êFe$ÔIIê)q6
+ù¬+Ë+Ò±`¡Úö9çÈálHCKÀ.^J
+HŽF{ÀAEäôì¢2qÃsÀZÊþSÔügT1öÙcó;dØÉÆðUûÉ¯Lš.À&ÁtvÉXáø[¯°ë&ÎFànlºÕÙÂK³Ï
+ÐSCö"à6xð/hc.ÔÃzEÔ|O,]ànûI DCY
+ØºTÂÍs`XDùÌÒ28ÍPAúöéT-J€Ö#£Q	ã@XPbÔB«E¬ÃMJÂ°ìÔZFÙ#§%
+ÄÎ.€1«Â"
+
+ä±÷ìí5'.K.ÉÜóÉÔŸñtZ
+3PlÜ÷PžFzjàŽ
+ôÇ²$pÐí§¬.ÊyäÐÂ9_f}ã\Ó~>>vÓ2ãÓ2U<l%((¯ %aC@€-§ šmºf
+nžÄ
+@th0²Â$iËÒkTçœ¶qJNô&*XÌhðü!CÅc!Ž&Ï»`JñGÑ>LR*+œ;>Îæ4.ÿP£=]ãÁ¥ bùAáõ RôR1`gT HErð (åþ)Fô"-jj¢Œw€ßYªÚê:Èhòf}17HtA?ÑöàÜ
+I1€°¶Oå
+weq-€à2\Öb)ÂníÂ²A
+ÃObVqSx;MEKÓÑöÍFü4ä[H%Éxj`°ô
+*bÂiTQÊº¿ýL€ËôÇr!hê
+ç6mZ
+MÍÝñœžP
+RŠd¿qÇç1Ð07mddhºÄ(šC+Y9R
+²oÆÙ±#bÂlAÑhé^Ë4ùÂ/`äC8YÉ NÇ8TOÛ²èòg%úÞîÑ4ÊS;ÑDa çÍ£©
+S
+s-}\5Hk#€
+µ@9U|6Ôv¡b
+Ò
+®-·UdõRzÅ"Ù§èb:2o`Lœ¢©^ej³#õs`FÏgÄ± ð»ÈKÞ@ôís¥3o)h¬F(€#kê,PóúŒ
+íçú@PÈûr¿Èèî3;Êi8>Å1ú9tò6¢SN-g
+@ö ¿ŒMÝ!XBaÊÄòÎNÛÌÞÕ#ãèLÊ]§m×PX .¹ý.N
+2>Å
+¹s&hÄÀM(f×ž@TÈ+Z²z€ÎEcw»ð6uÆ}pv
+WÀ ÷=ñN0š
+Èã4*àùé¶b¡€C©g¬{
+âìq7I|È(á@JÀ¢rP5šÔ^ÁU:GðÌÂª¥NHïàEê{svüf¬bõ³¡1Ïl8Úl]'¡á§¥N6L¢BBC­ilÈÌ«ÅÌ`j
+šéE
+9ýIÌ[Œ«kÂz!Ëã
+á¢ ¯WŠÒÝÕrq÷4È²HÊ¬	Õš`ð§tj74UaÐ
+ª²	-ÊQ¹¬­E¥éq€¬4ý0ü)ÚÑ/¡= 7ýiXpS«àâ]bèu
+©z¡ ã3ÔÓ	Îªðq
+ÈíÂ
+§OAWìòõç«N¯Íh@Ðã#<%\ÖzÄ
+uÌØØ
+
+
+Ù>I9Ìòç0­Îù4%îctÇäa
+J4}íD.°
+;­¡îA#u±v*ML
+C
+-s(Ž*y
+
+Æb	
+±51
+Be;±»È¹IÀ1úT$>)0
+ç0knv|ãeàº5)ùøSÄâÇú€bYíºPV÷+§Å'ÀCEÌu¡ßEUÀWæ¿Úhºás!Ü%ÓÜó2;óvU-T&V èî>'Ò"(Gº¹Äšu J«
+#äÄS:fâÙPþš¯ª&Ègæ#ÖózŠÙorR¶*FáÇ¹­Wµ@Ù{'è²èã9XLôX>
+Û
+rX°ë(qÒ3ÆTÊ¥ñ_ð»0È'(¥3\Æ8ÀT]8
+`ÚOÉJJ,d& ?Á
+¬?ŽÔÐiÊ"£4q;~b
+š0ó&È
+ôx82+kÎzæÌU©$$	wŠšJ}
+«ÕGcx×ª'
+@mª¶ò8>eŽVÀ`IelÐkKHÈ~ôÒòÌ.³ñYî	%¡wÐ(
+içûl B»«°«]&PøFOÍa9C+­] r7>ž+~,(,®Zê41¡%~Lã¹[u©óQÎ°zñ®©ð
+ðœõKÕ3À`fõ)YDÜ€"¡ÐG,ÄÔ2ÿØ 9v:š>Ç¢qNáž}>Ô£eol³§F
+]X>i"¡¶ðXdš@N5^/G47|Áœe/mjæ:ó0 Äq,~û8èÕï*ö#ŒªS9PW;±Îp²Tš$:uR
+$®œlþ§áŠ[4;œÒéc&Qr)YÛ\7"«ÀÕê.L	ª	­€(g@$0š¶šÆ,+Uàc"gm|4 Ë`NïøhLº
+?E
+3Š1· z i. %
+èörxŠBg
+Ök``zÊÐŽ0õ°÷?Idº:ëJ1{OŸC7MQ"ñD÷ÄÊ
+
+ôèv²)pB®ÞÐØ[vE
+ÉÅÆÚŠ
+;²PŸè%ý²ãCq²)ÃªŠ
+qReãÛ\°r:ª!®Uäá¡°(
+ýküWõ9cß
+f5îÉåzöÔ°¹ÂHµ [2Þ.>PUèÁStzê¹¥^jwÔ0jÀé{RRyúGYfíš¯ÀÌ[üÌrŒ¢Œ¥n1WÕ=Ž
+DÁ¡D¡gÒ*1%ÁÃe7Ê	O
+iE9ñ(0*ÃrMéÚËõ°«y«Y#5àÔ÷Ôü=¯qekÆ9rXZ?*N<Ç®¡puçA\Å>Î{P3#OQäÑñ»f«xi¢óªÒ£^uŒ|Rà
+å|¿H.	°Ââ­Í_YÔ%
+)fpDÜ)¥ãaÐŽ
+d%Árª±UÇ¢ä|qÍ ³F66nV
+£>PÔu­u.	X
+ÊhÛ5²y»þ2d?³!³STæášE¬·l š=d.F'2QòšÎâûÞM5Î#äÇOØê¢¹c2)ÿöùÉq/U£ØYÚ¿²gWµÀrórG[ÕÚÀgïÄÖ )²
+œZIKØË~ØðóÊ4:8¬ÑKÔLjfà1-²53FgÒA»Àõ &â{tüÈ4×ÖÀõÃvv
+<_;€Ÿ·ÖA¹m}<äKV0[*¡yŸF_Õ6
+ÎÃe ûÊT]^
+a
+LžbIøÞZÍ)¥Qàäw5Šm#
+G<4Øñ"JºIÕôL€{ÄDã°Xz€ú]#
+íGµq­øxJ¿€qí€aWÏD°CåUç­
+Woe4\úØ£ORpªàdóäÚ,iuíûŒ:Ô
+^y!à©ÞW{]?
+ÌA"ÒRÚü3ê)pHOò	5ã¹žs4m}Ã<€ÊçŒÊ\mÒ)UÆ
+yõÄch2
+m®ŒÐ#ŠZí|&â%¿+%
+K&Ç°Ÿ(
+.ŸÏQAZÄ!Z!{ZÉ®,x]Z#©V¢AÄtöëCQCa6$\£Gg5#|Âv×T,º=NÅ[ÅÉÄÌ
+D«UÒ9B,pûÙM^-
+*óo!X4K¬B"P€²{è8,
+§@kšÔªãw_àb6°Ç@aQ'Šx³Kªj
+Ùoc
+÷CÕOæýºSØ>ÙGÚ/
+ÏØYœ@¯Ô`uÆJ4VF Ëxd+ÐØ{	ëVEÁæÁ§	næ¥ÊbØ<mµ;IG<¹Nú%°YêÐFß€»Ís°ø,d8£y#ÔÄóŒf\·bÞ&pY€<+BžÇOtÃ
+Á5hÀ2 ÉW%¹.åví ¬bÑ¿ LOtÏQ°ÎfVÜ9š\G">c¬ù­Å_Ï,ë
+Ò7iÎØ`äuÎ1SðÞÒßüEgýH AññaÅèW$Ñ(éRâB]î#Íç@OžüÑÇRî$\!:EGYŒ!p
+±ºÓ&T£ìå¯m&²b0ÙœÕ9Fæ0Hã4$çÃìqÔüÔ?+Sú°RVÉàÓpcÑ'Ä ®³ýî-}ãXj$ní^ðúÓ¥¶6ú
+³ë¡]
+
+)0vúv :Ç<á
+œTÕ+e
+)Q!¡NL	ÆŠAEQ¶ÈóæVèPv*ngoìyÌ4Âqnx^*Nv4k¯TñQ}
+1ÓÏböÐôÔ7ÈmÅ
+TÅß ÅM
+|7uç-Rð&rwŸ;ñ*JªLdc±z
+IZ
+WI*ØÃbÃô38à,T>
+/Ö
+öåiè
+xWïlÏDþ(oy2LjðDh8h±óXÌâ7Ÿ7(â£dbšúô­À­9È\Ñ(JŽÈö&]S.9º-i²Î]ßa°úÒÉjÚf`ŽÄÒ¡Í Dd
+Ì>õ­!òTfËðùŠ
+ÆY0BÊ
+%ÎP·#
+ÚÂÌ¬p$šêÂÈ¢Íiáòü)Š2j1:ŸíÖáâvîñ\6eQÅJBOÍ>ÜML	
+ØUçíá 
+m02uYr·àÑZnÔfÒYÚòÅny
+ž[º:oÈ:Èñàñßæ2x"óAÈK0n
+è2Ði¥n|þÔÏŸZr0@ìÖ1ÖQ§>ÚFa(=X*±£îÈ~,s¯²~ÙVÐÎH
+
+¥V%Ç
+³wjDÐJð9Àž­B"fÍ ÖŸq€sáÜ?k5/ù©t°Óp&W	:
+Þ$qi²sô£{á
+9¢
+Yb
+Òr
+8ÒAkè¶f%
+É]ÅéÌPÇ§?uJ(§tf9ÌKðdPüË1cCnšÉú
+:;+&ÁB5MÊÛÔ³§maTëšŸmH($õð%Q®®|ÞÌ²6îhÙXk[ô@6&
+©,&1lkZ0 1O
+dpÇ¹Ž!Á³L]3Ò L ¬HÒnB8ÍJªÞa4lz%A# ïsSî4×žm
+wršl&¢Y°E2çé€Œ@
+e©#
+[_v À6ñÔØšM
+µ0HÉÿó
+TW)ØUµUNSGµÌGÎm<ûìL£
+Ë¥Üm§`äÇA«xÊH~n>€Å¥`
+ñN-
+±)ÍÏ³6¡ÚµðmX)«9ÍÃýC Ühðò¡ÈÍ×à§PÖ°2Šg^-,M'Qµ-$$ìÖv7ôAÎ 764è¥<F Ì
+.ø[H h1ðAP*ZÃ¯ž¿­59
+ù(OÁ/À|
+wT³â<vÃ×Lòº*RÍ4
+ÿßv|Vš
+žóšUlNPJ>Ûë`
+Î@lzEÓQ+jüVw 4hÙK+óE
+Ñ/r
+ '
+çmpŒä)P%Ø¯ÿk5Ô2'JèÅ·
+
+KS!§b
+kß!O¡S~À«I¡Ú*¿°KfÃ:±
+ÒÈ
+ätF³M"hUHC·5akRÝ"]äÆÈÞžk<[N:ÞJc§e	P2àr. š>
+{ý/ªß>eé	§yËD0`ÉL7²i¢s žtàC£q!8"êÌ fC!îzùç@K@pX(@
+
+d@Â@®[îHtÀÞ1<Òv .$!©Ç
+ZÅDDž¥ÏŸ%
+·t«vÉ¡@; È.
+±ÛÌ)ñ!
+Q±pÑ)ÇÎÈì
+¡:mêØu4š
+ÙWÍ5Ÿ'.XAôÆñÿP
+iØÕ2_[
+ÑÌÁZÌ·MHß	3ËT!
+ã¢)Û:
+°Éj
+%
+.i
+;èÈ
+œ@áátVéŽtÐÑÃêÙGò,Cm3á/Àé¡¹Y>áj|®Èö{ø)N€KÃÌHÏ(àòâ?}f§PÈFNsòŒØ(8NPh1 	w/P=s*nŒ²^%l\4p\ÁC.;M-ìÓä5[øV!¢À¥éó$@áØ
+¢FØ*\E/ûE2G#\¢ûÀŠ#äz¶žWH®) ³ñ²Ú¢}
+
+On$@SÜåJú³
+F^4ëQy
+H%mùÚL%fÆÏ ,°-öõ	€£
+­²uóÚvB,w©
+[¥À$³mü\VÉ£ýCòš|õ¡Îœ
+ànÚ î¥Éï!F#èæîZ·ÈçÂ 
+¹é:I\XöG¡BËnIšm¬" ñ²¢+ÛÔ2$²ÓÌHÐ$
+éÌXã$ðÓÂ±NCþ3ÒJkÅtpQž	~aH·ÐÀaPºSq€ScŸÌcS8$Ý¢Q{`>óQA±íJ
+Hå	g2N¥
+2é&Q¯åÌp fÝ2-â%
+îQu;B*S&<±šZÄÚfç­Ë
+B(ç(3®@ªÓÖzæÉ%Ú
+Â¶j®
+òshiŸeL^MUUîw²1cH0.»¢]w[DU
+Gº
+~sú;
+DæÇñóçºuš!OONæYžÕÓà7š¶ÎP£c/;W­å Ý ?·*ÁÿÓånGx¢8ðÑr€h ¬r«
+¬³îçÂÓ·ýÚù2MYo	ué3ÍD
+¢+ï"ÞY)ê¿[Ébm
+}O
+	fÆ±­7/
+×\ÑN6woñ:õvÈk§§áÚ
+"$
+i1\ZoW)ç°FŒHe?×eâ¡¶Ë€áD€øèÈ@ÁUÏÂPJ©d¬Ú¡UŠ)µÈ%&lè!ÎÌÌdŒÛÎ4·"äImb(°
+lºÔÖë§;@©rKsJO×»[EÙ·²i)nZ*õ!«-÷¥²³e±öMÒ$UØg5ò`ºø
+xYÔM1%
+ë6[í$Ý
+Z¢j}šlJFú
+{Á
+jžªlÍg]`º ³;Óå2%¬"Þ§àÊ	ýXåw¿+ûÝ3LË;Ó`E©Ò&Ê<­±æ²ªófle"íËU×Ló(YÓÜ*)	œK>>BŠå9ZoûriÈ£Ö¶ñ(³hñÀ¥»AêEÌ
+Úž@£÷ÐZUiö&SÄRJrŠíŸC$Pjg·
+­z[u±;<5's3¿$µÇ:iüØy¯3><Iÿ¡@>R$>åpG*Æ$
+4Wè
+Æ¶!ŠbÁ±ªóìïêª±cn«EiCÝ¢ïÞYÅK¥š¿yJÚZûÏ7	Ji3ÑZ2$ÜV²tXMìbXÛÅ]í:
+àÕìoÃù
+)SruKN©
+îÖÓŸD£óöÑxg¡î#6fÑ0e<8çjmX
+êzeiÕööŽ§+°
+øi
+Ò|¬^2:°_W'oW@Ks
+*Àµy²ÆpwcE&€emZžY`ßž}è
+[ä×±Ž Úø;zjúO$K
+#Þk1aÄ'
+ú'OM59ö{jüºê
+b š\ïYz]tºøÇ&·zëÎvueY¥ÅkðÓÅA«õ62Ð]SI°
+«_K­
+Øšd
+p_
+fÜ %s>å6ÊB·+ÊA
+AÍ=bfù
+¹ êË«EaøHÔì¬è§M^³üÈ¥
+QYMÌu¶yÇŒØñ¬©ÆçÇé$z
+qôm×_°èíòÇûlÁcÜ¶ÇS¡àÉÀ'²Pb-üeðv(¥ T¿ïgô€c`_ÞEÔB
+y°*ºÜz@ÄppI}HàFgqËŽkæ
+©äQužWü
+HhxNè m@ÿäÒå}	âÃ¡eBÁÏåÚzHfîúm®õì£ËL®]ôL	
+kA,ky4ú}€Q»à±6Ö#LC\bèUj!Ò³ïU³È%
+{+Iä€=Xö-N&±éd×G2÷zê-
+ªj+¿NÈnèôû$u&¬4,ØèÌH®4[ÊÍHÿ'W_$ã·üâ
+â8Ë
+Xoªôc\bà9ÎŽ¯õ^âý)Ql	Bé÷ìCXµL 0ØÉlf!Q#{9³hØÎDHÛßKý4
+
+j¿·V¶²E Ýž€qÐÆi +°ÈîTm	~
+4¥ŠÂÅ{YUK)S}RÑ_kïüqÁ()
+~«µ¬BE3ôêØTö¶'^1å_Ã@×SÚÉŠ¹' ñJæ>ÛCÙiUãy?Â×ôÍLÙ
+ »	?õºçtÊµñøqÌ@ÀŸ_¢^«æð_»îù}Ñ¿IkHÇD8Äë
+ÄÜ7/)`Ð€²àRð>x­·ßSÄ ÀŒÖÕÌQ³°|L<Z4îbêýŠÆ(8ÕÖ3BŠ 5©Ð]6A^VëfgL9Õ°f
+{Þwp)2Ô%ÊÉpSÑ!ìTÌÉ]pãkËž9Æ
+C
+Ï;ç2IÉXKKí
+ý`t1{J 2g[ðÊÁu&¢îÕºº
+€¥àsçlkŠÝºŒŒ#8£ÉöI@bøzÝ0*]$Ð çbP?Qk
+
+ZaA!y&pøê6ÇS
+ÆZÆ²|±	jvX(Á»Õà${÷1[¬¥B0/ÅÁÓYÈ]Y~ª>M^¶€üÌ)K1]*å@Ç¯u
+Ùb!Š(k6CY@ËtŸ4
+|Rëqf^È 5#±óJÃÃ(¬@®æU­ûÝfü	ÚÅ#P2d8¢2Ï$ÍDJ
+åÅÏ68pTJÜ$ÈÝ}oR
+Ï`fWÅ
+nØŽ
+œ0(êhèqŠíßÃÞb%wåZ(`1]KŒqYæ§ZÓÐm4	Qç
+2&R€1Ýs=îíoù·aÔ+s|ÉÀ¬³Ê©^â}rÃ	îÒÜ°¥È9è_õkÁ+Ô'ºž:TU9Lõ),ØÌ²J
+ÆLõ­ÛJXÈN7²ÍÃÄ9_Wð
+îBÔ÷xPèÙ(¢
+c<Ü¡(2
+ý ®DäV3¢dâYàßVåSo.áŽ_Š¬äË+»Î×à-_žpÖ@é6å¯p`Õœk¢;š0ÕéW!žöÆ£4Ï²Ž!B±­!r]ú"Ù¹øã³âìû}ŸŽÓX¡D1Ë,;=.aædBBI°ÚœØÌè)ø³ë°pŸd!ºféÂÑûjfTâÈ>¬>ÒOŽHÐÕ[Ø?Ÿ¿ä78Ù=.¡Vxcô5ú!KH°ÀþaCòÕÊg¶#¢j-XïÉ@øU[€`Cª:EWU(õócf9.pÑjx)ªGJ4Åä®còhÌýônÆŠ¢ãŒ²ÍZt©ò|œR+Sñ¢BŒ
+at5_ªŸîGÂöFx$Ì¶)ÞQóØ0.sz]%4óÏBqìž.ZkI3ÜPTnœ¢/Óç
+Z¶À:<<yÞJ¹HOL
+.[_
+¬ÙmŽB&öñŸC$n<ÐI!°4Õq]¹qCºV"QRÎéÒPa,Ïš	ù·K&u®F|j,:ë$|ŒšØ¥ÐrÐë
+ª³ÆÜ¶ÂËíeÖ	ø·æ¶2![öqMÚr
+ Î
+k·ª4.öY^v
+šž
+ÙHùÌ~
+:Áš`w¹m|x«Áû©xbR¶°žù£p$/€¡ßÇ *ÖOýø"
+|&Ä®žŒfHO!76	 ©Ð¥
+2#C­òì¥ÆM'øB0ùJ#Ú=0íx:k®
+§h}xŠ¿ÏïçÈSÈü°³Ör\éÕu\qXâ
+	tIºŠàeÅc
+ Ââ%
+äÑH~œä+U~WÑO1êU»æ,Žs0Ä`èÌòCâ~b L
+³£Æ íÊF¢'H=0
+ÑTŒQ{VJ`åQ#ÂùÆÙ}ìÖT	~cZåÃ×w,Š	Ó2-+P
+$èß©ô^.
+1À²)E|KbÓjÛh/ñj`üÅH;M4"K¥îØÍz0?
+0{ÔñëÁ;[ù^lÀ+	®§ìÎŸ,â8%#Q&¯ùLÛBÃ
+0eäEa#Óz
+tÏ%æ8Îô&õþŽÓFÓsC³8>AÙ\÷ÑöBøÁ&íŸ
+ÿÃ^MÉNšËÿj×0`Ì7)ªÙ0®4Ý%eŒ
+P³PšÙG+"GìA»ÐŒÓø@§*r
+
+ÅâUÇÆQL¹jJÙBVìÛüÒÄÊj"Ý,$K
+µ¶u³5|+±­Û?µlhÄ>Só¡Tîv]Ü~Iå\E>1±Á%ãTå>¯K$Þ`yJ
+Ìÿ¥v8>ñª9Æcq^3Y$±£ÄF
+Qáì%ûž«X?]ëS¬È6È+±­F¢Ö®Ö"ËZ
+W\Õ	ÕÈÕh`#uë0Ù/BŒä1F!»ÁBAm7€ZÌ}€4>¶îX¿çPªFý&$ë°ãÈ·Ù<q}³ÎEµ¬7ÄéUÖûd_
+J
+<19€ mDìèd`ÈÝ7òæ"Œmûz
+ME%è?€!Â×@òJ4
+i©lNQÌ_Ê}
+©rdQPŒ@|bÐà*TÖKÉ'Cq|Â±­ÐÖ+
+$ó{
+
+S±5 Oå+PÙ=Q`©€ý
+ïæëµXšÆ<,Zd+îÏ;M6F
+[GÕØQrå(ïx-²4à-
+ éâåoêFlÈ£Nt`
+ÆÕµ&((ZYÈbZÐn	Jõ1Ÿ³r5q¥Žf²ÑFv$»Ü#AWCËGŒ€(ûØL:=3"OªGG+ðyûÀüÜ±C^"W0¡]gjÏ
+òEHôÈºHÂ"íq#ž;µ$M9	A
+è©?uaGÞŠNìÎ$:kAš?m:šq
+ËÀÆ·P	ìâ¬aÍÅe¥ŒMQjÐŽ
+PÙ·U)Ôîáµ`Üjo®ÓtÛTDSë
+j¶DštÒ¬³æºÖð7ëgåóŒÀzjrë
+6&ÄµdÏ|9Zf»P(ÁÝMÎúÓV	ØJWê<5k¢úpÁ7(Ñ	` 
+`4ªI¡»#
+£B'»šB~~©%)6Ik¢0Ñ)M5*»7¬žhXÌ
+B)Û£BLðniS[¡pZëMY¡
+Z®sm#ŸL]GC!°ûúÿØ»0°*n0ŠQPÄ©Æ¶Ó£
+ 
+ïÙ
+î0+¶å
+Lâò2
+ŽÀZÇgÄÑº€_ðéÃ"p_a­þ­œe`ŠÐ%hóŒwÕ~¢§ËžÑ1Ž+xÎ©Î©î#ÇáIxË-u
+dÊa_ôøŠ
+Tvè;O¶,Â+Ò¢D
+]Ymë^&Ëö,õìúfb*-³»ÍÒjîZËHoËY&ÛC}dDv3;ìÁg}òœ¶à[â¬í2ÌŠ§ b>Ù4ëQ²L,c$z.uÅIã.,ÓbN€|€0±z>	
+®Ø'±éåK°U]
+`ÓEÑÄ4PWxÁ"RèYfÉ±Í/]d)È2Ê> iÀcTßûÎ³
+8ó<KºÛlŒ
+òÔºcaŸ8d_Â2ÒÖPÁ<Wž}`)²w²ÑZ÷@IægdF
+°Áá:(`ÊW÷åLEÀè#~
+L.-PkËŒž§k*LÔ)ßè
+ÕiYaÆ/
+0Ìæ]îÁšb[7fgÐòX
+ÝØ4kæ6¯eXNú ÕT)<R!¡Ä9·Aß6;Q
+E°x
+XøÅæøEœüÄ±©}TªÃ
+,|R=/Ÿ?ÔØÆÆB&(&W£Ä]HNnbøššmØ¢.*¯JT¬R.-DwÍrJÌsHÄ
+tFs|Ž'YR25²|ÍxÌb`%ø
+5`>x>ºldHEæéK=Pôä¬
+Ìàó]0ô­Ý&Â]
+Wß(pF®5ò
+ÆÖRÐ
+ ÌË`j\!
+~¿rF!\qÅûtÅ`78ffåÔe y$iîÉpð+ñž*ÂL£Ï÷åãp³Ü²¡Æž8äñÙjÚsf³8ždv¯Á}¿h°
+Ó©Áÿ_Ìþº>ï4õp¡îéŒ^Èa,Gâ.å^Ç#ÞÜ7í» ÏU¿,àcë\
+£ ¿fÆAVô ]bD7g*€GZqè900*
+37dhfõª €%ÿ ðã
+øtFŠ2·aI8åd
+Ó`»(`Í@:Ê
+Â§B#|kr
+$Èü ÓMiKuVyf²ÐÆf[EIEúÕè%Nxz3	¿ÇÇàÆbA< ZU'@Y rŽ#utõÎÍuý9×,I>
+{dÚg@Bw€o*éx6.Xjšà€X
+Ô"¥ÏGfÖŒGäŒý%{5$
+.YÊL-yx,©·M³	ïé3{®3$D©ÊÇ(ïÌÕØYynwÐc1¶(ÚB¶*2TxÖç€]tZ6Ï«ÑtÇ#IB8|ÔÂnšqËkº4
+PX·9ò
+`kj¬oŒZH
+€i!|I
+þÙ
+ uJ
+eV#ÃÀ²¢
+TÛI1¡!ù:l'£®,Ro®sèû¶e¡DY*T
+[{Ãt`#·ÐêèëaþdëÆ<ÂÔÓ
+üfjL#	M5§g%úC>J#{]+Ò©!œñj(Ì¢Þ|è^'wPÄûnds©êà1côöÁÐ ¡ÌÒHË
+HOGŽSJ¿ÕW°QLÎÙ_p1ìLrr$à-æ,3Ñ0P"å êGJÆHŽò«€Ü-¥ïß«a&Ÿª%ð±j¡0íûñP&ÿ Ý	ê å[t<§îªD	BÂ¶`ê\	âªUºr
+p€EGSæq+©2Ž¬¹TNâÑ,»Y-pŒUs
+&¥/Ît
+gö5c
++/
+o7áQt :ODÒÈ2SGñÃx'Q8.®w,'D.ÔÝp
+ç×*šyà3LNdg
+	<Ÿ
+Má×Òñzú².ÕªÎðl<ØÅBÊ Èün¿9ÝÑÕØMæqº¡BQ¥Ê«ÇT²	6jµè°¹amî®@&)bŽ×º5Ü
+
+œ@Ÿy×Ò¢J+=%þMâèÅ'ãÁ[7;ë÷Ÿy
++1D8Ð±¡aæÈÖçÀÂÝªÅ,
+}>Âup&ÝýŒ*ÇÖ/t
+Ñ¬1ðh~Û¯Û ºKRhÉ[HUÈ/"'·¹9ÃŒ)ôÆêG&×»œ@T_*±ÔqCSÆYg#-Žxi[Šj{Xµ%âÇVUÀ
+î%K%4:Aã ± LŽœRÄp|F£ârkmÖ±Î ©¶tÍx8%VèÙ[-lÿ[ÆùdÛ|¡óÇNÃqÎâckùý`M(ò£íY(ÕºüPQV:AìÓz]F#5îÇ9+ 
+Ô`4Gä9!
+Î
+äŠÈ`:P°íëÁkltZÑÛÓ5ì
+
+Ûa©®[
+ï|5A]Ý#	âäÁ°$ÐãZ-pGDNwå4k
+ÃMó¹jŒŽÖ4m$
+9(á:äîÏÄ%
+x­Î5%
+jaWßÂñe=-
+ÊCÞ|
+ãÄêF,¡ÜZ6Ñ[oÎïàY9Zô2ç rPc`é%I£¹x
+š5óQÊP$D"â»"¹¥CJ*šµ#°LÃÍ`óeýÈéuw3¥_j
+u&wwúÔ:RuUÌWoÔÆEÍ.&:
+"2³âÑnÚöcº'5by)X[¬
+63Þí3¬{Æm¢®«Ü¥'¹Í,6:@ÄðuÖ3ÞóÛŽ÷ªš40å'Se
+e%Í~'¯)³ô8[ñ©
+°¶(PÏhÆZóVf¥?M~«<Š Ð}
+v<·Ù.;	ažûÈ-6ÔÜ@õ ZZà\pCÈ	41âÚÝ¹'rbå"î?y¢0ªbÅ>&@ËuzBÍÙ GÊÔ×8ùÌ>uÜyhâgvêI
+SOMØ,á/YPy)íRúÂ¥àÓ*%%Õ€04š
+è±°ótÑ
+üúAß4€Oá§Ï4qrPþD3¢5/ÜÁ»0GQä4·ÄÅ6x	Y=ŽÀ.D :¥K¶áëSšºÂ[
+_
+aÕà
+Î)÷n4e>ò¢cWÃ@FÊ	Âú«
+ÀcD;l,lð-AÈé¬ÿÂe×%îb|œ=T€3šÆšÇLz4  ŒJð°AÌyà£QÚÍÙÓ=JYÀÍ+¹ÎŽ9ðU5qbO­&/4'ØòÓ\wI) ^BÁW39téú;r>?gcZF­ªVeLšo³h¶ŽÖÅÓ® ížgí8yÎø`av9kÉŒrQëÍ5C{vÙçÐ¬à©œÒÇÀÖvPØkþ	Xªí¬àÄ7r)¯\KgP+NË'³_:§2
+Ö"
+õÜÂÔ%!*6õ+ýÃî
+NWÎXg
+7Öž£h*©ŸµÀ^õ7([õ«÷!VJÀeÄ
+Œ
+µh-
+¯ýôYRY#!übæq4£úáÕPÑa#]©j±
+Lª	qïæÆåž1ÁªÉ"ÈòEd¿8ËÕAIÔNëu#cŒÂ2yk @(f¢:až{
+¥2FyF³Žž@RoÚØ
+©e&Ä
+?ÈNcB\x>ìwhü?ZèYÀ$çLÑÃµ9A°ô?ÐOA?xÛcô]çŠdÌæ]d~c%Iö³[ŒlÄø^ÑžME&h}š2QLÔùÜhŽFW,©»Ær5NžïàÎÌZØc«L{Ð·À×¡}
+rÊ5À¡+oíìV×ÒVž
+Ú4^ÀY°ø*óG¡]èÐª¶*ËžÇ`ßäQ
+#[A&@ÇØ ÿ ç\ógö_%LÛ
+POtHÔÿjÝSð®<MÝyô»	=ûž\€A.€¹ä9J,gþt¿ŽÉçûiÏu+ç€6ÀÀMÚâMP	t°VâÈVTÅÀà
+iI°ÝEM`LäJ!\Ü0	vôÎ	u¶qË
+i"|U;¢
+ubB4hVTH6|é~=O[°Lª/±sª
+NXE\»e²
+l
+Æ+²ÓÁáà>	Ø­§µµ*Öb= 0²9ªqÙDÕkÜb íN€eñq KºåNP©)
+å¬Yk€Êa{"¢êôv>D
+ó£q#öÌGh,,i€6Kpí:kZó°,DÁÆ 1«¬2#õ«2#ï£
+,·v7ìØ)ÕàeLÇÙTñ<P¢ÌŒ{nSõÛž1FííÖcÖø*
+û,Â,ÔöÇC°8äõ9»­úCÉ
+|f¶Vž"[ûE
+Fß]E>#º^w(BiqlÄ\Z}õw¶÷(3 /Õíá?{ H¥ [àºmC_
+©¶ñÓþuðŽw ñL3cchÀ> šr)313e 
+ŒÆJÛ
+s¶,»²1RVCžXp­ÎUÍÒÑæ¬
+èK€CtŸ	Á
+]cš#ÿEº]÷¹±ô;¹
+ŠØ
+€`µF`ËöÒµ!¯lsÇ9ÛqjhS#ñ,×øŒv¥S
+)Ð`<¬ÖÁVqöçµ
+>Ó1@=#`P	8)$UVíAváùšü`=7uÞc¥öâÆ
+/æ9H±­l
+Öö	=PP()]Væ¶[;°qö[a°
+,7yeÚæcsyË&à\nß^9çX@]hõªc
+ðÀì@WúN×h[\ž×[°ýŽÆàé¹¢ý*_
+]­ Æ[šÛÊÁ1$&0ø$ÁzÝ!/À6t8QÀè	DíÔ×wâ2Þ,
+š/èÁŠEx"
+šT¹£:ÑmPÔH)ž>XZ2é5ŠãŸë0éª¬eQpgÀŽDÚçõÂ
+0­ÒfÉÈHéIí€Ÿj'PsÀÙ©<X,êÉä +ÏÚúÿV¥adn»Å
+'ŸåOpM-¶ÌŠF¢Q«š,À¡PŒfÚÕ
+û[r(â8:I]Ó¢¢Å±€¯¢·P
+RLZkÇF£Íª
+c+s¥Jk\Á&#g°Yñô{Ñ\ìcèH«­ŒÖŸ±@ŠÔ/£ŒÁÊsyÖ¬ìº"Î­\]WïlçŠ¡=HP
+æ>&2œ:­IÅ'|¿4a`Kw!(zD}hLÅF
+§G&Pp
+ž
+ 5ã~ù .ÈABîGnÅúR&ÌéÊµ³æÈÙ UL
+En]áµ|î©¡f?tu€R±»¥Mi|¬~ŠZ#(n|^ñy€m»&
+q
+
+­®©Tp£â¹ËÂâ
+Ö6_¶ñíS£ÀßÇÚ¿mÓ€MûZûåp
+üí¬?Û³;Ým·Ÿ¶#g?xªšž×ÎKEÊ
+Ä6
+Ôàþ°Ò üMsíZPM
+ûâ±
+Èh+]èãb[9VKÅÇ×¶y³øRMö/a8g
+¹
+
+WÉî7:è¡n fèQÂÄTùÜGk£RtPâØÌîé@RèÌpíyÎF#ÇmnÈÇ0A}ŠÉR)Ú5T¢²n5:çå$]Cîj7­bVü"í3'`c2Î®»%@8&@ÇóíÀBP|Ö'6=ÉRHp%X°ÇÏSÚ|³`.XVÏ$µe(° 
+-Ñè¹i<ØPHhð4Kzªçãçœª~kSœ¢öR¬
+Å
+Ì8y#¢Ò ìpk5=µeMÆHÌš·Qp-Ñ¶ž}EÒ²KâØïg,ß'MpP²1¥uH€í0g';å:(×ÖUXžÔItIæV^ÃSvkœV¶ÕžÎ­©`Î0îæÑö¯ª,®_SLç
+GýÐ?>
+JzB«nÝ3ÞÀ
+^#yC©­Âu&,¥^ŽTpw@àÑL³$
+é
+a}xÅIQí<p^¹5$(LêŒÞ²»ð
+Øh^I0Ž^Ìœ*5F91"éò#ÞÚïT÷€ÏM«Qv
+WHŠ¿*¯r|5ªïÀM
+æ+øA
+Iwå¿0ðžp¶o
+é$ÛwYL/`K
+1ŸÁ"ÓbD<XcŽ\
+N ù_G!wðTjW
+¿'¥lVe
+žÕ|m¶RÛÊU
+­}c¹oÐLÏãòèe]šÝ»YÒNxÁ=±^xß®÷¥v
+°àÇWÛÐ
+AýÀ}Ø>nÍHûhw):w»?Æ¢h²Lšð->]Øb4]£F)Ÿ
+ùå{º(5iLæ8P°C\w%ž#`Ï ÞîÎ7Øõo-õP(nSãG§ñ<®[1"8	©OðDØHsÚãñŽ\¬SÏX
+
+üÞ-lè5gÂó<k
+åä=Lš3ÂuÛù³
+nU^¹te&|Ã
+à¯èHN9
+U×Ûmþ·K×Løö|ÒÄ
+falfcŸ÷œMÂ+áì©
+³cÓadtr{ÿqÚj(
+­!àÀu§ª5¯PH»»ÖlÃù
+dQ'§Î5Ùú>_êdÎ_BN/ïjïfMŸ2,
+Œb÷G	µýXŒd\ÀÑÁ1PŒüyh³ŠÕ
+ÍúÞš
+@L5¬@3mÊfvÓk=¿'@ž
+æ|æ­×!°`
+ý£ ÷°òãÉÖjlGú£Ò²­ÍìVA€yVðïXÇ}Ivm:³Aùð
+á»ž(Ná"ø?
+/<FÖ#¹MDfYáŽ>ócV¹rRüè
+i~tÚ€5ŽÎuá·nXM
+q%t]%âWI!E+:¥výWÏìMtuîÝ!ÛxUb
+víß§ñÆ`©VO}>}MŒ
+7¬Î@
+5ŽŽ&PrdÊBê	æXž
+w}T.}JÛ¢Â¬dÀIÜÂôiPx¢· JÌŠEž²>XÂÖi1	X4Ó	ªøuÛÖ
+;Ò
+Û\AU·|FÙó'8m\Q5îúð*à¡èO¿
+À{C&vOÕîN&qëõÄõKïè×IÇUqÒñèž×»
+w" ÄÍ&Ê.h:>
+×§âäãqìøv\9^EhÇµ`Ì@õ58ü0ós0%);x 6áåeàLèâ2+Föi²õ0ä£)%mãOñp8s|êÄ ®WJ1ªÄ(Ò Ù» î
+æÇ
+\`&Ž,
+Zß
+r@b<%áÜc$|Yí
+Ûí§fÜéT"îx{
+ð
+ï§wnù!Ã¡ÜïÈÂH=Aa ÐTOÙÈñ:
+9\­$×çÈä1råø\ P$¿«>i~Ÿ!fêŠýââÜYX#>.ã
+)]
+I¢¬u>&]P¥ÏöÂ
+ëÇE0aU4w;z'Pœ@#-®±Op}9ç6±µú%5Í!k³¬`ÿCåVbñpÆN7pŽ2AFÎÚ0AÄã×qôxvÜ:Ä&OÊ+ÙFy!Öô
+¯ÝMÁÌãÒ1ê8³)=.)¯«ÇäN¹sŒâ\
+FbÆ>²&6yó¡à4&
+9ÉþNµRÁjØ·Š~
+ [%Ü^3°ŠäaÑÚDÛ
+ŽM
+øÝk«CjÙöÃ*ò¢B`[N Œe@âð
+²X`
+wqµçFx&®øóS¡¥B2§¿ ^Ú'?µs¢ÖÄŽ
+Qx(ÅÏÙDMw«2º	'Tt9¹åR[fùÛ1áuA{WaÖóiÌ:51ûN=Tf7æ)cÌ÷pWWC×gŽ\ÇÔñÈÊwEgåâ[ŸÖék[ü#sŽ±åî
+iHPE&e  5c(11+æ¬µwÓÏ¹Œ*
+uÞy&äÇÃ×VË.mV>mª; b^ë/ìJxUg
+mÓcÃß¡Ð@aÚHL±&a±šá§9ÝØòÉà«Äá)Û~lu:5¡v{Íù
+å+yâÀf%iž,>*æisÔÌl»Hæª²föpëì³D
+o"@cå"'6ŸemŒ|j?V^!ûTGS¶ÝXI1³?+Ì;¢µ\t«cÐ ¢VÝDØ¡¿/Ž	]ãrúGM
+­È}D'Hà "ŸÂ§Ôfž*ÙùGró]	/¡Éâ,q6ŒŠ!Éàx
+].k
+éhjSë
+e%ek¹Y¿FÊ9ÈÒRD&vàO!ÊUÆl÷: )nÅWA¢ë9nB+9aE@·
+ÊAV-N)¶ ÖÞÑ
+¬Å¹s¿lÕ<
+*Aãçg"Dh7çÈ<_õfxC0y©
+ÊgŸ`¡Ç9ûmg8s,0­Z;ñØ^xóKþoFLP¯ÆWØðYDè$Æèi&ñÎ
+è&ÜàKÍÓŽËÔû×ÑVÝÍÁâ¥
+À
+ø[Yn©43ö
+0LïZ-ñBÂ0NŽ2}ñ^+ddÆrÝÉæ±XHáR
+[
+Äcu€ìhK°eBh¹œw4+RÐiqÿM7¬¶ÂÕ1Ã­t ÇáJªþ®enäX4Në.;ÿ
+®s*aûášFªp`æ/Ó]ï;qyn1þa6³¢ goE¹ÃñE
+àOsµç3µ)æèl¡¯`'ìU¶©;©+°{¬	-$EÇÄ¡dôÁ6
+®jËÛQà{§Ý
+Þ9lÕ'7éSÂÒ{ðçÈÌë6È,FÔ+»ÄÀ
+8ØJ9u^.ûÛÎ}8LjÕK'¡©
+ÆåbqHCÚÒœ
+ö@
+àÌÞ&G%ø`ò»`àV6Åœ|BÝ,eOÎ#Ät­mPØjB³2
+¥Á\ô
+c4L
+ä¬§À0kœüŠ|ÿŸÐzc.ÖÚÕ
+éüqÊŽv°žo]Ÿ^N_·Ör¿ÅFÎo¢¢
+Â ^¯ì
+*BÚÌý
+óŠîr{Ü2ªÄD÷ð¢»ÔîÉÞ*8µSÏD:GÐà3×5%µD7&ÖÔuÖ®|5¬MAÄaÞìžiÝKQDwdÒ
+èüs(ºB`E7H¹Žä1C&öÊ
+ó 
+§ySéOëáwÛ<Am ­I"iÓÊrýOz7Ÿòª"Ë
+yá{ù·Ûs
+Ç6:èÀéd8ä ýðk5
+Eõ%Šíf]ÿDí0?£Ó7)CÁ+³-hõ×œçw&7j^OÇ>š­ŽBÙÞ
+Úg¹/^à@Þ
+çö·bØU)CÕ+Û)Œ¶Ñ
+×
+ð§àp_žS&lâP82¹è: õI5G{c$Qåá
+øk-ep/Í@y­¿aøŠ2/!ù³r³~P
+íÁì(åJž,0FÃêca}/r-`öÅ«
+?üö-ü~pT52ñ}¹ 
+iÃÕ·?C¯d\]RèiOž0ôÒ
+àÝ±^¿~©74+a
+ÚÀRG:;d°Û
+RÄÞÚó¹þ;
+Ð×ÎI ðäq³œTÀš³BBHxð6¥J'T.èñßSÇúsA¿ì­1fØÊY¶ñä
+_ ÐC
+ÅÈT}Õá"÷žÂÀÜ*-ô
+|×@àCÒdŒÔ°1êOóvcêKøä
+ì`1I¿2FQ·¯
+DV€Z4Pc`Ç`ÛÖpÃÉÙëí8z
+Q³
+ 
+087ÖÕÉpÝZ(Á[d:ûkmÖXkŸzo>wíŸËZXÖMÝ6³[)Øk8/U%ÍV2HÅTìKä5âëé'[×ŒCB
+:ö
+
+çq_
+÷á(%1äd9ÐæÊï­ÚÌ`*ÖXïYa
+ýTŽSËo~
+I
+	.Jè·v»ËE
+«ŽöÀÎs!n€jT¥M[/©ÞÍìÆÃ±¹g{Žñï¡1
+vÝlÝ
+2ê,ÉþÄ¯Ù®
+B­\zð4}ßAŒ]€"úâ'ü®VB1¹·{pÀ&Sy³AíL¥d±œQ@TG--,t¶ee
+Nê°m%û,­ðlšô?L#	*A},f]ì0nŽc°ÕÚËD
+KF@œicçXÄzBÇ*uïtp¯jë¶¯iân(¯Â`'Û{ŽOšêC"³re ÖR0&c§Œ1
+vì
+»A!ÝwD±ûüBÖ¥Î
+eÎUhT!ôZ9Ä@`­ÉP¹_²#âs"8Ç'Hpî)|QäsoÜnc¬HMÖð	¬îõ	nêN#€<|¡;paŠÉ2ì
+6Íœ€ÐŒr
+xWo£ŸA`Ár¢°fÒTÆ$B.ÁÀ/O
+!á:Û ³/
+Ö6^lä¿
+BHÅêó%ÃËèáÞ+ØUiò[ìÚGXD
+AÖâb¬	Qz¢ßÆ5\OÞ$ç
+üC3P¹ íÌÐP¡ÄÝÑÓ'Õ¯Ù\y		ÃžwD+
+£ÌqùRÅ
+3^?@»p(ž{Ý2Ðòí×ÓêGKÜÞºŠW\
+DªeKÿ`ÄBõáê;æÝdI0€¬±&tahEå»;¡ g_6s,×VzÃ`Y¡(Šáþ,F¢€ºC^PÖôý"Û8nœngŠmâ÷ÁöïBûÐkZþŸñ/â 8LÚ±ýöÇð²ßp6@Â¿>!ž
+BÜµ¥cÒvÌsº·kWàxaú{ù%0Ú)S°
+Jt2àT°NDWä
+T§
+;sLíÕ!CøOÏoZ#~ð~GÐãQ ÂÏb/þÞKHObÂS/RMPxwÙc
+¯Âoá¯ð5K
+#à)Ã#ái6,<û ÀgÎÍváð:5<Þ80ßm-pÁÐðäÐÌÍ-
+
+J
+>Ä
+ŒVÁý­#ïG}%S¥Ö€0ÌŠL|(
+d%%ü¯_Ä7âSxt.üQ?gÂÃñ,Œ~oÄ¹qÀðixá(Ÿ/ÿÂ;á[ñ§x2Œ€j	ËøTäð¯'N|;¢ð@
+U z
+ú0	 ø#°<þbé(tÆ`ý,¢PÕr.Æ|ãuÅ/¶2>žQ©Ø
+aô|äm,FîW9kš%kâÐãÇ(¯Õ/pÜÌ$¡`¿H#SÁ*ø§ ªöC>=MßŽ¶éA¹åÛ~nÑ€ñUnf3»w>y
+\rÆÔŠn/­×}!e}pì
+!Á}TN-Jí²ZÚî:Š"^è,!²3qs?øÍâg&2<-
+ÿ
+Â³á%€ø!üIEö
+F`wbàÄµ
+Q¹ÜñQ<Ç+0\²uQÁm{P:£çÔßî6ÿ
+Zªîø,ØÐ
+X5:¥ŒËl[ò	Kr&fhDjéw-öRòhµº€Ëèiçú0B»
+\QÓNr>qSQGt­àît"ab©ï
+¯àÎ]Eò<
+Ê¯É£ÙÞ
+ÕMsá4œš{­µÖñ*³®ªo|@Ô©
+ÐRå0LC¡X¡.}qA«1·Õ©'
+€kò!6"šnr[ôÖ<µ[¢)ý7FràQM#êü#.[§ŒÓÌÔìóG¬gíá
+:-
+úÔ|Œ5	Uäà\¶%Ëá})}Åy e(®=c£ú!q«ÕDY0~1 r3lx¿
+
+Mw|¹1^¥ž §GÈüÇî vuÜiŠäfmãhZ¶öáÅŠsŸOBõ¶fN¯O5õëgõç(Ù.g6
+?ïjAÑf4Ý
+N,c@¯LÌ¬Õ?kŸ"2ðl2
+kbò¿.ð­4ÍþKuji
+Ôÿ%ûù1\§ŠÇÌ
+xo,è
+æsN3~pL/\Þú¶9Ÿ¿
+ñ·o!rA%°l@¹9úèDWý{@
+6µEè YdÆsnÈvx€š,îÔØ7ª¿Œ28ŠÜg±CiÛçµ°v
+ð}Úß7Ð¥ÓÅ
+ZcÚ®ì
+J$7ß*Ð|B¯0:ÇU²lH8
+>
+ôàïŠøéŸ¢!òa	0`}ÒàW"ìèq\¡
+FñÒxk¥id=Wºr±ìB?ëR¢*Ä?%éS7ÝLO0SzŸŸyÀ»ÈVüÚè¯9ÿxq?ÌZÝÆÖæ±ñš±8Ç¶µ5@ó<Ïå«kè_pÝÚmŠå(	+ßÃ§Ib|ŽêßŒx1Æg¯átæÂ­dÝ<!çJG5Ö(¹2ýàðèÞ¯6ã.oþTó¯IdµŸ3KüÛ0·$Þ'uT`Æ\¬pgÖ%ØYfºÖËþ²h¬?œHNv\5W8š]÷(ÆÎ{
+ø]F}Š×Èéð8Ž¹šŠÄ|$)Q0ØŠWÂ¶Ô­CYTb¡œŒt?7[©xÂ`Å8ð°?ÉÊ£{ÐòêÛcœJg?f%'8c6E7Æò¬«JâÁpêŠ z
+èíÃ1aÎáÉÒ 8:ÓÐE²Lö)ys×ŸÞd(ù u«¿5(t¥|Mõ_ÉXlV¢jÖªT4¢$ûqÈ<ÇEËÑ×ž5Fÿá÷â ²ß?I4¶HÆiP¢tSéÆŽCò€ÐC$ÂG^Ñ[haxÚ?0ê
+®\HmÝi€G_ò£0ì³[
+6l>áE6
+é¢;Ð-8Nêb§¬²L
+©àòT-0ÿ0þ±Qlßl&k¢õ-Áó¢ì
+ŽoýE]ÏTÈºž°ôç_yÙC€0LÓöÛŽÀÆ<Àµ6dxjWŽDwË*I^  Ž­€å[¹dÿ
+3Úä°sJök^éD N€všÐûk
+^40×ª ÞvŽ9hÁÓ'³oK
+Vß
+^ N]¿D¯Òo4IÒ²šš
+C³#Ô³žËœ®íÿègŒf£F:îºx®úì®yî
+~þÕ
+
+GÆ`9ž>Š_=º?¡Žl?7Ïw/Ð35œVMí:á«å²[''ìÁf¢vÑý'5«+ä8À¯ILÃ§C[
+«|ö¢TÈüÞ)šó
+&d90iþ.·#øÏ\(Tjÿ}¡%ô*Ð&z¯Rr»~è	cÖ,è?Ú×ç&ïõžø
+KB{;ä»@ü
+öŸYðDK-Œ>
+6Ý
+x2ÖeªZý2FWÌ9#äwÕwane"]ŸÅ~Yó«
+ñ))"Ãï²æ-­©ÖPõA€<þŽn.­ã|ÚÑÀJQ°[¿gÔku	@9§Ù
+ªçòPc©ã­y0
+ü î~ñq³ß:>ùær'®õ
+,å%%Š$ÄRK£¢œuqºGnLêt%¿
++U×DkNÏëðI:ÖÙÉ¿à'ùå5£÷öYXŽL8åhcÙáÙF¿	8
+Œ°eãoyúéã×QÓ­V2hê±Ÿ!,,Kp2{ZÐá\ð2»~Ltàk×š]±A³@Jô9úÁ9ô°µ
+T­
+yh­rZðÇ	Ú~oÏ%­
+è€^6,.AÿÝŸd.:÷ÛÜþKO9^--üÃ¬×{Ý3ú
+82sÇW-
+µ
+\üÿ×ò
+IdþYUÑc
+çàÏý'=IÐÆýŽZY
+¿DÃw^]]ø*j%ÍäaœôÜðõl%dôÎŠ$E×ÄåÃ`H©æI8×À]CAs°ímÐØš*Éš ÑæŒ{Ñûný^Z©óºo©m
+œúqTdØ#Ì­·kh¡gŠ<Ñ/
+¢o:	÷u,e{Ît}l«^ÿÕ»L€GåqPáRÚé`äzÝ¡î.
+
+q±ÊrSG²ÕÊ+Zð0EEœ:/psRÝŽì
+!ÏQ@ßËü{Úž
+_.Wg±Ïc4ù
+¯Qv€
+Ý6Ù ekbýÃ{ÞÑŸ¥²þáª®TóA^D{£·FOŽ-åV[ô0%¹>Š<ËénÕý[ÿ\WoíòLOhD?·­K?mÀ:ëÇ5JÓvYdØø:í6OVöùoÍ@l5AÄlär®@îs©¹	ÃOÇ^ÓŠ
+`ùþBðï·'PÃlžß.ãt	öðHíf
+Íí6¹Ã:OµŠøãÒaÈÒ0
+°fq#A_ŽÐ§7(ôé÷Põ 9òà,¥èÇä'QIÁïg¿
+
+n<OÕëä}
+
+ôî
+N£3Áü!Í*E.(&­}«Ž¢É®HV
+ššîgXDúåß:°(%úàe
+ŸXSüÌèº.Ûy¶š
+TsÜÉ ÈÀô@NµüÜ6ŸŸÞ<É9Gœ*PíÛ
+bå¿ð ï °°
+*Àl/
+1¡#Ð rÖ2äS'p¢àÛÅñó?ÿ`ã}æ	¹Iª)ÊÛ,°Ÿ
+F3Ÿ!_tÔùæru©Ÿ|3Sà(1çÇ']áú7±ÌçÈ
+s
+À:@bØº¯¿Ke1Ý(p¯|m¡ïRÈŠ»¶÷/Â/š 
+b±ð C5û($»Í_Oá>Ô°k$U:çN_ ürÙ®ëX4¯ö^õªF^
+vÞ
+e×ÙÐè¹Ð ñúJÌ×f
+²ØQ|·cV¬Ë¿"øy'é"?yH`€1Y
+*P(÷nãy@ìÙðy/²V	Ò©ÇÀKqi=¢Aã?FZ#
+:rm;]$+=LX¹ÀM²Í÷C6\÷óíÐ{·)I£UK{á|_ _Dºí*£®VÏiD7aŠA¡ÀÜ6l<»8`
+)ŠÆyPŒ,Ô)JÏ;ÞWC@éPíVùÔ &ã§;eù²Uü,:h1=g
+ÌQ$|i\(âÓîâøwÑ$
+ºÌY=±b&4[¡N]Åè¥rR®\
+ºí£_áqyüÆéÑ¥ÚAš°ÙD=áä\8W÷`¯üÃœlò
+`b$n%ÿ
+ÛÉ_!p±Ü÷ñ-ÿžÈ¿ü°ÂkëÁâ§Ç
+:%+Cïbx4±ªó>"D÷sh'Ý/þúÕebBRÚ*·ÙYÝ+ýî[È³ÞLÕT;®zÄ
+@ç¿ÝÐ]
+ÀšÛý	MÇßí1Á.3hÿ<RÇô-»×Užhì
+œet}è9Yìk
+=ðÆgFáÏOÎ¹ Í;Ã?(þu'=ÄÆúÁú^%«?_¯wþý÷I(v€ü
+ú»ÑHj·îWÈœüIª
+õhtÑ`n	Vµ
+*ÍÔ«ðÌŸÞp×eAR¬'ßSê.ZiT|cè*ø}Kh¬~lûjõ
+;{<Z5=%fO/ÌÏb&|Z
+}¡Å |ÎXäQÓE"&
+fy[Çxõe B3YbKõi	F¶$~|dGÇzÐòAt÷#Å.Í
+öe}y­
+
+/F1DBÚu~~ß
+~0[|c~Iq8å"UI]×ÁQü^Ygè%|Û^èPUjo·	ùZñOô.
+×?W Å
+EÁgiB!Í€~ÌDÛ	rÈ|PjR
+5Ñ":!yn
+`<e8¯9ql-€È!6
+ZQ6v1fXÀAl×c9>=9ã»]gnn
+á  y61s^+S{e!Îa°8WÖFMWÅ}3ì?¡è.\{fe®^Ñ8ýýW	?Î0Û
+é'
+aqß~WÓ	¥]bXE(4]·4"Ž??Ú
+_€Tcn45I*Ÿ<÷;â9;tÚÛyÀ`Xô%?U<bXŽVãz	"ÏiŸ`	uZLdÀo:$p
+Ù:>
+e${jž
+é
+<~
+VXä*mð@P¿+Mìi&#}oWfÅZ³tO`
+
+±a¢j²X
+`I)
+;{
+åi^\&­=0nz
+žeL
+~$662YgeáVN2^=ÄTyzÿ&
+W
+¬aqu©_VJid3h;
+,E
+óxskò*P\!Yï3Sa4=2h\&ä
+]M
+*$Z
+C}M/è%¹@~ºoÝÌ2ïy[+£.B
+r6yÏY4y72
+#
+	}l(I¥pGS6hÅz)X?
+
+ç5³þ
+,t°:gY7÷Aø;|*Â]Js
+4,oÐñ"0z1ß
+v
+=²hiKø
+
+_wö{H!r<2¢@Ñ;4s>ë
+zp>±nb;|gnyÀ^xc]ºpÕOktœ/âoÑ€^ÆAœc³¯WFJ~~
+GB
+E
+âx!n#
+t|t@C0»8Ä]$?ëC>9:`ocEó
+4`]Z
+©ô0ema0!!p³æ3tç.Ñ,6Ypdo{\ÂódÆ,cñ<$]1M
+ì¹;!VW
+#Ss×xj\µvï+xv<N%"i>~ÎAÏ9'I;Mâf\U%z_ëm!|ÿvÕ[Å&EøËg"D[/·gT*DîÁkTn¥kD{31øeÃ>­dn ð,UK4Ýx
+Ñ	í\Q4&5K9s~L
+@MñdÜ+yÓ"4V|f9ä@9^y|#mWç>(4`
+%g3v,Ïv#øYe8ë[9Û6e,VÒ
+>M}|¯)«SÎ;Ð;å@;éC<{úxsZ93yFj°(cc|¡d,d
+
+uálŠš5®Wœ{O¥|²|H{!.ha{10uXñpësi%fä=N8~>zU[!íh¿-
+SUe{zÎ=îði
+$y{gièPÅy?VMQß
+ÜiŒ?
+XßjX]>i	Q.JÖIè]1	
+kÃTIs÷åfMuTk¯&¹rBbT"ÑbÖ!?ë9Švœí } ÷x·ä
+`
+:q+XÀRvGN9ó{*dvÌ|d}k4]ªZå8${ï0-qzŽBv
+F|o«Q`ÝÈwfú]A|g
+ëf~	O{
+UA%erÉKe
+Ü?
+i7Xö8&A]¢áB DvZ*
+â!sÍ$M7H?|ÿ{
+E_ÕL:#	_ötŠgVÿ$í"	M9ãcý2!)	Ñ[ŽcPF¶m
+="%uY3
+|wU
+Ç-@Tºb?`I!'KQ{8Ý*Ú=©
+û
+dE7b{€|zvÒOØ"@A
+ŠPï3ìkš}ö,ä ºt4êzh|ìQèkPQzñ#z
+i'
+ý
+Øb)
+ÈPI70;3
+yHA»/a(gÈÚ*ì
+»í
+@ÿI3P#Nõ~V/E:ÿòNþI±	ª
+c
+
+G>/\ÒF;\à"(kÀGqŒL€te	ýUÄD^9b~{9Mâ<=#
+
+ãPG_^W@TE^û<¥~·D&büyb=èAVhÜÄmëdC-WÏ,b>Z!q9E·  \\÷ojUÏÄKK{@ð+Å}ªuÞ{L
++rù
+Cs
+^J¿vt']ÅBÁ!ñ{œŒ||-FRjú~
+
+#i»Oµ.7 ¬z€U
+B|Lu$
+4/TGËU;ö?ºgU
+Xù
+ò	òS[
+Y
+Ao
+QBý\w^í@ÅR}Ušù{ÊuPFùrz ðu)5.-
+T
+{
+Ïiöd:æí
+ñÔf3N}uæ&
+Vâ"RÈzdp,.F!<"k:>À|géQÝG¢\ö	þ*Å7Û~þC}u+eL=<cµ
+å"F#PI8mi`¢E?&]U
+{DR]ên7^5:
+"ÉoÈbvZP P.âQ+Ô/ï\lŒ=G±A@TœtZ(zmáîB
+{z]~kULýg	6pg1ì0ð}²{ï×FÀRÎ
+²^#Êr¹mŸÔ	|ž%õ65`ÜdIS×? ð8
+K-rh¿euPz(­&
+à3 JY&_^F^@|
+ biy€ËLÕµGjsÿ3c/Wgt|0@
+ÐU;(jè%§78GnmúÅO#
+Þ#")Ó|ÜeR~¯
+g'è~
+ Š^ÌFmoãd©
+û=@p
+ÉÌ\3+!;êåtœE·?5ZjÔÈo÷[éŠ&óDü;àœxt;C¡T-&";Q^ÆøõÚ:Çú<j¡}uP`ÀŠO-^	smœVêhj%nÞ
+
+lz
+²XctöúzÔ	Üô|
+)nðzz,ê/IFYG fü]AQWPè
+xw3è%ÂhËÛ9Q
+
+r°ó	rÿ'
+§ôj
+ûEC[Èzaí[x¡±Ë{4D$t':tÅ*Õ{eÖ	¬z#Â%f%Ü{5õTKâ^ßB	úRŸ&7%5S\J@ IB0
+_Šk¶m)Gkm
+I
+Ÿa&njg1JM
+4 /Ï
+>²yYJ*^
+3("'a»2
+>ã,Ÿ+Gpsã,ÇB_3Ÿ&éLç&r<	
+I¿q3fŽVLÇBÙLE
+7²õW
+\&±øHû01kÿX¹-Â%1'If@²ÞUAT[ÐK!ŠH»{¹{ãÐO8G>/@-w4U_A
+'\
+ôbLÖ
+
+SüOñe
+±ñs){
+PtšSî3	¶oðzÐF^9OÒM
+hv]Wê
+46_Y t"ÏFÃdS
+3['YCò)qØy©&
+Ð&ü!!DÏZ;
+±oDymÂl» }oöy±:>0zh]Û.TÆ`VNK?ðjxlnÈáWÅ'ör
+@m
+WcGá:€DamY °:ÞA,q@qWIø;'/l¶OŠPn
+õ\±7'XWñoPzq/¹$gk³|RThs®©GÊ°mA"o¹m¢K9|I\
+C{ù ôSùkòß»gÆ@7ËZR«I1OÖ×é|ÔZyrwfètv!ø~0
+³@Ù{z
+iTŒb
+?°&§)1hhÈpA\Mm*
+¬yUfùwgB
+
+ðr@%]bÞå/6U8
+
+.-^i7ÆzJ-&PéaVJ
+K9
+ic*ÛNß"d!í8P:PBÓvàGS-M
+"b
+¥zU@ö 8
+â\¬`E
+hyF]iy~ÞSH`k%×R^
+Bîg2T
+ã ËdÝ8×
+qmýD?7ÙN	Ö	\ey"!j
+5N9>óKb~zRmlRë4"8
+
+Ä+WK'ú9-åQª
+)Ev
+Ï|%tà+Z³PŒu>ðd€@9zäd#Ë0OáZ¬"q
+{î6f~æ{9TÔ~M/P;ÿ"v)PŽVF#Ò
+;-:
+pv>ÿ1zìuVöV·
+úSE
+W~ y07êg}-äyu£q
+Zoh{/k4sA 	ÿgE÷/Eµï]iBF-#S/i|Š\(¥6W79°Psµ^?{°8TÂ`Ö8$ìYo
+
+ŒÁV%~4>I%
+Ô	NB6ÄR1**æ}î(QKn¥|¯]{v$õ	^do%ÆIÝ&ÛÞ.|-PŒ8€abUf:X7Ø9
+Ès94;2`Uÿ
+Wª 
+Â9Ð	x.|/iR»?D5@
+ùn=#
+MÕ93^0QG2I|ü¬teVÁ/ÀTÉÞfA`?T+DÖOØG?\'
+8mIs^©:=ÀUi"Ò'·¬@¡j«uMc2o1-¡J9S/Íz³;
+
+%r]Þ'GA(×RÔ	â3®"{qnÎŽ
+z
+o(Ò!¢
++6c&/:Ë]Õ3 t=lLW
+©gÆm]­Ä^J(y'HTjP@šÀG{WtbµAvF\@o\â^ÉE¬Š,qc:Ä9÷&hNdyc í\-ÛDÔdÞ$ÞYDÜp}ò%O?BL.m
+Yd§zÍ0<?ê)c_-v #@J»MÑo
+,
+Š]E5eYº
+j&5 »!Ì>qrŽ5
+yíbÎ2?ü5Ñ'>!QJm9+`?à=@jVô4¡yK\Ž|'
+iU
+
+HE³
+,Ê³.Ip6jpLë1
+
+]V­ ù
+©_LR«?>tûxQp<«ÅUýzxKÊÏ
+Ô9Ò1íA_7ñtvzø
+šd<ç&û)t
+a#3f3®*ñ'J*	[6]Ÿ9
+R5aXK
+a×
+®SKhú
+$
+ÙLäs°_õ8>)€{ìc+Hu-){4gAVhbJÅ4WY^-r%Ip0
+/YÏ?[Ác__OpalQ6
+@ÃæIXb^ôfxd1Ã>/ÁZoÊ&/jP#49lI
+Q(Eû	uÒw9/k
+uMùjC
+HY¹
+v Î}OdJCÔE?\K
+Ë{s[Àkh]·Ät	C3"§n &Î
+¶,[ÀÓhz$qqy~ÀHT+è)e-X5€F
+²YSl0z²
+U.mx
+Ì2,7¢œëHD¢dóö_9Q
+90$ý[sÔvÜ-/ÊQ%M	ýabl­;ñ'(c­;A cyW4Dz__L¿5hC%uÄ+
+ØGÛfK
+â%Ì
+ë6°Çpb÷=53Üö^enõ*dä
+û~Y14<K¬.{, 	õ{ôH#¢=èr;° (¢SÏ;d\wŽ^{
+Å
+áf
+'$­Nvi=kTW$T5n8^=)Ý[ç=}Ìò:Ìg$?Z,ÁHIJFt*Uë$ZGEÜŽ/©Mdp\lÈz¯°0lÎK{`~Hœjç@ñat"
+	H\Ÿ
+
+È
+¿
+Rp
+K!f)!¢HŽ>ù	å:°ö~üp5qàkC+Ž»!dXïX  #s§.
+9
+9-
+fwóXíyWIt\	I/ýrúcÚKù3:)#	n0.°+V2://ç6nª	0¢Æ
+>jü
+Ín<
+u
+L@}ª$
+.oa_~ö&1pÛKz *~iÙNÐrø.ñ/\,
+fZüü?æaI1DÇw,FA 0lÉÅ
+x{œ%Dül1!KYX
+ ŒXd
+¢
+D}\$	OKa:.eNÆ]šaØ
+
+?cßxhrVv!
+Ëxõ85l7lêÂ2RÒ$;ueÅ#€O5Wt
+#Y^4
+9í
+q¹nbNÓO.ySj0!Ï
+ÈSBY5Œ5×	Tf¬k
+ë5 *>TFB#(«QÏ·	I,at:è-û
+
+l<]«
+þ9IL#!)î
+P-1©3%¢O4weúJI¥
+¹¥»T-
+
+¯Yº/rŽ28î
+ÒTÈl­/kX ?+;%))Ü}UIN	ø
+wHüBþ
+Ÿ"
+È-òý"š'¹
+ /\
+z
Index: /gsdl/trunk/gs2common/indexers/mg/about_mg.html
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/about_mg.html	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/about_mg.html	(revision 16561)
@@ -0,0 +1,75 @@
+<HTML>
+<HEAD>
+<TITLE>About MG</TITLE>
+</HEAD>
+<BODY>
+<H1>About MG</H1>
+ 
+<HR SIZE=4>
+
+MG is a public domain
+indexing and retrieval system for text, images, and textual images.
+MG is covered by a 
+<a href="http://deimos.kbs.citri.edu.au/mg/intro/copying.html">GNU 
+public licence</a>.
+<P>
+MG is available for ftp <a href="ftp://munnari.oz.au/pub/mg">here</a>
+
+<H2>Developers</H2>
+
+The MG system is an embodiment of ideas developed primarily by:
+<ul>
+<li> <a href="http://www.cosc.canterbury.ac.nz/~tim">Tim C. Bell</a>,
+	University of Canterbury;
+<li> <a href="http://www.cs.mu.oz.au/~alistair">Alistair Moffat</a>,
+	University of Melbourne;
+<li> <a href="http://www.cs.waikato.ac.nz/~ihw">Ian Witten</a>,
+	University of Waikato; and
+<li> <a href="http://www.cs.rmit.edu.au/~jz">Justin Zobel</a>,
+	RMIT.
+</ul>
+
+<H2>Programmers</H2>
+
+The bulk of the programming work has been undertaken by:
+<ul>
+<li><A HREF="http://www.cs.waikato.ac.nz/~singlis/">Stuart Inglis</A>,
+       University of Waikato;
+<li><A HREF="http://www.cs.waikato.ac.nz/~cgn/">Craig Nevill-Manning</A>,
+       University of Waikato;
+<li> Neil Sharman, University of Melbourne; and
+<li> Tim Shimmin, Collaborative Information Technology Research Institute (CITRI).
+</ul>
+A full list of contributors appears
+<A HREF="http://deimos.kbs.citri.edu.au/mg/intro/mgintro.html#xtocid2063010">
+here</a>.
+
+<H2>Institutional Support</H2>
+
+The MG project has been supported by the following organisations:
+<ul>
+<li>	The Australian Research Council;
+<li>	<a HREF="http://www.citri.edu.au/">CITRI</a>, Melbourne;
+<li>	The <a HREF="http://www.cpsc.ucalgary.ca/">University
+		of Calgary</a>, Canada;
+<li>	The <a HREF="http://www.cosc.canterbury.ac.nz/">University
+		of Canterbury</a>, New Zealand;
+<li>	The <a HREF="http://www.cs.mu.oz.au/">University
+		of Melbourne</a>, Australia;
+<li>	<a HREF="http://www.cs.rmit.edu.au/">RMIT</a>, Australia; and
+<li>	The <a HREF="http://www.cs.waikato.ac.nz/">University
+		of Waikato</a>, New Zealand.
+</ul>
+
+<H2>Backpointer</H2>
+
+If you didn't just come from there, the MG home page is accessible
+<a href="http://www.kbs.citri.edu.au/mg/">here</a>.
+
+<HR SIZE=4>
+<ADDRESS>alistair@cs.mu.oz.au</ADDRESS><BR>
+ 
+</BODY>
+</HTML>
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/acconfig.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/acconfig.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/acconfig.h	(revision 16561)
@@ -0,0 +1,35 @@
+/* Special definitions for mg, processed by autoheader.
+   Created by [TS:Aug/95] 
+   by grepping for AC_DEFINE on configure.in and using the appropriate defs 
+   of acconfig.h used by GNU tar.
+*/
+
+/* Define to the name of the distribution.  */
+#undef PACKAGE
+
+/* Define to the version of the distribution.  */
+#undef VERSION
+
+/* Define if `union wait' is the type of the first arg to wait functions.  */
+#undef HAVE_UNION_WAIT
+
+/* Define to 1 if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#undef PROTOTYPES
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+#undef WITH_DMALLOC
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+#undef WITH_REGEX
+
+/* Some braindead header files do not have the requ'd function declarations */
+#undef HAVE_FGETC_DECL
+#undef HAVE_FREAD_DECL
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+#undef HAVE_PR_BRKSIZE
Index: /gsdl/trunk/gs2common/indexers/mg/aclocal.m4
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/aclocal.m4	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/aclocal.m4	(revision 16561)
@@ -0,0 +1,140 @@
+# Local additions to Autoconf macros.
+# Copyright (C) 1992, 1994, 1995 Free Software Foundation, Inc.
+# François Pinard <pinard@iro.umontreal.ca>, 1992.
+
+# [TS:Aug/95] - chopped out the NLS stuff which I don't support
+
+## ------------------------------- ##
+## Check for function prototypes.  ##
+## ------------------------------- ##
+
+AC_DEFUN(fp_C_PROTOTYPES,
+[AC_REQUIRE([fp_PROG_CC_STDC])
+AC_MSG_CHECKING([for function prototypes])
+if test "$ac_cv_prog_cc_stdc" != no; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(PROTOTYPES)
+  U= ANSI2KNR=
+else
+  AC_MSG_RESULT(no)
+  U=_ ANSI2KNR=ansi2knr
+fi
+AC_SUBST(U)dnl
+AC_SUBST(ANSI2KNR)dnl
+])
+
+## ----------------------------------------- ##
+## ANSIfy the C compiler whenever possible.  ##
+## ----------------------------------------- ##
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so.  This macro tries various
+# options that select ANSI C on some system or another.  It considers the
+# compiler to be in ANSI C mode if it defines @code{__STDC__} to 1 and
+# handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{ac_cv_prog_cc_stdc} is set to @samp{no}.  If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+AC_DEFUN(fp_PROG_CC_STDC,
+[AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C)
+AC_CACHE_VAL(ac_cv_prog_cc_stdc,
+[ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX			-Aa -D_HPUX_SOURCE
+# SVR4			-Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xc
+do
+  CFLAGS="$ac_save_CFLAGS $ac_arg"
+  AC_TRY_COMPILE(
+[#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif	
+], [int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};],
+[ac_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CFLAGS="$ac_save_CFLAGS"
+])
+AC_MSG_RESULT($ac_cv_prog_cc_stdc)
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+])
+
+## --------------------------------------------------------- ##
+## Use AC_PROG_INSTALL, supplementing it with INSTALL_SCRIPT ##
+## substitution.					     ##
+## --------------------------------------------------------- ##
+
+AC_DEFUN(fp_PROG_INSTALL,
+[AC_PROG_INSTALL
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL} -m 755'
+AC_SUBST(INSTALL_SCRIPT)dnl
+])
+
+## ----------------------------------- ##
+## Check if --with-dmalloc was given.  ##
+## ----------------------------------- ##
+
+# I just checked, and GNU rx seems to work fine with a slightly
+# modified GNU m4.  So, I put out the test below in my aclocal.m4,
+# and will try to use it in my things.  The idea is to distribute
+# rx.[hc] and regex.[hc] together, for a while.  The WITH_REGEX symbol
+# (which should also be documented in acconfig.h) is used to decide
+# which of regex.h or rx.h should be included in the application.
+#
+# If `./configure --with-regex' is given, the package will use
+# the older regex.  Else, a check is made to see if rx is already
+# installed, as with newer Linux'es.  If not found, the package will
+# use the rx from the distribution.  If found, the package will use
+# the system's rx which, on Linux at least, will result in a smaller
+# executable file.
+
+AC_DEFUN(fp_WITH_DMALLOC,
+[AC_MSG_CHECKING(if malloc debugging is wanted)
+AC_ARG_WITH(dmalloc,
+[  --with-dmalloc          use dmalloc, as in
+                          ftp://ftp.letters.com/src/dmalloc/dmalloc.tar.gz],
+[if test "$withval" = yes; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(WITH_DMALLOC)
+  LIBS="$LIBS -ldmalloc"
+  LDFLAGS="$LDFLAGS -g"
+else
+  AC_MSG_RESULT(no)
+fi], [AC_MSG_RESULT(no)])
+])
+
+## --------------------------------- ##
+## Check if --with-regex was given.  ##
+## --------------------------------- ##
+
+AC_DEFUN(fp_WITH_REGEX,
+[AC_MSG_CHECKING(which of rx or regex is wanted)
+AC_ARG_WITH(regex,
+[  --with-regex            use older regex in lieu of GNU rx for matching],
+[if test "$withval" = yes; then
+  ac_with_regex=1
+  AC_MSG_RESULT(regex)
+  AC_DEFINE(WITH_REGEX)
+  AC_LIBOBJ([regex])
+fi])
+if test -z "$ac_with_regex"; then
+  AC_MSG_RESULT(rx)
+  AC_CHECK_FUNC(re_rx_search, , [AC_LIBOBJ([rx])])
+fi
+])
Index: /gsdl/trunk/gs2common/indexers/mg/build_options.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/build_options.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/build_options.in	(revision 16561)
@@ -0,0 +1,2 @@
+set stem_method = 3
+set do_indexes = 1
Index: /gsdl/trunk/gs2common/indexers/mg/config.guess
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/config.guess	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/config.guess	(revision 16561)
@@ -0,0 +1,1298 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-07-12'
+
+# This file 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int dummy(){}" > $dummy.c ;
+	for c in cc gcc c89 ; do
+	  ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+	  if test $? = 0 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	rm -f $dummy.c $dummy.o $dummy.rel ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# Netbsd (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	# Determine the machine/vendor (is the vendor relevant).
+	case "${UNAME_MACHINE}" in
+	    amiga) machine=m68k-unknown ;;
+	    arm32) machine=arm-unknown ;;
+	    atari*) machine=m68k-atari ;;
+	    sun3*) machine=m68k-sun ;;
+	    mac68k) machine=m68k-apple ;;
+	    macppc) machine=powerpc-apple ;;
+	    hp3[0-9][05]) machine=m68k-hp ;;
+	    ibmrt|romp-ibm) machine=romp-ibm ;;
+	    *) machine=${UNAME_MACHINE}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE}" in
+	    i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	if test $UNAME_RELEASE = "V4.0"; then
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+	fi
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	cat <<EOF >$dummy.s
+	.data
+\$Lformat:
+	.byte 37,100,45,37,120,10,0	# "%d-%x\n"
+
+	.text
+	.globl main
+	.align 4
+	.ent main
+main:
+	.frame \$30,16,\$26,0
+	ldgp \$29,0(\$27)
+	.prologue 1
+	.long 0x47e03d80 # implver \$0
+	lda \$2,-1
+	.long 0x47e20c21 # amask \$2,\$1
+	lda \$16,\$Lformat
+	mov \$0,\$17
+	not \$1,\$18
+	jsr \$26,printf
+	ldgp \$29,0(\$26)
+	mov 0,\$16
+	jsr \$26,exit
+	.end main
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+	if test "$?" = 0 ; then
+		case `./$dummy` in
+			0-0)
+				UNAME_MACHINE="alpha"
+				;;
+			1-0)
+				UNAME_MACHINE="alphaev5"
+				;;
+			1-1)
+				UNAME_MACHINE="alphaev56"
+				;;
+			1-101)
+				UNAME_MACHINE="alphapca56"
+				;;
+			2-303)
+				UNAME_MACHINE="alphaev6"
+				;;
+			2-307)
+				UNAME_MACHINE="alphaev67"
+				;;
+		esac
+	fi
+	rm -f $dummy.s $dummy
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    arc64:OpenBSD:*:*)
+	echo mips64el-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    arc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hkmips:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    pmax:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy \
+	  && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		eval $set_cc_for_build
+		$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+		rm -f $dummy.c $dummy
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+              case "${HPUX_REV}" in
+                11.[0-9][0-9])
+                  if [ -x /usr/bin/getconf ]; then
+                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                        esac ;;
+                    esac
+                  fi ;;
+              esac
+              if [ "${HP_ARCH}" = "" ]; then
+              sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+	eval $set_cc_for_build
+	(CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+	if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+	rm -f $dummy.c $dummy
+	fi ;;
+	esac
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    hppa*:OpenBSD:*:*)
+	echo hppa-unknown-openbsd
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+	echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3D:*:*:*)
+	echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY-2:*:*:*)
+	echo cray2-cray-unicos
+        exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i386-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+	  big)    echo mips-unknown-linux-gnu && exit 0 ;;
+	  little) echo mipsel-unknown-linux-gnu && exit 0 ;;
+	esac
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev67 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	ld_supported_targets=`cd /; ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;		
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-pc-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-pc-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    M68*:*:R3V[567]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                           # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	echo `uname -p`-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	if test "${UNAME_MACHINE}" = "x86pc"; then
+		UNAME_MACHINE=pc
+	fi
+	echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-[KW]:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+eval $set_cc_for_build
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/mg/config.h.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/config.h.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/config.h.in	(revision 16561)
@@ -0,0 +1,177 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define if using alloca.c.  */
+#undef C_ALLOCA
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have alloca, as a function or macro.  */
+#undef HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+#undef HAVE_ALLOCA_H
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define if on MINIX.  */
+#undef _MINIX
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+#undef off_t
+
+/* Define if the system does not provide POSIX.1 features except
+   with this defined.  */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work.  */
+#undef _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void).  */
+#undef RETSIGTYPE
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your <sys/time.h> declares struct tm.  */
+#undef TM_IN_SYS_TIME
+
+/* Define to the name of the distribution.  */
+#undef PACKAGE
+
+/* Define to the version of the distribution.  */
+#undef VERSION
+
+/* Define to 1 if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#undef PROTOTYPES
+
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+#undef WITH_DMALLOC
+
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+#undef WITH_REGEX
+
+/* Some braindead header files do not have the requ'd function declarations */
+#undef HAVE_FGETC_DECL
+#undef HAVE_FREAD_DECL
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+#undef HAVE_PR_BRKSIZE
+
+/* Define if you have the ftime function.  */
+#undef HAVE_FTIME
+
+/* Define if you have the ftruncate function.  */
+#undef HAVE_FTRUNCATE
+
+/* Define if you have the getpagesize function.  */
+#undef HAVE_GETPAGESIZE
+
+/* Define if you have the getrusage function.  */
+#undef HAVE_GETRUSAGE
+
+/* Define if you have the mallinfo function.  */
+#undef HAVE_MALLINFO
+
+/* Define if you have the select function.  */
+#undef HAVE_SELECT
+
+/* Define if you have the setbuffer function.  */
+#undef HAVE_SETBUFFER
+
+/* Define if you have the strcasecmp function.  */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strerror function.  */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strftime function.  */
+#undef HAVE_STRFTIME
+
+/* Define if you have the strstr function.  */
+#undef HAVE_STRSTR
+
+/* Define if you have the strtol function.  */
+#undef HAVE_STRTOL
+
+/* Define if you have the times function.  */
+#undef HAVE_TIMES
+
+/* Define if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define if you have the <dirent.h> header file.  */
+#undef HAVE_DIRENT_H
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <limits.h> header file.  */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <memory.h> header file.  */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <ndir.h> header file.  */
+#undef HAVE_NDIR_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/dir.h> header file.  */
+#undef HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file.  */
+#undef HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/procfs.h> header file.  */
+#undef HAVE_SYS_PROCFS_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the m library (-lm).  */
+#undef HAVE_LIBM
+
+/* Define if big endian */
+#undef WORDS_BIGENDIAN
Index: /gsdl/trunk/gs2common/indexers/mg/config.sub
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/config.sub	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/config.sub	(revision 16561)
@@ -0,0 +1,1375 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-06-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \
+	        | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \
+		| pyramid | mn10200 | mn10300 | tron | a29k \
+		| 580 | i960 | h8300 \
+		| x86 | ppcbe | mipsbe | mipsle | shbe | shle \
+		| hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+		| hppa64 \
+		| alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+		| alphaev6[78] \
+		| we32k | ns16k | clipper | i370 | sh | sh[34] \
+		| powerpc | powerpcle \
+		| 1750a | dsp16xx | pdp10 | pdp11 \
+		| mips16 | mips64 | mipsel | mips64el \
+		| mips64orion | mips64orionel | mipstx39 | mipstx39el \
+		| mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+		| mips64vr5000 | mips64vr5000el | mcore | s390 | s390x \
+		| sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
+		| v850 | c4x \
+		| thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
+		| pj | pjl | h8500 | z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	# FIXME: clean up the formatting here.
+	vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+	      | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \
+	      | arm-*  | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \
+	      | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+	      | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+	      | xmp-* | ymp-* \
+	      | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \
+	      | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+	      | hppa2.0n-* | hppa64-* \
+	      | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+	      | alphaev6[78]-* \
+	      | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+	      | clipper-* | orion-* \
+	      | sparclite-* | pdp10-* | pdp11-* | sh-* | sh[34]-* | sh[34]eb-* \
+	      | powerpc-* | powerpcle-* | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \
+	      | mips16-* | mips64-* | mipsel-* \
+	      | mips64el-* | mips64orion-* | mips64orionel-* \
+	      | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+	      | mipstx39-* | mipstx39el-* | mcore-* \
+	      | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
+	      | [cjt]90-* \
+	      | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+	      | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \
+	      | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	cray2)
+		basic_machine=cray2-cray
+		os=-unicos
+		;;
+	[cjt]90)
+		basic_machine=${basic_machine}-cray
+		os=-unicos
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mipsel*-linux*)
+		basic_machine=mipsel-unknown
+		os=-linux-gnu
+		;;
+	mips*-linux*)
+		basic_machine=mips-unknown
+		os=-linux-gnu
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	mmix*)
+		basic_machine=mmix-knuth
+		os=-mmixware
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+        pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2)
+		basic_machine=i686-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+	        ;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+	        ;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sparclite-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=t3e-cray
+		os=-unicos
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	windows32)
+		basic_machine=i386-pc
+		os=-windows32-msvcrt
+		;;
+	xmp)
+		basic_machine=xmp-cray
+		os=-unicos
+		;;
+        xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	mips)
+		if [ x$os = x-linux-gnu ]; then
+			basic_machine=mips-unknown
+		else
+			basic_machine=mips-mips
+		fi
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+        cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	c4x*)
+		basic_machine=c4x-none
+		os=-coff
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto*)
+		os=-nto-qnx
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-ns2 )
+	        os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+	        os=-mint
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	pdp10-*)
+		os=-tops20
+		;;
+        pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+        *-gould)
+		os=-sysv
+		;;
+        *-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+        *-sgi)
+		os=-irix
+		;;
+        *-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-vxsim* | -vxworks*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/mg/configure
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/configure	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/configure	(revision 16561)
@@ -0,0 +1,7886 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="src/text/mgquery.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os PACKAGE VERSION COMPAT32BITFLAGS CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT AWK YACC CC CFLAGS ac_ct_CC INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB CPP EGREP U ANSI2KNR ALLOCA LIBOBJS JNIINC JNISUFFIX JNIFLAGS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-dmalloc          use dmalloc, as in
+                          ftp://ftp.letters.com/src/dmalloc/dmalloc.tar.gz
+  --with-regex            use older regex in lieu of GNU rx for matching
+
+Some influential environment variables:
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+          ac_config_headers="$ac_config_headers config.h"
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking target system type" >&5
+echo $ECHO_N "checking target system type... $ECHO_C" >&6
+if test "${ac_cv_target+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_target_alias=$target_alias
+test "x$ac_cv_target_alias" = "x" &&
+  ac_cv_target_alias=$ac_cv_host_alias
+ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+echo "${ECHO_T}$ac_cv_target" >&6
+target=$ac_cv_target
+target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,$program_prefix,;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$,$program_suffix,;$program_transform_name"
+# Double any \ or $.  echo might interpret backslashes.
+# By default was `s,x,x', remove it if useless.
+cat <<\_ACEOF >conftest.sed
+s/[\\$]/&&/g;s/;s,x,x,$//
+_ACEOF
+program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
+rm conftest.sed
+
+
+PACKAGE=mg
+VERSION=1.2
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+
+
+
+
+echo "$as_me:$LINENO: checking to see if architecture is 64-bit" >&5
+echo $ECHO_N "checking to see if architecture is 64-bit... $ECHO_C" >&6
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5
+echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5
+echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$AWK" && break
+done
+
+for ac_prog in 'bison -y' byacc
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_YACC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$YACC"; then
+  ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_YACC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+  echo "$as_me:$LINENO: result: $YACC" >&5
+echo "${ECHO_T}$YACC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	    break 3
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6
+fi
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+	@echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+
+echo "$as_me:$LINENO: checking for AIX" >&5
+echo $ECHO_N "checking for AIX... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef _AIX
+  yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+cat >>confdefs.h <<\_ACEOF
+#define _ALL_SOURCE 1
+_ACEOF
+
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f conftest*
+
+
+echo "$as_me:$LINENO: checking for library containing strerror" >&5
+echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6
+if test "${ac_cv_search_strerror+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_strerror=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_strerror="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_strerror" = no; then
+  for ac_lib in cposix; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_strerror="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5
+echo "${ECHO_T}$ac_cv_search_strerror" >&6
+if test "$ac_cv_search_strerror" != no; then
+  test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS"
+
+fi
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking minix/config.h usability" >&5
+echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <minix/config.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking minix/config.h presence" >&5
+echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <minix/config.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: minix/config.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: minix/config.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_minix_config_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6
+
+fi
+if test $ac_cv_header_minix_config_h = yes; then
+  MINIX=yes
+else
+  MINIX=
+fi
+
+
+if test "$MINIX" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_SOURCE 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_1_SOURCE 2
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _MINIX 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for ${CC-cc} option to accept ANSI C" >&5
+echo $ECHO_N "checking for ${CC-cc} option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX			-Aa -D_HPUX_SOURCE
+# SVR4			-Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xc
+do
+  CFLAGS="$ac_save_CFLAGS $ac_arg"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif
+
+int
+main ()
+{
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc="$ac_arg"; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+CFLAGS="$ac_save_CFLAGS"
+
+fi
+
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+
+echo "$as_me:$LINENO: checking for function prototypes" >&5
+echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6
+if test "$ac_cv_prog_cc_stdc" != no; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define PROTOTYPES 1
+_ACEOF
+
+  U= ANSI2KNR=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  U=_ ANSI2KNR=ansi2knr
+fi
+
+echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6
+if test "${ac_cv_c_const+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this.  */
+  typedef int charset[2];
+  const charset x;
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *ccp;
+  char **p;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* AIX XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  ccp = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++ccp;
+  p = (char**) ccp;
+  ccp = (char const *const *) p;
+  { /* SCO 3.2v4 cc rejects this.  */
+    char *t;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* AIX XL C 1.02.0.0 rejects this saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; };
+    struct s *b; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+  }
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_const=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_const=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+echo "${ECHO_T}$ac_cv_c_const" >&6
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for off_t" >&5
+echo $ECHO_N "checking for off_t... $ECHO_C" >&6
+if test "${ac_cv_type_off_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((off_t *) 0)
+  return 0;
+if (sizeof (off_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_off_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_off_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5
+echo "${ECHO_T}$ac_cv_type_off_t" >&6
+if test $ac_cv_type_off_t = yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for size_t" >&5
+echo $ECHO_N "checking for size_t... $ECHO_C" >&6
+if test "${ac_cv_type_size_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((size_t *) 0)
+  return 0;
+if (sizeof (size_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_size_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_size_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+echo "${ECHO_T}$ac_cv_type_size_t" >&6
+if test $ac_cv_type_size_t = yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5
+echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6
+if test "${ac_cv_header_time+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_time=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_time=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5
+echo "${ECHO_T}$ac_cv_header_time" >&6
+if test $ac_cv_header_time = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TIME_WITH_SYS_TIME 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6
+if test "${ac_cv_struct_tm+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm *tp; tp->tm_sec;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_struct_tm=time.h
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+echo "${ECHO_T}$ac_cv_struct_tm" >&6
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+if test "$ac_cv_prog_cc_stdc" = '-Xc'; then
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+int
+main ()
+{
+ FILE *rubbish = popen("yes", "r");
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+CC="`echo $CC | sed 's/-Xc/-Xa/'`"    ac_cv_prog_cc_stdc='-Xa'
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+
+echo "$as_me:$LINENO: checking for main in -lm" >&5
+echo $ECHO_N "checking for main in -lm... $ECHO_C" >&6
+if test "${ac_cv_lib_m_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_m_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_m_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_m_main" >&5
+echo "${ECHO_T}$ac_cv_lib_m_main" >&6
+if test $ac_cv_lib_m_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBM 1
+_ACEOF
+
+  LIBS="-lm $LIBS"
+
+fi
+
+
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+  echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+  for ac_lib in dir; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+  test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+else
+  echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+  for ac_lib in x; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+  test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+fi
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+for ac_header in fcntl.h limits.h sys/time.h unistd.h string.h memory.h sys/procfs.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "fread" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FREAD_DECL 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "fgetc" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FGETC_DECL 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/procfs.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pr_brksize" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_PR_BRKSIZE 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments.  Useless!
+echo "$as_me:$LINENO: checking for working alloca.h" >&5
+echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6
+if test "${ac_cv_working_alloca_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <alloca.h>
+int
+main ()
+{
+char *p = (char *) alloca (2 * sizeof (int));
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_working_alloca_h=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_working_alloca_h=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5
+echo "${ECHO_T}$ac_cv_working_alloca_h" >&6
+if test $ac_cv_working_alloca_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA_H 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for alloca" >&5
+echo $ECHO_N "checking for alloca... $ECHO_C" >&6
+if test "${ac_cv_func_alloca_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
+# else
+#  if HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else
+#   ifdef _AIX
+ #pragma alloca
+#   else
+#    ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+int
+main ()
+{
+char *p = (char *) alloca (1);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_alloca_works=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_alloca_works=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5
+echo "${ECHO_T}$ac_cv_func_alloca_works" >&6
+
+if test $ac_cv_func_alloca_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA 1
+_ACEOF
+
+else
+  # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble.  Some versions do not even contain alloca or
+# contain a buggy version.  If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=alloca.$ac_objext
+
+cat >>confdefs.h <<\_ACEOF
+#define C_ALLOCA 1
+_ACEOF
+
+
+echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5
+echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6
+if test "${ac_cv_os_cray+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined(CRAY) && ! defined(CRAY2)
+webecray
+#else
+wenotbecray
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "webecray" >/dev/null 2>&1; then
+  ac_cv_os_cray=yes
+else
+  ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5
+echo "${ECHO_T}$ac_cv_os_cray" >&6
+if test $ac_cv_os_cray = yes; then
+  for ac_func in _getb67 GETB67 getb67; do
+    as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define CRAY_STACKSEG_END $ac_func
+_ACEOF
+
+    break
+fi
+
+  done
+fi
+
+echo "$as_me:$LINENO: checking stack direction for C alloca" >&5
+echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6
+if test "${ac_cv_c_stack_direction+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_c_stack_direction=0
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+find_stack_direction ()
+{
+  static char *addr = 0;
+  auto char dummy;
+  if (addr == 0)
+    {
+      addr = &dummy;
+      return find_stack_direction ();
+    }
+  else
+    return (&dummy > addr) ? 1 : -1;
+}
+
+int
+main ()
+{
+  exit (find_stack_direction () < 0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_stack_direction=1
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_stack_direction=-1
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5
+echo "${ECHO_T}$ac_cv_c_stack_direction" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+_ACEOF
+
+
+fi
+
+if test $ac_cv_c_compiler_gnu = yes; then
+    echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+    ac_pattern="Autoconf.*'x'"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+else
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+  fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6
+if test "${ac_cv_type_signal+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+# undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int
+main ()
+{
+int i;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_signal=void
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_signal=int
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+echo "${ECHO_T}$ac_cv_type_signal" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+
+for ac_func in vprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+echo "$as_me:$LINENO: checking for _doprnt" >&5
+echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6
+if test "${ac_cv_func__doprnt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+char (*f) () = _doprnt;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != _doprnt;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func__doprnt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func__doprnt=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+echo "${ECHO_T}$ac_cv_func__doprnt" >&6
+if test $ac_cv_func__doprnt = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in ftime select strftime strtol getrusage times mallinfo setbuffer getpagesize
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+for ac_func in ftruncate strstr strcasecmp
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  case $LIBOBJS in
+    "$ac_func.$ac_objext"   | \
+  *" $ac_func.$ac_objext"   | \
+    "$ac_func.$ac_objext "* | \
+  *" $ac_func.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;;
+esac
+
+fi
+done
+
+
+
+
+# *** Custom checking (based on GNU tar configure.in) ***
+# ---------------------------------------------------------------------------
+echo "$as_me:$LINENO: checking for HP-UX needing gmalloc" >&5
+echo $ECHO_N "checking for HP-UX needing gmalloc... $ECHO_C" >&6
+if test "`(uname -s) 2> /dev/null`" = 'HP-UX'; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  case $LIBOBJS in
+    "gmalloc.$ac_objext"   | \
+  *" gmalloc.$ac_objext"   | \
+    "gmalloc.$ac_objext "* | \
+  *" gmalloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS gmalloc.$ac_objext" ;;
+esac
+
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_VALLOC 1
+_ACEOF
+
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+
+for ac_func in valloc
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+fi
+
+# we need to set an extended JNI path for Mac OS/Darwin, as jni.h is on
+# a non-standard Path
+# is there a better way to do this??
+echo "$as_me:$LINENO: checking for OS to set JNI options" >&5
+echo $ECHO_N "checking for OS to set JNI options... $ECHO_C" >&6
+# set defaults
+JNIINC=""
+JNISUFFIX="so"
+JNIFLAGS="-shared"
+
+if test "`(uname -s) 2> /dev/null`" = 'Darwin'; then
+  echo "$as_me:$LINENO: result: Darwin" >&5
+echo "${ECHO_T}Darwin" >&6
+  JNIINC="-I/System/Library/Frameworks/JavaVM.framework/Headers/ "
+  JNISUFFIX="jnilib"
+  JNIFLAGS="-dynamiclib -fno-common -single_module -framework JavaVM"
+fi
+if test "`(uname -s) 2> /dev/null`" = 'SunOS'; then
+  echo "$as_me:$LINENO: result: Solaris" >&5
+echo "${ECHO_T}Solaris" >&6
+  JNIINC="-I\$(JAVA_HOME)/include/solaris "
+fi
+if test "`(uname -s) 2> /dev/null`" = 'Linux'; then
+  echo "$as_me:$LINENO: result: Linux" >&5
+echo "${ECHO_T}Linux" >&6
+  JNIINC="-I\$(JAVA_HOME)/include/linux "
+fi
+
+
+
+
+
+echo "$as_me:$LINENO: checking if malloc debugging is wanted" >&5
+echo $ECHO_N "checking if malloc debugging is wanted... $ECHO_C" >&6
+
+# Check whether --with-dmalloc or --without-dmalloc was given.
+if test "${with_dmalloc+set}" = set; then
+  withval="$with_dmalloc"
+  if test "$withval" = yes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define WITH_DMALLOC 1
+_ACEOF
+
+  LIBS="$LIBS -ldmalloc"
+  LDFLAGS="$LDFLAGS -g"
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi;
+
+echo "$as_me:$LINENO: checking which of rx or regex is wanted" >&5
+echo $ECHO_N "checking which of rx or regex is wanted... $ECHO_C" >&6
+
+# Check whether --with-regex or --without-regex was given.
+if test "${with_regex+set}" = set; then
+  withval="$with_regex"
+  if test "$withval" = yes; then
+  ac_with_regex=1
+  echo "$as_me:$LINENO: result: regex" >&5
+echo "${ECHO_T}regex" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define WITH_REGEX 1
+_ACEOF
+
+  case $LIBOBJS in
+    "regex.$ac_objext"   | \
+  *" regex.$ac_objext"   | \
+    "regex.$ac_objext "* | \
+  *" regex.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS regex.$ac_objext" ;;
+esac
+
+fi
+fi;
+if test -z "$ac_with_regex"; then
+  echo "$as_me:$LINENO: result: rx" >&5
+echo "${ECHO_T}rx" >&6
+  echo "$as_me:$LINENO: checking for re_rx_search" >&5
+echo $ECHO_N "checking for re_rx_search... $ECHO_C" >&6
+if test "${ac_cv_func_re_rx_search+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define re_rx_search to an innocuous variant, in case <limits.h> declares re_rx_search.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define re_rx_search innocuous_re_rx_search
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char re_rx_search (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef re_rx_search
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char re_rx_search ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_re_rx_search) || defined (__stub___re_rx_search)
+choke me
+#else
+char (*f) () = re_rx_search;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != re_rx_search;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_re_rx_search=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_re_rx_search=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_re_rx_search" >&5
+echo "${ECHO_T}$ac_cv_func_re_rx_search" >&6
+if test $ac_cv_func_re_rx_search = yes; then
+  :
+else
+  case $LIBOBJS in
+    "rx.$ac_objext"   | \
+  *" rx.$ac_objext"   | \
+    "rx.$ac_objext "* | \
+  *" rx.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS rx.$ac_objext" ;;
+esac
+
+fi
+
+fi
+
+
+# text for endianness
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+
+cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+ ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+
+# ---------------------------------------------------------------------------
+if test "$ac_cv_func_alloca" = 'no'; then
+  case $LIBOBJS in
+    "xmalloc.$ac_objext"   | \
+  *" xmalloc.$ac_objext"   | \
+    "xmalloc.$ac_objext "* | \
+  *" xmalloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS xmalloc.$ac_objext" ;;
+esac
+
+  case $LIBOBJS in
+    "error.$ac_objext"   | \
+  *" error.$ac_objext"   | \
+    "error.$ac_objext "* | \
+  *" error.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS error.$ac_objext" ;;
+esac
+
+fi
+
+
+# ---------------------------------------------------------------------------
+
+                                                                      ac_config_files="$ac_config_files Makefile src/text/Makefile src/images/Makefile src/scripts/Makefile lib/Makefile jni/Makefile java/org/greenstone/mg/Makefile"
+          ac_config_commands="$ac_config_commands default"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "src/text/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/text/Makefile" ;;
+  "src/images/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/images/Makefile" ;;
+  "src/scripts/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/scripts/Makefile" ;;
+  "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
+  "jni/Makefile" ) CONFIG_FILES="$CONFIG_FILES jni/Makefile" ;;
+  "java/org/greenstone/mg/Makefile" ) CONFIG_FILES="$CONFIG_FILES java/org/greenstone/mg/Makefile" ;;
+  "default" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
+  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@target@,$target,;t t
+s,@target_cpu@,$target_cpu,;t t
+s,@target_vendor@,$target_vendor,;t t
+s,@target_os@,$target_os,;t t
+s,@PACKAGE@,$PACKAGE,;t t
+s,@VERSION@,$VERSION,;t t
+s,@COMPAT32BITFLAGS@,$COMPAT32BITFLAGS,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@AWK@,$AWK,;t t
+s,@YACC@,$YACC,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@LN_S@,$LN_S,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@U@,$U,;t t
+s,@ANSI2KNR@,$ANSI2KNR,;t t
+s,@ALLOCA@,$ALLOCA,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@JNIINC@,$JNIINC,;t t
+s,@JNISUFFIX@,$JNISUFFIX,;t t
+s,@JNIFLAGS@,$JNIFLAGS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_COMMANDS section.
+#
+for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
+  ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
+  ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
+  ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
+$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_dest" : 'X\(//\)[^/]' \| \
+	 X"$ac_dest" : 'X\(//\)$' \| \
+	 X"$ac_dest" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_dest" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
+echo "$as_me: executing $ac_dest commands" >&6;}
+  case $ac_dest in
+    default ) test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h ;;
+  esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
Index: /gsdl/trunk/gs2common/indexers/mg/configure.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/configure.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/configure.in	(revision 16561)
@@ -0,0 +1,155 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(src/text/mgquery.c)
+AC_CONFIG_HEADER(config.h)
+
+dnl get $target_cpu, $target_vendor, and $target_os
+dnl (as well as host_*)
+AC_CANONICAL_SYSTEM
+AC_ARG_PROGRAM
+
+dnl
+PACKAGE=mg
+VERSION=1.2
+
+AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
+AC_SUBST(PACKAGE)
+AC_SUBST(VERSION)
+
+
+AC_MSG_CHECKING(to see if architecture is 64-bit)
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  AC_MSG_RESULT(yes)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  AC_MSG_RESULT(no)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+AC_SUBST(COMPAT32BITFLAGS)
+
+dnl Checks for programs.
+AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_YACC
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_RANLIB
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_AIX
+AC_ISC_POSIX
+AC_MINIX
+fp_C_PROTOTYPES
+AC_C_CONST
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+dnl --- This should be in the stdc checking -----------------
+if test "$ac_cv_prog_cc_stdc" = '-Xc'; then
+dnl The Pyramids running SMP_DC.OSx on MIPS r3000 chips with
+dnl /usr/opt/cc3.11/bin/cc treat -Xc as full ANSI mode - no POSIX or
+dnl XOPEN extensions (like popen).
+dnl Unfortunately -D_{XOPEN,POSIX}_SOURCE can't provide all that mg
+dnl needs. So drop back to -Xa
+AC_TRY_COMPILE(
+[#include <stdio.h>
+], [ FILE *rubbish = popen("yes", "r"); ],,
+[CC="`echo $CC | sed 's/-Xc/-Xa/'`"    ac_cv_prog_cc_stdc='-Xa'])
+fi
+dnl ----------------------
+
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lm:
+AC_CHECK_LIB(m, main)
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h limits.h sys/time.h unistd.h string.h memory.h sys/procfs.h)
+AC_EGREP_HEADER(fread, stdio.h, AC_DEFINE(HAVE_FREAD_DECL))
+AC_EGREP_HEADER(fgetc, stdio.h, AC_DEFINE(HAVE_FGETC_DECL))
+AC_EGREP_HEADER(pr_brksize, sys/procfs.h, AC_DEFINE(HAVE_PR_BRKSIZE))
+
+dnl Checks for library functions.
+AC_FUNC_ALLOCA
+AC_PROG_GCC_TRADITIONAL
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(ftime select strftime strtol getrusage times mallinfo setbuffer getpagesize)
+AC_REPLACE_FUNCS(ftruncate strstr strcasecmp)
+
+
+# *** Custom checking (based on GNU tar configure.in) ***
+# ---------------------------------------------------------------------------
+AC_MSG_CHECKING(for HP-UX needing gmalloc)
+if test "`(uname -s) 2> /dev/null`" = 'HP-UX'; then
+  AC_MSG_RESULT(yes)
+  AC_LIBOBJ(gmalloc)
+  AC_DEFINE(HAVE_VALLOC)
+else
+  AC_MSG_RESULT(no)
+  AC_CHECK_FUNCS(valloc)
+fi
+
+# we need to set an extended JNI path for Mac OS/Darwin, as jni.h is on
+# a non-standard Path
+# is there a better way to do this??
+AC_MSG_CHECKING(for OS to set JNI options)
+# set defaults
+JNIINC=""
+JNISUFFIX="so"
+JNIFLAGS="-shared"
+
+if test "`(uname -s) 2> /dev/null`" = 'Darwin'; then
+  AC_MSG_RESULT(Darwin)
+  JNIINC="-I/System/Library/Frameworks/JavaVM.framework/Headers/ "
+  JNISUFFIX="jnilib"
+  JNIFLAGS="-dynamiclib -fno-common -single_module -framework JavaVM"
+fi
+if test "`(uname -s) 2> /dev/null`" = 'SunOS'; then
+  AC_MSG_RESULT(Solaris)
+  JNIINC="-I\$(JAVA_HOME)/include/solaris "
+fi
+if test "`(uname -s) 2> /dev/null`" = 'Linux'; then
+  AC_MSG_RESULT(Linux)
+  JNIINC="-I\$(JAVA_HOME)/include/linux "
+fi
+
+AC_SUBST(JNIINC)
+AC_SUBST(JNISUFFIX)
+AC_SUBST(JNIFLAGS)
+
+fp_WITH_DMALLOC
+fp_WITH_REGEX
+
+# text for endianness
+AC_C_BIGENDIAN
+
+# ---------------------------------------------------------------------------
+if test "$ac_cv_func_alloca" = 'no'; then
+dnl    support functions for alloca.c
+  AC_LIBOBJ(xmalloc)
+  AC_LIBOBJ(error)
+fi
+
+
+# ---------------------------------------------------------------------------
+
+AC_OUTPUT([Makefile src/text/Makefile src/images/Makefile 
+          src/scripts/Makefile lib/Makefile
+	  jni/Makefile java/org/greenstone/mg/Makefile ], 
+[test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h])
Index: /gsdl/trunk/gs2common/indexers/mg/docs/Makefile
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/Makefile	(revision 16561)
@@ -0,0 +1,5 @@
+DISTFILES = Makefile mgmerge.README mg_invf_dump.changes \
+  README.man mg.1 mgintro.1 mgintro++.1
+
+dist:
+	ln $(DISTFILES) $(distdir)
Index: /gsdl/trunk/gs2common/indexers/mg/docs/README.man
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/README.man	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/README.man	(revision 16561)
@@ -0,0 +1,1 @@
+The rest of the man files are in the respective src directories.
Index: /gsdl/trunk/gs2common/indexers/mg/docs/mg.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/mg.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/mg.1	(revision 16561)
@@ -0,0 +1,462 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg 1 \*(Dt CITRI
+.\"=====================================================================
+.\" Author:
+.\"     Nelson H. F. Beebe
+.\"     Center for Scientific Computing
+.\"     Department of Mathematics
+.\"     University of Utah
+.\"     Salt Lake City, UT 84112
+.\"     USA
+.\"     Email: beebe@math.utah.edu (Internet)
+.\"=====================================================================
+.if t .ds Bi B\s-2IB\s+2T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Bi BibTeX
+.if t .ds Te T\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X
+.if n .ds Te TeX
+.\"=====================================================================
+.SH NAME
+mg \- full-text inverted index support
+.\"=====================================================================
+.SH DESCRIPTION
+.B mg
+is a suite of programs that can be used to create
+and query full-text inverted indexes for a
+collection of documents.
+.PP
+An inverted index is essentially a list of
+pointers to occurrences of every word in a
+collection of documents, so that, for example, in
+a collection of Shakespeare's works, one can pose
+questions like
+.I "How often is a fool mentioned in the plays?"
+and
+.I "Are Romeo and Juliet ever mentioned in the same sentence?"
+While search utilities like the UNIX
+.BR grep (1)
+family can be used for simple queries, they suffer
+from several limitations:
+.TP \w'\(bu'u+2n
+\(bu
+Each invocation requires a separate pass over the
+documents, which becomes impossibly slow if the
+document collection is very large.
+.TP
+\(bu
+It is difficult to compose Boolean queries like
+.IR "Caesar AND Brutus AND NOT Antony" .
+.TP
+\(bu
+They provide no mechanism for ranking matches
+according to importance, which is extremely
+important when queries are complex and numerous
+matches are found.
+.TP
+\(bu
+They provide no mechanism for displaying
+surrounding context (although the Free Software
+Foundation
+implementations for the GNU Project remedy this
+with their
+.BI \-A " num"
+(after)
+and
+.BI \-B " num"
+(before)
+switches).
+.TP
+\(bu
+They provide no mechanism for searching for
+occurrences in the same paragraph, unless
+preprocessing is done on the files to wrap
+paragraphs into long lines.  Even that will often
+fail, because input buffer sizes may be limited to
+a few hundred characters.
+.TP
+\(bu
+They provide no easy way to deal with grammatical
+word-ending variants, except by explicit
+enumeration, such as searching for
+.IR compress ,
+.IR compressed ,
+.IR compresses ,
+.IR compressing ,
+and
+.IR compression .
+.PP
+Most computer users have been faced with the
+problem of finding information from a large
+collection of files, such as electronic mail,
+on-line documentation, or source code.  Inverted
+indices provide a highly-effective solution to
+this problem.
+.PP
+The
+.B mg
+software is described in Appendix A of the book
+.RS
+.nf
+Ian H. Witten, Alistair Moffat, and Timothy C. Bell
+.I "Managing Gigabytes: Compressing and Indexing Documents and Images"
+Van Nostrand Reinhold
+1994
+xiv + 429 pages
+US$54.95
+ISBN 0-442-01863-0
+Library of Congress catalog number TA1637 .W58 1994
+.fi
+.RE
+.PP
+Many of the algorithms implemented in
+.B mg
+represent significant advances over previous work,
+both in speed, and in storage requirements.  On a
+fast workstation, in tens of minutes, or a few
+hours,
+.BR mgbuild (1)
+can create an index to
+.I all
+of the words in hundreds of thousands of documents
+occupying hundreds of megabytes, or even more than
+a gigabyte, of disk space.
+.BR mgquery (1)
+can then be used to answer complex queries, with
+responses often returned in a second or less.
+.B mg
+also contains algorithms to deal with images, so
+that with a small amount of descriptive text for
+each image, it is possible to do searches in
+collections of images, and to have retrievals
+display the images using a viewer like
+.BR xv (1).
+.PP
+.B mg
+can deal with compressed text and image files and
+surprisingly, it usually runs faster than it would
+if the files were not compressed!  Thus, the
+considerable disk space savings possible from
+file compression are not lost because of the need
+for fast document search and retrieval.
+.PP
+The Free Software Foundation GNU Project
+compression utilities
+.BR gzip (1)
+and
+.BR gunzip (1)
+are recommended for general use over older
+alternatives, like
+.BR compress (1),
+because of their speed and high compression
+ratios.
+.\"=====================================================================
+.SH AVAILABILITY
+The
+.B mg
+software can be obtained via anonymous ftp to the
+Australian archive host
+.I "munnari.oz.au [128.250.1.21]"
+from the directory
+.IR "/pub/mg" .
+.\"=====================================================================
+.SH "TYPICAL USE OF mg"
+Although
+.B mg
+consists of more than 20 separate programs, many
+of which have complicated command-line options,
+take heart: most users require only two or three
+of these programs, and nothing more than a
+document name on the command line.
+.PP
+A
+.I document
+for
+.B mg
+is a fragment of text suitable for retrieval as a
+unit when it is found to contain a requested word,
+or words.  In a collection of poetry, a document
+might be a stanza, while in a novel, it could be a
+paragraph.  In an index of first lines of poems, a
+document would likely be just a single line.
+.PP
+Just what constitutes a document is decided by a
+user-modifiable UNIX shell script,
+.BR mg_get (1).
+The default script provided with the
+.B mg
+source distribution knows about these named
+document collections:
+.TP \w'mailfiles'u+2n
+.I alice
+Lewis Carroll's
+.I "Alice in Wonderland"
+book.
+.TP
+.I allfiles
+all mail files in the directory tree
+.IR $HOME/Mail ,
+including all of its nested subdirectories.
+.TP
+.I mailfiles
+individual mail messages in
+.IR $HOME/mbox
+and
+.IR $HOME/.sentmail.
+.TP
+.I davinci
+A small collection of text and images from the
+work of Leonardo da Vinci.
+.PP
+A document collection name is used by
+.B mg
+as a
+.BR csh (1)
+.I case
+statement selector, and as a subdirectory name in the
+.B $MGDATA
+directory, or the current directory, if
+.B MGDATA
+is not defined.
+.\"=====================================================================
+.SH "EXTENDING mg_get"
+This section describes how to extend
+.BR mg_get (1)
+to handle a new document collection.
+.PP
+Let us take two examples: all \*(Bi
+.I .bib
+files, and all \*(Te files, contained in
+subdirectories under the login directory.
+.PP
+For \*(Bi, each bibliographic entry will be
+considered a separate document.  In order to
+facilitate easy identification of entries, we
+shall require them to begin at the start of a
+line; the
+.BR bibclean (1)
+utility can be used to standardize the format of
+.I .bib
+files, and to validate their string values, so
+that this requirement is met.
+.PP
+For \*(Te, each paragraph will be a separate
+document, and we assume that paragraphs are
+separated by blank lines.  We assume that files
+with extensions
+.IR .atx ,
+.IR .ltx ,
+.IR .stx ,
+and
+.I .tex
+contain input to common \*(Te macro package
+variants.
+.PP
+Make a personal copy of the
+.I mg_get
+script, using the one in the
+.B mg
+source distribution
+.RI ( mg-1.0/mg/mg_get.sh ),
+or the one in the local binary program directory,
+at many sites called
+.IR /usr/local/bin/mg_get .
+.PP
+Examination of the
+.I mg_get
+script shows that each document collection name is
+used in a
+.BR csh (1)
+.I case
+statement selector, and that most of work is done
+by very simple
+.BR awk (1)
+programs that extract documents from files.  In
+your private copy of the
+.I mg_get
+file, after the line
+.PP
+.nf
+  breaksw #davinci
+.fi
+.PP
+and before the line
+.PP
+.nf
+  default:
+.fi
+.PP
+insert this new code:
+.PP
+.nf
+  case bibfiles:
+# Takes a list of files that contain BibTeX entries, and splits them up
+#   by putting ^B after each entry. Assumes that each entry
+#   begins with a line '^@'.
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text':
+      find $HOME -name '*.bib' -print | \e
+        sort | \e
+        xargs -l100 awk \e
+          '/^@/&&NR!=1{print "^B"} {print $0} END{print "^B"}'
+    breaksw #-text
+
+    case '-cleanup':
+    breaksw #-cleanup
+
+  endsw #flag
+  breaksw #bibfiles
+
+  case texfiles:
+# Takes a list of TeX files and split them up
+#   by putting ^B after each paragraph. Assumes that each entry
+#   begins with a line '^@'.
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text':
+      find $HOME -name '*x' -print | \e
+        egrep '[.]tex$|[.]ltx$|[.]atx$|[.]stx$' | \e
+        sort | \e
+        xargs -l100 nawk '   /^ *$/ {if (b!=1) printf "^B";b=1} \e
+            \e!/^ *$/ {print;b=0} \e
+            END {printf "^B"}'
+    breaksw #-text
+
+    case '-cleanup':
+    breaksw #-cleanup
+
+  endsw #flag
+  breaksw #texfiles
+.fi
+.PP
+The ^B characters here are Control-B characters,
+.I not
+caret-B pairs.
+.PP
+If you have a large number of \*(Bi or \*(Te
+files, it is likely that a list of them would be
+too long for the UNIX shell to hold in a single
+variable, or on a single command line.  Thus,
+instead of storing the output of
+.BR find (1)
+in a variable, we proceed more cautiously, and
+employ it to produce a list of the required files,
+then pipe them to
+.BR xargs (1),
+which in turn passes up to 100 filenames at a time
+to
+.BR nawk (1)
+for document selection.
+.PP
+Install this modified
+.I mg_get
+script in your private directory for executable
+programs (e.g.
+.IR $HOME/bin ),
+create a directory
+.I $HOME/mgdata
+to hold the index, issue a
+.B rehash
+command if you are using
+.BR csh (1)
+or
+.BR tcsh (1),
+ensure that
+.I mg_get
+occurs in your search path
+.I before
+any system-wide one (the command
+.BI which " mg_get"
+will tell you which version will be selected),
+then create the inverted indexes by
+.BI mgbuild " bibfiles"
+and
+.B mgbuild
+.IR texfiles .
+These commands may take several minutes to run if
+you have a lot of \*(Bi or \*(Te files, or a large
+home directory tree.  Once they are complete, you
+can then query the index with the commands
+.BI mgquery " bibfiles"
+and
+.B mgquery
+.IR texfiles .
+These should respond very rapidly.
+.PP
+In order to keep your index up-to-date, you should
+arrange for it to be recreated automatically and
+regularly, probably every night.  You can do this
+with
+.BR cron (1).
+Use the command
+.B "crontab \-e"
+to edit your
+.I crontab
+file and add two lines like this:
+.PP
+.nf
+00 04 * * * mgbuild bibfiles >$HOME/mgdata/bibfiles.log 2>&1
+15 04 * * * mgbuild texfiles >$HOME/mgdata/texfiles.log 2>&1
+.fi
+.PP
+Save the file and exit the editor.  Now, every
+night at 4am and 4:15am,
+.BR mgbuild (1)
+will reconstruct your inverted indexes, and the
+results of the builds will be saved in log files
+in your
+.I $HOME/mgdata
+directory.
+.\"=====================================================================
+.SH "SEE ALSO"
+.na
+.BR awk (1),
+.BR bibclean (1),
+.BR bibtex (1),
+.BR compress (1),
+.BR csh (1),
+.BR grep (1),
+.BR gunzip (1),
+.BR gzip (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1),
+.BR nawk (1),
+.BR tcsh (1),
+.BR tex (1),
+.BR xargs (1),
+.BR xmg (1),
+.BR xv (1).
+.\"=====================================================================
+.\" This is for GNU Emacs file-specific customization:
+.\" Local Variables:
+.\" fill-column: 50
+.\" End:
Index: /gsdl/trunk/gs2common/indexers/mg/docs/mgintro++.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/mgintro++.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/mgintro++.1	(revision 16561)
@@ -0,0 +1,212 @@
+.\"------------------------------------------------------------  
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4 
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.ds r \&\s-1MG\s0
+.if n .ds - \%--
+.if t .ds - \(em
+.\"------------------------------------------------------------  
+.am SS
+.LP
+..
+.\"------------------------------------------------------------
+.TH MGINTRO++ 1 \*(Dt CITRI
+.\"--------------------------------------------------------------
+.SH NAME
+mgintro++ \- extended introduction to the MG system
+.\"-------------------------------------------------------------
+.SH DESCRIPTION
+This manual assumes the reader has already read
+.BR mgintro (1).
+.\"-------------------------------------------------------------
+.SS Creating Different Databases
+If a user wants to build databases other than for some
+predefined ones, such as "alice", "davinci", "mailfiles", "allfiles",
+then the user has a couple of choices.
+Ultimately (s)he must produce a text file with control-Bs 
+terminating the documents. 
+To do this one can produce one or more such files, or write a
+"get" command (typically in the form of a script or c program).
+.\"-------------------------------------------------------------
+.SS Using Input Files for mgbuild
+If you don't want to write a "get" script and just want to use
+one or more text files as input, then you must first generate
+the file with control-Bs. For a simple example, you could take
+any text file(s) such as "test1.txt" and "test2.txt", and use 
+.BR vi (1)
+to insert control-Bs by typing "control-V b".
+Next you should create a file with "set" statements 
+in the following form:
+.PP
+.IP
+\fBset pipe = 0 # do not use pipe - use file instead 
+.br
+\fBset input_files = 'test1.txt test2.txt'
+.LP
+Let's call this file, "build_options".
+Now issue the command:
+.IP
+.B mgbuild -s build_options test
+.LP
+This should build a database called "test" in the $MGDATA directory,
+based on the source data of "test1.txt" and "test2.txt".
+The build_options file is simply sourced by 
+.BR mgbuild (1)
+after it has set up its variables. 
+Therefore, any settings one makes in the
+build_options file will override the standard settings.
+See 
+.BR mgbuild (1) 
+for more information.
+.\"-------------------------------------------------------------
+.SS Writing A Get Program
+Instead of using files as input, it is often more convenient to 
+write a "get" program. This program is called by 
+.BR mgbuild (1)
+to get the text data with control-Bs as document terminators.
+It should take three options:
+.br
+(i) -init; (ii) -text; (iii) -cleanup.
+.br
+Get will be called with "init" first and with "cleanup" at the end.
+It will call get with "text" when it wants the text and it should
+write the text to stdout.
+.br
+See 
+.BR mg_get (1)
+for an example.
+.\"-------------------------------------------------------------
+.SS Regular Builds
+The MG system provides a static database; 
+there are no update commands.
+So if one wants to keep one's database reasonably up-to-date
+then one can have this done automatically on a regular basis by 
+.BR cron (1).
+A crontab file can be created using:
+crontab -e
+A crontab file contains lines of the form:
+.nf
+.IP
+\fBminute hour day-of-month month day-of-week shell-command.
+.LP
+.fi
+See
+.BR crontab (1)
+for more information.
+.nf
+An example crontab entry is:
+.IP
+\fB15 02 * * * mgbuild allfiles >$MGDATA/allfiles/allfiles.log 2>&1
+.LP
+.fi
+This will build up the mg database for "allfiles", your mail in
+the folders, every morning at 2:15am. 
+.\"
+.\"-------------------------------------------------------------
+.SS Command Structure
+There are 22 commands that make up the mg system. However,
+a user may only need to be aware of a few: 
+.BR mgbuild (1),
+.BR mgquery (1), 
+and perhaps
+.BR mg_get (1). 
+Many of the commands are called by 
+.BR mgbuild(1).
+The commands can be broken up into a hierarchy.
+.PP
+--------------------------------------
+.br
+MG--+--image compression
+    |  |
+    |  +--mgbilevel
+    |  |
+    |  +--mgfelics
+    |  |
+    |  +--mgtic
+    |  |
+    |  +--mgticbuild
+    |  |
+    |  +--mgticdump
+    |  |
+    |  +--mgticprune
+    |  |
+    |  +--mgticstat
+    |
+    +--text 
+       |
+       +--compression
+       |  |
+       |  +--mg_passes -T1
+       |  |
+       |  +--mg_passes -T2
+       |  |
+       |  +--mg_compression_dict
+       |  |
+       |  +--mg_fast_comp_dict
+       |
+       +--indexing
+       |  |
+       |  +--mg_passes -N1
+       |  |
+       |  +--mg_passes -N2
+       |  |
+       |  +--mg_perf_hash_build
+       |  |
+       |  +--mg_invf_dict
+       |  |
+       |  +--mg_invf_rebuild
+       |  
+       +--weights
+       |  |
+       |  +--mg_weights_build
+       |
+       +--query
+       |  |
+       |  +--mgquery
+       |
+       +--tools
+          |
+          +--mg_invf_dump
+          |
+          +--mg_text_estimate
+          |
+          +--mgdictlist
+          |
+          +--mgstat
+.br
+--------------------------------------
+.PP
+.nf
+.BR mgbuild (1)
+calls the following commands:
+.RS
+.BR mg_passes (1), mg_compression_dict (1)
+.BR mg_perf_hash_build (1), mg_invf_dict (1), mg_invf_rebuild (1)
+.BR mg_weights_build (1)
+.RE
+.fi
+.\"--------------------------------------------
+.SH SEE ALSO
+.BR mgintro (1),
+.BR mgbuild (1),
+.BR mg_get (1)
+.br
+"Guide To The \*r System", in Appendix A of the book:
+.PP
+.RS
+.nf
+Ian H. Witten, Alistair Moffat, and Timothy C. Bell
+.I "Managing Gigabytes: Compressing and Indexing Documents and Images"
+Van Nostrand Reinhold
+1994
+xiv + 429 pages
+US$54.95 
+ISBN 0-442-01863-0
+Library of Congress catalog number TA1637 .W58 1994.
+.fi
+.RE
+
Index: /gsdl/trunk/gs2common/indexers/mg/docs/mgintro.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/mgintro.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/mgintro.1	(revision 16561)
@@ -0,0 +1,155 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.ds r \&\s-1MG\s0
+.if n .ds - \%--
+.if t .ds - \(em
+.\"------------------------------------------------------------
+.am SS
+.LP
+..
+.\"------------------------------------------------------------
+.TH MGINTRO 1 \*(Dt CITRI
+.\"--------------------------------------------------------------
+.SH NAME
+mgintro \- introduction to the MG system
+.\"--------------------------------------------------------------
+.SH DESCRIPTION
+The MG (Managing Gigabytes) system is a collection of
+programs which comprise a full-text retrieval system.
+A full-text retrieval system allows one to create a
+database out of some given documents and then do queries
+upon it to retrieve any relevant documents.
+It is "full-text" in the sense that every word in the
+text is indexed and the query operates only on this index 
+to do the searching.
+.PP
+For example, one could have a database on the book,
+"Alice in Wonderland." A document could be represented by
+each paragraph in the book.
+Having built up the "Alice" database, one could do queries
+such as "cat alice grin" and retrieve any paragraphs which
+match the query. The matching could either be boolean, that
+is the retrieved paragraphs contain a boolean expression of
+the query terms e.g. "cat alice grin"; or the matching
+could be ranked i.e. the most relevant documents to the query
+in relevance order, using some standard heuristic measure.
+.\"--------------------------------------------------------------
+.SS Motivation
+If one wants to find some particular information which
+is stored in a computer text file then one has a few alternative
+courses of action. One can operate directly on the text files
+with utilities such as grep or can process the text files into
+some form of database. Grep is generally limited to identifying
+lines by matching on regular expressions. If the collection 
+of files which grep operates on becomes large, then continual
+passes over the entire text on each query becomes expensive.
+However, its usage is simple as no auxiliary files must be created.
+.PP
+A database consists of some data and indexes into that data. By having
+indexes one can query a large database quickly. Standard databases
+divide the data up into records of fields. This means that the granularity
+of search is a field. In a full-text system, such as \*r,
+there are no fields 
+(or there is an arbitrary sized list of word fields per document) 
+and instead every word is indexed. 
+Using this method, we can except free-form information and yet be fast on searches.
+The next question is what is the overhead of this database.
+In \*r most files which are produced are in a compressed form. The
+two notable compressed files being the given data and the index, called
+an "inverted file". By compressing the files it is possible to have the
+size of the database smaller than the size of the source data.
+.\"--------------------------------------------------------------
+.SS Typical Usage
+The most common use for \*r
+has been as a search database on unix mail files.
+However, any set of text data can be used, one just needs to determine
+what constitutes a document (see 
+.BR mgintro++ (1)
+).
+\*r has also been used on large collections such
+as Comact (Commonwealth Acts of Australia) which is around 132 megabytes 
+and also on sizes up to around 2 gigabytes for TREC 
+(a mixture of collections such as the Wall Street Journal 
+and Associated Press).
+.\"--------------------------------------------------------------
+.SS Getting Started with \*r
+The first thing to do is install the package; 
+please follow the INSTALL instructions.
+Having done this, it is necessary to set a couple of environment variables.
+MGDATA should be set to a directory which is to hold subdirectories for
+each database that you build. For example: 
+.IP
+.B mkdir ~/mgdata; setenv MGDATA ~/mgdata.
+.LP
+If you want to try out building some sample databases then there is
+some sample data such as the "Alice In Wonderland" book. To make sure
+this is accessible you should set the environment variable MGSAMPLE.
+For example: 
+.IP
+.B setenv MGSAMPLE ~/mg/SampleData
+.LP
+Here, "~/mg/SampleData" should contain alice.z .
+.PP
+To build the Alice database (to be contained in $MGDATA/alice subdirectory), 
+type the command 
+.IP
+.B mgbuild alice 
+.LP
+Assuming all went well and some status messages 
+were printed indicating the build was completed, then type 
+.IP
+.B mgquery alice 
+.LP
+to query the database. 
+You can type a few words at the prompt, hit return and
+some relevant documents, Alice paragraphs, should be retrieved.
+Type ".set query ranked" to do ranking queries. Please refer to the 
+.BR mgquery (1)
+man-page for more information on the commands and options of 
+.BR mgquery (1).
+.PP
+The next thing to do is to use \*r
+on a more personal database. If you have
+your mail stored in subdirectories of ~/Mail, such as is done if you use the
+typical set up of 
+.BR elm (1), 
+then type 
+.IP
+.B mgbuild allfiles 
+.LP
+If, however, you keep all your mail in ~/mbox or ~/sentmail, then type 
+.IP
+.B mgbuild mailfiles
+.LP
+.\"--------------------------------------------------------------
+.SH AVAILABILITY
+The \*r software for SunOS 4, Solaris, HPUX, and MIPS,
+can be ftped from: munnari.oz.au [128.250.1.21] in the directory
+/pub/mg. 
+.\"--------------------------------------------------------------
+.SH SEE ALSO
+.na
+.BR mgintro++ (1),
+.BR mgbuild (1),
+.BR mgquery (1)
+.br
+"Guide To The \*r System", in Appendix A of the book:
+.PP
+.RS
+.nf
+Ian H. Witten, Alistair Moffat, and Timothy C. Bell
+.I "Managing Gigabytes: Compressing and Indexing Documents and Images"
+Van Nostrand Reinhold
+1994
+xiv + 429 pages
+US$54.95
+ISBN 0-442-01863-0
+Library of Congress catalog number TA1637 .W58 1994.
+.fi
+.RE
Index: /gsdl/trunk/gs2common/indexers/mg/docs/mgmerge.README
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/docs/mgmerge.README	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/docs/mgmerge.README	(revision 16561)
@@ -0,0 +1,230 @@
+A CRASH COURSE GUIDE TO MGMERGE
+===============================
+
+Shane Hudson
+15 November 1994
+
+
+This document is intended as a note to the maintainers of mgmerge
+outlining the mgmerge utility, the changes
+that had to be made to the existing mg code to make mgmerge
+work, and the new source code for mgmerge.
+
+
+****  NOTE:    ****
+All the NEW/CHANGED files are in the file "new_mg.tar" which
+you should have received with this; files that were
+not changed are NOT in the tar file  so it will have to be
+extracted "on top of" the existing code. You'll figure it out :)
+
+
+****  DESCRIPTION:   ****
+mgmerge adds documents to an existing mg database
+without the need to rebuild the database from scratch.
+It works by building a temporary new database from the
+new documents, then merging the "old" and "new" databases
+to give one merged database.
+
+
+The source consists of:
+    mgmerge.sh		The main script, a lot like mgbuild.sh
+    mg_get_merge.sh	Script to retrieve new documents, a lot like
+			mg_get.sh
+    mg_merge.h		header file used by mg_invf_merge.c
+			and mg_text_merge.c
+    mg_text_merge.c	Program to append one compressed text file
+			to another
+    mg_invf_merge.c	Program to merge two inverted files and
+			stemmed dictionaries
+
+See the man pages for mgmerge, mg_get_merge, mg_text_merge and
+mg_invf_merge for a brief overview.
+
+mg_get_merge.sh should really be put into mg_get.sh, with a "-merge"
+option if it is being called by the mgmerge utility, but I was
+too lazy to change it to that.
+
+
+The files updated by mgmerge are:
+
+*.text			with mg_text_merge
+*.text.idx		with mg_text_merge
+*.text.idx.wgt		with mg_weights_build (after merging)
+*.invf			with mg_invf_merge
+*.invf.idx		with      "
+*.invf.dict		with      "
+*.weight		with mg_invf_merge or mg_weights_build
+*.invf.dict.blocked	with mg_invf_dict (after merging)
+
+The *.text.stats file and *.text.dict file remain the same.
+Any other database files (ie, *.invf.dict.hash)
+will be out of date after a merge and will need to be recomputed.
+
+--------------------------------------------------------------------------
+
+MG_TEXT_MERGE
+=============
+
+The two "merging" utilities are mg_text_merge and mg_invf_merge
+
+mg_text_merge simply appends the compressed text for the new
+documents to the old compressed text file.
+For this to succeed, the two files should have been created
+with the same parameters to mg_passes and the same compression
+model must be used. So *.text.dict from the old (static) database
+is used as the model for compressing the new documents.
+The default "-C" option to mg_compression_dict in mgbuild was
+changed to "-S" so novel words in the new documents can be coded.
+The option must be the same in mgmerge -- it is "-S" there too.
+
+---------------
+
+EFFECT on FILE COMPRESSION PERFORMANCE
+
+Since the compression model is not being updated,compression
+performance for the text will slowly degrade. The only solution
+is a periodic rebuild from scratch.
+
+---------------------------------------------------------------------------
+
+MG_INVF_MERGE
+=============
+
+mg_invf_merge updates the stemmed dictionary and
+inverted file.
+For reasons of simplicity, the stemmed dictionaries merged
+are in the ".invf.dict" format, NOT the ".invf.dict.blocked"
+format. 
+So if a database is to be merged the .invf.dict file should
+not be deleted after the .invf.dict.blocked file used by mgquery
+has been created.
+Also for simplicity, level 3 inverted files are not supported, nor
+are skipped format files.
+
+
+
+Merging the inverted files requires every entry in each file
+to be decoded and merged if an entry for the same term appears
+in the other inverted file.
+This can be a slow process if the old inverted file is very large.
+Especially since each entry is decoded bit-by-bit with
+the bit-level I/O rouutines.
+As was noted on page 94 of the book "Managing Gigabytes" (hereafter 
+called "MG"), switching from bernoulli coding (called "Bblock" in
+the mg source code) to gamma or delta coding for the
+inter-document gaps would be an advantage since they require no
+parameters.
+With Bblock coding, N (the number of documents) is a parameter and
+as N changes whenever documents are added, every entry must
+be decoded and recoded.
+My approach was to keep Bblock coding, but keep the N parameter
+constant by recording the artificial N  used to code document
+gaps (called "Nstatic") as well as the real number of documents.
+
+Then, any entry in the old inverted file (called "IFold") that
+is not merged with an entry in the new inverted file (called "IFnew")
+can be copied directly to the merged inverted file (called "IFmerge").
+This can give a significant increase in speed, since usually the
+size of IFnew is very small if only a few documents are being added.
+
+To store Nstatic, a field "static_num_of_docs" was added to
+the invf_dict_header and stem_dict_header structs defined
+in invf.h
+Any program that decodes inverted file entries also had to be changed.
+(The existing mg source code that I changed has been returned with
+this document.)
+Most changes were simply altering a line that
+called BIO_Bblock_Init() so it used the static_num_of_docs
+value rather than num_of_docs.
+
+So since the file format has changed, any existing collection will
+have to be rebuilt first even using mgmerge with it is not
+intended.
+
+-------------
+
+EFFECT on INVERTED FILE COMPRESSION PERFORMANCE
+
+As more merges are accumulatively performed on a database, the
+compression performance will decline.
+The solution is an option in mg_invf_merge to do a "slow"
+merge where every inverted file entry is decoded and recoded using
+the real value of N.
+This is not a lot slower than a fast merge when compared to the
+cost of completely rebuilding the collection from scratch, and 
+the option can be used periodically (when, say, 40% of the
+inverted file has been added since the previous slow merge).
+
+-----------------
+
+THE EXACT WEIGHTS FILE
+
+Recomputing exact document weights requires a complete scan over the
+merged inverted file. This is a waste since the weights for old
+documents will (hopefully) not change much anyway, and the weights
+for new documents can be computed exactly when merging the inverted files,
+at no extra time cost.
+So mg_invf_merge computes the weights for new documents and
+updates the .weight file.
+The weights for old documents are left untouched.
+mgmerge has an option to recompute the weights file.
+This is not much more expensive in terms of time, and is only
+periodically needed.
+
+The effect of leaving the old weights unchanged is difficult to
+asess since the "correct" ranking of documents for a query is
+subjective.
+The frequency with which a rebuild of the weights file with the
+"-w" option should be done is hard to guess.
+But if most merges involves only a few documents, it makes sense
+to not bother recomputing it since the change in old weights values
+is very small and the weights only stray from their "true"
+values slowly over accumulated merging.
+
+-------------------------------------------------------------------------
+
+EXPERIMENTAL RESULTS
+====================
+
+With some small test collections (only a few Mb of text)
+and a slow merge, and a weights file rebuild,
+mgmerge typically took under 20% of the time to completely
+rebuild. Using the default options (fast merge, dont rebuild weights)
+took around 10% or more.
+For larger collections, the savings were greater, especially if the
+added text is very small.
+For example, when the last 6Kb of text of the GNUBib collection was
+added to the rest of it (14Mb) using mgmerge, the total mgmerge time
+was around 20 seconds whereas a complete mgbuild took 280 seconds.
+Only one larger collection was tested (resource contraints prevented
+testing on any really large collections): two short (one-line)
+documents were added to the Gutenberg collection, which comprised
+of nearly 74Mb of source text and had an inverted file size of 9Mb.
+mgmerge took 42 seconds (or 142 seconds with a slow merge and rebuilding the
+weights file), and I'd estimate that an mgbuild on the same machine would 
+have taken at least 20 minutes.
+
+To summarise, mgmerge is not as fast as some sophisticated method
+such as the fixed-length blocks described in section 5.7 of "MG",
+but its huge advantage is that it required almost no changes to
+the existing mg code and is still far better than using mgbuild
+every time a document needs to be added to a collection.
+
+----------------------------------------------------------------------------
+
+OTHER NOTES
+===========
+
+Since it works like mgbuild, mgmerge needs mg_get_merge
+to return the SAME text each time "mg_get_merge -text .." is called.
+
+----
+
+One other thing discovered (but not fixed) was a bug in "invf.pass2.c":
+It crashes on parsed words longer than 128 characters.
+So if any text with words >128 chars is piped to mg_passes,
+the "-N1" and "-N2" options (the memory-efficient inversion method)
+had better be used and not the "-I1"/"-I2" option (which would only
+work on small collections anyway, being the memory-inefficient
+method).
+
Index: /gsdl/trunk/gs2common/indexers/mg/install-sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/install-sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/install-sh	(revision 16561)
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGDocInfo.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGDocInfo.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGDocInfo.java	(revision 16561)
@@ -0,0 +1,42 @@
+/*
+ *    MGDocInfo.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** DocInfo 'struct' class for a search result from mg
+ *  used by MGQueryResult
+ *
+ * @See MGQueryResult
+ */
+public class MGDocInfo
+{
+    public long num_ = 0;
+    public float rank_ = 0;
+
+    public MGDocInfo(long doc, float rank)
+    {
+	num_ = doc;
+	rank_ = rank;
+    }
+
+    public String toString()
+    {
+	return "" + num_ + " (" + rank_ + ")";
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGEquivTermInfo.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGEquivTermInfo.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGEquivTermInfo.java	(revision 16561)
@@ -0,0 +1,53 @@
+/*
+ *    MGEquivTermInfo.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** EquivTermInfo 'struct' class for a search result from mg
+ *  used by MGQueryResult
+ *
+ * @see MGQueryResult
+ */
+public class MGEquivTermInfo
+{
+    /** the term itself */
+    public String term_ = null;
+
+    /** the number of documents containing this term */
+    public long match_docs_ = 0;
+
+    /** overall term freq - word level */
+    public long term_freq_ = 0;
+
+
+    public MGEquivTermInfo(String term, long match, long freq)
+    {
+	term_ = term;
+	match_docs_ = match;
+	term_freq_ = freq;
+    }
+
+
+    public String toString()
+    {
+	String result = "\"" + term_ + "\"";
+	result += " (" + match_docs_ + "/" + term_freq_ + ")";
+	return result;
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGPassesWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGPassesWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGPassesWrapper.java	(revision 16561)
@@ -0,0 +1,154 @@
+/*
+ *    MGPassesWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** java wrapper class for access to gs3_mg_passes in C
+ *
+ * the native side implemented in MGPassesWrapperImpl.c
+ */
+
+public class MGPassesWrapper
+{
+    static {
+	System.loadLibrary("mgpassjni");
+	initIDs();
+    }
+    
+    static final public char INVF_LEVEL_1 = '1';
+    static final public char INVF_LEVEL_2 = '2';
+    static final public char INVF_LEVEL_3 = '3';
+    
+    static final public int TEXT_PASS_1 = 0;
+    static final public int TEXT_PASS_2 = 1;
+    static final public int INDEX_PASS_1 = 2;
+    static final public int INDEX_PASS_2 = 3;
+    static final public int SPECIAL_PASS = 4;
+
+    static final public int NO_STEM_OR_CASE = 0;
+    static final public int CASE_ONLY = 1;
+    static final public int STEM_ONLY = 2;
+    static final public int STEM_AND_CASE = 3;
+
+    static final public String STEMMER_ENGLISH = "english";
+    static final public String STEMMER_FRENCH = "french";
+    static final public String STEMMER_LOVIN = "lovin";
+    static final public String STEMMER_SIMPLE_FRENCH = "simple-french";
+
+    static final private char END_OF_DOCUMENT = (char) 2;
+
+    public MGPassesWrapper() {
+	initCSide();
+    }
+
+    /** initialise the pass through the documents */
+    public native boolean init();
+    
+    /** add a pass declaration */
+    public void addPass(int pass) {
+	switch (pass) {
+	case TEXT_PASS_1:
+	    addPass('T','1');
+	    break;
+	case TEXT_PASS_2:
+	    addPass('T','2');	    
+	    break;
+	case INDEX_PASS_1:
+	    addPass('I','1');
+	    break;
+	case INDEX_PASS_2:
+	    addPass('I','2');
+	    break;
+	case SPECIAL_PASS:
+	    addPass('S','1');
+	    break;
+	}
+    }
+    /** set the base path */
+    public native void setBasePath(String basepath);
+   /** set the file name */
+    public native void setFileName(String filename);
+
+    public native void setStemOptions(String stemmer_type, int stem_method);
+
+    public native void setInvfLevel(char level);
+
+    /** Specify  the size of the document buffer in kilobytes.
+	If any document is larger than  bufsize,  the  program
+	will  abort with an error message. 
+     */
+    public native void setBufferSize(long bufsize);
+    
+    /** Maximum amount of memory to use for  the index  pass-2  file
+	inversion  in  megabytes.
+    */
+    public native void setInversionMemLimit(int limit);
+
+    /** If true, treat   SGML  tags  as  non-words  when  building  the
+	inverted file.
+    */
+    public native void ignoreSGMLTags(boolean ignore);
+    
+    /** if mg_passes fails, the document that caused teh failure will be
+	output to teh trace file or STDERR. 
+    */
+    public native void dumpFailedDocument(boolean dump);
+    
+    /** output statistics on the compression performance to  a  file  
+	called  *.compression.stats.    frequency  specifies  the  interval
+	(in kilobytes of source text) between outputting each line of 
+	statistics. 
+    */
+    public native void outputCompStats(int frequency);
+
+    /** activate tracing, a line will be output every tracepos input bytes */
+    public native void enableTracing(int tracepos);
+    /** process a Greenstone document, which may consist of many MG documents (seeparated by ^B */
+    public boolean processDocument(String docs_text) {
+	// need -1 in the following to keep empty strings at the end
+	String [] docs = docs_text.split(String.valueOf(END_OF_DOCUMENT), -1);
+	System.err.println("GS document split into "+docs.length+" mg documents");
+	for (int i=0; i<docs.length; i++) {
+	    try {
+		processMGDocument(docs[i].getBytes("UTF-8"));
+	    } catch (Exception e) {
+		e.printStackTrace();
+	    }
+	}
+	return true;
+    }
+   
+    /** finalise the pass through the documents */
+    public native boolean finish();
+
+    /** get the exit value once finished */
+    public native int exitValue();
+ 
+    /** initialises field and method IDs for java side to enable access on C side */
+    private static native void initIDs();
+
+    /** initialises any C side stuff */
+    private native boolean initCSide();
+
+    private native void addPass(char pass_type, char pass_num);
+
+    /** process a MG document */
+    private native boolean processMGDocument(byte[] text);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGQueryResult.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGQueryResult.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGQueryResult.java	(revision 16561)
@@ -0,0 +1,155 @@
+/*
+ *    MGQueryResult.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+import java.util.Vector;
+
+
+/** a java version of QueryResult for mg
+ *
+ * contains get methods for the java side, and simple
+ * set methods to be called from c side
+ *
+ */
+public class MGQueryResult
+{
+    /** the list of MGDocInfo */
+    protected Vector docs_ = null;
+    /** the list of MGTermInfo */
+    protected Vector terms_ = null;
+    /** the total number of docs found - not likely to be the size of docs_ */
+    protected long total_num_docs_ = 0;
+
+
+    MGQueryResult() {
+	docs_ = new Vector();
+	terms_ = new Vector();
+    }
+
+
+    /** clear the info from the last query
+	should be called before setting any new docs/terms */
+    public void clear() {
+	total_num_docs_ = 0;
+	docs_.clear();
+	terms_.clear();
+    }
+
+    public boolean isClear() {
+	return (total_num_docs_ == 0 && docs_.isEmpty() && terms_.isEmpty());
+    }
+
+    /** returns the result as a String - useful for printing out results */
+    public String toString()
+    {
+	String result = "";
+	result += "docs (ranks): ";
+	for (int i = 0; i < docs_.size(); i++) {
+	    if (i > 0) result += ", ";
+	    result += ((MGDocInfo) docs_.elementAt(i)).toString();
+	}
+	result += "\nterms: ";
+	for (int i = 0; i < terms_.size(); i++) {
+	    if (i > 0) result += ", ";
+	    result += ((MGTermInfo) terms_.elementAt(i)).toString();
+	}
+	result += "\nactual number of docs found = " + total_num_docs_;
+	return result;
+    }
+
+
+    /** a shorter representation - just terms and total docs - not the 
+	individual docnums and ranks */
+    public String toShortString() {
+	String result = "";
+	result += "\nterms: ";
+	for (int i = 0; i < terms_.size(); i++) {
+	    if (i > 0) result += ", ";
+	    result += ((MGTermInfo) terms_.elementAt(i)).toString();
+	}
+	result += "\nactual number of docs found = "+total_num_docs_;
+	return result;
+    }
+
+
+    // set methods used by c++ code
+
+    public void setTotalDocs(long num) {
+	total_num_docs_=num;
+    }
+
+
+    public void addDoc(long doc, float rank) {
+	MGDocInfo doc_info = new MGDocInfo(doc, rank);
+	///ystem.out.println("(Java) Added doc " + doc_info);
+
+	docs_.add(doc_info);
+    }
+
+
+    public void addTerm(String term, int stem)
+    {
+	MGTermInfo ti = new MGTermInfo();
+	ti.term_ = term;
+	ti.stem_method_ = stem;
+	terms_.add(ti);
+	///ystem.out.println("(Java) Added term " + ti);
+    }
+
+
+    public void addEquivTerm(String term, String equivTerm,
+			     long match, long freq)
+    {
+	// Find the term to add the equivalent to
+	MGTermInfo ti = null;
+	for (int i = (terms_.size() - 1); i >= 0; i--) {
+	    ti = (MGTermInfo) terms_.elementAt(i);
+	    // Found
+	    if (ti.term_ == term) {
+		break;
+	    }
+	}
+
+	if (ti == null) {
+	    System.err.println("Internal error: No term exists to add to.\n");
+	}
+	else {
+	    ti.addEquivTerm(equivTerm, match, freq);
+	    ///ystem.out.println("(Java) Added equivalent term " + equivTerm + ", match: " + match + ", freq: " + freq);
+	}
+    }
+
+
+    // Get methods for the java side - GS2MGSearch.java, GS2MGRetrieve.java
+    public Vector getDocs() {
+	return docs_;
+    }
+
+
+    public Vector getTerms() {
+	return terms_;
+    }
+
+
+    public long getTotalDocs() {
+	return total_num_docs_;
+    }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGRetrieveWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGRetrieveWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGRetrieveWrapper.java	(revision 16561)
@@ -0,0 +1,102 @@
+/*
+ *    MGRetrieveWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** java wrapper class for access to mg in C
+ *
+ * the native side implemented in MGWrapperImpl.c
+ * uses MGQueryResult to hold the result of a query.
+ * the result of a getDocument is a String
+ * uses the jni
+ *
+ *@see MGQueryResult
+ */
+public class MGRetrieveWrapper {
+    /** the query result, filled in by runQuery */
+//    protected MGQueryResult mg_query_result_ = null;
+    
+    /** pointer to c MGWrapperData class - cached indexData and queryInfo */
+    protected long mg_data_ptr_ = 0;
+    
+    static {
+        System.loadLibrary ("mgretrievejni"); 
+        initIDs ();
+    }
+    
+    public MGRetrieveWrapper () { System.out.println("in MGRetrieveWrapper.java");
+//        mg_query_result_ = new MGQueryResult ();
+        initCSide ();
+    }
+    
+    /** initialises field and method IDs for java side to enable access on C side */
+        private static native void initIDs ();
+    
+    /** initialises the mg_data_ptr_ */
+    private native boolean initCSide ();
+
+    /** unloads the data */
+    public native boolean unloadIndexData ();
+    
+    /** sets the index to search - default is 'dtx' */
+    public native void setIndex (String index);
+
+    /** returns a document: number docnum at level level
+     * the base_dir and text_path paths should join together to provide
+     * the absolute location of the mg text files eg ..../index/text/demo
+     * returns the doc in utf-8
+     */
+    public native String getDocument (String base_dir, String text_dir, long docnum);
+    
+    
+//    the following is for search service // query param methods
+//    
+//    /** if on=true, sets default casefolding on - it's off by default */
+//    public native void setCase (boolean on);
+//    /** if on=true, sets default stemming on - it's off by default */
+//    public native void setStem (boolean on);
+//    /** default is 50 */
+//    public native void setMaxDocs (int num);
+//    /** if on=true, a query returns term freq info - default is on */
+//    public native void setReturnTerms (boolean on);
+//    /** sets the default boolean operator - AND(=1)/OR(=0) */
+//    public native void setMatchMode (int mode);
+//    
+//    /** returns a string with all the current query param settings */
+//    public native String getQueryParams ();
+//    
+//    /** sets maxnumeric */
+//    public native void setMaxNumeric (int maxnumeric);
+//    
+//    /** actually carry out the query.
+//     * Use the set methods to set query results.
+//     * Writes the result to query_result.
+//     * - maintains state between requests as can be slow
+//     * base_dir and index_path should join together to provide
+//     * the absolute location of the mg index files eg ..../index/dtx/demo
+//     * base_dir must end with a file separator (OS dependant)
+//     */
+//    public native void runQuery (String base_dir, String text_dir, String query_string);
+//    
+//    
+//    /** get the result out of the wrapper */
+//    public MGQueryResult getQueryResult () {
+//        return mg_query_result_;
+//    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGSearchWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGSearchWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGSearchWrapper.java	(revision 16561)
@@ -0,0 +1,103 @@
+/*
+ *    MGSearchWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** java wrapper class for access to mg in C
+ *
+ * the native side implemented in MGWrapperImpl.c
+ * uses MGQueryResult to hold the result of a query.
+ * the result of a getDocument is a String
+ * uses the jni
+ *
+ *@see MGQueryResult
+ */
+public class MGSearchWrapper
+{
+    /** the query result, filled in by runQuery */
+    protected MGQueryResult mg_query_result_ = null;
+
+    /** pointer to c MGWrapperData class - cached indexData and queryInfo */
+    protected long mg_data_ptr_ = 0;
+
+    static {
+	System.loadLibrary("mgsearchjni");
+	initIDs();
+    }
+
+    public MGSearchWrapper() {  System.out.println("in MGSearchWrapper.java");
+	mg_query_result_ = new MGQueryResult();
+	initCSide();
+    }
+
+    /** initialises field and method IDs for java side to enable access on C side */
+    private static native void initIDs();
+
+    /** initialises the mg_data_ptr_ */
+    private native boolean initCSide();
+
+    /** returns a document: number docnum at level level 
+     * the base_dir and text_path paths should join together to provide
+     * the absolute location of the mg text files eg ..../index/text/demo
+     * returns the doc in utf-8
+     */
+//    public native String getDocument(String base_dir, String text_dir, long docnum);
+
+    /** unloads the data */
+    public native boolean unloadIndexData();
+
+    // query param methods
+
+    /** if on=true, sets default casefolding on - it's off by default */
+    public native void setCase(boolean on);
+    /** if on=true, sets default stemming on - it's off by default */
+    public native void setStem(boolean on);
+    /** default is 50 */
+    public native void setMaxDocs(int num);
+    /** if on=true, a query returns term freq info - default is on */
+    public native void setReturnTerms(boolean on);
+    /** sets the index to search - default is 'dtx' */
+    public native void setIndex(String index);
+    /** sets the default boolean operator - AND(=1)/OR(=0) */
+    public native void setMatchMode(int mode);
+
+    /** returns a string with all the current query param settings */
+    public native String getQueryParams();
+
+    /** sets maxnumeric */
+    public native void setMaxNumeric(int maxnumeric); 
+
+    /** actually carry out the query.
+	Use the set methods to set query results.
+	Writes the result to query_result.
+     * - maintains state between requests as can be slow  
+     * base_dir and index_path should join together to provide
+     * the absolute location of the mg index files eg ..../index/dtx/demo 
+     * base_dir must end with a file separator (OS dependant)
+     */
+    public native void runQuery(String base_dir, String text_dir, String query_string);
+
+
+    /** get the result out of the wrapper */
+    public MGQueryResult getQueryResult()
+    {
+	return mg_query_result_;
+    }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGTermInfo.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGTermInfo.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGTermInfo.java	(revision 16561)
@@ -0,0 +1,72 @@
+/*
+ *    MGTermInfo.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+import java.util.Vector;
+
+
+/** TermInfo 'struct' class for a search result from mg
+ *  used by MGQueryResult
+ *
+ * @see MGQueryResult
+ */
+public class MGTermInfo
+{
+    /** the term itself */
+    public String term_ = null;
+
+    /** the stem and casefold method used
+	0 = none
+	1 = casefold only
+	2 = stem only
+	3 = casefold and stem */
+    public int stem_method_ = 0;
+
+    /** list of stemmed and casefolded equivalent terms - if stem_method_ is non-zero 
+      - Vector of Strings */
+    public Vector equiv_terms_ = null;
+
+
+    public MGTermInfo()
+    {
+	equiv_terms_ = new Vector();
+    }
+
+
+    public void addEquivTerm(String equivTerm, long match, long freq)
+    {
+	equiv_terms_.add(new MGEquivTermInfo(equivTerm, match, freq));
+    }
+
+
+    /** output the class as a string */
+    public String toString()
+    {
+	String result = "\"" + term_ + "\"";
+	result += " stem(" + stem_method_ + ")";
+	result += " equiv terms(";
+	for (int i = 0; i < equiv_terms_.size(); i++) {
+	    if (i > 0) result += ", ";
+	    result += equiv_terms_.elementAt(i);
+	}
+	result += ")";
+	return result;
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/MGWrapper.java	(revision 16561)
@@ -0,0 +1,102 @@
+/*
+ *    MGWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+
+/** java wrapper class for access to mg in C
+ *
+ * the native side implemented in MGWrapperImpl.c
+ * uses MGQueryResult to hold the result of a query.
+ * the result of a getDocument is a String
+ * uses the jni
+ *
+ *@see MGQueryResult
+ */
+public class MGWrapper
+{
+    /** the query result, filled in by runQuery */
+    protected MGQueryResult mg_query_result_ = null;
+
+    /** pointer to c MGWrapperData class - cached indexData and queryInfo */
+    protected long mg_data_ptr_ = 0;
+
+    static {
+	System.loadLibrary("mgjni");
+	initIDs();
+    }
+
+    public MGWrapper() {
+	mg_query_result_ = new MGQueryResult();
+	initCSide();
+    }
+
+    /** initialises field and method IDs for java side to enable access on C side */
+    private static native void initIDs();
+
+    /** initialises the mg_data_ptr_ */
+    private native boolean initCSide();
+
+    /** returns a document: number docnum at level level 
+     * the base_dir and text_path paths should join together to provide
+     * the absolute location of the mg text files eg ..../index/text/demo
+     * returns the doc in utf-8
+     */
+    public native String getDocument(String base_dir, String text_dir, long docnum);
+
+    /** unloads the data */
+    public native boolean unloadIndexData();
+
+    // query param methods
+
+    /** if on=true, sets default casefolding on - it's off by default */
+    public native void setCase(boolean on);
+    /** if on=true, sets default stemming on - it's off by default */
+    public native void setStem(boolean on);
+    /** default is 50 */
+    public native void setMaxDocs(int num);
+    /** if on=true, a query returns term freq info - default is on */
+    public native void setReturnTerms(boolean on);
+    /** sets the index to search - default is 'dtx' */
+    public native void setIndex(String index);
+    /** sets the default boolean operator - AND(=1)/OR(=0) */
+    public native void setMatchMode(int mode);
+
+    /** returns a string with all the current query param settings */
+    public native String getQueryParams();
+
+    /** sets maxnumeric */
+    public native void setMaxNumeric(int maxnumeric); 
+
+    /** actually carry out the query.
+	Use the set methods to set query results.
+	Writes the result to query_result.
+     * - maintains state between requests as can be slow  
+     * base_dir and index_path should join together to provide
+     * the absolute location of the mg index files eg ..../index/dtx/demo 
+     * base_dir must end with a file separator (OS dependant)
+     */
+    public native void runQuery(String base_dir, String text_dir, String query_string);
+
+
+    /** get the result out of the wrapper */
+    public MGQueryResult getQueryResult()
+    {
+	return mg_query_result_;
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Makefile.in	(revision 16561)
@@ -0,0 +1,62 @@
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+libdir = @libdir@
+subdir = java/org/greenstone/mg
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+AR = ar
+CC = @CC@
+CXX = @CXX@
+JAVAC = $(JAVA_HOME)/bin/javac
+JAVAH = $(JAVA_HOME)/bin/javah
+JAR = $(JAVA_HOME)/bin/jar
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -DSILENT -DSHORT_SUFFIX
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@ -DSILENT -DSHORT_SUFFIX
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS =  -lstdc++ @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+
+MGHOME=../../../..
+JAVACLASSDIR=./classes
+JAVASRCDIR=../../..
+JAVACOPTIONS= -deprecation -g -O
+JAVACLASSPATH=$(JAVACLASSDIR):
+
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+
+
+all: setup compile
+
+setup:
+	mkdir -p $(JAVACLASSDIR)
+
+compile: setup
+	$(JAVAC) -d $(JAVACLASSDIR) -sourcepath $(JAVASRCDIR) $(JAVACOPTIONS) *.java
+	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGHOME)/jni org.greenstone.mg.MGWrapper
+	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGHOME)/jni org.greenstone.mg.MGPassesWrapper
+	$(JAR) cf $(MGHOME)/mg.jar -C $(JAVACLASSDIR) org
+
+clean:
+	rm -rf $(JAVACLASSDIR) $(MGHOME)/mg.jar $(MGHOME)/jni/org_greenstone_mg_MGWrapper.h $(MGHOME)/jni/org_greenstone_mg_MGPassesWrapper.h
+
+install:
+
+distclean: clean
+	rm -f Makefile
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Queryer.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Queryer.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/Queryer.java	(revision 16561)
@@ -0,0 +1,175 @@
+/*
+ *    Queryer.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mg;
+
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/** Queryer - java port of c++ Queryer 
+ * - uses MGWrapper to access mg
+ * only has get document and search - full text browse not implemented
+ *
+ * run as:
+ * java org.greenstone.mg.Queryer <basedir> <indexdir> <textdir>
+ *@see MGWrapper
+ */
+public class Queryer {
+  
+    /** outputs to std out a help message describing the commands used by 
+     * Queryer 
+     */
+    public void printHelp()
+    {
+	System.out.println( "commands available are:\n"+
+			    "\t.q\t\tquit\n"+
+			    "\t.h\t\tprint this help message\n"+
+			    "\t.d\t\tprint the current query parameter settings\n" +
+			    "\t.i<index>\tsearch index <index>\n"+
+			    "\t.t0/.t1\t\tquery type some/all\n"+
+			    "\t.c0/.c1\t\tcasefolding off/on\n"+
+			    "\t.s0/.s1\t\tstemming off/on\n"+
+			    "\t.o0/.o1\t\tshort output off/on\n"+
+			    "\t.m<num>\t\tset max docs to return to num\n\n"+
+			    "\t.p<docnum>\tprint document docnum\n"+
+			    "\t<query string>\tdo a query\n");
+    }
+
+
+    public static void main(String[] args)
+    {
+	if (args.length != 3) {
+	    System.out.println("Usage: java org.greenstone.mg.Queryer <basedir> <indexdir> <textdir>");
+	    return;
+	}
+
+	Queryer self = new Queryer();
+
+	String base_dir = args[0];
+	String index_path = args[1];
+	String text_path = args[2];
+
+	// the jni class to access mg stuff
+	MGWrapper wrapper = new MGWrapper();
+	wrapper.setIndex(index_path);
+
+	System.out.println("Welcome to Java Queryer :-)");
+	self.printHelp();
+
+	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+	boolean shortOutput = false;
+
+	String command; // user input
+	char x; // the command letter
+	String data = null; // any auxiliary input to a command
+	while (true) {
+	    System.out.print(">"); // the prompt
+            try {
+                command = br.readLine();
+                command = command.trim();
+		if (command.startsWith(".")) {
+		    // a system command
+		    x = command.charAt(1);
+		    if (command.length() > 2) {
+			data = command.substring(2);
+			data = data.trim();
+		    }
+
+		    switch (x) {
+		    case 'q': // clean up and exit
+			wrapper.unloadIndexData();
+			return;
+		    case 'h': // print help message
+			self.printHelp();
+			break;
+		    case 'd': // print query param settings
+			String info = wrapper.getQueryParams();
+			System.out.println(info);
+			break;
+		    case 'p': // print doc
+			int docnum = Integer.parseInt(data);
+			String doc = wrapper.getDocument(base_dir, text_path, docnum);
+			System.out.println(doc);
+			break;
+		    case 'm': //match docs
+			int match = Integer.parseInt(data);
+			wrapper.setMaxDocs(match);
+			break;
+		    case 's': // set stem on/off
+			int stem = Integer.parseInt(data);
+			if (stem==0 ){
+			    wrapper.setStem(false);
+			} else if(stem==1) {
+			    wrapper.setStem(true);
+			} else {
+			    System.err.println("Error: stem should be 0 or 1");
+			}
+			break;
+		    case 'c': // set case on/off
+			int casef = Integer.parseInt(data);
+			if (casef==0) {
+			    wrapper.setCase(false);
+			} else if (casef==1) {
+			    wrapper.setCase(true);
+			} else {
+			    System.err.println("Error: case should be 0 or 1");
+			}
+			break;
+		    case 'i': // set index
+			wrapper.setIndex(data);
+			break;
+		    case 't': // set query type some/all
+			int type = Integer.parseInt(data);
+			if (type==0 || type==1) {
+			    wrapper.setMatchMode(type);
+			} else {
+			    System.err.println("Error: type should be 0 (some) or 1 (all)");
+			}
+			break;
+		    case 'o': // set output short/long
+			int output = Integer.parseInt(data);
+			if (output==0) {
+			    shortOutput = false;
+			} else if (output==1) {
+			    shortOutput = true;
+			} else {
+			    System.err.println("Error: output should be 0 or 1");
+			}
+			break;
+		    }
+		}
+		else {
+		    // a query
+		    wrapper.runQuery(base_dir, text_path, command);
+		    MGQueryResult res = wrapper.getQueryResult();
+		    System.out.println("(Java) Matching documents: " + res.getTotalDocs());
+		    if (shortOutput) {
+			System.out.println(res.toShortString());
+		    } else {
+			System.out.println(res.toString());
+		    }
+		}
+
+	    } catch (Exception e) {
+		System.out.println("Queryer error: "+e.getClass() + " "+e.getMessage());
+		e.printStackTrace();
+	    }
+	}
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/java/org/greenstone/mg/winMake.bat	(revision 16561)
@@ -0,0 +1,47 @@
+@echo off
+
+set MGHOME=..\..\..\..
+
+rem ---- Where to put class files ----
+set JAVACLASSDIR=classes
+
+rem ---- Name and location of java programs ----
+set JAVAC="%JAVA_HOME%\bin\javac"
+set JAVAH="%JAVA_HOME%\bin\javah"
+set JAVA="%JAVA_HOME%\bin\java"
+set JAVADOC="%JAVA_HOME%\bin\javadoc"
+set JAR="%JAVA_HOME%\bin\jar"
+
+set JAVACOPTIONS= -deprecation -g -O
+
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto all
+if ""%1"" == ""install"" goto install
+if ""%1"" == ""clean"" goto clean
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:all
+	if not exist %JAVACLASSDIR% mkdir %JAVACLASSDIR%
+	echo Compiling MG Java classes ...
+        %JAVAC% -d %JAVACLASSDIR% %JAVACOPTIONS% *.java
+	%JAVAH% -classpath %JAVACLASSDIR% -d %MGHOME%\jni org.greenstone.mg.MGWrapper
+	%JAVAH% -classpath %JAVACLASSDIR% -d %MGHOME%\jni org.greenstone.mg.MGPassesWrapper
+	%JAR% cf %MGHOME%\mg.jar -C %JAVACLASSDIR% org
+	goto done
+
+:install
+	goto done
+
+:clean
+	echo Cleaning up...
+	if exist %JAVACLASSDIR% rmdir /S /Q %JAVACLASSDIR%
+        if exist %MGHOME%\mg.jar del %MGHOME%\mg.jar
+        if exist %MGHOME%\jni\org_greenstone_mg_MGWrapper.h del %MGHOME%\jni\org_greenstone_mg_MGWrapper.h
+        if exist %MGHOME%\jni\org_greenstone_mg_MGPassesWrapper.h del %MGHOME%\jni\org_greenstone_mg_MGPassesWrapper.h
+	goto done
+
+:done
Index: /gsdl/trunk/gs2common/indexers/mg/jni/MGPassesWrapperImpl.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/MGPassesWrapperImpl.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/MGPassesWrapperImpl.c	(revision 16561)
@@ -0,0 +1,243 @@
+/*
+ *    MGPassesWrapperImpl.c
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include <jni.h>
+#include <assert.h>
+#include "sysfuncs.h"
+#include "org_greenstone_mg_MGPassesWrapper.h"
+
+#include "mg_passes_4jni.h"
+#include "mg_files.h"
+
+/* if we need to use java objects, we should initialise their field ids here*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_initIDs(JNIEnv *j_env, jclass j_cls)
+{
+  return;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_initCSide(JNIEnv *j_env, jobject j_obj)
+{
+  clear_variables();
+  return 1;  /* true - no errors */
+}  
+
+/* add a pass type T1, T2, I1, I2, S */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_addPass(JNIEnv *j_env, 
+					       jobject j_obj, 
+					       jchar j_pass_type,
+					       jchar j_pass_num)
+{
+  /* get the level as a c char */
+  const char pass_type = j_pass_type;
+  const char pass_num = j_pass_num;
+  add_pass(pass_type, pass_num);
+  
+}
+
+/* Set the filename */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setFileName(JNIEnv *j_env, 
+						   jobject j_obj,
+						   jstring j_filename)
+{
+  /* Get the filename as a C string */
+  const char* filename = (*j_env)->GetStringUTFChars(j_env, j_filename, NULL);
+ 
+  assert(filename != NULL);
+  set_filename(filename);
+  
+  /* Release the string */
+ (*j_env)->ReleaseStringUTFChars(j_env, j_filename, filename);
+
+}
+
+/* Set the base path */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setBasePath(JNIEnv *j_env, 
+						   jobject j_obj,
+						   jstring j_basepath)
+{
+  /* Get the base_path as a C string */
+  const char* basepath = (*j_env)->GetStringUTFChars(j_env, j_basepath, NULL);
+  assert(basepath != NULL);
+ 
+  set_basepath(basepath);
+  
+  /* Release the string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_basepath, basepath);
+
+}
+
+/* set the level for the inverted file */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setInvfLevel(JNIEnv *j_env, 
+						    jobject j_obj, 
+						    jchar j_level)
+{
+  /* get the level as a c char */
+  const char level = j_level;
+  set_invf_level(level);
+  
+}
+
+/* set the stemmer and stem method */
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mg_MGPassesWrapper_setStemOptions(JNIEnv *j_env, 
+						      jobject j_obj,
+						      jstring j_stemmer, 
+						      jint j_method)
+{
+
+  const char* stemmer = (*j_env)->GetStringUTFChars(j_env, j_stemmer, NULL);
+  int method = j_method;
+
+  assert(stemmer != NULL);
+  set_stem_options(stemmer, method);
+  
+  /* Release the string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_stemmer, stemmer);
+ }
+
+/** Specify  the size of the document buffer in kilobytes.
+    If any document is larger than  bufsize,  the  program
+    will  abort with an error message. 
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setBufferSize(JNIEnv *j_env, 
+						     jobject j_obj, 
+						     jlong j_bufsize){
+  long buffer = j_bufsize;
+  set_buffer_size(buffer);
+}
+    
+/** Maximum amount of memory to use for  the index  pass-2  file
+    inversion  in  megabytes.
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setInversionMemLimit(JNIEnv *j_env, 
+							    jobject j_obj, 
+							    jint j_limit) {
+  int limit = j_limit;
+  set_inversion_limit(limit);
+}
+
+/** If true, treat   SGML  tags  as  non-words  when  building  the
+    inverted file.
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_ignoreSGMLTags(JNIEnv *j_env, 
+						      jobject j_obj, 
+						      jboolean j_ignore){
+  int ignore = j_ignore;
+  ignore_sgml_tags(ignore);
+}
+
+/** if mg_passes fails, the document that caused the failure will be
+    output to the trace file or STDERR. 
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_dumpFailedDocument(JNIEnv *j_env, 
+							  jobject j_obj, 
+							  jboolean j_dump) {
+  int dump = j_dump;
+  dump_failed_document(dump);
+}
+
+/** output statistics on the compression performance to  a  file  
+    called  *.compression.stats.    frequency  specifies  the  interval
+    (in kilobytes of source text) between outputting each line of 
+    statistics. 
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_outputCompStats(JNIEnv *j_env, 
+						       jobject j_obj, 
+						       jint j_frequency){
+  int comp_stat_point = j_frequency;
+  set_comp_stat_point(comp_stat_point);
+  
+}
+/** activate tracing, a line will be output every tracepos input bytes */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_enableTracing(JNIEnv *j_env, 
+						     jobject j_obj, 
+						     jint j_tracepos){
+  int tracepos = j_tracepos;
+  set_trace_point(tracepos);
+}
+
+/** specify the name of the trace file */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGPassesWrapper_setTraceFile(JNIEnv *j_env, 
+						     jobject j_obj, 
+						     jstring j_tracefile){
+
+  const char* tracefile = (*j_env)->GetStringUTFChars(j_env, j_tracefile, NULL);
+  assert(tracefile != NULL);
+  set_trace_file(tracefile);
+  /* Release the string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_tracefile, tracefile);
+}
+
+/* initialise the pass through the documents. must be called after all
+   the set methods 
+*/
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mg_MGPassesWrapper_init(JNIEnv *j_env, 
+					    jobject j_obj) {
+
+  init_driver();
+  return 1;
+}
+
+
+/* process one document */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mg_MGPassesWrapper_processMGDocument(JNIEnv *j_env, 
+						       jobject j_obj,
+						       jbyteArray j_doc_text) {
+  /* Get the text as a C string */
+  int length = (*j_env)->GetArrayLength(j_env, j_doc_text);
+  u_char * text_buffer = (u_char *)(*j_env)->GetByteArrayElements(j_env, j_doc_text, NULL);
+  process_document(text_buffer, length);
+  /* Release the string */
+  (*j_env)->ReleaseByteArrayElements(j_env, j_doc_text, text_buffer,0);
+  return 1;
+}
+
+/* finalise the pass through the documents */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mg_MGPassesWrapper_finish(JNIEnv *j_env, 
+					    jobject j_obj) {
+  
+  finalise_driver();
+  return 1;
+}
+
+/** get the exit value once finished */
+JNIEXPORT jint JNICALL 
+Java_org_greenstone_mg_MGPassesWrapper_exitValue(JNIEnv *j_env, 
+					    jobject j_obj) {
+
+  return get_exit_value();
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/jni/MGRetrieveWrapperImpl.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/MGRetrieveWrapperImpl.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/MGRetrieveWrapperImpl.c	(revision 16561)
@@ -0,0 +1,318 @@
+/*
+ *    MGRetrieveWrapperImpl.c
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include "MGWrapperImpl.h"
+#include <jni.h>
+#include "org_greenstone_mg_MGRetrieveWrapper.h"
+
+#include "backend.h"
+#include "environment.h"
+#include "text_get.h"
+#include "stemmer.h"
+
+#define MAX_INDEXES_CACHED 3
+
+/* copied from mgquery, needed to convert paragraph numbers to document numbers
+   for greenstone */
+#if defined(PARADOCNUM) ||  defined(NZDL)
+static int GetDocNumFromParaNum(query_data *qd, int paranum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+  int low = 1, high = Documents;
+  int mid = (low+high)/2;
+
+  while ((mid = (low+high)/2) >=1 && mid <= Documents)
+    {
+      if (paranum > Paragraph[mid])
+        low = mid+1;
+      else if (paranum <= Paragraph[mid-1])
+        high = mid-1;
+      else
+        return mid;
+    }
+  FatalError(1, "Bad paragraph number.\n");
+  return 0;
+}
+#endif
+
+
+/*********************************************
+   initialisation stuff
+ *********************************************/
+
+/* cached ids for java stuff */
+jfieldID FID_mg_data = NULL; /* MGWrapperData */
+jfieldID FID_query_result = NULL; /* MGQueryResult */
+jmethodID MID_addDoc = NULL; /* MGQueryResult.addDoc() */
+jmethodID MID_addTerm = NULL; /* MGQueryResult.addTerm() */
+jmethodID MID_addEquivTerm = NULL; /* MGQueryResult.addEquivTerm() */
+jmethodID MID_setTotalDocs = NULL; /* MGQueryResult.setTotalDocs() */
+jmethodID MID_clearResult = NULL; /* MGQueryResult.clear() */
+
+
+/*******************************************
+   Index caching
+ *******************************************/
+
+query_data* cached_indexes[MAX_INDEXES_CACHED] = { NULL };
+
+
+/* Get the index data necessary to perform a query or document retrieval */
+query_data*
+loadIndexData(char* base_dir, char* index_path, char* text_path)
+{
+  char* index_path_name;
+  char* text_path_name;
+  query_data* qd;
+  int i = 0;
+
+  /* Form the path name of the desired indexes */
+  index_path_name = (char*) malloc(strlen(base_dir) + strlen(index_path) + 1);
+  assert(index_path_name != NULL);
+  strcpy(index_path_name, base_dir);
+  strcat(index_path_name, index_path);
+  printf("Index pathname: %s\n", index_path_name);
+
+  text_path_name = (char*) malloc(strlen(base_dir) + strlen(text_path) + 1);
+  assert(text_path_name != NULL);
+  strcpy(text_path_name, base_dir);
+  strcat(text_path_name, text_path);
+  printf("Text pathname: %s\n", text_path_name);
+
+  /* Search through the cached indexes for the desired one */
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    printf("(Cached) Pathname: %s\n", cached_indexes[i]->pathname);
+    printf("(Cached) Textpathname: %s\n", cached_indexes[i]->textpathname);
+
+    /* Check if the index has already been loaded */
+    if ((strcmp(index_path_name, cached_indexes[i]->pathname) == 0) &&
+	(strcmp(text_path_name, cached_indexes[i]->textpathname) == 0)) {
+      /* Index has already been loaded and cached, so return it */
+      printf("Found index!\n");
+      free(index_path_name);
+      free(text_path_name);
+      return cached_indexes[i];
+    }
+
+    i++;
+  }
+
+  /* Text strings no longer needed */
+  free(index_path_name);
+  free(text_path_name);
+
+  /* The index is not cached, so load it now */
+  qd = InitQuerySystem(base_dir, index_path, text_path, NULL);
+  if (!qd) {
+    printf("Error: Could not InitQuerySystem()...\n");
+    return NULL;
+  }
+
+  /* The index loaded OK, so cache it */
+  /* This could be a little more sophisticated, eg. replace least frequently used index */
+  if (i >= MAX_INDEXES_CACHED)
+    i = MAX_INDEXES_CACHED - 1;
+
+  /* Free the index being replaced */
+  if (cached_indexes[i] != NULL)
+    FinishQuerySystem(cached_indexes[i]);
+
+  /* Cache the loaded index, and return it */
+  cached_indexes[i] = qd;
+  return cached_indexes[i];
+}
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGRetrieveWrapper_initIDs(JNIEnv *j_env, jclass j_cls)
+{
+  /* a long-"J" */
+  FID_mg_data = (*j_env)->GetFieldID(j_env, j_cls, "mg_data_ptr_", "J");
+  assert(FID_mg_data != NULL);
+
+}
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_initCSide
+(JNIEnv *j_env, jobject j_obj)
+{
+  /* Allocate a MGWrapperData object to store query parameters */
+  MGWrapperData* data = (MGWrapperData*) malloc(sizeof(MGWrapperData));
+  assert(data != NULL);
+
+  /* Set default values - no stemming, no case-folding, boolean OR queries */
+  data->defaultStemMethod = 0;
+  data->defaultBoolCombine = 0;
+
+  /* Allocate a QueryInfo object to store more query parameters */
+  data->queryInfo = (QueryInfo*) malloc(sizeof(QueryInfo));
+  assert(data->queryInfo != NULL);
+
+  /* Set default values - 50 documents max, return term freqs, sort by rank */
+  data->queryInfo->index = NULL;
+  data->queryInfo->maxDocs = 50;
+  data->queryInfo->needTermFreqs = 1;
+
+  /* Save the object on the Java side */
+  (*j_env)->SetIntField(j_env, j_obj, FID_mg_data, (long) data);
+
+  /* Initialise MG environment variables */
+  InitEnv();
+  SetEnv("expert", "true", NULL);
+  SetEnv("mode", "docnums", NULL);
+  
+  return 1;  /* true - no errors */
+}
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    unloadIndexData
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_unloadIndexData
+(JNIEnv* j_env, jobject j_obj)
+{
+  /* Free all the loaded indexes */
+  int i = 0;
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    FinishQuerySystem(cached_indexes[i]);
+    cached_indexes[i] = NULL;
+    i++;
+  }
+
+  return 1;  /* true - no errors */
+}
+/* Choose MG index to search */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGRetrieveWrapper_setIndex(JNIEnv *j_env, jobject j_obj,
+					  jstring j_index)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  /* Get the index name as a C string */
+  const char* index = (*j_env)->GetStringUTFChars(j_env, j_index, NULL);
+  assert(index != NULL);
+  printf("Choosing index %s...\n", index);
+
+  /* Free the previous index name */
+  if (data->queryInfo->index)
+    free(data->queryInfo->index);
+
+  /* Allocate memory for the index name, and fill it */
+  data->queryInfo->index = (char*) malloc(strlen(index) + 1);
+  assert(data->queryInfo->index != NULL);
+  strcpy(data->queryInfo->index, index);
+
+  /* Release the index string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_index, index);
+}
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    getDocument
+ * Signature: (Ljava/lang/String;Ljava/lang/String;J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_getDocument
+(JNIEnv *j_env, jobject j_obj,
+					     jstring j_base_dir, jstring j_text_path,
+					     jlong j_docnum)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  char* index_path;
+  const char* base_dir;
+  const char* text_path;
+  query_data* qd;
+
+  u_long pos, len;
+  u_char* c_buffer = NULL;
+  u_char* uc_buffer = NULL;
+  int ULen;
+
+  jstring result;
+
+  /* Make sure an index has been specified */
+  index_path = data->queryInfo->index;
+  assert(index_path != NULL);
+
+  /* Obtain C versions of the two string parameters */
+  base_dir = (*j_env)->GetStringUTFChars(j_env, j_base_dir, NULL);
+  if (base_dir == NULL) {
+    return NULL;
+  }
+  text_path = (*j_env)->GetStringUTFChars(j_env, j_text_path, NULL);
+  if (text_path == NULL) {
+    (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+    return NULL;
+  }
+
+  /* Load the appropriate index for satisfying this request */
+  printf("Document retrieval, index path: %s\n", index_path);
+  qd = loadIndexData((char*) base_dir, (char*) index_path, (char*) text_path);
+
+  /* The C text strings are no longer needed */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+  (*j_env)->ReleaseStringUTFChars(j_env, j_text_path, text_path);
+
+  /* Check that the index was loaded successfully */
+  if (qd==NULL) {
+    return NULL;
+  }
+  /*assert(qd != NULL);*/
+
+  /* Get the document position and length in the text file */
+  printf("Fetching document number %ld...\n", (unsigned long) j_docnum);
+  FetchDocStart(qd, (unsigned long) j_docnum, &pos, &len);
+  printf("Fetched document start. Pos: %ld, Len: %ld\n", pos, len);
+
+  /* Allocate memory for the document text (from mg/src/text/mgquery.c:RawDocOutput()) */
+  c_buffer = (u_char*) malloc(len);
+  assert(c_buffer != NULL);
+  uc_buffer = (u_char*) malloc((int) (qd->td->cth.ratio * 1.01 * len) + 100);
+  assert(uc_buffer != NULL);
+
+  /* Seek to the correct position in the file and read the document text */
+  Fseek (qd->td->TextFile, pos, 0);
+  Fread (c_buffer, 1, len, qd->td->TextFile);
+
+  /* Decompress the document text into another buffer, and terminate it */
+  DecodeText (qd->cd, c_buffer, len, uc_buffer, &ULen);
+  uc_buffer[ULen] = '\0';
+
+  /* Load the document text into a Java string */
+  result = (*j_env)->NewStringUTF(j_env, uc_buffer);
+  assert(result != NULL);
+
+  /* Free C buffers */
+  free(c_buffer);
+  free(uc_buffer);
+
+  /* Return the document text */
+  return result;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/jni/MGSearchWrapperImpl.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/MGSearchWrapperImpl.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/MGSearchWrapperImpl.c	(revision 16561)
@@ -0,0 +1,631 @@
+/*
+ *    MGSearchWrapperImpl.c
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include "MGWrapperImpl.h"
+#include <jni.h>
+#include "org_greenstone_mg_MGSearchWrapper.h"
+
+#include "backend.h"
+#include "environment.h"
+#include "text_get.h"
+#include "stemmer.h"
+
+
+#define MAX_INDEXES_CACHED 3
+
+/* copied from mgquery, needed to convert paragraph numbers to document numbers
+   for greenstone */
+#if defined(PARADOCNUM) ||  defined(NZDL)
+static int GetDocNumFromParaNum(query_data *qd, int paranum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+  int low = 1, high = Documents;
+  int mid = (low+high)/2;
+
+  while ((mid = (low+high)/2) >=1 && mid <= Documents)
+    {
+      if (paranum > Paragraph[mid])
+        low = mid+1;
+      else if (paranum <= Paragraph[mid-1])
+        high = mid-1;
+      else
+        return mid;
+    }
+  FatalError(1, "Bad paragraph number.\n");
+  return 0;
+}
+#endif
+
+
+/*********************************************
+   initialisation stuff
+ *********************************************/
+
+/* cached ids for java stuff */
+jfieldID FID_mg_data = NULL; /* MGWrapperData */
+jfieldID FID_query_result = NULL; /* MGQueryResult */
+jmethodID MID_addDoc = NULL; /* MGQueryResult.addDoc() */
+jmethodID MID_addTerm = NULL; /* MGQueryResult.addTerm() */
+jmethodID MID_addEquivTerm = NULL; /* MGQueryResult.addEquivTerm() */
+jmethodID MID_setTotalDocs = NULL; /* MGQueryResult.setTotalDocs() */
+jmethodID MID_clearResult = NULL; /* MGQueryResult.clear() */
+
+
+/* to access objects and methods on java side, need their field/method ids - 
+ this initialises them at the start to avoid recalculating them each time they
+ are needed
+Note: the descriptors need to be exactly right, otherwise you get an error 
+saying "no such field" but no reference to the fact that it has the right 
+name but the wrong type.
+Note: apparently the jclass is a local ref and should only work 
+in the method that created it. It seems to work ok, but I'll make it
+global cos the book said I should, and it may avoid future hassles.
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_initIDs(JNIEnv *j_env, jclass j_cls)
+{
+  jclass JC_MGQueryResult;
+
+  /* a long-"J" */
+  FID_mg_data = (*j_env)->GetFieldID(j_env, j_cls, "mg_data_ptr_", "J");
+  assert(FID_mg_data != NULL);
+
+  /* an object -"L<class name>;" */
+  FID_query_result = (*j_env)->GetFieldID(j_env, j_cls, "mg_query_result_",
+					  "Lorg/greenstone/mg/MGQueryResult;");
+  assert(FID_query_result != NULL);
+
+  /* the methods we want to use */
+  JC_MGQueryResult = (*j_env)->FindClass(j_env, "org/greenstone/mg/MGQueryResult");
+
+  /* addDoc(long doc, float rank) */
+  MID_addDoc = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addDoc", "(JF)V");
+  assert(MID_addDoc != NULL);
+
+  /* addTerm(String term, int stem) */
+  MID_addTerm = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addTerm", "(Ljava/lang/String;I)V");
+  assert(MID_addTerm != NULL);
+
+  /* addEquivTerm(String term, String equivTerm, long match, long freq) */
+  MID_addEquivTerm = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addEquivTerm", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
+  assert(MID_addEquivTerm != NULL);
+
+  /* setTotalDocs(long) */
+  MID_setTotalDocs = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "setTotalDocs", "(J)V");
+  assert(MID_setTotalDocs != NULL);
+
+  /* clear(void) */
+  MID_clearResult = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "clear", "()V");
+  assert(MID_clearResult != NULL);
+}
+
+
+/* the java side MGSearchWrapper has a pointer to a C object - MGWrapperData
+   initialise this and set the pointer 
+*/
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_initCSide(JNIEnv *j_env, jobject j_obj)
+{
+  /* Allocate a MGWrapperData object to store query parameters */
+  MGWrapperData* data = (MGWrapperData*) malloc(sizeof(MGWrapperData));
+  assert(data != NULL);
+
+  /* Set default values - no stemming, no case-folding, boolean OR queries */
+  data->defaultStemMethod = 0;
+  data->defaultBoolCombine = 0;
+
+  /* Allocate a QueryInfo object to store more query parameters */
+  data->queryInfo = (QueryInfo*) malloc(sizeof(QueryInfo));
+  assert(data->queryInfo != NULL);
+
+  /* Set default values - 50 documents max, return term freqs, sort by rank */
+  data->queryInfo->index = NULL;
+  data->queryInfo->maxDocs = 50;
+  data->queryInfo->needTermFreqs = 1;
+
+  /* Save the object on the Java side */
+  (*j_env)->SetIntField(j_env, j_obj, FID_mg_data, (long) data);
+
+  /* Initialise MG environment variables */
+  InitEnv();
+  SetEnv("expert", "true", NULL);
+  SetEnv("mode", "docnums", NULL);
+  
+  return 1;  /* true - no errors */
+}
+
+
+/*******************************************
+   Index caching
+ *******************************************/
+
+query_data* cached_indexes[MAX_INDEXES_CACHED] = { NULL };
+
+
+/* Get the index data necessary to perform a query or document retrieval */
+query_data*
+loadIndexData(char* base_dir, char* index_path, char* text_path)
+{
+  char* index_path_name;
+  char* text_path_name;
+  query_data* qd;
+  int i = 0;
+
+  /* Form the path name of the desired indexes */
+  index_path_name = (char*) malloc(strlen(base_dir) + strlen(index_path) + 1);
+  assert(index_path_name != NULL);
+  strcpy(index_path_name, base_dir);
+  strcat(index_path_name, index_path);
+  printf("Index pathname: %s\n", index_path_name);
+
+  text_path_name = (char*) malloc(strlen(base_dir) + strlen(text_path) + 1);
+  assert(text_path_name != NULL);
+  strcpy(text_path_name, base_dir);
+  strcat(text_path_name, text_path);
+  printf("Text pathname: %s\n", text_path_name);
+
+  /* Search through the cached indexes for the desired one */
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    printf("(Cached) Pathname: %s\n", cached_indexes[i]->pathname);
+    printf("(Cached) Textpathname: %s\n", cached_indexes[i]->textpathname);
+
+    /* Check if the index has already been loaded */
+    if ((strcmp(index_path_name, cached_indexes[i]->pathname) == 0) &&
+	(strcmp(text_path_name, cached_indexes[i]->textpathname) == 0)) {
+      /* Index has already been loaded and cached, so return it */
+      printf("Found index!\n");
+      free(index_path_name);
+      free(text_path_name);
+      return cached_indexes[i];
+    }
+
+    i++;
+  }
+
+  /* Text strings no longer needed */
+  free(index_path_name);
+  free(text_path_name);
+
+  /* The index is not cached, so load it now */
+  qd = InitQuerySystem(base_dir, index_path, text_path, NULL);
+  if (!qd) {
+    printf("Error: Could not InitQuerySystem()...\n");
+    return NULL;
+  }
+
+  /* The index loaded OK, so cache it */
+  /* This could be a little more sophisticated, eg. replace least frequently used index */
+  if (i >= MAX_INDEXES_CACHED)
+    i = MAX_INDEXES_CACHED - 1;
+
+  /* Free the index being replaced */
+  if (cached_indexes[i] != NULL)
+    FinishQuerySystem(cached_indexes[i]);
+
+  /* Cache the loaded index, and return it */
+  cached_indexes[i] = qd;
+  return cached_indexes[i];
+}
+
+
+/* Clean up by unloading all cached indexes */
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_unloadIndexData(JNIEnv* j_env, jobject j_obj)
+{
+  /* Free all the loaded indexes */
+  int i = 0;
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    FinishQuerySystem(cached_indexes[i]);
+    cached_indexes[i] = NULL;
+    i++;
+  }
+
+  return 1;  /* true - no errors */
+}
+
+
+/*******************************************
+   do a query
+ *******************************************/
+
+/* do the actual query - the results are written to query_result held on the Java side */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_runQuery(JNIEnv *j_env, jobject j_obj,
+					  jstring j_base_dir, jstring j_text_path,
+					  jstring j_query)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  char* index_path;
+  const char* base_dir;
+  const char* text_path;
+  query_data* qd;
+
+  jobject result_ptr;
+  char* query;
+  int i, j;
+
+  jthrowable exc;
+  /* First of all, clear the previous result */
+  /* The result to write to */
+  result_ptr = (*j_env)->GetObjectField(j_env, j_obj, FID_query_result);
+  assert(result_ptr != NULL);
+
+  /* Clear any previous query results */
+  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_clearResult);
+  exc = (*j_env)->ExceptionOccurred(j_env);
+  if (exc) {
+    (*j_env)->ExceptionDescribe(j_env);
+    return;
+  }
+
+  /* Make sure an index has been specified */
+  index_path = data->queryInfo->index;
+  if (index_path == NULL) {
+    return;
+  }
+
+  /* Obtain C versions of the two string parameters */
+  base_dir = (*j_env)->GetStringUTFChars(j_env, j_base_dir, NULL);
+  if (base_dir == NULL) {
+    return;
+  }
+  text_path = (*j_env)->GetStringUTFChars(j_env, j_text_path, NULL);
+  if (text_path == NULL) {
+    (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+    return;
+  }
+
+  /* Load the appropriate index for satisfying this request */
+  qd = loadIndexData((char*) base_dir, (char*) index_path, (char*) text_path);
+
+  /* The C text strings are no longer needed */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+  (*j_env)->ReleaseStringUTFChars(j_env, j_text_path, text_path);
+
+  /* Check that the index was loaded successfully */
+  if (qd == NULL) {
+    return;
+  }
+  
+  /* Remove anything hanging around from last time */
+  FreeQueryDocs(qd);
+
+  /* Obtain a C version of the query string */
+  query = (char*) (*j_env)->GetStringUTFChars(j_env, j_query, NULL);
+  if (query == NULL) {
+    return;
+  }
+  printf("Searching for query \"%s\"...\n", query);
+
+  /* Make sure the query isn't empty */
+  if (strlen(query) == 0) {
+    printf("Warning: Empty query.\n");
+    return;
+  }
+
+  /* "Some" queries are done as ranked queries */
+  if (data->defaultBoolCombine == 0) {
+    RankedQueryInfo rqi;
+    rqi.QueryFreqs = 1;  /* Use the frequency of each query term in the query - OK? */
+    rqi.Exact = 1;  /* Perform exact ranking */
+    rqi.MaxDocsToRetrieve = data->queryInfo->maxDocs;  /* Get only the desired number */
+    rqi.MaxParasToRetrieve = rqi.MaxDocsToRetrieve;  /* OK? */
+    /* we may need to get more paragraphs to get enough documents. I copied the following from mgquery. it seems to work, not sure why - kjdon */
+    if (qd->id->ifh.InvfLevel == 3 && GetEnv ("maxparas")) {
+      rqi.MaxParasToRetrieve = atoi (GetEnv ("maxparas"));
+    }
+    
+    rqi.Sort = 1;  /* Sort the query terms by frequency before ranking */
+    rqi.AccumMethod = 'L';  /* Use a list when accumulating (has bugs though...) */
+    /* rqi.MaxAccums = -1; */ /* Use as many accumulators as necessary - CRASHES with list */
+    rqi.MaxAccums = 100000;
+    rqi.MaxTerms = -1;  /* Use all the query terms */
+    /* rqi.StopAtMaxAccum = 0;*/  /* Don't care (using as many accumulators as necessary) */
+    rqi.StopAtMaxAccum = 1;
+    rqi.HashTblSize = 1000;  /* Don't care (not using a hash table) */
+    rqi.skip_dump = NULL;  /* Don't dump skip information */
+
+    /* RankedQuery() reads 'casefold' and 'stem' parameters from the environment */
+    SetEnv("casefold", ((data->defaultStemMethod & 1) ? "on" : "off"), NULL);
+    SetEnv("stem", ((data->defaultStemMethod & 2) ? "on" : "off"), NULL);
+
+    RankedQuery(qd, query, &rqi);
+  }
+  /* "All" queries are done as boolean queries */
+  else {
+    BooleanQueryInfo bqi;
+    bqi.MaxDocsToRetrieve = data->queryInfo->maxDocs;
+
+    /* Had to add "words$o" to LIB_OBJS in mg/src/text/Makefile and recompile mg for this */
+    BooleanQuery(qd, query, &bqi, data->defaultStemMethod);
+  }
+
+  /* Finished with the C query string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_query, query);
+
+  /* Check the query was processed successfully */
+  if (qd->DL == NULL || qd->QTL == NULL || qd->TL == NULL) {
+    return;
+  }
+
+  /* Record the total number of matching documents */
+  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_setTotalDocs, (jlong) qd->DL->num);
+  exc = (*j_env)->ExceptionOccurred(j_env);
+  if (exc) {
+    (*j_env)->ExceptionDescribe(j_env);
+    return;
+  }
+
+  /* Record the matching documents, but only the number requested */
+  printf("Number of matching documents: %d\n", qd->DL->num);
+  
+  for (i = 0; (i < qd->DL->num && i < data->queryInfo->maxDocs); i++) {
+    int doc_num = qd->DL->DE[i].DocNum;
+    float doc_weight = qd->DL->DE[i].Weight;
+
+#if defined(PARADOCNUM) || defined(NZDL)
+    if (qd->id->ifh.InvfLevel == 3) {
+      /* pararaph level, need to convert to doc level*/
+      doc_num = GetDocNumFromParaNum(qd, doc_num);
+    }
+#endif
+    
+    
+    /* Call the addDoc function (Java side) to record a matching document */
+    (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addDoc,
+			     (jlong) doc_num, (jfloat) doc_weight);
+    exc = (*j_env)->ExceptionOccurred(j_env);
+    if (exc) {
+      (*j_env)->ExceptionDescribe(j_env);
+      return;
+    }
+  }
+
+  /* Record the term information, if desired */
+  if (data->queryInfo->needTermFreqs) {
+    /* The following code is a lot more complicated than it could be, but it is necessary
+       to compensate for an oddity in MG. */
+    unsigned char** stemmed_terms = malloc(sizeof(unsigned char*) * qd->TL->num);
+
+    printf("Number of terms: %d\n", qd->TL->num);
+    printf("Number of query terms: %d\n", qd->QTL->num);
+
+    /* Generate the stemmed form of each of the relevant terms */
+    for (i = 0; i < qd->TL->num; i++) {
+      u_char* raw_term = qd->TL->TE[i].Word;
+      unsigned int term_length = raw_term[0];
+
+      u_char* raw_stemmed_term = malloc(term_length + 1);
+      unsigned int stemmed_term_length;
+
+      /* Copy the term, and stem it */
+      for (j = 0; j <= term_length; j++)
+	raw_stemmed_term[j] = raw_term[j];
+      stemmer(data->defaultStemMethod, qd->sd->sdh.stemmer_num, raw_stemmed_term);
+
+      /* Allocate memory to store the stemmed term, and fill it */
+      stemmed_term_length = raw_stemmed_term[0];
+      stemmed_terms[i] = malloc(stemmed_term_length + 1);
+      assert(stemmed_terms[i] != NULL);
+      strncpy(stemmed_terms[i], &(raw_stemmed_term[1]), stemmed_term_length);
+      stemmed_terms[i][stemmed_term_length] = '\0';
+    }
+
+    /* Record every query term, along with their equivalent terms */
+    for (i = 0; i < qd->QTL->num; i++) {
+      u_char* raw_query_term = qd->QTL->QTE[i].Term;
+      unsigned int query_term_length = raw_query_term[0];
+      unsigned char* query_term;
+      jstring j_query_term;
+
+      u_char* raw_stemmed_query_term = malloc(query_term_length + 1);
+      unsigned int stemmed_query_term_length;
+      unsigned char* stemmed_query_term;
+
+      /* Allocate memory to store the query term, and fill it */
+      query_term = malloc(query_term_length + 1);
+      assert(query_term != NULL);
+      strncpy(query_term, &(raw_query_term[1]), query_term_length);
+      query_term[query_term_length] = '\0';
+
+      /* Allocate a new jstring for the query term */
+      j_query_term = (*j_env)->NewStringUTF(j_env, query_term);
+      assert(j_query_term != NULL);
+
+      /* Call the addTerm function (Java side) to record the query term */
+      (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addTerm,
+			       j_query_term, (jint) data->defaultStemMethod);
+      exc = (*j_env)->ExceptionOccurred(j_env);
+      if (exc) {
+	(*j_env)->ExceptionDescribe(j_env);
+	return;
+      }
+
+      /* Copy the query term, and stem it */
+      for (j = 0; j <= query_term_length; j++)
+	raw_stemmed_query_term[j] = raw_query_term[j];
+      stemmer(data->defaultStemMethod, qd->sd->sdh.stemmer_num, raw_stemmed_query_term);
+
+      /* Allocate memory to store the stemmed query term, and fill it */
+      stemmed_query_term_length = raw_stemmed_query_term[0];
+      stemmed_query_term = malloc(stemmed_query_term_length + 1);
+      assert(stemmed_query_term != NULL);
+      strncpy(stemmed_query_term, &(raw_stemmed_query_term[1]), stemmed_query_term_length);
+      stemmed_query_term[stemmed_query_term_length] = '\0';
+
+      /* Find all the terms equivalent to the query term */
+      for (j = 0; j < qd->TL->num; j++) {
+	/* Check if the stemmed query term matches the stemmed term */
+	if (strcmp(stemmed_query_term, stemmed_terms[j]) == 0) {
+	  u_char* raw_term = qd->TL->TE[j].Word;
+	  unsigned int term_length = raw_term[0];
+	  unsigned char* term;
+	  jstring j_term;
+
+	  /* Allocate memory to store the query term, and fill it */
+	  term = malloc(term_length + 1);
+	  assert(term != NULL);
+	  strncpy(term, &(raw_term[1]), term_length);
+	  term[term_length] = '\0';
+
+	  /* Allocate a new jstring for the query term */
+	  j_term = (*j_env)->NewStringUTF(j_env, term);
+	  assert(j_term != NULL);
+
+	  /* Call the addEquivTerm function (Java side) to record the equivalent term */
+	  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addEquivTerm,
+				   j_query_term, j_term,
+				   (jlong) qd->TL->TE[j].WE.doc_count,
+				   (jlong) qd->TL->TE[j].WE.count);
+	  exc = (*j_env)->ExceptionOccurred(j_env);
+	  if (exc) {
+	    (*j_env)->ExceptionDescribe(j_env);
+	    return;
+	  }
+	}
+      }
+    }
+  }
+}
+
+
+/*******************************************
+   set query options
+ *******************************************/
+
+/* Turn casefolding on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_setCase(JNIEnv *j_env, jobject j_obj, 
+					 jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  if (j_on) {
+    data->defaultStemMethod |= 1;
+  } else {
+    data->defaultStemMethod &= 0xe;
+  }
+}
+
+
+/* Turn stemming on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_setStem(JNIEnv *j_env, jobject j_obj, 
+					 jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  if (j_on) {
+    data->defaultStemMethod |= 2;
+  } else {
+    data->defaultStemMethod &= 0xd;
+  }
+}
+
+
+/* Set the maximum number of documents to return from a query */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_setMaxDocs(JNIEnv *j_env, jobject j_obj, 
+					    jint j_max)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->queryInfo->maxDocs = j_max;
+}
+
+/* set the maximum number of numeric to split*/
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mg_MGSearchWrapper_setMaxNumeric (JNIEnv *j_env, 
+						 jobject j_obj, 
+						 jint j_max) {
+
+  char text[20];
+  char* maxnumeric;
+  sprintf(text,"%d",j_max); 
+  maxnumeric = text;  
+  SetEnv("maxnumeric",maxnumeric, NULL);
+}
+
+
+/* Turn term frequency recording on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_setReturnTerms(JNIEnv *j_env, jobject j_obj, 
+						jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->queryInfo->needTermFreqs = j_on;
+}
+
+
+/* Choose MG index to search */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_setIndex(JNIEnv *j_env, jobject j_obj,
+					  jstring j_index)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  /* Get the index name as a C string */
+  const char* index = (*j_env)->GetStringUTFChars(j_env, j_index, NULL);
+  assert(index != NULL);
+  printf("Choosing index %s...\n", index);
+
+  /* Free the previous index name */
+  if (data->queryInfo->index)
+    free(data->queryInfo->index);
+
+  /* Allocate memory for the index name, and fill it */
+  data->queryInfo->index = (char*) malloc(strlen(index) + 1);
+  assert(data->queryInfo->index != NULL);
+  strcpy(data->queryInfo->index, index);
+
+  /* Release the index string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_index, index);
+}
+
+
+/* Choose boolean AND or boolean OR queries */
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mg_MGSearchWrapper_setMatchMode(JNIEnv *j_env, jobject j_obj,
+					      jint j_mode)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->defaultBoolCombine = j_mode;
+}
+
+
+/* Get a text representation of the current parameter values */
+JNIEXPORT jstring JNICALL
+Java_org_greenstone_mg_MGSearchWrapper_getQueryParams(JNIEnv *j_env, jobject j_obj)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  char result[512];  /* Assume this is big enough */
+
+  /* Print the data to a character array */
+  sprintf(result, "Query params:\nindex\t\t%s\ncasefold\t%d\nstem\t\t%d\nquery type\t%s\nmax docs\t%ld\n",
+	  (data->queryInfo->index == NULL ? "<none loaded>" : data->queryInfo->index),
+	  (data->defaultStemMethod & 1),
+	  (data->defaultStemMethod & 2),
+	  (data->defaultBoolCombine == 1 ? "all" : "some"),
+	  (data->queryInfo->maxDocs));
+
+  /* Convert to a jstring, and return */
+  return (*j_env)->NewStringUTF(j_env, result);
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.c	(revision 16561)
@@ -0,0 +1,741 @@
+/*
+ *    MGWrapperImpl.c
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include "MGWrapperImpl.h"
+#include <jni.h>
+#include "org_greenstone_mg_MGWrapper.h"
+
+#include "backend.h"
+#include "environment.h"
+#include "text_get.h"
+#include "stemmer.h"
+
+/*************************************************************************
+   NOTES
+
+ - Features supported by MGPP but not by MG (AFAIK)
+
+   - Sorting results by rank.
+     Done here as a post-processing operation. Could be more efficient in
+     some cases: the current solution is not very good when the number of
+     matching documents is large and the number of desired matches is
+     small. In this case it would be better to iterate through the array
+     picking out the best documents rather than sorting them all.
+
+   - Asking for query term frequencies to be returned.
+     This cannot be turned off in MG. If the term frequencies are not
+     required, they are simply not passed back to the Java side.
+
+   - Choosing the index for queries.
+     It is possible for MG to build Section and Document indexes (for
+     example), but these are two separate indexes, and must be loaded
+     separately. This module can load more than one index at a time, thus
+     processing queries to different indexes more quickly.
+     NOTE: This replaces TWO options in the MGPP version: returnLevel and
+           queryLevel.
+
+ *************************************************************************/
+
+
+#define MAX_INDEXES_CACHED 3
+
+/* copied from mgquery, needed to convert paragraph numbers to document numbers
+   for greenstone */
+#if defined(PARADOCNUM) ||  defined(NZDL)
+static int GetDocNumFromParaNum(query_data *qd, int paranum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+  int low = 1, high = Documents;
+  int mid = (low+high)/2;
+
+  while ((mid = (low+high)/2) >=1 && mid <= Documents)
+    {
+      if (paranum > Paragraph[mid])
+        low = mid+1;
+      else if (paranum <= Paragraph[mid-1])
+        high = mid-1;
+      else
+        return mid;
+    }
+  FatalError(1, "Bad paragraph number.\n");
+  return 0;
+}
+#endif
+
+
+/*********************************************
+   initialisation stuff
+ *********************************************/
+
+/* cached ids for java stuff */
+jfieldID FID_mg_data = NULL; /* MGWrapperData */
+jfieldID FID_query_result = NULL; /* MGQueryResult */
+jmethodID MID_addDoc = NULL; /* MGQueryResult.addDoc() */
+jmethodID MID_addTerm = NULL; /* MGQueryResult.addTerm() */
+jmethodID MID_addEquivTerm = NULL; /* MGQueryResult.addEquivTerm() */
+jmethodID MID_setTotalDocs = NULL; /* MGQueryResult.setTotalDocs() */
+jmethodID MID_clearResult = NULL; /* MGQueryResult.clear() */
+
+
+/* to access objects and methods on java side, need their field/method ids - 
+ this initialises them at the start to avoid recalculating them each time they
+ are needed
+Note: the descriptors need to be exactly right, otherwise you get an error 
+saying "no such field" but no reference to the fact that it has the right 
+name but the wrong type.
+Note: apparently the jclass is a local ref and should only work 
+in the method that created it. It seems to work ok, but I'll make it
+global cos the book said I should, and it may avoid future hassles.
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_initIDs(JNIEnv *j_env, jclass j_cls)
+{
+  jclass JC_MGQueryResult;
+
+  /* a long-"J" */
+  FID_mg_data = (*j_env)->GetFieldID(j_env, j_cls, "mg_data_ptr_", "J");
+  assert(FID_mg_data != NULL);
+
+  /* an object -"L<class name>;" */
+  FID_query_result = (*j_env)->GetFieldID(j_env, j_cls, "mg_query_result_",
+					  "Lorg/greenstone/mg/MGQueryResult;");
+  assert(FID_query_result != NULL);
+
+  /* the methods we want to use */
+  JC_MGQueryResult = (*j_env)->FindClass(j_env, "org/greenstone/mg/MGQueryResult");
+
+  /* addDoc(long doc, float rank) */
+  MID_addDoc = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addDoc", "(JF)V");
+  assert(MID_addDoc != NULL);
+
+  /* addTerm(String term, int stem) */
+  MID_addTerm = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addTerm", "(Ljava/lang/String;I)V");
+  assert(MID_addTerm != NULL);
+
+  /* addEquivTerm(String term, String equivTerm, long match, long freq) */
+  MID_addEquivTerm = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "addEquivTerm", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
+  assert(MID_addEquivTerm != NULL);
+
+  /* setTotalDocs(long) */
+  MID_setTotalDocs = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "setTotalDocs", "(J)V");
+  assert(MID_setTotalDocs != NULL);
+
+  /* clear(void) */
+  MID_clearResult = (*j_env)->GetMethodID(j_env, JC_MGQueryResult, "clear", "()V");
+  assert(MID_clearResult != NULL);
+}
+
+
+/* the java side MGWrapper has a pointer to a C object - MGWrapperData
+   initialise this and set the pointer 
+*/
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mg_MGWrapper_initCSide(JNIEnv *j_env, jobject j_obj)
+{
+  /* Allocate a MGWrapperData object to store query parameters */
+  MGWrapperData* data = (MGWrapperData*) malloc(sizeof(MGWrapperData));
+  assert(data != NULL);
+
+  /* Set default values - no stemming, no case-folding, boolean OR queries */
+  data->defaultStemMethod = 0;
+  data->defaultBoolCombine = 0;
+
+  /* Allocate a QueryInfo object to store more query parameters */
+  data->queryInfo = (QueryInfo*) malloc(sizeof(QueryInfo));
+  assert(data->queryInfo != NULL);
+
+  /* Set default values - 50 documents max, return term freqs, sort by rank */
+  data->queryInfo->index = NULL;
+  data->queryInfo->maxDocs = 50;
+  data->queryInfo->needTermFreqs = 1;
+
+  /* Save the object on the Java side */
+  (*j_env)->SetIntField(j_env, j_obj, FID_mg_data, (long) data);
+
+  /* Initialise MG environment variables */
+  InitEnv();
+  SetEnv("expert", "true", NULL);
+  SetEnv("mode", "docnums", NULL);
+  
+  return 1;  /* true - no errors */
+}
+
+
+/*******************************************
+   Index caching
+ *******************************************/
+
+query_data* cached_indexes[MAX_INDEXES_CACHED] = { NULL };
+
+
+/* Get the index data necessary to perform a query or document retrieval */
+query_data*
+loadIndexData(char* base_dir, char* index_path, char* text_path)
+{
+  char* index_path_name;
+  char* text_path_name;
+  query_data* qd;
+  int i = 0;
+
+  /* Form the path name of the desired indexes */
+  index_path_name = (char*) malloc(strlen(base_dir) + strlen(index_path) + 1);
+  assert(index_path_name != NULL);
+  strcpy(index_path_name, base_dir);
+  strcat(index_path_name, index_path);
+  printf("Index pathname: %s\n", index_path_name);
+
+  text_path_name = (char*) malloc(strlen(base_dir) + strlen(text_path) + 1);
+  assert(text_path_name != NULL);
+  strcpy(text_path_name, base_dir);
+  strcat(text_path_name, text_path);
+  printf("Text pathname: %s\n", text_path_name);
+
+  /* Search through the cached indexes for the desired one */
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    printf("(Cached) Pathname: %s\n", cached_indexes[i]->pathname);
+    printf("(Cached) Textpathname: %s\n", cached_indexes[i]->textpathname);
+
+    /* Check if the index has already been loaded */
+    if ((strcmp(index_path_name, cached_indexes[i]->pathname) == 0) &&
+	(strcmp(text_path_name, cached_indexes[i]->textpathname) == 0)) {
+      /* Index has already been loaded and cached, so return it */
+      printf("Found index!\n");
+      free(index_path_name);
+      free(text_path_name);
+      return cached_indexes[i];
+    }
+
+    i++;
+  }
+
+  /* Text strings no longer needed */
+  free(index_path_name);
+  free(text_path_name);
+
+  /* The index is not cached, so load it now */
+  qd = InitQuerySystem(base_dir, index_path, text_path, NULL);
+  if (!qd) {
+    printf("Error: Could not InitQuerySystem()...\n");
+    return NULL;
+  }
+
+  /* The index loaded OK, so cache it */
+  /* This could be a little more sophisticated, eg. replace least frequently used index */
+  if (i >= MAX_INDEXES_CACHED)
+    i = MAX_INDEXES_CACHED - 1;
+
+  /* Free the index being replaced */
+  if (cached_indexes[i] != NULL)
+    FinishQuerySystem(cached_indexes[i]);
+
+  /* Cache the loaded index, and return it */
+  cached_indexes[i] = qd;
+  return cached_indexes[i];
+}
+
+
+/* Clean up by unloading all cached indexes */
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mg_MGWrapper_unloadIndexData(JNIEnv* j_env, jobject j_obj)
+{
+  /* Free all the loaded indexes */
+  int i = 0;
+  while (i < MAX_INDEXES_CACHED && cached_indexes[i] != NULL) {
+    FinishQuerySystem(cached_indexes[i]);
+    cached_indexes[i] = NULL;
+    i++;
+  }
+
+  return 1;  /* true - no errors */
+}
+
+
+/****************************************************
+   retrieve a document
+ ****************************************************/
+
+/* Returns a document from mg as a string */
+JNIEXPORT jstring JNICALL
+Java_org_greenstone_mg_MGWrapper_getDocument(JNIEnv *j_env, jobject j_obj,
+					     jstring j_base_dir, jstring j_text_path,
+					     jlong j_docnum)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  char* index_path;
+  const char* base_dir;
+  const char* text_path;
+  query_data* qd;
+
+  u_long pos, len;
+  u_char* c_buffer = NULL;
+  u_char* uc_buffer = NULL;
+  int ULen;
+
+  jstring result;
+
+  /* Make sure an index has been specified */
+  index_path = data->queryInfo->index;
+  assert(index_path != NULL);
+
+  /* Obtain C versions of the two string parameters */
+  base_dir = (*j_env)->GetStringUTFChars(j_env, j_base_dir, NULL);
+  if (base_dir == NULL) {
+    return NULL;
+  }
+  text_path = (*j_env)->GetStringUTFChars(j_env, j_text_path, NULL);
+  if (text_path == NULL) {
+    (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+    return NULL;
+  }
+
+  /* Load the appropriate index for satisfying this request */
+  printf("Document retrieval, index path: %s\n", index_path);
+  qd = loadIndexData((char*) base_dir, (char*) index_path, (char*) text_path);
+
+  /* The C text strings are no longer needed */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+  (*j_env)->ReleaseStringUTFChars(j_env, j_text_path, text_path);
+
+  /* Check that the index was loaded successfully */
+  if (qd==NULL) {
+    return NULL;
+  }
+  /*assert(qd != NULL);*/
+
+  /* Get the document position and length in the text file */
+  printf("Fetching document number %ld...\n", (unsigned long) j_docnum);
+  FetchDocStart(qd, (unsigned long) j_docnum, &pos, &len);
+  printf("Fetched document start. Pos: %ld, Len: %ld\n", pos, len);
+
+  /* Allocate memory for the document text (from mg/src/text/mgquery.c:RawDocOutput()) */
+  c_buffer = (u_char*) malloc(len);
+  assert(c_buffer != NULL);
+  uc_buffer = (u_char*) malloc((int) (qd->td->cth.ratio * 1.01 * len) + 100);
+  assert(uc_buffer != NULL);
+
+  /* Seek to the correct position in the file and read the document text */
+  Fseek (qd->td->TextFile, pos, 0);
+  Fread (c_buffer, 1, len, qd->td->TextFile);
+
+  /* Decompress the document text into another buffer, and terminate it */
+  DecodeText (qd->cd, c_buffer, len, uc_buffer, &ULen);
+  uc_buffer[ULen] = '\0';
+
+  /* Load the document text into a Java string */
+  result = (*j_env)->NewStringUTF(j_env, uc_buffer);
+  assert(result != NULL);
+
+  /* Free C buffers */
+  free(c_buffer);
+  free(uc_buffer);
+
+  /* Return the document text */
+  return result;
+}
+
+
+/*******************************************
+   do a query
+ *******************************************/
+
+/* do the actual query - the results are written to query_result held on the Java side */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_runQuery(JNIEnv *j_env, jobject j_obj,
+					  jstring j_base_dir, jstring j_text_path,
+					  jstring j_query)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  char* index_path;
+  const char* base_dir;
+  const char* text_path;
+  query_data* qd;
+
+  jobject result_ptr;
+  char* query;
+  int i, j;
+
+  jthrowable exc;
+  /* First of all, clear the previous result */
+  /* The result to write to */
+  result_ptr = (*j_env)->GetObjectField(j_env, j_obj, FID_query_result);
+  assert(result_ptr != NULL);
+
+  /* Clear any previous query results */
+  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_clearResult);
+  exc = (*j_env)->ExceptionOccurred(j_env);
+  if (exc) {
+    (*j_env)->ExceptionDescribe(j_env);
+    return;
+  }
+
+  /* Make sure an index has been specified */
+  index_path = data->queryInfo->index;
+  if (index_path == NULL) {
+    return;
+  }
+
+  /* Obtain C versions of the two string parameters */
+  base_dir = (*j_env)->GetStringUTFChars(j_env, j_base_dir, NULL);
+  if (base_dir == NULL) {
+    return;
+  }
+  text_path = (*j_env)->GetStringUTFChars(j_env, j_text_path, NULL);
+  if (text_path == NULL) {
+    (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+    return;
+  }
+
+  /* Load the appropriate index for satisfying this request */
+  qd = loadIndexData((char*) base_dir, (char*) index_path, (char*) text_path);
+
+  /* The C text strings are no longer needed */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_base_dir, base_dir);
+  (*j_env)->ReleaseStringUTFChars(j_env, j_text_path, text_path);
+
+  /* Check that the index was loaded successfully */
+  if (qd == NULL) {
+    return;
+  }
+  
+  /* Remove anything hanging around from last time */
+  FreeQueryDocs(qd);
+
+  /* Obtain a C version of the query string */
+  query = (char*) (*j_env)->GetStringUTFChars(j_env, j_query, NULL);
+  if (query == NULL) {
+    return;
+  }
+  printf("Searching for query \"%s\"...\n", query);
+
+  /* Make sure the query isn't empty */
+  if (strlen(query) == 0) {
+    printf("Warning: Empty query.\n");
+    return;
+  }
+
+  /* "Some" queries are done as ranked queries */
+  if (data->defaultBoolCombine == 0) {
+    RankedQueryInfo rqi;
+    rqi.QueryFreqs = 1;  /* Use the frequency of each query term in the query - OK? */
+    rqi.Exact = 1;  /* Perform exact ranking */
+    rqi.MaxDocsToRetrieve = data->queryInfo->maxDocs;  /* Get only the desired number */
+    rqi.MaxParasToRetrieve = rqi.MaxDocsToRetrieve;  /* OK? */
+    /* we may need to get more paragraphs to get enough documents. I copied the following from mgquery. it seems to work, not sure why - kjdon */
+    if (qd->id->ifh.InvfLevel == 3 && GetEnv ("maxparas")) {
+      rqi.MaxParasToRetrieve = atoi (GetEnv ("maxparas"));
+    }
+    
+    rqi.Sort = 1;  /* Sort the query terms by frequency before ranking */
+    rqi.AccumMethod = 'L';  /* Use a list when accumulating (has bugs though...) */
+    /* rqi.MaxAccums = -1; */ /* Use as many accumulators as necessary - CRASHES with list */
+    rqi.MaxAccums = 100000;
+    rqi.MaxTerms = -1;  /* Use all the query terms */
+    /* rqi.StopAtMaxAccum = 0;*/  /* Don't care (using as many accumulators as necessary) */
+    rqi.StopAtMaxAccum = 1;
+    rqi.HashTblSize = 1000;  /* Don't care (not using a hash table) */
+    rqi.skip_dump = NULL;  /* Don't dump skip information */
+
+    /* RankedQuery() reads 'casefold' and 'stem' parameters from the environment */
+    SetEnv("casefold", ((data->defaultStemMethod & 1) ? "on" : "off"), NULL);
+    SetEnv("stem", ((data->defaultStemMethod & 2) ? "on" : "off"), NULL);
+
+    RankedQuery(qd, query, &rqi);
+  }
+  /* "All" queries are done as boolean queries */
+  else {
+    BooleanQueryInfo bqi;
+    bqi.MaxDocsToRetrieve = data->queryInfo->maxDocs;
+
+    /* Had to add "words$o" to LIB_OBJS in mg/src/text/Makefile and recompile mg for this */
+    BooleanQuery(qd, query, &bqi, data->defaultStemMethod);
+  }
+
+  /* Finished with the C query string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_query, query);
+
+  /* Check the query was processed successfully */
+  if (qd->DL == NULL || qd->QTL == NULL || qd->TL == NULL) {
+    return;
+  }
+
+  /* Record the total number of matching documents */
+  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_setTotalDocs, (jlong) qd->DL->num);
+  exc = (*j_env)->ExceptionOccurred(j_env);
+  if (exc) {
+    (*j_env)->ExceptionDescribe(j_env);
+    return;
+  }
+
+  /* Record the matching documents, but only the number requested */
+  printf("Number of matching documents: %d\n", qd->DL->num);
+  
+  for (i = 0; (i < qd->DL->num && i < data->queryInfo->maxDocs); i++) {
+    int doc_num = qd->DL->DE[i].DocNum;
+    float doc_weight = qd->DL->DE[i].Weight;
+
+#if defined(PARADOCNUM) || defined(NZDL)
+    if (qd->id->ifh.InvfLevel == 3) {
+      /* pararaph level, need to convert to doc level*/
+      doc_num = GetDocNumFromParaNum(qd, doc_num);
+    }
+#endif
+    
+    
+    /* Call the addDoc function (Java side) to record a matching document */
+    (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addDoc,
+			     (jlong) doc_num, (jfloat) doc_weight);
+    exc = (*j_env)->ExceptionOccurred(j_env);
+    if (exc) {
+      (*j_env)->ExceptionDescribe(j_env);
+      return;
+    }
+  }
+
+  /* Record the term information, if desired */
+  if (data->queryInfo->needTermFreqs) {
+    /* The following code is a lot more complicated than it could be, but it is necessary
+       to compensate for an oddity in MG. */
+    unsigned char** stemmed_terms = malloc(sizeof(unsigned char*) * qd->TL->num);
+
+    printf("Number of terms: %d\n", qd->TL->num);
+    printf("Number of query terms: %d\n", qd->QTL->num);
+
+    /* Generate the stemmed form of each of the relevant terms */
+    for (i = 0; i < qd->TL->num; i++) {
+      u_char* raw_term = qd->TL->TE[i].Word;
+      unsigned int term_length = raw_term[0];
+
+      u_char* raw_stemmed_term = malloc(term_length + 1);
+      unsigned int stemmed_term_length;
+
+      /* Copy the term, and stem it */
+      for (j = 0; j <= term_length; j++)
+	raw_stemmed_term[j] = raw_term[j];
+      stemmer(data->defaultStemMethod, qd->sd->sdh.stemmer_num, raw_stemmed_term);
+
+      /* Allocate memory to store the stemmed term, and fill it */
+      stemmed_term_length = raw_stemmed_term[0];
+      stemmed_terms[i] = malloc(stemmed_term_length + 1);
+      assert(stemmed_terms[i] != NULL);
+      strncpy(stemmed_terms[i], &(raw_stemmed_term[1]), stemmed_term_length);
+      stemmed_terms[i][stemmed_term_length] = '\0';
+    }
+
+    /* Record every query term, along with their equivalent terms */
+    for (i = 0; i < qd->QTL->num; i++) {
+      u_char* raw_query_term = qd->QTL->QTE[i].Term;
+      unsigned int query_term_length = raw_query_term[0];
+      unsigned char* query_term;
+      jstring j_query_term;
+
+      u_char* raw_stemmed_query_term = malloc(query_term_length + 1);
+      unsigned int stemmed_query_term_length;
+      unsigned char* stemmed_query_term;
+
+      /* Allocate memory to store the query term, and fill it */
+      query_term = malloc(query_term_length + 1);
+      assert(query_term != NULL);
+      strncpy(query_term, &(raw_query_term[1]), query_term_length);
+      query_term[query_term_length] = '\0';
+
+      /* Allocate a new jstring for the query term */
+      j_query_term = (*j_env)->NewStringUTF(j_env, query_term);
+      assert(j_query_term != NULL);
+
+      /* Call the addTerm function (Java side) to record the query term */
+      (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addTerm,
+			       j_query_term, (jint) data->defaultStemMethod);
+      exc = (*j_env)->ExceptionOccurred(j_env);
+      if (exc) {
+	(*j_env)->ExceptionDescribe(j_env);
+	return;
+      }
+
+      /* Copy the query term, and stem it */
+      for (j = 0; j <= query_term_length; j++)
+	raw_stemmed_query_term[j] = raw_query_term[j];
+      stemmer(data->defaultStemMethod, qd->sd->sdh.stemmer_num, raw_stemmed_query_term);
+
+      /* Allocate memory to store the stemmed query term, and fill it */
+      stemmed_query_term_length = raw_stemmed_query_term[0];
+      stemmed_query_term = malloc(stemmed_query_term_length + 1);
+      assert(stemmed_query_term != NULL);
+      strncpy(stemmed_query_term, &(raw_stemmed_query_term[1]), stemmed_query_term_length);
+      stemmed_query_term[stemmed_query_term_length] = '\0';
+
+      /* Find all the terms equivalent to the query term */
+      for (j = 0; j < qd->TL->num; j++) {
+	/* Check if the stemmed query term matches the stemmed term */
+	if (strcmp(stemmed_query_term, stemmed_terms[j]) == 0) {
+	  u_char* raw_term = qd->TL->TE[j].Word;
+	  unsigned int term_length = raw_term[0];
+	  unsigned char* term;
+	  jstring j_term;
+
+	  /* Allocate memory to store the query term, and fill it */
+	  term = malloc(term_length + 1);
+	  assert(term != NULL);
+	  strncpy(term, &(raw_term[1]), term_length);
+	  term[term_length] = '\0';
+
+	  /* Allocate a new jstring for the query term */
+	  j_term = (*j_env)->NewStringUTF(j_env, term);
+	  assert(j_term != NULL);
+
+	  /* Call the addEquivTerm function (Java side) to record the equivalent term */
+	  (*j_env)->CallVoidMethod(j_env, result_ptr, MID_addEquivTerm,
+				   j_query_term, j_term,
+				   (jlong) qd->TL->TE[j].WE.doc_count,
+				   (jlong) qd->TL->TE[j].WE.count);
+	  exc = (*j_env)->ExceptionOccurred(j_env);
+	  if (exc) {
+	    (*j_env)->ExceptionDescribe(j_env);
+	    return;
+	  }
+	}
+      }
+    }
+  }
+}
+
+
+/*******************************************
+   set query options
+ *******************************************/
+
+/* Turn casefolding on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_setCase(JNIEnv *j_env, jobject j_obj, 
+					 jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  if (j_on) {
+    data->defaultStemMethod |= 1;
+  } else {
+    data->defaultStemMethod &= 0xe;
+  }
+}
+
+
+/* Turn stemming on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_setStem(JNIEnv *j_env, jobject j_obj, 
+					 jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  if (j_on) {
+    data->defaultStemMethod |= 2;
+  } else {
+    data->defaultStemMethod &= 0xd;
+  }
+}
+
+
+/* Set the maximum number of documents to return from a query */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_setMaxDocs(JNIEnv *j_env, jobject j_obj, 
+					    jint j_max)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->queryInfo->maxDocs = j_max;
+}
+
+/* set the maximum number of numeric to split*/
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mg_MGWrapper_setMaxNumeric (JNIEnv *j_env, 
+						 jobject j_obj, 
+						 jint j_max) {
+
+  char text[20];
+  char* maxnumeric;
+  sprintf(text,"%d",j_max); 
+  maxnumeric = text;  
+  SetEnv("maxnumeric",maxnumeric, NULL);
+}
+
+
+/* Turn term frequency recording on or off */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_setReturnTerms(JNIEnv *j_env, jobject j_obj, 
+						jboolean j_on)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->queryInfo->needTermFreqs = j_on;
+}
+
+
+/* Choose MG index to search */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mg_MGWrapper_setIndex(JNIEnv *j_env, jobject j_obj,
+					  jstring j_index)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+
+  /* Get the index name as a C string */
+  const char* index = (*j_env)->GetStringUTFChars(j_env, j_index, NULL);
+  assert(index != NULL);
+  printf("Choosing index %s...\n", index);
+
+  /* Free the previous index name */
+  if (data->queryInfo->index)
+    free(data->queryInfo->index);
+
+  /* Allocate memory for the index name, and fill it */
+  data->queryInfo->index = (char*) malloc(strlen(index) + 1);
+  assert(data->queryInfo->index != NULL);
+  strcpy(data->queryInfo->index, index);
+
+  /* Release the index string */
+  (*j_env)->ReleaseStringUTFChars(j_env, j_index, index);
+}
+
+
+/* Choose boolean AND or boolean OR queries */
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mg_MGWrapper_setMatchMode(JNIEnv *j_env, jobject j_obj,
+					      jint j_mode)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  data->defaultBoolCombine = j_mode;
+}
+
+
+/* Get a text representation of the current parameter values */
+JNIEXPORT jstring JNICALL
+Java_org_greenstone_mg_MGWrapper_getQueryParams(JNIEnv *j_env, jobject j_obj)
+{
+  MGWrapperData* data = (MGWrapperData*) (*j_env)->GetIntField(j_env, j_obj, FID_mg_data);
+  char result[512];  /* Assume this is big enough */
+
+  /* Print the data to a character array */
+  sprintf(result, "Query params:\nindex\t\t%s\ncasefold\t%d\nstem\t\t%d\nquery type\t%s\nmax docs\t%ld\n",
+	  (data->queryInfo->index == NULL ? "<none loaded>" : data->queryInfo->index),
+	  (data->defaultStemMethod & 1),
+	  (data->defaultStemMethod & 2),
+	  (data->defaultBoolCombine == 1 ? "all" : "some"),
+	  (data->queryInfo->maxDocs));
+
+  /* Convert to a jstring, and return */
+  return (*j_env)->NewStringUTF(j_env, result);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/MGWrapperImpl.h	(revision 16561)
@@ -0,0 +1,52 @@
+/*
+ *    MGWrapperImpl.h
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include "backend.h"
+
+
+/*************************************************************************
+   NOTES
+
+ - These data structures are based on the MGPP ones but are modified
+   to reflect the different capabilities of the MG system.
+
+ *************************************************************************/
+
+ 
+typedef struct QueryInfo
+{
+  /* Name of index to use */
+  char* index;
+  /* Maximum number of documents to retrieve */
+  unsigned long maxDocs;
+  /* Whether term frequency information is desired (boolean value) */
+  int needTermFreqs;
+} QueryInfo;
+
+
+typedef struct MGWrapperData
+{
+  /* Information about a query, see above */
+  QueryInfo* queryInfo;
+  /* Whether to perform stemming and case-folding */
+  int defaultStemMethod;
+  /* Whether to perform boolean AND (1) or boolean OR (0) queries */
+  int defaultBoolCombine;
+} MGWrapperData;
Index: /gsdl/trunk/gs2common/indexers/mg/jni/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/Makefile.in	(revision 16561)
@@ -0,0 +1,68 @@
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+libdir = @libdir@
+subdir = jni
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+AR = ar
+CC = @CC@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -fpic -DSILENT -DSHORT_SUFFIX -DNZDL -DQUIET -DPARADOCNUM
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+#NOTE: libmgtext.a needs to come before libmglib.a !!!
+QUERY_LIBS = ../src/text/libmgtext.a ../lib/libmglib.a @LIBS@
+PASSES_LIBS = ../src/text/libmgpass.a ../lib/libmglib.a @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+
+JNIFLAGS=@JNIFLAGS@
+JNISUFFIX=@JNISUFFIX@
+JNIINC=@JNIINC@
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+
+CCOMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+
+.SUFFIXES:
+
+.SUFFIXES: .c .o
+.c.o:
+	$(CCOMPILE) $<
+
+# can we get rid of JAVA_HOME from here??
+INCLUDES = -I.. -I../lib -I../src/text -I$(JAVA_HOME)/include $(JNIINC)
+
+all: compile link
+
+compile:
+	$(CCOMPILE) MGSearchWrapperImpl.c MGRetrieveWrapperImpl.c MGPassesWrapperImpl.c
+
+link:
+	$(CC) $(LDFLAGS) $(JNIFLAGS) -o libmgretrievejni.$(JNISUFFIX) MGRetrieveWrapperImpl.o $(QUERY_LIBS)
+	$(CC) $(LDFLAGS) $(JNIFLAGS) -o libmgsearchjni.$(JNISUFFIX) MGSearchWrapperImpl.o $(QUERY_LIBS)
+	$(CC) $(LDFLAGS)  $(JNIFLAGS) -o libmgpassjni.$(JNISUFFIX)  MGPassesWrapperImpl.o  $(PASSES_LIBS)
+
+clean:
+	rm -rf *.o libmgretrievejni.$(JNISUFFIX) libmgsearchjni.$(JNISUFFIX) libmgpassjni.$(JNISUFFIX) 
+
+distclean: clean
+	rm -f Makefile
+
+install:
+#	../mkinstalldirs $(libdir); \
+#	cp libmgjni.$(JNISUFFIX) libmgpassjni.$(JNISUFFIX)  $(libdir)/;
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGPassesWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGPassesWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGPassesWrapper.h	(revision 16561)
@@ -0,0 +1,175 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mg_MGPassesWrapper */
+
+#ifndef _Included_org_greenstone_mg_MGPassesWrapper
+#define _Included_org_greenstone_mg_MGPassesWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_1
+#define org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_1 49L
+#undef org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_2
+#define org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_2 50L
+#undef org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_3
+#define org_greenstone_mg_MGPassesWrapper_INVF_LEVEL_3 51L
+#undef org_greenstone_mg_MGPassesWrapper_TEXT_PASS_1
+#define org_greenstone_mg_MGPassesWrapper_TEXT_PASS_1 0L
+#undef org_greenstone_mg_MGPassesWrapper_TEXT_PASS_2
+#define org_greenstone_mg_MGPassesWrapper_TEXT_PASS_2 1L
+#undef org_greenstone_mg_MGPassesWrapper_INDEX_PASS_1
+#define org_greenstone_mg_MGPassesWrapper_INDEX_PASS_1 2L
+#undef org_greenstone_mg_MGPassesWrapper_INDEX_PASS_2
+#define org_greenstone_mg_MGPassesWrapper_INDEX_PASS_2 3L
+#undef org_greenstone_mg_MGPassesWrapper_SPECIAL_PASS
+#define org_greenstone_mg_MGPassesWrapper_SPECIAL_PASS 4L
+#undef org_greenstone_mg_MGPassesWrapper_NO_STEM_OR_CASE
+#define org_greenstone_mg_MGPassesWrapper_NO_STEM_OR_CASE 0L
+#undef org_greenstone_mg_MGPassesWrapper_CASE_ONLY
+#define org_greenstone_mg_MGPassesWrapper_CASE_ONLY 1L
+#undef org_greenstone_mg_MGPassesWrapper_STEM_ONLY
+#define org_greenstone_mg_MGPassesWrapper_STEM_ONLY 2L
+#undef org_greenstone_mg_MGPassesWrapper_STEM_AND_CASE
+#define org_greenstone_mg_MGPassesWrapper_STEM_AND_CASE 3L
+#undef org_greenstone_mg_MGPassesWrapper_END_OF_DOCUMENT
+#define org_greenstone_mg_MGPassesWrapper_END_OF_DOCUMENT 2L
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    init
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGPassesWrapper_init
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setBasePath
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setBasePath
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setFileName
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setFileName
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setStemOptions
+ * Signature: (Ljava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setStemOptions
+  (JNIEnv *, jobject, jstring, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setInvfLevel
+ * Signature: (C)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setInvfLevel
+  (JNIEnv *, jobject, jchar);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setBufferSize
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setBufferSize
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    setInversionMemLimit
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_setInversionMemLimit
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    ignoreSGMLTags
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_ignoreSGMLTags
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    dumpFailedDocument
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_dumpFailedDocument
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    outputCompStats
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_outputCompStats
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    enableTracing
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_enableTracing
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    finish
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGPassesWrapper_finish
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    exitValue
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_greenstone_mg_MGPassesWrapper_exitValue
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGPassesWrapper_initCSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    addPass
+ * Signature: (CC)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGPassesWrapper_addPass
+  (JNIEnv *, jobject, jchar, jchar);
+
+/*
+ * Class:     org_greenstone_mg_MGPassesWrapper
+ * Method:    processMGDocument
+ * Signature: ([B)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGPassesWrapper_processMGDocument
+  (JNIEnv *, jobject, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGRetrieveWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGRetrieveWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGRetrieveWrapper.h	(revision 16561)
@@ -0,0 +1,53 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mg_MGRetrieveWrapper */
+
+#ifndef _Included_org_greenstone_mg_MGRetrieveWrapper
+#define _Included_org_greenstone_mg_MGRetrieveWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_initCSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    getDocument
+ * Signature: (Ljava/lang/String;Ljava/lang/String;J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_getDocument
+  (JNIEnv *, jobject, jstring, jstring, jlong);
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    unloadIndexData
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_unloadIndexData
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGRetrieveWrapper
+ * Method:    setIndex
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGRetrieveWrapper_setIndex
+  (JNIEnv *, jobject, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGSearchWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGSearchWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGSearchWrapper.h	(revision 16561)
@@ -0,0 +1,109 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mg_MGSearchWrapper */
+
+#ifndef _Included_org_greenstone_mg_MGSearchWrapper
+#define _Included_org_greenstone_mg_MGSearchWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGSearchWrapper_initCSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    unloadIndexData
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGSearchWrapper_unloadIndexData
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setCase
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setCase
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setStem
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setStem
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setMaxDocs
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setMaxDocs
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setReturnTerms
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setReturnTerms
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setIndex
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setIndex
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setMatchMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setMatchMode
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    getQueryParams
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mg_MGSearchWrapper_getQueryParams
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    setMaxNumeric
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_setMaxNumeric
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGSearchWrapper
+ * Method:    runQuery
+ * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGSearchWrapper_runQuery
+  (JNIEnv *, jobject, jstring, jstring, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/org_greenstone_mg_MGWrapper.h	(revision 16561)
@@ -0,0 +1,117 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mg_MGWrapper */
+
+#ifndef _Included_org_greenstone_mg_MGWrapper
+#define _Included_org_greenstone_mg_MGWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGWrapper_initCSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    getDocument
+ * Signature: (Ljava/lang/String;Ljava/lang/String;J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mg_MGWrapper_getDocument
+  (JNIEnv *, jobject, jstring, jstring, jlong);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    unloadIndexData
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mg_MGWrapper_unloadIndexData
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setCase
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setCase
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setStem
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setStem
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setMaxDocs
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setMaxDocs
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setReturnTerms
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setReturnTerms
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setIndex
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setIndex
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setMatchMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setMatchMode
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    getQueryParams
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mg_MGWrapper_getQueryParams
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    setMaxNumeric
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_setMaxNumeric
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mg_MGWrapper
+ * Method:    runQuery
+ * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mg_MGWrapper_runQuery
+  (JNIEnv *, jobject, jstring, jstring, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/jni/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/jni/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/jni/win32.mak	(revision 16561)
@@ -0,0 +1,88 @@
+###########################################################################
+#
+# win32 makefile -- mg\jni
+# 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  The 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.
+#
+###########################################################################
+ 
+MGHOME = ..
+
+AR = lib
+CC = cl
+CPPFLAGS = 
+
+DEFS = -DQUIET -DHAVE_CONFIG_H -D__WIN32__ -D_LITTLE_ENDIAN -DPARADOCNUM \
+       -DSHORT_SUFFIX
+INCLUDES = -I"$(MGHOME)" -I"$(MGHOME)\lib" -I"$(MGHOME)\src\text" \
+           -I"$(JAVA_HOME)\include" -I"$(JAVA_HOME)\include\win32"           
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES)
+ 
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+ 
+HEADERS = \
+    MGSearchWrapperImpl.h   org_greenstone_mg_MGSearchWrapper.h  \
+    MGRetrieveWrapperImpl.h   org_greenstone_mg_MGRetrieveWrapper.h  \
+    org_greenstone_mg_MGPassesWrapper.h
+
+
+SOURCES = \
+    MGSearchWrapperImpl.c MGRetrieveWrapperImpl.c MGPassesWrapperImpl.c
+
+SEARCHOBJECTS = \
+    MGSearchWrapperImpl$o $(MGHOME)\src\text\libmgtext.lib \
+    $(MGHOME)\lib\libmglib.lib
+
+RETRIEVEOBJECTS = \
+    MGRetrieveWrapperImpl$o  $(MGHOME)\src\text\libmgtext.lib \
+    $(MGHOME)\lib\libmglib.lib
+
+PASSES_OBJECTS = \
+    MGPassesWrapperImpl$o   $(MGHOME)\src\text\libmgpass.lib \
+    $(MGHOME)\lib\libmglib.lib
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : compile link
+
+compile:
+        $(COMPILE) MGSearchWrapperImpl.c MGRetrieveWrapperImpl.c MGPassesWrapperImpl.c
+
+link:
+        $(CC) -MD -LD $(SEARCHOBJECTS) -Femgsearchjni.dll
+	  $(CC) -MD -LD $(RETRIEVEOBJECTS) -Femgretrievejni.dll
+        $(CC) -MD -LD $(PASSES_OBJECTS) -Femgpassjni.dll
+
+
+install: 
+
+clean:
+        if exist *$o del *$o 
+	if exist mgsearchjni.dll del mgsearchjni.dll
+	if exist mgretrievejni.dll del mgretrievejni.dll
+	if exist mgpassjni.dll del mgpassjni.dll
Index: /gsdl/trunk/gs2common/indexers/mg/lib/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/Makefile.in	(revision 16561)
@@ -0,0 +1,173 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the general purpose library
+# Copyright (C) 1994  Neil Sharman; (C) 1995 Tim Shimmin
+#
+# 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.
+#
+#       @(#)Makefile.in	1.3 16 Mar 1994
+#
+###########################################################################
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+SHELL = /bin/sh
+srcdir = @srcdir@
+subdir = lib
+VPATH = @srcdir@
+ 
+AR = ar
+CC = @CC@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -fpic 
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+
+##########################################
+# Uncomment for compiling for the nzdl project
+#
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -DQUIET -fpic
+##########################################
+
+ 
+prefix = @prefix@
+datadir = $(prefix)/share
+localedir = $(datadir)/locale
+ 
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CFLAGS)
+ 
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+ 
+.SUFFIXES: ._c ._o
+.c._c:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $@
+._c._o:
+	@echo $(COMPILE) $<
+	@rm -f _$*.c
+	@ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+.c._o:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $*._c
+	@echo $(COMPILE) $*._c
+	@rm -f _$*.c
+	@ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+ 
+INCLUDES = -I../../.. -I.. -I$(srcdir)
+ 
+HEADERS = \
+    bitio_m_random.h   filestats.h        local_strings.h    lovinstem.h \
+    bitio_m_stdio.h    getopt.h           memlib.h \
+    bitio_gen.h        bitio_mem.h        mgheap.h             messages.h \
+    bitio_m.h          bitio_mems.h       huffman.h          timing.h \
+    bitio_m_mem.h      bitio_random.h     huffman_mem.h      perf_hash.h \
+    bitio_m_mems.h     bitio_stdio.h      huffman_stdio.h    sptree.h \
+    pathmax.h          getpagesize.h \
+    random.h           simplefrenchstem.h unitool.h  longlong.h
+# regex.h            rx.h               
+
+
+SOURCES = \
+    bitio_random.c    mgheap.c            memlib.c \
+    bitio_stdio.c     huffman.c         messages.c \
+    bitio_gen.c       filestats.c       huffman_mem.c     perf_hash.c \
+    bitio_mem.c       getopt.c          huffman_stdio.c   sptree.c \
+    bitio_mems.c      getopt1.c         local_strings.c   lovinstem.c \
+    timing.c  \
+    alloca.c          error.c           xmalloc.c         strstr.c \
+    gmalloc.c         ftruncate.c       strcasecmp.c      random.c \
+    simplefrenchstem.c                  unitool.c
+# regex.c           rx.c              
+
+OBJECTS = @ALLOCA@ @LIBOBJS@ \
+    bitio_random$o    mgheap$o            memlib$o \
+    bitio_stdio$o     huffman$o         messages$o \
+    bitio_gen$o       filestats$o       huffman_mem$o     perf_hash$o \
+    bitio_mem$o       getopt$o          huffman_stdio$o   sptree$o \
+    bitio_mems$o      getopt1$o         local_strings$o   lovinstem$o \
+    timing$o \
+    error$o           xmalloc$o \
+    random$o          simplefrenchstem$o \
+    unitool$o
+#   gmalloc$o regex$o		rx$o 
+
+LIB_OBJECTS =   \
+    bitio_random$o    mgheap$o            memlib$o \
+    bitio_stdio$o     huffman$o          \
+    bitio_gen$o       filestats$o       huffman_mem$o     perf_hash$o \
+    bitio_mem$o       getopt$o          huffman_stdio$o   sptree$o \
+    bitio_mems$o      getopt1$o         local_strings$o   lovinstem$o \
+    timing$o \
+    error$o           xmalloc$o \
+    random$o          simplefrenchstem$o \
+    unitool$o
+
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : libmglib.a gsdllibmg.a
+
+libmglib.a : $(OBJECTS)
+	rm -f libmglib.a
+	$(AR) cru libmglib.a $(OBJECTS)
+	$(RANLIB) libmglib.a
+
+$(OBJECTS): ../config.h
+
+gsdllibmg.a: $(LIB_OBJECTS)
+	rm -f gsdllibmg.a
+	$(AR) cru gsdllibmg.a $(LIB_OBJECTS)
+	$(RANLIB) gsdllibmg.a
+ 
+
+install: 
+installdirs:
+uninstall:
+execinstall:
+execuninstall:
+maninstall:
+manuninstall:
+ 
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $$file $(distdir) 2> /dev/null \
+	  || cp -p $$file $(distdir); \
+	done
+
+mostlyclean:
+	rm -f *.o *_.o
+ 
+clean: mostlyclean
+	rm -f libmglib.a gsdllibmg.a
+ 
+distclean: clean
+	rm -f Makefile
+ 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+
+#Makefile: Makefile.in ../../../config.status
+#	cd ../../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ 
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mg/lib/alloca.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/alloca.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/alloca.c	(revision 16561)
@@ -0,0 +1,496 @@
+/* alloca.c -- allocate automatically reclaimed memory
+   (Mostly) portable public-domain implementation -- D A Gwyn
+
+   This implementation of the PWB library alloca function,
+   which is used to allocate space off the run-time stack so
+   that it is automatically reclaimed upon procedure exit,
+   was inspired by discussions with J. Q. Johnson of Cornell.
+   J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+   There are some preprocessor constants that can
+   be defined when compiling for your specific system, for
+   improved efficiency; however, the defaults should be okay.
+
+   The general concept of this implementation is to keep
+   track of all alloca-allocated blocks, and reclaim any
+   that are found to be deeper in the stack than the current
+   invocation.  This heuristic does not reclaim storage as
+   soon as it becomes invalid, but it will do so eventually.
+
+   As a special case, alloca(0) reclaims storage without
+   allocating any.  It is a good idea to use alloca(0) in
+   your main control loop, etc. to force garbage collection.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#ifdef emacs
+#include "blockinput.h"
+#endif
+
+/* If compiling with GCC 2, this file's not needed.  */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+   there must be some other way alloca is supposed to work.  */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+   -- this is for usg, in which emacs must undefine static
+   in order to make unexec workable
+   */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+   provide an "address metric" ADDRESS_FUNCTION macro.  */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define	NULL	0
+
+/* Different portions of Emacs need to call different versions of
+   malloc.  The Emacs executable needs alloca to call xmalloc, because
+   ordinary malloc isn't protected from input signals.  On the other
+   hand, the utilities in lib-src need alloca to call malloc; some of
+   them are very simple, and don't have an xmalloc routine.
+
+   Non-Emacs programs expect this to call use xmalloc.
+
+   Callers below should use malloc.  */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION if you know the direction of stack
+   growth for your system; otherwise it will be automatically
+   deduced at run-time.
+
+   STACK_DIRECTION > 0 => grows toward higher addresses
+   STACK_DIRECTION < 0 => grows toward lower addresses
+   STACK_DIRECTION = 0 => direction of growth unknown  */
+
+#ifndef STACK_DIRECTION
+#define	STACK_DIRECTION	0	/* Direction unknown.  */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define	STACK_DIR	STACK_DIRECTION	/* Known at compile-time.  */
+
+#else /* STACK_DIRECTION == 0; need run-time code.  */
+
+static int stack_dir;		/* 1 or -1 once known.  */
+#define	STACK_DIR	stack_dir
+
+static void
+find_stack_direction ()
+{
+  static char *addr = NULL;	/* Address of first `dummy', once known.  */
+  auto char dummy;		/* To get stack address.  */
+
+  if (addr == NULL)
+    {				/* Initial entry.  */
+      addr = ADDRESS_FUNCTION (dummy);
+
+      find_stack_direction ();	/* Recurse once.  */
+    }
+  else
+    {
+      /* Second entry.  */
+      if (ADDRESS_FUNCTION (dummy) > addr)
+	stack_dir = 1;		/* Stack grew upward.  */
+      else
+	stack_dir = -1;		/* Stack grew downward.  */
+    }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+   (a) chain together all alloca'ed blocks;
+   (b) keep track of stack depth.
+
+   It is very important that sizeof(header) agree with malloc
+   alignment chunk size.  The following default should work okay.  */
+
+#ifndef	ALIGN_SIZE
+#define	ALIGN_SIZE	sizeof(double)
+#endif
+
+typedef union hdr
+{
+  char align[ALIGN_SIZE];	/* To force sizeof(header).  */
+  struct
+    {
+      union hdr *next;		/* For chaining headers.  */
+      char *deep;		/* For stack depth measure.  */
+    } h;
+} header;
+
+static header *last_alloca_header = NULL;	/* -> last alloca header.  */
+
+/* Return a pointer to at least SIZE bytes of storage,
+   which will be automatically reclaimed upon exit from
+   the procedure that called alloca.  Originally, this space
+   was supposed to be taken from the current stack frame of the
+   caller, but that method cannot be made to work for some
+   implementations of C, for example under Gould's UTX/32.  */
+
+pointer
+alloca (size)
+     unsigned size;
+{
+  auto char probe;		/* Probes stack depth: */
+  register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+  if (STACK_DIR == 0)		/* Unknown growth direction.  */
+    find_stack_direction ();
+#endif
+
+  /* Reclaim garbage, defined as all alloca'd storage that
+     was allocated from deeper in the stack than currently. */
+
+  {
+    register header *hp;	/* Traverses linked list.  */
+
+#ifdef emacs
+    BLOCK_INPUT;
+#endif
+
+    for (hp = last_alloca_header; hp != NULL;)
+      if ((STACK_DIR > 0 && hp->h.deep > depth)
+	  || (STACK_DIR < 0 && hp->h.deep < depth))
+	{
+	  register header *np = hp->h.next;
+
+	  free ((pointer) hp);	/* Collect garbage.  */
+
+	  hp = np;		/* -> next header.  */
+	}
+      else
+	break;			/* Rest are not deeper.  */
+
+    last_alloca_header = hp;	/* -> last valid storage.  */
+
+#ifdef emacs
+    UNBLOCK_INPUT;
+#endif
+  }
+
+  if (size == 0)
+    return NULL;		/* No allocation required.  */
+
+  /* Allocate combined header + user data storage.  */
+
+  {
+    register pointer new = malloc (sizeof (header) + size);
+    /* Address of header.  */
+
+    ((header *) new)->h.next = last_alloca_header;
+    ((header *) new)->h.deep = depth;
+
+    last_alloca_header = (header *) new;
+
+    /* User storage begins just after header.  */
+
+    return (pointer) ((char *) new + sizeof (header));
+  }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+  {
+    long shgrow:32;		/* Number of times stack has grown.  */
+    long shaseg:32;		/* Size of increments to stack.  */
+    long shhwm:32;		/* High water mark of stack.  */
+    long shsize:32;		/* Current size of stack (all segments).  */
+  };
+
+/* The stack segment linkage control information occurs at
+   the high-address end of a stack segment.  (The stack
+   grows from low addresses to high addresses.)  The initial
+   part of the stack segment linkage control information is
+   0200 (octal) words.  This provides for register storage
+   for the routine which overflows the stack.  */
+
+struct stack_segment_linkage
+  {
+    long ss[0200];		/* 0200 overflow words.  */
+    long sssize:32;		/* Number of words in this segment.  */
+    long ssbase:32;		/* Offset to stack base.  */
+    long:32;
+    long sspseg:32;		/* Offset to linkage control of previous
+				   segment of stack.  */
+    long:32;
+    long sstcpt:32;		/* Pointer to task common address block.  */
+    long sscsnm;		/* Private control structure number for
+				   microtasking.  */
+    long ssusr1;		/* Reserved for user.  */
+    long ssusr2;		/* Reserved for user.  */
+    long sstpid;		/* Process ID for pid based multi-tasking.  */
+    long ssgvup;		/* Pointer to multitasking thread giveup.  */
+    long sscray[7];		/* Reserved for Cray Research.  */
+    long ssa0;
+    long ssa1;
+    long ssa2;
+    long ssa3;
+    long ssa4;
+    long ssa5;
+    long ssa6;
+    long ssa7;
+    long sss0;
+    long sss1;
+    long sss2;
+    long sss3;
+    long sss4;
+    long sss5;
+    long sss6;
+    long sss7;
+  };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+   returned by the STKSTAT library routine.  */
+struct stk_stat
+  {
+    long now;			/* Current total stack size.  */
+    long maxc;			/* Amount of contiguous space which would
+				   be required to satisfy the maximum
+				   stack demand to date.  */
+    long high_water;		/* Stack high-water mark.  */
+    long overflows;		/* Number of stack overflow ($STKOFEN) calls.  */
+    long hits;			/* Number of internal buffer hits.  */
+    long extends;		/* Number of block extensions.  */
+    long stko_mallocs;		/* Block allocations by $STKOFEN.  */
+    long underflows;		/* Number of stack underflow calls ($STKRETN).  */
+    long stko_free;		/* Number of deallocations by $STKRETN.  */
+    long stkm_free;		/* Number of deallocations by $STKMRET.  */
+    long segments;		/* Current number of stack segments.  */
+    long maxs;			/* Maximum number of stack segments so far.  */
+    long pad_size;		/* Stack pad size.  */
+    long current_address;	/* Current stack segment address.  */
+    long current_size;		/* Current stack segment size.  This
+				   number is actually corrupted by STKSTAT to
+				   include the fifteen word trailer area.  */
+    long initial_address;	/* Address of initial segment.  */
+    long initial_size;		/* Size of initial segment.  */
+  };
+
+/* The following structure describes the data structure which trails
+   any stack segment.  I think that the description in 'asdef' is
+   out of date.  I only describe the parts that I am sure about.  */
+
+struct stk_trailer
+  {
+    long this_address;		/* Address of this block.  */
+    long this_size;		/* Size of this block (does not include
+				   this trailer).  */
+    long unknown2;
+    long unknown3;
+    long link;			/* Address of trailer block of previous
+				   segment.  */
+    long unknown5;
+    long unknown6;
+    long unknown7;
+    long unknown8;
+    long unknown9;
+    long unknown10;
+    long unknown11;
+    long unknown12;
+    long unknown13;
+    long unknown14;
+  };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+   I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+  struct stk_stat status;
+  struct stk_trailer *trailer;
+  long *block, size;
+  long result = 0;
+
+  /* We want to iterate through all of the segments.  The first
+     step is to get the stack status structure.  We could do this
+     more quickly and more directly, perhaps, by referencing the
+     $LM00 common block, but I know that this works.  */
+
+  STKSTAT (&status);
+
+  /* Set up the iteration.  */
+
+  trailer = (struct stk_trailer *) (status.current_address
+				    + status.current_size
+				    - 15);
+
+  /* There must be at least one stack segment.  Therefore it is
+     a fatal error if "trailer" is null.  */
+
+  if (trailer == 0)
+    abort ();
+
+  /* Discard segments that do not contain our argument address.  */
+
+  while (trailer != 0)
+    {
+      block = (long *) trailer->this_address;
+      size = trailer->this_size;
+      if (block == 0 || size == 0)
+	abort ();
+      trailer = (struct stk_trailer *) trailer->link;
+      if ((block <= address) && (address < (block + size)))
+	break;
+    }
+
+  /* Set the result to the offset in this segment and add the sizes
+     of all predecessor segments.  */
+
+  result = address - block;
+
+  if (trailer == 0)
+    {
+      return result;
+    }
+
+  do
+    {
+      if (trailer->this_size <= 0)
+	abort ();
+      result += trailer->this_size;
+      trailer = (struct stk_trailer *) trailer->link;
+    }
+  while (trailer != 0);
+
+  /* We are done.  Note that if you present a bogus address (one
+     not in any segment), you will get a different number back, formed
+     from subtracting the address of the first block.  This is probably
+     not what you want.  */
+
+  return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+   Determine the number of the cell within the stack,
+   given the address of the cell.  The purpose of this
+   routine is to linearize, in some sense, stack addresses
+   for alloca.  */
+
+static long
+i00afunc (long address)
+{
+  long stkl = 0;
+
+  long size, pseg, this_segment, stack;
+  long result = 0;
+
+  struct stack_segment_linkage *ssptr;
+
+  /* Register B67 contains the address of the end of the
+     current stack segment.  If you (as a subprogram) store
+     your registers on the stack and find that you are past
+     the contents of B67, you have overflowed the segment.
+
+     B67 also points to the stack segment linkage control
+     area, which is what we are really interested in.  */
+
+  stkl = CRAY_STACKSEG_END ();
+  ssptr = (struct stack_segment_linkage *) stkl;
+
+  /* If one subtracts 'size' from the end of the segment,
+     one has the address of the first word of the segment.
+
+     If this is not the first segment, 'pseg' will be
+     nonzero.  */
+
+  pseg = ssptr->sspseg;
+  size = ssptr->sssize;
+
+  this_segment = stkl - size;
+
+  /* It is possible that calling this routine itself caused
+     a stack overflow.  Discard stack segments which do not
+     contain the target address.  */
+
+  while (!(this_segment <= address && address <= stkl))
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+      if (pseg == 0)
+	break;
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      this_segment = stkl - size;
+    }
+
+  result = address - this_segment;
+
+  /* If you subtract pseg from the current end of the stack,
+     you get the address of the previous stack segment's end.
+     This seems a little convoluted to me, but I'll bet you save
+     a cycle somewhere.  */
+
+  while (pseg != 0)
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      result += size;
+    }
+  return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.c	(revision 16561)
@@ -0,0 +1,141 @@
+/**************************************************************************
+ *
+ * bitio_gen.c -- General supoport routines for bitio
+ * Copyright (C) 1994  Neil Sharman and Alistair Moffat
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "bitio_m.h"
+
+
+
+
+int fprintf (FILE *, const char *,...);
+
+unsigned long 
+BIO_Unary_Length (unsigned long val)
+{
+  register unsigned long num;
+  UNARY_LENGTH (val, num);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Binary_Length (unsigned long val, unsigned long b)
+{
+  register unsigned long num;
+  BINARY_LENGTH (val, b, num);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Gamma_Length (unsigned long val)
+{
+  register unsigned long num;
+  GAMMA_LENGTH (val, num);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Delta_Length (unsigned long val)
+{
+  register unsigned long num;
+  DELTA_LENGTH (val, num);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Elias_Length (unsigned long val, unsigned long b, double s)
+{
+  register unsigned long num;
+  ELIAS_LENGTH (val, b, s, num);
+  return (num);
+}
+
+unsigned long 
+BIO_Bblock_Length (unsigned long val, unsigned long b)
+{
+  register unsigned long num;
+  BBLOCK_LENGTH (val, b, num);
+  return (num);
+}
+
+
+int 
+BIO_Bblock_Init (int N, int p)
+{
+  int b;
+  b = (int) (0.5 + 0.6931471 * N / p);
+  return (b ? b : 1);
+}
+
+
+int 
+BIO_Bblock_Init_W (int N, int p)
+{
+  int logb;
+  FLOORLOG_2 ((N - p) / p, logb);
+  return (logb < 0 ? 1 : (1 << logb));
+}
+
+int 
+BIO_Bblock_Bound_b (int N, int p, int b)
+{
+  int clogb;
+  CEILLOG_2 (b, clogb);
+  return (p * (1 + clogb) + (N - p * ((1 << clogb) - b + 1)) / b);
+}
+
+int 
+BIO_Bblock_Bound (int N, int p)
+{
+  int b;
+  b = BIO_Bblock_Init_W (N, p);
+  return (BIO_Bblock_Bound_b (N, p, b));
+}
+
+
+/* adjustment is a hack to overcome inaccuracies in floating point,
+   where (int)(ln4/ln2) can equal 1, not 2. */
+/* copied from mg-1.3f, 17-09-01 kjm18 */
+int 
+BIO_Gamma_Bound (int N, int p)
+{
+  /* return ((int) (p * (2 * log2 ((double) N / p) + 1))); */
+  return ((int) (p * (2 * log2 (((double) N * 1.0001) / p) + 1)));
+}
+
+int 
+floorlog_2 (int b)
+{
+  int logb;
+  FLOORLOG_2 (b, logb);
+  return logb;
+}
+
+int 
+ceillog_2 (int b)
+{
+  int logb;
+  CEILLOG_2 (b, logb);
+  return logb;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_gen.h	(revision 16561)
@@ -0,0 +1,61 @@
+/**************************************************************************
+ *
+ * bitio_gen.h -- General supoport routines for bitio
+ * Copyright (C) 1994  Neil Sharman and Alistair Moffat
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing general bitio operations.
+ *
+ *  This file defines functions for calculation the length in bits of a number
+ *  coded unary, binary, delta, gamma, elias, and  bblock coding. It defines 
+ *  routines for calculating the parameter and bounds for bblock coding. 
+ *  Routines for calculating ceil and floor lg are also defined.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_GEN
+#define H_BITIO_GEN
+
+unsigned long BIO_Unary_Length (unsigned long val);
+
+unsigned long BIO_Binary_Length (unsigned long val, unsigned long b);
+
+unsigned long BIO_Gamma_Length (unsigned long val);
+
+unsigned long BIO_Delta_Length (unsigned long val);
+
+unsigned long BIO_Elias_Length (unsigned long val, unsigned long b, double s);
+
+unsigned long BIO_Bblock_Length (unsigned long val, unsigned long b);
+
+int BIO_Bblock_Init (int N, int p);
+
+int BIO_Bblock_Init_W (int N, int p);
+
+int BIO_Bblock_Bound_b (int N, int p, int b);
+
+int BIO_Bblock_Bound (int N, int p);
+
+int BIO_Gamma_Bound (int N, int p);
+
+int floorlog_2 (int b);
+
+int ceillog_2 (int b);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m.h	(revision 16561)
@@ -0,0 +1,541 @@
+/**************************************************************************
+ *
+ * bitio_m.h -- Macros for bitio
+ * Copyright (C) 1994  Neil Sharman and Alistair Moffat
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#ifndef H_BITIO_M
+#define H_BITIO_M
+
+/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
+
+#ifndef BIO_ENCODE_PROLOGUE
+#define BIO_ENCODE_PROLOGUE
+#endif
+
+#ifndef BIO_DECODE_PROLOGUE
+#define BIO_DECODE_PROLOGUE
+#endif
+
+#ifndef BIO_ENCODE_EPILOGUE
+#define BIO_ENCODE_EPILOGUE
+#endif
+
+#ifndef BIO_DECODE_EPILOGUE
+#define BIO_DECODE_EPILOGUE
+#endif
+
+#ifndef DECODE_ADD
+#define DECODE_ADD(b) (b) += (b) + DECODE_BIT
+#endif
+
+/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
+
+#define POSITIVE(f, x)							\
+  if ((x)<=0) 								\
+    fprintf(stderr,"Error: Cannot "#f" encode %lu\n",(unsigned long)x),exit(1);
+
+
+#define CEILLOG_2(x,v)							\
+do {									\
+  register int _B_x  = (x) - 1;						\
+  (v) = 0;								\
+  for (; _B_x ; _B_x>>=1, ++(v));					\
+} while(0)
+
+
+#define FLOORLOG_2(x,v)							\
+do {									\
+  register int _B_x  = (x);						\
+  (v) = -1;								\
+  for (; _B_x ; _B_x>>=1, ++(v));					\
+} while(0)
+
+
+/****************************************************************************/
+
+
+
+#define UNARY_ENCODE(x)							\
+do {									\
+  register unsigned long _B_x = (x);					\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(unary, _B_x);						\
+  while(--_B_x) ENCODE_BIT(0);						\
+  ENCODE_BIT(1);							\
+  BIO_ENCODE_EPILOGUE;							\
+} while(0)
+
+#define UNARY_ENCODE_L(x, count)					\
+do {									\
+  register unsigned long _B_x = (x);					\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(unary, _B_x);						\
+  (count) += _B_x;							\
+  while(--_B_x) ENCODE_BIT(0);						\
+  ENCODE_BIT(1);							\
+  BIO_ENCODE_EPILOGUE;							\
+} while(0)
+
+#define UNARY_DECODE(x)							\
+do {									\
+  BIO_DECODE_PROLOGUE;							\
+  (x) = 1;								\
+  while (!DECODE_BIT) ++(x);						\
+  BIO_DECODE_EPILOGUE;							\
+} while(0)
+
+#define UNARY_DECODE_L(x, count)					\
+do {									\
+  BIO_DECODE_PROLOGUE;							\
+  (x) = 1;								\
+  while (!DECODE_BIT) ++(x);						\
+  (count) += (x);							\
+  BIO_DECODE_EPILOGUE;							\
+} while(0)
+
+
+#define UNARY_LENGTH(x, count)						\
+do {									\
+  POSITIVE(unary, x);							\
+  (count) = (x);							\
+} while(0)
+
+
+/****************************************************************************/
+
+
+#define BINARY_ENCODE(x, b)						\
+do {									\
+  register unsigned long _B_x = (x);					\
+  register unsigned long _B_b = (b);					\
+  register int _B_nbits, _B_logofb, _B_thresh;				\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(binary, _B_x);						\
+  CEILLOG_2(_B_b, _B_logofb);						\
+  _B_thresh = (1<<_B_logofb) - _B_b;					\
+  if (--_B_x < _B_thresh)						\
+    _B_nbits = _B_logofb-1;						\
+  else									\
+    {									\
+      _B_nbits = _B_logofb;						\
+      _B_x += _B_thresh;						\
+    }									\
+  while (--_B_nbits>=0)							\
+    ENCODE_BIT((_B_x>>_B_nbits) & 0x1);					\
+  BIO_ENCODE_EPILOGUE;							\
+} while(0)
+
+#define BINARY_ENCODE_L(x, b, count)					\
+do {									\
+  register unsigned long _B_x = (x);					\
+  register unsigned long _B_b = (b);					\
+  register int _B_nbits, _B_logofb, _B_thresh;				\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(binary, _B_x);						\
+  CEILLOG_2(_B_b, _B_logofb);						\
+  _B_thresh = (1<<_B_logofb) - _B_b;					\
+  if (--_B_x < _B_thresh)						\
+    _B_nbits = _B_logofb-1;						\
+  else									\
+    {									\
+      _B_nbits = _B_logofb;						\
+      _B_x += _B_thresh;						\
+    }									\
+  (count) += _B_nbits;							\
+  while (--_B_nbits>=0)							\
+    ENCODE_BIT((_B_x>>_B_nbits) & 0x1);					\
+  BIO_ENCODE_EPILOGUE;							\
+} while(0)
+
+
+#define BINARY_DECODE(x, b)						\
+do {									\
+  register unsigned long _B_x = 0;					\
+  register unsigned long _B_b = (b);					\
+  register int _B_i, _B_logofb, _B_thresh;				\
+  BIO_DECODE_PROLOGUE;							\
+  if (_B_b != 1)							\
+    {									\
+      CEILLOG_2(_B_b, _B_logofb);					\
+      _B_thresh = (1<<_B_logofb) - _B_b;				\
+      --_B_logofb;							\
+      for (_B_i=0; _B_i < _B_logofb; ++_B_i)			 	\
+        DECODE_ADD(_B_x);						\
+      if (_B_x >= _B_thresh)						\
+        {								\
+          DECODE_ADD(_B_x);						\
+          _B_x -= _B_thresh;						\
+	}								\
+      (x) = _B_x+1;							\
+    }									\
+  else									\
+    (x) = 1;								\
+  BIO_DECODE_EPILOGUE;							\
+} while(0)
+
+#define BINARY_DECODE_L(x, b, count)					\
+do {									\
+  register unsigned long _B_x = 0;					\
+  register unsigned long _B_b = (b);					\
+  register int _B_i, _B_logofb, _B_thresh;				\
+  BIO_DECODE_PROLOGUE;							\
+  if (_B_b != 1)							\
+    {									\
+      CEILLOG_2(_B_b, _B_logofb);					\
+      _B_thresh = (1<<_B_logofb) - _B_b;				\
+      --_B_logofb;							\
+      (count) += _B_logofb;					       	\
+      for (_B_i=0; _B_i < _B_logofb; ++_B_i)			 	\
+        DECODE_ADD(_B_x);						\
+      if (_B_x >= _B_thresh)						\
+        {								\
+          DECODE_ADD(_B_x);						\
+          _B_x -= _B_thresh;						\
+          ++(count);							\
+	}								\
+      (x) = _B_x+1;							\
+    }									\
+  else									\
+    (x) = 1;								\
+  BIO_DECODE_EPILOGUE;							\
+} while(0)
+
+#define BINARY_LENGTH(x, b, count)					\
+do {									\
+  register unsigned long _B_x = (x);					\
+  register unsigned long _B_b = (b);					\
+  register int _B_nbits, _B_logofb, _B_thresh;				\
+  POSITIVE(binary, _B_x);						\
+  CEILLOG_2(_B_b, _B_logofb);						\
+  _B_thresh = (1<<_B_logofb) - _B_b;					\
+  if (--_B_x < _B_thresh)						\
+    _B_nbits = _B_logofb-1;						\
+  else									\
+    _B_nbits = _B_logofb;						\
+  (count) = _B_nbits;							\
+} while(0)
+
+/****************************************************************************/
+
+
+
+
+#define GAMMA_ENCODE(x)							\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register int _B_logofb;						\
+  register int _B_nbits;						\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(gamma, _B_xx);						\
+  FLOORLOG_2(_B_xx, _B_logofb);						\
+  _B_nbits = _B_logofb+1;						\
+  while(_B_logofb--) ENCODE_BIT(0);					\
+  while (--_B_nbits>=0)							\
+    ENCODE_BIT((_B_xx>>_B_nbits) & 0x1);				\
+  BIO_ENCODE_EPILOGUE;							\
+} while (0)
+
+#define GAMMA_ENCODE_L(x, count)					\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register int _B_logofb;						\
+  register int _B_nbits;						\
+  BIO_ENCODE_PROLOGUE;							\
+  POSITIVE(gamma, _B_xx);						\
+  FLOORLOG_2(_B_xx, _B_logofb);						\
+  _B_nbits = _B_logofb+1;						\
+  (count) += _B_logofb*2+1;						\
+  while(_B_logofb--) ENCODE_BIT(0);					\
+  while (--_B_nbits>=0)							\
+    ENCODE_BIT((_B_xx>>_B_nbits) & 0x1);				\
+  BIO_ENCODE_EPILOGUE;							\
+} while (0)
+
+#define GAMMA_DECODE(x)							\
+do {									\
+  register unsigned long _B_xx = 1;					\
+  register int _B_nbits = 0;						\
+  BIO_DECODE_PROLOGUE;							\
+  while(!DECODE_BIT) ++_B_nbits;					\
+  while (_B_nbits-- > 0)						\
+    DECODE_ADD(_B_xx);							\
+  (x) = _B_xx;								\
+  BIO_DECODE_EPILOGUE;							\
+} while (0)
+
+#define GAMMA_DECODE_L(x, count)					\
+do {									\
+  register unsigned long _B_xx = 1;					\
+  register int _B_nbits = 0;						\
+  BIO_DECODE_PROLOGUE;							\
+  while(!DECODE_BIT) ++_B_nbits;					\
+  (count) += _B_nbits*2+1;						\
+  while (_B_nbits-- > 0)						\
+    DECODE_ADD(_B_xx);							\
+  (x) = _B_xx;								\
+  BIO_DECODE_EPILOGUE;							\
+} while (0)
+
+#define GAMMA_LENGTH(x, count)						\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register int _B_logofb;						\
+  POSITIVE(gamma, _B_xx);						\
+  FLOORLOG_2(_B_xx, _B_logofb);						\
+  (count) = _B_logofb*2+1;						\
+} while (0)
+
+
+
+/****************************************************************************/
+
+
+#define DELTA_ENCODE(x)							\
+do {									\
+  register unsigned long _B_xxx = (x);					\
+  register int _B_logx;							\
+  FLOORLOG_2(_B_xxx, _B_logx);						\
+  GAMMA_ENCODE(_B_logx+1);						\
+  BINARY_ENCODE(_B_xxx+1, 1<<_B_logx);					\
+} while (0)
+
+#define DELTA_ENCODE_L(x, count)					\
+do {									\
+  register unsigned long _B_xxx = (x);					\
+  register int _B_logx;							\
+  FLOORLOG_2(_B_xxx, _B_logx);						\
+  GAMMA_ENCODE_L(_B_logx+1, count);					\
+  BINARY_ENCODE_L(_B_xxx+1, 1<<_B_logx, count);				\
+} while (0)
+
+
+#define DELTA_DECODE(x)							\
+do {									\
+  register unsigned long _B_xxx;					\
+  register int _B_logx;							\
+  GAMMA_DECODE(_B_logx); --_B_logx;					\
+  BINARY_DECODE(_B_xxx, 1<<_B_logx); --_B_xxx;				\
+  (x) = _B_xxx + (1<<_B_logx);						\
+} while (0)
+
+#define DELTA_DECODE_L(x, count)					\
+do {									\
+  register unsigned long _B_xxx;					\
+  register int _B_logx;							\
+  GAMMA_DECODE_L(_B_logx, count); --_B_logx;				\
+  BINARY_DECODE_L(_B_xxx, 1<<_B_logx, count); --_B_xxx;			\
+  (x) = _B_xxx + (1<<_B_logx);						\
+} while (0)
+
+#define DELTA_LENGTH(x, count)						\
+do {									\
+  register unsigned long _B_xxx = (x);					\
+  register int _B_logx, _B_dcount;					\
+  FLOORLOG_2(_B_xxx, _B_logx);						\
+  GAMMA_LENGTH(_B_logx+1, count);					\
+  BINARY_LENGTH(_B_xxx+1, 1<<_B_logx, _B_dcount);			\
+  (count) += _B_dcount;							\
+} while (0)
+
+
+/****************************************************************************/
+
+
+
+
+#define ELIAS_ENCODE(x, b, s)						\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register unsigned long _B_b = (b);					\
+  register double _B_s = (s);						\
+  register int _B_lower=1, _B_upper=1;					\
+  register int _B_k=0;							\
+  register double _B_pow=1.0;						\
+  POSITIVE(elias, _B_xx);						\
+  while (_B_xx>_B_upper) 						\
+    {									\
+      ++_B_k;								\
+      _B_lower = _B_upper+1;						\
+      _B_pow *= _B_s;							\
+      _B_upper += _B_b*_B_pow;						\
+    }									\
+  UNARY_ENCODE(_B_k+1);							\
+  BINARY_ENCODE(_B_xx-_B_lower+1, _B_upper-_B_lower+1);			\
+} while (0)
+
+#define ELIAS_ENCODE_L(x, b, s, count)					\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register unsigned long _B_b = (b);					\
+  register double _B_s = (s);						\
+  register int _B_lower=1, _B_upper=1;					\
+  register int _B_k=0;							\
+  register double _B_pow=1.0;						\
+  POSITIVE(elias, _B_xx);						\
+  while (_B_xx>_B_upper) 						\
+    {									\
+      ++_B_k;								\
+      _B_lower = _B_upper+1;						\
+      _B_pow *= _B_s;							\
+      _B_upper += _B_b*_B_pow;						\
+    }									\
+  UNARY_ENCODE_L(_B_k+1, count);					\
+  BINARY_ENCODE_L(_B_xx-_B_lower+1, _B_upper-_B_lower+1, count);	\
+} while (0)
+
+#define ELIAS_DECODE(x, b, s)						\
+do {									\
+  register unsigned long _B_xx;						\
+  register unsigned long _B_b = (b);					\
+  register double _B_s = (s);						\
+  register int _B_lower=1, _B_upper=1;					\
+  register int _B_k;							\
+  register double _B_pow=1.0;						\
+  UNARY_DECODE(_B_k); --_B_k;						\
+  while (_B_k--)							\
+    {									\
+      _B_lower = _B_upper+1;						\
+      _B_pow *= _B_s;							\
+      _B_upper += _B_b*_B_pow;						\
+    }									\
+  BINARY_DECODE(_B_xx, _B_upper-_B_lower+1); --_B_xx;			\
+  POSITIVE(gamma, _B_xx+_B_lower);				  	\
+  (x) = _B_xx+_B_lower;							\
+} while (0)
+
+#define ELIAS_DECODE_L(x, b, s, count)					\
+do {									\
+  register unsigned long _B_xx;						\
+  register unsigned long _B_b = (b);					\
+  register double _B_s = (s);						\
+  register int _B_lower=1, _B_upper=1;					\
+  register int _B_k;							\
+  register double _B_pow=1.0;						\
+  UNARY_DECODE_L(_B_k, count); --_B_k;					\
+  while (_B_k--)							\
+    {									\
+      _B_lower = _B_upper+1;						\
+      _B_pow *= _B_s;							\
+      _B_upper += _B_b*_B_pow;						\
+    }									\
+  BINARY_DECODE_L(_B_xx, _B_upper-_B_lower+1, count); --_B_xx;		\
+  POSITIVE(gamma, _B_xx+_B_lower);				  	\
+  (x) = _B_xx+_B_lower;							\
+} while (0)
+
+#define ELIAS_LENGTH(x, b, s, count)					\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register unsigned long _B_b = (b);					\
+  register double _B_s = (s);						\
+  register int _B_lower=1, _B_upper=1;					\
+  register int _B_k=0, _B_ecount;					\
+  register double _B_pow=1.0;						\
+  POSITIVE(gamma, _B_xx);						\
+  while (_B_xx>_B_upper) 						\
+    {									\
+      ++_B_k;								\
+      _B_lower = _B_upper+1;						\
+      _B_pow *= _B_s;							\
+      _B_upper += _B_b*_B_pow;						\
+    }									\
+  UNARY_LENGTH(_B_k+1, count);						\
+  BINARY_LENGTH(_B_xx-_B_lower+1, _B_upper-_B_lower+1, _B_ecount);	\
+  (count) += _B_ecount;							\
+} while (0)
+
+
+/****************************************************************************/
+
+
+#define BBLOCK_ENCODE(x, b)						\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register unsigned long _B_bb = (b);					\
+  register int _B_xdivb = 0;						\
+  POSITIVE(bblock, _B_xx);						\
+  --_B_xx;								\
+  while (_B_xx >= _B_bb) 						\
+    {									\
+      ++_B_xdivb;							\
+      _B_xx -= _B_bb;							\
+    }									\
+  UNARY_ENCODE(_B_xdivb+1);						\
+  BINARY_ENCODE(_B_xx+1, _B_bb);					\
+} while (0)
+
+#define BBLOCK_ENCODE_L(x, b, count)					\
+do {									\
+  register unsigned long _B_xx = (x);					\
+  register unsigned long _B_bb = (b);					\
+  register int _B_xdivb = 0;						\
+  POSITIVE(bblock, _B_xx);						\
+  --_B_xx;								\
+  while (_B_xx >= _B_bb) 						\
+    {									\
+      ++_B_xdivb;							\
+      _B_xx -= _B_bb;							\
+    }									\
+  UNARY_ENCODE_L(_B_xdivb+1, count);					\
+  BINARY_ENCODE_L(_B_xx+1, _B_bb, count);				\
+} while (0)
+
+#define BBLOCK_DECODE(x, b)						\
+do {									\
+  register unsigned long _B_x1, _B_xx = 0;				\
+  register unsigned long _B_bb = (b);					\
+  register int _B_xdivb;						\
+  UNARY_DECODE(_B_xdivb); --_B_xdivb;					\
+  while (_B_xdivb--)							\
+    _B_xx += _B_bb;							\
+  BINARY_DECODE(_B_x1, _B_bb);						\
+  (x) = _B_xx+_B_x1;							\
+} while (0)
+
+#define BBLOCK_DECODE_L(x, b, count)					\
+do {									\
+  register unsigned long _B_x1, _B_xx = 0;				\
+  register unsigned long _B_bb = (b);					\
+  register int _B_xdivb;						\
+  UNARY_DECODE_L(_B_xdivb, count); --_B_xdivb;				\
+  while (_B_xdivb--)							\
+    _B_xx += _B_bb;							\
+  BINARY_DECODE_L(_B_x1, _B_bb, count);					\
+  (x) = _B_xx+_B_x1;							\
+} while (0)
+
+#define BBLOCK_LENGTH(x, b, count)					\
+do {									\
+  register unsigned long _B_bcount, _B_xx = (x);			\
+  register unsigned long _B_bb = (b);					\
+  register int _B_xdivb = 0;						\
+  POSITIVE(bblock, _B_xx);						\
+  --_B_xx;								\
+  while (_B_xx >= _B_bb) 						\
+    {									\
+      ++_B_xdivb;							\
+      _B_xx -= _B_bb;							\
+    }									\
+  UNARY_LENGTH(_B_xdivb+1, count);					\
+  BINARY_LENGTH(_B_xx+1, _B_bb, _B_bcount);				\
+  (count) += _B_bcount;  						\
+} while (0)
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mem.h	(revision 16561)
@@ -0,0 +1,189 @@
+/**************************************************************************
+ *
+ * bitio_m_mem.h -- Macros for bitio to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are faster than the ones in "mems" files. But 
+ *  with these routines you cannot mix reads and writes on the array of chars,
+ *  or multiple write, at the same time and guarantee them to work, also you 
+ *  cannot seek to a point and do a write. The decode routine can detect when
+ *  you run off the end of the array and will produce an approate error 
+ *  message, and the encode routine will stop when it gets to the end of the 
+ *  character array. 
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_MEM
+#define H_BITIO_M_MEM
+
+typedef struct mem_bitio_state
+  {
+    unsigned char *Base;
+    unsigned char *Pos;
+    int Remaining;
+    unsigned char Buff;
+    unsigned char Btg;
+  }
+mem_bitio_state;
+
+
+
+
+#ifndef DECODE_ERROR
+#define DECODE_ERROR (fprintf(stderr,"Unexpected EOF in \"%s\" on line %d\n",\
+				__FILE__, __LINE__), exit(1))
+#endif
+
+
+
+#define ENCODE_START(p,r)						\
+  {									\
+    register unsigned char *__pos = p;					\
+    register unsigned char *__base = p;					\
+    register int __remaining = r;					\
+    register unsigned char __buff = 0;					\
+    register unsigned char __btg = sizeof(__buff)*8;
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    register unsigned char *__pos = (b).Pos;				\
+    register unsigned char *__base = (b).Base;				\
+    register int __remaining = (b).Remaining;				\
+    register unsigned char __buff = (b).Buff;				\
+    register unsigned char __btg = (b).Btg;
+
+#define ENCODE_BIT(b)							\
+  do {									\
+    --__btg;								\
+    if (b) __buff |= (1 << __btg);					\
+    if (!__btg)								\
+      {									\
+	if (__remaining)						\
+	  {								\
+	    *__pos++ = __buff;						\
+	    --__remaining;						\
+	  }  								\
+	__buff = 0;							\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+  } while(0)
+
+#define ENCODE_PAUSE(b)							\
+    (b).Pos = __pos;							\
+    (b).Base = __base;							\
+    (b).Remaining = __remaining;					\
+    (b).Buff = __buff;							\
+    (b).Btg =  __btg;							\
+  }
+
+#define ENCODE_FLUSH							\
+  do {									\
+    if (__btg != sizeof(__buff)*8)					\
+      if (__remaining)							\
+	{								\
+          *__pos++ = __buff;						\
+          --__remaining;						\
+	}  								\
+    __btg = sizeof(__buff)*8;						\
+  } while (0)
+
+#define ENCODE_DONE							\
+    ENCODE_FLUSH;							\
+  }
+
+
+#define DECODE_START(p,r)						\
+  {									\
+    register unsigned char *__pos = p;					\
+    register unsigned char *__base = p;					\
+    register int __remaining = r;					\
+    register unsigned char __buff = 0;					\
+    register unsigned char __btg = 0;
+
+#define DECODE_CONTINUE(b)						\
+  {									\
+    register unsigned char *__pos = (b).Pos;				\
+    register unsigned char *__base = (b).Base;				\
+    register int __remaining = (b).Remaining;				\
+    register unsigned char __buff = (b).Buff;				\
+    register unsigned char __btg = (b).Btg;
+
+#define DECODE_ADD_FF(b)						\
+  do {									\
+    if (!__btg)								\
+      {									\
+	if (__remaining)						\
+	  {								\
+  	    __buff = *__pos++;						\
+	    --__remaining;						\
+	  }								\
+        else								\
+          __buff = 0xff;						\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+    (b) += (b) + ((__buff >> --__btg) & 1);				\
+  } while(0)
+
+#define DECODE_ADD_00(b)						\
+  do {									\
+    if (!__btg)								\
+      {									\
+	if (__remaining)						\
+	  {								\
+  	    __buff = *__pos++;						\
+	    --__remaining;						\
+	  }								\
+        else								\
+          __buff = 0x00;						\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+    (b) += (b) + ((__buff >> --__btg) & 1);				\
+  } while(0)
+
+#define DECODE_BIT (__btg ? (__buff >> --__btg) & 1 :			\
+   (!__remaining ? 							\
+    (DECODE_ERROR, 0) :							\
+    (__buff = *__pos++, __remaining--, __btg = sizeof(__buff)*8, 	\
+     (__buff >> --__btg) & 1)))
+
+#define DECODE_DONE	;						\
+  }
+
+#define DECODE_PAUSE(b)							\
+    (b).Pos = __pos;							\
+    (b).Base = __base;							\
+    (b).Remaining = __remaining;					\
+    (b).Buff = __buff;							\
+    (b).Btg =  __btg;							\
+  }
+
+#define DECODE_SEEK(pos)						\
+  do {									\
+    register long __new_pos = (pos);					\
+    __pos = __base + (__new_pos >> 3);					\
+    __buff = *__pos++;							\
+    __btg = 8 - (__new_pos&7);						\
+    }									\
+  while(0)
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mems.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mems.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_mems.h	(revision 16561)
@@ -0,0 +1,112 @@
+/**************************************************************************
+ *
+ * bitio_m_mems.h -- Macros for bitio to memory (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are slower than the ones in "mem" files. but 
+ *  with these routines you can mix reads and writes, or multiple writes,  on
+ *  the array of chars at the same time and guarantee them to work, also you 
+ *  can seek to a point and do a write. The decode and encode macros cannot 
+ *  detect when the end off the character is reached and just continue 
+ *  processing.
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_MEMS
+#define H_BITIO_M_MEMS
+
+typedef struct mems_bitio_state
+  {
+    unsigned char *Base;
+    unsigned long pos;
+  }
+mems_bitio_state;
+
+
+#define ENCODE_START(b,p)						\
+  {									\
+    register unsigned char *__base = b;					\
+    register unsigned long __pos = p;
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    register unsigned char *__base = (b).Base;				\
+    register unsigned long __pos = (b).pos;
+
+#define ENCODE_BIT(b)							\
+  do {									\
+    if (b)								\
+      __base[__pos>>3] |= 0x80 >> (__pos&7);				\
+    else								\
+      __base[__pos>>3] &= 0xff7f >> (__pos&7);				\
+    ++__pos;								\
+  } while(0)
+
+#define ENCODE_PAUSE(b)							\
+    (b).Base = __base;							\
+    (b).pos = __pos;							\
+  }
+
+#define ENCODE_FLUSH
+
+
+#define ENCODE_DONE							\
+    ENCODE_FLUSH;							\
+  }
+
+
+#define DECODE_START(b,p)						\
+  {									\
+    register unsigned char *__base = b;					\
+    register unsigned long __pos = p;
+
+#define DECODE_CONTINUE(b)						\
+  {									\
+    register unsigned char *__base = (b).Base;				\
+    register unsigned long __pos = (b).pos;
+
+#define DECODE_ADD_FF(b)						\
+  do {									\
+    (b) += (b) + (__base[__pos>>3] & (0x80 >> (__pos&7)) != 0);		\
+    ++__pos;								\
+  } while(0)
+
+#define DECODE_ADD_00(b) DECODE_ADD_FF(b)
+
+#define DECODE_BIT 							\
+    (__pos++, ((__base[(__pos-1)>>3] & (0x80 >> ((__pos-1)&7))) != 0))
+
+#define DECODE_DONE	;						\
+  }
+
+#define DECODE_PAUSE(b)							\
+    (b).Base = __base;							\
+    (b).pos = __pos;							\
+  }
+
+#define DECODE_SEEK(pos) __pos = (pos)
+
+#define ENCODE_SEEK(pos) __pos = (pos)
+
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_random.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_random.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_random.h	(revision 16561)
@@ -0,0 +1,166 @@
+/**************************************************************************
+ *
+ * bitio_m_random.h -- Macros for bitio to a file (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are slower than the ones in "mem" files. but 
+ *  with these routines you can mix reads and writes, or multiple writes,  on
+ *  the array of chars at the same time and guarantee them to work, also you 
+ *  can seek to a point and do a write. The decode and encode macros cannot 
+ *  detect when the end off the character is reached and just continue 
+ *  processing.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_RANDOM
+#define H_BITIO_M_RANDOM
+
+typedef struct random_bitio_state
+  {
+    FILE *File;
+    unsigned char *Buf;
+    unsigned long Base;
+    unsigned long Used;
+    unsigned long pos;
+    unsigned long len;
+    unsigned long sft;
+  }
+random_bitio_state;
+
+
+#define ENCODE_START(f,l)						\
+  {									\
+    register FILE *__file = f;						\
+    register unsigned char *__buf;					\
+    register unsigned long __pos = 0;					\
+    register unsigned long __base = 0;					\
+    register unsigned long __is_used = 0;					\
+    register unsigned long __len = (l)-1;				\
+    register unsigned long __sft = 0;					\
+    while (__len) { ++__sft; __len >>=1; }				\
+    __len = 1<<__sft;							\
+    __buf = Xmalloc(__len);						\
+    fseek(__file, 0, 0);						\
+    fread(__buf, 1, __len, __file);
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    register FILE *__file = (b).File;					\
+    register unsigned char *__buf = (b).Buf;				\
+    register unsigned long __pos = (b).pos;				\
+    register unsigned long __base = (b).Base;				\
+    register unsigned long __is_used = (b).Used;				\
+    register unsigned long __len = (b).len;				\
+    register unsigned long __sft = (b).sft;
+
+#define SEEK fprintf(stderr, "Seek to %d\n",__base)
+#define READ fprintf(stderr, "Read of %d\n",__len)
+#define WRITE fprintf(stderr, "Write of %d\n",__is_used)
+
+#define WRITE_READ 							\
+	(__is_used ? (fseek(__file, __base, 0),				\
+                   fwrite(__buf, 1, __len, __file)) : 0,		\
+        __base += __len,						\
+	fseek(__file, __base, 0),					\
+        fread(__buf, 1, __len, __file),					\
+	__pos = 0, __is_used = 0)
+
+#define ENCODE_BIT(b)							\
+  do {									\
+    if (b)								\
+      __buf[__pos>>3] |= 0x80 >> (__pos&7);				\
+    else								\
+      __buf[__pos>>3] &= 0xff7f >> (__pos&7);				\
+    __is_used = 1;								\
+    ++__pos;								\
+    if ((__pos>>3) >= __len)						\
+      (void)WRITE_READ;							\
+  } while(0)
+
+#define ENCODE_PAUSE(b)							\
+    (b).File = __file;							\
+    (b).Buf = __buf;							\
+    (b).pos = __pos;							\
+    (b).Base = __base;							\
+    (b).Used = __is_used;							\
+    (b).len = __len;							\
+    (b).sft = __sft;							\
+  }
+
+#define ENCODE_FLUSH							\
+  if (__is_used)								\
+    {									\
+      fseek(__file, __base, 0);						\
+      fwrite(__buf, 1, __len, __file);					\
+      __is_used = 0;							\
+    }
+
+
+
+#define ENCODE_DONE							\
+    ENCODE_FLUSH;							\
+    free(__buf);							\
+  }
+
+
+#define DECODE_START(f,l) ENCODE_START(f,l)
+
+#define DECODE_CONTINUE(b) ENCODE_CONTINUE(b)
+
+#define DECODE_BIT 							\
+    (__pos++, (((__pos-1)>>3) >= __len ? WRITE_READ, __pos=1 : 0),	\
+     ((__buf[(__pos-1)>>3] & (0x80 >> ((__pos-1)&7))) != 0))
+
+#define DECODE_ADD_FF(b) ((b) += (b) + DECODE_BIT)
+
+#define DECODE_ADD_00(b) DECODE_ADD_FF(b)
+
+#define DECODE_DONE ENCODE_DONE
+
+#define DECODE_PAUSE(b)	ENCODE_PAUSE(b)
+
+#define ENCODE_SEEK(pos) 						\
+  do {									\
+    if ((((pos)>>3) >= __base) && ((((pos)+7)>>3) < (__base+__len)))   	\
+      __pos = (pos)-(__base << 3); 					\
+    else								\
+      {									\
+        ENCODE_FLUSH;							\
+        __base = (pos) >> 3;						\
+        __base = ((pos)  >> (__sft+3)) << __sft;			\
+        fseek(__file, __base, 0);					\
+        fread(__buf, 1, __len, __file);					\
+        __pos = (pos)&7; 						\
+        __pos = (pos)&((8 << __sft)-1);					\
+      }									\
+  } while(0)
+
+
+#define DECODE_SEEK(pos) ENCODE_SEEK(pos)
+
+#define ENCODE_TELL ((__base << 3) + __pos)
+
+#define DECODE_TELL ENCODE_TELL
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_stdio.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_stdio.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_m_stdio.h	(revision 16561)
@@ -0,0 +1,145 @@
+/**************************************************************************
+ *
+ * bitio_m_stdio.h -- Macros for bitio to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on a FILE*.
+ *  With these routines you cannot mix reads and writes on the FILE,
+ *  or multiple writes, at the same time and guarantee them to work, also you 
+ *  cannot seek to a point and do a write. The decode routine can detect when
+ *  you run off the end of the file and will produce an approate error message.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_STDIO
+#define H_BITIO_M_STDIO
+
+typedef struct stdio_bitio_state
+  {
+    FILE *File;
+    unsigned char Buff;
+    unsigned char Btg;
+  }
+stdio_bitio_state;
+
+
+
+#ifndef DECODE_ERROR
+#define DECODE_ERROR (fprintf(stderr,"Unexpected EOF in \"%s\" on line %d\n",\
+				__FILE__, __LINE__), exit(1))
+#endif
+
+
+
+#define ENCODE_START(f)							\
+  {									\
+    FILE *__outfile = f;						\
+    register unsigned char __buff = 0;					\
+    register unsigned char __btg = sizeof(__buff)*8;
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    FILE *__outfile = (b).File;						\
+    register unsigned char __buff = (b).Buff;				\
+    register unsigned char __btg = (b).Btg;
+
+#define ENCODE_BIT(b)							\
+  do {									\
+    --__btg;								\
+    if (b) __buff |= (1 << __btg);					\
+    if (!__btg)								\
+      {									\
+	putc(__buff, __outfile);					\
+	__buff = 0;							\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+  } while(0)
+
+#define ENCODE_PAUSE(b)							\
+    (b).File = __outfile;						\
+    (b).Buff = __buff;							\
+    (b).Btg =  __btg;							\
+  }
+
+#define ENCODE_DONE							\
+    if (__btg != sizeof(__buff)*8)					\
+      putc(__buff,__outfile);						\
+  }
+
+
+#define DECODE_START(f)							\
+  {									\
+    FILE *__infile = f;							\
+    register unsigned char __buff = 0;					\
+    register unsigned char __btg = 0;
+
+#define DECODE_CONTINUE(b)						\
+  {									\
+    FILE *__infile = (b).File;						\
+    register unsigned char __buff = (b).Buff;				\
+    register unsigned char __btg = (b).Btg;
+
+#define DECODE_ADD_FF(b)						\
+  do {									\
+    if (!__btg)								\
+      {									\
+	register int val = getc(__infile);				\
+	__buff = val == EOF ? 0xff : val;				\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+    (b) += (b) + ((__buff >> --__btg) & 1);				\
+  } while(0)
+
+#define DECODE_ADD_00(b)						\
+  do {									\
+    if (!__btg)								\
+      {									\
+	register int val = getc(__infile);				\
+	__buff = val == EOF ? 0xff : val;				\
+	__btg = sizeof(__buff)*8;					\
+      }									\
+    (b) += (b) + ((__buff >> --__btg) & 1);				\
+  } while(0)
+
+#define DECODE_BIT (__btg ? (__buff >> --__btg) & 1 :			\
+   (__buff = getc(__infile), (feof(__infile) ?				\
+    (DECODE_ERROR, 0) :							\
+    (__btg = sizeof(__buff)*8, (__buff >> --__btg) & 1))))
+
+#define DECODE_DONE	;						\
+  }
+
+#define DECODE_PAUSE(b)							\
+    (b).File = __infile;						\
+    (b).Buff = __buff;							\
+    (b).Btg =  __btg;							\
+  }
+
+
+#define DECODE_SEEK(pos)						\
+  do {									\
+    register long __new_pos = (pos);					\
+    fseek(__infile, __new_pos >> 3, 0);					\
+    __buff = getc(__infile);						\
+    __btg = 8 - (__new_pos&7);						\
+    }									\
+  while(0)
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.c	(revision 16561)
@@ -0,0 +1,237 @@
+/**************************************************************************
+ *
+ * bitio_mem.c -- Functions for bitio to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "bitio_m_mem.h"
+#include "bitio_m.h"
+
+int fprintf (FILE *, const char *,...);
+
+
+
+void 
+BIO_Mem_Encode_Start (void *buf, int rem, mem_bitio_state * bs)
+{
+  ENCODE_START (buf, rem)
+    ENCODE_PAUSE (*bs)
+}
+
+void 
+BIO_Mem_Encode_Done (mem_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_DONE
+}
+
+
+void 
+BIO_Mem_Decode_Start (void *buf, int rem, mem_bitio_state * bs)
+{
+  DECODE_START (buf, rem)
+    DECODE_PAUSE (*bs)
+}
+
+
+
+
+void 
+BIO_Mem_Unary_Encode (unsigned long val, mem_bitio_state * bs,
+		      unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_ENCODE_L (val, *bits);
+  else
+    UNARY_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Unary_Decode (mem_bitio_state * bs,
+		      unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_DECODE_L (val, *bits);
+  else
+    UNARY_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Mem_Binary_Encode (unsigned long val, unsigned long b,
+		       mem_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_ENCODE_L (val, b, *bits);
+  else
+    BINARY_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Binary_Decode (unsigned long b, mem_bitio_state * bs,
+		       unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_DECODE_L (val, b, *bits);
+  else
+    BINARY_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Mem_Gamma_Encode (unsigned long val, mem_bitio_state * bs,
+		      unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_ENCODE_L (val, *bits);
+  else
+    GAMMA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Gamma_Decode (mem_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_DECODE_L (val, *bits);
+  else
+    GAMMA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Mem_Delta_Encode (unsigned long val, mem_bitio_state * bs,
+		      unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_ENCODE_L (val, *bits);
+  else
+    DELTA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Delta_Decode (mem_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_DECODE_L (val, *bits);
+  else
+    DELTA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mem_Elias_Encode (unsigned long val, unsigned long b, double s,
+		      mem_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_ENCODE_L (val, b, s, *bits);
+  else
+    ELIAS_ENCODE (val, b, s);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Elias_Decode (unsigned long b, double s,
+		      mem_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_DECODE_L (val, b, s, *bits);
+  else
+    ELIAS_DECODE (val, b, s);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mem_Bblock_Encode (unsigned long val, unsigned long b,
+		       mem_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_ENCODE_L (val, b, *bits);
+  else
+    BBLOCK_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mem_Bblock_Decode (unsigned long b, mem_bitio_state * bs,
+		       unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_DECODE_L (val, b, *bits);
+  else
+    BBLOCK_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mem_Decode_Seek (unsigned long pos, mem_bitio_state * bs)
+{
+  DECODE_CONTINUE (*bs)
+    DECODE_SEEK (pos);
+  DECODE_PAUSE (*bs)
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mem.h	(revision 16561)
@@ -0,0 +1,103 @@
+/**************************************************************************
+ *
+ * bitio_mem.h -- Functions for bitio to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing bitwise input and output
+ *  of numbers on an array of chars. These routines are faster than the ones 
+ *  in "mems" files. But with these routines you cannot mix reads and writes 
+ *  on the array of chars, or multiple write, at the same time and guarantee
+ *  them to work, also you cannot seek to a point and do a write. The decode
+ *  routine can detect when you run off the end of the array and will produce
+ *  an approate error message, and the encode routine will stop when it gets
+ *  to the end of the character array. 
+ *
+ *
+ **************************************************************************/
+
+
+#ifndef H_BITIO_MEM
+#define H_BITIO_MEM
+
+
+
+typedef struct mem_bitio_state
+  {
+    unsigned char *Base;
+    unsigned char *Pos;
+    int Remaining;
+    unsigned char Buff;
+    unsigned char Btg;
+  }
+mem_bitio_state;
+
+
+/* NOTE : All bytes are filled high bit first */
+
+
+void BIO_Mem_Encode_Start (void *buf, int rem, mem_bitio_state * bs);
+void BIO_Mem_Encode_Done (mem_bitio_state * bs);
+
+
+
+void BIO_Mem_Decode_Start (void *buf, int rem, mem_bitio_state * bs);
+
+
+
+void BIO_Mem_Unary_Encode (unsigned long val, mem_bitio_state * bs,
+			   unsigned long *bits);
+unsigned long BIO_Mem_Unary_Decode (mem_bitio_state * bs,
+				    unsigned long *bits);
+
+
+
+void BIO_Mem_Binary_Encode (unsigned long val, unsigned long b,
+			    mem_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mem_Binary_Decode (unsigned long b, mem_bitio_state * bs,
+				     unsigned long *bits);
+
+
+
+void BIO_Mem_Gamma_Encode (unsigned long val, mem_bitio_state * bs,
+			   unsigned long *bits);
+unsigned long BIO_Mem_Gamma_Decode (mem_bitio_state * bs, unsigned long *bits);
+
+
+
+void BIO_Mem_Delta_Encode (unsigned long val, mem_bitio_state * bs,
+			   unsigned long *bits);
+unsigned long BIO_Mem_Delta_Decode (mem_bitio_state * bs, unsigned long *bits);
+
+
+void BIO_Mem_Elias_Encode (unsigned long val, unsigned long b, double s,
+			   mem_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mem_Elias_Decode (unsigned long b, double s,
+				 mem_bitio_state * bs, unsigned long *bits);
+
+
+void BIO_Mem_Bblock_Encode (unsigned long val, unsigned long b,
+			    mem_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mem_Bblock_Decode (unsigned long b, mem_bitio_state * bs,
+				     unsigned long *bits);
+
+
+void BIO_Mem_Decode_Seek (unsigned long pos, mem_bitio_state * bs);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.c	(revision 16561)
@@ -0,0 +1,247 @@
+/**************************************************************************
+ *
+ * bitio_mems.c -- Functions for bitio to memory (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "bitio_m_mems.h"
+#include "bitio_m.h"
+
+int fprintf (FILE *, const char *,...);
+
+
+
+void 
+BIO_Mems_Encode_Start (unsigned char *buf, unsigned long pos,
+		       mems_bitio_state * bs)
+{
+  ENCODE_START (buf, pos)
+    ENCODE_PAUSE (*bs)
+}
+
+void 
+BIO_Mems_Encode_Done (mems_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_DONE
+}
+
+
+void 
+BIO_Mems_Decode_Start (unsigned char *buf, unsigned long pos,
+		       mems_bitio_state * bs)
+{
+  DECODE_START (buf, pos)
+    DECODE_PAUSE (*bs)
+}
+
+
+
+
+void 
+BIO_Mems_Unary_Encode (unsigned long val, mems_bitio_state * bs,
+		       unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_ENCODE_L (val, *bits);
+  else
+    UNARY_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Unary_Decode (mems_bitio_state * bs,
+		       unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_DECODE_L (val, *bits);
+  else
+    UNARY_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Mems_Binary_Encode (unsigned long val, unsigned long b,
+			mems_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_ENCODE_L (val, b, *bits);
+  else
+    BINARY_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Binary_Decode (unsigned long b, mems_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_DECODE_L (val, b, *bits);
+  else
+    BINARY_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Mems_Gamma_Encode (unsigned long val, mems_bitio_state * bs,
+		       unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_ENCODE_L (val, *bits);
+  else
+    GAMMA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Gamma_Decode (mems_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_DECODE_L (val, *bits);
+  else
+    GAMMA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Mems_Delta_Encode (unsigned long val, mems_bitio_state * bs,
+		       unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_ENCODE_L (val, *bits);
+  else
+    DELTA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Delta_Decode (mems_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_DECODE_L (val, *bits);
+  else
+    DELTA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mems_Elias_Encode (unsigned long val, unsigned long b, double s,
+		       mems_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_ENCODE_L (val, b, s, *bits);
+  else
+    ELIAS_ENCODE (val, b, s);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Elias_Decode (unsigned long b, double s,
+		       mems_bitio_state * bs, unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_DECODE_L (val, b, s, *bits);
+  else
+    ELIAS_DECODE (val, b, s);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mems_Bblock_Encode (unsigned long val, unsigned long b,
+			mems_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_ENCODE_L (val, b, *bits);
+  else
+    BBLOCK_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Mems_Bblock_Decode (unsigned long b, mems_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_DECODE_L (val, b, *bits);
+  else
+    BBLOCK_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Mems_Decode_Seek (unsigned long pos, mems_bitio_state * bs)
+{
+  DECODE_CONTINUE (*bs)
+    DECODE_SEEK (pos);
+  DECODE_PAUSE (*bs)
+}
+
+void 
+BIO_Mems_Encode_Seek (unsigned long pos, mems_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_SEEK (pos);
+  ENCODE_PAUSE (*bs)
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_mems.h	(revision 16561)
@@ -0,0 +1,97 @@
+/**************************************************************************
+ *
+ * bitio_mems.h -- Functions for bitio to memory (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing bitwise input and output
+ *  of numbers on an array of chars. These routines are slower than the ones 
+ *  in "mem" files. but with these routines you can mix reads and writes, or 
+ *  multiple writes,  on the array of chars at the same time and guarantee
+ *  them to work, also you can seek to a point and do a write. The decode and 
+ *  encode functions cannot detect when the end off the character is reached
+ *  and just continue processing.
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_MEMS
+#define H_BITIO_MEMS
+
+
+
+typedef struct mems_bitio_state
+  {
+    unsigned char *Base;
+    unsigned long pos;
+  }
+mems_bitio_state;
+
+
+/* NOTE : All bytes are filled high bit first */
+
+
+void BIO_Mems_Encode_Start (char *buf, unsigned long pos, mems_bitio_state * bs);
+void BIO_Mems_Encode_Done (mems_bitio_state * bs);
+
+
+
+void BIO_Mems_Decode_Start (void *buf, unsigned long pos, mems_bitio_state * bs);
+
+
+
+void BIO_Mems_Unary_Encode (unsigned long val, mems_bitio_state * bs,
+			    unsigned long *bits);
+unsigned long BIO_Mems_Unary_Decode (mems_bitio_state * bs,
+				     unsigned long *bits);
+
+
+
+void BIO_Mems_Binary_Encode (unsigned long val, unsigned long b,
+			     mems_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mems_Binary_Decode (unsigned long b, mems_bitio_state * bs,
+				      unsigned long *bits);
+
+
+
+void BIO_Mems_Gamma_Encode (unsigned long val, mems_bitio_state * bs,
+			    unsigned long *bits);
+unsigned long BIO_Mems_Gamma_Decode (mems_bitio_state * bs, unsigned long *bits);
+
+
+
+void BIO_Mems_Delta_Encode (unsigned long val, mems_bitio_state * bs,
+			    unsigned long *bits);
+unsigned long BIO_Mems_Delta_Decode (mems_bitio_state * bs, unsigned long *bits);
+
+
+void BIO_Mems_Elias_Encode (unsigned long val, unsigned long b, double s,
+			    mems_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mems_Elias_Decode (unsigned long b, double s,
+				mems_bitio_state * bs, unsigned long *bits);
+
+
+void BIO_Mems_Bblock_Encode (unsigned long val, unsigned long b,
+			     mems_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Mems_Bblock_Decode (unsigned long b, mems_bitio_state * bs,
+				      unsigned long *bits);
+
+
+void BIO_Mems_Decode_Seek (unsigned long pos, mems_bitio_state * bs);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.c	(revision 16561)
@@ -0,0 +1,305 @@
+/*************************************************************************
+ *
+ * bitio_random.c -- Functions for bitio to a file (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+#include "longlong.h"
+
+#include "bitio_m_random.h"
+#include "bitio_m.h"
+
+
+void 
+BIO_Random_Start (FILE * f, unsigned long len,
+		  random_bitio_state * bs)
+{
+  ENCODE_START (f, len)
+    ENCODE_PAUSE (*bs)
+}
+
+void 
+BIO_Random_Done (random_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_DONE
+}
+
+void 
+BIO_Random_Encode_Bit (int bit, random_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_BIT (bit);
+  ENCODE_PAUSE (*bs)
+}
+
+int 
+BIO_Random_Decode_Bit (random_bitio_state * bs)
+{
+  register int val;
+  DECODE_CONTINUE (*bs)
+    val = DECODE_BIT;
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+void 
+BIO_Random_Unary_Encode (unsigned long val, random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_ENCODE_L (val, *bits);
+  else
+    UNARY_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Unary_Decode (random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_DECODE_L (val, *bits);
+  else
+    UNARY_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Random_Binary_Encode (unsigned long val, unsigned long b,
+			  random_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_ENCODE_L (val, b, *bits);
+  else
+    BINARY_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Binary_Decode (unsigned long b,
+			  random_bitio_state * bs,
+			  unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_DECODE_L (val, b, *bits);
+  else
+    BINARY_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Random_Gamma_Encode (unsigned long val, random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_ENCODE_L (val, *bits);
+  else
+    GAMMA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Gamma_Decode (random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_DECODE_L (val, *bits);
+  else
+    GAMMA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Random_Delta_Encode (unsigned long val, random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_ENCODE_L (val, *bits);
+  else
+    DELTA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Delta_Decode (random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_DECODE_L (val, *bits);
+  else
+    DELTA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Random_Elias_Encode (unsigned long val, unsigned long b, double s,
+			 random_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_ENCODE_L (val, b, s, *bits);
+  else
+    ELIAS_ENCODE (val, b, s);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Elias_Decode (unsigned long b, double s,
+			 random_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_DECODE_L (val, b, s, *bits);
+  else
+    ELIAS_DECODE (val, b, s);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Random_Bblock_Encode (unsigned long val, unsigned long b,
+			  random_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_ENCODE_L (val, b, *bits);
+  else
+    BBLOCK_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Random_Bblock_Decode (unsigned long b,
+			  random_bitio_state * bs,
+			  unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_DECODE_L (val, b, *bits);
+  else
+    BBLOCK_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Random_Seek (unsigned long pos, random_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_SEEK (pos);
+  ENCODE_PAUSE (*bs)
+}
+void 
+BIO_Random_Flush (random_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_FLUSH;
+  ENCODE_PAUSE (*bs)
+}
+
+unsigned long 
+BIO_Random_Tell (random_bitio_state * bs)
+{
+  register unsigned long t;
+  ENCODE_CONTINUE (*bs)
+    t = ENCODE_TELL;
+  ENCODE_PAUSE (*bs)
+    return (t);
+}
+
+#ifdef USE_LONG_LONG
+
+void 
+BIO_Random_Seek_LL (mg_ullong pos, random_bitio_state * bs)
+{
+  ENCODE_CONTINUE(*bs)
+
+    if ((((pos) >> 3) >= (mg_ullong)__base) &&
+	((((pos)+7) >> 3) < (mg_ullong)(__base + __len)))
+      {
+	__pos = (long)((pos) - (mg_ullong)(__base << 3));
+      }
+    else
+      {
+        ENCODE_FLUSH;
+	__base = (long)(((pos) >> (__sft+3)) << __sft);
+	
+	fseek(__file,__base,0);
+	fread(__buf,1,__len,__file);
+	__pos = (long)((pos) & ((8 << __sft)-1));
+      }
+  
+  ENCODE_PAUSE(*bs)
+}
+
+mg_ullong 
+BIO_Random_Tell_LL (random_bitio_state * bs)
+{
+  mg_ullong t;
+  ENCODE_CONTINUE(*bs)
+    t = (((mg_ullong)__base) << 3ull) + __pos;
+  ENCODE_PAUSE(*bs)
+  return(t);  
+}
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_random.h	(revision 16561)
@@ -0,0 +1,125 @@
+/**************************************************************************
+ *
+ * bitio_random.h -- Functions for bitio to a file (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing bitwise input and output
+ *  of numbers on an array of chars. These routines are slower than the ones 
+ *  in "mem" files. but with these routines you can mix reads and writes, or 
+ *  multiple writes,  on the array of chars at the same time and guarantee
+ *  them to work, also you can seek to a point and do a write. The decode and 
+ *  encode functions cannot detect when the end off the character is reached
+ *  and just continue processing.
+ *
+ *  Modified:
+ *   - long long seek and tell ops
+ *     (1999-08-03 Tim Bell <bhat@cs.mu.oz.au>)
+ **************************************************************************/
+
+#ifndef H_BITIO_RANDOM
+#define H_BITIO_RANDOM
+
+#include "longlong.h"
+
+
+typedef struct random_bitio_state
+  {
+    FILE *File;
+    unsigned char *Buf;
+    unsigned long Base;
+    unsigned long Used;
+    unsigned long pos;
+    unsigned long len;
+    unsigned long sft;
+  }
+random_bitio_state;
+
+
+/* NOTE : All bytes are filled high bit first */
+
+
+void BIO_Random_Start (FILE * f, unsigned long len,
+		       random_bitio_state * bs);
+void BIO_Random_Done (random_bitio_state * bs);
+
+
+
+void BIO_Random_Decode_Start (void *buf, unsigned long pos,
+			      random_bitio_state * bs);
+
+void BIO_Random_Encode_Bit (int bit, random_bitio_state * bs);
+
+int BIO_Random_Decode_Bit (random_bitio_state * bs);
+
+
+void BIO_Random_Unary_Encode (unsigned long val, random_bitio_state * bs,
+			      unsigned long *bits);
+unsigned long BIO_Random_Unary_Decode (random_bitio_state * bs,
+				       unsigned long *bits);
+
+
+
+void BIO_Random_Binary_Encode (unsigned long val, unsigned long b,
+			       random_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Random_Binary_Decode (unsigned long b, random_bitio_state * bs,
+					unsigned long *bits);
+
+
+
+void BIO_Random_Gamma_Encode (unsigned long val, random_bitio_state * bs,
+			      unsigned long *bits);
+unsigned long BIO_Random_Gamma_Decode (random_bitio_state * bs,
+				       unsigned long *bits);
+
+
+
+void BIO_Random_Delta_Encode (unsigned long val, random_bitio_state * bs,
+			      unsigned long *bits);
+unsigned long BIO_Random_Delta_Decode (random_bitio_state * bs,
+				       unsigned long *bits);
+
+
+void BIO_Random_Elias_Encode (unsigned long val, unsigned long b, double s,
+			      random_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Random_Elias_Decode (unsigned long b, double s,
+				       random_bitio_state * bs,
+				       unsigned long *bits);
+
+
+void BIO_Random_Bblock_Encode (unsigned long val, unsigned long b,
+			       random_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Random_Bblock_Decode (unsigned long b,
+					random_bitio_state * bs,
+					unsigned long *bits);
+
+
+void BIO_Random_Seek (unsigned long pos, random_bitio_state * bs);
+
+void BIO_Random_Flush (random_bitio_state * bs);
+
+unsigned long BIO_Random_Tell (random_bitio_state * bs);
+
+#ifdef USE_LONG_LONG
+
+void BIO_Random_Seek_LL (mg_ullong pos, random_bitio_state * bs);
+mg_ullong BIO_Random_Tell_LL (random_bitio_state * bs);
+
+#endif /* USE_LONG_LONG */
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.c	(revision 16561)
@@ -0,0 +1,262 @@
+/**************************************************************************
+ *
+ * bitio_stdio.c -- Functions for bitio to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "bitio_m_stdio.h"
+#include "bitio_m.h"
+
+
+
+void 
+BIO_Stdio_Encode_Start (FILE * f, stdio_bitio_state * bs)
+{
+  ENCODE_START (f)
+    ENCODE_PAUSE (*bs)
+}
+
+void 
+BIO_Stdio_Encode_Done (stdio_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_DONE
+}
+
+
+void 
+BIO_Stdio_Decode_Start (FILE * f, stdio_bitio_state * bs)
+{
+  DECODE_START (f)
+    DECODE_PAUSE (*bs)
+}
+
+void 
+BIO_Stdio_Encode_Bit (int bit, stdio_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    ENCODE_BIT (bit);
+  ENCODE_PAUSE (*bs)
+}
+
+
+int 
+BIO_Stdio_Decode_Bit (stdio_bitio_state * bs)
+{
+  register int val;
+  DECODE_CONTINUE (*bs)
+    val = DECODE_BIT;
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Stdio_Unary_Encode (unsigned long val, stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_ENCODE_L (val, *bits);
+  else
+    UNARY_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Unary_Decode (stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    UNARY_DECODE_L (val, *bits);
+  else
+    UNARY_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Stdio_Binary_Encode (unsigned long val, unsigned long b,
+			 stdio_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_ENCODE_L (val, b, *bits);
+  else
+    BINARY_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Binary_Decode (unsigned long b, stdio_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BINARY_DECODE_L (val, b, *bits);
+  else
+    BINARY_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+
+
+
+void 
+BIO_Stdio_Gamma_Encode (unsigned long val, stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_ENCODE_L (val, *bits);
+  else
+    GAMMA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Gamma_Decode (stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    GAMMA_DECODE_L (val, *bits);
+  else
+    GAMMA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Stdio_Delta_Encode (unsigned long val, stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_ENCODE_L (val, *bits);
+  else
+    DELTA_ENCODE (val);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Delta_Decode (stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    DELTA_DECODE_L (val, *bits);
+  else
+    DELTA_DECODE (val);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+
+void 
+BIO_Stdio_Elias_Encode (unsigned long val, unsigned long b, double s,
+			stdio_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_ENCODE_L (val, b, s, *bits);
+  else
+    ELIAS_ENCODE (val, b, s);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Elias_Decode (unsigned long b, double s,
+			stdio_bitio_state * bs,
+			unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    ELIAS_DECODE_L (val, b, s, *bits);
+  else
+    ELIAS_DECODE (val, b, s);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+
+
+void 
+BIO_Stdio_Bblock_Encode (unsigned long val, unsigned long b,
+			 stdio_bitio_state * bs, unsigned long *bits)
+{
+  ENCODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_ENCODE_L (val, b, *bits);
+  else
+    BBLOCK_ENCODE (val, b);
+  ENCODE_PAUSE (*bs)
+}
+
+
+unsigned long 
+BIO_Stdio_Bblock_Decode (unsigned long b, stdio_bitio_state * bs,
+			 unsigned long *bits)
+{
+  register unsigned long val;
+  DECODE_CONTINUE (*bs)
+    if (bits)
+    BBLOCK_DECODE_L (val, b, *bits);
+  else
+    BBLOCK_DECODE (val, b);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
+
+void 
+BIO_Stdio_Decode_Seek (unsigned long pos, stdio_bitio_state * bs)
+{
+  DECODE_CONTINUE (*bs)
+    DECODE_SEEK (pos);
+  DECODE_PAUSE (*bs)
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/bitio_stdio.h	(revision 16561)
@@ -0,0 +1,104 @@
+/**************************************************************************
+ *
+ * bitio_stdio.h -- Functions for bitio to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing bitwise input and output
+ *  of number on a FILE*. With these function you cannot mix reads and writes
+ *  on the FILE, or multiple writes, at the same time and guarantee them to 
+ *  work, also you  cannot seek to a point and do a write. The decode function
+ *  can detect when you run off the end of the file and will produce an 
+ *  approate error message.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_STDIO
+#define H_BITIO_STDIO
+
+
+typedef struct stdio_bitio_state
+  {
+    FILE *File;
+    unsigned char Buff;
+    unsigned char Btg;
+  }
+stdio_bitio_state;
+
+
+/* NOTE : All bytes are filled high bit first */
+
+
+void BIO_Stdio_Encode_Start (FILE * f, stdio_bitio_state * bs);
+void BIO_Stdio_Encode_Done (stdio_bitio_state * bs);
+
+
+
+void BIO_Stdio_Decode_Start (FILE * f, stdio_bitio_state * bs);
+
+
+
+void BIO_Stdio_Encode_Bit (int bit, stdio_bitio_state * bs);
+int BIO_Stdio_Decode_Bit (stdio_bitio_state * bs);
+
+
+void BIO_Stdio_Unary_Encode (unsigned long val, stdio_bitio_state * bs,
+			     unsigned long *bits);
+unsigned long BIO_Stdio_Unary_Decode (stdio_bitio_state * bs,
+				      unsigned long *bits);
+
+
+
+void BIO_Stdio_Binary_Encode (unsigned long val, unsigned long b,
+			      stdio_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Stdio_Binary_Decode (unsigned long b, stdio_bitio_state * bs,
+				       unsigned long *bits);
+
+
+
+void BIO_Stdio_Gamma_Encode (unsigned long val, stdio_bitio_state * bs,
+			     unsigned long *bits);
+unsigned long BIO_Stdio_Gamma_Decode (stdio_bitio_state * bs,
+				      unsigned long *bits);
+
+
+
+void BIO_Stdio_Delta_Encode (unsigned long val, stdio_bitio_state * bs,
+			     unsigned long *bits);
+unsigned long BIO_Stdio_Delta_Decode (stdio_bitio_state * bs,
+				      unsigned long *bits);
+
+
+
+void BIO_Stdio_Elias_Encode (unsigned long val, unsigned long b, double s,
+			     stdio_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Stdio_Elias_Decode (unsigned long b, double s,
+				      stdio_bitio_state * bs,
+				      unsigned long *bits);
+
+
+void BIO_Stdio_Bblock_Encode (unsigned long val, unsigned long b,
+			      stdio_bitio_state * bs, unsigned long *bits);
+unsigned long BIO_Stdio_Bblock_Decode (unsigned long b, stdio_bitio_state * bs,
+				       unsigned long *bits);
+
+
+void BIO_Stdio_Decode_Seek (unsigned long pos, stdio_bitio_state * bs);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/error.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/error.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/error.c	(revision 16561)
@@ -0,0 +1,127 @@
+/* error.c -- error handler for noninteractive utilities
+   Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#include <stdio.h>
+
+#if HAVE_VPRINTF || HAVE_DOPRNT
+# if __STDC__
+#  include <stdarg.h>
+#  define VA_START(args, lastarg) va_start(args, lastarg)
+# else
+#  include <varargs.h>
+#  define VA_START(args, lastarg) va_start(args)
+# endif
+#else
+# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else
+void exit ();
+#endif
+
+/* If NULL, error will flush stdout, then print on stderr the program
+   name, a colon and a space.  Otherwise, error will call this
+   function without parameters instead.  */
+void (*error_print_progname) () = NULL;
+
+/* The calling program should define program_name and set it to the
+   name of the executing program.  */
+#if 0 /* [RB/TS:10/95] do not use program_name */
+extern char *program_name;
+#endif
+
+#if HAVE_STRERROR
+char *strerror (int errnum);
+#else
+static char *
+private_strerror (errnum)
+     int errnum;
+{
+  extern char *sys_errlist[];
+  extern int sys_nerr;
+
+  if (errnum > 0 && errnum <= sys_nerr)
+    return sys_errlist[errnum];
+  return "Unknown system error";
+}
+#define strerror private_strerror
+#endif
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+   format string with optional args.
+   If ERRNUM is nonzero, print its corresponding system error message.
+   Exit with status STATUS if it is nonzero.  */
+/* VARARGS */
+
+void
+#if defined(VA_START) && __STDC__
+error (int status, int errnum, const char *message, ...)
+#else
+error (status, errnum, message, va_alist)
+     int status;
+     int errnum;
+     char *message;
+     va_dcl
+#endif
+{
+#ifdef VA_START
+  va_list args;
+#endif
+
+  if (error_print_progname)
+    (*error_print_progname) ();
+  else
+    {
+      fflush (stdout);
+#if 0 /* [RB/TS:10/95] do not use program_name */
+      fprintf (stderr, "%s: ", program_name);
+#endif
+    }
+
+#ifdef VA_START
+  VA_START (args, message);
+# if HAVE_VPRINTF
+  vfprintf (stderr, message, args);
+# else
+  _doprnt (message, args, stderr);
+# endif
+  va_end (args);
+#else
+  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+  if (errnum)
+    fprintf (stderr, ": %s", strerror (errnum));
+  putc ('\n', stderr);
+  fflush (stderr);
+  if (status)
+    exit (status);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/filestats.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/filestats.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/filestats.c	(revision 16561)
@@ -0,0 +1,115 @@
+/**************************************************************************
+ *
+ * filestats.c -- Functions for keeping stats on file accesses
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "filestats.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+File *
+Fopen (char *name, char *mode, unsigned long magic)
+{
+  FILE *f;
+  File *F;
+  unsigned long m;
+  if (!(f = fopen (name, mode)))
+    return (NULL);
+  if (magic)
+    switch (*mode)
+      {
+      case 'r':
+	fread ((char *) &m, sizeof (m), 1, f);
+	NTOHUL(m);  /* [RPAP - Jan 97: Endian Ordering] */
+	if (m != magic)
+	  {
+	    fclose (f);
+	    return (NULL);
+	  }
+	break;
+      case 'w':
+	HTONUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+	fwrite ((char *) &magic, sizeof (magic), 1, f);
+      }
+  if (!(F = Xmalloc (sizeof (File))))
+    {
+      fclose (f);
+      return (NULL);
+    }
+  F->pathname = Xstrdup (name);
+  F->name = strrchr (F->pathname, '/');
+  F->name = F->name ? F->name + 1 : F->pathname;
+  F->f = f;
+  F->Current.NumSeeks = F->Current.NumReads = F->Current.NumBytes = 0;
+  F->Cumulative = F->Current;
+  return (F);
+}
+
+
+int 
+Fclose (File * F)
+{
+  int num;
+  if (!F)
+    return (0);
+  num = fclose (F->f);
+  if (F->pathname)
+    Xfree (F->pathname);
+  Xfree (F);
+  return (num);
+}
+
+size_t 
+Fread (void *ptr, size_t size, size_t nitems, File * F)
+{
+  int num;
+  num = fread ((char *) ptr, size, nitems, F->f);
+  F->Current.NumReads++;
+  F->Current.NumBytes += num * size;
+  return (num);
+}
+
+int 
+Fseek (File * F, long offset, int ptrname)
+{
+  int num;
+  num = fseek (F->f, offset, ptrname);
+  F->Current.NumSeeks++;
+  return (num);
+}
+
+void 
+Rewind (File * F)
+{
+  rewind (F->f);
+  F->Current.NumSeeks++;
+}
+
+
+void 
+ZeroFileStats (File * F)
+{
+  F->Cumulative.NumSeeks += F->Current.NumSeeks;
+  F->Cumulative.NumReads += F->Current.NumReads;
+  F->Cumulative.NumBytes += F->Current.NumBytes;
+  F->Current.NumSeeks = F->Current.NumReads = F->Current.NumBytes = 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/filestats.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/filestats.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/filestats.h	(revision 16561)
@@ -0,0 +1,64 @@
+/**************************************************************************
+ *
+ * filestats.h -- Functions for keeping stats on file accesses
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_FILESTATS
+#define H_FILESTATS
+
+
+typedef struct FileStats
+  {
+    unsigned long NumSeeks;
+    unsigned long NumReads;
+    unsigned long NumBytes;
+  }
+FileStats;
+
+
+
+typedef struct
+  {
+    FILE *f;
+    char *pathname;
+    char *name;
+    FileStats Current;
+    FileStats Cumulative;
+  }
+File;
+
+/* if magic is 0 no magic number is read or written */
+File *Fopen (char *name, char *mode, unsigned long magic);
+
+size_t Fread (void *ptr, size_t size, size_t nitems, File * F);
+
+int Fseek (File * F, long offset, int ptrname);
+
+void Rewind (File * F);
+
+int Fclose (File * F);
+
+#define Getc(F) (F->Current.NumReads++, F->Current.NumBytes++, getc(F->f))
+
+/* This adds the Current file stats to the Cumulative stats and then zeros
+   the Current stats */
+void ZeroFileStats (File * F);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/ftruncate.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/ftruncate.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/ftruncate.c	(revision 16561)
@@ -0,0 +1,107 @@
+/* ftruncate emulations that work on some System V's.
+   This file is in the public domain.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef F_CHSIZE
+
+int
+ftruncate (fd, length)
+     int fd;
+     off_t length;
+{
+  return fcntl (fd, F_CHSIZE, length);
+}
+
+#else /* not F_CHSIZE */
+#ifdef F_FREESP
+
+/* By William Kucharski <kucharsk@netcom.com>.  */
+
+#include <sys/stat.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int
+ftruncate (fd, length)
+     int fd;
+     off_t length;
+{
+  struct flock fl;
+  struct stat filebuf;
+
+  if (fstat (fd, &filebuf) < 0)
+    return -1;
+
+  if (filebuf.st_size < length)
+    {
+      /* Extend file length. */
+      if (lseek (fd, (length - 1), SEEK_SET) < 0)
+	return -1;
+
+      /* Write a "0" byte. */
+      if (write (fd, "", 1) != 1)
+	return -1;
+    }
+  else
+    {
+
+      /* Truncate length. */
+
+      fl.l_whence = 0;
+      fl.l_len = 0;
+      fl.l_start = length;
+      fl.l_type = F_WRLCK;	/* write lock on file space */
+
+      /* This relies on the *undocumented* F_FREESP argument to fcntl,
+	 which truncates the file so that it ends at the position
+	 indicated by fl.l_start.  Will minor miracles never cease?  */
+
+      if (fcntl (fd, F_FREESP, &fl) < 0)
+	return -1;
+    }
+
+  return 0;
+}
+
+#else /* not F_CHSIZE nor F_FREESP */
+#ifdef HAVE_CHSIZE
+
+int
+ftruncate (fd, length)
+     int fd;
+     off_t length;
+{
+  return chsize (fd, length);
+}
+
+#else /* not F_CHSIZE nor F_FREESP nor HAVE_CHSIZE */
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+int
+ftruncate (fd, length)
+     int fd;
+     off_t length;
+{
+  errno = EIO;
+  return -1;
+}
+
+#endif /* not HAVE_CHSIZE */
+#endif /* not F_FREESP */
+#endif /* not F_CHSIZE */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/getopt.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/getopt.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/getopt.c	(revision 16561)
@@ -0,0 +1,750 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+   Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+#ifdef HAVE_CONFIG_H
+#include "sysfuncs.h"
+#endif
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif /* GNU C library.  */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+   long-named option.  Because this is not POSIX.2 compliant, it is
+   being phased out.  */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+  {
+    REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+  }
+ordering;
+
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* GNU C library.  */
+
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  int option_index;
+
+  optarg = 0;
+
+  /* Initialize the internal data when the first call is made.
+     Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  if (optind == 0)
+    {
+      first_nonopt = last_nonopt = optind = 1;
+
+      nextchar = NULL;
+
+      /* Determine how to handle the ordering of options and nonoptions.  */
+
+      if (optstring[0] == '-')
+	{
+	  ordering = RETURN_IN_ORDER;
+	  ++optstring;
+	}
+      else if (optstring[0] == '+')
+	{
+	  ordering = REQUIRE_ORDER;
+	  ++optstring;
+	}
+      else if (getenv ("POSIXLY_CORRECT") != NULL)
+	ordering = REQUIRE_ORDER;
+      else
+	ordering = PERMUTE;
+    }
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Now skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc
+		 && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+		 && (longopts == NULL
+		     || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+	    )
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* Special ARGV-element `--' means premature end of options.
+         Skip it like a null option,
+         then exchange with previous non-options as if it were an option,
+         then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+         and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return EOF;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+         either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+	  && (longopts == NULL
+	      || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+	)
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return EOF;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+         Start decoding its characters.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  if (longopts != NULL
+      && ((argv[optind][0] == '-'
+	   && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+	  || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+      ))
+    {
+      const struct option *p;
+      char *s = nextchar;
+      int exact = 0;
+      int ambig = 0;
+      const struct option *pfound = NULL;
+      int indfound;
+
+      while (*s && *s != '=')
+	s++;
+
+      /* Test all options for either exact match or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name;
+	   p++, option_index++)
+	if (!strncmp (p->name, nextchar, s - nextchar))
+	  {
+	    if (s - nextchar == strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, "%s: option `%s' is ambiguous\n",
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*s)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+	         allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = s + 1;
+	      else
+		{
+		  if (opterr)
+		    {
+		      if (argv[optind - 1][1] == '-')
+			/* --option */
+			fprintf (stderr,
+			    "%s: option `--%s' doesn't allow an argument\n",
+				 argv[0], pfound->name);
+		      else
+			/* +option or -option */
+			fprintf (stderr,
+			    "%s: option `%c%s' doesn't allow an argument\n",
+				 argv[0], argv[optind - 1][0], pfound->name);
+		    }
+		  nextchar += strlen (nextchar);
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr, "%s: option `%s' requires an argument\n",
+			     argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+         or the option starts with '--' or is not a valid short
+         option, then it's an error.
+         Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+	  || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, "%s: unrecognized option `--%s'\n",
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+#if 0
+	    if (c < 040 || c >= 0177)
+	      fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+		       argv[0], c);
+	    else
+	      fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+	    /* 1003.2 specifies the format of this message.  */
+	    fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+	  }
+	optopt = c;
+	return '?';
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = 0;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+#if 0
+		    fprintf (stderr, "%s: option `-%c' requires an argument\n",
+			     argv[0], c);
+#else
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr, "%s: option requires an argument -- %c\n",
+			     argv[0], c);
+#endif
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+	         increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/getopt.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/getopt.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/getopt.h	(revision 16561)
@@ -0,0 +1,130 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+  extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+  extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+  extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+  extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument          (or 0) if the option does not take an argument,
+   required_argument    (or 1) if the option requires an argument,
+   optional_argument    (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+  struct option
+    {
+#if	__STDC__
+      const char *name;
+#else
+      char *name;
+#endif
+      /* has_arg can't be an enum because some compilers complain about
+         type mismatches in all the code that assumes it is an int.  */
+      int has_arg;
+      int *flag;
+      int val;
+    };
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define	no_argument		0
+#define required_argument	1
+#define optional_argument	2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+  extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else				/* not __GNU_LIBRARY__ */
+  extern int getopt ();
+#endif				/* not __GNU_LIBRARY__ */
+  extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+			  const struct option *longopts, int *longind);
+  extern int getopt_long_only (int argc, char *const *argv,
+			       const char *shortopts,
+			       const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+  extern int _getopt_internal (int argc, char *const *argv,
+			       const char *shortopts,
+			       const struct option *longopts, int *longind,
+			       int long_only);
+#else				/* not __STDC__ */
+  extern int getopt ();
+  extern int getopt_long ();
+  extern int getopt_long_only ();
+
+  extern int _getopt_internal ();
+#endif				/* not __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif				/* _GETOPT_H */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/getopt1.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/getopt1.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/getopt1.c	(revision 16561)
@@ -0,0 +1,178 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+   Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+#ifdef HAVE_CONFIG_H
+#include "sysfuncs.h"
+#endif
+
+#include "getopt.h"
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"add", 1, 0, 0},
+	{"append", 0, 0, 0},
+	{"delete", 1, 0, 0},
+	{"verbose", 0, 0, 0},
+	{"create", 0, 0, 0},
+	{"file", 1, 0, 0},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+		       long_options, &option_index);
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case 0:
+	  printf ("option %s", long_options[option_index].name);
+	  if (optarg)
+	    printf (" with arg %s", optarg);
+	  printf ("\n");
+	  break;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case 'd':
+	  printf ("option d with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/getpagesize.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/getpagesize.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/getpagesize.h	(revision 16561)
@@ -0,0 +1,38 @@
+/* Emulate getpagesize on systems that lack it.  */
+
+#ifndef HAVE_GETPAGESIZE
+
+#ifdef VMS
+#define getpagesize() 512
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _SC_PAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#else
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifndef __WIN32__
+#include <sys/param.h>
+#endif
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#ifdef NBPC
+#define getpagesize() NBPC
+#endif /* NBPC */
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+#endif /* no _SC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/gmalloc.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/gmalloc.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/gmalloc.c	(revision 16561)
@@ -0,0 +1,1318 @@
+/* DO NOT EDIT THIS FILE -- it is automagically generated.  -*- C -*- */
+
+#define _MALLOC_INTERNAL
+
+/* The malloc headers and source files from the C library follow here.  */
+
+/* Declarations for `malloc' and friends.
+   Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef _MALLOC_H
+
+#define _MALLOC_H	1
+
+#ifdef _MALLOC_INTERNAL
+
+#ifdef	HAVE_CONFIG_H
+#include <sysfuncs.h>
+#endif
+
+#if	defined(_LIBC) || defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#else
+#ifndef memset
+#define	memset(s, zero, n)	bzero ((s), (n))
+#endif
+#ifndef memcpy
+#define	memcpy(d, s, n)		bcopy ((s), (d), (n))
+#endif
+#endif
+
+#if	defined(__GNU_LIBRARY__) || __STDC__
+#include <limits.h>
+#else
+#define	CHAR_BIT	8
+#endif
+
+#ifdef	HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#endif	/* _MALLOC_INTERNAL.  */
+
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+#if defined (__cplusplus) || __STDC__
+#undef	__P
+#define	__P(args)	args
+#undef	__ptr_t
+#define	__ptr_t		void *
+#else /* Not C++ or ANSI C.  */
+#undef	__P
+#define	__P(args)	()
+#undef	const
+#define	const
+#undef	__ptr_t
+#define	__ptr_t		char *
+#endif /* C++ or ANSI C.  */
+
+#if	__STDC__
+#include <stddef.h>
+#else
+#undef	size_t
+#define	size_t		unsigned int
+#undef	ptrdiff_t
+#define	ptrdiff_t	int
+#endif
+
+#ifndef	NULL
+#define	NULL	0
+#endif
+
+
+/* Allocate SIZE bytes of memory.  */
+extern __ptr_t malloc __P ((size_t __size));
+/* Re-allocate the previously allocated block
+   in __ptr_t, making the new block SIZE bytes long.  */
+extern __ptr_t realloc __P ((__ptr_t __ptr, size_t __size));
+/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0.  */
+extern __ptr_t calloc __P ((size_t __nmemb, size_t __size));
+/* Free a block allocated by `malloc', `realloc' or `calloc'.  */
+extern void free __P ((__ptr_t __ptr));
+
+/* Allocate SIZE bytes allocated to ALIGNMENT bytes.  */
+extern __ptr_t memalign __P ((size_t __alignment, size_t __size));
+
+/* Allocate SIZE bytes on a page boundary.  */
+extern __ptr_t valloc __P ((size_t __size));
+
+
+#ifdef _MALLOC_INTERNAL
+
+/* The allocator divides the heap into blocks of fixed size; large
+   requests receive one or more whole blocks, and small requests
+   receive a fragment of a block.  Fragment sizes are powers of two,
+   and all fragments of a block are the same size.  When all the
+   fragments in a block have been freed, the block itself is freed.  */
+#define INT_BIT		(CHAR_BIT * sizeof(int))
+#define BLOCKLOG	(INT_BIT > 16 ? 12 : 9)
+#define BLOCKSIZE	(1 << BLOCKLOG)
+#define BLOCKIFY(SIZE)	(((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
+
+/* Determine the amount of memory spanned by the initial heap table
+   (not an absolute limit).  */
+#define HEAP		(INT_BIT > 16 ? 4194304 : 65536)
+
+/* Number of contiguous free blocks allowed to build up at the end of
+   memory before they will be returned to the system.  */
+#define FINAL_FREE_BLOCKS	8
+
+/* Data structure giving per-block information.  */
+typedef union
+  {
+    /* Heap information for a busy block.  */
+    struct
+      {
+	/* Zero for a large block, or positive giving the
+	   logarithm to the base two of the fragment size.  */
+	int type;
+	union
+	  {
+	    struct
+	      {
+		size_t nfree;	/* Free fragments in a fragmented block.  */
+		size_t first;	/* First free fragment of the block.  */
+	      } frag;
+	    /* Size (in blocks) of a large cluster.  */
+	    size_t size;
+	  } info;
+      } busy;
+    /* Heap information for a free block
+       (that may be the first of a free cluster).  */
+    struct
+      {
+	size_t size;		/* Size (in blocks) of a free cluster.  */
+	size_t next;		/* Index of next free cluster.  */
+	size_t prev;		/* Index of previous free cluster.  */
+      } free;
+  } malloc_info;
+
+/* Pointer to first block of the heap.  */
+extern char *_heapbase;
+
+/* Table indexed by block number giving per-block information.  */
+extern malloc_info *_heapinfo;
+
+/* Address to block number and vice versa.  */
+#define BLOCK(A)	(((char *) (A) - _heapbase) / BLOCKSIZE + 1)
+#define ADDRESS(B)	((__ptr_t) (((B) - 1) * BLOCKSIZE + _heapbase))
+
+/* Current search index for the heap table.  */
+extern size_t _heapindex;
+
+/* Limit of valid info table indices.  */
+extern size_t _heaplimit;
+
+/* Doubly linked lists of free fragments.  */
+struct list
+  {
+    struct list *next;
+    struct list *prev;
+  };
+
+/* Free list headers for each fragment size.  */
+extern struct list _fraghead[];
+
+/* List of blocks allocated with `memalign' (or `valloc').  */
+struct alignlist
+  {
+    struct alignlist *next;
+    __ptr_t aligned;		/* The address that memaligned returned.  */
+    __ptr_t exact;		/* The address that malloc returned.  */
+  };
+extern struct alignlist *_aligned_blocks;
+
+/* Instrumentation.  */
+extern size_t _chunks_used;
+extern size_t _bytes_used;
+extern size_t _chunks_free;
+extern size_t _bytes_free;
+
+/* Internal version of `free' used in `morecore' (malloc.c). */
+extern void _free_internal __P ((__ptr_t __ptr));
+
+#endif /* _MALLOC_INTERNAL.  */
+
+/* Underlying allocation function; successive calls should
+   return contiguous pieces of memory.  */
+extern __ptr_t (*__morecore) __P ((ptrdiff_t __size));
+
+/* Default value of `__morecore'.  */
+extern __ptr_t __default_morecore __P ((ptrdiff_t __size));
+
+/* If not NULL, this function is called after each time
+   `__morecore' is called to increase the data size.  */
+extern void (*__after_morecore_hook) __P ((void));
+
+/* Nonzero if `malloc' has been called and done its initialization.  */
+extern int __malloc_initialized;
+
+/* Hooks for debugging versions.  */
+extern void (*__free_hook) __P ((__ptr_t __ptr));
+extern __ptr_t (*__malloc_hook) __P ((size_t __size));
+extern __ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
+
+/* Return values for `mprobe': these are the kinds of inconsistencies that
+   `mcheck' enables detection of.  */
+enum mcheck_status
+  {
+    MCHECK_DISABLED = -1,	/* Consistency checking is not turned on.  */
+    MCHECK_OK,			/* Block is fine.  */
+    MCHECK_FREE,		/* Block freed twice.  */
+    MCHECK_HEAD,		/* Memory before the block was clobbered.  */
+    MCHECK_TAIL			/* Memory after the block was clobbered.  */
+  };
+
+/* Activate a standard collection of debugging hooks.  This must be called
+   before `malloc' is ever called.  ABORTFUNC is called with an error code
+   (see enum above) when an inconsistency is detected.  If ABORTFUNC is
+   null, the standard function prints on stderr and then calls `abort'.  */
+extern int mcheck __P ((void (*__abortfunc) __P ((enum mcheck_status))));
+
+/* Check for aberrations in a particular malloc'd block.  You must have
+   called `mcheck' already.  These are the same checks that `mcheck' does
+   when you free or reallocate a block.  */
+extern enum mcheck_status mprobe __P ((__ptr_t __ptr));
+
+/* Activate a standard collection of tracing hooks.  */
+extern void mtrace __P ((void));
+
+/* Statistics available to the user.  */
+struct mstats
+  {
+    size_t bytes_total;		/* Total size of the heap. */
+    size_t chunks_used;		/* Chunks allocated by the user. */
+    size_t bytes_used;		/* Byte total of user-allocated chunks. */
+    size_t chunks_free;		/* Chunks in the free list. */
+    size_t bytes_free;		/* Byte total of chunks in the free list. */
+  };
+
+/* Pick up the current statistics. */
+extern struct mstats mstats __P ((void));
+
+/* Call WARNFUN with a warning message when memory usage is high.  */
+extern void memory_warnings __P ((__ptr_t __start,
+				  void (*__warnfun) __P ((const char *))));
+
+
+/* Relocating allocator.  */
+
+/* Allocate SIZE bytes, and store the address in *HANDLEPTR.  */
+extern __ptr_t r_alloc __P ((__ptr_t *__handleptr, size_t __size));
+
+/* Free the storage allocated in HANDLEPTR.  */
+extern void r_alloc_free __P ((__ptr_t *__handleptr));
+
+/* Adjust the block at HANDLEPTR to be SIZE bytes long.  */
+extern __ptr_t r_re_alloc __P ((__ptr_t *__handleptr, size_t __size));
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* malloc.h  */
+/* Allocate memory on a page boundary.
+   Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#if defined (__GNU_LIBRARY__) || defined (_LIBC)
+#include <stddef.h>
+#include <sys/cdefs.h>
+extern size_t __getpagesize __P ((void));
+#else
+#include "getpagesize.h"
+#define	 __getpagesize()	getpagesize()
+#endif
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+static size_t pagesize;
+
+__ptr_t
+valloc (size)
+     size_t size;
+{
+  if (pagesize == 0)
+    pagesize = __getpagesize ();
+
+  return memalign (pagesize, size);
+}
+/* Memory allocator `malloc'.
+   Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* How to really get more memory.  */
+__ptr_t (*__morecore) __P ((ptrdiff_t __size)) = __default_morecore;
+
+/* Debugging hook for `malloc'.  */
+__ptr_t (*__malloc_hook) __P ((size_t __size));
+
+/* Pointer to the base of the first block.  */
+char *_heapbase;
+
+/* Block information table.  Allocated with align/__free (not malloc/free).  */
+malloc_info *_heapinfo;
+
+/* Number of info entries.  */
+static size_t heapsize;
+
+/* Search index in the info table.  */
+size_t _heapindex;
+
+/* Limit of valid info table indices.  */
+size_t _heaplimit;
+
+/* Free lists for each fragment size.  */
+struct list _fraghead[BLOCKLOG];
+
+/* Instrumentation.  */
+size_t _chunks_used;
+size_t _bytes_used;
+size_t _chunks_free;
+size_t _bytes_free;
+
+/* Are you experienced?  */
+int __malloc_initialized;
+
+void (*__after_morecore_hook) __P ((void));
+
+/* Aligned allocation.  */
+static __ptr_t align __P ((size_t));
+static __ptr_t
+align (size)
+     size_t size;
+{
+  __ptr_t result;
+  unsigned long int adj;
+
+  result = (*__morecore) (size);
+  adj = (unsigned long int) ((unsigned long int) ((char *) result -
+						  (char *) NULL)) % BLOCKSIZE;
+  if (adj != 0)
+    {
+      adj = BLOCKSIZE - adj;
+      (void) (*__morecore) (adj);
+      result = (char *) result + adj;
+    }
+
+  if (__after_morecore_hook)
+    (*__after_morecore_hook) ();
+
+  return result;
+}
+
+/* Set everything up and remember that we have.  */
+static int initialize __P ((void));
+static int
+initialize ()
+{
+  heapsize = HEAP / BLOCKSIZE;
+  _heapinfo = (malloc_info *) align (heapsize * sizeof (malloc_info));
+  if (_heapinfo == NULL)
+    return 0;
+  memset (_heapinfo, 0, heapsize * sizeof (malloc_info));
+  _heapinfo[0].free.size = 0;
+  _heapinfo[0].free.next = _heapinfo[0].free.prev = 0;
+  _heapindex = 0;
+  _heapbase = (char *) _heapinfo;
+
+  /* Account for the _heapinfo block itself in the statistics.  */
+  _bytes_used = heapsize * sizeof (malloc_info);
+  _chunks_used = 1;
+
+  __malloc_initialized = 1;
+  return 1;
+}
+
+/* Get neatly aligned memory, initializing or
+   growing the heap info table as necessary. */
+static __ptr_t morecore __P ((size_t));
+static __ptr_t
+morecore (size)
+     size_t size;
+{
+  __ptr_t result;
+  malloc_info *newinfo, *oldinfo;
+  size_t newsize;
+
+  result = align (size);
+  if (result == NULL)
+    return NULL;
+
+  /* Check if we need to grow the info table.  */
+  if ((size_t) BLOCK ((char *) result + size) > heapsize)
+    {
+      newsize = heapsize;
+      while ((size_t) BLOCK ((char *) result + size) > newsize)
+	newsize *= 2;
+      newinfo = (malloc_info *) align (newsize * sizeof (malloc_info));
+      if (newinfo == NULL)
+	{
+	  (*__morecore) (-size);
+	  return NULL;
+	}
+      memcpy (newinfo, _heapinfo, heapsize * sizeof (malloc_info));
+      memset (&newinfo[heapsize], 0,
+	      (newsize - heapsize) * sizeof (malloc_info));
+      oldinfo = _heapinfo;
+      newinfo[BLOCK (oldinfo)].busy.type = 0;
+      newinfo[BLOCK (oldinfo)].busy.info.size
+	= BLOCKIFY (heapsize * sizeof (malloc_info));
+      _heapinfo = newinfo;
+      /* Account for the _heapinfo block itself in the statistics.  */
+      _bytes_used += newsize * sizeof (malloc_info);
+      ++_chunks_used;
+      _free_internal (oldinfo);
+      heapsize = newsize;
+    }
+
+  _heaplimit = BLOCK ((char *) result + size);
+  return result;
+}
+
+/* Allocate memory from the heap.  */
+__ptr_t
+malloc (size)
+     size_t size;
+{
+  __ptr_t result;
+  size_t block, blocks, lastblocks, start;
+  register size_t i;
+  struct list *next;
+
+  /* ANSI C allows `malloc (0)' to either return NULL, or to return a
+     valid address you can realloc and free (though not dereference).
+
+     It turns out that some extant code (sunrpc, at least Ultrix's version)
+     expects `malloc (0)' to return non-NULL and breaks otherwise.
+     Be compatible.  */
+
+#if	0
+  if (size == 0)
+    return NULL;
+#endif
+
+  if (__malloc_hook != NULL)
+    return (*__malloc_hook) (size);
+
+  if (!__malloc_initialized)
+    if (!initialize ())
+      return NULL;
+
+  if (size < sizeof (struct list))
+      size = sizeof (struct list);
+
+#ifdef SUNOS_LOCALTIME_BUG
+  if (size < 16)
+    size = 16;
+#endif
+
+  /* Determine the allocation policy based on the request size.  */
+  if (size <= BLOCKSIZE / 2)
+    {
+      /* Small allocation to receive a fragment of a block.
+	 Determine the logarithm to base two of the fragment size. */
+      register size_t log = 1;
+      --size;
+      while ((size /= 2) != 0)
+	++log;
+
+      /* Look in the fragment lists for a
+	 free fragment of the desired size. */
+      next = _fraghead[log].next;
+      if (next != NULL)
+	{
+	  /* There are free fragments of this size.
+	     Pop a fragment out of the fragment list and return it.
+	     Update the block's nfree and first counters. */
+	  result = (__ptr_t) next;
+	  next->prev->next = next->next;
+	  if (next->next != NULL)
+	    next->next->prev = next->prev;
+	  block = BLOCK (result);
+	  if (--_heapinfo[block].busy.info.frag.nfree != 0)
+	    _heapinfo[block].busy.info.frag.first = (unsigned long int)
+	      ((unsigned long int) ((char *) next->next - (char *) NULL)
+	       % BLOCKSIZE) >> log;
+
+	  /* Update the statistics.  */
+	  ++_chunks_used;
+	  _bytes_used += 1 << log;
+	  --_chunks_free;
+	  _bytes_free -= 1 << log;
+	}
+      else
+	{
+	  /* No free fragments of the desired size, so get a new block
+	     and break it into fragments, returning the first.  */
+	  result = malloc (BLOCKSIZE);
+	  if (result == NULL)
+	    return NULL;
+
+	  /* Link all fragments but the first into the free list.  */
+	  for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i)
+	    {
+	      next = (struct list *) ((char *) result + (i << log));
+	      next->next = _fraghead[log].next;
+	      next->prev = &_fraghead[log];
+	      next->prev->next = next;
+	      if (next->next != NULL)
+		next->next->prev = next;
+	    }
+
+	  /* Initialize the nfree and first counters for this block.  */
+	  block = BLOCK (result);
+	  _heapinfo[block].busy.type = log;
+	  _heapinfo[block].busy.info.frag.nfree = i - 1;
+	  _heapinfo[block].busy.info.frag.first = i - 1;
+
+	  _chunks_free += (BLOCKSIZE >> log) - 1;
+	  _bytes_free += BLOCKSIZE - (1 << log);
+	  _bytes_used -= BLOCKSIZE - (1 << log);
+	}
+    }
+  else
+    {
+      /* Large allocation to receive one or more blocks.
+	 Search the free list in a circle starting at the last place visited.
+	 If we loop completely around without finding a large enough
+	 space we will have to get more memory from the system.  */
+      blocks = BLOCKIFY (size);
+      start = block = _heapindex;
+      while (_heapinfo[block].free.size < blocks)
+	{
+	  block = _heapinfo[block].free.next;
+	  if (block == start)
+	    {
+	      /* Need to get more from the system.  Check to see if
+		 the new core will be contiguous with the final free
+		 block; if so we don't need to get as much.  */
+	      block = _heapinfo[0].free.prev;
+	      lastblocks = _heapinfo[block].free.size;
+	      if (_heaplimit != 0 && block + lastblocks == _heaplimit &&
+		  (*__morecore) (0) == ADDRESS (block + lastblocks) &&
+		  (morecore ((blocks - lastblocks) * BLOCKSIZE)) != NULL)
+		{
+ 		  /* Which block we are extending (the `final free
+ 		     block' referred to above) might have changed, if
+ 		     it got combined with a freed info table.  */
+ 		  block = _heapinfo[0].free.prev;
+  		  _heapinfo[block].free.size += (blocks - lastblocks);
+		  _bytes_free += (blocks - lastblocks) * BLOCKSIZE;
+		  continue;
+		}
+	      result = morecore (blocks * BLOCKSIZE);
+	      if (result == NULL)
+		return NULL;
+	      block = BLOCK (result);
+	      _heapinfo[block].busy.type = 0;
+	      _heapinfo[block].busy.info.size = blocks;
+	      ++_chunks_used;
+	      _bytes_used += blocks * BLOCKSIZE;
+	      return result;
+	    }
+	}
+
+      /* At this point we have found a suitable free list entry.
+	 Figure out how to remove what we need from the list. */
+      result = ADDRESS (block);
+      if (_heapinfo[block].free.size > blocks)
+	{
+	  /* The block we found has a bit left over,
+	     so relink the tail end back into the free list. */
+	  _heapinfo[block + blocks].free.size
+	    = _heapinfo[block].free.size - blocks;
+	  _heapinfo[block + blocks].free.next
+	    = _heapinfo[block].free.next;
+	  _heapinfo[block + blocks].free.prev
+	    = _heapinfo[block].free.prev;
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapindex = block + blocks;
+	}
+      else
+	{
+	  /* The block exactly matches our requirements,
+	     so just remove it from the list. */
+	  _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapinfo[block].free.prev;
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapindex = _heapinfo[block].free.next;
+	  --_chunks_free;
+	}
+
+      _heapinfo[block].busy.type = 0;
+      _heapinfo[block].busy.info.size = blocks;
+      ++_chunks_used;
+      _bytes_used += blocks * BLOCKSIZE;
+      _bytes_free -= blocks * BLOCKSIZE;
+    }
+
+  return result;
+}
+
+
+#ifndef _LIBC
+
+/* On some ANSI C systems, some libc functions call _malloc, _free
+   and _realloc.  Make them use the GNU functions.  */
+
+__ptr_t
+_malloc (size)
+     size_t size;
+{
+  return malloc (size);
+}
+
+void
+_free (ptr)
+     __ptr_t ptr;
+{
+  free (ptr);
+}
+
+__ptr_t
+_realloc (ptr, size)
+     __ptr_t ptr;
+     size_t size;
+{
+  return realloc (ptr, size);
+}
+
+#endif
+/* Free a block of memory allocated by `malloc'.
+   Copyright 1990, 1991, 1992 Free Software Foundation
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* Debugging hook for free.  */
+void (*__free_hook) __P ((__ptr_t __ptr));
+
+/* List of blocks allocated by memalign.  */
+struct alignlist *_aligned_blocks = NULL;
+
+/* Return memory to the heap.
+   Like `free' but don't call a __free_hook if there is one.  */
+void
+_free_internal (ptr)
+     __ptr_t ptr;
+{
+  int type;
+  size_t block, blocks;
+  register size_t i;
+  struct list *prev, *next;
+
+  block = BLOCK (ptr);
+
+  type = _heapinfo[block].busy.type;
+  switch (type)
+    {
+    case 0:
+      /* Get as many statistics as early as we can.  */
+      --_chunks_used;
+      _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE;
+      _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE;
+
+      /* Find the free cluster previous to this one in the free list.
+	 Start searching at the last block referenced; this may benefit
+	 programs with locality of allocation.  */
+      i = _heapindex;
+      if (i > block)
+	while (i > block)
+	  i = _heapinfo[i].free.prev;
+      else
+	{
+	  do
+	    i = _heapinfo[i].free.next;
+	  while (i > 0 && i < block);
+	  i = _heapinfo[i].free.prev;
+	}
+
+      /* Determine how to link this block into the free list.  */
+      if (block == i + _heapinfo[i].free.size)
+	{
+	  /* Coalesce this block with its predecessor.  */
+	  _heapinfo[i].free.size += _heapinfo[block].busy.info.size;
+	  block = i;
+	}
+      else
+	{
+	  /* Really link this block back into the free list.  */
+	  _heapinfo[block].free.size = _heapinfo[block].busy.info.size;
+	  _heapinfo[block].free.next = _heapinfo[i].free.next;
+	  _heapinfo[block].free.prev = i;
+	  _heapinfo[i].free.next = block;
+	  _heapinfo[_heapinfo[block].free.next].free.prev = block;
+	  ++_chunks_free;
+	}
+
+      /* Now that the block is linked in, see if we can coalesce it
+	 with its successor (by deleting its successor from the list
+	 and adding in its size).  */
+      if (block + _heapinfo[block].free.size == _heapinfo[block].free.next)
+	{
+	  _heapinfo[block].free.size
+	    += _heapinfo[_heapinfo[block].free.next].free.size;
+	  _heapinfo[block].free.next
+	    = _heapinfo[_heapinfo[block].free.next].free.next;
+	  _heapinfo[_heapinfo[block].free.next].free.prev = block;
+	  --_chunks_free;
+	}
+
+      /* Now see if we can return stuff to the system.  */
+      blocks = _heapinfo[block].free.size;
+      if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit
+	  && (*__morecore) (0) == ADDRESS (block + blocks))
+	{
+	  register size_t bytes = blocks * BLOCKSIZE;
+	  _heaplimit -= blocks;
+	  (*__morecore) (-bytes);
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapinfo[block].free.next;
+	  _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapinfo[block].free.prev;
+	  block = _heapinfo[block].free.prev;
+	  --_chunks_free;
+	  _bytes_free -= bytes;
+	}
+
+      /* Set the next search to begin at this block.  */
+      _heapindex = block;
+      break;
+
+    default:
+      /* Do some of the statistics.  */
+      --_chunks_used;
+      _bytes_used -= 1 << type;
+      ++_chunks_free;
+      _bytes_free += 1 << type;
+
+      /* Get the address of the first free fragment in this block.  */
+      prev = (struct list *) ((char *) ADDRESS (block) +
+			   (_heapinfo[block].busy.info.frag.first << type));
+
+      if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1)
+	{
+	  /* If all fragments of this block are free, remove them
+	     from the fragment list and free the whole block.  */
+	  next = prev;
+	  for (i = 1; i < (size_t) (BLOCKSIZE >> type); ++i)
+	    next = next->next;
+	  prev->prev->next = next;
+	  if (next != NULL)
+	    next->prev = prev->prev;
+	  _heapinfo[block].busy.type = 0;
+	  _heapinfo[block].busy.info.size = 1;
+
+	  /* Keep the statistics accurate.  */
+	  ++_chunks_used;
+	  _bytes_used += BLOCKSIZE;
+	  _chunks_free -= BLOCKSIZE >> type;
+	  _bytes_free -= BLOCKSIZE;
+
+	  free (ADDRESS (block));
+	}
+      else if (_heapinfo[block].busy.info.frag.nfree != 0)
+	{
+	  /* If some fragments of this block are free, link this
+	     fragment into the fragment list after the first free
+	     fragment of this block. */
+	  next = (struct list *) ptr;
+	  next->next = prev->next;
+	  next->prev = prev;
+	  prev->next = next;
+	  if (next->next != NULL)
+	    next->next->prev = next;
+	  ++_heapinfo[block].busy.info.frag.nfree;
+	}
+      else
+	{
+	  /* No fragments of this block are free, so link this
+	     fragment into the fragment list and announce that
+	     it is the first free fragment of this block. */
+	  prev = (struct list *) ptr;
+	  _heapinfo[block].busy.info.frag.nfree = 1;
+	  _heapinfo[block].busy.info.frag.first = (unsigned long int)
+	    ((unsigned long int) ((char *) ptr - (char *) NULL)
+	     % BLOCKSIZE >> type);
+	  prev->next = _fraghead[type].next;
+	  prev->prev = &_fraghead[type];
+	  prev->prev->next = prev;
+	  if (prev->next != NULL)
+	    prev->next->prev = prev;
+	}
+      break;
+    }
+}
+
+/* Return memory to the heap.  */
+void
+free (ptr)
+     __ptr_t ptr;
+{
+  register struct alignlist *l;
+
+  if (ptr == NULL)
+    return;
+
+  for (l = _aligned_blocks; l != NULL; l = l->next)
+    if (l->aligned == ptr)
+      {
+	l->aligned = NULL;	/* Mark the slot in the list as free.  */
+	ptr = l->exact;
+	break;
+      }
+
+  if (__free_hook != NULL)
+    (*__free_hook) (ptr);
+  else
+    _free_internal (ptr);
+}
+/* Copyright (C) 1991, 1993, 1994 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#ifdef _LIBC
+
+#include <ansidecl.h>
+#include <gnu-stabs.h>
+
+#undef	cfree
+
+function_alias(cfree, free, void, (ptr),
+	       DEFUN(cfree, (ptr), PTR ptr))
+
+#else
+
+void
+cfree (ptr)
+     __ptr_t ptr;
+{
+  free (ptr);
+}
+
+#endif
+/* Change the size of a block allocated by `malloc'.
+   Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+		     Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#if  (defined (MEMMOVE_MISSING) || \
+      !defined(_LIBC) && !defined(STDC_HEADERS) && !defined(USG))
+
+/* Snarfed directly from Emacs src/dispnew.c:
+   XXX Should use system bcopy if it handles overlap.  */
+#ifndef emacs
+
+/* Like bcopy except never gets confused by overlap.  */
+
+static void
+safe_bcopy (from, to, size)
+     char *from, *to;
+     int size;
+{
+  if (size <= 0 || from == to)
+    return;
+
+  /* If the source and destination don't overlap, then bcopy can
+     handle it.  If they do overlap, but the destination is lower in
+     memory than the source, we'll assume bcopy can handle that.  */
+  if (to < from || from + size <= to)
+    bcopy (from, to, size);
+
+  /* Otherwise, we'll copy from the end.  */
+  else
+    {
+      register char *endf = from + size;
+      register char *endt = to + size;
+
+      /* If TO - FROM is large, then we should break the copy into
+	 nonoverlapping chunks of TO - FROM bytes each.  However, if
+	 TO - FROM is small, then the bcopy function call overhead
+	 makes this not worth it.  The crossover point could be about
+	 anywhere.  Since I don't think the obvious copy loop is too
+	 bad, I'm trying to err in its favor.  */
+      if (to - from < 64)
+	{
+	  do
+	    *--endt = *--endf;
+	  while (endf != from);
+	}
+      else
+	{
+	  for (;;)
+	    {
+	      endt -= (to - from);
+	      endf -= (to - from);
+
+	      if (endt < to)
+		break;
+
+	      bcopy (endf, endt, to - from);
+	    }
+
+	  /* If SIZE wasn't a multiple of TO - FROM, there will be a
+	     little left over.  The amount left over is
+	     (endt + (to - from)) - to, which is endt - from.  */
+	  bcopy (from, to, endt - from);
+	}
+    }
+}     
+#endif	/* Not emacs.  */
+
+#define memmove(to, from, size) safe_bcopy ((from), (to), (size))
+
+#endif
+
+
+#define min(A, B) ((A) < (B) ? (A) : (B))
+
+/* Debugging hook for realloc.  */
+__ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
+
+/* Resize the given region to the new size, returning a pointer
+   to the (possibly moved) region.  This is optimized for speed;
+   some benchmarks seem to indicate that greater compactness is
+   achieved by unconditionally allocating and copying to a
+   new region.  This module has incestuous knowledge of the
+   internals of both free and malloc. */
+__ptr_t
+realloc (ptr, size)
+     __ptr_t ptr;
+     size_t size;
+{
+  __ptr_t result;
+  int type;
+  size_t block, blocks, oldlimit;
+
+  if (size == 0)
+    {
+      free (ptr);
+      return malloc (0);
+    }
+  else if (ptr == NULL)
+    return malloc (size);
+
+  if (__realloc_hook != NULL)
+    return (*__realloc_hook) (ptr, size);
+
+  block = BLOCK (ptr);
+
+  type = _heapinfo[block].busy.type;
+  switch (type)
+    {
+    case 0:
+      /* Maybe reallocate a large block to a small fragment.  */
+      if (size <= BLOCKSIZE / 2)
+	{
+	  result = malloc (size);
+	  if (result != NULL)
+	    {
+	      memcpy (result, ptr, size);
+	      _free_internal (ptr);
+	      return result;
+	    }
+	}
+
+      /* The new size is a large allocation as well;
+	 see if we can hold it in place. */
+      blocks = BLOCKIFY (size);
+      if (blocks < _heapinfo[block].busy.info.size)
+	{
+	  /* The new size is smaller; return
+	     excess memory to the free list. */
+	  _heapinfo[block + blocks].busy.type = 0;
+	  _heapinfo[block + blocks].busy.info.size
+	    = _heapinfo[block].busy.info.size - blocks;
+	  _heapinfo[block].busy.info.size = blocks;
+	  /* We have just created a new chunk by splitting a chunk in two.
+	     Now we will free this chunk; increment the statistics counter
+	     so it doesn't become wrong when _free_internal decrements it.  */
+	  ++_chunks_used;
+	  _free_internal (ADDRESS (block + blocks));
+	  result = ptr;
+	}
+      else if (blocks == _heapinfo[block].busy.info.size)
+	/* No size change necessary.  */
+	result = ptr;
+      else
+	{
+	  /* Won't fit, so allocate a new region that will.
+	     Free the old region first in case there is sufficient
+	     adjacent free space to grow without moving. */
+	  blocks = _heapinfo[block].busy.info.size;
+	  /* Prevent free from actually returning memory to the system.  */
+	  oldlimit = _heaplimit;
+	  _heaplimit = 0;
+	  _free_internal (ptr);
+	  _heaplimit = oldlimit;
+	  result = malloc (size);
+	  if (result == NULL)
+	    {
+	      /* Now we're really in trouble.  We have to unfree
+		 the thing we just freed.  Unfortunately it might
+		 have been coalesced with its neighbors.  */
+	      if (_heapindex == block)
+	        (void) malloc (blocks * BLOCKSIZE);
+	      else
+		{
+		  __ptr_t previous = malloc ((block - _heapindex) * BLOCKSIZE);
+		  (void) malloc (blocks * BLOCKSIZE);
+		  _free_internal (previous);
+		}
+	      return NULL;
+	    }
+	  if (ptr != result)
+	    memmove (result, ptr, blocks * BLOCKSIZE);
+	}
+      break;
+
+    default:
+      /* Old size is a fragment; type is logarithm
+	 to base two of the fragment size.  */
+      if (size > (size_t) (1 << (type - 1)) && size <= (size_t) (1 << type))
+	/* The new size is the same kind of fragment.  */
+	result = ptr;
+      else
+	{
+	  /* The new size is different; allocate a new space,
+	     and copy the lesser of the new size and the old. */
+	  result = malloc (size);
+	  if (result == NULL)
+	    return NULL;
+	  memcpy (result, ptr, min (size, (size_t) 1 << type));
+	  free (ptr);
+	}
+      break;
+    }
+
+  return result;
+}
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* Allocate an array of NMEMB elements each SIZE bytes long.
+   The entire array is initialized to zeros.  */
+__ptr_t
+calloc (nmemb, size)
+     register size_t nmemb;
+     register size_t size;
+{
+  register __ptr_t result = malloc (nmemb * size);
+
+  if (result != NULL)
+    (void) memset (result, 0, nmemb * size);
+
+  return result;
+}
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library 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, or (at your option)
+any later version.
+
+The GNU C Library 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 the GNU C Library; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#ifndef	__GNU_LIBRARY__
+#define	__sbrk	sbrk
+#endif
+
+#ifdef __GNU_LIBRARY__
+/* It is best not to declare this and cast its result on foreign operating
+   systems with potentially hostile include files.  */
+extern __ptr_t __sbrk __P ((int increment));
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Allocate INCREMENT more bytes of data space,
+   and return the start of data space, or NULL on errors.
+   If INCREMENT is negative, shrink data space.  */
+__ptr_t
+__default_morecore (increment)
+     ptrdiff_t increment;
+{
+  __ptr_t result = (__ptr_t) __sbrk ((int) increment);
+  if (result == (__ptr_t) -1)
+    return NULL;
+  return result;
+}
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+__ptr_t
+memalign (alignment, size)
+     size_t alignment;
+     size_t size;
+{
+  __ptr_t result;
+  unsigned long int adj;
+
+  size = ((size + alignment - 1) / alignment) * alignment;
+
+  result = malloc (size);
+  if (result == NULL)
+    return NULL;
+  adj = (unsigned long int) ((unsigned long int) ((char *) result -
+						  (char *) NULL)) % alignment;
+  if (adj != 0)
+    {
+      struct alignlist *l;
+      for (l = _aligned_blocks; l != NULL; l = l->next)
+	if (l->aligned == NULL)
+	  /* This slot is free.  Use it.  */
+	  break;
+      if (l == NULL)
+	{
+	  l = (struct alignlist *) malloc (sizeof (struct alignlist));
+	  if (l == NULL)
+	    {
+	      free (result);
+	      return NULL;
+	    }
+	  l->next = _aligned_blocks;
+	  _aligned_blocks = l;
+	}
+      l->exact = result;
+      result = l->aligned = (char *) result + alignment - adj;
+    }
+
+  return result;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman.c	(revision 16561)
@@ -0,0 +1,406 @@
+/**************************************************************************
+ *
+ * huffman.c -- Huffman coding functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "huffman.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+/* ->data on success,  
+   NULL on error 
+ */
+huff_data *
+Generate_Huffman_Data (int num, long *freqs, huff_data * data,
+		       u_long * mem)
+{
+  int HNum, i, count;
+  unsigned long *heap;
+  huff_data *hd = data;
+  if (!hd)
+    {
+      if (!(hd = Xmalloc (sizeof (huff_data))))
+	goto error0;
+    }
+  if (!(heap = Xmalloc (num * 2 * sizeof (*heap))))
+    goto error1;
+  if (!(hd->clens = Xmalloc (num * sizeof (char))))
+      goto error2;
+  hd->num_codes = num;
+  if (mem)
+    {
+      if (hd != data)
+	*mem += sizeof (huff_data);
+      *mem += num * sizeof (char);
+    }
+  bcopy ((char *) freqs, (char *) &heap[num], num * sizeof (*heap));
+  bzero ((char *) heap, num * sizeof (*heap));
+
+  /* Initialise the pointers to the leaves */
+  for (count = i = 0; i < num; i++)
+    if (heap[num + i])
+      heap[count++] = num + i;
+
+  /* Reorganise the pointers so that it is a heap */
+  HNum = count;
+  for (i = HNum / 2; i > 0; i--)
+    {
+      register int curr, child;
+      curr = i;
+      child = curr * 2;
+      while (child <= HNum)
+	{
+	  if (child < HNum && heap[heap[child]] < heap[heap[child - 1]])
+	    child++;
+	  if (heap[heap[curr - 1]] > heap[heap[child - 1]])
+	    {
+	      register u_long temp;
+	      temp = heap[child - 1];
+	      heap[child - 1] = heap[curr - 1];
+	      heap[curr - 1] = temp;
+	      curr = child;
+	      child = 2 * curr;
+	    }
+	  else
+	    break;
+	}
+    }
+
+
+  /* Form the huffman tree */
+  while (HNum > 1)
+    {
+      int pos[2];
+
+      for (i = 0; i < 2; i++)
+	{
+	  register int curr, child;
+	  pos[i] = heap[0];
+	  heap[0] = heap[--HNum];
+	  curr = 1;
+	  child = 2;
+	  while (child <= HNum)
+	    {
+	      if (child < HNum &&
+		  heap[heap[child]] < heap[heap[child - 1]])
+		child++;
+	      if (heap[heap[curr - 1]] > heap[heap[child - 1]])
+		{
+		  register int temp;
+		  temp = heap[child - 1];
+		  heap[child - 1] = heap[curr - 1];
+		  heap[curr - 1] = temp;
+		  curr = child;
+		  child = 2 * curr;
+		}
+	      else
+		break;
+	    }
+	}
+
+      heap[HNum + 1] = heap[pos[0]] + heap[pos[1]];
+      heap[pos[0]] = heap[pos[1]] = HNum + 1;
+
+      heap[HNum] = HNum + 1;
+
+      {
+	register int parent, curr;
+	HNum++;
+	curr = HNum;
+	parent = curr >> 1;
+	while (parent && heap[heap[parent - 1]] > heap[heap[curr - 1]])
+	  {
+	    register int temp;
+	    temp = heap[parent - 1];
+	    heap[parent - 1] = heap[curr - 1];
+	    heap[curr - 1] = temp;
+	    curr = parent;
+	    parent = curr >> 1;
+	  }
+      }
+    }
+
+
+  /* Calculate the code lens */
+  heap[0] = -1UL;
+  heap[1] = 0;
+  for (i = 2; i < num * 2; i++)
+    heap[i] = heap[heap[i]] + 1;
+
+  /* zero the lencount's array */
+  bzero ((char *) hd->lencount, sizeof (hd->lencount));
+  hd->maxcodelen = 0;
+  hd->mincodelen = 100;
+
+  /* Set the code length of each leaf in the huffman tree */
+  for (i = 0; i < num; i++)
+    {
+      register u_long codelen = heap[i + num];
+      hd->clens[i] = (char) codelen;
+      if (!codelen)
+	continue;
+      if (codelen > hd->maxcodelen)
+	hd->maxcodelen = codelen;
+      if (codelen < hd->mincodelen)
+	hd->mincodelen = codelen;
+      hd->lencount[codelen]++;
+    }
+
+
+  if (hd->maxcodelen == 0 && hd->mincodelen == 100)
+    {
+      hd->num_codes = 0;
+    }
+  else
+    {
+      /* Calculate the current codes for each different code length */
+      hd->min_code[hd->maxcodelen] = 0;
+      for (i = hd->maxcodelen - 1; i>=0; i--)
+	hd->min_code[i] = (hd->min_code[i + 1] + hd->lencount[i + 1]) >> 1;
+    }
+  free (heap);
+  return (hd);
+
+error2:
+  free (heap);
+error1:
+  if (!data)
+    free (hd);
+error0:
+  return (NULL);
+
+}
+
+unsigned long *
+Generate_Huffman_Codes (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *codes;
+  unsigned long mc[MAX_HUFFCODE_LEN + 1];
+
+  if (!data)
+    return (NULL);
+  if (!(codes = Xmalloc (data->num_codes * sizeof (*codes))))
+    return (NULL);
+  if (mem)
+    *mem += data->num_codes * sizeof (*codes);
+  bcopy ((char *) data->min_code, (char *) mc, sizeof (mc));
+  for (i = 0; i < data->num_codes; i++)
+    if (data->clens[i])
+      codes[i] = mc[(int) (data->clens[i])]++;
+
+  return (codes);
+}
+
+
+unsigned long **
+Generate_Huffman_Vals (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *fcode[MAX_HUFFCODE_LEN + 1];
+  unsigned long **values;
+  unsigned long *vals;
+
+  if (!data)
+    return (NULL);
+  if (!(vals = Xmalloc (data->num_codes * sizeof (*vals))))
+    return (NULL);
+  if (!(values = Xmalloc ((MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *))))
+    {
+      free (vals);
+      return (NULL);
+    }
+
+  bzero ((char *) values, (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *));
+
+  if (mem)
+    *mem += data->num_codes * sizeof (*vals) +
+      (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+  fcode[0] = values[0] = &vals[0];
+  for (i = 1; i <= data->maxcodelen; i++)
+    fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]];
+
+  for (i = 0; i < data->num_codes; i++)
+    if (data->clens[i])
+      *fcode[(int) (data->clens[i])]++ = i;
+  return (values);
+}
+
+
+
+double 
+Calculate_Huffman_Size (int num, long *freqs, long *counts)
+{
+  double size = 0;
+  huff_data hd;
+  int i;
+  if (!Generate_Huffman_Data (num, freqs, &hd, NULL))
+    return -1;
+  for (i = 0; i < num; i++)
+    size += counts[i] * hd.clens[i];
+  Xfree (hd.clens);
+  return size;
+}
+
+
+
+int 
+Write_Huffman_Data (FILE * f, huff_data * hd)
+{
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONSI(hd->num_codes);
+  HTONSI(hd->mincodelen);
+  HTONSI(hd->maxcodelen);
+
+  if (fwrite (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1)
+    return -1;
+
+  if (fwrite (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1)
+    return -1;
+
+  if (fwrite (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(hd->num_codes);
+  NTOHSI(hd->mincodelen);
+  NTOHSI(hd->maxcodelen);
+
+  if (hd->num_codes)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int i;
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; i++)
+	HTONSI(hd->lencount[i]);
+      for (i = 0; i < hd->maxcodelen + 1; i++)
+	HTONUL(hd->min_code[i]);
+
+      if (fwrite (hd->lencount + hd->mincodelen, sizeof (*hd->lencount),
+		  hd->maxcodelen - hd->mincodelen + 1, f) !=
+	  hd->maxcodelen - hd->mincodelen + 1)
+	return -1;
+
+      if (fwrite (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) !=
+	  hd->maxcodelen + 1)
+	return -1;
+
+      if (fwrite (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; i++)
+	NTOHSI(hd->lencount[i]);
+      for (i = 0; i < hd->maxcodelen + 1; i++)
+	NTOHUL(hd->min_code[i]);
+    }
+  return 0;
+}
+
+
+
+
+static int 
+General_Read_Huffman_Data (size_t (*rd) (), void *f,
+			   huff_data * hd, u_long * mem, u_long * disk)
+{
+  if (rd (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1)
+    return -1;
+
+  if (rd (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1)
+    return -1;
+
+  if (rd (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(hd->num_codes);
+  NTOHSI(hd->mincodelen);
+  NTOHSI(hd->maxcodelen);
+
+  if (disk)
+    (*disk) += sizeof (hd->num_codes) + sizeof (hd->mincodelen) +
+      sizeof (hd->maxcodelen);
+
+  hd->clens = NULL;
+
+  if (hd->num_codes)
+    {
+      int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+      bzero ((char *) (hd->lencount), sizeof (hd->lencount));
+      if (rd (hd->lencount + hd->mincodelen, sizeof (*hd->lencount),
+	      hd->maxcodelen - hd->mincodelen + 1, f) !=
+	  hd->maxcodelen - hd->mincodelen + 1)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; i++)
+	NTOHSI(hd->lencount[i]);
+
+      if (disk)
+	(*disk) += sizeof (*hd->lencount) * (hd->maxcodelen - hd->mincodelen + 1);
+
+      bzero ((char *) (hd->min_code), sizeof (hd->min_code));
+
+      if (rd (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) !=
+	  hd->maxcodelen + 1)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = 0; i < hd->maxcodelen + 1; i++)
+	NTOHUL(hd->min_code[i]);
+
+      if (disk)
+	(*disk) += sizeof (*hd->min_code) * (hd->maxcodelen + 1);
+
+      if (!(hd->clens = Xmalloc (sizeof (*hd->clens) * hd->num_codes)))
+	return -1;
+
+
+      if (rd (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes)
+	return -1;
+
+      if (disk)
+	(*disk) += sizeof (*hd->clens) * hd->num_codes;
+    }
+
+  if (mem)
+    *mem += sizeof (*hd->clens) * hd->num_codes;
+  return 0;
+}
+
+
+
+int 
+Read_Huffman_Data (FILE * f, huff_data * hd, u_long * mem, u_long * disk)
+{
+  return General_Read_Huffman_Data (fread, f, hd, mem, disk);
+}
+
+
+
+
+int 
+F_Read_Huffman_Data (File * f, huff_data * hd, u_long * mem, u_long * disk)
+{
+  return General_Read_Huffman_Data (Fread, f, hd, mem, disk);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman.h	(revision 16561)
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * huffman.h -- Huffman coding functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_HUFFMAN
+#define H_HUFFMAN
+#include <sys/types.h>
+#include "filestats.h"
+
+#ifdef __WIN32__
+#include "sysfuncs.h"
+#endif
+
+#define MAX_HUFFCODE_LEN 31
+
+typedef struct huff_data
+  {
+    int num_codes;
+    int mincodelen;
+    int maxcodelen;
+    int lencount[MAX_HUFFCODE_LEN + 1];
+    unsigned long min_code[MAX_HUFFCODE_LEN + 1];
+    char *clens;
+  }
+huff_data;
+
+
+huff_data *Generate_Huffman_Data (int num, long *freqs, huff_data * data,
+				  u_long * mem);
+
+unsigned long *Generate_Huffman_Codes (huff_data * data, u_long * mem);
+
+unsigned long **Generate_Huffman_Vals (huff_data * data, u_long * mem);
+
+int Write_Huffman_Data (FILE * f, huff_data * hd);
+
+int Read_Huffman_Data (FILE * f, huff_data * hd, u_long * mem, u_long * disk);
+
+int F_Read_Huffman_Data (File * f, huff_data * hd, u_long * mem, u_long * disk);
+
+/* Calculate the number of bits required to code the data with the
+   specified frequencies. Normally freqs and counts should point to
+   the same array. */
+double Calculate_Huffman_Size (int num, long *freqs, long *counts);
+
+
+
+#define HUFF_ENCODE(x, codes, lens)					\
+  do {									\
+    register int __i;							\
+    register int __clen = (lens)[x];					\
+    register unsigned long __code = (codes)[x];				\
+    for (__i=__clen-1; __i>=0; --__i)					\
+      ENCODE_BIT((__code >> __i) & 1);					\
+  } while(0)
+
+#define HUFF_ENCODE_L(x, codes, lens, count)				\
+  do {									\
+    HUFF_ENCODE(x, codes, lens);		   			\
+    (count) += (lens)[x];						\
+  } while(0)
+
+
+#define HUFF_DECODE(x, mcodes, values)					\
+  do {									\
+    register unsigned long *__min_code = (mcodes);			\
+    register unsigned long *__mclen = __min_code;			\
+    register unsigned long **__values = (values);			\
+    register unsigned long __code = 0;					\
+    do									\
+      {									\
+        DECODE_ADD(__code);						\
+      }									\
+    while (__code < *++__mclen);					\
+    (x) = __values[__mclen - __min_code][__code - *__mclen];		\
+  } while(0);
+
+
+#define HUFF_DECODE_L(x, mcodes, values, count)				\
+  do {									\
+    register unsigned long *__min_code = (mcodes);			\
+    register unsigned long *__mclen = __min_code;			\
+    register unsigned long **__values = (values);			\
+    register unsigned long __code = 0;					\
+    do									\
+      {									\
+        DECODE_ADD(__code);						\
+	(count)++;							\
+      }									\
+    while (__code < *++__mclen);					\
+    (x) = __values[__mclen - __min_code][__code - *__mclen];		\
+  } while(0);
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.c	(revision 16561)
@@ -0,0 +1,49 @@
+/**************************************************************************
+ *
+ * huffman_mem.c -- Huffman coding functions to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "bitio_m.h"
+#include "bitio_m_mem.h"
+#include "huffman.h"
+
+#if 0
+int fprintf (FILE *, const char *,...);
+#endif
+
+void 
+BIO_Mem_Huff_Encode (unsigned long val, unsigned long *codes,
+		     char *clens, mem_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    HUFF_ENCODE (val, codes, clens);
+  ENCODE_PAUSE (*bs)
+}
+
+unsigned long 
+BIO_Mem_Huff_Decode (unsigned long *mincodes,
+		     unsigned long **values, mem_bitio_state * bs)
+{
+  unsigned long val;
+  DECODE_CONTINUE (*bs)
+    HUFF_DECODE (val, mincodes, values);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman_mem.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**************************************************************************
+ *
+ * huffman_mem.h -- Huffman coding functions to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_HUFFMAN_MEM
+#define H_HUFFMAN_MEM
+
+
+
+void BIO_Mem_Huff_Encode (unsigned long val, unsigned long *codes,
+			  char *clens, mem_bitio_state * bs);
+
+unsigned long BIO_Mem_Huff_Decode (unsigned long *mincodes,
+			      unsigned long **values, mem_bitio_state * bs);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.c	(revision 16561)
@@ -0,0 +1,53 @@
+/**************************************************************************
+ *
+ * huffman_stdio.c -- Huffman coding function to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "huffman.h"
+
+#if 0
+int fprintf (FILE *, const char *,...);
+int _filbuf (FILE *);
+int _flsbuf (unsigned, FILE *);
+#endif
+
+void 
+BIO_Stdio_Huff_Encode (unsigned long val, unsigned long *codes,
+		       char *clens, stdio_bitio_state * bs)
+{
+  ENCODE_CONTINUE (*bs)
+    HUFF_ENCODE (val, codes, clens);
+  ENCODE_PAUSE (*bs)
+}
+
+unsigned long 
+BIO_Stdio_Huff_Decode (unsigned long *mincodes,
+		       unsigned long **values,
+		       stdio_bitio_state * bs)
+{
+  unsigned long val;
+  DECODE_CONTINUE (*bs)
+    HUFF_DECODE (val, mincodes, values);
+  DECODE_PAUSE (*bs)
+    return (val);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/huffman_stdio.h	(revision 16561)
@@ -0,0 +1,36 @@
+/**************************************************************************
+ *
+ * huffman_stdio.h -- Huffman coding function to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_HUFFMAN_STDIO
+#define H_HUFFMAN_STDIO
+
+
+
+void BIO_Stdio_Huff_Encode (unsigned long val, unsigned long *codes,
+			    char *clens, stdio_bitio_state * bs);
+
+
+unsigned long BIO_Stdio_Huff_Decode (unsigned long *mincodes,
+				     unsigned long **values,
+				     stdio_bitio_state * bs);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.c	(revision 16561)
@@ -0,0 +1,411 @@
+/**************************************************************************
+ *
+ * local_strings -- provides some extra string routines
+ * Copyright (C) 1994  Authors
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "local_strings.h"
+#include "memlib.h"
+
+/* =========================================================================
+ * Function: arg_atoi
+ * Description: 
+ *      Converts an ascii string to an integer
+ *      but allows a scale string immediately following.
+ *      i.e. "10Mb" or "20Kb" or "40Megabytes"
+ *      There must be no gap between the last digit and the scale.
+ *      Only the fist letter is significant.
+ *      K = Kilo
+ *      M = Mega
+ *      G = Giga
+ * Input: 
+ *      string of form: xxxxxxK or xxxxxxxM or xxxxxxxG
+ *      where x is a digit
+ * Output: 
+ *      a scaled integer
+ * ========================================================================= */
+
+#define KILO 	1024
+#define MEGA	1024*1024
+#define GIGA	1024*1024*1024
+
+int 
+arg_atoi (char *str)
+{
+  char *str_ptr = str;
+  char ch = '\0';
+  long scale = 1;
+
+  /* find any scale letter immediately after digits */
+  ch = *str_ptr;
+  while (!isalpha (ch) && (ch != '\0'))
+    ch = *str_ptr++;
+
+  /* replace scale letter by end-of-string character */
+  *str_ptr = '\0';
+
+  /* set scale appropriately */
+  switch (ch)
+    {
+    case 'K':
+      scale = KILO;
+      break;
+    case 'M':
+      scale = MEGA;
+      break;
+    case 'G':
+      scale = GIGA;
+      break;
+    default:
+      scale = 1;
+      break;
+    }
+
+  return (atoi (str) * scale);
+
+}
+
+/* ===============================================================================
+ * Function: compare
+ * Description: Compare two strings. 
+ * Input: Two binary strings, first byte of string indicates length. 
+ * Output: Like standard strcmp.
+ *      = 0 if s1 = s2
+ *      < 0 if s1 < s2
+ *      > 0 if s1 > s2
+ * =============================================================================== */
+int 
+compare (u_char * s1, u_char * s2)
+{
+  int l1 = *s1, l2 = *s2;
+  int l = (l1 < l2) ? l1 : l2;
+
+  while (l--)
+    if (*++s1 != *++s2)
+      return (*s1 - *s2);
+  return (l1 - l2);
+
+}
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static unsigned char casecharmap[] = {
+	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+/* ===============================================================================
+ * Function: casecompare   [RPAP - Jan 97: Stem Index Change]
+ * Description: Compare two strings in dictionary order
+ * Input: Two binary strings, first byte of string indicates length. 
+ * Output:
+ *      = 0 if s1 = s2
+ *      < 0 if s1 < s2
+ *      > 0 if s1 > s2
+ * =============================================================================== */
+int 
+casecompare (u_char * s1, u_char * s2)
+{
+  int l1 = *s1, l2 = *s2;
+  int l = (l1 < l2) ? l1 : l2;
+  int pos = 0;
+  register int diff = 0;
+
+  while (l--)
+    if ((diff = casecharmap[*++s1] - casecharmap[*++s2]) != 0)
+      return (diff);
+    else if ((diff = *s1 - *s2) != 0)
+      if (!pos)
+	pos = diff;
+
+  return ((l1 - l2) ? (l1 - l2) : (pos));
+
+}
+
+/* =========================================================================
+ * Function: copy_string
+ * Description: 
+ *      Copies a string with the len in 1st byte
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+u_char *
+copy_string (u_char * src_str)
+{
+  u_char *copy = Xmalloc (src_str[0] + 1);
+
+  if (!copy)
+    return NULL;
+
+  bcopy ((char *) src_str, (char *) (copy), src_str[0] + 1);
+  return copy;
+
+}
+
+
+/* ===============================================================================
+ * Function: prefixlen
+ * Description: Returns length of common prefix string between s1 and s2.
+ * Input: two binary strings
+ * Output: prefix length
+ * =============================================================================== */
+int 
+prefixlen (u_char * s1, u_char * s2)
+{
+  int l = (*s1 < *s2) ? *s1 : *s2;
+  int i = 0;
+
+  while (i < l && *++s1 == *++s2)
+    i++;
+  return (i);
+}
+
+
+/* ===============================================================================
+ * Function: char2str
+ * Description: 
+ *      Ensures that non-printable characters are converted to a string
+ *      which is printable i.e. it indentifies the character.
+ *      e.g. carriage-return = "\n".
+ * Input: character (may be non-printable)
+ * Output: pointer to static string
+ * =============================================================================== */
+
+char *
+char2str (u_char c)
+{
+  static char buf[10];
+  switch (c)
+    {
+    case '\n':
+      sprintf (buf, "\\n");
+      break;
+    case '\b':
+      sprintf (buf, "\\b");
+      break;
+    case '\f':
+      sprintf (buf, "\\f");
+      break;
+    case '\t':
+      sprintf (buf, "\\t");
+      break;
+    default:
+	if (c >= ' ' && c <= 126)
+	switch (c)
+	  {
+	  case '\\':
+	    sprintf (buf, "\\\\");
+	    break;
+	  case '\"':
+	    sprintf (buf, "\\\"");
+	    break;
+	  case '\'':
+	    sprintf (buf, "\\\'");
+	    break;
+	  default:
+	    buf[0] = c;
+	    buf[1] = '\0';
+	    break;
+	  }
+      else
+	sprintf (buf, "\\x%02x", c);
+      break;
+    }
+  return buf;
+}
+
+/* ===============================================================================
+ * Function: word2str
+ * Description: 
+ *      Converts a string with possible non-printable characters into a string
+ *      consisting entirely of printable characters.
+ * Input: string, length stored in first byte
+ * Output: pointer to static string
+ * =============================================================================== */
+
+char *
+word2str (u_char * s)
+{
+  static char buf[1024];
+  char *p = buf;
+  int i, len = (int) *s++;
+
+  bzero (buf, sizeof (buf));
+  for (i = 0; i < len; i++)
+    {
+      strcpy (p, char2str (s[i]));
+      p = strchr (p, '\0');
+    }
+  return buf;
+}
+
+
+/* =========================================================================
+ * Function: de_escape_string
+ * Description: 
+ *      Turn a string containing \ escapes into real characters
+ *      Note this destroys the string s in the process
+ * Input: string to de-escape 
+ * Output: the de-escaped string
+ * ========================================================================= */
+char *
+de_escape_string (char *s)
+{
+  char *org = s;
+  char *d = s;
+  while (*s)
+    {
+      if (*s == '\\')
+	{
+	  s++;
+	  switch (*s++)
+	    {
+	    case '\0':
+	      *d++ = '\\';
+	      s--;
+	      break;
+	    case '\\':
+	      *d++ = '\\';
+	      break;
+	    case 'b':
+	      *d++ = '\b';
+	      break;
+	    case 'f':
+	      *d++ = '\f';
+	      break;
+	    case 'n':
+	      *d++ = '\n';
+	      break;
+	    case 'r':
+	      *d++ = '\r';
+	      break;
+	    case 't':
+	      *d++ = '\r';
+	      break;
+	    case '\"':
+	      *d++ = '\"';
+	      break;
+	    case '\'':
+	      *d++ = '\'';
+	      break;
+	    case 'x':
+	      {
+		int num = 0;
+		if (isxdigit (*s))
+		  {
+		    char ch = toupper (*s++);
+		    num = ch <= '9' ? ch - '0' : ch - 'A' + 10;
+		  }
+		if (isxdigit (*s))
+		  {
+		    char ch = toupper (*s++);
+		    num = (num << 4) + (ch <= '9' ? ch - '0' : ch - 'A' + 10);
+		  }
+		*d++ = num;
+		break;
+	      }
+	    case '0':
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	      {
+		int num = *(s - 1) - '0';
+		if (*s >= '0' && *s <= '7')
+		  num = (num << 3) + *s++ - '0';
+		if (*s >= '0' && *s <= '7')
+		  num = (num << 3) + *s++ - '0';
+		*d++ = num;
+		break;
+	      }
+	    }
+	}
+      else
+	*d++ = *s++;
+    }
+  *d = '\0';
+  return (org);
+}
+
+/* =========================================================================
+ * Function: str255_to_string 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+char *
+str255_to_string (u_char * str255, char *str)
+{
+  static char buf[256];
+  char *str_base = NULL;
+  int len = *str255++;
+  int i = 0;
+
+  /* if haven't been given a str to write to then use local buffer */
+  if (!str)
+    str = &(buf[0]);
+
+  str_base = str;
+  for (i = 0; i < len; i++)
+    {
+      *str++ = *str255++;
+    }
+  *str = '\0';
+
+  return str_base;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/local_strings.h	(revision 16561)
@@ -0,0 +1,37 @@
+/**************************************************************************
+ *
+ * local_strings.h -- string routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef LOCAL_STRINGS_H
+#define LOCAL_STRINGS_H
+
+#include "sysfuncs.h"
+
+int arg_atoi (char *str);
+int compare (u_char * s1, u_char * s2);
+int casecompare (u_char * s1, u_char * s2);   /* [RPAP - Jan 97: Stem Index Change] */
+u_char *copy_string (u_char * src_str);
+char *str255_to_string (u_char * str255, char *str);
+int prefixlen (u_char * s1, u_char * s2);
+char *char2str (u_char c);
+char *word2str (u_char * s);
+char *de_escape_string (char *s);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/longlong.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/longlong.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/longlong.h	(revision 16561)
@@ -0,0 +1,81 @@
+/**************************************************************************
+ *
+ * longlong.h -- Use of GCC's long long integer types
+ * Copyright (C) 1999 Tim A.H. Bell
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_LONGLONG
+#define H_LONGLONG
+
+#ifndef __WIN32__
+#include "config.h"
+#endif
+
+/*
+  Use GCC's "long long" integer types for certain variables, to avoid
+  overflowing 32 bit integers.
+
+  Other files affected:
+  
+    lib:
+        bitio_random.c
+        bitio_random.h
+	
+    src/text:
+        ivf.pass1.c
+        ivf.pass2.c
+        build.h
+        mg_passes.c
+        text.h
+	mgstat.c
+*/
+
+/*#define TESTING_OVERFLOW*/
+
+#ifdef TESTING_OVERFLOW
+
+/* Test the overflow detection by using tiny (16-bit) types */
+typedef unsigned short int mg_ullong;
+typedef short int mg_llong;
+
+#define ULL_FS "u"
+#define LL_FS "d"
+
+#elif defined __GNUC__ && ! defined DISABLE_LONG_LONG
+
+/* We're using GCC, so it's okay to use "long long" (64-bit) types */
+#define USE_LONG_LONG
+
+typedef unsigned long long int mg_ullong;
+typedef long long int mg_llong;
+
+#define ULL_FS "llu"
+#define LL_FS "lld"
+
+#else
+
+/* Not using GCC, so fall back on plain "long" (32-bit) types */
+typedef unsigned long int mg_ullong;
+typedef long int mg_llong;
+
+#define ULL_FS "lu"
+#define LL_FS "ld"
+
+#endif /* __GNUC__ */
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.c	(revision 16561)
@@ -0,0 +1,1183 @@
+/**************************************************************************
+ *
+ * lovinstem.c -- Stemming code
+ * Copyright (C) 1994  Linh Huynh (linh@kbs.citri.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+
+/*****************************************************************************
+ *
+ *   Transformational Rules used in Recoding Stem Terminations
+ *
+ *   Parameter in functions:
+ *       ch : the second last character of the old ending
+ *            (after removing one of double consonants) 
+ *
+ ****************************************************************************/
+
+
+/************ Conditional rules associated with transformations **************/
+
+static int aio ();
+static int s ();
+static int pt ();
+static int m ();
+static int n ();
+
+static int 
+aio (ch)			/* Rule number 9     */
+     char ch;
+{
+  return ((ch != 'a') && (ch != 'i') && (ch != 'o'));
+}
+
+static int 
+s (ch)				/* Rule Number 24    */
+     char ch;
+{
+  return (ch != 's');
+}
+
+static int 
+pt (ch)				/* Rule number 28    */
+     char ch;
+{
+  return ((ch != 'p') && (ch != 't'));
+}
+
+static int 
+m (ch)				/* Rule number 30    */
+     char ch;
+{
+  return (ch != 'm');
+}
+
+static int 
+n (ch)				/* Rule number 32    */
+     char ch;
+{
+  return (ch != 'n');
+}
+
+
+/**************************************************************************
+ *
+ *  Context-sensitive rules associated with certain endings.
+ *
+ *  The notations of the rules are the same as in Lovins' paper
+ *  except that rule A is replaced by a NULL in the data structure.
+ *
+ *  Function parameters:
+ *      - stem_length: possible stem length
+ *      - end        : pointer points to the end of the possible stem.
+ *
+ *************************************************************************/
+
+
+/********* Context-sensitive rule function declarations ******************/
+
+static int B ();
+static int C ();
+static int D ();
+static int E ();
+static int F ();
+static int G ();
+static int H ();
+static int I ();
+static int J ();
+static int K ();
+static int L ();
+static int M ();
+static int N ();
+static int O ();
+static int P ();
+static int Q ();
+static int R ();
+static int S ();
+static int T ();
+static int U ();
+static int V ();
+static int W ();
+static int X ();
+static int Y ();
+static int Z ();
+static int AA ();
+static int BB ();
+static int CC ();
+
+static int 
+B (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (stem_length >= 3);
+}
+
+static int 
+C (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (stem_length >= 4);
+}
+
+static int 
+D (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (stem_length >= 5);
+}
+
+static int 
+E (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (*end != 'e');
+}
+
+static int 
+F (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return ((stem_length >= 3) && (*end != 'e'));
+}
+
+static int 
+G (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return ((stem_length >= 3) && (*end == 'f'));
+}
+
+static int 
+H (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+  return ((p1 == 't') || ((p1 == 'l') && (p2 == 'l')));
+}
+
+static int 
+I (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'o') && (p1 != 'e'));
+}
+
+static int 
+J (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'a') && (p1 != 'e'));
+}
+
+static int 
+K (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((stem_length >= 3) &&
+	  ((p1 == 'l') || (p1 == 'i') ||
+	   ((p1 == 'e') && (*(end - 2) == 'u'))));
+}
+
+static int 
+L (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+  if (p1 == 's')
+    return (p2 == 'o');
+  else
+    return ((p1 != 'u') && (p1 != 'x'));
+}
+
+static int 
+M (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'a') && (p1 != 'c') &&
+	  (p1 != 'e') && (p1 != 'm'));
+}
+
+static int 
+N (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  if (stem_length >= 3)
+    {
+      if (*(end - 2) == 's')
+	return (stem_length >= 4);
+      else
+	return 1;
+    }
+  else
+    return 0;
+}
+
+static int 
+O (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((p1 == 'l') || (p1 == 'i'));
+}
+
+static int 
+P (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (*end != 'c');
+}
+
+static int 
+Q (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+
+  return ((stem_length >= 3) && (p1 != 'l') && (p1 != 'n'));
+}
+
+static int 
+R (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+  return ((p1 == 'n') || (p1 == 'r'));
+}
+
+static int 
+S (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 't')
+    return (p2 != 't');
+  else
+    return ((p1 == 'r') && (p2 == 'd'));
+}
+
+static int 
+T (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 't')
+    return (p2 != 'o');
+  else
+    return (p1 == 's');
+}
+
+static int 
+U (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+
+  return ((p1 == 'l') || (p1 == 'm') ||
+	  (p1 == 'n') || (p1 == 'r'));
+}
+
+static int 
+V (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (*end == 'c');
+}
+
+static int 
+W (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+
+  return ((p1 != 's') && (p1 != 'u'));
+}
+
+static int 
+X (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1;
+  p1 = *end;
+
+  if (p1 == 'e')
+    return ((stem_length >= 3) && (*(end - 2) == 'u'));
+  else
+    return ((p1 == 'l') || (p1 == 'i'));
+}
+
+static int 
+Y (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return ((*end == 'n') && (*(end - 1) == 'i'));
+}
+
+static int 
+Z (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (*end != 'f');
+}
+
+static int 
+AA (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 'h')
+    return ((p2 == 'p') || (p2 == 't'));
+  else if (p1 == 'r')
+    return ((p2 == 'e') || (p2 == 'o'));
+  else if (p1 == 's')
+    return (p2 == 'e');
+  else
+    return ((p1 == 'd') || (p1 == 'f') ||
+	    (p1 == 'l') || (p1 == 't'));
+}
+
+static int 
+BB (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  if (stem_length >= 3)
+    {
+      if (stem_length >= 4)
+	return (strncmp (end - 3, "ryst", 4) != 0);
+      else
+	return (strncmp (end - 2, "met", 3) != 0);
+    }
+  else
+    return 0;
+}
+
+static int 
+CC (stem_length, end)
+     int stem_length;
+     char *end;
+{
+  return (*end == 'l');
+}
+
+/**************************************************************************/
+
+#define MIN_STEM_LENGTH 2
+#define MAX_ENDING_SIZE 11
+#define PREDEFINED_SIZE (MAX_ENDING_SIZE + MIN_STEM_LENGTH)
+#define EOS     '\0'
+/* [RPAP - Jan 97: Stem Index Change] */
+#define MAX_STEM_LENGTH 255
+
+static char *remove_ending (char *word, int w_len);
+static void recode_stem (char *stem_end);
+
+/* =========================================================================
+ * Function: stem
+ * Description: 
+ *      Implemetation of the Lovins' stemming algorithm described in
+ *      J.B. Lovins, "Development of a Stemming Algorithm",
+ *      Mechanical Translation and Computational Linguistics, Vol 11,1968.
+ * Input: a word string with the length in the first byte
+ * Output: the stemmed word
+ * ========================================================================= */
+
+void
+lovinstem (u_char * word)
+{
+  char *word_start;		/* points to first letter of word */
+  char *stem_end;		/* points to last character of stem portion */
+  int old_length;		/* length of word */
+  int new_length;		/* length after stemming */
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  u_char copy[MAX_STEM_LENGTH + 2];
+  bcopy ((char *) word, (char *) copy, *word + 1);
+
+  old_length = *copy; /* [RPAP - Jan 97: Stem Index Change] */
+
+  /* The word must be at least MIN_STEM_LENGTH characters long. */
+  if (old_length <= MIN_STEM_LENGTH)
+    return;
+
+  word_start = (char *) copy + 1;	/* jump over length byte */ /* [RPAP - Jan 97: Stem Index Change] */
+  copy[old_length + 1] = '\0';	/* null terminate string */ /* [RPAP - Jan 97: Stem Index Change] */
+
+  stem_end = remove_ending (word_start, old_length);
+  recode_stem (stem_end);
+
+  /* fix up new length */
+  /* have to use strlen because have no other way of telling length */
+  new_length = strlen (word_start);
+  *copy = new_length;  /* [RPAP - Jan 97: Stem Index Change] */
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  bcopy ((char *) copy, (char *) word, *copy + 1);
+
+}				/*stem_complex */
+
+/* =========================================================================
+
+ *   Data structure for recoding and the method for recoding the stem. 
+ *
+ * ========================================================================= */
+
+typedef struct
+{
+  char *old_end;		/* old ending */
+  char *new_end;		/* new ending */
+  char old_offset;		/* length of the old ending - 1 */
+  int (*cond) ();		/* condition rule */
+  char end_group;		/* signal the end of the group */
+}
+Recode_Rules;
+
+/*  
+   Stem terminations are divided into groups having the same last
+   character
+ */
+
+static Recode_Rules Rules[] =
+{
+  "uad", "uas", 2, NULL, 0,
+  "vad", "vas", 2, NULL, 0,
+  "cid", "cis", 2, NULL, 0,
+  "lid", "lis", 2, NULL, 0,
+  "erid", "eris", 3, NULL, 0,
+  "pand", "pans", 3, NULL, 0,
+  "end", "ens", 2, s, 0,
+  "end", "ens", 2, m, 0,
+  "ond", "ons", 2, NULL, 0,
+  "lud", "lus", 2, NULL, 0,
+  "rud", "rus", 2, NULL, 1,
+
+  "ul", "l", 1, aio, 1,
+
+  "istr", "ister", 3, NULL, 0,
+  "metr", "meter", 3, NULL, 0,
+  "her", "hes", 2, pt, 1,
+
+  "urs", "ur", 2, NULL, 1,
+
+  "uct", "uc", 2, NULL, 0,
+  "umpt", "um", 3, NULL, 0,
+  "rpt", "rb", 2, NULL, 0,
+  "mit", "mis", 2, NULL, 0,
+  "ert", "ers", 2, NULL, 0,
+  "et", "es", 1, n, 0,
+  "yt", "ys", 1, NULL, 1,
+
+  "iev", "ief", 2, NULL, 0,
+  "olv", "olut", 2, NULL, 1,
+
+  "bex", "bic", 2, NULL, 0,
+  "dex", "dic", 2, NULL, 0,
+  "pex", "pic", 2, NULL, 0,
+  "tex", "tic", 2, NULL, 0,
+  "ax", "ac", 1, NULL, 0,
+  "ex", "ec", 1, NULL, 0,
+  "ix", "ic", 1, NULL, 0,
+  "lux", "luc", 2, NULL, 1,
+
+  "yz", "ys", 1, NULL, 1
+};
+
+typedef struct last
+  {
+    char c;			/* The last character */
+    struct last *left;		/* used in balanced   */
+    struct last *right;		/* binary tree        */
+    Recode_Rules *pt;		/* location of approriate group */
+  }
+Last_Char_Node;
+
+/*------------
+       s
+     /   \
+    l     x
+   / \   / \
+  d   r t   z
+         \
+          v
+---------------*/
+static Last_Char_Node pr[] =
+{
+  'd', NULL, NULL, Rules,
+  'l', pr, pr + 2, Rules + 11,
+  'r', NULL, NULL, Rules + 12,
+  's', pr + 1, pr + 6, Rules + 15,
+  't', NULL, pr + 5, Rules + 16,
+  'v', NULL, NULL, Rules + 23,
+  'x', pr + 4, pr + 7, Rules + 25,
+  'z', NULL, NULL, Rules + 33,
+};
+
+
+/*****************************************************************************
+ *
+ *  Recode the stem according to the transformation rules.
+ *
+ *****************************************************************************/
+
+static void
+recode_stem (char *stem_end)
+{
+  static Last_Char_Node *root = pr + 3;
+  Last_Char_Node *p_last = root;
+  Recode_Rules *rule;		/* points to the transformation list */
+  char *h,			/* points to start of possible ending */
+    ch,				/* last character of the old ending  */
+    ch2;
+
+  h = stem_end - 1;
+  ch = *stem_end;
+  if (*h == ch)			/* Check for double consonant        */
+    {
+      if (strchr ("bdglmnprst", ch) != NULL)
+	{
+	  *stem_end = EOS;
+	  stem_end = h;
+	}
+    }
+
+  do				/* Check for the last character       */
+    {
+      ch2 = p_last->c;
+      if (ch == ch2)
+	break;
+      else if (ch > ch2)
+	p_last = p_last->right;
+      else
+	p_last = p_last->left;
+    }
+  while (p_last != NULL);
+
+  if (p_last != NULL)		/* Check for the rest of suffix list */
+    {
+      rule = p_last->pt;
+      for (;;)
+	{
+	  h = stem_end - rule->old_offset;
+	  if (strcmp (h, rule->old_end) == 0)
+	    {
+	      if (!rule->cond || (*rule->cond) (*(h - 1)))
+		{
+		  (void) strcpy (h, rule->new_end);
+		  break;
+		}
+	    }
+	  if (rule->end_group)
+	    break;
+	  rule++;
+	}
+    }
+}
+
+/* ======================================================================
+
+ *    List of endings and the method to remove the ending 
+ *
+ * ======================================================================*/
+
+/************ Data structures for list of endings  ********************/
+
+typedef struct
+{
+  char *ending;			/* old ending                */
+  int (*cond) ();		/* conditional rule          */
+  signed char left_offset;	/* used to find the siblings */
+  signed char right_offset;	/* in balanced binary tree   */
+}
+Ending_List;
+
+/*
+   The original list of endings is re-organised into groups having
+   the same length and the same first character.
+ */
+
+static Ending_List List[] =
+{
+  "a", NULL, 0, 0,
+
+  "ae", NULL, 0, 0,
+  "al", BB, -1, 2,
+  "ar", X, 0, 0,
+  "as", B, -1, 0,
+
+  "acy", NULL, 0, 1,
+  "age", B, 0, 0,
+  "aic", NULL, -2, 1,
+  "als", BB, 0, 0,
+  "ant", B, -2, 2,
+  "ars", O, 0, 0,
+  "ary", F, -1, 2,
+  "ata", NULL, 0, 0,
+  "ate", NULL, -1, 0,
+
+  "able", NULL, 0, 1,
+  "ably", NULL, 0, 0,
+  "ages", B, -2, 2,
+  "ally", B, 0, 0,
+  "ance", B, -1, 1,
+  "ancy", B, 0, 0,
+  "ants", B, -4, 4,
+  "aric", NULL, 0, 0,
+  "arly", K, -1, 1,
+  "ated", I, 0, 0,
+  "ates", NULL, -2, 2,
+  "atic", B, 0, 0,
+  "ator", NULL, -1, 0,
+
+  "acies", NULL, 0, 0,
+  "acity", NULL, -1, 1,
+  "aging", B, 0, 0,
+  "aical", NULL, -2, 2,
+  "alist", NULL, 0, 0,
+  "alism", B, -1, 0,
+  "ality", NULL, -3, 3,
+  "alize", NULL, 0, 1,
+  "allic", BB, 0, 0,
+  "anced", B, -2, 2,
+  "ances", B, 0, 0,
+  "antic", C, -1, 0,
+  "arial", NULL, -6, 6,
+  "aries", NULL, 0, 1,
+  "arily", NULL, 0, 0,
+  "arity", B, -2, 2,
+  "arize", NULL, 0, 0,
+  "aroid", NULL, -1, 0,
+  "ately", NULL, -3, 3,
+  "ating", I, 0, 1,
+  "ation", B, 0, 0,
+  "ative", NULL, -2, 2,
+  "ators", NULL, 0, 0,
+  "atory", NULL, -1, 1,
+  "ature", E, 0, 0,
+
+  "aceous", NULL, 0, 1,
+  "acious", B, 0, 0,
+  "action", G, -2, 2,
+  "alness", NULL, 0, 0,
+  "ancial", NULL, -1, 1,
+  "ancies", NULL, 0, 0,
+  "ancing", B, -4, 4,
+  "ariser", NULL, 0, 0,
+  "arized", NULL, -1, 1,
+  "arizer", NULL, 0, 0,
+  "atable", NULL, -2, 2,
+  "ations", B, 0, 0,
+  "atives", NULL, -1, 0,
+
+  "ability", NULL, 0, 1,
+  "aically", NULL, 0, 0,
+  "alistic", B, -2, 2,
+  "alities", NULL, 0, 0,
+  "ariness", E, -1, 0,
+  "aristic", NULL, -3, 3,
+  "arizing", NULL, 0, 1,
+  "ateness", NULL, 0, 0,
+  "atingly", NULL, -2, 2,
+  "ational", B, 0, 0,
+  "atively", NULL, -1, 1,
+  "ativism", NULL, 0, 0,
+
+  "ableness", NULL, 0, 1,
+  "arizable", NULL, 0, 0,
+
+  "allically", C, 0, 0,
+  "antaneous", NULL, -1, 1,
+  "antiality", NULL, 0, 0,
+  "arisation", NULL, -2, 2,
+  "arization", NULL, 0, 0,
+  "ationally", B, -1, 1,
+  "ativeness", NULL, 0, 0,
+
+  "antialness", NULL, 0, 0,
+  "arisations", NULL, -1, 1,
+  "arizations", NULL, 0, 0,
+
+  "alistically", B, 0, 1,
+  "arizability", NULL, 0, 0,
+
+  "e", NULL, 0, 0,
+
+  "ed", E, 0, 0,
+  "en", F, -1, 1,
+  "es", E, 0, 0,
+
+  "eal", Y, 0, 0,
+  "ear", Y, -1, 1,
+  "ely", E, 0, 0,
+  "ene", E, -2, 2,
+  "ent", C, 0, 0,
+  "ery", E, -1, 1,
+  "ese", NULL, 0, 0,
+
+  "ealy", Y, 0, 1,
+  "edly", E, 0, 0,
+  "eful", NULL, -2, 1,
+  "eity", NULL, 0, 0,
+  "ence", NULL, -2, 2,
+  "ency", NULL, 0, 0,
+  "ened", E, -1, 2,
+  "enly", E, 0, 0,
+  "eous", NULL, -1, 0,
+
+  "early", Y, 0, 1,
+  "ehood", NULL, 0, 0,
+  "eless", NULL, -2, 2,
+  "elity", NULL, 0, 0,
+  "ement", NULL, -1, 0,
+  "enced", NULL, -3, 3,
+  "ences", NULL, 0, 1,
+  "eness", E, 0, 0,
+  "ening", E, -2, 2,
+  "ental", NULL, 0, 0,
+  "ented", C, -1, 1,
+  "ently", NULL, 0, 0,
+
+  "eature", Z, 0, 0,
+  "efully", NULL, -1, 1,
+  "encies", NULL, 0, 0,
+  "encing", NULL, -2, 2,
+  "ential", NULL, 0, 0,
+  "enting", C, -1, 1,
+  "entist", NULL, 0, 1,
+  "eously", NULL, 0, 0,
+
+  "elihood", E, 0, 1,
+  "encible", NULL, 0, 0,
+  "entally", NULL, -2, 2,
+  "entials", NULL, 0, 0,
+  "entiate", NULL, -1, 1,
+  "entness", NULL, 0, 0,
+
+  "entation", NULL, 0, 0,
+  "entially", NULL, -1, 1,
+  "eousness", NULL, 0, 0,
+
+  "eableness", E, 0, 1,
+  "entations", NULL, 0, 0,
+  "entiality", NULL, -2, 2,
+  "entialize", NULL, 0, 0,
+  "entiation", NULL, -1, 0,
+
+  "entialness", NULL, 0, 0,
+
+  "ful", NULL, 0, 0,
+
+  "fully", NULL, 0, 0,
+
+  "fulness", NULL, 0, 0,
+
+  "hood", NULL, 0, 0,
+
+  "i", NULL, 0, 0,
+
+  "ia", NULL, 0, 0,
+  "ic", NULL, -1, 1,
+  "is", NULL, 0, 0,
+
+  "ial", NULL, 0, 0,
+  "ian", NULL, -1, 1,
+  "ics", NULL, 0, 1,
+  "ide", L, 0, 0,
+  "ied", NULL, -3, 3,
+  "ier", NULL, 0, 0,
+  "ies", P, -1, 0,
+  "ily", NULL, -1, 1,
+  "ine", M, 0, 0,
+  "ing", N, -5, 5,
+  "ion", Q, 0, 0,
+  "ish", C, -1, 1,
+  "ism", B, 0, 1,
+  "ist", NULL, 0, 0,
+  "ite", AA, -3, 3,
+  "ity", NULL, 0, 0,
+  "ium", NULL, -1, 0,
+  "ive", NULL, -1, 1,
+  "ize", F, 0, 0,
+
+  "ials", NULL, 0, 0,
+  "ians", NULL, -1, 0,
+  "ible", NULL, -1, 1,
+  "ibly", NULL, 0, 0,
+  "ical", NULL, -2, 2,
+  "ides", L, 0, 0,
+  "iers", NULL, -1, 1,
+  "iful", NULL, 0, 0,
+  "ines", M, -4, 4,
+  "ings", N, 0, 0,
+  "ions", B, -1, 1,
+  "ious", NULL, 0, 0,
+  "isms", B, -2, 2,
+  "ists", NULL, 0, 0,
+  "itic", H, -1, 1,
+  "ized", F, 0, 1,
+  "izer", F, 0, 0,
+
+  "ially", NULL, 0, 0,
+  "icant", NULL, -1, 1,
+  "ician", NULL, 0, 0,
+  "icide", NULL, -2, 2,
+  "icism", NULL, 0, 0,
+  "icist", NULL, -1, 0,
+  "icity", NULL, -3, 3,
+  "idine", I, 0, 1,
+  "iedly", NULL, 0, 0,
+  "ihood", NULL, -2, 2,
+  "inate", NULL, 0, 0,
+  "iness", NULL, -1, 0,
+  "ingly", B, -6, 6,
+  "inism", J, 0, 1,
+  "inity", CC, 0, 0,
+  "ional", NULL, -2, 2,
+  "ioned", NULL, 0, 0,
+  "ished", NULL, -1, 0,
+  "istic", NULL, -3, 3,
+  "ities", NULL, 0, 1,
+  "itous", NULL, 0, 0,
+  "ively", NULL, -2, 2,
+  "ivity", NULL, 0, 0,
+  "izers", F, -1, 1,
+  "izing", F, 0, 0,
+
+  "ialist", NULL, 0, 0,
+  "iality", NULL, -1, 1,
+  "ialize", NULL, 0, 0,
+  "ically", NULL, -2, 2,
+  "icance", NULL, 0, 0,
+  "icians", NULL, -1, 1,
+  "icists", NULL, 0, 0,
+  "ifully", NULL, -4, 4,
+  "ionals", NULL, 0, 0,
+  "ionate", D, -1, 1,
+  "ioning", NULL, 0, 0,
+  "ionist", NULL, -2, 2,
+  "iously", NULL, 0, 0,
+  "istics", NULL, -1, 1,
+  "izable", E, 0, 0,
+
+  "ibility", NULL, 0, 0,
+  "icalism", NULL, -1, 1,
+  "icalist", NULL, 0, 1,
+  "icality", NULL, 0, 0,
+  "icalize", NULL, -3, 3,
+  "ication", G, 0, 0,
+  "icianry", NULL, -1, 0,
+  "ination", NULL, -1, 1,
+  "ingness", NULL, 0, 0,
+  "ionally", NULL, -5, 5,
+  "isation", NULL, 0, 0,
+  "ishness", NULL, -1, 1,
+  "istical", NULL, 0, 1,
+  "iteness", NULL, 0, 0,
+  "iveness", NULL, -3, 3,
+  "ivistic", NULL, 0, 0,
+  "ivities", NULL, -1, 0,
+  "ization", F, -1, 1,
+  "izement", NULL, 0, 0,
+
+  "ibleness", NULL, 0, 0,
+  "icalness", NULL, -1, 1,
+  "ionalism", NULL, 0, 0,
+  "ionality", NULL, -2, 2,
+  "ionalize", NULL, 0, 0,
+  "iousness", NULL, -1, 1,
+  "izations", NULL, 0, 0,
+
+  "ionalness", NULL, 0, 1,
+  "istically", NULL, 0, 0,
+  "itousness", NULL, -2, 2,
+  "izability", NULL, 0, 0,
+  "izational", NULL, -1, 0,
+
+  "izationally", B, 0, 0,
+
+  "ly", B, 0, 0,
+
+  "less", NULL, 0, 1,
+  "lily", NULL, 0, 0,
+
+  "lessly", NULL, 0, 0,
+
+  "lessness", NULL, 0, 0,
+
+  "ness", NULL, 0, 0,
+
+  "nesses", NULL, 0, 0,
+
+  "o", NULL, 0, 0,
+
+  "on", S, 0, 1,
+  "or", T, 0, 0,
+
+  "oid", NULL, 0, 0,
+  "one", R, -1, 1,
+  "ous", NULL, 0, 0,
+
+  "ogen", NULL, 0, 0,
+
+  "oidal", NULL, 0, 0,
+  "oides", NULL, -1, 2,
+  "otide", NULL, 0, 0,
+  "ously", NULL, -1, 0,
+
+  "oidism", NULL, 0, 0,
+
+  "oidally", NULL, 0, 1,
+  "ousness", NULL, 0, 0,
+
+  "s", W, 0, 0,
+
+  "s'", NULL, 0, 0,
+
+  "um", U, 0, 1,
+  "us", V, 0, 0,
+
+  "ward", NULL, 0, 1,
+  "wise", NULL, 0, 0,
+
+  "y", B, 0, 0,
+
+  "yl", R, 0, 0,
+
+  "ying", B, 0, 1,
+  "yish", NULL, 0, 0,
+
+  "'s", NULL, 0, 0,
+};
+
+typedef struct node
+  {
+    char c;			/* First character  */
+    struct node *left;		/* used in balanced */
+    struct node *right;		/* binary tree      */
+    Ending_List *ptr[11];	/* the approriate location */
+  }
+First_Char_Node;
+
+static First_Char_Node First[] =
+{
+  '\'', NULL, NULL,
+  {NULL,
+   List + 293, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'a', First, NULL,
+  {List,
+   List + 2, List + 9, List + 20, List + 39, List + 58,
+   List + 70, List + 77, List + 82, List + 87, List + 89},
+
+  'e', First + 1, First + 4,
+  {List + 91,
+   List + 93, List + 98, List + 106, List + 116, List + 126,
+   List + 133, List + 138, List + 142, List + 145, NULL},
+
+  'f', NULL, NULL,
+  {NULL,
+   NULL, List + 146, NULL, List + 147, NULL,
+   List + 148, NULL, NULL, NULL, NULL},
+
+  'h', First + 3, First + 5,
+  {NULL,
+   NULL, NULL, List + 149, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'i', NULL, NULL,
+  {List + 150,
+   List + 152, List + 163, List + 181, List + 202, List + 222,
+   List + 239, List + 252, List + 258, NULL, List + 261},
+
+  'l', First + 2, First + 10,
+  {NULL,
+   List + 262, NULL, List + 263, NULL, List + 265,
+   NULL, List + 266, NULL, NULL, NULL},
+
+  'n', NULL, NULL,
+  {NULL,
+   NULL, NULL, List + 267, NULL, List + 268,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'o', First + 7, First + 9,
+  {List + 269,
+   List + 270, List + 273, List + 275, List + 277, List + 280,
+   List + 281, NULL, NULL, NULL, NULL},
+
+  's', NULL, NULL,
+  {List + 283,
+   List + 284, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'u', First + 8, First + 12,
+  {NULL,
+   List + 285, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'w', NULL, NULL,
+  {NULL,
+   NULL, NULL, List + 287, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'y', First + 11, NULL,
+  {List + 289,
+   List + 290, NULL, List + 291, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+};
+
+
+/******************************************************************************
+ *
+ *  Look for a match in the suffix list.
+ *  Return the pointer to the end of the new stem if there is a match.
+ *  Otherwise, return the pointer to the end of the original word.
+ *
+ *  Method:
+ *      * Search for the first character in the suffix list.
+ *      * If there is match, search for the rest of the suffix list.
+ *         
+ *****************************************************************************/
+
+static char *
+remove_ending (char *word, int w_len)
+{
+  static First_Char_Node *root = First + 6;	/* the root of binary tree is 'l' */
+  First_Char_Node *p_first;	/* Points to the first character      */
+  /* of the possible suffix             */
+  Ending_List *p_list;		/* Points to the List of Endings      */
+  char *suffix_start,		/* Points to start of possible suffix */
+   *stem_end,			/* Points to the end of possible stem */
+    ch1, ch2;
+  int s_len,			/* Possible stem length               */
+    e_offset,			/* Offset from the end to the start of */
+  /* possible suffix                    */
+    not_done = 1, cmp;
+
+  stem_end = word + w_len - 1;
+  if (w_len > PREDEFINED_SIZE)	/* find the start of suffix   */
+    suffix_start = word + w_len - MAX_ENDING_SIZE;
+  else
+    suffix_start = word + MIN_STEM_LENGTH;
+
+  ch1 = *suffix_start;
+  do				/* Search for the first character     */
+    {
+      p_first = root;
+      do
+	{
+	  ch2 = p_first->c;
+	  if (ch1 == ch2)
+	    break;
+	  else if (ch1 > ch2)
+	    p_first = p_first->right;
+	  else
+	    p_first = p_first->left;
+	}
+      while (p_first != NULL);
+
+      if (p_first != NULL)	/* Search for the rest */
+	{
+	  e_offset = stem_end - suffix_start;
+	  if ((p_list = p_first->ptr[e_offset]) != NULL)
+	    {
+	      for (;;)		/* no need to compare the first char  */
+		{
+		  cmp = strcmp (suffix_start + 1, p_list->ending + 1);
+		  if (cmp > 0)
+		    if (p_list->right_offset)
+		      p_list += p_list->right_offset;
+		    else
+		      break;
+		  else if (cmp < 0)
+		    if (p_list->left_offset)
+		      p_list += p_list->left_offset;
+		    else
+		      break;
+		  else
+		    {
+		      s_len = suffix_start - word;
+		      if (!p_list->cond ||
+			  (*p_list->cond) (s_len, suffix_start - 1))
+			{
+			  *suffix_start = EOS;
+			  stem_end = suffix_start - 1;
+			  not_done = 0;
+			}
+		      break;
+		    }
+		}
+	    }
+	}
+      suffix_start++;
+    }
+  while (not_done && ((ch1 = *suffix_start) != EOS));
+  return (stem_end);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/lovinstem.h	(revision 16561)
@@ -0,0 +1,28 @@
+/**************************************************************************
+ *
+ * lovinstem.h -- Lovins' stemmer header
+ * Copyright (C) 1994  Linh Huynh, 
+ *                     (glued into library by tes@kbs.citri.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef LOVINSTEM_H
+#define LOVINSTEM_H
+
+void lovinstem (unsigned char *word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/memlib.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/memlib.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/memlib.c	(revision 16561)
@@ -0,0 +1,42 @@
+/**************************************************************************
+ *
+ * memlib.c -- Malloc wrappers
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+/* Defined as strdup is not an ANSI function */
+/* change the name so we do not have problems with other libs */
+char *
+my_mg_strdup(const char *str)
+{
+  char *ret_str = malloc(strlen(str)+1);
+  if (ret_str) return strcpy(ret_str, str);
+  else return (char*) 0;
+}
+
+
+Malloc_func Xmalloc = malloc;
+
+Realloc_func Xrealloc = realloc;
+
+Free_func Xfree = free;
+
+Strdup_func Xstrdup = my_mg_strdup;
Index: /gsdl/trunk/gs2common/indexers/mg/lib/memlib.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/memlib.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/memlib.h	(revision 16561)
@@ -0,0 +1,49 @@
+/**************************************************************************
+ *
+ * memlib.h -- Malloc wrappers
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#ifndef H_MEMLIB
+#define H_MEMLIB
+
+#ifdef STD_MEM
+
+#define Xmalloc malloc
+#define Xrealloc realloc
+#define Xfree free
+#define Xstrdup strdup
+
+#else
+
+
+#include "sysfuncs.h"
+
+typedef void *(*Malloc_func) (size_t);
+typedef void *(*Realloc_func) (void *, size_t);
+typedef void (*Free_func) (void *);
+typedef char *(*Strdup_func) (const char *);
+
+extern Malloc_func Xmalloc;
+extern Realloc_func Xrealloc;
+extern Free_func Xfree;
+extern Strdup_func Xstrdup;
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/messages.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/messages.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/messages.c	(revision 16561)
@@ -0,0 +1,75 @@
+/**************************************************************************
+ *
+ * messages.c -- Message and error functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include <stdarg.h>
+#include "messages.h"
+
+char *msg_prefix = "";
+
+
+void VOLATILE 
+FatalError (int ExitCode, const char *fmt,...)
+{
+  char buf[1024];
+  char *s, *pfx;
+  va_list args;
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  s = strrchr (buf, '\n');
+  if (!s || *(s + 1) != '\0')
+    strcat (buf, "\n");
+  pfx = strrchr (msg_prefix, '/');
+  pfx = pfx ? pfx + 1 : msg_prefix;
+  fprintf (stderr, "%s%s%s", pfx, *pfx ? " : " : "", buf);
+  exit (ExitCode);
+}
+
+
+
+/*
+ * This function writes messages to stderr. Due to the fact that I can't 
+ * guarantee that the fprintf call is monatomic I have to implement a 
+ * semaphore system.
+ */
+void 
+Message (const char *fmt,...)
+{
+  char buf[1024];
+  char *s, *pfx;
+  va_list args;
+
+#ifdef QUIET
+  return; /* stop those messages!!!! */
+#endif  
+
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  s = strrchr (buf, '\n');
+  if (!s || *(s + 1) != '\0')
+    strcat (buf, "\n");
+
+  pfx = strrchr (msg_prefix, '/');
+  pfx = pfx ? pfx + 1 : msg_prefix;
+  fprintf (stderr, "%s%s%s", pfx, *pfx ? " : " : "", buf);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/messages.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/messages.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/messages.h	(revision 16561)
@@ -0,0 +1,37 @@
+/**************************************************************************
+ *
+ * messages.h -- Message and error functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_MESSAGES
+#define H_MESSAGES
+
+#ifdef __GNUC__
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+extern char *msg_prefix;
+
+void VOLATILE FatalError (int ExitCode, const char *fmt,...);
+
+void Message (const char *fmt,...);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.c	(revision 16561)
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * mgheap.c -- Heap routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "mgheap.h"
+
+#define SWAP(s, a, b) 						\
+do {								\
+     register int __i;						\
+     register char *__a = (a), *__b = (b);			\
+     for (__i = (s); __i; __i--)				\
+       {							\
+         register char __t = *__a;				\
+	 *__a++ = *__b; *__b++ = __t;				\
+       }							\
+} while(0)
+
+
+
+#define COPY(s, dst, src) 					\
+do {								\
+     register int __i;						\
+     register char *__dst = (dst), *__src = (src);		\
+     for (__i = (s); __i; __i--)				\
+       *__dst++ = *__src++;					\
+} while(0)
+
+
+
+static void 
+heap_heapify (void *heap, int size, int num, int curr, heap_comp hc)
+{
+  register int child = curr * 2;
+  while (child <= num)
+    {
+      if (child < num && hc ((char *) heap + child * size,
+			     (char *) heap + child * size - size) < 0)
+	child++;
+      if (hc ((char *) heap + (curr - 1) * size, (char *) heap + (child - 1) * size) > 0)
+	{
+	  SWAP (size, (char *) heap + (curr - 1) * size,
+		(char *) heap + (child - 1) * size);
+	  curr = child;
+	  child = child * 2;
+	}
+      else
+	break;
+    }
+}
+
+static void 
+heap_pullup (void *heap, int size, int num, heap_comp hc)
+{
+  register int curr = num;
+  register int parent = curr >> 1;
+  while (parent &&
+	 hc ((char *) heap + (curr - 1) * size, (char *) heap + (parent - 1) * size) < 0)
+    {
+      SWAP (size, (char *) heap + (curr - 1) * size,
+	    (char *) heap + (parent - 1) * size);
+      curr = parent;
+      parent = curr >> 1;
+    }
+}
+
+/************************************************************************
+ *
+ * NOTE: If you choose to change the comparison function the first thing 
+ *       you do after changing the function is call heap_build.
+ *
+ */
+void 
+heap_build (void *heap, int size, int num, heap_comp hc)
+{
+  register int i;
+  for (i = num / 2; i > 0; i--)
+    heap_heapify (heap, size, num, i, hc);
+}
+
+
+
+/****************************************************
+ *
+ * NOTE : The heap must be built before heap_sort is called. 
+ *        This has the effect of reversing the order of the array.
+ *		e.g. if your comparison function is designed to pull the
+ *                   biggest thing off the heap first then the result of
+ *		     sorting with this function will be to put the bigest
+ *                   thing at the end of the array.
+ *
+ */
+void 
+heap_sort (void *heap, int size, int num, heap_comp hc)
+{
+  register int i;
+  for (i = num; i > 1; i--)
+    {
+      SWAP (size, heap, (char *) heap + (i - 1) * size);
+      heap_heapify (heap, size, i - 1, 1, hc);
+    }
+}
+
+
+void 
+heap_deletehead (void *heap, int size, int *num, heap_comp hc)
+{
+  (*num)--;
+  SWAP (size, heap, (char *) heap + *num * size);
+  heap_heapify (heap, size, *num, 1, hc);
+}
+
+
+void 
+heap_changedhead (void *heap, int size, int num, heap_comp hc)
+{
+  heap_heapify (heap, size, num, 1, hc);
+}
+
+
+/***********************************************************************
+ *
+ * This assumes that the item has been added to the end of the
+ * array that is the heap. But that num has not been changed. 
+ *
+ */
+void 
+heap_additem (void *heap, int size, int *num, heap_comp hc)
+{
+  (*num)++;
+  heap_pullup (heap, size, *num, hc);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/mgheap.h	(revision 16561)
@@ -0,0 +1,82 @@
+/**************************************************************************
+ *
+ * mgheap.h -- Heap routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+/* Care should be taken when using these routines as NO error checking is
+ * done.
+ */
+
+
+
+/* The heap comparison routine should return 
+ * <0 : If a should be before b
+ *  0 : If a and b are equal
+ * >0 : If a should be after b
+ */
+typedef int (*heap_comp) (void *a, void *b);
+
+
+/************************************************************************
+ *
+ * NOTE: If you choose to change the comparison function the first thing 
+ *       you do after changing the function is call heap_build.
+ *
+ */
+void heap_build (void *heap, int size, int num, heap_comp hc);
+
+
+
+/************************************************************************
+ *
+ * NOTE : The heap must be built before heap_sort is called.
+ *        This has the effect of reversing the order of the array.
+ *		e.g. if your comparison function is designed to pull the
+ *                   biggest thing off the heap first then the result of
+ *		     sorting with this function will be to put the bigest
+ *                   thing at the end of the array.
+ *
+ */
+void heap_sort (void *heap, int size, int num, heap_comp hc);
+
+/***********************************************************************
+ *
+ * NOTE: After deleting the head the heap will be one item shorter.
+ *       The deleted item will be placed at the end of the heap.
+ *
+ */
+void heap_deletehead (void *heap, int size, int *num, heap_comp hc);
+
+
+/***********************************************************************
+ *
+ * If you change the "value" of the root node of the heap then you
+ * should call this to re-pritorize the heap.
+ *
+ */
+void heap_changedhead (void *heap, int size, int num, heap_comp hc);
+
+
+/***********************************************************************
+ *
+ * This assumes that the item has been added to the end of the
+ * array that is the heap. But that num has not been changed. 
+ *
+ */
+void heap_additem (void *heap, int size, int *num, heap_comp hc);
Index: /gsdl/trunk/gs2common/indexers/mg/lib/netorder.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/netorder.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/netorder.h	(revision 16561)
@@ -0,0 +1,178 @@
+#ifndef NETORDER_H
+#define NETORDER_H
+
+#include "sysfuncs.h"
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+#include "win32in.h"
+#else
+# include <netinet/in.h>
+#endif
+
+
+#ifndef WORDS_BIGENDIAN
+
+/* double */
+#define HTOND(d)                                                                                  \
+        do {                                                                                      \
+             unsigned long tmph, tmpl;                                                            \
+	     bcopy ((char *) &d, (char *) &tmph, sizeof(double) >> 1);                            \
+	     bcopy ((char *) &d + (sizeof(double) >> 1), (char *) &tmpl, sizeof (double) >> 1);   \
+	     tmph = htonl (tmph);                                                                 \
+	     tmpl = htonl (tmpl);                                                                 \
+	     bcopy ((char *) &tmpl, (char *) &d, sizeof (double) >> 1);                           \
+	     bcopy ((char *) &tmph, (char *) &d + (sizeof(double) >> 1), sizeof (double) >> 1);   \
+	}while(0)
+#define NTOHD(d)                                                                                  \
+        do {                                                                                      \
+             unsigned long tmph, tmpl;                                                            \
+	     bcopy ((char *) &d, (char *) &tmph, sizeof(double) >> 1);                            \
+	     bcopy ((char *) &d + (sizeof(double) >> 1), (char *) &tmpl, sizeof (double) >> 1);   \
+	     tmph = ntohl (tmph);                                                                 \
+	     tmpl = ntohl (tmpl);                                                                 \
+	     bcopy ((char *) &tmpl, (char *) &d, sizeof (double) >> 1);                           \
+	     bcopy ((char *) &tmph, (char *) &d + (sizeof(double) >> 1), sizeof (double) >> 1);   \
+        }while(0)
+#define HTOND2(hd, nd)                                                                            \
+        do {                                                                                      \
+             unsigned long tmph, tmpl;                                                            \
+	     bcopy ((char *) &hd, (char *) &tmph, sizeof(double) >> 1);                           \
+	     bcopy ((char *) &hd + (sizeof(double) >> 1), (char *) &tmpl, sizeof (double) >> 1);  \
+	     tmph = htonl (tmph);                                                                 \
+	     tmpl = htonl (tmpl);                                                                 \
+	     bcopy ((char *) &tmpl, (char *) &nd, sizeof (double) >> 1);                          \
+	     bcopy ((char *) &tmph, (char *) &nd + (sizeof(double) >> 1), sizeof (double) >> 1);  \
+	}while(0)
+#define NTOHD2(nd, hd)                                                                            \
+        do {                                                                                      \
+             unsigned long tmph, tmpl;                                                            \
+	     bcopy ((char *) &nd, (char *) &tmph, sizeof(double) >> 1);                           \
+	     bcopy ((char *) &nd + (sizeof(double) >> 1), (char *) &tmpl, sizeof (double) >> 1);  \
+	     tmph = ntohl (tmph);                                                                 \
+	     tmpl = ntohl (tmpl);                                                                 \
+	     bcopy ((char *) &tmpl, (char *) &hd, sizeof (double) >> 1);                          \
+	     bcopy ((char *) &tmph, (char *) &hd + (sizeof(double) >> 1), sizeof (double) >> 1);  \
+        }while(0)
+
+/* float */
+#define HTONF(f)                                                                   \
+        do {                                                                       \
+             unsigned long tmp;                                                    \
+             bcopy ((char *) &(f), (char *) &tmp, sizeof (float));                 \
+             HTONUL (tmp);                                                         \
+	     bcopy ((char *) &tmp, (char *) &(f), sizeof (float));                 \
+	}while(0)
+#define NTOHF(f)                                                                   \
+        do {                                                                       \
+             unsigned long tmp;                                                    \
+             bcopy ((char *) &(f), (char *) &tmp, sizeof (float));                 \
+	     NTOHUL (tmp);                                                         \
+	     bcopy ((char *) &tmp, (char *) &(f), sizeof (float));                 \
+	}while(0)
+#define HTONF2(hf, nf)                                                             \
+        do {                                                                       \
+             unsigned long tmp;                                                    \
+             bcopy ((char *) &(hf), (char *) &tmp, sizeof (float));                \
+             HTONUL (tmp);                                                         \
+	     bcopy ((char *) &tmp, (char *) &(nf), sizeof (float));                \
+	}while(0)
+#define NTOHF2(nf, hf)                                                             \
+        do {                                                                       \
+             unsigned long tmp;                                                    \
+             bcopy ((char *) &(nf), (char *) &tmp, sizeof (float));                \
+	     NTOHUL (tmp);                                                         \
+	     bcopy ((char *) &tmp, (char *) &(hf), sizeof (float));                \
+	}while(0)
+
+/* pointers */
+#define HTONP(p)          ((p) = (void *) htonl ((unsigned long) p))
+#define NTOHP(p)          ((p) = (void *) ntohl ((unsigned long) p))
+#define HTONP2(hp, np)    ((np) = (void *) htonl ((unsigned long) hp))
+#define NTOHP2(np, hp)    ((hp) = (void *) ntohl ((unsigned long) np))
+
+/* unsigned long */
+#define HTONUL(l)         ((l) = htonl((l)))
+#define NTOHUL(l)         ((l) = ntohl((l)))
+#define HTONUL2(hl, nl)   ((nl) = htonl ((hl)))
+#define NTOHUL2(nl, hl)   ((hl) = ntohl ((nl)))
+
+/* signed long */
+#define HTONSL(l)         ((l) = (long) htonl ((unsigned long) (l)))
+#define NTOHSL(l)         ((l) = (long) ntohl ((unsigned long) (l)))
+#define HTONSL2(hl, nl)   ((nl) = (long) htonl ((unsigned long) (hl)))
+#define NTOHSL2(nl, hl)   ((hl) = (long) ntohl ((unsigned long) (nl)))
+
+/* unsigned int */
+#define HTONUI(i)         ((i) = (unsigned int) htonl ((unsigned long) (i)))
+#define NTOHUI(i)         ((i) = (unsigned int) ntohl ((unsigned long) (i)))
+#define HTONUI2(hi, ni)   ((ni) = (unsigned int) htonl ((unsigned long) (hi)))
+#define NTOHUI2(ni, hi)   ((hi) = (unsigned int) ntohl ((unsigned long) (ni)))
+
+/* signed int */
+#define HTONSI(i)         ((i) = (int) htonl ((unsigned long) (i)))
+#define NTOHSI(i)         ((i) = (int) ntohl ((unsigned long) (i)))
+#define HTONSI2(hi, ni)   ((ni) = (int) htonl ((unsigned long) (hi)))
+#define NTOHSI2(ni, hi)   ((hi) = (int) ntohl ((unsigned long) (ni)))
+
+/* unsigned short */
+#define HTONUS(s)         ((s) = htons((s)))
+#define NTOHUS(s)         ((s) = ntohs((s)))
+#define HTONUS2(hs, ns)   ((ns) = htons((hs)))
+#define NTOHUS2(ns, hs)   ((hs) = ntohs((ns)))
+
+#else   /* WORDS_BIGENDIAN */
+
+/* double */
+#define HTOND(d)          (d)
+#define NTOHD(d)          (d)
+#define HTOND2(hd, nd)    ((hd) = (nd))
+#define NTOHD2(nd, hd)    ((nd) = (hd))
+
+/* float */
+#define HTONF(f)          (f)
+#define NTOHF(f)          (f)
+#define HTONF2(hf, nf)    ((nf) = (hf))
+#define NTOHF2(nf, hf)    ((hf) = (nf))
+
+/* pointers */
+#define HTONP(p)          (p)
+#define NTOHP(p)          (p)
+#define HTONP2(hp, np)    ((np) = (hp))
+#define NTOHP2(np, hp)    ((hp) = (np))
+
+/* unsigned long */
+#define HTONUL(l)         (l)
+#define NTOHUL(l)         (l)
+#define HTONUL2(hl, nl)   ((nl) = (hl))
+#define NTOHUL2(nl, hl)   ((hl) = (nl))
+
+/* signed long */
+#define HTONSL(l)         (l)
+#define NTOHSL(l)         (l)
+#define HTONSL2(hl, nl)   ((nl) = (hl))
+#define NTOHSL2(nl, hl)   ((hl) = (nl))
+
+/* unsigned int */
+#define HTONUI(i)         (i)
+#define NTOHUI(i)         (i)
+#define HTONUI2(hi, ni)   ((ni) = (hi))
+#define NTOHUI2(ni, hi)   ((hi) = (ni))
+
+/* signed int */
+#define HTONSI(i)         (i)
+#define NTOHSI(i)         (i)
+#define HTONSI2(hi, ni)   ((ni) = (hi))
+#define NTOHSI2(ni, hi)   ((hi) = (ni))
+
+/* unsigned short */
+#define HTONUS(s)         (s)
+#define NTOHUS(s)         (s)
+#define HTONUS2(hs, ns)   ((ns) = (hs))
+#define NTOHUS2(ns, hs)   ((hs) = (ns))
+
+
+
+#endif
+
+#endif /* netorder.h */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/pathmax.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/pathmax.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/pathmax.h	(revision 16561)
@@ -0,0 +1,53 @@
+/* Define PATH_MAX somehow.  Requires sys/types.h.
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#ifndef _PATHMAX_H
+#define _PATHMAX_H
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
+#include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
+#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
+#include <sys/param.h>
+#endif
+
+#if !defined(PATH_MAX) && defined(MAXPATHLEN)
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+#endif /* _PATHMAX_H */
Index: /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.c	(revision 16561)
@@ -0,0 +1,1079 @@
+/**************************************************************************
+ *
+ * perf_hash.c -- Perfect hashing functions
+ * Copyright (C) 1992  Bohdan S. Majewski
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ * 1994 Neil Sharman
+ *      Small modifications were made to the interface so that it fitted in
+ *      with mg.
+ *
+ **************************************************************************/
+
+#define STRUCT
+
+#include "sysfuncs.h"
+
+#include "local_strings.h"
+#include "memlib.h"
+#include "messages.h"
+#include "perf_hash.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#define FALSE 0
+#define TRUE  1
+
+#define STATIC 0
+
+/* Random Number stuff */
+static long seed[] = {0, 0};
+#define RANDOM() irandm(seed)
+#define SEED_RANDOM(the_seed) do{ seed[0] = the_seed; }while(0)
+
+/* Max number of keys - must be a power of 2 minus 1 */
+static int MAX_M;
+
+
+static int MAX_CH;
+
+
+/* the width of the mapping tables  */
+static int MAX_L;
+
+
+
+#define c     1.23
+static int MAX_N;
+
+/* An arbitrary amount to add to both MAX_N and n for building tree/acyclic
+   graph -- John McPherson - jrm21 (22-02-2001) */
+#define MAX_N_HEADROOM  100
+
+
+static u_char *translate;
+
+#define MEMBER 0
+#define ERASED 1
+
+
+
+/* direction constants for the edge oriented 3-graph representation */
+#define A              0
+#define B        (MAX_M)
+#define C          (2*B)
+
+/* NODEa, NODEb, NODEc, FIRST and NEXT are used to represent a 3-graph */
+static int *NODEa, *NODEb, *NODEc;
+static int *FIRST;
+static int *NEXT;
+
+
+#define norm(e) ((e) % MAX_M)
+/* in general should be ((e) % MAX_M) but MAX_M = 2^k - 1 */
+
+
+static char *mk;		/* for marking edges/vertices as removed */
+static int *g;			/* array g defines a minimal perfect hash function */
+
+
+
+/* stack of edges, created during the independency */
+/* test, used in the assignment step               */
+static struct
+  {
+    int sp;
+    int *st;
+  }
+S;
+#define push(s, e) s.st[s.sp++] = (e)
+#define pop(s, e)  e = s.st[--s.sp]
+
+#ifndef STRUCT
+static long **tb0, **tb1, **tb2;
+#else
+struct tb_entry **tb;
+#endif
+
+
+static int 
+unique (int v)
+{				/* checks if vertex v belongs to only one edge */
+  return (FIRST[v] != 0 && NEXT[FIRST[v]] == 0) ? FIRST[v] : 0;
+}				/*unique */
+
+
+
+
+
+
+
+
+static void 
+delete (int v, int e)
+{				/* deletes edge e from list of v */
+
+  int b;
+
+  b = FIRST[v];
+  assert (norm (b) != 0);
+  if (norm (b) == e)
+    FIRST[v] = NEXT[FIRST[v]];
+  else
+    {
+      while (norm (NEXT[b]) != e)
+	{
+	  b = NEXT[b];
+	  assert (norm (b) != 0);
+	}
+      NEXT[b] = NEXT[NEXT[b]];
+    }
+}				/*delete */
+
+
+
+
+
+
+#if 0
+/* recursively removes the edges of a 3-graph which have at least */
+/* one unique vertex. The removed edges are placed on stack S     */
+static void 
+ph_remove (int e, int v)
+{
+  int b;
+  mk[e] = ERASED;
+  push (S, e);
+
+  if (NODEa[e] != v)
+    delete (NODEa[e], e);
+  if (NODEb[e] != v)
+    delete (NODEb[e], e);
+  if (NODEc[e] != v)
+    delete (NODEc[e], e);
+
+  if (NODEa[e] != v && mk[b = norm (unique (NODEa[e]))] == MEMBER)
+    ph_remove (b, NODEa[e]);
+  if (NODEb[e] != v && mk[b = norm (unique (NODEb[e]))] == MEMBER)
+    ph_remove (b, NODEb[e]);
+  if (NODEc[e] != v && mk[b = norm (unique (NODEc[e]))] == MEMBER)
+    ph_remove (b, NODEc[e]);
+}
+#else
+
+
+static int StackPos, StackMax;
+static struct Stack
+  {
+    int e, v, m;
+  }
+ *Stack = NULL;
+
+#define StackAdd(_e, _v, _m)						\
+  do {									\
+    if (StackPos == StackMax)						\
+      {									\
+        StackMax <<= 1;							\
+	if (!(Stack = Xrealloc(Stack, sizeof(*Stack) * StackMax)))	\
+	  return(-1);							\
+      }									\
+    Stack[StackPos].e = (_e);						\
+    Stack[StackPos].v = (_v);						\
+    Stack[StackPos++].m = (_m);						\
+  } while(0)
+
+
+static int 
+ph_remove (int e, int v)
+{
+  if (!Stack)
+    {
+      StackMax = 256;
+      if (!(Stack = Xmalloc (sizeof (*Stack) * StackMax)))
+	return (-1);
+      StackPos = 0;
+    }
+  Stack[StackPos].e = e;
+  Stack[StackPos].v = v;
+  Stack[StackPos].m = 0;
+  StackPos++;
+  while (StackPos)
+    {
+      int m, b;
+      StackPos--;
+      e = Stack[StackPos].e;
+      v = Stack[StackPos].v;
+      m = Stack[StackPos].m;
+
+
+      switch (m)
+	{
+	case 0:
+	  {
+	    mk[e] = ERASED;
+	    push (S, e);
+
+	    if (NODEa[e] != v)
+	      delete (NODEa[e], e);
+	    if (NODEb[e] != v)
+	      delete (NODEb[e], e);
+	    if (NODEc[e] != v)
+	      delete (NODEc[e], e);
+
+	    if (NODEa[e] != v && mk[b = norm (unique (NODEa[e]))] == MEMBER)
+	      {
+		Stack[StackPos++].m = 1;
+		StackAdd (b, NODEa[e], 0);
+		break;
+	      }
+	  }
+	case 1:
+	  {
+	    if (NODEb[e] != v && mk[b = norm (unique (NODEb[e]))] == MEMBER)
+	      {
+		Stack[StackPos++].m = 2;
+		StackAdd (b, NODEb[e], 0);
+		break;
+	      }
+	  }
+	case 2:
+	  if (NODEc[e] != v && mk[b = norm (unique (NODEc[e]))] == MEMBER)
+	    StackAdd (b, NODEc[e], 0);
+	}
+    }
+  return (0);
+}
+#endif
+
+
+
+
+
+/* returns TRUE if and only if set of edges created in the tree_builder */
+/* procedure form a 3-graph with independent edges. Edges of a 3-graph  */
+/* are independent iff repeated deletion of edges containing vertices   */
+/* of degree 1 results in a graph with no edges in it.                  */
+static int 
+acyclic (int m, int n)
+{
+
+  int v, e;
+  /* build list of edges for each node */
+  for (v = 1; v <= n; FIRST[v++] = 0);
+  for (e = 1; e <= m; e++)
+    {
+      NEXT[A + e] = FIRST[NODEa[e]];
+      FIRST[NODEa[e]] = A + e;
+      NEXT[B + e] = FIRST[NODEb[e]];
+      FIRST[NODEb[e]] = B + e;
+      NEXT[C + e] = FIRST[NODEc[e]];
+      FIRST[NODEc[e]] = C + e;
+
+      /* mark edge e as belonging to the hypergraph */
+      mk[e] = MEMBER;
+    }
+
+
+  S.sp = 0;			/* empty the stack */
+  mk[0] = ERASED;		/* a sentinel for the test below */
+  for (v = 1; v <= n; v++)
+    {
+      if (mk[e = norm (unique (v))] == MEMBER)
+	ph_remove (e, v);
+    }
+
+  /* check if any edges were left in the graph */
+  return S.sp == m;
+}				/*acyclic */
+
+
+
+
+
+
+
+
+/* repeatedly maps words into edges of a 3-graph until the edges */
+/* are independent.                                              */
+static int 
+tree_builder (int num, u_char ** keys, int *n)
+{
+
+  int e, k = 0;
+  int i, j;
+  u_char *w;
+
+  do
+    {
+      /* Added variable - need to increase MAX_N on allocate as well */
+      *n = (int) (num * c + MAX_N_HEADROOM); /* john mcpherson 22-02-2001 */
+
+      /* initialize tables of random integers */
+      for (i = 0; i < MAX_CH; i++)
+	for (j = 0; j < MAX_L; j++)
+	  {
+#ifndef STRUCT
+	    tb0[i][j] = RANDOM () % *n;
+	    tb1[i][j] = RANDOM () % *n;
+	    tb2[i][j] = RANDOM () % *n;
+#else
+	    struct tb_entry *tbp = &tb[i][j];
+	    tbp->tb0 = RANDOM () % *n;
+	    tbp->tb1 = RANDOM () % *n;
+	    tbp->tb2 = RANDOM () % *n;
+#endif
+	  }
+
+      /* compute an edge (NODEa[e], NODEb[e], NODEc[e]) for each word */
+      for (e = 1; e <= num; e++)
+	{
+	  /*generate an edge corresponding to the ith word */
+	  int x = 0, y = 0, z = 0, l;
+	  w = keys[e - 1];
+
+	  l = *w++;
+	  j = (l - 1) % MAX_L;
+
+	  do
+	    {
+#ifndef STRUCT
+	      x += tb0[translate[*w]][j];
+	      y += tb1[translate[*w]][j];
+	      z += tb2[translate[*w]][j];
+#else
+	      struct tb_entry *tbp = &tb[translate[*w]][j];
+	      x += tbp->tb0;
+	      y += tbp->tb1;
+	      z += tbp->tb2;
+#endif
+	      if (++j >= MAX_L)
+		j = 0;
+	      w++;
+	    }
+	  while (--l);
+	  x = (x % *n) + 1;
+	  y = (y % *n) + 1;
+	  z = (z % *n) + 1;
+	  if (y == x && ++y > *n)
+	    y = 1;
+	  if (z == x && ++z > *n)
+	    z = 1;
+	  if (z == y)
+	    {
+	      if (++z > *n)
+		z = 1;
+	      if (z == x && ++z > *n)
+		z = 1;
+	    }
+
+	  NODEa[e] = x;
+	  NODEb[e] = y;
+	  NODEc[e] = z;
+	}
+      if (++k > 50)		/* make sure we will not wait for ever */
+	FatalError (1, "Unable to generate the perfect hash function. "
+		    "This is probably because there aren't enough words");
+    }
+  while (!acyclic (num, *n));
+  return k;
+}				/*tree_builder */
+
+
+
+
+
+
+
+
+
+/* calculates values of the g function/array so that i-th word is */
+/* placed at the (i-1)st position of a hash table.                */
+static void 
+assign (int n)
+{
+
+  int v, w = 0, e;
+
+  /* mark all the vertices of the 3-graph as unassigned */
+  for (v = 1; v <= n; mk[v++] = FALSE);
+
+  while (S.sp > 0)
+    {
+      int xor = 0;
+      pop (S, e);
+      v = NODEa[e];
+      if (mk[v])
+	xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      v = NODEb[e];
+      if (mk[v])
+	xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      v = NODEc[e];
+      if (mk[v])
+	xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      g[w] = (e - 1) ^ xor;
+    }
+}				/*assign */
+
+
+
+
+
+
+
+
+
+/*check returns 0 if no error in mphf is detected, else returns error code */
+static int 
+check (int m)
+{
+
+  int e, err_code = 0;
+
+  /*for each word compute its place in hash table and check if correct */
+  for (e = 1; e <= m; e++)
+    {
+      if ((e - 1) != (g[NODEa[e]] ^ g[NODEb[e]] ^ g[NODEc[e]]))
+	{
+	  (void) fprintf (stderr, "Error for %dth word detected.\n", e);
+	  err_code = e;
+	}
+    }
+  return err_code;
+}				/*check */
+
+
+
+
+
+
+
+#if 0
+void 
+outputh (int m, int n)
+{
+
+  int i, j, w = (int) ceil (log10 ((double) n + 1.0)) + 1;
+
+#if 0
+  char *PRG =
+  "int h(char *s)\n"
+  "{\n"
+  "  int u, v, w, i;\n"
+  "i = (strlen(s) - 1) % _HXL;\n"
+  "  u = v = w = 0;\n"
+  "  while(*s)\n"
+  "    {\n"
+  "      u += mt1[(*s) - _HFC][i];\n"
+  "      v += mt2[(*s) - _HFC][i];\n"
+  "      w += mt3[(*s++) - _HFC][i];\n"
+  "      if(++i >= _HXL) i = 0;\n"
+  "    }\n"
+  "  u %= _HFN; v %= _HFN; w %= _HFN;\n"
+  "  if(u == v && ++v >= _HFN) v = 0;\n"
+  "  if(w == u && ++w >= _HFN) w = 0;\n"
+  "  if(w == v && ++w >= _HFN) w = 0;\n"
+  "  if(w == u && ++w >= _HFN) w = 0;\n"
+  "  return (g[u] ^ g[v] ^ g[w]) % _HFM;\n"
+  "};\n\n";
+#endif
+
+  char *PRG =
+  "int h(char *s)\n"
+  "{\n"
+  "  int u, v, w, i;\n"
+  "  i = (strlen(s) - 1) % _HXL;\n"
+  "  u = v = w = 0;\n"
+  "  while(*s)\n"
+  "    {\n"
+  "      if((u += mt1[(*s) - _HFC][i]) >= _HFN) u -= _HFN;\n"
+  "      if((v += mt2[(*s) - _HFC][i]) >= _HFN) v -= _HFN;\n"
+  "      if((w += mt3[(*s++) - _HFC][i]) >= _HFN) w -= _HFN;\n"
+  "      if(++i >= _HXL) i = 0;\n"
+  "    }\n"
+  "  if(u == v && ++v >= _HFN) v = 0;\n"
+  "  if(w == v) \n"
+  "    {\n"
+  "      if(++w >= _HFN) w = 0;\n"
+  "      if(w == u && ++w >= _HFN) w = 0;\n"
+  "    }\n"
+  "  else\n"
+  "    if(w == u)\n"
+  "      {\n"
+  "	     if(++w >= _HFN) w = 0;\n"
+  "	     if(w == v && ++w >= _HFN) w = 0;\n"
+  "      }\n"
+  "  return ((g[u] ^ g[v] ^ g[w]) > _HFM)? 0 : (g[u] ^ g[v] ^ g[w]);\n"
+  "}\n\n";
+
+  printf ("#define _HFN %d\n#define _HFC %d\n", n, fc);
+  printf ("#define _HFM %d\n#define _HXL %d\n", m, MAX_L);
+  printf ("#define _HFA %d\n\nstatic int g[_HFN] = {\n", lc - fc + 1);
+  for (i = 1; i < n; i++)
+    {
+      if (i % 8 == 0)
+	putchar ('\n');
+      printf ("%5d, ", g[i]);
+    }
+  printf ("%5d};\n\n", g[n]);
+
+  printf ("static int mt1[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; j++)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; i++)
+	printf ("%*d,", w, tb0[j][i]);
+      printf ("%*d},\n", w, tb0[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; i++)
+    printf ("%*d,", w, tb0[lc][i]);
+  printf ("%*d}\n", w, tb0[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("static int mt2[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; j++)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; i++)
+	printf ("%*d,", w, tb1[j][i]);
+      printf ("%*d},\n", w, tb1[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; i++)
+    printf ("%*d,", w, tb1[lc][i]);
+  printf ("%*d}\n", w, tb1[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("static int mt3[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; j++)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; i++)
+	printf ("%*d,", w, tb2[j][i]);
+      printf ("%*d},\n", w, tb2[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; i++)
+    printf ("%*d,", w, tb2[lc][i]);
+  printf ("%*d}\n", w, tb2[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("%s", PRG);
+}				/*outputh */
+#endif
+
+
+
+
+
+
+
+static void 
+make_trans_func (int num, u_char ** keys)
+{
+  int i, j;
+
+  bzero ((char *) translate, sizeof (translate));
+
+  MAX_L = 1;
+  for (i = 0; i < num; i++)
+    {
+      unsigned len = keys[i][0];
+      u_char *s = keys[i] + 1;
+      for (; len; len--, s++)
+	translate[*s] = 1;
+      if (i)
+	{
+	  int l;
+	  l = prefixlen (keys[i - 1], keys[i]);
+	  if (l + 1 > MAX_L)
+	    MAX_L = l + 1;
+	}
+    }
+  j = 0;
+  for (i = 0; i < 256; i++)
+    if (translate[i])
+      translate[i] = j++;
+  MAX_CH = j;
+}
+
+
+static void 
+temp_free (void)
+{
+  if (NODEa)
+    Xfree (NODEa);
+  if (NODEb)
+    Xfree (NODEb);
+  if (NODEc)
+    Xfree (NODEc);
+  if (FIRST)
+    Xfree (FIRST);
+  if (NEXT)
+    Xfree (NEXT);
+  if (S.st)
+    Xfree (S.st);
+  if (mk)
+    Xfree (mk);
+}
+
+static void 
+all_free (void)
+{
+  int i;
+  temp_free ();
+  if (translate)
+    Xfree (translate);
+  if (g)
+    Xfree (g);
+#ifndef STRUCT
+  for (i = 0; i < MAX_CH; i++)
+    {
+      if (tb0 && tb0[i])
+	Xfree (tb0[i]);
+      if (tb1 && tb1[i])
+	Xfree (tb1[i]);
+      if (tb2 && tb2[i])
+	Xfree (tb2[i]);
+    }
+  if (tb0)
+    Xfree (tb0);
+  if (tb1)
+    Xfree (tb1);
+  if (tb2)
+    Xfree (tb2);
+#else
+  for (i = 0; i < MAX_CH; i++)
+    if (tb && tb[i])
+      Xfree (tb[i]);
+  if (tb)
+    Xfree (tb);
+#endif
+}
+
+static int 
+allocate_memory (void)
+{
+  int i, ok = 1;
+  NODEa = Xmalloc (sizeof (int) * (MAX_M + 1));
+  NODEb = Xmalloc (sizeof (int) * (MAX_M + 1));
+  NODEc = Xmalloc (sizeof (int) * (MAX_M + 1));
+  FIRST = Xmalloc (sizeof (int) * (MAX_N + 1));
+  NEXT = Xmalloc (sizeof (int) * (MAX_M + 1) * 3);
+  S.st = Xmalloc (sizeof (int) * MAX_M);
+  mk = Xmalloc (sizeof (char) * (MAX_N + 1));
+  g = Xmalloc (sizeof (int) * (MAX_N + 1));
+#ifndef STRUCT
+  tb0 = Xmalloc (sizeof (int *) * MAX_CH);
+  tb1 = Xmalloc (sizeof (int *) * MAX_CH);
+  tb2 = Xmalloc (sizeof (int *) * MAX_CH);
+  for (i = 0; i < MAX_CH; i++)
+    {
+      if (tb0)
+	if (!(tb0[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+      if (tb1)
+	if (!(tb1[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+      if (tb2)
+	if (!(tb2[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+    }
+
+  if (!(NODEa && NODEb && NODEc && FIRST && NEXT && S.st && mk && g &&
+	tb0 && tb1 && tb2 && ok))
+    {
+      all_free ();
+      return (-1);
+    }
+#else
+  tb = Xmalloc (sizeof (struct tb_entry *) * MAX_CH);
+  for (i = 0; i < MAX_CH; i++)
+    if (tb)
+      if (!(tb[i] = Xmalloc (sizeof (struct tb_entry) * MAX_L)))
+	  ok = 0;
+
+  if (!(NODEa && NODEb && NODEc && FIRST && NEXT && S.st && mk && g &&
+	tb && ok))
+    {
+      all_free ();
+      return (-1);
+    }
+#endif
+  return (0);
+}
+
+
+perf_hash_data *
+gen_hash_func (int num, u_char ** keys, int r)
+{
+  int n;
+  perf_hash_data *phd;
+  translate = Xmalloc (sizeof (u_char) * 256);
+  bzero ((char *) translate, sizeof (u_char) * 256);  /* [RPAP - Feb 97: WIN32 Port] */
+  make_trans_func (num, keys);
+
+  if (r <= 0)
+    SEED_RANDOM ((long) time ((time_t *) NULL));
+  else
+    SEED_RANDOM (r);
+
+  MAX_M = num + 1;
+
+  /* For extremely small lexicons (<4 words), this isn't good enough! 
+   -- John McPherson jrm21 22-02-2001 */
+  /* MAX_N = MAX_M + MAX_M / 4; */
+  MAX_N = (int)(ceil (1.25 * MAX_M)); 
+  /* Add some space for "experimentation" of n in tree_builder() */
+  MAX_N += MAX_N_HEADROOM;
+
+  if (allocate_memory () == -1)
+    return (NULL);
+
+  /* construct an "acyclic" hypergraph */
+  tree_builder (num, keys, &n);
+  /* compute function g */
+  assign (n);
+
+  if (check (num) != 0)
+    FatalError (1, "An error has been detected in the mphf.");
+  temp_free ();
+
+  if (!(phd = Xmalloc (sizeof (perf_hash_data))))
+    return (NULL);
+
+  bcopy ((char *) &g[1], (char *) g, n * sizeof (int));
+
+  phd->MAX_L = MAX_L;
+  phd->MAX_N = n;
+  phd->MAX_M = num;
+  phd->MAX_CH = MAX_CH;
+  phd->translate = translate;
+  phd->g = g;
+#ifndef STRUCT
+  phd->tb0 = tb0;
+  phd->tb1 = tb1;
+  phd->tb2 = tb2;
+#else
+  phd->tb = tb;
+#endif
+  return (phd);
+}
+
+int 
+write_perf_hash_data (FILE * f, perf_hash_data * phd)
+{
+  int tot, i;
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONSI(phd->MAX_L);
+  HTONSI(phd->MAX_N);
+  HTONSI(phd->MAX_M);
+  HTONSI(phd->MAX_CH);
+
+  tot = fwrite ((char *) &phd->MAX_L, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_N, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_M, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_CH, sizeof (int), 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(phd->MAX_L);
+  NTOHSI(phd->MAX_N);
+  NTOHSI(phd->MAX_M);
+  NTOHSI(phd->MAX_CH);
+
+  tot += fwrite ((char *) phd->translate, sizeof (u_char), 256, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; i++)
+    HTONSI(phd->g[i]);
+
+  tot += fwrite ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; i++)
+    NTOHSI(phd->g[i]);
+
+#ifndef STRUCT
+  for (i = 0; i < phd->MAX_CH; i++)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int j;
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  HTONSI(phd->tb0[i][j]);
+	  HTONSI(phd->tb1[i][j]);
+	  HTONSI(phd->tb2[i][j]);
+	}
+
+      tot += fwrite ((char *) phd->tb0[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fwrite ((char *) phd->tb1[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fwrite ((char *) phd->tb2[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  NTOHSI(phd->tb0[i][j]);
+	  NTOHSI(phd->tb1[i][j]);
+	  NTOHSI(phd->tb2[i][j]);
+	}
+    }
+#else
+  for (i = 0; i < phd->MAX_CH; i++)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int j;
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  HTONSL(phd->tb[i][j].tb0);
+	  HTONSL(phd->tb[i][j].tb1);
+	  HTONSL(phd->tb[i][j].tb2);
+	}
+
+      tot += fwrite ((char *) phd->tb[i], sizeof (struct tb_entry), phd->MAX_L, f) *
+	sizeof (struct tb_entry);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  NTOHSL(phd->tb[i][j].tb0);
+	  NTOHSL(phd->tb[i][j].tb1);
+	  NTOHSL(phd->tb[i][j].tb2);
+	}
+    }
+#endif
+  return (tot);
+}
+
+
+perf_hash_data *
+read_perf_hash_data (FILE * f)
+{
+  perf_hash_data *phd;
+  int i, tot, ok = 1;
+  if (!(phd = Xmalloc (sizeof (perf_hash_data))))
+    return (NULL);
+  tot = fread ((char *) &phd->MAX_L, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_N, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_M, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_CH, sizeof (int), 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(phd->MAX_L);
+  NTOHSI(phd->MAX_N);
+  NTOHSI(phd->MAX_M);
+  NTOHSI(phd->MAX_CH);
+
+  if (tot != 4 * sizeof (int))
+      return (NULL);
+  phd->translate = Xmalloc (sizeof (u_char) * 256);
+  phd->g = Xmalloc (sizeof (int) * (phd->MAX_N + 1));
+#ifndef STRUCT
+  phd->tb0 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  phd->tb1 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  phd->tb2 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  for (i = 0; i < phd->MAX_CH; i++)
+    {
+      if (phd->tb0)
+	if (!(phd->tb0[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+      if (phd->tb1)
+	if (!(phd->tb1[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+      if (phd->tb2)
+	if (!(phd->tb2[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+    }
+  if (!(phd->translate && phd->g && phd->tb0 && phd->tb1 && phd->tb2 && ok))
+    {
+      if (phd->translate)
+	Xfree (phd->translate);
+      if (phd->g)
+	Xfree (phd->g);
+      for (i = 0; i < MAX_CH; i++)
+	{
+	  if (phd->tb0 && phd->tb0[i])
+	    Xfree (phd->tb0[i]);
+	  if (phd->tb1 && phd->tb1[i])
+	    Xfree (phd->tb1[i]);
+	  if (phd->tb2 && phd->tb2[i])
+	    Xfree (phd->tb2[i]);
+	}
+      if (phd->tb0)
+	Xfree (phd->tb0);
+      if (phd->tb1)
+	Xfree (phd->tb1);
+      if (phd->tb2)
+	Xfree (phd->tb2);
+      Xfree (phd);
+      return (NULL);
+    }
+  tot += fread ((char *) phd->translate, sizeof (u_char), 256, f);
+  tot += fread ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; i++)
+    NTOHSI(phd->g[i]);
+
+  for (i = 0; i < phd->MAX_CH; i++)
+    {
+      int j;
+
+      tot += fread ((char *) phd->tb0[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fread ((char *) phd->tb1[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fread ((char *) phd->tb2[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  NTOHSI(phd->tb0[i][j]);
+	  NTOHSI(phd->tb1[i][j]);
+	  NTOHSI(phd->tb2[i][j]);
+	}
+    }
+#else
+  phd->tb = Xmalloc (sizeof (struct tb_entry *) * phd->MAX_CH);
+  for (i = 0; i < phd->MAX_CH; i++)
+    if (phd->tb)
+      if (!(phd->tb[i] = Xmalloc (sizeof (struct tb_entry) * phd->MAX_L)))
+	  ok = 0;
+  if (!(phd->translate && phd->g && phd->tb && ok))
+    {
+      if (phd->translate)
+	Xfree (phd->translate);
+      if (phd->g)
+	Xfree (phd->g);
+      for (i = 0; i < MAX_CH; i++)
+	if (phd->tb && phd->tb[i])
+	  Xfree (phd->tb[i]);
+      if (phd->tb)
+	Xfree (phd->tb);
+      Xfree (phd);
+      return (NULL);
+    }
+  tot += fread ((char *) phd->translate, sizeof (u_char), 256, f);
+  tot += fread ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; i++)
+    NTOHSI(phd->g[i]);
+
+  for (i = 0; i < phd->MAX_CH; i++)
+    {
+      int j;
+
+      tot += fread ((char *) phd->tb[i], sizeof (struct tb_entry), phd->MAX_L, f) *
+	sizeof (struct tb_entry);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; j++)
+	{
+	  NTOHSL(phd->tb[i][j].tb0);
+	  NTOHSL(phd->tb[i][j].tb1);
+	  NTOHSL(phd->tb[i][j].tb2);
+	}
+    }
+#endif
+  return (phd);
+}
+
+void 
+free_perf_hash (perf_hash_data * phd)
+{
+  int i;
+  if (phd->translate)
+    Xfree (phd->translate);
+  if (phd->g)
+    Xfree (phd->g);
+  for (i = 0; i < phd->MAX_CH; i++)
+    if (phd->tb && phd->tb[i])
+      Xfree (phd->tb[i]);
+  if (phd->tb)
+    Xfree (phd->tb);
+  Xfree (phd);
+}
+
+
+int 
+perf_hash (perf_hash_data * phd, u_char * s)
+{
+  int u, v, w, i, l;
+  l = *s++;
+  i = (l - 1) % phd->MAX_L;
+  u = v = w = 0;
+  while (l)
+    {
+#ifndef STRUCT
+      if ((u += phd->tb0[phd->translate[*s]][i]) >= phd->MAX_N)
+	u -= phd->MAX_N;
+      if ((v += phd->tb1[phd->translate[*s]][i]) >= phd->MAX_N)
+	v -= phd->MAX_N;
+      if ((w += phd->tb2[phd->translate[*s]][i]) >= phd->MAX_N)
+	w -= phd->MAX_N;
+#else
+      struct tb_entry *tbp = &phd->tb[phd->translate[*s]][i];
+      if ((u += tbp->tb0) >= phd->MAX_N)
+	u -= phd->MAX_N;
+      if ((v += tbp->tb1) >= phd->MAX_N)
+	v -= phd->MAX_N;
+      if ((w += tbp->tb2) >= phd->MAX_N)
+	w -= phd->MAX_N;
+#endif
+      if (++i >= phd->MAX_L)
+	i = 0;
+      l--;
+      s++;
+    }
+  if (u == v && ++v >= phd->MAX_N)
+    v = 0;
+  if (w == v)
+    {
+      if (++w >= phd->MAX_N)
+	w = 0;
+      if (w == u && ++w >= phd->MAX_N)
+	w = 0;
+    }
+  else if (w == u)
+    {
+      if (++w >= phd->MAX_N)
+	w = 0;
+      if (w == v && ++w >= phd->MAX_N)
+	w = 0;
+    }
+  return ((phd->g[u] ^ phd->g[v] ^ phd->g[w]) > phd->MAX_M) ? 0 :
+    (phd->g[u] ^ phd->g[v] ^ phd->g[w]);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/perf_hash.h	(revision 16561)
@@ -0,0 +1,55 @@
+/**************************************************************************
+ *
+ * perf_hash.h -- Perfect hashing functions
+ * Copyright (C) 1994  Bohdan S. Majewski and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_PERF_HASH
+#define H_PERF_HASH
+
+
+
+
+struct tb_entry
+  {
+    long tb0, tb1, tb2;
+  };
+
+typedef struct
+  {
+    int MAX_L;
+    int MAX_N;
+    int MAX_M;
+    int MAX_CH;
+    u_char *translate;
+    int *g;
+    struct tb_entry **tb;
+  }
+perf_hash_data;
+
+perf_hash_data *gen_hash_func (int num, u_char ** keys, int r);
+
+int perf_hash (perf_hash_data * phd, u_char * s);
+
+int write_perf_hash_data (FILE * f, perf_hash_data * phd);
+
+void free_perf_hash (perf_hash_data * phd);
+
+perf_hash_data *read_perf_hash_data (FILE * f);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/random.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/random.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/random.c	(revision 16561)
@@ -0,0 +1,211 @@
+/**************************************************************************
+ *
+ * random.c -- pseudo random number generator
+ * Copyright (C) 1994  Chris Wallace (csw@bruce.cs.monash.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+/*
+ *	A random number generator called as a function by
+ *	random (iseed)	or	irandm (iseed)
+ *	The parameter should be a pointer to a 2-element long vector.
+ *	The first function returns a double uniform in 0 .. 1.
+ *	The second returns a long integer uniform in 0 .. 2**31-1
+ *	Both update iseed[] in exactly the same way.
+ *	iseed[] must be a 2-element integer vector.
+ *	The initial value of the second element may be anything.
+ *
+ *	The period of the random sequence is 2**32 * (2**32-1)
+ *	The table mt[0:127] is defined by mt[i] = 69069 ** (128-i)
+ */
+
+#define MASK ((long) 593970775)
+/*	or in hex, 23674657	*/
+
+#define SCALE ((double) 1.0 / (1024.0 * 1024.0 * 1024.0 * 2.0))
+/*	i.e. 2 to power -31	*/
+
+static long mt [128] =   {
+      902906369,
+     2030498053,
+     -473499623,
+     1640834941,
+      723406961,
+     1993558325,
+     -257162999,
+    -1627724755,
+      913952737,
+      278845029,
+     1327502073,
+    -1261253155,
+      981676113,
+    -1785280363,
+     1700077033,
+      366908557,
+    -1514479167,
+     -682799163,
+      141955545,
+     -830150595,
+      317871153,
+     1542036469,
+     -946413879,
+    -1950779155,
+      985397153,
+      626515237,
+      530871481,
+      783087261,
+    -1512358895,
+     1031357269,
+    -2007710807,
+    -1652747955,
+    -1867214463,
+      928251525,
+     1243003801,
+    -2132510467,
+     1874683889,
+     -717013323,
+      218254473,
+    -1628774995,
+    -2064896159,
+       69678053,
+      281568889,
+    -2104168611,
+     -165128239,
+     1536495125,
+      -39650967,
+      546594317,
+     -725987007,
+     1392966981,
+     1044706649,
+      687331773,
+    -2051306575,
+     1544302965,
+     -758494647,
+    -1243934099,
+      -75073759,
+      293132965,
+    -1935153095,
+      118929437,
+      807830417,
+    -1416222507,
+    -1550074071,
+      -84903219,
+     1355292929,
+     -380482555,
+    -1818444007,
+     -204797315,
+      170442609,
+    -1636797387,
+      868931593,
+     -623503571,
+     1711722209,
+      381210981,
+     -161547783,
+     -272740131,
+    -1450066095,
+     2116588437,
+     1100682473,
+      358442893,
+    -1529216831,
+     2116152005,
+     -776333095,
+     1265240893,
+     -482278607,
+     1067190005,
+      333444553,
+       86502381,
+      753481377,
+       39000101,
+     1779014585,
+      219658653,
+     -920253679,
+     2029538901,
+     1207761577,
+    -1515772851,
+     -236195711,
+      442620293,
+      423166617,
+    -1763648515,
+     -398436623,
+    -1749358155,
+     -538598519,
+     -652439379,
+      430550625,
+    -1481396507,
+     2093206905,
+    -1934691747,
+     -962631983,
+     1454463253,
+    -1877118871,
+     -291917555,
+    -1711673279,
+      201201733,
+     -474645415,
+      -96764739,
+    -1587365199,
+     1945705589,
+     1303896393,
+     1744831853,
+      381957665,
+     2135332261,
+      -55996615,
+    -1190135011,
+     1790562961,
+    -1493191723,
+      475559465,
+          69069
+		};
+
+double 
+random (long is [2])
+{
+	long it, leh, nit;
+
+	it = is [0];
+	leh = is [1];
+	if (it <= 0)	
+		it = (it + it) ^ MASK;
+	else
+		it = it + it;
+	nit = it - 1;
+/*	to ensure all-ones pattern omitted    */
+	leh = leh * mt[nit & 127] + nit;
+	is [0] = it;    is [1] = leh;
+	if (leh < 0) leh = ~leh;
+	return (SCALE * ((long) (leh | 1)));
+}
+
+
+
+long 
+irandm (long is [2])
+{
+	long it, leh, nit;
+
+	it = is [0];
+	leh = is [1];
+	if (it <= 0)	
+		it = (it + it) ^ MASK;
+	else
+		it = it + it;
+	nit = it - 1;
+/*	to ensure all-ones pattern omitted    */
+	leh = leh * mt[nit & 127] + nit;
+	is [0] = it;    is [1] = leh;
+	if (leh < 0) leh = ~leh;
+	return (leh);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/random.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/random.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/random.h	(revision 16561)
@@ -0,0 +1,42 @@
+/**************************************************************************
+ *
+ * random.h -- pseudo random number generator
+ * Copyright (C) 1994  Chris Wallace (csw@bruce.cs.monash.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+/*
+ *	A random number generator called as a function by
+ *	random (iseed)	or	irandm (iseed)
+ *	The parameter should be a pointer to a 2-element long vector.
+ *	The first function returns a double uniform in 0 .. 1.
+ *	The second returns a long integer uniform in 0 .. 2**31-1
+ *	Both update iseed[] in exactly the same way.
+ *	iseed[] must be a 2-element integer vector.
+ *	The initial value of the second element may be anything.
+ *
+ *	The period of the random sequence is 2**32 * (2**32-1)
+ *	The table mt[0:127] is defined by mt[i] = 69069 ** (128-i)
+ */
+
+double random (long is [2]);
+long irandm (long is [2]);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/regex.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/regex.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/regex.c	(revision 16561)
@@ -0,0 +1,5426 @@
+/* Extended regular expression matching and search library,
+   version 0.12.
+   (Implements POSIX draft P10003.2/D11.2, except for
+   internationalization features.)
+
+   Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+  #pragma alloca
+#endif
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+/* We need this for `regex.h', and perhaps for the Emacs include files.  */
+#include <sys/types.h>
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if HAVE_LIBINTL_H || defined (_LIBC)
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+/* The `emacs' switch turns on certain matching commands
+   that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+#else  /* not emacs */
+
+/* If we are not linking with Emacs proper,
+   we can't use the relocating allocator
+   even if sysfuncs.h says that we can.  */
+#undef REL_ALLOC
+
+#if defined (STDC_HEADERS) || defined (_LIBC)
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+   `BSTRING', as far as I know, and neither of them use this code.  */
+#ifndef INHIBIT_STRING_HEADER
+#if HAVE_STRING_H || STDC_HEADERS || defined (_LIBC)
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n)	memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n)	memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n)	memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+#endif
+
+/* Define the syntax stuff for \<, \>, etc.  */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+   commands in re_match_2.  */
+#ifndef Sword 
+#define Sword 1
+#endif
+
+#ifdef SWITCH_ENUM_BUG
+#define SWITCH_ENUM_CAST(x) ((int)(x))
+#else
+#define SWITCH_ENUM_CAST(x) (x)
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set.  */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+   register int c;
+   static int done = 0;
+
+   if (done)
+     return;
+
+   bzero (re_syntax_table, sizeof re_syntax_table);
+
+   for (c = 'a'; c <= 'z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = 'A'; c <= 'Z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = '0'; c <= '9'; c++)
+     re_syntax_table[c] = Sword;
+
+   re_syntax_table['_'] = Sword;
+
+   done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+
+
+/* Get the interface, including the syntax bits.  */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes.  */
+#include <ctype.h>
+
+/* Jim Meyering writes:
+
+   "... Some ctype macros are valid only for character codes that
+   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+   using /bin/cc or gcc but without giving an ansi option).  So, all
+   ctype uses should be through macros like ISPRINT...  If
+   STDC_HEADERS is defined, then autoconf has verified that the ctype
+   macros don't need to be guarded with references to isascii. ...
+   Defining isascii to 1 should let any compiler worth its salt
+   eliminate the && through constant folding."  */
+
+#ifndef ISASCII
+#  if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
+#    define ISASCII(c) 1
+#  else
+#    define ISASCII(c) isascii(c)
+#  endif
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (ISASCII (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (ISASCII (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (ISASCII (c) && isprint (c))
+#define ISDIGIT(c) (ISASCII (c) && isdigit (c))
+#define ISALNUM(c) (ISASCII (c) && isalnum (c))
+#define ISALPHA(c) (ISASCII (c) && isalpha (c))
+#define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
+#define ISLOWER(c) (ISASCII (c) && islower (c))
+#define ISPUNCT(c) (ISASCII (c) && ispunct (c))
+#define ISSPACE(c) (ISASCII (c) && isspace (c))
+#define ISUPPER(c) (ISASCII (c) && isupper (c))
+#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+   since ours (we hope) works properly with all combinations of
+   machines, compilers, `char' and `unsigned char' argument types.
+   (Per Bothner suggested the basic approach.)  */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else  /* not __STDC__ */
+/* As in Harbison and Steele.  */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+
+
+/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
+   use `alloca' instead of `malloc'.  This is because using malloc in
+   re_search* or re_match* could cause memory leaks when C-g is used in
+   Emacs; also, malloc is slower and causes storage fragmentation.  On
+   the other hand, malloc is more portable, and easier to debug.  
+   
+   Because we sometimes use alloca, some routines have to be macros,
+   not functions -- `alloca'-allocated space disappears at the end of the
+   function it is called in.  */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+#define REGEX_FREE free
+
+#else /* not REGEX_MALLOC  */
+
+/* Emacs already defines alloca, sometimes.  */
+#ifndef alloca
+
+/* Make alloca work the best possible way.  */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top.  */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */ 
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable.  */
+#define REGEX_REALLOCATE(source, osize, nsize)				\
+  (destination = (char *) alloca (nsize),				\
+   bcopy (source, destination, osize),					\
+   destination)
+
+/* No need to do anything to free, after alloca.  */
+#define REGEX_FREE(arg) ((void)0) /* Do nothing!  But inhibit gcc warning.  */
+
+#endif /* not REGEX_MALLOC */
+
+/* Define how to allocate the failure stack.  */
+
+#ifdef REL_ALLOC
+#define REGEX_ALLOCATE_STACK(size)				\
+  r_alloc (&failure_stack_ptr, (size))
+#define REGEX_REALLOCATE_STACK(source, osize, nsize)		\
+  r_re_alloc (&failure_stack_ptr, (nsize))
+#define REGEX_FREE_STACK(ptr)					\
+  r_alloc_free (&failure_stack_ptr)
+
+#else /* not REL_ALLOC */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE_STACK malloc
+#define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize)
+#define REGEX_FREE_STACK free
+
+#else /* not REGEX_MALLOC */
+
+#define REGEX_ALLOCATE_STACK alloca
+
+#define REGEX_REALLOCATE_STACK(source, osize, nsize)			\
+   REGEX_REALLOCATE (source, osize, nsize)
+/* No need to explicitly free anything.  */
+#define REGEX_FREE_STACK(arg)
+
+#endif /* not REGEX_MALLOC */
+#endif /* not REL_ALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+   `string1' or just past its end.  This works if PTR is NULL, which is
+   a good thing.  */
+#define FIRST_STRING_P(ptr) 					\
+  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail.  */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define RETALLOC_IF(addr, n, t) \
+  if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits.  */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+static int re_match_2_internal ();
+
+
+/* These are the command codes that appear in compiled regular
+   expressions.  Some opcodes are followed by argument bytes.  A
+   command code can specify any interpretation whatsoever for its
+   arguments.  Zero bytes may appear in the compiled regular expression.  */
+
+typedef enum
+{
+  no_op = 0,
+
+  /* Succeed right away--no more backtracking.  */
+  succeed,
+
+        /* Followed by one byte giving n, then by n literal bytes.  */
+  exactn,
+
+        /* Matches any (more or less) character.  */
+  anychar,
+
+        /* Matches any one char belonging to specified set.  First
+           following byte is number of bitmap bytes.  Then come bytes
+           for a bitmap saying which chars are in.  Bits in each byte
+           are ordered low-bit-first.  A character is in the set if its
+           bit is 1.  A character too large to have a bit in the map is
+           automatically not in the set.  */
+  charset,
+
+        /* Same parameters as charset, but match any character that is
+           not one of those specified.  */
+  charset_not,
+
+        /* Start remembering the text that is matched, for storing in a
+           register.  Followed by one byte with the register number, in
+           the range 0 to one less than the pattern buffer's re_nsub
+           field.  Then followed by one byte with the number of groups
+           inner to this one.  (This last has to be part of the
+           start_memory only because we need it in the on_failure_jump
+           of re_match_2.)  */
+  start_memory,
+
+        /* Stop remembering the text that is matched and store it in a
+           memory register.  Followed by one byte with the register
+           number, in the range 0 to one less than `re_nsub' in the
+           pattern buffer, and one byte with the number of inner groups,
+           just like `start_memory'.  (We need the number of inner
+           groups here because we don't have any easy way of finding the
+           corresponding start_memory when we're at a stop_memory.)  */
+  stop_memory,
+
+        /* Match a duplicate of something remembered. Followed by one
+           byte containing the register number.  */
+  duplicate,
+
+        /* Fail unless at beginning of line.  */
+  begline,
+
+        /* Fail unless at end of line.  */
+  endline,
+
+        /* Succeeds if at beginning of buffer (if emacs) or at beginning
+           of string to be matched (if not).  */
+  begbuf,
+
+        /* Analogously, for end of buffer/string.  */
+  endbuf,
+ 
+        /* Followed by two byte relative address to which to jump.  */
+  jump, 
+
+	/* Same as jump, but marks the end of an alternative.  */
+  jump_past_alt,
+
+        /* Followed by two-byte relative address of place to resume at
+           in case of failure.  */
+  on_failure_jump,
+	
+        /* Like on_failure_jump, but pushes a placeholder instead of the
+           current string position when executed.  */
+  on_failure_keep_string_jump,
+  
+        /* Throw away latest failure point and then jump to following
+           two-byte relative address.  */
+  pop_failure_jump,
+
+        /* Change to pop_failure_jump if know won't have to backtrack to
+           match; otherwise change to jump.  This is used to jump
+           back to the beginning of a repeat.  If what follows this jump
+           clearly won't match what the repeat does, such that we can be
+           sure that there is no use backtracking out of repetitions
+           already matched, then we change it to a pop_failure_jump.
+           Followed by two-byte address.  */
+  maybe_pop_jump,
+
+        /* Jump to following two-byte address, and push a dummy failure
+           point. This failure point will be thrown away if an attempt
+           is made to use it for a failure.  A `+' construct makes this
+           before the first repeat.  Also used as an intermediary kind
+           of jump when compiling an alternative.  */
+  dummy_failure_jump,
+
+	/* Push a dummy failure point and continue.  Used at the end of
+	   alternatives.  */
+  push_dummy_failure,
+
+        /* Followed by two-byte relative address and two-byte number n.
+           After matching N times, jump to the address upon failure.  */
+  succeed_n,
+
+        /* Followed by two-byte relative address, and two-byte number n.
+           Jump to the address N times, then fail.  */
+  jump_n,
+
+        /* Set the following two-byte relative address to the
+           subsequent two-byte number.  The address *includes* the two
+           bytes of number.  */
+  set_number_at,
+
+  wordchar,	/* Matches any word-constituent character.  */
+  notwordchar,	/* Matches any char that is not a word-constituent.  */
+
+  wordbeg,	/* Succeeds if at word beginning.  */
+  wordend,	/* Succeeds if at word end.  */
+
+  wordbound,	/* Succeeds if at a word boundary.  */
+  notwordbound	/* Succeeds if not at a word boundary.  */
+
+#ifdef emacs
+  ,before_dot,	/* Succeeds if before point.  */
+  at_dot,	/* Succeeds if at point.  */
+  after_dot,	/* Succeeds if after point.  */
+
+	/* Matches any character whose syntax is specified.  Followed by
+           a byte which contains a syntax code, e.g., Sword.  */
+  syntaxspec,
+
+	/* Matches any character whose syntax is not that specified.  */
+  notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+
+
+/* Common operations on the compiled pattern.  */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
+
+#define STORE_NUMBER(destination, number)				\
+  do {									\
+    (destination)[0] = (number) & 0377;					\
+    (destination)[1] = (number) >> 8;					\
+  } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+   the byte after where the number is stored.  Therefore, DESTINATION
+   must be an lvalue.  */
+
+#define STORE_NUMBER_AND_INCR(destination, number)			\
+  do {									\
+    STORE_NUMBER (destination, number);					\
+    (destination) += 2;							\
+  } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+   at SOURCE.  */
+
+#define EXTRACT_NUMBER(destination, source)				\
+  do {									\
+    (destination) = *(source) & 0377;					\
+    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;		\
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+    int *dest;
+    unsigned char *source;
+{
+  int temp = SIGN_EXTEND_CHAR (*(source + 1)); 
+  *dest = *source & 0377;
+  *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros.  */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+   SOURCE must be an lvalue.  */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source)			\
+  do {									\
+    EXTRACT_NUMBER (destination, source);				\
+    (source) += 2; 							\
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+    int *destination;
+    unsigned char **source;
+{ 
+  extract_number (destination, *source);
+  *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+  extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+   it is doing (if the variable `debug' is nonzero).  If linked with the
+   main program in `iregex.c', you can enter patterns and strings
+   interactively.  And if linked with the main program in `main.c' and
+   the other test files, you can run the already-written tests.  */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging.  */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging.  */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) 				\
+  if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)			\
+  if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+/* Print the fastmap in human-readable form.  */
+
+void
+print_fastmap (fastmap)
+    char *fastmap;
+{
+  unsigned was_a_range = 0;
+  unsigned i = 0;  
+  
+  while (i < (1 << BYTEWIDTH))
+    {
+      if (fastmap[i++])
+	{
+	  was_a_range = 0;
+          putchar (i - 1);
+          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
+            {
+              was_a_range = 1;
+              i++;
+            }
+	  if (was_a_range)
+            {
+              printf ("-");
+              putchar (i - 1);
+            }
+        }
+    }
+  putchar ('\n'); 
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+   the START pointer into it and ending just before the pointer END.  */
+
+void
+print_partial_compiled_pattern (start, end)
+    unsigned char *start;
+    unsigned char *end;
+{
+  int mcnt, mcnt2;
+  unsigned char *p = start;
+  unsigned char *pend = end;
+
+  if (start == NULL)
+    {
+      printf ("(null)\n");
+      return;
+    }
+    
+  /* Loop over pattern commands.  */
+  while (p < pend)
+    {
+      printf ("%d:\t", p - start);
+
+      switch ((re_opcode_t) *p++)
+	{
+        case no_op:
+          printf ("/no_op");
+          break;
+
+	case exactn:
+	  mcnt = *p++;
+          printf ("/exactn/%d", mcnt);
+          do
+	    {
+              putchar ('/');
+	      putchar (*p++);
+            }
+          while (--mcnt);
+          break;
+
+	case start_memory:
+          mcnt = *p++;
+          printf ("/start_memory/%d/%d", mcnt, *p++);
+          break;
+
+	case stop_memory:
+          mcnt = *p++;
+	  printf ("/stop_memory/%d/%d", mcnt, *p++);
+          break;
+
+	case duplicate:
+	  printf ("/duplicate/%d", *p++);
+	  break;
+
+	case anychar:
+	  printf ("/anychar");
+	  break;
+
+	case charset:
+        case charset_not:
+          {
+            register int c, last = -100;
+	    register int in_range = 0;
+
+	    printf ("/charset [%s",
+	            (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+            
+            assert (p + *p < pend);
+
+            for (c = 0; c < 256; c++)
+	      if (c / 8 < *p
+		  && (p[1 + (c/8)] & (1 << (c % 8))))
+		{
+		  /* Are we starting a range?  */
+		  if (last + 1 == c && ! in_range)
+		    {
+		      putchar ('-');
+		      in_range = 1;
+		    }
+		  /* Have we broken a range?  */
+		  else if (last + 1 != c && in_range)
+              {
+		      putchar (last);
+		      in_range = 0;
+		    }
+                
+		  if (! in_range)
+		    putchar (c);
+
+		  last = c;
+              }
+
+	    if (in_range)
+	      putchar (last);
+
+	    putchar (']');
+
+	    p += 1 + *p;
+	  }
+	  break;
+
+	case begline:
+	  printf ("/begline");
+          break;
+
+	case endline:
+          printf ("/endline");
+          break;
+
+	case on_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+  	  printf ("/on_failure_jump to %d", p + mcnt - start);
+          break;
+
+	case on_failure_keep_string_jump:
+          extract_number_and_incr (&mcnt, &p);
+  	  printf ("/on_failure_keep_string_jump to %d", p + mcnt - start);
+          break;
+
+	case dummy_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+  	  printf ("/dummy_failure_jump to %d", p + mcnt - start);
+          break;
+
+	case push_dummy_failure:
+          printf ("/push_dummy_failure");
+          break;
+          
+        case maybe_pop_jump:
+          extract_number_and_incr (&mcnt, &p);
+  	  printf ("/maybe_pop_jump to %d", p + mcnt - start);
+	  break;
+
+        case pop_failure_jump:
+	  extract_number_and_incr (&mcnt, &p);
+  	  printf ("/pop_failure_jump to %d", p + mcnt - start);
+	  break;          
+          
+        case jump_past_alt:
+	  extract_number_and_incr (&mcnt, &p);
+  	  printf ("/jump_past_alt to %d", p + mcnt - start);
+	  break;          
+          
+        case jump:
+	  extract_number_and_incr (&mcnt, &p);
+  	  printf ("/jump to %d", p + mcnt - start);
+	  break;
+
+        case succeed_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+	  printf ("/succeed_n to %d, %d times", p + mcnt - start, mcnt2);
+          break;
+        
+        case jump_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+	  printf ("/jump_n to %d, %d times", p + mcnt - start, mcnt2);
+          break;
+        
+        case set_number_at: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+	  printf ("/set_number_at location %d to %d", p + mcnt - start, mcnt2);
+          break;
+        
+        case wordbound:
+	  printf ("/wordbound");
+	  break;
+
+	case notwordbound:
+	  printf ("/notwordbound");
+          break;
+
+	case wordbeg:
+	  printf ("/wordbeg");
+	  break;
+          
+	case wordend:
+	  printf ("/wordend");
+          
+#ifdef emacs
+	case before_dot:
+	  printf ("/before_dot");
+          break;
+
+	case at_dot:
+	  printf ("/at_dot");
+          break;
+
+	case after_dot:
+	  printf ("/after_dot");
+          break;
+
+	case syntaxspec:
+          printf ("/syntaxspec");
+	  mcnt = *p++;
+	  printf ("/%d", mcnt);
+          break;
+	  
+	case notsyntaxspec:
+          printf ("/notsyntaxspec");
+	  mcnt = *p++;
+	  printf ("/%d", mcnt);
+	  break;
+#endif /* emacs */
+
+	case wordchar:
+	  printf ("/wordchar");
+          break;
+	  
+	case notwordchar:
+	  printf ("/notwordchar");
+          break;
+
+	case begbuf:
+	  printf ("/begbuf");
+          break;
+
+	case endbuf:
+	  printf ("/endbuf");
+          break;
+
+        default:
+          printf ("?%d", *(p-1));
+	}
+
+      putchar ('\n');
+    }
+
+  printf ("%d:\tend of pattern.\n", p - start);
+}
+
+
+void
+print_compiled_pattern (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  unsigned char *buffer = bufp->buffer;
+
+  print_partial_compiled_pattern (buffer, buffer + bufp->used);
+  printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+  if (bufp->fastmap_accurate && bufp->fastmap)
+    {
+      printf ("fastmap: ");
+      print_fastmap (bufp->fastmap);
+    }
+
+  printf ("re_nsub: %d\t", bufp->re_nsub);
+  printf ("regs_alloc: %d\t", bufp->regs_allocated);
+  printf ("can_be_null: %d\t", bufp->can_be_null);
+  printf ("newline_anchor: %d\n", bufp->newline_anchor);
+  printf ("no_sub: %d\t", bufp->no_sub);
+  printf ("not_bol: %d\t", bufp->not_bol);
+  printf ("not_eol: %d\t", bufp->not_eol);
+  printf ("syntax: %d\n", bufp->syntax);
+  /* Perhaps we should print the translate table?  */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+    const char *where;
+    const char *string1;
+    const char *string2;
+    int size1;
+    int size2;
+{
+  unsigned this_char;
+  
+  if (where == NULL)
+    printf ("(null)");
+  else
+    {
+      if (FIRST_STRING_P (where))
+        {
+          for (this_char = where - string1; this_char < size1; this_char++)
+            putchar (string1[this_char]);
+
+          where = string2;    
+        }
+
+      for (this_char = where - string2; this_char < size2; this_char++)
+        putchar (string2[this_char]);
+    }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+  
+  re_syntax_options = syntax;
+  return ret;
+}
+
+
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+static const char *re_error_msgid[] =
+  { "Success",					/* REG_NOERROR */
+    "No match",					/* REG_NOMATCH */
+    "Invalid regular expression",		/* REG_BADPAT */
+    "Invalid collation character",		/* REG_ECOLLATE */
+    "Invalid character class name",		/* REG_ECTYPE */
+    "Trailing backslash",			/* REG_EESCAPE */
+    "Invalid back reference",			/* REG_ESUBREG */
+    "Unmatched [ or [^",			/* REG_EBRACK */
+    "Unmatched ( or \\(",			/* REG_EPAREN */
+    "Unmatched \\{",				/* REG_EBRACE */
+    "Invalid content of \\{\\}",		/* REG_BADBR */
+    "Invalid range end",			/* REG_ERANGE */
+    "Memory exhausted",				/* REG_ESPACE */
+    "Invalid preceding regular expression",	/* REG_BADRPT */
+    "Premature end of regular expression",	/* REG_EEND */
+    "Regular expression too big",		/* REG_ESIZE */
+    "Unmatched ) or \\)",			/* REG_ERPAREN */
+  };
+
+
+/* Avoiding alloca during matching, to placate r_alloc.  */
+
+/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the
+   searching and matching functions should not call alloca.  On some
+   systems, alloca is implemented in terms of malloc, and if we're
+   using the relocating allocator routines, then malloc could cause a
+   relocation, which might (if the strings being searched are in the
+   ralloc heap) shift the data out from underneath the regexp
+   routines.
+
+   Here's another reason to avoid allocation: Emacs 
+   processes input from X in a signal handler; processing X input may
+   call malloc; if input arrives while a matching routine is calling
+   malloc, then we're scrod.  But Emacs can't just block input while
+   calling matching routines; then we don't notice interrupts when
+   they come in.  So, Emacs blocks input around all regexp calls
+   except the matching calls, which it leaves unprotected, in the
+   faith that they will not malloc.  */
+
+/* Normally, this is fine.  */
+#define MATCH_MAY_ALLOCATE
+
+/* When using GNU C, we are not REALLY using the C alloca, no matter
+   what sysfuncs.h may say.  So don't take precautions for it.  */
+#ifdef __GNUC__
+#undef C_ALLOCA
+#endif
+
+/* The match routines may not allocate if (1) they would do it with malloc
+   and (2) it's not safe for them to use malloc.
+   Note that if REL_ALLOC is defined, matching would not use malloc for the
+   failure stack, but we would still use it for the register vectors;
+   so REL_ALLOC should not affect this.  */
+#if (defined (C_ALLOCA) || defined (REGEX_MALLOC)) && defined (emacs)
+#undef MATCH_MAY_ALLOCATE
+#endif
+
+
+
+/* Failure stack declarations and macros; both re_compile_fastmap and
+   re_match_2 use a failure stack.  These have to be macros because of
+   REGEX_ALLOCATE_STACK.  */
+   
+
+/* Number of failure points for which to initially allocate space
+   when matching.  If this number is exceeded, we allocate more
+   space, so it is not a hard limit.  */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack.  Would be
+   exactly that if always used MAX_FAILURE_SPACE each time we failed.
+   This is a variable only so users of regex can assign to it; we never
+   change it ourselves.  */
+#if defined (MATCH_MAY_ALLOCATE)
+int re_max_failures = 200000;
+#else
+int re_max_failures = 2000;
+#endif
+
+union fail_stack_elt
+{
+  unsigned char *pointer;
+  int integer;
+};
+
+typedef union fail_stack_elt fail_stack_elt_t;
+
+typedef struct
+{
+  fail_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;			/* Offset of next open position.  */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
+
+
+/* Define macros to initialize and free the failure stack.
+   Do `return -2' if the alloc fails.  */
+
+#ifdef MATCH_MAY_ALLOCATE
+#define INIT_FAIL_STACK()						\
+  do {									\
+    fail_stack.stack = (fail_stack_elt_t *)				\
+      REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t));	\
+									\
+    if (fail_stack.stack == NULL)					\
+      return -2;							\
+									\
+    fail_stack.size = INIT_FAILURE_ALLOC;				\
+    fail_stack.avail = 0;						\
+  } while (0)
+
+#define RESET_FAIL_STACK()  REGEX_FREE_STACK (fail_stack.stack)
+#else
+#define INIT_FAIL_STACK()						\
+  do {									\
+    fail_stack.avail = 0;						\
+  } while (0)
+
+#define RESET_FAIL_STACK()
+#endif
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+   Return 1 if succeeds, and 0 if either ran out of memory
+   allocating space for it or it was already too large.  
+   
+   REGEX_REALLOCATE_STACK requires `destination' be declared.   */
+
+#define DOUBLE_FAIL_STACK(fail_stack)					\
+  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS		\
+   ? 0									\
+   : ((fail_stack).stack = (fail_stack_elt_t *)				\
+        REGEX_REALLOCATE_STACK ((fail_stack).stack, 			\
+          (fail_stack).size * sizeof (fail_stack_elt_t),		\
+          ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),	\
+									\
+      (fail_stack).stack == NULL					\
+      ? 0								\
+      : ((fail_stack).size <<= 1, 					\
+         1)))
+
+
+/* Push pointer POINTER on FAIL_STACK. 
+   Return 1 if was able to do so and 0 if ran out of memory allocating
+   space to do so.  */
+#define PUSH_PATTERN_OP(POINTER, FAIL_STACK)				\
+  ((FAIL_STACK_FULL ()							\
+    && !DOUBLE_FAIL_STACK (FAIL_STACK))					\
+   ? 0									\
+   : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER,	\
+      1))
+
+/* Push a pointer value onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_POINTER(item)					\
+  fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item)
+
+/* This pushes an integer-valued item onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_INT(item)					\
+  fail_stack.stack[fail_stack.avail++].integer = (item)
+
+/* Push a fail_stack_elt_t value onto the failure stack.
+   Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_ELT(item)					\
+  fail_stack.stack[fail_stack.avail++] =  (item)
+
+/* These three POP... operations complement the three PUSH... operations.
+   All assume that `fail_stack' is nonempty.  */
+#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer
+#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer
+#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging.  */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_INT
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+   if we ever fail back to it.  
+   
+   Requires variables fail_stack, regstart, regend, reg_info, and
+   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
+   declared.
+   
+   Does `return FAILURE_CODE' if runs out of memory.  */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)	\
+  do {									\
+    char *destination;							\
+    /* Must be int, so when we don't save any registers, the arithmetic	\
+       of 0 + -1 isn't done as unsigned.  */				\
+    int this_reg;							\
+    									\
+    DEBUG_STATEMENT (failure_id++);					\
+    DEBUG_STATEMENT (nfailure_points_pushed++);				\
+    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);		\
+    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
+    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
+									\
+    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);		\
+    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);	\
+									\
+    /* Ensure we have enough space allocated for what we will push.  */	\
+    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)			\
+      {									\
+        if (!DOUBLE_FAIL_STACK (fail_stack))				\
+          return failure_code;						\
+									\
+        DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",		\
+		       (fail_stack).size);				\
+        DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+      }									\
+									\
+    /* Push the info, starting with the registers.  */			\
+    DEBUG_PRINT1 ("\n");						\
+									\
+    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg;	\
+         this_reg++)							\
+      {									\
+	DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);			\
+        DEBUG_STATEMENT (num_regs_pushed++);				\
+									\
+	DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);		\
+        PUSH_FAILURE_POINTER (regstart[this_reg]);			\
+                                                                        \
+	DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);		\
+        PUSH_FAILURE_POINTER (regend[this_reg]);			\
+									\
+	DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);	\
+        DEBUG_PRINT2 (" match_null=%d",					\
+                      REG_MATCH_NULL_STRING_P (reg_info[this_reg]));	\
+        DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));	\
+        DEBUG_PRINT2 (" matched_something=%d",				\
+                      MATCHED_SOMETHING (reg_info[this_reg]));		\
+        DEBUG_PRINT2 (" ever_matched=%d",				\
+                      EVER_MATCHED_SOMETHING (reg_info[this_reg]));	\
+	DEBUG_PRINT1 ("\n");						\
+        PUSH_FAILURE_ELT (reg_info[this_reg].word);			\
+      }									\
+									\
+    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
+    PUSH_FAILURE_INT (lowest_active_reg);				\
+									\
+    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
+    PUSH_FAILURE_INT (highest_active_reg);				\
+									\
+    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);		\
+    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);		\
+    PUSH_FAILURE_POINTER (pattern_place);				\
+									\
+    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);		\
+    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
+				 size2);				\
+    DEBUG_PRINT1 ("'\n");						\
+    PUSH_FAILURE_POINTER (string_place);				\
+									\
+    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);		\
+    DEBUG_PUSH (failure_id);						\
+  } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+   for each register.  */
+#define NUM_REG_ITEMS  3
+
+/* Individual items aside from the registers.  */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack.  */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items.  */
+#define NUM_FAILURE_ITEMS						\
+  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS 	\
+    + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it.  */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+   We restore into the parameters, all of which should be lvalues:
+     STR -- the saved data position.
+     PAT -- the saved pattern position.
+     LOW_REG, HIGH_REG -- the highest and lowest active registers.
+     REGSTART, REGEND -- arrays of string positions.
+     REG_INFO -- array of information about each subexpression.
+   
+   Also assumes the variables `fail_stack' and (if debugging), `bufp',
+   `pend', `string1', `size1', `string2', and `size2'.  */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{									\
+  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)			\
+  int this_reg;								\
+  const unsigned char *string_temp;					\
+									\
+  assert (!FAIL_STACK_EMPTY ());					\
+									\
+  /* Remove failure points and point to how many regs pushed.  */	\
+  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");				\
+  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);	\
+  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);	\
+									\
+  assert (fail_stack.avail >= NUM_NONREG_ITEMS);			\
+									\
+  DEBUG_POP (&failure_id);						\
+  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);		\
+									\
+  /* If the saved string location is NULL, it came from an		\
+     on_failure_keep_string_jump opcode, and we want to throw away the	\
+     saved NULL, thus retaining our current position in the string.  */	\
+  string_temp = POP_FAILURE_POINTER ();					\
+  if (string_temp != NULL)						\
+    str = (const char *) string_temp;					\
+									\
+  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);			\
+  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);	\
+  DEBUG_PRINT1 ("'\n");							\
+									\
+  pat = (unsigned char *) POP_FAILURE_POINTER ();			\
+  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);			\
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);			\
+									\
+  /* Restore register info.  */						\
+  high_reg = (unsigned) POP_FAILURE_INT ();				\
+  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);		\
+									\
+  low_reg = (unsigned) POP_FAILURE_INT ();				\
+  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);		\
+									\
+  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)		\
+    {									\
+      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);			\
+									\
+      reg_info[this_reg].word = POP_FAILURE_ELT ();			\
+      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);		\
+									\
+      regend[this_reg] = (const char *) POP_FAILURE_POINTER ();		\
+      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);		\
+									\
+      regstart[this_reg] = (const char *) POP_FAILURE_POINTER ();	\
+      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);		\
+    }									\
+									\
+  set_regs_matched_done = 0;						\
+  DEBUG_STATEMENT (nfailure_points_popped++);				\
+} /* POP_FAILURE_POINT */
+
+
+
+
+/* Structure for per-register (a.k.a. per-group) information.
+   Other register information, such as the
+   starting and ending positions (which are addresses), and the list of
+   inner groups (which is a bits list) are maintained in separate
+   variables.  
+   
+   We are making a (strictly speaking) nonportable assumption here: that
+   the compiler will pack our bit fields into something that fits into
+   the type of `word', i.e., is something that fits into one item on the
+   failure stack.  */
+
+typedef union
+{
+  fail_stack_elt_t word;
+  struct
+  {
+      /* This field is one if this group can match the empty string,
+         zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
+#define MATCH_NULL_UNSET_VALUE 3
+    unsigned match_null_string_p : 2;
+    unsigned is_active : 1;
+    unsigned matched_something : 1;
+    unsigned ever_matched_something : 1;
+  } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R)  ((R).bits.is_active)
+#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+   for the subexpressions which we are currently inside.  Also records
+   that those subexprs have matched.  */
+#define SET_REGS_MATCHED()						\
+  do									\
+    {									\
+      if (!set_regs_matched_done)					\
+	{								\
+	  unsigned r;							\
+	  set_regs_matched_done = 1;					\
+	  for (r = lowest_active_reg; r <= highest_active_reg; r++)	\
+	    {								\
+	      MATCHED_SOMETHING (reg_info[r])				\
+		= EVER_MATCHED_SOMETHING (reg_info[r])			\
+		= 1;							\
+	    }								\
+	}								\
+    }									\
+  while (0)
+
+/* Registers are set to a sentinel when they haven't yet matched.  */
+static char reg_unset_dummy;
+#define REG_UNSET_VALUE (&reg_unset_dummy)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Subroutine declarations and macros for regex_compile.  */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it 
+   if necessary.  Also cast from a signed character in the constant
+   string passed to us by the user to an unsigned char that we can use
+   as an array index (in, e.g., `translate').  */
+#define PATFETCH(c)							\
+  do {if (p == pend) return REG_EEND;					\
+    c = (unsigned char) *p++;						\
+    if (translate) c = translate[c]; 					\
+  } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+   translation.  */
+#define PATFETCH_RAW(c)							\
+  do {if (p == pend) return REG_EEND;					\
+    c = (unsigned char) *p++; 						\
+  } while (0)
+
+/* Go backwards one character in the pattern.  */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D.  We
+   cast the subscript to translate because some data is declared as
+   `char *', to avoid warnings when a string constant is passed.  But
+   when we use a character as a subscript we must make it unsigned.  */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'.  */
+
+/* If the buffer isn't allocated when it comes in, use this.  */
+#define INIT_BUF_SIZE  32
+
+/* Make sure we have at least N more bytes of space in buffer.  */
+#define GET_BUFFER_SPACE(n)						\
+    while (b - bufp->buffer + (n) > bufp->allocated)			\
+      EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it.  */
+#define BUF_PUSH(c)							\
+  do {									\
+    GET_BUFFER_SPACE (1);						\
+    *b++ = (unsigned char) (c);						\
+  } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
+#define BUF_PUSH_2(c1, c2)						\
+  do {									\
+    GET_BUFFER_SPACE (2);						\
+    *b++ = (unsigned char) (c1);					\
+    *b++ = (unsigned char) (c2);					\
+  } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes.  */
+#define BUF_PUSH_3(c1, c2, c3)						\
+  do {									\
+    GET_BUFFER_SPACE (3);						\
+    *b++ = (unsigned char) (c1);					\
+    *b++ = (unsigned char) (c2);					\
+    *b++ = (unsigned char) (c3);					\
+  } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO.  We store a
+   relative address offset by the three bytes the jump itself occupies.  */
+#define STORE_JUMP(op, loc, to) \
+  store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump.  */
+#define STORE_JUMP2(op, loc, to, arg) \
+  store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP(op, loc, to) \
+  insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP2(op, loc, to, arg) \
+  insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+   into the pattern are two bytes long.  So if 2^16 bytes turns out to
+   be too small, many things would have to change.  */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+   reset the pointers that pointed into the old block to point to the
+   correct places in the new one.  If extending the buffer results in it
+   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
+#define EXTEND_BUFFER()							\
+  do { 									\
+    unsigned char *old_buffer = bufp->buffer;				\
+    if (bufp->allocated == MAX_BUF_SIZE) 				\
+      return REG_ESIZE;							\
+    bufp->allocated <<= 1;						\
+    if (bufp->allocated > MAX_BUF_SIZE)					\
+      bufp->allocated = MAX_BUF_SIZE; 					\
+    bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+    if (bufp->buffer == NULL)						\
+      return REG_ESPACE;						\
+    /* If the buffer moved, move all the pointers into it.  */		\
+    if (old_buffer != bufp->buffer)					\
+      {									\
+        b = (b - old_buffer) + bufp->buffer;				\
+        begalt = (begalt - old_buffer) + bufp->buffer;			\
+        if (fixup_alt_jump)						\
+          fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+        if (laststart)							\
+          laststart = (laststart - old_buffer) + bufp->buffer;		\
+        if (pending_exact)						\
+          pending_exact = (pending_exact - old_buffer) + bufp->buffer;	\
+      }									\
+  } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+   {start,stop}_memory, the maximum number of groups we can report
+   things about is what fits in that byte.  */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers.  We just
+   ignore the excess.  */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack.  */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+  pattern_offset_t begalt_offset;
+  pattern_offset_t fixup_alt_jump;
+  pattern_offset_t inner_group_offset;
+  pattern_offset_t laststart_offset;  
+  regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+  compile_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;			/* Offset of next open position.  */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
+
+/* The next available element.  */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list.  */
+#define SET_LIST_BIT(c)                               \
+  (b[((unsigned char) (c)) / BYTEWIDTH]               \
+   |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern.  */
+#define GET_UNSIGNED_NUMBER(num) 					\
+  { if (p != pend)							\
+     {									\
+       PATFETCH (c); 							\
+       while (ISDIGIT (c)) 						\
+         { 								\
+           if (num < 0)							\
+              num = 0;							\
+           num = num * 10 + c - '0'; 					\
+           if (p == pend) 						\
+              break; 							\
+           PATFETCH (c);						\
+         } 								\
+       } 								\
+    }		
+
+#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+#define IS_CHAR_CLASS(string)						\
+   (STREQ (string, "alpha") || STREQ (string, "upper")			\
+    || STREQ (string, "lower") || STREQ (string, "digit")		\
+    || STREQ (string, "alnum") || STREQ (string, "xdigit")		\
+    || STREQ (string, "space") || STREQ (string, "print")		\
+    || STREQ (string, "punct") || STREQ (string, "graph")		\
+    || STREQ (string, "cntrl") || STREQ (string, "blank"))
+
+
+#ifndef MATCH_MAY_ALLOCATE
+
+/* If we cannot allocate large objects within re_match_2_internal,
+   we make the fail stack and register vectors global.
+   The fail stack, we grow to the maximum size when a regexp
+   is compiled.
+   The register vectors, we adjust in size each time we
+   compile a regexp, according to the number of registers it needs.  */
+
+static fail_stack_type fail_stack;
+
+/* Size with which the following vectors are currently allocated.
+   That is so we can make them bigger as needed,
+   but never make them smaller.  */
+static int regs_allocated_size;
+
+static const char **     regstart, **     regend;
+static const char ** old_regstart, ** old_regend;
+static const char **best_regstart, **best_regend;
+static register_info_type *reg_info; 
+static const char **reg_dummy;
+static register_info_type *reg_info_dummy;
+
+/* Make the register vectors big enough for NUM_REGS registers,
+   but don't make them smaller.  */
+
+static
+regex_grow_registers (num_regs)
+     int num_regs;
+{
+  if (num_regs > regs_allocated_size)
+    {
+      RETALLOC_IF (regstart,	 num_regs, const char *);
+      RETALLOC_IF (regend,	 num_regs, const char *);
+      RETALLOC_IF (old_regstart, num_regs, const char *);
+      RETALLOC_IF (old_regend,	 num_regs, const char *);
+      RETALLOC_IF (best_regstart, num_regs, const char *);
+      RETALLOC_IF (best_regend,	 num_regs, const char *);
+      RETALLOC_IF (reg_info,	 num_regs, register_info_type);
+      RETALLOC_IF (reg_dummy,	 num_regs, const char *);
+      RETALLOC_IF (reg_info_dummy, num_regs, register_info_type);
+
+      regs_allocated_size = num_regs;
+    }
+}
+
+#endif /* not MATCH_MAY_ALLOCATE */
+
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+   Returns one of error codes defined in `regex.h', or zero for success.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate'
+   fields are set in BUFP on entry.
+
+   If it succeeds, results are put in BUFP (if it returns an error, the
+   contents of BUFP are undefined):
+     `buffer' is the compiled pattern;
+     `syntax' is set to SYNTAX;
+     `used' is set to the length of the compiled pattern;
+     `fastmap_accurate' is zero;
+     `re_nsub' is the number of subexpressions in PATTERN;
+     `not_bol' and `not_eol' are zero;
+   
+   The `fastmap' and `newline_anchor' fields are neither
+   examined nor set.  */
+
+/* Return, freeing storage we allocated.  */
+#define FREE_STACK_RETURN(value)		\
+  return (free (compile_stack.stack), value)
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+     const char *pattern;
+     int size;
+     reg_syntax_t syntax;
+     struct re_pattern_buffer *bufp;
+{
+  /* We fetch characters from PATTERN here.  Even though PATTERN is
+     `char *' (i.e., signed), we declare these variables as unsigned, so
+     they can be reliably used as array indices.  */
+  register unsigned char c, c1;
+  
+  /* A random temporary spot in PATTERN.  */
+  const char *p1;
+
+  /* Points to the end of the buffer, where we should append.  */
+  register unsigned char *b;
+  
+  /* Keeps track of unclosed groups.  */
+  compile_stack_type compile_stack;
+
+  /* Points to the current (ending) position in the pattern.  */
+  const char *p = pattern;
+  const char *pend = pattern + size;
+  
+  /* How to translate the characters in the pattern.  */
+  char *translate = bufp->translate;
+
+  /* Address of the count-byte of the most recently inserted `exactn'
+     command.  This makes it possible to tell if a new exact-match
+     character can be added to that command or if the character requires
+     a new `exactn' command.  */
+  unsigned char *pending_exact = 0;
+
+  /* Address of start of the most recently finished expression.
+     This tells, e.g., postfix * where to find the start of its
+     operand.  Reset at the beginning of groups and alternatives.  */
+  unsigned char *laststart = 0;
+
+  /* Address of beginning of regexp, or inside of last group.  */
+  unsigned char *begalt;
+
+  /* Place in the uncompiled pattern (i.e., the {) to
+     which to go back if the interval is invalid.  */
+  const char *beg_interval;
+                
+  /* Address of the place where a forward jump should go to the end of
+     the containing expression.  Each alternative of an `or' -- except the
+     last -- ends with a forward jump of this sort.  */
+  unsigned char *fixup_alt_jump = 0;
+
+  /* Counts open-groups as they are encountered.  Remembered for the
+     matching close-group on the compile stack, so the same register
+     number is put in the stop_memory as the start_memory.  */
+  regnum_t regnum = 0;
+
+#ifdef DEBUG
+  DEBUG_PRINT1 ("\nCompiling pattern: ");
+  if (debug)
+    {
+      unsigned debug_count;
+      
+      for (debug_count = 0; debug_count < size; debug_count++)
+        putchar (pattern[debug_count]);
+      putchar ('\n');
+    }
+#endif /* DEBUG */
+
+  /* Initialize the compile stack.  */
+  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+  if (compile_stack.stack == NULL)
+    return REG_ESPACE;
+
+  compile_stack.size = INIT_COMPILE_STACK_SIZE;
+  compile_stack.avail = 0;
+
+  /* Initialize the pattern buffer.  */
+  bufp->syntax = syntax;
+  bufp->fastmap_accurate = 0;
+  bufp->not_bol = bufp->not_eol = 0;
+
+  /* Set `used' to zero, so that if we return an error, the pattern
+     printer (for debugging) will think there's no pattern.  We reset it
+     at the end.  */
+  bufp->used = 0;
+  
+  /* Always count groups, whether or not bufp->no_sub is set.  */
+  bufp->re_nsub = 0;				
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+  /* Initialize the syntax table.  */
+   init_syntax_once ();
+#endif
+
+  if (bufp->allocated == 0)
+    {
+      if (bufp->buffer)
+	{ /* If zero allocated, but buffer is non-null, try to realloc
+             enough space.  This loses if buffer's address is bogus, but
+             that is the user's responsibility.  */
+          RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+        }
+      else
+        { /* Caller did not allocate a buffer.  Do it for them.  */
+          bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+        }
+      if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE);
+
+      bufp->allocated = INIT_BUF_SIZE;
+    }
+
+  begalt = b = bufp->buffer;
+
+  /* Loop through the uncompiled pattern until we're at the end.  */
+  while (p != pend)
+    {
+      PATFETCH (c);
+
+      switch (c)
+        {
+        case '^':
+          {
+            if (   /* If at start of pattern, it's an operator.  */
+                   p == pattern + 1
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's come before.  */
+                || at_begline_loc_p (pattern, p, syntax))
+              BUF_PUSH (begline);
+            else
+              goto normal_char;
+          }
+          break;
+
+
+        case '$':
+          {
+            if (   /* If at end of pattern, it's an operator.  */
+                   p == pend 
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's next.  */
+                || at_endline_loc_p (p, pend, syntax))
+               BUF_PUSH (endline);
+             else
+               goto normal_char;
+           }
+           break;
+
+
+	case '+':
+        case '?':
+          if ((syntax & RE_BK_PLUS_QM)
+              || (syntax & RE_LIMITED_OPS))
+            goto normal_char;
+        handle_plus:
+        case '*':
+          /* If there is no previous pattern... */
+          if (!laststart)
+            {
+              if (syntax & RE_CONTEXT_INVALID_OPS)
+                FREE_STACK_RETURN (REG_BADRPT);
+              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                goto normal_char;
+            }
+
+          {
+            /* Are we optimizing this jump?  */
+            boolean keep_string_p = false;
+            
+            /* 1 means zero (many) matches is allowed.  */
+            char zero_times_ok = 0, many_times_ok = 0;
+
+            /* If there is a sequence of repetition chars, collapse it
+               down to just one (the right one).  We can't combine
+               interval operators with these because of, e.g., `a{2}*',
+               which should only match an even number of `a's.  */
+
+            for (;;)
+              {
+                zero_times_ok |= c != '+';
+                many_times_ok |= c != '?';
+
+                if (p == pend)
+                  break;
+
+                PATFETCH (c);
+
+                if (c == '*'
+                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+                  ;
+
+                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
+                  {
+                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+                    PATFETCH (c1);
+                    if (!(c1 == '+' || c1 == '?'))
+                      {
+                        PATUNFETCH;
+                        PATUNFETCH;
+                        break;
+                      }
+
+                    c = c1;
+                  }
+                else
+                  {
+                    PATUNFETCH;
+                    break;
+                  }
+
+                /* If we get here, we found another repeat character.  */
+               }
+
+            /* Star, etc. applied to an empty pattern is equivalent
+               to an empty pattern.  */
+            if (!laststart)  
+              break;
+
+            /* Now we know whether or not zero matches is allowed
+               and also whether or not two or more matches is allowed.  */
+            if (many_times_ok)
+              { /* More than one repetition is allowed, so put in at the
+                   end a backward relative jump from `b' to before the next
+                   jump we're going to put in below (which jumps from
+                   laststart to after this jump).  
+
+                   But if we are at the `*' in the exact sequence `.*\n',
+                   insert an unconditional jump backwards to the .,
+                   instead of the beginning of the loop.  This way we only
+                   push a failure point once, instead of every time
+                   through the loop.  */
+                assert (p - 1 > pattern);
+
+                /* Allocate the space for the jump.  */
+                GET_BUFFER_SPACE (3);
+
+                /* We know we are not at the first character of the pattern,
+                   because laststart was nonzero.  And we've already
+                   incremented `p', by the way, to be the character after
+                   the `*'.  Do we have to do something analogous here
+                   for null bytes, because of RE_DOT_NOT_NULL?  */
+                if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+		    && zero_times_ok
+                    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+                    && !(syntax & RE_DOT_NEWLINE))
+                  { /* We have .*\n.  */
+                    STORE_JUMP (jump, b, laststart);
+                    keep_string_p = true;
+                  }
+                else
+                  /* Anything else.  */
+                  STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+                /* We've added more stuff to the buffer.  */
+                b += 3;
+              }
+
+            /* On failure, jump from laststart to b + 3, which will be the
+               end of the buffer after this jump is inserted.  */
+            GET_BUFFER_SPACE (3);
+            INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+                                       : on_failure_jump,
+                         laststart, b + 3);
+            pending_exact = 0;
+            b += 3;
+
+            if (!zero_times_ok)
+              {
+                /* At least one repetition is required, so insert a
+                   `dummy_failure_jump' before the initial
+                   `on_failure_jump' instruction of the loop. This
+                   effects a skip over that instruction the first time
+                   we hit that loop.  */
+                GET_BUFFER_SPACE (3);
+                INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+                b += 3;
+              }
+            }
+	  break;
+
+
+	case '.':
+          laststart = b;
+          BUF_PUSH (anychar);
+          break;
+
+
+        case '[':
+          {
+            boolean had_char_class = false;
+
+            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+            /* Ensure that we have enough space to push a charset: the
+               opcode, the length count, and the bitset; 34 bytes in all.  */
+	    GET_BUFFER_SPACE (34);
+
+            laststart = b;
+
+            /* We test `*p == '^' twice, instead of using an if
+               statement, so we only need one BUF_PUSH.  */
+            BUF_PUSH (*p == '^' ? charset_not : charset); 
+            if (*p == '^')
+              p++;
+
+            /* Remember the first position in the bracket expression.  */
+            p1 = p;
+
+            /* Push the number of bytes in the bitmap.  */
+            BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* Clear the whole map.  */
+            bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* charset_not matches newline according to a syntax bit.  */
+            if ((re_opcode_t) b[-2] == charset_not
+                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+              SET_LIST_BIT ('\n');
+
+            /* Read in characters and ranges, setting map bits.  */
+            for (;;)
+              {
+                if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                PATFETCH (c);
+
+                /* \ might escape characters inside [...] and [^...].  */
+                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+                  {
+                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+                    PATFETCH (c1);
+                    SET_LIST_BIT (c1);
+                    continue;
+                  }
+
+                /* Could be the end of the bracket expression.  If it's
+                   not (i.e., when the bracket expression is `[]' so
+                   far), the ']' character bit gets set way below.  */
+                if (c == ']' && p != p1 + 1)
+                  break;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character class.  */
+                if (had_char_class && c == '-' && *p != ']')
+                  FREE_STACK_RETURN (REG_ERANGE);
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character: if this is a hyphen not at the
+                   beginning or the end of a list, then it's the range
+                   operator.  */
+                if (c == '-' 
+                    && !(p - 2 >= pattern && p[-2] == '[') 
+                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+                    && *p != ']')
+                  {
+                    reg_errcode_t ret
+                      = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+                  }
+
+                else if (p[0] == '-' && p[1] != ']')
+                  { /* This handles ranges made up of characters only.  */
+                    reg_errcode_t ret;
+
+		    /* Move past the `-'.  */
+                    PATFETCH (c1);
+                    
+                    ret = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
+                  }
+
+                /* See if we're at the beginning of a possible character
+                   class.  */
+
+                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+                  { /* Leave room for the null.  */
+                    char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+                    PATFETCH (c);
+                    c1 = 0;
+
+                    /* If pattern is `[[:'.  */
+                    if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                    for (;;)
+                      {
+                        PATFETCH (c);
+                        if (c == ':' || c == ']' || p == pend
+                            || c1 == CHAR_CLASS_MAX_LENGTH)
+                          break;
+                        str[c1++] = c;
+                      }
+                    str[c1] = '\0';
+
+                    /* If isn't a word bracketed by `[:' and:`]':
+                       undo the ending character, the letters, and leave 
+                       the leading `:' and `[' (but set bits for them).  */
+                    if (c == ':' && *p == ']')
+                      {
+                        int ch;
+                        boolean is_alnum = STREQ (str, "alnum");
+                        boolean is_alpha = STREQ (str, "alpha");
+                        boolean is_blank = STREQ (str, "blank");
+                        boolean is_cntrl = STREQ (str, "cntrl");
+                        boolean is_digit = STREQ (str, "digit");
+                        boolean is_graph = STREQ (str, "graph");
+                        boolean is_lower = STREQ (str, "lower");
+                        boolean is_print = STREQ (str, "print");
+                        boolean is_punct = STREQ (str, "punct");
+                        boolean is_space = STREQ (str, "space");
+                        boolean is_upper = STREQ (str, "upper");
+                        boolean is_xdigit = STREQ (str, "xdigit");
+                        
+                        if (!IS_CHAR_CLASS (str))
+			  FREE_STACK_RETURN (REG_ECTYPE);
+
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);					
+
+                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);
+
+                        for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+                          {
+			    /* This was split into 3 if's to
+			       avoid an arbitrary limit in some compiler.  */
+                            if (   (is_alnum  && ISALNUM (ch))
+                                || (is_alpha  && ISALPHA (ch))
+                                || (is_blank  && ISBLANK (ch))
+                                || (is_cntrl  && ISCNTRL (ch)))
+			      SET_LIST_BIT (ch);
+			    if (   (is_digit  && ISDIGIT (ch))
+                                || (is_graph  && ISGRAPH (ch))
+                                || (is_lower  && ISLOWER (ch))
+                                || (is_print  && ISPRINT (ch)))
+			      SET_LIST_BIT (ch);
+			    if (   (is_punct  && ISPUNCT (ch))
+                                || (is_space  && ISSPACE (ch))
+                                || (is_upper  && ISUPPER (ch))
+                                || (is_xdigit && ISXDIGIT (ch)))
+			      SET_LIST_BIT (ch);
+                          }
+                        had_char_class = true;
+                      }
+                    else
+                      {
+                        c1++;
+                        while (c1--)    
+                          PATUNFETCH;
+                        SET_LIST_BIT ('[');
+                        SET_LIST_BIT (':');
+                        had_char_class = false;
+                      }
+                  }
+                else
+                  {
+                    had_char_class = false;
+                    SET_LIST_BIT (c);
+                  }
+              }
+
+            /* Discard any (non)matching list bytes that are all 0 at the
+               end of the map.  Decrease the map-length byte too.  */
+            while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) 
+              b[-1]--; 
+            b += b[-1];
+          }
+          break;
+
+
+	case '(':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_open;
+          else
+            goto normal_char;
+
+
+        case ')':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_close;
+          else
+            goto normal_char;
+
+
+        case '\n':
+          if (syntax & RE_NEWLINE_ALT)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+	case '|':
+          if (syntax & RE_NO_BK_VBAR)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+        case '{':
+           if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+             goto handle_interval;
+           else
+             goto normal_char;
+
+
+        case '\\':
+          if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);
+
+          /* Do not translate the character after the \, so that we can
+             distinguish, e.g., \B from \b, even if we normally would
+             translate, e.g., B to b.  */
+          PATFETCH_RAW (c);
+
+          switch (c)
+            {
+            case '(':
+              if (syntax & RE_NO_BK_PARENS)
+                goto normal_backslash;
+
+            handle_open:
+              bufp->re_nsub++;
+              regnum++;
+
+              if (COMPILE_STACK_FULL)
+                { 
+                  RETALLOC (compile_stack.stack, compile_stack.size << 1,
+                            compile_stack_elt_t);
+                  if (compile_stack.stack == NULL) return REG_ESPACE;
+
+                  compile_stack.size <<= 1;
+                }
+
+              /* These are the values to restore when we hit end of this
+                 group.  They are all relative offsets, so that if the
+                 whole pattern moves because of realloc, they will still
+                 be valid.  */
+              COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+              COMPILE_STACK_TOP.fixup_alt_jump 
+                = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+              COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+              COMPILE_STACK_TOP.regnum = regnum;
+
+              /* We will eventually replace the 0 with the number of
+                 groups inner to this one.  But do not push a
+                 start_memory for groups beyond the last one we can
+                 represent in the compiled pattern.  */
+              if (regnum <= MAX_REGNUM)
+                {
+                  COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+                  BUF_PUSH_3 (start_memory, regnum, 0);
+                }
+                
+              compile_stack.avail++;
+
+              fixup_alt_jump = 0;
+              laststart = 0;
+              begalt = b;
+	      /* If we've reached MAX_REGNUM groups, then this open
+		 won't actually generate any code, so we'll have to
+		 clear pending_exact explicitly.  */
+	      pending_exact = 0;
+              break;
+
+
+            case ')':
+              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+              if (COMPILE_STACK_EMPTY)
+                if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                  goto normal_backslash;
+                else
+                  FREE_STACK_RETURN (REG_ERPAREN);
+
+            handle_close:
+              if (fixup_alt_jump)
+                { /* Push a dummy failure point at the end of the
+                     alternative for a possible future
+                     `pop_failure_jump' to pop.  See comments at
+                     `push_dummy_failure' in `re_match_2'.  */
+                  BUF_PUSH (push_dummy_failure);
+                  
+                  /* We allocated space for this jump when we assigned
+                     to `fixup_alt_jump', in the `handle_alt' case below.  */
+                  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+                }
+
+              /* See similar code for backslashed left paren above.  */
+              if (COMPILE_STACK_EMPTY)
+                if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                  goto normal_char;
+                else
+                  FREE_STACK_RETURN (REG_ERPAREN);
+
+              /* Since we just checked for an empty stack above, this
+                 ``can't happen''.  */
+              assert (compile_stack.avail != 0);
+              {
+                /* We don't just want to restore into `regnum', because
+                   later groups should continue to be numbered higher,
+                   as in `(ab)c(de)' -- the second group is #2.  */
+                regnum_t this_group_regnum;
+
+                compile_stack.avail--;		
+                begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+                fixup_alt_jump
+                  = COMPILE_STACK_TOP.fixup_alt_jump
+                    ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 
+                    : 0;
+                laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+                this_group_regnum = COMPILE_STACK_TOP.regnum;
+		/* If we've reached MAX_REGNUM groups, then this open
+		   won't actually generate any code, so we'll have to
+		   clear pending_exact explicitly.  */
+		pending_exact = 0;
+
+                /* We're at the end of the group, so now we know how many
+                   groups were inside this one.  */
+                if (this_group_regnum <= MAX_REGNUM)
+                  {
+                    unsigned char *inner_group_loc
+                      = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+                    
+                    *inner_group_loc = regnum - this_group_regnum;
+                    BUF_PUSH_3 (stop_memory, this_group_regnum,
+                                regnum - this_group_regnum);
+                  }
+              }
+              break;
+
+
+            case '|':					/* `\|'.  */
+              if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+                goto normal_backslash;
+            handle_alt:
+              if (syntax & RE_LIMITED_OPS)
+                goto normal_char;
+
+              /* Insert before the previous alternative a jump which
+                 jumps to this alternative if the former fails.  */
+              GET_BUFFER_SPACE (3);
+              INSERT_JUMP (on_failure_jump, begalt, b + 6);
+              pending_exact = 0;
+              b += 3;
+
+              /* The alternative before this one has a jump after it
+                 which gets executed if it gets matched.  Adjust that
+                 jump so it will jump to this alternative's analogous
+                 jump (put in below, which in turn will jump to the next
+                 (if any) alternative's such jump, etc.).  The last such
+                 jump jumps to the correct final destination.  A picture:
+                          _____ _____ 
+                          |   | |   |   
+                          |   v |   v 
+                         a | b   | c   
+
+                 If we are at `b', then fixup_alt_jump right now points to a
+                 three-byte space after `a'.  We'll put in the jump, set
+                 fixup_alt_jump to right after `b', and leave behind three
+                 bytes which we'll fill in when we get to after `c'.  */
+
+              if (fixup_alt_jump)
+                STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+              /* Mark and leave space for a jump after this alternative,
+                 to be filled in later either by next alternative or
+                 when know we're at the end of a series of alternatives.  */
+              fixup_alt_jump = b;
+              GET_BUFFER_SPACE (3);
+              b += 3;
+
+              laststart = 0;
+              begalt = b;
+              break;
+
+
+            case '{': 
+              /* If \{ is a literal.  */
+              if (!(syntax & RE_INTERVALS)
+                     /* If we're at `\{' and it's not the open-interval 
+                        operator.  */
+                  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+                  || (p - 2 == pattern  &&  p == pend))
+                goto normal_backslash;
+
+            handle_interval:
+              {
+                /* If got here, then the syntax allows intervals.  */
+
+                /* At least (most) this many matches must be made.  */
+                int lower_bound = -1, upper_bound = -1;
+
+                beg_interval = p - 1;
+
+                if (p == pend)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      FREE_STACK_RETURN (REG_EBRACE);
+                  }
+
+                GET_UNSIGNED_NUMBER (lower_bound);
+
+                if (c == ',')
+                  {
+                    GET_UNSIGNED_NUMBER (upper_bound);
+                    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+                  }
+                else
+                  /* Interval such as `{1}' => match exactly once. */
+                  upper_bound = lower_bound;
+
+                if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+                    || lower_bound > upper_bound)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      FREE_STACK_RETURN (REG_BADBR);
+                  }
+
+                if (!(syntax & RE_NO_BK_BRACES)) 
+                  {
+                    if (c != '\\') FREE_STACK_RETURN (REG_EBRACE);
+
+                    PATFETCH (c);
+                  }
+
+                if (c != '}')
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      FREE_STACK_RETURN (REG_BADBR);
+                  }
+
+                /* We just parsed a valid interval.  */
+
+                /* If it's invalid to have no preceding re.  */
+                if (!laststart)
+                  {
+                    if (syntax & RE_CONTEXT_INVALID_OPS)
+                      FREE_STACK_RETURN (REG_BADRPT);
+                    else if (syntax & RE_CONTEXT_INDEP_OPS)
+                      laststart = b;
+                    else
+                      goto unfetch_interval;
+                  }
+
+                /* If the upper bound is zero, don't want to succeed at
+                   all; jump from `laststart' to `b + 3', which will be
+                   the end of the buffer after we insert the jump.  */
+                 if (upper_bound == 0)
+                   {
+                     GET_BUFFER_SPACE (3);
+                     INSERT_JUMP (jump, laststart, b + 3);
+                     b += 3;
+                   }
+
+                 /* Otherwise, we have a nontrivial interval.  When
+                    we're all done, the pattern will look like:
+                      set_number_at <jump count> <upper bound>
+                      set_number_at <succeed_n count> <lower bound>
+                      succeed_n <after jump addr> <succeed_n count>
+                      <body of loop>
+                      jump_n <succeed_n addr> <jump count>
+                    (The upper bound and `jump_n' are omitted if
+                    `upper_bound' is 1, though.)  */
+                 else 
+                   { /* If the upper bound is > 1, we need to insert
+                        more at the end of the loop.  */
+                     unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+                     GET_BUFFER_SPACE (nbytes);
+
+                     /* Initialize lower bound of the `succeed_n', even
+                        though it will be set during matching by its
+                        attendant `set_number_at' (inserted next),
+                        because `re_compile_fastmap' needs to know.
+                        Jump to the `jump_n' we might insert below.  */
+                     INSERT_JUMP2 (succeed_n, laststart,
+                                   b + 5 + (upper_bound > 1) * 5,
+                                   lower_bound);
+                     b += 5;
+
+                     /* Code to initialize the lower bound.  Insert 
+                        before the `succeed_n'.  The `5' is the last two
+                        bytes of this `set_number_at', plus 3 bytes of
+                        the following `succeed_n'.  */
+                     insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+                     b += 5;
+
+                     if (upper_bound > 1)
+                       { /* More than one repetition is allowed, so
+                            append a backward jump to the `succeed_n'
+                            that starts this interval.
+                            
+                            When we've reached this during matching,
+                            we'll have matched the interval once, so
+                            jump back only `upper_bound - 1' times.  */
+                         STORE_JUMP2 (jump_n, b, laststart + 5,
+                                      upper_bound - 1);
+                         b += 5;
+
+                         /* The location we want to set is the second
+                            parameter of the `jump_n'; that is `b-2' as
+                            an absolute address.  `laststart' will be
+                            the `set_number_at' we're about to insert;
+                            `laststart+3' the number to set, the source
+                            for the relative address.  But we are
+                            inserting into the middle of the pattern --
+                            so everything is getting moved up by 5.
+                            Conclusion: (b - 2) - (laststart + 3) + 5,
+                            i.e., b - laststart.
+                            
+                            We insert this at the beginning of the loop
+                            so that if we fail during matching, we'll
+                            reinitialize the bounds.  */
+                         insert_op2 (set_number_at, laststart, b - laststart,
+                                     upper_bound - 1, b);
+                         b += 5;
+                       }
+                   }
+                pending_exact = 0;
+                beg_interval = NULL;
+              }
+              break;
+
+            unfetch_interval:
+              /* If an invalid interval, match the characters as literals.  */
+               assert (beg_interval);
+               p = beg_interval;
+               beg_interval = NULL;
+
+               /* normal_char and normal_backslash need `c'.  */
+               PATFETCH (c);	
+
+               if (!(syntax & RE_NO_BK_BRACES))
+                 {
+                   if (p > pattern  &&  p[-1] == '\\')
+                     goto normal_backslash;
+                 }
+               goto normal_char;
+
+#ifdef emacs
+            /* There is no way to specify the before_dot and after_dot
+               operators.  rms says this is ok.  --karl  */
+            case '=':
+              BUF_PUSH (at_dot);
+              break;
+
+            case 's':	
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+              break;
+
+            case 'S':
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+              break;
+#endif /* emacs */
+
+
+            case 'w':
+              laststart = b;
+              BUF_PUSH (wordchar);
+              break;
+
+
+            case 'W':
+              laststart = b;
+              BUF_PUSH (notwordchar);
+              break;
+
+
+            case '<':
+              BUF_PUSH (wordbeg);
+              break;
+
+            case '>':
+              BUF_PUSH (wordend);
+              break;
+
+            case 'b':
+              BUF_PUSH (wordbound);
+              break;
+
+            case 'B':
+              BUF_PUSH (notwordbound);
+              break;
+
+            case '`':
+              BUF_PUSH (begbuf);
+              break;
+
+            case '\'':
+              BUF_PUSH (endbuf);
+              break;
+
+            case '1': case '2': case '3': case '4': case '5':
+            case '6': case '7': case '8': case '9':
+              if (syntax & RE_NO_BK_REFS)
+                goto normal_char;
+
+              c1 = c - '0';
+
+              if (c1 > regnum)
+                FREE_STACK_RETURN (REG_ESUBREG);
+
+              /* Can't back reference to a subexpression if inside of it.  */
+              if (group_in_compile_stack (compile_stack, c1))
+                goto normal_char;
+
+              laststart = b;
+              BUF_PUSH_2 (duplicate, c1);
+              break;
+
+
+            case '+':
+            case '?':
+              if (syntax & RE_BK_PLUS_QM)
+                goto handle_plus;
+              else
+                goto normal_backslash;
+
+            default:
+            normal_backslash:
+              /* You might think it would be useful for \ to mean
+                 not to translate; but if we don't translate it
+                 it will never match anything.  */
+              c = TRANSLATE (c);
+              goto normal_char;
+            }
+          break;
+
+
+	default:
+        /* Expects the character in `c'.  */
+	normal_char:
+	      /* If no exactn currently being built.  */
+          if (!pending_exact 
+
+              /* If last exactn not at current position.  */
+              || pending_exact + *pending_exact + 1 != b
+              
+              /* We have only one byte following the exactn for the count.  */
+	      || *pending_exact == (1 << BYTEWIDTH) - 1
+
+              /* If followed by a repetition operator.  */
+              || *p == '*' || *p == '^'
+	      || ((syntax & RE_BK_PLUS_QM)
+		  ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+		  : (*p == '+' || *p == '?'))
+	      || ((syntax & RE_INTERVALS)
+                  && ((syntax & RE_NO_BK_BRACES)
+		      ? *p == '{'
+                      : (p[0] == '\\' && p[1] == '{'))))
+	    {
+	      /* Start building a new exactn.  */
+              
+              laststart = b;
+
+	      BUF_PUSH_2 (exactn, 0);
+	      pending_exact = b - 1;
+            }
+            
+	  BUF_PUSH (c);
+          (*pending_exact)++;
+	  break;
+        } /* switch (c) */
+    } /* while p != pend */
+
+  
+  /* Through the pattern now.  */
+  
+  if (fixup_alt_jump)
+    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+  if (!COMPILE_STACK_EMPTY) 
+    FREE_STACK_RETURN (REG_EPAREN);
+
+  /* If we don't want backtracking, force success
+     the first time we reach the end of the compiled pattern.  */
+  if (syntax & RE_NO_POSIX_BACKTRACKING)
+    BUF_PUSH (succeed);
+
+  free (compile_stack.stack);
+
+  /* We have succeeded; set the length of the buffer.  */
+  bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+  if (debug)
+    {
+      DEBUG_PRINT1 ("\nCompiled pattern: \n");
+      print_compiled_pattern (bufp);
+    }
+#endif /* DEBUG */
+
+#ifndef MATCH_MAY_ALLOCATE
+  /* Initialize the failure stack to the largest possible stack.  This
+     isn't necessary unless we're trying to avoid calling alloca in
+     the search and match routines.  */
+  {
+    int num_regs = bufp->re_nsub + 1;
+
+    /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
+       is strictly greater than re_max_failures, the largest possible stack
+       is 2 * re_max_failures failure points.  */
+    if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS))
+      {
+	fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);
+
+#ifdef emacs
+	if (! fail_stack.stack)
+	  fail_stack.stack
+	    = (fail_stack_elt_t *) xmalloc (fail_stack.size 
+					    * sizeof (fail_stack_elt_t));
+	else
+	  fail_stack.stack
+	    = (fail_stack_elt_t *) xrealloc (fail_stack.stack,
+					     (fail_stack.size
+					      * sizeof (fail_stack_elt_t)));
+#else /* not emacs */
+	if (! fail_stack.stack)
+	  fail_stack.stack
+	    = (fail_stack_elt_t *) malloc (fail_stack.size 
+					   * sizeof (fail_stack_elt_t));
+	else
+	  fail_stack.stack
+	    = (fail_stack_elt_t *) realloc (fail_stack.stack,
+					    (fail_stack.size
+					     * sizeof (fail_stack_elt_t)));
+#endif /* not emacs */
+      }
+
+    regex_grow_registers (num_regs);
+  }
+#endif /* not MATCH_MAY_ALLOCATE */
+
+  return REG_NOERROR;
+} /* regex_compile */
+
+
+/* Subroutines for `regex_compile'.  */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG.  */
+
+static void
+store_op1 (op, loc, arg)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg1);
+  STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+   for OP followed by two-byte integer parameter ARG.  */
+
+static void
+insert_op1 (op, loc, arg, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 3;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 5;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
+   after an alternative or a begin-subexpression.  We assume there is at
+   least one character before the ^.  */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+    const char *pattern, *p;
+    reg_syntax_t syntax;
+{
+  const char *prev = p - 2;
+  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+  
+  return
+       /* After a subexpression?  */
+       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+       /* After an alternative?  */
+    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
+   at least one character after the $, i.e., `P < PEND'.  */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+    const char *p, *pend;
+    int syntax;
+{
+  const char *next = p;
+  boolean next_backslash = *next == '\\';
+  const char *next_next = p + 1 < pend ? p + 1 : NULL;
+  
+  return
+       /* Before a subexpression?  */
+       (syntax & RE_NO_BK_PARENS ? *next == ')'
+        : next_backslash && next_next && *next_next == ')')
+       /* Before an alternative?  */
+    || (syntax & RE_NO_BK_VBAR ? *next == '|'
+        : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and 
+   false if it's not.  */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+    compile_stack_type compile_stack;
+    regnum_t regnum;
+{
+  int this_element;
+
+  for (this_element = compile_stack.avail - 1;  
+       this_element >= 0; 
+       this_element--)
+    if (compile_stack.stack[this_element].regnum == regnum)
+      return true;
+
+  return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
+   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
+   Then we set the translation of all bits between the starting and
+   ending characters (inclusive) in the compiled pattern B.
+   
+   Return an error code.
+   
+   We use these short variable names so we can use the same macros as
+   `regex_compile' itself.  */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+    const char **p_ptr, *pend;
+    char *translate;
+    reg_syntax_t syntax;
+    unsigned char *b;
+{
+  unsigned this_char;
+
+  const char *p = *p_ptr;
+  int range_start, range_end;
+  
+  if (p == pend)
+    return REG_ERANGE;
+
+  /* Even though the pattern is a signed `char *', we need to fetch
+     with unsigned char *'s; if the high bit of the pattern character
+     is set, the range endpoints will be negative if we fetch using a
+     signed char *.
+
+     We also want to fetch the endpoints without translating them; the 
+     appropriate translation is done in the bit-setting loop below.  */
+  /* The SVR4 compiler on the 3B2 had trouble with unsigned const char *.  */
+  range_start = ((const unsigned char *) p)[-2];
+  range_end   = ((const unsigned char *) p)[0];
+
+  /* Have to increment the pointer into the pattern string, so the
+     caller isn't still at the ending character.  */
+  (*p_ptr)++;
+
+  /* If the start is after the end, the range is empty.  */
+  if (range_start > range_end)
+    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+  /* Here we see why `this_char' has to be larger than an `unsigned
+     char' -- the range is inclusive, so if `range_end' == 0xff
+     (assuming 8-bit characters), we would otherwise go into an infinite
+     loop, since all characters <= 0xff.  */
+  for (this_char = range_start; this_char <= range_end; this_char++)
+    {
+      SET_LIST_BIT (TRANSLATE (this_char));
+    }
+  
+  return REG_NOERROR;
+}
+
+
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
+   characters can start a string that matches the pattern.  This fastmap
+   is used by re_search to skip quickly over impossible starting points.
+
+   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+   area as BUFP->fastmap.
+   
+   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+   the pattern buffer.
+
+   Returns 0 if we succeed, -2 if an internal error.   */
+
+int
+re_compile_fastmap (bufp)
+     struct re_pattern_buffer *bufp;
+{
+  int j, k;
+#ifdef MATCH_MAY_ALLOCATE
+  fail_stack_type fail_stack;
+#endif
+#ifndef REGEX_MALLOC
+  char *destination;
+#endif
+  /* We don't push any register information onto the failure stack.  */
+  unsigned num_regs = 0;
+  
+  register char *fastmap = bufp->fastmap;
+  unsigned char *pattern = bufp->buffer;
+  unsigned long size = bufp->used;
+  unsigned char *p = pattern;
+  register unsigned char *pend = pattern + size;
+
+  /* This holds the pointer to the failure stack, when
+     it is allocated relocatably.  */
+  fail_stack_elt_t *failure_stack_ptr;
+
+  /* Assume that each path through the pattern can be null until
+     proven otherwise.  We set this false at the bottom of switch
+     statement, to which we get only if a particular path doesn't
+     match the empty string.  */
+  boolean path_can_be_null = true;
+
+  /* We aren't doing a `succeed_n' to begin with.  */
+  boolean succeed_n_p = false;
+
+  assert (fastmap != NULL && p != NULL);
+  
+  INIT_FAIL_STACK ();
+  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
+  bufp->fastmap_accurate = 1;	    /* It will be when we're done.  */
+  bufp->can_be_null = 0;
+      
+  while (1)
+    {
+      if (p == pend || *p == succeed)
+	{
+	  /* We have reached the (effective) end of pattern.  */
+	  if (!FAIL_STACK_EMPTY ())
+	    {
+	      bufp->can_be_null |= path_can_be_null;
+
+	      /* Reset for next path.  */
+	      path_can_be_null = true;
+
+	      p = fail_stack.stack[--fail_stack.avail].pointer;
+
+	      continue;
+	    }
+	  else
+	    break;
+	}
+
+      /* We should never be about to go beyond the end of the pattern.  */
+      assert (p < pend);
+      
+      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+	{
+
+        /* I guess the idea here is to simply not bother with a fastmap
+           if a backreference is used, since it's too hard to figure out
+           the fastmap for the corresponding group.  Setting
+           `can_be_null' stops `re_search_2' from using the fastmap, so
+           that is all we do.  */
+	case duplicate:
+	  bufp->can_be_null = 1;
+          goto done;
+
+
+      /* Following are the cases which match a character.  These end
+         with `break'.  */
+
+	case exactn:
+          fastmap[p[1]] = 1;
+	  break;
+
+
+        case charset:
+          for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+	    if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+              fastmap[j] = 1;
+	  break;
+
+
+	case charset_not:
+	  /* Chars beyond end of map must be allowed.  */
+	  for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+	  for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+	    if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+              fastmap[j] = 1;
+          break;
+
+
+	case wordchar:
+	  for (j = 0; j < (1 << BYTEWIDTH); j++)
+	    if (SYNTAX (j) == Sword)
+	      fastmap[j] = 1;
+	  break;
+
+
+	case notwordchar:
+	  for (j = 0; j < (1 << BYTEWIDTH); j++)
+	    if (SYNTAX (j) != Sword)
+	      fastmap[j] = 1;
+	  break;
+
+
+        case anychar:
+	  {
+	    int fastmap_newline = fastmap['\n'];
+
+	    /* `.' matches anything ...  */
+	    for (j = 0; j < (1 << BYTEWIDTH); j++)
+	      fastmap[j] = 1;
+
+	    /* ... except perhaps newline.  */
+	    if (!(bufp->syntax & RE_DOT_NEWLINE))
+	      fastmap['\n'] = fastmap_newline;
+
+	    /* Return if we have already set `can_be_null'; if we have,
+	       then the fastmap is irrelevant.  Something's wrong here.  */
+	    else if (bufp->can_be_null)
+	      goto done;
+
+	    /* Otherwise, have to check alternative paths.  */
+	    break;
+	  }
+
+#ifdef emacs
+        case syntaxspec:
+	  k = *p++;
+	  for (j = 0; j < (1 << BYTEWIDTH); j++)
+	    if (SYNTAX (j) == (enum syntaxcode) k)
+	      fastmap[j] = 1;
+	  break;
+
+
+	case notsyntaxspec:
+	  k = *p++;
+	  for (j = 0; j < (1 << BYTEWIDTH); j++)
+	    if (SYNTAX (j) != (enum syntaxcode) k)
+	      fastmap[j] = 1;
+	  break;
+
+
+      /* All cases after this match the empty string.  These end with
+         `continue'.  */
+
+
+	case before_dot:
+	case at_dot:
+	case after_dot:
+          continue;
+#endif /* not emacs */
+
+
+        case no_op:
+        case begline:
+        case endline:
+	case begbuf:
+	case endbuf:
+	case wordbound:
+	case notwordbound:
+	case wordbeg:
+	case wordend:
+        case push_dummy_failure:
+          continue;
+
+
+	case jump_n:
+        case pop_failure_jump:
+	case maybe_pop_jump:
+	case jump:
+        case jump_past_alt:
+	case dummy_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+	  p += j;	
+	  if (j > 0)
+	    continue;
+            
+          /* Jump backward implies we just went through the body of a
+             loop and matched nothing.  Opcode jumped to should be
+             `on_failure_jump' or `succeed_n'.  Just treat it like an
+             ordinary jump.  For a * loop, it has pushed its failure
+             point already; if so, discard that as redundant.  */
+          if ((re_opcode_t) *p != on_failure_jump
+	      && (re_opcode_t) *p != succeed_n)
+	    continue;
+
+          p++;
+          EXTRACT_NUMBER_AND_INCR (j, p);
+          p += j;		
+	  
+          /* If what's on the stack is where we are now, pop it.  */
+          if (!FAIL_STACK_EMPTY () 
+	      && fail_stack.stack[fail_stack.avail - 1].pointer == p)
+            fail_stack.avail--;
+
+          continue;
+
+
+        case on_failure_jump:
+        case on_failure_keep_string_jump:
+	handle_on_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+
+          /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+             end of the pattern.  We don't want to push such a point,
+             since when we restore it above, entering the switch will
+             increment `p' past the end of the pattern.  We don't need
+             to push such a point since we obviously won't find any more
+             fastmap entries beyond `pend'.  Such a pattern can match
+             the null string, though.  */
+          if (p + j < pend)
+            {
+              if (!PUSH_PATTERN_OP (p + j, fail_stack))
+		{
+		  RESET_FAIL_STACK ();
+		  return -2;
+		}
+            }
+          else
+            bufp->can_be_null = 1;
+
+          if (succeed_n_p)
+            {
+              EXTRACT_NUMBER_AND_INCR (k, p);	/* Skip the n.  */
+              succeed_n_p = false;
+	    }
+
+          continue;
+
+
+	case succeed_n:
+          /* Get to the number of times to succeed.  */
+          p += 2;		
+
+          /* Increment p past the n for when k != 0.  */
+          EXTRACT_NUMBER_AND_INCR (k, p);
+          if (k == 0)
+	    {
+              p -= 4;
+  	      succeed_n_p = true;  /* Spaghetti code alert.  */
+              goto handle_on_failure_jump;
+            }
+          continue;
+
+
+	case set_number_at:
+          p += 4;
+          continue;
+
+
+	case start_memory:
+        case stop_memory:
+	  p += 2;
+	  continue;
+
+
+	default:
+          abort (); /* We have listed all the cases.  */
+        } /* switch *p++ */
+
+      /* Getting here means we have found the possible starting
+         characters for one path of the pattern -- and that the empty
+         string does not match.  We need not follow this path further.
+         Instead, look at the next alternative (remembered on the
+         stack), or quit if no more.  The test at the top of the loop
+         does these things.  */
+      path_can_be_null = false;
+      p = pend;
+    } /* while p */
+
+  /* Set `can_be_null' for the last path (also the first path, if the
+     pattern is empty).  */
+  bufp->can_be_null |= path_can_be_null;
+
+ done:
+  RESET_FAIL_STACK ();
+  return 0;
+} /* re_compile_fastmap */
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+
+
+/* Searching routines.  */
+
+/* Like re_search_2, below, but only one string is specified, and
+   doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, startpos, range;
+     struct re_registers *regs;
+{
+  return re_search_2 (bufp, NULL, 0, string, size, startpos, range, 
+		      regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+   virtual concatenation of STRING1 and STRING2, starting first at index
+   STARTPOS, then at STARTPOS + 1, and so on.
+   
+   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+   
+   RANGE is how far to scan while trying to match.  RANGE = 0 means try
+   only at STARTPOS; in general, the last start tried is STARTPOS +
+   RANGE.
+   
+   In REGS, return the indices of the virtual concatenation of STRING1
+   and STRING2 that matched the entire BUFP->buffer and its contained
+   subexpressions.
+   
+   Do not consider matching one past the index STOP in the virtual
+   concatenation of STRING1 and STRING2.
+
+   We return either the position in the strings at which the match was
+   found, -1 if no match, or -2 if error (such as failure
+   stack overflow).  */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+{
+  int val;
+  register char *fastmap = bufp->fastmap;
+  register char *translate = bufp->translate;
+  int total_size = size1 + size2;
+  int endpos = startpos + range;
+
+  /* Check for out-of-range STARTPOS.  */
+  if (startpos < 0 || startpos > total_size)
+    return -1;
+    
+  /* Fix up RANGE if it might eventually take us outside
+     the virtual concatenation of STRING1 and STRING2.  */
+  if (endpos < -1)
+    range = -1 - startpos;
+  else if (endpos > total_size)
+    range = total_size - startpos;
+
+  /* If the search isn't to be a backwards one, don't waste time in a
+     search for a pattern that must be anchored.  */
+  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+    {
+      if (startpos > 0)
+	return -1;
+      else
+	range = 1;
+    }
+
+  /* Update the fastmap now if not correct already.  */
+  if (fastmap && !bufp->fastmap_accurate)
+    if (re_compile_fastmap (bufp) == -2)
+      return -2;
+  
+  /* Loop through the string, looking for a place to start matching.  */
+  for (;;)
+    { 
+      /* If a fastmap is supplied, skip quickly over characters that
+         cannot be the start of a match.  If the pattern can match the
+         null string, however, we don't need to skip characters; we want
+         the first null string.  */
+      if (fastmap && startpos < total_size && !bufp->can_be_null)
+	{
+	  if (range > 0)	/* Searching forwards.  */
+	    {
+	      register const char *d;
+	      register int lim = 0;
+	      int irange = range;
+
+              if (startpos < size1 && startpos + range >= size1)
+                lim = range - (size1 - startpos);
+
+	      d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+   
+              /* Written out as an if-else to avoid testing `translate'
+                 inside the loop.  */
+	      if (translate)
+                while (range > lim
+                       && !fastmap[(unsigned char)
+				   translate[(unsigned char) *d++]])
+                  range--;
+	      else
+                while (range > lim && !fastmap[(unsigned char) *d++])
+                  range--;
+
+	      startpos += irange - range;
+	    }
+	  else				/* Searching backwards.  */
+	    {
+	      register char c = (size1 == 0 || startpos >= size1
+                                 ? string2[startpos - size1] 
+                                 : string1[startpos]);
+
+	      if (!fastmap[(unsigned char) TRANSLATE (c)])
+		goto advance;
+	    }
+	}
+
+      /* If can't match the null string, and that's all we have left, fail.  */
+      if (range >= 0 && startpos == total_size && fastmap
+          && !bufp->can_be_null)
+	return -1;
+
+      val = re_match_2_internal (bufp, string1, size1, string2, size2,
+				 startpos, regs, stop);
+#ifndef REGEX_MALLOC
+#ifdef C_ALLOCA
+      alloca (0);
+#endif
+#endif
+
+      if (val >= 0)
+	return startpos;
+        
+      if (val == -2)
+	return -2;
+
+    advance:
+      if (!range) 
+        break;
+      else if (range > 0) 
+        {
+          range--; 
+          startpos++;
+        }
+      else
+        {
+          range++; 
+          startpos--;
+        }
+    }
+  return -1;
+} /* re_search_2 */
+
+
+/* Declarations and macros for re_match_2.  */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+               common_op_match_null_string_p (),
+               group_match_null_string_p ();
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+   and `string2' into an offset from the beginning of that string.  */
+#define POINTER_TO_OFFSET(ptr)			\
+  (FIRST_STRING_P (ptr)				\
+   ? ((regoff_t) ((ptr) - string1))		\
+   : ((regoff_t) ((ptr) - string2 + size1)))
+
+/* Macros for dealing with the split strings in re_match_2.  */
+
+#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
+
+/* Call before fetching a character with *d.  This switches over to
+   string2 if necessary.  */
+#define PREFETCH()							\
+  while (d == dend)						    	\
+    {									\
+      /* End of string2 => fail.  */					\
+      if (dend == end_match_2) 						\
+        goto fail;							\
+      /* End of string1 => advance to string2.  */ 			\
+      d = string2;						        \
+      dend = end_match_2;						\
+    }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+   of `string1' and `string2'.  If only one string, it's `string2'.  */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)	
+
+
+/* Test if D points to a character which is word-constituent.  We have
+   two special cases to check for: if past the end of string1, look at
+   the first character in string2; and if before the beginning of
+   string2, look at the last character in string1.  */
+#define WORDCHAR_P(d)							\
+  (SYNTAX ((d) == end1 ? *string2					\
+           : (d) == string2 - 1 ? *(end1 - 1) : *(d))			\
+   == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+   to being word-constituent.  */
+#define AT_WORD_BOUNDARY(d)						\
+  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)				\
+   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc.  */
+#ifdef MATCH_MAY_ALLOCATE
+#define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL
+#define FREE_VARIABLES()						\
+  do {									\
+    REGEX_FREE_STACK (fail_stack.stack);				\
+    FREE_VAR (regstart);						\
+    FREE_VAR (regend);							\
+    FREE_VAR (old_regstart);						\
+    FREE_VAR (old_regend);						\
+    FREE_VAR (best_regstart);						\
+    FREE_VAR (best_regend);						\
+    FREE_VAR (reg_info);						\
+    FREE_VAR (reg_dummy);						\
+    FREE_VAR (reg_info_dummy);						\
+  } while (0)
+#else
+#define FREE_VARIABLES() ((void)0) /* Do nothing!  But inhibit gcc warning.  */
+#endif /* not MATCH_MAY_ALLOCATE */
+
+/* These values must meet several constraints.  They must not be valid
+   register values; since we have a limit of 255 registers (because
+   we use only one byte in the pattern for the register number), we can
+   use numbers larger than 255.  They must differ by 1, because of
+   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
+   be larger than the value for the highest register, so we do not try
+   to actually save any registers when none are active.  */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+
+
+/* Matching routines.  */
+
+#ifndef emacs   /* Emacs never uses this.  */
+/* re_match is like re_match_2 except it takes only a single string.  */
+
+int
+re_match (bufp, string, size, pos, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, pos;
+     struct re_registers *regs;
+{
+  int result = re_match_2_internal (bufp, NULL, 0, string, size,
+				    pos, regs, size);
+  alloca (0);
+  return result;
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+   and SIZE2, respectively).  We start matching at POS, and stop
+   matching at STOP.
+   
+   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+   store offsets for the substring each group matched in REGS.  See the
+   documentation for exactly how many groups we fill.
+
+   We return -1 if no match, -2 if an internal error (such as the
+   failure stack overflowing).  Otherwise, we return the length of the
+   matched substring.  */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  int result = re_match_2_internal (bufp, string1, size1, string2, size2,
+				    pos, regs, stop);
+  alloca (0);
+  return result;
+}
+
+/* This is a separate function so that we can force an alloca cleanup
+   afterwards.  */
+static int
+re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  /* General temporaries.  */
+  int mcnt;
+  unsigned char *p1;
+
+  /* Just past the end of the corresponding string.  */
+  const char *end1, *end2;
+
+  /* Pointers into string1 and string2, just past the last characters in
+     each to consider matching.  */
+  const char *end_match_1, *end_match_2;
+
+  /* Where we are in the data, and the end of the current string.  */
+  const char *d, *dend;
+  
+  /* Where we are in the pattern, and the end of the pattern.  */
+  unsigned char *p = bufp->buffer;
+  register unsigned char *pend = p + bufp->used;
+
+  /* Mark the opcode just after a start_memory, so we can test for an
+     empty subpattern when we get to the stop_memory.  */
+  unsigned char *just_past_start_mem = 0;
+
+  /* We use this to map every character in the string.  */
+  char *translate = bufp->translate;
+
+  /* Failure point stack.  Each place that can handle a failure further
+     down the line pushes a failure point on this stack.  It consists of
+     restart, regend, and reg_info for all registers corresponding to
+     the subexpressions we're currently inside, plus the number of such
+     registers, and, finally, two char *'s.  The first char * is where
+     to resume scanning the pattern; the second one is where to resume
+     scanning the strings.  If the latter is zero, the failure point is
+     a ``dummy''; if a failure happens and the failure point is a dummy,
+     it gets discarded and the next next one is tried.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  fail_stack_type fail_stack;
+#endif
+#ifdef DEBUG
+  static unsigned failure_id = 0;
+  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+  /* This holds the pointer to the failure stack, when
+     it is allocated relocatably.  */
+  fail_stack_elt_t *failure_stack_ptr;
+
+  /* We fill all the registers internally, independent of what we
+     return, for use in backreferences.  The number here includes
+     an element for register zero.  */
+  unsigned num_regs = bufp->re_nsub + 1;
+  
+  /* The currently active registers.  */
+  unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+  unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+  /* Information on the contents of registers. These are pointers into
+     the input strings; they record just what was matched (on this
+     attempt) by a subexpression part of the pattern, that is, the
+     regnum-th regstart pointer points to where in the pattern we began
+     matching and the regnum-th regend points to right after where we
+     stopped matching the regnum-th subexpression.  (The zeroth register
+     keeps track of what the whole pattern matches.)  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **regstart, **regend;
+#endif
+
+  /* If a group that's operated upon by a repetition operator fails to
+     match anything, then the register for its start will need to be
+     restored because it will have been set to wherever in the string we
+     are when we last see its open-group operator.  Similarly for a
+     register's end.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **old_regstart, **old_regend;
+#endif
+
+  /* The is_active field of reg_info helps us keep track of which (possibly
+     nested) subexpressions we are currently in. The matched_something
+     field of reg_info[reg_num] helps us tell whether or not we have
+     matched any of the pattern so far this time through the reg_num-th
+     subexpression.  These two fields get reset each time through any
+     loop their register is in.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
+  register_info_type *reg_info; 
+#endif
+
+  /* The following record the register info as found in the above
+     variables when we find a match better than any we've seen before. 
+     This happens as we backtrack through the failure points, which in
+     turn happens only if we have not yet matched the entire string. */
+  unsigned best_regs_set = false;
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **best_regstart, **best_regend;
+#endif
+  
+  /* Logically, this is `best_regend[0]'.  But we don't want to have to
+     allocate space for that if we're not allocating space for anything
+     else (see below).  Also, we never need info about register 0 for
+     any of the other register vectors, and it seems rather a kludge to
+     treat `best_regend' differently than the rest.  So we keep track of
+     the end of the best match so far in a separate variable.  We
+     initialize this to NULL so that when we backtrack the first time
+     and need to test it, it's not garbage.  */
+  const char *match_end = NULL;
+
+  /* This helps SET_REGS_MATCHED avoid doing redundant work.  */
+  int set_regs_matched_done = 0;
+
+  /* Used when we pop values we don't care about.  */
+#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
+  const char **reg_dummy;
+  register_info_type *reg_info_dummy;
+#endif
+
+#ifdef DEBUG
+  /* Counts the total number of registers pushed.  */
+  unsigned num_regs_pushed = 0; 	
+#endif
+
+  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+  
+  INIT_FAIL_STACK ();
+  
+#ifdef MATCH_MAY_ALLOCATE
+  /* Do not bother to initialize all the register variables if there are
+     no groups in the pattern, as it takes a fair amount of time.  If
+     there are groups, we include space for register 0 (the whole
+     pattern), even though we never use it, since it simplifies the
+     array indexing.  We should fix this.  */
+  if (bufp->re_nsub)
+    {
+      regstart = REGEX_TALLOC (num_regs, const char *);
+      regend = REGEX_TALLOC (num_regs, const char *);
+      old_regstart = REGEX_TALLOC (num_regs, const char *);
+      old_regend = REGEX_TALLOC (num_regs, const char *);
+      best_regstart = REGEX_TALLOC (num_regs, const char *);
+      best_regend = REGEX_TALLOC (num_regs, const char *);
+      reg_info = REGEX_TALLOC (num_regs, register_info_type);
+      reg_dummy = REGEX_TALLOC (num_regs, const char *);
+      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+      if (!(regstart && regend && old_regstart && old_regend && reg_info 
+            && best_regstart && best_regend && reg_dummy && reg_info_dummy)) 
+        {
+          FREE_VARIABLES ();
+          return -2;
+        }
+    }
+  else
+    {
+      /* We must initialize all our variables to NULL, so that
+         `FREE_VARIABLES' doesn't try to free them.  */
+      regstart = regend = old_regstart = old_regend = best_regstart
+        = best_regend = reg_dummy = NULL;
+      reg_info = reg_info_dummy = (register_info_type *) NULL;
+    }
+#endif /* MATCH_MAY_ALLOCATE */
+
+  /* The starting position is bogus.  */
+  if (pos < 0 || pos > size1 + size2)
+    {
+      FREE_VARIABLES ();
+      return -1;
+    }
+    
+  /* Initialize subexpression text positions to -1 to mark ones that no
+     start_memory/stop_memory has been seen for. Also initialize the
+     register information struct.  */
+  for (mcnt = 1; mcnt < num_regs; mcnt++)
+    {
+      regstart[mcnt] = regend[mcnt] 
+        = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+        
+      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+      IS_ACTIVE (reg_info[mcnt]) = 0;
+      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+    }
+  
+  /* We move `string1' into `string2' if the latter's empty -- but not if
+     `string1' is null.  */
+  if (size2 == 0 && string1 != NULL)
+    {
+      string2 = string1;
+      size2 = size1;
+      string1 = 0;
+      size1 = 0;
+    }
+  end1 = string1 + size1;
+  end2 = string2 + size2;
+
+  /* Compute where to stop matching, within the two strings.  */
+  if (stop <= size1)
+    {
+      end_match_1 = string1 + stop;
+      end_match_2 = string2;
+    }
+  else
+    {
+      end_match_1 = end1;
+      end_match_2 = string2 + stop - size1;
+    }
+
+  /* `p' scans through the pattern as `d' scans through the data. 
+     `dend' is the end of the input string that `d' points within.  `d'
+     is advanced into the following input string whenever necessary, but
+     this happens before fetching; therefore, at the beginning of the
+     loop, `d' can be pointing at the end of a string, but it cannot
+     equal `string2'.  */
+  if (size1 > 0 && pos <= size1)
+    {
+      d = string1 + pos;
+      dend = end_match_1;
+    }
+  else
+    {
+      d = string2 + pos - size1;
+      dend = end_match_2;
+    }
+
+  DEBUG_PRINT1 ("The compiled pattern is: ");
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+  DEBUG_PRINT1 ("The string to match is: `");
+  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+  DEBUG_PRINT1 ("'\n");
+  
+  /* This loops over pattern commands.  It exits by returning from the
+     function if the match is complete, or it drops through if the match
+     fails at this starting point in the input data.  */
+  for (;;)
+    {
+      DEBUG_PRINT2 ("\n0x%x: ", p);
+
+      if (p == pend)
+	{ /* End of pattern means we might have succeeded.  */
+          DEBUG_PRINT1 ("end of pattern ... ");
+          
+	  /* If we haven't matched the entire string, and we want the
+             longest match, try backtracking.  */
+          if (d != end_match_2)
+	    {
+	      /* 1 if this match ends in the same string (string1 or string2)
+		 as the best previous match.  */
+	      boolean same_str_p = (FIRST_STRING_P (match_end) 
+				    == MATCHING_IN_FIRST_STRING);
+	      /* 1 if this match is the best seen so far.  */
+	      boolean best_match_p;
+
+	      /* AIX compiler got confused when this was combined
+		 with the previous declaration.  */
+	      if (same_str_p)
+		best_match_p = d > match_end;
+	      else
+		best_match_p = !MATCHING_IN_FIRST_STRING;
+
+              DEBUG_PRINT1 ("backtracking.\n");
+              
+              if (!FAIL_STACK_EMPTY ())
+                { /* More failure points to try.  */
+
+                  /* If exceeds best match so far, save it.  */
+                  if (!best_regs_set || best_match_p)
+                    {
+                      best_regs_set = true;
+                      match_end = d;
+                      
+                      DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+                      
+                      for (mcnt = 1; mcnt < num_regs; mcnt++)
+                        {
+                          best_regstart[mcnt] = regstart[mcnt];
+                          best_regend[mcnt] = regend[mcnt];
+                        }
+                    }
+                  goto fail;	       
+                }
+
+              /* If no failure points, don't restore garbage.  And if
+                 last match is real best match, don't restore second
+                 best one. */
+              else if (best_regs_set && !best_match_p)
+                {
+  	        restore_best_regs:
+                  /* Restore best match.  It may happen that `dend ==
+                     end_match_1' while the restored d is in string2.
+                     For example, the pattern `x.*y.*z' against the
+                     strings `x-' and `y-z-', if the two strings are
+                     not consecutive in memory.  */
+                  DEBUG_PRINT1 ("Restoring best registers.\n");
+                  
+                  d = match_end;
+                  dend = ((d >= string1 && d <= end1)
+		           ? end_match_1 : end_match_2);
+
+		  for (mcnt = 1; mcnt < num_regs; mcnt++)
+		    {
+		      regstart[mcnt] = best_regstart[mcnt];
+		      regend[mcnt] = best_regend[mcnt];
+		    }
+                }
+            } /* d != end_match_2 */
+
+	succeed_label:
+          DEBUG_PRINT1 ("Accepting match.\n");
+
+          /* If caller wants register contents data back, do it.  */
+          if (regs && !bufp->no_sub)
+	    {
+              /* Have the register data arrays been allocated?  */
+              if (bufp->regs_allocated == REGS_UNALLOCATED)
+                { /* No.  So allocate them with malloc.  We need one
+                     extra element beyond `num_regs' for the `-1' marker
+                     GNU code uses.  */
+                  regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+                  regs->start = TALLOC (regs->num_regs, regoff_t);
+                  regs->end = TALLOC (regs->num_regs, regoff_t);
+                  if (regs->start == NULL || regs->end == NULL)
+		    {
+		      FREE_VARIABLES ();
+		      return -2;
+		    }
+                  bufp->regs_allocated = REGS_REALLOCATE;
+                }
+              else if (bufp->regs_allocated == REGS_REALLOCATE)
+                { /* Yes.  If we need more elements than were already
+                     allocated, reallocate them.  If we need fewer, just
+                     leave it alone.  */
+                  if (regs->num_regs < num_regs + 1)
+                    {
+                      regs->num_regs = num_regs + 1;
+                      RETALLOC (regs->start, regs->num_regs, regoff_t);
+                      RETALLOC (regs->end, regs->num_regs, regoff_t);
+                      if (regs->start == NULL || regs->end == NULL)
+			{
+			  FREE_VARIABLES ();
+			  return -2;
+			}
+                    }
+                }
+              else
+		{
+		  /* These braces fend off a "empty body in an else-statement"
+		     warning under GCC when assert expands to nothing.  */
+		  assert (bufp->regs_allocated == REGS_FIXED);
+		}
+
+              /* Convert the pointer data in `regstart' and `regend' to
+                 indices.  Register zero has to be set differently,
+                 since we haven't kept track of any info for it.  */
+              if (regs->num_regs > 0)
+                {
+                  regs->start[0] = pos;
+                  regs->end[0] = (MATCHING_IN_FIRST_STRING
+				  ? ((regoff_t) (d - string1))
+			          : ((regoff_t) (d - string2 + size1)));
+                }
+              
+              /* Go through the first `min (num_regs, regs->num_regs)'
+                 registers, since that is all we initialized.  */
+	      for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+		{
+                  if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+                    regs->start[mcnt] = regs->end[mcnt] = -1;
+                  else
+                    {
+		      regs->start[mcnt]
+			= (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]);
+                      regs->end[mcnt]
+			= (regoff_t) POINTER_TO_OFFSET (regend[mcnt]);
+                    }
+		}
+              
+              /* If the regs structure we return has more elements than
+                 were in the pattern, set the extra elements to -1.  If
+                 we (re)allocated the registers, this is the case,
+                 because we always allocate enough to have at least one
+                 -1 at the end.  */
+              for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+                regs->start[mcnt] = regs->end[mcnt] = -1;
+	    } /* regs && !bufp->no_sub */
+
+          DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+                        nfailure_points_pushed, nfailure_points_popped,
+                        nfailure_points_pushed - nfailure_points_popped);
+          DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+          mcnt = d - pos - (MATCHING_IN_FIRST_STRING 
+			    ? string1 
+			    : string2 - size1);
+
+          DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+          FREE_VARIABLES ();
+          return mcnt;
+        }
+
+      /* Otherwise match next pattern command.  */
+      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
+	{
+        /* Ignore these.  Used to ignore the n of succeed_n's which
+           currently have n == 0.  */
+        case no_op:
+          DEBUG_PRINT1 ("EXECUTING no_op.\n");
+          break;
+
+	case succeed:
+          DEBUG_PRINT1 ("EXECUTING succeed.\n");
+	  goto succeed_label;
+
+        /* Match the next n pattern characters exactly.  The following
+           byte in the pattern defines n, and the n bytes after that
+           are the characters to match.  */
+	case exactn:
+	  mcnt = *p++;
+          DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+          /* This is written out as an if-else so we don't waste time
+             testing `translate' inside the loop.  */
+          if (translate)
+	    {
+	      do
+		{
+		  PREFETCH ();
+		  if (translate[(unsigned char) *d++] != (char) *p++)
+                    goto fail;
+		}
+	      while (--mcnt);
+	    }
+	  else
+	    {
+	      do
+		{
+		  PREFETCH ();
+		  if (*d++ != (char) *p++) goto fail;
+		}
+	      while (--mcnt);
+	    }
+	  SET_REGS_MATCHED ();
+          break;
+
+
+        /* Match any character except possibly a newline or a null.  */
+	case anychar:
+          DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+          PREFETCH ();
+
+          if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+              || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+	    goto fail;
+
+          SET_REGS_MATCHED ();
+          DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
+          d++;
+	  break;
+
+
+	case charset:
+	case charset_not:
+	  {
+	    register unsigned char c;
+	    boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+            DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+	    PREFETCH ();
+	    c = TRANSLATE (*d); /* The character to match.  */
+
+            /* Cast to `unsigned' instead of `unsigned char' in case the
+               bit list is a full 32 bytes long.  */
+	    if (c < (unsigned) (*p * BYTEWIDTH)
+		&& p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+	      not = !not;
+
+	    p += 1 + *p;
+
+	    if (!not) goto fail;
+            
+	    SET_REGS_MATCHED ();
+            d++;
+	    break;
+	  }
+
+
+        /* The beginning of a group is represented by start_memory.
+           The arguments are the register number in the next byte, and the
+           number of groups inner to this one in the next.  The text
+           matched within the group is recorded (in the internal
+           registers data structure) under the register number.  */
+        case start_memory:
+	  DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+          /* Find out if this group can match the empty string.  */
+	  p1 = p;		/* To send to group_match_null_string_p.  */
+          
+          if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+            REG_MATCH_NULL_STRING_P (reg_info[*p]) 
+              = group_match_null_string_p (&p1, pend, reg_info);
+
+          /* Save the position in the string where we were the last time
+             we were at this open-group operator in case the group is
+             operated upon by a repetition operator, e.g., with `(a*)*b'
+             against `ab'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                             ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+                             : regstart[*p];
+	  DEBUG_PRINT2 ("  old_regstart: %d\n", 
+			 POINTER_TO_OFFSET (old_regstart[*p]));
+
+          regstart[*p] = d;
+	  DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+          IS_ACTIVE (reg_info[*p]) = 1;
+          MATCHED_SOMETHING (reg_info[*p]) = 0;
+
+	  /* Clear this whenever we change the register activity status.  */
+	  set_regs_matched_done = 0;
+          
+          /* This is the new highest active register.  */
+          highest_active_reg = *p;
+          
+          /* If nothing was active before, this is the new lowest active
+             register.  */
+          if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+            lowest_active_reg = *p;
+
+          /* Move past the register number and inner group count.  */
+          p += 2;
+	  just_past_start_mem = p;
+
+          break;
+
+
+        /* The stop_memory opcode represents the end of a group.  Its
+           arguments are the same as start_memory's: the register
+           number, and the number of inner groups.  */
+	case stop_memory:
+	  DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+             
+          /* We need to save the string position the last time we were at
+             this close-group operator in case the group is operated
+             upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+             against `aba'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                           ? REG_UNSET (regend[*p]) ? d : regend[*p]
+			   : regend[*p];
+	  DEBUG_PRINT2 ("      old_regend: %d\n", 
+			 POINTER_TO_OFFSET (old_regend[*p]));
+
+          regend[*p] = d;
+	  DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+          /* This register isn't active anymore.  */
+          IS_ACTIVE (reg_info[*p]) = 0;
+
+	  /* Clear this whenever we change the register activity status.  */
+	  set_regs_matched_done = 0;
+
+          /* If this was the only register active, nothing is active
+             anymore.  */
+          if (lowest_active_reg == highest_active_reg)
+            {
+              lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+              highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+            }
+          else
+            { /* We must scan for the new highest active register, since
+                 it isn't necessarily one less than now: consider
+                 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
+                 new highest active register is 1.  */
+              unsigned char r = *p - 1;
+              while (r > 0 && !IS_ACTIVE (reg_info[r]))
+                r--;
+              
+              /* If we end up at register zero, that means that we saved
+                 the registers as the result of an `on_failure_jump', not
+                 a `start_memory', and we jumped to past the innermost
+                 `stop_memory'.  For example, in ((.)*) we save
+                 registers 1 and 2 as a result of the *, but when we pop
+                 back to the second ), we are at the stop_memory 1.
+                 Thus, nothing is active.  */
+	      if (r == 0)
+                {
+                  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+                  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+                }
+              else
+                highest_active_reg = r;
+            }
+          
+          /* If just failed to match something this time around with a
+             group that's operated on by a repetition operator, try to
+             force exit from the ``loop'', and restore the register
+             information for this group that we had before trying this
+             last match.  */
+          if ((!MATCHED_SOMETHING (reg_info[*p])
+               || just_past_start_mem == p - 1)
+	      && (p + 2) < pend)              
+            {
+              boolean is_a_jump_n = false;
+              
+              p1 = p + 2;
+              mcnt = 0;
+              switch ((re_opcode_t) *p1++)
+                {
+                  case jump_n:
+		    is_a_jump_n = true;
+                  case pop_failure_jump:
+		  case maybe_pop_jump:
+		  case jump:
+		  case dummy_failure_jump:
+                    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+		    if (is_a_jump_n)
+		      p1 += 2;
+                    break;
+                  
+                  default:
+                    /* do nothing */ ;
+                }
+	      p1 += mcnt;
+        
+              /* If the next operation is a jump backwards in the pattern
+	         to an on_failure_jump right before the start_memory
+                 corresponding to this stop_memory, exit from the loop
+                 by forcing a failure after pushing on the stack the
+                 on_failure_jump's jump in the pattern, and d.  */
+              if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+                  && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+		{
+                  /* If this group ever matched anything, then restore
+                     what its registers were before trying this last
+                     failed match, e.g., with `(a*)*b' against `ab' for
+                     regstart[1], and, e.g., with `((a*)*(b*)*)*'
+                     against `aba' for regend[3].
+                     
+                     Also restore the registers for inner groups for,
+                     e.g., `((a*)(b*))*' against `aba' (register 3 would
+                     otherwise get trashed).  */
+                     
+                  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+		    {
+		      unsigned r; 
+        
+                      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+                      
+		      /* Restore this and inner groups' (if any) registers.  */
+                      for (r = *p; r < *p + *(p + 1); r++)
+                        {
+                          regstart[r] = old_regstart[r];
+
+                          /* xx why this test?  */
+                          if (old_regend[r] >= regstart[r])
+                            regend[r] = old_regend[r];
+                        }     
+                    }
+		  p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+                  goto fail;
+                }
+            }
+          
+          /* Move past the register number and the inner group count.  */
+          p += 2;
+          break;
+
+
+	/* \<digit> has been turned into a `duplicate' command which is
+           followed by the numeric value of <digit> as the register number.  */
+        case duplicate:
+	  {
+	    register const char *d2, *dend2;
+	    int regno = *p++;   /* Get which register to match against.  */
+	    DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+	    /* Can't back reference a group which we've never matched.  */
+            if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+              goto fail;
+              
+            /* Where in input to try to start matching.  */
+            d2 = regstart[regno];
+            
+            /* Where to stop matching; if both the place to start and
+               the place to stop matching are in the same string, then
+               set to the place to stop, otherwise, for now have to use
+               the end of the first string.  */
+
+            dend2 = ((FIRST_STRING_P (regstart[regno]) 
+		      == FIRST_STRING_P (regend[regno]))
+		     ? regend[regno] : end_match_1);
+	    for (;;)
+	      {
+		/* If necessary, advance to next segment in register
+                   contents.  */
+		while (d2 == dend2)
+		  {
+		    if (dend2 == end_match_2) break;
+		    if (dend2 == regend[regno]) break;
+
+                    /* End of string1 => advance to string2. */
+                    d2 = string2;
+                    dend2 = regend[regno];
+		  }
+		/* At end of register contents => success */
+		if (d2 == dend2) break;
+
+		/* If necessary, advance to next segment in data.  */
+		PREFETCH ();
+
+		/* How many characters left in this segment to match.  */
+		mcnt = dend - d;
+                
+		/* Want how many consecutive characters we can match in
+                   one shot, so, if necessary, adjust the count.  */
+                if (mcnt > dend2 - d2)
+		  mcnt = dend2 - d2;
+                  
+		/* Compare that many; failure if mismatch, else move
+                   past them.  */
+		if (translate 
+                    ? bcmp_translate (d, d2, mcnt, translate) 
+                    : bcmp (d, d2, mcnt))
+		  goto fail;
+		d += mcnt, d2 += mcnt;
+
+		/* Do this because we've match some characters.  */
+		SET_REGS_MATCHED ();
+	      }
+	  }
+	  break;
+
+
+        /* begline matches the empty string at the beginning of the string
+           (unless `not_bol' is set in `bufp'), and, if
+           `newline_anchor' is set, after newlines.  */
+	case begline:
+          DEBUG_PRINT1 ("EXECUTING begline.\n");
+          
+          if (AT_STRINGS_BEG (d))
+            {
+              if (!bufp->not_bol) break;
+            }
+          else if (d[-1] == '\n' && bufp->newline_anchor)
+            {
+              break;
+            }
+          /* In all other cases, we fail.  */
+          goto fail;
+
+
+        /* endline is the dual of begline.  */
+	case endline:
+          DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+          if (AT_STRINGS_END (d))
+            {
+              if (!bufp->not_eol) break;
+            }
+          
+          /* We have to ``prefetch'' the next character.  */
+          else if ((d == end1 ? *string2 : *d) == '\n'
+                   && bufp->newline_anchor)
+            {
+              break;
+            }
+          goto fail;
+
+
+	/* Match at the very beginning of the data.  */
+        case begbuf:
+          DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+          if (AT_STRINGS_BEG (d))
+            break;
+          goto fail;
+
+
+	/* Match at the very end of the data.  */
+        case endbuf:
+          DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+	  if (AT_STRINGS_END (d))
+	    break;
+          goto fail;
+
+
+        /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
+           pushes NULL as the value for the string on the stack.  Then
+           `pop_failure_point' will keep the current value for the
+           string, instead of restoring it.  To see why, consider
+           matching `foo\nbar' against `.*\n'.  The .* matches the foo;
+           then the . fails against the \n.  But the next thing we want
+           to do is match the \n against the \n; if we restored the
+           string value, we would be back at the foo.
+           
+           Because this is used only in specific cases, we don't need to
+           check all the things that `on_failure_jump' does, to make
+           sure the right things get saved on the stack.  Hence we don't
+           share its code.  The only reason to push anything on the
+           stack at all is that otherwise we would have to change
+           `anychar's code to do something besides goto fail in this
+           case; that seems worse than this.  */
+        case on_failure_keep_string_jump:
+          DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+          
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+          PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+          break;
+
+
+	/* Uses of on_failure_jump:
+        
+           Each alternative starts with an on_failure_jump that points
+           to the beginning of the next alternative.  Each alternative
+           except the last ends with a jump that in effect jumps past
+           the rest of the alternatives.  (They really jump to the
+           ending jump of the following alternative, because tensioning
+           these jumps is a hassle.)
+
+           Repeats start with an on_failure_jump that points past both
+           the repetition text and either the following jump or
+           pop_failure_jump back to this on_failure_jump.  */
+	case on_failure_jump:
+        on_failure:
+          DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+          /* If this on_failure_jump comes right before a group (i.e.,
+             the original * applied to a group), save the information
+             for that group and all inner ones, so that if we fail back
+             to this point, the group's information will be correct.
+             For example, in \(a*\)*\1, we need the preceding group,
+             and in \(\(a*\)b*\)\2, we need the inner group.  */
+
+          /* We can't use `p' to check ahead because we push
+             a failure point to `p + mcnt' after we do this.  */
+          p1 = p;
+
+          /* We need to skip no_op's before we look for the
+             start_memory in case this on_failure_jump is happening as
+             the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+             against aba.  */
+          while (p1 < pend && (re_opcode_t) *p1 == no_op)
+            p1++;
+
+          if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+            {
+              /* We have a new highest active register now.  This will
+                 get reset at the start_memory we are about to get to,
+                 but we will have saved all the registers relevant to
+                 this repetition op, as described above.  */
+              highest_active_reg = *(p1 + 1) + *(p1 + 2);
+              if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+                lowest_active_reg = *(p1 + 1);
+            }
+
+          DEBUG_PRINT1 (":\n");
+          PUSH_FAILURE_POINT (p + mcnt, d, -2);
+          break;
+
+
+        /* A smart repeat ends with `maybe_pop_jump'.
+	   We change it to either `pop_failure_jump' or `jump'.  */
+        case maybe_pop_jump:
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+          {
+	    register unsigned char *p2 = p;
+
+            /* Compare the beginning of the repeat with what in the
+               pattern follows its end. If we can establish that there
+               is nothing that they would both match, i.e., that we
+               would have to backtrack because of (as in, e.g., `a*a')
+               then we can change to pop_failure_jump, because we'll
+               never have to backtrack.
+               
+               This is not true in the case of alternatives: in
+               `(a|ab)*' we do need to backtrack to the `ab' alternative
+               (e.g., if the string was `ab').  But instead of trying to
+               detect that here, the alternative has put on a dummy
+               failure point which is what we will end up popping.  */
+
+	    /* Skip over open/close-group commands.
+	       If what follows this loop is a ...+ construct,
+	       look at what begins its body, since we will have to
+	       match at least one of that.  */
+	    while (1)
+	      {
+		if (p2 + 2 < pend
+		    && ((re_opcode_t) *p2 == stop_memory
+			|| (re_opcode_t) *p2 == start_memory))
+		  p2 += 3;
+		else if (p2 + 6 < pend
+			 && (re_opcode_t) *p2 == dummy_failure_jump)
+		  p2 += 6;
+		else
+		  break;
+	      }
+
+	    p1 = p + mcnt;
+	    /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+	       to the `maybe_finalize_jump' of this case.  Examine what 
+	       follows.  */
+
+            /* If we're at the end of the pattern, we can change.  */
+            if (p2 == pend)
+	      {
+		/* Consider what happens when matching ":\(.*\)"
+		   against ":/".  I don't really understand this code
+		   yet.  */
+  	        p[-3] = (unsigned char) pop_failure_jump;
+                DEBUG_PRINT1
+                  ("  End of pattern: change to `pop_failure_jump'.\n");
+              }
+
+            else if ((re_opcode_t) *p2 == exactn
+		     || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+	      {
+		register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+
+                if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+                  {
+  		    p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+                  
+		else if ((re_opcode_t) p1[3] == charset
+			 || (re_opcode_t) p1[3] == charset_not)
+		  {
+		    int not = (re_opcode_t) p1[3] == charset_not;
+                    
+		    if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+			&& p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+		      not = !not;
+
+                    /* `not' is equal to 1 if c would match, which means
+                        that we can't change to pop_failure_jump.  */
+		    if (!not)
+                      {
+  		        p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+		  }
+	      }
+            else if ((re_opcode_t) *p2 == charset)
+	      {
+#ifdef DEBUG
+		register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+#endif
+
+                if ((re_opcode_t) p1[3] == exactn
+		    && ! ((int) p2[1] * BYTEWIDTH > (int) p1[4]
+			  && (p2[1 + p1[4] / BYTEWIDTH]
+			      & (1 << (p1[4] % BYTEWIDTH)))))
+                  {
+  		    p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+                  
+		else if ((re_opcode_t) p1[3] == charset_not)
+		  {
+		    int idx;
+		    /* We win if the charset_not inside the loop
+		       lists every character listed in the charset after.  */
+		    for (idx = 0; idx < (int) p2[1]; idx++)
+		      if (! (p2[2 + idx] == 0
+			     || (idx < (int) p1[4]
+				 && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
+			break;
+
+		    if (idx == p2[1])
+                      {
+  		        p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+		  }
+		else if ((re_opcode_t) p1[3] == charset)
+		  {
+		    int idx;
+		    /* We win if the charset inside the loop
+		       has no overlap with the one after the loop.  */
+		    for (idx = 0;
+			 idx < (int) p2[1] && idx < (int) p1[4];
+			 idx++)
+		      if ((p2[2 + idx] & p1[5 + idx]) != 0)
+			break;
+
+		    if (idx == p2[1] || idx == p1[4])
+                      {
+  		        p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+		  }
+	      }
+	  }
+	  p -= 2;		/* Point at relative address again.  */
+	  if ((re_opcode_t) p[-1] != pop_failure_jump)
+	    {
+	      p[-1] = (unsigned char) jump;
+              DEBUG_PRINT1 ("  Match => jump.\n");
+	      goto unconditional_jump;
+	    }
+        /* Note fall through.  */
+
+
+	/* The end of a simple repeat has a pop_failure_jump back to
+           its matching on_failure_jump, where the latter will push a
+           failure point.  The pop_failure_jump takes off failure
+           points put on by this pop_failure_jump's matching
+           on_failure_jump; we got through the pattern to here from the
+           matching on_failure_jump, so didn't fail.  */
+        case pop_failure_jump:
+          {
+            /* We need to pass separate storage for the lowest and
+               highest registers, even though we don't care about the
+               actual values.  Otherwise, we will restore only one
+               register from the stack, since lowest will == highest in
+               `pop_failure_point'.  */
+            unsigned dummy_low_reg, dummy_high_reg;
+            unsigned char *pdummy;
+            const char *sdummy;
+
+            DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+            POP_FAILURE_POINT (sdummy, pdummy,
+                               dummy_low_reg, dummy_high_reg,
+                               reg_dummy, reg_dummy, reg_info_dummy);
+          }
+          /* Note fall through.  */
+
+          
+        /* Unconditionally jump (without popping any failure points).  */
+        case jump:
+	unconditional_jump:
+	  EXTRACT_NUMBER_AND_INCR (mcnt, p);	/* Get the amount to jump.  */
+          DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+	  p += mcnt;				/* Do the jump.  */
+          DEBUG_PRINT2 ("(to 0x%x).\n", p);
+	  break;
+
+	
+        /* We need this opcode so we can detect where alternatives end
+           in `group_match_null_string_p' et al.  */
+        case jump_past_alt:
+          DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+          goto unconditional_jump;
+
+
+        /* Normally, the on_failure_jump pushes a failure point, which
+           then gets popped at pop_failure_jump.  We will end up at
+           pop_failure_jump, also, and with a pattern of, say, `a+', we
+           are skipping over the on_failure_jump, so we have to push
+           something meaningless for pop_failure_jump to pop.  */
+        case dummy_failure_jump:
+          DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+          /* It doesn't matter what we push for the string here.  What
+             the code at `fail' tests is the value for the pattern.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          goto unconditional_jump;
+
+
+        /* At the end of an alternative, we need to push a dummy failure
+           point in case we are followed by a `pop_failure_jump', because
+           we don't want the failure point for the alternative to be
+           popped.  For example, matching `(a|ab)*' against `aab'
+           requires that we match the `ab' alternative.  */
+        case push_dummy_failure:
+          DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+          /* See comments just above at `dummy_failure_jump' about the
+             two zeroes.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          break;
+
+        /* Have to succeed matching what follows at least n times.
+           After that, handle like `on_failure_jump'.  */
+        case succeed_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+          assert (mcnt >= 0);
+          /* Originally, this is how many times we HAVE to succeed.  */
+          if (mcnt > 0)
+            {
+               mcnt--;
+	       p += 2;
+               STORE_NUMBER_AND_INCR (p, mcnt);
+               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p, mcnt);
+            }
+	  else if (mcnt == 0)
+            {
+              DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
+	      p[2] = (unsigned char) no_op;
+              p[3] = (unsigned char) no_op;
+              goto on_failure;
+            }
+          break;
+        
+        case jump_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+          /* Originally, this is how many times we CAN jump.  */
+          if (mcnt)
+            {
+               mcnt--;
+               STORE_NUMBER (p + 2, mcnt);
+	       goto unconditional_jump;	     
+            }
+          /* If don't have to jump any more, skip over the rest of command.  */
+	  else      
+	    p += 4;		     
+          break;
+        
+	case set_number_at:
+	  {
+            DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            p1 = p + mcnt;
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
+	    STORE_NUMBER (p1, mcnt);
+            break;
+          }
+
+        case wordbound:
+          DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+          if (AT_WORD_BOUNDARY (d))
+	    break;
+          goto fail;
+
+	case notwordbound:
+          DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+	  if (AT_WORD_BOUNDARY (d))
+	    goto fail;
+          break;
+
+	case wordbeg:
+          DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+	  if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+	    break;
+          goto fail;
+
+	case wordend:
+          DEBUG_PRINT1 ("EXECUTING wordend.\n");
+	  if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+              && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+	    break;
+          goto fail;
+
+#ifdef emacs
+  	case before_dot:
+          DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+ 	  if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+  	    goto fail;
+  	  break;
+  
+  	case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+ 	  if (PTR_CHAR_POS ((unsigned char *) d) != point)
+  	    goto fail;
+  	  break;
+  
+  	case after_dot:
+          DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+          if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+  	    goto fail;
+  	  break;
+#if 0 /* not emacs19 */
+	case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+	  if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+	    goto fail;
+	  break;
+#endif /* not emacs19 */
+
+	case syntaxspec:
+          DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+	  mcnt = *p++;
+	  goto matchsyntax;
+
+        case wordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+	  mcnt = (int) Sword;
+        matchsyntax:
+	  PREFETCH ();
+	  /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
+	  d++;
+	  if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt)
+	    goto fail;
+          SET_REGS_MATCHED ();
+	  break;
+
+	case notsyntaxspec:
+          DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+	  mcnt = *p++;
+	  goto matchnotsyntax;
+
+        case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+	  mcnt = (int) Sword;
+        matchnotsyntax:
+	  PREFETCH ();
+	  /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
+	  d++;
+	  if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt)
+	    goto fail;
+	  SET_REGS_MATCHED ();
+          break;
+
+#else /* not emacs */
+	case wordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+	  PREFETCH ();
+          if (!WORDCHAR_P (d))
+            goto fail;
+	  SET_REGS_MATCHED ();
+          d++;
+	  break;
+	  
+	case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+	  PREFETCH ();
+	  if (WORDCHAR_P (d))
+            goto fail;
+          SET_REGS_MATCHED ();
+          d++;
+	  break;
+#endif /* not emacs */
+          
+        default:
+          abort ();
+	}
+      continue;  /* Successfully executed one pattern command; keep going.  */
+
+
+    /* We goto here if a matching operation fails. */
+    fail:
+      if (!FAIL_STACK_EMPTY ())
+	{ /* A restart point is known.  Restore to that state.  */
+          DEBUG_PRINT1 ("\nFAIL:\n");
+          POP_FAILURE_POINT (d, p,
+                             lowest_active_reg, highest_active_reg,
+                             regstart, regend, reg_info);
+
+          /* If this failure point is a dummy, try the next one.  */
+          if (!p)
+	    goto fail;
+
+          /* If we failed to the end of the pattern, don't examine *p.  */
+	  assert (p <= pend);
+          if (p < pend)
+            {
+              boolean is_a_jump_n = false;
+              
+              /* If failed to a backwards jump that's part of a repetition
+                 loop, need to pop this failure point and use the next one.  */
+              switch ((re_opcode_t) *p)
+                {
+                case jump_n:
+                  is_a_jump_n = true;
+                case maybe_pop_jump:
+                case pop_failure_jump:
+                case jump:
+                  p1 = p + 1;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  p1 += mcnt;	
+
+                  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+                      || (!is_a_jump_n
+                          && (re_opcode_t) *p1 == on_failure_jump))
+                    goto fail;
+                  break;
+                default:
+                  /* do nothing */ ;
+                }
+            }
+
+          if (d >= string1 && d <= end1)
+	    dend = end_match_1;
+        }
+      else
+        break;   /* Matching at this starting point really fails.  */
+    } /* for (;;) */
+
+  if (best_regs_set)
+    goto restore_best_regs;
+
+  FREE_VARIABLES ();
+
+  return -1;         			/* Failure to match.  */
+} /* re_match_2 */
+
+
+/* Subroutine definitions for re_match_2.  */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+   
+   Return true if the pattern up to the corresponding stop_memory can
+   match the empty string, and false otherwise.
+   
+   If we find the matching stop_memory, sets P to point to one past its number.
+   Otherwise, sets P to an undefined byte less than or equal to END.
+
+   We don't handle duplicates properly (yet).  */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  /* Point to after the args to the start_memory.  */
+  unsigned char *p1 = *p + 2;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and return true or
+	 false, as appropriate, when we get to one that can't, or to the
+         matching stop_memory.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+        /* Could be either a loop or a series of alternatives.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          
+          /* If the next operation is not a jump backwards in the
+	     pattern.  */
+
+	  if (mcnt >= 0)
+	    {
+              /* Go through the on_failure_jumps of the alternatives,
+                 seeing if any of the alternatives cannot match nothing.
+                 The last alternative starts with only a jump,
+                 whereas the rest start with on_failure_jump and end
+                 with a jump, e.g., here is the pattern for `a|b|c':
+
+                 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+                 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+                 /exactn/1/c						
+
+                 So, we have to first go through the first (n-1)
+                 alternatives and then deal with the last one separately.  */
+
+
+              /* Deal with the first (n-1) alternatives, which start
+                 with an on_failure_jump (see above) that jumps to right
+                 past a jump_past_alt.  */
+
+              while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+                {
+                  /* `mcnt' holds how many bytes long the alternative
+                     is, including the ending `jump_past_alt' and
+                     its number.  */
+
+                  if (!alt_match_null_string_p (p1, p1 + mcnt - 3, 
+				                      reg_info))
+                    return false;
+
+                  /* Move to right after this alternative, including the
+		     jump_past_alt.  */
+                  p1 += mcnt;	
+
+                  /* Break if it's the beginning of an n-th alternative
+                     that doesn't begin with an on_failure_jump.  */
+                  if ((re_opcode_t) *p1 != on_failure_jump)
+                    break;
+		
+		  /* Still have to check that it's not an n-th
+		     alternative that starts with an on_failure_jump.  */
+		  p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+                    {
+		      /* Get to the beginning of the n-th alternative.  */
+                      p1 -= 3;
+                      break;
+                    }
+                }
+
+              /* Deal with the last alternative: go back and get number
+                 of the `jump_past_alt' just before it.  `mcnt' contains
+                 the length of the alternative.  */
+              EXTRACT_NUMBER (mcnt, p1 - 2);
+
+              if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+                return false;
+
+              p1 += mcnt;	/* Get past the n-th alternative.  */
+            } /* if mcnt > 0 */
+          break;
+
+          
+        case stop_memory:
+	  assert (p1[1] == **p);
+          *p = p1 + 2;
+          return true;
+
+        
+        default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    } /* while p1 < end */
+
+  return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+   It expects P to be the first byte of a single alternative and END one
+   byte past the last. The alternative can contain groups.  */
+   
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+    unsigned char *p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  unsigned char *p1 = p;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and break when we get 
+         to one that can't.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+	/* It's a loop.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+          break;
+          
+	default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    }  /* while p1 < end */
+
+  return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+   alt_match_null_string_p.  
+   
+   Sets P to one after the op and its arguments, if any.  */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  boolean ret;
+  int reg_no;
+  unsigned char *p1 = *p;
+
+  switch ((re_opcode_t) *p1++)
+    {
+    case no_op:
+    case begline:
+    case endline:
+    case begbuf:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case wordbound:
+    case notwordbound:
+#ifdef emacs
+    case before_dot:
+    case at_dot:
+    case after_dot:
+#endif
+      break;
+
+    case start_memory:
+      reg_no = *p1;
+      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+      ret = group_match_null_string_p (&p1, end, reg_info);
+      
+      /* Have to set this here in case we're checking a group which
+         contains a group and a back reference to it.  */
+
+      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+        REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+      if (!ret)
+        return false;
+      break;
+          
+    /* If this is an optimized succeed_n for zero times, make the jump.  */
+    case jump:
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+      if (mcnt >= 0)
+        p1 += mcnt;
+      else
+        return false;
+      break;
+
+    case succeed_n:
+      /* Get to the number of times to succeed.  */
+      p1 += 2;		
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+      if (mcnt == 0)
+        {
+          p1 -= 4;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+        }
+      else
+        return false;
+      break;
+
+    case duplicate: 
+      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+        return false;
+      break;
+
+    case set_number_at:
+      p1 += 4;
+
+    default:
+      /* All other opcodes mean we cannot match the empty string.  */
+      return false;
+  }
+
+  *p = p1;
+  return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+   bytes; nonzero otherwise.  */
+   
+static int
+bcmp_translate (s1, s2, len, translate)
+     unsigned char *s1, *s2;
+     register int len;
+     char *translate;
+{
+  register unsigned char *p1 = s1, *p2 = s2;
+  while (len)
+    {
+      if (translate[*p1++] != translate[*p2++]) return 1;
+      len--;
+    }
+  return 0;
+}
+
+
+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length SIZE) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+   
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.
+   
+   We call regex_compile to do the actual compilation.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+     const char *pattern;
+     int length;
+     struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+  
+  /* GNU code is written to assume at least RE_NREGS registers will be set
+     (and at least one extra will be -1).  */
+  bufp->regs_allocated = REGS_UNALLOCATED;
+  
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  bufp->no_sub = 0;
+  
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+  
+  ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+  if (!ret)
+    return NULL;
+  return gettext (re_error_msgid[(int) ret]);
+}     
+
+
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#ifdef _REGEX_RE_COMP
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+    const char *s;
+{
+  reg_errcode_t ret;
+  
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+	return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (!re_comp_buf.buffer)
+    {
+      re_comp_buf.buffer = (unsigned char *) malloc (200);
+      if (re_comp_buf.buffer == NULL)
+        return gettext (re_error_msgid[(int) REG_ESPACE]);
+      re_comp_buf.allocated = 200;
+
+      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+      if (re_comp_buf.fastmap == NULL)
+	return gettext (re_error_msgid[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+  
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (re_error_msgid[(int) ret]);
+}
+
+
+int
+re_exec (s)
+    const char *s;
+{
+  const int len = strlen (s);
+  return
+    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+
+/* POSIX.2 functions.  Don't define these for Emacs.  */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' and `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *preg;
+    const char *pattern; 
+    int cflags;
+{
+  reg_errcode_t ret;
+  unsigned syntax
+    = (cflags & REG_EXTENDED) ?
+      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+  /* regex_compile will allocate the space for the compiled pattern.  */
+  preg->buffer = 0;
+  preg->allocated = 0;
+  preg->used = 0;
+  
+  /* Don't bother to use a fastmap when searching.  This simplifies the
+     REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+     characters after newlines into the fastmap.  This way, we just try
+     every character.  */
+  preg->fastmap = 0;
+  
+  if (cflags & REG_ICASE)
+    {
+      unsigned i;
+      
+      preg->translate = (char *) malloc (CHAR_SET_SIZE);
+      if (preg->translate == NULL)
+        return (int) REG_ESPACE;
+
+      /* Map uppercase characters to corresponding lowercase ones.  */
+      for (i = 0; i < CHAR_SET_SIZE; i++)
+        preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+    }
+  else
+    preg->translate = NULL;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+
+  preg->no_sub = !!(cflags & REG_NOSUB);
+
+  /* POSIX says a null character in the pattern terminates it, so we 
+     can use strlen here in compiling the pattern.  */
+  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+  
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN) ret = REG_EPAREN;
+  
+  return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+   
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+   
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+   
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *preg;
+    const char *string; 
+    size_t nmatch; 
+    regmatch_t pmatch[]; 
+    int eflags;
+{
+  int ret;
+  struct re_registers regs;
+  regex_t private_preg;
+  int len = strlen (string);
+  boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+  private_preg = *preg;
+  
+  private_preg.not_bol = !!(eflags & REG_NOTBOL);
+  private_preg.not_eol = !!(eflags & REG_NOTEOL);
+  
+  /* The user has told us exactly how many registers to return
+     information about, via `nmatch'.  We have to pass that on to the
+     matching routines.  */
+  private_preg.regs_allocated = REGS_FIXED;
+  
+  if (want_reg_info)
+    {
+      regs.num_regs = nmatch;
+      regs.start = TALLOC (nmatch, regoff_t);
+      regs.end = TALLOC (nmatch, regoff_t);
+      if (regs.start == NULL || regs.end == NULL)
+        return (int) REG_NOMATCH;
+    }
+
+  /* Perform the searching operation.  */
+  ret = re_search (&private_preg, string, len,
+                   /* start: */ 0, /* range: */ len,
+                   want_reg_info ? &regs : (struct re_registers *) 0);
+  
+  /* Copy the register information to the POSIX structure.  */
+  if (want_reg_info)
+    {
+      if (ret >= 0)
+        {
+          unsigned r;
+
+          for (r = 0; r < nmatch; r++)
+            {
+              pmatch[r].rm_so = regs.start[r];
+              pmatch[r].rm_eo = regs.end[r];
+            }
+        }
+
+      /* If we needed the temporary register info, free the space now.  */
+      free (regs.start);
+      free (regs.end);
+    }
+
+  /* We want zero return to mean success, unlike `re_search'.  */
+  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+    int errcode;
+    const regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (errcode < 0
+      || errcode >= (sizeof (re_error_msgid) / sizeof (re_error_msgid[0])))
+    /* Only error codes returned by the rest of the code should be passed 
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (re_error_msgid[errcode]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+  
+  if (errbuf_size != 0)
+    {
+      if (msg_size > errbuf_size)
+        {
+          strncpy (errbuf, msg, errbuf_size - 1);
+          errbuf[errbuf_size - 1] = 0;
+        }
+      else
+        strcpy (errbuf, msg);
+    }
+
+  return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  if (preg->buffer != NULL)
+    free (preg->buffer);
+  preg->buffer = NULL;
+  
+  preg->allocated = 0;
+  preg->used = 0;
+
+  if (preg->fastmap != NULL)
+    free (preg->fastmap);
+  preg->fastmap = NULL;
+  preg->fastmap_accurate = 0;
+
+  if (preg->translate != NULL)
+    free (preg->translate);
+  preg->translate = NULL;
+}
+
+#endif /* not emacs  */
+
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
Index: /gsdl/trunk/gs2common/indexers/mg/lib/regex.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/regex.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/regex.h	(revision 16561)
@@ -0,0 +1,497 @@
+/* Definitions for data structures and routines for the regular
+   expression library, version 0.12.
+
+   Copyright (C) 1985, 89, 90, 91, 92, 93, 95 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+
+#if !defined (_POSIX_C_SOURCE) && !defined (_POSIX_SOURCE) && defined (VMS)
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+   should be there.  */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals. 
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.  
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically, 
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES. 
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal. 
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+
+
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */ 
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK							\
+  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL			\
+   | RE_NO_BK_PARENS            | RE_NO_BK_REFS				\
+   | RE_NO_BK_VBAR               | RE_NO_EMPTY_RANGES			\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK 						\
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP							\
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES				\
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS				\
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP							\
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE			\
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS				\
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP						\
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON						\
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL		\
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC						\
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC					\
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED					\
+  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR				\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED				\
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS				\
+   | RE_NO_BK_VBAR	    | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1) 
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+ 
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+  REG_NOERROR = 0,	/* Success.  */
+  REG_NOMATCH,		/* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,		/* Invalid pattern.  */
+  REG_ECOLLATE,		/* Not implemented.  */
+  REG_ECTYPE,		/* Invalid character class name.  */
+  REG_EESCAPE,		/* Trailing backslash.  */
+  REG_ESUBREG,		/* Invalid back reference.  */
+  REG_EBRACK,		/* Unmatched left bracket.  */
+  REG_EPAREN,		/* Parenthesis imbalance.  */ 
+  REG_EBRACE,		/* Unmatched \{.  */
+  REG_BADBR,		/* Invalid contents of \{\}.  */
+  REG_ERANGE,		/* Invalid range end.  */
+  REG_ESPACE,		/* Ran out of memory.  */
+  REG_BADRPT,		/* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,		/* Premature end.  */
+  REG_ESIZE,		/* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN		/* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+
+
+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+	/* Space that holds the compiled pattern.  It is declared as
+          `unsigned char *' because its elements are
+           sometimes used as array indexes.  */
+  unsigned char *buffer;
+
+	/* Number of bytes to which `buffer' points.  */
+  unsigned long allocated;
+
+	/* Number of bytes actually used in `buffer'.  */
+  unsigned long used;	
+
+        /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+           the fastmap, if there is one, to skip over impossible
+           starting points for matches.  */
+  char *fastmap;
+
+        /* Either a translate table to apply to all characters before
+           comparing them, or zero for no translation.  The translation
+           is applied to a pattern when it is compiled and to a string
+           when it is matched.  */
+  char *translate;
+
+	/* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+        /* Zero if this pattern cannot match the empty string, one else.
+           Well, in truth it's used only in `re_search_2', to see
+           whether or not we should use the fastmap, so we don't set
+           this absolutely perfectly; see `re_compile_fastmap' (the
+           `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+             for `max (RE_NREGS, re_nsub + 1)' groups.
+           If REGS_REALLOCATE, reallocate space if necessary.
+           If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+        /* Set to zero when `regex_compile' compiles a pattern; set to one
+           by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+        /* If set, `re_match_2' does not return information about
+           subexpressions.  */
+  unsigned no_sub : 1;
+
+        /* If set, a beginning-of-line anchor doesn't match at the
+           beginning of the string.  */ 
+  unsigned not_bol : 1;
+
+        /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+        /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+
+
+/* Declarations for routines.  */
+
+/* To avoid duplicating every routine declaration -- once with a
+   prototype (if we are ANSI), and once without (if we aren't) -- we
+   use the following macro to declare argument types.  This
+   unfortunately clutters up the declarations a bit, but I think it's
+   worth it.  */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+extern const char *re_compile_pattern
+  _RE_ARGS ((const char *pattern, int length,
+             struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+extern int re_search
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+            int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+extern int re_search_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+extern int re_match
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+             int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+extern int re_match_2 
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+extern void re_set_registers
+  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+             unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+#ifdef _REGEX_RE_COMP
+/* 4.2 bsd compatibility.  */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+#endif
+
+/* POSIX compatibility.  */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+  _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+             regmatch_t pmatch[], int eflags));
+extern size_t regerror
+  _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+             size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
Index: /gsdl/trunk/gs2common/indexers/mg/lib/rx.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/rx.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/rx.c	(revision 16561)
@@ -0,0 +1,7272 @@
+
+/*	Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of the librx library.
+
+Librx is free software; you can redistribute it and/or modify it under
+the terms of the GNU Library General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+Librx 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 Library General Public
+License along with this software; see the file COPYING.LIB.  If not,
+write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA
+02139, USA.  */
+
+/* NOTE!!!  AIX is so losing it requires this to be the first thing in the 
+ * file. 
+ * Do not put ANYTHING before it!  
+ */
+#if !defined (__GNUC__) && defined (_AIX)
+ #pragma alloca
+#endif
+
+/* To make linux happy? */
+#ifndef	_GNU_SOURCE
+#define	_GNU_SOURCE
+#endif
+
+#if HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+#  include <malloc.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+
+
+char rx_version_string[] = "GNU Rx version 0.07.1";
+
+			/* ``Too hard!''
+			 *	    -- anon.
+			 */
+
+
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef isgraph
+#define isgraph(c) (isprint (c) && !isspace (c))
+#endif
+#ifndef isblank
+#define isblank(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#include <sys/types.h>
+
+#undef MAX
+#undef MIN
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+
+#ifndef __GCC__
+#undef __inline__
+#define __inline__
+#endif
+
+/* Emacs already defines alloca, sometimes.  */
+#ifndef alloca
+
+/* Make alloca work the best possible way.  */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top.  */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */ 
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+/* Memory management and stuff for emacs. */
+
+#define CHARBITS 8
+#define remalloc(M, S) (M ? realloc (M, S) : malloc (S))
+
+
+/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
+ * use `alloca' instead of `malloc' for the backtracking stack.
+ *
+ * Emacs will die miserably if we don't do this.
+ */
+
+#ifdef REGEX_MALLOC
+#define REGEX_ALLOCATE malloc
+#else /* not REGEX_MALLOC  */
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#define REGEX_ALLOCATE _alloca
+#else
+#define REGEX_ALLOCATE alloca
+#endif /* __WIN32__ */
+#endif /* not REGEX_MALLOC */
+
+
+#ifdef RX_WANT_RX_DEFS
+#define RX_DECL extern
+#define RX_DEF_QUAL 
+#else
+#define RX_WANT_RX_DEFS
+#define RX_DECL static
+#define RX_DEF_QUAL static
+#endif
+#include "rx.h"
+#undef RX_DECL
+#define RX_DECL RX_DEF_QUAL
+
+
+#ifndef emacs
+
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#else /* not SYNTAX_TABLE */
+
+#ifdef __STDC__
+static void
+init_syntax_once (void)
+#else
+static void
+init_syntax_once ()
+#endif
+{
+   register int c;
+   static int done = 0;
+
+   if (done)
+     return;
+
+   bzero (re_syntax_table, sizeof re_syntax_table);
+
+   for (c = 'a'; c <= 'z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = 'A'; c <= 'Z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = '0'; c <= '9'; c++)
+     re_syntax_table[c] = Sword;
+
+   re_syntax_table['_'] = Sword;
+
+   done = 1;
+}
+#endif /* not SYNTAX_TABLE */
+#endif /* not emacs */
+
+
+/* Compile with `-DRX_DEBUG' and use the following flags.
+ *
+ * Debugging flags:
+ *   	rx_debug - print information as a regexp is compiled
+ * 	rx_debug_trace - print information as a regexp is executed
+ */
+
+#ifdef RX_DEBUG
+
+int rx_debug_compile = 0;
+int rx_debug_trace = 0;
+static struct re_pattern_buffer * dbug_rxb = 0;
+
+#ifdef __STDC__
+typedef void (*side_effect_printer) (struct rx *, void *, FILE *);
+#else
+typedef void (*side_effect_printer) ();
+#endif
+
+#ifdef __STDC__
+static void print_cset (struct rx *rx, rx_Bitset cset, FILE * fp);
+#else
+static void print_cset ();
+#endif
+
+#ifdef __STDC__
+static void
+print_rexp (struct rx *rx,
+	    struct rexp_node *node, int depth,
+	    side_effect_printer seprint, FILE * fp)
+#else
+static void
+print_rexp (rx, node, depth, seprint, fp)
+     struct rx *rx;
+     struct rexp_node *node;
+     int depth;
+     side_effect_printer seprint;
+     FILE * fp;
+#endif
+{
+  if (!node)
+    return;
+  else
+    {
+      switch (node->type)
+	{
+	case r_cset:
+	  {
+	    fprintf (fp, "%*s", depth, "");
+	    print_cset (rx, node->params.cset, fp);
+	    fputc ('\n', fp);
+	    break;
+	  }
+
+ 	case r_opt:
+	case r_star:
+	  fprintf (fp, "%*s%s\n", depth, "",
+		   node->type == r_opt ? "opt" : "star");
+	  print_rexp (rx, node->params.pair.left, depth + 3, seprint, fp);
+	  break;
+
+	case r_2phase_star:
+	  fprintf (fp, "%*s2phase star\n", depth, "");
+	  print_rexp (rx, node->params.pair.right, depth + 3, seprint, fp);
+	  print_rexp (rx, node->params.pair.left, depth + 3, seprint, fp);
+	  break;
+
+
+	case r_alternate:
+	case r_concat:
+	  fprintf (fp, "%*s%s\n", depth, "",
+		   node->type == r_alternate ? "alt" : "concat");
+	  print_rexp (rx, node->params.pair.left, depth + 3, seprint, fp);
+	  print_rexp (rx, node->params.pair.right, depth + 3, seprint, fp);
+	  break;
+	case r_side_effect:
+	  fprintf (fp, "%*sSide effect: ", depth, "");
+	  seprint (rx, node->params.side_effect, fp);
+	  fputc ('\n', fp);
+	}
+    }
+}
+
+#ifdef __STDC__
+static void
+print_nfa (struct rx * rx,
+	   struct rx_nfa_state * n,
+	   side_effect_printer seprint, FILE * fp)
+#else
+static void
+print_nfa (rx, n, seprint, fp)
+     struct rx * rx;
+     struct rx_nfa_state * n;
+     side_effect_printer seprint;
+     FILE * fp;
+#endif
+{
+  while (n)
+    {
+      struct rx_nfa_edge *e = n->edges;
+      struct rx_possible_future *ec = n->futures;
+      fprintf (fp, "node %d %s\n", n->id,
+	       n->is_final ? "final" : (n->is_start ? "start" : ""));
+      while (e)
+	{
+	  fprintf (fp, "   edge to %d, ", e->dest->id);
+	  switch (e->type)
+	    {
+	    case ne_epsilon:
+	      fprintf (fp, "epsilon\n");
+	      break;
+	    case ne_side_effect:
+	      fprintf (fp, "side effect ");
+	      seprint (rx, e->params.side_effect, fp);
+	      fputc ('\n', fp);
+	      break;
+	    case ne_cset:
+	      fprintf (fp, "cset ");
+	      print_cset (rx, e->params.cset, fp);
+	      fputc ('\n', fp);
+	      break;
+	    }
+	  e = e->next;
+	}
+
+      while (ec)
+	{
+	  int x;
+	  struct rx_nfa_state_set * s;
+	  struct rx_se_list * l;
+	  fprintf (fp, "   eclosure to {");
+	  for (s = ec->destset; s; s = s->cdr)
+	    fprintf (fp, "%d ", s->car->id);
+	  fprintf (fp, "} (");
+	  for (l = ec->effects; l; l = l->cdr)
+	    {
+	      seprint (rx, l->car, fp);
+	      fputc (' ', fp);
+	    }
+	  fprintf (fp, ")\n");
+	  ec = ec->next;
+	}
+      n = n->next;
+    }
+}
+
+static char * efnames [] =
+{
+  "bogon",
+  "re_se_try",
+  "re_se_pushback",
+  "re_se_push0",
+  "re_se_pushpos",
+  "re_se_chkpos",
+  "re_se_poppos",
+  "re_se_at_dot",
+  "re_se_syntax",
+  "re_se_not_syntax",
+  "re_se_begbuf",
+  "re_se_hat",
+  "re_se_wordbeg",
+  "re_se_wordbound",
+  "re_se_notwordbound",
+  "re_se_wordend",
+  "re_se_endbuf",
+  "re_se_dollar",
+  "re_se_fail",
+};
+
+static char * efnames2[] =
+{
+  "re_se_win",
+  "re_se_lparen",
+  "re_se_rparen",
+  "re_se_backref",
+  "re_se_iter",
+  "re_se_end_iter",
+  "re_se_tv"
+};
+
+static char * inx_names[] = 
+{
+  "rx_backtrack_point",
+  "rx_do_side_effects",
+  "rx_cache_miss",
+  "rx_next_char",
+  "rx_backtrack",
+  "rx_error_inx",
+  "rx_num_instructions"
+};
+
+
+#ifdef __STDC__
+static void
+re_seprint (struct rx * rx, void * effect, FILE * fp)
+#else
+static void
+re_seprint (rx, effect, fp)
+     struct rx * rx;
+     void * effect;
+     FILE * fp;
+#endif
+{
+  if ((int)effect < 0)
+    fputs (efnames[-(int)effect], fp);
+  else if (dbug_rxb)
+    {
+      struct re_se_params * p = &dbug_rxb->se_params[(int)effect];
+      fprintf (fp, "%s(%d,%d)", efnames2[p->se], p->op1, p->op2);
+    }
+  else
+    fprintf (fp, "[complex op # %d]", (int)effect);
+}
+
+
+/* These are so the regex.c regression tests will compile. */
+void
+print_compiled_pattern (rxb)
+     struct re_pattern_buffer * rxb;
+{
+}
+
+void
+print_fastmap (fm)
+     char * fm;
+{
+}
+
+#endif /* RX_DEBUG */
+
+
+
+
+/* This page: Bitsets.  Completely unintersting. */
+
+#ifdef __STDC__
+RX_DECL int
+rx_bitset_is_equal (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL int
+rx_bitset_is_equal (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  RX_subset s = b[0];
+  b[0] = ~a[0];
+
+  for (x = rx_bitset_numb_subsets(size) - 1; a[x] == b[x]; --x)
+    ;
+
+  b[0] = s;
+  return !x && s == a[0];
+}
+
+#ifdef __STDC__
+RX_DECL int
+rx_bitset_is_subset (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL int
+rx_bitset_is_subset (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x = rx_bitset_numb_subsets(size) - 1;
+  while (x-- && (a[x] & b[x]) == a[x]);
+  return x == -1;
+}
+
+
+#ifdef __STDC__
+RX_DECL int
+rx_bitset_empty (int size, rx_Bitset set)
+#else
+RX_DECL int
+rx_bitset_empty (size, set)
+     int size;
+     rx_Bitset set;
+#endif
+{
+  int x;
+  RX_subset s = set[0];
+  set[0] = 1;
+  for (x = rx_bitset_numb_subsets(size) - 1; !set[x]; --x)
+    ;
+  set[0] = s;
+  return !s;
+}
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_null (int size, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_null (size, b)
+     int size;
+     rx_Bitset b;
+#endif
+{
+  bzero (b, rx_sizeof_bitset(size));
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_universe (int size, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_universe (size, b)
+     int size;
+     rx_Bitset b;
+#endif
+{
+  int x = rx_bitset_numb_subsets (size);
+  while (x--)
+    *b++ = ~(RX_subset)0;
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_complement (int size, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_complement (size, b)
+     int size;
+     rx_Bitset b;
+#endif
+{
+  int x = rx_bitset_numb_subsets (size);
+  while (x--)
+    {
+      *b = ~*b;
+      ++b;
+    }
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_assign (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_assign (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] = b[x];
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_union (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_union (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] |= b[x];
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_intersection (int size,
+			rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_intersection (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] &= b[x];
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_difference (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_difference (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] &=  ~ b[x];
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_revdifference (int size,
+			 rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_revdifference (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] = ~a[x] & b[x];
+}
+
+#ifdef __STDC__
+RX_DECL void
+rx_bitset_xor (int size, rx_Bitset a, rx_Bitset b)
+#else
+RX_DECL void
+rx_bitset_xor (size, a, b)
+     int size;
+     rx_Bitset a;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  for (x = rx_bitset_numb_subsets(size) - 1; x >=0; --x)
+    a[x] ^= b[x];
+}
+
+
+#ifdef __STDC__
+RX_DECL unsigned long
+rx_bitset_hash (int size, rx_Bitset b)
+#else
+RX_DECL unsigned long
+rx_bitset_hash (size, b)
+     int size;
+     rx_Bitset b;
+#endif
+{
+  int x;
+  unsigned long hash = (unsigned long)rx_bitset_hash;
+
+  for (x = rx_bitset_numb_subsets(size) - 1; x >= 0; --x)
+    hash ^= rx_bitset_subset_val(b, x);
+
+  return hash;
+}
+
+
+RX_DECL RX_subset rx_subset_singletons [RX_subset_bits] = 
+{
+  0x1,
+  0x2,
+  0x4,
+  0x8,
+  0x10,
+  0x20,
+  0x40,
+  0x80,
+  0x100,
+  0x200,
+  0x400,
+  0x800,
+  0x1000,
+  0x2000,
+  0x4000,
+  0x8000,
+  0x10000,
+  0x20000,
+  0x40000,
+  0x80000,
+  0x100000,
+  0x200000,
+  0x400000,
+  0x800000,
+  0x1000000,
+  0x2000000,
+  0x4000000,
+  0x8000000,
+  0x10000000,
+  0x20000000,
+  0x40000000,
+  0x80000000
+};
+
+#ifdef RX_DEBUG
+
+#ifdef __STDC__
+static void
+print_cset (struct rx *rx, rx_Bitset cset, FILE * fp)
+#else
+static void
+print_cset (rx, cset, fp)
+     struct rx *rx;
+     rx_Bitset cset;
+     FILE * fp;
+#endif
+{
+  int x;
+  fputc ('[', fp);
+  for (x = 0; x < rx->local_cset_size; ++x)
+    if (RX_bitset_member (cset, x))
+      {
+	if (isprint(x))
+	  fputc (x, fp);
+	else
+	  fprintf (fp, "\\0%o ", x);
+      }
+  fputc (']', fp);
+}
+
+#endif /*  RX_DEBUG */
+
+
+
+
+static unsigned long rx_hash_masks[4] =
+{
+  0x12488421,
+  0x96699669,
+  0xbe7dd7eb,
+  0xffffffff
+};
+
+
+/* Hash tables */
+#ifdef __STDC__
+RX_DECL struct rx_hash_item * 
+rx_hash_find (struct rx_hash * table,
+	      unsigned long hash,
+	      void * value,
+	      struct rx_hash_rules * rules)
+#else
+RX_DECL struct rx_hash_item * 
+rx_hash_find (table, hash, value, rules)
+     struct rx_hash * table;
+     unsigned long hash;
+     void * value;
+     struct rx_hash_rules * rules;
+#endif
+{
+  rx_hash_eq eq = rules->eq;
+  int maskc = 0;
+  long mask = rx_hash_masks [0];
+  int bucket = (hash & mask) % 13;
+
+  while (table->children [bucket])
+    {
+      table = table->children [bucket];
+      ++maskc;
+      mask = rx_hash_masks[maskc];
+      bucket = (hash & mask) % 13;
+    }
+
+  {
+    struct rx_hash_item * it = table->buckets[bucket];
+    while (it)
+      if (eq (it->data, value))
+	return it;
+      else
+	it = it->next_same_hash;
+  }
+
+  return 0;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rx_hash_item *
+rx_hash_store (struct rx_hash * table,
+	       unsigned long hash,
+	       void * value,
+	       struct rx_hash_rules * rules)
+#else
+RX_DECL struct rx_hash_item *
+rx_hash_store (table, hash, value, rules)
+     struct rx_hash * table;
+     unsigned long hash;
+     void * value;
+     struct rx_hash_rules * rules;
+#endif
+{
+  rx_hash_eq eq = rules->eq;
+  int maskc = 0;
+  long mask = rx_hash_masks[0];
+  int bucket = (hash & mask) % 13;
+  int depth = 0;
+  
+  while (table->children [bucket])
+    {
+      table = table->children [bucket];
+      ++maskc;
+      mask = rx_hash_masks[maskc];
+      bucket = (hash & mask) % 13;
+      ++depth;
+    }
+  
+  {
+    struct rx_hash_item * it = table->buckets[bucket];
+    while (it)
+      if (eq (it->data, value))
+	return it;
+      else
+	it = it->next_same_hash;
+  }
+  
+  {
+    if (   (depth < 3)
+	&& (table->bucket_size [bucket] >= 4))
+      {
+	struct rx_hash * newtab = ((struct rx_hash *)
+				   rules->hash_alloc (rules));
+	if (!newtab)
+	  goto add_to_bucket;
+	bzero (newtab, sizeof (*newtab));
+	newtab->parent = table;
+	{
+	  struct rx_hash_item * them = table->buckets[bucket];
+	  unsigned long newmask = rx_hash_masks[maskc + 1];
+	  while (them)
+	    {
+	      struct rx_hash_item * save = them->next_same_hash;
+	      int new_buck = (them->hash & newmask) % 13;
+	      them->next_same_hash = newtab->buckets[new_buck];
+	      newtab->buckets[new_buck] = them;
+	      them->table = newtab;
+	      them = save;
+	      ++newtab->bucket_size[new_buck];
+	      ++newtab->refs;
+	    }
+	  table->refs = (table->refs - table->bucket_size[bucket] + 1);
+	  table->bucket_size[bucket] = 0;
+	  table->buckets[bucket] = 0;
+	  table->children[bucket] = newtab;
+	  table = newtab;
+	  bucket = (hash & newmask) % 13;
+	}
+      }
+  }
+ add_to_bucket:
+  {
+    struct rx_hash_item  * it = ((struct rx_hash_item *)
+				 rules->hash_item_alloc (rules, value));
+    if (!it)
+      return 0;
+    it->hash = hash;
+    it->table = table;
+    /* DATA and BINDING are to be set in hash_item_alloc */
+    it->next_same_hash = table->buckets [bucket];
+    table->buckets[bucket] = it;
+    ++table->bucket_size [bucket];
+    ++table->refs;
+    return it;
+  }
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_hash_free (struct rx_hash_item * it, struct rx_hash_rules * rules)
+#else
+RX_DECL void
+rx_hash_free (it, rules)
+     struct rx_hash_item * it;
+     struct rx_hash_rules * rules;
+#endif
+{
+  if (it)
+    {
+      struct rx_hash * table = it->table;
+      unsigned long hash = it->hash;
+      int depth = (table->parent
+		   ? (table->parent->parent
+		      ? (table->parent->parent->parent
+			 ? 3
+			 : 2)
+		      : 1)
+		   : 0);
+      int bucket = (hash & rx_hash_masks [depth]) % 13;
+      struct rx_hash_item ** pos = &table->buckets [bucket];
+      
+      while (*pos != it)
+	pos = &(*pos)->next_same_hash;
+      *pos = it->next_same_hash;
+      rules->free_hash_item (it, rules);
+      --table->bucket_size[bucket];
+      --table->refs;
+      while (!table->refs && depth)
+	{
+	  struct rx_hash * save = table;
+	  table = table->parent;
+	  --depth;
+	  bucket = (hash & rx_hash_masks [depth]) % 13;
+	  --table->refs;
+	  table->children[bucket] = 0;
+	  rules->free_hash (save, rules);
+	}
+    }
+}
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_hash_table (struct rx_hash * tab, rx_hash_freefn freefn,
+		    struct rx_hash_rules * rules)
+#else
+RX_DECL void
+rx_free_hash_table (tab, freefn, rules)
+     struct rx_hash * tab;
+     rx_hash_freefn freefn;
+     struct rx_hash_rules * rules;
+#endif
+{
+  int x;
+
+  for (x = 0; x < 13; ++x)
+    if (tab->children[x])
+      {
+	rx_free_hash_table (tab->children[x], freefn, rules);
+	rules->free_hash (tab->children[x], rules);
+      }
+    else
+      {
+	struct rx_hash_item * them = tab->buckets[x];
+	while (them)
+	  {
+	    struct rx_hash_item * that = them;
+	    them = that->next_same_hash;
+	    freefn (that);
+	    rules->free_hash_item (that, rules);
+	  }
+      }
+}
+
+
+
+
+/* Utilities for manipulating bitset represntations of characters sets. */
+
+#ifdef __STDC__
+RX_DECL rx_Bitset
+rx_cset (struct rx *rx)
+#else
+RX_DECL rx_Bitset
+rx_cset (rx)
+     struct rx *rx;
+#endif
+{
+  rx_Bitset b = (rx_Bitset) malloc (rx_sizeof_bitset (rx->local_cset_size));
+  if (b)
+    rx_bitset_null (rx->local_cset_size, b);
+  return b;
+}
+
+
+#ifdef __STDC__
+RX_DECL rx_Bitset
+rx_copy_cset (struct rx *rx, rx_Bitset a)
+#else
+RX_DECL rx_Bitset
+rx_copy_cset (rx, a)
+     struct rx *rx;
+     rx_Bitset a;
+#endif
+{
+  rx_Bitset cs = rx_cset (rx);
+
+  if (cs)
+    rx_bitset_union (rx->local_cset_size, cs, a);
+
+  return cs;
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_cset (struct rx * rx, rx_Bitset c)
+#else
+RX_DECL void
+rx_free_cset (rx, c)
+     struct rx * rx;
+     rx_Bitset c;
+#endif
+{
+  if (c)
+    free ((char *)c);
+}
+
+
+
+/* Hash table memory allocation policy for the regexp compiler */
+
+#ifdef __STDC__
+static struct rx_hash *
+compiler_hash_alloc (struct rx_hash_rules * rules)
+#else
+static struct rx_hash *
+compiler_hash_alloc (rules)
+     struct rx_hash_rules * rules;
+#endif
+{
+  return (struct rx_hash *)malloc (sizeof (struct rx_hash));
+}
+
+
+#ifdef __STDC__
+static struct rx_hash_item *
+compiler_hash_item_alloc (struct rx_hash_rules * rules, void * value)
+#else
+static struct rx_hash_item *
+compiler_hash_item_alloc (rules, value)
+     struct rx_hash_rules * rules;
+     void * value;
+#endif
+{
+  struct rx_hash_item * it;
+  it = (struct rx_hash_item *)malloc (sizeof (*it));
+  if (it)
+    {
+      it->data = value;
+      it->binding = 0;
+    }
+  return it;
+}
+
+
+#ifdef __STDC__
+static void
+compiler_free_hash (struct rx_hash * tab,
+		    struct rx_hash_rules * rules)
+#else
+static void
+compiler_free_hash (tab, rules)
+     struct rx_hash * tab;
+     struct rx_hash_rules * rules;
+#endif
+{
+  free ((char *)tab);
+}
+
+
+#ifdef __STDC__
+static void
+compiler_free_hash_item (struct rx_hash_item * item,
+			 struct rx_hash_rules * rules)
+#else
+static void
+compiler_free_hash_item (item, rules)
+     struct rx_hash_item * item;
+     struct rx_hash_rules * rules;
+#endif
+{
+  free ((char *)item);
+}
+
+
+
+/* This page: REXP_NODE (expression tree) structures. */
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rexp_node (struct rx *rx,
+	   enum rexp_node_type type)
+#else
+RX_DECL struct rexp_node *
+rexp_node (rx, type)
+     struct rx *rx;
+     enum rexp_node_type type;
+#endif
+{
+  struct rexp_node *n;
+
+  n = (struct rexp_node *)malloc (sizeof (*n));
+  bzero (n, sizeof (*n));
+  if (n)
+    n->type = type;
+  return n;
+}
+
+
+/* free_rexp_node assumes that the bitset passed to rx_mk_r_cset
+ * can be freed using rx_free_cset.
+ */
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_cset (struct rx * rx,
+	      rx_Bitset b)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_cset (rx, b)
+     struct rx * rx;
+     rx_Bitset b;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_cset);
+  if (n)
+    n->params.cset = b;
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_concat (struct rx * rx,
+		struct rexp_node * a,
+		struct rexp_node * b)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_concat (rx, a, b)
+     struct rx * rx;
+     struct rexp_node * a;
+     struct rexp_node * b;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_concat);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = b;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_alternate (struct rx * rx,
+		   struct rexp_node * a,
+		   struct rexp_node * b)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_alternate (rx, a, b)
+     struct rx * rx;
+     struct rexp_node * a;
+     struct rexp_node * b;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_alternate);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = b;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_opt (struct rx * rx,
+	     struct rexp_node * a)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_opt (rx, a)
+     struct rx * rx;
+     struct rexp_node * a;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_opt);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = 0;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_star (struct rx * rx,
+	      struct rexp_node * a)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_star (rx, a)
+     struct rx * rx;
+     struct rexp_node * a;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_star);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = 0;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_2phase_star (struct rx * rx,
+		     struct rexp_node * a,
+		     struct rexp_node * b)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_2phase_star (rx, a, b)
+     struct rx * rx;
+     struct rexp_node * a;
+     struct rexp_node * b;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_2phase_star);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = b;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_side_effect (struct rx * rx,
+		     rx_side_effect a)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_side_effect (rx, a)
+     struct rx * rx;
+     rx_side_effect a;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_side_effect);
+  if (n)
+    {
+      n->params.side_effect = a;
+      n->params.pair.right = 0;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node *
+rx_mk_r_data  (struct rx * rx,
+	       void * a)
+#else
+RX_DECL struct rexp_node *
+rx_mk_r_data  (rx, a)
+     struct rx * rx;
+     void * a;
+#endif
+{
+  struct rexp_node * n = rexp_node (rx, r_data);
+  if (n)
+    {
+      n->params.pair.left = a;
+      n->params.pair.right = 0;
+    }
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_rexp (struct rx * rx, struct rexp_node * node)
+#else
+RX_DECL void
+rx_free_rexp (rx, node)
+     struct rx * rx;
+     struct rexp_node * node;
+#endif
+{
+  if (node)
+    {
+      switch (node->type)
+	{
+	case r_cset:
+	  if (node->params.cset)
+	    rx_free_cset (rx, node->params.cset);
+
+	case r_side_effect:
+	  break;
+	  
+	case r_concat:
+	case r_alternate:
+	case r_2phase_star:
+	case r_opt:
+	case r_star:
+	  rx_free_rexp (rx, node->params.pair.left);
+	  rx_free_rexp (rx, node->params.pair.right);
+	  break;
+
+	case r_data:
+	  /* This shouldn't occur. */
+	  break;
+	}
+      free ((char *)node);
+    }
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rexp_node * 
+rx_copy_rexp (struct rx *rx,
+	   struct rexp_node *node)
+#else
+RX_DECL struct rexp_node * 
+rx_copy_rexp (rx, node)
+     struct rx *rx;
+     struct rexp_node *node;
+#endif
+{
+  if (!node)
+    return 0;
+  else
+    {
+      struct rexp_node *n = rexp_node (rx, node->type);
+      if (!n)
+	return 0;
+      switch (node->type)
+	{
+	case r_cset:
+	  n->params.cset = rx_copy_cset (rx, node->params.cset);
+	  if (!n->params.cset)
+	    {
+	      rx_free_rexp (rx, n);
+	      return 0;
+	    }
+	  break;
+
+	case r_side_effect:
+	  n->params.side_effect = node->params.side_effect;
+	  break;
+
+	case r_concat:
+	case r_alternate:
+	case r_opt:
+	case r_2phase_star:
+	case r_star:
+	  n->params.pair.left =
+	    rx_copy_rexp (rx, node->params.pair.left);
+	  n->params.pair.right =
+	    rx_copy_rexp (rx, node->params.pair.right);
+	  if (   (node->params.pair.left && !n->params.pair.left)
+	      || (node->params.pair.right && !n->params.pair.right))
+	    {
+	      rx_free_rexp  (rx, n);
+	      return 0;
+	    }
+	  break;
+	case r_data:
+	  /* shouldn't happen */
+	  break;
+	}
+      return n;
+    }
+}
+
+
+
+
+/* This page: functions to build and destroy graphs that describe nfa's */
+
+/* Constructs a new nfa node. */
+#ifdef __STDC__
+RX_DECL struct rx_nfa_state *
+rx_nfa_state (struct rx *rx)
+#else
+RX_DECL struct rx_nfa_state *
+rx_nfa_state (rx)
+     struct rx *rx;
+#endif
+{
+  struct rx_nfa_state * n = (struct rx_nfa_state *)malloc (sizeof (*n));
+  if (!n)
+    return 0;
+  bzero (n, sizeof (*n));
+  n->next = rx->nfa_states;
+  rx->nfa_states = n;
+  return n;
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_nfa_state (struct rx_nfa_state * n)
+#else
+RX_DECL void
+rx_free_nfa_state (n)
+  struct rx_nfa_state * n;
+#endif
+{
+  free ((char *)n);
+}
+
+
+/* This looks up an nfa node, given a numeric id.  Numeric id's are
+ * assigned after the nfa has been built.
+ */
+#ifdef __STDC__
+RX_DECL struct rx_nfa_state * 
+rx_id_to_nfa_state (struct rx * rx,
+		    int id)
+#else
+RX_DECL struct rx_nfa_state * 
+rx_id_to_nfa_state (rx, id)
+     struct rx * rx;
+     int id;
+#endif
+{
+  struct rx_nfa_state * n;
+  for (n = rx->nfa_states; n; n = n->next)
+    if (n->id == id)
+      return n;
+  return 0;
+}
+
+
+/* This adds an edge between two nodes, but doesn't initialize the 
+ * edge label.
+ */
+
+#ifdef __STDC__
+RX_DECL struct rx_nfa_edge * 
+rx_nfa_edge (struct rx *rx,
+	     enum rx_nfa_etype type,
+	     struct rx_nfa_state *start,
+	     struct rx_nfa_state *dest)
+#else
+RX_DECL struct rx_nfa_edge * 
+rx_nfa_edge (rx, type, start, dest)
+     struct rx *rx;
+     enum rx_nfa_etype type;
+     struct rx_nfa_state *start;
+     struct rx_nfa_state *dest;
+#endif
+{
+  struct rx_nfa_edge *e;
+  e = (struct rx_nfa_edge *)malloc (sizeof (*e));
+  if (!e)
+    return 0;
+  e->next = start->edges;
+  start->edges = e;
+  e->type = type;
+  e->dest = dest;
+  return e;
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_nfa_edge (struct rx_nfa_edge * e)
+#else
+RX_DECL void
+rx_free_nfa_edge (e)
+     struct rx_nfa_edge * e;
+#endif
+{
+  free ((char *)e);
+}
+
+
+/* This constructs a POSSIBLE_FUTURE, which is a kind epsilon-closure
+ * of an NFA.  These are added to an nfa automaticly by eclose_nfa.
+ */  
+
+#ifdef __STDC__
+static struct rx_possible_future * 
+rx_possible_future (struct rx * rx,
+		 struct rx_se_list * effects)
+#else
+static struct rx_possible_future * 
+rx_possible_future (rx, effects)
+     struct rx * rx;
+     struct rx_se_list * effects;
+#endif
+{
+  struct rx_possible_future *ec;
+  ec = (struct rx_possible_future *) malloc (sizeof (*ec));
+  if (!ec)
+    return 0;
+  ec->destset = 0;
+  ec->next = 0;
+  ec->effects = effects;
+  return ec;
+}
+
+
+#ifdef __STDC__
+static void
+rx_free_possible_future (struct rx_possible_future * pf)
+#else
+static void
+rx_free_possible_future (pf)
+     struct rx_possible_future * pf;
+#endif
+{
+  free ((char *)pf);
+}
+
+
+#ifdef __STDC__
+RX_DECL void
+rx_free_nfa (struct rx *rx)
+#else
+RX_DECL void
+rx_free_nfa (rx)
+     struct rx *rx;
+#endif
+{
+  while (rx->nfa_states)
+    {
+      while (rx->nfa_states->edges)
+	{
+	  switch (rx->nfa_states->edges->type)
+	    {
+	    case ne_cset:
+	      rx_free_cset (rx, rx->nfa_states->edges->params.cset);
+	      break;
+	    default:
+	      break;
+	    }
+	  {
+	    struct rx_nfa_edge * e;
+	    e = rx->nfa_states->edges;
+	    rx->nfa_states->edges = rx->nfa_states->edges->next;
+	    rx_free_nfa_edge (e);
+	  }
+	} /* while (rx->nfa_states->edges) */
+      {
+	/* Iterate over the partial epsilon closures of rx->nfa_states */
+	struct rx_possible_future * pf = rx->nfa_states->futures;
+	while (pf)
+	  {
+	    struct rx_possible_future * pft = pf;
+	    pf = pf->next;
+	    rx_free_possible_future (pft);
+	  }
+      }
+      {
+	struct rx_nfa_state *n;
+	n = rx->nfa_states;
+	rx->nfa_states = rx->nfa_states->next;
+	rx_free_nfa_state (n);
+      }
+    }
+}
+
+
+
+
+/* This page: translating a pattern expression into an nfa and doing the 
+ * static part of the nfa->super-nfa translation.
+ */
+
+/* This is the thompson regexp->nfa algorithm. 
+ * It is modified to allow for `side-effect epsilons.'  Those are
+ * edges that are taken whenever a similar epsilon edge would be,
+ * but which imply that some side effect occurs when the edge 
+ * is taken.
+ *
+ * Side effects are used to model parts of the pattern langauge 
+ * that are not regular (in the formal sense).
+ */
+
+#ifdef __STDC__
+RX_DECL int
+rx_build_nfa (struct rx *rx,
+	      struct rexp_node *rexp,
+	      struct rx_nfa_state **start,
+	      struct rx_nfa_state **end)
+#else
+RX_DECL int
+rx_build_nfa (rx, rexp, start, end)
+     struct rx *rx;
+     struct rexp_node *rexp;
+     struct rx_nfa_state **start;
+     struct rx_nfa_state **end;
+#endif
+{
+  struct rx_nfa_edge *edge;
+
+  /* Start & end nodes may have been allocated by the caller. */
+  *start = *start ? *start : rx_nfa_state (rx);
+
+  if (!*start)
+    return 0;
+
+  if (!rexp)
+    {
+      *end = *start;
+      return 1;
+    }
+
+  *end = *end ? *end : rx_nfa_state (rx);
+
+  if (!*end)
+    {
+      rx_free_nfa_state (*start);
+      return 0;
+    }
+
+  switch (rexp->type)
+    {
+    case r_data:
+      return 0;
+
+    case r_cset:
+      edge = rx_nfa_edge (rx, ne_cset, *start, *end);
+      if (!edge)
+	return 0;
+      edge->params.cset = rx_copy_cset (rx, rexp->params.cset);
+      if (!edge->params.cset)
+	{
+	  rx_free_nfa_edge (edge);
+	  return 0;
+	}
+      return 1;
+ 
+    case r_opt:
+      return (rx_build_nfa (rx, rexp->params.pair.left, start, end)
+	      && rx_nfa_edge (rx, ne_epsilon, *start, *end));
+
+    case r_star:
+      {
+	struct rx_nfa_state * star_start = 0;
+	struct rx_nfa_state * star_end = 0;
+	return (rx_build_nfa (rx, rexp->params.pair.left,
+			      &star_start, &star_end)
+		&& star_start
+		&& star_end
+		&& rx_nfa_edge (rx, ne_epsilon, star_start, star_end)
+		&& rx_nfa_edge (rx, ne_epsilon, *start, star_start)
+		&& rx_nfa_edge (rx, ne_epsilon, star_end, *end)
+
+		&& rx_nfa_edge (rx, ne_epsilon, star_end, star_start));
+      }
+
+    case r_2phase_star:
+      {
+	struct rx_nfa_state * star_start = 0;
+	struct rx_nfa_state * star_end = 0;
+	struct rx_nfa_state * loop_exp_start = 0;
+	struct rx_nfa_state * loop_exp_end = 0;
+
+	return (rx_build_nfa (rx, rexp->params.pair.left,
+			      &star_start, &star_end)
+		&& rx_build_nfa (rx, rexp->params.pair.right,
+				 &loop_exp_start, &loop_exp_end)
+		&& star_start
+		&& star_end
+		&& loop_exp_end
+		&& loop_exp_start
+		&& rx_nfa_edge (rx, ne_epsilon, star_start, *end)
+		&& rx_nfa_edge (rx, ne_epsilon, *start, star_start)
+		&& rx_nfa_edge (rx, ne_epsilon, star_end, *end)
+
+		&& rx_nfa_edge (rx, ne_epsilon, star_end, loop_exp_start)
+		&& rx_nfa_edge (rx, ne_epsilon, loop_exp_end, star_start));
+      }
+
+
+    case r_concat:
+      {
+	struct rx_nfa_state *shared = 0;
+	return
+	  (rx_build_nfa (rx, rexp->params.pair.left, start, &shared)
+	   && rx_build_nfa (rx, rexp->params.pair.right, &shared, end));
+      }
+
+    case r_alternate:
+      {
+	struct rx_nfa_state *ls = 0;
+	struct rx_nfa_state *le = 0;
+	struct rx_nfa_state *rs = 0;
+	struct rx_nfa_state *re = 0;
+	return (rx_build_nfa (rx, rexp->params.pair.left, &ls, &le)
+		&& rx_build_nfa (rx, rexp->params.pair.right, &rs, &re)
+		&& rx_nfa_edge (rx, ne_epsilon, *start, ls)
+		&& rx_nfa_edge (rx, ne_epsilon, *start, rs)
+		&& rx_nfa_edge (rx, ne_epsilon, le, *end)
+		&& rx_nfa_edge (rx, ne_epsilon, re, *end));
+      }
+
+    case r_side_effect:
+      edge = rx_nfa_edge (rx, ne_side_effect, *start, *end);
+      if (!edge)
+	return 0;
+      edge->params.side_effect = rexp->params.side_effect;
+      return 1;
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+/* RX_NAME_NFA_STATES identifies all nodes with outgoing non-epsilon
+ * transitions.  Only these nodes can occur in super-states.  
+ * All nodes are given an integer id. 
+ * The id is non-negative if the node has non-epsilon out-transitions, negative
+ * otherwise (this is because we want the non-negative ids to be used as 
+ * array indexes in a few places).
+ */
+
+#ifdef __STDC__
+RX_DECL void
+rx_name_nfa_states (struct rx *rx)
+#else
+RX_DECL void
+rx_name_nfa_states (rx)
+     struct rx *rx;
+#endif
+{
+  struct rx_nfa_state *n = rx->nfa_states;
+
+  rx->nodec = 0;
+  rx->epsnodec = -1;
+
+  while (n)
+    {
+      struct rx_nfa_edge *e = n->edges;
+
+      if (n->is_start)
+	n->eclosure_needed = 1;
+
+      while (e)
+	{
+	  switch (e->type)
+	    {
+	    case ne_epsilon:
+	    case ne_side_effect:
+	      break;
+
+	    case ne_cset:
+	      n->id = rx->nodec++;
+	      {
+		struct rx_nfa_edge *from_n = n->edges;
+		while (from_n)
+		  {
+		    from_n->dest->eclosure_needed = 1;
+		    from_n = from_n->next;
+		  }
+	      }
+	      goto cont;
+	    }
+	  e = e->next;
+	}
+      n->id = rx->epsnodec--;
+    cont:
+      n = n->next;
+    }
+  rx->epsnodec = -rx->epsnodec;
+}
+
+
+
+/* This page: data structures for the static part of the nfa->supernfa
+ * translation.
+ *
+ * There are side effect lists -- lists of side effects occuring
+ * along an uninterrupted, acyclic path of side-effect epsilon edges.
+ * Such paths are collapsed to single edges in the course of computing
+ * epsilon closures.  Such single edges are labled with a list of all
+ * the side effects entailed in crossing them.  Like lists of side
+ * effects are made == by the constructors below.
+ *
+ * There are also nfa state sets.  These are used to hold a list of all
+ * states reachable from a starting state for a given type of transition
+ * and side effect list.   These are also hash-consed.
+ */
+
+/* The next several functions compare, construct, etc. lists of side
+ * effects.  See ECLOSE_NFA (below) for details.
+ */
+
+/* Ordering of rx_se_list
+ * (-1, 0, 1 return value convention).
+ */
+
+#ifdef __STDC__
+static int 
+se_list_cmp (void * va, void * vb)
+#else
+static int 
+se_list_cmp (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  struct rx_se_list * a = (struct rx_se_list *)va;
+  struct rx_se_list * b = (struct rx_se_list *)vb;
+
+  return ((va == vb)
+	  ? 0
+	  : (!va
+	     ? -1
+	     : (!vb
+		? 1
+		: ((long)a->car < (long)b->car
+		   ? 1
+		   : ((long)a->car > (long)b->car
+		      ? -1
+		      : se_list_cmp ((void *)a->cdr, (void *)b->cdr))))));
+}
+
+
+#ifdef __STDC__
+static int 
+se_list_equal (void * va, void * vb)
+#else
+static int 
+se_list_equal (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  return !(se_list_cmp (va, vb));
+}
+
+static struct rx_hash_rules se_list_hash_rules =
+{
+  se_list_equal,
+  compiler_hash_alloc,
+  compiler_free_hash,
+  compiler_hash_item_alloc,
+  compiler_free_hash_item
+};
+
+
+#ifdef __STDC__
+static struct rx_se_list * 
+side_effect_cons (struct rx * rx,
+		  void * se, struct rx_se_list * list)
+#else
+static struct rx_se_list * 
+side_effect_cons (rx, se, list)
+     struct rx * rx;
+     void * se;
+     struct rx_se_list * list;
+#endif
+{
+  struct rx_se_list * l;
+  l = ((struct rx_se_list *) malloc (sizeof (*l)));
+  if (!l)
+    return 0;
+  l->car = se;
+  l->cdr = list;
+  return l;
+}
+
+
+#ifdef __STDC__
+static struct rx_se_list *
+hash_cons_se_prog (struct rx * rx,
+		   struct rx_hash * memo,
+		   void * car, struct rx_se_list * cdr)
+#else
+static struct rx_se_list *
+hash_cons_se_prog (rx, memo, car, cdr)
+     struct rx * rx;
+     struct rx_hash * memo;
+     void * car;
+     struct rx_se_list * cdr;
+#endif
+{
+  long hash = (long)car ^ (long)cdr;
+  struct rx_se_list template;
+
+  template.car = car;
+  template.cdr = cdr;
+  {
+    struct rx_hash_item * it = rx_hash_store (memo, hash,
+					      (void *)&template,
+					      &se_list_hash_rules);
+    if (!it)
+      return 0;
+    if (it->data == (void *)&template)
+      {
+	struct rx_se_list * consed;
+	consed = (struct rx_se_list *) malloc (sizeof (*consed));
+	*consed = template;
+	it->data = (void *)consed;
+      }
+    return (struct rx_se_list *)it->data;
+  }
+}
+     
+
+#ifdef __STDC__
+static struct rx_se_list *
+hash_se_prog (struct rx * rx, struct rx_hash * memo, struct rx_se_list * prog)
+#else
+static struct rx_se_list *
+hash_se_prog (rx, memo, prog)
+     struct rx * rx;
+     struct rx_hash * memo;
+     struct rx_se_list * prog;
+#endif
+{
+  struct rx_se_list * answer = 0;
+  while (prog)
+    {
+      answer = hash_cons_se_prog (rx, memo, prog->car, answer);
+      if (!answer)
+	return 0;
+      prog = prog->cdr;
+    }
+  return answer;
+}
+
+#ifdef __STDC__
+static int 
+nfa_set_cmp (void * va, void * vb)
+#else
+static int 
+nfa_set_cmp (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  struct rx_nfa_state_set * a = (struct rx_nfa_state_set *)va;
+  struct rx_nfa_state_set * b = (struct rx_nfa_state_set *)vb;
+
+  return ((va == vb)
+	  ? 0
+	  : (!va
+	     ? -1
+	     : (!vb
+		? 1
+		: (a->car->id < b->car->id
+		   ? 1
+		   : (a->car->id > b->car->id
+		      ? -1
+		      : nfa_set_cmp ((void *)a->cdr, (void *)b->cdr))))));
+}
+
+#ifdef __STDC__
+static int 
+nfa_set_equal (void * va, void * vb)
+#else
+static int 
+nfa_set_equal (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  return !nfa_set_cmp (va, vb);
+}
+
+static struct rx_hash_rules nfa_set_hash_rules =
+{
+  nfa_set_equal,
+  compiler_hash_alloc,
+  compiler_free_hash,
+  compiler_hash_item_alloc,
+  compiler_free_hash_item
+};
+
+
+#ifdef __STDC__
+static struct rx_nfa_state_set * 
+nfa_set_cons (struct rx * rx,
+	      struct rx_hash * memo, struct rx_nfa_state * state,
+	      struct rx_nfa_state_set * set)
+#else
+static struct rx_nfa_state_set * 
+nfa_set_cons (rx, memo, state, set)
+     struct rx * rx;
+     struct rx_hash * memo;
+     struct rx_nfa_state * state;
+     struct rx_nfa_state_set * set;
+#endif
+{
+  struct rx_nfa_state_set template;
+  struct rx_hash_item * node;
+  template.car = state;
+  template.cdr = set;
+  node = rx_hash_store (memo,
+			(((long)state) >> 8) ^ (long)set,
+			&template, &nfa_set_hash_rules);
+  if (!node)
+    return 0;
+  if (node->data == &template)
+    {
+      struct rx_nfa_state_set * l;
+      l = (struct rx_nfa_state_set *) malloc (sizeof (*l));
+      node->data = (void *) l;
+      if (!l)
+	return 0;
+      *l = template;
+    }
+  return (struct rx_nfa_state_set *)node->data;
+}
+
+
+#ifdef __STDC__
+static struct rx_nfa_state_set * 
+nfa_set_enjoin (struct rx * rx,
+		struct rx_hash * memo, struct rx_nfa_state * state,
+		struct rx_nfa_state_set * set)
+#else
+static struct rx_nfa_state_set * 
+nfa_set_enjoin (rx, memo, state, set)
+     struct rx * rx;
+     struct rx_hash * memo;
+     struct rx_nfa_state * state;
+     struct rx_nfa_state_set * set;
+#endif
+{
+  if (!set || state->id < set->car->id)
+    return nfa_set_cons (rx, memo, state, set);
+  if (state->id == set->car->id)
+    return set;
+  else
+    {
+      struct rx_nfa_state_set * newcdr
+	= nfa_set_enjoin (rx, memo, state, set->cdr);
+      if (newcdr != set->cdr)
+	set = nfa_set_cons (rx, memo, set->car, newcdr);
+      return set;
+    }
+}
+
+
+
+
+/* This page: computing epsilon closures.  The closures aren't total.
+ * Each node's closures are partitioned according to the side effects entailed
+ * along the epsilon edges.  Return true on success.
+ */ 
+
+struct eclose_frame
+{
+  struct rx_se_list *prog_backwards;
+};
+
+
+#ifdef __STDC__
+static int 
+eclose_node (struct rx *rx, struct rx_nfa_state *outnode,
+	     struct rx_nfa_state *node, struct eclose_frame *frame)
+#else
+static int 
+eclose_node (rx, outnode, node, frame)
+     struct rx *rx;
+     struct rx_nfa_state *outnode;
+     struct rx_nfa_state *node;
+     struct eclose_frame *frame;
+#endif
+{
+  struct rx_nfa_edge *e = node->edges;
+
+  /* For each node, we follow all epsilon paths to build the closure.
+   * The closure omits nodes that have only epsilon edges.
+   * The closure is split into partial closures -- all the states in
+   * a partial closure are reached by crossing the same list of
+   * of side effects (though not necessarily the same path).
+   */
+  if (node->mark)
+    return 1;
+  node->mark = 1;
+
+  if (node->id >= 0 || node->is_final)
+    {
+      struct rx_possible_future **ec;
+      struct rx_se_list * prog_in_order
+	= ((struct rx_se_list *)hash_se_prog (rx,
+					      &rx->se_list_memo,
+					      frame->prog_backwards));
+      int cmp;
+
+      ec = &outnode->futures;
+
+      while (*ec)
+	{
+	  cmp = se_list_cmp ((void *)(*ec)->effects, (void *)prog_in_order);
+	  if (cmp <= 0)
+	    break;
+	  ec = &(*ec)->next;
+	}
+      if (!*ec || (cmp < 0))
+	{
+	  struct rx_possible_future * saved = *ec;
+	  *ec = rx_possible_future (rx, prog_in_order);
+	  (*ec)->next = saved;
+	  if (!*ec)
+	    return 0;
+	}
+      if (node->id >= 0)
+	{
+	  (*ec)->destset = nfa_set_enjoin (rx, &rx->set_list_memo,
+					   node, (*ec)->destset);
+	  if (!(*ec)->destset)
+	    return 0;
+	}
+    }
+
+  while (e)
+    {
+      switch (e->type)
+	{
+	case ne_epsilon:
+	  if (!eclose_node (rx, outnode, e->dest, frame))
+	    return 0;
+	  break;
+	case ne_side_effect:
+	  {
+	    frame->prog_backwards = side_effect_cons (rx, 
+						      e->params.side_effect,
+						      frame->prog_backwards);
+	    if (!frame->prog_backwards)
+	      return 0;
+	    if (!eclose_node (rx, outnode, e->dest, frame))
+	      return 0;
+	    {
+	      struct rx_se_list * dying = frame->prog_backwards;
+	      frame->prog_backwards = frame->prog_backwards->cdr;
+	      free ((char *)dying);
+	    }
+	    break;
+	  }
+	default:
+	  break;
+	}
+      e = e->next;
+    }
+  node->mark = 0;
+  return 1;
+}
+
+
+#ifdef __STDC__
+RX_DECL int 
+rx_eclose_nfa (struct rx *rx)
+#else
+RX_DECL int 
+rx_eclose_nfa (rx)
+     struct rx *rx;
+#endif
+{
+  struct rx_nfa_state *n = rx->nfa_states;
+  struct eclose_frame frame;
+  static int rx_id = 0;
+  
+  frame.prog_backwards = 0;
+  rx->rx_id = rx_id++;
+  bzero (&rx->se_list_memo, sizeof (rx->se_list_memo));
+  bzero (&rx->set_list_memo, sizeof (rx->set_list_memo));
+  while (n)
+    {
+      n->futures = 0;
+      if (n->eclosure_needed && !eclose_node (rx, n, n, &frame))
+	return 0;
+      /* clear_marks (rx); */
+      n = n->next;
+    }
+  return 1;
+}
+
+
+/* This deletes epsilon edges from an NFA.  After running eclose_node,
+ * we have no more need for these edges.  They are removed to simplify
+ * further operations on the NFA.
+ */
+
+#ifdef __STDC__
+RX_DECL void 
+rx_delete_epsilon_transitions (struct rx *rx)
+#else
+RX_DECL void 
+rx_delete_epsilon_transitions (rx)
+     struct rx *rx;
+#endif
+{
+  struct rx_nfa_state *n = rx->nfa_states;
+  struct rx_nfa_edge **e;
+
+  while (n)
+    {
+      e = &n->edges;
+      while (*e)
+	{
+	  struct rx_nfa_edge *t;
+	  switch ((*e)->type)
+	    {
+	    case ne_epsilon:
+	    case ne_side_effect:
+	      t = *e;
+	      *e = t->next;
+	      rx_free_nfa_edge (t);
+	      break;
+
+	    default:
+	      e = &(*e)->next;
+	      break;
+	    }
+	}
+      n = n->next;
+    }
+}
+
+
+
+/* This page: storing the nfa in a contiguous region of memory for
+ * subsequent conversion to a super-nfa.
+ */
+
+/* This is for qsort on an array of nfa_states. The order
+ * is based on state ids and goes 
+ *		[0...MAX][MIN..-1] where (MAX>=0) and (MIN<0)
+ * This way, positive ids double as array indices.
+ */
+
+#ifdef __STDC__
+static int 
+nfacmp (void * va, void * vb)
+#else
+static int 
+nfacmp (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  struct rx_nfa_state **a = (struct rx_nfa_state **)va;
+  struct rx_nfa_state **b = (struct rx_nfa_state **)vb;
+  return (*a == *b		/* &&&& 3.18 */
+	  ? 0
+	  : (((*a)->id < 0) == ((*b)->id < 0)
+	     ? (((*a)->id  < (*b)->id) ? -1 : 1)
+	     : (((*a)->id < 0)
+		? 1 : -1)));
+}
+
+#ifdef __STDC__
+static int 
+count_hash_nodes (struct rx_hash * st)
+#else
+static int 
+count_hash_nodes (st)
+     struct rx_hash * st;
+#endif
+{
+  int x;
+  int count = 0;
+  for (x = 0; x < 13; ++x)
+    count += ((st->children[x])
+	      ? count_hash_nodes (st->children[x])
+	      : st->bucket_size[x]);
+  
+  return count;
+}
+
+
+#ifdef __STDC__
+static void 
+se_memo_freer (struct rx_hash_item * node)
+#else
+static void 
+se_memo_freer (node)
+     struct rx_hash_item * node;
+#endif
+{
+  free ((char *)node->data);
+}
+
+
+#ifdef __STDC__
+static void 
+nfa_set_freer (struct rx_hash_item * node)
+#else
+static void 
+nfa_set_freer (node)
+     struct rx_hash_item * node;
+#endif
+{
+  free ((char *)node->data);
+}
+
+
+/* This copies an entire NFA into a single malloced block of memory.
+ * Mostly this is for compatability with regex.c, though it is convenient
+ * to have the nfa nodes in an array.
+ */
+
+#ifdef __STDC__
+RX_DECL int 
+rx_compactify_nfa (struct rx *rx,
+		   void **mem, unsigned long *size)
+#else
+RX_DECL int 
+rx_compactify_nfa (rx, mem, size)
+     struct rx *rx;
+     void **mem;
+     unsigned long *size;
+#endif
+{
+  int total_nodec;
+  struct rx_nfa_state *n;
+  int edgec = 0;
+  int eclosec = 0;
+  int se_list_consc = count_hash_nodes (&rx->se_list_memo);
+  int nfa_setc = count_hash_nodes (&rx->set_list_memo);
+  unsigned long total_size;
+
+  /* This takes place in two stages.   First, the total size of the
+   * nfa is computed, then structures are copied.  
+   */   
+  n = rx->nfa_states;
+  total_nodec = 0;
+  while (n)
+    {
+      struct rx_nfa_edge *e = n->edges;
+      struct rx_possible_future *ec = n->futures;
+      ++total_nodec;
+      while (e)
+	{
+	  ++edgec;
+	  e = e->next;
+	}
+      while (ec)
+	{
+	  ++eclosec;
+	  ec = ec->next;
+	}
+      n = n->next;
+    }
+
+  total_size = (total_nodec * sizeof (struct rx_nfa_state)
+		+ edgec * rx_sizeof_bitset (rx->local_cset_size)
+		+ edgec * sizeof (struct rx_nfa_edge)
+		+ nfa_setc * sizeof (struct rx_nfa_state_set)
+		+ eclosec * sizeof (struct rx_possible_future)
+		+ se_list_consc * sizeof (struct rx_se_list)
+		+ rx->reserved);
+
+  if (total_size > *size)
+    {
+      *mem = remalloc (*mem, total_size);
+      if (*mem)
+	*size = total_size;
+      else
+	return 0;
+    }
+  /* Now we've allocated the memory; this copies the NFA. */
+  {
+    static struct rx_nfa_state **scratch = 0;
+    static int scratch_alloc = 0;
+    struct rx_nfa_state *state_base = (struct rx_nfa_state *) * mem;
+    struct rx_nfa_state *new_state = state_base;
+    struct rx_nfa_edge *new_edge =
+      (struct rx_nfa_edge *)
+	((char *) state_base + total_nodec * sizeof (struct rx_nfa_state));
+    struct rx_se_list * new_se_list =
+      (struct rx_se_list *)
+	((char *)new_edge + edgec * sizeof (struct rx_nfa_edge));
+    struct rx_possible_future *new_close =
+      ((struct rx_possible_future *)
+       ((char *) new_se_list
+	+ se_list_consc * sizeof (struct rx_se_list)));
+    struct rx_nfa_state_set * new_nfa_set =
+      ((struct rx_nfa_state_set *)
+       ((char *)new_close + eclosec * sizeof (struct rx_possible_future)));
+    char *new_bitset =
+      ((char *) new_nfa_set + nfa_setc * sizeof (struct rx_nfa_state_set));
+    int x;
+    struct rx_nfa_state *n;
+
+    if (scratch_alloc < total_nodec)
+      {
+	scratch = ((struct rx_nfa_state **)
+		   remalloc (scratch, total_nodec * sizeof (*scratch)));
+	if (scratch)
+	  scratch_alloc = total_nodec;
+	else
+	  {
+	    scratch_alloc = 0;
+	    return 0;
+	  }
+      }
+
+    for (x = 0, n = rx->nfa_states; n; n = n->next)
+      scratch[x++] = n;
+
+    qsort (scratch, total_nodec,
+	   sizeof (struct rx_nfa_state *), (int (*)())nfacmp);
+
+    for (x = 0; x < total_nodec; ++x)
+      {
+	struct rx_possible_future *eclose = scratch[x]->futures;
+	struct rx_nfa_edge *edge = scratch[x]->edges;
+	struct rx_nfa_state *cn = new_state++;
+	cn->futures = 0;
+	cn->edges = 0;
+	cn->next = (x == total_nodec - 1) ? 0 : (cn + 1);
+	cn->id = scratch[x]->id;
+	cn->is_final = scratch[x]->is_final;
+	cn->is_start = scratch[x]->is_start;
+	cn->mark = 0;
+	while (edge)
+	  {
+	    int indx = (edge->dest->id < 0
+			 ? (total_nodec + edge->dest->id)
+			 : edge->dest->id);
+	    struct rx_nfa_edge *e = new_edge++;
+	    rx_Bitset cset = (rx_Bitset) new_bitset;
+	    new_bitset += rx_sizeof_bitset (rx->local_cset_size);
+	    rx_bitset_null (rx->local_cset_size, cset);
+	    rx_bitset_union (rx->local_cset_size, cset, edge->params.cset);
+	    e->next = cn->edges;
+	    cn->edges = e;
+	    e->type = edge->type;
+	    e->dest = state_base + indx;
+	    e->params.cset = cset;
+	    edge = edge->next;
+	  }
+	while (eclose)
+	  {
+	    struct rx_possible_future *ec = new_close++;
+	    struct rx_hash_item * sp;
+	    struct rx_se_list ** sepos;
+	    struct rx_se_list * sesrc;
+	    struct rx_nfa_state_set * destlst;
+	    struct rx_nfa_state_set ** destpos;
+	    ec->next = cn->futures;
+	    cn->futures = ec;
+	    for (sepos = &ec->effects, sesrc = eclose->effects;
+		 sesrc;
+		 sesrc = sesrc->cdr, sepos = &(*sepos)->cdr)
+	      {
+		sp = rx_hash_find (&rx->se_list_memo,
+				   (long)sesrc->car ^ (long)sesrc->cdr,
+				   sesrc, &se_list_hash_rules);
+		if (sp->binding)
+		  {
+		    sesrc = (struct rx_se_list *)sp->binding;
+		    break;
+		  }
+		*new_se_list = *sesrc;
+		sp->binding = (void *)new_se_list;
+		*sepos = new_se_list;
+		++new_se_list;
+	      }
+	    *sepos = sesrc;
+	    for (destpos = &ec->destset, destlst = eclose->destset;
+		 destlst;
+		 destpos = &(*destpos)->cdr, destlst = destlst->cdr)
+	      {
+		sp = rx_hash_find (&rx->set_list_memo,
+				   ((((long)destlst->car) >> 8)
+				    ^ (long)destlst->cdr),
+				   destlst, &nfa_set_hash_rules);
+		if (sp->binding)
+		  {
+		    destlst = (struct rx_nfa_state_set *)sp->binding;
+		    break;
+		  }
+		*new_nfa_set = *destlst;
+		new_nfa_set->car = state_base + destlst->car->id;
+		sp->binding = (void *)new_nfa_set;
+		*destpos = new_nfa_set;
+		++new_nfa_set;
+	      }
+	    *destpos = destlst;
+	    eclose = eclose->next;
+	  }
+      }
+  }
+  rx_free_hash_table (&rx->se_list_memo, se_memo_freer, &se_list_hash_rules);
+  bzero (&rx->se_list_memo, sizeof (rx->se_list_memo));
+  rx_free_hash_table (&rx->set_list_memo, nfa_set_freer, &nfa_set_hash_rules);
+  bzero (&rx->set_list_memo, sizeof (rx->set_list_memo));
+
+  rx_free_nfa (rx);
+  rx->nfa_states = (struct rx_nfa_state *)*mem;
+  return 1;
+}
+
+
+
+/* The functions in the next several pages define the lazy-NFA-conversion used
+ * by matchers.  The input to this construction is an NFA such as 
+ * is built by compactify_nfa (rx.c).  The output is the superNFA.
+ */
+
+/* Match engines can use arbitrary values for opcodes.  So, the parse tree 
+ * is built using instructions names (enum rx_opcode), but the superstate
+ * nfa is populated with mystery opcodes (void *).
+ *
+ * For convenience, here is an id table.  The opcodes are == to their inxs
+ *
+ * The lables in re_search_2 would make good values for instructions.
+ */
+
+void * rx_id_instruction_table[rx_num_instructions] =
+{
+  (void *) rx_backtrack_point,
+  (void *) rx_do_side_effects,
+  (void *) rx_cache_miss,
+  (void *) rx_next_char,
+  (void *) rx_backtrack,
+  (void *) rx_error_inx
+};
+
+
+
+
+/* Memory mgt. for superstate graphs. */
+
+#ifdef __STDC__
+static char *
+rx_cache_malloc (struct rx_cache * cache, int bytes)
+#else
+static char *
+rx_cache_malloc (cache, bytes)
+     struct rx_cache * cache;
+     int bytes;
+#endif
+{
+  while (cache->bytes_left < bytes)
+    {
+      if (cache->memory_pos)
+	cache->memory_pos = cache->memory_pos->next;
+      if (!cache->memory_pos)
+	{
+	  cache->morecore (cache);
+	  if (!cache->memory_pos)
+	    return 0;
+	}
+      cache->bytes_left = cache->memory_pos->bytes;
+      cache->memory_addr = ((char *)cache->memory_pos
+			    + sizeof (struct rx_blocklist));
+    }
+  cache->bytes_left -= bytes;
+  {
+    char * addr = cache->memory_addr;
+    cache->memory_addr += bytes;
+    return addr;
+  }
+}
+
+#ifdef __STDC__
+static void
+rx_cache_free (struct rx_cache * cache,
+	       struct rx_freelist ** freelist, char * mem)
+#else
+static void
+rx_cache_free (cache, freelist, mem)
+     struct rx_cache * cache;
+     struct rx_freelist ** freelist;
+     char * mem;
+#endif
+{
+  struct rx_freelist * it = (struct rx_freelist *)mem;
+  it->next = *freelist;
+  *freelist = it;
+}
+
+
+/* The partially instantiated superstate graph has a transition 
+ * table at every node.  There is one entry for every character.
+ * This fills in the transition for a set.
+ */
+#ifdef __STDC__
+static void 
+install_transition (struct rx_superstate *super,
+		    struct rx_inx *answer, rx_Bitset trcset) 
+#else
+static void 
+install_transition (super, answer, trcset)
+     struct rx_superstate *super;
+     struct rx_inx *answer;
+     rx_Bitset trcset;
+#endif
+{
+  struct rx_inx * transitions = super->transitions;
+  int chr;
+  for (chr = 0; chr < 256; )
+    if (!*trcset)
+      {
+	++trcset;
+	chr += 32;
+      }
+    else
+      {
+	RX_subset sub = *trcset;
+	RX_subset mask = 1;
+	int bound = chr + 32;
+	while (chr < bound)
+	  {
+	    if (sub & mask)
+	      transitions [chr] = *answer;
+	    ++chr;
+	    mask <<= 1;
+	  }
+	++trcset;
+      }
+}
+
+
+#ifdef __STDC__
+static int
+qlen (struct rx_superstate * q)
+#else
+static int
+qlen (q)
+     struct rx_superstate * q;
+#endif
+{
+  int count = 1;
+  struct rx_superstate * it;
+  if (!q)
+    return 0;
+  for (it = q->next_recyclable; it != q; it = it->next_recyclable)
+    ++count;
+  return count;
+}
+
+#ifdef __STDC__
+static void
+check_cache (struct rx_cache * cache)
+#else
+static void
+check_cache (cache)
+     struct rx_cache * cache;
+#endif
+{
+  struct rx_cache * you_fucked_up = 0;
+  int total = cache->superstates;
+  int semi = cache->semifree_superstates;
+  if (semi != qlen (cache->semifree_superstate))
+    check_cache (you_fucked_up);
+  if ((total - semi) != qlen (cache->lru_superstate))
+    check_cache (you_fucked_up);
+}
+
+/* When a superstate is old and neglected, it can enter a 
+ * semi-free state.  A semi-free state is slated to die.
+ * Incoming transitions to a semi-free state are re-written
+ * to cause an (interpreted) fault when they are taken.
+ * The fault handler revives the semi-free state, patches
+ * incoming transitions back to normal, and continues.
+ *
+ * The idea is basicly to free in two stages, aborting 
+ * between the two if the state turns out to be useful again.
+ * When a free is aborted, the rescued superstate is placed
+ * in the most-favored slot to maximize the time until it
+ * is next semi-freed.
+ */
+
+#ifdef __STDC__
+static void
+semifree_superstate (struct rx_cache * cache)
+#else
+static void
+semifree_superstate (cache)
+     struct rx_cache * cache;
+#endif
+{
+  int disqualified = cache->semifree_superstates;
+  if (disqualified == cache->superstates)
+    return;
+  while (cache->lru_superstate->locks)
+    {
+      cache->lru_superstate = cache->lru_superstate->next_recyclable;
+      ++disqualified;
+      if (disqualified == cache->superstates)
+	return;
+    }
+  {
+    struct rx_superstate * it = cache->lru_superstate;
+    it->next_recyclable->prev_recyclable = it->prev_recyclable;
+    it->prev_recyclable->next_recyclable = it->next_recyclable;
+    cache->lru_superstate = (it == it->next_recyclable
+			     ? 0
+			     : it->next_recyclable);
+    if (!cache->semifree_superstate)
+      {
+	cache->semifree_superstate = it;
+	it->next_recyclable = it;
+	it->prev_recyclable = it;
+      }
+    else
+      {
+	it->prev_recyclable = cache->semifree_superstate->prev_recyclable;
+	it->next_recyclable = cache->semifree_superstate;
+	it->prev_recyclable->next_recyclable = it;
+	it->next_recyclable->prev_recyclable = it;
+      }
+    {
+      struct rx_distinct_future *df;
+      it->is_semifree = 1;
+      ++cache->semifree_superstates;
+      df = it->transition_refs;
+      if (df)
+	{
+	  df->prev_same_dest->next_same_dest = 0;
+	  for (df = it->transition_refs; df; df = df->next_same_dest)
+	    {
+	      df->future_frame.inx = cache->instruction_table[rx_cache_miss];
+	      df->future_frame.data = 0;
+	      df->future_frame.data_2 = (void *) df;
+	      /* If there are any NEXT-CHAR instruction frames that
+	       * refer to this state, we convert them to CACHE-MISS frames.
+	       */
+	      if (!df->effects
+		  && (df->edge->options->next_same_super_edge[0]
+		      == df->edge->options))
+		install_transition (df->present, &df->future_frame,
+				    df->edge->cset);
+	    }
+	  df = it->transition_refs;
+	  df->prev_same_dest->next_same_dest = df;
+	}
+    }
+  }
+}
+
+
+#ifdef __STDC__
+static void 
+refresh_semifree_superstate (struct rx_cache * cache,
+			     struct rx_superstate * super)
+#else
+static void 
+refresh_semifree_superstate (cache, super)
+     struct rx_cache * cache;
+     struct rx_superstate * super;
+#endif
+{
+  struct rx_distinct_future *df;
+
+  if (super->transition_refs)
+    {
+      super->transition_refs->prev_same_dest->next_same_dest = 0; 
+      for (df = super->transition_refs; df; df = df->next_same_dest)
+	{
+	  df->future_frame.inx = cache->instruction_table[rx_next_char];
+	  df->future_frame.data = (void *) super->transitions;
+	  /* CACHE-MISS instruction frames that refer to this state,
+	   * must be converted to NEXT-CHAR frames.
+	   */
+	  if (!df->effects
+	      && (df->edge->options->next_same_super_edge[0]
+		  == df->edge->options))
+	    install_transition (df->present, &df->future_frame,
+				df->edge->cset);
+	}
+      super->transition_refs->prev_same_dest->next_same_dest
+	= super->transition_refs;
+    }
+  if (cache->semifree_superstate == super)
+    cache->semifree_superstate = (super->prev_recyclable == super
+				  ? 0
+				  : super->prev_recyclable);
+  super->next_recyclable->prev_recyclable = super->prev_recyclable;
+  super->prev_recyclable->next_recyclable = super->next_recyclable;
+
+  if (!cache->lru_superstate)
+    (cache->lru_superstate
+     = super->next_recyclable
+     = super->prev_recyclable
+     = super);
+  else
+    {
+      super->next_recyclable = cache->lru_superstate;
+      super->prev_recyclable = cache->lru_superstate->prev_recyclable;
+      super->next_recyclable->prev_recyclable = super;
+      super->prev_recyclable->next_recyclable = super;
+    }
+  super->is_semifree = 0;
+  --cache->semifree_superstates;
+}
+
+#ifdef __STDC__
+static void
+rx_refresh_this_superstate (struct rx_cache * cache, struct rx_superstate * superstate)
+#else
+static void
+rx_refresh_this_superstate (cache, superstate)
+     struct rx_cache * cache;
+     struct rx_superstate * superstate;
+#endif
+{
+  if (superstate->is_semifree)
+    refresh_semifree_superstate (cache, superstate);
+  else if (cache->lru_superstate == superstate)
+    cache->lru_superstate = superstate->next_recyclable;
+  else if (superstate != cache->lru_superstate->prev_recyclable)
+    {
+      superstate->next_recyclable->prev_recyclable
+	= superstate->prev_recyclable;
+      superstate->prev_recyclable->next_recyclable
+	= superstate->next_recyclable;
+      superstate->next_recyclable = cache->lru_superstate;
+      superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
+      superstate->next_recyclable->prev_recyclable = superstate;
+      superstate->prev_recyclable->next_recyclable = superstate;
+    }
+}
+
+#ifdef __STDC__
+static void 
+release_superset_low (struct rx_cache * cache,
+		     struct rx_superset *set)
+#else
+static void 
+release_superset_low (cache, set)
+     struct rx_cache * cache;
+     struct rx_superset *set;
+#endif
+{
+  if (!--set->refs)
+    {
+      if (set->cdr)
+	release_superset_low (cache, set->cdr);
+
+      set->starts_for = 0;
+
+      rx_hash_free
+	(rx_hash_find
+	 (&cache->superset_table,
+	  (unsigned long)set->car ^ set->id ^ (unsigned long)set->cdr,
+	  (void *)set,
+	  &cache->superset_hash_rules),
+	 &cache->superset_hash_rules);
+      rx_cache_free (cache, &cache->free_supersets, (char *)set);
+    }
+}
+
+#ifdef __STDC__
+RX_DECL void 
+rx_release_superset (struct rx *rx,
+		     struct rx_superset *set)
+#else
+RX_DECL void 
+rx_release_superset (rx, set)
+     struct rx *rx;
+     struct rx_superset *set;
+#endif
+{
+  release_superset_low (rx->cache, set);
+}
+
+/* This tries to add a new superstate to the superstate freelist.
+ * It might, as a result, free some edge pieces or hash tables.
+ * If nothing can be freed because too many locks are being held, fail.
+ */
+
+#ifdef __STDC__
+static int
+rx_really_free_superstate (struct rx_cache * cache)
+#else
+static int
+rx_really_free_superstate (cache)
+     struct rx_cache * cache;
+#endif
+{
+  int locked_superstates = 0;
+  struct rx_superstate * it;
+
+  if (!cache->superstates)
+    return 0;
+
+  {
+    /* This is a total guess.  The idea is that we should expect as
+     * many misses as we've recently experienced.  I.e., cache->misses
+     * should be the same as cache->semifree_superstates.
+     */
+    while ((cache->hits + cache->misses) > cache->superstates_allowed)
+      {
+	cache->hits >>= 1;
+	cache->misses >>= 1;
+      }
+    if (  ((cache->hits + cache->misses) * cache->semifree_superstates)
+	< (cache->superstates		 * cache->misses))
+      {
+	semifree_superstate (cache);
+	semifree_superstate (cache);
+      }
+  }
+
+  while (cache->semifree_superstate && cache->semifree_superstate->locks)
+    {
+      refresh_semifree_superstate (cache, cache->semifree_superstate);
+      ++locked_superstates;
+      if (locked_superstates == cache->superstates)
+	return 0;
+    }
+
+  if (cache->semifree_superstate)
+    {
+      it = cache->semifree_superstate;
+      it->next_recyclable->prev_recyclable = it->prev_recyclable;
+      it->prev_recyclable->next_recyclable = it->next_recyclable;
+      cache->semifree_superstate = ((it == it->next_recyclable)
+				    ? 0
+				    : it->next_recyclable);
+      --cache->semifree_superstates;
+    }
+  else
+    {
+      while (cache->lru_superstate->locks)
+	{
+	  cache->lru_superstate = cache->lru_superstate->next_recyclable;
+	  ++locked_superstates;
+	  if (locked_superstates == cache->superstates)
+	    return 0;
+	}
+      it = cache->lru_superstate;
+      it->next_recyclable->prev_recyclable = it->prev_recyclable;
+      it->prev_recyclable->next_recyclable = it->next_recyclable;
+      cache->lru_superstate = ((it == it->next_recyclable)
+				    ? 0
+				    : it->next_recyclable);
+    }
+
+  if (it->transition_refs)
+    {
+      struct rx_distinct_future *df;
+      for (df = it->transition_refs,
+	   df->prev_same_dest->next_same_dest = 0;
+	   df;
+	   df = df->next_same_dest)
+	{
+	  df->future_frame.inx = cache->instruction_table[rx_cache_miss];
+	  df->future_frame.data = 0;
+	  df->future_frame.data_2 = (void *) df;
+	  df->future = 0;
+	}
+      it->transition_refs->prev_same_dest->next_same_dest =
+	it->transition_refs;
+    }
+  {
+    struct rx_super_edge *tc = it->edges;
+    while (tc)
+      {
+	struct rx_distinct_future * df;
+	struct rx_super_edge *tct = tc->next;
+	df = tc->options;
+	df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+	while (df)
+	  {
+	    struct rx_distinct_future *dft = df;
+	    df = df->next_same_super_edge[0];
+	    
+	    
+	    if (dft->future && dft->future->transition_refs == dft)
+	      {
+		dft->future->transition_refs = dft->next_same_dest;
+		if (dft->future->transition_refs == dft)
+		  dft->future->transition_refs = 0;
+	      }
+	    dft->next_same_dest->prev_same_dest = dft->prev_same_dest;
+	    dft->prev_same_dest->next_same_dest = dft->next_same_dest;
+	    rx_cache_free (cache, &cache->free_discernable_futures,
+			   (char *)dft);
+	  }
+	rx_cache_free (cache, &cache->free_transition_classes, (char *)tc);
+	tc = tct;
+      }
+  }
+  
+  if (it->contents->superstate == it)
+    it->contents->superstate = 0;
+  release_superset_low (cache, it->contents);
+  rx_cache_free (cache, &cache->free_superstates, (char *)it);
+  --cache->superstates;
+  return 1;
+}
+
+#ifdef __STDC__
+static char *
+rx_cache_get (struct rx_cache * cache,
+	      struct rx_freelist ** freelist)
+#else
+static char *
+rx_cache_get (cache, freelist)
+     struct rx_cache * cache;
+     struct rx_freelist ** freelist;
+#endif
+{
+  while (!*freelist && rx_really_free_superstate (cache))
+    ;
+  if (!*freelist)
+    return 0;
+  {
+    struct rx_freelist * it = *freelist;
+    *freelist = it->next;
+    return (char *)it;
+  }
+}
+
+#ifdef __STDC__
+static char *
+rx_cache_malloc_or_get (struct rx_cache * cache,
+			struct rx_freelist ** freelist, int bytes)
+#else
+static char *
+rx_cache_malloc_or_get (cache, freelist, bytes)
+     struct rx_cache * cache;
+     struct rx_freelist ** freelist;
+     int bytes;
+#endif
+{
+  if (!*freelist)
+    {
+      char * answer = rx_cache_malloc (cache, bytes);
+      if (answer)
+	return answer;
+    }
+
+  return rx_cache_get (cache, freelist);
+}
+
+#ifdef __STDC__
+static char *
+rx_cache_get_superstate (struct rx_cache * cache)
+#else
+static char *
+rx_cache_get_superstate (cache)
+	  struct rx_cache * cache;
+#endif
+{
+  char * answer;
+  int bytes = (   sizeof (struct rx_superstate)
+	       +  cache->local_cset_size * sizeof (struct rx_inx));
+  if (!cache->free_superstates
+      && (cache->superstates < cache->superstates_allowed))
+    {
+      answer = rx_cache_malloc (cache, bytes);
+      if (answer)
+	{
+	  ++cache->superstates;
+	  return answer;
+	}
+    }
+  answer = rx_cache_get (cache, &cache->free_superstates);
+  if (!answer)
+    {
+      answer = rx_cache_malloc (cache, bytes);
+      if (answer)
+	++cache->superstates_allowed;
+    }
+  ++cache->superstates;
+  return answer;
+}
+
+
+
+
+#ifdef __STDC__
+static int
+supersetcmp (void * va, void * vb)
+#else
+static int
+supersetcmp (va, vb)
+     void * va;
+     void * vb;
+#endif
+{
+  struct rx_superset * a = (struct rx_superset *)va;
+  struct rx_superset * b = (struct rx_superset *)vb;
+  return (   (a == b)
+	  || (a && b && (a->car == b->car) && (a->cdr == b->cdr)));
+}
+
+#ifdef __STDC__
+static struct rx_hash_item *
+superset_allocator (struct rx_hash_rules * rules, void * val)
+#else
+static struct rx_hash_item *
+superset_allocator (rules, val)
+     struct rx_hash_rules * rules;
+     void * val;
+#endif
+{
+  struct rx_cache * cache
+    = ((struct rx_cache *)
+       ((char *)rules
+	- (unsigned long)(&((struct rx_cache *)0)->superset_hash_rules)));
+  struct rx_superset * template = (struct rx_superset *)val;
+  struct rx_superset * newset
+    = ((struct rx_superset *)
+       rx_cache_malloc_or_get (cache,
+			       &cache->free_supersets,
+			       sizeof (*template)));
+  if (!newset)
+    return 0;
+  newset->refs = 0;
+  newset->car = template->car;
+  newset->id = template->car->id;
+  newset->cdr = template->cdr;
+  newset->superstate = 0;
+  rx_protect_superset (rx, template->cdr);
+  newset->hash_item.data = (void *)newset;
+  newset->hash_item.binding = 0;
+  return &newset->hash_item;
+}
+
+#ifdef __STDC__
+static struct rx_hash * 
+super_hash_allocator (struct rx_hash_rules * rules)
+#else
+static struct rx_hash * 
+super_hash_allocator (rules)
+     struct rx_hash_rules * rules;
+#endif
+{
+  struct rx_cache * cache
+    = ((struct rx_cache *)
+       ((char *)rules
+	- (unsigned long)(&((struct rx_cache *)0)->superset_hash_rules)));
+  return ((struct rx_hash *)
+	  rx_cache_malloc_or_get (cache,
+				  &cache->free_hash, sizeof (struct rx_hash)));
+}
+
+
+#ifdef __STDC__
+static void
+super_hash_liberator (struct rx_hash * hash, struct rx_hash_rules * rules)
+#else
+static void
+super_hash_liberator (hash, rules)
+     struct rx_hash * hash;
+     struct rx_hash_rules * rules;
+#endif
+{
+  struct rx_cache * cache
+    = ((struct rx_cache *)
+       (char *)rules - (long)(&((struct rx_cache *)0)->superset_hash_rules));
+  rx_cache_free (cache, &cache->free_hash, (char *)hash);
+}
+
+#ifdef __STDC__
+static void
+superset_hash_item_liberator (struct rx_hash_item * it,
+			      struct rx_hash_rules * rules)
+#else
+static void
+superset_hash_item_liberator (it, rules) /* Well, it does ya know. */
+     struct rx_hash_item * it;
+     struct rx_hash_rules * rules;
+#endif
+{
+}
+
+int rx_cache_bound = 128;
+static int rx_default_cache_got = 0;
+
+#ifdef __STDC__
+static int
+bytes_for_cache_size (int supers, int cset_size)
+#else
+static int
+bytes_for_cache_size (supers, cset_size)
+     int supers;
+     int cset_size;
+#endif
+{
+  /* What the hell is this? !!!*/
+  return (int)
+    ((float)supers *
+     (  (1.03 * (float) (  rx_sizeof_bitset (cset_size)
+			 + sizeof (struct rx_super_edge)))
+      + (1.80 * (float) sizeof (struct rx_possible_future))
+      + (float) (  sizeof (struct rx_superstate)
+		 + cset_size * sizeof (struct rx_inx))));
+}
+
+#ifdef __STDC__
+static void
+rx_morecore (struct rx_cache * cache)
+#else
+static void
+rx_morecore (cache)
+     struct rx_cache * cache;
+#endif
+{
+  if (rx_default_cache_got >= rx_cache_bound)
+    return;
+
+  rx_default_cache_got += 16;
+  cache->superstates_allowed = rx_cache_bound;
+  {
+    struct rx_blocklist ** pos = &cache->memory;
+    int size = bytes_for_cache_size (16, cache->local_cset_size);
+    while (*pos)
+      pos = &(*pos)->next;
+    *pos = ((struct rx_blocklist *)
+	    malloc (size + sizeof (struct rx_blocklist))); 
+    if (!*pos)
+      return;
+
+    (*pos)->next = 0;
+    (*pos)->bytes = size;
+    cache->memory_pos = *pos;
+    cache->memory_addr = (char *)*pos + sizeof (**pos);
+    cache->bytes_left = size;
+  }
+}
+
+static struct rx_cache default_cache = 
+{
+  {
+    supersetcmp,
+    super_hash_allocator,
+    super_hash_liberator,
+    superset_allocator,
+    superset_hash_item_liberator,
+  },
+  0,
+  0,
+  0,
+  0,
+  rx_morecore,
+
+  0,
+  0,
+  0,
+  0,
+  0,
+
+  0,
+  0,
+
+  0,
+
+  0,
+  0,
+  0,
+  0,
+  128,
+
+  256,
+  rx_id_instruction_table,
+
+  {
+    0,
+    0,
+    {0},
+    {0},
+    {0}
+  }
+};
+
+/* This adds an element to a superstate set.  These sets are lists, such
+ * that lists with == elements are ==.  The empty set is returned by
+ * superset_cons (rx, 0, 0) and is NOT equivelent to 
+ * (struct rx_superset)0.
+ */
+
+#ifdef __STDC__
+RX_DECL struct rx_superset *
+rx_superset_cons (struct rx * rx,
+		  struct rx_nfa_state *car, struct rx_superset *cdr)
+#else
+RX_DECL struct rx_superset *
+rx_superset_cons (rx, car, cdr)
+     struct rx * rx;
+     struct rx_nfa_state *car;
+     struct rx_superset *cdr;
+#endif
+{
+  struct rx_cache * cache = rx->cache;
+  if (!car && !cdr)
+    {
+      if (!cache->empty_superset)
+	{
+	  cache->empty_superset
+	    = ((struct rx_superset *)
+	       rx_cache_malloc_or_get (cache, &cache->free_supersets,
+				       sizeof (struct rx_superset)));
+	  if (!cache->empty_superset)
+	    return 0;
+	  bzero (cache->empty_superset, sizeof (struct rx_superset));
+	  cache->empty_superset->refs = 1000;
+	}
+      return cache->empty_superset;
+    }
+  {
+    struct rx_superset template;
+    struct rx_hash_item * hit;
+    template.car = car;
+    template.cdr = cdr;
+    template.id = car->id;
+    hit = rx_hash_store (&cache->superset_table,
+			 (unsigned long)car ^ car->id ^ (unsigned long)cdr,
+			 (void *)&template,
+			 &cache->superset_hash_rules);
+    return (hit
+	    ?  (struct rx_superset *)hit->data
+	    : 0);
+  }
+}
+
+/* This computes a union of two NFA state sets.  The sets do not have the
+ * same representation though.  One is a RX_SUPERSET structure (part
+ * of the superstate NFA) and the other is an NFA_STATE_SET (part of the NFA).
+ */
+
+#ifdef __STDC__
+RX_DECL struct rx_superset *
+rx_superstate_eclosure_union
+  (struct rx * rx, struct rx_superset *set, struct rx_nfa_state_set *ecl) 
+#else
+RX_DECL struct rx_superset *
+rx_superstate_eclosure_union (rx, set, ecl)
+     struct rx * rx;
+     struct rx_superset *set;
+     struct rx_nfa_state_set *ecl;
+#endif
+{
+  if (!ecl)
+    return set;
+
+  if (!set->car)
+    return rx_superset_cons (rx, ecl->car,
+			     rx_superstate_eclosure_union (rx, set, ecl->cdr));
+  if (set->car == ecl->car)
+    return rx_superstate_eclosure_union (rx, set, ecl->cdr);
+
+  {
+    struct rx_superset * tail;
+    struct rx_nfa_state * first;
+
+    if (set->car > ecl->car)
+      {
+	tail = rx_superstate_eclosure_union (rx, set->cdr, ecl);
+	first = set->car;
+      }
+    else
+      {
+	tail = rx_superstate_eclosure_union (rx, set, ecl->cdr);
+	first = ecl->car;
+      }
+    if (!tail)
+      return 0;
+    else
+      {
+	struct rx_superset * answer;
+	answer = rx_superset_cons (rx, first, tail);
+	if (!answer)
+	  {
+	    rx_protect_superset (rx, tail);
+	    rx_release_superset (rx, tail);
+	    return 0;
+	  }
+	else
+	  return answer;
+      }
+  }
+}
+
+
+
+
+
+/*
+ * This makes sure that a list of rx_distinct_futures contains
+ * a future for each possible set of side effects in the eclosure
+ * of a given state.  This is some of the work of filling in a
+ * superstate transition. 
+ */
+
+#ifdef __STDC__
+static struct rx_distinct_future *
+include_futures (struct rx *rx,
+		 struct rx_distinct_future *df, struct rx_nfa_state
+		 *state, struct rx_superstate *superstate) 
+#else
+static struct rx_distinct_future *
+include_futures (rx, df, state, superstate)
+     struct rx *rx;
+     struct rx_distinct_future *df;
+     struct rx_nfa_state *state;
+     struct rx_superstate *superstate;
+#endif
+{
+  struct rx_possible_future *future;
+  struct rx_cache * cache = rx->cache;
+  for (future = state->futures; future; future = future->next)
+    {
+      struct rx_distinct_future *dfp;
+      struct rx_distinct_future *insert_before = 0;
+      if (df)
+	df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+      for (dfp = df; dfp; dfp = dfp->next_same_super_edge[0])
+	if (dfp->effects == future->effects)
+	  break;
+	else
+	  {
+	    int order = rx->se_list_cmp (rx, dfp->effects, future->effects);
+	    if (order > 0)
+	      {
+		insert_before = dfp;
+		dfp = 0;
+		break;
+	      }
+	  }
+      if (df)
+	df->next_same_super_edge[1]->next_same_super_edge[0] = df;
+      if (!dfp)
+	{
+	  dfp
+	    = ((struct rx_distinct_future *)
+	       rx_cache_malloc_or_get (cache, &cache->free_discernable_futures,
+				       sizeof (struct rx_distinct_future)));
+	  if (!dfp)
+	    return 0;
+	  if (!df)
+	    {
+	      df = insert_before = dfp;
+	      df->next_same_super_edge[0] = df->next_same_super_edge[1] = df;
+	    }
+	  else if (!insert_before)
+	    insert_before = df;
+	  else if (insert_before == df)
+	    df = dfp;
+
+	  dfp->next_same_super_edge[0] = insert_before;
+	  dfp->next_same_super_edge[1]
+	    = insert_before->next_same_super_edge[1];
+	  dfp->next_same_super_edge[1]->next_same_super_edge[0] = dfp;
+	  dfp->next_same_super_edge[0]->next_same_super_edge[1] = dfp;
+	  dfp->next_same_dest = dfp->prev_same_dest = dfp;
+	  dfp->future = 0;
+	  dfp->present = superstate;
+	  dfp->future_frame.inx = rx->instruction_table[rx_cache_miss];
+	  dfp->future_frame.data = 0;
+	  dfp->future_frame.data_2 = (void *) dfp;
+	  dfp->side_effects_frame.inx
+	    = rx->instruction_table[rx_do_side_effects];
+	  dfp->side_effects_frame.data = 0;
+	  dfp->side_effects_frame.data_2 = (void *) dfp;
+	  dfp->effects = future->effects;
+	}
+    }
+  return df;
+}
+
+
+
+
+/* This constructs a new superstate from its state set.  The only 
+ * complexity here is memory management.
+ */
+#ifdef __STDC__
+RX_DECL struct rx_superstate *
+rx_superstate (struct rx *rx,
+	       struct rx_superset *set)
+#else
+RX_DECL struct rx_superstate *
+rx_superstate (rx, set)
+     struct rx *rx;
+     struct rx_superset *set;
+#endif
+{
+  struct rx_cache * cache = rx->cache;
+  struct rx_superstate * superstate = 0;
+
+  /* Does the superstate already exist in the cache? */
+  if (set->superstate)
+    {
+      if (set->superstate->rx_id != rx->rx_id)
+	{
+	  /* Aha.  It is in the cache, but belongs to a superstate
+	   * that refers to an NFA that no longer exists.
+	   * (We know it no longer exists because it was evidently
+	   *  stored in the same region of memory as the current nfa
+	   *  yet it has a different id.)
+	   */
+	  superstate = set->superstate;
+	  if (!superstate->is_semifree)
+	    {
+	      if (cache->lru_superstate == superstate)
+		{
+		  cache->lru_superstate = superstate->next_recyclable;
+		  if (cache->lru_superstate == superstate)
+		    cache->lru_superstate = 0;
+		}
+	      {
+		superstate->next_recyclable->prev_recyclable
+		  = superstate->prev_recyclable;
+		superstate->prev_recyclable->next_recyclable
+		  = superstate->next_recyclable;
+		if (!cache->semifree_superstate)
+		  {
+		    (cache->semifree_superstate
+		     = superstate->next_recyclable
+		     = superstate->prev_recyclable
+		     = superstate);
+		  }
+		else
+		  {
+		    superstate->next_recyclable = cache->semifree_superstate;
+		    superstate->prev_recyclable
+		      = cache->semifree_superstate->prev_recyclable;
+		    superstate->next_recyclable->prev_recyclable
+		      = superstate;
+		    superstate->prev_recyclable->next_recyclable
+		      = superstate;
+		    cache->semifree_superstate = superstate;
+		  }
+		++cache->semifree_superstates;
+	      }
+	    }
+	  set->superstate = 0;
+	  goto handle_cache_miss;
+	}
+      ++cache->hits;
+      superstate = set->superstate;
+
+      rx_refresh_this_superstate (cache, superstate);
+      return superstate;
+    }
+
+ handle_cache_miss:
+
+  /* This point reached only for cache misses. */
+  ++cache->misses;
+#if RX_DEBUG
+  if (rx_debug_trace > 1)
+    {
+      struct rx_superset * setp = set;
+      fprintf (stderr, "Building a superstet %d(%d): ", rx->rx_id, set);
+      while (setp)
+	{
+	  fprintf (stderr, "%d ", setp->id);
+	  setp = setp->cdr;
+	}
+      fprintf (stderr, "(%d)\n", set);
+    }
+#endif
+  superstate = (struct rx_superstate *)rx_cache_get_superstate (cache);
+  if (!superstate)
+    return 0;
+
+  if (!cache->lru_superstate)
+    (cache->lru_superstate
+     = superstate->next_recyclable
+     = superstate->prev_recyclable
+     = superstate);
+  else
+    {
+      superstate->next_recyclable = cache->lru_superstate;
+      superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
+      (  superstate->prev_recyclable->next_recyclable
+       = superstate->next_recyclable->prev_recyclable
+       = superstate);
+    }
+  superstate->rx_id = rx->rx_id;
+  superstate->transition_refs = 0;
+  superstate->locks = 0;
+  superstate->is_semifree = 0;
+  set->superstate = superstate;
+  superstate->contents = set;
+  rx_protect_superset (rx, set);
+  superstate->edges = 0;
+  {
+    int x;
+    /* None of the transitions from this superstate are known yet. */
+    for (x = 0; x < rx->local_cset_size; ++x) /* &&&&& 3.8 % */
+      {
+	struct rx_inx * ifr = &superstate->transitions[x];
+	ifr->inx = rx->instruction_table [rx_cache_miss];
+	ifr->data = ifr->data_2 = 0;
+      }
+  }
+  return superstate;
+}
+
+
+
+/* This computes the destination set of one edge of the superstate NFA.
+ * Note that a RX_DISTINCT_FUTURE is a superstate edge.
+ * Returns 0 on an allocation failure.
+ */
+
+#ifdef __STDC__
+static int 
+solve_destination (struct rx *rx, struct rx_distinct_future *df)
+#else
+static int 
+solve_destination (rx, df)
+     struct rx *rx;
+     struct rx_distinct_future *df;
+#endif
+{
+  struct rx_super_edge *tc = df->edge;
+  struct rx_superset *nfa_state;
+  struct rx_superset *nil_set = rx_superset_cons (rx, 0, 0);
+  struct rx_superset *solution = nil_set;
+  struct rx_superstate *dest;
+
+  rx_protect_superset (rx, solution);
+  /* Iterate over all NFA states in the state set of this superstate. */
+  for (nfa_state = df->present->contents;
+       nfa_state->car;
+       nfa_state = nfa_state->cdr)
+    {
+      struct rx_nfa_edge *e;
+      /* Iterate over all edges of each NFA state. */
+      for (e = nfa_state->car->edges; e; e = e->next)
+        /* If we find an edge that is labeled with 
+	 * the characters we are solving for.....
+	 */
+	if (rx_bitset_is_subset (rx->local_cset_size,
+				 tc->cset, e->params.cset))
+	  {
+	    struct rx_nfa_state *n = e->dest;
+	    struct rx_possible_future *pf;
+	    /* ....search the partial epsilon closures of the destination
+	     * of that edge for a path that involves the same set of
+	     * side effects we are solving for.
+	     * If we find such a RX_POSSIBLE_FUTURE, we add members to the
+	     * stateset we are computing.
+	     */
+	    for (pf = n->futures; pf; pf = pf->next)
+	      if (pf->effects == df->effects)
+		{
+		  struct rx_superset * old_sol;
+		  old_sol = solution;
+		  solution = rx_superstate_eclosure_union (rx, solution,
+							   pf->destset);
+		  if (!solution)
+		    return 0;
+		  rx_protect_superset (rx, solution);
+		  rx_release_superset (rx, old_sol);
+		}
+	  }
+    }
+  /* It is possible that the RX_DISTINCT_FUTURE we are working on has 
+   * the empty set of NFA states as its definition.  In that case, this
+   * is a failure point.
+   */
+  if (solution == nil_set)
+    {
+      df->future_frame.inx = (void *) rx_backtrack;
+      df->future_frame.data = 0;
+      df->future_frame.data_2 = 0;
+      return 1;
+    }
+  dest = rx_superstate (rx, solution);
+  rx_release_superset (rx, solution);
+  if (!dest)
+    return 0;
+
+  {
+    struct rx_distinct_future *dft;
+    dft = df;
+    df->prev_same_dest->next_same_dest = 0;
+    while (dft)
+      {
+	dft->future = dest;
+	dft->future_frame.inx = rx->instruction_table[rx_next_char];
+	dft->future_frame.data = (void *) dest->transitions;
+	dft = dft->next_same_dest;
+      }
+    df->prev_same_dest->next_same_dest = df;
+  }
+  if (!dest->transition_refs)
+    dest->transition_refs = df;
+  else
+    {
+      struct rx_distinct_future *dft = dest->transition_refs->next_same_dest;
+      dest->transition_refs->next_same_dest = df->next_same_dest;
+      df->next_same_dest->prev_same_dest = dest->transition_refs;
+      df->next_same_dest = dft;
+      dft->prev_same_dest = df;
+    }
+  return 1;
+}
+
+
+/* This takes a superstate and a character, and computes some edges
+ * from the superstate NFA.  In particular, this computes all edges
+ * that lead from SUPERSTATE given CHR.   This function also 
+ * computes the set of characters that share this edge set.
+ * This returns 0 on allocation error.
+ * The character set and list of edges are returned through 
+ * the paramters CSETOUT and DFOUT.
+} */
+
+#ifdef __STDC__
+static int 
+compute_super_edge (struct rx *rx, struct rx_distinct_future **dfout,
+			  rx_Bitset csetout, struct rx_superstate *superstate,
+			  unsigned char chr)  
+#else
+static int 
+compute_super_edge (rx, dfout, csetout, superstate, chr)
+     struct rx *rx;
+     struct rx_distinct_future **dfout;
+     rx_Bitset csetout;
+     struct rx_superstate *superstate;
+     unsigned char chr;
+#endif
+{
+  struct rx_superset *stateset = superstate->contents;
+
+  /* To compute the set of characters that share edges with CHR, 
+   * we start with the full character set, and subtract.
+   */
+  rx_bitset_universe (rx->local_cset_size, csetout);
+  *dfout = 0;
+
+  /* Iterate over the NFA states in the superstate state-set. */
+  while (stateset->car)
+    {
+      struct rx_nfa_edge *e;
+      for (e = stateset->car->edges; e; e = e->next)
+	if (RX_bitset_member (e->params.cset, chr))
+	  {
+	    /* If we find an NFA edge that applies, we make sure there
+	     * are corresponding edges in the superstate NFA.
+	     */
+	    {
+	      struct rx_distinct_future * saved;
+	      saved = *dfout;
+	      *dfout = include_futures (rx, *dfout, e->dest, superstate);
+	      if (!*dfout)
+		{
+		  struct rx_distinct_future * df;
+		  df = saved;
+		  df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+		  while (df)
+		    {
+		      struct rx_distinct_future *dft;
+		      dft = df;
+		      df = df->next_same_super_edge[0];
+
+		      if (dft->future && dft->future->transition_refs == dft)
+			{
+			  dft->future->transition_refs = dft->next_same_dest;
+			  if (dft->future->transition_refs == dft)
+			    dft->future->transition_refs = 0;
+			}
+		      dft->next_same_dest->prev_same_dest = dft->prev_same_dest;
+		      dft->prev_same_dest->next_same_dest = dft->next_same_dest;
+		      rx_cache_free (rx->cache,
+				     &rx->cache->free_discernable_futures,
+				     (char *)dft);
+		    }
+		  return 0;
+		}
+	    }
+	    /* We also trim the character set a bit. */
+	    rx_bitset_intersection (rx->local_cset_size,
+				    csetout, e->params.cset);
+	  }
+	else
+	  /* An edge that doesn't apply at least tells us some characters
+	   * that don't share the same edge set as CHR.
+	   */
+	  rx_bitset_difference (rx->local_cset_size, csetout, e->params.cset);
+      stateset = stateset->cdr;
+    }
+  return 1;
+}
+
+
+
+/* This is a constructor for RX_SUPER_EDGE structures.  These are
+ * wrappers for lists of superstate NFA edges that share character sets labels.
+ * If a transition class contains more than one rx_distinct_future (superstate
+ * edge), then it represents a non-determinism in the superstate NFA.
+ */
+
+#ifdef __STDC__
+static struct rx_super_edge *
+rx_super_edge (struct rx *rx,
+	       struct rx_superstate *super, rx_Bitset cset,
+	       struct rx_distinct_future *df) 
+#else
+static struct rx_super_edge *
+rx_super_edge (rx, super, cset, df)
+     struct rx *rx;
+     struct rx_superstate *super;
+     rx_Bitset cset;
+     struct rx_distinct_future *df;
+#endif
+{
+  struct rx_super_edge *tc =
+    (struct rx_super_edge *)rx_cache_malloc_or_get
+      (rx->cache, &rx->cache->free_transition_classes,
+       sizeof (struct rx_super_edge) + rx_sizeof_bitset (rx->local_cset_size));
+
+  if (!tc)
+    return 0;
+  tc->next = super->edges;
+  super->edges = tc;
+  tc->rx_backtrack_frame.inx = rx->instruction_table[rx_backtrack_point];
+  tc->rx_backtrack_frame.data = 0;
+  tc->rx_backtrack_frame.data_2 = (void *) tc;
+  tc->options = df;
+  tc->cset = (rx_Bitset) ((char *) tc + sizeof (*tc));
+  rx_bitset_assign (rx->local_cset_size, tc->cset, cset);
+  if (df)
+    {
+      struct rx_distinct_future * dfp = df;
+      df->next_same_super_edge[1]->next_same_super_edge[0] = 0;
+      while (dfp)
+	{
+	  dfp->edge = tc;
+	  dfp = dfp->next_same_super_edge[0];
+	}
+      df->next_same_super_edge[1]->next_same_super_edge[0] = df;
+    }
+  return tc;
+}
+
+
+/* There are three kinds of cache miss.  The first occurs when a
+ * transition is taken that has never been computed during the
+ * lifetime of the source superstate.  That cache miss is handled by
+ * calling COMPUTE_SUPER_EDGE.  The second kind of cache miss
+ * occurs when the destination superstate of a transition doesn't
+ * exist.  SOLVE_DESTINATION is used to construct the destination superstate.
+ * Finally, the third kind of cache miss occurs when the destination
+ * superstate of a transition is in a `semi-free state'.  That case is
+ * handled by UNFREE_SUPERSTATE.
+ *
+ * The function of HANDLE_CACHE_MISS is to figure out which of these
+ * cases applies.
+ */
+
+#ifdef __STDC__
+static void
+install_partial_transition  (struct rx_superstate *super,
+			     struct rx_inx *answer,
+			     RX_subset set, int offset)
+#else
+static void
+install_partial_transition  (super, answer, set, offset)
+     struct rx_superstate *super;
+     struct rx_inx *answer;
+     RX_subset set;
+     int offset;
+#endif
+{
+  int start = offset;
+  int end = start + 32;
+  RX_subset pos = 1;
+  struct rx_inx * transitions = super->transitions;
+  
+  while (start < end)
+    {
+      if (set & pos)
+	transitions[start] = *answer;
+      pos <<= 1;
+      ++start;
+    }
+}
+
+
+#ifdef __STDC__
+RX_DECL struct rx_inx *
+rx_handle_cache_miss
+  (struct rx *rx, struct rx_superstate *super, unsigned char chr, void *data) 
+#else
+RX_DECL struct rx_inx *
+rx_handle_cache_miss (rx, super, chr, data)
+     struct rx *rx;
+     struct rx_superstate *super;
+     unsigned char chr;
+     void *data;
+#endif
+{
+  int offset = chr / RX_subset_bits;
+  struct rx_distinct_future *df = data;
+
+  if (!df)			/* must be the shared_cache_miss_frame */
+    {
+      /* Perhaps this is just a transition waiting to be filled. */
+      struct rx_super_edge *tc;
+      RX_subset mask = rx_subset_singletons [chr % RX_subset_bits];
+
+      for (tc = super->edges; tc; tc = tc->next)
+	if (tc->cset[offset] & mask)
+	  {
+	    struct rx_inx * answer;
+	    df = tc->options;
+	    answer = ((tc->options->next_same_super_edge[0] != tc->options)
+		      ? &tc->rx_backtrack_frame
+		      : (df->effects
+			 ? &df->side_effects_frame
+			 : &df->future_frame));
+	    install_partial_transition (super, answer,
+					tc->cset [offset], offset * 32);
+	    return answer;
+	  }
+      /* Otherwise, it's a flushed or  newly encountered edge. */
+      {
+	char cset_space[1024];	/* this limit is far from unreasonable */
+	rx_Bitset trcset;
+	struct rx_inx *answer;
+
+	if (rx_sizeof_bitset (rx->local_cset_size) > sizeof (cset_space))
+	  return 0;		/* If the arbitrary limit is hit, always fail */
+				/* cleanly. */
+	trcset = (rx_Bitset)cset_space;
+	rx_lock_superstate (rx, super);
+	if (!compute_super_edge (rx, &df, trcset, super, chr))
+	  {
+	    rx_unlock_superstate (rx, super);
+	    return 0;
+	  }
+	if (!df)		/* We just computed the fail transition. */
+	  {
+	    static struct rx_inx
+	      shared_fail_frame = { 0, 0, (void *)rx_backtrack, 0 };
+	    answer = &shared_fail_frame;
+	  }
+	else
+	  {
+	    tc = rx_super_edge (rx, super, trcset, df);
+	    if (!tc)
+	      {
+		rx_unlock_superstate (rx, super);
+		return 0;
+	      }
+	    answer = ((tc->options->next_same_super_edge[0] != tc->options)
+		      ? &tc->rx_backtrack_frame
+		      : (df->effects
+			 ? &df->side_effects_frame
+			 : &df->future_frame));
+	  }
+	install_partial_transition (super, answer,
+				    trcset[offset], offset * 32);
+	rx_unlock_superstate (rx, super);
+	return answer;
+      }
+    }
+  else if (df->future) /* A cache miss on an edge with a future? Must be
+			* a semi-free destination. */
+    {				
+      if (df->future->is_semifree)
+	refresh_semifree_superstate (rx->cache, df->future);
+      return &df->future_frame;
+    }
+  else
+    /* no future superstate on an existing edge */
+    {
+      rx_lock_superstate (rx, super);
+      if (!solve_destination (rx, df))
+	{
+	  rx_unlock_superstate (rx, super);
+	  return 0;
+	}
+      if (!df->effects
+	  && (df->edge->options->next_same_super_edge[0] == df->edge->options))
+	install_partial_transition (super, &df->future_frame,
+				    df->edge->cset[offset], offset * 32);
+      rx_unlock_superstate (rx, super);
+      return &df->future_frame;
+    }
+}
+
+
+
+
+
+/* The rest of the code provides a regex.c compatable interface. */
+
+
+__const__ char *re_error_msg[] =
+{
+  0,						/* REG_NOUT */
+  "No match",					/* REG_NOMATCH */
+  "Invalid regular expression",			/* REG_BADPAT */
+  "Invalid collation character",		/* REG_ECOLLATE */
+  "Invalid character class name",		/* REG_ECTYPE */
+  "Trailing backslash",				/* REG_EESCAPE */
+  "Invalid back reference",			/* REG_ESUBREG */
+  "Unmatched [ or [^",				/* REG_EBRACK */
+  "Unmatched ( or \\(",				/* REG_EPAREN */
+  "Unmatched \\{",				/* REG_EBRACE */
+  "Invalid content of \\{\\}",			/* REG_BADBR */
+  "Invalid range end",				/* REG_ERANGE */
+  "Memory exhausted",				/* REG_ESPACE */
+  "Invalid preceding regular expression",	/* REG_BADRPT */
+  "Premature end of regular expression",	/* REG_EEND */
+  "Regular expression too big",			/* REG_ESIZE */
+  "Unmatched ) or \\)",				/* REG_ERPAREN */
+};
+
+
+
+
+/* 
+ * Macros used while compiling patterns.
+ *
+ * By convention, PEND points just past the end of the uncompiled pattern,
+ * P points to the read position in the pattern.  `translate' is the name
+ * of the translation table (`TRANSLATE' is the name of a macro that looks
+ * things up in `translate').
+ */
+
+
+/*
+ * Fetch the next character in the uncompiled pattern---translating it 
+ * if necessary. *Also cast from a signed character in the constant
+ * string passed to us by the user to an unsigned char that we can use
+ * as an array index (in, e.g., `translate').
+ */
+#define PATFETCH(c)							\
+ do {if (p == pend) return REG_EEND;					\
+    c = (unsigned char) *p++;						\
+    c = translate[c];		 					\
+ } while (0)
+
+/* 
+ * Fetch the next character in the uncompiled pattern, with no
+ * translation.
+ */
+#define PATFETCH_RAW(c)							\
+  do {if (p == pend) return REG_EEND;					\
+    c = (unsigned char) *p++; 						\
+  } while (0)
+
+/* Go backwards one character in the pattern.  */
+#define PATUNFETCH p--
+
+
+#define TRANSLATE(d) translate[(unsigned char) (d)]
+
+typedef unsigned regnum_t;
+
+/* Since offsets can go either forwards or backwards, this type needs to
+ * be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.
+ */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+  struct rexp_node ** top_expression; /* was begalt */
+  struct rexp_node ** last_expression; /* was laststart */
+  pattern_offset_t inner_group_offset;
+  regnum_t regnum;
+} compile_stack_elt_t;
+
+typedef struct
+{
+  compile_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;			/* Offset of next open position.  */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
+
+/* The next available element.  */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list.  */
+#define SET_LIST_BIT(c)                               \
+  (b[((unsigned char) (c)) / CHARBITS]               \
+   |= 1 << (((unsigned char) c) % CHARBITS))
+
+/* Get the next unsigned number in the uncompiled pattern.  */
+#define GET_UNSIGNED_NUMBER(num) 					\
+  { if (p != pend)							\
+     {									\
+       PATFETCH (c); 							\
+       while (isdigit (c)) 						\
+         { 								\
+           if (num < 0)							\
+              num = 0;							\
+           num = num * 10 + c - '0'; 					\
+           if (p == pend) 						\
+              break; 							\
+           PATFETCH (c);						\
+         } 								\
+       } 								\
+    }		
+
+#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+#define IS_CHAR_CLASS(string)						\
+   (!strcmp (string, "alpha") || !strcmp (string, "upper")		\
+    || !strcmp (string, "lower") || !strcmp (string, "digit")		\
+    || !strcmp (string, "alnum") || !strcmp (string, "xdigit")		\
+    || !strcmp (string, "space") || !strcmp (string, "print")		\
+    || !strcmp (string, "punct") || !strcmp (string, "graph")		\
+    || !strcmp (string, "cntrl") || !strcmp (string, "blank"))
+
+
+
+/* These predicates are used in regex_compile. */
+
+/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
+ * after an alternative or a begin-subexpression.  We assume there is at
+ * least one character before the ^.  
+ */
+
+#ifdef __STDC__
+static boolean
+at_begline_loc_p (__const__ char *pattern, __const__ char * p, reg_syntax_t syntax)
+#else
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+     __const__ char *pattern;
+     __const__ char * p;
+     reg_syntax_t syntax;
+#endif
+{
+  __const__ char *prev = p - 2;
+  boolean prev_prev_backslash = ((prev > pattern) && (prev[-1] == '\\'));
+  
+    return
+      
+      (/* After a subexpression?  */
+       ((*prev == '(') && ((syntax & RE_NO_BK_PARENS) || prev_prev_backslash))
+       ||
+       /* After an alternative?  */
+       ((*prev == '|') && ((syntax & RE_NO_BK_VBAR) || prev_prev_backslash))
+       );
+}
+
+/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
+ * at least one character after the $, i.e., `P < PEND'.
+ */
+
+#ifdef __STDC__
+static boolean
+at_endline_loc_p (__const__ char *p, __const__ char *pend, int syntax)
+#else
+static boolean
+at_endline_loc_p (p, pend, syntax)
+     __const__ char *p;
+     __const__ char *pend;
+     int syntax;
+#endif
+{
+  __const__ char *next = p;
+  boolean next_backslash = (*next == '\\');
+  __const__ char *next_next = (p + 1 < pend) ? (p + 1) : 0;
+  
+  return
+    (
+     /* Before a subexpression?  */
+     ((syntax & RE_NO_BK_PARENS)
+      ? (*next == ')')
+      : (next_backslash && next_next && (*next_next == ')')))
+    ||
+     /* Before an alternative?  */
+     ((syntax & RE_NO_BK_VBAR)
+      ? (*next == '|')
+      : (next_backslash && next_next && (*next_next == '|')))
+     );
+}
+
+
+
+unsigned char rx_id_translation[256] =
+{
+  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255
+};
+
+/* The compiler keeps an inverted translation table.
+ * This looks up/inititalize elements.
+ * VALID is an array of booleans that validate CACHE.
+ */
+
+#ifdef __STDC__
+static rx_Bitset
+inverse_translation (struct re_pattern_buffer * rxb,
+		     char * valid, rx_Bitset cache,
+		     unsigned char * translate, int c)
+#else
+static rx_Bitset
+inverse_translation (rxb, valid, cache, translate, c)
+     struct re_pattern_buffer * rxb;
+     char * valid;
+     rx_Bitset cache;
+     unsigned char * translate;
+     int c;
+#endif
+{
+  rx_Bitset cs
+    = cache + c * rx_bitset_numb_subsets (rxb->rx.local_cset_size); 
+
+  if (!valid[c])
+    {
+      int x;
+      int c_tr = TRANSLATE(c);
+      rx_bitset_null (rxb->rx.local_cset_size, cs);
+      for (x = 0; x < 256; ++x)	/* &&&& 13.37 */
+	if (TRANSLATE(x) == c_tr)
+	  RX_bitset_enjoin (cs, x);
+      valid[c] = 1;
+    }
+  return cs;
+}
+
+
+
+
+
+/* More subroutine declarations and macros for regex_compile.  */
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and 
+   false if it's not.  */
+
+#ifdef __STDC__
+static boolean
+group_in_compile_stack (compile_stack_type compile_stack, regnum_t regnum)
+#else
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+    compile_stack_type compile_stack;
+    regnum_t regnum;
+#endif
+{
+  int this_element;
+
+  for (this_element = compile_stack.avail - 1;  
+       this_element >= 0; 
+       this_element--)
+    if (compile_stack.stack[this_element].regnum == regnum)
+      return true;
+
+  return false;
+}
+
+
+/*
+ * Read the ending character of a range (in a bracket expression) from the
+ * uncompiled pattern *P_PTR (which ends at PEND).  We assume the
+ * starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
+ * Then we set the translation of all bits between the starting and
+ * ending characters (inclusive) in the compiled pattern B.
+ * 
+ * Return an error code.
+ * 
+ * We use these short variable names so we can use the same macros as
+ * `regex_compile' itself.  
+ */
+
+#ifdef __STDC__
+static reg_errcode_t
+compile_range (struct re_pattern_buffer * rxb, rx_Bitset cs,
+	       __const__ char ** p_ptr, __const__ char * pend,
+	       unsigned char * translate, reg_syntax_t syntax,
+	       rx_Bitset inv_tr,  char * valid_inv_tr)
+#else
+static reg_errcode_t
+compile_range (rxb, cs, p_ptr, pend, translate, syntax, inv_tr, valid_inv_tr)
+     struct re_pattern_buffer * rxb;
+     rx_Bitset cs;
+     __const__ char ** p_ptr;
+     __const__ char * pend;
+     unsigned char * translate;
+     reg_syntax_t syntax;
+     rx_Bitset inv_tr;
+     char * valid_inv_tr;
+#endif
+{
+  unsigned this_char;
+
+  __const__ char *p = *p_ptr;
+
+  unsigned char range_end;
+  unsigned char range_start = TRANSLATE(p[-2]);
+
+  if (p == pend)
+    return REG_ERANGE;
+
+  PATFETCH (range_end);
+
+  (*p_ptr)++;
+
+  if (range_start > range_end)
+    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+  for (this_char = range_start; this_char <= range_end; this_char++)
+    {
+      rx_Bitset it =
+	inverse_translation (rxb, valid_inv_tr, inv_tr, translate, this_char);
+      rx_bitset_union (rxb->rx.local_cset_size, cs, it);
+    }
+  
+  return REG_NOERROR;
+}
+
+
+
+/* This searches a regexp for backreference side effects.
+ * It fills in the array OUT with 1 at the index of every register pair
+ * referenced by a backreference.
+ *
+ * This is used to help optimize patterns for searching.  The information is
+ * useful because, if the caller doesn't want register values, backreferenced
+ * registers are the only registers for which we need rx_backtrack.
+ */
+
+#ifdef __STDC__
+static void
+find_backrefs (char * out, struct rexp_node * rexp,
+	       struct re_se_params * params)
+#else
+static void
+find_backrefs (out, rexp, params)
+     char * out;
+     struct rexp_node * rexp;
+     struct re_se_params * params;
+#endif
+{
+  if (rexp)
+    switch (rexp->type)
+      {
+      case r_cset:
+      case r_data:
+	return;
+      case r_alternate:
+      case r_concat:
+      case r_opt:
+      case r_star:
+      case r_2phase_star:
+	find_backrefs (out, rexp->params.pair.left, params);
+	find_backrefs (out, rexp->params.pair.right, params);
+	return;
+      case r_side_effect:
+	if (   ((long)rexp->params.side_effect >= 0)
+	    && (params [(long)rexp->params.side_effect].se == re_se_backref))
+	  out[ params [(long)rexp->params.side_effect].op1] = 1;
+	return;
+      }
+}
+
+
+
+
+/* Returns 0 unless the pattern can match the empty string. */
+
+#ifdef __STDC__
+static int
+compute_fastset (struct re_pattern_buffer * rxb, struct rexp_node * rexp)
+#else
+static int
+compute_fastset (rxb, rexp)
+     struct re_pattern_buffer * rxb;
+     struct rexp_node * rexp;
+#endif
+{
+  if (!rexp)
+    return 1;
+  switch (rexp->type)
+    {
+    case r_data:
+      return 1;
+    case r_cset:
+      {
+	rx_bitset_union (rxb->rx.local_cset_size,
+			 rxb->fastset, rexp->params.cset);
+      }
+      return 0;
+    case r_concat:
+      return (compute_fastset (rxb, rexp->params.pair.left)
+	      && compute_fastset (rxb, rexp->params.pair.right));
+    case r_2phase_star:
+      compute_fastset (rxb, rexp->params.pair.left);
+      /* compute_fastset (rxb, rexp->params.pair.right);  nope... */
+      return 1;
+    case r_alternate:
+      return !!(compute_fastset (rxb, rexp->params.pair.left)
+		+ compute_fastset (rxb, rexp->params.pair.right));
+    case r_opt:
+    case r_star:
+      compute_fastset (rxb, rexp->params.pair.left);
+      return 1;
+    case r_side_effect:
+      return 1;
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+/* returns
+ *  1 -- yes, definately anchored by the given side effect.
+ *  2 -- maybe anchored, maybe the empty string.
+ *  0 -- definately not anchored
+ *  There is simply no other possibility.
+ */
+
+#ifdef __STDC__
+static int
+is_anchored (struct rexp_node * rexp, rx_side_effect se)
+#else
+static int
+is_anchored (rexp, se)
+     struct rexp_node * rexp;
+     rx_side_effect se;
+#endif
+{
+  if (!rexp)
+    return 2;
+  switch (rexp->type)
+    {
+    case r_cset:
+    case r_data:
+      return 0;
+    case r_concat:
+    case r_2phase_star:
+      {
+	int l = is_anchored (rexp->params.pair.left, se);
+	return (l == 2 ? is_anchored (rexp->params.pair.right, se) : l);
+      }
+    case r_alternate:
+      {
+	int l = is_anchored (rexp->params.pair.left, se);
+	int r = l ? is_anchored (rexp->params.pair.right, se) : 0;
+
+	if (l == r)
+	  return l;
+	else if ((l == 0) || (r == 0))
+	  return 0;
+	else
+	  return 2;
+      }
+    case r_opt:
+    case r_star:
+      return is_anchored (rexp->params.pair.left, se) ? 2 : 0;
+      
+    case r_side_effect:
+      return ((rexp->params.side_effect == se)
+	      ? 1 : 2);
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+/* This removes register assignments that aren't required by backreferencing.
+ * This can speed up explore_future, especially if it eliminates
+ * non-determinism in the superstate NFA.
+ * 
+ * NEEDED is an array of characters, presumably filled in by FIND_BACKREFS.
+ * The non-zero elements of the array indicate which register assignments
+ * can NOT be removed from the expression.
+ */
+
+#ifdef __STDC__
+static struct rexp_node *
+remove_unecessary_side_effects (struct rx * rx, char * needed,
+				struct rexp_node * rexp,
+				struct re_se_params * params)
+#else
+static struct rexp_node *
+remove_unecessary_side_effects (rx, needed, rexp, params)
+     struct rx * rx;
+     char * needed;
+     struct rexp_node * rexp;
+     struct re_se_params * params;
+#endif
+{
+  struct rexp_node * l;
+  struct rexp_node * r;
+  if (!rexp)
+    return 0;
+  else
+    switch (rexp->type)
+      {
+      case r_cset:
+      case r_data:
+	return rexp;
+      case r_alternate:
+      case r_concat:
+      case r_2phase_star:
+	l = remove_unecessary_side_effects (rx, needed,
+					    rexp->params.pair.left, params);
+	r = remove_unecessary_side_effects (rx, needed,
+					    rexp->params.pair.right, params);
+	if ((l && r) || (rexp->type != r_concat))
+	  {
+	    rexp->params.pair.left = l;
+	    rexp->params.pair.right = r;
+	    return rexp;
+	  }
+	else
+	  {
+	    rexp->params.pair.left = rexp->params.pair.right = 0;
+	    rx_free_rexp (rx, rexp);
+	    return l ? l : r;
+	  }
+      case r_opt:
+      case r_star:
+	l = remove_unecessary_side_effects (rx, needed,
+					    rexp->params.pair.left, params);
+	if (l)
+	  {
+	    rexp->params.pair.left = l;
+	    return rexp;
+	  }
+	else
+	  {
+	    rexp->params.pair.left = 0;
+	    rx_free_rexp (rx, rexp);
+	    return 0;
+	  }
+      case r_side_effect:
+	{
+	  int se = (long)rexp->params.side_effect;
+	  if (   (se >= 0)
+	      && (   ((enum re_side_effects)params[se].se == re_se_lparen)
+		  || ((enum re_side_effects)params[se].se == re_se_rparen))
+	      && (params [se].op1 > 0)
+	      && (!needed [params [se].op1]))
+	    {
+	      rx_free_rexp (rx, rexp);
+	      return 0;
+	    }
+	  else
+	    return rexp;
+	}
+      }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+
+#ifdef __STDC__
+static int
+pointless_if_repeated (struct rexp_node * node, struct re_se_params * params)
+#else
+static int
+pointless_if_repeated (node, params)
+     struct rexp_node * node;
+     struct re_se_params * params;
+#endif
+{
+  if (!node)
+    return 1;
+  switch (node->type)
+    {
+    case r_cset:
+      return 0;
+    case r_alternate:
+    case r_concat:
+    case r_2phase_star:
+      return (pointless_if_repeated (node->params.pair.left, params)
+	      && pointless_if_repeated (node->params.pair.right, params));
+    case r_opt:
+    case r_star:
+      return pointless_if_repeated (node->params.pair.left, params);
+    case r_side_effect:
+      switch ((enum re_side_effects) (((long)node->params.side_effect < 0)
+	      ? (enum re_side_effects) (long) node->params.side_effect
+	      : (enum re_side_effects)params[(long)node->params.side_effect].se))
+	{
+	case re_se_try:
+	case re_se_at_dot:
+	case re_se_begbuf:
+	case re_se_hat:
+	case re_se_wordbeg:
+	case re_se_wordbound:
+	case re_se_notwordbound:
+	case re_se_wordend:
+	case re_se_endbuf:
+	case re_se_dollar:
+	case re_se_fail:
+	case re_se_win:
+	  return 1;
+	case re_se_lparen:
+	case re_se_rparen:
+	case re_se_iter:
+	case re_se_end_iter:
+	case re_se_syntax:
+	case re_se_not_syntax:
+	case re_se_backref:
+	  return 0;
+	}
+    case r_data:
+    default:
+      return 0;
+    }
+}
+
+
+
+
+#ifdef __STDC__
+static int
+registers_on_stack (struct re_pattern_buffer * rxb,
+		    struct rexp_node * rexp, int in_danger,
+		    struct re_se_params * params)
+#else
+static int
+registers_on_stack (rxb, rexp, in_danger, params)
+     struct re_pattern_buffer * rxb;
+     struct rexp_node * rexp;
+     int in_danger;
+     struct re_se_params * params;
+#endif
+{
+  if (!rexp)
+    return 0;
+  else
+    switch (rexp->type)
+      {
+      case r_cset:
+      case r_data:
+	return 0;
+      case r_alternate:
+      case r_concat:
+	return (   registers_on_stack (rxb, rexp->params.pair.left,
+				       in_danger, params)
+		|| (registers_on_stack
+		    (rxb, rexp->params.pair.right,
+		     in_danger, params)));
+      case r_opt:
+	return registers_on_stack (rxb, rexp->params.pair.left, 0, params);
+      case r_star:
+	return registers_on_stack (rxb, rexp->params.pair.left, 1, params);
+      case r_2phase_star:
+	return
+	  (   registers_on_stack (rxb, rexp->params.pair.left, 1, params)
+	   || registers_on_stack (rxb, rexp->params.pair.right, 1, params));
+      case r_side_effect:
+	{
+	  int se = (long)rexp->params.side_effect;
+	  if (   in_danger
+	      && (se >= 0)
+	      && (params [se].op1 > 0)
+	      && (   ((enum re_side_effects)params[se].se == re_se_lparen)
+		  || ((enum re_side_effects)params[se].se == re_se_rparen)))
+	    return 1;
+	  else
+	    return 0;
+	}
+      }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+
+static char idempotent_complex_se[] =
+{
+#define RX_WANT_SE_DEFS 1
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#define RX_DEF_SE(IDEM, NAME, VALUE)	      
+#define RX_DEF_CPLX_SE(IDEM, NAME, VALUE)     IDEM,
+#include "rx.h"
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#undef RX_WANT_SE_DEFS
+  23
+};
+
+static char idempotent_se[] =
+{
+  13,
+#define RX_WANT_SE_DEFS 1
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#define RX_DEF_SE(IDEM, NAME, VALUE)	      IDEM,
+#define RX_DEF_CPLX_SE(IDEM, NAME, VALUE)     
+#include "rx.h"
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#undef RX_WANT_SE_DEFS
+  42
+};
+
+
+
+
+
+#ifdef __STDC__
+static int
+has_any_se (struct rx * rx,
+	    struct rexp_node * rexp)
+#else
+static int
+has_any_se (rx, rexp)
+     struct rx * rx;
+     struct rexp_node * rexp;
+#endif
+{
+  if (!rexp)
+    return 0;
+
+  switch (rexp->type)
+    {
+    case r_cset:
+    case r_data:
+      return 0;
+
+    case r_side_effect:
+      return 1;
+      
+    case r_2phase_star:
+    case r_concat:
+    case r_alternate:
+      return
+	(   has_any_se (rx, rexp->params.pair.left)
+	 || has_any_se (rx, rexp->params.pair.right));
+
+    case r_opt:
+    case r_star:
+      return has_any_se (rx, rexp->params.pair.left);
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+
+/* This must be called AFTER `convert_hard_loops' for a given REXP. */
+#ifdef __STDC__
+static int
+has_non_idempotent_epsilon_path (struct rx * rx,
+				 struct rexp_node * rexp,
+				 struct re_se_params * params)
+#else
+static int
+has_non_idempotent_epsilon_path (rx, rexp, params)
+     struct rx * rx;
+     struct rexp_node * rexp;
+     struct re_se_params * params;
+#endif
+{
+  if (!rexp)
+    return 0;
+
+  switch (rexp->type)
+    {
+    case r_cset:
+    case r_data:
+    case r_star:
+      return 0;
+
+    case r_side_effect:
+      return
+	!((long)rexp->params.side_effect > 0
+	  ? idempotent_complex_se [ params [(long)rexp->params.side_effect].se ]
+	  : idempotent_se [-(long)rexp->params.side_effect]);
+      
+    case r_alternate:
+      return
+	(   has_non_idempotent_epsilon_path (rx,
+					     rexp->params.pair.left, params)
+	 || has_non_idempotent_epsilon_path (rx,
+					     rexp->params.pair.right, params));
+
+    case r_2phase_star:
+    case r_concat:
+      return
+	(   has_non_idempotent_epsilon_path (rx,
+					     rexp->params.pair.left, params)
+	 && has_non_idempotent_epsilon_path (rx,
+					     rexp->params.pair.right, params));
+
+    case r_opt:
+      return has_non_idempotent_epsilon_path (rx,
+					      rexp->params.pair.left, params);
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+
+/* This computes rougly what it's name suggests.   It can (and does) go wrong 
+ * in the direction of returning spurious 0 without causing disasters.
+ */
+#ifdef __STDC__
+static int
+begins_with_complex_se (struct rx * rx, struct rexp_node * rexp)
+#else
+static int
+begins_with_complex_se (rx, rexp)
+     struct rx * rx;
+     struct rexp_node * rexp;
+#endif
+{
+  if (!rexp)
+    return 0;
+
+  switch (rexp->type)
+    {
+    case r_cset:
+    case r_data:
+      return 0;
+
+    case r_side_effect:
+      return ((long)rexp->params.side_effect >= 0);
+      
+    case r_alternate:
+      return
+	(   begins_with_complex_se (rx, rexp->params.pair.left)
+	 && begins_with_complex_se (rx, rexp->params.pair.right));
+
+
+    case r_concat:
+      return has_any_se (rx, rexp->params.pair.left);
+    case r_opt:
+    case r_star:
+    case r_2phase_star:
+      return 0;
+    }
+
+  /* this should never happen */
+  return 0;
+}
+
+
+
+/* This destructively removes some of the re_se_tv side effects from 
+ * a rexp tree.  In particular, during parsing re_se_tv was inserted on the
+ * right half of every | to guarantee that posix path preference could be 
+ * honored.  This function removes some which it can be determined aren't 
+ * needed.  
+ */
+
+#ifdef __STDC__
+static void
+speed_up_alt (struct rx * rx,
+	      struct rexp_node * rexp,
+	      int unposix)
+#else
+static void
+speed_up_alt (rx, rexp, unposix)
+     struct rx * rx;
+     struct rexp_node * rexp;
+     int unposix;
+#endif
+{
+  if (!rexp)
+    return;
+
+  switch (rexp->type)
+    {
+    case r_cset:
+    case r_data:
+    case r_side_effect:
+      return;
+
+    case r_opt:
+    case r_star:
+      speed_up_alt (rx, rexp->params.pair.left, unposix);
+      return;
+
+    case r_2phase_star:
+    case r_concat:
+      speed_up_alt (rx, rexp->params.pair.left, unposix);
+      speed_up_alt (rx, rexp->params.pair.right, unposix);
+      return;
+
+    case r_alternate:
+      /* the right child is guaranteed to be (concat re_se_tv <subexp>) */
+
+      speed_up_alt (rx, rexp->params.pair.left, unposix);
+      speed_up_alt (rx, rexp->params.pair.right->params.pair.right, unposix);
+      
+      if (   unposix
+	  || (begins_with_complex_se
+	      (rx, rexp->params.pair.right->params.pair.right))
+	  || !(   has_any_se (rx, rexp->params.pair.right->params.pair.right)
+	       || has_any_se (rx, rexp->params.pair.left)))
+	{
+	  struct rexp_node * conc = rexp->params.pair.right;
+	  rexp->params.pair.right = conc->params.pair.right;
+	  conc->params.pair.right = 0;
+	  rx_free_rexp (rx, conc);
+	}
+    }
+}
+
+
+
+
+
+
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+   Returns one of error codes defined in `regex.h', or zero for success.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate'
+   fields are set in BUFP on entry.
+
+   If it succeeds, results are put in BUFP (if it returns an error, the
+   contents of BUFP are undefined):
+     `buffer' is the compiled pattern;
+     `syntax' is set to SYNTAX;
+     `used' is set to the length of the compiled pattern;
+     `fastmap_accurate' is set to zero;
+     `re_nsub' is set to the number of groups in PATTERN;
+     `not_bol' and `not_eol' are set to zero.
+   
+   The `fastmap' and `newline_anchor' fields are neither
+   examined nor set.  */
+
+
+
+#ifdef __STDC__
+RX_DECL reg_errcode_t
+rx_compile (__const__ char *pattern, int size,
+	    reg_syntax_t syntax,
+	    struct re_pattern_buffer * rxb) 
+#else
+RX_DECL reg_errcode_t
+rx_compile (pattern, size, syntax, rxb)
+     __const__ char *pattern;
+     int size;
+     reg_syntax_t syntax;
+     struct re_pattern_buffer * rxb;
+#endif
+{
+  RX_subset
+    inverse_translate [CHAR_SET_SIZE * rx_bitset_numb_subsets(CHAR_SET_SIZE)];
+  char
+    validate_inv_tr [CHAR_SET_SIZE * rx_bitset_numb_subsets(CHAR_SET_SIZE)];
+
+  /* We fetch characters from PATTERN here.  Even though PATTERN is
+     `char *' (i.e., signed), we declare these variables as unsigned, so
+     they can be reliably used as array indices.  */
+  register unsigned char c, c1;
+  
+  /* A random tempory spot in PATTERN.  */
+  __const__ char *p1;
+  
+  /* Keeps track of unclosed groups.  */
+  compile_stack_type compile_stack;
+
+  /* Points to the current (ending) position in the pattern.  */
+  __const__ char *p = pattern;
+  __const__ char *pend = pattern + size;
+  
+  /* How to translate the characters in the pattern.  */
+  unsigned char *translate = (rxb->translate
+			      ? rxb->translate
+			      : rx_id_translation);
+
+  /* When parsing is done, this will hold the expression tree. */
+  struct rexp_node * rexp = 0;
+
+  /* In the midst of compilation, this holds onto the regexp 
+   * first parst while rexp goes on to aquire additional constructs.
+   */
+  struct rexp_node * orig_rexp = 0;
+  struct rexp_node * fewer_side_effects = 0;
+
+  /* This and top_expression are saved on the compile stack. */
+  struct rexp_node ** top_expression = &rexp;
+  struct rexp_node ** last_expression = top_expression;
+  
+  /* Parameter to `goto append_node' */
+  struct rexp_node * append;
+
+  /* Counts open-groups as they are encountered.  This is the index of the
+   * innermost group being compiled.
+   */
+  regnum_t regnum = 0;
+
+  /* Place in the uncompiled pattern (i.e., the {) to
+   * which to go back if the interval is invalid.  
+   */
+  __const__ char *beg_interval;
+
+  struct re_se_params * params = 0;
+  int paramc = 0;		/* How many complex side effects so far? */
+
+  rx_side_effect side;		/* param to `goto add_side_effect' */
+
+  bzero (validate_inv_tr, sizeof (validate_inv_tr));
+
+  rxb->rx.instruction_table = rx_id_instruction_table;
+
+
+  /* Initialize the compile stack.  */
+  compile_stack.stack =  (( compile_stack_elt_t *) malloc ((INIT_COMPILE_STACK_SIZE) * sizeof ( compile_stack_elt_t)));
+  if (compile_stack.stack == 0)
+    return REG_ESPACE;
+
+  compile_stack.size = INIT_COMPILE_STACK_SIZE;
+  compile_stack.avail = 0;
+
+  /* Initialize the pattern buffer.  */
+  rxb->rx.cache = &default_cache;
+  rxb->syntax = syntax;
+  rxb->fastmap_accurate = 0;
+  rxb->not_bol = rxb->not_eol = 0;
+  rxb->least_subs = 0;
+  
+  /* Always count groups, whether or not rxb->no_sub is set.  
+   * The whole pattern is implicitly group 0, so counting begins
+   * with 1.
+   */
+  rxb->re_nsub = 0;
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+  /* Initialize the syntax table.  */
+   init_syntax_once ();
+#endif
+
+  /* Loop through the uncompiled pattern until we're at the end.  */
+  while (p != pend)
+    {
+      PATFETCH (c);
+
+      switch (c)
+        {
+        case '^':
+          {
+            if (   /* If at start of pattern, it's an operator.  */
+                   p == pattern + 1
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's come before.  */
+                || at_begline_loc_p (pattern, p, syntax))
+	      {
+		struct rexp_node * n
+		  = rx_mk_r_side_effect (&rxb->rx, (rx_side_effect)re_se_hat);
+		if (!n)
+		  return REG_ESPACE;
+		append = n;
+		goto append_node;
+	      }
+            else
+              goto normal_char;
+          }
+          break;
+
+
+        case '$':
+          {
+            if (   /* If at end of pattern, it's an operator.  */
+                   p == pend 
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's next.  */
+                || at_endline_loc_p (p, pend, syntax))
+	      {
+		struct rexp_node * n
+		  = rx_mk_r_side_effect (&rxb->rx, (rx_side_effect)re_se_dollar);
+		if (!n)
+		  return REG_ESPACE;
+		append = n;
+		goto append_node;
+	      }
+             else
+               goto normal_char;
+           }
+           break;
+
+
+	case '+':
+        case '?':
+          if ((syntax & RE_BK_PLUS_QM)
+              || (syntax & RE_LIMITED_OPS))
+            goto normal_char;
+
+        handle_plus:
+        case '*':
+          /* If there is no previous pattern... */
+          if (pointless_if_repeated (*last_expression, params))
+            {
+              if (syntax & RE_CONTEXT_INVALID_OPS)
+                return REG_BADRPT;
+              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                goto normal_char;
+            }
+
+          {
+            /* 1 means zero (many) matches is allowed.  */
+            char zero_times_ok = 0, many_times_ok = 0;
+
+            /* If there is a sequence of repetition chars, collapse it
+               down to just one (the right one).  We can't combine
+               interval operators with these because of, e.g., `a{2}*',
+               which should only match an even number of `a's.  */
+
+            for (;;)
+              {
+                zero_times_ok |= c != '+';
+                many_times_ok |= c != '?';
+
+                if (p == pend)
+                  break;
+
+                PATFETCH (c);
+
+                if (c == '*'
+                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+                  ;
+
+                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+
+                    PATFETCH (c1);
+                    if (!(c1 == '+' || c1 == '?'))
+                      {
+                        PATUNFETCH;
+                        PATUNFETCH;
+                        break;
+                      }
+
+                    c = c1;
+                  }
+                else
+                  {
+                    PATUNFETCH;
+                    break;
+                  }
+
+                /* If we get here, we found another repeat character.  */
+               }
+
+            /* Star, etc. applied to an empty pattern is equivalent
+               to an empty pattern.  */
+            if (!last_expression)
+              break;
+
+	    /* Now we know whether or not zero matches is allowed
+	     * and also whether or not two or more matches is allowed.
+	     */
+
+	    {
+	      struct rexp_node * inner_exp = *last_expression;
+	      int need_sync = 0;
+
+	      if (many_times_ok
+		  && has_non_idempotent_epsilon_path (&rxb->rx,
+						      inner_exp, params))
+		{
+		  struct rexp_node * pusher
+		    = rx_mk_r_side_effect (&rxb->rx,
+					   (rx_side_effect)re_se_pushpos);
+		  struct rexp_node * checker
+		    = rx_mk_r_side_effect (&rxb->rx,
+					   (rx_side_effect)re_se_chkpos);
+		  struct rexp_node * pushback
+		    = rx_mk_r_side_effect (&rxb->rx,
+					   (rx_side_effect)re_se_pushback);
+		  rx_Bitset cs = rx_cset (&rxb->rx);
+		  struct rexp_node * lit_t = rx_mk_r_cset (&rxb->rx, cs);
+		  struct rexp_node * fake_state
+		    = rx_mk_r_concat (&rxb->rx, pushback, lit_t);
+		  struct rexp_node * phase2
+		    = rx_mk_r_concat (&rxb->rx, checker, fake_state);
+		  struct rexp_node * popper
+		    = rx_mk_r_side_effect (&rxb->rx,
+					   (rx_side_effect)re_se_poppos);
+		  struct rexp_node * star
+		    = rx_mk_r_2phase_star (&rxb->rx, inner_exp, phase2);
+		  struct rexp_node * a
+		    = rx_mk_r_concat (&rxb->rx, pusher, star);
+		  struct rexp_node * whole_thing
+		    = rx_mk_r_concat (&rxb->rx, a, popper);
+		  if (!(pusher && star && pushback && lit_t && fake_state
+			&& lit_t && phase2 && checker && popper
+			&& a && whole_thing))
+		    return REG_ESPACE;
+		  RX_bitset_enjoin (cs, 't');
+		  *last_expression = whole_thing;
+		}
+	      else
+		{
+		  struct rexp_node * star =
+		    (many_times_ok ? rx_mk_r_star : rx_mk_r_opt)
+		      (&rxb->rx, *last_expression);
+		  if (!star)
+		    return REG_ESPACE;
+		  *last_expression = star;
+		  need_sync = has_any_se (&rxb->rx, *last_expression);
+		}
+	      if (!zero_times_ok)
+		{
+		  struct rexp_node * concat
+		    = rx_mk_r_concat (&rxb->rx, inner_exp,
+				      rx_copy_rexp (&rxb->rx,
+						    *last_expression));
+		  if (!concat)
+		    return REG_ESPACE;
+		  *last_expression = concat;
+		}
+	      if (need_sync)
+		{
+		  int sync_se = paramc;
+		  params = (params
+			    ? ((struct re_se_params *)
+			       realloc (params,
+					sizeof (*params) * (1 + paramc)))
+			    : ((struct re_se_params *)
+			       malloc (sizeof (*params))));
+		  if (!params)
+		    return REG_ESPACE;
+		  ++paramc;
+		  params [sync_se].se = re_se_tv;
+		  side = (rx_side_effect)sync_se;
+		  goto add_side_effect;
+		}
+	    }
+	    /* The old regex.c used to optimize `.*\n'.  
+	     * Maybe rx should too?
+	     */
+	  }
+	  break;
+
+
+	case '.':
+	  {
+	    rx_Bitset cs = rx_cset (&rxb->rx);
+	    struct rexp_node * n = rx_mk_r_cset (&rxb->rx, cs);
+	    if (!(cs && n))
+	      return REG_ESPACE;
+
+	    rx_bitset_universe (rxb->rx.local_cset_size, cs);
+	    if (!(rxb->syntax & RE_DOT_NEWLINE))
+	      RX_bitset_remove (cs, '\n');
+	    if (!(rxb->syntax & RE_DOT_NOT_NULL))
+	      RX_bitset_remove (cs, 0);
+
+	    append = n;
+	    goto append_node;
+	    break;
+	  }
+
+
+        case '[':
+	  if (p == pend) return REG_EBRACK;
+          {
+            boolean had_char_class = false;
+	    rx_Bitset cs = rx_cset (&rxb->rx);
+	    struct rexp_node * node = rx_mk_r_cset (&rxb->rx, cs);
+	    int is_inverted = *p == '^';
+	    
+	    if (!(node && cs))
+	      return REG_ESPACE;
+	    
+	    /* This branch of the switch is normally exited with
+	     *`goto append_node'
+	     */
+	    append = node;
+	    
+            if (is_inverted)
+	      p++;
+	    
+            /* Remember the first position in the bracket expression.  */
+            p1 = p;
+	    
+            /* Read in characters and ranges, setting map bits.  */
+            for (;;)
+              {
+                if (p == pend) return REG_EBRACK;
+		
+                PATFETCH (c);
+		
+                /* \ might escape characters inside [...] and [^...].  */
+                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+		    
+                    PATFETCH (c1);
+		    {
+		      rx_Bitset it = inverse_translation (rxb, 
+							  validate_inv_tr,
+							  inverse_translate,
+							  translate,
+							  c1);
+		      rx_bitset_union (rxb->rx.local_cset_size, cs, it);
+		    }
+                    continue;
+                  }
+		
+                /* Could be the end of the bracket expression.  If it's
+                   not (i.e., when the bracket expression is `[]' so
+                   far), the ']' character bit gets set way below.  */
+                if (c == ']' && p != p1 + 1)
+                  goto finalize_class_and_append;
+		
+                /* Look ahead to see if it's a range when the last thing
+                   was a character class.  */
+                if (had_char_class && c == '-' && *p != ']')
+                  return REG_ERANGE;
+		
+                /* Look ahead to see if it's a range when the last thing
+                   was a character: if this is a hyphen not at the
+                   beginning or the end of a list, then it's the range
+                   operator.  */
+                if (c == '-' 
+                    && !(p - 2 >= pattern && p[-2] == '[') 
+                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+                    && *p != ']')
+                  {
+                    reg_errcode_t ret
+                      = compile_range (rxb, cs, &p, pend, translate, syntax,
+				       inverse_translate, validate_inv_tr);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+		
+                else if (p[0] == '-' && p[1] != ']')
+                  { /* This handles ranges made up of characters only.  */
+                    reg_errcode_t ret;
+		    
+		    /* Move past the `-'.  */
+                    PATFETCH (c1);
+                    
+                    ret = compile_range (rxb, cs, &p, pend, translate, syntax,
+					 inverse_translate, validate_inv_tr);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+		
+                /* See if we're at the beginning of a possible character
+                   class.  */
+		
+		else if ((syntax & RE_CHAR_CLASSES)
+			 && (c == '[') && (*p == ':'))
+                  {
+                    char str[CHAR_CLASS_MAX_LENGTH + 1];
+		    
+                    PATFETCH (c);
+                    c1 = 0;
+		    
+                    /* If pattern is `[[:'.  */
+                    if (p == pend) return REG_EBRACK;
+		    
+                    for (;;)
+                      {
+                        PATFETCH (c);
+                        if (c == ':' || c == ']' || p == pend
+                            || c1 == CHAR_CLASS_MAX_LENGTH)
+			  break;
+                        str[c1++] = c;
+                      }
+                    str[c1] = '\0';
+		    
+                    /* If isn't a word bracketed by `[:' and:`]':
+                       undo the ending character, the letters, and leave 
+                       the leading `:' and `[' (but set bits for them).  */
+                    if (c == ':' && *p == ']')
+                      {
+                        int ch;
+                        boolean is_alnum = !strcmp (str, "alnum");
+                        boolean is_alpha = !strcmp (str, "alpha");
+                        boolean is_blank = !strcmp (str, "blank");
+                        boolean is_cntrl = !strcmp (str, "cntrl");
+                        boolean is_digit = !strcmp (str, "digit");
+                        boolean is_graph = !strcmp (str, "graph");
+                        boolean is_lower = !strcmp (str, "lower");
+                        boolean is_print = !strcmp (str, "print");
+                        boolean is_punct = !strcmp (str, "punct");
+                        boolean is_space = !strcmp (str, "space");
+                        boolean is_upper = !strcmp (str, "upper");
+                        boolean is_xdigit = !strcmp (str, "xdigit");
+                        
+                        if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+			
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);					
+			
+                        if (p == pend) return REG_EBRACK;
+			
+                        for (ch = 0; ch < 1 << CHARBITS; ch++)
+                          {
+                            if (   (is_alnum  && isalnum (ch))
+                                || (is_alpha  && isalpha (ch))
+                                || (is_blank  && isblank (ch))
+                                || (is_cntrl  && iscntrl (ch))
+                                || (is_digit  && isdigit (ch))
+                                || (is_graph  && isgraph (ch))
+                                || (is_lower  && islower (ch))
+                                || (is_print  && isprint (ch))
+                                || (is_punct  && ispunct (ch))
+                                || (is_space  && isspace (ch))
+                                || (is_upper  && isupper (ch))
+                                || (is_xdigit && isxdigit (ch)))
+			      {
+				rx_Bitset it =
+				  inverse_translation (rxb, 
+						       validate_inv_tr,
+						       inverse_translate,
+						       translate,
+						       ch);
+				rx_bitset_union (rxb->rx.local_cset_size,
+						 cs, it);
+			      }
+                          }
+                        had_char_class = true;
+                      }
+                    else
+                      {
+                        c1++;
+                        while (c1--)    
+                          PATUNFETCH;
+			{
+			  rx_Bitset it =
+			    inverse_translation (rxb, 
+						 validate_inv_tr,
+						 inverse_translate,
+						 translate,
+						 '[');
+			  rx_bitset_union (rxb->rx.local_cset_size,
+					   cs, it);
+			}
+			{
+			  rx_Bitset it =
+			    inverse_translation (rxb, 
+						 validate_inv_tr,
+						 inverse_translate,
+						 translate,
+						 ':');
+			  rx_bitset_union (rxb->rx.local_cset_size,
+					   cs, it);
+			}
+                        had_char_class = false;
+                      }
+                  }
+                else
+                  {
+                    had_char_class = false;
+		    {
+		      rx_Bitset it = inverse_translation (rxb, 
+							  validate_inv_tr,
+							  inverse_translate,
+							  translate,
+							  c);
+		      rx_bitset_union (rxb->rx.local_cset_size, cs, it);
+		    }
+                  }
+              }
+
+	  finalize_class_and_append:
+	    if (is_inverted)
+	      {
+		rx_bitset_complement (rxb->rx.local_cset_size, cs);
+		if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+		  RX_bitset_remove (cs, '\n');
+	      }
+	    goto append_node;
+          }
+          break;
+
+
+	case '(':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_open;
+          else
+            goto normal_char;
+
+
+        case ')':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_close;
+          else
+            goto normal_char;
+
+
+        case '\n':
+          if (syntax & RE_NEWLINE_ALT)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+	case '|':
+          if (syntax & RE_NO_BK_VBAR)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+        case '{':
+	  if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+	    goto handle_interval;
+	  else
+	    goto normal_char;
+
+
+        case '\\':
+          if (p == pend) return REG_EESCAPE;
+
+          /* Do not translate the character after the \, so that we can
+             distinguish, e.g., \B from \b, even if we normally would
+             translate, e.g., B to b.  */
+          PATFETCH_RAW (c);
+
+          switch (c)
+            {
+            case '(':
+              if (syntax & RE_NO_BK_PARENS)
+                goto normal_backslash;
+
+            handle_open:
+              rxb->re_nsub++;
+              regnum++;
+              if (COMPILE_STACK_FULL)
+                { 
+                  ((compile_stack.stack) =
+		   (compile_stack_elt_t *) realloc (compile_stack.stack, ( compile_stack.size << 1) * sizeof (
+													      compile_stack_elt_t)));
+                  if (compile_stack.stack == 0) return REG_ESPACE;
+
+                  compile_stack.size <<= 1;
+                }
+
+	      if (*last_expression)
+		{
+		  struct rexp_node * concat
+		    = rx_mk_r_concat (&rxb->rx, *last_expression, 0);
+		  if (!concat)
+		    return REG_ESPACE;
+		  *last_expression = concat;
+		  last_expression = &concat->params.pair.right;
+		}
+
+              /*
+	       * These are the values to restore when we hit end of this
+               * group.  
+	       */
+	      COMPILE_STACK_TOP.top_expression = top_expression;
+	      COMPILE_STACK_TOP.last_expression = last_expression;
+              COMPILE_STACK_TOP.regnum = regnum;
+	      
+              compile_stack.avail++;
+	      
+	      top_expression = last_expression;
+	      break;
+
+
+            case ')':
+              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+            handle_close:
+              /* See similar code for backslashed left paren above.  */
+              if (COMPILE_STACK_EMPTY)
+                if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                  goto normal_char;
+                else
+                  return REG_ERPAREN;
+
+              /* Since we just checked for an empty stack above, this
+                 ``can't happen''.  */
+
+              {
+                /* We don't just want to restore into `regnum', because
+                   later groups should continue to be numbered higher,
+                   as in `(ab)c(de)' -- the second group is #2.  */
+                regnum_t this_group_regnum;
+		struct rexp_node ** inner = top_expression;
+
+                compile_stack.avail--;
+		top_expression = COMPILE_STACK_TOP.top_expression;
+		last_expression = COMPILE_STACK_TOP.last_expression;
+                this_group_regnum = COMPILE_STACK_TOP.regnum;
+		{
+		  int left_se = paramc;
+		  int right_se = paramc + 1;
+
+		  params = (params
+			    ? ((struct re_se_params *)
+			       realloc (params,
+					(paramc + 2) * sizeof (params[0])))
+			    : ((struct re_se_params *)
+			       malloc (2 * sizeof (params[0]))));
+		  if (!params)
+		    return REG_ESPACE;
+		  paramc += 2;
+
+		  params[left_se].se = re_se_lparen;
+		  params[left_se].op1 = this_group_regnum;
+		  params[right_se].se = re_se_rparen;
+		  params[right_se].op1 = this_group_regnum;
+		  {
+		    struct rexp_node * left
+		      = rx_mk_r_side_effect (&rxb->rx,
+					     (rx_side_effect)left_se);
+		    struct rexp_node * right
+		      = rx_mk_r_side_effect (&rxb->rx,
+					     (rx_side_effect)right_se);
+		    struct rexp_node * c1
+		      = (*inner
+			 ? rx_mk_r_concat (&rxb->rx, left, *inner) : left);
+		    struct rexp_node * c2
+		      = rx_mk_r_concat (&rxb->rx, c1, right);
+		    if (!(left && right && c1 && c2))
+		      return REG_ESPACE;
+		    *inner = c2;
+		  }
+		}
+		break;
+	      }
+
+            case '|':					/* `\|'.  */
+              if ((syntax & RE_LIMITED_OPS) || (syntax & RE_NO_BK_VBAR))
+                goto normal_backslash;
+            handle_alt:
+              if (syntax & RE_LIMITED_OPS)
+                goto normal_char;
+
+	      {
+		struct rexp_node * alt
+		  = rx_mk_r_alternate (&rxb->rx, *top_expression, 0);
+		if (!alt)
+		  return REG_ESPACE;
+		*top_expression = alt;
+		last_expression = &alt->params.pair.right;
+		{
+		  int sync_se = paramc;
+
+		  params = (params
+			    ? ((struct re_se_params *)
+			       realloc (params,
+					(paramc + 1) * sizeof (params[0])))
+			    : ((struct re_se_params *)
+			       malloc (sizeof (params[0]))));
+		  if (!params)
+		    return REG_ESPACE;
+		  ++paramc;
+
+		  params[sync_se].se = re_se_tv;
+		  {
+		    struct rexp_node * sync
+		      = rx_mk_r_side_effect (&rxb->rx,
+					     (rx_side_effect)sync_se);
+		    struct rexp_node * conc
+		      = rx_mk_r_concat (&rxb->rx, sync, 0);
+
+		    if (!sync || !conc)
+		      return REG_ESPACE;
+
+		    *last_expression = conc;
+		    last_expression = &conc->params.pair.right;
+		  }
+		}
+	      }
+              break;
+
+
+            case '{': 
+              /* If \{ is a literal.  */
+              if (!(syntax & RE_INTERVALS)
+                     /* If we're at `\{' and it's not the open-interval 
+                        operator.  */
+                  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+                  || (p - 2 == pattern  &&  p == pend))
+                goto normal_backslash;
+
+            handle_interval:
+              {
+                /* If got here, then the syntax allows intervals.  */
+
+                /* At least (most) this many matches must be made.  */
+                int lower_bound = -1, upper_bound = -1;
+
+                beg_interval = p - 1;
+
+                if (p == pend)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      return REG_EBRACE;
+                  }
+
+                GET_UNSIGNED_NUMBER (lower_bound);
+
+                if (c == ',')
+                  {
+                    GET_UNSIGNED_NUMBER (upper_bound);
+                    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+                  }
+                else
+                  /* Interval such as `{1}' => match exactly once. */
+                  upper_bound = lower_bound;
+
+                if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+                    || lower_bound > upper_bound)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                if (!(syntax & RE_NO_BK_BRACES)) 
+                  {
+                    if (c != '\\') return REG_EBRACE;
+                    PATFETCH (c);
+                  }
+
+                if (c != '}')
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                /* We just parsed a valid interval.  */
+
+                /* If it's invalid to have no preceding re.  */
+                if (pointless_if_repeated (*last_expression, params))
+                  {
+                    if (syntax & RE_CONTEXT_INVALID_OPS)
+                      return REG_BADRPT;
+                    else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                      goto unfetch_interval;
+		    /* was: else laststart = b; */
+                  }
+
+                /* If the upper bound is zero, don't want to iterate
+                 * at all.
+		 */
+                 if (upper_bound == 0)
+		   {
+		     if (*last_expression)
+		       {
+			 rx_free_rexp (&rxb->rx, *last_expression);
+			 *last_expression = 0;
+		       }
+		   }
+		else
+		  /* Otherwise, we have a nontrivial interval. */
+		  {
+		    int iter_se = paramc;
+		    int end_se = paramc + 1;
+		    params = (params
+			      ? ((struct re_se_params *)
+				 realloc (params,
+					  sizeof (*params) * (2 + paramc)))
+			      : ((struct re_se_params *)
+				 malloc (2 * sizeof (*params))));
+		    if (!params)
+		      return REG_ESPACE;
+		    paramc += 2;
+		    params [iter_se].se = re_se_iter;
+		    params [iter_se].op1 = lower_bound;
+		    params[iter_se].op2 = upper_bound;
+
+		    params[end_se].se = re_se_end_iter;
+		    params[end_se].op1 = lower_bound;
+		    params[end_se].op2 = upper_bound;
+		    {
+		      struct rexp_node * push0
+			= rx_mk_r_side_effect (&rxb->rx,
+					       (rx_side_effect)re_se_push0);
+		      struct rexp_node * start_one_iter
+			= rx_mk_r_side_effect (&rxb->rx,
+					       (rx_side_effect)iter_se);
+		      struct rexp_node * phase1
+			= rx_mk_r_concat (&rxb->rx, start_one_iter,
+					  *last_expression);
+		      struct rexp_node * pushback
+			= rx_mk_r_side_effect (&rxb->rx,
+					       (rx_side_effect)re_se_pushback);
+		      rx_Bitset cs = rx_cset (&rxb->rx);
+		      struct rexp_node * lit_t
+			= rx_mk_r_cset (&rxb->rx, cs);
+		      struct rexp_node * phase2
+			= rx_mk_r_concat (&rxb->rx, pushback, lit_t);
+		      struct rexp_node * loop
+			= rx_mk_r_2phase_star (&rxb->rx, phase1, phase2);
+		      struct rexp_node * push_n_loop
+			= rx_mk_r_concat (&rxb->rx, push0, loop);
+		      struct rexp_node * final_test
+			= rx_mk_r_side_effect (&rxb->rx,
+					       (rx_side_effect)end_se);
+		      struct rexp_node * full_exp
+			= rx_mk_r_concat (&rxb->rx, push_n_loop, final_test);
+
+		      if (!(push0 && start_one_iter && phase1
+			    && pushback && lit_t && phase2
+			    && loop && push_n_loop && final_test && full_exp))
+			return REG_ESPACE;
+
+		      RX_bitset_enjoin(cs, 't');
+
+		      *last_expression = full_exp;
+		    }
+		  }
+                beg_interval = 0;
+              }
+              break;
+
+            unfetch_interval:
+              /* If an invalid interval, match the characters as literals.  */
+               p = beg_interval;
+               beg_interval = 0;
+
+               /* normal_char and normal_backslash need `c'.  */
+               PATFETCH (c);	
+
+               if (!(syntax & RE_NO_BK_BRACES))
+                 {
+                   if (p > pattern  &&  p[-1] == '\\')
+                     goto normal_backslash;
+                 }
+               goto normal_char;
+
+#ifdef emacs
+            /* There is no way to specify the before_dot and after_dot
+               operators.  rms says this is ok.  --karl  */
+            case '=':
+	      side = (rx_side_effect)rx_se_at_dot;
+	      goto add_side_effect;
+              break;
+
+            case 's':
+	    case 'S':
+	      {
+		rx_Bitset cs = rx_cset (&rxb->rx);
+		struct rexp_node * set = rx_mk_r_cset (&rxb->rx, cs);
+		if (!(cs && set))
+		  return REG_ESPACE;
+		if (c == 'S')
+		  rx_bitset_universe (rxb->rx.local_cset_size, cs);
+
+		PATFETCH (c);
+		{
+		  int x;
+		  enum syntaxcode code = syntax_spec_code [c];
+		  for (x = 0; x < 256; ++x)
+		    {
+		      
+		      if (SYNTAX (x) == code)
+			{
+			  rx_Bitset it =
+			    inverse_translation (rxb, validate_inv_tr,
+						 inverse_translate,
+						 translate, x);
+			  rx_bitset_xor (rxb->rx.local_cset_size, cs, it);
+			}
+		    }
+		}
+		append = set;
+		goto append_node;
+	      }
+              break;
+#endif /* emacs */
+
+
+            case 'w':
+            case 'W':
+	      {
+		rx_Bitset cs = rx_cset (&rxb->rx);
+		struct rexp_node * n = (cs ? rx_mk_r_cset (&rxb->rx, cs) : 0);
+		if (!(cs && n))
+		  return REG_ESPACE;
+		if (c == 'W')
+		  rx_bitset_universe (rxb->rx.local_cset_size ,cs);
+		{
+		  int x;
+		  for (x = rxb->rx.local_cset_size - 1; x > 0; --x)
+		    if (SYNTAX(x) & Sword)
+		      RX_bitset_toggle (cs, x);
+		}
+		append = n;
+		goto append_node;
+	      }
+              break;
+
+/* With a little extra work, some of these side effects could be optimized
+ * away (basicly by looking at what we already know about the surrounding
+ * chars).  
+ */
+            case '<':
+	      side = (rx_side_effect)re_se_wordbeg;
+	      goto add_side_effect;
+              break;
+
+            case '>':
+              side = (rx_side_effect)re_se_wordend;
+	      goto add_side_effect;
+              break;
+
+            case 'b':
+              side = (rx_side_effect)re_se_wordbound;
+	      goto add_side_effect;
+              break;
+
+            case 'B':
+              side = (rx_side_effect)re_se_notwordbound;
+	      goto add_side_effect;
+              break;
+
+            case '`':
+	      side = (rx_side_effect)re_se_begbuf;
+	      goto add_side_effect;
+	      break;
+	      
+            case '\'':
+	      side = (rx_side_effect)re_se_endbuf;
+	      goto add_side_effect;
+              break;
+
+	    add_side_effect:
+	      {
+		struct rexp_node * se
+		  = rx_mk_r_side_effect (&rxb->rx, side);
+		if (!se)
+		  return REG_ESPACE;
+		append = se;
+		goto append_node;
+	      }
+	      break;
+
+            case '1': case '2': case '3': case '4': case '5':
+            case '6': case '7': case '8': case '9':
+              if (syntax & RE_NO_BK_REFS)
+                goto normal_char;
+
+              c1 = c - '0';
+
+              if (c1 > regnum)
+                return REG_ESUBREG;
+
+              /* Can't back reference to a subexpression if inside of it.  */
+              if (group_in_compile_stack (compile_stack, c1))
+		return REG_ESUBREG;
+
+	      {
+		int backref_se = paramc;
+		params = (params
+			  ? ((struct re_se_params *)
+			     realloc (params,
+				      sizeof (*params) * (1 + paramc)))
+			  : ((struct re_se_params *)
+			     malloc (sizeof (*params))));
+		if (!params)
+		  return REG_ESPACE;
+		++paramc;
+		params[backref_se].se = re_se_backref;
+		params[backref_se].op1 = c1;
+		side = (rx_side_effect)backref_se;
+		goto add_side_effect;
+	      }
+              break;
+
+            case '+':
+            case '?':
+              if (syntax & RE_BK_PLUS_QM)
+                goto handle_plus;
+              else
+                goto normal_backslash;
+
+            default:
+            normal_backslash:
+              /* You might think it would be useful for \ to mean
+                 not to translate; but if we don't translate it
+                 it will never match anything.  */
+              c = TRANSLATE (c);
+              goto normal_char;
+            }
+          break;
+
+
+	default:
+        /* Expects the character in `c'.  */
+	normal_char:
+	    {
+	      rx_Bitset cs = rx_cset(&rxb->rx);
+	      struct rexp_node * match = rx_mk_r_cset (&rxb->rx, cs);
+	      rx_Bitset it;
+	      if (!(cs && match))
+		return REG_ESPACE;
+	      it = inverse_translation (rxb, validate_inv_tr,
+					inverse_translate, translate, c);
+	      rx_bitset_union (CHAR_SET_SIZE, cs, it);
+	      append = match;
+
+	    append_node:
+	      /* This genericly appends the rexp APPEND to *LAST_EXPRESSION
+	       * and then parses the next character normally.
+	       */
+	      if (*last_expression)
+		{
+		  struct rexp_node * concat
+		    = rx_mk_r_concat (&rxb->rx, *last_expression, append);
+		  if (!concat)
+		    return REG_ESPACE;
+		  *last_expression = concat;
+		  last_expression = &concat->params.pair.right;
+		}
+	      else
+		*last_expression = append;
+	    }
+	} /* switch (c) */
+    } /* while p != pend */
+
+  
+  {
+    int win_se = paramc;
+    params = (params
+	      ? ((struct re_se_params *)
+		 realloc (params,
+			  sizeof (*params) * (1 + paramc)))
+	      : ((struct re_se_params *)
+		 malloc (sizeof (*params))));
+    if (!params)
+      return REG_ESPACE;
+    ++paramc;
+    params[win_se].se = re_se_win;
+    {
+      struct rexp_node * se
+	= rx_mk_r_side_effect (&rxb->rx, (rx_side_effect)win_se);
+      struct rexp_node * concat
+	= rx_mk_r_concat (&rxb->rx, rexp, se);
+      if (!(se && concat))
+	return REG_ESPACE;
+      rexp = concat;
+    }
+  }
+
+
+  /* Through the pattern now.  */
+
+  if (!COMPILE_STACK_EMPTY) 
+    return REG_EPAREN;
+
+      free (compile_stack.stack);
+
+  orig_rexp = rexp;
+#ifdef RX_DEBUG
+  if (rx_debug_compile)
+    {
+      dbug_rxb = rxb;
+      fputs ("\n\nCompiling ", stdout);
+      fwrite (pattern, 1, size, stdout);
+      fputs (":\n", stdout);
+      rxb->se_params = params;
+      print_rexp (&rxb->rx, orig_rexp, 2, re_seprint, stdout);
+    }
+#endif
+  {
+    rx_Bitset cs = rx_cset(&rxb->rx);
+    rx_Bitset cs2 = rx_cset(&rxb->rx);
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+    char * se_map = (char *) _alloca (paramc);
+#else
+    char * se_map = (char *) alloca (paramc);
+#endif
+    struct rexp_node * new_rexp = 0;
+
+
+    bzero (se_map, paramc);
+    find_backrefs (se_map, rexp, params);
+    fewer_side_effects =
+      remove_unecessary_side_effects (&rxb->rx, se_map,
+				      rx_copy_rexp (&rxb->rx, rexp), params);
+
+    speed_up_alt (&rxb->rx, rexp, 0);
+    speed_up_alt (&rxb->rx, fewer_side_effects, 1);
+
+    {
+      char * syntax_parens = rxb->syntax_parens;
+      if (syntax_parens == (char *)0x1)
+	rexp = remove_unecessary_side_effects
+	  (&rxb->rx, se_map, rexp, params);
+      else if (syntax_parens)
+	{
+	  int x;
+	  for (x = 0; x < paramc; ++x)
+	    if ((   (params[x].se == re_se_lparen)
+		 || (params[x].se == re_se_rparen))
+		&& (!syntax_parens [params[x].op1]))
+	      se_map [x] = 1;
+	  rexp = remove_unecessary_side_effects
+	    (&rxb->rx, se_map, rexp, params);
+	}
+    }
+
+    /* At least one more optimization would be nice to have here but i ran out 
+     * of time.  The idea would be to delay side effects.  
+     * For examle, `(abc)' is the same thing as `abc()' except that the
+     * left paren is offset by 3 (which we know at compile time).
+     * (In this comment, write that second pattern `abc(:3:)' 
+     * where `(:3:' is a syntactic unit.)
+     *
+     * Trickier:  `(abc|defg)'  is the same as `(abc(:3:|defg(:4:))'
+     * (The paren nesting may be hard to follow -- that's an alternation
+     *	of `abc(:3:' and `defg(:4:' inside (purely syntactic) parens
+     *  followed by the closing paren from the original expression.)
+     *
+     * Neither the expression tree representation nor the the nfa make
+     * this very easy to write. :(
+     */
+
+  /* What we compile is different than what the parser returns.
+   * Suppose the parser returns expression R.
+   * Let R' be R with unnecessary register assignments removed 
+   * (see REMOVE_UNECESSARY_SIDE_EFFECTS, above).
+   *
+   * What we will compile is the expression:
+   *
+   *    m{try}R{win}\|s{try}R'{win}
+   *
+   * {try} and {win} denote side effect epsilons (see EXPLORE_FUTURE).
+   * 
+   * When trying a match, we insert an `m' at the beginning of the 
+   * string if the user wants registers to be filled, `s' if not.
+   */
+    new_rexp =
+      rx_mk_r_alternate
+	(&rxb->rx,
+	 rx_mk_r_concat (&rxb->rx, rx_mk_r_cset (&rxb->rx, cs2), rexp),
+	 rx_mk_r_concat (&rxb->rx,
+			 rx_mk_r_cset (&rxb->rx, cs), fewer_side_effects));
+
+    if (!(new_rexp && cs && cs2))
+      return REG_ESPACE;
+    RX_bitset_enjoin (cs2, '\0'); /* prefixed to the rexp used for matching. */
+    RX_bitset_enjoin (cs, '\1'); /* prefixed to the rexp used for searching. */
+    rexp = new_rexp;
+  }
+
+#ifdef RX_DEBUG
+  if (rx_debug_compile)
+    {
+      fputs ("\n...which is compiled as:\n", stdout);
+      print_rexp (&rxb->rx, rexp, 2, re_seprint, stdout);
+    }
+#endif
+  {
+    struct rx_nfa_state *start = 0;
+    struct rx_nfa_state *end = 0;
+
+    if (!rx_build_nfa (&rxb->rx, rexp, &start, &end))
+      return REG_ESPACE;	/*  */
+    else
+      {
+	void * mem = (void *)rxb->buffer;
+	unsigned long size = rxb->allocated;
+	int start_id;
+	char * perm_mem;
+	int iterator_size = paramc * sizeof (params[0]);
+
+	end->is_final = 1;
+	start->is_start = 1;
+	rx_name_nfa_states (&rxb->rx);
+	start_id = start->id;
+#ifdef RX_DEBUG
+	if (rx_debug_compile)
+	  {
+	    fputs ("...giving the NFA: \n", stdout);
+	    dbug_rxb = rxb;
+	    print_nfa (&rxb->rx, rxb->rx.nfa_states, re_seprint, stdout);
+	  }
+#endif
+	if (!rx_eclose_nfa (&rxb->rx))
+	  return REG_ESPACE;
+	else
+	  {
+	    rx_delete_epsilon_transitions (&rxb->rx);
+	    
+	    /* For compatability reasons, we need to shove the
+	     * compiled nfa into one chunk of malloced memory.
+	     */
+	    rxb->rx.reserved = (   sizeof (params[0]) * paramc
+				+  rx_sizeof_bitset (rxb->rx.local_cset_size));
+#ifdef RX_DEBUG
+	    if (rx_debug_compile)
+	      {
+		dbug_rxb = rxb;
+		fputs ("...which cooks down (uncompactified) to: \n", stdout);
+		print_nfa (&rxb->rx, rxb->rx.nfa_states, re_seprint, stdout);
+	      }
+#endif
+	    if (!rx_compactify_nfa (&rxb->rx, &mem, &size))
+	      return REG_ESPACE;
+	    rxb->buffer = mem;
+	    rxb->allocated = size;
+	    rxb->rx.buffer = mem;
+	    rxb->rx.allocated = size;
+	    perm_mem = ((char *)rxb->rx.buffer
+			+ rxb->rx.allocated - rxb->rx.reserved);
+	    rxb->se_params = ((struct re_se_params *)perm_mem);
+	    bcopy (params, rxb->se_params, iterator_size);
+	    perm_mem += iterator_size;
+	    rxb->fastset = (rx_Bitset) perm_mem;
+	    rxb->start = rx_id_to_nfa_state (&rxb->rx, start_id);
+	  }
+	rx_bitset_null (rxb->rx.local_cset_size, rxb->fastset);
+	rxb->can_match_empty = compute_fastset (rxb, orig_rexp);
+	rxb->match_regs_on_stack =
+	  registers_on_stack (rxb, orig_rexp, 0, params); 
+	rxb->search_regs_on_stack =
+	  registers_on_stack (rxb, fewer_side_effects, 0, params);
+	if (rxb->can_match_empty)
+	  rx_bitset_universe (rxb->rx.local_cset_size, rxb->fastset);
+	rxb->is_anchored = is_anchored (orig_rexp, (rx_side_effect) re_se_hat);
+	rxb->begbuf_only = is_anchored (orig_rexp,
+					(rx_side_effect) re_se_begbuf);
+      }
+    rx_free_rexp (&rxb->rx, rexp);
+    if (params)
+      free (params);
+#ifdef RX_DEBUG
+    if (rx_debug_compile)
+      {
+	dbug_rxb = rxb;
+	fputs ("...which cooks down to: \n", stdout);
+	print_nfa (&rxb->rx, rxb->rx.nfa_states, re_seprint, stdout);
+      }
+#endif
+  }
+  return REG_NOERROR;
+}
+
+
+
+
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.  */
+
+__const__ char * rx_error_msg[] =
+{ 0,						/* REG_NOERROR */
+    "No match",					/* REG_NOMATCH */
+    "Invalid regular expression",		/* REG_BADPAT */
+    "Invalid collation character",		/* REG_ECOLLATE */
+    "Invalid character class name",		/* REG_ECTYPE */
+    "Trailing backslash",			/* REG_EESCAPE */
+    "Invalid back reference",			/* REG_ESUBREG */
+    "Unmatched [ or [^",			/* REG_EBRACK */
+    "Unmatched ( or \\(",			/* REG_EPAREN */
+    "Unmatched \\{",				/* REG_EBRACE */
+    "Invalid content of \\{\\}",		/* REG_BADBR */
+    "Invalid range end",			/* REG_ERANGE */
+    "Memory exhausted",				/* REG_ESPACE */
+    "Invalid preceding regular expression",	/* REG_BADRPT */
+    "Premature end of regular expression",	/* REG_EEND */
+    "Regular expression too big",		/* REG_ESIZE */
+    "Unmatched ) or \\)",			/* REG_ERPAREN */
+};
+
+
+
+
+
+char rx_slowmap [256] =
+{
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+#ifdef __STDC__
+RX_DECL void
+rx_blow_up_fastmap (struct re_pattern_buffer * rxb)
+#else
+RX_DECL void
+rx_blow_up_fastmap (rxb)
+     struct re_pattern_buffer * rxb;
+#endif
+{
+  int x;
+  for (x = 0; x < 256; ++x)	/* &&&& 3.6 % */
+    rxb->fastmap [x] = !!RX_bitset_member (rxb->fastset, x);
+  rxb->fastmap_accurate = 1;
+}
+
+
+
+
+
+#if !defined(REGEX_MALLOC) && !defined(__GNUC__)
+#define RE_SEARCH_2_FN	inner_re_search_2
+#define RE_S2_QUAL static
+#else
+#define RE_SEARCH_2_FN	re_search_2
+#define RE_S2_QUAL 
+#endif
+
+struct re_search_2_closure
+{
+  __const__ char * string1;
+  int size1;
+  __const__ char * string2;
+  int size2;
+};
+
+
+static __inline__ enum rx_get_burst_return
+re_search_2_get_burst (pos, vclosure, stop)
+     struct rx_string_position * pos;
+     void * vclosure;
+     int stop;
+{
+  struct re_search_2_closure * closure;
+  closure = (struct re_search_2_closure *)vclosure;
+  if (!closure->string2)
+    {
+      int inset;
+
+      inset = pos->pos - pos->string;
+      if ((inset < -1) || (inset > closure->size1))
+	return rx_get_burst_no_more;
+      else
+	{
+	  pos->pos = (__const__ unsigned char *) closure->string1 + inset;
+	  pos->string = (__const__ unsigned char *) closure->string1;
+	  pos->size = closure->size1;
+	  pos->end = ((__const__ unsigned char *)
+		      MIN(closure->string1 + closure->size1,
+			  closure->string1 + stop));
+	  pos->offset = 0;
+	  return ((pos->pos < pos->end)
+		  ? rx_get_burst_ok
+		  :  rx_get_burst_no_more);
+	}
+    }
+  else if (!closure->string1)
+    {
+      int inset;
+
+      inset = pos->pos - pos->string;
+      pos->pos = (__const__ unsigned char *) closure->string2 + inset;
+      pos->string = (__const__ unsigned char *) closure->string2;
+      pos->size = closure->size2;
+      pos->end = ((__const__ unsigned char *)
+		  MIN(closure->string2 + closure->size2,
+		      closure->string2 + stop));
+      pos->offset = 0;
+      return ((pos->pos < pos->end)
+	      ? rx_get_burst_ok
+	      :  rx_get_burst_no_more);
+    }
+  else
+    {
+      int inset;
+
+      inset = pos->pos - pos->string + pos->offset;
+      if (inset < closure->size1)
+	{
+	  pos->pos = (__const__ unsigned char *) closure->string1 + inset;
+	  pos->string = (__const__ unsigned char *) closure->string1;
+	  pos->size = closure->size1;
+	  pos->end = ((__const__ unsigned char *)
+		      MIN(closure->string1 + closure->size1,
+			  closure->string1 + stop));
+	  pos->offset = 0;
+	  return rx_get_burst_ok;
+	}
+      else
+	{
+	  pos->pos = ((__const__ unsigned char *)
+		      closure->string2 + inset - closure->size1);
+	  pos->string = (__const__ unsigned char *) closure->string2;
+	  pos->size = closure->size2;
+	  pos->end = ((__const__ unsigned char *)
+		      MIN(closure->string2 + closure->size2,
+			  closure->string2 + stop - closure->size1));
+	  pos->offset = closure->size1;
+	  return ((pos->pos < pos->end)
+		  ? rx_get_burst_ok
+		  :  rx_get_burst_no_more);
+	}
+    }
+}
+
+
+static __inline__ enum rx_back_check_return
+re_search_2_back_check (pos, lparen, rparen, translate, vclosure, stop)
+     struct rx_string_position * pos;
+     int lparen;
+     int rparen;
+     unsigned char * translate;
+     void * vclosure;
+     int stop;
+{
+  struct rx_string_position there;
+  struct rx_string_position past;
+
+  there = *pos;
+  there.pos = there.string + lparen - there.offset;
+  re_search_2_get_burst (&there, vclosure, stop);
+
+  past = *pos;
+  past.pos = past.string + rparen - there.offset;
+  re_search_2_get_burst (&past, vclosure, stop);
+
+  ++pos->pos;
+  re_search_2_get_burst (pos, vclosure, stop);
+
+  while (   (there.pos != past.pos)
+	 && (pos->pos != pos->end))
+    if (TRANSLATE(*there.pos) != TRANSLATE(*pos->pos))
+      return rx_back_check_fail;
+    else
+      {
+	++there.pos;
+	++pos->pos;
+	if (there.pos == there.end)
+	  re_search_2_get_burst (&there, vclosure, stop);
+	if (pos->pos == pos->end)
+	  re_search_2_get_burst (pos, vclosure, stop);
+      }
+
+  if (there.pos != past.pos)
+    return rx_back_check_fail;
+  --pos->pos;
+  re_search_2_get_burst (pos, vclosure, stop);
+  return rx_back_check_pass;
+}
+
+static __inline__ int
+re_search_2_fetch_char (pos, offset, app_closure, stop)
+     struct rx_string_position * pos;
+     int offset;
+     void * app_closure;
+     int stop;
+{
+  struct re_search_2_closure * closure;
+  closure = (struct re_search_2_closure *)app_closure;
+  if (offset == 0)
+    {
+      if (pos->pos >= pos->string)
+	return *pos->pos;
+      else
+	{
+	  if (   (pos->string == (__const__ unsigned char *) closure->string2)
+	      && (closure->string1)
+	      && (closure->size1))
+	    return closure->string1[closure->size1 - 1];
+	  else
+	    return 0;		/* sure, why not. */
+	}
+    }
+  if (pos->pos == pos->end)
+    return *closure->string2;
+  else
+    return pos->pos[1];
+}
+     
+
+#ifdef __STDC__
+RE_S2_QUAL int
+RE_SEARCH_2_FN (struct re_pattern_buffer *rxb,
+		__const__ char * string1, int size1,
+		__const__ char * string2, int size2,
+		int startpos, int range,
+		struct re_registers *regs,
+		int stop)
+#else
+RE_S2_QUAL int
+RE_SEARCH_2_FN (rxb,
+		string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *rxb;
+     __const__ char * string1;
+     int size1;
+     __const__ char * string2;
+     int size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+#endif
+{
+  int answer;
+  struct re_search_2_closure closure;
+  closure.string1 = string1;
+  closure.size1 = size1;
+  closure.string2 = string2;
+  closure.size2 = size2;
+  answer = rx_search (rxb, startpos, range, stop, size1 + size2,
+		      re_search_2_get_burst,
+		      re_search_2_back_check,
+		      re_search_2_fetch_char,
+		      (void *)&closure,
+		      regs,
+		      0,
+		      0);
+  switch (answer)
+    {
+    case rx_search_continuation:
+      abort ();
+    case rx_search_error:
+      return -2;
+    case rx_search_soft_fail:
+    case rx_search_fail:
+      return -1;
+    default:
+      return answer;
+    }
+}
+
+/* Export rx_search to callers outside this file.  */
+
+int
+re_rx_search (rxb, startpos, range, stop, total_size,
+	      get_burst, back_check, fetch_char,
+	      app_closure, regs, resume_state, save_state)
+     struct re_pattern_buffer * rxb;
+     int startpos;
+     int range;
+     int stop;
+     int total_size;
+     rx_get_burst_fn get_burst;
+     rx_back_check_fn back_check;
+     rx_fetch_char_fn fetch_char;
+     void * app_closure;
+     struct re_registers * regs;
+     struct rx_search_state * resume_state;
+     struct rx_search_state * save_state;
+{
+  return rx_search (rxb, startpos, range, stop, total_size,
+		    get_burst, back_check, fetch_char, app_closure,
+		    regs, resume_state, save_state);
+}
+
+#if !defined(REGEX_MALLOC) && !defined(__GNUC__)
+#ifdef __STDC__
+int
+re_search_2 (struct re_pattern_buffer *rxb,
+	     __const__ char * string1, int size1,
+	     __const__ char * string2, int size2,
+	     int startpos, int range,
+	     struct re_registers *regs,
+	     int stop)
+#else
+int
+re_search_2 (rxb, string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *rxb;
+     __const__ char * string1;
+     int size1;
+     __const__ char * string2;
+     int size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+#endif
+{
+  int ret;
+  ret = inner_re_search_2 (rxb, string1, size1, string2, size2, startpos,
+			   range, regs, stop);
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+  _alloca (0);
+#else
+  alloca (0);
+#endif
+  return ret;
+}
+#endif
+
+
+/* Like re_search_2, above, but only one string is specified, and
+ * doesn't let you say where to stop matching.
+ */
+
+#ifdef __STDC__
+int
+re_search (struct re_pattern_buffer * rxb, __const__ char *string,
+	   int size, int startpos, int range,
+	   struct re_registers *regs)
+#else
+int
+re_search (rxb, string, size, startpos, range, regs)
+     struct re_pattern_buffer * rxb;
+     __const__ char * string;
+     int size;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+#endif
+{
+  return re_search_2 (rxb, 0, 0, string, size, startpos, range, regs, size);
+}
+
+#ifdef __STDC__
+int
+re_match_2 (struct re_pattern_buffer * rxb,
+	    __const__ char * string1, int size1,
+	    __const__ char * string2, int size2,
+	    int pos, struct re_registers *regs, int stop)
+#else
+int
+re_match_2 (rxb, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer * rxb;
+     __const__ char * string1;
+     int size1;
+     __const__ char * string2;
+     int size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+#endif
+{
+  struct re_registers some_regs;
+  regoff_t start;
+  regoff_t end;
+  int srch;
+  int save = rxb->regs_allocated;
+  struct re_registers * regs_to_pass = regs;
+
+  if (!regs)
+    {
+      some_regs.start = &start;
+      some_regs.end = &end;
+      some_regs.num_regs = 1;
+      regs_to_pass = &some_regs;
+      rxb->regs_allocated = REGS_FIXED;
+    }
+
+  srch = re_search_2 (rxb, string1, size1, string2, size2,
+		      pos, 1, regs_to_pass, stop);
+  if (regs_to_pass != regs)
+    rxb->regs_allocated = save;
+  if (srch < 0)
+    return srch;
+  return regs_to_pass->end[0] - regs_to_pass->start[0];
+}
+
+/* re_match is like re_match_2 except it takes only a single string.  */
+
+#ifdef __STDC__
+int
+re_match (struct re_pattern_buffer * rxb,
+	  __const__ char * string,
+	  int size, int pos,
+	  struct re_registers *regs)
+#else
+int
+re_match (rxb, string, size, pos, regs)
+     struct re_pattern_buffer * rxb;
+     __const__ char *string;
+     int size;
+     int pos;
+     struct re_registers *regs;
+#endif
+{
+  return re_match_2 (rxb, string, size, 0, 0, pos, regs, size);
+}
+
+
+
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+#ifdef __STDC__
+reg_syntax_t
+re_set_syntax (reg_syntax_t syntax)
+#else
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+#endif
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+  return ret;
+}
+
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+#ifdef __STDC__
+void
+re_set_registers (struct re_pattern_buffer *bufp,
+		  struct re_registers *regs,
+		  unsigned num_regs,
+		  regoff_t * starts, regoff_t * ends)
+#else
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+     struct re_pattern_buffer *bufp;
+     struct re_registers *regs;
+     unsigned num_regs;
+     regoff_t * starts;
+     regoff_t * ends;
+#endif
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t) 0;
+    }
+}
+
+
+
+
+
+#ifdef __STDC__
+static int 
+cplx_se_sublist_len (struct rx_se_list * list)
+#else
+static int 
+cplx_se_sublist_len (list)
+     struct rx_se_list * list;
+#endif
+{
+  int x = 0;
+  while (list)
+    {
+      if ((long)list->car >= 0)
+	++x;
+      list = list->cdr;
+    }
+  return x;
+}
+
+
+/* For rx->se_list_cmp */
+
+#ifdef __STDC__
+static int 
+posix_se_list_order (struct rx * rx,
+		     struct rx_se_list * a, struct rx_se_list * b)
+#else
+static int 
+posix_se_list_order (rx, a, b)
+     struct rx * rx;
+     struct rx_se_list * a;
+     struct rx_se_list * b;
+#endif
+{
+  int al = cplx_se_sublist_len (a);
+  int bl = cplx_se_sublist_len (b);
+
+  if (!al && !bl)
+    return ((a == b)
+	    ? 0
+	    : ((a < b) ? -1 : 1));
+  
+  else if (!al)
+    return -1;
+
+  else if (!bl)
+    return 1;
+
+  else
+    {
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+      rx_side_effect * av = ((rx_side_effect *)
+			     _alloca (sizeof (rx_side_effect) * (al + 1)));
+      rx_side_effect * bv = ((rx_side_effect *)
+			     _alloca (sizeof (rx_side_effect) * (bl + 1)));
+#else
+      rx_side_effect * av = ((rx_side_effect *)
+			     alloca (sizeof (rx_side_effect) * (al + 1)));
+      rx_side_effect * bv = ((rx_side_effect *)
+			     alloca (sizeof (rx_side_effect) * (bl + 1)));
+#endif
+      struct rx_se_list * ap = a;
+      struct rx_se_list * bp = b;
+      int ai, bi;
+      
+      for (ai = al - 1; ai >= 0; --ai)
+	{
+	  while ((long)ap->car < 0)
+	    ap = ap->cdr;
+	  av[ai] = ap->car;
+	  ap = ap->cdr;
+	}
+      av[al] = (rx_side_effect)-2;
+      for (bi = bl - 1; bi >= 0; --bi)
+	{
+	  while ((long)bp->car < 0)
+	    bp = bp->cdr;
+	  bv[bi] = bp->car;
+	  bp = bp->cdr;
+	}
+      bv[bl] = (rx_side_effect)-1;
+
+      {
+	int ret;
+	int x = 0;
+	while (av[x] == bv[x])
+	  ++x;
+ 	ret = (((unsigned *)(av[x]) < (unsigned *)(bv[x])) ? -1 : 1);
+	return ret;
+      }
+    }
+}
+
+
+
+
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length SIZE) and puts the result in RXB.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in RXB on entry.
+
+   We call rx_compile to do the actual compilation.  */
+
+#ifdef __STDC__
+__const__ char *
+re_compile_pattern (__const__ char *pattern,
+		    int length,
+		    struct re_pattern_buffer * rxb)
+#else
+__const__ char *
+re_compile_pattern (pattern, length, rxb)
+     __const__ char *pattern;
+     int length;
+     struct re_pattern_buffer * rxb;
+#endif
+{
+  reg_errcode_t ret;
+
+  /* GNU code is written to assume at least RE_NREGS registers will be set
+     (and at least one extra will be -1).  */
+  rxb->regs_allocated = REGS_UNALLOCATED;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  rxb->no_sub = 0;
+
+  rxb->rx.local_cset_size = 256;
+
+  /* Match anchors at newline.  */
+  rxb->newline_anchor = 1;
+ 
+  rxb->re_nsub = 0;
+  rxb->start = 0;
+  rxb->se_params = 0;
+  rxb->rx.nodec = 0;
+  rxb->rx.epsnodec = 0;
+  rxb->rx.instruction_table = 0;
+  rxb->rx.nfa_states = 0;
+  rxb->rx.se_list_cmp = posix_se_list_order;
+  rxb->rx.start_set = 0;
+
+  ret = rx_compile (pattern, length, re_syntax_options, rxb);
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+  _alloca (0);
+#else
+  alloca (0);
+#endif
+  return rx_error_msg[(int) ret];
+}
+
+
+
+#ifdef __STDC__
+int
+re_compile_fastmap (struct re_pattern_buffer * rxb)
+#else
+int
+re_compile_fastmap (rxb)
+     struct re_pattern_buffer * rxb;
+#endif
+{
+  rx_blow_up_fastmap (rxb);
+  return 0;
+}
+
+
+
+
+
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them if this is an Emacs or POSIX compilation.  */
+
+#if (!defined (emacs) && !defined (_POSIX_SOURCE)) || defined(USE_BSD_REGEX)
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer rx_comp_buf;
+
+#ifdef __STDC__
+char *
+re_comp (__const__ char *s)
+#else
+char *
+re_comp (s)
+    __const__ char *s;
+#endif
+{
+  reg_errcode_t ret;
+
+  if (!s || (*s == '\0'))
+    {
+      if (!rx_comp_buf.buffer)
+	return "No previous regular expression";
+      return 0;
+    }
+
+  if (!rx_comp_buf.fastmap)
+    {
+      rx_comp_buf.fastmap = (char *) malloc (1 << CHARBITS);
+      if (!rx_comp_buf.fastmap)
+	return "Memory exhausted";
+    }
+
+  /* Since `rx_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  rx_comp_buf.newline_anchor = 1;
+
+  rx_comp_buf.re_nsub = 0;
+  rx_comp_buf.start = 0;
+  rx_comp_buf.se_params = 0;
+  rx_comp_buf.rx.nodec = 0;
+  rx_comp_buf.rx.epsnodec = 0;
+  rx_comp_buf.rx.instruction_table = 0;
+  rx_comp_buf.rx.nfa_states = 0;
+  rx_comp_buf.rx.start = 0;
+  rx_comp_buf.rx.se_list_cmp = posix_se_list_order;
+  rx_comp_buf.rx.local_cset_size = 256;
+
+  ret = rx_compile (s, strlen (s), re_syntax_options, &rx_comp_buf);
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+  _alloca (0);
+#else
+  alloca (0);
+#endif
+
+  /* Yes, we're discarding `__const__' here.  */
+  return (char *) rx_error_msg[(int) ret];
+}
+
+
+#ifdef __STDC__
+int
+re_exec (__const__ char *s)
+#else
+int
+re_exec (s)
+    __const__ char *s;
+#endif
+{
+  __const__ int len = strlen (s);
+  return
+    0 <= re_search (&rx_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+
+
+
+
+/* POSIX.2 functions.  Don't define these for Emacs.  */
+
+#if !defined(emacs)
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' and `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+
+#ifdef __STDC__
+int
+regcomp (regex_t * preg, __const__ char * pattern, int cflags)
+#else
+int
+regcomp (preg, pattern, cflags)
+    regex_t * preg;
+    __const__ char * pattern;
+    int cflags;
+#endif
+{
+  reg_errcode_t ret;
+  unsigned syntax
+    = cflags & REG_EXTENDED ? RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+  /* regex_compile will allocate the space for the compiled pattern.  */
+  preg->buffer = 0;
+  preg->allocated = 0;
+  preg->fastmap = malloc (256);
+  if (!preg->fastmap)
+    return REG_ESPACE;
+  preg->fastmap_accurate = 0;
+
+  if (cflags & REG_ICASE)
+    {
+      unsigned i;
+
+      preg->translate = (unsigned char *) malloc (256);
+      if (!preg->translate)
+        return (int) REG_ESPACE;
+
+      /* Map uppercase characters to corresponding lowercase ones.  */
+      for (i = 0; i < CHAR_SET_SIZE; i++)
+        preg->translate[i] = isupper (i) ? tolower (i) : i;
+    }
+  else
+    preg->translate = 0;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+
+  preg->no_sub = !!(cflags & REG_NOSUB);
+
+  /* POSIX says a null character in the pattern terminates it, so we
+     can use strlen here in compiling the pattern.  */
+  preg->re_nsub = 0;
+  preg->start = 0;
+  preg->se_params = 0;
+  preg->syntax_parens = 0;
+  preg->rx.nodec = 0;
+  preg->rx.epsnodec = 0;
+  preg->rx.instruction_table = 0;
+  preg->rx.nfa_states = 0;
+  preg->rx.local_cset_size = 256;
+  preg->rx.start = 0;
+  preg->rx.se_list_cmp = posix_se_list_order;
+  preg->rx.start_set = 0;
+  ret = rx_compile (pattern, strlen (pattern), syntax, preg);
+
+#ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+  _alloca (0);
+#else
+  alloca (0);
+#endif
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN) ret = REG_EPAREN;
+
+  return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+#ifdef __STDC__
+int
+regexec (__const__ regex_t *preg, __const__ char *string,
+	 size_t nmatch, regmatch_t pmatch[],
+	 int eflags)
+#else
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    __const__ regex_t *preg;
+    __const__ char *string;
+    size_t nmatch;
+    regmatch_t pmatch[];
+    int eflags;
+#endif
+{
+  int ret;
+  struct re_registers regs;
+  regex_t private_preg;
+  int len = strlen (string);
+  boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+  private_preg = *preg;
+
+  private_preg.not_bol = !!(eflags & REG_NOTBOL);
+  private_preg.not_eol = !!(eflags & REG_NOTEOL);
+
+  /* The user has told us exactly how many registers to return
+   * information about, via `nmatch'.  We have to pass that on to the
+   * matching routines.
+   */
+  private_preg.regs_allocated = REGS_FIXED;
+
+  if (want_reg_info)
+    {
+      regs.num_regs = nmatch;
+      regs.start =  (( regoff_t *) malloc ((nmatch) * sizeof ( regoff_t)));
+      regs.end =  (( regoff_t *) malloc ((nmatch) * sizeof ( regoff_t)));
+      if (regs.start == 0 || regs.end == 0)
+        return (int) REG_NOMATCH;
+    }
+
+  /* Perform the searching operation.  */
+  ret = re_search (&private_preg,
+		   string, len,
+                   /* start: */ 0,
+		   /* range: */ len,
+                   want_reg_info ? &regs : (struct re_registers *) 0);
+
+  /* Copy the register information to the POSIX structure.  */
+  if (want_reg_info)
+    {
+      if (ret >= 0)
+        {
+          unsigned r;
+
+          for (r = 0; r < nmatch; r++)
+            {
+              pmatch[r].rm_so = regs.start[r];
+              pmatch[r].rm_eo = regs.end[r];
+            }
+        }
+
+      /* If we needed the temporary register info, free the space now.  */
+      free (regs.start);
+      free (regs.end);
+    }
+
+  /* We want zero return to mean success, unlike `re_search'.  */
+  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   */
+
+#ifdef __STDC__
+size_t
+regerror (int rx_errcode, __const__ regex_t *preg,
+	  char *errbuf, size_t errbuf_size)
+#else
+size_t
+regerror (rx_errcode, preg, errbuf, errbuf_size)
+    int rx_errcode;
+    __const__ regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+#endif
+{
+  __const__ char *msg
+    = rx_error_msg[rx_errcode] == 0 ? "Success" : rx_error_msg[rx_errcode];
+  size_t msg_size = strlen (msg) + 1; /* Includes the 0.  */
+
+  if (errbuf_size != 0)
+    {
+      if (msg_size > errbuf_size)
+        {
+          strncpy (errbuf, msg, errbuf_size - 1);
+          errbuf[errbuf_size - 1] = 0;
+        }
+      else
+        strcpy (errbuf, msg);
+    }
+
+  return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+#ifdef __STDC__
+void
+regfree (regex_t *preg)
+#else
+void
+regfree (preg)
+    regex_t *preg;
+#endif
+{
+  if (preg->buffer != 0)
+    free (preg->buffer);
+  preg->buffer = 0;
+  preg->allocated = 0;
+
+  if (preg->fastmap != 0)
+    free (preg->fastmap);
+  preg->fastmap = 0;
+  preg->fastmap_accurate = 0;
+
+  if (preg->translate != 0)
+    free (preg->translate);
+  preg->translate = 0;
+}
+
+#endif /* not emacs  */
+
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/lib/rx.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/rx.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/rx.h	(revision 16561)
@@ -0,0 +1,3755 @@
+#if !defined(RXH) || defined(RX_WANT_SE_DEFS)
+#define RXH
+
+/*	Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of the librx library.
+
+Librx is free software; you can redistribute it and/or modify it under
+the terms of the GNU Library General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+Librx 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 Library General Public
+License along with this software; see the file COPYING.LIB.  If not,
+write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA
+02139, USA.  */
+/*  t. lord	Wed Sep 23 18:20:57 1992	*/
+
+
+
+
+
+
+
+
+
+#ifndef RX_WANT_SE_DEFS
+
+/* This page: Bitsets */
+
+#ifndef RX_subset
+typedef unsigned int RX_subset;
+#define RX_subset_bits	(32)
+#define RX_subset_mask	(RX_subset_bits - 1)
+#endif
+
+typedef RX_subset * rx_Bitset;
+
+#ifdef __STDC__
+typedef void (*rx_bitset_iterator) (void *, int member_index);
+#else
+typedef void (*rx_bitset_iterator) ();
+#endif
+
+#define rx_bitset_subset(N)  ((N) / RX_subset_bits)
+#define rx_bitset_subset_val(B,N)  ((B)[rx_bitset_subset(N)])
+#define RX_bitset_access(B,N,OP) \
+  ((B)[rx_bitset_subset(N)] OP rx_subset_singletons[(N) & RX_subset_mask])
+#define RX_bitset_member(B,N)   RX_bitset_access(B, N, &)
+#define RX_bitset_enjoin(B,N)   RX_bitset_access(B, N, |=)
+#define RX_bitset_remove(B,N)   RX_bitset_access(B, N, &= ~)
+#define RX_bitset_toggle(B,N)   RX_bitset_access(B, N, ^= )
+#define rx_bitset_numb_subsets(N) (((N) + RX_subset_bits - 1) / RX_subset_bits)
+#define rx_sizeof_bitset(N)	(rx_bitset_numb_subsets(N) * sizeof(RX_subset))
+
+
+
+
+/* This page: Splay trees. */
+
+#ifdef __STDC__
+typedef int (*rx_sp_comparer) (void * a, void * b);
+#else
+typedef int (*rx_sp_comparer) ();
+#endif
+
+struct rx_sp_node 
+{
+  void * key;
+  void * data;
+  struct rx_sp_node * kids[2];
+};
+
+#ifdef __STDC__
+typedef void (*rx_sp_key_data_freer) (struct rx_sp_node *);
+#else
+typedef void (*rx_sp_key_data_freer) ();
+#endif
+
+
+
+/* giant inflatable hash trees */
+
+struct rx_hash_item
+{
+  struct rx_hash_item * next_same_hash;
+  struct rx_hash * table;
+  unsigned long hash;
+  void * data;
+  void * binding;
+};
+
+struct rx_hash
+{
+  struct rx_hash * parent;
+  int refs;
+  struct rx_hash * children[13];
+  struct rx_hash_item * buckets [13];
+  int bucket_size [13];
+};
+
+struct rx_hash_rules;
+
+#ifdef __STDC__
+/* should return like == */
+typedef int (*rx_hash_eq)(void *, void *);
+typedef struct rx_hash * (*rx_alloc_hash)(struct rx_hash_rules *);
+typedef void (*rx_free_hash)(struct rx_hash *,
+			    struct rx_hash_rules *);
+typedef struct rx_hash_item * (*rx_alloc_hash_item)(struct rx_hash_rules *,
+						    void *);
+typedef void (*rx_free_hash_item)(struct rx_hash_item *,
+				 struct rx_hash_rules *);
+#else
+typedef int (*rx_hash_eq)();
+typedef struct rx_hash * (*rx_alloc_hash)();
+typedef void (*rx_free_hash)();
+typedef struct rx_hash_item * (*rx_alloc_hash_item)();
+typedef void (*rx_free_hash_item)();
+#endif
+
+struct rx_hash_rules
+{
+  rx_hash_eq eq;
+  rx_alloc_hash hash_alloc;
+  rx_free_hash free_hash;
+  rx_alloc_hash_item hash_item_alloc;
+  rx_free_hash_item free_hash_item;
+};
+
+
+
+/* Forward declarations */
+
+struct rx_cache;
+struct rx_superset;
+struct rx;
+struct rx_se_list;
+
+
+
+
+/* 
+ * GLOSSARY
+ *
+ * regexp
+ * regular expression
+ * expression
+ * pattern - a `regular' expression.  The expression
+ *       need not be formally regular -- it can contain
+ *       constructs that don't correspond to purely regular
+ *       expressions.
+ *
+ * buffer
+ * string - the string (or strings) being searched or matched.
+ *
+ * pattern buffer - a structure of type `struct re_pattern_buffer'
+ *       This in turn contains a `struct rx', which holds the
+ *       NFA compiled from a pattern, as well as some of the state
+ *       of a matcher using the pattern.
+ *
+ * NFA - nondeterministic finite automata.  Some people
+ *       use this term to a member of the class of 
+ *       regular automata (those corresponding to a regular
+ *       language).  However, in this code, the meaning is
+ *       more general.  The automata used by Rx are comperable
+ *       in power to what are usually called `push down automata'.
+ *
+ *       Two NFA are built by rx for every pattern.  One is built
+ *       by the compiler.  The other is built from the first, on
+ *       the fly, by the matcher.  The latter is called the `superstate
+ *       NFA' because its states correspond to sets of states from
+ *       the first NFA.  (Joe Keane gets credit for the name
+ *       `superstate NFA').
+ *
+ * NFA edges
+ * epsilon edges
+ * side-effect edges - The NFA compiled from a pattern can have three
+ *       kinds of edges.  Epsilon edges can be taken freely anytime
+ *       their source state is reached.  Character set edges can be
+ *       taken when their source state is reached and when the next 
+ *       character in the buffer is a member of the set.  Side effect
+ *       edges imply a transition that can only be taken after the
+ *       indicated side effect has been successfully accomplished.
+ *       Some examples of side effects are:
+ *
+ *		Storing the current match position to record the
+ *              location of a parentesized subexpression.
+ *
+ *              Advancing the matcher over N characters if they
+ *              match the N characters previously matched by a 
+ *              parentesized subexpression.
+ *
+ *       Both of those kinds of edges occur in the NFA generated
+ *       by the pattern:  \(.\)\1
+ *
+ *       Epsilon and side effect edges are similar.  Unfortunately,
+ *       some of the code uses the name `epsilon edge' to mean
+ *       both epsilon and side effect edges.  For example,  the
+ *       function has_non_idempotent_epsilon_path computes the existance
+ *       of a non-trivial path containing only a mix of epsilon and
+ *       side effect edges.  In that case `nonidempotent epsilon' is being
+ *       used to mean `side effect'.
+ */
+
+
+
+
+
+
+/* LOW LEVEL PATTERN BUFFERS */
+
+/* Suppose that from some NFA state, more than one path through
+ * side-effect edges is possible.  In what order should the paths
+ * be tried?  A function of type rx_se_list_order answers that
+ * question.  It compares two lists of side effects, and says
+ * which list comes first.
+ */
+ 
+#ifdef __STDC__
+typedef int (*rx_se_list_order) (struct rx *,
+				 struct rx_se_list *, 
+				 struct rx_se_list *);
+#else
+typedef int (*rx_se_list_order) ();
+#endif
+
+
+
+/* Struct RX holds a compiled regular expression - that is, an nfa
+ * ready to be converted on demand to a more efficient superstate nfa.
+ * This is for the low level interface.  The high-level interfaces enclose
+ * this in a `struct re_pattern_buffer'.  
+ */
+struct rx
+{
+  /* The compiler assigns a unique id to every pattern.
+   * Like sequence numbers in X, there is a subtle bug here
+   * if you use Rx in a system that runs for a long time.
+   * But, because of the way the caches work out, it is almost
+   * impossible to trigger the Rx version of this bug.
+   *
+   * The id is used to validate superstates found in a cache
+   * of superstates.  It isn't sufficient to let a superstate
+   * point back to the rx for which it was compiled -- the caller
+   * may be re-using a `struct rx' in which case the superstate
+   * is not really valid.  So instead, superstates are validated
+   * by checking the sequence number of the pattern for which
+   * they were built.
+   */
+  int rx_id;
+
+  /* This is memory mgt. state for superstates.  This may be 
+   * shared by more than one struct rx.
+   */
+  struct rx_cache * cache;
+
+  /* Every regex defines the size of its own character set. 
+   * A superstate has an array of this size, with each element
+   * a `struct rx_inx'.  So, don't make this number too large.
+   * In particular, don't make it 2^16.
+   */
+  int local_cset_size;
+
+  /* After the NFA is built, it is copied into a contiguous region
+   * of memory (mostly for compatability with GNU regex).
+   * Here is that region, and it's size:
+   */
+  void * buffer;
+  unsigned long allocated;
+
+  /* Clients of RX can ask for some extra storage in the space pointed
+   * to by BUFFER.  The field RESERVED is an input parameter to the
+   * compiler.  After compilation, this much space will be available 
+   * at (buffer + allocated - reserved)
+   */
+  unsigned long reserved;
+
+  /* --------- The remaining fields are for internal use only. --------- */
+  /* --------- But! they must be initialized to 0.	       --------- */
+
+  /* NODEC is the number of nodes in the NFA with non-epsilon
+   * transitions. 
+   */
+  int nodec;
+
+  /* EPSNODEC is the number of nodes with only epsilon transitions. */
+  int epsnodec;
+
+  /* The sum (NODEC + EPSNODEC) is the total number of states in the
+   * compiled NFA.
+   */
+
+  /* Lists of side effects as stored in the NFA are `hash consed'..meaning
+   * that lists with the same elements are ==.  During compilation, 
+   * this table facilitates hash-consing.
+   */
+  struct rx_hash se_list_memo;
+
+  /* Lists of NFA states are also hashed. 
+   */
+  struct rx_hash set_list_memo;
+
+
+
+
+  /* The compiler and matcher must build a number of instruction frames.
+   * The format of these frames is fixed (c.f. struct rx_inx).  The values
+   * of the instructions is not fixed.
+   *
+   * An enumerated type (enum rx_opcode) defines the set of instructions
+   * that the compiler or matcher might generate.  When filling an instruction
+   * frame, the INX field is found by indexing this instruction table
+   * with an opcode:
+   */
+  void ** instruction_table;
+
+  /* The list of all states in an NFA.
+   * During compilation, the NEXT field of NFA states links this list.
+   * After compilation, all the states are compacted into an array,
+   * ordered by state id numbers.  At that time, this points to the base 
+   * of that array.
+   */
+  struct rx_nfa_state *nfa_states;
+
+  /* Every nfa begins with one distinguished starting state:
+   */
+  struct rx_nfa_state *start;
+
+  /* This orders the search through super-nfa paths.
+   * See the comment near the typedef of rx_se_list_order.
+   */
+  rx_se_list_order se_list_cmp;
+
+  struct rx_superset * start_set;
+};
+
+
+
+
+
+/* SYNTAX TREES */
+
+/* Compilation is in stages.  
+ *
+ * In the first stage, a pattern specified by a string is 
+ * translated into a syntax tree.  Later stages will convert
+ * the syntax tree into an NFA optimized for conversion to a
+ * superstate-NFA.
+ *
+ * This page is about syntax trees.
+ */
+
+enum rexp_node_type
+{
+  r_cset,			/* Match from a character set. `a' or `[a-z]'*/
+  r_concat,			/* Concat two subexpressions.   `ab' */
+  r_alternate,			/* Choose one of two subexpressions. `a\|b' */
+  r_opt,			/* Optional subexpression. `a?' */
+  r_star,			/* Repeated subexpression. `a*' */
+
+
+  /* A 2phase-star is a variation on a repeated subexpression.
+   * In this case, there are two subexpressions.  The first, if matched,
+   * begins a repitition (otherwise, the whole expression is matches the
+   * empth string).  
+   * 
+   * After matching the first subexpression, a 2phase star either finishes,
+   * or matches the second subexpression.  If the second subexpression is
+   * matched, then the whole construct repeats.
+   *
+   * 2phase stars are used in two circumstances.  First, they
+   * are used as part of the implementation of POSIX intervals (counted
+   * repititions).  Second, they are used to implement proper star
+   * semantics when the repeated subexpression contains paths of
+   * only side effects.  See rx_compile for more information.
+   */
+  r_2phase_star,
+
+
+  /* c.f. "typedef void * rx_side_effect" */
+  r_side_effect,
+
+  /* This is an extension type:  It is for transient use in source->source
+   * transformations (implemented over syntax trees).
+   */
+  r_data
+};
+
+/* A side effect is a matcher-specific action associated with
+ * transitions in the NFA.  The details of side effects are up
+ * to the matcher.  To the compiler and superstate constructors
+ * side effects are opaque:
+ */
+
+typedef void * rx_side_effect;
+
+/* Nodes in a syntax tree are of this type:
+ */
+struct rexp_node
+{
+  enum rexp_node_type type;
+  union
+  {
+    rx_Bitset cset;
+    rx_side_effect side_effect;
+    struct
+      {
+	struct rexp_node *left;
+	struct rexp_node *right;
+      } pair;
+    void * data;
+  } params;
+};
+
+
+
+
+/* NFA
+ *
+ * A syntax tree is compiled into an NFA.  This page defines the structure
+ * of that NFA.
+ */
+
+struct rx_nfa_state
+{
+  /* These are kept in a list as the NFA is being built. */
+  struct rx_nfa_state *next;
+
+  /* After the NFA is built, states are given integer id's.
+   * States whose outgoing transitions are all either epsilon or 
+   * side effect edges are given ids less than 0.  Other states
+   * are given successive non-negative ids starting from 0.
+   */
+  int id;
+
+  /* The list of NFA edges that go from this state to some other. */
+  struct rx_nfa_edge *edges;
+
+  /* If you land in this state, then you implicitly land
+   * in all other states reachable by only epsilon translations.
+   * Call the set of maximal paths to such states the epsilon closure
+   * of this state.
+   *
+   * There may be other states that are reachable by a mixture of
+   * epsilon and side effect edges.  Consider the set of maximal paths
+   * of that sort from this state.  Call it the epsilon-side-effect
+   * closure of the state.
+   * 
+   * The epsilon closure of the state is a subset of the epsilon-side-
+   * effect closure.  It consists of all the paths that contain 
+   * no side effects -- only epsilon edges.
+   * 
+   * The paths in the epsilon-side-effect closure  can be partitioned
+   * into equivalance sets. Two paths are equivalant if they have the
+   * same set of side effects, in the same order.  The epsilon-closure
+   * is one of these equivalance sets.  Let's call these equivalance
+   * sets: observably equivalant path sets.  That name is chosen
+   * because equivalance of two paths means they cause the same side
+   * effects -- so they lead to the same subsequent observations other
+   * than that they may wind up in different target states.
+   *
+   * The superstate nfa, which is derived from this nfa, is based on
+   * the observation that all of the paths in an observably equivalant
+   * path set can be explored at the same time, provided that the
+   * matcher keeps track not of a single nfa state, but of a set of
+   * states.   In particular, after following all the paths in an
+   * observably equivalant set, you wind up at a set of target states.
+   * That set of target states corresponds to one state in the
+   * superstate NFA.
+   *
+   * Staticly, before matching begins, it is convenient to analyze the
+   * nfa.  Each state is labeled with a list of the observably
+   * equivalant path sets who's union covers all the
+   * epsilon-side-effect paths beginning in this state.  This list is
+   * called the possible futures of the state.
+   *
+   * A trivial example is this NFA:
+   *             s1
+   *         A  --->  B
+   *
+   *             s2  
+   *            --->  C
+   *
+   *             epsilon           s1
+   *            --------->  D   ------> E
+   * 
+   * 
+   * In this example, A has two possible futures.
+   * One invokes the side effect `s1' and contains two paths,
+   * one ending in state B, the other in state E.
+   * The other invokes the side effect `s2' and contains only
+   * one path, landing in state C.
+   */
+  struct rx_possible_future *futures;
+
+
+  /* There are exactly two distinguished states in every NFA: */
+  unsigned int is_final:1;
+  unsigned int is_start:1;
+
+  /* These are used during NFA construction... */
+  unsigned int eclosure_needed:1;
+  unsigned int mark:1;
+};
+
+
+/* An edge in an NFA is typed: */
+enum rx_nfa_etype
+{
+  /* A cset edge is labled with a set of characters one of which
+   * must be matched for the edge to be taken.
+   */
+  ne_cset,
+
+  /* An epsilon edge is taken whenever its starting state is
+   * reached. 
+   */
+  ne_epsilon,
+
+  /* A side effect edge is taken whenever its starting state is
+   * reached.  Side effects may cause the match to fail or the
+   * position of the matcher to advance.
+   */
+  ne_side_effect		/* A special kind of epsilon. */
+};
+
+struct rx_nfa_edge
+{
+  struct rx_nfa_edge *next;
+  enum rx_nfa_etype type;
+  struct rx_nfa_state *dest;
+  union
+  {
+    rx_Bitset cset;
+    rx_side_effect side_effect;
+  } params;
+};
+
+
+
+/* A possible future consists of a list of side effects
+ * and a set of destination states.  Below are their
+ * representations.  These structures are hash-consed which
+ * means that lists with the same elements share a representation
+ * (their addresses are ==).
+ */
+
+struct rx_nfa_state_set
+{
+  struct rx_nfa_state * car;
+  struct rx_nfa_state_set * cdr;
+};
+
+struct rx_se_list
+{
+  rx_side_effect car;
+  struct rx_se_list * cdr;
+};
+
+struct rx_possible_future
+{
+  struct rx_possible_future *next;
+  struct rx_se_list * effects;
+  struct rx_nfa_state_set * destset;
+};
+
+
+
+
+/* This begins the description of the superstate NFA.
+ *
+ * The superstate NFA corresponds to the NFA in these ways:
+ *
+ * Every superstate NFA states SUPER correspond to sets of NFA states,
+ * nfa_states(SUPER).
+ *
+ * Superstate edges correspond to NFA paths.
+ *
+ * The superstate has no epsilon transitions;
+ * every edge has a character label, and a (possibly empty) side
+ * effect label.   The side effect label corresponds to a list of
+ * side effects that occur in the NFA.  These parts are referred
+ * to as:   superedge_character(EDGE) and superedge_sides(EDGE).
+ *
+ * For a superstate edge EDGE starting in some superstate SUPER,
+ * the following is true (in pseudo-notation :-):
+ *
+ *       exists DEST in nfa_states s.t. 
+ *         exists nfaEDGE in nfa_edges s.t.
+ *                 origin (nfaEDGE) == DEST
+ *              && origin (nfaEDGE) is a member of nfa_states(SUPER)
+ *              && exists PF in possible_futures(dest(nfaEDGE)) s.t.
+ * 	                sides_of_possible_future (PF) == superedge_sides (EDGE)
+ *
+ * also:
+ *
+ *      let SUPER2 := superedge_destination(EDGE)
+ *          nfa_states(SUPER2)
+ *           == union of all nfa state sets S s.t.
+ *                          exists PF in possible_futures(dest(nfaEDGE)) s.t.
+ * 	                       sides_of_possible_future (PF) == superedge_sides (EDGE)
+ *                          && S == dests_of_possible_future (PF) }
+ *
+ * Or in english, every superstate is a set of nfa states.  A given
+ * character and a superstate implies many transitions in the NFA --
+ * those that begin with an edge labeled with that character from a
+ * state in the set corresponding to the superstate.
+ * 
+ * The destinations of those transitions each have a set of possible
+ * futures.  A possible future is a list of side effects and a set of
+ * destination NFA states.  Two sets of possible futures can be
+ * `merged' by combining all pairs of possible futures that have the
+ * same side effects.  A pair is combined by creating a new future
+ * with the same side effect but the union of the two destination sets.
+ * In this way, all the possible futures suggested by a superstate
+ * and a character can be merged into a set of possible futures where
+ * no two elements of the set have the same set of side effects.
+ *
+ * The destination of a possible future, being a set of NFA states, 
+ * corresponds to a supernfa state.  So, the merged set of possible
+ * futures we just created can serve as a set of edges in the
+ * supernfa.
+ *
+ * The representation of the superstate nfa and the nfa is critical.
+ * The nfa has to be compact, but has to facilitate the rapid
+ * computation of missing superstates.  The superstate nfa has to 
+ * be fast to interpret, lazilly constructed, and bounded in space.
+ *
+ * To facilitate interpretation, the superstate data structures are 
+ * peppered with `instruction frames'.  There is an instruction set
+ * defined below which matchers using the supernfa must be able to
+ * interpret.
+ *
+ * We'd like to make it possible but not mandatory to use code
+ * addresses to represent instructions (c.f. gcc's computed goto).
+ * Therefore, we define an enumerated type of opcodes, and when
+ * writing one of these instructions into a data structure, use
+ * the opcode as an index into a table of instruction values.
+ * 
+ * Here are the opcodes that occur in the superstate nfa:
+ */
+ 
+
+/* Every superstate contains a table of instruction frames indexed 
+ * by characters.  A normal `move' in a matcher is to fetch the next
+ * character and use it as an index into a superstates transition
+ * table.
+ *
+ * In the fasted case, only one edge follows from that character.
+ * In other cases there is more work to do.
+ * 
+ * The descriptions of the opcodes refer to data structures that are
+ * described further below. 
+ */
+
+enum rx_opcode
+{
+  /* 
+   * BACKTRACK_POINT is invoked when a character transition in 
+   * a superstate leads to more than one edge.  In that case,
+   * the edges have to be explored independently using a backtracking
+   * strategy.
+   *
+   * A BACKTRACK_POINT instruction is stored in a superstate's 
+   * transition table for some character when it is known that that
+   * character crosses more than one edge.  On encountering this
+   * instruction, the matcher saves enough state to backtrack to this
+   * point in the match later.
+   */
+  rx_backtrack_point = 0,	/* data is (struct transition_class *) */
+
+  /* 
+   * RX_DO_SIDE_EFFECTS evaluates the side effects of an epsilon path.
+   * There is one occurence of this instruction per rx_distinct_future.
+   * This instruction is skipped if a rx_distinct_future has no side effects.
+   */
+  rx_do_side_effects = rx_backtrack_point + 1,
+
+  /* data is (struct rx_distinct_future *) */
+
+  /* 
+   * RX_CACHE_MISS instructions are stored in rx_distinct_futures whose
+   * destination superstate has been reclaimed (or was never built).
+   * It recomputes the destination superstate.
+   * RX_CACHE_MISS is also stored in a superstate transition table before
+   * any of its edges have been built.
+   */
+  rx_cache_miss = rx_do_side_effects + 1,
+  /* data is (struct rx_distinct_future *) */
+
+  /* 
+   * RX_NEXT_CHAR is called to consume the next character and take the
+   * corresponding transition.  This is the only instruction that uses 
+   * the DATA field of the instruction frame instead of DATA_2.
+   * (see EXPLORE_FUTURE in regex.c).
+   */
+  rx_next_char = rx_cache_miss + 1, /* data is (struct superstate *) */
+
+  /* RX_BACKTRACK indicates that a transition fails.
+   */
+  rx_backtrack = rx_next_char + 1, /* no data */
+
+  /* 
+   * RX_ERROR_INX is stored only in places that should never be executed.
+   */
+  rx_error_inx = rx_backtrack + 1, /* Not supposed to occur. */
+
+  rx_num_instructions = rx_error_inx + 1
+};
+
+/* An id_instruction_table holds the values stored in instruction
+ * frames.  The table is indexed by the enums declared above.
+ */
+extern void * rx_id_instruction_table[rx_num_instructions];
+
+/* The heart of the matcher is a `word-code-interpreter' 
+ * (like a byte-code interpreter, except that instructions
+ * are a full word wide).
+ *
+ * Instructions are not stored in a vector of code, instead,
+ * they are scattered throughout the data structures built
+ * by the regexp compiler and the matcher.  One word-code instruction,
+ * together with the arguments to that instruction, constitute
+ * an instruction frame (struct rx_inx).
+ *
+ * This structure type is padded by hand to a power of 2 because
+ * in one of the dominant cases, we dispatch by indexing a table
+ * of instruction frames.  If that indexing can be accomplished
+ * by just a shift of the index, we're happy.
+ *
+ * Instructions take at most one argument, but there are two
+ * slots in an instruction frame that might hold that argument.
+ * These are called data and data_2.  The data slot is only
+ * used for one instruction (RX_NEXT_CHAR).  For all other 
+ * instructions, data should be set to 0.
+ *
+ * RX_NEXT_CHAR is the most important instruction by far.
+ * By reserving the data field for its exclusive use, 
+ * instruction dispatch is sped up in that case.  There is
+ * no need to fetch both the instruction and the data,
+ * only the data is needed.  In other words, a `cycle' begins
+ * by fetching the field data.  If that is non-0, then it must
+ * be the destination state of a next_char transition, so
+ * make that value the current state, advance the match position
+ * by one character, and start a new cycle.  On the other hand,
+ * if data is 0, fetch the instruction and do a more complicated
+ * dispatch on that.
+ */
+
+struct rx_inx 
+{
+  void * data;
+  void * data_2;
+  void * inx;
+  void * fnord;
+};
+
+#ifndef RX_TAIL_ARRAY
+#define RX_TAIL_ARRAY  1
+#endif
+
+/* A superstate corresponds to a set of nfa states.  Those sets are
+ * represented by STRUCT RX_SUPERSET.  The constructors
+ * guarantee that only one (shared) structure is created for a given set.
+ */
+struct rx_superset
+{
+  int refs;			/* This is a reference counted structure. */
+
+  /* We keep these sets in a cache because (in an unpredictable way),
+   * the same set is often created again and again.  But that is also
+   * problematic -- compatibility with POSIX and GNU regex requires
+   * that we not be able to tell when a program discards a particular
+   * NFA (thus invalidating the supersets created from it).
+   *
+   * But when a cache hit appears to occur, we will have in hand the
+   * nfa for which it may have happened.  That is why every nfa is given
+   * its own sequence number.  On a cache hit, the cache is validated
+   * by comparing the nfa sequence number to this field:
+   */
+  int id;
+
+  struct rx_nfa_state * car;	/* May or may not be a valid addr. */
+  struct rx_superset * cdr;
+
+  /* If the corresponding superstate exists: */
+  struct rx_superstate * superstate;
+
+
+  /* There is another bookkeeping problem.  It is expensive to 
+   * compute the starting nfa state set for an nfa.  So, once computed,
+   * it is cached in the `struct rx'.
+   *
+   * But, the state set can be flushed from the superstate cache.
+   * When that happens, we can't know if the corresponding `struct rx'
+   * is still alive or if it has been freed or re-used by the program.
+   * So, the cached pointer to this set in a struct rx might be invalid
+   * and we need a way to validate it.
+   *
+   * Fortunately, even if this set is flushed from the cache, it is
+   * not freed.  It just goes on the free-list of supersets.
+   * So we can still examine it.  
+   *
+   * So to validate a starting set memo, check to see if the
+   * starts_for field still points back to the struct rx in question,
+   * and if the ID matches the rx sequence number.
+   */
+  struct rx * starts_for;
+
+  /* This is used to link into a hash bucket so these objects can
+   * be `hash-consed'.
+   */
+  struct rx_hash_item hash_item;
+};
+
+#define rx_protect_superset(RX,CON) (++(CON)->refs)
+
+/* The terminology may be confusing (rename this structure?).
+ * Every character occurs in at most one rx_super_edge per super-state.
+ * But, that structure might have more than one option, indicating a point
+ * of non-determinism. 
+ *
+ * In other words, this structure holds a list of superstate edges
+ * sharing a common starting state and character label.  The edges
+ * are in the field OPTIONS.  All superstate edges sharing the same
+ * starting state and character are in this list.
+ */
+struct rx_super_edge
+{
+  struct rx_super_edge *next;
+  struct rx_inx rx_backtrack_frame;
+  int cset_size;
+  rx_Bitset cset;
+  struct rx_distinct_future *options;
+};
+
+/* A superstate is a set of nfa states (RX_SUPERSET) along
+ * with a transition table.  Superstates are built on demand and reclaimed
+ * without warning.  To protect a superstate from this ghastly fate,
+ * use LOCK_SUPERSTATE. 
+ */
+struct rx_superstate
+{
+  int rx_id;			/* c.f. the id field of rx_superset */
+  int locks;			/* protection from reclamation */
+
+  /* Within a superstate cache, all the superstates are kept in a big
+   * queue.  The tail of the queue is the state most likely to be
+   * reclaimed.  The *recyclable fields hold the queue position of 
+   * this state.
+   */
+  struct rx_superstate * next_recyclable;
+  struct rx_superstate * prev_recyclable;
+
+  /* The supernfa edges that exist in the cache and that have
+   * this state as their destination are kept in this list:
+   */
+  struct rx_distinct_future * transition_refs;
+
+  /* The list of nfa states corresponding to this superstate: */
+  struct rx_superset * contents;
+
+  /* The list of edges in the cache beginning from this state. */
+  struct rx_super_edge * edges;
+
+  /* A tail of the recyclable queue is marked as semifree.  A semifree
+   * state has no incoming next_char transitions -- any transition
+   * into a semifree state causes a complex dispatch with the side
+   * effect of rescuing the state from its semifree state.
+   *
+   * An alternative to this might be to make next_char more expensive,
+   * and to move a state to the head of the recyclable queue whenever
+   * it is entered.  That way, popular states would never be recycled.
+   *
+   * But unilaterally making next_char more expensive actually loses.
+   * So, incoming transitions are only made expensive for states near
+   * the tail of the recyclable queue.  The more cache contention
+   * there is, the more frequently a state will have to prove itself
+   * and be moved back to the front of the queue.  If there is less 
+   * contention, then popular states just aggregate in the front of 
+   * the queue and stay there.
+   */
+  int is_semifree;
+
+
+  /* This keeps track of the size of the transition table for this
+   * state.  There is a half-hearted attempt to support variable sized
+   * superstates.
+   */
+  int trans_size;
+
+  /* Indexed by characters... */
+  struct rx_inx transitions[RX_TAIL_ARRAY];
+};
+
+
+/* A list of distinct futures define the edges that leave from a 
+ * given superstate on a given character.  c.f. rx_super_edge.
+ */
+
+struct rx_distinct_future
+{
+  struct rx_distinct_future * next_same_super_edge[2];
+  struct rx_distinct_future * next_same_dest;
+  struct rx_distinct_future * prev_same_dest;
+  struct rx_superstate * present;	/* source state */
+  struct rx_superstate * future;	/* destination state */
+  struct rx_super_edge * edge;
+
+
+  /* The future_frame holds the instruction that should be executed
+   * after all the side effects are done, when it is time to complete
+   * the transition to the next state.
+   *
+   * Normally this is a next_char instruction, but it may be a
+   * cache_miss instruction as well, depending on whether or not
+   * the superstate is in the cache and semifree.
+   * 
+   * If this is the only future for a given superstate/char, and
+   * if there are no side effects to be performed, this frame is
+   * not used (directly) at all.  Instead, its contents are copied
+   * into the transition table of the starting state of this dist. future.
+   */
+  struct rx_inx future_frame;
+
+  struct rx_inx side_effects_frame;
+  struct rx_se_list * effects;
+};
+
+#define rx_lock_superstate(R,S)  ((S)->locks++)
+#define rx_unlock_superstate(R,S) (--(S)->locks)
+
+
+
+/* This page destined for rx.h */
+
+struct rx_blocklist
+{
+  struct rx_blocklist * next;
+  int bytes;
+};
+
+struct rx_freelist
+{
+  struct rx_freelist * next;
+};
+
+struct rx_cache;
+
+#ifdef __STDC__
+typedef void (*rx_morecore_fn)(struct rx_cache *);
+#else
+typedef void (*rx_morecore_fn)();
+#endif
+
+/* You use this to control the allocation of superstate data 
+ * during matching.  Most of it should be initialized to 0.
+ *
+ * A MORECORE function is necessary.  It should allocate
+ * a new block of memory or return 0.
+ * A default that uses malloc is called `rx_morecore'.
+ *
+ * The number of SUPERSTATES_ALLOWED indirectly limits how much memory
+ * the system will try to allocate.  The default is 128.  Batch style
+ * applications that are very regexp intensive should use as high a number
+ * as possible without thrashing.
+ * 
+ * The LOCAL_CSET_SIZE is the number of characters in a character set.
+ * It is therefore the number of entries in a superstate transition table.
+ * Generally, it should be 256.  If your character set has 16 bits, 
+ * it is better to translate your regexps into equivalent 8 bit patterns.
+ */
+
+struct rx_cache
+{
+  struct rx_hash_rules superset_hash_rules;
+
+  /* Objects are allocated by incrementing a pointer that 
+   * scans across rx_blocklists.
+   */
+  struct rx_blocklist * memory;
+  struct rx_blocklist * memory_pos;
+  int bytes_left;
+  char * memory_addr;
+  rx_morecore_fn morecore;
+
+  /* Freelists. */
+  struct rx_freelist * free_superstates;
+  struct rx_freelist * free_transition_classes;
+  struct rx_freelist * free_discernable_futures;
+  struct rx_freelist * free_supersets;
+  struct rx_freelist * free_hash;
+
+  /* Two sets of superstates -- those that are semifreed, and those
+   * that are being used.
+   */
+  struct rx_superstate * lru_superstate;
+  struct rx_superstate * semifree_superstate;
+
+  struct rx_superset * empty_superset;
+
+  int superstates;
+  int semifree_superstates;
+  int hits;
+  int misses;
+  int superstates_allowed;
+
+  int local_cset_size;
+  void ** instruction_table;
+
+  struct rx_hash superset_table;
+};
+
+
+
+
+/* The lowest-level search function supports arbitrarily fragmented
+ * strings and (optionally) suspendable/resumable searches.
+ *
+ * Callers have to provide a few hooks.
+ */
+
+#ifndef __GNUC__
+#ifdef __STDC__
+#define __const__ const
+#else
+#define __const__
+#endif
+#endif
+
+/* This holds a matcher position */
+struct rx_string_position
+{
+  __const__ unsigned char * pos;	/* The current pos. */
+  __const__ unsigned char * string; /* The current string burst. */
+  __const__ unsigned char * end;	/* First invalid position >= POS. */
+  int offset;			/* Integer address of the current burst. */
+  int size;			/* Current string's size. */
+  int search_direction;		/* 1 or -1 */
+  int search_end;		/* First position to not try. */
+};
+
+
+enum rx_get_burst_return
+{
+  rx_get_burst_continuation,
+  rx_get_burst_error,
+  rx_get_burst_ok,
+  rx_get_burst_no_more
+};
+
+
+/* A call to get burst should make POS valid.  It might be invalid
+ * if the STRING field doesn't point to a burst that actually
+ * contains POS.
+ *
+ * GET_BURST should take a clue from SEARCH_DIRECTION (1 or -1) as to
+ * whether or not to pad to the left.  Padding to the right is always
+ * appropriate, but need not go past the point indicated by STOP.
+ *
+ * If a continuation is returned, then the reentering call to
+ * a search function will retry the get_burst.
+ */
+
+#ifdef __STDC__
+typedef enum rx_get_burst_return
+  (*rx_get_burst_fn) (struct rx_string_position * pos,
+		      void * app_closure,
+		      int stop);
+					       
+#else
+typedef enum rx_get_burst_return (*rx_get_burst_fn) ();
+#endif
+
+
+enum rx_back_check_return
+{
+  rx_back_check_continuation,
+  rx_back_check_error,
+  rx_back_check_pass,
+  rx_back_check_fail
+};
+
+/* Back_check should advance the position it is passed 
+ * over rparen - lparen characters and return pass iff
+ * the characters starting at POS match those indexed
+ * by [LPAREN..RPAREN].
+ *
+ * If a continuation is returned, then the reentering call to
+ * a search function will retry the back_check.
+ */
+
+#ifdef __STDC__
+typedef enum rx_back_check_return
+  (*rx_back_check_fn) (struct rx_string_position * pos,
+		       int lparen,
+		       int rparen,
+		       unsigned char * translate,
+		       void * app_closure,
+		       int stop);
+					       
+#else
+typedef enum rx_back_check_return (*rx_back_check_fn) ();
+#endif
+
+
+
+
+/* A call to fetch_char should return the character at POS or POS + 1.
+ * Returning continuations here isn't supported.  OFFSET is either 0 or 1
+ * and indicates which characters is desired.
+ */
+
+#ifdef __STDC__
+typedef int (*rx_fetch_char_fn) (struct rx_string_position * pos,
+				 int offset,
+				 void * app_closure,
+				 int stop);
+#else
+typedef int (*rx_fetch_char_fn) ();
+#endif
+
+
+enum rx_search_return
+{
+  rx_search_continuation = -4,
+  rx_search_error = -3,
+  rx_search_soft_fail = -2,	/* failed by running out of string */
+  rx_search_fail = -1		/* failed only by reaching failure states */
+  /* return values >= 0 indicate the position of a successful match */
+};
+
+
+
+
+
+
+
+/* regex.h
+ * 
+ * The remaining declarations replace regex.h.
+ */
+
+/* This is an array of error messages corresponding to the error codes.
+ */
+extern __const__ char *re_error_msg[];
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+  REG_NOERROR = 0,	/* Success.  */
+  REG_NOMATCH,		/* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,		/* Invalid pattern.  */
+  REG_ECOLLATE,		/* Not implemented.  */
+  REG_ECTYPE,		/* Invalid character class name.  */
+  REG_EESCAPE,		/* Trailing backslash.  */
+  REG_ESUBREG,		/* Invalid back reference.  */
+  REG_EBRACK,		/* Unmatched left bracket.  */
+  REG_EPAREN,		/* Parenthesis imbalance.  */ 
+  REG_EBRACE,		/* Unmatched \{.  */
+  REG_BADBR,		/* Invalid contents of \{\}.  */
+  REG_ERANGE,		/* Invalid range end.  */
+  REG_ESPACE,		/* Ran out of memory.  */
+  REG_BADRPT,		/* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,		/* Premature end.  */
+  REG_ESIZE,		/* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN		/* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+
+/* The regex.c support, as a client of rx, defines a set of possible
+ * side effects that can be added to the edge lables of nfa edges.
+ * Here is the list of sidef effects in use.
+ */
+
+enum re_side_effects
+{
+#define RX_WANT_SE_DEFS 1
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#define RX_DEF_SE(IDEM, NAME, VALUE)	      NAME VALUE,
+#define RX_DEF_CPLX_SE(IDEM, NAME, VALUE)     NAME VALUE,
+#include "rx.h"
+#undef RX_DEF_SE
+#undef RX_DEF_CPLX_SE
+#undef RX_WANT_SE_DEFS
+   re_floogle_flap = 65533
+};
+
+/* These hold paramaters for the kinds of side effects that are possible
+ * in the supported pattern languages.  These include things like the 
+ * numeric bounds of {} operators and the index of paren registers for 
+ * subexpression measurement or backreferencing.
+ */
+struct re_se_params
+{
+  enum re_side_effects se;
+  int op1;
+  int op2;
+};
+
+typedef unsigned reg_syntax_t;
+
+struct re_pattern_buffer
+{
+  struct rx rx;
+  reg_syntax_t syntax;		/* See below for syntax bit definitions. */
+
+  unsigned int no_sub:1;	/* If set, don't  return register offsets. */
+  unsigned int not_bol:1;	/* If set, the anchors ('^' and '$') don't */
+  unsigned int not_eol:1;	/*     match at the ends of the string.  */  
+  unsigned int newline_anchor:1;/* If true, an anchor at a newline matches.*/
+  unsigned int least_subs:1;	/* If set, and returning registers, return
+				 * as few values as possible.  Only 
+				 * backreferenced groups and group 0 (the whole
+				 * match) will be returned.
+				 */
+
+  /* If true, this says that the matcher should keep registers on its
+   * backtracking stack.  For many patterns, we can easily determine that
+   * this isn't necessary.
+   */
+  unsigned int match_regs_on_stack:1;
+  unsigned int search_regs_on_stack:1;
+
+  /* is_anchored and begbuf_only are filled in by rx_compile. */
+  unsigned int is_anchored:1;	/* Anchorded by ^? */
+  unsigned int begbuf_only:1;	/* Anchored to char position 0? */
+
+  
+  /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+   * for `max (RE_NREGS, re_nsub + 1)' groups.
+   * If REGS_REALLOCATE, reallocate space if necessary.
+   * If REGS_FIXED, use what's there.  
+   */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned int regs_allocated:2;
+
+  
+  /* Either a translate table to apply to all characters before
+   * comparing them, or zero for no translation.  The translation
+   * is applied to a pattern when it is compiled and to a string
+   * when it is matched.
+   */
+  unsigned char * translate;
+
+  /* If this is a valid pointer, it tells rx not to store the extents of 
+   * certain subexpressions (those corresponding to non-zero entries).
+   * Passing 0x1 is the same as passing an array of all ones.  Passing 0x0
+   * is the same as passing an array of all zeros.
+   * The array should contain as many entries as their are subexps in the 
+   * regexp.
+   *
+   * For POSIX compatability, when using regcomp and regexec this field
+   * is zeroed and ignored.
+   */
+  char * syntax_parens;
+
+	/* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+  void * buffer;		/* Malloced memory for the nfa. */
+  unsigned long allocated;	/* Size of that memory. */
+
+  /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+   * the fastmap, if there is one, to skip over impossible
+   * starting points for matches.  */
+  char *fastmap;
+
+  unsigned int fastmap_accurate:1; /* These three are internal. */
+  unsigned int can_match_empty:1;  
+  struct rx_nfa_state * start;	/* The nfa starting state. */
+
+  /* This is the list of iterator bounds for {lo,hi} constructs.
+   * The memory pointed to is part of the rx->buffer.
+   */
+  struct re_se_params *se_params;
+
+  /* This is a bitset representation of the fastmap.
+   * This is a true fastmap that already takes the translate
+   * table into account.
+   */
+  rx_Bitset fastset;
+};
+
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+
+
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals. 
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.  
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically, 
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES. 
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal. 
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+
+
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */ 
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK							\
+  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL			\
+   | RE_NO_BK_PARENS            | RE_NO_BK_REFS				\
+   | RE_NO_BK_VAR               | RE_NO_EMPTY_RANGES			\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK 						\
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP							\
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES				\
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS				\
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP							\
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE			\
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS				\
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP						\
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON						\
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL		\
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC						\
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC					\
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED					\
+  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR				\
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED				\
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS			\
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES				\
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS				\
+   | RE_NO_BK_VBAR	    | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1) 
+
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+ 
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ * `re_match_2' returns information about at least this many registers
+ * the first time a `regs' structure is passed. 
+ *
+ * Also, this is the greatest number of backreferenced subexpressions
+ * allowed in a pattern being matched without caller-supplied registers.
+ */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+extern int rx_cache_bound;
+extern char rx_version_string[];
+
+
+
+
+#ifdef RX_WANT_RX_DEFS
+
+/* This is decls to the interesting subsystems and lower layers
+ * of rx.  Everything which doesn't have a public counterpart in 
+ * regex.c is declared here.
+ */
+
+
+#ifdef __STDC__
+typedef void (*rx_hash_freefn) (struct rx_hash_item * it);
+#else /* ndef __STDC__ */
+typedef void (*rx_hash_freefn) ();
+#endif /* ndef __STDC__ */
+
+
+
+
+
+#ifdef __STDC__
+RX_DECL int rx_bitset_is_equal (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL int rx_bitset_is_subset (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL int rx_bitset_empty (int size, rx_Bitset set);
+RX_DECL void rx_bitset_null (int size, rx_Bitset b);
+RX_DECL void rx_bitset_universe (int size, rx_Bitset b);
+RX_DECL void rx_bitset_complement (int size, rx_Bitset b);
+RX_DECL void rx_bitset_assign (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL void rx_bitset_union (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL void rx_bitset_intersection (int size,
+				     rx_Bitset a, rx_Bitset b);
+RX_DECL void rx_bitset_difference (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL void rx_bitset_revdifference (int size,
+				      rx_Bitset a, rx_Bitset b);
+RX_DECL void rx_bitset_xor (int size, rx_Bitset a, rx_Bitset b);
+RX_DECL unsigned long rx_bitset_hash (int size, rx_Bitset b);
+RX_DECL struct rx_hash_item * rx_hash_find (struct rx_hash * table,
+					    unsigned long hash,
+					    void * value,
+					    struct rx_hash_rules * rules);
+RX_DECL struct rx_hash_item * rx_hash_store (struct rx_hash * table,
+					     unsigned long hash,
+					     void * value,
+					     struct rx_hash_rules * rules);
+RX_DECL void rx_hash_free (struct rx_hash_item * it, struct rx_hash_rules * rules);
+RX_DECL void rx_free_hash_table (struct rx_hash * tab, rx_hash_freefn freefn,
+				 struct rx_hash_rules * rules);
+RX_DECL rx_Bitset rx_cset (struct rx *rx);
+RX_DECL rx_Bitset rx_copy_cset (struct rx *rx, rx_Bitset a);
+RX_DECL void rx_free_cset (struct rx * rx, rx_Bitset c);
+RX_DECL struct rexp_node * rexp_node (struct rx *rx,
+				      enum rexp_node_type type);
+RX_DECL struct rexp_node * rx_mk_r_cset (struct rx * rx,
+					 rx_Bitset b);
+RX_DECL struct rexp_node * rx_mk_r_concat (struct rx * rx,
+					   struct rexp_node * a,
+					   struct rexp_node * b);
+RX_DECL struct rexp_node * rx_mk_r_alternate (struct rx * rx,
+					      struct rexp_node * a,
+					      struct rexp_node * b);
+RX_DECL struct rexp_node * rx_mk_r_opt (struct rx * rx,
+					struct rexp_node * a);
+RX_DECL struct rexp_node * rx_mk_r_star (struct rx * rx,
+					 struct rexp_node * a);
+RX_DECL struct rexp_node * rx_mk_r_2phase_star (struct rx * rx,
+						struct rexp_node * a,
+						struct rexp_node * b);
+RX_DECL struct rexp_node * rx_mk_r_side_effect (struct rx * rx,
+						rx_side_effect a);
+RX_DECL struct rexp_node * rx_mk_r_data  (struct rx * rx,
+					  void * a);
+RX_DECL void rx_free_rexp (struct rx * rx, struct rexp_node * node);
+RX_DECL struct rexp_node * rx_copy_rexp (struct rx *rx,
+					 struct rexp_node *node);
+RX_DECL struct rx_nfa_state * rx_nfa_state (struct rx *rx);
+RX_DECL void rx_free_nfa_state (struct rx_nfa_state * n);
+RX_DECL struct rx_nfa_state * rx_id_to_nfa_state (struct rx * rx,
+						  int id);
+RX_DECL struct rx_nfa_edge * rx_nfa_edge (struct rx *rx,
+					  enum rx_nfa_etype type,
+					  struct rx_nfa_state *start,
+					  struct rx_nfa_state *dest);
+RX_DECL void rx_free_nfa_edge (struct rx_nfa_edge * e);
+RX_DECL void rx_free_nfa (struct rx *rx);
+RX_DECL int rx_build_nfa (struct rx *rx,
+			  struct rexp_node *rexp,
+			  struct rx_nfa_state **start,
+			  struct rx_nfa_state **end);
+RX_DECL void rx_name_nfa_states (struct rx *rx);
+RX_DECL int rx_eclose_nfa (struct rx *rx);
+RX_DECL void rx_delete_epsilon_transitions (struct rx *rx);
+RX_DECL int rx_compactify_nfa (struct rx *rx,
+			       void **mem, unsigned long *size);
+RX_DECL void rx_release_superset (struct rx *rx,
+				  struct rx_superset *set);
+RX_DECL struct rx_superset * rx_superset_cons (struct rx * rx,
+					       struct rx_nfa_state *car, struct rx_superset *cdr);
+RX_DECL struct rx_superset * rx_superstate_eclosure_union
+  (struct rx * rx, struct rx_superset *set, struct rx_nfa_state_set *ecl);
+RX_DECL struct rx_superstate * rx_superstate (struct rx *rx,
+					      struct rx_superset *set);
+RX_DECL struct rx_inx * rx_handle_cache_miss
+  (struct rx *rx, struct rx_superstate *super, unsigned char chr, void *data);
+RX_DECL reg_errcode_t rx_compile (__const__ char *pattern, int size,
+				  reg_syntax_t syntax,
+				  struct re_pattern_buffer * rxb);
+RX_DECL void rx_blow_up_fastmap (struct re_pattern_buffer * rxb);
+#else /* STDC */
+RX_DECL int rx_bitset_is_equal ();
+RX_DECL int rx_bitset_is_subset ();
+RX_DECL int rx_bitset_empty ();
+RX_DECL void rx_bitset_null ();
+RX_DECL void rx_bitset_universe ();
+RX_DECL void rx_bitset_complement ();
+RX_DECL void rx_bitset_assign ();
+RX_DECL void rx_bitset_union ();
+RX_DECL void rx_bitset_intersection ();
+RX_DECL void rx_bitset_difference ();
+RX_DECL void rx_bitset_revdifference ();
+RX_DECL void rx_bitset_xor ();
+RX_DECL unsigned long rx_bitset_hash ();
+RX_DECL struct rx_hash_item * rx_hash_find ();
+RX_DECL struct rx_hash_item * rx_hash_store ();
+RX_DECL void rx_hash_free ();
+RX_DECL void rx_free_hash_table ();
+RX_DECL rx_Bitset rx_cset ();
+RX_DECL rx_Bitset rx_copy_cset ();
+RX_DECL void rx_free_cset ();
+RX_DECL struct rexp_node * rexp_node ();
+RX_DECL struct rexp_node * rx_mk_r_cset ();
+RX_DECL struct rexp_node * rx_mk_r_concat ();
+RX_DECL struct rexp_node * rx_mk_r_alternate ();
+RX_DECL struct rexp_node * rx_mk_r_opt ();
+RX_DECL struct rexp_node * rx_mk_r_star ();
+RX_DECL struct rexp_node * rx_mk_r_2phase_star ();
+RX_DECL struct rexp_node * rx_mk_r_side_effect ();
+RX_DECL struct rexp_node * rx_mk_r_data  ();
+RX_DECL void rx_free_rexp ();
+RX_DECL struct rexp_node * rx_copy_rexp ();
+RX_DECL struct rx_nfa_state * rx_nfa_state ();
+RX_DECL void rx_free_nfa_state ();
+RX_DECL struct rx_nfa_state * rx_id_to_nfa_state ();
+RX_DECL struct rx_nfa_edge * rx_nfa_edge ();
+RX_DECL void rx_free_nfa_edge ();
+RX_DECL void rx_free_nfa ();
+RX_DECL int rx_build_nfa ();
+RX_DECL void rx_name_nfa_states ();
+RX_DECL int rx_eclose_nfa ();
+RX_DECL void rx_delete_epsilon_transitions ();
+RX_DECL int rx_compactify_nfa ();
+RX_DECL void rx_release_superset ();
+RX_DECL struct rx_superset * rx_superset_cons ();
+RX_DECL struct rx_superset * rx_superstate_eclosure_union ();
+RX_DECL struct rx_superstate * rx_superstate ();
+RX_DECL struct rx_inx * rx_handle_cache_miss ();
+RX_DECL reg_errcode_t rx_compile ();
+RX_DECL void rx_blow_up_fastmap ();
+#endif /* STDC */
+
+
+#endif /* RX_WANT_RX_DEFS */
+
+
+
+
+#ifdef __STDC__
+extern int re_search_2 (struct re_pattern_buffer *rxb,
+			__const__ char * string1, int size1,
+			__const__ char * string2, int size2,
+			int startpos, int range,
+			struct re_registers *regs,
+			int stop);
+extern int re_search (struct re_pattern_buffer * rxb, __const__ char *string,
+		      int size, int startpos, int range,
+		      struct re_registers *regs);
+extern int re_match_2 (struct re_pattern_buffer * rxb,
+		       __const__ char * string1, int size1,
+		       __const__ char * string2, int size2,
+		       int pos, struct re_registers *regs, int stop);
+extern int re_match (struct re_pattern_buffer * rxb,
+		     __const__ char * string,
+		     int size, int pos,
+		     struct re_registers *regs);
+extern reg_syntax_t re_set_syntax (reg_syntax_t syntax);
+extern void re_set_registers (struct re_pattern_buffer *bufp,
+			      struct re_registers *regs,
+			      unsigned num_regs,
+			      regoff_t * starts, regoff_t * ends);
+extern __const__ char * re_compile_pattern (__const__ char *pattern,
+					int length,
+					struct re_pattern_buffer * rxb);
+extern int re_compile_fastmap (struct re_pattern_buffer * rxb);
+extern char * re_comp (__const__ char *s);
+extern int re_exec (__const__ char *s);
+extern int regcomp (regex_t * preg, __const__ char * pattern, int cflags);
+extern int regexec (__const__ regex_t *preg, __const__ char *string,
+		    size_t nmatch, regmatch_t pmatch[],
+		    int eflags);
+extern size_t regerror (int errcode, __const__ regex_t *preg,
+			char *errbuf, size_t errbuf_size);
+extern void regfree (regex_t *preg);
+
+#else /* STDC */
+extern int re_search_2 ();
+extern int re_search ();
+extern int re_match_2 ();
+extern int re_match ();
+extern reg_syntax_t re_set_syntax ();
+extern void re_set_registers ();
+extern __const__ char * re_compile_pattern ();
+extern int re_compile_fastmap ();
+extern char * re_comp ();
+extern int re_exec ();
+extern int regcomp ();
+extern int regexec ();
+extern size_t regerror ();
+extern void regfree ();
+
+#endif /* STDC */
+
+
+
+
+#ifdef RX_WANT_RX_DEFS
+
+struct rx_counter_frame
+{
+  int tag;
+  int val;
+  struct rx_counter_frame * inherited_from; /* If this is a copy. */
+  struct rx_counter_frame * cdr;
+};
+
+struct rx_backtrack_frame
+{
+  char * counter_stack_sp;
+
+  /* A frame is used to save the matchers state when it crosses a 
+   * backtracking point.  The `stk_' fields correspond to variables
+   * in re_search_2 (just strip off thes `stk_').  They are documented
+   * tere.
+   */
+  struct rx_superstate * stk_super;
+  unsigned int stk_c;
+  struct rx_string_position stk_test_pos;
+  int stk_last_l;
+  int stk_last_r;
+  int stk_test_ret;
+
+  /* This is the list of options left to explore at the backtrack
+   * point for which this frame was created. 
+   */
+  struct rx_distinct_future * df;
+  struct rx_distinct_future * first_df;
+
+#ifdef RX_DEBUG
+   int stk_line_no;
+#endif
+};
+
+struct rx_stack_chunk
+{
+  struct rx_stack_chunk * next_chunk;
+  int bytes_left;
+  char * sp;
+};
+
+enum rx_outer_entry
+{
+  rx_outer_start,
+  rx_outer_fastmap,
+  rx_outer_test,
+  rx_outer_restore_pos
+};
+
+enum rx_fastmap_return
+{
+  rx_fastmap_continuation,
+  rx_fastmap_error,
+  rx_fastmap_ok,
+  rx_fastmap_fail
+};
+
+enum rx_fastmap_entry
+{
+  rx_fastmap_start,
+  rx_fastmap_string_break
+};
+
+enum rx_test_return
+{
+  rx_test_continuation,
+  rx_test_error,
+  rx_test_fail,
+  rx_test_ok
+};
+
+enum rx_test_internal_return
+{
+  rx_test_internal_error,
+  rx_test_found_first,
+  rx_test_line_finished
+};
+
+enum rx_test_match_entry
+{
+  rx_test_start,
+  rx_test_cache_hit_loop,
+  rx_test_backreference_check,
+  rx_test_backtrack_return
+};
+
+struct rx_search_state
+{
+  /* Two groups of registers are kept.  The group with the register state
+   * of the current test match, and the group that holds the state at the end
+   * of the best known match, if any.
+   *
+   * For some patterns, there may also be registers saved on the stack.
+   */
+  unsigned num_regs;		/* Includes an element for register zero. */
+  regoff_t * lparen;		/* scratch space for register returns */
+  regoff_t * rparen;
+  regoff_t * best_lpspace;	/* in case the user doesn't want these */
+  regoff_t * best_rpspace;	/* values, we still need space to store
+				 * them.  Normally, this memoryis unused
+				 * and the space pointed to by REGS is 
+				 * used instead.
+				 */
+  
+  int last_l;			/* Highest index of a valid lparen. */
+  int last_r;			/* It's dual. */
+  
+  int * best_lparen;		/* This contains the best known register */
+  int * best_rparen;		/* assignments. 
+				 * This may point to the same mem as
+				 * best_lpspace, or it might point to memory
+				 * passed by the caller.
+				 */
+  int best_last_l;		/* best_last_l:best_lparen::last_l:lparen */
+  int best_last_r;
+
+
+  unsigned char * translate;  
+
+  struct rx_string_position outer_pos;
+
+  struct rx_superstate * start_super;
+  int nfa_choice;
+  int first_found;		/* If true, return after finding any match. */
+  int ret_val;
+
+  /* For continuations... */
+  enum rx_outer_entry outer_search_resume_pt;
+  struct re_pattern_buffer * saved_rxb;
+  int saved_startpos;
+  int saved_range;
+  int saved_stop;
+  int saved_total_size;
+  rx_get_burst_fn saved_get_burst;
+  rx_back_check_fn saved_back_check;
+  struct re_registers * saved_regs;
+  
+  /**
+   ** state for fastmap
+   **/
+  char * fastmap;
+  int fastmap_chr;
+  int fastmap_val;
+
+  /* for continuations in the fastmap procedure: */
+  enum rx_fastmap_entry fastmap_resume_pt;
+
+  /**
+   ** state for test_match 
+   **/
+
+  /* The current superNFA position of the matcher. */
+  struct rx_superstate * super;
+  
+  /* The matcher interprets a series of instruction frames.
+   * This is the `instruction counter' for the interpretation.
+   */
+  struct rx_inx * ifr;
+  
+  /* We insert a ghost character in the string to prime
+   * the nfa.  test_pos.pos, test_pos.str_half, and test_pos.end_half
+   * keep track of the test-match position and string-half.
+   */
+  unsigned char c;
+  
+  /* Position within the string. */
+  struct rx_string_position test_pos;
+
+  struct rx_stack_chunk * counter_stack;
+  struct rx_stack_chunk * backtrack_stack;
+  int backtrack_frame_bytes;
+  int chunk_bytes;
+  struct rx_stack_chunk * free_chunks;
+
+  /* To return from this function, set test_ret and 
+   * `goto test_do_return'.
+   *
+   * Possible return values are:
+   *     1   --- end of string while the superNFA is still going
+   *     0   --- internal error (out of memory)
+   *	-1   --- search completed by reaching the superNFA fail state
+   *    -2   --- a match was found, maybe not the longest.
+   *
+   * When the search is complete (-1), best_last_r indicates whether
+   * a match was found.
+   *
+   * -2 is return only if search_state.first_found is non-zero.
+   *
+   * if search_state.first_found is non-zero, a return of -1 indicates no match,
+   * otherwise, best_last_r has to be checked.
+   */
+  int test_ret;
+
+  int could_have_continued;
+  
+#ifdef RX_DEBUG
+  int backtrack_depth;
+  /* There is a search tree with every node as set of deterministic
+   * transitions in the super nfa.  For every branch of a 
+   * backtrack point is an edge in the tree.
+   * This counts up a pre-order of nodes in that tree.
+   * It's saved on the search stack and printed when debugging. 
+   */
+  int line_no;
+  int lines_found;
+#endif
+
+
+  /* For continuations within the match tester */
+  enum rx_test_match_entry test_match_resume_pt;
+  struct rx_inx * saved_next_tr_table;
+  struct rx_inx * saved_this_tr_table;
+  int saved_reg;
+  struct rx_backtrack_frame * saved_bf;
+  
+};
+
+
+
+extern char rx_slowmap[];
+extern unsigned char rx_id_translation[];
+
+static __inline__ void
+init_fastmap (rxb, search_state)
+     struct re_pattern_buffer * rxb;
+     struct rx_search_state * search_state;
+{
+  search_state->fastmap = (rxb->fastmap
+			   ? (char *)rxb->fastmap
+			   : (char *)rx_slowmap);
+  /* Update the fastmap now if not correct already. 
+   * When the regexp was compiled, the fastmap was computed
+   * and stored in a bitset.  This expands the bitset into a
+   * character array containing 1s and 0s.
+   */
+  if ((search_state->fastmap == rxb->fastmap) && !rxb->fastmap_accurate)
+    rx_blow_up_fastmap (rxb);
+  search_state->fastmap_chr = -1;
+  search_state->fastmap_val = 0;
+  search_state->fastmap_resume_pt = rx_fastmap_start;
+}
+
+static __inline__ void
+uninit_fastmap (rxb, search_state)
+     struct re_pattern_buffer * rxb;
+     struct rx_search_state * search_state;
+{
+  /* Unset the fastmap sentinel */
+  if (search_state->fastmap_chr >= 0)
+    search_state->fastmap[search_state->fastmap_chr]
+      = search_state->fastmap_val;
+}
+
+static __inline__ int
+fastmap_search (rxb, stop, get_burst, app_closure, search_state)
+     struct re_pattern_buffer * rxb;
+     int stop;
+     rx_get_burst_fn get_burst;
+     void * app_closure;
+     struct rx_search_state * search_state;
+{
+  enum rx_fastmap_entry pc;
+
+  if (0)
+    {
+    return_continuation:
+      search_state->fastmap_resume_pt = pc;
+      return rx_fastmap_continuation;
+    }
+
+  pc = search_state->fastmap_resume_pt;
+
+  switch (pc)
+    {
+    default:
+      return rx_fastmap_error;
+    case rx_fastmap_start:
+    init_fastmap_sentinal:
+      /* For the sake of fast fastmapping, set a sentinal in the fastmap.
+       * This sentinal will trap the fastmap loop when it reaches the last
+       * valid character in a string half.
+       *
+       * This must be reset when the fastmap/search loop crosses a string 
+       * boundry, and before returning to the caller.  So sometimes,
+       * the fastmap loop is restarted with `continue', othertimes by
+       * `goto init_fastmap_sentinal'.
+       */
+      if (search_state->outer_pos.size)
+	{
+	  search_state->fastmap_chr = ((search_state->outer_pos.search_direction == 1)
+				       ? *(search_state->outer_pos.end - 1)
+				       : *search_state->outer_pos.string);
+	  search_state->fastmap_val
+	    = search_state->fastmap[search_state->fastmap_chr];
+	  search_state->fastmap[search_state->fastmap_chr] = 1;
+	}
+      else
+	{
+	  search_state->fastmap_chr = -1;
+	  search_state->fastmap_val = 0;
+	}
+      
+      if (search_state->outer_pos.pos >= search_state->outer_pos.end)
+	goto fastmap_hit_bound;
+      else
+	{
+	  if (search_state->outer_pos.search_direction == 1)
+	    {
+	      if (search_state->fastmap_val)
+		{
+		  for (;;)
+		    {
+		      while (!search_state->fastmap[*search_state->outer_pos.pos])
+			++search_state->outer_pos.pos;
+		      return rx_fastmap_ok;
+		    }
+		}
+	      else
+		{
+		  for (;;)
+		    {
+		      while (!search_state->fastmap[*search_state->outer_pos.pos])
+			++search_state->outer_pos.pos;
+		      if (*search_state->outer_pos.pos != search_state->fastmap_chr)
+			return rx_fastmap_ok;
+		      else 
+			{
+			  ++search_state->outer_pos.pos;
+			  if (search_state->outer_pos.pos == search_state->outer_pos.end)
+			    goto fastmap_hit_bound;
+			}
+		    }
+		}
+	    }
+	  else
+	    {
+	      __const__ unsigned char * bound;
+	      bound = search_state->outer_pos.string - 1;
+	      if (search_state->fastmap_val)
+		{
+		  for (;;)
+		    {
+		      while (!search_state->fastmap[*search_state->outer_pos.pos])
+			--search_state->outer_pos.pos;
+		      return rx_fastmap_ok;
+		    }
+		}
+	      else
+		{
+		  for (;;)
+		    {
+		      while (!search_state->fastmap[*search_state->outer_pos.pos])
+			--search_state->outer_pos.pos;
+		      if ((*search_state->outer_pos.pos != search_state->fastmap_chr) || search_state->fastmap_val)
+			return rx_fastmap_ok;
+		      else 
+			{
+			  --search_state->outer_pos.pos;
+			  if (search_state->outer_pos.pos == bound)
+			    goto fastmap_hit_bound;
+			}
+		    }
+		}
+	    }
+	}
+      
+    case rx_fastmap_string_break:
+    fastmap_hit_bound:
+      {
+	/* If we hit a bound, it may be time to fetch another burst
+	 * of string, or it may be time to return a continuation to 
+ 	 * the caller, or it might be time to fail.
+	 */
+
+	int burst_state;
+	burst_state = get_burst (&search_state->outer_pos, app_closure, stop);
+	switch (burst_state)
+	  {
+	  default:
+	  case rx_get_burst_error:
+	    return rx_fastmap_error;
+	  case rx_get_burst_continuation:
+	    {
+	      pc = rx_fastmap_string_break;
+	      goto return_continuation;
+	    }
+	  case rx_get_burst_ok:
+	    goto init_fastmap_sentinal;
+	  case rx_get_burst_no_more:
+	    /* ...not a string split, simply no more string. 
+	     *
+	     * When searching backward, running out of string
+	     * is reason to quit.
+	     *
+	     * When searching forward, we allow the possibility
+	     * of an (empty) match after the last character in the
+	     * virtual string.  So, fall through to the matcher
+	     */
+	    return (  (search_state->outer_pos.search_direction == 1)
+		    ? rx_fastmap_ok
+		    : rx_fastmap_fail);
+	  }
+      }
+    }
+
+}
+
+
+
+
+#ifdef emacs
+/* The `emacs' switch turns on certain matching commands
+ * that make sense only in Emacs. 
+ */
+#include "sysfuncs.h"
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+#endif /* emacs */
+
+/* Setting RX_MEMDBUG is useful if you have dbmalloc.  Maybe with similar
+ * packages too.
+ */
+#ifdef RX_MEMDBUG
+#include <malloc.h>
+#endif /* RX_RX_MEMDBUG */
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+ * `BSTRING', as far as I know, and neither of them use this code.  
+ */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+
+#ifndef bcmp
+#define bcmp(s1, s2, n)	memcmp ((s1), (s2), (n))
+#endif
+
+#ifndef bcopy
+#define bcopy(s, d, n)	memcpy ((d), (s), (n))
+#endif
+
+#ifndef bzero
+#define bzero(s, n)	memset ((s), 0, (n))
+#endif
+
+#else /*  HAVE_STRING_H || STDC_HEADERS */
+#include <strings.h>
+#endif   /* not (HAVE_STRING_H || STDC_HEADERS) */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else /* not STDC_HEADERS */
+char *malloc ();
+char *realloc ();
+#endif /* not STDC_HEADERS */
+
+
+
+
+
+/* How many characters in the character set.  */
+#define CHAR_SET_SIZE (1 << CHARBITS)
+
+#ifndef emacs
+/* Define the syntax basics for \<, \>, etc.
+ * This must be nonzero for the wordchar and notwordchar pattern
+ * commands in re_match_2.
+ */
+#ifndef Sword 
+#define Sword 1
+#endif
+#define SYNTAX(c) re_syntax_table[c]
+#ifdef SYNTAX_TABLE
+extern char *re_syntax_table;
+#else
+RX_DECL char re_syntax_table[CHAR_SET_SIZE];
+#endif
+#endif /* not emacs */
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+ *  of `string1' and `string2'.  If only one string, it's `string2'.  
+ */
+
+#define AT_STRINGS_BEG() \
+  (   -1		 \
+   == ((search_state.test_pos.pos - search_state.test_pos.string) \
+       + search_state.test_pos.offset))
+
+#define AT_STRINGS_END() \
+  (   (total_size - 1)	 \
+   == ((search_state.test_pos.pos - search_state.test_pos.string) \
+       + search_state.test_pos.offset))
+
+
+/* Test if POS + 1 points to a character which is word-constituent.  We have
+ * two special cases to check for: if past the end of string1, look at
+ * the first character in string2; and if before the beginning of
+ * string2, look at the last character in string1.
+ *
+ * Assumes `string1' exists, so use in conjunction with AT_STRINGS_BEG ().  
+ */
+#define LETTER_P(POS,OFF)						\
+  (   SYNTAX (fetch_char(POS, OFF, app_closure, stop))			\
+   == Sword)
+
+/* Test if the character at D and the one after D differ with respect
+ * to being word-constituent.  
+ */
+#define AT_WORD_BOUNDARY(d)						\
+  (AT_STRINGS_BEG () || AT_STRINGS_END () || LETTER_P (d,0) != LETTER_P (d, 1))
+
+
+#ifdef RX_SUPPORT_CONTINUATIONS
+#define RX_STACK_ALLOC(BYTES) malloc(BYTES)
+#define RX_STACK_FREE(MEM) free(MEM)
+#else
+#define RX_STACK_ALLOC(BYTES) alloca(BYTES)
+#define RX_STACK_FREE(MEM) \
+      ((struct rx_stack_chunk *)MEM)->next_chunk = search_state.free_chunks; \
+      search_state.free_chunks = ((struct rx_stack_chunk *)MEM);
+
+#endif
+
+#define PUSH(CHUNK_VAR,BYTES)   \
+  if (!CHUNK_VAR || (CHUNK_VAR->bytes_left < (BYTES)))  \
+    {					\
+      struct rx_stack_chunk * new_chunk;	\
+      if (search_state.free_chunks)			\
+	{				\
+	  new_chunk = search_state.free_chunks;	\
+	  search_state.free_chunks = search_state.free_chunks->next_chunk; \
+	}				\
+      else				\
+	{				\
+	  new_chunk = (struct rx_stack_chunk *)RX_STACK_ALLOC(search_state.chunk_bytes); \
+	  if (!new_chunk)		\
+	    {				\
+	      search_state.ret_val = 0;		\
+	      goto test_do_return;	\
+	    }				\
+	}				\
+      new_chunk->sp = (char *)new_chunk + sizeof (struct rx_stack_chunk); \
+      new_chunk->bytes_left = (search_state.chunk_bytes \
+			       - (BYTES) \
+			       - sizeof (struct rx_stack_chunk)); \
+      new_chunk->next_chunk = CHUNK_VAR; \
+      CHUNK_VAR = new_chunk;		\
+    } \
+  else \
+    (CHUNK_VAR->sp += (BYTES)), (CHUNK_VAR->bytes_left -= (BYTES))
+
+#define POP(CHUNK_VAR,BYTES) \
+  if (CHUNK_VAR->sp == ((char *)CHUNK_VAR + sizeof(*CHUNK_VAR))) \
+    { \
+      struct rx_stack_chunk * new_chunk = CHUNK_VAR->next_chunk; \
+      RX_STACK_FREE(CHUNK_VAR); \
+      CHUNK_VAR = new_chunk; \
+    } \
+  else \
+    (CHUNK_VAR->sp -= BYTES), (CHUNK_VAR->bytes_left += BYTES)
+
+
+
+#define SRCH_TRANSLATE(C)  search_state.translate[(unsigned char) (C)]
+
+
+
+
+
+#ifdef __STDC__
+RX_DECL __inline__ int
+rx_search  (struct re_pattern_buffer * rxb,
+	    int startpos,
+	    int range,
+	    int stop,
+	    int total_size,
+	    rx_get_burst_fn get_burst,
+	    rx_back_check_fn back_check,
+	    rx_fetch_char_fn fetch_char,
+	    void * app_closure,
+	    struct re_registers * regs,
+	    struct rx_search_state * resume_state,
+	    struct rx_search_state * save_state)
+#else
+RX_DECL __inline__ int
+rx_search  (rxb, startpos, range, stop, total_size,
+	    get_burst, back_check, fetch_char,
+	    app_closure, regs, resume_state, save_state)
+     struct re_pattern_buffer * rxb;
+     int startpos;
+     int range;
+     int stop;
+     int total_size;
+     rx_get_burst_fn get_burst;
+     rx_back_check_fn back_check;
+     rx_fetch_char_fn fetch_char;
+     void * app_closure;
+     struct re_registers * regs;
+     struct rx_search_state * resume_state;
+     struct rx_search_state * save_state;
+#endif
+{
+  int pc;
+  int test_state;
+  struct rx_search_state search_state;
+
+  search_state.free_chunks = 0;
+  if (!resume_state)
+    pc = rx_outer_start;
+  else
+    {
+      search_state = *resume_state;
+      regs = search_state.saved_regs;
+      rxb = search_state.saved_rxb;
+      startpos = search_state.saved_startpos;
+      range = search_state.saved_range;
+      stop = search_state.saved_stop;
+      total_size = search_state.saved_total_size;
+      get_burst = search_state.saved_get_burst;
+      back_check = search_state.saved_back_check;
+      pc = search_state.outer_search_resume_pt;
+      if (0)
+	{
+	return_continuation:
+	  if (save_state)
+	    {
+	      *save_state = search_state;
+	      save_state->saved_regs = regs;
+	      save_state->saved_rxb = rxb;
+	      save_state->saved_startpos = startpos;
+	      save_state->saved_range = range;
+	      save_state->saved_stop = stop;
+	      save_state->saved_total_size = total_size;
+	      save_state->saved_get_burst = get_burst;
+	      save_state->saved_back_check = back_check;
+	      save_state->outer_search_resume_pt = pc;
+	    }
+	  return rx_search_continuation;
+	}
+    }
+
+  switch (pc)
+    {
+    case rx_outer_start:
+      search_state.ret_val = rx_search_fail;
+      (  search_state.lparen
+       = search_state.rparen
+       = search_state.best_lpspace
+       = search_state.best_rpspace
+       = 0);
+      
+      /* figure the number of registers we may need for use in backreferences.
+       * the number here includes an element for register zero.  
+       */
+      search_state.num_regs = rxb->re_nsub + 1;
+      
+      
+      /* check for out-of-range startpos.  */
+      if ((startpos < 0) || (startpos > total_size))
+	return rx_search_fail;
+      
+      /* fix up range if it might eventually take us outside the string. */
+      {
+	int endpos;
+	endpos = startpos + range;
+	if (endpos < -1)
+	  range = (-1 - startpos);
+	else if (endpos > (total_size + 1))
+	  range = total_size - startpos;
+      }
+      
+      /* if the search isn't to be a backwards one, don't waste time in a
+       * long search for a pattern that says it is anchored.
+       */
+      if (rxb->begbuf_only && (range > 0))
+	{
+	  if (startpos > 0)
+	    return rx_search_fail;
+	  else
+	    range = 1;
+	}
+      
+      /* decide whether to use internal or user-provided reg buffers. */
+      if (!regs || rxb->no_sub)
+	{
+	  search_state.best_lpspace =
+	    (regoff_t *)REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t));
+	  search_state.best_rpspace =
+	    (regoff_t *)REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t));
+	  search_state.best_lparen = search_state.best_lpspace;
+	  search_state.best_rparen = search_state.best_rpspace;
+	}
+      else
+	{	
+	  /* have the register data arrays been allocated?  */
+	  if (rxb->regs_allocated == REGS_UNALLOCATED)
+	    { /* no.  so allocate them with malloc.  we need one
+		 extra element beyond `search_state.num_regs' for the `-1' marker
+		 gnu code uses.  */
+	      regs->num_regs = MAX (RE_NREGS, rxb->re_nsub + 1);
+	      regs->start = ((regoff_t *)
+			     malloc (regs->num_regs * sizeof ( regoff_t)));
+	      regs->end = ((regoff_t *)
+			   malloc (regs->num_regs * sizeof ( regoff_t)));
+	      if (regs->start == 0 || regs->end == 0)
+		return rx_search_error;
+	      rxb->regs_allocated = REGS_REALLOCATE;
+	    }
+	  else if (rxb->regs_allocated == REGS_REALLOCATE)
+	    { /* yes.  if we need more elements than were already
+		 allocated, reallocate them.  if we need fewer, just
+		 leave it alone.  */
+	      if (regs->num_regs < search_state.num_regs + 1)
+		{
+		  regs->num_regs = search_state.num_regs + 1;
+		  regs->start = ((regoff_t *)
+				 realloc (regs->start,
+					  regs->num_regs * sizeof (regoff_t)));
+		  regs->end = ((regoff_t *)
+			       realloc (regs->end,
+					regs->num_regs * sizeof ( regoff_t)));
+		  if (regs->start == 0 || regs->end == 0)
+		    return rx_search_error;
+		}
+	    }
+	  else if (rxb->regs_allocated != REGS_FIXED)
+	    return rx_search_error;
+	  
+	  if (regs->num_regs < search_state.num_regs + 1)
+	    {
+	      search_state.best_lpspace =
+		((regoff_t *)
+		 REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t)));
+	      search_state.best_rpspace =
+		((regoff_t *)
+		 REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t)));
+	      search_state.best_lparen = search_state.best_lpspace;
+	      search_state.best_rparen = search_state.best_rpspace;
+	    }
+	  else
+	    {
+	      search_state.best_lparen = regs->start;
+	      search_state.best_rparen = regs->end;
+	    }
+	}
+      
+      search_state.lparen =
+	(regoff_t *) REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t));
+      search_state.rparen =
+	(regoff_t *) REGEX_ALLOCATE (search_state.num_regs * sizeof(regoff_t)); 
+      
+      if (! (   search_state.best_rparen
+	     && search_state.best_lparen
+	     && search_state.lparen && search_state.rparen))
+	return rx_search_error;
+      
+      search_state.best_last_l = search_state.best_last_r = -1;
+      
+      search_state.translate = (rxb->translate
+				? rxb->translate
+				: rx_id_translation);
+      
+      
+      
+      /*
+       * two nfa's were compiled.  
+       * `0' is complete.
+       * `1' faster but gets registers wrong and ends too soon.
+       */
+      search_state.nfa_choice = (regs && !rxb->least_subs) ? '\0' : '\1';
+      
+      /* we have the option to look for the best match or the first
+       * one we can find.  if the user isn't asking for register information,
+       * we don't need to find the best match.
+       */
+      search_state.first_found = !regs;
+      
+      if (range >= 0)
+	{
+	  search_state.outer_pos.search_end = startpos + range;
+	  search_state.outer_pos.search_direction = 1;
+	}
+      else
+	{
+	  search_state.outer_pos.search_end = startpos + range;
+	  search_state.outer_pos.search_direction = -1;
+	}
+      
+      /* the vacuous search always turns up nothing. */
+      if ((search_state.outer_pos.search_direction == 1)
+	  ? (startpos > search_state.outer_pos.search_end)
+	  : (startpos < search_state.outer_pos.search_end))
+	return rx_search_fail;
+      
+      /* now we build the starting state of the supernfa. */
+      {
+	struct rx_superset * start_contents;
+	struct rx_nfa_state_set * start_nfa_set;
+	
+	/* we presume here that the nfa start state has only one
+	 * possible future with no side effects.  
+	 */
+	start_nfa_set = rxb->start->futures->destset;
+	if (   rxb->rx.start_set
+	    && (rxb->rx.start_set->starts_for == &rxb->rx))
+	  start_contents = rxb->rx.start_set;
+	else
+	  {
+	    start_contents =
+	      rx_superstate_eclosure_union (&rxb->rx,
+					    rx_superset_cons (&rxb->rx, 0, 0),
+					    start_nfa_set);
+	    
+	    if (!start_contents)
+	      return rx_search_fail;
+	    
+	    start_contents->starts_for = &rxb->rx;
+	    rxb->rx.start_set = start_contents;
+	  }
+	if (   start_contents->superstate
+	    && (start_contents->superstate->rx_id == rxb->rx.rx_id))
+	  {
+	    search_state.start_super = start_contents->superstate;
+	    rx_lock_superstate (&rxb->rx, search_state.start_super);
+	  }
+	else
+	  {
+	    rx_protect_superset (&rxb->rx, start_contents);
+	    
+	    search_state.start_super = rx_superstate (&rxb->rx, start_contents);
+	    if (!search_state.start_super)
+	      return rx_search_fail;
+	    rx_lock_superstate (&rxb->rx, search_state.start_super);
+	    rx_release_superset (&rxb->rx, start_contents);
+	  }
+      }
+      
+      
+      /* The outer_pos tracks the position within the strings
+       * as seen by loop that calls fastmap_search.
+       *
+       * The caller supplied get_burst function actually 
+       * gives us pointers to chars.
+       * 
+       * Communication with the get_burst function is through an
+       * rx_string_position structure.  Here, the structure for
+       * outer_pos is initialized.   It is set to point to the
+       * NULL string, at an offset of STARTPOS.  STARTPOS is out
+       * of range of the NULL string, so the first call to 
+       * getburst will patch up the rx_string_position to point
+       * to valid characters.
+       */
+
+      (  search_state.outer_pos.string
+       = search_state.outer_pos.end
+       = 0);
+
+      search_state.outer_pos.offset = 0;
+      search_state.outer_pos.size = 0;
+      search_state.outer_pos.pos = (unsigned char *)startpos;
+      init_fastmap (rxb, &search_state);
+
+      search_state.fastmap_resume_pt = rx_fastmap_start;
+    case rx_outer_fastmap:
+      /* do { */
+    pseudo_do:
+      {
+	{
+	  int fastmap_state;
+	  fastmap_state = fastmap_search (rxb, stop, get_burst, app_closure,
+					  &search_state);
+	  switch (fastmap_state)
+	    {
+	    case rx_fastmap_continuation:
+	      pc = rx_outer_fastmap;
+	      goto return_continuation;
+	    case rx_fastmap_fail:
+	      goto finish;
+	    case rx_fastmap_ok:
+	      break;
+	    }
+	}
+	
+	/* now the fastmap loop has brought us to a plausible 
+	 * starting point for a match.  so, it's time to run the
+	 * nfa and see if a match occured.
+	 */
+	startpos = (  search_state.outer_pos.pos
+		    - search_state.outer_pos.string
+		    + search_state.outer_pos.offset);
+/*|*/	if ((range > 0) && (startpos == search_state.outer_pos.search_end))
+/*|*/	  goto finish;
+      }
+
+      search_state.test_match_resume_pt = rx_test_start;
+      /* do interrupted for entry point... */
+    case rx_outer_test:
+      /* ...do continued */
+      {
+	goto test_match;
+      test_returns_to_search:
+	switch (test_state)
+	  {
+	  case rx_test_continuation:
+	    pc = rx_outer_test;
+	    goto return_continuation;
+	  case rx_test_error:
+	    search_state.ret_val = rx_search_error;
+	    goto finish;
+	  case rx_test_fail:
+	    break;
+	  case rx_test_ok:
+	    goto finish;
+	  }
+	search_state.outer_pos.pos += search_state.outer_pos.search_direction;
+	startpos += search_state.outer_pos.search_direction;
+#if 0
+/*|*/	if (search_state.test_pos.pos < search_state.test_pos.end)
+/*|*/	  break;
+#endif
+      }
+      /* do interrupted for entry point... */
+    case rx_outer_restore_pos:
+      {
+	int x;
+	x = get_burst (&search_state.outer_pos, app_closure, stop);
+	switch (x)
+	  {
+	  case rx_get_burst_continuation:
+	    pc = rx_outer_restore_pos;
+	    goto return_continuation;
+	  case rx_get_burst_error:
+	    search_state.ret_val = rx_search_error;
+	    goto finish;
+	  case rx_get_burst_no_more:
+	    if (rxb->can_match_empty)
+	      break;
+	    goto finish;
+	  case rx_get_burst_ok:
+	    break;
+	  }
+      } /* } while (...see below...) */
+
+      if ((search_state.outer_pos.search_direction == 1)
+	  ? (startpos < search_state.outer_pos.search_end)
+	  : (startpos > search_state.outer_pos.search_end))
+	goto pseudo_do;
+
+	
+    finish:
+      uninit_fastmap (rxb, &search_state);
+      if (search_state.start_super)
+	rx_unlock_superstate (&rxb->rx, search_state.start_super);
+      
+#ifdef regex_malloc
+      if (search_state.lparen) free (search_state.lparen);
+      if (search_state.rparen) free (search_state.rparen);
+      if (search_state.best_lpspace) free (search_state.best_lpspace);
+      if (search_state.best_rpspace) free (search_state.best_rpspace);
+#endif
+      return search_state.ret_val;
+    }
+
+
+ test_match:
+  {
+    enum rx_test_match_entry test_pc;
+    int inx;
+    test_pc = search_state.test_match_resume_pt;
+    if (test_pc == rx_test_start)
+      {
+#ifdef RX_DEBUG
+	search_state.backtrack_depth = 0;
+#endif
+	search_state.last_l = search_state.last_r = 0;
+	search_state.lparen[0] = startpos;
+	search_state.super = search_state.start_super;
+	search_state.c = search_state.nfa_choice;
+	search_state.test_pos.pos = search_state.outer_pos.pos - 1;    
+	search_state.test_pos.string = search_state.outer_pos.string;
+	search_state.test_pos.end = search_state.outer_pos.end;
+	search_state.test_pos.offset = search_state.outer_pos.offset;
+	search_state.test_pos.size = search_state.outer_pos.size;
+	search_state.test_pos.search_direction = 1;
+	search_state.counter_stack = 0;
+	search_state.backtrack_stack = 0;
+	search_state.backtrack_frame_bytes =
+	  (sizeof (struct rx_backtrack_frame)
+	   + (rxb->match_regs_on_stack
+	      ? sizeof (regoff_t) * (search_state.num_regs + 1) * 2
+	      : 0));
+	search_state.chunk_bytes = search_state.backtrack_frame_bytes * 64;
+	search_state.test_ret = rx_test_line_finished;
+	search_state.could_have_continued = 0;
+      }  
+    /* This is while (1)...except that the body of the loop is interrupted 
+     * by some alternative entry points.
+     */
+  pseudo_while_1:
+    switch (test_pc)
+      {
+      case rx_test_cache_hit_loop:
+	goto resume_continuation_1;
+      case rx_test_backreference_check:
+	goto resume_continuation_2;
+      case rx_test_backtrack_return:
+	goto resume_continuation_3;
+      case rx_test_start:
+#ifdef RX_DEBUG
+	/* There is a search tree with every node as set of deterministic
+	 * transitions in the super nfa.  For every branch of a 
+	 * backtrack point is an edge in the tree.
+	 * This counts up a pre-order of nodes in that tree.
+	 * It's saved on the search stack and printed when debugging. 
+	 */
+	search_state.line_no = 0;
+	search_state.lines_found = 0;
+#endif
+	
+      top_of_cycle:
+	/* A superstate is basicly a transition table, indexed by 
+	 * characters from the string being tested, and containing 
+	 * RX_INX (`instruction frame') structures.
+	 */
+	search_state.ifr = &search_state.super->transitions [search_state.c];
+	
+      recurse_test_match:
+	/* This is the point to which control is sent when the
+	 * test matcher `recurses'.  Before jumping here, some variables
+	 * need to be saved on the stack and the next instruction frame
+	 * has to be computed.
+	 */
+	
+      restart:
+	/* Some instructions don't advance the matcher, but just
+	 * carry out some side effects and fetch a new instruction.
+	 * To dispatch that new instruction, `goto restart'.
+	 */
+	
+	{
+	  struct rx_inx * next_tr_table;
+	  struct rx_inx * this_tr_table;
+	  /* The fastest route through the loop is when the instruction 
+	   * is RX_NEXT_CHAR.  This case is detected when SEARCH_STATE.IFR->DATA
+	   * is non-zero.  In that case, it points to the next
+	   * superstate. 
+	   *
+	   * This allows us to not bother fetching the bytecode.
+	   */
+	  next_tr_table = (struct rx_inx *)search_state.ifr->data;
+	  this_tr_table = search_state.super->transitions;
+	  while (next_tr_table)
+	    {
+#ifdef RX_DEBUG_0
+	      if (rx_debug_trace)
+		{
+		  struct rx_superset * setp;
+		  
+		  fprintf (stderr, "%d %d>> re_next_char @ %d (%d)",
+			   search_state.line_no,
+			   search_state.backtrack_depth,
+			   (search_state.test_pos.pos - search_state.test_pos.string
+			    + search_state.test_pos.offset), search_state.c);
+		  
+		  search_state.super =
+		    ((struct rx_superstate *)
+		     ((char *)this_tr_table
+		      - ((unsigned long)
+			 ((struct rx_superstate *)0)->transitions)));
+		  
+		  setp = search_state.super->contents;
+		  fprintf (stderr, "   superstet (rx=%d, &=%x: ",
+			   rxb->rx.rx_id, setp);
+		  while (setp)
+		    {
+		      fprintf (stderr, "%d ", setp->id);
+		      setp = setp->cdr;
+		    }
+		  fprintf (stderr, "\n");
+		}
+#endif
+	      this_tr_table = next_tr_table;
+	      ++search_state.test_pos.pos;
+	      if (search_state.test_pos.pos == search_state.test_pos.end)
+		{
+		  int burst_state;
+		try_burst_1:
+		  burst_state = get_burst (&search_state.test_pos,
+					   app_closure, stop);
+		  switch (burst_state)
+		    {
+		    case rx_get_burst_continuation:
+		      search_state.saved_this_tr_table = this_tr_table;
+		      search_state.saved_next_tr_table = next_tr_table;
+		      test_pc = rx_test_cache_hit_loop;
+		      goto test_return_continuation;
+		      
+		    resume_continuation_1:
+		      /* Continuation one jumps here to do its work: */
+		      search_state.saved_this_tr_table = this_tr_table;
+		      search_state.saved_next_tr_table = next_tr_table;
+		      goto try_burst_1;
+		      
+		    case rx_get_burst_ok:
+		      /* get_burst succeeded...keep going */
+		      break;
+		      
+		    case rx_get_burst_no_more:
+		      search_state.test_ret = rx_test_line_finished;
+		      search_state.could_have_continued = 1;
+		      goto test_do_return;
+		      
+		    case rx_get_burst_error:
+		      /* An error... */
+		      search_state.test_ret = rx_test_internal_error;
+		      goto test_do_return;
+		    }
+		}
+	      search_state.c = *search_state.test_pos.pos;
+	      search_state.ifr = this_tr_table + search_state.c;
+	      next_tr_table = (struct rx_inx *)search_state.ifr->data;
+	    } /* Fast loop through cached transition tables */
+	  
+	  /* Here when we ran out of cached next-char transitions. 
+	   * So, it will be necessary to do a more expensive
+	   * dispatch on the current instruction.  The superstate
+	   * pointer is allowed to become invalid during next-char
+	   * transitions -- now we must bring it up to date.
+	   */
+	  search_state.super =
+	    ((struct rx_superstate *)
+	     ((char *)this_tr_table
+	      - ((unsigned long)
+		 ((struct rx_superstate *)0)->transitions)));
+	}
+	
+	/* We've encountered an instruction other than next-char.
+	 * Dispatch that instruction:
+	 */
+	inx = (int)search_state.ifr->inx;
+#ifdef RX_DEBUG_0
+	if (rx_debug_trace)
+	  {
+	    struct rx_superset * setp = search_state.super->contents;
+	    
+	    fprintf (stderr, "%d %d>> %s @ %d (%d)", search_state.line_no,
+		     search_state.backtrack_depth,
+		     inx_names[inx],
+		     (search_state.test_pos.pos - search_state.test_pos.string
+		      + (test_pos.half == 0 ? 0 : size1)), search_state.c);
+	    
+	    fprintf (stderr, "   superstet (rx=%d, &=%x: ",
+		     rxb->rx.rx_id, setp);
+	    while (setp)
+	      {
+		fprintf (stderr, "%d ", setp->id);
+		setp = setp->cdr;
+	      }
+	    fprintf (stderr, "\n");
+	  }
+#endif
+	switch ((enum rx_opcode)inx)
+	  {
+	  case rx_do_side_effects:
+	    
+	    /*  RX_DO_SIDE_EFFECTS occurs when we cross epsilon 
+	     *  edges associated with parentheses, backreferencing, etc.
+	     */
+	    {
+	      struct rx_distinct_future * df =
+		(struct rx_distinct_future *)search_state.ifr->data_2;
+	      struct rx_se_list * el = df->effects;
+	      /* Side effects come in lists.  This walks down
+	       * a list, dispatching.
+	       */
+	      while (el)
+		{
+		  long effect;
+		  effect = (long)el->car;
+		  if (effect < 0)
+		    {
+#ifdef RX_DEBUG_0
+		      if (rx_debug_trace)
+			{
+			  struct rx_superset * setp = search_state.super->contents;
+			  
+			  fprintf (stderr, "....%d %d>> %s\n", search_state.line_no,
+				   search_state.backtrack_depth,
+				   efnames[-effect]);
+			}
+#endif
+		      switch ((enum re_side_effects) effect)
+
+			{
+			case re_se_pushback:
+			  search_state.ifr = &df->future_frame;
+			  if (!search_state.ifr->data)
+			    {
+			      struct rx_superstate * sup;
+			      sup = search_state.super;
+			      rx_lock_superstate (rx, sup);
+			      if (!rx_handle_cache_miss (&rxb->rx,
+							 search_state.super,
+							 search_state.c,
+							 (search_state.ifr
+							  ->data_2)))
+				{
+				  rx_unlock_superstate (rx, sup);
+				  search_state.test_ret = rx_test_internal_error;
+				  goto test_do_return;
+				}
+			      rx_unlock_superstate (rx, sup);
+			    }
+			  /* --search_state.test_pos.pos; */
+			  search_state.c = 't';
+			  search_state.super
+			    = ((struct rx_superstate *)
+			       ((char *)search_state.ifr->data
+				- (long)(((struct rx_superstate *)0)
+					 ->transitions)));
+			  goto top_of_cycle;
+			  break;
+			case re_se_push0:
+			  {
+			    struct rx_counter_frame * old_cf
+			      = (search_state.counter_stack
+				 ? ((struct rx_counter_frame *)
+				    search_state.counter_stack->sp)
+				 : 0);
+			    struct rx_counter_frame * cf;
+			    PUSH (search_state.counter_stack,
+				  sizeof (struct rx_counter_frame));
+			    cf = ((struct rx_counter_frame *)
+				  search_state.counter_stack->sp);
+			    cf->tag = re_se_iter;
+			    cf->val = 0;
+			    cf->inherited_from = 0;
+			    cf->cdr = old_cf;
+			    break;
+			  }
+			case re_se_fail:
+			  goto test_do_return;
+			case re_se_begbuf:
+			  if (!AT_STRINGS_BEG ())
+			    goto test_do_return;
+			  break;
+			case re_se_endbuf:
+			  if (!AT_STRINGS_END ())
+			    goto test_do_return;
+			  break;
+			case re_se_wordbeg:
+			  if (   LETTER_P (&search_state.test_pos, 1)
+			      && (   AT_STRINGS_BEG()
+				  || !LETTER_P (&search_state.test_pos, 0)))
+			    break;
+			  else
+			    goto test_do_return;
+			case re_se_wordend:
+			  if (   !AT_STRINGS_BEG ()
+			      && LETTER_P (&search_state.test_pos, 0)
+			      && (AT_STRINGS_END ()
+				  || !LETTER_P (&search_state.test_pos, 1)))
+			    break;
+			  else
+			    goto test_do_return;
+			case re_se_wordbound:
+			  if (AT_WORD_BOUNDARY (&search_state.test_pos))
+			    break;
+			  else
+			    goto test_do_return;
+			case re_se_notwordbound:
+			  if (!AT_WORD_BOUNDARY (&search_state.test_pos))
+			    break;
+			  else
+			    goto test_do_return;
+			case re_se_hat:
+			  if (AT_STRINGS_BEG ())
+			    {
+			      if (rxb->not_bol)
+				goto test_do_return;
+			      else
+				break;
+			    }
+			  else
+			    {
+			      char pos_c = *search_state.test_pos.pos;
+			      if (   (SRCH_TRANSLATE (pos_c)
+				      == SRCH_TRANSLATE('\n'))
+				  && rxb->newline_anchor)
+				break;
+			      else
+				goto test_do_return;
+			    }
+			case re_se_dollar:
+			  if (AT_STRINGS_END ())
+			    {
+			      if (rxb->not_eol)
+				goto test_do_return;
+			      else
+				break;
+			    }
+			  else
+			    {
+			      if (   (   SRCH_TRANSLATE (fetch_char
+						    (&search_state.test_pos, 1,
+						     app_closure, stop))
+				      == SRCH_TRANSLATE ('\n'))
+				  && rxb->newline_anchor)
+				break;
+			      else
+				goto test_do_return;
+			    }
+			  
+			case re_se_try:
+			  /* This is the first side effect in every
+			   * expression.
+			   *
+			   *  FOR NO GOOD REASON...get rid of it...
+			   */
+			  break;
+			  
+			case re_se_pushpos:
+			  {
+			    int urhere =
+			      ((int)(search_state.test_pos.pos
+				     - search_state.test_pos.string)
+			       + search_state.test_pos.offset);
+			    struct rx_counter_frame * old_cf
+			      = (search_state.counter_stack
+				 ? ((struct rx_counter_frame *)
+				    search_state.counter_stack->sp)
+				 : 0);
+			    struct rx_counter_frame * cf;
+			    PUSH(search_state.counter_stack,
+				 sizeof (struct rx_counter_frame));
+			    cf = ((struct rx_counter_frame *)
+				  search_state.counter_stack->sp);
+			    cf->tag = re_se_pushpos;
+			    cf->val = urhere;
+			    cf->inherited_from = 0;
+			    cf->cdr = old_cf;
+			    break;
+			  }
+			  
+			case re_se_chkpos:
+			  {
+			    int urhere =
+			      ((int)(search_state.test_pos.pos
+				     - search_state.test_pos.string)
+			       + search_state.test_pos.offset);
+			    struct rx_counter_frame * cf
+			      = ((struct rx_counter_frame *)
+				 search_state.counter_stack->sp);
+			    if (cf->val == urhere)
+			      goto test_do_return;
+			    cf->val = urhere;
+			    break;
+			  }
+			  break;
+			  
+			case re_se_poppos:
+			  POP(search_state.counter_stack,
+			      sizeof (struct rx_counter_frame));
+			  break;
+			  
+			  
+			case re_se_at_dot:
+			case re_se_syntax:
+			case re_se_not_syntax:
+#ifdef emacs
+			  /* 
+			   * this release lacks emacs support
+			   */
+#endif
+			  break;
+			case re_se_win:
+			case re_se_lparen:
+			case re_se_rparen:
+			case re_se_backref:
+			case re_se_iter:
+			case re_se_end_iter:
+			case re_se_tv:
+			case re_floogle_flap:
+			  search_state.ret_val = 0;
+			  goto test_do_return;
+			}
+		    }
+		  else
+		    {
+#ifdef RX_DEBUG_0
+		      if (rx_debug_trace)
+			fprintf (stderr, "....%d %d>> %s %d %d\n", search_state.line_no,
+				 search_state.backtrack_depth,
+				 efnames2[rxb->se_params [effect].se],
+				 rxb->se_params [effect].op1,
+				 rxb->se_params [effect].op2);
+#endif
+		      switch (rxb->se_params [effect].se)
+			{
+			case re_se_win:
+			  /* This side effect indicates that we've 
+			   * found a match, though not necessarily the 
+			   * best match.  This is a fancy assignment to 
+			   * register 0 unless the caller didn't 
+			   * care about registers.  In which case,
+			   * this stops the match.
+			   */
+			  {
+			    int urhere =
+			      ((int)(search_state.test_pos.pos
+				     - search_state.test_pos.string)
+			       + search_state.test_pos.offset);
+			    
+			    if (   (search_state.best_last_r < 0)
+				|| (urhere + 1 > search_state.best_rparen[0]))
+			      {
+				/* Record the best known and keep
+				 * looking.
+				 */
+				int x;
+				for (x = 0; x <= search_state.last_l; ++x)
+				  search_state.best_lparen[x] = search_state.lparen[x];
+				search_state.best_last_l = search_state.last_l;
+				for (x = 0; x <= search_state.last_r; ++x)
+				  search_state.best_rparen[x] = search_state.rparen[x];
+				search_state.best_rparen[0] = urhere + 1;
+				search_state.best_last_r = search_state.last_r;
+			      }
+			    /* If we're not reporting the match-length 
+			     * or other register info, we need look no
+			     * further.
+			     */
+			    if (search_state.first_found)
+			      {
+				search_state.test_ret = rx_test_found_first;
+				goto test_do_return;
+			      }
+			  }
+			  break;
+			case re_se_lparen:
+			  {
+			    int urhere =
+			      ((int)(search_state.test_pos.pos
+				     - search_state.test_pos.string)
+			       + search_state.test_pos.offset);
+			    
+			    int reg = rxb->se_params [effect].op1;
+#if 0
+			    if (reg > search_state.last_l)
+#endif
+			      {
+				search_state.lparen[reg] = urhere + 1;
+				/* In addition to making this assignment,
+				 * we now know that lower numbered regs
+				 * that haven't already been assigned,
+				 * won't be.  We make sure they're
+				 * filled with -1, so they can be
+				 * recognized as unassigned.
+				 */
+				if (search_state.last_l < reg)
+				  while (++search_state.last_l < reg)
+				    search_state.lparen[search_state.last_l] = -1;
+			      }
+			    break;
+			  }
+			  
+			case re_se_rparen:
+			  {
+			    int urhere =
+			      ((int)(search_state.test_pos.pos
+				     - search_state.test_pos.string)
+			       + search_state.test_pos.offset);
+			    int reg = rxb->se_params [effect].op1;
+			    search_state.rparen[reg] = urhere + 1;
+			    if (search_state.last_r < reg)
+			      {
+				while (++search_state.last_r < reg)
+				  search_state.rparen[search_state.last_r]
+				    = -1;
+			      }
+			    break;
+			  }
+			  
+			case re_se_backref:
+			  {
+			    int reg = rxb->se_params [effect].op1;
+			    if (   reg > search_state.last_r
+				|| search_state.rparen[reg] < 0)
+			      goto test_do_return;
+			    
+			    {
+			      int backref_status;
+			    check_backreference:
+			      backref_status
+				= back_check (&search_state.test_pos,
+					      search_state.lparen[reg],
+					      search_state.rparen[reg],
+					      search_state.translate,
+					      app_closure,
+					      stop);
+			      switch (backref_status)
+				{
+				case rx_back_check_continuation:
+				  search_state.saved_reg = reg;
+				  test_pc = rx_test_backreference_check;
+				  goto test_return_continuation;
+				resume_continuation_2:
+				  reg = search_state.saved_reg;
+				  goto check_backreference;
+				case rx_back_check_fail:
+				  /* Fail */
+				  goto test_do_return;
+				case rx_back_check_pass:
+				  /* pass --
+				   * test_pos now advanced to last
+				   * char matched by backref
+				   */
+				  break;
+				}
+			    }
+			    break;
+			  }
+			case re_se_iter:
+			  {
+			    struct rx_counter_frame * csp
+			      = ((struct rx_counter_frame *)
+				 search_state.counter_stack->sp);
+			    if (csp->val == rxb->se_params[effect].op2)
+			      goto test_do_return;
+			    else
+			      ++csp->val;
+			    break;
+			  }
+			case re_se_end_iter:
+			  {
+			    struct rx_counter_frame * csp
+			      = ((struct rx_counter_frame *)
+				 search_state.counter_stack->sp);
+			    if (csp->val < rxb->se_params[effect].op1)
+			      goto test_do_return;
+			    else
+			      {
+				struct rx_counter_frame * source = csp;
+				while (source->inherited_from)
+				  source = source->inherited_from;
+				if (!source || !source->cdr)
+				  {
+				    POP(search_state.counter_stack,
+					sizeof(struct rx_counter_frame));
+				  }
+				else
+				  {
+				    source = source->cdr;
+				    csp->val = source->val;
+				    csp->tag = source->tag;
+				    csp->cdr = 0;
+				    csp->inherited_from = source;
+				  }
+			      }
+			    break;
+			  }
+			case re_se_tv:
+			  /* is a noop */
+			  break;
+			case re_se_try:
+			case re_se_pushback:
+			case re_se_push0:
+			case re_se_pushpos:
+			case re_se_chkpos:
+			case re_se_poppos:
+			case re_se_at_dot:
+			case re_se_syntax:
+			case re_se_not_syntax:
+			case re_se_begbuf:
+			case re_se_hat:
+			case re_se_wordbeg:
+			case re_se_wordbound:
+			case re_se_notwordbound:
+			case re_se_wordend:
+			case re_se_endbuf:
+			case re_se_dollar:
+			case re_se_fail:
+			case re_floogle_flap:
+			  search_state.ret_val = 0;
+			  goto test_do_return;
+			}
+		    }
+		  el = el->cdr;
+		}
+	      /* Now the side effects are done,
+	       * so get the next instruction.
+	       * and move on.
+	       */
+	      search_state.ifr = &df->future_frame;
+	      goto restart;
+	    }
+	    
+	  case rx_backtrack_point:
+	    {
+	      /* A backtrack point indicates that we've reached a
+	       * non-determinism in the superstate NFA.  This is a
+	       * loop that exhaustively searches the possibilities.
+	       *
+	       * A backtracking strategy is used.  We keep track of what
+	       * registers are valid so we can erase side effects.
+	       *
+	       * First, make sure there is some stack space to hold 
+	       * our state.
+	       */
+	      
+	      struct rx_backtrack_frame * bf;
+	      
+	      PUSH(search_state.backtrack_stack,
+		   search_state.backtrack_frame_bytes);
+#ifdef RX_DEBUG_0
+	      ++search_state.backtrack_depth;
+#endif
+	      
+	      bf = ((struct rx_backtrack_frame *)
+		    search_state.backtrack_stack->sp);
+	      {
+		bf->stk_super = search_state.super;
+		/* We prevent the current superstate from being
+		 * deleted from the superstate cache.
+		 */
+		rx_lock_superstate (&rxb->rx, search_state.super);
+#ifdef RX_DEBUG_0
+		bf->stk_search_state.line_no = search_state.line_no;
+#endif
+		bf->stk_c = search_state.c;
+		bf->stk_test_pos = search_state.test_pos;
+		bf->stk_last_l = search_state.last_l;
+		bf->stk_last_r = search_state.last_r;
+		bf->df = ((struct rx_super_edge *)
+			  search_state.ifr->data_2)->options;
+		bf->first_df = bf->df;
+		bf->counter_stack_sp = (search_state.counter_stack
+					? search_state.counter_stack->sp
+					: 0);
+		bf->stk_test_ret = search_state.test_ret;
+		if (rxb->match_regs_on_stack)
+		  {
+		    int x;
+		    regoff_t * stk =
+		      (regoff_t *)((char *)bf + sizeof (*bf));
+		    for (x = 0; x <= search_state.last_l; ++x)
+		      stk[x] = search_state.lparen[x];
+		    stk += x;
+		    for (x = 0; x <= search_state.last_r; ++x)
+		      stk[x] = search_state.rparen[x];
+		  }
+	      }
+	      
+	      /* Here is a while loop whose body is mainly a function
+	       * call and some code to handle a return from that
+	       * function.
+	       *
+	       * From here on for the rest of `case backtrack_point' it
+	       * is unsafe to assume that the search_state copies of 
+	       * variables saved on the backtracking stack are valid
+	       * -- so read their values from the backtracking stack.
+	       *
+	       * This lets us use one generation fewer stack saves in
+	       * the call-graph of a search.
+	       */
+	      
+	    while_non_det_options:
+#ifdef RX_DEBUG_0
+	      ++search_state.lines_found;
+	      if (rx_debug_trace)
+		fprintf (stderr, "@@@ %d calls %d @@@\n",
+			 search_state.line_no, search_state.lines_found);
+	      
+	      search_state.line_no = search_state.lines_found;
+#endif
+	      
+	      if (bf->df->next_same_super_edge[0] == bf->first_df)
+		{
+		  /* This is a tail-call optimization -- we don't recurse
+		   * for the last of the possible futures.
+		   */
+		  search_state.ifr = (bf->df->effects
+				      ? &bf->df->side_effects_frame
+				      : &bf->df->future_frame);
+		  
+		  rx_unlock_superstate (&rxb->rx, search_state.super);
+		  POP(search_state.backtrack_stack,
+		      search_state.backtrack_frame_bytes);
+#ifdef RX_DEBUG
+		  --search_state.backtrack_depth;
+#endif
+		  goto restart;
+		}
+	      else
+		{
+		  if (search_state.counter_stack)
+		    {
+		      struct rx_counter_frame * old_cf
+			= ((struct rx_counter_frame *)search_state.counter_stack->sp);
+		      struct rx_counter_frame * cf;
+		      PUSH(search_state.counter_stack, sizeof (struct rx_counter_frame));
+		      cf = ((struct rx_counter_frame *)search_state.counter_stack->sp);
+		      cf->tag = old_cf->tag;
+		      cf->val = old_cf->val;
+		      cf->inherited_from = old_cf;
+		      cf->cdr = 0;
+		    }			
+		  /* `Call' this test-match block */
+		  search_state.ifr = (bf->df->effects
+				      ? &bf->df->side_effects_frame
+				      : &bf->df->future_frame);
+		  goto recurse_test_match;
+		}
+	      
+	      /* Returns in this block are accomplished by
+	       * goto test_do_return.  There are two cases.
+	       * If there is some search-stack left,
+	       * then it is a return from a `recursive' call.
+	       * If there is no search-stack left, then
+	       * we should return to the fastmap/search loop.
+	       */
+	      
+	    test_do_return:
+	      
+	      if (!search_state.backtrack_stack)
+		{
+#ifdef RX_DEBUG_0
+		  if (rx_debug_trace)
+		    fprintf (stderr, "!!! %d bails returning %d !!!\n",
+			     search_state.line_no, search_state.test_ret);
+#endif
+		  
+		  /* No more search-stack -- this test is done. */
+		  if (search_state.test_ret)
+		    goto return_from_test_match;
+		  else
+		    goto error_in_testing_match;
+		}
+	      
+	      /* Returning from a recursive call to 
+	       * the test match block:
+	       */
+	      
+	      bf = ((struct rx_backtrack_frame *)
+		    search_state.backtrack_stack->sp);
+#ifdef RX_DEBUG_0
+	      if (rx_debug_trace)
+		fprintf (stderr, "+++ %d returns %d (to %d)+++\n",
+			 search_state.line_no,
+			 search_state.test_ret,
+			 bf->stk_search_state.line_no);
+#endif
+	      
+	      while (search_state.counter_stack
+		     && (!bf->counter_stack_sp
+			 || (bf->counter_stack_sp
+			     != search_state.counter_stack->sp)))
+		{
+		  POP(search_state.counter_stack,
+		      sizeof (struct rx_counter_frame));
+		}
+	      
+	      if (search_state.test_ret == rx_test_error)
+		{
+		  POP (search_state.backtrack_stack,
+		       search_state.backtrack_frame_bytes);
+		  goto test_do_return;
+		}
+	      
+	      /* If a non-longest match was found and that is good 
+	       * enough, return immediately.
+	       */
+	      if (   (search_state.test_ret == rx_test_found_first)
+		  && search_state.first_found)
+		{
+		  rx_unlock_superstate (&rxb->rx, bf->stk_super);
+		  POP (search_state.backtrack_stack,
+		       search_state.backtrack_frame_bytes);
+		  goto test_do_return;
+		}
+	      
+	      search_state.test_ret = bf->stk_test_ret;
+	      search_state.last_l = bf->stk_last_l;
+	      search_state.last_r = bf->stk_last_r;
+	      bf->df = bf->df->next_same_super_edge[0];
+	      search_state.super = bf->stk_super;
+	      search_state.c = bf->stk_c;
+#ifdef RX_DEBUG_0
+	      search_state.line_no = bf->stk_search_state.line_no;
+#endif
+	      
+	      if (rxb->match_regs_on_stack)
+		{
+		  int x;
+		  regoff_t * stk =
+		    (regoff_t *)((char *)bf + sizeof (*bf));
+		  for (x = 0; x <= search_state.last_l; ++x)
+		    search_state.lparen[x] = stk[x];
+		  stk += x;
+		  for (x = 0; x <= search_state.last_r; ++x)
+		    search_state.rparen[x] = stk[x];
+		}
+	      
+	      {
+		int x;
+	      try_burst_2:
+		x = get_burst (&bf->stk_test_pos, app_closure, stop);
+		switch (x)
+		  {
+		  case rx_get_burst_continuation:
+		    search_state.saved_bf = bf;
+		    test_pc = rx_test_backtrack_return;
+		    goto test_return_continuation;
+		  resume_continuation_3:
+		    bf = search_state.saved_bf;
+		    goto try_burst_2;
+		  case rx_get_burst_no_more:
+		    /* Since we've been here before, it is some kind of
+		     * error that we can't return.
+		     */
+		  case rx_get_burst_error:
+		    search_state.test_ret = rx_test_internal_error;
+		    goto test_do_return;
+		  case rx_get_burst_ok:
+		    break;
+		  }
+	      }
+	      search_state.test_pos = bf->stk_test_pos;
+	      goto while_non_det_options;
+	    }
+	    
+	    
+	  case rx_cache_miss:
+	    /* Because the superstate NFA is lazily constructed,
+	     * and in fact may erode from underneath us, we sometimes
+	     * have to construct the next instruction from the hard way.
+	     * This invokes one step in the lazy-conversion.
+	     */
+	    search_state.ifr = rx_handle_cache_miss (&rxb->rx,
+						     search_state.super,
+						     search_state.c,
+						     search_state.ifr->data_2);
+	    if (!search_state.ifr)
+	      {
+		search_state.test_ret = rx_test_internal_error;
+		goto test_do_return;
+	      }
+	    goto restart;
+	    
+	  case rx_backtrack:
+	    /* RX_BACKTRACK means that we've reached the empty
+	     * superstate, indicating that match can't succeed
+	     * from this point.
+	     */
+	    goto test_do_return;
+	    
+	  case rx_next_char:
+	  case rx_error_inx:
+	  case rx_num_instructions:
+	    search_state.ret_val = 0;
+	    goto test_do_return;
+	  }
+	goto pseudo_while_1;
+      }
+    
+    /* Healthy exits from the test-match loop do a 
+     * `goto return_from_test_match'   On the other hand, 
+     * we might end up here.
+     */
+  error_in_testing_match:
+    test_state = rx_test_error;
+    goto test_returns_to_search;
+    
+    /***** fastmap/search loop body
+     *	      considering the results testing for a match
+     */
+    
+  return_from_test_match:
+    
+    if (search_state.best_last_l >= 0)
+      {
+	if (regs && (regs->start != search_state.best_lparen))
+	  {
+	    bcopy (search_state.best_lparen, regs->start,
+		   regs->num_regs * sizeof (int));
+	    bcopy (search_state.best_rparen, regs->end,
+		   regs->num_regs * sizeof (int));
+	  }
+	if (regs && !rxb->no_sub)
+	  {
+	    int q;
+	    int bound = (regs->num_regs > search_state.num_regs
+			 ? regs->num_regs
+			 : search_state.num_regs);
+	    regoff_t * s = regs->start;
+	    regoff_t * e = regs->end;
+	    for (q = search_state.best_last_l + 1;  q < bound; ++q)
+	      s[q] = e[q] = -1;
+	  }
+	search_state.ret_val = search_state.best_lparen[0];
+	test_state = rx_test_ok;
+	goto test_returns_to_search;
+      }
+    else
+      {
+	test_state = rx_test_fail;
+	goto test_returns_to_search;
+      }
+    
+  test_return_continuation:
+    search_state.test_match_resume_pt = test_pc;
+    test_state = rx_test_continuation;
+    goto test_returns_to_search;
+  }
+}
+
+
+
+#endif /* RX_WANT_RX_DEFS */
+
+
+
+#else /* RX_WANT_SE_DEFS */
+  /* Integers are used to represent side effects.
+   *
+   * Simple side effects are given negative integer names by these enums.
+   * 
+   * Non-negative names are reserved for complex effects.
+   *
+   * Complex effects are those that take arguments.  For example, 
+   * a register assignment associated with a group is complex because
+   * it requires an argument to tell which group is being matched.
+   * 
+   * The integer name of a complex effect is an index into rxb->se_params.
+   */
+ 
+  RX_DEF_SE(1, re_se_try, = -1)		/* Epsilon from start state */
+
+  RX_DEF_SE(0, re_se_pushback, = re_se_try - 1)
+  RX_DEF_SE(0, re_se_push0, = re_se_pushback -1)
+  RX_DEF_SE(0, re_se_pushpos, = re_se_push0 - 1)
+  RX_DEF_SE(0, re_se_chkpos, = re_se_pushpos -1)
+  RX_DEF_SE(0, re_se_poppos, = re_se_chkpos - 1)
+
+  RX_DEF_SE(1, re_se_at_dot, = re_se_poppos - 1)	/* Emacs only */
+  RX_DEF_SE(0, re_se_syntax, = re_se_at_dot - 1) /* Emacs only */
+  RX_DEF_SE(0, re_se_not_syntax, = re_se_syntax - 1) /* Emacs only */
+
+  RX_DEF_SE(1, re_se_begbuf, = re_se_not_syntax - 1) /* match beginning of buffer */
+  RX_DEF_SE(1, re_se_hat, = re_se_begbuf - 1) /* match beginning of line */
+
+  RX_DEF_SE(1, re_se_wordbeg, = re_se_hat - 1) 
+  RX_DEF_SE(1, re_se_wordbound, = re_se_wordbeg - 1)
+  RX_DEF_SE(1, re_se_notwordbound, = re_se_wordbound - 1)
+
+  RX_DEF_SE(1, re_se_wordend, = re_se_notwordbound - 1)
+  RX_DEF_SE(1, re_se_endbuf, = re_se_wordend - 1)
+
+  /* This fails except at the end of a line. 
+   * It deserves to go here since it is typicly one of the last steps 
+   * in a match.
+   */
+  RX_DEF_SE(1, re_se_dollar, = re_se_endbuf - 1)
+
+  /* Simple effects: */
+  RX_DEF_SE(1, re_se_fail, = re_se_dollar - 1)
+
+  /* Complex effects.  These are used in the 'se' field of 
+   * a struct re_se_params.  Indexes into the se array
+   * are stored as instructions on nfa edges.
+   */
+  RX_DEF_CPLX_SE(1, re_se_win, = 0)
+  RX_DEF_CPLX_SE(1, re_se_lparen, = re_se_win + 1)
+  RX_DEF_CPLX_SE(1, re_se_rparen, = re_se_lparen + 1)
+  RX_DEF_CPLX_SE(0, re_se_backref, = re_se_rparen + 1)
+  RX_DEF_CPLX_SE(0, re_se_iter, = re_se_backref + 1) 
+  RX_DEF_CPLX_SE(0, re_se_end_iter, = re_se_iter + 1)
+  RX_DEF_CPLX_SE(0, re_se_tv, = re_se_end_iter + 1)
+
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.c	(revision 16561)
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * simplefrenchstem.c -- a simple french stemmer
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "simplefrenchstem.h"
+
+
+
+/* =========================================================================
+ * Function: simplefrenchstem
+ * Description: a simple french stemmer
+ * Input: a word string with the length in the first byte
+ * Output: the stemmed word
+ * ========================================================================= */
+
+void simplefrenchstem (unsigned char *word) {      
+  unsigned short out[256]; /* temp space */
+  unsigned short *wordstart; /* points to first letter of word */
+  int last;                  /* last points to the last character */
+
+  /* decode */
+  utf8_word_to_unicode (word, out, 255);
+  wordstart = out + 1;
+  last = out[0]-1;
+  
+
+  if (last > 4) {
+    if (wordstart[last]=='x') {
+      if (wordstart[last-1]=='u' && wordstart[last-2]=='a') {
+	wordstart[last-1]='l';
+      }
+      last--;
+
+    } else {
+      if (last>=0 && wordstart[last]=='s') last--;
+      if (last>=0 && wordstart[last]=='r') last--;
+      if (last>=0 && wordstart[last]=='e') last--;
+
+      /* letter with accent e + ' -- there are two possible encodings  */
+      if (last>=0 && wordstart[last]==0xe9) { 
+	last--;
+      } else if (last>=1 && wordstart[last-1]=='e' && wordstart[last]==0x301) {
+	last -= 2; 
+      }
+
+      if (last >= 1 && wordstart[last]==wordstart[last-1]) last--;
+    }  /* end else */
+
+    out[0] = (unsigned char)(last+1);
+  } /* end if (len > 4) */
+
+  /* re-code, make sure the result is not longer than the input */
+  unicode_to_utf8_word (out, word, word[0]+1);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/simplefrenchstem.h	(revision 16561)
@@ -0,0 +1,26 @@
+/**************************************************************************
+ *
+ * simplefrenchstem.h -- a simple french stemmer
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef SIMPLEFRENCHSTEM_H
+#define SIMPLEFRENCHSTEM_H
+
+void simplefrenchstem (unsigned char *word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/sptree.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/sptree.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/sptree.c	(revision 16561)
@@ -0,0 +1,374 @@
+/**************************************************************************
+ *
+ * sptree.c -- Splay tree code
+ * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#include	"sysfuncs.h"
+#include	"sptree.h"
+
+#define TRACE(w)
+
+#define POOL_SIZE 1024
+
+
+typedef struct node_rec node;
+
+struct node_rec
+  {
+    node *left, *rght;
+    node *par;
+    char *datsun;
+  };
+
+struct pool
+  {
+    int length, pos;
+    struct pool *next;
+    node nodes[POOL_SIZE];
+  };
+
+static node *SP_splay ();
+
+#define	CNULL	((char *) NULL)
+#define	NNULL	((node *) NULL)
+#define	SNULL	((Splay_Tree *) NULL)
+
+
+/*
+ *    createset():
+ *              allocates memory for and initialises a set.
+ *
+ *      Accesses outside variables:     none
+ *
+ *      Return value...:                none
+ */
+
+Splay_Tree *
+SP_createset (int (*cmp) (void *, void *))
+{
+  Splay_Tree *S;
+
+  if (!(S = (Splay_Tree *) malloc (sizeof (Splay_Tree))))
+    return (SNULL);
+  S->cmp = cmp;
+  S->root = CNULL;
+  S->no_of_items = 0;
+  S->mem_in_use = sizeof (Splay_Tree);
+  S->pool = NULL;
+  S->pool_chunks = 1024;
+  return S;
+}				/* createset() */
+
+
+
+static node *
+SP_get_node (Splay_Tree * S)
+{
+  struct pool *p = S->pool;
+  if (!p || p->pos == p->length)
+    {
+      p = malloc (sizeof (struct pool));
+      if (!p)
+	return NULL;
+      S->mem_in_use += sizeof (struct pool);
+      p->length = POOL_SIZE;
+      p->pos = 0;
+      p->next = S->pool;
+      S->pool = p;
+    }
+  return &(p->nodes[p->pos++]);
+}
+
+
+
+/*
+ *    SP_freeset():
+ *              free the memory allocated to a splay tree. Note this Does not
+ *              free the data in the tree you must do this yourself.
+ *
+ *      Accesses outside variables:     none
+ *
+ *      Return value...:                none
+ */
+
+void 
+SP_freeset (Splay_Tree * S)
+{
+  while (S->pool)
+    {
+      struct pool *p = S->pool;
+      S->pool = p->next;
+      free (p);
+    }
+  free (S);
+}
+
+
+/*
+ *    insert():
+ *              inserts the item given into the set given in the order
+ *              specified by the comparison function in the set 
+ *              definition.
+ *
+ *      Accesses outside variables:     none
+ *
+ *      Return value...:                none
+ */
+
+
+void *
+SP_insert (void *item, Splay_Tree * S)
+{
+  int dir = 0;
+  register node *curr, *prev;
+  node *tmp;
+
+  prev = NNULL;
+  curr = (node *) S->root;
+  for (; curr;)
+    {
+      prev = curr;
+      dir = S->cmp (item, curr->datsun);
+      if (dir < 0)
+	curr = curr->left;
+      else
+	curr = curr->rght;
+    }
+  if (!(tmp = SP_get_node (S)))
+    return NULL;
+  tmp->datsun = item;
+
+  if (prev == NNULL)
+    S->root = (char *) tmp;
+  else if (dir < 0)
+    prev->left = tmp;
+  else
+    prev->rght = tmp;
+
+  tmp->par = prev;
+  tmp->left = NNULL;
+  tmp->rght = NNULL;
+  S->no_of_items++;
+  S->root = (char *) SP_splay (tmp);
+  return (tmp->datsun);
+}				/* insert() */
+
+/*
+ *    get_first():
+ *              returns a pointer to the first item in the set. If the set 
+ *              is empty a NULL pointer is returned.
+ *
+ *      Accesses outside variables:     none
+ */
+
+void *
+SP_get_first (Splay_Tree * S)
+{
+  node *curr, *prev;
+
+  for (curr = (node *) S->root, prev = NNULL; curr;
+       prev = curr, curr = curr->left)
+    ;				/* NULLBODY */
+
+  if (prev)
+    S->root = (char *) SP_splay (prev);
+  return (prev ? prev->datsun : CNULL);
+}				/* get_first() */
+
+/*
+ *    get_next():
+ *              returns the in-order successor of the currently fingered
+ *              item of the set. If no item is fingered or if there is 
+ *              no in-order successor a NULL pointer is returned.
+ *
+ *      Accesses outside variables:     none
+ */
+
+void *
+SP_get_next (Splay_Tree * S)
+{
+  node *curr, *prev;
+
+  curr = (node *) S->root;
+  if (!curr || !curr->rght)
+    return (CNULL);
+
+  for (prev = curr, curr = curr->rght; curr;
+       prev = curr, curr = curr->left)
+    ;				/* NULLBODY */
+
+  if (prev)
+    S->root = (char *) SP_splay (prev);
+  return (prev ? prev->datsun : CNULL);
+}				/* get_next() */
+
+/*
+ *    deletemin():
+ *              removes the first element in the set and returns a
+ *              pointer to it. If the set is empty a NULL pointer 
+ *              is returned.
+ *
+ *      Accesses outside variables:     none
+ */
+
+void *
+SP_deletemin (Splay_Tree * S)
+{
+  node *tmp;
+  char *dat;
+
+  dat = SP_get_first (S);
+  if (dat)
+    {
+      tmp = (node *) S->root;
+      S->root = (char *) tmp->rght;
+      if (S->root)
+	((node *) S->root)->par = NULL;
+    }
+  S->no_of_items--;
+  return dat;
+}				/* deletemin() */
+
+
+/*
+ *    member():
+ *              searches the given set for an item which matches (has
+ *              the same key) and returns a pointer to a copy of that item.
+ *              If no matching item is found a NULL pointer is 
+ *              returned.
+ *
+ *      Accesses outside variables:     none
+ */
+
+void *
+SP_member (void *dat, Splay_Tree * S)
+{
+  register node *curr;
+  register int dir;
+  static int calls = 0;
+
+  TRACE ("in member");
+  for (curr = (node *) S->root; curr;)
+    {
+      TRACE ("call to cmp");
+      dir = S->cmp (dat, curr->datsun);
+      TRACE ("out of cmp");
+      if (dir < 0)
+	curr = curr->left;
+      else if (dir > 0)
+	curr = curr->rght;
+      else
+	{
+	  /* splay occasionaly to speed up execution */
+	  if ((calls++ % 4) == 0 && curr)
+	    S->root = (char *) SP_splay (curr);
+	  TRACE ("out of member");
+	  return (curr->datsun);
+	}
+    }
+  TRACE ("out of member");
+  return (CNULL);
+}				/* member() */
+
+#define	ROTATEL(p,q) 					\
+	do{						\
+		p->par = q->par;			\
+		q->par = p;				\
+		q->rght = p->left;			\
+		if(q->rght)				\
+			q->rght->par = q;		\
+		p->left = q;				\
+		if(p->par)				\
+			if(q == p->par->left)		\
+				p->par->left = p;	\
+			else				\
+				p->par->rght = p;	\
+	}while(0)
+
+#define	ROTATER(p,q) 					\
+	do{						\
+		p->par = q->par;			\
+		q->par = p;				\
+		q->left = p->rght;			\
+		if(q->left)				\
+			q->left->par = q;		\
+		p->rght = q;				\
+		if(p->par)				\
+			if(q == p->par->left)		\
+				p->par->left = p;	\
+			else				\
+				p->par->rght = p;	\
+	}while(0)
+
+
+/*
+ *    splay():
+ *              i
+ *
+ *      Accesses outside variables:     i
+ *
+ *      Return value...:                i
+ */
+
+static node *
+SP_splay (p)
+     register node *p;
+{
+  register node *q, *g;
+
+  q = p->par;
+  /* splay the tree about p */
+  while (q != NNULL)
+    {
+      g = q->par;
+      if (p == q->left)
+	{
+	  if (g == NNULL)
+	    ROTATER (p, q);
+	  else if (q == g->left)
+	    {
+	      ROTATER (q, g);
+	      ROTATER (p, q);
+	    }
+	  else
+	    {
+	      ROTATER (p, q);
+	      ROTATEL (p, g);
+	    }
+	}
+      else
+	{
+	  if (g == NNULL)
+	    ROTATEL (p, q);
+	  else if (q == g->rght)
+	    {
+	      ROTATEL (q, g);
+	      ROTATEL (p, q);
+	    }
+	  else
+	    {
+	      ROTATEL (p, q);
+	      ROTATER (p, g);
+	    }
+	}
+      q = p->par;
+    }
+  return (p);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/sptree.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/sptree.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/sptree.h	(revision 16561)
@@ -0,0 +1,45 @@
+/**************************************************************************
+ *
+ * sptree.h -- Splay tree code
+ * Copyright (C) 1994  Gary Eddy and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#ifndef SPTREE_H
+#define SPTREE_H
+
+typedef struct set_rec Splay_Tree;
+
+struct set_rec
+  {
+    int no_of_items;
+    unsigned long mem_in_use;
+    int (*cmp) (void *, void *);
+    void *root;
+    void *pool;
+    int pool_chunks;
+  };
+
+Splay_Tree *SP_createset (int (*cmp) (void *, void *));
+void *SP_insert (void *item, Splay_Tree * S);
+void *SP_get_first (Splay_Tree * S);
+void *SP_get_next (Splay_Tree * S);
+void *SP_deletemin (Splay_Tree * S);
+void *SP_member (void *dat, Splay_Tree * S);
+void SP_freeset (Splay_Tree * S);
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/strcasecmp.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/strcasecmp.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/strcasecmp.c	(revision 16561)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c	5.5 (Berkeley) 11/24/87";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static unsigned char charmap[] = {
+	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(s1, s2)
+    char *s1, *s2;
+{
+    register unsigned char u1, u2;
+
+    for (;;) {
+	u1 = (unsigned char) *s1++;
+	u2 = (unsigned char) *s2++;
+	if (charmap[u1] != charmap[u2]) {
+	    return charmap[u1] - charmap[u2];
+	}
+	if (u1 == '\0') {
+	    return 0;
+	}
+    }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/lib/strstr.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/strstr.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/strstr.c	(revision 16561)
@@ -0,0 +1,44 @@
+/* strstr.c -- return the offset of one string within another
+   Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* Written by Mike Rendell <michael@cs.mun.ca>.  */
+
+/* Return the starting address of string S2 in S1;
+   return 0 if it is not found. */
+
+char *
+strstr (s1, s2)
+     char *s1;
+     char *s2;
+{
+  int i;
+  char *p1;
+  char *p2;
+  char *s = s1;
+
+  for (p2 = s2, i = 0; *s; p2 = s2, i++, s++)
+    {
+      for (p1 = s; *p1 && *p2 && *p1 == *p2; p1++, p2++)
+	;
+      if (!*p2)
+	break;
+    }
+  if (!*p2)
+    return s1 + i;
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/lib/timing.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/timing.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/timing.c	(revision 16561)
@@ -0,0 +1,217 @@
+/**************************************************************************
+ *
+ * timing.c -- Program timing routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "timing.h"
+
+
+/*
+ * Return the time in seconds since 00:00:00 GMT, Jan. 1, 1970 to the
+ * best precision possible
+ *
+ */
+double 
+RealTime (void)
+{
+  return ((double) time (NULL));
+}
+
+
+
+/*
+ * Return the amount of system and user CPU used by the process to date to 
+ * the best precision possible. If user is non-null then it is initialised to 
+ * the user time. If sys is non-null then it is initialised to the system time.
+ *
+ */
+double 
+CPUTime (double *user, double *sys)
+{
+
+#if defined(HAVE_TIMES)
+
+  struct tms buffer;
+  static double clk_tck = 0;
+  double u, s;
+
+  times (&buffer);
+
+  if (clk_tck == 0)
+    clk_tck = CLK_TCK;
+
+  u = (double) buffer.tms_utime / clk_tck;
+  s = (double) buffer.tms_stime / clk_tck;
+  if (user)
+    *user = u;
+  if (sys)
+    *sys = s;
+  return u + s;
+
+#elif defined(HAVE_GETRUSAGE)
+
+  struct rusage ruse;
+  getrusage (RUSAGE_SELF, &ruse);
+  if (user)
+    *user = (double) ruse.ru_utime.tv_sec +
+      (double) ruse.ru_utime.tv_usec / 1000000;
+  if (sys)
+    *sys = (double) ruse.ru_stime.tv_sec +
+      (double) ruse.ru_stime.tv_usec / 1000000;
+  return ((double) ruse.ru_utime.tv_sec + ruse.ru_stime.tv_sec +
+	((double) ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec) / 1000000);
+
+#else
+
+  /* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  if (user) *user = 0.0;
+  if (sys) *sys = 0.0;
+  return 0.0;
+#else
+  -->Can NOT find a system time routine to use ! !!
+#endif  /* __WIN32__ */
+
+#endif
+
+}
+
+
+
+
+/*
+ * Get the Real and CPU time and store them in a ProgTime structure
+ */
+void 
+GetTime (ProgTime * StartTime)
+{
+  StartTime->RealTime = RealTime ();
+  StartTime->CPUTime = CPUTime (NULL, NULL);
+}
+
+
+
+
+/*
+ * Display the Real and CPU time elapsed since the StartTime anf FinishTime 
+ * structures were initialised. If FinishTime is NULL then FinishTime is
+ * Now.
+ */
+char *
+ElapsedTime (ProgTime * StartTime,
+	     ProgTime * FinishTime)
+{
+  static char buf[50];
+  double Real, CPU, hour, min, sec;
+  if (!FinishTime)
+    {
+      Real = RealTime () - StartTime->RealTime;
+      CPU = CPUTime (NULL, NULL) - StartTime->CPUTime;
+    }
+  else
+    {
+      Real = FinishTime->RealTime - StartTime->RealTime;
+      CPU = FinishTime->CPUTime - StartTime->CPUTime;
+    }
+  hour = floor (CPU / 3600);
+  min = floor ((CPU - hour * 3600) / 60);
+  sec = CPU - hour * 3600 - min * 60;
+  sprintf (buf, "%02.0f:%02.0f:%05.2f cpu, %02d:%02d:%02d elapsed.",
+	   hour, min, sec,
+	   ((int) ceil (Real)) / 3600,
+	   (((int) ceil (Real)) % 3600) / 60, ((int) ceil (Real)) % 60);
+  return (buf);
+}
+
+
+#define MILLION 1000000
+
+/* ===============================================================================
+ * Function: cputime_string
+ * Description:
+ *      Prints out given cpu time into a string buffer.
+ * Input: cpu-time in clock_t or timeval format
+ * Output: pointer to internal buffer 
+ * =============================================================================== */
+
+#ifdef HAVE_TIMES
+char *
+cputime_string (clock_t clk)
+{
+  static char buf[15];
+  double CPU, hour, min, sec;
+
+  CPU = (double) clk / (double) CLK_TCK;
+  hour = floor (CPU / 3600);
+  min = floor ((CPU - hour * 3600) / 60);
+  sec = CPU - hour * 3600 - min * 60;
+  sprintf (buf, "%02.0f:%02.0f:%05.2f", hour, min, sec);
+  return (buf);
+}
+#else
+char *
+cputime_string (struct timeval *t)
+{
+  static char buf[15];
+  double CPU, hour, min, sec;
+
+  CPU = (double) t->tv_sec + (double) t->tv_usec / MILLION;
+  hour = floor (CPU / 3600);
+  min = floor ((CPU - hour * 3600) / 60);
+  sec = CPU - hour * 3600 - min * 60;
+  sprintf (buf, "%02.0f:%02.0f:%05.2f", hour, min, sec);
+  return (buf);
+}
+#endif
+
+
+
+/* ===============================================================================
+ * Function: time_normalise
+ * Description:
+ *      If the micro-second component is negative or over a million,
+ *      then must take the over/under flow and add it to the second component.
+ * Usage:
+ *      Use this routine when you have just been doing straight addition and
+ *      subtraction on the micro-second component. For this could lead to a
+ *      negative number or over a million.
+ *
+ * =============================================================================== */
+
+#ifndef HAVE_TIMES
+
+#ifndef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+static 
+#endif
+void
+time_normalise (struct timeval *t)
+{
+  while (t->tv_usec < 0)
+    {
+      t->tv_usec += MILLION;
+      t->tv_sec--;
+    }
+  while (t->tv_usec > MILLION)
+    {
+      t->tv_usec -= MILLION;
+      t->tv_sec++;
+    }
+}
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/timing.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/timing.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/timing.h	(revision 16561)
@@ -0,0 +1,73 @@
+/**************************************************************************
+ *
+ * timing.h -- Program timing routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_TIMING
+#define H_TIMING
+
+
+#ifdef HAVE_TIMES
+# if TIME_WITH_SYS_TIME
+#  include <sys/times.h>
+#  include <time.h>
+#  ifndef CLK_TCK
+#   define CLK_TCK sysconf(_SC_CLK_TCK)
+#  endif
+# else
+#  if HAVE_SYS_TIME_H
+#   include <sys/time.h>
+#  else
+#   include <time.h>
+#  endif
+# endif
+#else
+/* [RPAP - Feb 97: WIN32 Port] */
+# ifdef __WIN32__
+   struct timeval { long tv_sec, tv_usec; };
+# else
+#  include <sys/time.h>
+#  include <sys/resource.h>
+# endif
+#endif
+
+
+typedef struct
+  {
+    double RealTime, CPUTime;
+  }
+ProgTime;
+
+double RealTime (void);
+
+double CPUTime (double *user, double *sys);
+
+void GetTime (ProgTime * StartTime);
+
+/* FinishTime may be NULL */
+char *ElapsedTime (ProgTime * StartTime, ProgTime * FinishTime);
+
+#ifdef HAVE_TIMES
+char *cputime_string (clock_t clk);
+#else
+void time_normalise (struct timeval *t);
+char *cputime_string (struct timeval *t);
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/unitool.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/unitool.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/unitool.c	(revision 16561)
@@ -0,0 +1,1758 @@
+#include "unitool.h"
+#include "sysfuncs.h"
+
+
+/* unicode_cat_data is a combination of the letter, digit, and
+ * space information in a form that is quick to look up.
+ * The information about each byte is stored in two bits
+ *
+ * 00 - neither letter, digit or space
+ * 01 - letter
+ * 10 - space
+ * 11 - digit
+ *
+ * You can test for letter | space by looking at the right
+ * column
+ */
+unsigned long *unicode_cat_data=NULL;
+
+
+typedef struct unidecompose {
+  unsigned short ch;
+  unsigned short str[3];
+} unidecompose;
+
+typedef struct unirelate {
+  unsigned short ch1;
+  unsigned short ch2;
+} unirelate;
+
+typedef struct unirange {
+  unsigned short first;
+  unsigned short last;
+} unirange;
+
+
+
+/* This decomposition data only includes canonical decompositional
+ * characters.  Any unused bytes are set to 0x0. */
+
+#define NUM_DECOMPOSE_INFO 1269
+static const unidecompose decompose_info[NUM_DECOMPOSE_INFO] = {
+  {0xC0,0x41,0x300,0x0}, {0xC1,0x41,0x301,0x0}, {0xC2,0x41,0x302,0x0}, 
+  {0xC3,0x41,0x303,0x0}, {0xC4,0x41,0x308,0x0}, {0xC5,0x41,0x30A,0x0}, 
+  {0xC7,0x43,0x327,0x0}, {0xC8,0x45,0x300,0x0}, {0xC9,0x45,0x301,0x0}, 
+  {0xCA,0x45,0x302,0x0}, {0xCB,0x45,0x308,0x0}, {0xCC,0x49,0x300,0x0}, 
+  {0xCD,0x49,0x301,0x0}, {0xCE,0x49,0x302,0x0}, {0xCF,0x49,0x308,0x0}, 
+  {0xD1,0x4E,0x303,0x0}, {0xD2,0x4F,0x300,0x0}, {0xD3,0x4F,0x301,0x0}, 
+  {0xD4,0x4F,0x302,0x0}, {0xD5,0x4F,0x303,0x0}, {0xD6,0x4F,0x308,0x0}, 
+  {0xD9,0x55,0x300,0x0}, {0xDA,0x55,0x301,0x0}, {0xDB,0x55,0x302,0x0}, 
+  {0xDC,0x55,0x308,0x0}, {0xDD,0x59,0x301,0x0}, {0xE0,0x61,0x300,0x0}, 
+  {0xE1,0x61,0x301,0x0}, {0xE2,0x61,0x302,0x0}, {0xE3,0x61,0x303,0x0}, 
+  {0xE4,0x61,0x308,0x0}, {0xE5,0x61,0x30A,0x0}, {0xE7,0x63,0x327,0x0}, 
+  {0xE8,0x65,0x300,0x0}, {0xE9,0x65,0x301,0x0}, {0xEA,0x65,0x302,0x0}, 
+  {0xEB,0x65,0x308,0x0}, {0xEC,0x69,0x300,0x0}, {0xED,0x69,0x301,0x0}, 
+  {0xEE,0x69,0x302,0x0}, {0xEF,0x69,0x308,0x0}, {0xF1,0x6E,0x303,0x0}, 
+  {0xF2,0x6F,0x300,0x0}, {0xF3,0x6F,0x301,0x0}, {0xF4,0x6F,0x302,0x0}, 
+  {0xF5,0x6F,0x303,0x0}, {0xF6,0x6F,0x308,0x0}, {0xF9,0x75,0x300,0x0}, 
+  {0xFA,0x75,0x301,0x0}, {0xFB,0x75,0x302,0x0}, {0xFC,0x75,0x308,0x0}, 
+  {0xFD,0x79,0x301,0x0}, {0xFF,0x79,0x308,0x0}, {0x100,0x41,0x304,0x0}, 
+  {0x101,0x61,0x304,0x0}, {0x102,0x41,0x306,0x0}, {0x103,0x61,0x306,0x0}, 
+  {0x104,0x41,0x328,0x0}, {0x105,0x61,0x328,0x0}, {0x106,0x43,0x301,0x0}, 
+  {0x107,0x63,0x301,0x0}, {0x108,0x43,0x302,0x0}, {0x109,0x63,0x302,0x0}, 
+  {0x10A,0x43,0x307,0x0}, {0x10B,0x63,0x307,0x0}, {0x10C,0x43,0x30C,0x0}, 
+  {0x10D,0x63,0x30C,0x0}, {0x10E,0x44,0x30C,0x0}, {0x10F,0x64,0x30C,0x0}, 
+  {0x112,0x45,0x304,0x0}, {0x113,0x65,0x304,0x0}, {0x114,0x45,0x306,0x0}, 
+  {0x115,0x65,0x306,0x0}, {0x116,0x45,0x307,0x0}, {0x117,0x65,0x307,0x0}, 
+  {0x118,0x45,0x328,0x0}, {0x119,0x65,0x328,0x0}, {0x11A,0x45,0x30C,0x0}, 
+  {0x11B,0x65,0x30C,0x0}, {0x11C,0x47,0x302,0x0}, {0x11D,0x67,0x302,0x0}, 
+  {0x11E,0x47,0x306,0x0}, {0x11F,0x67,0x306,0x0}, {0x120,0x47,0x307,0x0}, 
+  {0x121,0x67,0x307,0x0}, {0x122,0x47,0x327,0x0}, {0x123,0x67,0x327,0x0}, 
+  {0x124,0x48,0x302,0x0}, {0x125,0x68,0x302,0x0}, {0x128,0x49,0x303,0x0}, 
+  {0x129,0x69,0x303,0x0}, {0x12A,0x49,0x304,0x0}, {0x12B,0x69,0x304,0x0}, 
+  {0x12C,0x49,0x306,0x0}, {0x12D,0x69,0x306,0x0}, {0x12E,0x49,0x328,0x0}, 
+  {0x12F,0x69,0x328,0x0}, {0x130,0x49,0x307,0x0}, {0x134,0x4A,0x302,0x0}, 
+  {0x135,0x6A,0x302,0x0}, {0x136,0x4B,0x327,0x0}, {0x137,0x6B,0x327,0x0}, 
+  {0x139,0x4C,0x301,0x0}, {0x13A,0x6C,0x301,0x0}, {0x13B,0x4C,0x327,0x0}, 
+  {0x13C,0x6C,0x327,0x0}, {0x13D,0x4C,0x30C,0x0}, {0x13E,0x6C,0x30C,0x0}, 
+  {0x143,0x4E,0x301,0x0}, {0x144,0x6E,0x301,0x0}, {0x145,0x4E,0x327,0x0}, 
+  {0x146,0x6E,0x327,0x0}, {0x147,0x4E,0x30C,0x0}, {0x148,0x6E,0x30C,0x0}, 
+  {0x14C,0x4F,0x304,0x0}, {0x14D,0x6F,0x304,0x0}, {0x14E,0x4F,0x306,0x0}, 
+  {0x14F,0x6F,0x306,0x0}, {0x150,0x4F,0x30B,0x0}, {0x151,0x6F,0x30B,0x0}, 
+  {0x154,0x52,0x301,0x0}, {0x155,0x72,0x301,0x0}, {0x156,0x52,0x327,0x0}, 
+  {0x157,0x72,0x327,0x0}, {0x158,0x52,0x30C,0x0}, {0x159,0x72,0x30C,0x0}, 
+  {0x15A,0x53,0x301,0x0}, {0x15B,0x73,0x301,0x0}, {0x15C,0x53,0x302,0x0}, 
+  {0x15D,0x73,0x302,0x0}, {0x15E,0x53,0x327,0x0}, {0x15F,0x73,0x327,0x0}, 
+  {0x160,0x53,0x30C,0x0}, {0x161,0x73,0x30C,0x0}, {0x162,0x54,0x327,0x0}, 
+  {0x163,0x74,0x327,0x0}, {0x164,0x54,0x30C,0x0}, {0x165,0x74,0x30C,0x0}, 
+  {0x168,0x55,0x303,0x0}, {0x169,0x75,0x303,0x0}, {0x16A,0x55,0x304,0x0}, 
+  {0x16B,0x75,0x304,0x0}, {0x16C,0x55,0x306,0x0}, {0x16D,0x75,0x306,0x0}, 
+  {0x16E,0x55,0x30A,0x0}, {0x16F,0x75,0x30A,0x0}, {0x170,0x55,0x30B,0x0}, 
+  {0x171,0x75,0x30B,0x0}, {0x172,0x55,0x328,0x0}, {0x173,0x75,0x328,0x0}, 
+  {0x174,0x57,0x302,0x0}, {0x175,0x77,0x302,0x0}, {0x176,0x59,0x302,0x0}, 
+  {0x177,0x79,0x302,0x0}, {0x178,0x59,0x308,0x0}, {0x179,0x5A,0x301,0x0}, 
+  {0x17A,0x7A,0x301,0x0}, {0x17B,0x5A,0x307,0x0}, {0x17C,0x7A,0x307,0x0}, 
+  {0x17D,0x5A,0x30C,0x0}, {0x17E,0x7A,0x30C,0x0}, {0x1A0,0x4F,0x31B,0x0}, 
+  {0x1A1,0x6F,0x31B,0x0}, {0x1AF,0x55,0x31B,0x0}, {0x1B0,0x75,0x31B,0x0}, 
+  {0x1CD,0x41,0x30C,0x0}, {0x1CE,0x61,0x30C,0x0}, {0x1CF,0x49,0x30C,0x0}, 
+  {0x1D0,0x69,0x30C,0x0}, {0x1D1,0x4F,0x30C,0x0}, {0x1D2,0x6F,0x30C,0x0}, 
+  {0x1D3,0x55,0x30C,0x0}, {0x1D4,0x75,0x30C,0x0}, {0x1D5,0xDC,0x304,0x0}, 
+  {0x1D6,0xFC,0x304,0x0}, {0x1D7,0xDC,0x301,0x0}, {0x1D8,0xFC,0x301,0x0}, 
+  {0x1D9,0xDC,0x30C,0x0}, {0x1DA,0xFC,0x30C,0x0}, {0x1DB,0xDC,0x300,0x0}, 
+  {0x1DC,0xFC,0x300,0x0}, {0x1DE,0xC4,0x304,0x0}, {0x1DF,0xE4,0x304,0x0}, 
+  {0x1E0,0x41,0x307,0x304}, {0x1E1,0x61,0x307,0x304}, {0x1E2,0xC6,0x304,0x0}, 
+  {0x1E3,0xE6,0x304,0x0}, {0x1E6,0x47,0x30C,0x0}, {0x1E7,0x67,0x30C,0x0}, 
+  {0x1E8,0x4B,0x30C,0x0}, {0x1E9,0x6B,0x30C,0x0}, {0x1EA,0x4F,0x328,0x0}, 
+  {0x1EB,0x6F,0x328,0x0}, {0x1EC,0x1EA,0x304,0x0}, {0x1ED,0x1EB,0x304,0x0}, 
+  {0x1EE,0x1B7,0x30C,0x0}, {0x1EF,0x292,0x30C,0x0}, {0x1F0,0x6A,0x30C,0x0}, 
+  {0x1F4,0x47,0x301,0x0}, {0x1F5,0x67,0x301,0x0}, {0x1FA,0xC5,0x301,0x0}, 
+  {0x1FB,0xE5,0x301,0x0}, {0x1FC,0xC6,0x301,0x0}, {0x1FD,0xE6,0x301,0x0}, 
+  {0x1FE,0xD8,0x301,0x0}, {0x1FF,0xF8,0x301,0x0}, {0x200,0x41,0x30F,0x0}, 
+  {0x201,0x61,0x30F,0x0}, {0x202,0x41,0x311,0x0}, {0x203,0x61,0x311,0x0}, 
+  {0x204,0x45,0x30F,0x0}, {0x205,0x65,0x30F,0x0}, {0x206,0x45,0x311,0x0}, 
+  {0x207,0x65,0x311,0x0}, {0x208,0x49,0x30F,0x0}, {0x209,0x69,0x30F,0x0}, 
+  {0x20A,0x49,0x311,0x0}, {0x20B,0x69,0x311,0x0}, {0x20C,0x4F,0x30F,0x0}, 
+  {0x20D,0x6F,0x30F,0x0}, {0x20E,0x4F,0x311,0x0}, {0x20F,0x6F,0x311,0x0}, 
+  {0x210,0x52,0x30F,0x0}, {0x211,0x72,0x30F,0x0}, {0x212,0x52,0x311,0x0}, 
+  {0x213,0x72,0x311,0x0}, {0x214,0x55,0x30F,0x0}, {0x215,0x75,0x30F,0x0}, 
+  {0x216,0x55,0x311,0x0}, {0x217,0x75,0x311,0x0}, {0x310,0x306,0x307,0x0}, 
+  {0x340,0x300,0x0,0x0}, {0x341,0x301,0x0,0x0}, {0x343,0x313,0x0,0x0}, 
+  {0x344,0x308,0x30D,0x0}, {0x374,0x2B9,0x0,0x0}, {0x37E,0x3B,0x0,0x0}, 
+  {0x385,0xA8,0x30D,0x0}, {0x386,0x391,0x30D,0x0}, {0x387,0xB7,0x0,0x0}, 
+  {0x388,0x395,0x30D,0x0}, {0x389,0x397,0x30D,0x0}, {0x38A,0x399,0x30D,0x0}, 
+  {0x38C,0x39F,0x30D,0x0}, {0x38E,0x3A5,0x30D,0x0}, {0x38F,0x3A9,0x30D,0x0}, 
+  {0x390,0x3B9,0x344,0x0}, {0x3AA,0x399,0x308,0x0}, {0x3AB,0x3A5,0x308,0x0}, 
+  {0x3AC,0x3B1,0x30D,0x0}, {0x3AD,0x3B5,0x30D,0x0}, {0x3AE,0x3B7,0x30D,0x0}, 
+  {0x3AF,0x3B9,0x30D,0x0}, {0x3B0,0x3C5,0x344,0x0}, {0x3CA,0x3B9,0x308,0x0}, 
+  {0x3CB,0x3C5,0x308,0x0}, {0x3CC,0x3BF,0x30D,0x0}, {0x3CD,0x3C5,0x30D,0x0}, 
+  {0x3CE,0x3C9,0x30D,0x0}, {0x3D3,0x3D2,0x30D,0x0}, {0x3D4,0x3D2,0x308,0x0}, 
+  {0x401,0x415,0x308,0x0}, {0x403,0x413,0x301,0x0}, {0x407,0x406,0x308,0x0}, 
+  {0x40C,0x41A,0x301,0x0}, {0x40E,0x423,0x306,0x0}, {0x419,0x418,0x306,0x0}, 
+  {0x439,0x438,0x306,0x0}, {0x451,0x435,0x308,0x0}, {0x453,0x433,0x301,0x0}, 
+  {0x457,0x456,0x308,0x0}, {0x45C,0x43A,0x301,0x0}, {0x45E,0x443,0x306,0x0}, 
+  {0x476,0x474,0x30F,0x0}, {0x477,0x475,0x30F,0x0}, {0x4C1,0x416,0x306,0x0}, 
+  {0x4C2,0x436,0x306,0x0}, {0x4D0,0x410,0x306,0x0}, {0x4D1,0x430,0x306,0x0}, 
+  {0x4D2,0x410,0x308,0x0}, {0x4D3,0x430,0x308,0x0}, {0x4D6,0x415,0x306,0x0}, 
+  {0x4D7,0x435,0x306,0x0}, {0x4DA,0x4D8,0x308,0x0}, {0x4DB,0x4D9,0x308,0x0}, 
+  {0x4DC,0x416,0x308,0x0}, {0x4DD,0x436,0x308,0x0}, {0x4DE,0x417,0x308,0x0}, 
+  {0x4DF,0x437,0x308,0x0}, {0x4E2,0x418,0x304,0x0}, {0x4E3,0x438,0x304,0x0}, 
+  {0x4E4,0x418,0x308,0x0}, {0x4E5,0x438,0x308,0x0}, {0x4E6,0x41E,0x308,0x0}, 
+  {0x4E7,0x43E,0x308,0x0}, {0x4EA,0x4E8,0x308,0x0}, {0x4EB,0x4E9,0x308,0x0}, 
+  {0x4EE,0x423,0x304,0x0}, {0x4EF,0x443,0x304,0x0}, {0x4F0,0x423,0x308,0x0}, 
+  {0x4F1,0x443,0x308,0x0}, {0x4F2,0x423,0x30B,0x0}, {0x4F3,0x443,0x30B,0x0}, 
+  {0x4F4,0x427,0x308,0x0}, {0x4F5,0x447,0x308,0x0}, {0x4F8,0x42B,0x308,0x0}, 
+  {0x4F9,0x44B,0x308,0x0}, {0x929,0x928,0x93C,0x0}, {0x931,0x930,0x93C,0x0}, 
+  {0x934,0x933,0x93C,0x0}, {0x958,0x915,0x93C,0x0}, {0x959,0x916,0x93C,0x0}, 
+  {0x95A,0x917,0x93C,0x0}, {0x95B,0x91C,0x93C,0x0}, {0x95C,0x921,0x93C,0x0}, 
+  {0x95D,0x922,0x93C,0x0}, {0x95E,0x92B,0x93C,0x0}, {0x95F,0x92F,0x93C,0x0}, 
+  {0x9B0,0x9AC,0x9BC,0x0}, {0x9CB,0x9C7,0x9BE,0x0}, {0x9CC,0x9C7,0x9D7,0x0}, 
+  {0x9DC,0x9A1,0x9BC,0x0}, {0x9DD,0x9A2,0x9BC,0x0}, {0x9DF,0x9AF,0x9BC,0x0}, 
+  {0xA59,0xA16,0xA3C,0x0}, {0xA5A,0xA17,0xA3C,0x0}, {0xA5B,0xA1C,0xA3C,0x0}, 
+  {0xA5C,0xA21,0xA3C,0x0}, {0xA5E,0xA2B,0xA3C,0x0}, {0xB48,0xB47,0xB56,0x0}, 
+  {0xB4B,0xB47,0xB3E,0x0}, {0xB4C,0xB47,0xB57,0x0}, {0xB5C,0xB21,0xB3C,0x0}, 
+  {0xB5D,0xB22,0xB3C,0x0}, {0xB5F,0xB2F,0xB3C,0x0}, {0xB94,0xB92,0xBD7,0x0}, 
+  {0xBCA,0xBC6,0xBBE,0x0}, {0xBCB,0xBC7,0xBBE,0x0}, {0xBCC,0xBC6,0xBD7,0x0}, 
+  {0xC48,0xC46,0xC56,0x0}, {0xCC0,0xCBF,0xCD5,0x0}, {0xCC7,0xCC6,0xCD5,0x0}, 
+  {0xCC8,0xCC6,0xCD6,0x0}, {0xCCA,0xCC6,0xCC2,0x0}, {0xCCB,0xCC6,0xCC2,0xCD5}, 
+  {0xD4A,0xD46,0xD3E,0x0}, {0xD4B,0xD47,0xD3E,0x0}, {0xD4C,0xD46,0xD57,0x0}, 
+  {0xE33,0xE4D,0xE32,0x0}, {0xEB3,0xECD,0xEB2,0x0}, {0xF43,0xF42,0xFB7,0x0}, 
+  {0xF4D,0xF4C,0xFB7,0x0}, {0xF52,0xF51,0xFB7,0x0}, {0xF57,0xF56,0xFB7,0x0}, 
+  {0xF5C,0xF5B,0xFB7,0x0}, {0xF69,0xF40,0xFB5,0x0}, {0xF73,0xF72,0xF71,0x0}, 
+  {0xF75,0xF71,0xF74,0x0}, {0xF76,0xFB2,0xF80,0x0}, {0xF77,0xF76,0xF71,0x0}, 
+  {0xF78,0xFB3,0xF80,0x0}, {0xF79,0xF78,0xF71,0x0}, {0xF81,0xF80,0xF71,0x0}, 
+  {0xF93,0xF92,0xFB7,0x0}, {0xF9D,0xF9C,0xFB7,0x0}, {0xFA2,0xFA1,0xFB7,0x0}, 
+  {0xFA7,0xFA6,0xFB7,0x0}, {0xFAC,0xFAB,0xFB7,0x0}, {0xFB9,0xF90,0xFB5,0x0}, 
+  {0x1E00,0x41,0x325,0x0}, {0x1E01,0x61,0x325,0x0}, {0x1E02,0x42,0x307,0x0}, 
+  {0x1E03,0x62,0x307,0x0}, {0x1E04,0x42,0x323,0x0}, {0x1E05,0x62,0x323,0x0}, 
+  {0x1E06,0x42,0x331,0x0}, {0x1E07,0x62,0x331,0x0}, {0x1E08,0xC7,0x301,0x0}, 
+  {0x1E09,0xE7,0x301,0x0}, {0x1E0A,0x44,0x307,0x0}, {0x1E0B,0x64,0x307,0x0}, 
+  {0x1E0C,0x44,0x323,0x0}, {0x1E0D,0x64,0x323,0x0}, {0x1E0E,0x44,0x331,0x0}, 
+  {0x1E0F,0x64,0x331,0x0}, {0x1E10,0x44,0x327,0x0}, {0x1E11,0x64,0x327,0x0}, 
+  {0x1E12,0x44,0x32D,0x0}, {0x1E13,0x64,0x32D,0x0}, {0x1E14,0x112,0x300,0x0}, 
+  {0x1E15,0x113,0x300,0x0}, {0x1E16,0x112,0x301,0x0}, {0x1E17,0x113,0x301,0x0}, 
+  {0x1E18,0x45,0x32D,0x0}, {0x1E19,0x65,0x32D,0x0}, {0x1E1A,0x45,0x330,0x0}, 
+  {0x1E1B,0x65,0x330,0x0}, {0x1E1C,0x114,0x327,0x0}, {0x1E1D,0x115,0x327,0x0}, 
+  {0x1E1E,0x46,0x307,0x0}, {0x1E1F,0x66,0x307,0x0}, {0x1E20,0x47,0x304,0x0}, 
+  {0x1E21,0x67,0x304,0x0}, {0x1E22,0x48,0x307,0x0}, {0x1E23,0x68,0x307,0x0}, 
+  {0x1E24,0x48,0x323,0x0}, {0x1E25,0x68,0x323,0x0}, {0x1E26,0x48,0x308,0x0}, 
+  {0x1E27,0x68,0x308,0x0}, {0x1E28,0x48,0x327,0x0}, {0x1E29,0x68,0x327,0x0}, 
+  {0x1E2A,0x48,0x32E,0x0}, {0x1E2B,0x68,0x32E,0x0}, {0x1E2C,0x49,0x330,0x0}, 
+  {0x1E2D,0x69,0x330,0x0}, {0x1E2E,0xCF,0x301,0x0}, {0x1E2F,0xEF,0x301,0x0}, 
+  {0x1E30,0x4B,0x301,0x0}, {0x1E31,0x6B,0x301,0x0}, {0x1E32,0x4B,0x323,0x0}, 
+  {0x1E33,0x6B,0x323,0x0}, {0x1E34,0x4B,0x331,0x0}, {0x1E35,0x6B,0x331,0x0}, 
+  {0x1E36,0x4C,0x323,0x0}, {0x1E37,0x6C,0x323,0x0}, {0x1E38,0x1E36,0x304,0x0}, 
+  {0x1E39,0x1E37,0x304,0x0}, {0x1E3A,0x4C,0x331,0x0}, {0x1E3B,0x6C,0x331,0x0}, 
+  {0x1E3C,0x4C,0x32D,0x0}, {0x1E3D,0x6C,0x32D,0x0}, {0x1E3E,0x4D,0x301,0x0}, 
+  {0x1E3F,0x6D,0x301,0x0}, {0x1E40,0x4D,0x307,0x0}, {0x1E41,0x6D,0x307,0x0}, 
+  {0x1E42,0x4D,0x323,0x0}, {0x1E43,0x6D,0x323,0x0}, {0x1E44,0x4E,0x307,0x0}, 
+  {0x1E45,0x6E,0x307,0x0}, {0x1E46,0x4E,0x323,0x0}, {0x1E47,0x6E,0x323,0x0}, 
+  {0x1E48,0x4E,0x331,0x0}, {0x1E49,0x6E,0x331,0x0}, {0x1E4A,0x4E,0x32D,0x0}, 
+  {0x1E4B,0x6E,0x32D,0x0}, {0x1E4C,0xD5,0x301,0x0}, {0x1E4D,0xF5,0x301,0x0}, 
+  {0x1E4E,0xD5,0x308,0x0}, {0x1E4F,0xF5,0x308,0x0}, {0x1E50,0x14C,0x300,0x0}, 
+  {0x1E51,0x14D,0x300,0x0}, {0x1E52,0x14C,0x301,0x0}, {0x1E53,0x14D,0x301,0x0}, 
+  {0x1E54,0x50,0x301,0x0}, {0x1E55,0x70,0x301,0x0}, {0x1E56,0x50,0x307,0x0}, 
+  {0x1E57,0x70,0x307,0x0}, {0x1E58,0x52,0x307,0x0}, {0x1E59,0x72,0x307,0x0}, 
+  {0x1E5A,0x52,0x323,0x0}, {0x1E5B,0x72,0x323,0x0}, {0x1E5C,0x1E5A,0x304,0x0}, 
+  {0x1E5D,0x1E5B,0x304,0x0}, {0x1E5E,0x52,0x331,0x0}, {0x1E5F,0x72,0x331,0x0}, 
+  {0x1E60,0x53,0x307,0x0}, {0x1E61,0x73,0x307,0x0}, {0x1E62,0x53,0x323,0x0}, 
+  {0x1E63,0x73,0x323,0x0}, {0x1E64,0x15A,0x307,0x0}, {0x1E65,0x15B,0x307,0x0}, 
+  {0x1E66,0x160,0x307,0x0}, {0x1E67,0x161,0x307,0x0}, {0x1E68,0x1E62,0x307,0x0}, 
+  {0x1E69,0x1E63,0x307,0x0}, {0x1E6A,0x54,0x307,0x0}, {0x1E6B,0x74,0x307,0x0}, 
+  {0x1E6C,0x54,0x323,0x0}, {0x1E6D,0x74,0x323,0x0}, {0x1E6E,0x54,0x331,0x0}, 
+  {0x1E6F,0x74,0x331,0x0}, {0x1E70,0x54,0x32D,0x0}, {0x1E71,0x74,0x32D,0x0}, 
+  {0x1E72,0x55,0x324,0x0}, {0x1E73,0x75,0x324,0x0}, {0x1E74,0x55,0x330,0x0}, 
+  {0x1E75,0x75,0x330,0x0}, {0x1E76,0x55,0x32D,0x0}, {0x1E77,0x75,0x32D,0x0}, 
+  {0x1E78,0x168,0x301,0x0}, {0x1E79,0x169,0x301,0x0}, {0x1E7A,0x16A,0x308,0x0}, 
+  {0x1E7B,0x16B,0x308,0x0}, {0x1E7C,0x56,0x303,0x0}, {0x1E7D,0x76,0x303,0x0}, 
+  {0x1E7E,0x56,0x323,0x0}, {0x1E7F,0x76,0x323,0x0}, {0x1E80,0x57,0x300,0x0}, 
+  {0x1E81,0x77,0x300,0x0}, {0x1E82,0x57,0x301,0x0}, {0x1E83,0x77,0x301,0x0}, 
+  {0x1E84,0x57,0x308,0x0}, {0x1E85,0x77,0x308,0x0}, {0x1E86,0x57,0x307,0x0}, 
+  {0x1E87,0x77,0x307,0x0}, {0x1E88,0x57,0x323,0x0}, {0x1E89,0x77,0x323,0x0}, 
+  {0x1E8A,0x58,0x307,0x0}, {0x1E8B,0x78,0x307,0x0}, {0x1E8C,0x58,0x308,0x0}, 
+  {0x1E8D,0x78,0x308,0x0}, {0x1E8E,0x59,0x307,0x0}, {0x1E8F,0x79,0x307,0x0}, 
+  {0x1E90,0x5A,0x302,0x0}, {0x1E91,0x7A,0x302,0x0}, {0x1E92,0x5A,0x323,0x0}, 
+  {0x1E93,0x7A,0x323,0x0}, {0x1E94,0x5A,0x331,0x0}, {0x1E95,0x7A,0x331,0x0}, 
+  {0x1E96,0x68,0x331,0x0}, {0x1E97,0x74,0x308,0x0}, {0x1E98,0x77,0x30A,0x0}, 
+  {0x1E99,0x79,0x30A,0x0}, {0x1E9B,0x17F,0x307,0x0}, {0x1EA0,0x41,0x323,0x0}, 
+  {0x1EA1,0x61,0x323,0x0}, {0x1EA2,0x41,0x309,0x0}, {0x1EA3,0x61,0x309,0x0}, 
+  {0x1EA4,0xC2,0x301,0x0}, {0x1EA5,0xE2,0x301,0x0}, {0x1EA6,0xC2,0x300,0x0}, 
+  {0x1EA7,0xE2,0x300,0x0}, {0x1EA8,0xC2,0x309,0x0}, {0x1EA9,0xE2,0x309,0x0}, 
+  {0x1EAA,0xC2,0x303,0x0}, {0x1EAB,0xE2,0x303,0x0}, {0x1EAC,0xC2,0x323,0x0}, 
+  {0x1EAD,0xE2,0x323,0x0}, {0x1EAE,0x102,0x301,0x0}, {0x1EAF,0x103,0x301,0x0}, 
+  {0x1EB0,0x102,0x300,0x0}, {0x1EB1,0x103,0x300,0x0}, {0x1EB2,0x102,0x309,0x0}, 
+  {0x1EB3,0x103,0x309,0x0}, {0x1EB4,0x102,0x303,0x0}, {0x1EB5,0x103,0x303,0x0}, 
+  {0x1EB6,0x102,0x323,0x0}, {0x1EB7,0x103,0x323,0x0}, {0x1EB8,0x45,0x323,0x0}, 
+  {0x1EB9,0x65,0x323,0x0}, {0x1EBA,0x45,0x309,0x0}, {0x1EBB,0x65,0x309,0x0}, 
+  {0x1EBC,0x45,0x303,0x0}, {0x1EBD,0x65,0x303,0x0}, {0x1EBE,0xCA,0x301,0x0}, 
+  {0x1EBF,0xEA,0x301,0x0}, {0x1EC0,0xCA,0x300,0x0}, {0x1EC1,0xEA,0x300,0x0}, 
+  {0x1EC2,0xCA,0x309,0x0}, {0x1EC3,0xEA,0x309,0x0}, {0x1EC4,0xCA,0x303,0x0}, 
+  {0x1EC5,0xEA,0x303,0x0}, {0x1EC6,0xCA,0x323,0x0}, {0x1EC7,0xEA,0x323,0x0}, 
+  {0x1EC8,0x49,0x309,0x0}, {0x1EC9,0x69,0x309,0x0}, {0x1ECA,0x49,0x323,0x0}, 
+  {0x1ECB,0x69,0x323,0x0}, {0x1ECC,0x4F,0x323,0x0}, {0x1ECD,0x6F,0x323,0x0}, 
+  {0x1ECE,0x4F,0x309,0x0}, {0x1ECF,0x6F,0x309,0x0}, {0x1ED0,0xD4,0x301,0x0}, 
+  {0x1ED1,0xF4,0x301,0x0}, {0x1ED2,0xD4,0x300,0x0}, {0x1ED3,0xF4,0x300,0x0}, 
+  {0x1ED4,0xD4,0x309,0x0}, {0x1ED5,0xF4,0x309,0x0}, {0x1ED6,0xD4,0x303,0x0}, 
+  {0x1ED7,0xF4,0x303,0x0}, {0x1ED8,0xD4,0x323,0x0}, {0x1ED9,0xF4,0x323,0x0}, 
+  {0x1EDA,0x1A0,0x301,0x0}, {0x1EDB,0x1A1,0x301,0x0}, {0x1EDC,0x1A0,0x300,0x0}, 
+  {0x1EDD,0x1A1,0x300,0x0}, {0x1EDE,0x1A0,0x309,0x0}, {0x1EDF,0x1A1,0x309,0x0}, 
+  {0x1EE0,0x1A0,0x303,0x0}, {0x1EE1,0x1A1,0x303,0x0}, {0x1EE2,0x1A0,0x323,0x0}, 
+  {0x1EE3,0x1A1,0x323,0x0}, {0x1EE4,0x55,0x323,0x0}, {0x1EE5,0x75,0x323,0x0}, 
+  {0x1EE6,0x55,0x309,0x0}, {0x1EE7,0x75,0x309,0x0}, {0x1EE8,0x1AF,0x301,0x0}, 
+  {0x1EE9,0x1B0,0x301,0x0}, {0x1EEA,0x1AF,0x300,0x0}, {0x1EEB,0x1B0,0x300,0x0}, 
+  {0x1EEC,0x1AF,0x309,0x0}, {0x1EED,0x1B0,0x309,0x0}, {0x1EEE,0x1AF,0x303,0x0}, 
+  {0x1EEF,0x1B0,0x303,0x0}, {0x1EF0,0x1AF,0x323,0x0}, {0x1EF1,0x1B0,0x323,0x0}, 
+  {0x1EF2,0x59,0x300,0x0}, {0x1EF3,0x79,0x300,0x0}, {0x1EF4,0x59,0x323,0x0}, 
+  {0x1EF5,0x79,0x323,0x0}, {0x1EF6,0x59,0x309,0x0}, {0x1EF7,0x79,0x309,0x0}, 
+  {0x1EF8,0x59,0x303,0x0}, {0x1EF9,0x79,0x303,0x0}, {0x1F00,0x3B1,0x313,0x0}, 
+  {0x1F01,0x3B1,0x314,0x0}, {0x1F02,0x1F00,0x300,0x0}, {0x1F03,0x1F01,0x300,0x0}, 
+  {0x1F04,0x1F00,0x301,0x0}, {0x1F05,0x1F01,0x301,0x0}, {0x1F06,0x1F00,0x342,0x0}, 
+  {0x1F07,0x1F01,0x342,0x0}, {0x1F08,0x391,0x313,0x0}, {0x1F09,0x391,0x314,0x0}, 
+  {0x1F0A,0x1F08,0x300,0x0}, {0x1F0B,0x1F09,0x300,0x0}, {0x1F0C,0x1F08,0x301,0x0}, 
+  {0x1F0D,0x1F09,0x301,0x0}, {0x1F0E,0x1F08,0x342,0x0}, {0x1F0F,0x1F09,0x342,0x0}, 
+  {0x1F10,0x3B5,0x313,0x0}, {0x1F11,0x3B5,0x314,0x0}, {0x1F12,0x1F10,0x300,0x0}, 
+  {0x1F13,0x1F11,0x300,0x0}, {0x1F14,0x1F10,0x301,0x0}, {0x1F15,0x1F11,0x301,0x0}, 
+  {0x1F18,0x395,0x313,0x0}, {0x1F19,0x395,0x314,0x0}, {0x1F1A,0x1F18,0x300,0x0}, 
+  {0x1F1B,0x1F19,0x300,0x0}, {0x1F1C,0x1F18,0x301,0x0}, {0x1F1D,0x1F19,0x301,0x0}, 
+  {0x1F20,0x3B7,0x313,0x0}, {0x1F21,0x3B7,0x314,0x0}, {0x1F22,0x1F20,0x300,0x0}, 
+  {0x1F23,0x1F21,0x300,0x0}, {0x1F24,0x1F20,0x301,0x0}, {0x1F25,0x1F21,0x301,0x0}, 
+  {0x1F26,0x1F20,0x342,0x0}, {0x1F27,0x1F21,0x342,0x0}, {0x1F28,0x397,0x313,0x0}, 
+  {0x1F29,0x397,0x314,0x0}, {0x1F2A,0x1F28,0x300,0x0}, {0x1F2B,0x1F29,0x300,0x0}, 
+  {0x1F2C,0x1F28,0x301,0x0}, {0x1F2D,0x1F29,0x301,0x0}, {0x1F2E,0x1F28,0x342,0x0}, 
+  {0x1F2F,0x1F29,0x342,0x0}, {0x1F30,0x3B9,0x313,0x0}, {0x1F31,0x3B9,0x314,0x0}, 
+  {0x1F32,0x1F30,0x300,0x0}, {0x1F33,0x1F31,0x300,0x0}, {0x1F34,0x1F30,0x301,0x0}, 
+  {0x1F35,0x1F31,0x301,0x0}, {0x1F36,0x1F30,0x342,0x0}, {0x1F37,0x1F31,0x342,0x0}, 
+  {0x1F38,0x399,0x313,0x0}, {0x1F39,0x399,0x314,0x0}, {0x1F3A,0x1F38,0x300,0x0}, 
+  {0x1F3B,0x1F39,0x300,0x0}, {0x1F3C,0x1F38,0x301,0x0}, {0x1F3D,0x1F39,0x301,0x0}, 
+  {0x1F3E,0x1F38,0x342,0x0}, {0x1F3F,0x1F39,0x342,0x0}, {0x1F40,0x3BF,0x313,0x0}, 
+  {0x1F41,0x3BF,0x314,0x0}, {0x1F42,0x1F40,0x300,0x0}, {0x1F43,0x1F41,0x300,0x0}, 
+  {0x1F44,0x1F40,0x301,0x0}, {0x1F45,0x1F41,0x301,0x0}, {0x1F48,0x39F,0x313,0x0}, 
+  {0x1F49,0x39F,0x314,0x0}, {0x1F4A,0x1F48,0x300,0x0}, {0x1F4B,0x1F49,0x300,0x0}, 
+  {0x1F4C,0x1F48,0x301,0x0}, {0x1F4D,0x1F49,0x301,0x0}, {0x1F50,0x3C5,0x313,0x0}, 
+  {0x1F51,0x3C5,0x314,0x0}, {0x1F52,0x1F50,0x300,0x0}, {0x1F53,0x1F51,0x300,0x0}, 
+  {0x1F54,0x1F50,0x301,0x0}, {0x1F55,0x1F51,0x301,0x0}, {0x1F56,0x1F50,0x342,0x0}, 
+  {0x1F57,0x1F51,0x342,0x0}, {0x1F59,0x3A5,0x314,0x0}, {0x1F5B,0x1F59,0x300,0x0}, 
+  {0x1F5D,0x1F59,0x301,0x0}, {0x1F5F,0x1F59,0x342,0x0}, {0x1F60,0x3C9,0x313,0x0}, 
+  {0x1F61,0x3C9,0x314,0x0}, {0x1F62,0x1F60,0x300,0x0}, {0x1F63,0x1F61,0x300,0x0}, 
+  {0x1F64,0x1F60,0x301,0x0}, {0x1F65,0x1F61,0x301,0x0}, {0x1F66,0x1F60,0x342,0x0}, 
+  {0x1F67,0x1F61,0x342,0x0}, {0x1F68,0x3A9,0x313,0x0}, {0x1F69,0x3A9,0x314,0x0}, 
+  {0x1F6A,0x1F68,0x300,0x0}, {0x1F6B,0x1F69,0x300,0x0}, {0x1F6C,0x1F68,0x301,0x0}, 
+  {0x1F6D,0x1F69,0x301,0x0}, {0x1F6E,0x1F68,0x342,0x0}, {0x1F6F,0x1F69,0x342,0x0}, 
+  {0x1F70,0x3B1,0x300,0x0}, {0x1F71,0x3B1,0x301,0x0}, {0x1F72,0x3B5,0x300,0x0}, 
+  {0x1F73,0x3B5,0x301,0x0}, {0x1F74,0x3B7,0x300,0x0}, {0x1F75,0x3B7,0x301,0x0}, 
+  {0x1F76,0x3B9,0x300,0x0}, {0x1F77,0x3B9,0x301,0x0}, {0x1F78,0x3BF,0x300,0x0}, 
+  {0x1F79,0x3BF,0x301,0x0}, {0x1F7A,0x3C5,0x300,0x0}, {0x1F7B,0x3C5,0x301,0x0}, 
+  {0x1F7C,0x3C9,0x300,0x0}, {0x1F7D,0x3C9,0x301,0x0}, {0x1F80,0x1F00,0x345,0x0}, 
+  {0x1F81,0x1F01,0x345,0x0}, {0x1F82,0x1F02,0x345,0x0}, {0x1F83,0x1F03,0x345,0x0}, 
+  {0x1F84,0x1F04,0x345,0x0}, {0x1F85,0x1F05,0x345,0x0}, {0x1F86,0x1F06,0x345,0x0}, 
+  {0x1F87,0x1F07,0x345,0x0}, {0x1F88,0x1F08,0x345,0x0}, {0x1F89,0x1F09,0x345,0x0}, 
+  {0x1F8A,0x1F0A,0x345,0x0}, {0x1F8B,0x1F0B,0x345,0x0}, {0x1F8C,0x1F0C,0x345,0x0}, 
+  {0x1F8D,0x1F0D,0x345,0x0}, {0x1F8E,0x1F0E,0x345,0x0}, {0x1F8F,0x1F0F,0x345,0x0}, 
+  {0x1F90,0x1F20,0x345,0x0}, {0x1F91,0x1F21,0x345,0x0}, {0x1F92,0x1F22,0x345,0x0}, 
+  {0x1F93,0x1F23,0x345,0x0}, {0x1F94,0x1F24,0x345,0x0}, {0x1F95,0x1F25,0x345,0x0}, 
+  {0x1F96,0x1F26,0x345,0x0}, {0x1F97,0x1F27,0x345,0x0}, {0x1F98,0x1F28,0x345,0x0}, 
+  {0x1F99,0x1F29,0x345,0x0}, {0x1F9A,0x1F2A,0x345,0x0}, {0x1F9B,0x1F2B,0x345,0x0}, 
+  {0x1F9C,0x1F2C,0x345,0x0}, {0x1F9D,0x1F2D,0x345,0x0}, {0x1F9E,0x1F2E,0x345,0x0}, 
+  {0x1F9F,0x1F2F,0x345,0x0}, {0x1FA0,0x1F60,0x345,0x0}, {0x1FA1,0x1F61,0x345,0x0}, 
+  {0x1FA2,0x1F62,0x345,0x0}, {0x1FA3,0x1F63,0x345,0x0}, {0x1FA4,0x1F64,0x345,0x0}, 
+  {0x1FA5,0x1F65,0x345,0x0}, {0x1FA6,0x1F66,0x345,0x0}, {0x1FA7,0x1F67,0x345,0x0}, 
+  {0x1FA8,0x1F68,0x345,0x0}, {0x1FA9,0x1F69,0x345,0x0}, {0x1FAA,0x1F6A,0x345,0x0}, 
+  {0x1FAB,0x1F6B,0x345,0x0}, {0x1FAC,0x1F6C,0x345,0x0}, {0x1FAD,0x1F6D,0x345,0x0}, 
+  {0x1FAE,0x1F6E,0x345,0x0}, {0x1FAF,0x1F6F,0x345,0x0}, {0x1FB0,0x3B1,0x306,0x0}, 
+  {0x1FB1,0x3B1,0x304,0x0}, {0x1FB2,0x1F70,0x345,0x0}, {0x1FB3,0x3B1,0x345,0x0}, 
+  {0x1FB4,0x1F71,0x345,0x0}, {0x1FB6,0x3B1,0x342,0x0}, {0x1FB7,0x1FB6,0x345,0x0}, 
+  {0x1FB8,0x391,0x306,0x0}, {0x1FB9,0x391,0x304,0x0}, {0x1FBA,0x391,0x300,0x0}, 
+  {0x1FBB,0x391,0x301,0x0}, {0x1FBC,0x391,0x345,0x0}, {0x1FBE,0x3B9,0x0,0x0}, 
+  {0x1FC1,0xA8,0x342,0x0}, {0x1FC2,0x1F74,0x345,0x0}, {0x1FC3,0x3B7,0x345,0x0}, 
+  {0x1FC4,0x1F75,0x345,0x0}, {0x1FC6,0x3B7,0x342,0x0}, {0x1FC7,0x1FC6,0x345,0x0}, 
+  {0x1FC8,0x395,0x300,0x0}, {0x1FC9,0x395,0x301,0x0}, {0x1FCA,0x397,0x300,0x0}, 
+  {0x1FCB,0x397,0x301,0x0}, {0x1FCC,0x397,0x345,0x0}, {0x1FCD,0x1FBF,0x300,0x0}, 
+  {0x1FCE,0x1FBF,0x301,0x0}, {0x1FCF,0x1FBF,0x342,0x0}, {0x1FD0,0x3B9,0x306,0x0}, 
+  {0x1FD1,0x3B9,0x304,0x0}, {0x1FD2,0x3CA,0x300,0x0}, {0x1FD3,0x3CA,0x301,0x0}, 
+  {0x1FD6,0x3B9,0x342,0x0}, {0x1FD7,0x3CA,0x342,0x0}, {0x1FD8,0x399,0x306,0x0}, 
+  {0x1FD9,0x399,0x304,0x0}, {0x1FDA,0x399,0x300,0x0}, {0x1FDB,0x399,0x301,0x0}, 
+  {0x1FDD,0x1FFE,0x300,0x0}, {0x1FDE,0x1FFE,0x301,0x0}, {0x1FDF,0x1FFE,0x342,0x0}, 
+  {0x1FE0,0x3C5,0x306,0x0}, {0x1FE1,0x3C5,0x304,0x0}, {0x1FE2,0x3CB,0x300,0x0}, 
+  {0x1FE3,0x3CB,0x301,0x0}, {0x1FE4,0x3C1,0x313,0x0}, {0x1FE5,0x3C1,0x314,0x0}, 
+  {0x1FE6,0x3C5,0x342,0x0}, {0x1FE7,0x3CB,0x342,0x0}, {0x1FE8,0x3A5,0x306,0x0}, 
+  {0x1FE9,0x3A5,0x304,0x0}, {0x1FEA,0x3A5,0x300,0x0}, {0x1FEB,0x3A5,0x301,0x0}, 
+  {0x1FEC,0x3A1,0x314,0x0}, {0x1FED,0xA8,0x300,0x0}, {0x1FEE,0xA8,0x301,0x0}, 
+  {0x1FEF,0x60,0x0,0x0}, {0x1FF2,0x1F7C,0x345,0x0}, {0x1FF3,0x3C9,0x345,0x0}, 
+  {0x1FF4,0x1F79,0x345,0x0}, {0x1FF6,0x3C9,0x342,0x0}, {0x1FF7,0x1FF6,0x345,0x0}, 
+  {0x1FF8,0x39F,0x300,0x0}, {0x1FF9,0x39F,0x301,0x0}, {0x1FFA,0x3A9,0x300,0x0}, 
+  {0x1FFB,0x3A9,0x301,0x0}, {0x1FFC,0x3A9,0x345,0x0}, {0x1FFD,0xB4,0x0,0x0}, 
+  {0x2000,0x2002,0x0,0x0}, {0x2001,0x2003,0x0,0x0}, {0x2126,0x3A9,0x0,0x0}, 
+  {0x212A,0x4B,0x0,0x0}, {0x212B,0xC5,0x0,0x0}, {0x2204,0x2203,0x338,0x0}, 
+  {0x2209,0x2208,0x338,0x0}, {0x220C,0x220B,0x338,0x0}, {0x2224,0x2223,0x338,0x0}, 
+  {0x2226,0x2225,0x338,0x0}, {0x2241,0x7E,0x338,0x0}, {0x2244,0x2243,0x338,0x0}, 
+  {0x2247,0x2245,0x338,0x0}, {0x2249,0x2248,0x338,0x0}, {0x2260,0x3D,0x338,0x0}, 
+  {0x2262,0x2261,0x338,0x0}, {0x226D,0x224D,0x338,0x0}, {0x226E,0x3C,0x338,0x0}, 
+  {0x226F,0x3E,0x338,0x0}, {0x2270,0x2264,0x338,0x0}, {0x2271,0x2265,0x338,0x0}, 
+  {0x2274,0x2272,0x338,0x0}, {0x2275,0x2273,0x338,0x0}, {0x2278,0x2276,0x338,0x0}, 
+  {0x2279,0x2277,0x338,0x0}, {0x2280,0x227A,0x338,0x0}, {0x2281,0x227B,0x338,0x0}, 
+  {0x2284,0x2282,0x338,0x0}, {0x2285,0x2283,0x338,0x0}, {0x2288,0x2286,0x338,0x0}, 
+  {0x2289,0x2287,0x338,0x0}, {0x22AC,0x22A2,0x338,0x0}, {0x22AD,0x22A8,0x338,0x0}, 
+  {0x22AE,0x22A9,0x338,0x0}, {0x22AF,0x22AB,0x338,0x0}, {0x22E0,0x227C,0x338,0x0}, 
+  {0x22E1,0x227D,0x338,0x0}, {0x22E2,0x2291,0x338,0x0}, {0x22E3,0x2292,0x338,0x0}, 
+  {0x22EA,0x22B2,0x338,0x0}, {0x22EB,0x22B3,0x338,0x0}, {0x22EC,0x22B4,0x338,0x0}, 
+  {0x22ED,0x22B5,0x338,0x0}, {0x2329,0x3008,0x0,0x0}, {0x232A,0x3009,0x0,0x0}, 
+  {0x304C,0x304B,0x3099,0x0}, {0x304E,0x304D,0x3099,0x0}, {0x3050,0x304F,0x3099,0x0}, 
+  {0x3052,0x3051,0x3099,0x0}, {0x3054,0x3053,0x3099,0x0}, {0x3056,0x3055,0x3099,0x0}, 
+  {0x3058,0x3057,0x3099,0x0}, {0x305A,0x3059,0x3099,0x0}, {0x305C,0x305B,0x3099,0x0}, 
+  {0x305E,0x305D,0x3099,0x0}, {0x3060,0x305F,0x3099,0x0}, {0x3062,0x3061,0x3099,0x0}, 
+  {0x3065,0x3064,0x3099,0x0}, {0x3067,0x3066,0x3099,0x0}, {0x3069,0x3068,0x3099,0x0}, 
+  {0x3070,0x306F,0x3099,0x0}, {0x3071,0x306F,0x309A,0x0}, {0x3073,0x3072,0x3099,0x0}, 
+  {0x3074,0x3072,0x309A,0x0}, {0x3076,0x3075,0x3099,0x0}, {0x3077,0x3075,0x309A,0x0}, 
+  {0x3079,0x3078,0x3099,0x0}, {0x307A,0x3078,0x309A,0x0}, {0x307C,0x307B,0x3099,0x0}, 
+  {0x307D,0x307B,0x309A,0x0}, {0x3094,0x3046,0x3099,0x0}, {0x309E,0x309D,0x3099,0x0}, 
+  {0x30AC,0x30AB,0x3099,0x0}, {0x30AE,0x30AD,0x3099,0x0}, {0x30B0,0x30AF,0x3099,0x0}, 
+  {0x30B2,0x30B1,0x3099,0x0}, {0x30B4,0x30B3,0x3099,0x0}, {0x30B6,0x30B5,0x3099,0x0}, 
+  {0x30B8,0x30B7,0x3099,0x0}, {0x30BA,0x30B9,0x3099,0x0}, {0x30BC,0x30BB,0x3099,0x0}, 
+  {0x30BE,0x30BD,0x3099,0x0}, {0x30C0,0x30BF,0x3099,0x0}, {0x30C2,0x30C1,0x3099,0x0}, 
+  {0x30C5,0x30C4,0x3099,0x0}, {0x30C7,0x30C6,0x3099,0x0}, {0x30C9,0x30C8,0x3099,0x0}, 
+  {0x30D0,0x30CF,0x3099,0x0}, {0x30D1,0x30CF,0x309A,0x0}, {0x30D3,0x30D2,0x3099,0x0}, 
+  {0x30D4,0x30D2,0x309A,0x0}, {0x30D6,0x30D5,0x3099,0x0}, {0x30D7,0x30D5,0x309A,0x0}, 
+  {0x30D9,0x30D8,0x3099,0x0}, {0x30DA,0x30D8,0x309A,0x0}, {0x30DC,0x30DB,0x3099,0x0}, 
+  {0x30DD,0x30DB,0x309A,0x0}, {0x30F4,0x30A6,0x3099,0x0}, {0x30F7,0x30EF,0x3099,0x0}, 
+  {0x30F8,0x30F0,0x3099,0x0}, {0x30F9,0x30F1,0x3099,0x0}, {0x30FA,0x30F2,0x3099,0x0}, 
+  {0x30FE,0x30FD,0x3099,0x0}, {0xF900,0x8C48,0x0,0x0}, {0xF901,0x66F4,0x0,0x0}, 
+  {0xF902,0x8ECA,0x0,0x0}, {0xF903,0x8CC8,0x0,0x0}, {0xF904,0x6ED1,0x0,0x0}, 
+  {0xF905,0x4E32,0x0,0x0}, {0xF906,0x53E5,0x0,0x0}, {0xF907,0x9F9C,0x0,0x0}, 
+  {0xF908,0x9F9C,0x0,0x0}, {0xF909,0x5951,0x0,0x0}, {0xF90A,0x91D1,0x0,0x0}, 
+  {0xF90B,0x5587,0x0,0x0}, {0xF90C,0x5948,0x0,0x0}, {0xF90D,0x61F6,0x0,0x0}, 
+  {0xF90E,0x7669,0x0,0x0}, {0xF90F,0x7F85,0x0,0x0}, {0xF910,0x863F,0x0,0x0}, 
+  {0xF911,0x87BA,0x0,0x0}, {0xF912,0x88F8,0x0,0x0}, {0xF913,0x908F,0x0,0x0}, 
+  {0xF914,0x6A02,0x0,0x0}, {0xF915,0x6D1B,0x0,0x0}, {0xF916,0x70D9,0x0,0x0}, 
+  {0xF917,0x73DE,0x0,0x0}, {0xF918,0x843D,0x0,0x0}, {0xF919,0x916A,0x0,0x0}, 
+  {0xF91A,0x99F1,0x0,0x0}, {0xF91B,0x4E82,0x0,0x0}, {0xF91C,0x5375,0x0,0x0}, 
+  {0xF91D,0x6B04,0x0,0x0}, {0xF91E,0x721B,0x0,0x0}, {0xF91F,0x862D,0x0,0x0}, 
+  {0xF920,0x9E1E,0x0,0x0}, {0xF921,0x5D50,0x0,0x0}, {0xF922,0x6FEB,0x0,0x0}, 
+  {0xF923,0x85CD,0x0,0x0}, {0xF924,0x8964,0x0,0x0}, {0xF925,0x62C9,0x0,0x0}, 
+  {0xF926,0x81D8,0x0,0x0}, {0xF927,0x881F,0x0,0x0}, {0xF928,0x5ECA,0x0,0x0}, 
+  {0xF929,0x6717,0x0,0x0}, {0xF92A,0x6D6A,0x0,0x0}, {0xF92B,0x72FC,0x0,0x0}, 
+  {0xF92C,0x90CE,0x0,0x0}, {0xF92D,0x4F86,0x0,0x0}, {0xF92E,0x51B7,0x0,0x0}, 
+  {0xF92F,0x52DE,0x0,0x0}, {0xF930,0x64C4,0x0,0x0}, {0xF931,0x6AD3,0x0,0x0}, 
+  {0xF932,0x7210,0x0,0x0}, {0xF933,0x76E7,0x0,0x0}, {0xF934,0x8001,0x0,0x0}, 
+  {0xF935,0x8606,0x0,0x0}, {0xF936,0x865C,0x0,0x0}, {0xF937,0x8DEF,0x0,0x0}, 
+  {0xF938,0x9732,0x0,0x0}, {0xF939,0x9B6F,0x0,0x0}, {0xF93A,0x9DFA,0x0,0x0}, 
+  {0xF93B,0x788C,0x0,0x0}, {0xF93C,0x797F,0x0,0x0}, {0xF93D,0x7DA0,0x0,0x0}, 
+  {0xF93E,0x83C9,0x0,0x0}, {0xF93F,0x9304,0x0,0x0}, {0xF940,0x9E7F,0x0,0x0}, 
+  {0xF941,0x8AD6,0x0,0x0}, {0xF942,0x58DF,0x0,0x0}, {0xF943,0x5F04,0x0,0x0}, 
+  {0xF944,0x7C60,0x0,0x0}, {0xF945,0x807E,0x0,0x0}, {0xF946,0x7262,0x0,0x0}, 
+  {0xF947,0x78CA,0x0,0x0}, {0xF948,0x8CC2,0x0,0x0}, {0xF949,0x96F7,0x0,0x0}, 
+  {0xF94A,0x58D8,0x0,0x0}, {0xF94B,0x5C62,0x0,0x0}, {0xF94C,0x6A13,0x0,0x0}, 
+  {0xF94D,0x6DDA,0x0,0x0}, {0xF94E,0x6F0F,0x0,0x0}, {0xF94F,0x7D2F,0x0,0x0}, 
+  {0xF950,0x7E37,0x0,0x0}, {0xF951,0x96FB,0x0,0x0}, {0xF952,0x52D2,0x0,0x0}, 
+  {0xF953,0x808B,0x0,0x0}, {0xF954,0x51DC,0x0,0x0}, {0xF955,0x51CC,0x0,0x0}, 
+  {0xF956,0x7A1C,0x0,0x0}, {0xF957,0x7DBE,0x0,0x0}, {0xF958,0x83F1,0x0,0x0}, 
+  {0xF959,0x9675,0x0,0x0}, {0xF95A,0x8B80,0x0,0x0}, {0xF95B,0x62CF,0x0,0x0}, 
+  {0xF95C,0x6A02,0x0,0x0}, {0xF95D,0x8AFE,0x0,0x0}, {0xF95E,0x4E39,0x0,0x0}, 
+  {0xF95F,0x5BE7,0x0,0x0}, {0xF960,0x6012,0x0,0x0}, {0xF961,0x7387,0x0,0x0}, 
+  {0xF962,0x7570,0x0,0x0}, {0xF963,0x5317,0x0,0x0}, {0xF964,0x78FB,0x0,0x0}, 
+  {0xF965,0x4FBF,0x0,0x0}, {0xF966,0x5FA9,0x0,0x0}, {0xF967,0x4E0D,0x0,0x0}, 
+  {0xF968,0x6CCC,0x0,0x0}, {0xF969,0x6578,0x0,0x0}, {0xF96A,0x7D22,0x0,0x0}, 
+  {0xF96B,0x53C3,0x0,0x0}, {0xF96C,0x585E,0x0,0x0}, {0xF96D,0x7701,0x0,0x0}, 
+  {0xF96E,0x8449,0x0,0x0}, {0xF96F,0x8AAA,0x0,0x0}, {0xF970,0x6BBA,0x0,0x0}, 
+  {0xF971,0x8FB0,0x0,0x0}, {0xF972,0x6C88,0x0,0x0}, {0xF973,0x62FE,0x0,0x0}, 
+  {0xF974,0x82E5,0x0,0x0}, {0xF975,0x63A0,0x0,0x0}, {0xF976,0x7565,0x0,0x0}, 
+  {0xF977,0x4EAE,0x0,0x0}, {0xF978,0x5169,0x0,0x0}, {0xF979,0x51C9,0x0,0x0}, 
+  {0xF97A,0x6881,0x0,0x0}, {0xF97B,0x7CE7,0x0,0x0}, {0xF97C,0x826F,0x0,0x0}, 
+  {0xF97D,0x8AD2,0x0,0x0}, {0xF97E,0x91CF,0x0,0x0}, {0xF97F,0x52F5,0x0,0x0}, 
+  {0xF980,0x5442,0x0,0x0}, {0xF981,0x5973,0x0,0x0}, {0xF982,0x5EEC,0x0,0x0}, 
+  {0xF983,0x65C5,0x0,0x0}, {0xF984,0x6FFE,0x0,0x0}, {0xF985,0x792A,0x0,0x0}, 
+  {0xF986,0x95AD,0x0,0x0}, {0xF987,0x9A6A,0x0,0x0}, {0xF988,0x9E97,0x0,0x0}, 
+  {0xF989,0x9ECE,0x0,0x0}, {0xF98A,0x529B,0x0,0x0}, {0xF98B,0x66C6,0x0,0x0}, 
+  {0xF98C,0x6B77,0x0,0x0}, {0xF98D,0x8F62,0x0,0x0}, {0xF98E,0x5E74,0x0,0x0}, 
+  {0xF98F,0x6190,0x0,0x0}, {0xF990,0x6200,0x0,0x0}, {0xF991,0x649A,0x0,0x0}, 
+  {0xF992,0x6F23,0x0,0x0}, {0xF993,0x7149,0x0,0x0}, {0xF994,0x7489,0x0,0x0}, 
+  {0xF995,0x79CA,0x0,0x0}, {0xF996,0x7DF4,0x0,0x0}, {0xF997,0x806F,0x0,0x0}, 
+  {0xF998,0x8F26,0x0,0x0}, {0xF999,0x84EE,0x0,0x0}, {0xF99A,0x9023,0x0,0x0}, 
+  {0xF99B,0x934A,0x0,0x0}, {0xF99C,0x5217,0x0,0x0}, {0xF99D,0x52A3,0x0,0x0}, 
+  {0xF99E,0x54BD,0x0,0x0}, {0xF99F,0x70C8,0x0,0x0}, {0xF9A0,0x88C2,0x0,0x0}, 
+  {0xF9A1,0x8AAA,0x0,0x0}, {0xF9A2,0x5EC9,0x0,0x0}, {0xF9A3,0x5FF5,0x0,0x0}, 
+  {0xF9A4,0x637B,0x0,0x0}, {0xF9A5,0x6BAE,0x0,0x0}, {0xF9A6,0x7C3E,0x0,0x0}, 
+  {0xF9A7,0x7375,0x0,0x0}, {0xF9A8,0x4EE4,0x0,0x0}, {0xF9A9,0x56F9,0x0,0x0}, 
+  {0xF9AA,0x5BE7,0x0,0x0}, {0xF9AB,0x5DBA,0x0,0x0}, {0xF9AC,0x601C,0x0,0x0}, 
+  {0xF9AD,0x73B2,0x0,0x0}, {0xF9AE,0x7469,0x0,0x0}, {0xF9AF,0x7F9A,0x0,0x0}, 
+  {0xF9B0,0x8046,0x0,0x0}, {0xF9B1,0x9234,0x0,0x0}, {0xF9B2,0x96F6,0x0,0x0}, 
+  {0xF9B3,0x9748,0x0,0x0}, {0xF9B4,0x9818,0x0,0x0}, {0xF9B5,0x4F8B,0x0,0x0}, 
+  {0xF9B6,0x79AE,0x0,0x0}, {0xF9B7,0x91B4,0x0,0x0}, {0xF9B8,0x96B8,0x0,0x0}, 
+  {0xF9B9,0x60E1,0x0,0x0}, {0xF9BA,0x4E86,0x0,0x0}, {0xF9BB,0x50DA,0x0,0x0}, 
+  {0xF9BC,0x5BEE,0x0,0x0}, {0xF9BD,0x5C3F,0x0,0x0}, {0xF9BE,0x6599,0x0,0x0}, 
+  {0xF9BF,0x6A02,0x0,0x0}, {0xF9C0,0x71CE,0x0,0x0}, {0xF9C1,0x7642,0x0,0x0}, 
+  {0xF9C2,0x84FC,0x0,0x0}, {0xF9C3,0x907C,0x0,0x0}, {0xF9C4,0x9F8D,0x0,0x0}, 
+  {0xF9C5,0x6688,0x0,0x0}, {0xF9C6,0x962E,0x0,0x0}, {0xF9C7,0x5289,0x0,0x0}, 
+  {0xF9C8,0x677B,0x0,0x0}, {0xF9C9,0x67F3,0x0,0x0}, {0xF9CA,0x6D41,0x0,0x0}, 
+  {0xF9CB,0x6E9C,0x0,0x0}, {0xF9CC,0x7409,0x0,0x0}, {0xF9CD,0x7559,0x0,0x0}, 
+  {0xF9CE,0x786B,0x0,0x0}, {0xF9CF,0x7D10,0x0,0x0}, {0xF9D0,0x985E,0x0,0x0}, 
+  {0xF9D1,0x516D,0x0,0x0}, {0xF9D2,0x622E,0x0,0x0}, {0xF9D3,0x9678,0x0,0x0}, 
+  {0xF9D4,0x502B,0x0,0x0}, {0xF9D5,0x5D19,0x0,0x0}, {0xF9D6,0x6DEA,0x0,0x0}, 
+  {0xF9D7,0x8F2A,0x0,0x0}, {0xF9D8,0x5F8B,0x0,0x0}, {0xF9D9,0x6144,0x0,0x0}, 
+  {0xF9DA,0x6817,0x0,0x0}, {0xF9DB,0x7387,0x0,0x0}, {0xF9DC,0x9686,0x0,0x0}, 
+  {0xF9DD,0x5229,0x0,0x0}, {0xF9DE,0x540F,0x0,0x0}, {0xF9DF,0x5C65,0x0,0x0}, 
+  {0xF9E0,0x6613,0x0,0x0}, {0xF9E1,0x674E,0x0,0x0}, {0xF9E2,0x68A8,0x0,0x0}, 
+  {0xF9E3,0x6CE5,0x0,0x0}, {0xF9E4,0x7406,0x0,0x0}, {0xF9E5,0x75E2,0x0,0x0}, 
+  {0xF9E6,0x7F79,0x0,0x0}, {0xF9E7,0x88CF,0x0,0x0}, {0xF9E8,0x88E1,0x0,0x0}, 
+  {0xF9E9,0x91CC,0x0,0x0}, {0xF9EA,0x96E2,0x0,0x0}, {0xF9EB,0x533F,0x0,0x0}, 
+  {0xF9EC,0x6EBA,0x0,0x0}, {0xF9ED,0x541D,0x0,0x0}, {0xF9EE,0x71D0,0x0,0x0}, 
+  {0xF9EF,0x7498,0x0,0x0}, {0xF9F0,0x85FA,0x0,0x0}, {0xF9F1,0x96A3,0x0,0x0}, 
+  {0xF9F2,0x9C57,0x0,0x0}, {0xF9F3,0x9E9F,0x0,0x0}, {0xF9F4,0x6797,0x0,0x0}, 
+  {0xF9F5,0x6DCB,0x0,0x0}, {0xF9F6,0x81E8,0x0,0x0}, {0xF9F7,0x7ACB,0x0,0x0}, 
+  {0xF9F8,0x7B20,0x0,0x0}, {0xF9F9,0x7C92,0x0,0x0}, {0xF9FA,0x72C0,0x0,0x0}, 
+  {0xF9FB,0x7099,0x0,0x0}, {0xF9FC,0x8B58,0x0,0x0}, {0xF9FD,0x4EC0,0x0,0x0}, 
+  {0xF9FE,0x8336,0x0,0x0}, {0xF9FF,0x523A,0x0,0x0}, {0xFA00,0x5207,0x0,0x0}, 
+  {0xFA01,0x5EA6,0x0,0x0}, {0xFA02,0x62D3,0x0,0x0}, {0xFA03,0x7CD6,0x0,0x0}, 
+  {0xFA04,0x5B85,0x0,0x0}, {0xFA05,0x6D1E,0x0,0x0}, {0xFA06,0x66B4,0x0,0x0}, 
+  {0xFA07,0x8F3B,0x0,0x0}, {0xFA08,0x884C,0x0,0x0}, {0xFA09,0x964D,0x0,0x0}, 
+  {0xFA0A,0x898B,0x0,0x0}, {0xFA0B,0x5ED3,0x0,0x0}, {0xFA0C,0x5140,0x0,0x0}, 
+  {0xFA0D,0x55C0,0x0,0x0}, {0xFA10,0x585A,0x0,0x0}, {0xFA12,0x6674,0x0,0x0}, 
+  {0xFA15,0x51DE,0x0,0x0}, {0xFA16,0x732A,0x0,0x0}, {0xFA17,0x76CA,0x0,0x0}, 
+  {0xFA18,0x793C,0x0,0x0}, {0xFA19,0x795E,0x0,0x0}, {0xFA1A,0x7965,0x0,0x0}, 
+  {0xFA1B,0x798F,0x0,0x0}, {0xFA1C,0x9756,0x0,0x0}, {0xFA1D,0x7CBE,0x0,0x0}, 
+  {0xFA1E,0x7FBD,0x0,0x0}, {0xFA20,0x8612,0x0,0x0}, {0xFA22,0x8AF8,0x0,0x0}, 
+  {0xFA25,0x9038,0x0,0x0}, {0xFA26,0x90FD,0x0,0x0}, {0xFA2A,0x98EF,0x0,0x0}, 
+  {0xFA2B,0x98FC,0x0,0x0}, {0xFA2C,0x9928,0x0,0x0}, {0xFA2D,0x9DB4,0x0,0x0}, 
+  {0xFB1F,0x5F2,0x5B7,0x0}, {0xFB2A,0x5E9,0x5C1,0x0}, {0xFB2B,0x5E9,0x5C2,0x0}, 
+  {0xFB2C,0x5E9,0x5BC,0x5C1}, {0xFB2D,0x5E9,0x5BC,0x5C2}, {0xFB2E,0x5D0,0x5B7,0x0}, 
+  {0xFB2F,0x5D0,0x5B8,0x0}, {0xFB30,0x5D0,0x5BC,0x0}, {0xFB31,0x5D1,0x5BC,0x0}, 
+  {0xFB32,0x5D2,0x5BC,0x0}, {0xFB33,0x5D3,0x5BC,0x0}, {0xFB34,0x5D4,0x5BC,0x0}, 
+  {0xFB35,0x5D5,0x5BC,0x0}, {0xFB36,0x5D6,0x5BC,0x0}, {0xFB38,0x5D8,0x5BC,0x0}, 
+  {0xFB39,0x5D9,0x5BC,0x0}, {0xFB3A,0x5DA,0x5BC,0x0}, {0xFB3B,0x5DB,0x5BC,0x0}, 
+  {0xFB3C,0x5DC,0x5BC,0x0}, {0xFB3E,0x5DE,0x5BC,0x0}, {0xFB40,0x5E0,0x5BC,0x0}, 
+  {0xFB41,0x5E1,0x5BC,0x0}, {0xFB43,0x5E3,0x5BC,0x0}, {0xFB44,0x5E4,0x5BC,0x0}, 
+  {0xFB46,0x5E6,0x5BC,0x0}, {0xFB47,0x5E7,0x5BC,0x0}, {0xFB48,0x5E8,0x5BC,0x0}, 
+  {0xFB49,0x5E9,0x5BC,0x0}, {0xFB4A,0x5EA,0x5BC,0x0}, {0xFB4B,0x5D5,0x5B9,0x0}, 
+  {0xFB4C,0x5D1,0x5BF,0x0}, {0xFB4D,0x5DB,0x5BF,0x0}, {0xFB4E,0x5E4,0x5BF,0x0} 
+};
+
+
+/* All upper-case translations are included here */
+
+#define NUM_TOUPPER_INFO 677
+static const unirelate toupper_info[NUM_TOUPPER_INFO] = {
+  {0x61,0x41}, {0x62,0x42}, {0x63,0x43}, {0x64,0x44}, {0x65,0x45}, 
+  {0x66,0x46}, {0x67,0x47}, {0x68,0x48}, {0x69,0x49}, {0x6A,0x4A}, 
+  {0x6B,0x4B}, {0x6C,0x4C}, {0x6D,0x4D}, {0x6E,0x4E}, {0x6F,0x4F}, 
+  {0x70,0x50}, {0x71,0x51}, {0x72,0x52}, {0x73,0x53}, {0x74,0x54}, 
+  {0x75,0x55}, {0x76,0x56}, {0x77,0x57}, {0x78,0x58}, {0x79,0x59}, 
+  {0x7A,0x5A}, {0xE0,0xC0}, {0xE1,0xC1}, {0xE2,0xC2}, {0xE3,0xC3}, 
+  {0xE4,0xC4}, {0xE5,0xC5}, {0xE6,0xC6}, {0xE7,0xC7}, {0xE8,0xC8}, 
+  {0xE9,0xC9}, {0xEA,0xCA}, {0xEB,0xCB}, {0xEC,0xCC}, {0xED,0xCD}, 
+  {0xEE,0xCE}, {0xEF,0xCF}, {0xF0,0xD0}, {0xF1,0xD1}, {0xF2,0xD2}, 
+  {0xF3,0xD3}, {0xF4,0xD4}, {0xF5,0xD5}, {0xF6,0xD6}, {0xF8,0xD8}, 
+  {0xF9,0xD9}, {0xFA,0xDA}, {0xFB,0xDB}, {0xFC,0xDC}, {0xFD,0xDD}, 
+  {0xFE,0xDE}, {0xFF,0x178}, {0x101,0x100}, {0x103,0x102}, {0x105,0x104}, 
+  {0x107,0x106}, {0x109,0x108}, {0x10B,0x10A}, {0x10D,0x10C}, {0x10F,0x10E}, 
+  {0x111,0x110}, {0x113,0x112}, {0x115,0x114}, {0x117,0x116}, {0x119,0x118}, 
+  {0x11B,0x11A}, {0x11D,0x11C}, {0x11F,0x11E}, {0x121,0x120}, {0x123,0x122}, 
+  {0x125,0x124}, {0x127,0x126}, {0x129,0x128}, {0x12B,0x12A}, {0x12D,0x12C}, 
+  {0x12F,0x12E}, {0x131,0x49}, {0x133,0x132}, {0x135,0x134}, {0x137,0x136}, 
+  {0x13A,0x139}, {0x13C,0x13B}, {0x13E,0x13D}, {0x140,0x13F}, {0x142,0x141}, 
+  {0x144,0x143}, {0x146,0x145}, {0x148,0x147}, {0x14B,0x14A}, {0x14D,0x14C}, 
+  {0x14F,0x14E}, {0x151,0x150}, {0x153,0x152}, {0x155,0x154}, {0x157,0x156}, 
+  {0x159,0x158}, {0x15B,0x15A}, {0x15D,0x15C}, {0x15F,0x15E}, {0x161,0x160}, 
+  {0x163,0x162}, {0x165,0x164}, {0x167,0x166}, {0x169,0x168}, {0x16B,0x16A}, 
+  {0x16D,0x16C}, {0x16F,0x16E}, {0x171,0x170}, {0x173,0x172}, {0x175,0x174}, 
+  {0x177,0x176}, {0x17A,0x179}, {0x17C,0x17B}, {0x17E,0x17D}, {0x17F,0x53}, 
+  {0x183,0x182}, {0x185,0x184}, {0x188,0x187}, {0x18C,0x18B}, {0x192,0x191}, 
+  {0x199,0x198}, {0x1A1,0x1A0}, {0x1A3,0x1A2}, {0x1A5,0x1A4}, {0x1A8,0x1A7}, 
+  {0x1AD,0x1AC}, {0x1B0,0x1AF}, {0x1B4,0x1B3}, {0x1B6,0x1B5}, {0x1B9,0x1B8}, 
+  {0x1BD,0x1BC}, {0x1C5,0x1C4}, {0x1C6,0x1C4}, {0x1C8,0x1C7}, {0x1C9,0x1C7}, 
+  {0x1CB,0x1CA}, {0x1CC,0x1CA}, {0x1CE,0x1CD}, {0x1D0,0x1CF}, {0x1D2,0x1D1}, 
+  {0x1D4,0x1D3}, {0x1D6,0x1D5}, {0x1D8,0x1D7}, {0x1DA,0x1D9}, {0x1DC,0x1DB}, 
+  {0x1DD,0x18E}, {0x1DF,0x1DE}, {0x1E1,0x1E0}, {0x1E3,0x1E2}, {0x1E5,0x1E4}, 
+  {0x1E7,0x1E6}, {0x1E9,0x1E8}, {0x1EB,0x1EA}, {0x1ED,0x1EC}, {0x1EF,0x1EE}, 
+  {0x1F2,0x1F1}, {0x1F3,0x1F1}, {0x1F5,0x1F4}, {0x1FB,0x1FA}, {0x1FD,0x1FC}, 
+  {0x1FF,0x1FE}, {0x201,0x200}, {0x203,0x202}, {0x205,0x204}, {0x207,0x206}, 
+  {0x209,0x208}, {0x20B,0x20A}, {0x20D,0x20C}, {0x20F,0x20E}, {0x211,0x210}, 
+  {0x213,0x212}, {0x215,0x214}, {0x217,0x216}, {0x253,0x181}, {0x254,0x186}, 
+  {0x256,0x189}, {0x257,0x18A}, {0x259,0x18F}, {0x25B,0x190}, {0x260,0x193}, 
+  {0x263,0x194}, {0x268,0x197}, {0x269,0x196}, {0x26F,0x19C}, {0x272,0x19D}, 
+  {0x275,0x19F}, {0x283,0x1A9}, {0x288,0x1AE}, {0x28A,0x1B1}, {0x28B,0x1B2}, 
+  {0x292,0x1B7}, {0x3AC,0x386}, {0x3AD,0x388}, {0x3AE,0x389}, {0x3AF,0x38A}, 
+  {0x3B1,0x391}, {0x3B2,0x392}, {0x3B3,0x393}, {0x3B4,0x394}, {0x3B5,0x395}, 
+  {0x3B6,0x396}, {0x3B7,0x397}, {0x3B8,0x398}, {0x3B9,0x399}, {0x3BA,0x39A}, 
+  {0x3BB,0x39B}, {0x3BC,0x39C}, {0x3BD,0x39D}, {0x3BE,0x39E}, {0x3BF,0x39F}, 
+  {0x3C0,0x3A0}, {0x3C1,0x3A1}, {0x3C2,0x3A3}, {0x3C3,0x3A3}, {0x3C4,0x3A4}, 
+  {0x3C5,0x3A5}, {0x3C6,0x3A6}, {0x3C7,0x3A7}, {0x3C8,0x3A8}, {0x3C9,0x3A9}, 
+  {0x3CA,0x3AA}, {0x3CB,0x3AB}, {0x3CC,0x38C}, {0x3CD,0x38E}, {0x3CE,0x38F}, 
+  {0x3D0,0x392}, {0x3D1,0x398}, {0x3D5,0x3A6}, {0x3D6,0x3A0}, {0x3E3,0x3E2}, 
+  {0x3E5,0x3E4}, {0x3E7,0x3E6}, {0x3E9,0x3E8}, {0x3EB,0x3EA}, {0x3ED,0x3EC}, 
+  {0x3EF,0x3EE}, {0x3F0,0x39A}, {0x3F1,0x3A1}, {0x3F2,0x3A3}, {0x430,0x410}, 
+  {0x431,0x411}, {0x432,0x412}, {0x433,0x413}, {0x434,0x414}, {0x435,0x415}, 
+  {0x436,0x416}, {0x437,0x417}, {0x438,0x418}, {0x439,0x419}, {0x43A,0x41A}, 
+  {0x43B,0x41B}, {0x43C,0x41C}, {0x43D,0x41D}, {0x43E,0x41E}, {0x43F,0x41F}, 
+  {0x440,0x420}, {0x441,0x421}, {0x442,0x422}, {0x443,0x423}, {0x444,0x424}, 
+  {0x445,0x425}, {0x446,0x426}, {0x447,0x427}, {0x448,0x428}, {0x449,0x429}, 
+  {0x44A,0x42A}, {0x44B,0x42B}, {0x44C,0x42C}, {0x44D,0x42D}, {0x44E,0x42E}, 
+  {0x44F,0x42F}, {0x451,0x401}, {0x452,0x402}, {0x453,0x403}, {0x454,0x404}, 
+  {0x455,0x405}, {0x456,0x406}, {0x457,0x407}, {0x458,0x408}, {0x459,0x409}, 
+  {0x45A,0x40A}, {0x45B,0x40B}, {0x45C,0x40C}, {0x45E,0x40E}, {0x45F,0x40F}, 
+  {0x461,0x460}, {0x463,0x462}, {0x465,0x464}, {0x467,0x466}, {0x469,0x468}, 
+  {0x46B,0x46A}, {0x46D,0x46C}, {0x46F,0x46E}, {0x471,0x470}, {0x473,0x472}, 
+  {0x475,0x474}, {0x477,0x476}, {0x479,0x478}, {0x47B,0x47A}, {0x47D,0x47C}, 
+  {0x47F,0x47E}, {0x481,0x480}, {0x491,0x490}, {0x493,0x492}, {0x495,0x494}, 
+  {0x497,0x496}, {0x499,0x498}, {0x49B,0x49A}, {0x49D,0x49C}, {0x49F,0x49E}, 
+  {0x4A1,0x4A0}, {0x4A3,0x4A2}, {0x4A5,0x4A4}, {0x4A7,0x4A6}, {0x4A9,0x4A8}, 
+  {0x4AB,0x4AA}, {0x4AD,0x4AC}, {0x4AF,0x4AE}, {0x4B1,0x4B0}, {0x4B3,0x4B2}, 
+  {0x4B5,0x4B4}, {0x4B7,0x4B6}, {0x4B9,0x4B8}, {0x4BB,0x4BA}, {0x4BD,0x4BC}, 
+  {0x4BF,0x4BE}, {0x4C2,0x4C1}, {0x4C4,0x4C3}, {0x4C8,0x4C7}, {0x4CC,0x4CB}, 
+  {0x4D1,0x4D0}, {0x4D3,0x4D2}, {0x4D5,0x4D4}, {0x4D7,0x4D6}, {0x4D9,0x4D8}, 
+  {0x4DB,0x4DA}, {0x4DD,0x4DC}, {0x4DF,0x4DE}, {0x4E1,0x4E0}, {0x4E3,0x4E2}, 
+  {0x4E5,0x4E4}, {0x4E7,0x4E6}, {0x4E9,0x4E8}, {0x4EB,0x4EA}, {0x4EF,0x4EE}, 
+  {0x4F1,0x4F0}, {0x4F3,0x4F2}, {0x4F5,0x4F4}, {0x4F9,0x4F8}, {0x561,0x531}, 
+  {0x562,0x532}, {0x563,0x533}, {0x564,0x534}, {0x565,0x535}, {0x566,0x536}, 
+  {0x567,0x537}, {0x568,0x538}, {0x569,0x539}, {0x56A,0x53A}, {0x56B,0x53B}, 
+  {0x56C,0x53C}, {0x56D,0x53D}, {0x56E,0x53E}, {0x56F,0x53F}, {0x570,0x540}, 
+  {0x571,0x541}, {0x572,0x542}, {0x573,0x543}, {0x574,0x544}, {0x575,0x545}, 
+  {0x576,0x546}, {0x577,0x547}, {0x578,0x548}, {0x579,0x549}, {0x57A,0x54A}, 
+  {0x57B,0x54B}, {0x57C,0x54C}, {0x57D,0x54D}, {0x57E,0x54E}, {0x57F,0x54F}, 
+  {0x580,0x550}, {0x581,0x551}, {0x582,0x552}, {0x583,0x553}, {0x584,0x554}, 
+  {0x585,0x555}, {0x586,0x556}, {0x1E01,0x1E00}, {0x1E03,0x1E02}, {0x1E05,0x1E04}, 
+  {0x1E07,0x1E06}, {0x1E09,0x1E08}, {0x1E0B,0x1E0A}, {0x1E0D,0x1E0C}, {0x1E0F,0x1E0E}, 
+  {0x1E11,0x1E10}, {0x1E13,0x1E12}, {0x1E15,0x1E14}, {0x1E17,0x1E16}, {0x1E19,0x1E18}, 
+  {0x1E1B,0x1E1A}, {0x1E1D,0x1E1C}, {0x1E1F,0x1E1E}, {0x1E21,0x1E20}, {0x1E23,0x1E22}, 
+  {0x1E25,0x1E24}, {0x1E27,0x1E26}, {0x1E29,0x1E28}, {0x1E2B,0x1E2A}, {0x1E2D,0x1E2C}, 
+  {0x1E2F,0x1E2E}, {0x1E31,0x1E30}, {0x1E33,0x1E32}, {0x1E35,0x1E34}, {0x1E37,0x1E36}, 
+  {0x1E39,0x1E38}, {0x1E3B,0x1E3A}, {0x1E3D,0x1E3C}, {0x1E3F,0x1E3E}, {0x1E41,0x1E40}, 
+  {0x1E43,0x1E42}, {0x1E45,0x1E44}, {0x1E47,0x1E46}, {0x1E49,0x1E48}, {0x1E4B,0x1E4A}, 
+  {0x1E4D,0x1E4C}, {0x1E4F,0x1E4E}, {0x1E51,0x1E50}, {0x1E53,0x1E52}, {0x1E55,0x1E54}, 
+  {0x1E57,0x1E56}, {0x1E59,0x1E58}, {0x1E5B,0x1E5A}, {0x1E5D,0x1E5C}, {0x1E5F,0x1E5E}, 
+  {0x1E61,0x1E60}, {0x1E63,0x1E62}, {0x1E65,0x1E64}, {0x1E67,0x1E66}, {0x1E69,0x1E68}, 
+  {0x1E6B,0x1E6A}, {0x1E6D,0x1E6C}, {0x1E6F,0x1E6E}, {0x1E71,0x1E70}, {0x1E73,0x1E72}, 
+  {0x1E75,0x1E74}, {0x1E77,0x1E76}, {0x1E79,0x1E78}, {0x1E7B,0x1E7A}, {0x1E7D,0x1E7C}, 
+  {0x1E7F,0x1E7E}, {0x1E81,0x1E80}, {0x1E83,0x1E82}, {0x1E85,0x1E84}, {0x1E87,0x1E86}, 
+  {0x1E89,0x1E88}, {0x1E8B,0x1E8A}, {0x1E8D,0x1E8C}, {0x1E8F,0x1E8E}, {0x1E91,0x1E90}, 
+  {0x1E93,0x1E92}, {0x1E95,0x1E94}, {0x1E9B,0x1E60}, {0x1EA1,0x1EA0}, {0x1EA3,0x1EA2}, 
+  {0x1EA5,0x1EA4}, {0x1EA7,0x1EA6}, {0x1EA9,0x1EA8}, {0x1EAB,0x1EAA}, {0x1EAD,0x1EAC}, 
+  {0x1EAF,0x1EAE}, {0x1EB1,0x1EB0}, {0x1EB3,0x1EB2}, {0x1EB5,0x1EB4}, {0x1EB7,0x1EB6}, 
+  {0x1EB9,0x1EB8}, {0x1EBB,0x1EBA}, {0x1EBD,0x1EBC}, {0x1EBF,0x1EBE}, {0x1EC1,0x1EC0}, 
+  {0x1EC3,0x1EC2}, {0x1EC5,0x1EC4}, {0x1EC7,0x1EC6}, {0x1EC9,0x1EC8}, {0x1ECB,0x1ECA}, 
+  {0x1ECD,0x1ECC}, {0x1ECF,0x1ECE}, {0x1ED1,0x1ED0}, {0x1ED3,0x1ED2}, {0x1ED5,0x1ED4}, 
+  {0x1ED7,0x1ED6}, {0x1ED9,0x1ED8}, {0x1EDB,0x1EDA}, {0x1EDD,0x1EDC}, {0x1EDF,0x1EDE}, 
+  {0x1EE1,0x1EE0}, {0x1EE3,0x1EE2}, {0x1EE5,0x1EE4}, {0x1EE7,0x1EE6}, {0x1EE9,0x1EE8}, 
+  {0x1EEB,0x1EEA}, {0x1EED,0x1EEC}, {0x1EEF,0x1EEE}, {0x1EF1,0x1EF0}, {0x1EF3,0x1EF2}, 
+  {0x1EF5,0x1EF4}, {0x1EF7,0x1EF6}, {0x1EF9,0x1EF8}, {0x1F00,0x1F08}, {0x1F01,0x1F09}, 
+  {0x1F02,0x1F0A}, {0x1F03,0x1F0B}, {0x1F04,0x1F0C}, {0x1F05,0x1F0D}, {0x1F06,0x1F0E}, 
+  {0x1F07,0x1F0F}, {0x1F10,0x1F18}, {0x1F11,0x1F19}, {0x1F12,0x1F1A}, {0x1F13,0x1F1B}, 
+  {0x1F14,0x1F1C}, {0x1F15,0x1F1D}, {0x1F20,0x1F28}, {0x1F21,0x1F29}, {0x1F22,0x1F2A}, 
+  {0x1F23,0x1F2B}, {0x1F24,0x1F2C}, {0x1F25,0x1F2D}, {0x1F26,0x1F2E}, {0x1F27,0x1F2F}, 
+  {0x1F30,0x1F38}, {0x1F31,0x1F39}, {0x1F32,0x1F3A}, {0x1F33,0x1F3B}, {0x1F34,0x1F3C}, 
+  {0x1F35,0x1F3D}, {0x1F36,0x1F3E}, {0x1F37,0x1F3F}, {0x1F40,0x1F48}, {0x1F41,0x1F49}, 
+  {0x1F42,0x1F4A}, {0x1F43,0x1F4B}, {0x1F44,0x1F4C}, {0x1F45,0x1F4D}, {0x1F51,0x1F59}, 
+  {0x1F53,0x1F5B}, {0x1F55,0x1F5D}, {0x1F57,0x1F5F}, {0x1F60,0x1F68}, {0x1F61,0x1F69}, 
+  {0x1F62,0x1F6A}, {0x1F63,0x1F6B}, {0x1F64,0x1F6C}, {0x1F65,0x1F6D}, {0x1F66,0x1F6E}, 
+  {0x1F67,0x1F6F}, {0x1F70,0x1FBA}, {0x1F71,0x1FBB}, {0x1F72,0x1FC8}, {0x1F73,0x1FC9}, 
+  {0x1F74,0x1FCA}, {0x1F75,0x1FCB}, {0x1F76,0x1FDA}, {0x1F77,0x1FDB}, {0x1F78,0x1FF8}, 
+  {0x1F79,0x1FF9}, {0x1F7A,0x1FEA}, {0x1F7B,0x1FEB}, {0x1F7C,0x1FFA}, {0x1F7D,0x1FFB}, 
+  {0x1F80,0x1F88}, {0x1F81,0x1F89}, {0x1F82,0x1F8A}, {0x1F83,0x1F8B}, {0x1F84,0x1F8C}, 
+  {0x1F85,0x1F8D}, {0x1F86,0x1F8E}, {0x1F87,0x1F8F}, {0x1F90,0x1F98}, {0x1F91,0x1F99}, 
+  {0x1F92,0x1F9A}, {0x1F93,0x1F9B}, {0x1F94,0x1F9C}, {0x1F95,0x1F9D}, {0x1F96,0x1F9E}, 
+  {0x1F97,0x1F9F}, {0x1FA0,0x1FA8}, {0x1FA1,0x1FA9}, {0x1FA2,0x1FAA}, {0x1FA3,0x1FAB}, 
+  {0x1FA4,0x1FAC}, {0x1FA5,0x1FAD}, {0x1FA6,0x1FAE}, {0x1FA7,0x1FAF}, {0x1FB0,0x1FB8}, 
+  {0x1FB1,0x1FB9}, {0x1FB3,0x1FBC}, {0x1FC3,0x1FCC}, {0x1FD0,0x1FD8}, {0x1FD1,0x1FD9}, 
+  {0x1FE0,0x1FE8}, {0x1FE1,0x1FE9}, {0x1FE5,0x1FEC}, {0x1FF3,0x1FFC}, {0x2170,0x2160}, 
+  {0x2171,0x2161}, {0x2172,0x2162}, {0x2173,0x2163}, {0x2174,0x2164}, {0x2175,0x2165}, 
+  {0x2176,0x2166}, {0x2177,0x2167}, {0x2178,0x2168}, {0x2179,0x2169}, {0x217A,0x216A}, 
+  {0x217B,0x216B}, {0x217C,0x216C}, {0x217D,0x216D}, {0x217E,0x216E}, {0x217F,0x216F}, 
+  {0x24D0,0x24B6}, {0x24D1,0x24B7}, {0x24D2,0x24B8}, {0x24D3,0x24B9}, {0x24D4,0x24BA}, 
+  {0x24D5,0x24BB}, {0x24D6,0x24BC}, {0x24D7,0x24BD}, {0x24D8,0x24BE}, {0x24D9,0x24BF}, 
+  {0x24DA,0x24C0}, {0x24DB,0x24C1}, {0x24DC,0x24C2}, {0x24DD,0x24C3}, {0x24DE,0x24C4}, 
+  {0x24DF,0x24C5}, {0x24E0,0x24C6}, {0x24E1,0x24C7}, {0x24E2,0x24C8}, {0x24E3,0x24C9}, 
+  {0x24E4,0x24CA}, {0x24E5,0x24CB}, {0x24E6,0x24CC}, {0x24E7,0x24CD}, {0x24E8,0x24CE}, 
+  {0x24E9,0x24CF}, {0xFF41,0xFF21}, {0xFF42,0xFF22}, {0xFF43,0xFF23}, {0xFF44,0xFF24}, 
+  {0xFF45,0xFF25}, {0xFF46,0xFF26}, {0xFF47,0xFF27}, {0xFF48,0xFF28}, {0xFF49,0xFF29}, 
+  {0xFF4A,0xFF2A}, {0xFF4B,0xFF2B}, {0xFF4C,0xFF2C}, {0xFF4D,0xFF2D}, {0xFF4E,0xFF2E}, 
+  {0xFF4F,0xFF2F}, {0xFF50,0xFF30}, {0xFF51,0xFF31}, {0xFF52,0xFF32}, {0xFF53,0xFF33}, 
+  {0xFF54,0xFF34}, {0xFF55,0xFF35}, {0xFF56,0xFF36}, {0xFF57,0xFF37}, {0xFF58,0xFF38}, 
+  {0xFF59,0xFF39}, {0xFF5A,0xFF3A} 
+};
+
+
+
+/* All lower-case translations are included here */
+
+#define NUM_TOLOWER_INFO 705
+static const unirelate tolower_info[NUM_TOLOWER_INFO] = {
+  {0x41,0x61}, {0x42,0x62}, {0x43,0x63}, {0x44,0x64}, {0x45,0x65}, 
+  {0x46,0x66}, {0x47,0x67}, {0x48,0x68}, {0x49,0x69}, {0x4A,0x6A}, 
+  {0x4B,0x6B}, {0x4C,0x6C}, {0x4D,0x6D}, {0x4E,0x6E}, {0x4F,0x6F}, 
+  {0x50,0x70}, {0x51,0x71}, {0x52,0x72}, {0x53,0x73}, {0x54,0x74}, 
+  {0x55,0x75}, {0x56,0x76}, {0x57,0x77}, {0x58,0x78}, {0x59,0x79}, 
+  {0x5A,0x7A}, {0xC0,0xE0}, {0xC1,0xE1}, {0xC2,0xE2}, {0xC3,0xE3}, 
+  {0xC4,0xE4}, {0xC5,0xE5}, {0xC6,0xE6}, {0xC7,0xE7}, {0xC8,0xE8}, 
+  {0xC9,0xE9}, {0xCA,0xEA}, {0xCB,0xEB}, {0xCC,0xEC}, {0xCD,0xED}, 
+  {0xCE,0xEE}, {0xCF,0xEF}, {0xD0,0xF0}, {0xD1,0xF1}, {0xD2,0xF2}, 
+  {0xD3,0xF3}, {0xD4,0xF4}, {0xD5,0xF5}, {0xD6,0xF6}, {0xD8,0xF8}, 
+  {0xD9,0xF9}, {0xDA,0xFA}, {0xDB,0xFB}, {0xDC,0xFC}, {0xDD,0xFD}, 
+  {0xDE,0xFE}, {0x100,0x101}, {0x102,0x103}, {0x104,0x105}, {0x106,0x107}, 
+  {0x108,0x109}, {0x10A,0x10B}, {0x10C,0x10D}, {0x10E,0x10F}, {0x110,0x111}, 
+  {0x112,0x113}, {0x114,0x115}, {0x116,0x117}, {0x118,0x119}, {0x11A,0x11B}, 
+  {0x11C,0x11D}, {0x11E,0x11F}, {0x120,0x121}, {0x122,0x123}, {0x124,0x125}, 
+  {0x126,0x127}, {0x128,0x129}, {0x12A,0x12B}, {0x12C,0x12D}, {0x12E,0x12F}, 
+  {0x130,0x69}, {0x132,0x133}, {0x134,0x135}, {0x136,0x137}, {0x139,0x13A}, 
+  {0x13B,0x13C}, {0x13D,0x13E}, {0x13F,0x140}, {0x141,0x142}, {0x143,0x144}, 
+  {0x145,0x146}, {0x147,0x148}, {0x14A,0x14B}, {0x14C,0x14D}, {0x14E,0x14F}, 
+  {0x150,0x151}, {0x152,0x153}, {0x154,0x155}, {0x156,0x157}, {0x158,0x159}, 
+  {0x15A,0x15B}, {0x15C,0x15D}, {0x15E,0x15F}, {0x160,0x161}, {0x162,0x163}, 
+  {0x164,0x165}, {0x166,0x167}, {0x168,0x169}, {0x16A,0x16B}, {0x16C,0x16D}, 
+  {0x16E,0x16F}, {0x170,0x171}, {0x172,0x173}, {0x174,0x175}, {0x176,0x177}, 
+  {0x178,0xFF}, {0x179,0x17A}, {0x17B,0x17C}, {0x17D,0x17E}, {0x181,0x253}, 
+  {0x182,0x183}, {0x184,0x185}, {0x186,0x254}, {0x187,0x188}, {0x189,0x256}, 
+  {0x18A,0x257}, {0x18B,0x18C}, {0x18E,0x1DD}, {0x18F,0x259}, {0x190,0x25B}, 
+  {0x191,0x192}, {0x193,0x260}, {0x194,0x263}, {0x196,0x269}, {0x197,0x268}, 
+  {0x198,0x199}, {0x19C,0x26F}, {0x19D,0x272}, {0x19F,0x275}, {0x1A0,0x1A1}, 
+  {0x1A2,0x1A3}, {0x1A4,0x1A5}, {0x1A7,0x1A8}, {0x1A9,0x283}, {0x1AC,0x1AD}, 
+  {0x1AE,0x288}, {0x1AF,0x1B0}, {0x1B1,0x28A}, {0x1B2,0x28B}, {0x1B3,0x1B4}, 
+  {0x1B5,0x1B6}, {0x1B7,0x292}, {0x1B8,0x1B9}, {0x1BC,0x1BD}, {0x1C4,0x1C6}, 
+  {0x1C5,0x1C6}, {0x1C7,0x1C9}, {0x1C8,0x1C9}, {0x1CA,0x1CC}, {0x1CB,0x1CC}, 
+  {0x1CD,0x1CE}, {0x1CF,0x1D0}, {0x1D1,0x1D2}, {0x1D3,0x1D4}, {0x1D5,0x1D6}, 
+  {0x1D7,0x1D8}, {0x1D9,0x1DA}, {0x1DB,0x1DC}, {0x1DE,0x1DF}, {0x1E0,0x1E1}, 
+  {0x1E2,0x1E3}, {0x1E4,0x1E5}, {0x1E6,0x1E7}, {0x1E8,0x1E9}, {0x1EA,0x1EB}, 
+  {0x1EC,0x1ED}, {0x1EE,0x1EF}, {0x1F1,0x1F3}, {0x1F2,0x1F3}, {0x1F4,0x1F5}, 
+  {0x1FA,0x1FB}, {0x1FC,0x1FD}, {0x1FE,0x1FF}, {0x200,0x201}, {0x202,0x203}, 
+  {0x204,0x205}, {0x206,0x207}, {0x208,0x209}, {0x20A,0x20B}, {0x20C,0x20D}, 
+  {0x20E,0x20F}, {0x210,0x211}, {0x212,0x213}, {0x214,0x215}, {0x216,0x217}, 
+  {0x386,0x3AC}, {0x388,0x3AD}, {0x389,0x3AE}, {0x38A,0x3AF}, {0x38C,0x3CC}, 
+  {0x38E,0x3CD}, {0x38F,0x3CE}, {0x391,0x3B1}, {0x392,0x3B2}, {0x393,0x3B3}, 
+  {0x394,0x3B4}, {0x395,0x3B5}, {0x396,0x3B6}, {0x397,0x3B7}, {0x398,0x3B8}, 
+  {0x399,0x3B9}, {0x39A,0x3BA}, {0x39B,0x3BB}, {0x39C,0x3BC}, {0x39D,0x3BD}, 
+  {0x39E,0x3BE}, {0x39F,0x3BF}, {0x3A0,0x3C0}, {0x3A1,0x3C1}, {0x3A3,0x3C3}, 
+  {0x3A4,0x3C4}, {0x3A5,0x3C5}, {0x3A6,0x3C6}, {0x3A7,0x3C7}, {0x3A8,0x3C8}, 
+  {0x3A9,0x3C9}, {0x3AA,0x3CA}, {0x3AB,0x3CB}, {0x3E2,0x3E3}, {0x3E4,0x3E5}, 
+  {0x3E6,0x3E7}, {0x3E8,0x3E9}, {0x3EA,0x3EB}, {0x3EC,0x3ED}, {0x3EE,0x3EF}, 
+  {0x401,0x451}, {0x402,0x452}, {0x403,0x453}, {0x404,0x454}, {0x405,0x455}, 
+  {0x406,0x456}, {0x407,0x457}, {0x408,0x458}, {0x409,0x459}, {0x40A,0x45A}, 
+  {0x40B,0x45B}, {0x40C,0x45C}, {0x40E,0x45E}, {0x40F,0x45F}, {0x410,0x430}, 
+  {0x411,0x431}, {0x412,0x432}, {0x413,0x433}, {0x414,0x434}, {0x415,0x435}, 
+  {0x416,0x436}, {0x417,0x437}, {0x418,0x438}, {0x419,0x439}, {0x41A,0x43A}, 
+  {0x41B,0x43B}, {0x41C,0x43C}, {0x41D,0x43D}, {0x41E,0x43E}, {0x41F,0x43F}, 
+  {0x420,0x440}, {0x421,0x441}, {0x422,0x442}, {0x423,0x443}, {0x424,0x444}, 
+  {0x425,0x445}, {0x426,0x446}, {0x427,0x447}, {0x428,0x448}, {0x429,0x449}, 
+  {0x42A,0x44A}, {0x42B,0x44B}, {0x42C,0x44C}, {0x42D,0x44D}, {0x42E,0x44E}, 
+  {0x42F,0x44F}, {0x460,0x461}, {0x462,0x463}, {0x464,0x465}, {0x466,0x467}, 
+  {0x468,0x469}, {0x46A,0x46B}, {0x46C,0x46D}, {0x46E,0x46F}, {0x470,0x471}, 
+  {0x472,0x473}, {0x474,0x475}, {0x476,0x477}, {0x478,0x479}, {0x47A,0x47B}, 
+  {0x47C,0x47D}, {0x47E,0x47F}, {0x480,0x481}, {0x490,0x491}, {0x492,0x493}, 
+  {0x494,0x495}, {0x496,0x497}, {0x498,0x499}, {0x49A,0x49B}, {0x49C,0x49D}, 
+  {0x49E,0x49F}, {0x4A0,0x4A1}, {0x4A2,0x4A3}, {0x4A4,0x4A5}, {0x4A6,0x4A7}, 
+  {0x4A8,0x4A9}, {0x4AA,0x4AB}, {0x4AC,0x4AD}, {0x4AE,0x4AF}, {0x4B0,0x4B1}, 
+  {0x4B2,0x4B3}, {0x4B4,0x4B5}, {0x4B6,0x4B7}, {0x4B8,0x4B9}, {0x4BA,0x4BB}, 
+  {0x4BC,0x4BD}, {0x4BE,0x4BF}, {0x4C1,0x4C2}, {0x4C3,0x4C4}, {0x4C7,0x4C8}, 
+  {0x4CB,0x4CC}, {0x4D0,0x4D1}, {0x4D2,0x4D3}, {0x4D4,0x4D5}, {0x4D6,0x4D7}, 
+  {0x4D8,0x4D9}, {0x4DA,0x4DB}, {0x4DC,0x4DD}, {0x4DE,0x4DF}, {0x4E0,0x4E1}, 
+  {0x4E2,0x4E3}, {0x4E4,0x4E5}, {0x4E6,0x4E7}, {0x4E8,0x4E9}, {0x4EA,0x4EB}, 
+  {0x4EE,0x4EF}, {0x4F0,0x4F1}, {0x4F2,0x4F3}, {0x4F4,0x4F5}, {0x4F8,0x4F9}, 
+  {0x531,0x561}, {0x532,0x562}, {0x533,0x563}, {0x534,0x564}, {0x535,0x565}, 
+  {0x536,0x566}, {0x537,0x567}, {0x538,0x568}, {0x539,0x569}, {0x53A,0x56A}, 
+  {0x53B,0x56B}, {0x53C,0x56C}, {0x53D,0x56D}, {0x53E,0x56E}, {0x53F,0x56F}, 
+  {0x540,0x570}, {0x541,0x571}, {0x542,0x572}, {0x543,0x573}, {0x544,0x574}, 
+  {0x545,0x575}, {0x546,0x576}, {0x547,0x577}, {0x548,0x578}, {0x549,0x579}, 
+  {0x54A,0x57A}, {0x54B,0x57B}, {0x54C,0x57C}, {0x54D,0x57D}, {0x54E,0x57E}, 
+  {0x54F,0x57F}, {0x550,0x580}, {0x551,0x581}, {0x552,0x582}, {0x553,0x583}, 
+  {0x554,0x584}, {0x555,0x585}, {0x556,0x586}, {0x10A0,0x10D0}, {0x10A1,0x10D1}, 
+  {0x10A2,0x10D2}, {0x10A3,0x10D3}, {0x10A4,0x10D4}, {0x10A5,0x10D5}, {0x10A6,0x10D6}, 
+  {0x10A7,0x10D7}, {0x10A8,0x10D8}, {0x10A9,0x10D9}, {0x10AA,0x10DA}, {0x10AB,0x10DB}, 
+  {0x10AC,0x10DC}, {0x10AD,0x10DD}, {0x10AE,0x10DE}, {0x10AF,0x10DF}, {0x10B0,0x10E0}, 
+  {0x10B1,0x10E1}, {0x10B2,0x10E2}, {0x10B3,0x10E3}, {0x10B4,0x10E4}, {0x10B5,0x10E5}, 
+  {0x10B6,0x10E6}, {0x10B7,0x10E7}, {0x10B8,0x10E8}, {0x10B9,0x10E9}, {0x10BA,0x10EA}, 
+  {0x10BB,0x10EB}, {0x10BC,0x10EC}, {0x10BD,0x10ED}, {0x10BE,0x10EE}, {0x10BF,0x10EF}, 
+  {0x10C0,0x10F0}, {0x10C1,0x10F1}, {0x10C2,0x10F2}, {0x10C3,0x10F3}, {0x10C4,0x10F4}, 
+  {0x10C5,0x10F5}, {0x1E00,0x1E01}, {0x1E02,0x1E03}, {0x1E04,0x1E05}, {0x1E06,0x1E07}, 
+  {0x1E08,0x1E09}, {0x1E0A,0x1E0B}, {0x1E0C,0x1E0D}, {0x1E0E,0x1E0F}, {0x1E10,0x1E11}, 
+  {0x1E12,0x1E13}, {0x1E14,0x1E15}, {0x1E16,0x1E17}, {0x1E18,0x1E19}, {0x1E1A,0x1E1B}, 
+  {0x1E1C,0x1E1D}, {0x1E1E,0x1E1F}, {0x1E20,0x1E21}, {0x1E22,0x1E23}, {0x1E24,0x1E25}, 
+  {0x1E26,0x1E27}, {0x1E28,0x1E29}, {0x1E2A,0x1E2B}, {0x1E2C,0x1E2D}, {0x1E2E,0x1E2F}, 
+  {0x1E30,0x1E31}, {0x1E32,0x1E33}, {0x1E34,0x1E35}, {0x1E36,0x1E37}, {0x1E38,0x1E39}, 
+  {0x1E3A,0x1E3B}, {0x1E3C,0x1E3D}, {0x1E3E,0x1E3F}, {0x1E40,0x1E41}, {0x1E42,0x1E43}, 
+  {0x1E44,0x1E45}, {0x1E46,0x1E47}, {0x1E48,0x1E49}, {0x1E4A,0x1E4B}, {0x1E4C,0x1E4D}, 
+  {0x1E4E,0x1E4F}, {0x1E50,0x1E51}, {0x1E52,0x1E53}, {0x1E54,0x1E55}, {0x1E56,0x1E57}, 
+  {0x1E58,0x1E59}, {0x1E5A,0x1E5B}, {0x1E5C,0x1E5D}, {0x1E5E,0x1E5F}, {0x1E60,0x1E61}, 
+  {0x1E62,0x1E63}, {0x1E64,0x1E65}, {0x1E66,0x1E67}, {0x1E68,0x1E69}, {0x1E6A,0x1E6B}, 
+  {0x1E6C,0x1E6D}, {0x1E6E,0x1E6F}, {0x1E70,0x1E71}, {0x1E72,0x1E73}, {0x1E74,0x1E75}, 
+  {0x1E76,0x1E77}, {0x1E78,0x1E79}, {0x1E7A,0x1E7B}, {0x1E7C,0x1E7D}, {0x1E7E,0x1E7F}, 
+  {0x1E80,0x1E81}, {0x1E82,0x1E83}, {0x1E84,0x1E85}, {0x1E86,0x1E87}, {0x1E88,0x1E89}, 
+  {0x1E8A,0x1E8B}, {0x1E8C,0x1E8D}, {0x1E8E,0x1E8F}, {0x1E90,0x1E91}, {0x1E92,0x1E93}, 
+  {0x1E94,0x1E95}, {0x1EA0,0x1EA1}, {0x1EA2,0x1EA3}, {0x1EA4,0x1EA5}, {0x1EA6,0x1EA7}, 
+  {0x1EA8,0x1EA9}, {0x1EAA,0x1EAB}, {0x1EAC,0x1EAD}, {0x1EAE,0x1EAF}, {0x1EB0,0x1EB1}, 
+  {0x1EB2,0x1EB3}, {0x1EB4,0x1EB5}, {0x1EB6,0x1EB7}, {0x1EB8,0x1EB9}, {0x1EBA,0x1EBB}, 
+  {0x1EBC,0x1EBD}, {0x1EBE,0x1EBF}, {0x1EC0,0x1EC1}, {0x1EC2,0x1EC3}, {0x1EC4,0x1EC5}, 
+  {0x1EC6,0x1EC7}, {0x1EC8,0x1EC9}, {0x1ECA,0x1ECB}, {0x1ECC,0x1ECD}, {0x1ECE,0x1ECF}, 
+  {0x1ED0,0x1ED1}, {0x1ED2,0x1ED3}, {0x1ED4,0x1ED5}, {0x1ED6,0x1ED7}, {0x1ED8,0x1ED9}, 
+  {0x1EDA,0x1EDB}, {0x1EDC,0x1EDD}, {0x1EDE,0x1EDF}, {0x1EE0,0x1EE1}, {0x1EE2,0x1EE3}, 
+  {0x1EE4,0x1EE5}, {0x1EE6,0x1EE7}, {0x1EE8,0x1EE9}, {0x1EEA,0x1EEB}, {0x1EEC,0x1EED}, 
+  {0x1EEE,0x1EEF}, {0x1EF0,0x1EF1}, {0x1EF2,0x1EF3}, {0x1EF4,0x1EF5}, {0x1EF6,0x1EF7}, 
+  {0x1EF8,0x1EF9}, {0x1F08,0x1F00}, {0x1F09,0x1F01}, {0x1F0A,0x1F02}, {0x1F0B,0x1F03}, 
+  {0x1F0C,0x1F04}, {0x1F0D,0x1F05}, {0x1F0E,0x1F06}, {0x1F0F,0x1F07}, {0x1F18,0x1F10}, 
+  {0x1F19,0x1F11}, {0x1F1A,0x1F12}, {0x1F1B,0x1F13}, {0x1F1C,0x1F14}, {0x1F1D,0x1F15}, 
+  {0x1F28,0x1F20}, {0x1F29,0x1F21}, {0x1F2A,0x1F22}, {0x1F2B,0x1F23}, {0x1F2C,0x1F24}, 
+  {0x1F2D,0x1F25}, {0x1F2E,0x1F26}, {0x1F2F,0x1F27}, {0x1F38,0x1F30}, {0x1F39,0x1F31}, 
+  {0x1F3A,0x1F32}, {0x1F3B,0x1F33}, {0x1F3C,0x1F34}, {0x1F3D,0x1F35}, {0x1F3E,0x1F36}, 
+  {0x1F3F,0x1F37}, {0x1F48,0x1F40}, {0x1F49,0x1F41}, {0x1F4A,0x1F42}, {0x1F4B,0x1F43}, 
+  {0x1F4C,0x1F44}, {0x1F4D,0x1F45}, {0x1F59,0x1F51}, {0x1F5B,0x1F53}, {0x1F5D,0x1F55}, 
+  {0x1F5F,0x1F57}, {0x1F68,0x1F60}, {0x1F69,0x1F61}, {0x1F6A,0x1F62}, {0x1F6B,0x1F63}, 
+  {0x1F6C,0x1F64}, {0x1F6D,0x1F65}, {0x1F6E,0x1F66}, {0x1F6F,0x1F67}, {0x1F88,0x1F80}, 
+  {0x1F89,0x1F81}, {0x1F8A,0x1F82}, {0x1F8B,0x1F83}, {0x1F8C,0x1F84}, {0x1F8D,0x1F85}, 
+  {0x1F8E,0x1F86}, {0x1F8F,0x1F87}, {0x1F98,0x1F90}, {0x1F99,0x1F91}, {0x1F9A,0x1F92}, 
+  {0x1F9B,0x1F93}, {0x1F9C,0x1F94}, {0x1F9D,0x1F95}, {0x1F9E,0x1F96}, {0x1F9F,0x1F97}, 
+  {0x1FA8,0x1FA0}, {0x1FA9,0x1FA1}, {0x1FAA,0x1FA2}, {0x1FAB,0x1FA3}, {0x1FAC,0x1FA4}, 
+  {0x1FAD,0x1FA5}, {0x1FAE,0x1FA6}, {0x1FAF,0x1FA7}, {0x1FB8,0x1FB0}, {0x1FB9,0x1FB1}, 
+  {0x1FBA,0x1F70}, {0x1FBB,0x1F71}, {0x1FBC,0x1FB3}, {0x1FC8,0x1F72}, {0x1FC9,0x1F73}, 
+  {0x1FCA,0x1F74}, {0x1FCB,0x1F75}, {0x1FCC,0x1FC3}, {0x1FD8,0x1FD0}, {0x1FD9,0x1FD1}, 
+  {0x1FDA,0x1F76}, {0x1FDB,0x1F77}, {0x1FE8,0x1FE0}, {0x1FE9,0x1FE1}, {0x1FEA,0x1F7A}, 
+  {0x1FEB,0x1F7B}, {0x1FEC,0x1FE5}, {0x1FF8,0x1F78}, {0x1FF9,0x1F79}, {0x1FFA,0x1F7C}, 
+  {0x1FFB,0x1F7D}, {0x1FFC,0x1FF3}, {0x2160,0x2170}, {0x2161,0x2171}, {0x2162,0x2172}, 
+  {0x2163,0x2173}, {0x2164,0x2174}, {0x2165,0x2175}, {0x2166,0x2176}, {0x2167,0x2177}, 
+  {0x2168,0x2178}, {0x2169,0x2179}, {0x216A,0x217A}, {0x216B,0x217B}, {0x216C,0x217C}, 
+  {0x216D,0x217D}, {0x216E,0x217E}, {0x216F,0x217F}, {0x24B6,0x24D0}, {0x24B7,0x24D1}, 
+  {0x24B8,0x24D2}, {0x24B9,0x24D3}, {0x24BA,0x24D4}, {0x24BB,0x24D5}, {0x24BC,0x24D6}, 
+  {0x24BD,0x24D7}, {0x24BE,0x24D8}, {0x24BF,0x24D9}, {0x24C0,0x24DA}, {0x24C1,0x24DB}, 
+  {0x24C2,0x24DC}, {0x24C3,0x24DD}, {0x24C4,0x24DE}, {0x24C5,0x24DF}, {0x24C6,0x24E0}, 
+  {0x24C7,0x24E1}, {0x24C8,0x24E2}, {0x24C9,0x24E3}, {0x24CA,0x24E4}, {0x24CB,0x24E5}, 
+  {0x24CC,0x24E6}, {0x24CD,0x24E7}, {0x24CE,0x24E8}, {0x24CF,0x24E9}, {0xFF21,0xFF41}, 
+  {0xFF22,0xFF42}, {0xFF23,0xFF43}, {0xFF24,0xFF44}, {0xFF25,0xFF45}, {0xFF26,0xFF46}, 
+  {0xFF27,0xFF47}, {0xFF28,0xFF48}, {0xFF29,0xFF49}, {0xFF2A,0xFF4A}, {0xFF2B,0xFF4B}, 
+  {0xFF2C,0xFF4C}, {0xFF2D,0xFF4D}, {0xFF2E,0xFF4E}, {0xFF2F,0xFF4F}, {0xFF30,0xFF50}, 
+  {0xFF31,0xFF51}, {0xFF32,0xFF52}, {0xFF33,0xFF53}, {0xFF34,0xFF54}, {0xFF35,0xFF55}, 
+  {0xFF36,0xFF56}, {0xFF37,0xFF57}, {0xFF38,0xFF58}, {0xFF39,0xFF59}, {0xFF3A,0xFF5A}
+};
+
+
+
+#define NUM_TOSIMPLIFIED_INFO 2145
+static const unirelate tosimplified_info[NUM_TOSIMPLIFIED_INFO] = {
+  {0x4E48,0x9EBD}, {0x4E82,0x4E71}, {0x4E9E,0x4E9A}, {0x4F47,0x4F2B}, {0x4F59,0x9980}, 
+  {0x4F86,0x6765}, {0x4F96,0x4ED1}, {0x4FE0,0x4FA0}, {0x5000,0x4F25}, {0x5006,0x4FE9}, 
+  {0x5009,0x4ED3}, {0x500B,0x4E2A}, {0x5011,0x4EEC}, {0x502B,0x4F26}, {0x5049,0x4F1F}, 
+  {0x5074,0x4FA7}, {0x5075,0x4FA6}, {0x5096,0x4F27}, {0x5098,0x4F1E}, {0x5099,0x5907}, 
+  {0x50AD,0x4F63}, {0x50B3,0x4F20}, {0x50B4,0x4F1B}, {0x50B5,0x503A}, {0x50B7,0x4F24}, 
+  {0x50BE,0x503E}, {0x50C2,0x507B}, {0x50C5,0x4EC5}, {0x50C9,0x4F65}, {0x50D1,0x4FA8}, 
+  {0x50D5,0x4EC6}, {0x50DE,0x4F2A}, {0x50E5,0x4FA5}, {0x50E8,0x507E}, {0x50F9,0x4EF7}, 
+  {0x5100,0x4EEA}, {0x5102,0x4FAC}, {0x5104,0x4EBF}, {0x5108,0x4FA9}, {0x5109,0x4FED}, 
+  {0x5110,0x50A7}, {0x5114,0x4FE6}, {0x5115,0x4FAA}, {0x511F,0x507F}, {0x512A,0x4F18}, 
+  {0x5132,0x50A8}, {0x5137,0x4FEA}, {0x513A,0x50A9}, {0x513B,0x50A5}, {0x513C,0x4FE8}, 
+  {0x5152,0x513F}, {0x5169,0x4E24}, {0x51C8,0x51C0}, {0x51CD,0x51BB}, {0x51F1,0x51EF}, 
+  {0x5244,0x522D}, {0x5247,0x5219}, {0x525B,0x521A}, {0x526E,0x5250}, {0x5274,0x5240}, 
+  {0x5275,0x521B}, {0x5283,0x5212}, {0x5287,0x5267}, {0x5289,0x5218}, {0x528A,0x523D}, 
+  {0x528C,0x523F}, {0x528D,0x5251}, {0x5291,0x5242}, {0x52C1,0x52B2}, {0x52D5,0x52A8}, 
+  {0x52D9,0x52A1}, {0x52DB,0x52CB}, {0x52DD,0x80DC}, {0x52DE,0x52B3}, {0x52E2,0x52BF}, 
+  {0x52F1,0x52A2}, {0x52F5,0x52B1}, {0x52F8,0x529D}, {0x532D,0x5326}, {0x532F,0x6C47}, 
+  {0x5331,0x532E}, {0x5340,0x533A}, {0x5354,0x534F}, {0x5399,0x538D}, {0x53A0,0x5395}, 
+  {0x53AD,0x538C}, {0x53B2,0x5389}, {0x53B4,0x53A3}, {0x53C3,0x53C2}, {0x53E2,0x4E1B}, 
+  {0x540E,0x5F8C}, {0x54BC,0x5459}, {0x54E1,0x5458}, {0x5504,0x5457}, {0x554F,0x95EE}, 
+  {0x5553,0x542F}, {0x555E,0x54D1}, {0x559A,0x5524}, {0x55AA,0x4E27}, {0x55AC,0x4E54}, 
+  {0x55AE,0x5355}, {0x55B2,0x54DF}, {0x55C6,0x545B}, {0x55C7,0x556C}, {0x55CE,0x5417}, 
+  {0x55DA,0x545C}, {0x55E9,0x5522}, {0x55F6,0x54D4}, {0x5606,0x53F9}, {0x560D,0x55BD}, 
+  {0x5614,0x5455}, {0x5616,0x5567}, {0x5617,0x5C1D}, {0x561C,0x551B}, {0x5629,0x54D7}, 
+  {0x562E,0x5520}, {0x562F,0x5578}, {0x5630,0x53FD}, {0x5635,0x54D3}, {0x5638,0x5452}, 
+  {0x565D,0x549D}, {0x5660,0x54D2}, {0x5665,0x54DD}, {0x5666,0x54D5}, {0x566F,0x55F3}, 
+  {0x5672,0x54D9}, {0x5674,0x55B7}, {0x5678,0x5428}, {0x5680,0x549B}, {0x5687,0x5413}, 
+  {0x568C,0x54DC}, {0x5695,0x565C}, {0x5699,0x556E}, {0x56A6,0x5456}, {0x56A8,0x5499}, 
+  {0x56B3,0x55BE}, {0x56B4,0x4E25}, {0x56B6,0x5624}, {0x56C0,0x556D}, {0x56C1,0x55EB}, 
+  {0x56C2,0x56A3}, {0x56C5,0x5181}, {0x56C8,0x5453}, {0x56D1,0x5631}, {0x5707,0x56F5}, 
+  {0x570B,0x56FD}, {0x570D,0x56F4}, {0x5712,0x56ED}, {0x5713,0x5706}, {0x5716,0x56FE}, 
+  {0x5718,0x56E2}, {0x57E1,0x57AD}, {0x57F7,0x6267}, {0x5805,0x575A}, {0x580A,0x57A9}, 
+  {0x5816,0x57B4}, {0x581D,0x57DA}, {0x582F,0x5C27}, {0x5831,0x62A5}, {0x5834,0x573A}, 
+  {0x584A,0x5757}, {0x584B,0x8314}, {0x584F,0x57B2}, {0x5852,0x57D8}, {0x5857,0x6D82}, 
+  {0x5862,0x575E}, {0x5864,0x57D9}, {0x5875,0x5C18}, {0x5879,0x5811}, {0x588A,0x57AB}, 
+  {0x589C,0x5760}, {0x58AE,0x5815}, {0x58B3,0x575F}, {0x58BB,0x5899}, {0x58BE,0x57A6}, 
+  {0x58C7,0x575B}, {0x58D3,0x538B}, {0x58D8,0x5792}, {0x58D9,0x5739}, {0x58DA,0x5786}, 
+  {0x58DE,0x574F}, {0x58DF,0x5784}, {0x58E0,0x5785}, {0x58E2,0x575C}, {0x58E9,0x575D}, 
+  {0x58EF,0x58EE}, {0x58FA,0x58F6}, {0x58FD,0x5BFF}, {0x5922,0x68A6}, {0x593E,0x5939}, 
+  {0x5950,0x5942}, {0x5969,0x5941}, {0x596A,0x593A}, {0x596C,0x5956}, {0x596E,0x594B}, 
+  {0x599D,0x5986}, {0x5A41,0x5A04}, {0x5A66,0x5987}, {0x5A6D,0x5A05}, {0x5AA7,0x5A32}, 
+  {0x5ABD,0x5988}, {0x5AD7,0x59AA}, {0x5AF5,0x59A9}, {0x5AFB,0x5A34}, {0x5B00,0x59AB}, 
+  {0x5B08,0x5A06}, {0x5B0B,0x5A75}, {0x5B0C,0x5A07}, {0x5B19,0x5AF1}, {0x5B21,0x5AD2}, 
+  {0x5B2A,0x5AD4}, {0x5B30,0x5A74}, {0x5B38,0x5A76}, {0x5B4C,0x5A08}, {0x5B6B,0x5B59}, 
+  {0x5B78,0x5B66}, {0x5B7F,0x5B6A}, {0x5BE2,0x5BDD}, {0x5BE6,0x5B9E}, {0x5BE7,0x5B81}, 
+  {0x5BE9,0x5BA1}, {0x5BEB,0x5199}, {0x5BEC,0x5BBD}, {0x5BF5,0x5BA0}, {0x5BF6,0x5B9D}, 
+  {0x5C07,0x5C06}, {0x5C08,0x4E13}, {0x5C0B,0x5BFB}, {0x5C0D,0x5BF9}, {0x5C0E,0x5BFC}, 
+  {0x5C37,0x5C34}, {0x5C62,0x5C61}, {0x5C64,0x5C42}, {0x5C68,0x5C66}, {0x5C6C,0x5C5E}, 
+  {0x5CA1,0x5188}, {0x5CF4,0x5C98}, {0x5CF6,0x5C9B}, {0x5CFD,0x5CE1}, {0x5D0D,0x5D03}, 
+  {0x5D17,0x5C97}, {0x5D22,0x5CE5}, {0x5D2C,0x5CBD}, {0x5D50,0x5C9A}, {0x5D81,0x5D5D}, 
+  {0x5D84,0x5D2D}, {0x5D87,0x5C96}, {0x5D97,0x5D02}, {0x5DA0,0x5CE4}, {0x5DA7,0x5CC4}, 
+  {0x5DB8,0x5D58}, {0x5DBA,0x5CAD}, {0x5DBC,0x5C7F}, {0x5DCB,0x5CBF}, {0x5DD2,0x5CE6}, 
+  {0x5DD4,0x5DC5}, {0x5DF0,0x5DEF}, {0x5E25,0x5E05}, {0x5E2B,0x5E08}, {0x5E33,0x5E10}, 
+  {0x5E36,0x5E26}, {0x5E40,0x5E27}, {0x5E43,0x5E0F}, {0x5E57,0x5E3C}, {0x5E58,0x5E3B}, 
+  {0x5E5F,0x5E1C}, {0x5E63,0x5E01}, {0x5E6B,0x5E2E}, {0x5E6C,0x5E31}, {0x5E79,0x5E72}, 
+  {0x5E7E,0x51E0}, {0x5EAB,0x5E93}, {0x5EDF,0x5E99}, {0x5EE0,0x5382}, {0x5EE1,0x5E91}, 
+  {0x5EE2,0x5E9F}, {0x5EE3,0x5E7F}, {0x5EEC,0x5E90}, {0x5EF3,0x5385}, {0x5F33,0x5F2A}, 
+  {0x5F35,0x5F20}, {0x5F48,0x5F39}, {0x5F4C,0x5F25}, {0x5F4E,0x5F2F}, {0x5F8C,0x540E}, 
+  {0x5F91,0x5F84}, {0x5F9E,0x4ECE}, {0x5FA0,0x5F95}, {0x5FA9,0x590D}, {0x5FB9,0x5F7B}, 
+  {0x60B5,0x6005}, {0x60B6,0x95F7}, {0x60E1,0x6076}, {0x60F1,0x607C}, {0x60F2,0x607D}, 
+  {0x60FB,0x607B}, {0x611B,0x7231}, {0x611C,0x60EC}, {0x6134,0x6006}, {0x6137,0x607A}, 
+  {0x613E,0x5FFE}, {0x614B,0x6001}, {0x6158,0x60E8}, {0x615A,0x60ED}, {0x615F,0x6078}, 
+  {0x6163,0x60EF}, {0x6164,0x60AB}, {0x616A,0x6004}, {0x616B,0x6002}, {0x616E,0x8651}, 
+  {0x6173,0x60AD}, {0x6176,0x5E86}, {0x6182,0x5FE7}, {0x618A,0x60EB}, {0x6190,0x601C}, 
+  {0x6191,0x51ED}, {0x6192,0x6126}, {0x619A,0x60EE}, {0x61A4,0x6124}, {0x61AB,0x60AF}, 
+  {0x61AE,0x6003}, {0x61B2,0x5BAA}, {0x61B6,0x5FC6}, {0x61C7,0x6073}, {0x61C9,0x5E94}, 
+  {0x61CC,0x603F}, {0x61DF,0x603C}, {0x61E3,0x61D1}, {0x61E8,0x6079}, {0x61F2,0x60E9}, 
+  {0x61F6,0x61D2}, {0x61F7,0x6000}, {0x61F8,0x60AC}, {0x61FA,0x5FCF}, {0x61FC,0x60E7}, 
+  {0x61FE,0x6151}, {0x6200,0x604B}, {0x6207,0x6206}, {0x6214,0x620B}, {0x6227,0x6217}, 
+  {0x6229,0x622C}, {0x6230,0x6218}, {0x6232,0x620F}, {0x633E,0x631F}, {0x6368,0x820D}, 
+  {0x636B,0x626A}, {0x6383,0x626B}, {0x6384,0x62A1}, {0x6399,0x6323}, {0x63C0,0x62E3}, 
+  {0x63DA,0x626C}, {0x63DB,0x6362}, {0x63EE,0x6325}, {0x640D,0x635F}, {0x6417,0x6363}, 
+  {0x6436,0x62A2}, {0x6451,0x63B4}, {0x645C,0x63BC}, {0x645F,0x6402}, {0x646F,0x631A}, 
+  {0x6473,0x62A0}, {0x6476,0x629F}, {0x647B,0x63BA}, {0x6488,0x635E}, {0x6493,0x6320}, 
+  {0x649F,0x6322}, {0x64A3,0x63B8}, {0x64A5,0x62E8}, {0x64AB,0x629A}, {0x64B2,0x6251}, 
+  {0x64B3,0x63FF}, {0x64BB,0x631E}, {0x64BE,0x631D}, {0x64BF,0x6361}, {0x64C1,0x62E5}, 
+  {0x64C4,0x63B3}, {0x64C7,0x62E9}, {0x64CA,0x51FB}, {0x64CB,0x6321}, {0x64D4,0x62C5}, 
+  {0x64DA,0x636E}, {0x64E0,0x6324}, {0x64EC,0x62DF}, {0x64EF,0x6448}, {0x64F0,0x62E7}, 
+  {0x64F1,0x6401}, {0x64F2,0x63B7}, {0x64F4,0x6269}, {0x64F7,0x64B7}, {0x64FA,0x6446}, 
+  {0x64FB,0x64DE}, {0x64FC,0x64B8}, {0x64FE,0x6270}, {0x6504,0x6445}, {0x6506,0x64B5}, 
+  {0x650F,0x62E2}, {0x6514,0x62E6}, {0x6516,0x6484}, {0x6519,0x6400}, {0x651B,0x64BA}, 
+  {0x651D,0x6444}, {0x6522,0x6512}, {0x6523,0x631B}, {0x6524,0x644A}, {0x652A,0x6405}, 
+  {0x652C,0x63FD}, {0x6557,0x8D25}, {0x6575,0x654C}, {0x6578,0x6570}, {0x6582,0x655B}, 
+  {0x6583,0x6BD9}, {0x6595,0x6593}, {0x65AC,0x65A9}, {0x65B7,0x65AD}, {0x6642,0x65F6}, 
+  {0x6649,0x664B}, {0x665D,0x663C}, {0x6688,0x6655}, {0x6689,0x6656}, {0x66A2,0x7545}, 
+  {0x66AB,0x6682}, {0x66C4,0x6654}, {0x66C7,0x6619}, {0x66C9,0x6653}, {0x66D6,0x66A7}, 
+  {0x66E0,0x65F7}, {0x66EC,0x6652}, {0x66F8,0x4E66}, {0x6703,0x4F1A}, {0x6727,0x80E7}, 
+  {0x6771,0x4E1C}, {0x6898,0x67A7}, {0x689D,0x6761}, {0x689F,0x67AD}, {0x68C4,0x5F03}, 
+  {0x68D6,0x67A8}, {0x68D7,0x67A3}, {0x68DF,0x680B}, {0x68E7,0x6808}, {0x68F2,0x6816}, 
+  {0x690F,0x6860}, {0x694A,0x6768}, {0x6953,0x67AB}, {0x6968,0x6862}, {0x696D,0x4E1A}, 
+  {0x6975,0x6781}, {0x69AA,0x6769}, {0x69AE,0x8363}, {0x69BF,0x6864}, {0x69CB,0x6784}, 
+  {0x69CD,0x67AA}, {0x69E7,0x6920}, {0x69F3,0x6868}, {0x6A01,0x6869}, {0x6A02,0x4E50}, 
+  {0x6A05,0x679E}, {0x6A13,0x697C}, {0x6A19,0x6807}, {0x6A1E,0x67A2}, {0x6A23,0x6837}, 
+  {0x6A38,0x6734}, {0x6A39,0x6811}, {0x6A3A,0x6866}, {0x6A48,0x6861}, {0x6A4B,0x6865}, 
+  {0x6A5F,0x673A}, {0x6A62,0x692D}, {0x6A89,0x67FD}, {0x6A94,0x6863}, {0x6A9C,0x6867}, 
+  {0x6AA2,0x68C0}, {0x6AA3,0x6A2F}, {0x6AB3,0x69DF}, {0x6AB8,0x67E0}, {0x6ABB,0x69DB}, 
+  {0x6AC3,0x67DC}, {0x6AD3,0x6A79}, {0x6ADA,0x6988}, {0x6ADB,0x6809}, {0x6ADD,0x691F}, 
+  {0x6ADE,0x6A7C}, {0x6ADF,0x680E}, {0x6AE7,0x69E0}, {0x6AE8,0x680C}, {0x6AEA,0x67A5}, 
+  {0x6AEC,0x6987}, {0x6AF3,0x680A}, {0x6AF8,0x6989}, {0x6AFB,0x6A31}, {0x6B04,0x680F}, 
+  {0x6B0A,0x6743}, {0x6B0F,0x6924}, {0x6B12,0x683E}, {0x6B16,0x6984}, {0x6B1E,0x68C2}, 
+  {0x6B3D,0x94A6}, {0x6B50,0x6B27}, {0x6B5F,0x6B24}, {0x6B61,0x6B22}, {0x6B72,0x5C81}, 
+  {0x6B77,0x5386}, {0x6B78,0x5F52}, {0x6B98,0x6B8B}, {0x6B9E,0x6B92}, {0x6BA4,0x6B87}, 
+  {0x6BAB,0x6B9A}, {0x6BAE,0x6B93}, {0x6BAF,0x6BA1}, {0x6BB2,0x6B7C}, {0x6BBA,0x6740}, 
+  {0x6BBB,0x58F3}, {0x6BC6,0x6BB4}, {0x6BFF,0x6BF5}, {0x6C08,0x6BE1}, {0x6C0C,0x6C07}, 
+  {0x6C23,0x6C14}, {0x6C2B,0x6C22}, {0x6C2C,0x6C29}, {0x6D79,0x6D43}, {0x6D87,0x6CFE}, 
+  {0x6DEA,0x6CA6}, {0x6DF5,0x6E0A}, {0x6DF6,0x6D9E}, {0x6DFA,0x6D45}, {0x6E19,0x6DA3}, 
+  {0x6E26,0x6DA1}, {0x6E2C,0x6D4B}, {0x6E3E,0x6D51}, {0x6E5E,0x6D48}, {0x6E6F,0x6C64}, 
+  {0x6E96,0x51C6}, {0x6E9D,0x6C9F}, {0x6EC4,0x6CA7}, {0x6EC5,0x706D}, {0x6ECC,0x6DA4}, 
+  {0x6ECE,0x8365}, {0x6EEC,0x6CAA}, {0x6EEF,0x6EDE}, {0x6EF2,0x6E17}, {0x6EF7,0x5364}, 
+  {0x6EF8,0x6D52}, {0x6EFF,0x6EE1}, {0x6F01,0x6E14}, {0x6F1A,0x6CA4}, {0x6F22,0x6C49}, 
+  {0x6F23,0x6D9F}, {0x6F2C,0x6E0D}, {0x6F32,0x6DA8}, {0x6F38,0x6E10}, {0x6F3F,0x6D46}, 
+  {0x6F41,0x988D}, {0x6F51,0x6CFC}, {0x6F54,0x6D01}, {0x6F59,0x6CA9}, {0x6F5B,0x6F5C}, 
+  {0x6F64,0x6DA6}, {0x6F6F,0x6D54}, {0x6F70,0x6E83}, {0x6F77,0x6ED7}, {0x6F7F,0x6DA0}, 
+  {0x6F80,0x6DA9}, {0x6F86,0x6D47}, {0x6F87,0x6D9D}, {0x6F97,0x6DA7}, {0x6FA0,0x6E11}, 
+  {0x6FA4,0x6CFD}, {0x6FA9,0x6CF6}, {0x6FAE,0x6D4D}, {0x6FB1,0x6DC0}, {0x6FC1,0x6D4A}, 
+  {0x6FC3,0x6D53}, {0x6FD5,0x6E7F}, {0x6FD8,0x6CDE}, {0x6FDF,0x6D4E}, {0x6FE4,0x6D9B}, 
+  {0x6FEB,0x6EE5}, {0x6FF0,0x6F4D}, {0x6FF1,0x6EE8}, {0x6FFA,0x6E85}, {0x6FFC,0x6CFA}, 
+  {0x6FFE,0x6EE4}, {0x7005,0x6EE2}, {0x7006,0x6E0E}, {0x7009,0x6CFB}, {0x700B,0x6E16}, 
+  {0x700F,0x6D4F}, {0x7015,0x6FD2}, {0x7018,0x6CF8}, {0x701D,0x6CA5}, {0x701F,0x6F47}, 
+  {0x7020,0x6F46}, {0x7027,0x6CF7}, {0x7028,0x6FD1}, {0x7032,0x6F4B}, {0x703E,0x6F9C}, 
+  {0x7043,0x6CA3}, {0x7044,0x6EE0}, {0x7051,0x6D12}, {0x7055,0x6F13}, {0x7058,0x6EE9}, 
+  {0x705D,0x704F}, {0x7063,0x6E7E}, {0x7064,0x6EE6}, {0x7067,0x6EDF}, {0x70CF,0x4E4C}, 
+  {0x70F4,0x70C3}, {0x7121,0x65E0}, {0x7149,0x70BC}, {0x7152,0x709C}, {0x7162,0x8315}, 
+  {0x7165,0x7115}, {0x7169,0x70E6}, {0x716C,0x7080}, {0x7192,0x8367}, {0x7197,0x709D}, 
+  {0x71B1,0x70ED}, {0x71BE,0x70BD}, {0x71C1,0x70E8}, {0x71C8,0x706F}, {0x71D2,0x70E7}, 
+  {0x71D9,0x70EB}, {0x71DC,0x7116}, {0x71DF,0x8425}, {0x71E6,0x707F}, {0x71ED,0x70DB}, 
+  {0x71F4,0x70E9}, {0x71FC,0x70EC}, {0x71FE,0x7118}, {0x720D,0x70C1}, {0x7210,0x7089}, 
+  {0x721B,0x70C2}, {0x722D,0x4E89}, {0x7232,0x4E3A}, {0x723A,0x7237}, {0x723E,0x5C14}, 
+  {0x7258,0x724D}, {0x727D,0x7275}, {0x7296,0x8366}, {0x72A2,0x728A}, {0x72A7,0x727A}, 
+  {0x72C0,0x72B6}, {0x72F9,0x72ED}, {0x72FD,0x72C8}, {0x7319,0x72F0}, {0x7336,0x72B9}, 
+  {0x733B,0x72F2}, {0x7341,0x72B8}, {0x7344,0x72F1}, {0x7345,0x72EE}, {0x7368,0x72EC}, 
+  {0x736A,0x72EF}, {0x736B,0x7303}, {0x7370,0x72DE}, {0x7372,0x83B7}, {0x7375,0x730E}, 
+  {0x7377,0x72B7}, {0x7378,0x517D}, {0x737A,0x736D}, {0x737B,0x732E}, {0x737C,0x7315}, 
+  {0x7380,0x7321}, {0x73FE,0x73B0}, {0x743F,0x73F2}, {0x744B,0x73AE}, {0x7463,0x7410}, 
+  {0x7469,0x83B9}, {0x746A,0x739B}, {0x7489,0x740F}, {0x74A3,0x7391}, {0x74A6,0x7477}, 
+  {0x74B0,0x73AF}, {0x74BD,0x73BA}, {0x74CA,0x743C}, {0x74CF,0x73D1}, {0x74D4,0x748E}, 
+  {0x74DA,0x74D2}, {0x750C,0x74EF}, {0x7523,0x4EA7}, {0x755D,0x4EA9}, {0x7562,0x6BD5}, 
+  {0x756B,0x753B}, {0x7576,0x5F53}, {0x7587,0x7574}, {0x758A,0x53E0}, {0x75D9,0x75C9}, 
+  {0x7602,0x75D6}, {0x760B,0x75AF}, {0x760D,0x75A1}, {0x7613,0x75EA}, {0x761E,0x7617}, 
+  {0x7621,0x75AE}, {0x7627,0x759F}, {0x763B,0x7618}, {0x7642,0x7597}, {0x7646,0x75E8}, 
+  {0x7647,0x75EB}, {0x7649,0x7605}, {0x7658,0x75A0}, {0x765F,0x762A}, {0x7662,0x75D2}, 
+  {0x7664,0x7596}, {0x7665,0x75C7}, {0x7667,0x75AC}, {0x7669,0x765E}, {0x766C,0x7663}, 
+  {0x766D,0x763F}, {0x766E,0x763E}, {0x7670,0x75C8}, {0x7671,0x762B}, {0x7672,0x766B}, 
+  {0x767C,0x53D1}, {0x769A,0x7691}, {0x76B8,0x76B2}, {0x76BA,0x76B1}, {0x76DE,0x76CF}, 
+  {0x76E1,0x5C3D}, {0x76E3,0x76D1}, {0x76E4,0x76D8}, {0x76E7,0x5362}, {0x775C,0x7741}, 
+  {0x775E,0x7750}, {0x7798,0x770D}, {0x779E,0x7792}, {0x77BC,0x7751}, {0x77DA,0x77A9}, 
+  {0x77EF,0x77EB}, {0x7864,0x7856}, {0x7868,0x7817}, {0x786F,0x781A}, {0x78A9,0x7855}, 
+  {0x78AD,0x7800}, {0x78B8,0x781C}, {0x78BA,0x786E}, {0x78BC,0x7801}, {0x78DA,0x7816}, 
+  {0x78E3,0x789C}, {0x78E7,0x789B}, {0x78EF,0x77F6}, {0x78FD,0x7857}, {0x790E,0x7840}, 
+  {0x7919,0x788D}, {0x7926,0x77FF}, {0x792A,0x783A}, {0x792B,0x783E}, {0x792C,0x77FE}, 
+  {0x7931,0x783B}, {0x798D,0x7978}, {0x798E,0x796F}, {0x79AA,0x7985}, {0x79AE,0x793C}, 
+  {0x79B0,0x7962}, {0x79B1,0x7977}, {0x7A2E,0x79CD}, {0x7A31,0x79F0}, {0x7A4C,0x7A23}, 
+  {0x7A4D,0x79EF}, {0x7A4E,0x9896}, {0x7A61,0x7A51}, {0x7A62,0x79FD}, {0x7A69,0x7A33}, 
+  {0x7AA9,0x7A9D}, {0x7AAA,0x6D3C}, {0x7AAE,0x7A77}, {0x7AB6,0x7AAD}, {0x7ABA,0x7AA5}, 
+  {0x7AC4,0x7A9C}, {0x7AC5,0x7A8D}, {0x7AC7,0x7AA6}, {0x7AC8,0x7076}, {0x7ACA,0x7A83}, 
+  {0x7AEA,0x7AD6}, {0x7AF6,0x7ADE}, {0x7B46,0x7B14}, {0x7B67,0x7B15}, {0x7B8B,0x7B3A}, 
+  {0x7B8F,0x7B5D}, {0x7BC0,0x8282}, {0x7BC4,0x8303}, {0x7BC9,0x7B51}, {0x7BCB,0x7BA7}, 
+  {0x7BE4,0x7B03}, {0x7BE9,0x7B5B}, {0x7BF3,0x7B5A}, {0x7C00,0x7BA6}, {0x7C0D,0x7BD3}, 
+  {0x7C1E,0x7BAA}, {0x7C21,0x7B80}, {0x7C23,0x7BD1}, {0x7C2B,0x7BAB}, {0x7C3D,0x7B7E}, 
+  {0x7C3E,0x5E18}, {0x7C43,0x7BEE}, {0x7C4C,0x7B79}, {0x7C5C,0x7BA8}, {0x7C5F,0x7C41}, 
+  {0x7C60,0x7B3C}, {0x7C69,0x7B3E}, {0x7C6A,0x7C16}, {0x7C6C,0x7BF1}, {0x7C6E,0x7BA9}, 
+  {0x7CDD,0x7CC1}, {0x7CDE,0x7CAA}, {0x7CE7,0x7CAE}, {0x7CF2,0x7C9D}, {0x7CF4,0x7C74}, 
+  {0x7CF6,0x7C9C}, {0x7CF9,0x7E9F}, {0x7CFE,0x7EA0}, {0x7D00,0x7EAA}, {0x7D02,0x7EA3}, 
+  {0x7D04,0x7EA6}, {0x7D05,0x7EA2}, {0x7D06,0x7EA1}, {0x7D07,0x7EA5}, {0x7D08,0x7EA8}, 
+  {0x7D09,0x7EAB}, {0x7D0B,0x7EB9}, {0x7D0D,0x7EB3}, {0x7D10,0x7EBD}, {0x7D13,0x7EBE}, 
+  {0x7D14,0x7EAF}, {0x7D15,0x7EB0}, {0x7D17,0x7EB1}, {0x7D19,0x7EB8}, {0x7D1A,0x7EA7}, 
+  {0x7D1B,0x7EB7}, {0x7D1C,0x7EAD}, {0x7D21,0x7EBA}, {0x7D30,0x7EC6}, {0x7D31,0x7EC2}, 
+  {0x7D32,0x7EC1}, {0x7D33,0x7EC5}, {0x7D39,0x7ECD}, {0x7D3A,0x7EC0}, {0x7D3C,0x7ECB}, 
+  {0x7D3F,0x7ED0}, {0x7D40,0x7ECC}, {0x7D42,0x7EC8}, {0x7D44,0x7EC4}, {0x7D46,0x7ECA}, 
+  {0x7D4E,0x7ED7}, {0x7D50,0x7ED3}, {0x7D5D,0x7ED4}, {0x7D5E,0x7EDE}, {0x7D61,0x7EDC}, 
+  {0x7D62,0x7EDA}, {0x7D66,0x7ED9}, {0x7D68,0x7ED2}, {0x7D71,0x7EDF}, {0x7D72,0x4E1D}, 
+  {0x7D73,0x7EDB}, {0x7D76,0x7EDD}, {0x7D79,0x7EE2}, {0x7D81,0x7ED1}, {0x7D83,0x7EE1}, 
+  {0x7D86,0x7EE0}, {0x7D88,0x7EE8}, {0x7D89,0x7EE3}, {0x7D8F,0x7EE5}, {0x7D93,0x7ECF}, 
+  {0x7D9C,0x7EFC}, {0x7D9E,0x7F0D}, {0x7DA2,0x7EF8}, {0x7DA3,0x7EFB}, {0x7DAB,0x7EBF}, 
+  {0x7DAC,0x7EF6}, {0x7DAD,0x7EF4}, {0x7DB0,0x7EFE}, {0x7DB1,0x7EB2}, {0x7DB2,0x7F51}, 
+  {0x7DB3,0x7EF7}, {0x7DB4,0x7F00}, {0x7DB8,0x7EB6}, {0x7DB9,0x7EFA}, {0x7DBA,0x7EEE}, 
+  {0x7DBB,0x7EFD}, {0x7DBD,0x7EF0}, {0x7DBE,0x7EEB}, {0x7DBF,0x7EF5}, {0x7DC4,0x7EF2}, 
+  {0x7DC7,0x7F01}, {0x7DCA,0x7D27}, {0x7DCB,0x7EEF}, {0x7DD1,0x7EFF}, {0x7DD2,0x7EEA}, 
+  {0x7DD4,0x7EF1}, {0x7DD7,0x7F03}, {0x7DD8,0x7F04}, {0x7DD9,0x7F02}, {0x7DDD,0x7F09}, 
+  {0x7DDE,0x7F0E}, {0x7DE0,0x7F14}, {0x7DE1,0x7F17}, {0x7DE3,0x7F18}, {0x7DE6,0x7F0C}, 
+  {0x7DE8,0x7F16}, {0x7DE9,0x7F13}, {0x7DEC,0x7F05}, {0x7DEF,0x7EAC}, {0x7DF1,0x7F11}, 
+  {0x7DF2,0x7F08}, {0x7DF4,0x7EC3}, {0x7DF6,0x7F0F}, {0x7DF9,0x7F07}, {0x7E08,0x8426}, 
+  {0x7E09,0x7F19}, {0x7E0A,0x7F22}, {0x7E0B,0x7F12}, {0x7E10,0x7EC9}, {0x7E11,0x7F23}, 
+  {0x7E1B,0x7F1A}, {0x7E1D,0x7F1C}, {0x7E1E,0x7F1F}, {0x7E1F,0x7F1B}, {0x7E23,0x53BF}, 
+  {0x7E27,0x7EE6}, {0x7E2B,0x7F1D}, {0x7E2D,0x7F21}, {0x7E2E,0x7F29}, {0x7E31,0x7EB5}, 
+  {0x7E32,0x7F27}, {0x7E35,0x7F26}, {0x7E36,0x7D77}, {0x7E37,0x7F15}, {0x7E39,0x7F25}, 
+  {0x7E3D,0x603B}, {0x7E3E,0x7EE9}, {0x7E45,0x7F2B}, {0x7E46,0x7F2A}, {0x7E52,0x7F2F}, 
+  {0x7E54,0x7EC7}, {0x7E55,0x7F2E}, {0x7E5A,0x7F2D}, {0x7E5E,0x7ED5}, {0x7E62,0x7F0B}, 
+  {0x7E69,0x7EF3}, {0x7E6A,0x7ED8}, {0x7E6D,0x8327}, {0x7E6E,0x7F30}, {0x7E6F,0x7F33}, 
+  {0x7E70,0x7F32}, {0x7E73,0x7F34}, {0x7E79,0x7ECE}, {0x7E7C,0x7EE7}, {0x7E7D,0x7F24}, 
+  {0x7E7E,0x7F31}, {0x7E88,0x7F2C}, {0x7E8A,0x7EA9}, {0x7E8C,0x7EED}, {0x7E8F,0x7F20}, 
+  {0x7E93,0x7F28}, {0x7E96,0x7EA4}, {0x7E98,0x7F35}, {0x7E9C,0x7F06}, {0x7F4C,0x7F42}, 
+  {0x7F70,0x7F5A}, {0x7F77,0x7F62}, {0x7F85,0x7F57}, {0x7F86,0x7F74}, {0x7F88,0x7F81}, 
+  {0x7FA5,0x7F9F}, {0x7FA9,0x4E49}, {0x7FD2,0x4E60}, {0x7FF9,0x7FD8}, {0x802C,0x8027}, 
+  {0x802E,0x8022}, {0x8056,0x5723}, {0x805E,0x95FB}, {0x806F,0x8054}, {0x8070,0x806A}, 
+  {0x8072,0x58F0}, {0x8073,0x8038}, {0x8075,0x8069}, {0x8076,0x8042}, {0x8077,0x804C}, 
+  {0x8079,0x804D}, {0x807D,0x542C}, {0x807E,0x804B}, {0x8085,0x8083}, {0x8105,0x80C1}, 
+  {0x811B,0x80EB}, {0x8139,0x80C0}, {0x814E,0x80BE}, {0x8156,0x80E8}, {0x8161,0x8136}, 
+  {0x8166,0x8111}, {0x816B,0x80BF}, {0x8178,0x80A0}, {0x819A,0x80A4}, {0x81A0,0x80F6}, 
+  {0x81A9,0x817B}, {0x81BD,0x80C6}, {0x81BE,0x810D}, {0x81BF,0x8113}, {0x81C9,0x8138}, 
+  {0x81CD,0x8110}, {0x81CF,0x8191}, {0x81D8,0x814A}, {0x81DA,0x80EA}, {0x81DF,0x810F}, 
+  {0x81E0,0x8114}, {0x81E8,0x4E34}, {0x81FA,0x53F0}, {0x8207,0x4E0E}, {0x8208,0x5174}, 
+  {0x8209,0x4E3E}, {0x820A,0x65E7}, {0x8259,0x8231}, {0x8264,0x8223}, {0x8266,0x8230}, 
+  {0x826B,0x823B}, {0x8271,0x8270}, {0x8277,0x8273}, {0x82BB,0x520D}, {0x82E7,0x82CE}, 
+  {0x838A,0x5E84}, {0x8396,0x830E}, {0x83A2,0x835A}, {0x83A7,0x82CB}, {0x83EF,0x534E}, 
+  {0x8407,0x82CC}, {0x840A,0x83B1}, {0x842C,0x4E07}, {0x8435,0x83B4}, {0x8449,0x53F6}, 
+  {0x8452,0x836D}, {0x8464,0x836E}, {0x8466,0x82C7}, {0x8477,0x8364}, {0x8493,0x83BC}, 
+  {0x8494,0x83B3}, {0x84BC,0x82CD}, {0x84C0,0x836A}, {0x84CB,0x76D6}, {0x84EE,0x83B2}, 
+  {0x84EF,0x82C1}, {0x84FD,0x835C}, {0x851E,0x848C}, {0x8523,0x848B}, {0x8526,0x8311}, 
+  {0x852D,0x836B}, {0x8541,0x8368}, {0x8546,0x8487}, {0x854E,0x835E}, {0x8552,0x836C}, 
+  {0x8553,0x82B8}, {0x8555,0x83B8}, {0x8558,0x835B}, {0x8562,0x8489}, {0x8569,0x8361}, 
+  {0x856A,0x829C}, {0x856D,0x8427}, {0x8577,0x84E3}, {0x8588,0x835F}, {0x858A,0x84DF}, 
+  {0x858C,0x8297}, {0x8594,0x8537}, {0x859F,0x83B6}, {0x85A6,0x8350}, {0x85A9,0x8428}, 
+  {0x85BA,0x8360}, {0x85CD,0x84DD}, {0x85CE,0x8369}, {0x85DD,0x827A}, {0x85E5,0x836F}, 
+  {0x85EA,0x85AE}, {0x85F4,0x8574}, {0x85F6,0x82C8}, {0x85F9,0x853C}, {0x85FA,0x853A}, 
+  {0x8604,0x8572}, {0x8606,0x82A6}, {0x8607,0x82CF}, {0x860B,0x82F9}, {0x861A,0x85D3}, 
+  {0x861E,0x8539}, {0x8622,0x830F}, {0x862D,0x5170}, {0x863A,0x84E0}, {0x863F,0x841D}, 
+  {0x8655,0x5904}, {0x865C,0x864F}, {0x865F,0x53F7}, {0x8667,0x4E8F}, {0x86FA,0x86F1}, 
+  {0x8706,0x86AC}, {0x8755,0x8680}, {0x8766,0x867E}, {0x8778,0x8717}, {0x8784,0x86F3}, 
+  {0x879E,0x8682}, {0x87A2,0x8424}, {0x87BB,0x877C}, {0x87C4,0x86F0}, {0x87C8,0x8748}, 
+  {0x87CE,0x87A8}, {0x87E3,0x866E}, {0x87EC,0x8749}, {0x87EF,0x86F2}, {0x87F2,0x866B}, 
+  {0x87F6,0x86CF}, {0x87FB,0x8681}, {0x8805,0x8747}, {0x8806,0x867F}, {0x8810,0x86F4}, 
+  {0x8811,0x877E}, {0x881F,0x8721}, {0x8823,0x86CE}, {0x8831,0x86CA}, {0x8836,0x8695}, 
+  {0x883B,0x86EE}, {0x8846,0x4F17}, {0x8853,0x672F}, {0x885B,0x536B}, {0x885D,0x51B2}, 
+  {0x8879,0x53EA}, {0x88CA,0x8885}, {0x88CF,0x91CC}, {0x88DC,0x8865}, {0x88DD,0x88C5}, 
+  {0x8932,0x88E4}, {0x8933,0x88E2}, {0x8938,0x891B}, {0x893B,0x4EB5}, {0x8947,0x88E5}, 
+  {0x8956,0x8884}, {0x895D,0x88E3}, {0x8960,0x88C6}, {0x8964,0x8934}, {0x896A,0x889C}, 
+  {0x896F,0x886C}, {0x8972,0x88AD}, {0x898B,0x89C1}, {0x898F,0x89C4}, {0x8993,0x89C5}, 
+  {0x8996,0x89C6}, {0x8998,0x89C7}, {0x89A1,0x89CB}, {0x89A6,0x89CE}, {0x89AA,0x4EB2}, 
+  {0x89AC,0x89CA}, {0x89AF,0x89CF}, {0x89B2,0x89D0}, {0x89B7,0x89D1}, {0x89BA,0x89C9}, 
+  {0x89BD,0x89C8}, {0x89BF,0x89CC}, {0x89C0,0x89C2}, {0x89F4,0x89DE}, {0x89F6,0x89EF}, 
+  {0x89F8,0x89E6}, {0x8A01,0x8BA0}, {0x8A02,0x8BA2}, {0x8A03,0x8BA3}, {0x8A08,0x8BA1}, 
+  {0x8A0A,0x8BAF}, {0x8A0C,0x8BA7}, {0x8A0E,0x8BA8}, {0x8A10,0x8BA6}, {0x8A13,0x8BAD}, 
+  {0x8A15,0x8BAA}, {0x8A16,0x8BAB}, {0x8A18,0x8BB0}, {0x8A1B,0x8BB9}, {0x8A1D,0x8BB6}, 
+  {0x8A1F,0x8BBC}, {0x8A23,0x8BC0}, {0x8A25,0x8BB7}, {0x8A2A,0x8BBF}, {0x8A2D,0x8BBE}, 
+  {0x8A31,0x8BB8}, {0x8A34,0x8BC9}, {0x8A36,0x8BC3}, {0x8A3A,0x8BCA}, {0x8A41,0x8BC2}, 
+  {0x8A46,0x8BCB}, {0x8A4E,0x8BB5}, {0x8A50,0x8BC8}, {0x8A52,0x8BD2}, {0x8A54,0x8BCF}, 
+  {0x8A55,0x8BC4}, {0x8A58,0x8BCE}, {0x8A5B,0x8BC5}, {0x8A5E,0x8BCD}, {0x8A61,0x8BE9}, 
+  {0x8A62,0x8BE2}, {0x8A63,0x8BE3}, {0x8A66,0x8BD5}, {0x8A69,0x8BD7}, {0x8A6B,0x8BE7}, 
+  {0x8A6C,0x8BDF}, {0x8A6D,0x8BE1}, {0x8A6E,0x8BE0}, {0x8A70,0x8BD8}, {0x8A71,0x8BDD}, 
+  {0x8A72,0x8BE5}, {0x8A73,0x8BE6}, {0x8A75,0x8BDC}, {0x8A7C,0x8BD9}, {0x8A7F,0x8BD6}, 
+  {0x8A84,0x8BD4}, {0x8A85,0x8BDB}, {0x8A86,0x8BD3}, {0x8A87,0x5938}, {0x8A8D,0x8BA4}, 
+  {0x8A91,0x8BF3}, {0x8A92,0x8BF6}, {0x8A95,0x8BDE}, {0x8A98,0x8BF1}, {0x8A9A,0x8BEE}, 
+  {0x8A9E,0x8BED}, {0x8AA0,0x8BDA}, {0x8AA1,0x8BEB}, {0x8AA3,0x8BEC}, {0x8AA4,0x8BEF}, 
+  {0x8AA5,0x8BF0}, {0x8AA6,0x8BF5}, {0x8AA8,0x8BF2}, {0x8AAC,0x8BF4}, {0x8AB0,0x8C01}, 
+  {0x8AB2,0x8BFE}, {0x8AB6,0x8C07}, {0x8AB9,0x8BFD}, {0x8ABC,0x8C0A}, {0x8ABF,0x8C03}, 
+  {0x8AC2,0x8C04}, {0x8AC4,0x8C06}, {0x8AC7,0x8C08}, {0x8AC9,0x8BFF}, {0x8ACB,0x8BF7}, 
+  {0x8ACD,0x8BE4}, {0x8ACF,0x8BF9}, {0x8AD1,0x8BFC}, {0x8AD2,0x8C05}, {0x8AD6,0x8BBA}, 
+  {0x8AD7,0x8C02}, {0x8ADB,0x8C00}, {0x8ADC,0x8C0D}, {0x8ADE,0x8C1D}, {0x8AE2,0x8BE8}, 
+  {0x8AE4,0x8C14}, {0x8AE6,0x8C1B}, {0x8AE7,0x8C10}, {0x8AEB,0x8C0F}, {0x8AED,0x8C15}, 
+  {0x8AEE,0x8C18}, {0x8AF1,0x8BB3}, {0x8AF3,0x8C19}, {0x8AF6,0x8C0C}, {0x8AF7,0x8BBD}, 
+  {0x8AF8,0x8BF8}, {0x8AFA,0x8C1A}, {0x8AFC,0x8C16}, {0x8AFE,0x8BFA}, {0x8B00,0x8C0B}, 
+  {0x8B01,0x8C12}, {0x8B02,0x8C13}, {0x8B04,0x8A8A}, {0x8B05,0x8BCC}, {0x8B0A,0x8C0E}, 
+  {0x8B0E,0x8C1C}, {0x8B10,0x8C27}, {0x8B14,0x8C11}, {0x8B16,0x8C21}, {0x8B17,0x8C24}, 
+  {0x8B19,0x8C26}, {0x8B1A,0x8C25}, {0x8B1B,0x8BB2}, {0x8B1D,0x8C22}, {0x8B21,0x8C23}, 
+  {0x8B28,0x8C1F}, {0x8B2B,0x8C2A}, {0x8B2C,0x8C2C}, {0x8B2D,0x8C2B}, {0x8B33,0x8BB4}, 
+  {0x8B39,0x8C28}, {0x8B3E,0x8C29}, {0x8B49,0x8BC1}, {0x8B4E,0x8C32}, {0x8B4F,0x8BA5}, 
+  {0x8B56,0x8C2E}, {0x8B58,0x8BC6}, {0x8B59,0x8C2F}, {0x8B5A,0x8C2D}, {0x8B5C,0x8C31}, 
+  {0x8B6B,0x8C35}, {0x8B6F,0x8BD1}, {0x8B70,0x8BAE}, {0x8B74,0x8C34}, {0x8B77,0x62A4}, 
+  {0x8B7D,0x8A89}, {0x8B80,0x8BFB}, {0x8B8A,0x53D8}, {0x8B8E,0x96E0}, {0x8B92,0x8C17}, 
+  {0x8B93,0x8BA9}, {0x8B95,0x8C30}, {0x8B96,0x8C36}, {0x8B9C,0x8C20}, {0x8B9E,0x8C33}, 
+  {0x8C48,0x5C82}, {0x8C50,0x4E30}, {0x8C9D,0x8D1D}, {0x8C9E,0x8D1E}, {0x8CA0,0x8D1F}, 
+  {0x8CA1,0x8D22}, {0x8CA2,0x8D21}, {0x8CA7,0x8D2B}, {0x8CA8,0x8D27}, {0x8CA9,0x8D29}, 
+  {0x8CAA,0x8D2A}, {0x8CAB,0x8D2F}, {0x8CAC,0x8D23}, {0x8CAF,0x8D2E}, {0x8CB0,0x8D33}, 
+  {0x8CB2,0x8D40}, {0x8CB3,0x8D30}, {0x8CB4,0x8D35}, {0x8CB6,0x8D2C}, {0x8CB7,0x4E70}, 
+  {0x8CB8,0x8D37}, {0x8CBA,0x8D36}, {0x8CBB,0x8D39}, {0x8CBC,0x8D34}, {0x8CBD,0x8D3B}, 
+  {0x8CBF,0x8D38}, {0x8CC0,0x8D3A}, {0x8CC1,0x8D32}, {0x8CC2,0x8D42}, {0x8CC3,0x8D41}, 
+  {0x8CC4,0x8D3F}, {0x8CC5,0x8D45}, {0x8CC7,0x8D44}, {0x8CC8,0x8D3E}, {0x8CCA,0x8D3C}, 
+  {0x8CD1,0x8D48}, {0x8CD2,0x8D4A}, {0x8CD3,0x5BBE}, {0x8CD5,0x8D47}, {0x8CDA,0x8D49}, 
+  {0x8CDC,0x8D50}, {0x8CDE,0x8D4F}, {0x8CE0,0x8D54}, {0x8CE1,0x8D53}, {0x8CE2,0x8D24}, 
+  {0x8CE3,0x5356}, {0x8CE4,0x8D31}, {0x8CE6,0x8D4B}, {0x8CE7,0x8D55}, {0x8CEA,0x8D28}, 
+  {0x8CEB,0x8D4D}, {0x8CEC,0x8D26}, {0x8CED,0x8D4C}, {0x8CF4,0x8D56}, {0x8CFA,0x8D5A}, 
+  {0x8CFB,0x8D59}, {0x8CFC,0x8D2D}, {0x8CFD,0x8D5B}, {0x8CFE,0x8D5C}, {0x8D04,0x8D3D}, 
+  {0x8D05,0x8D58}, {0x8D08,0x8D60}, {0x8D0A,0x8D5E}, {0x8D0B,0x8D5D}, {0x8D0D,0x8D61}, 
+  {0x8D0F,0x8D62}, {0x8D10,0x8D46}, {0x8D16,0x8D4E}, {0x8D1B,0x8D63}, {0x8D1C,0x8D43}, 
+  {0x8D95,0x8D76}, {0x8D99,0x8D75}, {0x8DA8,0x8D8B}, {0x8DB2,0x8DB1}, {0x8E10,0x8DF5}, 
+  {0x8E34,0x8E0A}, {0x8E4C,0x8DC4}, {0x8E55,0x8DF8}, {0x8E63,0x8E52}, {0x8E7A,0x8DF7}, 
+  {0x8E89,0x8DB8}, {0x8E8A,0x8E0C}, {0x8E8B,0x8DFB}, {0x8E8D,0x8DC3}, {0x8E91,0x8E2F}, 
+  {0x8E92,0x8DDE}, {0x8E93,0x8E2C}, {0x8E9A,0x8DF9}, {0x8EA1,0x8E51}, {0x8EA5,0x8E7F}, 
+  {0x8EA6,0x8E9C}, {0x8EAA,0x8E8F}, {0x8EC0,0x8EAF}, {0x8ECA,0x8F66}, {0x8ECB,0x8F67}, 
+  {0x8ECC,0x8F68}, {0x8ECD,0x519B}, {0x8ED2,0x8F69}, {0x8ED4,0x8F6B}, {0x8EDB,0x8F6D}, 
+  {0x8EDF,0x8F6F}, {0x8EE4,0x8F77}, {0x8EEB,0x8F78}, {0x8EF2,0x8F71}, {0x8EF8,0x8F74}, 
+  {0x8EF9,0x8F75}, {0x8EFA,0x8F7A}, {0x8EFB,0x8F72}, {0x8EFC,0x8F76}, {0x8EFE,0x8F7C}, 
+  {0x8F03,0x8F83}, {0x8F05,0x8F82}, {0x8F07,0x8F81}, {0x8F09,0x8F7D}, {0x8F0A,0x8F7E}, 
+  {0x8F12,0x8F84}, {0x8F14,0x8F85}, {0x8F15,0x8F7B}, {0x8F1B,0x8F86}, {0x8F1C,0x8F8E}, 
+  {0x8F1D,0x8F89}, {0x8F1E,0x8F8B}, {0x8F1F,0x8F8D}, {0x8F25,0x8F8A}, {0x8F26,0x8F87}, 
+  {0x8F29,0x8F88}, {0x8F2A,0x8F6E}, {0x8F2F,0x8F91}, {0x8F33,0x8F8F}, {0x8F38,0x8F93}, 
+  {0x8F3B,0x8F90}, {0x8F3E,0x8F97}, {0x8F3F,0x8206}, {0x8F42,0x6BC2}, {0x8F44,0x8F96}, 
+  {0x8F45,0x8F95}, {0x8F46,0x8F98}, {0x8F49,0x8F6C}, {0x8F4D,0x8F99}, {0x8F4E,0x8F7F}, 
+  {0x8F54,0x8F9A}, {0x8F5F,0x8F70}, {0x8F61,0x8F94}, {0x8F62,0x8F79}, {0x8F64,0x8F73}, 
+  {0x8FA6,0x529E}, {0x8FAD,0x8F9E}, {0x8FAE,0x8FAB}, {0x8FAF,0x8FA9}, {0x8FB2,0x519C}, 
+  {0x9015,0x8FF3}, {0x9019,0x8FD9}, {0x9023,0x8FDE}, {0x9032,0x8FDB}, {0x904B,0x8FD0}, 
+  {0x904E,0x8FC7}, {0x9054,0x8FBE}, {0x9055,0x8FDD}, {0x905C,0x900A}, {0x905E,0x9012}, 
+  {0x9060,0x8FDC}, {0x9069,0x9002}, {0x9072,0x8FDF}, {0x9077,0x8FC1}, {0x9078,0x9009}, 
+  {0x907A,0x9057}, {0x907C,0x8FBD}, {0x9081,0x8FC8}, {0x9084,0x8FD8}, {0x9087,0x8FE9}, 
+  {0x908A,0x8FB9}, {0x908F,0x903B}, {0x9090,0x9026}, {0x90DF,0x90CF}, {0x90F5,0x90AE}, 
+  {0x9106,0x90D3}, {0x9109,0x4E61}, {0x9112,0x90B9}, {0x9114,0x90AC}, {0x9116,0x90E7}, 
+  {0x9127,0x9093}, {0x912D,0x90D1}, {0x9130,0x90BB}, {0x9132,0x90F8}, {0x9134,0x90BA}, 
+  {0x9136,0x90D0}, {0x913A,0x909D}, {0x9148,0x90E6}, {0x9196,0x915D}, {0x919C,0x4E11}, 
+  {0x91AB,0x533B}, {0x91AC,0x9171}, {0x91C0,0x917F}, {0x91C1,0x8845}, {0x91C3,0x917E}, 
+  {0x91C5,0x917D}, {0x91CB,0x91CA}, {0x91D2,0x9485}, {0x91D3,0x9486}, {0x91D4,0x9487}, 
+  {0x91D5,0x948C}, {0x91D7,0x948A}, {0x91D8,0x9489}, {0x91D9,0x948B}, {0x91DD,0x9488}, 
+  {0x91E3,0x9493}, {0x91E4,0x9490}, {0x91E7,0x948F}, {0x91E9,0x9492}, {0x91F5,0x9497}, 
+  {0x91F7,0x948D}, {0x91F9,0x9495}, {0x91FA,0x948E}, {0x9200,0x94AF}, {0x9201,0x94AB}, 
+  {0x9204,0x94AD}, {0x9208,0x949A}, {0x9209,0x94A0}, {0x920D,0x949D}, {0x920E,0x94A9}, 
+  {0x9210,0x94A4}, {0x9211,0x94A3}, {0x9214,0x949E}, {0x9215,0x94AE}, {0x921E,0x94A7}, 
+  {0x9223,0x9499}, {0x9225,0x94AC}, {0x9226,0x949B}, {0x9227,0x94AA}, {0x922E,0x94CC}, 
+  {0x9230,0x94C8}, {0x9233,0x94B6}, {0x9234,0x94C3}, {0x9237,0x94B4}, {0x9238,0x94B9}, 
+  {0x9239,0x94CD}, {0x923A,0x94B0}, {0x923D,0x94B8}, {0x923E,0x94C0}, {0x923F,0x94BF}, 
+  {0x9240,0x94BE}, {0x9245,0x949C}, {0x9248,0x94CA}, {0x9249,0x94C9}, {0x924D,0x94CB}, 
+  {0x9251,0x94C2}, {0x9255,0x94B7}, {0x9257,0x94B3}, {0x925A,0x94C6}, {0x925B,0x94C5}, 
+  {0x925E,0x94BA}, {0x9262,0x94B5}, {0x9266,0x94B2}, {0x926C,0x94BC}, {0x926D,0x94BD}, 
+  {0x9278,0x94F0}, {0x927A,0x94D2}, {0x927B,0x94EC}, {0x927F,0x94EA}, {0x9280,0x94F6}, 
+  {0x9283,0x94F3}, {0x9285,0x94DC}, {0x9291,0x94E3}, {0x9293,0x94E8}, {0x9296,0x94E2}, 
+  {0x9298,0x94ED}, {0x929A,0x94EB}, {0x929C,0x8854}, {0x92A0,0x94D1}, {0x92A3,0x94F7}, 
+  {0x92A5,0x94F1}, {0x92A6,0x94DF}, {0x92A8,0x94F5}, {0x92A9,0x94E5}, {0x92AA,0x94D5}, 
+  {0x92AB,0x94EF}, {0x92AC,0x94D0}, {0x92B1,0x94DE}, {0x92B7,0x9500}, {0x92B9,0x9508}, 
+  {0x92BB,0x9511}, {0x92BC,0x9509}, {0x92C1,0x94DD}, {0x92C3,0x9512}, {0x92C5,0x950C}, 
+  {0x92C7,0x94A1}, {0x92CC,0x94E4}, {0x92CF,0x94D7}, {0x92D2,0x950B}, {0x92DD,0x950A}, 
+  {0x92DF,0x9513}, {0x92E3,0x94D8}, {0x92E4,0x9504}, {0x92E5,0x9503}, {0x92E6,0x9514}, 
+  {0x92E8,0x9507}, {0x92EA,0x94FA}, {0x92ED,0x9510}, {0x92EE,0x94D6}, {0x92EF,0x9506}, 
+  {0x92F0,0x9502}, {0x92F1,0x94FD}, {0x92F6,0x950D}, {0x92F8,0x952F}, {0x92FC,0x94A2}, 
+  {0x9301,0x951E}, {0x9306,0x9516}, {0x9307,0x952B}, {0x9308,0x9529}, {0x9310,0x9525}, 
+  {0x9312,0x9515}, {0x9315,0x951F}, {0x9318,0x9524}, {0x9319,0x9531}, {0x931A,0x94EE}, 
+  {0x931B,0x951B}, {0x931F,0x952C}, {0x9320,0x952D}, {0x9322,0x94B1}, {0x9326,0x9526}, 
+  {0x9328,0x951A}, {0x932B,0x9521}, {0x932E,0x9522}, {0x932F,0x9519}, {0x9332,0x5F55}, 
+  {0x9333,0x9530}, {0x9338,0x94FC}, {0x9340,0x951D}, {0x9341,0x9528}, {0x9343,0x952A}, 
+  {0x9346,0x9494}, {0x9347,0x9534}, {0x934B,0x9505}, {0x934D,0x9540}, {0x9354,0x9537}, 
+  {0x9358,0x94E1}, {0x935B,0x953B}, {0x9364,0x9538}, {0x9365,0x9532}, {0x9369,0x9518}, 
+  {0x936C,0x9539}, {0x9370,0x953E}, {0x9375,0x952E}, {0x9376,0x9536}, {0x937A,0x9517}, 
+  {0x937E,0x953A}, {0x9382,0x9541}, {0x9384,0x953F}, {0x9387,0x9545}, {0x938A,0x9551}, 
+  {0x9396,0x9501}, {0x9398,0x9549}, {0x93A2,0x94A8}, {0x93A3,0x84E5}, {0x93A6,0x954F}, 
+  {0x93A7,0x94E0}, {0x93A9,0x94E9}, {0x93AA,0x953C}, {0x93AC,0x9550}, {0x93AE,0x9547}, 
+  {0x93B0,0x9552}, {0x93B3,0x954D}, {0x93B5,0x9553}, {0x93B8,0x954C}, {0x93BF,0x954E}, 
+  {0x93C3,0x955E}, {0x93C7,0x955F}, {0x93C8,0x94FE}, {0x93CC,0x9546}, {0x93CD,0x9559}, 
+  {0x93D1,0x955D}, {0x93D7,0x94FF}, {0x93D8,0x9535}, {0x93DC,0x9557}, {0x93DD,0x9558}, 
+  {0x93DE,0x955B}, {0x93DF,0x94F2}, {0x93E1,0x955C}, {0x93E2,0x9556}, {0x93E4,0x9542}, 
+  {0x93E8,0x933E}, {0x93F5,0x94E7}, {0x93F7,0x9564}, {0x93F9,0x956A}, {0x9403,0x94D9}, 
+  {0x940B,0x94F4}, {0x9410,0x9563}, {0x9412,0x94F9}, {0x9413,0x9566}, {0x9414,0x9561}, 
+  {0x9418,0x949F}, {0x9419,0x956B}, {0x941D,0x9562}, {0x9420,0x9568}, {0x9426,0x950E}, 
+  {0x9427,0x950F}, {0x9428,0x9544}, {0x942E,0x9570}, {0x9432,0x956F}, {0x9433,0x956D}, 
+  {0x9435,0x94C1}, {0x9438,0x94CE}, {0x943A,0x94DB}, {0x943F,0x9571}, {0x9444,0x94F8}, 
+  {0x944A,0x956C}, {0x944C,0x9554}, {0x9452,0x9274}, {0x9454,0x9572}, {0x9460,0x94C4}, 
+  {0x9463,0x9573}, {0x9465,0x9565}, {0x946D,0x9567}, {0x9470,0x94A5}, {0x9472,0x9576}, 
+  {0x9477,0x954A}, {0x9479,0x9569}, {0x947C,0x9523}, {0x947D,0x94BB}, {0x947E,0x92AE}, 
+  {0x947F,0x51FF}, {0x9577,0x957F}, {0x9580,0x95E8}, {0x9582,0x95E9}, {0x9583,0x95EA}, 
+  {0x9586,0x95EB}, {0x9589,0x95ED}, {0x958B,0x5F00}, {0x958C,0x95F6}, {0x958E,0x95F3}, 
+  {0x958F,0x95F0}, {0x9591,0x95F2}, {0x9593,0x95F4}, {0x9594,0x95F5}, {0x9598,0x95F8}, 
+  {0x95A1,0x9602}, {0x95A3,0x9601}, {0x95A5,0x9600}, {0x95A8,0x95FA}, {0x95A9,0x95FD}, 
+  {0x95AB,0x9603}, {0x95AC,0x9606}, {0x95AD,0x95FE}, {0x95B2,0x9605}, {0x95B6,0x960A}, 
+  {0x95B9,0x9609}, {0x95BB,0x960E}, {0x95BC,0x960F}, {0x95BD,0x960D}, {0x95BE,0x9608}, 
+  {0x95BF,0x960C}, {0x95C3,0x9612}, {0x95C8,0x95F1}, {0x95CA,0x9614}, {0x95CB,0x9615}, 
+  {0x95CC,0x9611}, {0x95D0,0x9617}, {0x95D4,0x9616}, {0x95D5,0x9619}, {0x95D6,0x95EF}, 
+  {0x95DC,0x5173}, {0x95DE,0x961A}, {0x95E1,0x9610}, {0x95E5,0x95FC}, {0x9658,0x9649}, 
+  {0x965D,0x9655}, {0x9663,0x9635}, {0x9670,0x9634}, {0x9673,0x9648}, {0x9678,0x9646}, 
+  {0x967D,0x9633}, {0x9689,0x9667}, {0x968A,0x961F}, {0x968E,0x9636}, {0x9695,0x9668}, 
+  {0x969B,0x9645}, {0x96A8,0x968F}, {0x96AA,0x9669}, {0x96B1,0x9690}, {0x96B4,0x9647}, 
+  {0x96B8,0x96B6}, {0x96D6,0x867D}, {0x96D9,0x53CC}, {0x96DB,0x96CF}, {0x96DC,0x6742}, 
+  {0x96E2,0x79BB}, {0x96E3,0x96BE}, {0x96F2,0x4E91}, {0x96FB,0x7535}, {0x9727,0x96FE}, 
+  {0x973D,0x9701}, {0x9742,0x96F3}, {0x9744,0x972D}, {0x9748,0x7075}, {0x975A,0x9753}, 
+  {0x975C,0x9759}, {0x9768,0x9765}, {0x978F,0x5DE9}, {0x97BD,0x9792}, {0x97C3,0x9791}, 
+  {0x97C9,0x97AF}, {0x97CB,0x97E6}, {0x97CC,0x97E7}, {0x97D3,0x97E9}, {0x97D9,0x97EA}, 
+  {0x97DC,0x97EC}, {0x97DE,0x97EB}, {0x97FF,0x54CD}, {0x9801,0x9875}, {0x9802,0x9876}, 
+  {0x9803,0x9877}, {0x9805,0x9879}, {0x9806,0x987A}, {0x9807,0x9878}, {0x9808,0x987B}, 
+  {0x980A,0x987C}, {0x980C,0x9882}, {0x980E,0x9880}, {0x980F,0x9883}, {0x9810,0x9884}, 
+  {0x9811,0x987D}, {0x9812,0x9881}, {0x9813,0x987F}, {0x9817,0x9887}, {0x9818,0x9886}, 
+  {0x981C,0x988C}, {0x9821,0x9889}, {0x9824,0x9890}, {0x9826,0x988F}, {0x982D,0x5934}, 
+  {0x9830,0x988A}, {0x9837,0x9894}, {0x9838,0x9888}, {0x983B,0x9891}, {0x983D,0x9893}, 
+  {0x9846,0x9897}, {0x984C,0x9898}, {0x984D,0x989D}, {0x984E,0x989A}, {0x9853,0x989B}, 
+  {0x9854,0x989C}, {0x9858,0x613F}, {0x9859,0x98A1}, {0x985B,0x98A0}, {0x985E,0x7C7B}, 
+  {0x9862,0x989F}, {0x9865,0x98A2}, {0x9867,0x987E}, {0x986B,0x98A4}, {0x986C,0x98A5}, 
+  {0x986F,0x663E}, {0x9870,0x98A6}, {0x9871,0x9885}, {0x9873,0x989E}, {0x9874,0x98A7}, 
+  {0x98A8,0x98CE}, {0x98AE,0x98D1}, {0x98AF,0x98D2}, {0x98B6,0x98D3}, {0x98BC,0x98D5}, 
+  {0x98C4,0x98D8}, {0x98C6,0x98D9}, {0x98C8,0x98DA}, {0x98DB,0x98DE}, {0x98E0,0x9963}, 
+  {0x98E9,0x9968}, {0x98EA,0x996A}, {0x98EB,0x996B}, {0x98ED,0x996C}, {0x98EF,0x996D}, 
+  {0x98F2,0x996E}, {0x98F4,0x9974}, {0x98FC,0x9972}, {0x98FD,0x9971}, {0x98FE,0x9970}, 
+  {0x9903,0x997A}, {0x9905,0x997C}, {0x9909,0x9977}, {0x990A,0x517B}, {0x990C,0x9975}, 
+  {0x9911,0x997D}, {0x9912,0x9981}, {0x9913,0x997F}, {0x9918,0x4F59}, {0x991B,0x9984}, 
+  {0x991E,0x996F}, {0x9921,0x9985}, {0x9928,0x9986}, {0x9933,0x9967}, {0x9937,0x9987}, 
+  {0x993C,0x9969}, {0x993E,0x998F}, {0x993F,0x998A}, {0x9943,0x998D}, {0x9945,0x9992}, 
+  {0x9948,0x9990}, {0x9949,0x9991}, {0x994A,0x9993}, {0x994B,0x9988}, {0x994C,0x9994}, 
+  {0x9951,0x9965}, {0x9952,0x9976}, {0x9957,0x98E8}, {0x995C,0x990D}, {0x995E,0x998B}, 
+  {0x9962,0x9995}, {0x99AC,0x9A6C}, {0x99AD,0x9A6D}, {0x99AE,0x51AF}, {0x99B1,0x9A6E}, 
+  {0x99B3,0x9A70}, {0x99B4,0x9A6F}, {0x99C1,0x9A73}, {0x99D0,0x9A7B}, {0x99D1,0x9A7D}, 
+  {0x99D2,0x9A79}, {0x99D4,0x9A75}, {0x99D5,0x9A7E}, {0x99D8,0x9A80}, {0x99D9,0x9A78}, 
+  {0x99DB,0x9A76}, {0x99DD,0x9A7C}, {0x99DF,0x9A77}, {0x99E1,0x9A82}, {0x99E2,0x9A88}, 
+  {0x99ED,0x9A87}, {0x99F1,0x9A86}, {0x99FF,0x9A8F}, {0x9A01,0x9A8B}, {0x9A05,0x9A93}, 
+  {0x9A0D,0x9A92}, {0x9A0E,0x9A91}, {0x9A0F,0x9A90}, {0x9A16,0x9A9B}, {0x9A19,0x9A97}, 
+  {0x9A2B,0x9A9E}, {0x9A2D,0x9A98}, {0x9A2E,0x9A9D}, {0x9A30,0x817E}, {0x9A36,0x9A7A}, 
+  {0x9A37,0x9A9A}, {0x9A38,0x9A9F}, {0x9A3E,0x9AA1}, {0x9A40,0x84E6}, {0x9A41,0x9A9C}, 
+  {0x9A42,0x9A96}, {0x9A43,0x9AA0}, {0x9A44,0x9AA2}, {0x9A45,0x9A71}, {0x9A4A,0x9A85}, 
+  {0x9A4D,0x9A81}, {0x9A4F,0x9AA3}, {0x9A55,0x9A84}, {0x9A57,0x9A8C}, {0x9A5A,0x60CA}, 
+  {0x9A5B,0x9A7F}, {0x9A5F,0x9AA4}, {0x9A62,0x9A74}, {0x9A64,0x9AA7}, {0x9A65,0x9AA5}, 
+  {0x9A6A,0x9A8A}, {0x9AAF,0x80AE}, {0x9ACF,0x9AC5}, {0x9AD4,0x4F53}, {0x9AD5,0x9ACC}, 
+  {0x9AD6,0x9ACB}, {0x9B06,0x677E}, {0x9B22,0x9B13}, {0x9B25,0x6597}, {0x9B27,0x95F9}, 
+  {0x9B29,0x960B}, {0x9B2E,0x9604}, {0x9B31,0x90C1}, {0x9B4E,0x9B49}, {0x9B58,0x9B47}, 
+  {0x9B5A,0x9C7C}, {0x9B6F,0x9C81}, {0x9B74,0x9C82}, {0x9B77,0x9C7F}, {0x9B81,0x9C85}, 
+  {0x9B83,0x9C86}, {0x9B8E,0x9C87}, {0x9B90,0x9C90}, {0x9B91,0x9C8D}, {0x9B92,0x9C8B}, 
+  {0x9B9A,0x9C92}, {0x9B9D,0x9C9E}, {0x9B9E,0x9C95}, {0x9BAA,0x9C94}, {0x9BAB,0x9C9B}, 
+  {0x9BAD,0x9C91}, {0x9BAE,0x9C9C}, {0x9BC0,0x9CA7}, {0x9BC1,0x9CA0}, {0x9BC7,0x9CA9}, 
+  {0x9BC9,0x9CA4}, {0x9BCA,0x9CA8}, {0x9BD4,0x9CBB}, {0x9BD6,0x9CAD}, {0x9BDB,0x9CB7}, 
+  {0x9BDD,0x9CB4}, {0x9BE1,0x9CB1}, {0x9BE2,0x9CB5}, {0x9BE4,0x9CB2}, {0x9BE7,0x9CB3}, 
+  {0x9BE8,0x9CB8}, {0x9BEA,0x9CAE}, {0x9BEB,0x9CB0}, {0x9BF0,0x9CB6}, {0x9BF4,0x9CBA}, 
+  {0x9BFD,0x9CAB}, {0x9BFF,0x9CCA}, {0x9C08,0x9CBD}, {0x9C09,0x9CC7}, {0x9C0D,0x9CC5}, 
+  {0x9C10,0x9CC4}, {0x9C12,0x9CC6}, {0x9C13,0x9CC3}, {0x9C20,0x9CCB}, {0x9C23,0x9CA5}, 
+  {0x9C25,0x9CCF}, {0x9C28,0x9CCE}, {0x9C29,0x9CD0}, {0x9C2D,0x9CCD}, {0x9C31,0x9CA2}, 
+  {0x9C32,0x9CCC}, {0x9C33,0x9CD3}, {0x9C35,0x9CD8}, {0x9C37,0x9CA6}, {0x9C39,0x9CA3}, 
+  {0x9C3B,0x9CD7}, {0x9C3E,0x9CD4}, {0x9C45,0x9CD9}, {0x9C48,0x9CD5}, {0x9C49,0x9CD6}, 
+  {0x9C52,0x9CDF}, {0x9C54,0x9CDD}, {0x9C56,0x9CDC}, {0x9C57,0x9CDE}, {0x9C58,0x9C9F}, 
+  {0x9C5D,0x9CBC}, {0x9C5F,0x9C8E}, {0x9C67,0x9CE2}, {0x9C6D,0x9C9A}, {0x9C78,0x9C88}, 
+  {0x9C7A,0x9CA1}, {0x9CE5,0x9E1F}, {0x9CE9,0x9E20}, {0x9CEC,0x51EB}, {0x9CF3,0x51E4}, 
+  {0x9CF4,0x9E23}, {0x9CF6,0x9E22}, {0x9D06,0x9E29}, {0x9D07,0x9E28}, {0x9D09,0x9E26}, 
+  {0x9D15,0x9E35}, {0x9D1B,0x9E33}, {0x9D1D,0x9E32}, {0x9D1F,0x9E31}, {0x9D23,0x9E2A}, 
+  {0x9D26,0x9E2F}, {0x9D28,0x9E2D}, {0x9D2F,0x9E38}, {0x9D30,0x9E39}, {0x9D3B,0x9E3F}, 
+  {0x9D3F,0x9E3D}, {0x9D42,0x9E3A}, {0x9D51,0x9E43}, {0x9D52,0x9E46}, {0x9D53,0x9E41}, 
+  {0x9D5C,0x9E48}, {0x9D5D,0x9E45}, {0x9D60,0x9E44}, {0x9D61,0x9E49}, {0x9D6A,0x9E4C}, 
+  {0x9D6C,0x9E4F}, {0x9D6F,0x9E4E}, {0x9D72,0x9E4A}, {0x9D87,0x9E2B}, {0x9D89,0x9E51}, 
+  {0x9D93,0x9E4B}, {0x9D98,0x9E55}, {0x9D9A,0x9E57}, {0x9DA5,0x9E5B}, {0x9DA9,0x9E5C}, 
+  {0x9DAF,0x83BA}, {0x9DB4,0x9E64}, {0x9DBB,0x9E58}, {0x9DBC,0x9E63}, {0x9DC0,0x9E5A}, 
+  {0x9DC2,0x9E5E}, {0x9DC4,0x9E21}, {0x9DD3,0x9E67}, {0x9DD7,0x9E25}, {0x9DD9,0x9E37}, 
+  {0x9DDA,0x9E68}, {0x9DE5,0x9E36}, {0x9DE6,0x9E6A}, {0x9DEF,0x9E69}, {0x9DF2,0x9E6B}, 
+  {0x9DF3,0x9E47}, {0x9DF8,0x9E6C}, {0x9DF9,0x9E70}, {0x9DFA,0x9E6D}, {0x9E0C,0x9E71}, 
+  {0x9E15,0x9E2C}, {0x9E1A,0x9E66}, {0x9E1B,0x9E73}, {0x9E1D,0x9E42}, {0x9E1E,0x9E3E}, 
+  {0x9E7A,0x9E7E}, {0x9E7C,0x7877}, {0x9E7D,0x76D0}, {0x9E97,0x4E3D}, {0x9EA5,0x9EA6}, 
+  {0x9EA9,0x9EB8}, {0x9EBD,0x4E48}, {0x9ECC,0x9EC9}, {0x9EDE,0x70B9}, {0x9EE8,0x515A}, 
+  {0x9EF2,0x9EEA}, {0x9EF7,0x9EE9}, {0x9EFD,0x9EFE}, {0x9EFF,0x9F0B}, {0x9F09,0x9F0D}, 
+  {0x9F4A,0x9F50}, {0x9F4B,0x658B}, {0x9F4F,0x9F51}, {0x9F52,0x9F7F}, {0x9F54,0x9F80}, 
+  {0x9F59,0x9F85}, {0x9F5C,0x9F87}, {0x9F5F,0x9F83}, {0x9F60,0x9F86}, {0x9F61,0x9F84}, 
+  {0x9F66,0x9F88}, {0x9F6A,0x9F8A}, {0x9F6C,0x9F89}, {0x9F72,0x9F8B}, {0x9F77,0x9F8C}, 
+  {0x9F8D,0x9F99}, {0x9F90,0x5E9E}, {0x9F94,0x9F9A}, {0x9F95,0x9F9B}, {0x9F9C,0x9F9F}
+};
+
+
+
+/* the letters correspond to unicode characters which
+ * have Lu, Ll, Lt, Lm, Lo, Mn, Mc, or Me in their
+ * GENERAL CATEGORY */
+
+#define NUM_LETTER_INFO 276
+static const unirange letter_info[NUM_LETTER_INFO] = {
+  {0x41,0x5a}, {0x61,0x7a}, {0xaa,0xaa}, {0xb5,0xb5}, 
+  {0xba,0xba}, {0xc0,0xd6}, {0xd8,0xf6}, {0xf8,0x1f5}, 
+  {0x1fa,0x217}, {0x250,0x2a8}, {0x2b0,0x2b8}, {0x2bb,0x2c1}, 
+  {0x2d0,0x2d1}, {0x2e0,0x2e4}, {0x300,0x345}, {0x360,0x361}, 
+  {0x37a,0x37a}, {0x386,0x386}, {0x388,0x38a}, {0x38c,0x38c}, 
+  {0x38e,0x3a1}, {0x3a3,0x3ce}, {0x3d0,0x3d6}, {0x3da,0x3da}, 
+  {0x3dc,0x3dc}, {0x3de,0x3de}, {0x3e0,0x3e0}, {0x3e2,0x3f3}, 
+  {0x401,0x40c}, {0x40e,0x44f}, {0x451,0x45c}, {0x45e,0x481}, 
+  {0x483,0x486}, {0x490,0x4c4}, {0x4c7,0x4c8}, {0x4cb,0x4cc}, 
+  {0x4d0,0x4eb}, {0x4ee,0x4f5}, {0x4f8,0x4f9}, {0x531,0x556}, 
+  {0x559,0x559}, {0x561,0x587}, {0x591,0x5a1}, {0x5a3,0x5b9}, 
+  {0x5bb,0x5bd}, {0x5bf,0x5bf}, {0x5c1,0x5c2}, {0x5c4,0x5c4}, 
+  {0x5d0,0x5ea}, {0x5f0,0x5f2}, {0x621,0x63a}, {0x640,0x652}, 
+  {0x670,0x6b7}, {0x6ba,0x6be}, {0x6c0,0x6ce}, {0x6d0,0x6d3}, 
+  {0x6d5,0x6e8}, {0x6ea,0x6ed}, {0x901,0x903}, {0x905,0x939}, 
+  {0x93c,0x94d}, {0x951,0x954}, {0x958,0x963}, {0x981,0x983}, 
+  {0x985,0x98c}, {0x98f,0x990}, {0x993,0x9a8}, {0x9aa,0x9b0}, 
+  {0x9b2,0x9b2}, {0x9b6,0x9b9}, {0x9bc,0x9bc}, {0x9be,0x9c4}, 
+  {0x9c7,0x9c8}, {0x9cb,0x9cd}, {0x9d7,0x9d7}, {0x9dc,0x9dd}, 
+  {0x9df,0x9e3}, {0x9f0,0x9f1}, {0xa02,0xa02}, {0xa05,0xa0a}, 
+  {0xa0f,0xa10}, {0xa13,0xa28}, {0xa2a,0xa30}, {0xa32,0xa33}, 
+  {0xa35,0xa36}, {0xa38,0xa39}, {0xa3c,0xa3c}, {0xa3e,0xa42}, 
+  {0xa47,0xa48}, {0xa4b,0xa4d}, {0xa59,0xa5c}, {0xa5e,0xa5e}, 
+  {0xa70,0xa74}, {0xa81,0xa83}, {0xa85,0xa8b}, {0xa8d,0xa8d}, 
+  {0xa8f,0xa91}, {0xa93,0xaa8}, {0xaaa,0xab0}, {0xab2,0xab3}, 
+  {0xab5,0xab9}, {0xabc,0xac5}, {0xac7,0xac9}, {0xacb,0xacd}, 
+  {0xae0,0xae0}, {0xb01,0xb03}, {0xb05,0xb0c}, {0xb0f,0xb10}, 
+  {0xb13,0xb28}, {0xb2a,0xb30}, {0xb32,0xb33}, {0xb36,0xb39}, 
+  {0xb3c,0xb43}, {0xb47,0xb48}, {0xb4b,0xb4d}, {0xb56,0xb57}, 
+  {0xb5c,0xb5d}, {0xb5f,0xb61}, {0xb82,0xb83}, {0xb85,0xb8a}, 
+  {0xb8e,0xb90}, {0xb92,0xb95}, {0xb99,0xb9a}, {0xb9c,0xb9c}, 
+  {0xb9e,0xb9f}, {0xba3,0xba4}, {0xba8,0xbaa}, {0xbae,0xbb5}, 
+  {0xbb7,0xbb9}, {0xbbe,0xbc2}, {0xbc6,0xbc8}, {0xbca,0xbcd}, 
+  {0xbd7,0xbd7}, {0xc01,0xc03}, {0xc05,0xc0c}, {0xc0e,0xc10}, 
+  {0xc12,0xc28}, {0xc2a,0xc33}, {0xc35,0xc39}, {0xc3e,0xc44}, 
+  {0xc46,0xc48}, {0xc4a,0xc4d}, {0xc55,0xc56}, {0xc60,0xc61}, 
+  {0xc82,0xc83}, {0xc85,0xc8c}, {0xc8e,0xc90}, {0xc92,0xca8}, 
+  {0xcaa,0xcb3}, {0xcb5,0xcb9}, {0xcbe,0xcc4}, {0xcc6,0xcc8}, 
+  {0xcca,0xccd}, {0xcd5,0xcd6}, {0xcde,0xcde}, {0xce0,0xce1}, 
+  {0xd02,0xd03}, {0xd05,0xd0c}, {0xd0e,0xd10}, {0xd12,0xd28}, 
+  {0xd2a,0xd39}, {0xd3e,0xd43}, {0xd46,0xd48}, {0xd4a,0xd4d}, 
+  {0xd57,0xd57}, {0xd60,0xd61}, {0xe01,0xe2e}, {0xe30,0xe3a}, 
+  {0xe40,0xe4e}, {0xe81,0xe82}, {0xe84,0xe84}, {0xe87,0xe88}, 
+  {0xe8a,0xe8a}, {0xe8d,0xe8d}, {0xe94,0xe97}, {0xe99,0xe9f}, 
+  {0xea1,0xea3}, {0xea5,0xea5}, {0xea7,0xea7}, {0xeaa,0xeab}, 
+  {0xead,0xeae}, {0xeb0,0xeb9}, {0xebb,0xebd}, {0xec0,0xec4}, 
+  {0xec6,0xec6}, {0xec8,0xecd}, {0xedc,0xedd}, {0xf18,0xf19}, 
+  {0xf35,0xf35}, {0xf37,0xf37}, {0xf39,0xf39}, {0xf40,0xf47}, 
+  {0xf49,0xf69}, {0xf71,0xf84}, {0xf86,0xf87}, {0xf90,0xf95}, 
+  {0xf97,0xf97}, {0xf99,0xfad}, {0xfb1,0xfb7}, {0xfb9,0xfb9}, 
+  {0x10a0,0x10c5}, {0x10d0,0x10f6}, {0x1100,0x1159}, {0x115f,0x11a2}, 
+  {0x11a8,0x11f9}, {0x1820,0x1877}, {0x1880,0x18a9}, {0x1e00,0x1e9b}, 
+  {0x1ea0,0x1ef9}, {0x1f00,0x1f15}, 
+  {0x1f18,0x1f1d}, {0x1f20,0x1f45}, {0x1f48,0x1f4d}, {0x1f50,0x1f57}, 
+  {0x1f59,0x1f59}, {0x1f5b,0x1f5b}, {0x1f5d,0x1f5d}, {0x1f5f,0x1f7d}, 
+  {0x1f80,0x1fb4}, {0x1fb6,0x1fbc}, {0x1fbe,0x1fbe}, {0x1fc2,0x1fc4}, 
+  {0x1fc6,0x1fcc}, {0x1fd0,0x1fd3}, {0x1fd6,0x1fdb}, {0x1fe0,0x1fec}, 
+  {0x1ff2,0x1ff4}, {0x1ff6,0x1ffc}, {0x207f,0x207f}, {0x20d0,0x20e1}, 
+  {0x2102,0x2102}, {0x2107,0x2107}, {0x210a,0x2113}, {0x2115,0x2115}, 
+  {0x2118,0x211d}, {0x2124,0x2124}, {0x2126,0x2126}, {0x2128,0x2128}, 
+  {0x212a,0x2131}, {0x2133,0x2138}, {0x3005,0x3005}, {0x302a,0x302f}, 
+  {0x3031,0x3035}, {0x3041,0x3094}, {0x3099,0x309e}, {0x30a1,0x30fa}, 
+  {0x30fc,0x30fe}, {0x3105,0x312c}, {0x3131,0x318e}, {0x4e00,0x9fa5}, 
+  {0xac00,0xd7a3}, {0xf900,0xfa2d}, {0xfb00,0xfb06}, {0xfb13,0xfb17}, 
+  {0xfb1e,0xfb28}, {0xfb2a,0xfb36}, {0xfb38,0xfb3c}, {0xfb3e,0xfb3e}, 
+  {0xfb40,0xfb41}, {0xfb43,0xfb44}, {0xfb46,0xfbb1}, {0xfbd3,0xfd3d}, 
+  {0xfd50,0xfd8f}, {0xfd92,0xfdc7}, {0xfdf0,0xfdfb}, {0xfe20,0xfe23}, 
+  {0xfe70,0xfe72}, {0xfe74,0xfe74}, {0xfe76,0xfefc}, {0xff21,0xff3a}, 
+  {0xff41,0xff5a}, {0xff66,0xffbe}, {0xffc2,0xffc7}, {0xffca,0xffcf}, 
+  {0xffd2,0xffd7}, {0xffda,0xffdc}
+};
+
+
+/* the digits correspond to unicode characters which
+ * have Nd in their GENERAL CATEGORY */
+
+#define NUM_DIGIT_INFO 17
+static const unirange digit_info[NUM_DIGIT_INFO] = {
+  {0x30,0x39}, {0x660,0x669}, {0x6f0,0x6f9}, {0x966,0x96f}, 
+  {0x9e6,0x9ef}, {0xa66,0xa6f}, {0xae6,0xaef}, {0xb66,0xb6f}, 
+  {0xbe7,0xbef}, {0xc66,0xc6f}, {0xce6,0xcef}, {0xd66,0xd6f}, 
+  {0xe50,0xe59}, {0xed0,0xed9}, {0xf20,0xf29}, {0xff10,0xff19},
+  {0x1810,0x1819}
+};
+
+
+/* the spaces correspond to unicode characters which
+ * have Zs, Zl, or Zp in their GENERAL CATEGORY */
+
+#define NUM_SPACE_INFO 5
+static const unirange space_info[NUM_SPACE_INFO] = {
+  {0x20,0x20}, {0xa0,0xa0}, {0x2000,0x200b}, {0x2028,0x2029}, 
+  {0x3000,0x3000}
+};
+
+
+
+/* parse_utf8_char parses the next utf-8 character, placing its
+ * unicode equivalent in *value. The length of the utf-8 character
+ * is returned */
+int parse_utf8_char(const unsigned char *here,
+		    const unsigned char *end,
+		    unsigned short *value) {
+  unsigned short c1;
+  unsigned short c2;
+  unsigned short c3;
+  
+  *value = 0; /* will be the value for illegal characters */
+  
+  if (here <= end) {
+    c1 = (unsigned short)(*here);
+    
+    if (c1 <= 0x7f) {
+      /* found a one-byte character */
+      *value = c1;
+      return 1;
+      
+    } else if (c1 >= 0xc0 && c1 <= 0xdf && here+1 <= end &&
+	       (c2=(unsigned short)(*(here+1))) >= 0x80) {
+      /* found a two-byte character */
+      *value = ((c1 & 0x1f) << 6) + (c2 & 0x3f);
+      return 2;
+      
+    } else if (c1 >= 0xe0 && c1 <= 0xef && here+2 <= end &&
+	       (c2=(unsigned int)(*(here+1))) >= 0x80 &&
+	       (c3=(unsigned int)(*(here+2))) >= 0x80) {
+      /* found a three-byte character */
+      *value = ((c1 & 0xf) << 12) + ((c2 & 0x3f) << 6) +
+	(c3 & 0x3f);
+      return 3;
+      
+    } else {
+      /* error in encoding, return the length as 1 */
+      return 1;
+    }
+  }
+
+  /* end of string */
+  return 0;
+}
+
+
+/* output_utf8_char encodes a unicode character as a UTF-8 character.
+ * The length of the encoding is returned. If the string was not
+ * long enough to encode the character 0 is returned. */
+int output_utf8_char (unsigned short value,
+		      unsigned char *here,
+		      unsigned char *end) {
+  int len = (int)(end - here + 1); /* end is the last character */
+  if (value < 0x80) {
+    /* will be encoded in one byte */
+    if (len >= 1) {
+      *here = (unsigned char)value;
+      return 1;
+    }
+
+  } else if (value < 0x800) {
+    /* will be encoded in two bytes */
+    if (len >= 2) {
+      *here++ = 0xc0 | (unsigned char)((value >> 6) & 0x1f);
+      *here = 0x80 | (unsigned char)(value & 0x3f);
+
+      return 2;
+    }
+
+  } else {
+    /* will be encoded in three bytes */
+    if (len >= 3) {
+      *here++ = 0xe0 | (unsigned char)((value >> 12) & 0xf);
+      *here++ = 0x80 | (unsigned char)((value >> 6) & 0x3f);
+      *here = 0x80 | (unsigned char)(value & 0x3f);
+      return 3;
+    }
+  }
+
+  return 0;
+}
+
+
+static int lookup_decompose_table (unsigned short value) {
+  int first = 0;
+  int last = NUM_DECOMPOSE_INFO;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < decompose_info[here].ch) {
+      last = here;
+    } else if (value > decompose_info[here].ch) {
+      first = here+1;
+    } else {
+      return here;
+    }
+  }
+
+  return -1;
+}
+
+/* decompose_str will decompose a unicode string into its canonical
+ * equivalents. NULL is returned if the input array was not 
+ * large enough to contain the fully decomposed string (the array
+ * will be in a correct, but partially decomposed state). The input 
+ * must be null-terminated. */
+unsigned short *decompose_str (unsigned short *input,
+			       int max_output_len) {
+  int curpos = 0;
+  int curlen = unicode_strlen (input);
+  int tablepos = 0;
+  int leninc;
+  int i;
+
+  while (input[curpos] != 0) {
+    tablepos = lookup_decompose_table (input[curpos]);
+    if (tablepos >= 0) {
+      /* found a decomposition */
+      leninc = unicode_strnlen (decompose_info[tablepos].str, 3)-1;
+      if (curlen+leninc < max_output_len) {
+	/* enough space for this decomposition */
+
+	if (leninc > 1) {
+	  /* we have to shift the array along */
+	  for (i=curlen-1; i>=curpos; i--) {
+	    input[i+leninc] = input[i];
+	  }
+	  input[curlen+leninc] = 0;
+	  curlen += leninc;
+	}
+
+	/* decompose the character */
+	for (i=0; i<=leninc; i++) {
+	  input[curpos+i] = decompose_info[tablepos].str[i];
+	}
+
+	/* note: curpos is not incremented because the decomposed */
+	/*       characters might need decomposing */
+
+      } else {
+	/* no space left in string */
+	return NULL;
+      }
+
+    } else {
+      /* the character cannot be decomposed */
+      curpos++;
+    }
+  }
+
+  return input;
+}
+
+
+
+/* unicode_cat_data is a combination of the letter, digit, and
+ * space information in a form that is quick to look up.
+ * The information about each byte is stored in two bits
+ *
+ * 00 - neither letter, digit or space
+ * 01 - letter
+ * 10 - space
+ * 11 - digit
+ *
+ * You can test for letter | space by looking at the right
+ * column
+ */
+unsigned long *unicode_cat_data;
+
+void create_unicat_data () {
+  int i=0;
+  int min_start=0;
+  int min_end=0;
+  register unsigned long min_value=0; /* filling the whole byte */
+  
+  const unirange *letter_here = letter_info;
+  const unirange *letter_end = letter_info+NUM_LETTER_INFO;
+  const unirange *digit_here = digit_info;
+  const unirange *digit_end = digit_info+NUM_DIGIT_INFO;
+  const unirange *space_here = space_info;
+  const unirange *space_end = space_info+NUM_SPACE_INFO;
+
+  if (unicode_cat_data != NULL) return;
+  unicode_cat_data = malloc (0x10000/4);
+  if (unicode_cat_data == NULL) return;
+  
+  while (i <= 0xffff) {
+    /* decide what to fill with next */
+    min_start = i;
+    min_end = 0xffff;
+    min_value = 0; /* 00 */
+    
+    while (letter_here < letter_end && letter_here->last < i) letter_here++;
+    if (letter_here < letter_end && letter_here->last <= min_end) {
+      min_start = letter_here->first;
+      min_end = letter_here->last;
+      min_value = 0x55555555; /* 01 */
+    }
+    
+    while (digit_here < digit_end && digit_here->last < i) digit_here++;
+    if (digit_here < digit_end && digit_here->last <= min_end) {
+      min_start = digit_here->first;
+      min_end = digit_here->last;
+      min_value = 0xffffffff; /* 11 */
+    }
+    
+    while (space_here < space_end && space_here->last < i) space_here++;
+    if (space_here < space_end && space_here->last <= min_end) {
+      min_start = space_here->first;
+      min_end = space_here->last;
+      min_value = 0xaaaaaaaa; /* 10 */
+    }
+
+    if (min_start < i) min_start = i;
+    else if (min_start > i) {
+      min_end = min_start-1;
+      min_start = i;
+      min_value = 0; /* 00 */
+    }
+
+    /* fill the entries from min_start to min_end with min_value */
+    while (i <= min_end) {
+      /* special version for common case */
+      if (i % 16 == 0) {
+	unicode_cat_data[i/16] = min_value; /* doesn't matter if too long */
+	i += 16;
+	if (i > min_end) {
+	  i = min_end+1;
+	  break;
+	}
+	
+      } else {
+	register unsigned long mask = 3 << ((i % 16) * 2);
+	unicode_cat_data[i/16] = (unicode_cat_data[i/16] & (~mask)) |
+				  (min_value & mask);
+	i++;
+      }
+    }
+  }
+}
+
+/* does a binary search on a range table */
+static int in_unirange (const unirange *info, int num_info,
+			unsigned short value) {
+  int first = 0;
+  int last = num_info;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < info[here].first) {
+      last = here;
+    } else if (value > info[here].last) {
+      first = here+1;
+    } else {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+
+
+/* tests to see whether 'value' is a valid Unicode letter */
+int is_unicode_letter (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(letter_info, NUM_LETTER_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x1);
+}
+
+/* tests to see whether 'value' is a valid Unicode digit */
+int is_unicode_digit (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(digit_info, NUM_DIGIT_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x3);
+}
+
+/* tests to see whether 'value' is a valid Unicode letter or 
+ * digit */
+int is_unicode_letdig (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return (in_unirange(letter_info, NUM_LETTER_INFO, value) ||
+	    in_unirange(digit_info, NUM_DIGIT_INFO, value));
+    
+  }
+
+  return ((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x1);
+}
+
+/* tests to see whether 'value' is a valid space
+ * The test includes both "C" spaces and "Unicode" spaces, i.e. 
+ * form-feed, newline, carriage return, horizontal tab,
+ * vertical tab, and the Zs, Zl, and Zp Unicode categorizations */
+int is_unicode_space (unsigned short value) {
+  if (value < 0x80) return isspace (value);
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(space_info, NUM_SPACE_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x2);
+}
+
+/* returns the length of the unicode string */
+int unicode_strlen (const unsigned short *str) {
+  int len = 0;
+
+  /* keep going till you find the null-terminator */
+  while (*str != 0) {
+    len++;
+    str++;
+  }
+
+  return len;
+}
+
+/* returns the length of the unicode string, up to a maximum */
+int unicode_strnlen (const unsigned short *str, int max_length) {
+  int len = 0;
+
+  /* keep going till you find the null-terminator */
+  while (*str != 0 && len < max_length ) {
+    len++;
+    str++;
+  }
+
+  return len;
+}
+
+
+/* Does a binary search on a relate table, returns -1 if it is not
+ * found. */
+static int search_unirelate (const unirelate *info, int num_info,
+			     unsigned short value) {
+  int first = 0;
+  int last = num_info;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < info[here].ch1) {
+      last = here;
+    } else if (value > info[here].ch1) {
+      first = here+1;
+    } else {
+      return here;
+    }
+  }
+
+  return -1;
+}
+
+
+/* returns the upper-case equivalent of value */
+unsigned short unicode_toupper (unsigned short value) {
+  int related = search_unirelate (toupper_info, NUM_TOUPPER_INFO, value);
+  if (related >= 0) {
+    value = toupper_info[related].ch2;
+  }
+  return value;
+}
+
+/* returns the lower-case equivalent of value */
+unsigned short unicode_tolower (unsigned short value) {
+  int related = search_unirelate (tolower_info, NUM_TOLOWER_INFO, value);
+  if (related >= 0) {
+    value = tolower_info[related].ch2;
+  }
+  return value;
+}
+
+/* returns the simplified Chinese character equivalent of 
+ * another Chinese character */
+unsigned short unicode_tosimplified (unsigned short value) {
+  int related = search_unirelate (tosimplified_info, NUM_TOSIMPLIFIED_INFO, value);
+  if (related >= 0) {
+    value = tosimplified_info[related].ch2;
+  }
+  return value;
+}
+
+
+/* converts a utf-8 word (string with length stored in the first byte
+ * to a Unicode array. To handle all situations the output buffer should
+ * be 256 unsigned shorts long. The output will also have the length as
+ * the first entry. */
+unsigned short *utf8_word_to_unicode (const unsigned char *input,
+				      unsigned short *output,
+				      int max_output_length) {
+  int inlen = *input;
+  const unsigned char *inhere = input+1;
+  const unsigned char *inend = &inhere[inlen-1];
+  unsigned short c;
+  int clen;
+  
+  int outlen = 0;
+  unsigned short *outhere = output+1;
+  
+  while (inhere <= inend && inlen < max_output_length-1) {
+    /* decode the character */
+    clen = parse_utf8_char (inhere, inend, &c);
+    inhere += clen;
+
+    if (clen > 0) {
+      /* store the character */
+      *outhere = c;
+      outhere++;
+      outlen++;
+    }
+  }
+
+  output[0] = outlen;
+  return output;
+}
+
+
+/* converts a unicode word buffer (with the length stored in the
+ * entry) to a utf8 encoded word output (with the length stored in
+ * the first byte. Only 255 bytes (not characters) can be stored 
+ * in the output. */
+unsigned char *unicode_to_utf8_word (const unsigned short *input,
+				     unsigned char *output,
+				     int max_output_length) {
+  int inlen = *input;
+  const unsigned short *word = input + 1;
+  int in_i = 0;
+  int clen;
+
+  int outlen = 0;
+  unsigned char *outhere = output+1;
+  unsigned char *outend = output+max_output_length;
+
+  while (in_i < inlen) {
+    if ((clen=output_utf8_char(word[in_i], outhere, outend)) == 0) {
+      /* ran out of space to encode the character */
+      break;
+    }
+
+    outhere += clen;
+    outlen += clen;
+    in_i++;
+  }
+
+  output[0] = outlen;
+
+  return output;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/lib/unitool.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/unitool.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/unitool.h	(revision 16561)
@@ -0,0 +1,88 @@
+#ifndef UNITOOL_H
+#define UNITOOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* This module is based on Unicode 2.1 */
+
+
+/* parse_utf8_char parses the next utf-8 character, placing its
+ * unicode equivalent in *value. The length of the utf-8 character
+ * is returned. end is the address of the last character. */
+int parse_utf8_char (const unsigned char *here,
+		     const unsigned char *end,
+		     unsigned short *value);
+
+/* output_utf8_char encodes a unicode character as a UTF-8 character.
+ * The length of the encoding is returned. If the string was not
+ * long enough to encode the character 0 is returned. end is the
+ * address of the last character */
+int output_utf8_char (unsigned short value,
+		      unsigned char *here,
+		      unsigned char *end);
+
+/* decompose_str will decompose a unicode string into its canonical
+ * equivalents. NULL is returned if the input array was not 
+ * large enough to contain the fully decomposed string (the array
+ * will be in a correct, but partially decomposed state). The input 
+ * must be null-terminated. */
+unsigned short *decompose_str (unsigned short *input,
+			       int max_output_len);
+
+/* tests to see whether 'value' is a valid Unicode letter */
+int is_unicode_letter (unsigned short value);
+
+/* tests to see whether 'value' is a valid Unicode digit */
+int is_unicode_digit (unsigned short value);
+
+/* tests to see whether 'value' is a valid Unicode letter or 
+ * digit */
+int is_unicode_letdig (unsigned short value);
+
+/* tests to see whether 'value' is a valid space
+ * The test includes both "C" spaces and "Unicode" spaces, i.e. 
+ * form-feed, newline, carriage return, horizontal tab,
+ * vertical tab, and the Zs, Zl, and Zp Unicode categorizations */
+int is_unicode_space (unsigned short value);
+
+/* returns the length of the unicode string */
+int unicode_strlen (const unsigned short *str);
+
+/* returns the length of the unicode string, up to a maximum */
+int unicode_strnlen (const unsigned short *str, int max_length);
+
+/* returns the upper-case equivalent of value */
+unsigned short unicode_toupper (unsigned short value);
+
+/* returns the lower-case equivalent of value */
+unsigned short unicode_tolower (unsigned short value);
+
+/* returns the simplified Chinese character equivalent of 
+ * another Chinese character */
+unsigned short unicode_tosimplified (unsigned short value);
+
+
+/* converts a utf-8 word (string with length stored in the first byte
+ * to a Unicode array. To handle all situations the output buffer should
+ * be 256 unsigned shorts long. The output will also have the length as
+ * the first entry. */
+unsigned short *utf8_word_to_unicode (const unsigned char *input,
+				      unsigned short *output,
+				      int max_output_length);
+
+/* converts a unicode word buffer (with the length stored in the
+ * entry) to a utf8 encoded word output (with the length stored in
+ * the first byte. Only 255 bytes (not characters) can be stored 
+ * in the output. */
+unsigned char *unicode_to_utf8_word (const unsigned short *input,
+				     unsigned char *output,
+				     int max_output_length);
+
+#ifdef __cplusplus
+	   }
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/lib/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/win32.mak	(revision 16561)
@@ -0,0 +1,91 @@
+###########################################################################
+#
+# win32 makefile -- mg\lib
+# 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  The 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.
+#
+###########################################################################
+ 
+MGHOME = ..
+
+AR = lib
+CC = cl
+CPPFLAGS = 
+
+DEFS = -DQUIET -DHAVE_CONFIG_H -D__WIN32__ -D_LITTLE_ENDIAN -DPARADOCNUM \
+       -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+
+INCLUDES = -I"." -I"$(MGHOME)"
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES)
+ 
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+o = .obj
+ 
+HEADERS = \
+    bitio_m_random.h   filestats.h        local_strings.h    lovinstem.h \
+    bitio_m_stdio.h    getopt.h           memlib.h \
+    bitio_gen.h        bitio_mem.h        mgheap.h             messages.h \
+    bitio_m.h          bitio_mems.h       huffman.h          timing.h \
+    bitio_m_mem.h      bitio_random.h     huffman_mem.h      perf_hash.h \
+    bitio_m_mems.h     bitio_stdio.h      huffman_stdio.h    sptree.h \
+                       rx.h               pathmax.h          getpagesize.h \
+    random.h           win32in.h          simplefrenchstem.h unitool.h \
+    longlong.h	
+
+SOURCES = \
+    bitio_random.c    mgheap.c          memlib.c \
+    bitio_stdio.c     huffman.c         messages.c \
+    bitio_gen.c       filestats.c       huffman_mem.c     perf_hash.c \
+    bitio_mem.c       getopt.c          huffman_stdio.c   sptree.c \
+    bitio_mems.c      getopt1.c         local_strings.c   lovinstem.c \
+    timing.c                            rx.c              \
+    alloca.c          error.c           xmalloc.c         strstr.c \
+    ftruncate.c       strcasecmp.c      random.c          win32in.c \
+    simplefrenchstem.c                  unitool.h
+
+OBJECTS =   rx$o \
+    bitio_random$o    mgheap$o            memlib$o \
+    bitio_stdio$o     huffman$o         messages$o \
+    bitio_gen$o       filestats$o       huffman_mem$o     perf_hash$o \
+    bitio_mem$o       getopt$o          huffman_stdio$o   sptree$o \
+    bitio_mems$o      getopt1$o         local_strings$o   lovinstem$o \
+    timing$o           \
+    error$o           xmalloc$o \
+    random$o          win32in$o         simplefrenchstem$o \
+    unitool$o
+ 
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : libmglib.lib
+
+libmglib.lib : $(OBJECTS)
+        if exist libmglib.lib del libmglib.lib
+	$(AR) /out:libmglib.lib $(OBJECTS)
+
+install: 
+
+clean:
+        if exist *$o del *$o 
+        if exist libmglib.lib del libmglib.lib
Index: /gsdl/trunk/gs2common/indexers/mg/lib/win32in.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/win32in.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/win32in.c	(revision 16561)
@@ -0,0 +1,86 @@
+#include "longlong.h"
+
+/* This module provides an equivalent of <netinet/in.h> on  */
+/* unix systems. */
+
+unsigned long htonl(unsigned long x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) |
+		((x << 8) & 0x00FF0000) | ((x << 24) & 0xFF000000);
+#endif
+
+	return x;
+}
+
+mg_ullong ntohll(mg_ullong x)
+{
+#if defined (LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = 
+	  ((x >> 56) & 0x00000000000000FF) | ((x >> 40) & 0x000000000000FF00) |
+	  ((x >> 24) & 0x0000000000FF0000) | ((x >> 8)  & 0x00000000FF000000) |
+	  ((x << 8)  & 0x000000FF00000000) | ((x << 24) & 0x0000FF0000000000) |
+		((x << 40) & 0x00FF000000000000) | ((x << 56) & 0xFF00000000000000);
+#endif
+  return x;
+}
+
+unsigned long ntohl(unsigned long x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) |
+		((x << 8) & 0x00FF0000) | ((x << 24) & 0xFF000000);
+#endif
+	return x;
+}
+
+unsigned short htons(unsigned short x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00);
+#endif
+	return x;
+}
+
+unsigned short ntohs(unsigned short x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00);
+#endif
+	return x;
+}
+
+/* just to test stuff
+#include <stdio.h>
+
+main () {
+	long x;
+	short sx;
+
+	x = 0x000000FF;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0x0000FF00;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0x00FF0000;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0xFF000000;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n\n",(int)x);
+
+	sx = 0x00FF;
+	printf ("before %i, ",(int)sx);
+	sx = htons (sx);
+	printf ("after %i\n",(int)sx);
+
+	sx = 0xFF00;
+	printf ("before %i, ",(int)sx);
+	sx = htons (sx);
+	printf ("after %i\n",(int)sx);
+}
+*/
Index: /gsdl/trunk/gs2common/indexers/mg/lib/win32in.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/win32in.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/win32in.h	(revision 16561)
@@ -0,0 +1,10 @@
+/* This module provides an equivalent of <netinet/in.h> on  */
+/* unix systems. */
+
+#include "longlong.h"
+
+unsigned long htonl(unsigned long x);
+unsigned long ntohl(unsigned long x);
+mg_ullong ntohll(mg_ullong x);
+unsigned short htons(unsigned short x);
+unsigned short ntohs(unsigned short x);
Index: /gsdl/trunk/gs2common/indexers/mg/lib/xmalloc.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/lib/xmalloc.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/lib/xmalloc.c	(revision 16561)
@@ -0,0 +1,111 @@
+/* xmalloc.c -- malloc with out of memory checking
+   Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+/* This is for other GNU distributions with internationalized messages.
+   The GNU C Library itself does not yet support such messages.  */
+#if HAVE_LIBINTL_H
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+/* Exit value when the requested amount of memory is not available.
+   The caller may set it to some other value.  */
+int xmalloc_exit_failure = EXIT_FAILURE;
+
+#if defined(__WIN32__)
+void error (int, int, const char *, ...);
+#else
+#if __STDC__ && (HAVE_VPRINTF || HAVE_DOPRNT)
+void error (int, int, const char *, ...);
+#else
+void error (int, int, char *, char *, char *, char *, char *, char *, char *, char *, char *);
+#endif
+#endif
+
+static VOID *
+fixup_null_alloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = 0;
+  if (n == 0)
+    p = malloc ((size_t) 1);
+  if (p == 0)
+    error (xmalloc_exit_failure, 0, gettext ("Memory exhausted"));
+  return p;
+}
+
+/* Allocate N bytes of memory dynamically, with error checking.  */
+
+VOID *
+xmalloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = malloc (n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+   with error checking.
+   If P is NULL, run xmalloc.  */
+
+VOID *
+xrealloc (p, n)
+     VOID *p;
+     size_t n;
+{
+  if (p == 0)
+    return xmalloc (n);
+  p = realloc (p, n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_compression_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_compression_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_compression_dict.1	(revision 16561)
@@ -0,0 +1,219 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_compression_dict 1 \*(Dt CITRI
+.SH NAME
+mg_compression_dict \- build a compression dictionary.
+.SH SYNOPSIS
+.B mg_compression_dict
+[
+.B \-h
+]
+[
+.BR \-C " |"
+.BR \-P " |"
+.B  \-S
+]
+.if n .ti +9n
+[
+.BR \-0 " |"
+.BR \-1 " |"
+.BR \-2 " |"
+.B  \-3
+]
+[
+.BR \-H " |"
+.BR \-B " |"
+.BR \-D " |"
+.BR \-Y " |"
+.B  \-M
+]
+.if n .ti +9n
+.if t .ti +.5i
+[
+.BI \-l " lookback"
+]
+[
+.BI \-k " mem"
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_compression_dict
+builds a compression dictionary based on the statistics gathered
+during the first pass over the text.  The options to the program are
+mainly concerned with limiting the amount of memory the dictionary
+will use and with how the text compressor will cope with any novel
+words found during the compression phase.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-C
+Build a complete dictionary from the statistics file.  If during the
+text compression phase a novel word is found, then the compressor will
+produce an error message and stop.
+.TP
+.B \-P
+Build a partial dictionary from the statistics file.  This dictionary
+assumes that the statistics file are based on the entire text.  The
+statistics of words not includes in the dictionary are used to
+calculate the escape probability.  If novel words are being coded
+character by character, then there may not be a Huffman code for every
+possible character.  This means that the compressor may fail if a novel
+word contains a novel character.
+.TP
+.B \-S
+Build a seed dictionary from the statistics file.  This dictionary
+assumes that the statistics file is based on only a portion of the
+text to be compressed.  The probability of a novel word is based on the
+number of words that have only occurred once.  If novel words are being
+coded character by character, then the Huffman codes for characters are
+based on the frequency of characters in the dictionary.
+.TP
+.B \-0
+All words from the statistics file are included in the built
+dictionary.
+.TP
+.B \-1
+Words are included in the dictionary until the dictionary reaches the
+desired size.  Words are selected for the dictionary based on the order
+they occurred in the source text.
+.TP
+.B \-2
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.
+.TP
+.B \-3
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.  Words are the shuffled back and forth between the
+`keep' and `discard' lists to find the `optimal' set of words that
+should be in the dictionary.
+.TP
+.B \-H
+This specifies that novel words will be coded character by character
+using Huffman codes.
+.TP
+.B \-B
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using binary codes.  The binary code represents their occurrence
+position in the auxiliary dictionary.
+.TP
+.B \-D
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using delta codes.  The delta code represents their occurrence position
+in the auxiliary dictionary.
+.TP
+.B \-Y
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This generally
+produces better compression than
+.B \-B
+or
+.BR \-D .
+.TP
+.B \-M
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This method is
+adaptive within documents, and generally produces better compression
+than
+.BR \-B ,
+.B \-D
+or
+.BR \-Y .
+.TP
+.BI \-l " lookback"
+The generated dictionary is designed to be front coded when it is
+loaded into memory.  Under normal circumstances, a front-coded
+dictionary would require scanning from the beginning in order to find
+any particular word.  However, every
+.I lookback
+words in the dictionary, the whole word is stored and a pointer to that
+word maintained.  E.g., if
+.I lookback
+is 4, then every fourth word is stored in its entirety.
+.TP
+.BI \-k " mem"
+This limits the amount of memory to use for the generated
+dictionary.  Words are selected for the dictionary based of the text
+statistics, and whether
+.BR \-0 , " \-1" , " \-2"
+or
+.B \-3
+is specified.  The memory is calculated assuming a lookback of 0,
+irrespective of what actual lookback is specified.  This means that if
+a non-zero lookback is given, the dictionary will actually occupy
+less space than specified by
+.BR \-k .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.stats
+Statistics about the source text.
+.TP
+.B *.text.dict
+Compression dictionary for the source text.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_decompress_text.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_decompress_text.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_decompress_text.1	(revision 16561)
@@ -0,0 +1,78 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_decompress_text 1 \*(Dt CITRI
+.SH NAME
+mg_decompress_text \- decompresses the compressed text of a collection
+.SH SYNOPSIS
+.B mg_decompress_text
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_decompress_text
+decompresses the compressed text of a collection, inserts paragraph and
+document markers, writing the output to
+.IR stdout ".  "
+This program was created
+to be used as a type of
+.BR mg_get (1).
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.text
+Compressed documents.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.invf.paragraph
+Paragraph counts for documents.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_passes (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_fast_comp_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_fast_comp_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_fast_comp_dict.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_fast_comp_dict 1 \*(Dt CITRI
+.SH NAME
+mg_fast_comp_dict \- build a fast loading compression dictionary
+.SH SYNOPSIS
+.B mg_fast_comp_dict
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_fast_comp_dict
+builds a fast-loading compression dictionary from the standard
+compression dictionary.  The new dictionary has the property that
+.BR mgquery (1)
+can load it in very quickly, making the startup time for
+.BR mgquery (1)
+much shorter.  The new dictionary is larger on disk than the original,
+but they are no different when it comes to memory usage.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.dict
+Compression dictionary.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get.1	(revision 16561)
@@ -0,0 +1,154 @@
+.TH mg_get 1 "31 January 1994"
+.SH NAME
+mg_get \- output source texts for processing
+.SH SYNOPSIS
+ 
+.B mg_get
+.I collection-name
+.RB [ \-init | \-i | \-text\fP|\fB\-t\fP|\fB\-cleanup\fP|\fB\-c\fP]
+.SH DESCRIPTION
+.LP
+This program is the default one used by mgbuild to generate the source text for the MG system. Any program may be used to generate the source text for mgbuild as long as it confirms to the interface specified here. 
+.SH OPTIONS
+.LP
+The
+.I collection\-name
+must appear before any other option. Only the first option has any significance. If no option is specified
+.B \-text
+is assumed.
+.RS
+.TP
+.BR \-init " and " \-i
+The program is called once with this flag at the start of building a collection.
+.TP
+.BR \-text " and " \-t
+The program is called with this flag multiple times during the building of a collection. The program outputs (on stdout) the text of the collection. \*(lq\fBDocuments\fP\*(rq within the collection are separated with by ctrl-B's (ASCII code 2). \*(lq\fBParagraphs\fP\*(rq within the collection are separated with ctrl-C's (ASCII code 3). A collection need not contain paragraphs unless a level 3 inverted file is being generated (see 
+.B mgbuild 
+and 
+.BR mg_passes ). 
+.TP
+.BR \-cleanup " and " \-c
+The program is called once with this flag at the completion of building a collection.
+.SH ENVIRONMENT
+.TP
+.SB MGDATA
+If this environment variable exists then its value is used a the default 
+directory where the mg collection files are. If this variable does not exist
+then the directory \*(lq\fB.\fP\*(rq is used by default. The command line 
+option 
+.BI \-d " directory"
+overrides the directory in 
+.BR MGDATA .
+.TP
+.SB MG_GETRC
+This environment variable specifies where the file containing the users 
+mg source configurations is. If not set, mg_get uses a default of 
+.B \*\~/.mg_getrc.
+
+This file contains TAB delimited lines of the form
+.RS 
+.RS 
+.I CollectionName
+.I CollectionType
+.I files or directory
+.RE
+
+.I CollectionName
+is the name of the collection supplied to mg_get.
+
+.I CollectionType
+specifies how mg_get should process the
+named files and directories and are descibed below.
+
+.I files or directory
+is either a list of files separted by blanks or a single directory.
+Some of the
+.I CollectionTypes
+deal with files and others with just a single directory.
+Any files used ending with .gz or .Z are decompressed with gzip before
+processing. References to '~' expands to the users HOME directory.
+
+.I CollectionType
+is one of
+.RS
+.TP
+.BR PARA
+For text based documents. The list of files specified are treated as
+a series of paragraphs separated by blank lines. Each paragraph becomes
+a seperate document on the indexed collection.
+.TP
+.BR MAIL
+for mail files. The list of files specified are treated as UNIX mail
+files separated by lines starting with 'From'. Each mail message becomes
+a seperate document on the indexed collection. As a extra feature any
+embedded tarmail encoded contents (enclosed by a 'xbtoa Begin' and 'xbtoa End'
+pair are removed.
+.TP
+.BR DIR
+(and DIR2)
+For a single directory of files. Each file in the directory (and in any of
+it's subdirectories) are treated as a single document. With
+.I DIR
+the pathname of the file is prefixed to the contents of each file as an
+extra line while this is not done for
+.I DIR2
+collections.
+.TP
+.BR BIB
+for biblography files. The list of files specified are treated as
+a series of biblography files (eg BIBTEX or TROFF) separated by lines
+starting with '@'. Each reference  becomes a seperate document on the indexed
+collection.
+.TP
+.BR TXTIMG
+for integrated collections of text and images. The single directory should
+contain files that have the same prefix if they are related. For example,
+monaLisa.pgm might be a gray-level image, and monaLisa.txt would be a textual
+file describing the image. The suffixes recognised are:
+.BR   .txt
+for  ascii text
+.BR  .ptm
+for  scanned text stored as a bilevel image
+.BR  .pbm
+for a black and white image (typically a line drawing)
+.BR  .pgm
+for a gray-scale image
+.BR In addition, if no corresponding ascii text file is found for
+a .pbm or .pgm file, then one is created with suffix .tmp.txt,
+and it stores the name of the image file (in principle it could
+store the OCR of a .txt.pbm file). At present the .tmp.txt files
+are deleted by the '-cleanup' option.
+
+.SH "EXAMPLE"
+
+An example ~/.mg_getrc file might look like:
+   alice   PARA    /users/alice13a.txt.Z
+   mail95  MAIL    ~/MAIL/1995/*
+   doc     DIR     ~/documents
+   bibs    BIB     ~/etc/REFS/*.bib ~/etc/REFS/CC/*.bib
+   letters DIR     ~/LETTERS
+   davinci TXTIMG  ~/images/davinci
+
+.SH "SEE ALSO"
+.LP
+.BR mg_compression_dict (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_make_fast_dict (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1)
+
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_get_merge.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_get_merge 1 \*(Dt CITRI
+.SH NAME
+mg_get_merge \- output source texts for processing
+.SH SYNOPSIS
+.B mg_get_merge
+.I collection-name
+.if n .ti +9n
+[
+.BR \-init " |"
+.BR \-i " |"
+.BR \-text " |"
+.BR \-t " |"
+.BR \-cleanup " |"
+.B  \-c
+]
+.SH DESCRIPTION
+This program is the default one used by
+.BR mgmerge (1)
+to generate the source text for new documents to appended to
+an
+.BR mg (1)
+system database.
+Its function is exactly the same as
+.BR mg_get (1)
+except that it generates the 
+.I new 
+documents to be added to an
+.BR mg (1)
+system database that has already been built using
+.BR mgbuild(1).
+Any program may be used to generate the source
+text for
+.BR mgmerge (1),
+as long as it confirms to the interface specified
+here.
+.SH OPTIONS
+The
+.I collection-name
+must appear before any other option.  Only the first option has any
+significance.  If no option is specified,
+.B \-text
+is assumed.
+.TP "\w'\fB\-cleanup\fP and \fB\-c\fP'u+2n"
+.BR \-init " and " \-i
+The program is called once with this flag at the start of merging a
+collection.
+.TP
+.BR \-text " and " \-t
+The program is called with this flag multiple times during the
+merging of a collection.  The program outputs (on
+.IR stdout )
+the text of the collection.
+.I Documents
+within the collection are separated by ctrl-B's (ASCII code 2).
+.TP
+.BR \-cleanup " and " \-c
+The program is called once with this flag at the completion of
+merging a collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_get (1),
+.BR mgbuild (1),
+.BR mgmerge (1),
+.BR mg_text_merge (1),
+.BR mg_invf_merge (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_hilite_words.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_hilite_words.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_hilite_words.1	(revision 16561)
@@ -0,0 +1,95 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_hilite_words 1 \*(Dt CITRI
+.SH NAME
+mg_hilite_words \- display text and highlight particular words which it contains
+.SH SYNOPSIS
+.B mg_hilite_words
+.if n .ti +9n
+[
+.B --stem_method [0-3]
+]
+.if n .ti +9n
+[
+.B --stemmer [english|lovin|french|simplefrench]
+]
+.if n .ti +9n
+[
+.B --style [bold|underline|italic|emphasis|strong]
+]
+.if n .ti +9n
+[
+.B --pager [less|more|html|???]
+]
+.if n .ti +9n
+.I list-of-words-to-highlight
+
+.SH DESCRIPTION
+.B mg_hilite_words
+reads text from stdin and outputs it into a pager such as
+less. It's command arguments include a list of stemmed words
+which if extracted from the text will be highlighted on the output
+using a highlighting style.
+
+It has been extended to output the highlighted text using html
+straight to stdout.
+
+.SH OPTIONS
+Options may appear in any order, but the
+.IR list-of-words-to-highlight ,
+must be last.
+.TP
+.B --stem_method [0-3]
+This option allows one to set the stemming method for the extraction of
+the words from the incoming text.
+This should match the way the inverted file index was created.
+The default is 3, which is fold and stem.
+.TP
+.B --stemmer [english|lovin|french|simplefrench]
+This option allows you to select the stemmer to use.  The
+default is the English stemmer.
+.TP
+.B --style [bold|underline|italic|emphasis|strong]
+This option sets the highlighting style.
+.IR Bold 
+and 
+.IR underline
+are utilised by the pager or for html output.
+.IR Italic,
+.IR emphasis,
+and
+.IR strong
+can only be used for html output.
+.TP
+.B --pager [less|more|html|???]
+One can use the standard 
+.IR less
+or
+.IR more
+pages for displaying the highlighted text.
+Or one can generate highlighted html using the paging setting of
+.IR html.
+One can also specify a custom pager of their own choosing (i.e. ???).
+.TP
+.B list-of-words-to-highlight
+Each word is taken as a command line argument.
+Each word should already be stemmed and folded according to
+the stem option.
+.SH USAGE
+This program was written primarily to be called by 
+.IR mgquery(1)
+.
+When the user selects the mode of "hilite" then this program
+is called for displaying the query-result documents instead of
+just the pager.
+.SH "SEE ALSO"
+.na
+.BR mgquery (1).
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dict.1	(revision 16561)
@@ -0,0 +1,104 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_dict 1 \*(Dt CITRI
+.SH NAME
+mg_invf_dict \- build an `on-disk' stemmed dictionary
+.SH SYNOPSIS
+.B mg_invf_dict
+[
+.B \-h
+]
+[
+.B \-F
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_invf_dict
+builds an `on-disk' stemmed dictionary from the
+compressed stemmed dictionary that the
+.BR mgquery (1)
+program can access.  Each entry in the dictionary contains the disk
+pointer to the associated inverted file entry, and various other pieces
+of information about the stemmed word.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " num"
+The dictionary is stored in blocks on disk; this option is used to set
+the block size.  The default is 4-KB blocks.
+.TP
+.B \-F
+This option causes the disk blocks to be aligned on the appropriate
+boundaries.  For example, if 4-KB blocks are used, they will be aligned
+in the file on 4-KB boundaries.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dump.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dump.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_dump.1	(revision 16561)
@@ -0,0 +1,142 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_dump 1 \*(Dt CITRI
+.SH NAME
+mg_invf_dump \- Dump out an inverted file in ASCII
+.SH SYNOPSIS
+.B mg_invf_dump
+[
+.B \-h
+]
+[
+.B \-b
+]
+[
+.B \-w
+]
+[
+.B \-t
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program dumps out an inverted file produced by the
+.BR mg (1)
+system as ASCII numbers.  This program could be used in conjunction
+with
+.BR mgdictlist (1)
+to write simple shell programs that work with the inverted files.  The
+output from the program looks something like this:
+.LP
+.RS
+.ft 3
+.nf
+6 337
+1
+ 1 1
+1
+ 5 1
+1
+ 2 1
+1
+ 1 1
+4
+ 1 3
+ 2 7
+ 3 2
+ 5 2
+1
+ 1 1
+ . . .
+.fi
+.ft
+.RE
+The first number (6) is the number of documents in the collection.  It
+is followed by the number of different terms in the collection.  For
+each term, the number of different documents it occurs in follows. The
+document numbers that the term occurs in are listed next.  If
+.B \-w
+is specified, then the number of times the term occurred in the
+document is printed alongside the document number.
+If
+.B \-t
+is specified, the term itself is also displayed in quotes.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-b
+This option will cause the output from the program to be in fixed-size
+binary numbers, rather than in ASCII.
+.TP
+.B \-w
+If the inverted file is a level-2 or level-3 inverted file, this causes
+the word counts per document to be output.
+.TP
+.B \-t
+This option causes each term to be displayed along with the number of
+documents it appears in. The term is printed in quotes,
+as in "the".
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf.dict
+The compressed stemmed dictionary.
+.TP
+.B *.invf.ORG
+The original inverted file saved by
+.BR mg_invf_rebuild .
+.TP
+.B *.invf
+The inverted file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_merge.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_merge 1 \*(Dt CITRI
+.SH NAME
+mg_invf_merge \- Merge two inverted files and stemmed dictionaries
+.SH SYNOPSIS
+.B mg_invf_merge
+[
+.B \-h
+]
+[
+.B \-s
+]
+[
+.B \-w
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program takes two
+.BR mg (1)
+database compressed text files and appends the second to
+the first.
+It is part of the
+.BR mgmerge (1)
+addition to the
+.BR mg (1)
+package.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-s
+This option will cause a slow merge to be performed on the inverted files,
+where each inverted file entry is decoded and recoded.
+The default is a fast merge. Accumulated fast merges slowly degrade
+compression performance on the resulting inverted file so
+a periodic slow merge is recommended.
+.TP
+.B \-w
+By default the database .weights file is updated with
+new weights for the new documents in
+.B mg_text_merge.
+The
+.B -w
+option turns this step off.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.old.invf.dict
+The old compressed stemmed dictionary.
+.TP
+.B *.new.invf.dict
+The new compressed stemmed dictionary.
+.TP
+.B *.invf.dict
+The resulting merged compressed stemmed dictionary.
+.TP
+.B *.old.invf.ORG
+The original inverted file saved by
+.BR mg_invf_rebuild .
+.TP
+.B *.old.invf
+The old inverted file.
+.TP
+.B *.new.invf
+The new inverted file.
+.TP
+.B *.invf
+The resulting merged inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgmerge (1),
+.BR mg_get_merge (1),
+.BR mg_text_merge (1),
+.BR mg_invf_rebuild (1),
+.BR mg_invf_dict (1),
+.BR mg_weights_build (1),
+.BR mgquery (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_rebuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_rebuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_invf_rebuild.1	(revision 16561)
@@ -0,0 +1,156 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_rebuild 1 \*(Dt CITRI
+.SH NAME
+mg_invf_rebuild \- Rebuild an mg inverted file with or without skips.
+.SH SYNOPSIS
+.B mg_invf_rebuild
+.RB [ \-h ]
+.if n .ti +9n
+[
+.BR \-0 " |"
+.B \-1
+[
+.BI \-k " num"
+] |
+.B \-2
+[
+.BI \-s " num"
+]
+[
+.BI \-m " num"
+]
+]
+.if n .ti +9n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_invf_rebuild
+builds a new inverted file, with or without skipping, from an old
+inverted file.  This file uses
+.I *.invf.ORG
+and
+.I *.invf.idx.ORG
+as the
+source from which it builds
+.I *.invf
+and
+.IR *.invf.idx .
+If
+.I *.invf.ORG
+or
+.I *.invf.idx.ORG
+do not exist, the program renames
+.I *.invf
+or
+.I *.invf.idx
+to
+.I *.invf.ORG
+or
+.I *.invf.idx.ORG
+as appropriate.  The old inverted file may contain skipping.  This
+means that it is possible to delete the
+.I *.ORG
+files
+after the new inverted file is built.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-m\fP \fInum\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-0
+This generates a non-skipped inverted file.  This option is normally
+only needed if the
+.I *.ORG
+files have been deleted.
+.TP
+.B \-1
+This generates a skipped inverted file.  The
+.BI \-k " num"
+argument specifies the number of pointers hopped over with each skip.
+.TP
+.B \-2
+This option generates a skipped inverted file.  The skipped inverted
+file is built so that it is `optimal' for ranking using a specific
+number of accumulators.  Each term in the inverted file has a
+different skip length.  The arguments
+.BR \-s " and " \-m
+control the sizes of the skips.
+.TP
+.BI \-k " num"
+This specifies the number of pointers that should be hopped over with
+each skip.  This option is only valid if
+.B \-1
+is specified.
+.TP
+.BI \-m " num"
+This specifies the intended number of accumulators that will be used
+when ranking queries are done on the collection.
+.TP
+.BI \-s " num"
+This specifies the minimum size for skips.  If the calculation of the
+optimal skip size results in a number smaller than
+.IR num ,
+the skip size is set to
+.IR num .
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.ORG
+Original inverted file.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.invf.idx.ORG
+The original index into the inverted file.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_passes.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_passes.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_passes.1	(revision 16561)
@@ -0,0 +1,441 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_passes 1 \*(Dt CITRI
+.SH NAME
+mg_passes \- builds mg databases
+.SH SYNOPSIS
+.B mg_passes
+[
+.B \-h
+]
+[
+.B \-G
+]
+[
+.B \-S
+]
+[
+.B \-D
+]
+[
+.B \-W
+]
+.if n .ti +9n
+[
+.BR \-1 " |"
+.BR \-2 " |"
+.B  \-3
+]
+[
+.BI \-C " compstatpoint"
+]
+.if n .ti +9n
+[
+.BI \-n " tracename"
+]
+.if t .ti +.5i
+[
+.BI \-b " bufsize"
+]
+[
+.BI \-m " memlimit"
+]
+.if n .ti +9n
+[
+.BI \-c " numchunks"
+]
+[
+.BI \-a " stemmer"
+]
+[
+.BI \-s " stemmethod"
+]
+[
+.BI \-t " tracepos"
+]
+.if n .ti +9n
+[
+.B \-T1
+]
+[
+.B \-T2
+]
+.if t .ti +.5i
+[
+.B \-I1
+]
+[
+.B \-I2
+]
+.if n .ti +9n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+[
+.I filename(s)
+]
+.SH DESCRIPTION
+.B mg_passes
+is the program that does most of the work when building
+.BR mg (1)
+database systems.  The input documents can come from either
+.I stdin
+or from a list of files on the command line.  Individual documents
+must be separated with control-B characters.  In general,
+.B mg_passes
+must be run twice to build a database, first with the
+.B \-T1
+and
+.B \-I1
+options, and second with the
+.B \-T2
+and
+.B \-I2
+options.  Several other programs must be run in order to get an
+.BR mg (1)
+database that is ready for the
+.BR mgquery (1)
+program.  The
+.SB EXAMPLE
+section below gives an example of how to build a complete
+.BR mg (1)
+database.
+.SH OPTIONS
+Options may appear in any order, but the
+.IR filename(s) ,
+if specified, must be last.
+.TP "\w'\fB\-C\fP \fIcompstatpoint\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-G
+Treat SGML tags as non-words when building the inverted file.  An SGML
+tag is anything between angle brackets, i.e., `<' and `>'.
+.TP
+.B \-S
+This option causes a special pass to be executed.  It is up to a user
+to modify
+.I mg.special.c
+in the source code to do something with the documents it is given.
+.TP
+.B \-D
+If
+.B mg_passes
+fails, then print the document that caused the failure to the trace
+file if tracing is active, or to
+.I stderr
+if it is not.
+.TP
+.B \-W
+This option enables the generation of the weights file when
+.B \-I2
+is specified.  It causes
+.B \-I2
+to use a little more memory and CPU.
+.TP
+.B \-1
+Produce a level-1 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+A level-1 inverted file makes it possible for
+.BR mgquery (1)
+to do Boolean queries.  Ranked queries can still be done,
+although the quality of the ranking is abysmal.
+.TP
+.B \-2
+Produce a level-2 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+This is the default when neither
+.BR \-1 ", " "\-2 " "or " \-3
+is specified.
+A level-2 inverted file makes it possible for
+.BR mgquery (1)
+to do Boolean queries and cosine-ranked queries.
+.TP
+.B \-3
+Produce a level-3 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+This has been implemented to enable paragraph-level inversion.
+Paragraphs are delimited by control-C characters in the source text.
+.TP
+.BI \-C " compstatpoint"
+This option causes statistics on the compression performance to be
+output to a file called
+.IR *.compression.stats .
+.I compstatpoint
+specifies the interval between outputting each line of statistics.  The
+units of
+.I compstatpoint
+are kilobytes of source text.  E.g., if
+.I compstatpoint
+is 10, then a line is output to the file every 10 KB of input
+source.  Each line of the file consists of 4 numbers The first number
+is the amount of input text, in bytes, processed so far.  The second
+number is the amount of input text, in bytes, processed since the
+last line was output to the file.  The third number is the number of
+output bytes generated since the last line was output to the file, and
+the fourth number is the compression achieved since the last line was
+output, i.e., the third number divided by the second number.
+.TP
+.BI \-n " tracename"
+This specifies the filename to use for the trace log, if tracing is
+enabled using the
+.B \-t
+option.  If
+.BI \-n " tracename"
+is not given and tracing is enabled, a default trace filename will be
+used.
+.TP
+.BI \-s " stemmethod"
+This specifies the method to use to \*(lqstem\*(rq the words in the
+inverted file dictionary.  This is a bit mask specifying the
+operations to do on words as they are parsed out of the text, where
+bit number 0 is the low-order (rightmost) bit.  Bit 0 does case
+folding, and bit 1 does simple stemming, so the value 3 for
+.I stemmethod
+does both case folding and stemming.
+.TP
+.BI \-a " stemmer"
+This specifies the stemmer to use when stemming words.  This
+is a description of the language the stemmer is intended for
+or a description of the stemmer.  Valid options include:
+english, lovin, french, and simplefrench.
+.TP
+.BI \-b " bufsize"
+Specify the size of the document buffer in kilobytes.  If any document
+is larger than
+.IR bufsize ,
+the program will abort with an error message.  This should probably be
+replaced with some system which automatically increases the buffer
+size as required.  The default size is 3072 KB (3 MB).
+.TP
+.BI \-m " memlimit"
+Maximum amount of memory to use for the pass-2 file inversion in
+megabytes.  This option is only useful when used in conjunction with
+the option
+.BR \-I1 .
+The larger this value, the faster the pass-2 inversion will proceed.
+The default value is 5 MB.
+.TP
+.BI \-c " numchunks"
+The maximum number of inversion chunks to write to disk.  Each chunk
+will be approximately as large as
+.IR memlimit .
+This option is only useful when used in conjunction with the option
+.BR \-I2 .
+The larger this value, the faster the pass-2 inversion will proceed.
+The default value is 5 MB.
+.TP
+.BI \-t " tracepos"
+This option activates tracing.  A line will be generated in the
+trace file for every
+.I tracepos
+input bytes processed.  The default name for the trace file can be
+overridden using the
+.BI \-n " tracename"
+option.
+.TP
+.B \-T1
+Generate the
+.I *.text.stats
+file.
+.TP
+.B \-T2
+Generate the
+.IR *.text ,
+.IR *.text.idx ,
+and possibly the
+.I *.text.dict.aux
+files.  Using this option requires that the
+.I *.text.dict
+file be present.
+.TP
+.B \-I1
+Generate the
+.IR *.invf.dict ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files.
+.TP
+.B \-I2
+Generate the
+.I *.invf
+and
+.I *.invf.idx
+files.  Using this option requires
+that the
+.IR *.invf.dict.hash ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files
+be present.  The
+.I *.invf.dict.hash
+file is generated by
+.BR mg_perf_hash_build (1)
+from the
+.I *.invf.dict.build
+file.  If the
+.B \-W
+option is specified, the
+.I *.weight
+file will also be generated.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is to be
+written.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection that will be
+created.
+.TP
+.I filename(s)
+This specifies the source text. If this is not specified, then the
+program expects the source text from
+.IR stdin .
+.SH EXAMPLE
+What follows is a UNIX
+.BR csh (1)
+script as an example of how to build an
+.BR mg (1)
+document collection.
+.LP
+.nf
+.DT
+.ft B
+.I #! /bin/csh
+.I
+# The first argument on the command line specifies the
+.I
+#   source of the text
+set source = ($1)
+.PP
+.I
+# The second argument is the name of the collection
+set text = ($2)
+.PP
+.I
+# Create *.text.stats,  *.invf.dict.build,
+.I
+#   *.invf.chunk and *.invf.chunks.trans
+${source} | mg_passes -T1 -I1 -m 1 -t 1 -f ${text}
+.PP
+.I
+# Create *.text.dict
+mg_compression_dict -f ${text}
+.PP
+.I
+# Create *.invf.dict.hash
+mg_perf_hash_build -f ${text}
+.PP
+.I
+# Create *.text,  *.text.idx,
+.I
+#   *.invf and *.invf.idx
+${source} | mg_passes -T2 -I2 -c 2 -t 1 -f ${text}
+.PP
+.I
+# Create *.text.idx.wgt and *.weight.approx
+mg_weights_build -f ${text} -b 8
+.PP
+.I
+# Create *.invf.dict
+mg_invf_dict -f ${text} -b 4096
+.PP
+.I
+# Create *.text.dict
+mg_fast_comp_dict -f ${text}
+.ft R
+.fi
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.chunk
+Inverted file chunk descriptor file.  When the inverted file is
+created it is created in chunks that use no more than a set amount of
+memory.  This file describes those chunks.
+.TP
+.B *.invf.chunk.trans
+Word-occurrence-order to lexical-order translation file.  The
+.B *.invf.chunk
+file is written in word-occurrence order but is required by
+.B \-I2
+to be in lexical order.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for an order-preserving perfect hash function.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.trace
+The default trace file.
+.TP
+.B *.compression.stats
+Statistics about the compression of the text.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_perf_hash_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_perf_hash_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_perf_hash_build.1	(revision 16561)
@@ -0,0 +1,89 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_perf_hash_build 1 \*(Dt CITRI
+.SH NAME
+mg_perf_hash_build \- generate an order-preserving hash function for the stemmed dictionary
+.SH SYNOPSIS
+.B mg_perf_hash_build
+[
+.B \-h
+]
+[
+.BI \-r " rndseed"
+]
+[
+.BI \-d  " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_perf_hash_build
+generates an order-preserving hashing function from the compressed
+stemmed dictionary and writes it out to disk.
+.BR mg_passes (1)
+will make use of the hash function when it builds the inverted file.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-r " rndseed"
+This specifies the random seed to be used in the generation of the
+hash function; it must be an integer.  If this is not specified, the
+current time will be used as the random seed.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf.dict
+Stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for a order-preserving perfect hash function.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_stem_idx.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_stem_idx.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_stem_idx.1	(revision 16561)
@@ -0,0 +1,95 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_stem_idx 1 \*(Dt CITRI
+.SH NAME
+mg_stem_idx \- builds a stem index file
+.SH SYNOPSIS
+.B mg_stem_idx
+[
+.B \-h
+]
+[
+.B \-F
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.B \-s 1|2|3
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_stem_idx
+generates a stem index file for a collection that is to be built with
+stem indexes.  This program should be called three times: once for each
+.B -s
+parameter.  It uses the stemmed dictionary to create the stem index which
+contains pointers into the stemmed dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " num"
+The dictionary is stored in blocks on disk; this option is used to set
+the block size.  The default is 4-KB blocks.
+.TP
+.B \-F
+This option causes the disk blocks to be aligned on the appropriate
+boundaries.  For example, if 4-KB blocks are used, they will be aligned
+in the file on 4-KB boundaries.
+.TP
+.B -s 1|2|3
+The stem method to apply for the stem index.
+.br
+1 = casefolded and non-stemmed
+.br
+2 = non-casefolded and stemmed
+.br
+3 = casefolded and stemmed
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.invf.dict.blocked
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked.1
+Stem index with stem index method 1.
+.TP
+.B *.invf.dict.blocked.2
+Stem index with stem index method 2.
+.TP
+.B *.invf.dict.blocked.3
+Stem index with stem index method 3.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_invf_dict (1),
+.BR mgstemidxlist (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_estimate.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_estimate.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_estimate.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_text_estimate 1 \*(Dt CITRI
+.SH NAME
+mg_text_estimate \- Estimate the size of the compressed text based on the text statistics and a compression dictionary.
+.SH SYNOPSIS
+.B mg_text_estimate
+[
+.B \-h
+]
+[
+.BR \-H " |"
+.BR \-B " |"
+.BR \-D " |"
+.BR \-Y " |"
+.B \-M
+]
+.if n .ti +9n
+.I stats-dict
+.I compression-dict
+.SH DESCRIPTION
+This program estimates the size of the compressed text that would be
+generated if text with certain statistics were compressed with a
+particular dictionary.  This program has no real use in the
+.BR mg (1)
+system, except for experimenting with the
+.BR mg_compression_dict (1)
+program.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-m\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-H
+This specifies that novel words will be coded character by character
+using Huffman codes.
+.TP
+.B \-B
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using binary codes.  The binary code represents their occurrence
+position in the auxiliary dictionary.
+.TP
+.B \-D
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using delta codes.  The delta code represents their occurrence position
+in the auxiliary dictionary.
+.TP
+.B \-Y
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This generally
+produces better compression than
+.B \-B
+or
+.BR \-D .
+.TP
+.B \-M
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This method is
+adaptive within documents, and generally produces better compression
+than
+.BR \-B ,
+.B \-D
+or
+.BR \-Y .
+.SH FILES
+.TP 20
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_text_merge.1	(revision 16561)
@@ -0,0 +1,76 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_text_merge 1 \*(Dt CITRI
+.SH NAME
+mg_text_merge \- Append one compressed text file to another
+.SH SYNOPSIS
+.B mg_text_merge
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program takes two
+.BR mg (1)
+database inverted files and stemmed
+dictionaries, named "old" and "new" respectively, and creates
+a merged stemmed dictionary and inverted file.
+It is part of the
+.BR mgmerge (1)
+addition to the
+.BR mg (1)
+package.
+The two compressed text files are
+.IR "name" .old.text
+and
+.IR "name" .new.text.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.old.text
+The old compressed text.
+.TP
+.B *.new.text
+The new compressed text that will be appended
+.TP
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgmerge (1),
+.BR mg_get_merge (1),
+.BR mg_invf_merge (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mgquery (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_weights_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_weights_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mg_weights_build.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_weights_build 1 \*(Dt CITRI
+.SH NAME
+mg_weights_build \- builds the approximate weights file
+.SH SYNOPSIS
+.B mg_weights_build
+[
+.B \-h
+]
+[
+.B \-s
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_weights_build
+is the program that generates the approximate weights file and the
+interleaved text index and exact weights file.  To do this, it uses
+the text index and either the exact weights file, or the inverted file
+and stemmed dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-s
+Save the weights generated from the
+.I *.invf
+and
+.I *.invf.dict
+file in a
+.I *.weight
+file.
+.TP
+.BI \-b " num"
+This specifies the number of bits of precision that should be used for
+the approximate weights.  If not specified, it defaults to 8.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbilevel.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbilevel.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbilevel.1	(revision 16561)
@@ -0,0 +1,116 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbilevel 1 \*(Dt CITRI
+.SH NAME
+mgbilevel \- compresses a bilevel image using a specified template
+.SH SYNOPSIS
+.B mgbilevel
+.B \-e
+[
+.B \-t
+.I \*(lqstring\*(rq
+]
+.I infile
+[
+.I \>compressed-outfile
+]
+.nf
+or
+.fi
+.B mgbilevel
+.B \-d
+.I compressed-infile
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+.B mgbilevel
+performs (up to) a two-stage template-based compression.  The
+user can specify the template to be used in the compression process.  The
+default template size is a two-stage template 22/10.  The compressed file
+contains the template type in the stream.  The output files are written
+to
+.IR stdout .
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-t\fP \fI\*(lqstring\*(rq\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-e
+Compresses
+.I infile
+using a template specified, and writes to
+.IR compressed-outfile .
+.TP
+.B \-d
+Decompresses
+.I compressed-infile
+using the template contained in the compressed file.  Writes to
+.IR outfile .
+.TP
+\fB\-t \fP\fI\*(lqstring\*(rq\fP
+This option specifies which template combination is to
+be used in the compression.  Five different characters
+can occur in the string: `.' for a pixel not
+included in the template, `p' for a pixel included in the 1st
+level template, `2' for a pixel included in the 2nd level
+template (a subset of the 1st level), `*' denoting the position
+of the
+.I current
+pixel, and a `;' denoting the end of each template line.
+The default template is a two-level 22/10 template:
+.IP
+.RS
+.nf
+\&.ppppp.;
+pp222pp;
+p22222p;
+p22*.\|.\|.;
+.fi
+.RE
+.IP
+This would be specified on the command line as:
+.nf
+\&\fB\-T\fP ".ppppp.;pp222pp;p22222p;p22*.\|.\|.;"
+.fi
+.TP
+.I infile
+This is the input
+.I .pbm
+file to be compressed.
+.TP
+.I outfile
+This is the decompressed
+.I .pbm
+file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild.1	(revision 16561)
@@ -0,0 +1,169 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbuild 1 \*(Dt CITRI
+.SH NAME
+mgbuild \- build an mg system database
+.SH SYNOPSIS
+.B mgbuild
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.BI \-d " mgdata dir"
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgbuild
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely build an
+.BR mg (1)
+system database ready for queries to be made
+by
+.BR mgquery.
+This program makes use of the
+.BR mg_get (1)
+script to obtain the text of the collection.
+.SH OPTIONS
+Options can occur in any order, but the collection name must be last.
+.TP "\w'\fIcollection-name\fP'u+2n"
+.BI \-c
+This specifies whether the
+.I get
+program is \*(lqcomplex\*(rq.  If a
+.I get
+program is \*(lqcomplex\*(rq, then it requires initialisation and
+cleanup with the
+.B \-i
+and
+.B \-c
+options.
+.TP
+.BI \-g " get"
+This specifies the program to use for getting the source text for the
+build.  If no
+.B \-g
+option is given, the default program
+.BR mg_get (1)
+is used.
+.TP
+.BI \-s " source"
+The
+.B mgbuild
+program consists of two parts.  The first part initializes variables
+to default values.  The second part uses these variables to control
+how the
+.BR mg (1)
+database is built.  This option specifies a program to execute between
+the first and second parts.  The details of what the variables are, and
+how they may be changed, are in comments in the
+.B mgbuild
+program.
+.TP
+.BI \-d " mgdata dir"
+Setting this option allows one to override the MGDATA environment variable.
+.TP
+.I collection-name
+This is the collection name, as required by the
+.BR mg_get (1)
+program.  It serves both as a
+.I case
+statement selector, and as the name of a subdirectory that holds the
+indexing files.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.chunk
+Inverted file chunk descriptor file.  When the inverted file is
+created, it is written in chunks that use no more than a set amount of
+memory.  This file describes those chunks.
+.TP
+.B *.invf.chunk.trans
+Word-occurrence-order to lexical-order translation file.  The
+.B *.invf.chunk
+file is written in word-occurrence order but is required by
+.B \-N2
+to be in lexical order.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for an order-preserving perfect hash function.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Text statistics.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild_4.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild_4.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgbuild_4.1	(revision 16561)
@@ -0,0 +1,53 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbuild_4 1 \*(Dt CITRI
+.SH NAME
+mgbuild_4 \- build an mg system database using 4 passes instead of 2
+.SH SYNOPSIS
+.B mgbuild_4
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.BI \-d " mgdata dir"
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgbuild_4
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely build an
+.BR mg (1)
+system database ready for queries to be made
+by
+.BR mgquery.
+This program makes use of the
+.BR mg_get (1)
+script to obtain the text of the collection.
+In 
+.BR mgbuild (1)
+the build is done by two passes over the input text. The first
+pass creates the compressed text dictionary and inverted file dictionary,
+the second pass creates the compressed text and inverted file. What 
+.BR mgbuild_4
+does is use a separate pass for each function. This will be slower but will
+not require as much RAM at one time. Therefore, if your build is failing
+due to running short of memory, then 
+.BR mgbuild_4
+is worth a try.
+.SH "SEE ALSO"
+.na
+.BR mgbuild (1)
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgdictlist.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgdictlist.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgdictlist.1	(revision 16561)
@@ -0,0 +1,72 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgdictlist 1 \*(Dt CITRI
+.SH NAME
+mgdictlist \- list the contents of a dictionary
+.SH SYNOPSIS
+.B mgdictlist
+[
+.B \-q
+]
+.I filename
+.SH DESCRIPTION
+.B mgdictlist
+lists the contents of either a compressed compression dictionary, a
+text statistics file, or a compressed stemmed dictionary.  Unlike all
+the other programs in the
+.BR mg (1)
+suite, it is necessary to specify the complete pathname of the
+dictionary to list.
+.SH OPTIONS
+The
+.I filename
+must be last.
+.TP "\w'\fIfilename\fP'u+2n"
+.B \-q
+Generate an abbreviated output (machine-readable rather than
+human-readable).
+.TP
+.I filename
+This is the name of the dictionary to list.
+.B mgdictlist
+automatically detects whether the file is a compressed compression
+dictionary, text statistics file, or a compressed stemmed dictionary.
+.SH FILES
+.TP 20
+.B *.text.dict
+Text statistics file.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.invf.build
+Compressed stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgfelics.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgfelics.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgfelics.1	(revision 16561)
@@ -0,0 +1,103 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgfelics 1 \*(Dt CITRI
+.SH NAME
+mgfelics \- fast and efficient lossless image (de)compression
+.SH SYNOPSIS
+.B mgfelics
+.B \-e
+[
+.BI \-k " n"
+]
+.I infile
+[
+.I \>compressed-outfile
+]
+.br
+or
+.br
+.B mgfelics
+.B \-d
+.I compressed-infile
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+.B mgfelics
+is a lossless compression process which can (de)compress portable
+bitmap files in
+.I pgm
+format.
+.B mgfelics
+writes to
+.IR stdout .
+.SH OPTIONS
+.TP "\w'\fB\-k\fP \fIn\fP'u+2n"
+.BI \-k " n"
+An integer parameter (< 32) which controls the compression speed and
+ratio.  The value of
+.I n
+is included in the output stream, and therefore is not needed in the
+decompression stage.  The default value of
+.I n
+is equal to the number of bits required to represent each grey level.
+.TP
+.B \-e
+Encodes the file
+.IR infile ,
+sending the output to
+.IR stdout .
+.TP
+.B \-d
+Decodes the file from
+.IR compressed-infile ,
+sending the output to
+.IR stdout .
+.SH NOTE
+.B mgfelics
+uses a combination of adjusted binary and Rice codes, the choice of
+which for any given pixel depends on the environment as determined by
+the intensity values of neighbouring pixels.  The result is a very
+fast and lossless algorithm for compressing grayscale images.  The
+optional parameter
+.BI \-k " n"
+corresponds to the maximum exponent of the same name in Rice encoding.
+Smaller values of
+.BI \-k " n"
+can result in significant improvement in compression times with a
+corresponding loss in compression ratio.  However the reduction in
+compression ratio with a small
+.BI \-k " n"
+parameter may be insignificant for many images.
+.SH REFERENCES
+Algorithm by Paul G. Howard and Jeffrey Scott Vitter
+of Brown University, RI, and Duke University, NC, respectively.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgmerge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgmerge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgmerge.1	(revision 16561)
@@ -0,0 +1,175 @@
+`.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgmerge 1 \*(Dt CITRI
+.SH NAME
+mgmerge \- update an mg system database with new documents
+.SH SYNOPSIS
+.B mgmerge
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.B \-S
+]
+[
+.B \-w
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgmerge
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely merge a current
+.BR mg (1)
+system database ready with some new documents, saving the need for a complete
+database rebuild with
+.B mgbuild.
+It does this by building a second
+.BR mg (1)
+system database from the new documents and then merging this database
+with the old one.
+This program makes use of the
+.BR mg_get_merge (1)
+script to obtain the text of the collection.
+.B mgmerge
+cannot edit or delete documents already in a database; only new documents
+can be added.
+.SH OPTIONS
+Options can occur in any order, but the collection name must be last.
+.TP "\w'\fIcollection-name\fP'u+2n"
+.BI \-c
+This specifies whether the
+.I get
+program is \*(lqcomplex\*(rq.  If a
+.I get
+program is \*(lqcomplex\*(rq, then it requires initialisation and
+cleanup with the
+.B \-i
+and
+.B \-c
+options.
+.TP
+.BI \-g " get"
+This specifies the program to use for getting the source text for the
+build.  If no
+.B \-g
+option is given, the default program
+.BR mg_get_merge (1)
+is used.
+.TP
+.BI \-s " source"
+The
+.B mgmerge
+program consists of two parts.  The first part initializes variables
+to default values.  The second part uses these variables to control
+how the
+.BR mg (1)
+database is built.  This option specifies a program to execute between
+the first and second parts.  The details of what the variables are, and
+how they may be changed, are in comments in the
+.B mgmerge
+program.
+If this option is used, the program should ideally be the same one that
+was called by
+.BR mgbuild (1)
+to build the database since some parameters need to be consistent between
+.B mgbuild
+and
+.B mgmerge.
+.TP
+.B \-S
+This option will cause a slow merge to be performed on the inverted files,
+where each inverted file entry is decoded and recoded.
+The default is a fast merge. Accumulated fast merges slowly degrade
+compression performance on the resulting inverted file so
+a periodic slow merge is recommended.
+.TP
+.B \-w
+Adding new documents can have an effect on the weight of the previous ones.
+By default the weights for documents already in the collection are not 
+recomputed since the change in their values is usually small.
+This option forces new weights to be recomputed.
+Periodic use of this option, as for the "-S" option, is recomended,
+otherwise query rankings may become inaccurate.
+.TP
+.I collection-name
+This is the collection name, as required by the
+.BR mg_get_merge (1)
+program.  It serves both as a
+.I case
+statement selector, and as the name of a subdirectory that holds the
+indexing files.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+Note that a temporary directory under the
+.B MGDATA
+directory is used to perform the merge.
+The default name for this directory is
+.BR MERGE .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Text statistics.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgbuild (1),
+.BR mg_get_merge (1),
+.BR mg_get (1),
+.BR mg_invf_merge (1),
+.BR mg_text_merge (1),
+.BR mg_query (1),
+.BR mg_weights_build (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgquery.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgquery.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgquery.1	(revision 16561)
@@ -0,0 +1,693 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgquery 1 \*(Dt CITRI
+.SH NAME
+mgquery \- query program for the mg system
+.SH SYNOPSIS
+.B mgquery
+[
+.B \-h
+]
+[
+.B \-D
+]
+[
+.BI \-f " name"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+[
+.I collection-name
+]
+.SH DESCRIPTION
+.B mgquery
+enables users to make Boolean or ranked queries from a data base
+generated by the
+.BR mg (1)
+system.  It accepts queries from
+.I stdin
+and sends the retrieved documents to
+.IR stdout .
+Information on the resource usage of
+.B mgquery
+as it processes queries can be obtained interactively.
+.SH OPTIONS
+Options may appear in any order, but the
+.IR collection-name ,
+if specified, must be last.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-D
+This option causes the entire text to be decompressed and sent to
+.IR stdout .
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection that will be
+used.  If a collection with the specified base
+.I name
+does not exist, an error message will be displayed and
+.B mgquery
+will exit.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.SH USAGE
+Prior to processing the command line arguments, the
+.B mgquery
+program attempts to read in a startup script called
+.IR ./.mgrc .
+If that fails, it attempts to read in the file
+.IR $HOME/.mgrc .
+The startup file can only contain commands\(emno queries are
+permitted in the
+.I .mgrc
+file.  Lines starting with \*(lq\fB#\fP\*(rq in the file are comments.
+The most common use for the
+.I .mgrc
+file is to personalise the initial values of the predefined parameters
+with
+.B .set
+commands.
+.LP
+The input to
+.B mgquery
+consists of a series of input lines.  The backslash
+character
+.RB (\*(lq \e \*(rq)
+is used at the end of lines to indicate
+that input continues on the next line.
+.LP
+Input lines on which the first character is a dot
+.RB (\*(lq . \*(rq)
+are commands to the
+.B mgquery
+program.  Input lines that do not start with a dot are queries.
+.LP
+A query consists of two parts.  One part is a Boolean or ranked query
+that identifies documents.  The second part is a post-processing
+pattern matching operation.  Any text between the first speech mark
+(\*(lq) and the last speech mark (\*(rq) is considered to be a
+post-processing pattern.
+.SH COMMANDS
+The
+.B mgquery
+program can accept the following commands.
+.TP 17
+.B .help
+Display several pages of help text.
+.TP
+.B .quit
+Quit the program.
+.TP
+.B .warranty
+Display the
+.BR mg (1)
+warranty.
+.TP
+.B .conditions
+Display the conditions of use and distribution of
+.BR mg (1).
+.TP
+.BI ".set " "name value"
+Set the parameter
+.I name
+to the specified
+.IR value .
+If the parameter is a Boolean
+.I value
+and the
+.I value
+is omitted, the parameter will be inverted (i.e., if it was
+.IR true ,
+then it will change to
+.IR false ;
+if it was
+.IR false ,
+then it will change to
+.IR true ).
+.TP
+.BI ".unset " name
+Delete the parameter
+.I name
+from the currently-defined parameters.
+.TP
+.B .reset
+Reset the parameters to the state that they had after the processing
+of the
+.B mgquery
+command line.
+.TP
+.B .display
+Display the values of all the currently-defined parameters.
+.TP
+.B .push
+Push the currently-defined parameters onto a stack.
+.TP
+.B .pop
+Pops a set of parameters off the stack, replacing the currently-defined
+ones.
+.TP
+.BI ".output " arg
+This is used to specify where to send the text of the documents.  Once
+the
+.B .output
+command is specified, all subsequent output will be sent to the place
+specified by
+.IR arg .
+If
+.I arg
+is not specified subsequent output will be directed to
+.IR stdout .
+.I Arg
+may be any of the following.
+.RS
+.TP 13
+.BI "> " filename
+Send output to the specified file.
+.TP
+.BI ">> " filename
+Append output to the specified file.
+.TP
+.BI "| " command
+Pipe the output to
+.IR command ,
+which is executed by
+.IR sh .
+.RE
+.TP
+.BI ".input " arg
+This is used to specify where input (queries and commands) comes
+from.  Once the
+.B .input
+command is specified all subsequent input will be come from the place
+specified by
+.IR arg .
+If
+.I arg
+is not specified subsequent input will come from
+.IR stdin .
+.RS
+.TP 13
+.BI "< " filename
+Get input from the specified file.
+.TP
+.BI "| " command
+The input comes from the standard output of
+.IR command ,
+which is executed by
+.IR sh .
+.RE
+.SH PARAMETERS
+The following parameters are predefined and have special
+significance.  Each parameter will be followed by its default
+value.  Parameters are initialised before the
+.I .mgrc
+file is read or the command line arguments are processed.
+.TP 17
+.BI accumulator_method " `array'"
+This parameter is used during ranking, and specifies how the
+weight for each document should be accumulated.  The following
+methods are available:
+.IR array ,
+.IR splay_tree ,
+.IR hash_table ,
+and
+.IR list .
+.TP
+.BI briefstats " `off'"
+This is a Boolean parameter that determines whether the
+totals for disk, memory and time usage statistics will be
+displayed at the end of each query.
+.IR Note :
+this takes precedence over the parameters
+.BR diskstats ,
+.BR memstats " and " timestats .
+This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI buffer " `1048576'"
+When the documents are being read in, they are read into a
+buffer of this size and then displayed from this buffer.  If
+the documents are larger than this buffer, the buffer is
+expanded automatically.  Having a large buffer gives a very
+slight performance improvement, because it allows the order of
+disk operations to be optimised.  The buffer size is measured
+in bytes.
+.TP
+.BI diskstats " `off'"
+This is a Boolean parameter that determines whether the disk
+usage statistics for the preceding query will be displayed
+after each query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI doc_sepstr " `---------------------------------- %n\en\'"
+This specifies the string that will be used to separate
+documents when they are displayed for `Boolean' or `docnums'
+queries.  The standard C escape character sequences
+may be used to place special characters in the
+string.  For example, a newline would be `\en'.  To include a `%',
+use the sequence `%%'.  To include the
+.BR mg (1)
+document number, use the sequence `%n'.  The following escape character
+sequences are available
+.nf
+.ta 1.7iL
+.B Sequence	Meaning
+`\e\e'	backslash
+`\eb'	backspace
+`\ef'	formfeed
+`\en'	newline
+`\er'	carriage return
+`\et'	tab
+`\e"'	speech marks
+`\e''	quote mark
+`\ex\fIhh\fP'	ASCII code in hexadecimal
+`\ennn'	ASCII code in octal
+.fi
+.TP
+.BI expert " `false'"
+If this is
+.IR true ,
+then much of the dialogue output is suppressed.  This parameter may
+take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI hash_tbl_size " `1000'"
+One of the options during ranking queries is to use a hash
+table to accumulate the weights for each document.  The hash
+table is a simple chained type.  This parameter specifies the
+size of the hash table and may take any value between 8 and
+268435456 (2^28).
+.TP
+.BI heads_length " `50'"
+When the mode is
+.BR heads ,
+this specifies the number of characters that will be output for each
+document.
+.TP
+.BI maxdocs " `all'"
+The maximum number of documents to display in response to a
+query.  This parameter may take on a numeric value between 1
+and 429467295 (2^32 - 1) or the word
+.IR all .
+.TP
+.BI maxparas " `1000'"
+The maximum number of paragraphs to identify during a ranked
+query with paragraph indexing.  After the paragraphs have been
+identified, the paragraphs are converted into documents, and
+because some of the paragraphs may refer to the same documents
+the final number of answers may be less than
+.BR maxparas .
+The
+.B maxdocs
+parameter will then be applied.  This parameter may take on a numeric
+value between 1 and 429467295 (2^32 - 1).
+.TP
+.BI max_accumulators " `50000'"
+This parameter limits the number of different paragraph and
+document numbers to be accumulated during ranked queries when
+the parameter
+.B accumulator_method
+is set to
+.IR splay_tree ,
+.IR hash_table ,
+or
+.IR list .
+This parameter may take any value between 8 and 268435456 (2^28).
+.TP
+.BI max_terms " `all'"
+This parameter limits the number of terms that will actually
+be used during a ranked query.  If more terms than the number
+specified by
+.B max_terms
+are entered, then the extra terms will be discarded.  If
+.B sorted_terms
+is on, then the limiting will be done after the terms have been
+sorted.  This parameter may take any value between 1 and 429467295
+(2^32 - 1), or the word
+.IR all.
+.TP
+.BI memstats " `off'"
+This is a Boolean parameter that determines whether the memory
+usage statistics for the preceding query will be displayed
+after each query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI mgdir " `.'"
+This is set to the directory where the
+.BR mg (1)
+data files may be found.  If
+the environment variable
+.B MGDATA
+exists, then this is instead initialised to the value of
+.BR MGDATA .
+The value of this parameter may be changed, either in the
+.I .mgrc
+file with a
+.BI ".set mgdir "directory
+command, or from the command line using the
+.BI \-d " directory"
+option.  Once the \*(lq\fB>\fP\*(rq prompt appears, changing this
+parameter will have no effect.
+.TP
+.BI mgname " `bible'"
+This is set to the name of the
+.BR mg (1)
+collection that is to be used for the session.  The value of this
+parameter may be changed, either in the
+.I .mgrc
+file with a
+.BI ".set mgname "name
+command, or from the command line using the
+.BI \-f " name"
+option.  Once the \*(lq\fB>\fP\*(rq prompt appears, changing this
+parameter will have no effect.
+.TP
+.BI mode " `text'"
+This specifies how documents should be displayed when they
+are retrieved.  It may take six different values:
+.IR text ,
+.IR hilite ,
+.IR docnums ,
+.IR heads ,
+.IR silent ,
+or
+.IR count .
+.I text
+displays the contents of the document.
+.I hilite
+displays the contents of the document and highlights any of the 
+stemmed query terms.
+.I docnums
+displays only the document numbers.
+.I heads
+is used to print out the head of each document.
+.I silent
+retrieves all the documents but displays nothing except how many
+documents were retrieved.  This mode is intended to be used in timing
+experiments.
+.I count
+does the minimum
+amount of work required to determine how many documents would
+be retrieved, but does not retrieve them.
+.TP
+.BI optimise_type " `1'"
+There are three types of boolean query optimisation (parse tree
+rearrangement). Type 0 leaves parse tree unaltered. Type 1 optimises
+for AND of terms and AND of OR of terms. Type 2 converts the tree
+into DNF (an experiment :-).
+.TP
+.BI pager " `more'"
+This is the name of the program that will be used to display
+the help and the retrieved documents.  If the environment
+variable
+.B PAGER
+is defined, then
+.B pager
+takes on that value.
+.TP
+.BI hilite_style " `bold'"
+This specifies the type of highlighting method.
+It may take one of two different values:
+.IR bold,
+or
+.IR underline.
+.TP
+.BI para_sepstr " `\en######## PARAGRAPH %n ########\en'"
+This specifies the string that will be used to separate paragraphs.
+The standard C escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written
+as `\en'.  To include a `%', use the sequence `%%'.  To include the
+paragraph number within the document, use the sequence `%n'.
+.TP
+.BI para_start " `***** Weight = %w *****\en'"
+This specifies the string that will be used at the head of paragraphs
+for a paragraph-level index following a ranked query.  The standard
+C-language escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written as
+`\en'.  To include a `%', use the sequence `%%'.  To include the
+paragraph weight, use the sequence `%w'.
+.TP
+.BI qfreq " `true'"
+This determine whether the ranked queries will take into account the
+number of times each query term is specified.  When this is
+.IR true ,
+the number of times a term appears in the query is used in the
+ranking.  When this is
+.IR false ,
+all query terms are assumed to occur only once.  This parameter may
+take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI query  " `Boolean'"
+This specifies the type of queries that are to be specified.
+It can take four different values:
+.IR Boolean ,
+.IR ranked ,
+.IR docnums " or " approx-ranked.
+.I Boolean
+is for Boolean queries.
+The
+.BR yacc (1)
+grammar for Boolean queries is as follows.
+.IP
+.nf
+        query   : or;
+.IP
+        or      : or '|' and
+                | and ;
+.IP
+        and     : and '&' not
+                | and not
+                | not ;
+.IP
+        not     : term
+                | '!' not ;
+.IP
+        term    : TERM
+                | '(' or ')' ;
+.fi
+.IP
+.IR ranked " and " approx-ranked
+are for queries ranked by the cosine measure.
+.I approx-ranked
+uses only the low-precision document lengths, and therefore only
+produces an approximation to full cosine ranking.
+.IP
+.nf
+        query   : TERM
+                | query TERM ;
+.fi
+.IP
+.I docnums
+allows the entry of document numbers.  Multiple numbers separated by
+spaces may be specified, or ranges separated by hyphens.
+.IP
+.nf
+        query   : range
+                | query range ;
+.IP
+        range   : num
+                | num '-' num ;
+.fi
+.TP
+.BI ranked_doc_sepstr " `-------------------------------- %n %w\en'"
+This specifies the string that will be used to separate documents when
+they are displayed for `ranked' or `approx-ranked' queries.  The
+standard C escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written as
+`\en'.  To include a `%', use the sequence `%%'.  To include the
+.BR mg (1)
+document number, use the sequence `%n'.  To include the document
+weight, use the sequence `%w'.
+.TP
+.BI sizestats " `false'"
+If this is
+.IR true ,
+then various numbers are output at the end of each query indicating
+what went on during the query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI skip_dump " `skips.%d'"
+If this parameter is set, then a file will be produced in the current
+directory during ranked queries on skipped inverted files when
+.B accumulator_method
+is set to
+.IR splay_tree ,
+.IR hash_table ,
+or
+.IR list .
+The name of the file is the value of this parameter.  A `%d' in the
+file name will be replaced with the process id of
+.BR mgquery .
+This file will contain information about the usage of skips during the
+query processing.  This option is expensive; use
+.B .unset skip_dump
+to obtain optimal performance.
+.TP
+.BI sorted_terms " `on'"
+This specifies whether or not the terms should be sorted into
+decreasing occurrence in documents so that the least-often occurring
+terms are processed first when ranked queries are being done.  When
+this is
+.IR true ,
+the terms are sorted.  When this is
+.IR false ,
+the terms are not sorted, and are instead processed in order of
+occurrence.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI stop_at_max_accum " `on'"
+This specifies what should happen when the maximum number of
+accumulators set by
+.B max_accumulators
+is reached.  When this is
+.IR true ,
+the processing of terms is stopped at the completion of the current
+term.  When this is
+.IR false ,
+processing continues but no new accumulators are created.  This
+parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI terminator " `'"
+This specifies the string that will be output after the last document
+from the previous query has been output.  The standard C escape
+character sequences may be used to place special characters in the
+string.  For example, a newline would be written as `\en'.  To include
+a `%', use the sequence `%%'.
+.TP
+.BI timestats " `false'"
+If this is
+.IR true ,
+then the time to process a query is displayed in both real time and
+CPU time.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI verbatim " `off'"
+This is a Boolean parameter that determines whether the program
+should attempt to do a regular-expression match on the retrieved
+text.  If verbatim is
+.I on
+and a post-processing string is specified with the query, then the
+post-processing string will be searched for in the documents just
+before they are displayed.  If the string is found, the document will
+be displayed; if not, the document will not be displayed.  If verbatim
+is
+.IR off ,
+the post-processing string will be considered a regular expression
+as in
+.BR egrep (1)
+or
+.BR vi (1).
+E.g., if verbatim is
+.I on,
+\*(lq\fBand.*the\fP\*(rq will look for the 8-character sequence
+\*(lq\fBand.*the\fP\*(rq.  If verbatim is
+.IR off ,
+\*(lq\fBand.*the\fP\*(rq will look for the sequence
+\*(lq\fBand\fP\*(rq followed somewhere later in the document by the
+sequence \*(lq\fBthe\fP\*(rq.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.I .mgrc
+.B mgquery
+startup file
+.TP
+.B help.mg
+Help file for
+.BR mgquery .
+The contents of this file is displayed with the
+.B .help
+command.
+.TP
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+The `on-disk' stemmed dictionary.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.dict
+Compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR egrep (1),
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1),
+.BR vi (1),
+.BR yacc (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstat.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstat.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstat.1	(revision 16561)
@@ -0,0 +1,131 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgstat 1 \*(Dt CITRI
+.SH NAME
+mgstat \- print out statistics about a document collection
+.SH SYNOPSIS
+.B mgstat
+[
+.B \-h
+]
+[
+.B \-E
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgstat
+prints out various statistics about an existing
+.BR mg (1)
+document collection.  Depending on the size of the collection, sizes
+will be printed in either kilobytes or megabytes.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stdout .
+.TP
+.B \-E
+This option forces sizes to be printed in bytes rather than kilobytes
+or megabytes.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text
+Compressed documents.
+.TP
+.B *.invf
+Inverted file.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.TP
+.B *.invf.dict.blocked
+Compressed stemmed dictionary and index into the inverted file merged
+into an inverted file.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.invf.chunk
+Maps stemmed terms from occurrence order to lexical order.
+.TP
+.B *.invf.chunk.trans
+Describes where the source text is broken up into chunks for the
+inversion pass.
+.TP
+.B *.invf.dict.hash
+A perfect hash function for the terms in the stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstemidxlist.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstemidxlist.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgstemidxlist.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgstemidxlist 1 \*(Dt CITRI
+.SH NAME
+mgstemidxlist \- dumps the stem index file to STDOUT in ASCII format
+.SH SYNOPSIS
+.B mgstemidxlist
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.B \-s 1|2|3
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgstemidxlist
+is a utility program which passes through the stem index and displays
+the information.  (This program was used for debugging but is included
+for completeness!)  The header information is printed out, then for each
+block in the file the block header is displayed.  For each word in the
+stem index, the output is formatted as:
+
+.RS
+.I number_of_references \*(lqword\*(rq
+.br
+.RS
+.I "->  number_of_cases block 3-in-4_block offset"
+.br
+.I "->  ..."
+.RE
+.RE
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B -s 1|2|3
+The stem index file with the associated stem method.
+.br
+1 = casefolded and non-stemmed
+.br
+2 = non-casefolded and stemmed
+.br
+3 = casefolded and stemmed
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.invf.dict.blocked.1
+Stem index with stem index method 1.
+.TP
+.B *.invf.dict.blocked.2
+Stem index with stem index method 2.
+.TP
+.B *.invf.dict.blocked.3
+Stem index with stem index method 3.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_invf_dict (1),
+.BR mg_stem_idx (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgtic.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgtic.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgtic.1	(revision 16561)
@@ -0,0 +1,137 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgtic 1 \*(Dt CITRI
+.SH NAME
+mgtic \- textual image compression.
+.SH SYNOPSIS
+.B mgtic
+.B \-e
+[
+.B \-L
+]
+[
+.B \-Q
+]
+[
+.B \-X
+]
+.I libraryfile
+.I infile
+.if n .ti +9n
+[
+.I \>compressed\-outfile
+]
+.nf
+or
+.fi
+.B mgtic
+.B \-d
+[
+.B \-L
+]
+[
+.BI \-X " libraryfile"
+]
+.I compressed-infile
+.if n .ti +9n
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+This is the main routine in the
+.B mgtic
+system.
+.B mgtic
+is used to
+processes a series of files, and by using the
+.I libraryfile
+produced by
+.BR mgticbuild (1),
+compresses input files with respect to the marks contained in the
+library.  On the CCITT test faxes, lossless mode achieved around 20:1
+compression, and lossy, around 40:1 compression.  On more textual
+images, the amount of compression is determined by the resolution of
+the original file, and the amount of noise in the image.  By default,
+.B mgtic
+will reconstruct a lossy version of the image.  This
+can be changed to lossless by using the
+.B \-L
+option.
+.SH OPTIONS
+Options may appear in any order, but must come before the input
+filenames.
+.TP "\w'\fB\-X\fP \fI[libraryfile]\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-e
+Encode
+.I infile
+with respect to the library.
+.TP
+.B \-d
+Decodes
+.IR compressed-infile
+with respect to the library.
+.TP
+.B \-l
+This specifies lossy mode, and is the default mode for (de)compression.
+Do not generate the residue.  If used in encoding mode, does not store
+the residue in the output.  If used in decoding mode, the residue (if
+it exists) is ignored.
+.TP
+.B \-L
+Use lossless mode for (de)compression, which generates the residue.
+If used in encoding mode, this stores the residue in the compressed
+output.  If used in decoding mode (assuming the residue is present),
+this allows the original file to be recovered exactly.
+.TP
+.BI \-R\  filename
+Stores the residue as an external file
+.I filename
+which could possibly be stored on another medium.  During
+decompression, if the residue file specified does not relate to the
+compressed file, the resulting decompressed image will be a mess.
+Note that only the width and height of the residue image is checked.
+.TP
+.BI \-Q
+Uses a quicker form of compression.  This option decreases the time
+spent looking for a good match during encoding.  On average this
+option decreases compression ratios, but increases compression speed.
+.TP
+.BI \-X " [libraryfile]"
+Use an external library file, instead of (de)compressing the library
+within the normal stream.  When decoding, the
+.B \-X
+option must be followed by the
+.IR libraryfile .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticbuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticbuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticbuild.1	(revision 16561)
@@ -0,0 +1,61 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticbuild 1 \*(Dt CITRI
+.SH NAME
+mgticbuild \- build a library of marks from training documents
+.SH SYNOPSIS
+.B mgticbuild
+[
+.B \-h
+]
+.I "libraryfile infile1 infile2 .\|.\|."
+.SH DESCRIPTION
+.B mgticbuild
+builds an uncompressed library of marks that have been
+extracted from a series of input files.  If the
+.I libraryfile
+does
+not exist, it is created, otherwise new marks are appended to the
+file.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.TP
+.I infile(s)
+The training files; the only currently-supported format is
+.IR pbm .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticdump.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticdump.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticdump.1	(revision 16561)
@@ -0,0 +1,55 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticdump 1 \*(Dt CITRI
+.SH NAME
+mgticdump \- displays the library in a user-friendly manner
+.SH SYNOPSIS
+.B mgticdump
+[
+.B \-h
+]
+.I libraryfile
+.SH DESCRIPTION
+.B mgticdump
+displays the marks in a library in a user-viewable form.  The output
+is to
+.I stdout
+and is in a plain ASCII form.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticprune.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticprune.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticprune.1	(revision 16561)
@@ -0,0 +1,91 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticprune 1 \*(Dt CITRI
+.SH NAME
+mgticprune \- prunes the library to remove duplicates
+.SH SYNOPSIS
+.B mgticprune
+[
+.BI \-a " n"
+]
+[
+.BI \-s " n"
+]
+[
+.B \-Q
+]
+.I libraryfile
+.if n .ti +9n
+[
+.I pruned-libraryfile
+]
+.SH DESCRIPTION
+.B mgticprune
+examines
+.I libraryfile
+and removes duplicate marks.  If
+.I pruned-libraryfile
+is not specified,
+.I libraryfile
+is overwritten with the marks in the pruned library.  The Combined
+Symbol Independent Strategy (CSIS) method is used to match marks.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIpruned-libraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-a " n"
+Prune marks when their area is under or equal to the threshold,
+.IR n .
+The default value is\ 3.
+.TP
+.BI \-s " n"
+Prune marks when their width and height is under or equal to
+the threshold,
+.IR n .
+The default value is\ 0.
+.TP
+.BI \-Q
+Use a quicker method of pruning.  The number of iterations through the
+averaging and pruning processes is decreased.  This usually reduces
+time by a half, but compression ratios decrease.
+.TP
+.I libraryfile
+This specifies the name of the library file
+.TP
+.I pruned-libraryfile
+This specifies the name of the prune library, after duplicates and
+matches have been removed.  If this file is not specified, then
+.I libraryfile
+will be overwritten.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticstat.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticstat.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/mgticstat.1	(revision 16561)
@@ -0,0 +1,57 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticstat 1 \*(Dt CITRI
+.SH NAME
+mgticstat \- displays the statistics for the library
+.SH SYNOPSIS
+.B mgticstat
+[
+.B \-h
+]
+.I libraryfile
+.SH DESCRIPTION
+.B mgticstat
+displays statistics about the
+.I libraryfile
+file.
+The output of this program contains the average mark size, average
+centroid position, and the number of marks with an area less than or
+equal to a few powers of\ 2.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1).
Index: /gsdl/trunk/gs2common/indexers/mg/man/man1/xmg.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/man/man1/xmg.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/man/man1/xmg.1	(revision 16561)
@@ -0,0 +1,130 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH xmg 1 \*(Dt CITRI
+.SH NAME
+xmg \- An X interface to mgquery
+.SH SYNOPSIS
+.B xmg
+[
+.I collection-name
+]
+.SH DESCRIPTION
+.B xmg
+is an X interface to the
+.BR mg (1)
+information retrieval system.
+.PP
+Type a query in the ``Query'' box.  The escape key clears the query text.
+.PP
+The first line or so of each matching document is listed.
+.PP
+Click on any line to show the complete document.
+.PP
+Shift-click to show the document in a new window.
+.PP
+Press
+.I Next
+and
+.I Previous
+to cycle through the list of documents (shift works here too).
+.PP
+If you change the query, existing windows continue to refer to the
+original query.
+.PP
+You can select either Boolean or ranked queries:
+.RS
+.TP "\w'\fIBoolean queries:\fP'u+2n"
+.I "Boolean queries:"
+use ``&'' for AND, ``|'' for OR, ``!'' for NOT, and parentheses for
+grouping.
+.TP
+.I "Ranked queries:"
+Documents are listed in order of relevance.  A weight is shown
+alongside each.  Weights are normalized to 100 for the most relevant
+document.  Choose
+.I "Approximate Ranking"
+for greater retrieval speed.
+.RE
+.PP
+In document windows (other than the main window):
+.RS
+.br
+Typing `n' = next document;
+.br
+Typing `N' = next document in new window;
+.br
+Typing `p' = previous document;
+.br
+Typing `P' = previous document in new window;
+.br
+Typing `q' = close window.
+.RE
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.TP
+.SB Xdefaults
+To override the default font options, use RESOURCE_MANAGER properties
+like the following in your
+.I .Xdefaults
+file:
+.IP
+.RS
+.nf
+.I "xmg.textfont: fixed"
+.I "xmg.buttonfont: fixed"
+.fi
+.RE
+.IP
+To override the default query type of ranked, use
+.IP
+.RS
+.I "xmg.querytype: boolean"
+.RE
+.IP
+To override the default collection name (the first one listed by
+.BR ls (1)
+from your MGDATA environment variable), use
+.IP
+.RS
+.I "xmg.collection: mail93"
+.RE
+.SH "SEE ALSO"
+.na
+.BR ls (1),
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/mkinstalldirs
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/mkinstalldirs	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/mkinstalldirs	(revision 16561)
@@ -0,0 +1,32 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1994-03-25
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do 
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d in ${1+"$@"} ; do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp" 1>&2
+        mkdir "$pathcomp" || errstatus=$?
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/Makefile.in	(revision 16561)
@@ -0,0 +1,216 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the MG system
+# Copyright (C) 1994  Neil Sharman
+#
+# 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.
+#
+#       @(#)Makefile.in	1.10 22 Mar 1994
+#
+###########################################################################
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+subdir = src/images
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+ 
+CC = @CC@
+CFLAGS = @CFLAGS@  @COMPAT32BITFLAGS@
+CPPFLAGS = @CPPFLAGS@
+DEFS = @DEFS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = ../../lib/libmglib.a @LIBS@
+TOUCH = echo timestamp >
+ 
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+ 
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+ 
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+ 
+.SUFFIXES: ._c ._o
+.c._c:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $@
+._c._o:
+	@echo $(COMPILE) $<
+	@rm -f _$*.c
+	@ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+.c._o:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $*._c
+	@echo $(COMPILE) $*._c
+	@rm -f _$*.c
+	@ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+ 
+INCLUDES = -I../../../.. -I../.. -I../../lib -I.
+
+
+SOURCES = \
+  arithcode.c     extractor.c     mgbilevel.c     mgticdump.c     utils.c \
+  bilevel.c       felics.c        mgfelics.c      mgticprune.c    mgticstat.c\
+  codeoffsets.c   marklist.c      mgtic.c         pbmtools.c      lstevent.c \
+  codesyms.c      match.c         mgticbuild.c    sortmarks.c     ppm.c  
+
+
+ 
+HEADERS = \
+  arithcode.h     codeoffsets.h   marklist.h      sortmarks.h \
+  basictypes.h    codesyms.h      match.h         utils.h     \
+  bilevel.h       extractor.h     pbmtools.h      model.h
+
+MISC          = Makefile.in 
+
+ALLFILES      =	$(SOURCES) $(HEADERS) $(MISC) $(MAN)
+
+DISTFILES     = $(ALLFILES) 
+
+EXEC          = mgfelics mgbilevel mgtic mgticbuild mgticprune mgticstat \
+                mgticdump
+
+MAN           = mgfelics.1 mgbilevel.1 mgtic.1 mgticbuild.1  mgticprune.1 \
+                mgticstat.1 mgticdump.1
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+all: $(EXEC)
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+FELICS_OBJS = felics$o mgfelics$o utils$o
+
+mgfelics: $(FELICS_OBJS)
+	$(LINK) $(FELICS_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+BILEVEL_OBJS = \
+	arithcode$o bilevel$o extractor$o marklist$o mgbilevel$o \
+	pbmtools$o utils$o
+            
+mgbilevel: $(BILEVEL_OBJS)
+	$(LINK) $(BILEVEL_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+TIC_OBJS = \
+	mgtic$o extractor$o marklist$o pbmtools$o utils$o match$o \
+	sortmarks$o codesyms$o ppm$o lstevent$o arithcode$o codeoffsets$o bilevel$o
+
+mgtic: $(TIC_OBJS) 
+	$(LINK) $(TIC_OBJS) $(LIBS) 
+
+TICBUILD_OBJS = mgticbuild$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticbuild: $(TICBUILD_OBJS)
+	$(LINK) $(TICBUILD_OBJS) $(LIBS)
+ 
+TICPRUNE_OBJS = \
+	mgticprune$o extractor$o marklist$o pbmtools$o \
+	utils$o match$o
+
+mgticprune: $(TICPRUNE_OBJS)
+	$(LINK) $(TICPRUNE_OBJS) $(LIBS)
+ 
+TICSTAT_OBJS = mgticstat$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticstat: $(TICSTAT_OBJS)
+	$(LINK) $(TICSTAT_OBJS) $(LIBS) 
+ 
+TICDUMP = mgticdump$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticdump: $(TICDUMP)
+	$(LINK) $(TICDUMP) $(LIBS) 
+ 
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+ansi2knr: ansi2knr.o
+	$(LINK) ansi2knr.o $(LIBS)
+
+install: execinstall #maninstall
+uninstall: execuninstall manuninstall
+
+execinstall: $(EXEC)
+	for name in $(EXEC); do \
+	  $(INSTALL_PROGRAM) $$name \
+	  $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+execuninstall:
+	for name in $(EXEC); do \
+	 rm -f $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+maninstall: $(MAN)
+	$(top_srcdir)/mkinstalldirs $(mandir); \
+	for f in $(MAN); do \
+	  $(INSTALL_DATA) $$f $(mandir)/$$f ;\
+	done
+
+manuninstall: $(MAN)
+	for f in $(MAN); do \
+	  rm -f $(mandir)/$$f ;\
+	done
+
+
+mostlyclean:
+	rm -f *$o _*.c _*.o *._c *._o core core.*
+ 
+clean: mostlyclean
+	rm -f $(EXEC)
+ 
+distclean: clean
+	rm -f ansi2knr
+	rm -f Makefile
+ 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+ 
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+	  || cp -p $(srcdir)/$$file $(distdir); \
+	done
+
+# screws up distclean (jrm21) 
+#Makefile: Makefile.in ../../../../config.status
+#	cd ../../../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ 
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.c	(revision 16561)
@@ -0,0 +1,471 @@
+/**************************************************************************
+ *
+ * arithcode.c -- Arithmetic coding
+ * Copyright (C) 1994  Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************
+ *
+ *	Arithmetic encoding. Taken largely from Witten, Neal, Cleary,
+ *		CACM, 1987, p520.
+ *	Includes bitfiddling routines.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "arithcode.h"
+
+
+FILE *arith_in, *arith_out;
+
+/*
+   $log$
+ */
+
+static char *RCSID = "$Id$";
+
+#undef BECAREFUL
+#define INTEGRATED
+
+
+codevalue S_low = 0, S_high = 0, S_value = 0;
+long S_bitstofollow = 0;
+int S_buffer = 0, S_bitstogo = 0;
+
+long cmpbytes = 0, rawbytes = 0;
+
+#ifdef INTEGRATED
+long CountOfBitsOut;
+#endif
+
+
+/*==================================*/
+
+#define BITPLUSFOLLOW(b)		\
+{	OUTPUTBIT((b));			\
+	while (bitstofollow)		\
+	{	OUTPUTBIT(!(b));	\
+		bitstofollow -= 1;	\
+	}				\
+}
+
+#ifdef INTEGRATED
+#define OUTPUTBIT(b)			\
+{	buffer >>= 1;			\
+	if (b) buffer |= 0x80;	 	\
+	bitstogo -= 1;			\
+	if (bitstogo==0)		\
+	{	putc(buffer, arith_out);	\
+		bitstogo = 8;		\
+		cmpbytes += 1;		\
+	}				\
+	CountOfBitsOut++;		\
+}
+#else
+#define OUTPUTBIT(b)			\
+{	buffer >>= 1;			\
+	if (b) buffer |= 0x80;	 	\
+	bitstogo -= 1;			\
+	if (bitstogo==0)		\
+	{	putc(buffer, arith_out);	\
+		bitstogo = 8;		\
+		cmpbytes += 1;		\
+	}				\
+}
+#endif
+
+#define ADDNEXTINPUTBIT(v)		\
+{	if (bitstogo==0)		\
+	{	buffer = getc(arith_in);	\
+		bitstogo = 8;		\
+	}				\
+	v = (v<<1)+(buffer&1);		\
+	buffer >>= 1;			\
+	bitstogo -=1;			\
+}
+
+/*==================================*/
+
+void
+arithmetic_encode (lbnd, hbnd, totl)
+     int lbnd, hbnd, totl;
+{
+  register codevalue low, high;
+
+#ifdef BECAREFUL
+  if (!(0 <= lbnd && lbnd < hbnd && hbnd <= totl && totl < maxfrequency))
+    {
+      fprintf (stderr, "(corrupt file) coding error: %d %d %d\n", lbnd, hbnd, totl);
+      abort ();
+    }
+#endif
+
+  {
+    register codevalue range;
+    range = S_high - S_low + 1;
+    high = S_low + range * hbnd / totl - 1;
+    low = S_low + range * lbnd / totl;
+  }
+
+  {
+    register codevalue H;
+    register long bitstofollow;
+    register int buffer, bitstogo;
+
+    bitstofollow = S_bitstofollow;
+    buffer = S_buffer;
+    bitstogo = S_bitstogo;
+    H = half;
+
+    for (;;)
+      {
+	if (high < H)
+	  {
+	    BITPLUSFOLLOW (0);
+	  }
+	else if (low >= H)
+	  {
+	    BITPLUSFOLLOW (1);
+	    low -= H;
+	    high -= H;
+	  }
+	else if (low >= firstqtr && high < thirdqtr)
+	  {
+	    bitstofollow++;
+	    low -= firstqtr;
+	    high -= firstqtr;
+	  }
+	else
+	  break;
+	low += low;
+	high += high;
+	high++;
+      }
+    S_bitstofollow = bitstofollow;
+    S_buffer = buffer;
+    S_bitstogo = bitstogo;
+    S_low = low;
+    S_high = high;
+  }
+}
+
+/*==================================*/
+
+/* Made into a macro 
+
+   codevalue
+   arithmetic_decode_target(totl)
+   register int totl;
+   {    return (((S_value-S_low+1)*totl-1) / (S_high -S_low+1));
+   } 
+ */
+
+/*==================================*/
+
+void
+arithmetic_decode (lbnd, hbnd, totl)
+     int lbnd, hbnd, totl;
+{
+  register codevalue low, high;
+
+#ifdef BECAREFUL
+  if (!(0 <= lbnd && lbnd < hbnd && hbnd <= totl && totl < maxfrequency))
+    {
+      fprintf (stderr, "(corrupt file) coding error: %d %d %d\n", lbnd, hbnd, totl);
+      abort ();
+    }
+#endif
+
+  {
+    register codevalue range;
+    range = S_high - S_low + 1;
+    high = S_low + range * hbnd / totl - 1;
+    low = S_low + range * lbnd / totl;
+  }
+
+  {
+    register codevalue value, H;
+    register int buffer, bitstogo;
+
+    buffer = S_buffer;
+    bitstogo = S_bitstogo;
+    H = half;
+    value = S_value;
+
+    for (;;)
+      {
+	if (high < H)
+	  {			/* nothing */
+	  }
+	else if (low >= H)
+	  {
+	    value -= H;
+	    low -= H;
+	    high -= H;
+	  }
+	else if (low >= firstqtr && high < thirdqtr)
+	  {
+	    value -= firstqtr;
+	    low -= firstqtr;
+	    high -= firstqtr;
+	  }
+	else
+	  break;
+	low += low;
+	high += high;
+	high += 1;
+	ADDNEXTINPUTBIT (value);
+      }
+    S_buffer = buffer;
+    S_bitstogo = bitstogo;
+    S_low = low;
+    S_high = high;
+    S_value = value;
+  }
+}
+
+/*==================================*/
+
+
+static void
+startoutputingbits ()
+{
+  S_buffer = 0;
+  S_bitstogo = 8;
+}
+
+static void
+startinputingbits ()
+{
+  S_buffer = 0;
+  S_bitstogo = 0;
+}
+
+static void
+doneoutputingbits ()
+{				/* write another byte if necessary */
+  if (S_bitstogo < 8)
+    {
+      putc (S_buffer >> S_bitstogo, arith_out);
+      cmpbytes += 1;
+    }
+}
+
+/*==================================*/
+
+static void
+startencoding ()
+{
+  S_low = 0;
+  S_high = topvalue;
+  S_bitstofollow = 0;
+}
+
+static void
+startdecoding ()
+{
+  register int i;
+  register int buffer, bitstogo;
+  S_low = 0;
+  S_high = topvalue;
+  S_value = 0;
+  bitstogo = S_bitstogo;
+  buffer = S_buffer;
+  for (i = 0; i < codevaluebits; i++)
+    ADDNEXTINPUTBIT (S_value);
+  S_bitstogo = bitstogo;
+  S_buffer = buffer;
+}
+
+static void
+doneencoding ()
+{
+  register long bitstofollow;
+  register int buffer, bitstogo;
+  bitstofollow = S_bitstofollow;
+  buffer = S_buffer;
+  bitstogo = S_bitstogo;
+  bitstofollow += 1;
+  if (S_low < firstqtr)
+    {
+      BITPLUSFOLLOW (0);
+    }
+  else
+    {
+      BITPLUSFOLLOW (1);
+    }
+  S_bitstofollow = bitstofollow;
+  S_buffer = buffer;
+  S_bitstogo = bitstogo;
+}
+
+
+
+void 
+EncodeGammaDist (int Off)
+/* Encode Off with probability distribution derived from the Elias gamma distribution. */
+{
+  int Len, i;
+
+  Off++;
+  for (Len = 31; Len >= 0 && !(Off & (1 << Len)); Len--);
+
+  for (i = Len; i >= 0; i--)
+    arithmetic_encode (0, 1, 2);
+  arithmetic_encode (1, 2, 2);
+
+  for (i = Len - 1; i >= 0; i--)
+    if (Off & (1 << i))
+      arithmetic_encode (1, 2, 2);
+    else
+      arithmetic_encode (0, 1, 2);
+}
+
+
+
+int 
+DecodeGammaDist ()
+/* Decode next Off using probability distribution derived from the Elias gamma distribution. Inverse of EncodeGammaDist. */
+{
+  int i = 1, Len = 0, bit;
+
+  do
+    {
+      bit = arithmetic_decode_target (2) < 1 ? 0 : 1;
+      if (bit)
+	arithmetic_decode (1, 2, 2);
+      else
+	{
+	  arithmetic_decode (0, 1, 2);
+	  Len++;
+	}
+    }
+  while (!bit);
+
+  Len--;
+  while (Len-- > 0)
+    {
+      bit = arithmetic_decode_target (2) < 1 ? 0 : 1;
+      if (bit)
+	arithmetic_decode (1, 2, 2);
+      else
+	arithmetic_decode (0, 1, 2);
+      i = (i << 1) | bit;
+    }
+  return (i - 1);
+}
+
+
+
+
+
+void 
+EncodeGammaSigned (int snum, int *pos, int *neg)
+{
+  if (snum >= 0)
+    arithmetic_encode (0, *pos, *pos + (*neg)), (*pos)++;
+  else
+    arithmetic_encode (*pos, *pos + (*neg), *pos + (*neg)), (*neg)++;
+  if (*pos + (*neg) >= maxfrequency)
+    {
+      *pos = (*pos + 1) / 2;
+      *neg = (*neg + 1) / 2;
+    }
+  EncodeGammaDist (abs (snum));
+}
+
+
+int 
+DecodeGammaSigned (int *pos, int *neg)
+{
+  int signedpos = 1;
+  if (arithmetic_decode_target (*pos + (*neg)) < *pos)
+    arithmetic_decode (0, *pos, *pos + (*neg)), (*pos)++;
+  else
+    arithmetic_decode (*pos, *pos + (*neg), *pos + (*neg)), (*neg)++, signedpos = 0;
+
+  if (*pos + (*neg) >= maxfrequency)
+    {
+      *pos = (*pos + 1) / 2;
+      *neg = (*neg + 1) / 2;
+    }
+
+  if (signedpos)
+    return DecodeGammaDist ();
+  else
+    return (-DecodeGammaDist ());
+}
+
+
+void 
+InitArithEncoding (void)
+{
+  S_low = 0, S_high = 0, S_value = 0;
+  S_bitstofollow = 0;
+  S_buffer = 0, S_bitstogo = 0;
+  cmpbytes = 0, rawbytes = 0;
+
+  startoutputingbits ();
+  startencoding ();
+}
+
+void 
+InitArithDecoding (void)
+{
+  S_low = 0, S_high = 0, S_value = 0;
+  S_bitstofollow = 0;
+  S_buffer = 0, S_bitstogo = 0;
+  cmpbytes = 0, rawbytes = 0;
+
+  startinputingbits ();
+  startdecoding ();
+}
+
+void 
+CloseDownArithEncoding (void)
+{
+  doneencoding ();
+  doneoutputingbits ();
+}
+
+void 
+CloseDownArithDecoding (void)
+{
+}
+
+
+
+
+
+/* encode that famous number, 42 uses 12 bits */
+void 
+EncodeChecksum ()
+{
+  EncodeGammaDist (42);
+}
+
+void 
+DecodeChecksum (char str[])
+{
+  if (DecodeGammaDist () != 42)
+    {
+      fprintf (stderr, "%s: checksum error, file corrupt.\n", str);
+      exit (1);
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/arithcode.h	(revision 16561)
@@ -0,0 +1,78 @@
+/**************************************************************************
+ *
+ * arithcode.h -- Arithmetic coding
+ * Copyright (C) 1994  Alistair Moffat
+ *
+ * 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 _arithcode_h
+#define _arithcode_h
+
+extern FILE *arith_in, *arith_out;
+
+#define codevaluebits 16
+#if (codevaluebits<16)
+typedef unsigned short codevalue;
+#else
+typedef unsigned long codevalue;
+#endif
+
+#define topvalue ((codevalue)((1<<codevaluebits)-1))
+#define maxfrequency ((topvalue+1)/4 + 1)
+
+#define firstqtr (topvalue/4+1)
+#define half     (2*firstqtr)
+#define thirdqtr (3*firstqtr)
+
+#define	escape_event	U->totalcnt-U->notfound, U->totalcnt, U->totalcnt
+
+void arithmetic_encode ();
+codevalue arithmetic_decode_target ();
+
+#define	arithmetic_decode_target(totl)	\
+	(  ((S_value-S_low+1)*(totl)-1) / (S_high-S_low+1)  )
+
+void arithmetic_decode ();
+
+
+extern codevalue S_low, S_high, S_value;
+extern long S_bitstofollow;
+extern int S_buffer, S_bitstogo;
+
+extern long cmpbytes, rawbytes;
+
+extern long CountOfBitsOut;
+
+void EncodeGammaDist (int Off);
+int DecodeGammaDist ();
+
+void EncodeGammaSigned (int snum, int *pos, int *neg);
+int DecodeGammaSigned (int *pos, int *neg);
+
+
+void InitArithEncoding (void);
+void InitArithDecoding (void);
+
+void CloseDownArithEncoding (void);
+void CloseDownArithDecoding (void);
+
+void EncodeChecksum ();
+void DecodeChecksum (char str[]);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/basictypes.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/basictypes.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/basictypes.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**************************************************************************
+ *
+ * basictypes.h -- base data types
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************
+ *
+ * This file contains information relating to the base data types
+ *
+ **************************************************************************/
+
+#ifndef __basictypes_h
+#define __basictypes_h
+
+typedef unsigned long Pixel;
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.c	(revision 16561)
@@ -0,0 +1,747 @@
+/**************************************************************************
+ *
+ * bilevel.c -- Compress a bilevel bitmap
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************
+ *
+ *  
+ *  
+ *  This is the code used to compress a bitmap.  The template is given in
+ *  the following format. 
+ *   '.'  = pixel not used
+ *   'p'  = pixel used in 1st order context (the superset)
+ *   '2'  = pixel used in 2nd order context (the subset)
+ *   ';'  = marker used to denote the end of a row in the template
+ *   '*'  = the current context pixel.
+ *  for instance, the common 22/10 template would be shown as:
+ *     ".ppppp.;"
+ *     "pp222pp;"
+ *     "p22222p;"
+ *     "p22*...;"
+ * 
+ **************************************************************************/
+
+
+#include "sysfuncs.h"
+
+#include "bilevel.h"
+#include "extractor.h"
+#include "pbmtools.h"
+#include "marklist.h"
+#include "arithcode.h"
+#include "utils.h"
+
+
+
+#define INITVALUE 2
+#define INCREMENT 4
+
+#define MAX_T_W 32
+
+/* note memory usage is 2*TEMPLATESIZE*sizeof(prob), which is currently 4.9 Mb */
+#define TEMPLATESIZE 612317
+
+
+#define HASH(x) ((x)%TEMPLATESIZE)
+
+
+typedef struct
+  {
+    unsigned short s, n;
+  }
+prob;				/* set and notset */
+typedef struct
+  {
+    short int x, y;
+  }
+coordinate;
+
+coordinate MM[MAX_T_W][MAX_T_W];	/* the actual (x,y) positions */
+unsigned int MMcount[MAX_T_W];	/* number of bits in this row of the template */
+unsigned int MMmask[MAX_T_W];	/* the "current" mask value */
+unsigned int MMbits[MAX_T_W];	/* the bitmask to mask out higher bits */
+unsigned int MMrows;		/* total number of rows */
+unsigned int MM2level[MAX_T_W];	/* bitmask to calculate the 2nd level mask */
+int twolevel, curr_row, surr;
+
+prob *P = NULL;			/* probability array, up to N bits */
+prob *P2 = NULL;
+
+void 
+bl_clearmodel ()
+{
+  int i;
+
+  P = (prob *) malloc (TEMPLATESIZE * sizeof (prob));
+  if (!P)
+    error_msg ("bl_clearmodel()", "not enough memory.", "");
+
+  P2 = (prob *) malloc (TEMPLATESIZE * sizeof (prob));
+  if (!P2)
+    error_msg ("bl_clearmodel()", "not enough memory.", "");
+
+  for (i = 0; i < TEMPLATESIZE; i++)
+    {
+      P[i].s = P[i].n = INITVALUE;
+      P2[i].s = P2[i].n = INITVALUE;
+    }
+}
+
+
+void 
+bl_freemodel ()
+{
+  free (P);
+  free (P2);
+}
+
+
+
+
+#define update_counts(p, pixel)                        \
+    pixel ? (p->s+=INCREMENT) : (p->n+=INCREMENT);     \
+                                                       \
+    if(p->s+p->n >= (unsigned)(maxfrequency)) {        \
+       p->s=(p->s+1)/(unsigned)2;                      \
+       p->n=(p->n+1)/(unsigned)2;                      \
+    }
+
+
+
+
+/* mask is the bigger one, mask2l is smaller one */
+static void 
+bl_encode_in_context (unsigned long mask, unsigned long mask2l, unsigned long pixel)
+{
+  register int twol, t;
+  prob *p = &P[mask];
+  prob *p2 = &P2[mask2l];
+
+  twol = twolevel && (p->s + p->n < (unsigned) (2 * INITVALUE + 5 * INCREMENT));
+
+  t = twol ? (p2->s + p2->n) : (p->s + p->n);
+
+  if (twol == 0)
+    {
+      if (pixel)
+	arithmetic_encode (0, p->s, t);
+      else
+	arithmetic_encode (p->s, t, t);
+      update_counts (p, pixel);
+    }
+  else
+    {
+      if (pixel)
+	arithmetic_encode (0, p2->s, t);
+      else
+	arithmetic_encode (p2->s, t, t);
+      update_counts (p2, pixel);
+      update_counts (p, pixel);
+    }
+}
+
+
+static void 
+bl_decode_in_context (unsigned long mask, unsigned long mask2l, unsigned long *val)
+{
+  register int twol, pixel, t;
+  prob *p = &P[mask];
+  prob *p2 = &P2[mask2l];
+
+
+  if (twolevel && (p->s + p->n < (unsigned) (2 * INITVALUE + 5 * INCREMENT)))
+    {
+      pixel = arithmetic_decode_target (p2->s + p2->n) < p2->s, twol = 1;
+    }
+  else
+    {
+      pixel = arithmetic_decode_target (p->s + p->n) < p->s, twol = 0;
+    }
+
+  t = twol ? (p2->s + p2->n) : (p->s + p->n);
+
+  if (twol == 0)
+    {
+      if (pixel)
+	arithmetic_decode (0, p->s, t);
+      else
+	arithmetic_decode (p->s, t, t);
+      update_counts (p, pixel);
+    }
+  else
+    {
+      if (pixel)
+	arithmetic_decode (0, p2->s, t);
+      else
+	arithmetic_decode (p2->s, t, t);
+      update_counts (p2, pixel);
+      update_counts (p, pixel);
+    }
+
+  *val = pixel;
+}
+
+static void 
+bl_string_to_array (char inputstr[], char parsestr[], int *w, int *h)
+{
+  int r, c;
+  int i, j, lastc, p, l;
+  int xc, yc;
+  int foundstatus;
+
+  surr = twolevel = 0;
+  for (i = 0; i < MAX_T_W; i++)
+    MMcount[i] = MM2level[i] = 0;
+  MMrows = 0;
+
+  for (i = 0; i < (int) strlen (inputstr); i++)
+    {
+      p = inputstr[i];
+      if ((p != '.') && (p != '2') && (p != '*') && (p != 'p') && (p != ';'))
+	{
+	  fprintf (stderr, "Template string can only contain [.p2*;], not \"%c\".\n", inputstr[i]);
+	  exit (1);
+	}
+    }
+
+  /* get the width and height, test rectangularity... */
+  parsestr[l = 0] = 0;
+  for (c = 0, r = 0, lastc = -1, p = 0; p < (int) strlen (inputstr); p++)
+    {
+      if ((inputstr[p] == ';'))
+	{
+	  r++;
+	  if (lastc != -1)
+	    {			/* this is the 2nd row at least */
+	      if (lastc != c)
+		{
+		  fprintf (stderr, "The rows have to be the same width. "
+			   "Row %d has width %d (previous row has width %d)\n", r, c, lastc);
+		  exit (1);
+		}
+	    }
+	  lastc = c;
+	  c = 0;
+	}
+      else
+	{
+	  c++;
+	  parsestr[l] = inputstr[p];
+	  parsestr[++l] = 0;
+	}
+    }
+
+  if (c != 0)
+    lastc = c;
+  *w = lastc;
+  *h = r;
+  if ((*w) <= 0)
+    {
+      fprintf (stderr, "Must have at least one column!\n");
+      exit (1);
+    }
+  if ((*h) <= 0)
+    {
+      fprintf (stderr, "Must have at least one row! Terminate each row with a ';'\n");
+      exit (1);
+    }
+  if ((*w > MAX_T_W) || (*h > MAX_T_W))
+    {
+      fprintf (stderr, "template size bigger than internal threshold of %d\n", MAX_T_W);
+      exit (1);
+    }
+
+  /* get the (x,y) position of the current (*) pixel */
+  for (xc = -1, yc = 0, i = 0; i < *w; i++)
+    for (j = 0; j < *h; j++)
+      if (parsestr[j * (*w) + i] == '*')
+	{
+	  if (xc >= 0)
+	    {
+	      fprintf (stderr, "There can only be ONE current ('*') pixel.\n");
+	      exit (1);
+	    }
+	  xc = i;
+	  yc = j;
+	  curr_row = j + 1;
+	}
+  if (xc < 0)
+    error_msg ("bl_string_to_array", "Error in mask, no current ('*') pixel.", "");
+  /* found the position (xc,yc) */
+
+
+  /* convert parsestr[] into a MM[][] structure */
+  for (j = 0; j < *h; j++)
+    {
+      foundstatus = 0;
+      for (i = 0; i < *w; i++)
+	{
+	  p = j * (*w) + i;
+	  if ((parsestr[p] == 'p') || (parsestr[p] == '2'))
+	    {
+	      if (!foundstatus)
+		foundstatus = 1;
+	      MM2level[j] = MM2level[j] * 2 | (parsestr[p] == '2');
+	      if (parsestr[p] == '2')
+		twolevel = 1;
+
+	      MM[j][MMcount[j]].x = i - xc;
+	      MM[j][MMcount[j]++].y = j - yc;
+	      if (((i - xc) >= 0) && ((j - yc) >= 0))
+		surr = 1;
+	    }
+	  else
+	    {
+	      if (foundstatus == 1)
+		foundstatus = 2;
+	    }
+
+	}
+      if (foundstatus)
+	MMrows++;
+      MMbits[j] = (1 << MMcount[j]) - 1;
+    }
+
+  /* reverse matrix, so [][0] is the closest to the right */
+  {
+    coordinate temp;
+    for (i = 0; i < MMrows; i++)
+      for (j = 0; j < MMcount[i] / (unsigned) 2; j++)
+	{
+	  temp = MM[i][MMcount[i] - 1 - j];
+	  MM[i][MMcount[i] - 1 - j] = MM[i][j];
+	  MM[i][j] = temp;
+	}
+  }
+}
+
+
+
+
+
+
+
+
+void 
+bl_writetemplate (char inputstr[])
+{
+  register int i, j;
+  int w, h;
+  char ind[255];
+  char *str;
+
+  str = (char *) malloc (strlen (inputstr) * sizeof (char) + 2);
+
+  bl_string_to_array (inputstr, str, &w, &h);
+
+  EncodeGammaDist (surr);
+  EncodeGammaDist (w);
+  EncodeGammaDist (h);
+  ind['.'] = 0;
+  ind['p'] = 1;
+  ind['2'] = 2;
+  ind['*'] = 3;
+  for (j = 0; j < h; j++)
+    for (i = 0; i < w; i++)
+      EncodeGammaDist (ind[(int) str[j * w + i]]);
+
+  free (str);
+}
+
+
+void 
+bl_readtemplate ()
+{
+  register int i, j;
+  int w, h, p;
+  char ind[4];
+  char *inputstr, *str;
+
+  surr = DecodeGammaDist (surr);
+  if (surr)
+    {
+      fprintf (stderr, "It's fine compressing an image using a 'surrounding' template, but I can't "
+	       "decompress it!\n");
+      exit (1);
+    }
+  w = DecodeGammaDist ();
+  h = DecodeGammaDist ();
+
+  inputstr = (char *) malloc ((w + 2) * h * sizeof (char) + 2);
+  ind[0] = '.';
+  ind[1] = 'p';
+  ind[2] = '2';
+  ind[3] = '*';
+  for (p = 0, j = 0; j < h; j++)
+    {
+      for (i = 0; i < w; i++)
+	{
+	  inputstr[p++] = ind[DecodeGammaDist ()];
+	}
+      inputstr[p++] = ';';
+      inputstr[p] = 0;
+    }
+
+  str = (char *) malloc (strlen (inputstr) * sizeof (char) + 2);
+  bl_string_to_array (inputstr, str, &w, &h);
+}
+
+
+
+
+
+/* Currently if there are more than 32 bits, the topmost 32 will be discarded, if the 
+   topbits are to be used in compression, define _MORETHAN32 */
+#undef _MORETHAN32
+
+void 
+bl_compress_mark (marktype d)
+{
+  register int r, c;
+  register int i, j;
+  register unsigned long mask, mask2level;
+  char *array[MAX_T_W];
+
+
+  for (i = 0; i < MAX_T_W; i++)
+    {
+      array[i] = (char *) calloc ((size_t) d.w + 2 * MAX_T_W + 1, (size_t) sizeof (char));
+      array[i] = array[i] + MAX_T_W;
+    }
+
+  EncodeGammaDist (d.w);
+  EncodeGammaDist (d.h);
+
+  for (r = 0; r < min (curr_row, d.h); r++)
+    for (c = 0; c < d.w; c++)
+      array[r % MAX_T_W][c] = pbm_getpixel (d.bitmap, c, r);
+
+  for (r = 0; r < d.h; r++)
+    {
+      if (V)
+	if (r && (r % 200 == 0))
+	  fprintf (stderr, ".");
+
+      if (r + curr_row < d.h)
+	for (c = 0; c < d.w; c++)
+	  array[(r + curr_row) % MAX_T_W][c] = pbm_getpixel (d.bitmap, c, r + curr_row);
+      else
+	for (c = 0; c < d.w; c++)
+	  array[(r + curr_row) % MAX_T_W][c] = 0;
+
+      for (c = 0; c < d.w; c++)
+	{
+	  for (mask = 0, mask2level = 0, i = 0; i < MMrows; i++)
+	    {
+	      if (c != 0)
+		{
+		  MMmask[i] = (MMmask[i] * 2);
+		  MMmask[i] |= array[(r + MM[i][0].y + MAX_T_W) % MAX_T_W][c + MM[i][0].x];
+		}
+	      else
+		{
+		  for (MMmask[i] = 0, j = MMcount[i] - 1; j >= 0; j--)
+		    MMmask[i] = 2 * MMmask[i] | array[(r + MM[i][j].y + MAX_T_W) % MAX_T_W][c + MM[i][j].x];
+		}
+	      MMmask[i] &= MMbits[i];
+#ifdef _MORETHAN32
+	      mask = ((mask * 8191) << MMcount[i]) | MMmask[i];
+	      mask2level = ((mask2level * 499) << MMcount[i]) | (MMmask[i] & MM2level[i]);
+#else
+	      mask = (mask << MMcount[i]) | MMmask[i];
+	      mask2level = (mask2level << MMcount[i]) | (MMmask[i] & MM2level[i]);
+#endif
+	    }
+
+	  mask = HASH (mask);
+	  mask2level = HASH (mask2level);
+
+	  bl_encode_in_context (mask, mask2level, pbm_getpixel (d.bitmap, c, r));
+	}
+    }
+  for (i = 0; i < MAX_T_W; i++)
+    free (array[i] - MAX_T_W);
+}
+
+
+
+void 
+bl_decompress_mark (marktype * d)
+{
+  register int r, c;
+  register int i, j;
+  unsigned long pix;
+  register unsigned long mask, mask2level;
+  char *array[MAX_T_W];
+
+
+  d->w = DecodeGammaDist ();
+  d->h = DecodeGammaDist ();
+
+  d->bitmap = pbm_allocarray (d->w, d->h);
+  for (i = 0; i < MAX_T_W; i++)
+    {
+      array[i] = (char *) calloc ((size_t) d->w + 2 * MAX_T_W + 1, (size_t) sizeof (char));
+      array[i] = array[i] + MAX_T_W;
+    }
+
+  for (r = 0; r < d->h; r++)
+    {
+      if (V)
+	if (r && (r % 200 == 0))
+	  fprintf (stderr, ".");
+      for (c = 0; c < d->w; c++)
+	array[r % MAX_T_W][c] = pbm_getpixel (d->bitmap, c, r);
+      for (c = 0; c < d->w; c++)
+	{
+	  for (mask = 0, mask2level = 0, i = 0; i < MMrows; i++)
+	    {
+	      if (c != 0)
+		{
+		  MMmask[i] = (MMmask[i] * 2);
+		  MMmask[i] |= array[(r + MM[i][0].y + MAX_T_W) % MAX_T_W][c + MM[i][0].x];
+		}
+	      else
+		{
+		  for (MMmask[i] = 0, j = MMcount[i] - 1; j >= 0; j--)	/* get all pixels */
+		    MMmask[i] = 2 * MMmask[i] | array[(r + MM[i][j].y + MAX_T_W) % MAX_T_W][c + MM[i][j].x];
+		}
+	      MMmask[i] &= MMbits[i];
+
+#ifdef _MORETHAN32
+	      mask = ((mask * 8191) << MMcount[i]) | MMmask[i];
+	      mask2level = ((mask2level * 499) << MMcount[i]) | (MMmask[i] & MM2level[i]);
+#else
+	      mask = (mask << MMcount[i]) | MMmask[i];
+	      mask2level = (mask2level << MMcount[i]) | (MMmask[i] & MM2level[i]);
+#endif
+	    }
+
+	  mask = HASH (mask);
+	  mask2level = HASH (mask2level);
+
+	  bl_decode_in_context (mask, mask2level, &pix);
+	  if (pix)
+	    {
+	      pbm_putpixel (d->bitmap, c, r, pix);
+	      array[r % MAX_T_W][c] = pix;
+	    }
+	}
+    }
+  for (i = 0; i < MAX_T_W; i++)
+    free (array[i] - MAX_T_W);
+}
+
+
+
+void 
+bl_compress (marktype d, char inputstr[])
+{
+  bl_clearmodel ();
+  bl_writetemplate (inputstr);
+  bl_compress_mark (d);
+  bl_freemodel ();
+}
+
+
+void 
+bl_decompress (marktype * d)
+{
+  bl_clearmodel ();
+  bl_readtemplate ();
+  bl_decompress_mark (d);
+  bl_freemodel ();
+}
+
+
+
+
+
+
+/* 
+   The bitmap in d2, can be examined clairvoyantly.  The ordering of the
+   params is therefore non-clairvoyant, and clairvoyant in that order.
+
+ */
+/*
+   coordinate M1[6]={        { 0,-2},
+   {-1,-1},{ 0,-1}, {1,-1},
+   {-2,0},  {-1, 0}};
+   };
+   coordinate M2[13]={       { 0,-2},
+   {-1,-1}, { 0,-1},   {1, -1},
+   {-2,0}, {-1, 0}, { 0, 0},   { 1, 0},  {2,0},
+   {-1, 1}, { 0, 1},   { 1, 1},
+   { 0, 2}
+ */
+
+coordinate M1[6] =
+{
+  {0, -2},
+  {-2, 0},
+  {1, -1},
+  {-1, -1},
+  {0, -1},
+  {-1, 0}};
+coordinate M2[13] =
+{
+  {0, -2},
+  {1, -1},
+  {-2, 0},
+  {2, 0},
+  {-1, 1},
+  {1, 1},
+  {0, 2},
+  {-1, -1},
+  {0, -1},
+  {1, 0},
+  {-1, 0},
+  {0, 1},
+  {0, 0}
+};
+#define M1S 6
+#define M1S_2level 3
+#define M2S 13
+#define M2S_2level 6
+
+
+void 
+bl_clair_compress (marktype d1, marktype d2)
+{
+  register int r, c;
+  register int i, j, n;
+  register unsigned long mask, mask2level = 0;
+  int cols, rows;
+  int gap;
+  Pixel **b1, **b2;
+
+
+  bl_clearmodel ();
+  twolevel = 1;
+
+  b1 = d1.bitmap;
+  b2 = d2.bitmap;
+  cols = d1.w;
+  rows = d1.h;
+
+  gap = max (50, rows / 10);
+
+  EncodeGammaDist (cols);
+  EncodeGammaDist (rows);
+
+  for (r = 0; r < rows; r++)
+    {
+      if (V)
+	if (!(r % gap))
+	  fprintf (stderr, ".");
+      for (c = 0; c < cols; c++)
+	{
+
+	  mask = 0;
+	  for (n = 0; n < M1S; n++)
+	    {
+	      i = c + M1[n].x;
+	      j = r + M1[n].y;
+	      mask = (2 * mask) | pbm_getpixel_trunc (b1, i, j, cols, rows);
+	    }
+
+	  mask2level = mask & ((1 << M1S_2level) - 1);
+
+	  for (n = 0; n < M2S; n++)
+	    {
+	      i = c + M2[n].x;
+	      j = r + M2[n].y;
+	      mask = (2 * mask) | pbm_getpixel_trunc (b2, i, j, cols, rows);
+	    }
+
+	  mask2level = (mask2level << M2S_2level) | (mask & ((1 << M2S_2level) - 1));
+
+	  bl_encode_in_context (mask, mask2level, pbm_getpixel (b1, c, r));
+
+	}
+    }
+  if (V)
+    fprintf (stderr, "\n");
+  bl_freemodel ();
+}
+
+
+void 
+bl_clair_decompress (marktype d1, marktype d2)
+{
+  register int r, c;
+  register int i, j, n;
+  register unsigned long mask, mask2level = 0;
+  unsigned long p;
+  int cols, rows;
+  int gap;
+  Pixel **b1, **b2;
+
+  bl_clearmodel ();
+  twolevel = 1;
+
+  b1 = d1.bitmap;
+  b2 = d2.bitmap;
+  cols = d1.w;
+  rows = d1.h;
+
+  gap = max (50, rows / 10);
+
+  i = DecodeGammaDist ();
+  j = DecodeGammaDist ();
+  if ((i != cols) || (j != rows))
+    error_msg ("bl_clair_decompress", "incorrect size of residue image", "");
+
+  if (V)
+    fprintf (stderr, "cols=%d rows=%d\n", cols, rows);
+
+  for (r = 0; r < rows; r++)
+    {
+      if (V)
+	if (!(r % gap))
+	  fprintf (stderr, ".");
+      for (c = 0; c < cols; c++)
+	{
+	  mask = 0;
+	  for (n = 0; n < M1S; n++)
+	    {
+	      i = c + M1[n].x;
+	      j = r + M1[n].y;
+	      mask = (2 * mask) | pbm_getpixel_trunc (b1, i, j, cols, rows);
+	    }
+	  mask2level = mask & ((1 << M1S_2level) - 1);
+	  for (n = 0; n < M2S; n++)
+	    {
+	      i = c + M2[n].x;
+	      j = r + M2[n].y;
+	      mask = (2 * mask) | pbm_getpixel_trunc (b2, i, j, cols, rows);
+	    }
+	  mask2level = (mask2level << M2S_2level) | (mask & ((1 << M2S_2level) - 1));
+
+
+	  bl_decode_in_context (mask, mask2level, &p);
+	  if (p)
+	    pbm_putpixel (b1, c, r, 1);
+	}
+    }
+  if (V)
+    fprintf (stderr, "\n");
+  bl_freemodel ();
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/bilevel.h	(revision 16561)
@@ -0,0 +1,47 @@
+/**************************************************************************
+ *
+ * bilevel.h -- Compress a bilevel bitmap
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __BILEVEL_H
+#define __BILEVEL_H
+
+#include "marklist.h"
+
+/* must be called before the start of de/compression */
+void bl_clearmodel ();
+void bl_freemodel ();
+
+/* writes the template to the stream, and sets up the de/compression params */
+void bl_writetemplate (char inputstr[]);
+void bl_readtemplate ();
+
+/* actually performs the de/compression */
+void bl_compress (marktype d, char str[]);
+void bl_compress_mark (marktype d);
+void bl_decompress (marktype * d);
+void bl_decompress_mark (marktype * d);
+
+/* clairvoyantly de/compress d1 with respect to clairvoyantly viewable d2 */
+void bl_clair_compress (marktype d1, marktype d2);
+void bl_clair_decompress (marktype d1, marktype d2);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.c	(revision 16561)
@@ -0,0 +1,257 @@
+/**************************************************************************
+ *
+ * codeoffsets.c -- Functions which code the mark offsets
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "arithcode.h"
+#include "utils.h"
+
+#define TOP_INC 2
+#define MID_INC 1
+#define BOT_INC 1
+
+
+void 
+EncodeOffsets (marklistptr symbol_list, int nsyms)
+{
+  int xpos = 1, xneg = 1, ypos = 1, yneg = 1;
+  marklistptr step;
+  int found;
+  int dox = 0, leveltop = 1, levelmid = 1, levelbot = 1;
+  elemlist xlist, ylist;	/* all previous offsets */
+  int p, f, t, xoff, yoff, symnum, count;
+  elemlist *xcond, *ycond;	/* previous offsets conditioned on symbol num */
+
+  xcond = (elemlist *) malloc (nsyms * sizeof (elemlist));
+  if (!xcond)
+    error_msg ("EncodeOffsets", "Out of memory...", "");
+  ycond = (elemlist *) malloc (nsyms * sizeof (elemlist));
+  if (!ycond)
+    error_msg ("EncodeOffsets", "Out of memory...", "");
+
+  for (t = 0; t < nsyms; t++)
+    {
+      xcond[t].head = ycond[t].head = NULL;
+      xcond[t].tot = ycond[t].tot = 0;
+    }
+  xlist.head = NULL;
+  xlist.tot = 0;
+  ylist.head = NULL;
+  ylist.tot = 0;
+  for (step = symbol_list, count = 0; step; step = step->next, count++)
+    {
+      xoff = step->data.xoffset;
+      yoff = step->data.yoffset;
+      symnum = step->data.symnum;
+      for (dox = 1; dox >= 0; dox--)
+	{
+	  /* encode the offsets, x first then y */
+
+	  if (dox)
+	    found = elem_find (&xcond[symnum], xoff, &p, &f, &t);
+	  else
+	    found = elem_find (&ycond[symnum], yoff, &p, &f, &t);
+
+	  if (found)
+	    {
+	      arithmetic_encode (0, leveltop, leveltop + levelmid + levelbot);
+	      leveltop += TOP_INC;
+	      arithmetic_encode (p, f, t);
+	    }
+	  else
+	    {
+	      if (dox)
+		found = elem_find (&xlist, xoff, &p, &f, &t);
+	      else
+		found = elem_find (&ylist, yoff, &p, &f, &t);
+
+	      if (found)
+		{
+		  arithmetic_encode (leveltop, leveltop + levelmid, leveltop + levelmid + levelbot);
+		  levelmid += MID_INC;
+		  arithmetic_encode (p, f, t);
+		}
+	      else
+		{
+		  arithmetic_encode (leveltop + levelmid, leveltop + levelmid + levelbot, leveltop + levelmid + levelbot);
+		  levelbot += BOT_INC;
+		  if (dox)
+		    EncodeGammaSigned (xoff, &xpos, &xneg);
+		  else
+		    EncodeGammaSigned (yoff, &ypos, &yneg);
+		}
+	    }
+	  if (dox)
+	    {
+	      elem_add (&xlist, xoff);
+	      elem_add (&xcond[symnum], xoff);
+	    }
+	  else
+	    {
+	      elem_add (&ylist, yoff);
+	      elem_add (&ycond[symnum], yoff);
+	    }
+
+	  if (leveltop + levelmid >= maxfrequency)
+	    {
+	      leveltop = (leveltop + 1) / 2;
+	      levelmid = (levelmid + 1) / 2;
+	      levelbot = (levelbot + 1) / 2;
+	    }
+	}
+    }
+  for (t = 0; t < nsyms; t++)
+    {
+      elem_free (&xcond[t]);
+      elem_free (&ycond[t]);
+    }
+  elem_free (&xlist);
+  elem_free (&ylist);
+  if (V)
+    fprintf (stderr, "offset models: top %d, mid %d, bot %d\n", (leveltop - 1) / TOP_INC, (levelmid - 1) / MID_INC, (levelbot - 1) / BOT_INC);
+}
+
+
+
+
+
+void 
+DecodeOffsets (marklistptr symbol_list, int nsyms)
+{
+  int xpos = 1, xneg = 1, ypos = 1, yneg = 1;
+  marklistptr step;
+  int i, target, dox;
+  int leveltop = 1, levelmid = 1, levelbot = 1;
+  elemlist xlist, ylist;
+  int p, f, t, xoff = 0, yoff = 0, symnum, count = 0;
+  elemlist *xcond, *ycond;
+
+  xcond = (elemlist *) malloc (nsyms * sizeof (elemlist));
+  if (!xcond)
+    error_msg ("EncodeOffsets", "Out of memory...", "");
+  ycond = (elemlist *) malloc (nsyms * sizeof (elemlist));
+  if (!ycond)
+    error_msg ("EncodeOffsets", "Out of memory...", "");
+
+  for (i = 0; i < nsyms; i++)
+    {
+      xcond[i].head = ycond[i].head = NULL;
+      xcond[i].tot = ycond[i].tot = 0;
+    }
+
+  xlist.head = NULL;
+  xlist.tot = 0;
+  ylist.head = NULL;
+  ylist.tot = 0;
+
+  for (step = symbol_list, count = 0; step; step = step->next, count++)
+    {
+      symnum = step->data.symnum;
+      for (dox = 1; dox >= 0; dox--)
+	{
+	  /* decode x offset first, then y */
+	  target = arithmetic_decode_target (leveltop + levelmid + levelbot);
+	  if (target < leveltop)
+	    {
+	      arithmetic_decode (0, leveltop, leveltop + levelmid + levelbot);
+	      leveltop += TOP_INC;
+
+	      if (dox)
+		i = arithmetic_decode_target (xcond[symnum].tot);
+	      else
+		i = arithmetic_decode_target (ycond[symnum].tot);
+
+	      if (dox)
+		{
+		  elem_getrange (&xcond[symnum], i, &xoff);
+		  elem_find (&xcond[symnum], xoff, &p, &f, &t);
+		}
+	      else
+		{
+		  elem_getrange (&ycond[symnum], i, &yoff);
+		  elem_find (&ycond[symnum], yoff, &p, &f, &t);
+		}
+	      arithmetic_decode (p, f, t);
+	    }
+	  else if (target < leveltop + levelmid)
+	    {
+	      arithmetic_decode (leveltop, leveltop + levelmid, leveltop + levelmid + levelbot);
+	      levelmid += MID_INC;
+
+	      if (dox)
+		i = arithmetic_decode_target (xlist.tot);
+	      else
+		i = arithmetic_decode_target (ylist.tot);
+
+	      if (dox)
+		{
+		  elem_getrange (&xlist, i, &xoff);
+		  elem_find (&xlist, xoff, &p, &f, &t);
+		}
+	      else
+		{
+		  elem_getrange (&ylist, i, &yoff);
+		  elem_find (&ylist, yoff, &p, &f, &t);
+		}
+	      arithmetic_decode (p, f, t);
+	    }
+	  else
+	    {
+	      arithmetic_decode (leveltop + levelmid, leveltop + levelmid + levelbot, leveltop + levelmid + levelbot);
+	      levelbot += BOT_INC;
+	      if (dox)
+		xoff = DecodeGammaSigned (&xpos, &xneg);
+	      else
+		yoff = DecodeGammaSigned (&ypos, &yneg);
+	    }
+
+	  if (dox)
+	    {
+	      step->data.xoffset = xoff;
+	      elem_add (&xlist, xoff);
+	      elem_add (&xcond[symnum], xoff);
+	    }
+	  else
+	    {
+	      step->data.yoffset = yoff;
+	      elem_add (&ylist, yoff);
+	      elem_add (&ycond[symnum], yoff);
+	    }
+
+	  if (leveltop + levelmid >= maxfrequency)
+	    {
+	      leveltop = (leveltop + 1) / 2;
+	      levelmid = (levelmid + 1) / 2;
+	      levelbot = (levelbot + 1) / 2;
+	    }
+	}
+    }
+  for (t = 0; t < nsyms; t++)
+    {
+      elem_free (&xcond[t]);
+      elem_free (&ycond[t]);
+    }
+  elem_free (&xlist);
+  elem_free (&ylist);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/codeoffsets.h	(revision 16561)
@@ -0,0 +1,32 @@
+/**************************************************************************
+ *
+ * codeoffsets.h -- Functions which code the mark offsets
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __CODEOFFSETS_H
+#define __CODEOFFSETS_H
+
+#include "marklist.h"
+
+void EncodeOffsets (marklistptr MarkList, int num_symbols);
+void DecodeOffsets (marklistptr MarkList, int num_symbols);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.c	(revision 16561)
@@ -0,0 +1,96 @@
+/**************************************************************************
+ *
+ * codesyms.c -- Functions which code the symbol sequence
+ * Copyright (C) 1994  Hugh Emberson
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "arithcode.h"
+#include "marklist.h"
+#include "pbmtools.h"
+#include "utils.h"
+#include "codesyms.h"
+
+int charsetsize = 256;
+
+void 
+EncodeSymbols (marklistptr MarkList, int NSyms)
+{
+  marklistptr step;
+  unsigned short Events[PPM_CHUNK];
+  int j = 0;
+
+  charsetsize = max (NSyms, 256);
+
+  step = MarkList;
+  while (step)
+    {
+      Events[j] = step->data.symnum;
+
+      assert (Events[j] > 0 || Events[j] < (unsigned) NSyms);
+
+      j++;
+
+      if (j == PPM_CHUNK)
+	{
+	  encodestring (Events, PPM_CHUNK);
+	  j = 0;
+	}
+      step = step->next;
+    };
+
+  if (j != 0)
+    encodestring (Events, j);
+
+  encodestring (Events, 0);
+}
+
+
+marklistptr 
+DecodeSymbols (int NSyms)
+{
+  marklistptr MarkList = NULL;
+  unsigned short Events[PPM_CHUNK];
+  int j, n;
+  marktype d;
+
+  charsetsize = max (NSyms, 256);
+
+  do
+    {
+      decodestring (Events, &n);
+
+      for (j = 0; j < n; j++)
+	{
+	  d.symnum = Events[j];
+	  marklist_add (&MarkList, d);
+	}
+    }
+  while (n != 0);
+
+  return MarkList;
+}
+
+void 
+InitPPM (void)
+{
+  kbytes = 896;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/codesyms.h	(revision 16561)
@@ -0,0 +1,44 @@
+/**************************************************************************
+ *
+ * codesyms.h -- Functions which code the symbol sequence
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 _CodeSymbols_h_
+#define _CodeSymbols_h_
+
+#include "marklist.h"
+#include "arithcode.h"
+
+#ifndef FIXEDORDER
+#define FIXEDORDER	3
+#endif
+
+#define PPM_CHUNK	1024
+
+extern int kbytes;
+extern void decodestring ();
+extern void encodestring ();
+
+void EncodeSymbols (marklistptr MarkList, int NSyms);
+marklistptr DecodeSymbols (int NSyms);
+
+void InitPPM (void);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.c	(revision 16561)
@@ -0,0 +1,423 @@
+/**************************************************************************
+ *
+ * extractor.c -- Functions related to extracting marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "extractor.h"
+#include "pbmtools.h"
+#include "marklist.h"
+#include "utils.h"
+
+
+/* 
+   'splitjoined' defines whether the extractor modules tries to break
+   "seemingly" merged/joined marks.
+ */
+int splitjoined = 1;
+
+#define MAX_FILL_STACK_SIZE 10000
+
+/* RunRegionFill uses line conherence to implement a better fill
+   routine.  The stack has currently got 10000 elements, although the
+   biggest I have encountered used a maximum stack size of 1294.  usage:
+   RunRegionFill(bitmap,mark,i,j,xpos,ypos); xpos and ypos are the top
+   left corners of the mark.  */
+
+int maxst = 0, st = 0;
+struct
+  {
+    int x, y;
+  }
+a[MAX_FILL_STACK_SIZE];
+
+#define push(i,j) a[st].x=i;a[st].y=j;st++;if(st>MAX_FILL_STACK_SIZE){error_msg ("extractor/flood_fill","woooo! Fill stack overflow!","");}
+#define pop(i,j) st--;i=a[st].x;j=a[st].y
+
+void 
+RunRegionFill (Pixel ** bitmap, marktype * mark, int i, int j, int xl, int yt, int cols, int rows)
+{
+  int l, r, t, start_up, start_down;
+
+  push (i, j);
+  while (st > 0)
+    {
+      if (st > maxst)
+	maxst = st;
+
+      pop (l, j);
+      r = l;
+
+      while (pbm_getpixel (bitmap, l - 1, j))
+	l--;
+      while (pbm_getpixel (bitmap, r + 1, j))
+	r++;
+      start_up = start_down = 1;
+      for (t = min (r + 1, cols - 1); t >= max (l - 1, 0); t--)
+	{
+	  if ((t >= l) && (t <= r))
+	    {
+	      pbm_putpixel (bitmap, t, j, 0);
+
+	      if (mark)
+		pbm_putpixel (mark->bitmap, t - xl, j - yt, 1);
+	    }
+
+	  if ((j - 1 >= 0) && (pbm_getpixel (bitmap, t, j - 1)))
+	    {
+	      if (start_up)
+		{
+		  push (t, j - 1);
+		  start_up = 0;
+		}
+	    }
+	  else
+	    start_up = 1;
+	  if ((j + 1 < rows) && (pbm_getpixel (bitmap, t, j + 1)))
+	    {
+	      if (start_down)
+		{
+		  push (t, j + 1);
+		  start_down = 0;
+		}
+	    }
+	  else
+	    start_down = 1;
+	}
+    }
+}
+
+
+
+
+/*
+
+   ExtractNextMark starts at (tx,ty) and looks for a set (==1) pixel.
+   The mark is then boundary traced to give an outline {The process is a
+   4-pixel exterior check -- giving 8 way connectivity}, and then
+   RunRegionFill is used to remove the mark and set the pixels in the
+   mark.
+
+   usage: ExtractNextMark(bitmap,&marklist,&startx,&starty,int cols,int rows);
+   Defines:
+   gp - gets a pixel, but returns 0 for outside regions.
+   boundary_{x,y}comp - returns the {x,y} component of the direction.
+   dir 0=right, 1=down, 2=left, 3=up.
+ */
+
+#define gp(i,j) ( (((i)<0)||((j)<0)||((i)>=cols)||((j)>=rows))? 0 : pbm_getpixel(bitmap,(i),(j)))
+
+#define boundary_xcomp(x) (x==0?1:(x==2?-1 : 0))
+#define boundary_ycomp(x) (x==1?1:(x==3?-1 : 0))
+
+int 
+ExtractNextMark (Pixel ** bitmap, marklistptr * marklist, int *tx, int *ty, int cols, int rows)
+{
+  int i, j, xl, xr, yt, yb;
+  register int dir, tempdir;
+  marktype d;
+
+  xl = INT_MAX;
+  xr = (-1);
+  yt = INT_MAX;
+  yb = (-1);
+
+  i = *tx;
+  j = *ty;
+  if ((i < 0) || (j < 0))
+    error_msg ("ExtractNextMark()", "Stupid values for x,y in findnextmark()", "");
+  while (!pbm_getpixel (bitmap, i, j))
+    {
+      i++;
+      if (i >= cols)
+	{
+	  i = 0;
+	  j++;
+	  if (j >= rows)
+	    return 0;
+	}
+    }
+
+  *tx = i;
+  *ty = j;
+  dir = 0;
+  j = j - 1;			/* start above */
+  do
+    {
+      if (i > xr)
+	xr = i - 1;
+      if (i < xl)
+	xl = i + 1;
+      if (j < yt)
+	yt = j + 1;
+      if (j > yb)
+	yb = j - 1;
+      tempdir = (dir + 1) % 4;
+      if (gp (i + boundary_xcomp (tempdir), j + boundary_ycomp (tempdir)) == 0)
+	dir = tempdir;
+      else
+	{
+	  tempdir = dir;
+	  if (gp (i + boundary_xcomp (tempdir), j + boundary_ycomp (tempdir)) == 0)
+	    dir = tempdir;
+	  else
+	    {
+	      tempdir = (dir + 3) % 4;
+	      if (gp (i + boundary_xcomp (tempdir), j + boundary_ycomp (tempdir)) == 0)
+		dir = tempdir;
+	      else
+		dir = (dir + 2) % 4;
+	    }
+	}
+      i += boundary_xcomp (dir);
+      j += boundary_ycomp (dir);
+    }
+  while ((i != (*tx)) || (j != (*ty - 1)));
+
+  d.w = (xr - xl + 1);
+  d.h = (yb - yt + 1);
+  d.xpos = xl;
+  d.ypos = yt;
+  d.bitmap = pbm_allocarray (d.w, d.h);
+
+  RunRegionFill (bitmap, &d, *tx, *ty, xl, yt, cols, rows);
+
+  marktype_calc_features (&d, "?");
+  marklist_add (marklist, d);
+
+  *tx = *tx + 1;
+  return 1;
+}
+
+
+
+
+static void 
+normalise (float *p, int w)
+{
+  int x;
+  float mx;
+
+  mx = p[0];
+  for (x = 0; x < w; x++)
+    if (p[x] > mx)
+      mx = p[x];
+  if (mx != 0)
+    {
+      for (x = 0; x < w; x++)
+	p[x] = p[x] / mx;
+    }
+}
+
+
+
+#define MAXPROFILE 1000
+float profile[MAXPROFILE], p2[MAXPROFILE];
+
+
+static int 
+possibly_joined (marktype * d)
+{
+  float aspect;
+  int i, n, x, y;
+  int leftable, l2;
+
+
+  aspect = d->w / (float) d->h;
+
+  if ((aspect >= 1.1) || (d->w < MAXPROFILE))
+    {
+      /* examine it more closely */
+
+
+      /* set up profile[] with a vertical profile */
+      for (x = 0; x < d->w; x++)
+	{
+	  profile[x] = 0;
+	  for (y = 0; y < d->h; y++)
+	    if (pbm_getpixel (d->bitmap, x, y))
+	      profile[x]++;
+	}
+
+      /* average the profile from (up to) 3 either way */
+      for (x = 0; x < d->w; x++)
+	{
+	  n = 0;
+	  p2[x] = 0;
+	  for (i = x - 3; i <= x + 3; i++)
+	    if ((i >= 0) && (i < d->w))
+	      {
+		n++;
+		p2[x] += profile[x];
+	      }
+	  p2[x] = p2[x] / (float) n;
+	}
+      for (x = 0; x < d->w; x++)
+	profile[x] = p2[x];
+
+      /* normalise profile[] */
+      normalise (profile, d->w);
+
+      /* find a little local minimum, with profile <0.25, make p2[] either 0 or 1 */
+      for (x = 0; x < d->w; x++)
+	p2[x] = 0;
+      for (x = 3; x < d->w - 3; x++)
+	{
+	  if (((profile[x - 2] - profile[x]) > 0.00) &&
+	      ((profile[x + 2] - profile[x]) > 0.00) &&
+	      (profile[x] < .25))
+	    p2[x] = 1;
+	}
+
+      /* travel rightwards, checking aspect ratio, looking at p2 now */
+      leftable = l2 = 0;
+      for (x = 0; x < d->w; x++)
+	if (p2[x] == 1)
+	  {
+	    if ((fabs ((abs (x - leftable) / (float) d->h) - 0.7) > 0.3) &&
+		(fabs ((abs (x - l2) / (float) d->h) - 0.7) > 0.3))
+	      p2[x] = 0;
+	    if (p2[x] == 1)
+	      {
+		leftable = l2;
+		l2 = x;
+	      }
+	  }
+
+      /* travel inwards, checking widths of chars, looking at p2 */
+      /* if a char has an aspect ratio of <= ? kill it */
+      l2 = 0;
+      leftable = d->w - 1;
+      x = 0;
+      y = d->w - 1;
+      while (x <= y)
+	{
+	  if (p2[x])
+	    {
+	      if (abs (x - l2) < max (d->h * 0.6, 5))
+		p2[x] = 0;
+	      if (p2[x])
+		l2 = x;
+	    }
+	  if (p2[y])
+	    {
+	      if (abs (y - leftable) < max (d->h * 0.6, 5))
+		p2[y] = 0;
+	      if (p2[y])
+		leftable = y;
+	    }
+	  x++;
+	  y--;
+	}
+
+
+      for (x = 0; x < d->w; x++)
+	if (p2[x])
+	  return 1;
+    }
+  return 0;
+}
+
+
+static void 
+split_mark (marklistptr * list, marktype * joinedmark)
+{
+  marktype new, old;
+  int x, found = 0, left = 0;
+  int i, j;
+  int oldw, oldh;
+  int overwrite = 1;
+
+  old = marktype_copy (*joinedmark);
+  oldw = joinedmark->w;
+  oldh = joinedmark->h;
+  for (x = 0; x < old.w; x++)
+    if (p2[x])
+      {
+	found = x;
+	break;
+      }
+  do
+    {
+      /* split at (found) position */
+      new.w = found + 1 - left;
+      new.h = old.h;
+      new.xpos = old.xpos + left;
+      new.ypos = old.ypos;
+      new.bitmap = pbm_allocarray (new.w, new.h);
+
+      for (i = 0; i < new.w; i++)
+	for (j = 0; j < new.h; j++)
+	  pbm_putpixel (new.bitmap, i, j, pbm_getpixel (old.bitmap, left + i, j));
+
+      marktype_adj_bound (&new);
+
+      if (overwrite)
+	{
+	  overwrite = 0;
+	  pbm_freearray (&(joinedmark->bitmap), oldh);
+	  *joinedmark = new;
+	}
+      else
+	marklist_add (list, new);
+
+
+      p2[found] = 0;
+      left = found;
+      found = 0;
+      for (x = left; x < old.w; x++)
+	if (p2[x])
+	  {
+	    found = x;
+	    break;
+	  }
+      if (left == old.w - 1)
+	break;			/* if we've got to the right */
+      if (found == 0)
+	found = old.w - 1;
+    }
+  while (found);
+  pbm_freearray (&(old.bitmap), oldh);
+}
+
+
+
+
+
+void 
+ExtractAllMarks (Pixel ** bitmap, marklistptr * list, int cols, int rows)
+{
+  int startx, starty, count;
+  marklistptr step;
+
+  startx = starty = 0;
+  while (ExtractNextMark (bitmap, list, &startx, &starty, cols, rows))
+    ;
+
+  if (splitjoined)
+    for (step = (*list), count = 0; step; step = step->next, count++)
+      if (possibly_joined (&(step->data)))
+	split_mark (list, &(step->data));
+
+  for (step = (*list), count = 0; step; step = step->next, count++)
+    step->data.symnum = count;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/extractor.h	(revision 16561)
@@ -0,0 +1,38 @@
+/**************************************************************************
+ *
+ * extractor.h -- Functions related to extracting marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 _EXTRACTOR_H_
+#define _EXTRACTOR_H_
+
+#include "marklist.h"
+#include "pbmtools.h"
+
+extern int splitjoined;
+
+void RunRegionFill (Pixel ** bitmap, marktype * mark, int i, int j, int xl, int yt, int cols, int rows);
+
+int ExtractNextMark (Pixel ** bitmap, marklistptr * marklist, int *tx, int *ty, int cols, int rows);
+void ExtractAllMarks (Pixel ** bitmap, marklistptr * marklist, int cols, int rows);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/felics.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/felics.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/felics.c	(revision 16561)
@@ -0,0 +1,340 @@
+/**************************************************************************
+ *
+ * felics.c -- The main guts of the felics algorithm
+ * Copyright (C) 1994  Neil Sharman and Kerry Guise
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+
+
+#undef putw
+#define putw(x)	{					\
+	short z;					\
+	byteptr = (unsigned char *)(x);			\
+	for (z = 0; z < sizeof(int); z++)		\
+		(void) putc((int) byteptr[z], fp_out);	\
+}
+#define INTSIZE sizeof(int) * 8
+#define BOOLEAN short
+#define TRUE 1
+#define FALSE 0
+
+#define IN_RANGE 0
+#define OUT_OF_RANGE 1
+#define BELOW_RANGE 0
+#define ABOVE_RANGE 1
+
+
+void 
+encode (unsigned int *line, int width, int height, int maxval, int maxk,
+	FILE * fp_in, FILE * fp_out, int (*get) (FILE *))
+{
+  int x, y;
+  unsigned long P, L, H, N1, N2, delta;
+  unsigned long cumul_param[256][INTSIZE];
+  unsigned max_cumul;
+
+  memset (cumul_param, '0', sizeof (cumul_param));
+
+  if (maxk)
+    max_cumul = maxk;
+  else
+    CEILLOG_2 (maxval, max_cumul);
+
+  ENCODE_START (fp_out)
+
+    for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+	if (y > 0)
+	  {
+	    if (x > 0)
+	      {			/* ******** */
+		N1 = line[x];	/* ****1*** */
+		N2 = line[x - 1];	/* ***2P*** */
+	      }			/* ******** */
+	    else
+	      {			/* |******* */
+		N1 = line[x];	/* |12***** */
+		N2 = line[x + 1];	/* |P***** */
+	      }			/* |******* */
+	  }
+	else
+	  {
+	    if (x > 1)
+	      {
+		N1 = line[x - 1];	/* -------- */
+		N2 = line[x - 2];	/* **12P*** */
+	      }			/* ******** */
+	    else if (x > 0)
+	      {
+		N1 = line[x - 1];	/* +------- */
+		N2 = N1;	/* |3P***** */
+	      }			/* |******* */
+	    else
+	      {
+		line[x] = get (fp_in);	/* +------- */
+		BINARY_ENCODE (line[x] + 1, maxval + 1);	/* |P****** */
+		continue;	/* |******* */
+	      }
+	  }
+
+
+	H = N1 > N2 ? N1 : N2;
+	L = N1 < N2 ? N1 : N2;
+	delta = H - L;
+
+	line[x] = P = get (fp_in);
+
+	if ((L <= P) && (P <= H))
+	  {
+	    /* IN-RANGE */
+	    unsigned long range, logofrange, thresh, numlong;
+	    int nbits;
+
+	    ENCODE_BIT (IN_RANGE);
+
+	    P -= L;
+
+	    range = delta + 1;
+
+	    if (range > 1)
+	      {
+		/* 2**i <= delta + 1 < 2**(i+1) ; find i ... */
+		CEILLOG_2 (range, logofrange);
+
+		/* number of n-bit values in the range */
+		thresh = (1 << logofrange) - range;
+
+		/* number of n+1-bit values in the range */
+		numlong = range - thresh;
+
+		/* rotate the n-bit codes into the centre */
+		P -= (numlong >> 1);
+		if ((long) P < 0)
+		  P += range;
+
+		if (P < thresh)
+		  nbits = logofrange - 1;
+		else
+		  {
+		    nbits = logofrange;
+		    P += thresh;
+		  }
+		while (--nbits >= 0)
+		  ENCODE_BIT ((P >> nbits) & 1);
+	      }
+	  }
+	else
+	  {
+	    /* OUT_OF_RANGE */
+	    unsigned long diff, parm, i, min;
+
+	    ENCODE_BIT (OUT_OF_RANGE);
+
+	    if (P < L)
+	      {
+		ENCODE_BIT (BELOW_RANGE);
+		diff = L - P - 1;
+	      }
+	    else
+	      {
+		ENCODE_BIT (ABOVE_RANGE);
+		diff = P - H - 1;
+	      }
+
+	    /* Now code 'diff' as a Golomb-Rice code */
+
+	    parm = 0;
+	    min = cumul_param[delta][0];
+	    for (i = 1; i < max_cumul; i++)
+	      if (cumul_param[delta][i] < min)
+		{
+		  min = cumul_param[delta][i];
+		  parm = i;
+		}
+
+	    UNARY_ENCODE ((diff >> parm) + 1);
+	    BINARY_ENCODE ((diff & ((1 << parm) - 1)) + 1, 1 << parm);
+
+	    for (i = 0; i < max_cumul; i++)
+	      cumul_param[delta][i] += (diff >> i) + 1 + i;
+	  }
+
+      }
+
+  ENCODE_DONE
+
+}
+
+
+
+
+
+
+void 
+decode (unsigned int *line, int width, int height, int maxval, int maxk,
+	FILE * fp_in, FILE * fp_out)
+{
+  int x, y;
+  unsigned int P, L, H, N1, N2, delta;
+  unsigned long cumul_param[256][INTSIZE];
+  unsigned max_cumul;
+
+  memset (cumul_param, '0', sizeof (cumul_param));
+
+  if (maxk)
+    max_cumul = maxk;
+  else
+    CEILLOG_2 (maxval, max_cumul);
+
+  DECODE_START (fp_in);
+
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+	if (y > 0)
+	  {
+	    if (x > 0)
+	      {			/* ******** */
+		N1 = line[x];	/* ****1*** */
+		N2 = line[x - 1];	/* ***2P*** */
+	      }			/* ******** */
+	    else
+	      {			/* |******* */
+		N1 = line[x];	/* |12***** */
+		N2 = line[x + 1];	/* |P***** */
+	      }			/* |******* */
+	  }
+	else
+	  {
+	    if (x > 1)
+	      {
+		N1 = line[x - 1];	/* -------- */
+		N2 = line[x - 2];	/* **12P*** */
+	      }			/* ******** */
+	    else if (x > 0)
+	      {
+		N1 = line[x - 1];	/* +------- */
+		N2 = N1;	/* |3P***** */
+	      }			/* |******* */
+	    else
+	      {
+		BINARY_DECODE (line[x], maxval + 1);
+		line[x]--;
+		if (maxval < 256)
+		  putc (line[x], fp_out);	/* +------- */
+		else		/* |P****** */
+		  fprintf (fp_out, "%u ", line[x]);	/* |******* */
+		continue;
+	      }
+	  }
+
+
+	H = N1 > N2 ? N1 : N2;
+	L = N1 < N2 ? N1 : N2;
+	delta = H - L;
+
+	if (DECODE_BIT == IN_RANGE)
+	  {
+	    unsigned long i, range, logofrange, thresh, numlong;
+	    unsigned xx, l;
+
+	    range = delta + 1;
+
+	    if (range > 1)
+	      {
+		/* 2**i <= delta + 1 < 2**(i+1) ; find i ... */
+		CEILLOG_2 (range, logofrange);
+
+		/* number of n-bit values in the range */
+		thresh = (1 << logofrange) - range;
+		for (P = i = 0; i < logofrange - 1; i++)
+		  DECODE_ADD (P);
+		xx = P;
+		l = logofrange - 1;
+		if (P >= thresh)
+		  {
+		    DECODE_ADD (P);
+		    xx = P;
+		    l++;
+		    P -= thresh;
+		  }
+
+		/* number of n+1-bit values in the range */
+		numlong = range - thresh;
+
+		/* rotate the n-bit codes into the centre */
+		P += (numlong >> 1);
+		if ((long) P >= range)
+		  P -= range;
+
+		P += L;
+	      }
+	    else
+	      P = L;
+	  }
+	else
+	  {
+	    int out_of_range = DECODE_BIT;
+	    unsigned long diff, unary, binary, i, min, parm;
+
+	    /* Now code 'diff' as a Golomb-Rice code */
+
+	    parm = 0;
+	    min = cumul_param[delta][0];
+	    for (i = 1; i < max_cumul; i++)
+	      if (cumul_param[delta][i] < min)
+		{
+		  min = cumul_param[delta][i];
+		  parm = i;
+		}
+
+	    UNARY_DECODE (unary);
+	    diff = (unary - 1) << parm;
+	    BINARY_DECODE (binary, 1 << parm);
+	    diff |= binary - 1;
+
+	    for (i = 0; i < max_cumul; i++)
+	      cumul_param[delta][i] += (diff >> i) + 1 + i;
+
+	    if (out_of_range == BELOW_RANGE)
+	      {
+		P = L - diff - 1;
+	      }
+	    else
+	      {
+		P = diff + H + 1;
+	      }
+	  }
+
+	line[x] = P;
+
+	if (maxval < 256)
+	  (void) putc (P, fp_out);
+	else
+	  fprintf (fp_out, "%u ", P);
+      }
+
+  DECODE_DONE
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/lstevent.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/lstevent.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/lstevent.c	(revision 16561)
@@ -0,0 +1,424 @@
+/**************************************************************************
+ *
+ * lstevent.c -- Routines to implement "events" using MTF lists.
+ * Copyright (C) 1987  Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "arithcode.h"
+#include "model.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:16:56  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:26  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:33:37  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1994/09/20  04:41:40  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+void
+write_method ()
+{
+  fprintf (stderr, "C");
+}
+
+
+/*===========================================*/
+
+/*
+ * [Rodney Brown - Sep/95]
+ * On IBM RS/6000 AIX version 3.2 the compiler defines NULL as (void *)0
+ * when in ANSI mode. Hence the (event) and (point) casts
+ */
+
+
+#define GETNEWNODE(e,node)		\
+do 					\
+{	node = p32(numnodes) ;		\
+	node->eventnum = (event)e ;	\
+	node->count = increment ;	\
+	node->foll.totalcnt = 0 ;	\
+	node->foll.notfound = 0 ;	\
+	node->foll.list = (point)NULL ;	\
+	numnodes += 1 ;			\
+	nfreenodes -= 1 ;		\
+}					\
+while (false)
+
+/*===========================================*/
+
+#define INSERTNODE(U,node)		\
+do					\
+{	node->next = U->list ;		\
+	U->list = p16(node) ;		\
+	U->totalcnt += increment;	\
+}					\
+while (false)
+
+/*===========================================*/
+
+int
+halvelist (p)
+     register eventnode *p;
+{
+  register int sum = 0;
+  while (p != ENULL)
+    {
+      sum += p->count = (p->count + 1) / 2;
+      p = p32 (p->next);
+    }
+  return (sum);
+}
+
+/*===========================================*/
+
+void
+halveallcounts (U)
+     register eventset *U;
+{
+  U->notfound = (U->notfound + 1) / 2;
+  U->totalcnt = U->notfound;
+  U->totalcnt += halvelist (p32 (U->list));
+}
+
+/*===========================================*/
+
+eventnode *
+encode_event_noex (U, e, success)
+     register eventset *U;
+     event e;
+     boolean *success;
+{
+  register eventnode *pnode, *node;
+  register int lbnd, totl;
+
+  if (U->list == (point)NULL)
+    {
+      *success = false;
+      U->notfound += increment;
+      U->totalcnt += increment;
+      GETNEWNODE (e, node);
+      INSERTNODE (U, node);
+      return (node);
+    }
+
+  lbnd = 0;
+  pnode = ENULL;
+  node = p32 (U->list);
+  while (node != ENULL)
+    {
+      if (e == node->eventnum)
+	break;
+      lbnd += node->count;
+      pnode = node;
+      node = p32 (node->next);
+    }
+
+  totl = U->totalcnt;
+
+  if (node == ENULL)
+    {				/* event not found in list */
+      if ((totl > U->notfound))
+	arithmetic_encode (totl - U->notfound, totl, totl);
+      U->notfound += increment;
+      U->totalcnt += increment;
+      *success = false;
+      GETNEWNODE (e, node);
+      INSERTNODE (U, node);
+    }
+  else
+    {				/* found in the list, so go ahead and code */
+      arithmetic_encode (lbnd, lbnd + node->count, totl);
+      node->count += increment;
+      U->totalcnt += increment;
+      *success = true;
+      /* and move to front */
+      if (pnode != ENULL)
+	{
+	  pnode->next = node->next;
+	  node->next = U->list;
+	  U->list = p16 (node);
+	}
+    }
+  if (U->totalcnt > maxtotalcnt)
+    halveallcounts (U);
+  return (node);
+}
+
+/*===========================================*/
+
+eventnode *
+decode_event_noex (U, success, e)
+     register eventset *U;
+     boolean *success;
+     event *e;
+{
+  register eventnode *node, *pnode;
+  register int tget, lbnd, totl;
+
+  if (U->list == (point)NULL)
+    {
+      *success = false;
+      U->totalcnt += increment;
+      U->notfound += increment;
+      GETNEWNODE (NULL, node);
+      INSERTNODE (U, node);
+      return (node);
+    }
+  totl = U->totalcnt;
+  tget = arithmetic_decode_target (totl);
+
+  if (tget >= totl - U->notfound)
+    {
+      if (totl > U->notfound)
+	arithmetic_decode (totl - U->notfound, totl, totl);
+      U->totalcnt += increment;
+      U->notfound += increment;
+      *success = false;
+      GETNEWNODE (NULL, node);
+      INSERTNODE (U, node);
+    }
+  else
+    {
+      pnode = ENULL;
+      node = p32 (U->list);
+      lbnd = 0;
+      for (;;)
+	{
+	  if (tget < (lbnd += node->count))
+	    break;
+	  pnode = node;
+	  node = p32 (node->next);
+	}
+
+      arithmetic_decode (lbnd - node->count, lbnd, totl);
+      node->count += increment;
+      U->totalcnt += increment;
+      *e = node->eventnum;
+      *success = true;
+      /* and move to front */
+      if (pnode != ENULL)
+	{
+	  pnode->next = node->next;
+	  node->next = U->list;
+	  U->list = p16 (node);
+	}
+    }
+  if (U->totalcnt > maxtotalcnt)
+    halveallcounts (U);
+  return (node);
+}
+
+/*===========================================*/
+
+eventnode *
+encode_event (U, e, success)
+     register eventset *U;
+     event e;
+     boolean *success;
+{
+  register eventnode *pnode, *node, *p;
+  register int lbnd, totl;
+
+  if (U->list == (point)NULL)
+    {
+      *success = false;
+      U->notfound += increment;
+      U->totalcnt += increment;
+      GETNEWNODE (e, node);
+      INSERTNODE (U, node);
+      return (node);
+    }
+
+  lbnd = 0;
+  pnode = ENULL;
+  node = p32 (U->list);
+  while (node != ENULL)
+    {
+      if (!excluded[node->eventnum])
+	{
+	  if (e == node->eventnum)
+	    break;
+	  lbnd += node->count;
+	}
+      pnode = node;
+      node = p32 (node->next);
+    }
+
+  if (node == ENULL)
+    {				/* event not found in list, and whole list has been scanned */
+      totl = lbnd + U->notfound;
+      if ((totl > U->notfound))
+	arithmetic_encode (totl - U->notfound, totl, totl);
+      U->notfound += increment;
+      U->totalcnt += increment;
+      *success = false;
+      GETNEWNODE (e, node);
+      INSERTNODE (U, node);
+    }
+  else
+    {				/* found in the list, so go ahead and code */
+      /* first, total up rest of list */
+      totl = lbnd + node->count;
+      p = p32 (node->next);
+      while (p != ENULL)
+	{
+	  if (!excluded[p->eventnum])
+	    totl += p->count;
+	  p = p32 (p->next);
+	}
+      totl += U->notfound;
+
+      arithmetic_encode (lbnd, lbnd + node->count, totl);
+      node->count += increment;
+      U->totalcnt += increment;
+      *success = true;
+      /* and move to front */
+      if (pnode != ENULL)
+	{
+	  pnode->next = node->next;
+	  node->next = U->list;
+	  U->list = p16 (node);
+	}
+    }
+  if (U->totalcnt > maxtotalcnt)
+    halveallcounts (U);
+  return (node);
+}
+
+/*===========================================*/
+
+eventnode *
+decode_event (U, success, e)
+     register eventset *U;
+     boolean *success;
+     event *e;
+{
+  register eventnode *node, *pnode;
+  register int tget, lbnd, totl;
+
+  if (U->list == (point)NULL)
+    {
+      *success = false;
+      U->totalcnt += increment;
+      U->notfound += increment;
+      GETNEWNODE (NULL, node);
+      INSERTNODE (U, node);
+      return (node);
+    }
+  totl = 0;
+  node = p32 (U->list);
+  while (node != ENULL)
+    {
+      if (!excluded[node->eventnum])
+	totl += node->count;
+      node = p32 (node->next);
+    }
+  totl += U->notfound;
+  tget = arithmetic_decode_target (totl);
+
+  if (tget >= totl - U->notfound)
+    {
+      if (totl > U->notfound)
+	arithmetic_decode (totl - U->notfound, totl, totl);
+      U->totalcnt += increment;
+      U->notfound += increment;
+      *success = false;
+      GETNEWNODE (NULL, node);
+      INSERTNODE (U, node);
+    }
+  else
+    {
+      pnode = ENULL;
+      node = p32 (U->list);
+      lbnd = 0;
+      for (;;)
+	{
+	  if (!excluded[node->eventnum])
+	    {
+	      lbnd += node->count;
+	      if (tget < lbnd)
+		break;
+	    }
+	  pnode = node;
+	  node = p32 (node->next);
+	}
+
+      arithmetic_decode (lbnd - node->count, lbnd, totl);
+      node->count += increment;
+      U->totalcnt += increment;
+      *e = node->eventnum;
+      *success = true;
+      /* and move to front */
+      if (pnode != ENULL)
+	{
+	  pnode->next = node->next;
+	  node->next = U->list;
+	  U->list = p16 (node);
+	}
+    }
+  if (U->totalcnt > maxtotalcnt)
+    halveallcounts (U);
+  return (node);
+}
+
+/*===========================================*/
+
+eventnode *
+newnode (e)
+     event e;
+{
+  register eventnode *node;
+  GETNEWNODE (e, node);
+  node->next = (point)NULL;
+  node->prev = (point)NULL;
+  return (node);
+}
+
+/*===========================================*/
+
+/* not required in this version */
+
+#if 0
+eventnode *
+addevent (U, e)
+     register eventset *U;
+     event e;
+{
+  register eventnode *node;
+  /* get an event node */
+  GETNEWNODE (e, node);
+  INSERTNODE (U, node);
+  if (U->totalcnt > maxtotalcnt)
+    halveallcounts (U);
+  return (node);
+}
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.c	(revision 16561)
@@ -0,0 +1,827 @@
+/**************************************************************************
+ *
+ * marklist.c -- Functions managing the list of marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "utils.h"
+
+void 
+marktype_dump (marktype d)
+{
+  fprintf (stderr, "symnum: %d\n", d.symnum);
+  fprintf (stderr, "xpos: %d, ypos: %d\n", d.xpos, d.ypos);
+  fprintf (stderr, "w: %d, h: %d\n", d.w, d.h);
+  fprintf (stderr, "xcen: %d, ycen: %d\n", d.xcen, d.ycen);
+  write_library_mark (stderr, d);
+}
+
+
+/* copy, including make a copy of the bitmap */
+marktype 
+marktype_copy (marktype d)
+{
+  marktype d2;
+  int i, j;
+
+  d2 = d;
+  d2.bitmap = pbm_allocarray (d2.w, d2.h);
+  for (i = 0; i < d2.w; i++)
+    for (j = 0; j < d2.h; j++)
+      pbm_putpixel (d2.bitmap, i, j, pbm_getpixel (d.bitmap, i, j));
+  return d2;
+}
+
+
+float 
+marktype_density (marktype d)
+{
+  return d.set / (float) (d.w * d.h);
+}
+
+
+
+void 
+marktype_calc_centroid (marktype * b)
+{
+  int r, c;
+  int count = 0;
+  int xtot = 0, ytot = 0;
+
+  for (r = 0; r < b->h; r++)
+    for (c = 0; c < b->w; c++)
+      if (pbm_getpixel (b->bitmap, c, r))
+	{
+	  count++;
+	  xtot += c;
+	  ytot += r;
+	}
+  if (count)
+    {
+      xtot /= count;
+      ytot /= count;
+    }
+  b->xcen = xtot;
+  b->ycen = ytot;
+}
+
+
+
+static void 
+marktype_area (marktype * d)
+{
+  int x, y;
+  int set = 0;
+
+  for (y = 0; y < d->h; y++)
+    for (x = 0; x < d->w; x++)
+      if (pbm_getpixel (d->bitmap, x, y))
+	set++;
+  d->set = set;
+}
+
+
+static void 
+marktype_h_runs (marktype * d)
+{
+  int x, y;
+  int inrun = 0, num = 0;
+  int foundblack = 0;
+
+  for (y = 0; y < d->h; y++)
+    {
+      inrun = 0;
+      foundblack = 0;
+      for (x = 0; x < d->w; x++)
+	{
+	  if ((!foundblack) && (pbm_getpixel (d->bitmap, x, y)))
+	    foundblack = 1;
+	  if ((foundblack) && (pbm_getpixel (d->bitmap, x, y) == 0))
+	    inrun = 1;
+	  if ((inrun) && (pbm_getpixel (d->bitmap, x, y)))
+	    {
+	      num++;
+	      inrun = 0;
+	      foundblack = 0;
+	    }
+	}
+    }
+  d->h_runs = num;
+}
+
+static void 
+marktype_v_runs (marktype * d)
+{
+  int x, y;
+  int inrun = 0, num = 0;
+  int foundblack = 0;
+
+
+  for (x = 0; x < d->w; x++)
+    {
+      inrun = 0;
+      foundblack = 0;
+      for (y = 0; y < d->h; y++)
+	{
+	  if ((!foundblack) && (pbm_getpixel (d->bitmap, x, y)))
+	    foundblack = 1;
+	  if ((foundblack) && (pbm_getpixel (d->bitmap, x, y) == 0))
+	    inrun = 1;
+	  if ((inrun) && (pbm_getpixel (d->bitmap, x, y)))
+	    {
+	      num++;
+	      inrun = 0;
+	      foundblack = 0;
+	    }
+	}
+    }
+  d->v_runs = num;
+}
+
+
+/* assume we already have the centroid! */
+static void 
+marktype_prog_cent (marktype * d)
+{
+  int x, y;
+  int xtot = 0, ytot = 0;
+  int num = 0;
+
+  xtot = ytot = num = 0;
+  for (x = 0; x < d->xcen; x++)
+    for (y = 0; y < d->ycen; y++)
+      if (pbm_getpixel (d->bitmap, x, y))
+	{
+	  xtot += x;
+	  ytot += y;
+	  num++;
+	}
+  if (num == 0)
+    {
+      d->xcen1 = -1;
+      d->ycen1 = -1;
+    }
+  else
+    {
+      d->xcen1 = xtot / num;
+      d->ycen1 = ytot / num;
+    }
+
+
+  xtot = ytot = num = 0;
+  for (x = d->xcen; x < d->w; x++)
+    for (y = 0; y < d->ycen; y++)
+      if (pbm_getpixel (d->bitmap, x, y))
+	{
+	  xtot += x;
+	  ytot += y;
+	  num++;
+	}
+  if (num == 0)
+    {
+      d->xcen2 = -1;
+      d->ycen2 = -1;
+    }
+  else
+    {
+      d->xcen2 = xtot / num;
+      d->ycen2 = ytot / num;
+    }
+
+
+
+  xtot = ytot = num = 0;
+  for (x = 0; x < d->xcen; x++)
+    for (y = d->ycen; y < d->h; y++)
+      if (pbm_getpixel (d->bitmap, x, y))
+	{
+	  xtot += x;
+	  ytot += y;
+	  num++;
+	}
+
+
+  if (num == 0)
+    {
+      d->xcen3 = -1;
+      d->ycen3 = -1;
+    }
+  else
+    {
+      d->xcen3 = xtot / num;
+      d->ycen3 = ytot / num;
+    }
+
+  xtot = ytot = num = 0;
+  for (x = d->xcen; x < d->w; x++)
+    for (y = d->ycen; y < d->h; y++)
+      if (pbm_getpixel (d->bitmap, x, y))
+	{
+	  xtot += x;
+	  ytot += y;
+	  num++;
+	}
+  if (num == 0)
+    {
+      d->xcen4 = -1;
+      d->ycen4 = -1;
+    }
+  else
+    {
+      d->xcen4 = xtot / num;
+      d->ycen4 = ytot / num;
+    }
+}
+
+
+
+/* call this to get all features */
+void 
+marktype_calc_features (marktype * d, char str[])
+{
+  if (strlen (str))
+    strcpy (d->name, str);
+  marktype_calc_centroid (d);
+  marktype_area (d);
+  marktype_prog_cent (d);
+  marktype_h_runs (d);
+  marktype_v_runs (d);
+}
+
+
+
+
+
+void 
+marktype_adj_bound (marktype * m)
+{
+  int x, y, w, h;
+  int xpos, ypos;
+  int top = 0, left = 0, right = 0, bot = 0;
+  marktype temp;
+
+  w = m->w;
+  h = m->h;
+  xpos = m->xpos;
+  ypos = m->ypos;
+
+  for (y = 0; y < h; y++)
+    for (x = 0; x < w; x++)
+      if (pbm_getpixel (m->bitmap, x, y))
+	{
+	  top = y;
+	  x = w;
+	  y = h;
+	}
+
+  for (y = h - 1; y >= 0; y--)
+    for (x = 0; x < w; x++)
+      if (pbm_getpixel (m->bitmap, x, y))
+	{
+	  bot = y;
+	  x = w;
+	  y = -1;
+	}
+
+  for (x = 0; x < w; x++)
+    for (y = 0; y < h; y++)
+      if (pbm_getpixel (m->bitmap, x, y))
+	{
+	  left = x;
+	  x = w;
+	  y = h;
+	}
+
+  for (x = w - 1; x >= 0; x--)
+    for (y = 0; y < h; y++)
+      if (pbm_getpixel (m->bitmap, x, y))
+	{
+	  right = x;
+	  x = -1;
+	  y = h;
+	}
+
+  temp.w = right - left + 1;
+  temp.h = bot - top + 1;
+  temp.xpos = xpos + left;
+  temp.ypos = ypos + top;
+  temp.bitmap = pbm_allocarray (temp.w, temp.h);
+
+  for (x = 0; x < temp.w; x++)
+    for (y = 0; y < temp.h; y++)
+      pbm_putpixel (temp.bitmap, x, y, pbm_getpixel (m->bitmap, x + left, y + top));
+
+  pbm_freearray (&(m->bitmap), h);
+
+  marktype_calc_features (&temp, "?");
+  *m = temp;
+}
+
+
+
+
+
+
+/* returns a newly created mark reprenting the average of the marklist */
+void 
+marklist_average (marklistptr list, marktype * avgmark)
+{
+  int w = 0, h = 0;
+  int t = INT_MAX, b = 0, l = INT_MAX, r = 0, matched = 0;
+  marktype d, temp;
+  marklistptr step;
+  static int a[100][100];
+  int x, y;
+
+  if (list == NULL)
+    error_msg ("marklist_average", "Can't average 'nothing'!", "");
+
+  for (step = list; step; step = step->next)
+    {
+      matched++;
+      temp = step->data;
+      w = max (w, temp.w);
+      h = max (h, temp.h);
+      r = max (r, temp.xcen);
+      l = min (l, temp.xcen);
+      b = max (b, temp.ycen);
+      t = min (t, temp.ycen);
+    }
+  w = w + (r - l);
+  h = h + (b - t);
+  if ((w >= 100) || (h >= 100))
+    matched = 0;
+
+  if (matched > 1)
+    {
+      int x_off, y_off;
+      for (x = 0; x < w; x++)
+	for (y = 0; y < h; y++)
+	  a[x][y] = 0;
+
+      for (step = list; step; step = step->next)
+	{
+	  x_off = r - step->data.xcen;
+	  y_off = b - step->data.ycen;
+	  for (x = 0; x < step->data.w; x++)
+	    for (y = 0; y < step->data.h; y++)
+	      if (pbm_getpixel (step->data.bitmap, x, y))
+		a[x + x_off][y + y_off]++;
+	}
+
+      d.w = w;
+      d.h = h;
+      d.bitmap = pbm_allocarray (w, h);
+      for (x = 0; x < w; x++)
+	for (y = 0; y < h; y++)
+	  if (a[x][y] > max (1, (matched / 2)))		/* > gives better CR than >= */
+	    pbm_putpixel (d.bitmap, x, y, 1);
+      marktype_adj_bound (&d);
+      strcpy (d.name, "?");
+      marktype_calc_centroid (&d);
+    }
+  else
+    d = marktype_copy (list->data);
+
+  *avgmark = d;
+}
+
+
+
+
+
+
+void 
+marklist_setnull (marklistptr * list)
+{
+  *list = NULL;
+}
+
+
+marklistptr 
+marklist_add (marklistptr * list, marktype m)
+{
+  marklistptr p;
+
+  if (*list == NULL)
+    {
+      *list = (marklistptr) malloc (sizeof (marklisttype));
+      if ((*list) == NULL)
+	error_msg ("marklist_add", "OUT of Memory in marklist_add", "");
+      p = *list;
+    }
+  else
+    {
+      p = *list;
+      while (p->next != NULL)
+	p = p->next;
+      p->next = (marklistptr) malloc (sizeof (marklisttype));
+      if (p->next == NULL)
+	error_msg ("marklist_add", "OUT of Memory in marklist_add", "");
+      p = p->next;
+    }
+  p->data = m;
+  p->next = NULL;
+  return p;
+
+}
+
+
+
+marklistptr 
+marklist_addcopy (marklistptr * list, marktype m)
+{
+  marktype d;
+
+  d = marktype_copy (m);
+  return marklist_add (list, d);
+}
+
+
+
+int 
+marklist_length (marklistptr list)
+{
+  int count = 0;
+
+  while (list)
+    {
+      count++;
+      list = list->next;
+    }
+  return count;
+}
+
+
+
+
+
+void 
+marklist_dump (marklistptr list)
+{
+  int count = 0;
+  marktype d;
+
+  while (list)
+    {
+      d = list->data;
+      marktype_dump (d);
+
+      count++;
+      list = list->next;
+    }
+}
+
+
+
+
+/* pos starts from 0, to up marklist_length-1 */
+marklistptr 
+marklist_getat (marklistptr list, int pos, marktype * d)
+{
+  int count = 0;
+
+  while (list)
+    {
+      if (count == pos)
+	{
+	  *d = list->data;
+	  return list;
+	}
+      count++;
+      list = list->next;
+    }
+  fprintf (stderr, "marklist_getat(): access off ends of list: pos %d\n", pos);
+  return NULL;
+}
+
+
+
+void 
+marklist_free (marklistptr * list)
+{
+  marklistptr n;
+
+  while ((*list) != NULL)
+    {
+      n = (*list)->next;
+      pbm_freearray (&((*list)->data.bitmap), (*list)->data.h);
+      free ((*list));
+      (*list) = n;
+    }
+}
+
+
+
+int 
+marklist_removeat (marklistptr * list, int pos)
+{
+  marklistptr c, n, p = NULL;
+  int count = 0;
+
+  if ((pos == 0) && (*list))
+    {				/* if removing the 1st element */
+      *list = (*list)->next;
+      return 1;
+    }
+  else
+    {
+      c = *list;
+      while (c)
+	{
+	  if (count == pos)
+	    {			/* two cases, no previous, >=1 previous */
+	      if (p == NULL)	/* no previous */
+		{
+		  n = c->next;
+		  pbm_freearray (&c->data.bitmap, c->data.h);
+		  free (c);
+		  c = n;
+		  return 1;
+		}
+	      else
+		/* >=1 previous */
+		{
+		  n = c->next;
+		  pbm_freearray (&c->data.bitmap, c->data.h);
+		  free (c);
+		  p->next = n;
+		  return 1;
+		}
+	    }
+	  count++;
+	  p = c;
+	  c = c->next;
+	}
+    }
+  fprintf (stderr, "marklist_removeat(): access off ends of list: pos %d\n", pos);
+  return 0;			/* nothing was removed */
+}
+
+
+
+marklistptr 
+marklist_next (marklistptr list)
+{
+  return list->next;
+}
+
+
+/* only writes out ascii form */
+void 
+write_library_mark (FILE * fp, marktype d)
+{
+  int r, c, rows, cols;
+  int xtot = d.xcen, ytot = d.ycen;
+
+  rows = d.h;
+  cols = d.w;
+
+  fprintf (fp, "Mark: %d\n", d.symnum);
+  fprintf (fp, "Char: %s\n", d.name);
+  fprintf (fp, "Cols: %d Rows: %d\n", d.w, d.h);
+  for (r = 0; r < d.h; r++)
+    {
+      for (c = 0; c < d.w; c++)
+	if ((r == ytot) && (c == xtot))
+	  putc (pbm_getpixel (d.bitmap, c, r) ? 'C' : '!', fp);
+	else
+	  putc (pbm_getpixel (d.bitmap, c, r) ? 'X' : '.', fp);
+      putc ('\n', fp);
+    }
+}
+
+
+
+/* only reads in ascii library */
+int 
+read_library_mark (FILE * fp, marktype * d)
+{
+  int r, c, rows, cols, numread, ch1 = 'C';
+  char str1[100], str2[100];
+
+  numread = fscanf (fp, "%s %d\n", str1, &(d->symnum));
+  if ((numread != 2) || (strcmp (str1, "Mark:") != 0))
+    return 0;
+  fscanf (fp, "%s %s\n", str1, (d->name));
+  fscanf (fp, "%s %d %s %d\n", str1, &(d->w), str2, &(d->h));
+  cols = d->w;
+  rows = d->h;
+  d->bitmap = pbm_allocarray (cols, rows);
+  for (r = 0; r < rows; r++)
+    {
+      for (c = 0; c < cols; c++)
+	{
+	  ch1 = getc (fp);
+	  if ((ch1 == 'X') || (ch1 == 'C'))
+	    pbm_putpixel (d->bitmap, c, r, 1);
+	  else
+	    pbm_putpixel (d->bitmap, c, r, 0);
+	}
+      ch1 = getc (fp);
+    }
+  marktype_calc_features (d, "?");
+
+  return 1;
+}
+
+
+
+
+
+
+
+int 
+read_library (char libraryname[], marklistptr * library)
+{
+  int err, count;
+  FILE *lib;
+  marktype d;
+  marklistptr step;
+
+  lib = fopen (libraryname, "rb");
+  if (lib == NULL)
+    error_msg ("read_library()", "Trouble opening library file", "");
+  *library = NULL;
+  count = 0;
+  while (!isEOF (lib))
+    {
+      err = read_library_mark (lib, &d);
+      if (!err)
+	error_msg ("read_library()", "unknown format of the library file.", "");
+      d.symnum = count;
+      count++;
+      if ((*library) == NULL)
+	step = marklist_add (library, d);
+      else
+	step = marklist_add (&step, d);
+    }
+  count = marklist_length (*library);
+  fclose (lib);
+
+  return count;
+}
+
+
+
+
+
+
+/**********************************************************************
+
+'elem' routines for coding offsets
+
+the elem datatype contains the 3 params for arithmetic coding,
+stored as a no-duplicates order linked list.
+
+************************************************************************/
+
+
+
+
+void 
+elem_add (elemlist * list, int num)
+{
+  elemptr temp, nn;
+  int p, f;
+
+  list->tot++;
+  if (list->head == NULL)
+    {
+      list->head = (elemptr) malloc (sizeof (elem));
+      if ((list->head) == NULL)
+	error_msg ("", "ekk", "");
+      temp = list->head;
+      temp->next = NULL;
+      temp->num = num;
+      temp->pos = 0;
+      temp->freq = 1;
+    }
+  else
+    {				/* add to list */
+      temp = list->head;
+      p = 0;
+      f = 0;
+      while ((num > temp->num) && (temp->next != NULL))
+	{
+	  p = temp->pos;
+	  f = temp->freq;
+	  temp = temp->next;
+	}
+
+      if (temp->num == num)
+	temp->freq++;
+      else
+	{			/* insert it */
+	  nn = (elemptr) malloc (sizeof (elem));
+	  if (nn == NULL)
+	    error_msg ("", "ekk", "");
+
+	  if (num < temp->num)
+	    {
+	      nn->num = temp->num;
+	      nn->pos = temp->pos;
+	      nn->freq = temp->freq;
+	      nn->next = temp->next;
+	      temp->num = num;
+	      temp->pos = p + f;
+	      temp->freq = 1;
+	      temp->next = nn;
+	    }
+	  else
+	    {
+	      nn->next = temp->next;
+	      temp->next = nn;
+	      nn->pos = temp->pos + temp->freq;
+	      nn->num = num;
+	      nn->freq = 1;
+	      temp = nn;
+	    }
+	}
+    }
+  /* update frequencies */
+  temp = temp->next;
+  if (temp)
+    do
+      {
+	temp->pos++;
+	temp = temp->next;
+      }
+    while (temp);
+}
+
+
+void 
+elem_free (elemlist * list)
+{
+  elemptr n, p;
+
+  p = list->head;
+  while (p != NULL)
+    {
+      n = p->next;
+      free (p);
+      p = n;
+    }
+  list->head = NULL;
+}
+
+int 
+elem_find (elemlist * list, int n, int *p, int *f, int *t)
+{
+  elemptr temp;
+
+  temp = list->head;
+  while (temp)
+    {
+      if (temp->num == n)
+	{
+	  *p = temp->pos;
+	  *f = temp->freq + temp->pos;
+	  *t = list->tot;
+	  return 1;
+	}
+      temp = temp->next;
+    }
+  return 0;
+}
+
+
+int 
+elem_getrange (elemlist * list, int range, int *num)
+{
+  elemptr temp;
+
+  temp = list->head;
+  while (temp)
+    {
+      if ((range >= temp->pos) && (range < (temp->pos + temp->freq)))
+	{
+	  *num = temp->num;
+	  return 1;
+	}
+      temp = temp->next;
+    }
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/marklist.h	(revision 16561)
@@ -0,0 +1,114 @@
+/**************************************************************************
+ *
+ * marklist.h -- Functions managing the list of marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __MARKLIST_H
+#define __MARKLIST_H
+
+#include "sysfuncs.h"
+#include "basictypes.h"
+
+typedef struct
+  {
+    int symnum;
+    int xpos, ypos;
+    int w, h;
+    int xcen, ycen;
+    int xoffset, yoffset;
+    char name[10];
+    Pixel **bitmap;
+
+    int xcen1, ycen1;
+    int xcen2, ycen2;
+    int xcen3, ycen3;
+    int xcen4, ycen4;
+    int set;
+    int h_runs;
+    int v_runs;
+  }
+marktype;
+
+
+typedef struct marklisttype *marklistptr;
+typedef struct marklisttype
+  {
+    marktype data;
+    marklistptr next;
+  }
+marklisttype;
+
+
+void marktype_dump (marktype d);
+marktype marktype_copy (marktype d);
+
+float marktype_density (marktype d);
+void marktype_calc_centroid (marktype * b);
+void marktype_calc_features (marktype * d, char str[]);
+void marktype_adj_bound (marktype * m);
+
+void marklist_average (marklistptr list, marktype * avgmark);
+void marklist_setnull (marklistptr * list);
+marklistptr marklist_add (marklistptr * list, marktype m);
+marklistptr marklist_addcopy (marklistptr * list, marktype m);
+int marklist_length (marklistptr list);
+void marklist_dump (marklistptr list);
+marklistptr marklist_getat (marklistptr list, int pos, marktype * d);
+void marklist_free (marklistptr * list);
+int marklist_removeat (marklistptr * list, int pos);
+marklistptr marklist_next (marklistptr list);
+
+void write_library_mark (FILE * fp, marktype d);
+int read_library_mark (FILE * fp, marktype * d);
+int read_library (char libraryname[], marklistptr * library);
+
+
+
+
+
+
+/* 
+   The 'elem' structure is an ordered linked list, with fields corresponding
+   directly to the arithmetic coding routines.  
+ */
+typedef struct elem *elemptr;
+typedef struct elem
+  {
+    int num;
+    int pos;
+    int freq;
+    elemptr next;
+  }
+elem;
+
+typedef struct elemlist
+  {
+    elemptr head;
+    int tot;
+  }
+elemlist;
+
+void elem_add (elemlist * list, int num);
+void elem_free (elemlist * list);
+int elem_find (elemlist * list, int n, int *p, int *f, int *t);
+int elem_getrange (elemlist * list, int range, int *num);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/match.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/match.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/match.c	(revision 16561)
@@ -0,0 +1,417 @@
+/**************************************************************************
+ *
+ * match.c -- Functions related to matching marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "utils.h"
+
+
+/* error mask goes from 0..8, single pixel errors are worth nothing */
+int 
+WXOR_match (marktype b1, marktype b2)
+{
+  int r, c, i, j;
+  int minr, minc;
+  int maxr, maxc;
+  int offx, offy;
+  int p1, p2;
+  int count = 0;
+  marktype d;
+
+  offx = b1.xcen - b2.xcen;
+  offy = b1.ycen - b2.ycen;
+
+  minc = min (0, offx);
+  minr = min (0, offy);
+
+  maxc = max (b1.w, b2.w + offx);
+  maxr = max (b1.h, b2.h + offy);
+
+  d.w = maxc - minc + 1;
+  d.h = maxr - minr + 1;
+  d.bitmap = pbm_allocarray (d.w, d.h);
+
+
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      {
+	if ((r >= 0) && (c >= 0) && (r < b1.h) && (c < b1.w))
+	  p1 = pbm_getpixel (b1.bitmap, c, r);
+	else
+	  p1 = 0;		/* 0 = back ground colour */
+	if ((r - offy >= 0) && (c - offx >= 0) && (r - offy < b2.h) && (c - offx < b2.w))
+	  p2 = pbm_getpixel (b2.bitmap, c - offx, r - offy);
+	else
+	  p2 = 0;		/* background colour */
+	pbm_putpixel (d.bitmap, c - minc, r - minr, p1 != p2);
+      }
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      if (pbm_getpixel (d.bitmap, c - minc, r - minr))
+	for (i = -1; i <= 1; i++)
+	  for (j = -1; j <= 1; j++)
+	    if ((r + j >= 0) && (c + i >= 0) && (r + j < d.h) && (c + i < d.w) && ((i != 0) || (j != 0)) && (pbm_getpixel (d.bitmap, c + i - minc, r + j - minr)))
+	      count++;
+  pbm_freearray (&d.bitmap, d.h);
+  return count;
+}
+
+
+
+/* define an array for most matches, if it is a massive mark, then WXOR is called */
+#define S_SIZE 500
+
+static unsigned char scratch[S_SIZE][S_SIZE], sc2[S_SIZE][S_SIZE];
+
+
+
+
+/* Weighted AND-NOT */
+
+int 
+WAN_match (marktype b1, marktype b2)
+{
+  int r, c, i, j;
+  int minr, minc;
+  int maxr, maxc;
+  int offx, offy;
+  int p1, p2;
+  int count = 0;
+
+  offx = b1.xcen - b2.xcen;
+  offy = b1.ycen - b2.ycen;
+
+  minc = min (0, offx);
+  minr = min (0, offy);
+
+  maxc = max (b1.w, b2.w + offx);
+  maxr = max (b1.h, b2.h + offy);
+
+  if (((maxc - minc) >= S_SIZE) || ((maxr - minr) >= S_SIZE))
+    return WXOR_match (b1, b2);
+
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      {
+	scratch[r - minr][c - minc] = sc2[r - minr][c - minc] = 0;
+	if ((r >= 0) && (c >= 0) && (r < b1.h) && (c < b1.w))
+	  p1 = pbm_getpixel (b1.bitmap, c, r);
+	else
+	  p1 = 0;		/* 0 == background colour */
+	if ((r - offy >= 0) && (c - offx >= 0) && (r - offy < b2.h) && (c - offx < b2.w))
+	  p2 = pbm_getpixel (b2.bitmap, c - offx, r - offy);
+	else
+	  p2 = 0;
+
+	if ((p1 == 0) && (p2 == 1))	/* p1 not set, p2 = set */
+	  scratch[r - minr][c - minc] = 1;
+	if ((p1 == 1) && (p2 == 0))	/* p1 set, p2 not set */
+	  sc2[r - minr][c - minc] = 1;
+      }
+
+  count = 0;
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      if (scratch[r - minr][c - minc])
+	for (i = -1; i <= 1; i++)
+	  for (j = -1; j <= 1; j++)
+	    if ((r + j >= minr) && (c + i >= minc) && (r + j < maxr) && (c + i < maxc) && ((i != 0) || (j != 0)) && scratch[r - minr + j][c - minc + i])
+	      count++;
+
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      if (sc2[r - minr][c - minc])
+	for (i = -1; i <= 1; i++)
+	  for (j = -1; j <= 1; j++)
+	    if ((r + j >= minr) && (c + i >= minc) && (r + j < maxr) && (c + i < maxc) && ((i != 0) || (j != 0)) && sc2[r - minr + j][c - minc + i])
+	      count++;
+
+  return count;
+}
+
+/* oc 8 must be in a clock wise order, with oc[0] a corner */
+struct
+{
+  int x, y;
+}
+oc[8] =
+{
+  {
+    -1, -1
+  }
+  ,
+  {
+    0, -1
+  }
+  ,
+  {
+    1, -1
+  }
+  ,
+  {
+    1, 0
+  }
+  ,
+  {
+    1, 1
+  }
+  ,
+  {
+    0, 1
+  }
+  ,
+  {
+    -1, 1
+  }
+  ,
+  {
+    -1, 0
+  }
+};
+
+
+
+static void 
+xormatchscratch (marktype * b1, marktype * b2, int minr, int maxr, int minc, int maxc, int offx, int offy)
+{
+  int r, c, rmm, roo, coo;
+  int p1, p2;
+  Pixel **bm1 = b1->bitmap, **bm2 = b2->bitmap;
+  int w1 = b1->w, h1 = b1->h, w2 = b2->w, h2 = b2->h;
+
+  for (r = minr; r < maxr; r++)
+    {
+      rmm = r - minr;
+      roo = r - offy;
+      p1 = 0;
+      for (c = minc; c < 0; c++)
+	{
+	  coo = c - offx;
+	  scratch[rmm][c - minc] = sc2[rmm][c - minc] = 0;
+	  if ((coo >= 0) && (roo >= 0) && (coo < w2) && (roo < h2))
+	    p2 = pbm_getpixel (bm2, coo, roo);
+	  else
+	    p2 = 0;		/* background */
+	  scratch[rmm][c - minc] = p1 != p2;
+	}
+      for (c = 0; c < maxc; c++)
+	{
+	  coo = c - offx;
+	  scratch[rmm][c - minc] = sc2[rmm][c - minc] = 0;
+	  if ((r >= 0) && (c < w1) && (r < h1))
+	    p1 = pbm_getpixel (bm1, c, r);
+	  else
+	    p1 = 0;		/* background */
+	  if ((coo >= 0) && (roo >= 0) && (coo < w2) && (roo < h2))
+	    p2 = pbm_getpixel (bm2, coo, roo);
+	  else
+	    p2 = 0;		/* background */
+	  scratch[rmm][c - minc] = p1 != p2;
+	}
+    }
+}
+
+
+
+int 
+CSIS_match (marktype * b1, marktype * b2)
+{
+  int r, c, i;
+  int minr, minc;
+  int maxr, maxc;
+  int offx, offy, fepcount;
+  int totscratch = 0;
+
+
+  offx = b1->xcen - b2->xcen;
+  offy = b1->ycen - b2->ycen;
+
+  minc = min (0, offx);
+  minr = min (0, offy);
+
+  maxc = max (b1->w, b2->w + offx);
+  maxr = max (b1->h, b2->h + offy);
+
+  if (((maxc - minc) >= S_SIZE) || ((maxr - minr) >= S_SIZE))
+    return WXOR_match (*b1, *b2);
+
+  /* set the scratch array to 1 if there is an XOR match */
+
+  xormatchscratch (b1, b2, minr, maxr, minc, maxc, offx, offy);
+  /* We how have the XOR map. Scratch contains the number of neighbours, 1=1 neighbour set...etc */
+
+  /* CSIS rule 1, if FEP (four error pels) then exit */
+  fepcount = 0;
+  for (r = minr; r < maxr - 1; r++)
+    for (c = minc; c < maxc - 1; c++)
+      if ((scratch[r - minr][c - minc]) && (scratch[r - minr + 1][c - minc]) &&
+	  (scratch[r - minr][c - minc + 1]) && (scratch[r - minr + 1][c - minc + 1]))
+	{
+	  fepcount++;
+	  if (fepcount == 1)
+	    return 10001;
+	}
+
+  /* foreach set pixel, update scratch with the number of pels around in five dir's. 
+     FPN (five-pel neighbour hood) */
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      if (scratch[r - minr][c - minc])
+	{
+	  sc2[r - minr][c - minc] = 1;	/* itself */
+	  for (i = 1; i < 8; i += 3)
+	    if (((r + oc[i].y) >= minr) && ((c + oc[i].x) >= minc) && ((r + oc[i].y) < maxr) && ((c + oc[i].x) < maxc))
+	      if (scratch[r + oc[i].y - minr][c + oc[i].x - minc])
+		sc2[r - minr][c - minc]++;
+	}
+  /* now scratch has the value of the FPN - 1=single error, 5=4 neighbouring errors etc... */
+
+  /* set scratch and sc2 to be the same */
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      totscratch += scratch[r - minr][c - minc] = sc2[r - minr][c - minc];
+
+  for (r = minr; r < maxr; r++)
+    for (c = minc; c < maxc; c++)
+      {
+	/* CSIS rule 2i: an error pel has 2 or more neighbours---this num is the same as PMS */
+	if (scratch[r - minr][c - minc] >= 3)
+	  if ((r - minr >= 1) && (c - minc >= 1) && (r - minr < (maxr - 1)) && (c - minc < (maxc - 1)))
+	    {
+	      int i, numsep = 0;
+	      /* find how many neighbours a set pixel has, only check the 4 edges-FPN */
+	      for (i = 1; i < 8; i += 2)
+		if (scratch[r + oc[i].y - minr][c + oc[i].x - minc])	/* if a scratch pixel is set */
+		  {
+		    if ((scratch[r + oc[(i + 2) % 8].y - minr][c + oc[(i + 2) % 8].x - minc] == 0) &&
+			(scratch[r + oc[(i + 6) % 8].y - minr][c + oc[(i + 6) % 8].x - minc] == 0)
+		      )
+		      numsep++;
+		  }
+
+	      if ((b1->w >= 12) || (b2->w >= 12) || (b1->h >= 12) || (b2->h >= 12))
+		{
+
+		  /* PMS rule 2ii: at least two of its neigh. error p's aren't connected */
+		  if (numsep >= 2)
+		    {
+		      int i, j, p, p2, same;
+
+		      /* PMS rule 2(iii), look for regions in the original images of 3x3 the same */
+		      if ((c >= 0) && (r >= 0) && (c < b1->w) && (r < b1->h))
+			{
+			  p = pbm_getpixel (b1->bitmap, c, r);
+			  same = 0;
+			  for (i = -1; i <= 1; i++)
+			    for (j = -1; j <= 1; j++)
+			      {
+				if ((c + i >= 0) && (r + j >= 0) && (c + i < b1->w) && (r + j < b1->h))
+				  p2 = pbm_getpixel (b1->bitmap, c + i, r + j);
+				else
+				  p2 = 0;
+				if (p2 == p)
+				  same++;
+			      }	/* i,j loop */
+			  /* PMS rule 2(iii) */
+			  if (same == 9)
+			    {
+			      return 10002;
+			    }
+			}	/* (*b1) in range */
+
+		      if ((c - offx >= 0) && (r - offy >= 0) && (c - offx < b2->w) && (r - offy < b2->h))
+			{
+			  p = pbm_getpixel (b2->bitmap, c - offx, r - offy);
+			  same = 0;
+			  for (i = -1; i <= 1; i++)
+			    for (j = -1; j <= 1; j++)
+			      {
+				if ((c + i - offx >= 0) && (r + j - offy >= 0) && (c + i - offx < b2->w) && (r + j - offy < b2->h))
+				  p2 = pbm_getpixel (b2->bitmap, c + i - offx, r + j - offy);
+				else
+				  p2 = 0;
+				if (p2 == p)
+				  same++;
+			      }	/* i,j loop */
+			  /* PMS rule 2(iii) */
+			  if (same == 9)
+			    {
+			      return 10003;
+			    }
+			}
+		    }
+		}
+	      else
+		{		/* do the CSIS rule */
+		  /* CSIS rule 2ii: at least two of its neigh. error p's aren't connected */
+		  if (numsep >= 2)
+		    {
+		      int i, p, p2, same;
+
+		      /* PMS rule 2(iii), look for regions in the original images of 3x3 the same */
+		      if ((c >= 0) && (r >= 0) && (c < b1->w) && (r < b1->h))
+			{
+			  p = pbm_getpixel (b1->bitmap, c, r);
+			  same = 1;
+			  for (i = 1; i < 8; i += 2)
+			    if ((c + oc[i].x >= 0) && (r + oc[i].y >= 0) && (c + oc[i].x < b1->w) && (r + oc[i].y < b1->h))
+			      p2 = pbm_getpixel (b1->bitmap, c + oc[i].x, r + oc[i].y);
+			    else
+			      p2 = 0;
+			  if (p2 == p)
+			    same++;
+
+			  /* CSIS rule 2(iii) */
+			  if (same == 5)
+			    {
+			      return 10002;
+			    }
+			}
+
+		      if ((c - offx >= 0) && (r - offy >= 0) && (c - offx < b2->w) && (r - offy < b2->h))
+			{
+			  p = pbm_getpixel (b2->bitmap, c - offx, r - offy);
+			  same = 1;
+			  for (i = 1; i < 8; i += 2)
+			    if ((c + oc[i].x - offx >= 0) && (r + oc[i].y - offy >= 0) && (c + oc[i].x - offx < b2->w) && (r + oc[i].y - offy < b2->h))
+			      p2 = pbm_getpixel (b2->bitmap, c + oc[i].x - offx, r + oc[i].y - offy);
+			    else
+			      p2 = 0;
+			  if (p2 == p)
+			    same++;
+
+			  /* CSIS rule 2(iii) */
+			  if (same == 5)
+			    {
+			      return 10003;
+			    }
+			}
+		    }
+		}
+	    }
+      }
+
+  return totscratch;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/match.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/match.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/match.h	(revision 16561)
@@ -0,0 +1,47 @@
+/**************************************************************************
+ *
+ * match.h -- Functions related to matching marks
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __MATCH_H
+#define __MATCH_H
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "utils.h"
+
+
+/*
+   #define NOT_SCREENED(b1,b2) ((abs(b1.w-b2.w)<=4) && (abs(b1.h-b2.h)<=4))
+ */
+
+#define NOT_SCREENED(b1,b2) ((abs(b1.xcen-b2.xcen)+abs(b1.ycen-b2.ycen))<=3)
+
+#define MATCH_VAL 1000
+#define MATCH(b1,b2) (CSIS_match(b1,b2))
+
+int XOR_match (marktype b1, marktype b2);
+int WXOR_match (marktype b1, marktype b2);
+int WAN_match (marktype b1, marktype b2);
+int PMS_match (marktype b1, marktype b2);
+int CSIS_match (marktype * b1, marktype * b2);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.1	(revision 16561)
@@ -0,0 +1,116 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbilevel 1 \*(Dt CITRI
+.SH NAME
+mgbilevel \- compresses a bilevel image using a specified template
+.SH SYNOPSIS
+.B mgbilevel
+.B \-e
+[
+.B \-t
+.I \*(lqstring\*(rq
+]
+.I infile
+[
+.I \>compressed-outfile
+]
+.nf
+or
+.fi
+.B mgbilevel
+.B \-d
+.I compressed-infile
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+.B mgbilevel
+performs (up to) a two-stage template-based compression.  The
+user can specify the template to be used in the compression process.  The
+default template size is a two-stage template 22/10.  The compressed file
+contains the template type in the stream.  The output files are written
+to
+.IR stdout .
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-t\fP \fI\*(lqstring\*(rq\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-e
+Compresses
+.I infile
+using a template specified, and writes to
+.IR compressed-outfile .
+.TP
+.B \-d
+Decompresses
+.I compressed-infile
+using the template contained in the compressed file.  Writes to
+.IR outfile .
+.TP
+\fB\-t \fP\fI\*(lqstring\*(rq\fP
+This option specifies which template combination is to
+be used in the compression.  Five different characters
+can occur in the string: `.' for a pixel not
+included in the template, `p' for a pixel included in the 1st
+level template, `2' for a pixel included in the 2nd level
+template (a subset of the 1st level), `*' denoting the position
+of the
+.I current
+pixel, and a `;' denoting the end of each template line.
+The default template is a two-level 22/10 template:
+.IP
+.RS
+.nf
+\&.ppppp.;
+pp222pp;
+p22222p;
+p22*.\|.\|.;
+.fi
+.RE
+.IP
+This would be specified on the command line as:
+.nf
+\&\fB\-T\fP ".ppppp.;pp222pp;p22222p;p22*.\|.\|.;"
+.fi
+.TP
+.I infile
+This is the input
+.I .pbm
+file to be compressed.
+.TP
+.I outfile
+This is the decompressed
+.I .pbm
+file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgbilevel.c	(revision 16561)
@@ -0,0 +1,149 @@
+/**************************************************************************
+ *
+ * mgbilevel.c -- Program to compress/decompress bilevel images
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "utils.h"
+#include "pbmtools.h"
+#include "arithcode.h"
+#include "bilevel.h"
+
+
+
+static char default_template[] =
+{
+  ".ppppp.;"
+  "pp222pp;"
+  "p22222p;"
+  "p22*...;"
+};
+
+void 
+Encode_bitmap (marktype b, char template[])
+{
+  InitArithEncoding ();
+
+  bl_compress (b, template);
+
+  CloseDownArithEncoding ();
+}
+
+void 
+Decode_bitmap (marktype * b)
+{
+  InitArithDecoding ();
+
+  bl_decompress (b);
+
+  CloseDownArithDecoding ();
+}
+
+
+void 
+usage ()
+{
+  fprintf (stderr,
+	   "usage:\n"
+	   "       mgbilevel -e  [-t \"string\"]  infile      >compressed\n"
+	   "or\n"
+	   "       mgbilevel -d                 compressed  >outfile\n");
+  exit (1);
+}
+
+#define BUF_SIZE 65536
+
+void 
+main (int argc, char *args[])
+{
+  marktype d;
+  char *filename = NULL, *template = NULL;
+  Pixel **bitmap;
+  int encode = 0, decode = 0, i;
+  char buffer[BUF_SIZE];
+
+  if (argc < 2)
+    usage ();
+  template = default_template;
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp (args[i], "-e") == 0)
+	encode = 1;
+      else if (strcmp (args[i], "-d") == 0)
+	decode = 1;
+      else if (strcmp (args[i], "-h") == 0)
+	usage ();
+      else if (strcmp (args[i], "-t") == 0)
+	{
+	  template = args[i + 1];
+	  i++;
+	}
+      else if (args[i][0] == '-')
+	error_msg (args[0], "unknown switch:", args[i]);
+      else if (!filename)
+	filename = args[i];
+      else
+	error_msg (args[0], "only specify 1 filename", "");
+    }
+  if (encode == decode)
+    {
+      fprintf (stderr, "You must specify either encode XOR decode.\n");
+      exit (1);
+    }
+
+  if (!filename)
+    {
+      fprintf (stderr, "You must specify a filename.\n");
+      exit (1);
+    }
+
+  if (encode)
+    {
+      bitmap = pbm_readnamedfile (filename, &d.w, &d.h);
+      if (bitmap == NULL)
+	error_msg (args[0], "problem reading bitmap", "");
+
+      arith_out = stdout;
+      setbuffer (stdout, buffer, BUF_SIZE);
+      magic_write (stdout, MAGIC_BILEVEL);
+      if (bitmap)
+	{
+	  d.bitmap = bitmap;
+	  Encode_bitmap (d, template);
+	}
+    }
+  else if (decode)
+    {
+      FILE *tpin;
+      tpin = fopen (filename, "rb");
+      if (!tpin)
+	error_msg ("mgbilevel", "Can't open file:", filename);
+      arith_in = tpin;
+      setbuffer (tpin, buffer, BUF_SIZE);
+      magic_check (tpin, MAGIC_BILEVEL);
+      Decode_bitmap (&d);
+      fclose (tpin);
+
+      setbuffer (stdout, buffer, BUF_SIZE);
+      pbm_writefile (stdout, d.bitmap, d.w, d.h);
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.1	(revision 16561)
@@ -0,0 +1,103 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgfelics 1 \*(Dt CITRI
+.SH NAME
+mgfelics \- fast and efficient lossless image (de)compression
+.SH SYNOPSIS
+.B mgfelics
+.B \-e
+[
+.BI \-k " n"
+]
+.I infile
+[
+.I \>compressed-outfile
+]
+.br
+or
+.br
+.B mgfelics
+.B \-d
+.I compressed-infile
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+.B mgfelics
+is a lossless compression process which can (de)compress portable
+bitmap files in
+.I pgm
+format.
+.B mgfelics
+writes to
+.IR stdout .
+.SH OPTIONS
+.TP "\w'\fB\-k\fP \fIn\fP'u+2n"
+.BI \-k " n"
+An integer parameter (< 32) which controls the compression speed and
+ratio.  The value of
+.I n
+is included in the output stream, and therefore is not needed in the
+decompression stage.  The default value of
+.I n
+is equal to the number of bits required to represent each grey level.
+.TP
+.B \-e
+Encodes the file
+.IR infile ,
+sending the output to
+.IR stdout .
+.TP
+.B \-d
+Decodes the file from
+.IR compressed-infile ,
+sending the output to
+.IR stdout .
+.SH NOTE
+.B mgfelics
+uses a combination of adjusted binary and Rice codes, the choice of
+which for any given pixel depends on the environment as determined by
+the intensity values of neighbouring pixels.  The result is a very
+fast and lossless algorithm for compressing grayscale images.  The
+optional parameter
+.BI \-k " n"
+corresponds to the maximum exponent of the same name in Rice encoding.
+Smaller values of
+.BI \-k " n"
+can result in significant improvement in compression times with a
+corresponding loss in compression ratio.  However the reduction in
+compression ratio with a small
+.BI \-k " n"
+parameter may be insignificant for many images.
+.SH REFERENCES
+Algorithm by Paul G. Howard and Jeffrey Scott Vitter
+of Brown University, RI, and Duke University, NC, respectively.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgfelics.c	(revision 16561)
@@ -0,0 +1,155 @@
+/**************************************************************************
+ *
+ * mgfelics.c -- Program to compress/decompress grey scale images
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "utils.h"
+
+
+#define MAXKVALUE 32
+
+
+
+void encode (unsigned int *line, int width, int height, int maxval, int maxk,
+	     FILE * fp_in, FILE * fp_out, int (*get) (FILE *));
+
+void decode (unsigned int *line, int width, int height, int maxval, int maxk,
+	     FILE * fp_in, FILE * fp_out);
+
+
+void 
+usage (char *prog)
+{
+  fprintf (stderr, "usage:\n");
+  fprintf (stderr, "\t%s -e [-k n]   infile    [>compressed]\n"
+	   "or\n"
+	   "\t%s -d  compressed-infile   [>outfile]\n", prog, prog);
+  exit (1);
+}
+
+
+
+
+int 
+main (int argc, char **argv)
+{
+  FILE *fp_in = (stdin), *fp_out = (stdout);
+  int width, height, maxval;
+  u_long magic;
+  char *filename = NULL;
+  unsigned int *line;
+  int maxk = 0;
+  int i;
+  int encode_flag = 0, decode_flag = 0;
+
+  if (argc < 2)
+    usage (argv[0]);
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (argv[i], "-h"))
+	usage (argv[0]);
+      else if (!strcmp (argv[i], "-e"))
+	encode_flag = 1;
+      else if (!strcmp (argv[i], "-d"))
+	decode_flag = 1;
+      else if (!strcmp (argv[i], "-k"))
+	{
+	  maxk = atoi (argv[++i]);
+	}
+      else if (argv[i][0] == '-')
+	error_msg (argv[0], "unknown switch:", argv[i]);
+      else if (!filename)
+	filename = argv[i];
+      else
+	error_msg (argv[0], "too many filenames specified.", "");
+    }
+
+  if (encode_flag == decode_flag)
+    error_msg (argv[0], "please specify either encode XOR decode.", "");
+
+  if (!filename)
+    error_msg (argv[0], "please specify a filename.", "");
+
+  if ((encode_flag) && (maxk >= MAXKVALUE))
+    {				/* to fit into 32 bit integers */
+      fprintf (stderr, "%s: k value of %d is too large; it must be less than %d\n",
+	       argv[0], maxk, MAXKVALUE);
+      exit (1);
+    }
+
+
+  if ((fp_in = fopen (filename, "rb")) == NULL)
+    error_msg (argv[0], "unable to open file:", filename);
+
+  /* read file header to determine the type and dimensions */
+
+
+  magic = magic_read (fp_in);
+  if (magic != MAGIC_FELICS)
+    {
+      rewind (fp_in);
+      magic = getmagicno_short_pop (fp_in);
+    }
+
+  if ((decode_flag && (magic != MAGIC_FELICS)) ||
+      (encode_flag && ((magic != MAGIC_P2) && (magic != MAGIC_P5))))
+    {
+      fclose (fp_in);
+      error_msg (argv[0], encode_flag ? "only PGM files can be used." : "only felics compressed files can be used.", "");
+    }
+
+  width = gethint (fp_in);
+  height = gethint (fp_in);
+  maxval = gethint (fp_in);
+
+  if ((line = (unsigned int *) malloc (width * sizeof (int *))) == NULL)
+    {
+      fclose (fp_in);
+      error_msg (argv[0], "unable to allocate memory,", "");
+    }
+
+
+  if (encode_flag)
+    {
+      magic_write (fp_out, MAGIC_FELICS);
+      fprintf (fp_out, "%d\n%d\n%d\n%d\n", width, height, maxval, maxk);
+      encode (line, width, height, maxval, maxk, fp_in, fp_out,
+	      magic == MAGIC_P2 ? getint : fgetc);
+    }
+  else if (decode_flag)
+    {
+      if (maxk)
+	warn (argv[0], "k value not required for decompression, ignored.", "");
+
+      maxk = gethint (fp_in);
+      fprintf (fp_out, "P%d\n%d %d\n%d\n", maxval < 256 ? 5 : 2,
+	       width, height, maxval);
+      decode (line, width, height, maxval, maxk, fp_in, fp_out);
+    }
+
+
+  fclose (fp_in);
+  fclose (fp_out);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.1	(revision 16561)
@@ -0,0 +1,137 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgtic 1 \*(Dt CITRI
+.SH NAME
+mgtic \- textual image compression.
+.SH SYNOPSIS
+.B mgtic
+.B \-e
+[
+.B \-L
+]
+[
+.B \-Q
+]
+[
+.B \-X
+]
+.I libraryfile
+.I infile
+.if n .ti +9n
+[
+.I \>compressed\-outfile
+]
+.nf
+or
+.fi
+.B mgtic
+.B \-d
+[
+.B \-L
+]
+[
+.BI \-X " libraryfile"
+]
+.I compressed-infile
+.if n .ti +9n
+[
+.I \>outfile
+]
+.SH DESCRIPTION
+This is the main routine in the
+.B mgtic
+system.
+.B mgtic
+is used to
+processes a series of files, and by using the
+.I libraryfile
+produced by
+.BR mgticbuild (1),
+compresses input files with respect to the marks contained in the
+library.  On the CCITT test faxes, lossless mode achieved around 20:1
+compression, and lossy, around 40:1 compression.  On more textual
+images, the amount of compression is determined by the resolution of
+the original file, and the amount of noise in the image.  By default,
+.B mgtic
+will reconstruct a lossy version of the image.  This
+can be changed to lossless by using the
+.B \-L
+option.
+.SH OPTIONS
+Options may appear in any order, but must come before the input
+filenames.
+.TP "\w'\fB\-X\fP \fI[libraryfile]\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-e
+Encode
+.I infile
+with respect to the library.
+.TP
+.B \-d
+Decodes
+.IR compressed-infile
+with respect to the library.
+.TP
+.B \-l
+This specifies lossy mode, and is the default mode for (de)compression.
+Do not generate the residue.  If used in encoding mode, does not store
+the residue in the output.  If used in decoding mode, the residue (if
+it exists) is ignored.
+.TP
+.B \-L
+Use lossless mode for (de)compression, which generates the residue.
+If used in encoding mode, this stores the residue in the compressed
+output.  If used in decoding mode (assuming the residue is present),
+this allows the original file to be recovered exactly.
+.TP
+.BI \-R\  filename
+Stores the residue as an external file
+.I filename
+which could possibly be stored on another medium.  During
+decompression, if the residue file specified does not relate to the
+compressed file, the resulting decompressed image will be a mess.
+Note that only the width and height of the residue image is checked.
+.TP
+.BI \-Q
+Uses a quicker form of compression.  This option decreases the time
+spent looking for a good match during encoding.  On average this
+option decreases compression ratios, but increases compression speed.
+.TP
+.BI \-X " [libraryfile]"
+Use an external library file, instead of (de)compressing the library
+within the normal stream.  When decoding, the
+.B \-X
+option must be followed by the
+.IR libraryfile .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgtic.c	(revision 16561)
@@ -0,0 +1,596 @@
+/**************************************************************************
+ *
+ * mgtic.c -- Program to compress/decompress textual images
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************
+ *
+ * This file is the basis of mgtic, which compresses the components of an
+ * image. 
+ * The compressed file contains the following sections:
+ *         mgtic magic_no
+ *         mgtic version
+ *         lossy/lossless flag 
+ *         internal/external library flag 
+ *         cols, rows, number of marks, library size
+ *         [library sequence]
+ *         symbol sequence
+ *         offset sequence
+ *         [residue bitmap]
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+#include "match.h"
+#include "sortmarks.h"
+#include "codesyms.h"
+#include "arithcode.h"
+#include "codeoffsets.h"
+#include "bilevel.h"
+
+static char library_template[] =
+{
+  ".ppppp.;"
+  "pp222pp;"
+  "p22222p;"
+  "p22*...;"
+};
+
+void 
+usage ()
+{
+  fprintf (stderr, "usage: \n"
+	"\tmgtic -e [-L] [-Q] [-X]  libraryfile   infile    [>compressed]\n"
+	   "or\n"
+	   "\tmgtic -d [-L] [-X libraryfile]  compressed  [>outfile]\n");
+  exit (1);
+}
+
+
+/* encode the list of marks 'list', w.r.t. the library 'library' */
+void 
+match_sequence (marklistptr list, marklistptr library, marklistptr * symbol_list, Pixel ** recon, int quick)
+{
+  int carryx, carryy, gap;
+  int lastx = 0, lasty = 0;
+  int i, j;
+  marktype d, d2;
+  marklistptr liststep, librarystep;
+  int score, found;
+  int r, c;
+  int p;
+
+  carryx = carryy = 0;
+  gap = max (1, marklist_length (list) / 10);
+
+  for (liststep = list, i = 0; liststep; liststep = liststep->next, i++)
+    {
+      if (V)
+	if (!(i % gap))
+	  fprintf (stderr, ".");
+      d = liststep->data;
+
+      found = -1;
+      score = INT_MAX;
+      for (librarystep = library, j = 0; librarystep; librarystep = librarystep->next, j++)
+	{
+	  d2 = librarystep->data;
+
+	  if (NOT_SCREENED (d2, d))
+	    if ((p = MATCH (&d, &d2)) < MATCH_VAL)
+	      {
+		if (p < score)
+		  {
+		    score = p;
+		    found = j;
+		    if (quick)
+		      break;
+		  }
+	      }
+	}
+      /*
+         if(found<0){
+         for(librarystep=library,j=0; librarystep; librarystep=librarystep->next,j++){
+         d2=librarystep->data;
+
+         if((p=MATCH(&d,&d2))<MATCH_VAL){
+         if(p<score){
+         score=p;found=j; if(quick) break;
+         }
+         }
+         }
+         }
+       */
+      if (found >= 0)
+	{
+	  liststep->data.symnum = found;
+	  marklist_getat (library, found, &d2);
+	  d2.symnum = found;
+
+	  d2.xoffset = d.xpos - lastx + carryx;		/* propagate changes to next mark */
+	  d2.yoffset = d.ypos - lasty + carryy;
+	  carryx = (d2.xcen - d.xcen);	/* offet centroids because thats where we match them! */
+	  carryy = (d2.ycen - d.ycen);
+	  d2.xoffset -= carryx;	/* make the changes to this mark */
+	  d2.yoffset -= carryy;
+	  lastx = d.xpos + d2.w;
+	  lasty = d.ypos;
+
+	  marklist_add (symbol_list, d2);
+
+	  /* add to the reconstructed image--this is the lossy version */
+	  for (r = 0; r < d2.h; r++)
+	    for (c = 0; c < d2.w; c++)
+	      if (pbm_getpixel (d2.bitmap, c, r))
+		pbm_putpixel_trunc (recon, d.xpos + c - carryx, d.ypos + r - carryy, 1, d2.w, d2.h);
+	}
+      else
+	{
+	  /* didn't match at all..hopefully noise */
+	  if ((liststep->data.w >= 10) || (liststep->data.w >= 10))
+	    {
+	      fprintf (stderr, "mgtic: warning, unable to match a char.\n");
+	      write_library_mark (stderr, liststep->data);
+	    }
+	  if (V)
+	    fprintf (stderr, "x");
+	  liststep->data.symnum = -1;
+	}
+    }
+  if (V)
+    fprintf (stderr, "\n");
+}
+
+
+
+
+void 
+main (int argc, char *args[])
+{
+  int i, n, r, c, ch;
+  FILE *inf;
+  marklistptr library = NULL, list = NULL, step = NULL;
+  marklistptr symbol_list = NULL;
+  marktype d, d2;
+  Pixel **bitmap, **recon, **bitmapcopy;
+  char *splitfilename = NULL;
+  int cols, rows, count, librarysize, lossy = 1;
+  int quick = 0;
+  int ticencode = 0, ticdecode = 0, external = 0;
+  char *libraryname = NULL, *infile = NULL;
+  char *s1 = NULL, *s2 = NULL;
+  char bufferin[BUFSIZ], bufferout[BUFSIZ];
+
+  if (argc < 2)
+    usage ();
+
+  while ((ch = getopt (argc, args, "edlLQhvXR:")) != -1)
+    switch (ch)
+      {
+      case 'e':
+	ticencode = 1;
+	break;
+      case 'd':
+	ticdecode = 1;
+	break;
+      case 'l':
+	lossy = 1;
+	break;
+      case 'L':
+	lossy = 0;
+	break;
+      case 'Q':
+	quick = 1;
+	break;
+      case 'v':
+	V = 1;
+	break;
+      case 'X':
+	external = 1;
+	break;
+      case 'R':
+	splitfilename = optarg;
+	break;
+      case 'h':
+      case '?':
+	usage ();
+      }
+
+  for (i = 1; i < argc; i++)
+    if (args[i][0] != '-')
+      if (strcmp (args[i - 1], "-R"))
+	{			/* ignore arg following -R, ie. -R <filename> */
+	  if (!s1)
+	    s1 = args[i];
+	  else if (!s2)
+	    s2 = args[i];
+	  else
+	    error_msg (args[0], "too many filenames", "");
+	}
+
+  if (ticencode == ticdecode)
+    error_msg (args[0], "please specify either encode XOR decode", "");
+
+  if (ticencode)
+    {
+	  /*****************
+	    ENCODING STAGE
+	  *****************/
+      long PrevBits = 0;
+      long BITS_header = 0, BITS_symbols = 0, BITS_offsets = 0, BITS_residue = 0,
+        BITS_footer = 0, BITS_library = 0;
+
+      libraryname = s1;
+      if (!libraryname)
+	error_msg (args[0], "please specify a library file", "");
+      infile = s2;
+      if (!infile)
+	error_msg (args[0], "please specify a file name", "");
+
+      inf = fopen (infile, "rb");
+      if (inf == NULL)
+	error_msg (args[0], "Trouble opening file:", infile);
+      setbuf (inf, bufferin);
+
+      count = librarysize = read_library (libraryname, &library);
+
+      if (V)
+	fprintf (stderr, "%s: processing...\n", args[0]);
+
+      if (pbm_isapbmfile (inf))
+	{
+	  if (V)
+	    fprintf (stderr, "reading file %s...\n", infile);
+	  bitmap = pbm_readfile (inf, &cols, &rows);
+	  fclose (inf);
+
+	  bitmapcopy = pbm_copy (bitmap, cols, rows);
+
+	  if (V)
+	    fprintf (stderr, "extracting...");
+	  ExtractAllMarks (bitmap, &list, cols, rows);
+	  if (V)
+	    fprintf (stderr, "(%d marks)\n", marklist_length (list));
+
+	  /* sort into reading order */
+	  if (V)
+	    fprintf (stderr, "sorting...\n");
+	  list = sortmarks (list);
+
+	  pbm_freearray (&bitmap, rows);	/* clear old version */
+	  bitmap = bitmapcopy;	/* point to the copy */
+	  recon = pbm_allocarray (cols, rows);
+
+	  if (V)
+	    fprintf (stderr, "matching...");
+
+	  match_sequence (list, library, &symbol_list, recon, quick);
+
+
+	  PrevBits = 0;
+	  setbuf (stdout, bufferout);
+	  /* start output */
+	  magic_write (stdout, MAGIC_TIC);
+	  InitArithEncoding ();
+
+	  EncodeGammaDist (1);	/* version 1 of the program */
+	  EncodeGammaDist (lossy);	/* lossy=1 = no residue */
+	  EncodeGammaDist (external);	/* 1== external file */
+
+	  count = marklist_length (symbol_list);
+	  if (V)
+	    fprintf (stderr, "encoding cols, rows, and number of symbols=%d\n", count);
+
+	  EncodeGammaDist (cols);
+	  EncodeGammaDist (rows);
+	  EncodeGammaDist (count);
+	  EncodeGammaDist (librarysize);
+
+	  BITS_header = CountOfBitsOut - PrevBits;
+	  PrevBits = CountOfBitsOut;
+
+
+	  /* output the library sequence */
+	  if (external == 0)
+	    {
+	      if (V)
+		fprintf (stderr, "encoding library\n");
+
+	      bl_clearmodel ();
+	      bl_writetemplate (library_template);
+	      for (step = library; step; step = step->next)
+		bl_compress_mark (step->data);
+	      bl_freemodel ();
+	      BITS_library = CountOfBitsOut - PrevBits;
+	      PrevBits = CountOfBitsOut;
+	    }
+
+	  /* output the symbol sequence */
+	  if (V)
+	    fprintf (stderr, "encoding symbol sequence\n");
+	  InitPPM ();
+	  EncodeSymbols (symbol_list, count);
+	  BITS_symbols = CountOfBitsOut - PrevBits;
+	  PrevBits = CountOfBitsOut;
+
+
+	  /* output the offset sequence */
+	  if (V)
+	    fprintf (stderr, "encoding offset sequence\n");
+	  EncodeOffsets (symbol_list, count);
+	  BITS_offsets = CountOfBitsOut - PrevBits;
+	  PrevBits = CountOfBitsOut;
+
+	  EncodeChecksum ();	/* code lossy checksum */
+
+	  /* calculate the residue...and compress it---if need be! */
+	  if (!lossy)
+	    {
+	      if (V)
+		fprintf (stderr, "encoding residue...\n");
+
+	      d.bitmap = bitmap;
+	      d.h = rows;
+	      d.w = cols;
+	      d2.bitmap = recon;
+	      d2.h = rows;
+	      d2.w = cols;
+
+	      if (splitfilename)
+		{
+		  FILE *temp;
+
+		  CloseDownArithEncoding ();
+		  fclose (stdout);
+		  /* no residue result */
+		  BITS_footer = CountOfBitsOut - PrevBits;
+
+		  temp = fopen (splitfilename, "wb");
+		  if (temp == NULL)
+		    error_msg (args[0], "Trouble creating file:", splitfilename);
+
+		  arith_out = temp;
+		  InitArithEncoding ();
+		  bl_clair_compress (d, d2);
+		  CloseDownArithEncoding ();
+		  BITS_residue = CountOfBitsOut;
+		  fclose (temp);
+		}
+	      else
+		{
+		  bl_clair_compress (d, d2);
+		  BITS_residue = CountOfBitsOut - PrevBits;
+		  PrevBits = CountOfBitsOut;
+		  EncodeChecksum ();	/* code lossless checksum */
+		  CloseDownArithEncoding ();
+		  BITS_footer = CountOfBitsOut - PrevBits;
+		}
+	    }
+	  else
+	    {
+	      if (V)
+		fprintf (stderr, "not encoding residue..lossy mode\n");
+	      CloseDownArithEncoding ();
+	      BITS_footer = CountOfBitsOut - PrevBits;
+	    }
+
+	  /* because we edit the values above */
+	  CountOfBitsOut = BITS_header + BITS_library + BITS_symbols + BITS_offsets + BITS_residue + BITS_footer;
+
+	  fprintf (stderr, "bits: header=%ld, library=%ld, "
+		   "symbols=%ld, offsets=%ld, residue=%ld, footer=%ld\n",
+		   BITS_header, BITS_library, BITS_symbols,
+		   BITS_offsets, BITS_residue, BITS_footer);
+	  fprintf (stderr, "total bits: %ld, ", CountOfBitsOut);
+	  fprintf (stderr, "Lossy CR: %4.2f", (cols * rows) / (float) (CountOfBitsOut - BITS_residue));
+	  if (external)
+	    fprintf (stderr, " (excluding external lib)");
+	  fprintf (stderr, ", Lossless CR: %4.2f\n", (!lossy) * (cols * rows) / (float) (CountOfBitsOut));
+	}
+      else
+	error_msg (args[0], "unknown format of bitmap--expecting PBM.", "");
+    }
+  else
+    {
+	  /*****************
+	    DECODING STAGE
+	  *****************/
+      int lastx, lasty;
+      librarysize = 0;
+
+      if (external)
+	{
+	  libraryname = s1;
+	  infile = s2;
+	  count = librarysize = read_library (libraryname, &library);
+	}
+      else
+	{
+	  infile = s1;
+	  if (infile && s2)
+	    error_msg (args[0], "too many filenames", "");
+	}
+
+      if (!freopen (infile, "rb", stdin))
+	error_msg (args[0], "Trouble opening file:", infile);
+
+
+      if (V)
+	fprintf (stderr, "decompressing...\n");
+
+      setbuf (stdin, bufferin);
+      magic_check (stdin, MAGIC_TIC);
+
+      InitArithDecoding ();
+
+      {
+	int version = DecodeGammaDist ();
+	if (version != 1)
+	  error_msg (args[0], "Need later version of decompressor.", "");
+      }
+
+      {
+	int templossy = DecodeGammaDist ();
+	if (!lossy)
+	  lossy = templossy;	/* can only choose if encoded file is lossless */
+	if (V)
+	  {
+	    if (lossy)
+	      fprintf (stderr, "lossy mode\n");
+	    else
+	      fprintf (stderr, "lossless mode\n");
+	  }
+      }
+
+      if (DecodeGammaDist ())
+	{			/* if compressed file doesn't contain library */
+	  if (!external)
+	    error_msg (args[0], "compressed file doesn't contain library, specify externally", "");
+	  external = 1;
+	}
+      else
+	{			/* if compressed file contains library */
+	  if (external)
+	    fprintf (stderr, "ignoring external library file\n");
+	  external = 0;
+	}
+
+      cols = DecodeGammaDist ();
+      rows = DecodeGammaDist ();
+      count = DecodeGammaDist ();
+
+      i = DecodeGammaDist ();	/* librarysize */
+      if (external)
+	{
+	  if (i > librarysize)
+	    error_msg (args[0], "external library file is too small!", "");
+	  else if (i < librarysize)
+	    fprintf (stderr, "%s: warning, expecting a different (smaller) library.\n", args[0]);
+	}
+      librarysize = i;
+
+      if (V)
+	fprintf (stderr, "cols %d, rows %d, num syms %d, library size %d\n", cols, rows, count, librarysize);
+
+      /* decode library */
+      if (external == 0)
+	{
+	  if (V)
+	    fprintf (stderr, "reading library\n");
+	  bl_clearmodel ();
+	  bl_readtemplate ();
+	  for (n = 0; n < librarysize; n++)
+	    {
+	      bl_decompress_mark (&d);
+	      d.symnum = n;
+	      if (library == NULL)
+		step = marklist_add (&library, d);
+	      else
+		step = marklist_add (&step, d);
+	    }
+	  bl_freemodel ();
+	  if (V)
+	    fprintf (stderr, "read %d marks from library\n", marklist_length (library));
+	}
+
+      recon = pbm_allocarray (cols, rows);
+
+      /* decode symbols */
+      InitPPM ();
+      if (V)
+	fprintf (stderr, "decompressing %d symbols\n", count);
+      symbol_list = DecodeSymbols (count);
+
+      /* decode offsets */
+      if (V)
+	fprintf (stderr, "reading offsets...\n");
+      DecodeOffsets (symbol_list, count);
+      lastx = lasty = 0;
+      for (step = symbol_list; step; step = step->next)
+	{
+	  lastx = lastx + step->data.xoffset;
+	  lasty = lasty + step->data.yoffset;
+
+	  marklist_getat (library, step->data.symnum, &d2);
+	  for (r = 0; r < d2.h; r++)
+	    for (c = 0; c < d2.w; c++)
+	      if (pbm_getpixel (d2.bitmap, c, r))
+		pbm_putpixel_trunc (recon, lastx + c, lasty + r, 1, cols, rows);	/* we don't care, already warned them! */
+	  lastx += d2.w;
+	}
+
+      DecodeChecksum (args[0]);
+
+      /* decode the residue */
+      if (!lossy)
+	{
+	  if (V)
+	    fprintf (stderr, "decoding residue...\n");
+
+	  bitmap = pbm_allocarray (cols, rows);
+	  d.bitmap = bitmap;
+	  d.w = d2.w = cols;
+	  d.h = d2.h = rows;
+
+	  d2.bitmap = recon;
+
+	  /* NOTE: the 2nd argument is clairvoyantly compressed */
+	  if (splitfilename)
+	    {
+	      FILE *temp;
+
+	      CloseDownArithDecoding ();
+	      fclose (stdin);
+
+	      temp = fopen (splitfilename, "rb");
+	      if (temp == NULL)
+		error_msg (args[0], "Trouble opening file:", splitfilename);
+
+	      arith_in = temp;
+	      InitArithDecoding ();
+	      bl_clair_decompress (d, d2);
+	      CloseDownArithDecoding ();
+	      fclose (temp);
+	    }
+	  else
+	    {
+	      bl_clair_decompress (d, d2);
+	      DecodeChecksum (args[0]);
+	      CloseDownArithDecoding ();
+	    }
+	  pbm_freearray (&recon, rows);
+	  recon = bitmap;	/* point to the bitmap */
+	}
+      else
+	{
+	  CloseDownArithDecoding ();
+	}
+
+      if (V)
+	fprintf (stderr, "writing pbm file...\n");
+      setbuf (stdout, bufferout);
+      pbm_writefile (stdout, recon, cols, rows);
+      pbm_freearray (&recon, rows);
+    }				/* end decoding */
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.1	(revision 16561)
@@ -0,0 +1,61 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticbuild 1 \*(Dt CITRI
+.SH NAME
+mgticbuild \- build a library of marks from training documents
+.SH SYNOPSIS
+.B mgticbuild
+[
+.B \-h
+]
+.I "libraryfile infile1 infile2 .\|.\|."
+.SH DESCRIPTION
+.B mgticbuild
+builds an uncompressed library of marks that have been
+extracted from a series of input files.  If the
+.I libraryfile
+does
+not exist, it is created, otherwise new marks are appended to the
+file.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.TP
+.I infile(s)
+The training files; the only currently-supported format is
+.IR pbm .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticbuild.c	(revision 16561)
@@ -0,0 +1,145 @@
+/**************************************************************************
+ *
+ * mgticbuild.c -- Program to build the textual image library
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+
+
+void 
+usage ()
+{
+  fprintf (stderr, "usage: \n"
+	"\tmgticbuild   libraryfile   infile.pbm  [infile2 infile3 ...]\n");
+  exit (1);
+}
+
+void 
+main (int argc, char *args[])
+{
+  int i;
+  FILE *lib = NULL, *inf = NULL;
+  marklistptr list = NULL, step;
+  Pixel **bitmap;
+  int cols, rows;
+  int count, pos = 0;
+  char *libraryname = NULL;
+
+  if (argc < 2)
+    usage ();
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (args[i], "-v"))
+	V = 1;
+      else if (!strcmp (args[i], "-h"))
+	usage ();
+      else if (args[i][0] == '-')
+	error_msg (args[0], "unknown switch:", args[i]);
+      else if (!libraryname)
+	libraryname = args[i];
+      else
+	pos = i;
+    }
+
+  if (!libraryname)
+    error_msg (args[0], "please specify a library file", "");
+  if (pos < 2)
+    usage ();
+
+  count = 0;
+  for (i = pos; i < argc; i++)
+    if (args[i][0] != '-')
+      {
+	inf = fopen (args[i], "rb");
+	count = 0;
+	if (inf == NULL)
+	  fprintf (stderr, "Trouble opening file: %s\n", args[i]);
+	else
+	  {
+	    if (pbm_isapbmfile (inf))
+	      {
+		if (V)
+		  fprintf (stderr, "%s: processing...\n", args[0]);
+		if (V)
+		  fprintf (stderr, "reading file %s...", args[i]);
+		bitmap = pbm_readfile (inf, &cols, &rows);
+		if (V && (count == 0))
+		  fprintf (stderr, "building...");
+
+		list = NULL;
+
+		ExtractAllMarks (bitmap, &list, cols, rows);
+
+		if (V)
+		  fprintf (stderr, "writing...");
+
+		if ((!lib) && (marklist_length (list)))
+		  {
+		    lib = fopen (libraryname, "r");
+		    if (lib)
+		      {
+			fclose (lib);
+			lib = fopen (libraryname, "ab");
+			if (lib == NULL)
+			  error_msg (args[0], "trouble appending library file", "");
+			fprintf (stderr, "\nwarning: appending library file: %s\n", libraryname);
+		      }
+		    else
+		      {
+			lib = fopen (libraryname, "wb");
+			if (lib == NULL)
+			  error_msg (args[0], "trouble creating library file", "");
+		      }
+		  }		/* if !lib */
+
+		step = list;
+		while (step)
+		  {
+		    step->data.symnum = count;
+		    write_library_mark (lib, step->data);
+		    count++;
+		    step = step->next;
+		  }
+		pbm_freearray (&bitmap, rows);
+	      }
+	    else
+	      fprintf (stderr, "warning: %s is not a PBM file.\n", args[i]);
+	    fclose (inf);
+	  }
+	if (count)
+	  {
+	    if (V)
+	      fprintf (stderr, "%d marks written.\n", count);
+	  }
+	else
+	  fprintf (stderr, "No marks written!\n");
+      }
+
+  if (lib)
+    fclose (lib);
+  marklist_free (&list);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.1	(revision 16561)
@@ -0,0 +1,55 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticdump 1 \*(Dt CITRI
+.SH NAME
+mgticdump \- displays the library in a user-friendly manner
+.SH SYNOPSIS
+.B mgticdump
+[
+.B \-h
+]
+.I libraryfile
+.SH DESCRIPTION
+.B mgticdump
+displays the marks in a library in a user-viewable form.  The output
+is to
+.I stdout
+and is in a plain ASCII form.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticdump.c	(revision 16561)
@@ -0,0 +1,86 @@
+/**************************************************************************
+ *
+ * mgticdump.c -- Program to dump out a library in a human readable form
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+
+
+void 
+usage ()
+{
+  fprintf (stderr, "usage:\n"
+	   "\tmgticdump   libraryfile\n");
+  exit (1);
+}
+
+
+
+void 
+main (int argc, char *args[])
+{
+  FILE *lib;
+  int count, i;
+  marktype d;
+  char *libraryname = NULL;
+  FILE *outf = (stdout);
+
+  if (argc < 2)
+    usage ();
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (args[i], "-h"))
+	usage ();
+      else if (args[i][0] == '-')
+	error_msg (args[0], "unknown switch:", args[i]);
+      else if (!libraryname)
+	libraryname = args[i];
+      else
+	error_msg (args[0], "too many filenames", "");
+    }
+
+  if (!libraryname)
+    error_msg (args[0], "please specify a library file", "");
+
+
+  lib = fopen (libraryname, "rb");
+  if (lib == NULL)
+    error_msg (args[0], "trouble opening library file:", libraryname);
+
+  count = 0;
+  while (!isEOF (lib))
+    {
+      read_library_mark (lib, &d);
+
+      write_library_mark (outf, d);
+      count++;
+
+      pbm_freearray (&d.bitmap, d.h);
+    }
+  fprintf (stderr, "count=%d", count);
+  fclose (lib);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.1	(revision 16561)
@@ -0,0 +1,91 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticprune 1 \*(Dt CITRI
+.SH NAME
+mgticprune \- prunes the library to remove duplicates
+.SH SYNOPSIS
+.B mgticprune
+[
+.BI \-a " n"
+]
+[
+.BI \-s " n"
+]
+[
+.B \-Q
+]
+.I libraryfile
+.if n .ti +9n
+[
+.I pruned-libraryfile
+]
+.SH DESCRIPTION
+.B mgticprune
+examines
+.I libraryfile
+and removes duplicate marks.  If
+.I pruned-libraryfile
+is not specified,
+.I libraryfile
+is overwritten with the marks in the pruned library.  The Combined
+Symbol Independent Strategy (CSIS) method is used to match marks.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIpruned-libraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-a " n"
+Prune marks when their area is under or equal to the threshold,
+.IR n .
+The default value is\ 3.
+.TP
+.BI \-s " n"
+Prune marks when their width and height is under or equal to
+the threshold,
+.IR n .
+The default value is\ 0.
+.TP
+.BI \-Q
+Use a quicker method of pruning.  The number of iterations through the
+averaging and pruning processes is decreased.  This usually reduces
+time by a half, but compression ratios decrease.
+.TP
+.I libraryfile
+This specifies the name of the library file
+.TP
+.I pruned-libraryfile
+This specifies the name of the prune library, after duplicates and
+matches have been removed.  If this file is not specified, then
+.I libraryfile
+will be overwritten.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticprune.c	(revision 16561)
@@ -0,0 +1,393 @@
+/**************************************************************************
+ *
+ * mgticprune.c -- Program to prune a textual image library
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+#include "match.h"
+
+marklistptr *avglib = NULL;
+int avglibsize;
+int libsize = 0;
+
+
+void 
+usage ()
+{
+  fprintf (stderr, "usage:\n"
+    "\tmgticbuild [-a n]  [-s n]  [-Q]   libraryfile   [pruned-library]\n");
+  exit (1);
+}
+
+
+
+
+/* foreach list in the avglib[] array, an average mark is returned */
+marklistptr 
+average_library ()
+{
+  marklistptr librarylist;
+  int i;
+  marktype avgmark;
+
+  librarylist = NULL;
+  for (i = 0; i < libsize; i++)
+    {
+      if (avglib[i])
+	{
+	  marklist_average (avglib[i], &avgmark);
+	  avgmark.symnum = i;
+	  marklist_addcopy (&librarylist, avgmark);
+	}
+    }
+  return librarylist;
+}
+
+
+void 
+average_space_enlarge ()
+{
+  int j;
+
+  if (libsize >= avglibsize)
+    {
+      avglib = (marklistptr *) realloc (avglib, (avglibsize + 100) * sizeof (marklistptr));
+      if (!avglib)
+	error_msg ("mgticprune", "out of memory in reallocating..", "");
+      for (j = avglibsize; j < avglibsize + 100; j++)
+	avglib[j] = NULL;
+      avglibsize += 100;
+    }
+}
+
+
+/* for each mark in 'origlist', if it can't be matched with one in 
+   'librarylist', then add to the librarylist, and update the relevant
+   avglib[] position */
+void 
+greedy_add (marklistptr origlist, marklistptr * librarylist)
+{
+  marklistptr step, step_i;
+  int i, j, notfound;
+  marktype d_i;
+
+  for (i = 0, step_i = origlist; step_i; i++, step_i = step_i->next)
+    {
+      d_i = step_i->data;
+
+      notfound = 1;
+      for (j = 0, step = *librarylist; step; j++, step = step->next)
+	{
+	  if (NOT_SCREENED (d_i, step->data))
+	    if (MATCH (&d_i, &step->data) < MATCH_VAL)
+	      {
+		d_i.symnum = j;
+		marklist_addcopy (&avglib[j], d_i);
+		notfound = 0;
+		break;
+	      }
+	}
+
+      if (notfound)
+	{
+	  d_i.symnum = libsize;
+	  marklist_addcopy (librarylist, d_i);
+	  marklist_addcopy (&avglib[libsize], d_i);
+	  libsize++;
+	  if (libsize >= avglibsize)
+	    average_space_enlarge ();
+	}
+    }
+}
+
+
+
+/* for each mark in 'library', if another matches with it, remove
+   one copy from the library, and erase the relevant avglib[] */
+void 
+greedy_prune (marklistptr * library)
+{
+  int count;
+  int i, j;
+  marklistptr step;
+  marktype d;
+
+  count = marklist_length (*library);
+  /* an invariant, all marks <i have been kept in the pruned library */
+  for (i = 0; i < count; i++)
+    {
+      marklist_getat (*library, i, &d);
+
+      for (j = 0, step = *library; j < i; j++, step = step->next)
+	{
+	  if (NOT_SCREENED (d, step->data))
+	    if (MATCH (&d, &step->data) < MATCH_VAL)
+	      {
+		/* [i,d] has matched with library element [j,d2]. */
+		marklist_removeat (library, i);		/* because we kill it here */
+		marklist_free (&avglib[d.symnum]);
+		avglib[d.symnum] = NULL;
+		i--, count--;
+		break;
+	      }
+	}
+    }
+}
+
+
+void 
+prune_on_size (marklistptr * list, int sizethres)
+{
+  int count, i;
+  marktype d;
+
+  if (sizethres)
+    {
+      count = marklist_length (*list);
+      i = 0;
+      do
+	{
+	  marklist_getat (*list, i, &d);
+	  if ((d.w <= sizethres) && (d.h <= sizethres))
+	    {
+	      marklist_removeat (list, i);
+	      i--;
+	      count--;
+	    }
+	  i++;
+	}
+      while (i < count);
+    }
+}
+
+
+
+void 
+prune_on_area (marklistptr * list, int areathres)
+{
+  int count, i;
+  marktype d;
+
+  if (areathres)
+    {
+      count = marklist_length (*list);
+      i = 0;
+      do
+	{
+	  marklist_getat (*list, i, &d);
+	  if (d.set <= areathres)
+	    {
+	      marklist_removeat (list, i);
+	      i--;
+	      count--;
+	    }
+	  i++;
+	}
+      while (i < count);
+    }
+}
+
+
+void 
+averagedout_addagain (marklistptr origlist, marklistptr * librarylist)
+{
+  marklistptr step, stepi;
+  int notfound;
+  int i, j;
+
+  for (i = 0, step = origlist; step; i++, step = step->next)
+    {
+      notfound = 1;
+
+      for (j = 0, stepi = *librarylist; stepi; j++, stepi = stepi->next)
+	if (NOT_SCREENED (step->data, stepi->data))
+	  if (MATCH (&step->data, &stepi->data) < MATCH_VAL)
+	    {
+	      notfound = 0;
+	      break;
+	    }
+
+      if (notfound)
+	{
+	  marklist_addcopy (librarylist, step->data);
+	  marklist_addcopy (&avglib[libsize], step->data);
+	  libsize++;
+	  if (libsize >= avglibsize)
+	    average_space_enlarge ();
+	}
+    }
+}
+
+
+
+
+
+void 
+main (int argc, char *args[])
+{
+  int i;
+  FILE *lib;
+
+  marklistptr origlist = NULL, librarylist = NULL;
+  marklistptr step;
+  char tempn[1000], sysstr[1000];
+  int count;
+  char *libraryname = NULL, *prunename = NULL;
+  int sizethres = 3, areathres = 0;
+  int pass;
+  int num_passes = 3;
+  int overwrite = 0;
+
+  V = 0;
+
+  if (argc < 2)
+    usage ();
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (args[i], "-v"))
+	V = 1;
+      else if (!strcmp (args[i], "-h"))
+	usage ();
+      else if (!strcmp (args[i], "-Q"))
+	{
+	  num_passes = 0;
+	}
+      else if (!strcmp (args[i], "-s"))
+	{
+	  i++;
+	  if ((i < argc) && (isinteger (args[i])))
+	    sizethres = atoi (args[i]);
+	  else
+	    error_msg (args[0], "follow the -s option with a numeric arg >=0", "");
+	}
+      else if (!strcmp (args[i], "-a"))
+	{
+	  i++;
+	  if ((i < argc) && (isinteger (args[i])))
+	    areathres = atoi (args[i]);
+	  else
+	    error_msg (args[0], "follow the -a option with a numeric arg >=0", "");
+	}
+      else if (args[i][0] == '-')
+	error_msg (args[0], "unknown switch:", args[i]);
+      else if (!libraryname)
+	libraryname = args[i];
+      else if (!prunename)
+	prunename = args[i];
+      else
+	error_msg (args[0], "too many filenames", "");
+    }
+
+  if (!libraryname)
+    error_msg (args[0], "please specify a library file", "");
+
+  if (V)
+    fprintf (stderr, "%s: processing...\n", args[0]);
+
+  overwrite = 0;
+  if (!prunename)
+    {
+      prunename = libraryname;
+      if (V)
+	fprintf (stderr, "overwriting previous library file.\n");
+      overwrite = 1;
+    }
+
+  if (overwrite)
+    {
+      tmpnam (tempn);
+    }
+  else
+    strcpy (tempn, prunename);
+
+
+  avglibsize = count = read_library (libraryname, &origlist);
+  if (V)
+    fprintf (stderr, "%d marks...", count);
+
+  avglib = (marklistptr *) malloc (count * sizeof (marklistptr));
+  if (!avglib)
+    error_msg (args[0], "out of memory", "");
+  for (i = 0; i < avglibsize; i++)
+    avglib[i] = NULL;
+
+
+  prune_on_size (&origlist, sizethres);
+
+  prune_on_area (&origlist, areathres);
+  if (V)
+    fprintf (stderr, "after threshold pruning, %d marks\n", count = marklist_length (origlist));
+
+
+  if (V)
+    fprintf (stderr, "starting greedy library creation...\n");
+
+  greedy_add (origlist, &librarylist);
+
+
+  for (pass = 0; pass < num_passes; pass++)
+    {
+      marklist_free (&librarylist);
+      if (V)
+	fprintf (stderr, "averaging...\n");
+      librarylist = average_library ();
+      averagedout_addagain (origlist, &librarylist);
+      greedy_prune (&librarylist);
+      if (V)
+	fprintf (stderr, "after pass %d greedy prune %d marks\n", 1 + pass, marklist_length (librarylist));
+    }
+
+
+  lib = fopen (tempn, "wb");
+  if (lib == NULL)
+    error_msg (args[0], "trouble creating prune-library file.", "");
+
+
+  if (V)
+    fprintf (stderr, "writing to file: %s...", prunename);
+  for (step = librarylist, count = 0; step; step = step->next, count++)
+    {
+      step->data.symnum = count;
+      write_library_mark (lib, step->data);
+    }
+
+  if (V)
+    fprintf (stderr, "%d marks.\n", count);
+  fclose (lib);
+
+  if (overwrite)
+    {
+      sprintf (sysstr, "mv %s %s", tempn, prunename);
+      system (sysstr);		/* move temp to proper name */
+    }
+
+  for (i = 0; i < avglibsize; i++)
+    {
+      marklist_free (&avglib[i]);
+      free (avglib[i]);
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.1	(revision 16561)
@@ -0,0 +1,57 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgticstat 1 \*(Dt CITRI
+.SH NAME
+mgticstat \- displays the statistics for the library
+.SH SYNOPSIS
+.B mgticstat
+[
+.B \-h
+]
+.I libraryfile
+.SH DESCRIPTION
+.B mgticstat
+displays statistics about the
+.I libraryfile
+file.
+The output of this program contains the average mark size, average
+centroid position, and the number of marks with an area less than or
+equal to a few powers of\ 2.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fIlibraryfile\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.I libraryfile
+This specifies the name of the library file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/mgticstat.c	(revision 16561)
@@ -0,0 +1,130 @@
+/**************************************************************************
+ *
+ * mgticstat.c -- Program to print out stats about a textual image library
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+
+void 
+usage ()
+{
+  fprintf (stderr, "usage:\n"
+	   "\tmgticstat   libraryfile\n");
+  exit (1);
+}
+
+void 
+main (int argc, char *args[])
+{
+  FILE *lib, *outf = (stdout);
+  int count;
+  int small1, small2, small4, small9, small16, small25, small36, small49,
+    small64, small81, small100;
+  marktype d;
+  int avx, avy;
+  int hhh, www, i;
+  int avruns_h = 0, avruns_v = 0;
+  int av_area = 0;
+  char *libraryname = NULL;
+
+
+  if (argc < 2)
+    usage ();
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (args[i], "-v"))
+	V = 1;
+      else if (!strcmp (args[i], "-h"))
+	usage ();
+      else if (args[i][0] == '-')
+	error_msg (args[0], "unknown switch:", args[i]);
+      else if (!libraryname)
+	libraryname = args[i];
+      else
+	error_msg (args[0], "too many filenames", "");
+    }
+
+  if (!libraryname)
+    error_msg (args[0], "please specify a library file", "");
+
+
+  lib = fopen (libraryname, "r");
+  if (lib == NULL)
+    error_msg (args[0], "Trouble opening library file", "");
+
+  count = 0;
+  small1 = small2 = small4 = small9 = small16 = small25 = small36 = small49 = small64 = small81 = small100 = 0;
+  avx = avy = 0;
+  www = hhh = 0;
+  while (!isEOF (lib))
+    {
+      if (!read_library_mark (lib, &d))
+	error_msg (args[0], "error in library file", "");
+
+      av_area += d.set;
+      if (d.set <= 1)
+	small1++;
+      if (d.set <= 2)
+	small2++;
+      if (d.set <= 4)
+	small4++;
+      if (d.set <= 9)
+	small9++;
+      if (d.set <= 16)
+	small16++;
+      if (d.set <= 25)
+	small25++;
+      if (d.set <= 36)
+	small36++;
+      if (d.set <= 49)
+	small49++;
+      if (d.set <= 64)
+	small64++;
+      if (d.set <= 81)
+	small81++;
+      if (d.set <= 100)
+	small100++;
+      avx += d.xcen;
+      avy += d.ycen;
+      www += d.w;
+      hhh += d.h;
+      avruns_h += d.h_runs;
+      avruns_v += d.v_runs;
+      pbm_freearray (&d.bitmap, d.h);
+      count++;
+      if (count % 5000 == 0)
+	fprintf (outf, "%dk ", count / 1000);
+    }
+  fprintf (outf, "number of marks=%d\n", count);
+  fprintf (outf, "average area=%d\n", av_area / count);
+  fprintf (outf, "area <=1:%d, <=2:%d, <=4:%d, <=9:%d, <=16:%d, <=25:%d, <=36:%d, <=49:%d, <=64:%d, <=81:%d, <=100:%d\n", small1, small2, small4, small9, small16, small25, small36, small49, small64, small81, small100);
+
+  fprintf (outf, "average num runs, horizontal=%d, vertical=%d\n", avruns_h / count, avruns_v / count);
+  fprintf (outf, "average centroid position=(%d,%d)\n", avx / count, avy / count);
+  fprintf (outf, "average width=%d, average height=%d\n", www / count, hhh / count);
+  fclose (lib);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/model.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/model.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/model.h	(revision 16561)
@@ -0,0 +1,92 @@
+/**************************************************************************
+ *
+ * model.h -- Part pf PPM data compression
+ * Copyright (C) 1987  Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#define defaultkbytes	896
+#define minkbytes	56
+#define maxkbytes	((65536/1024)*sizeof(eventnode))
+
+#ifndef increment
+#define	increment	8
+#endif
+#ifndef halveat
+#define halveat		64
+#endif
+#define maxtotalcnt 	(increment*halveat)
+
+typedef int boolean;
+#define false           0
+#define true            1
+
+typedef short event;
+typedef unsigned short point;
+
+typedef struct
+  {
+    short totalcnt;
+    short notfound;
+    point list;			/* list storing the event records */
+  }
+eventset;
+
+typedef struct
+  {
+    event eventnum;
+    short count;
+    point next, prev;
+    eventset foll;
+  }
+eventnode, *eventptr;
+
+#define memsize		(kbytes*1024)
+#define maxnodes	(memsize/sizeof(eventnode))
+/* #define charsetsize     256 -- MAHE */
+#define nchars          (charsetsize+1)
+
+extern int charsetsize;		/* -- MAHE */
+
+extern eventptr E;
+extern long kbytes;
+extern point numnodes, nfreenodes;
+extern boolean excluded[];	/* -- MAHE */
+
+#define ENULL		(E)
+
+typedef unsigned u;
+#define p32(p)	(E+p)
+#define p16(p)	(p-E)
+
+/* in ppm.c */
+void write_model ();
+void encodestring ();
+void decodestring ();
+void encodefile ();
+void decodefile ();
+
+/* in lstevent.c */
+void write_method ();
+eventnode *encode_event_noex ();
+eventnode *decode_event_noex ();
+eventnode *encode_event ();
+eventnode *decode_event ();
+/* eventnode *addevent(); */
+eventnode *newnode ();
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.c	(revision 16561)
@@ -0,0 +1,288 @@
+/**************************************************************************
+ *
+ * pbmtools.c -- Routines for dealing with portable bitmap files (PBM)
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "pbmtools.h"
+#include "extractor.h"
+#include "utils.h"
+
+
+
+void 
+pbm_putpixel_range (Pixel ** bitmap, int c, int r, int val, int cols, int rows)
+{
+  if (((c) >= 0) && ((c) < cols) && ((r) >= 0) && ((r) < rows))
+    pbm_putpixel (bitmap, c, r, val);
+  else
+    fprintf (stderr, "putpitxel (%d %d) outside (%d %d)\n", c, r, cols, rows);
+}
+
+int 
+pbm_getpixel_range (Pixel ** bitmap, int c, int r, int cols, int rows)
+{
+  if (((c) >= 0) && ((c) < cols) && ((r) >= 0) && ((r) < rows))
+    return pbm_getpixel (bitmap, c, r);
+  else
+    {
+      fprintf (stderr, "getpixel (%d %d) outside (%d %d)\n", c, r, cols, rows);
+      return 0;
+    }
+}
+
+
+
+
+
+/*
+   Returns 1 if *fp points to a PBM (magic is P1 or P4) file.
+   leaves the fp in it's original position.
+   usage: if(pbm_isapbmfile(fp)) process(fp);
+ */
+int 
+pbm_isapbmfile (FILE * fp)
+{
+  u_long magic;
+
+  magic = getmagicno_short (fp);
+  if ((magic == MAGIC_P4) || (magic == MAGIC_P1))
+    return 1;
+  else
+    return 0;
+}
+
+
+/*
+   Frees the memory used in the bitmap structure.
+   usage: pbm_freearray(&bitmap);
+   Sets bitmap to be NULL on return.
+ */
+void 
+pbm_freearray (Pixel *** bitmap, int rows)
+{
+  int r;
+  if (rows == 0)
+    rows++;
+  for (r = 0; r < rows; r++)
+    free ((*bitmap)[r]);
+  free (*bitmap);
+  (*bitmap) = NULL;
+}
+
+
+/*
+   This function allocates the bitmap.
+   usage: bitmap=pbm_allocarray(cols,rows);
+ */
+Pixel **
+pbm_allocarray (int cols, int rows)
+{
+  Pixel **p;
+  int i, newc = (cols + PBITS) / PBITS;
+
+  if (cols == 0)
+    cols++;
+  if (rows == 0)
+    rows++;
+  p = (Pixel **) malloc (rows * sizeof (Pixel *));
+  if (p == NULL)
+    error_msg ("pbm_allocarray", "out of memory", "");
+  for (i = 0; i < rows; i++)
+    {
+      p[i] = (Pixel *) calloc (newc, sizeof (Pixel));
+      if (p[i] == NULL)
+	error_msg ("pbm_allocarray", "out of memory", "");
+    }
+  return p;
+}
+
+
+
+Pixel **
+pbm_copy (Pixel ** bitmap, int cols, int rows)
+{
+  Pixel **p;
+  int newc = (cols + PBITS) / PBITS;
+  int x, y;
+
+  p = pbm_allocarray (cols, rows);
+  for (x = 0; x < newc; x++)
+    for (y = 0; y < rows; y++)
+      p[y][x] = bitmap[y][x];
+  return p;
+}
+
+
+
+
+/*
+   Reads in a pbm file and returns a pointer to the allocated bitmap, 
+   in the P4 format.
+   Assumes fp was opened and keeps it that way.
+   usage: bitmap=pbm_readfile(fp,&cols,&rows);
+ */
+Pixel **
+pbm_readfile (FILE * fp, int *cols, int *rows)
+{
+  Pixel **p = NULL;
+  char s[100];
+  unsigned char *buf;
+  int r, c, ii = 0;
+  int p1 = 0;
+  int arg;
+
+
+  do
+    {
+      readline (s, fp);
+    }
+  while (s[0] == '#');
+
+  if (strcmp (s, "P1") == 0)
+    p1 = 1;
+  else if (strcmp (s, "P4") == 0)
+    p1 = 0;
+  else
+    error_msg ("pbm_readfile", "only pbm files are supported", "");
+
+  do
+    {
+      readline (s, fp);
+    }
+  while (s[0] == '#');
+
+  arg = sscanf (s, "%d %d", cols, rows);
+  if (arg == 1)
+    {
+      readline (s, fp);
+      arg = sscanf (s, "%d", rows);
+      if (arg != 1)
+	error_msg ("pbm_readfile", "bizzare pbm format", "");
+    }
+  p = pbm_allocarray (*cols, *rows);
+  buf = (unsigned char *) malloc ((*cols + 2) * sizeof (unsigned char));
+  if (!buf)
+    error_msg ("pbm_readfile", "out of memory", "");
+
+  if (p1)
+    {
+      for (r = 0; r < *rows; r++)
+	for (c = 0; c < *cols; c++)
+	  {
+	    fscanf (fp, "%d", &ii);
+	    pbm_putpixel (p, c, r, ii);
+	  }
+    }
+  else
+    {
+      for (r = 0; r < *rows; r++)
+	{
+	  c = 0;
+	  fread (buf, 1, (*cols + 7) / 8, fp);
+	  for (c = 0; c < *cols; c++)
+	    {
+	      if (!(c % 8))
+		ii = buf[c / 8];
+	      if (ii < 0)
+		error_msg ("pbm_readfile", "file reading error", "");
+	      pbm_putpixel (p, c, r, (ii & 128) ? 1 : 0);
+	      ii <<= 1;
+	    }
+	}
+    }				/* p4 type */
+  free (buf);
+  return p;
+}
+
+
+/*
+   Writes a pbm file given a file pointer and bitmap. Assumes that
+   fp is already open, and keeps in that way.
+   usage: pbm_writefile(fp,bitmap,cols,rows);
+ */
+void 
+pbm_writefile (FILE * fp, Pixel ** bitmap, int cols, int rows)
+{
+  int r, c, ii = 0, j;
+  unsigned char *buf;
+
+  buf = (unsigned char *) malloc (((cols + 14) / 8) * sizeof (unsigned char));
+
+  fprintf (fp, "%s\n", "P4");
+  fprintf (fp, "%d %d\n", cols, rows);
+
+  for (r = 0; r < rows; r++)
+    {
+      for (c = 0; c < cols; c += 8)
+	{
+	  ii = 0;
+	  for (j = 0; j < 8; j++)
+	    if (c + j < cols)
+	      {
+		if (pbm_getpixel (bitmap, c + j, r))
+		  ii |= (128 >> j);
+	      }
+	  buf[c / 8] = ii;
+	}
+      fwrite (buf, 1, (cols + 7) / 8, fp);
+    }
+  free (buf);
+/*      putc('\n',fp);   if this line is included programs don't say things like 'short image' &c, but then again,
+   in most cases a 'cmp' between the original will fail because of the last byte...sigh, "standards". */
+}
+
+#define BUF_SIZE 131072
+char buffer[BUF_SIZE];
+
+void 
+pbm_writenamedfile (char fn[], Pixel ** bitmap, int cols, int rows)
+{
+  FILE *tempfp;
+
+  tempfp = fopen (fn, "wb");
+  if (tempfp == NULL)
+    error_msg ("pbm_writenamedfile", "can't open file:", fn);
+  setbuffer (tempfp, buffer, BUF_SIZE);
+  pbm_writefile (tempfp, bitmap, cols, rows);
+  fclose (tempfp);
+}
+
+
+
+Pixel **
+pbm_readnamedfile (char fn[], int *cols, int *rows)
+{
+  FILE *tempfp;
+  Pixel **temp;
+
+  tempfp = fopen (fn, "rb");
+  if (tempfp == NULL)
+    return NULL;
+  setbuffer (tempfp, buffer, BUF_SIZE);
+
+  temp = pbm_readfile (tempfp, cols, rows);
+
+  fclose (tempfp);
+  return temp;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/pbmtools.h	(revision 16561)
@@ -0,0 +1,86 @@
+/**************************************************************************
+ *
+ * pbmtools.h -- Routines for dealing with portable bitmap files (PBM)
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __PBMTOOLS_H
+#define __PBMTOOLS_H
+
+#include "sysfuncs.h"
+#include "basictypes.h"
+
+#include "utils.h"
+#include "marklist.h"
+
+#define PBITS 32
+#define PSH 5
+
+
+/* The next two are ordered pixels col0=left, cols31=right */
+
+/*
+   #define pbm_getpixel(bitmap,c,r)  \
+   ((bitmap[r][(c)>>PSH] >> ((PBITS-1)-((c)&(PBITS-1))))&1)
+   #define pbm_putpixel(bitmap,c,r,val) \
+   if((val))  \
+   bitmap[r][(c)>>PSH] |=  (((unsigned)1<<(PBITS-1)) >> ((c)&(PBITS-1))); \
+   else \
+   bitmap[r][(c)>>PSH] &= ~(((unsigned)1<<(PBITS-1)) >> ((c)&(PBITS-1))) 
+ */
+
+
+
+
+/* The next two defines are ordered pixel cols0=right, cols31=left */
+
+#define pbm_getpixel(bitmap,c,r)                            \
+         ((bitmap[r][(c)>>PSH] >> ((c)&(PBITS-1)))&1)
+#define pbm_putpixel(bitmap,c,r,val)                        \
+         if((val))                                          \
+              bitmap[r][(c)>>PSH] |= (1<<((c)&(PBITS-1)));  \
+         else                                               \
+              bitmap[r][(c)>>PSH] &= ~(1<<((c)&(PBITS-1)))
+
+
+#define pbm_putpixel_trunc(bitmap,c,r,val,cols,rows) \
+       if(((c)>=0)&&((c)<cols)&&((r)>=0)&&((r)<rows)) \
+             pbm_putpixel(bitmap,c,r,val)
+
+#define pbm_getpixel_trunc(bitmap,c,r,cols,rows)       \
+       ((((c)>=0)&&((c)<cols)&&((r)>=0)&&((r)<rows)) ?  \
+             pbm_getpixel(bitmap,c,r) : 0)
+
+
+
+
+int pbm_isapbmfile (FILE * fp);
+void pbm_freearray (Pixel *** bitmap, int rows);
+Pixel **pbm_copy (Pixel ** bitmap, int cols, int rows);
+Pixel **pbm_allocarray (int cols, int rows);
+Pixel **pbm_readfile (FILE * fp, int *cols, int *rows);
+Pixel **pbm_readnamedfile (char fn[], int *cols, int *rows);
+void pbm_writefile (FILE * fp, Pixel ** bitmap, int cols, int rows);
+void pbm_writenamedfile (char fn[], Pixel ** bitmap, int cols, int rows);
+
+void pbm_putpixel_range (Pixel ** bitmap, int c, int r, int val, int cols, int rows);
+int pbm_getpixel_range (Pixel ** bitmap, int c, int r, int cols, int rows);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/ppm.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/ppm.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/ppm.c	(revision 16561)
@@ -0,0 +1,588 @@
+/**************************************************************************
+ *
+ * ppm.c -- PPM data compression
+ * Copyright (C) 1987  Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "arithcode.h"
+#include "model.h"
+
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:16:56  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:33  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:33:55  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1994/09/20  04:42:02  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+/* FIXEDORDER sets the maximum context length that will be used.
+   Making FIXEDORDER large increases the compression if there is enough
+   space to store the model, but when space limit is reached increasing
+   FIXEDORDER will decrease compression because of model thrashing.
+   Third order (FIXEDORDER=3) is suitable for a few hundred Kb of model
+   space, FIXEDORDER=1 is suitable for a few tens of kB.
+   This version requires that FIXEDORDER be a preprocessor rather
+   than runtime variable to allow loops to be unrolled and make for faster
+   execution. Maximum in this implementation is 3 */
+
+#ifndef FIXEDORDER
+#define FIXEDORDER	3
+#endif
+
+/* global variables */
+eventptr E = NULL;		/* array of eventnodes */
+point numnodes = 0, nfreenodes = 0;
+long kbytes = defaultkbytes;
+/*boolean               excluded[nchars]={false};  - MAHE */
+boolean excluded[16384] =
+{false};			/* excluded characters */
+
+/* local variables */
+#define defaultchunksize        4096	/* compression chunks */
+
+#define EOSchar         (charsetsize)
+
+unsigned chunksize = defaultchunksize;
+eventptr s[FIXEDORDER + 1] =
+{NULL},				/* current contexts */
+  t[FIXEDORDER + 1] =
+{NULL};				/* new contexts */
+
+#if (FIXEDORDER==3)
+#define SHIFT_ST	s[1] = t[0]; s[2] = t[1] ; s[3] = t[2]
+#else
+#if (FIXEDORDER==2)
+#define SHIFT_ST	s[1] = t[0]; s[2] = t[1]
+#else
+#if (FIXEDORDER==1)
+#define SHIFT_ST	s[1] = t[0]
+#else
+#define SHIFT_ST
+#endif
+#endif
+#endif
+
+/* follow a character list and set the exclusions */
+#define SET_EX(p)				\
+do						\
+{	register eventptr q;			\
+	q = p32((p32(p))->next);		\
+	for (; q!=ENULL; q=p32(q->next))	\
+		excluded[q->eventnum] = true;	\
+}						\
+while (false)
+
+/* clear the exclusions */
+#define CLEAR_EX(p)				\
+do						\
+{	register eventptr q;			\
+	q = p32((p32(p))->next);		\
+	for (; q!=ENULL; q=p32(q->next))	\
+		excluded[q->eventnum] = false;	\
+}						\
+while (false)
+
+/*==================================*/
+
+void
+write_model ()
+{
+  fprintf (stderr, "ppm");
+  write_method ();
+#ifdef NOEXCLUSIONS
+  fprintf (stderr, "nx");
+#endif
+  fprintf (stderr, "-o%1d", FIXEDORDER);
+  fprintf (stderr, "-k%ld", kbytes);
+}
+
+/*==================================*/
+
+void
+encode_in_order (n, e)
+     int n;
+     event e;
+/* encode character e in order n */
+{
+  boolean encoded;
+  int i, c;
+  t[n] = encode_event_noex (&s[n]->foll, e, &encoded);
+  if (encoded)
+    c = n;
+  else
+    for (c = n - 1; c >= 0; c--)
+      {
+#ifdef NOEXCLUSIONS
+	t[c] = encode_event_noex (&s[c]->foll, e, &encoded);
+#else
+	SET_EX (s[c + 1]->foll.list);
+	t[c] = encode_event (&s[c]->foll, e, &encoded);
+#endif
+	if (encoded)
+	  break;
+      }
+  if (!encoded)
+    arithmetic_encode (e, e + 1, nchars);
+  for (i = c + 1; i <= n; i++)
+    if (i > 0)
+      t[i]->prev = p16 (t[i - 1]);
+    else
+      t[0]->prev = p16 (s[0]);
+  for (i = c - 1; i >= 0; i--)
+    t[i] = p32 (t[i + 1]->prev);
+#ifndef NOEXCLUSIONS
+  if (c != n)
+    CLEAR_EX (s[c + 1]->foll.list);
+#endif
+  SHIFT_ST;
+}
+
+/*==================================*/
+
+/* not required in this version */
+
+#if 0
+
+void
+decode_in_order (n, e)
+     int n;
+     event *e;
+{
+  boolean decoded;
+  int i, c;
+  t[n] = decode_event_noex (&s[n]->foll, &decoded, e);
+  if (decoded)
+    c = n;
+  else
+    for (c = n - 1; c >= 0; c--)
+      {
+#ifdef NOEXCLUSIONS
+	t[c] = decode_event_noex (&s[c]->foll, &decoded, e);
+#else
+	SET_EX (s[c + 1]->foll.list);
+	t[c] = decode_event (&s[c]->foll, &decoded, e);
+#endif
+	if (decoded)
+	  break;
+      }
+  if (!decoded)
+    {
+      *e = arithmetic_decode_target (nchars);
+      arithmetic_decode (*e, *e + 1, nchars);
+    }
+  for (i = c + 1; i <= n; i++)
+    {
+      t[i]->eventnum = *e;
+      if (i > 0)
+	t[i]->prev = p16 (t[i - 1]);
+      else
+	t[0]->prev = p16 (s[0]);
+    }
+  {
+    register int i;
+    for (i = c - 1; i >= 0; i--)
+      t[i] = p32 (t[i + 1]->prev);
+  }
+#ifndef NOEXCLUSIONS
+  if (c != n)
+    CLEAR_EX (s[c + 1]->foll.list);
+#endif
+  SHIFT_ST;
+}
+
+#endif
+
+/*==================================*/
+
+void
+start_model ()
+{
+  int i;
+  /* is this the first call? */
+  if (E == NULL)
+    E = (eventptr) malloc ((unsigned) kbytes * 1024);
+  /* start off with no information */
+  numnodes = 0;
+  nfreenodes = maxnodes;
+  for (i = 0; i <= FIXEDORDER; i++)
+    {
+      s[i] = newnode (NULL);
+      if (i > 0)
+	s[i]->prev = p16 (s[i - 1]);
+    }
+}
+
+/*==================================*/
+
+void
+encodestring (str, n)
+     unsigned short *str;
+     int n;
+/* encode all n characters of the supplied string str */
+{
+  event e;
+  boolean encoded;
+  register eventptr S, T;
+  int i;
+#ifndef NOEXCLUSIONS
+  register eventptr Sex;
+#endif
+
+  if (E == NULL)
+    start_model ();
+  /* set the initial context node */
+  S = s[FIXEDORDER];
+
+  for (i = 0; i < n; i++)
+    {
+      /* encode one character */
+      e = *str++;
+      /* this next monstrosity is logically equivalent to 
+         encode_in_order(FIXEDORDER,e)
+         the loop has been unrolled to make it all go faster */
+      for (;;)
+	{
+	  T = encode_event_noex (&S->foll, e, &encoded);
+	  if (encoded)
+	    break;
+	  t[FIXEDORDER] = T;
+
+#ifdef NOEXCLUSIONS
+#if (FIXEDORDER>2)
+	  S = p32 (S->prev);
+	  t[2] = encode_event_noex (&S->foll, e, &encoded);
+	  t[3]->prev = p16 (t[2]);
+	  if (encoded)
+	    break;
+#endif
+
+#if (FIXEDORDER>1)
+	  S = p32 (S->prev);
+	  t[1] = encode_event_noex (&S->foll, e, &encoded);
+	  t[2]->prev = p16 (t[1]);
+	  if (encoded)
+	    break;
+#endif
+
+#if (FIXEDORDER>0)
+	  S = p32 (S->prev);
+	  t[0] = encode_event_noex (&S->foll, e, &encoded);
+	  t[1]->prev = p16 (t[0]);
+	  if (encoded)
+	    break;
+#endif
+#else
+#if (FIXEDORDER>2)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[2] = encode_event (&S->foll, e, &encoded);
+	  t[3]->prev = p16 (t[2]);
+	  if (encoded)
+	    {
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>1)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[1] = encode_event (&S->foll, e, &encoded);
+	  t[2]->prev = p16 (t[1]);
+	  if (encoded)
+	    {
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>0)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[0] = encode_event (&S->foll, e, &encoded);
+	  t[1]->prev = p16 (t[0]);
+	  if (encoded)
+	    {
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+	  CLEAR_EX (Sex->foll.list);
+#endif
+#endif
+
+	  arithmetic_encode (e, e + 1, nchars);
+	  t[0]->prev = p16 (s[0]);
+	  break;
+	}
+      /* phew! Update the current context to the latest node */
+      S = p32 (T->prev);
+
+      /* is there a danger of running out of space? */
+      if (nfreenodes <= FIXEDORDER)
+	{
+	  start_model ();
+	  S = s[FIXEDORDER];
+	}
+    }
+
+  /* save the current contexts for the various order submodels */
+  s[FIXEDORDER] = S;
+  for (i = FIXEDORDER; i > 0; i--)
+    s[i - 1] = p32 (s[i]->prev);
+  /* and send the terminator character */
+  encode_in_order (FIXEDORDER, EOSchar);
+  /* and check the space again */
+  if (nfreenodes <= FIXEDORDER)
+    start_model ();
+}
+
+/*==================================*/
+
+void
+decodestring (str, n)
+     unsigned short *str;
+     int *n;
+{
+  event e;
+  boolean decoded;
+  register eventptr S, T;
+#ifndef NOEXCLUSIONS
+  register eventptr Sex;
+#endif
+
+  /* just mimic the corresponding encoding actions */
+  if (E == NULL)
+    start_model ();
+  S = s[FIXEDORDER];
+
+  *n = 0;
+  for (;;)
+    {				/* logically equivalent to "decode_in_order(FIXEDORDER,&e)" */
+
+      for (;;)
+	{
+	  T = decode_event_noex (&S->foll, &decoded, &e);
+	  if (decoded)
+	    break;
+	  t[FIXEDORDER] = T;
+
+#ifdef NOEXCLUSIONS
+#if (FIXEDORDER>2)
+	  S = p32 (S->prev);
+	  t[2] = decode_event_noex (&S->foll, &decoded, &e);
+	  t[3]->prev = p16 (t[2]);
+	  if (decoded)
+	    {
+	      t[3]->eventnum = e;
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>1)
+	  S = p32 (S->prev);
+	  t[1] = decode_event_noex (&S->foll, &decoded, &e);
+	  t[2]->prev = p16 (t[1]);
+	  if (decoded)
+	    {
+#if (FIXEDORDER>2)
+	      t[3]->eventnum =
+#endif
+		t[2]->eventnum = e;
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>0)
+	  S = p32 (S->prev);
+	  t[0] = decode_event_noex (&S->foll, &decoded, &e);
+	  t[1]->prev = p16 (t[0]);
+	  if (decoded)
+	    {
+#if (FIXEDORDER>2)
+	      t[3]->eventnum =
+#endif
+#if (FIXEDORDER>1)
+		t[2]->eventnum =
+#endif
+		t[1]->eventnum = e;
+	      break;
+	    }
+#endif
+#else
+#if (FIXEDORDER>2)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[2] = decode_event (&S->foll, &decoded, &e);
+	  t[3]->prev = p16 (t[2]);
+	  if (decoded)
+	    {
+	      t[3]->eventnum = e;
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>1)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[1] = decode_event (&S->foll, &decoded, &e);
+	  t[2]->prev = p16 (t[1]);
+	  if (decoded)
+	    {
+#if (FIXEDORDER>2)
+	      t[3]->eventnum =
+#endif
+		t[2]->eventnum = e;
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+#endif
+
+#if (FIXEDORDER>0)
+	  Sex = S;
+	  SET_EX (Sex->foll.list);
+	  S = p32 (S->prev);
+	  t[0] = decode_event (&S->foll, &decoded, &e);
+	  t[1]->prev = p16 (t[0]);
+	  if (decoded)
+	    {
+#if (FIXEDORDER>2)
+	      t[3]->eventnum =
+#endif
+#if (FIXEDORDER>1)
+		t[2]->eventnum =
+#endif
+		t[1]->eventnum = e;
+	      CLEAR_EX (Sex->foll.list);
+	      break;
+	    }
+	  CLEAR_EX (Sex->foll.list);
+#endif
+#endif
+
+	  e = arithmetic_decode_target (nchars);
+	  arithmetic_decode (e, e + 1, nchars);
+#if (FIXEDORDER>2)
+	  t[3]->eventnum =
+#endif
+#if (FIXEDORDER>1)
+	    t[2]->eventnum =
+#endif
+#if (FIXEDORDER>0)
+	    t[1]->eventnum =
+#endif
+	    t[0]->eventnum = e;
+	  t[0]->prev = p16 (s[0]);
+	  break;
+	}
+      S = p32 (T->prev);
+
+      /* check the space requirements */
+      if (nfreenodes <= FIXEDORDER)
+	{
+	  start_model ();
+	  S = s[FIXEDORDER];
+	}
+      /* are we done?  or do we save the char */
+      if (e == EOSchar)
+	break;
+      *str++ = e;
+      (*n)++;
+    }
+  s[FIXEDORDER] = S;
+}
+
+
+#if 0				/* Writing my own version of this - MAHE */
+void
+encodefile ()
+/* encodes a file by breaking into a sequence of fixed length chunks */
+{
+  unsigned char *str;
+  int c, n = 0;
+
+  str = (unsigned char *) malloc ((unsigned) chunksize);
+  startoutputingbits ();
+  startencoding ();
+
+  /* to encode the file, break it into chunks and encode the
+     chunks one by one */
+  while ((c = getchar ()) != EOF)
+    {
+      str[n++] = c;
+      if (n == chunksize)
+	{
+	  encodestring (str, n);
+	  rawbytes += n;
+	  n = 0;
+	}
+    }
+  /* send final partial chunk */
+  if (n > 0)
+    {
+      encodestring (str, n);
+      rawbytes += n;
+    }
+  /* send EOF as an empty string */
+  encodestring (str, 0);
+  doneencoding ();
+  doneoutputingbits ();
+}
+
+void
+decodefile ()
+{
+  unsigned char *str;
+  int n, i;
+
+  str = (unsigned char *) malloc ((unsigned) chunksize);
+  startinputingbits ();
+  startdecoding ();
+  for (;;)
+    {
+      decodestring (str, &n);
+      if (n == 0)
+	break;
+      for (i = 0; i < n; i++)
+	putchar ((int) str[i]);
+      rawbytes += n;
+    }
+}
+
+#endif /* #if 0 */
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.c	(revision 16561)
@@ -0,0 +1,93 @@
+/**************************************************************************
+ *
+ * sortmarks.c -- Functions which sort the marks into a "readable sequence"
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "marklist.h"
+#include "sortmarks.h"
+
+#define GAP 4
+
+
+
+static int 
+CmpOnY (const void *e1, const void *e2)
+{
+  return (((marktype *) e1)->ycen * 0 + ((marktype *) e1)->ypos) -
+    (((marktype *) e2)->ycen * 0 + ((marktype *) e2)->ypos);
+}
+
+static int 
+CmpOnX (const void *e1, const void *e2)
+{
+  return (((marktype *) e1)->xcen * 0 + ((marktype *) e1)->xpos) -
+    (((marktype *) e2)->xcen * 0 + ((marktype *) e2)->xpos);
+}
+
+
+
+
+
+
+
+
+marklistptr 
+sortmarks (marklistptr listofmarks)
+{
+  marktype *table;
+  int count = 0;
+  int i, s, e;
+
+
+  table = calloc ((unsigned int) marklist_length (listofmarks), sizeof (marklisttype));
+
+  while (listofmarks)
+    {
+      table[count] = listofmarks->data;
+      marklist_removeat (&listofmarks, 0);
+      count++;
+    }
+
+  /* sort first on the y position of their centroids */
+  qsort ((void *) table, (unsigned int) (count), sizeof (*table), CmpOnY);
+
+  for (s = 0; s < count;)
+    {
+      int c;
+
+      for (e = s + 1, c = 0; (e < count) &&
+	   ((table[e].ypos + table[e].ycen * 0) - (table[e - 1].ypos + table[e - 1].ycen * 0) <= GAP); c++, e++);
+
+      if (e < count && c >= 1)
+	qsort (&table[s], (unsigned int) (e - s), sizeof (*table), CmpOnX);
+      s = e;
+    }
+
+  for (i = 0; i < count; i++)
+    {
+      table[i].symnum = i;
+      marklist_add (&listofmarks, table[i]);
+    }
+
+  return listofmarks;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/sortmarks.h	(revision 16561)
@@ -0,0 +1,30 @@
+/**************************************************************************
+ *
+ * sortmarks.h -- Functions which sort the marks into a "readable sequence"
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 _SortMarks_h_
+#define _SortMarks_h_
+
+marklistptr sortmarks (marklistptr listofmarks);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/utils.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/utils.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/utils.c	(revision 16561)
@@ -0,0 +1,270 @@
+/**************************************************************************
+ *
+ * utils.c -- Functions which are common utilities for the image programs
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "utils.h"
+
+
+int V = 0;
+
+
+int 
+isEOF (FILE * fp)
+{
+  int ch;
+
+  ch = getc (fp);
+  ungetc (ch, fp);
+  if (ch == EOF)
+    return 1;
+  return 0;
+}
+
+
+int 
+getmagicno_short (FILE * fp)
+{
+  int ch1, ch2;
+  ch1 = getc (fp);
+  if (ch1 == EOF)
+    {
+      ungetc (ch1, fp);
+      return 0;
+    }
+  ch2 = getc (fp);
+  if (ch2 == EOF)
+    {
+      ungetc (ch2, fp);
+      return 0;
+    }
+  ungetc (ch2, fp);
+  ungetc (ch1, fp);
+
+  return ((ch1 << 8) | ch2);
+}
+
+int 
+getmagicno_short_pop (FILE * fp)
+{
+  int ch1, ch2;
+  ch1 = getc (fp);
+  if (ch1 == EOF)
+    return 0;
+  ch2 = getc (fp);
+  if (ch2 == EOF)
+    return 0;
+
+  return ((ch1 << 8) | ch2);  
+}
+
+int 
+getmagicno_byte (FILE * fp)
+{
+  int ch1;
+  ch1 = getc (fp);
+  if (ch1 == EOF)
+    {
+      ungetc (ch1, fp);
+      return 0;
+    }
+  ungetc (ch1, fp);
+  return (ch1);
+}
+
+
+int 
+getmagicno_long (FILE * fp)
+{
+  int ch1, ch2, ch3, ch4;
+  ch1 = getc (fp);
+  if (ch1 == EOF)
+    {
+      ungetc (ch1, fp);
+      return 0;
+    }
+  ch2 = getc (fp);
+  if (ch2 == EOF)
+    {
+      ungetc (ch2, fp);
+      return 0;
+    }
+  ch3 = getc (fp);
+  if (ch3 == EOF)
+    {
+      ungetc (ch3, fp);
+      return 0;
+    }
+  ch4 = getc (fp);
+  if (ch4 == EOF)
+    {
+      ungetc (ch4, fp);
+      return 0;
+    }
+  ungetc (ch4, fp);
+  ungetc (ch3, fp);
+  ungetc (ch2, fp);
+  ungetc (ch1, fp);
+
+  return ((ch1 << 24) | (ch2 << 16) | (ch3 << 8) | (ch4));
+}
+
+
+
+
+void 
+error_msg (char *prog, char *message, char *extra)
+{
+  fprintf (stderr, "%s: %s %s\n", prog, message, extra);
+  exit (1);
+}
+
+void 
+warn (char *prog, char *message, char *extra)
+{
+  fprintf (stderr, "%s: %s %s\n", prog, message, extra);
+}
+
+
+
+void 
+readline (char str[], FILE * fp)
+{
+  int i = 0, ch;
+
+  while (((ch = fgetc (fp)) != '\n') && (!feof (fp)))
+    str[i++] = ch;
+  str[i] = '\0';
+}
+
+
+
+
+
+
+
+int 
+getint (FILE * fp)
+{
+  register char ch;
+  register unsigned int i = 0;
+
+  do
+    {
+      ch = getc (fp);
+      if (feof (fp))
+	return EOF;
+    }
+  while (ch < '0' || ch > '9');
+
+  do
+    {
+      i = i * 10 + ch - '0';
+      ch = getc (fp);
+      if (feof (fp))
+	return EOF;
+    }
+  while (ch >= '0' && ch <= '9');
+
+  return i;
+}
+
+/* get_header_int i.e. get an integer from a PNM header ; basically  
+   the same as
+   above except for comment checking ; above kept because of lower  
+   overhead */
+
+unsigned int 
+gethint (FILE * fp)
+{
+  register char ch, prev = '\0';
+  register unsigned int i = 0;
+
+  do
+    {
+      ch = getc (fp);
+      if ((ch == '#') && (prev == '\n'))
+	while (((ch = getc (fp)) != '\n') && !(feof (fp)));
+      prev = ch;
+      if (feof (fp))
+	return EOF;
+    }
+  while (ch < '0' || ch > '9');
+
+  do
+    {
+      i = i * 10 + ch - '0';
+      ch = getc (fp);
+      if (feof (fp))
+	return EOF;
+    }
+  while (ch >= '0' && ch <= '9');
+
+  return i;
+}
+
+
+
+
+void 
+magic_write (FILE * fp, u_long magic_num)
+{
+  HTONUL(magic_num);  /* [RPAP - Jan 97: Endian Ordering] */
+  if (fwrite (&magic_num, sizeof (magic_num), 1, fp) != 1)
+    error_msg ("magic num", "Couldn't write magic number.", "");
+}
+
+
+void 
+magic_check (FILE * fp, u_long magic_num)
+{
+  u_long magic;
+  if (fread (&magic, sizeof (magic), 1, fp) != 1 || NTOHUL(magic) != magic_num)  /* [RPAP - Jan 97: Endian Ordering] */
+    error_msg ("magic num", "Incorrect magic number.", "");
+}
+
+
+u_long 
+magic_read (FILE * fp)
+{
+  u_long magic;
+  if (fread (&magic, sizeof (magic), 1, fp) != 1)
+    error_msg ("magic num", "Couldn't read magic number.", "");
+  return NTOHUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+}
+
+
+
+
+int 
+isinteger (char s[])
+{
+  int i = 0;
+
+  for (i = 0; s[i] != '\0'; i++)
+    if (!isdigit (s[i]))
+      return 0;
+  return 1;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/utils.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/utils.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/utils.h	(revision 16561)
@@ -0,0 +1,79 @@
+/**************************************************************************
+ *
+ * utils.h -- Functions which are common utilities for the image programs
+ * Copyright (C) 1994  Stuart Inglis
+ *
+ * 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 __UTILS_H
+#define __UTILS_H
+
+#include "sysfuncs.h"
+
+extern int V;
+
+#define DEBUG
+
+#ifndef min
+#define min(a,b) ((a)<(b) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) ((a)>(b) ? (a) : (b))
+#endif
+
+#define GEN_MAGIC(a,b,c,d) ((unsigned long)(((a)<<24) + ((b)<<16) + \
+                                            ((c)<<8) + (d)))
+ 
+/* --- MG magic file codes [TS:Aug/95] --- */
+#define MAGIC_FELICS            GEN_MAGIC('M','G','F','X')
+#define MAGIC_BILEVEL           GEN_MAGIC('M','G','B','I')
+#define MAGIC_TIC               GEN_MAGIC('M','G','T','C')
+#define IS_MAGIC(a) ((((u_long)(a)) & 0xffff0000) == MAGIC_XXXX)
+/* --------------------------------------- */
+
+#define MAGIC_P1      0x5031	/* P1   - pbm */
+#define MAGIC_P2      0x5032	/* P2   - pgm */
+#define MAGIC_P3      0x5033	/* P3   - ppm */
+#define MAGIC_P4      0x5034	/* P4   - rawbits pbm */
+#define MAGIC_P5      0x5035	/* P5   - rawbits pgm */
+#define MAGIC_P6      0x5036	/* P6   - rawbits ppm */
+
+int isEOF (FILE * fp);
+int getmagicno_byte (FILE * fp);
+int getmagicno_short (FILE * fp);
+int getmagicno_short_pop (FILE * fp);
+int getmagicno_long (FILE * fp);
+
+
+
+void error_msg (char *prog, char *message, char *extra);
+void warn (char *prog, char *message, char *extra);
+
+void readline (char str[], FILE * fp);
+
+void magic_write (FILE * fp, u_long magic_num);
+void magic_check (FILE * fp, u_long magic_num);
+u_long magic_read (FILE * fp);
+/* kerry's code */
+
+int getint (FILE * fp);
+unsigned int gethint (FILE * fp);
+int isinteger (char s[]);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/images/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/images/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/images/win32.mak	(revision 16561)
@@ -0,0 +1,165 @@
+# Generated automatically from Makefile.in by configure.
+###########################################################################
+#
+# Makefile.in -- Makefile for the MG system
+# Copyright (C) 1994  Neil Sharman
+#
+# 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.
+#
+#       @(#)Makefile.in	1.10 22 Mar 1994
+#
+###########################################################################
+
+MGHOME = ..\..
+# for now put executables in indexers/bin
+INSTALLDIR = ..\..\..\bin
+
+CC = cl
+CPPFLAGS = 
+DEFS = -DHAVE_CONFIG_H -D__WIN32__ -D_LITTLE_ENDIAN -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"." -I"$(MGHOME)" -I"$(MGHOME)\lib"
+LDFLAGS =
+LIBS = $(MGHOME)\lib\libmglib.lib 
+ 
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES)
+LINK = $(CC) $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+ 
+o = .obj
+e = .exe
+
+ 
+HEADERS = \
+  arithcode.h     codeoffsets.h   marklist.h      sortmarks.h \
+  basictypes.h    codesyms.h      match.h         utils.h     \
+  bilevel.h       extractor.h     pbmtools.h      model.h
+
+
+SOURCES = \
+  arithcode.c     extractor.c     mgbilevel.c     mgticdump.c     utils.c \
+  bilevel.c       felics.c        mgfelics.c      mgticprune.c    mgticstat.c\
+  codeoffsets.c   marklist.c      mgtic.c         pbmtools.c      lstevent.c \
+  codesyms.c      match.c         mgticbuild.c    sortmarks.c     ppm.c  
+
+
+
+MISC          = Makefile.in 
+
+ALLFILES      =	$(SOURCES) $(HEADERS) $(MISC) $(MAN)
+
+DISTFILES     = $(ALLFILES) 
+
+EXEC          = mgfelics$e mgbilevel$e mgtic$e mgticbuild$e mgticprune$e mgticstat$e \
+                mgticdump$e
+
+MAN           = mgfelics.1 mgbilevel.1 mgtic.1 mgticbuild.1  mgticprune.1 \
+                mgticstat.1 mgticdump.1
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+all: $(EXEC)
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+FELICS_OBJS = felics$o mgfelics$o utils$o
+
+mgfelics$e: $(FELICS_OBJS)
+	$(LINK) $(FELICS_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+BILEVEL_OBJS = \
+	arithcode$o bilevel$o extractor$o marklist$o mgbilevel$o \
+	pbmtools$o utils$o
+            
+mgbilevel$e: $(BILEVEL_OBJS)
+	$(LINK) $(BILEVEL_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+TIC_OBJS = \
+	mgtic$o extractor$o marklist$o pbmtools$o utils$o match$o \
+	sortmarks$o codesyms$o ppm$o lstevent$o arithcode$o codeoffsets$o bilevel$o
+
+mgtic$e: $(TIC_OBJS) 
+	$(LINK) $(TIC_OBJS) $(LIBS) 
+
+TICBUILD_OBJS = mgticbuild$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticbuild$e: $(TICBUILD_OBJS)
+	$(LINK) $(TICBUILD_OBJS) $(LIBS)
+ 
+TICPRUNE_OBJS = \
+	mgticprune$o extractor$o marklist$o pbmtools$o \
+	utils$o match$o
+
+mgticprune$e: $(TICPRUNE_OBJS)
+	$(LINK) $(TICPRUNE_OBJS) $(LIBS)
+ 
+TICSTAT_OBJS = mgticstat$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticstat$e: $(TICSTAT_OBJS)
+	$(LINK) $(TICSTAT_OBJS) $(LIBS) 
+ 
+TICDUMP = mgticdump$o extractor$o marklist$o pbmtools$o utils$o
+
+mgticdump$e: $(TICDUMP)
+	$(LINK) $(TICDUMP) $(LIBS) 
+ 
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+#ansi2knr: ansi2knr.o
+#	$(LINK) ansi2knr.o $(LIBS)
+#
+install: 
+uninstall: 
+
+mostlyclean:
+	if exist *$o del *$o
+	if exist  _*.c del  _*.c
+	if exist _*.o  del  _*.o 
+	if exist *._c  del *._c
+	if exist *._o  del *._o
+	if exist core  del  core
+	if exist core.* del  core.*
+ 
+clean: mostlyclean
+	if exist *$e del *$e
+ 
+distclean: clean
+	del ansi2knr
+	 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+ 
+#dist: $(DISTFILES)
+#	for file in $(DISTFILES); do \
+#	  ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+#	  || cp -p $(srcdir)/$$file $(distdir); \
+#	done
+ 
+#Makefile: Makefile.in ../../config.status
+#	cd ../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ 
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+#.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/Makefile.in	(revision 16561)
@@ -0,0 +1,151 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the MG system
+# Copyright (C) 1994  Neil Sharman
+#
+# 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.
+#
+#       @(#)Makefile.in	1.10 22 Mar 1994
+#
+###########################################################################
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+subdir = src/scripts
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+ 
+DEFS = @DEFS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+TOUCH = echo timestamp >
+ 
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+ 
+SHFILES       =	mgbuild.sh mgbuild_4.sh mg_get.sh xmg.sh  mg_get_merge.sh mgmerge.sh
+
+MISC          = Makefile.in 
+
+ALLFILES      =	$(SHFILES) $(MISC) $(MAN)
+
+DISTFILES     = $(ALLFILES) 
+
+EXEC          = mgbuild mg_get xmg mg_get_merge mgmerge mgbuild_4
+
+MAN           = mgbuild.1 mgbuild_4.1 mg_get.1 xmg.1 mg_get_merge.1 mgmerge.1
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+all:	$(EXEC)
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+mgbuild : mgbuild.sh
+	@rm -f $@
+	cat mgbuild.sh >$@
+	chmod +x $@
+
+mgbuild_4 : mgbuild_4.sh
+	@rm -f $@
+	cat mgbuild_4.sh >$@
+	chmod +x $@
+
+mg_get : mg_get.sh
+	@rm -f $@
+	cat mg_get.sh >$@
+	chmod +x $@
+
+mgmerge : mgmerge.sh
+	@rm -f $@
+	cat mgmerge.sh >$@
+	chmod +x $@
+
+mg_get_merge : mg_get_merge.sh
+	@rm -f $@
+	cat mg_get_merge.sh >$@
+	chmod +x $@
+
+xmg : xmg.sh
+	@rm -f $@
+	cat xmg.sh >$@
+	chmod +x $@
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+install: execinstall #maninstall
+uninstall: execuninstall manuninstall
+
+execinstall: $(EXEC)
+	for name in $(EXEC); do \
+	  $(INSTALL_PROGRAM) $$name \
+	  $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+execuninstall:
+	for name in $(EXEC); do \
+	 rm -f $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+maninstall: $(MAN)
+	$(top_srcdir)/mkinstalldirs $(mandir); \
+	for f in $(MAN); do \
+	  $(INSTALL_DATA) $$f $(mandir)/$$f ;\
+	done
+
+manuninstall: $(MAN)
+	for f in $(MAN); do \
+	  rm -f $(mandir)/$$f ;\
+	done
+
+mostlyclean:
+ 
+clean: mostlyclean
+	rm -f $(EXEC)
+ 
+distclean: clean
+	rm -f ansi2knr
+	rm -f Makefile
+ 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+ 
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+	  || cp -p $(srcdir)/$$file $(distdir); \
+	done
+
+# screws up distclean (jrm21) 
+#Makefile: Makefile.in ../../../../config.status
+#	cd ../../../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ 
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.1	(revision 16561)
@@ -0,0 +1,154 @@
+.TH mg_get 1 "31 January 1994"
+.SH NAME
+mg_get \- output source texts for processing
+.SH SYNOPSIS
+ 
+.B mg_get
+.I collection-name
+.RB [ \-init | \-i | \-text\fP|\fB\-t\fP|\fB\-cleanup\fP|\fB\-c\fP]
+.SH DESCRIPTION
+.LP
+This program is the default one used by mgbuild to generate the source text for the MG system. Any program may be used to generate the source text for mgbuild as long as it confirms to the interface specified here. 
+.SH OPTIONS
+.LP
+The
+.I collection\-name
+must appear before any other option. Only the first option has any significance. If no option is specified
+.B \-text
+is assumed.
+.RS
+.TP
+.BR \-init " and " \-i
+The program is called once with this flag at the start of building a collection.
+.TP
+.BR \-text " and " \-t
+The program is called with this flag multiple times during the building of a collection. The program outputs (on stdout) the text of the collection. \*(lq\fBDocuments\fP\*(rq within the collection are separated with by ctrl-B's (ASCII code 2). \*(lq\fBParagraphs\fP\*(rq within the collection are separated with ctrl-C's (ASCII code 3). A collection need not contain paragraphs unless a level 3 inverted file is being generated (see 
+.B mgbuild 
+and 
+.BR mg_passes ). 
+.TP
+.BR \-cleanup " and " \-c
+The program is called once with this flag at the completion of building a collection.
+.SH ENVIRONMENT
+.TP
+.SB MGDATA
+If this environment variable exists then its value is used a the default 
+directory where the mg collection files are. If this variable does not exist
+then the directory \*(lq\fB.\fP\*(rq is used by default. The command line 
+option 
+.BI \-d " directory"
+overrides the directory in 
+.BR MGDATA .
+.TP
+.SB MG_GETRC
+This environment variable specifies where the file containing the users 
+mg source configurations is. If not set, mg_get uses a default of 
+.B \*\~/.mg_getrc.
+
+This file contains TAB delimited lines of the form
+.RS 
+.RS 
+.I CollectionName
+.I CollectionType
+.I files or directory
+.RE
+
+.I CollectionName
+is the name of the collection supplied to mg_get.
+
+.I CollectionType
+specifies how mg_get should process the
+named files and directories and are descibed below.
+
+.I files or directory
+is either a list of files separted by blanks or a single directory.
+Some of the
+.I CollectionTypes
+deal with files and others with just a single directory.
+Any files used ending with .gz or .Z are decompressed with gzip before
+processing. References to '~' expands to the users HOME directory.
+
+.I CollectionType
+is one of
+.RS
+.TP
+.BR PARA
+For text based documents. The list of files specified are treated as
+a series of paragraphs separated by blank lines. Each paragraph becomes
+a seperate document on the indexed collection.
+.TP
+.BR MAIL
+for mail files. The list of files specified are treated as UNIX mail
+files separated by lines starting with 'From'. Each mail message becomes
+a seperate document on the indexed collection. As a extra feature any
+embedded tarmail encoded contents (enclosed by a 'xbtoa Begin' and 'xbtoa End'
+pair are removed.
+.TP
+.BR DIR
+(and DIR2)
+For a single directory of files. Each file in the directory (and in any of
+it's subdirectories) are treated as a single document. With
+.I DIR
+the pathname of the file is prefixed to the contents of each file as an
+extra line while this is not done for
+.I DIR2
+collections.
+.TP
+.BR BIB
+for biblography files. The list of files specified are treated as
+a series of biblography files (eg BIBTEX or TROFF) separated by lines
+starting with '@'. Each reference  becomes a seperate document on the indexed
+collection.
+.TP
+.BR TXTIMG
+for integrated collections of text and images. The single directory should
+contain files that have the same prefix if they are related. For example,
+monaLisa.pgm might be a gray-level image, and monaLisa.txt would be a textual
+file describing the image. The suffixes recognised are:
+.BR   .txt
+for  ascii text
+.BR  .ptm
+for  scanned text stored as a bilevel image
+.BR  .pbm
+for a black and white image (typically a line drawing)
+.BR  .pgm
+for a gray-scale image
+.BR In addition, if no corresponding ascii text file is found for
+a .pbm or .pgm file, then one is created with suffix .tmp.txt,
+and it stores the name of the image file (in principle it could
+store the OCR of a .txt.pbm file). At present the .tmp.txt files
+are deleted by the '-cleanup' option.
+
+.SH "EXAMPLE"
+
+An example ~/.mg_getrc file might look like:
+   alice   PARA    /users/alice13a.txt.Z
+   mail95  MAIL    ~/MAIL/1995/*
+   doc     DIR     ~/documents
+   bibs    BIB     ~/etc/REFS/*.bib ~/etc/REFS/CC/*.bib
+   letters DIR     ~/LETTERS
+   davinci TXTIMG  ~/images/davinci
+
+.SH "SEE ALSO"
+.LP
+.BR mg_compression_dict (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_make_fast_dict (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1)
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get.sh	(revision 16561)
@@ -0,0 +1,447 @@
+#! /bin/csh -f
+###########################################################################
+#
+# mg_get.sh -- script to get text for mgbuild
+# Copyright (C) 1994  Tim Bell
+# Changed to allow it to be driven from a file ~/.mg_getrc
+# by Bruce McKenzie, Oct 1994
+#
+# 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.
+#
+#       @(#)mg_get.sh	1.11 25 Mar 1994
+#
+###########################################################################
+#
+# "mg_get" formats the text for mgbuild by collecting the appropriate
+#   documents, and outputing them with control-B after each document.
+#   The name of the text must be supplied as an argument.
+#   mg_get is called with -i (init) first, then with -t (text) each time the 
+#   text is needed, then with -c (cleanup).
+#   e.g. mg_get alice -t
+#
+# The file ~/.mg_get contains lines of the form:
+#     name	TYPE	files and directories
+# where name is the name supplied to mg_get, TYPE is one of
+# PARA, MAIL, DIR, DIR2, BIB or TXTIMG and says how mg_get should process the
+# named files and directories
+#
+###########################################################################
+
+if ($?MG_GETRC) then
+  set mg_getrc = $MG_GETRC
+else
+  set mg_getrc = ~/.mg_getrc
+endif
+
+# Added by [TS:May/95]
+# if we don't have an mg_getrc file in existence then create a default one
+if !(-e $mg_getrc) then 
+  echo "Can not find a .mg_getrc, so creating a default one: $mg_getrc"
+
+  if ($?MGSAMPLE) then
+    set SampleData = $MGSAMPLE
+  else
+    set SampleData = ./SampleData
+  endif
+
+  cat > $mg_getrc << END
+alice	PARA	$SampleData/alice13a.txt.Z
+davinci	TXTIMG	$SampleData/davinci
+mailfiles	MAIL	~/mbox ~/.sentmail
+allfiles	DIR	~/Mail
+mods	LINESEP	$SampleData/MODIFICATIONS
+bible	PLAIN	$SampleData/bible/genesis.txt.Z $SampleData/bible/revelation.txt.Z
+END
+endif
+
+if (! $?MGIMAGEVIEWER) then
+  setenv MGIMAGEVIEWER "xv -"
+endif
+
+set rm = /bin/rm
+
+switch ($#argv)
+  case 1:
+  set flag = '-text'
+  breaksw
+
+  case 2:
+  set flag = $2
+  switch ($flag)
+     case '-i':
+	set flag = '-init'
+	breaksw
+     case '-t':
+	set flag = '-text'
+	breaksw
+     case '-c':
+	set flag = '-cleanup'
+	breaksw
+     case '-init':
+     case '-text':
+     case '-cleanup':
+     breaksw
+     default:
+     echo 'Usage: mg_get <document> [-i | -t | -c]'
+     exit(1)
+     endsw
+  breaksw
+
+  default:
+  echo 'Usage: get <document> [-i | -t | -c]'
+  exit(1)
+  endsw
+
+set COLLECTION = $1
+set TYPE  = `grep \^$1 $mg_getrc | cut -f2`
+set FILES = `grep \^$1 $mg_getrc | cut -f3`
+
+set bindir = $0
+set bindir = $bindir:h
+
+switch ($TYPE)
+  case PLAIN:
+# Just outputs the given text, assumes ^Bs already inserted 
+  switch ($flag)
+     case '-init':
+     breaksw
+
+     case '-text':
+      foreach f ($FILES)
+	if ($f:e == 'Z') then
+	  set decoder=zcat
+	else if ($f:e == 'gz') then
+	  set decoder=gunzip
+	else
+	  set decoder=cat
+	endif
+        $decoder < $f
+      end
+     breaksw #-text
+
+     case '-cleanup':
+     breaksw
+  endsw #flag
+  breaksw
+
+  case LINESEP:
+# Assumes that the documents are separated by a line of =====
+  switch ($flag)
+     case '-init':
+     breaksw
+
+     case '-text':
+      foreach f ($FILES)
+	if ($f:e == 'Z') then
+	  set decoder=zcat
+	else if ($f:e == 'gz') then
+	  set decoder=gunzip
+	else
+	  set decoder=cat
+	endif
+        $decoder < $f |  sed -e '/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/s/.*//' < $f
+      end
+     breaksw #-text
+
+     case '-cleanup':
+     breaksw
+  endsw #flag
+  breaksw
+
+  case PARA:
+# Splits the file at blank lines, which corresponds to paragraphs.
+  switch ($flag)
+     case '-init':
+     breaksw
+
+     case '-text':
+      foreach f ($FILES)
+	if ($f:e == 'Z') then
+	  set decoder=zcat
+	else if ($f:e == 'gz') then
+	  set decoder=gunzip
+	else
+	  set decoder=cat
+	endif
+      $decoder <$f |awk '   /^ *$/ {if (b!=1) printf "";b=1} \
+		\!/^ *$/ {print;b=0} \
+		 END {printf ""}'
+       end #foreach
+     breaksw #-text
+
+     case '-cleanup':
+     breaksw
+  endsw #flag
+  breaksw
+
+  case BIB:
+# Takes a list of files that contain bibliographies, and splits them up 
+#   by putting ^B after each reference. Assumes that each reference
+#   begins with a line '^@'.
+
+  switch ($flag)
+    case '-init':
+    breaksw
+ 
+    case '-text':
+      foreach f ($FILES)
+	if ($f:e == 'Z') then
+	  set decoder=zcat
+	else if ($f:e == 'gz') then
+	  set decoder=gunzip
+	else
+	  set decoder=cat
+	endif
+      $decoder <$f |awk '/^@/&&NR!=1{printf ""} {print $0} END{print ""}'
+      end # foreach
+    breaksw #-text
+ 
+    case '-cleanup':
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #BIB
+
+  case MAIL:
+# Takes a list of files that contain mail, and splits them up 
+#   by putting ^B after each message. Assumes that each message
+#   begins with a line '^From '.
+
+  switch ($flag)
+    case '-init':
+    breaksw
+ 
+    case '-text':
+      foreach f ($FILES)
+	if ($f:e == 'Z') then
+	  set decoder=zcat
+	else if ($f:e == 'gz') then
+	  set decoder=gunzip
+	else
+	  set decoder=cat
+	endif
+      $decoder <$f| awk '/xbtoa Begin/,/xbtoa End/ {next} /^From /&&NR!=1{printf ""} {print $0} END{print ""}'
+      end # foreach
+    breaksw #-text
+ 
+    case '-cleanup':
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #MAIL
+
+  case DIR:
+# Recursively concatenates every file in every subdirectory of the given
+#   directory.
+
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text'
+      find $FILES -type f -name '*.gz' -exec echo '<' {} '>' \;		\
+			 -exec gzcat {} \; -exec echo -n '' \;
+      find $FILES -type f -name '*.Z' -exec echo '<' {} '>' \;		\
+			 -exec zcat {} \; -exec echo -n '' \;
+#     find $FILES -type f -not -regex '.*\.\(gz\|Z\)' -exec echo '<' {} \
+#			'>' \; -exec cat {} \; -exec echo -n '' \;
+      find $FILES -type f \! \( -name '*.gz' -o -name '*.Z' \) \
+			-exec echo '<' {} '>' \; \
+			-exec cat {} \; -exec echo -n '' \;
+
+    breaksw
+
+    case '-cleanup':
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #DIR
+
+
+
+  case DIR2:
+# Recursively concatenates every file in every subdirectory of the given
+#   directory. Does not include filename
+
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text'
+      find $FILES -type f -name '*.gz'  		\
+			 -exec gzcat {} \; -exec echo -n '' \;
+      find $FILES -type f -name '*.Z' 	 		\
+			 -exec zcat {} \; -exec echo -n '' \;
+#     find $FILES -type f -not -regex '.*\.\(gz\|Z\)'	\
+#			-exec cat {} \; -exec echo -n '' \;
+      find $FILES -type f \! \( -name '*.gz' -o -name '*.Z' \) \
+			-exec cat {} \; -exec echo -n '' \;
+    breaksw
+
+    case '-cleanup':
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #DIR2
+
+
+
+
+  case TXTIMG:
+# compress and index the collection of text and images
+# (this code is suitable for all sorts of integrated collections
+#  of text and images)
+# Files that are related have the same prefix. For example,
+#  monaLisa.pgm might be a gray-level image, and
+#  monaLisa.txt would be a textual file describing the image.
+# The suffixes recognised are:
+#    .txt	ascii text
+#    .ptm	scanned text stored as a bilevel image
+#    .pbm	a black and white image (typically a line drawing)
+#    .pgm	a gray-scale image
+# In addition, if no corresponding ascii text file is found for
+#  a .pbm or .pgm file, then one is created with suffix .tmp.txt,
+#  and it stores the name of the image file (in principle it could
+#  store the OCR of a .txt.pbm file). At present the .tmp.txt files
+#  are deleted by the '-cleanup' option.
+  
+#### the next two 'set' statements define which directory the files
+#    come from, and where they will be stored. This is the only
+#    part of this code that is specific to the 'davinci' collection
+  set sourceDir = $FILES
+  set targetDir = $MGDATA/$COLLECTION
+
+  switch ($flag)
+    case '-init':
+      mkdir $targetDir >&/dev/null	   # create the directory to store
+					   # compressed images in
+      $rm -rf $targetDir/* >&/dev/null     # in case it already existed
+    
+      # take care if no match to foreach statements
+      set nonomatch
+
+      # process all pbm (black and white) images of text
+      foreach f ($sourceDir/*.ptm)
+        if ($f == "$sourceDir/*.ptm") then
+                break
+        endif
+	set base = $f:t
+	
+        $bindir/mgticbuild $targetDir/$base:r.ticlib.$$ $f
+        $bindir/mgticprune $targetDir/$base:r.ticlib.$$
+
+	$bindir/mgtic -L -e $targetDir/$base:r.ticlib.$$ $f >$targetDir/$base:r.tic
+	$rm $targetDir/$base:r.ticlib.$$
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do OCR here to get its contents
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '#######' $f > $r.tmp
+	endif
+      end
+      # process all pgm (gray scale) images
+      foreach f ($sourceDir/*.pgm)
+        if ($f == "$sourceDir/*.pgm") then
+                break
+        endif
+	set base = $f:t
+	$bindir/mgfelics -e $f >$targetDir/$base:r.flx
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do image recognition here to create one(!)
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '#######' $f > $r.tmp
+	endif
+      end
+      # process all pbm (black and white) images
+      foreach f ($sourceDir/*.pbm)
+        if ($f == "$sourceDir/*.pbm") then
+                break
+        endif
+	set base = $f:t
+	$bindir/mgbilevel -e $f >$targetDir/$base:r.blv
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do drawing recognition here to create one(!)
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '####### No corresponding text file available for this image' >$r.tmp
+	    echo 'Original image file name was' $f >> $r.tmp
+	endif
+      end
+      unset nonomatch
+    breaksw #-init
+
+    case '-text'
+      # take care if no match to foreach statements
+      set nonomatch
+
+      #output each text file and tmp.txt, with names of other associated files
+
+      foreach f ($sourceDir/*.txt $sourceDir/*.tmp)
+        if ($f == "$sourceDir/*.txt") then
+           continue
+        endif
+        if ($f == "$sourceDir/*.tmp") then
+           continue
+        endif
+	set r = $f:r	#root name of file
+	set base = $r:t
+        set first_image = 1 # set so that I print out image header once
+                            # also signifies if header printed out at all
+
+	foreach d ($targetDir/$base.*)
+          if ($d == "$targetDir/$base.*") then
+                break
+          endif
+
+          if ($first_image) then
+            set first_image = 0
+            echo '::::::::::'  #separate the image information from the text
+            echo 'Image(s) available:'
+          endif
+
+	  switch ($d:e)	# work out decoding method from suffix
+            case 'tic':
+              echo 'MGDATA (mgtic -d ' $d '  | '$MGIMAGEVIEWER') '
+            breaksw
+            case 'flx':
+              echo 'MGDATA (mgfelics -d ' $d '  | '$MGIMAGEVIEWER')'
+            breaksw
+            case 'blv':
+              echo 'MGDATA (mgbilevel -d ' $d '  | '$MGIMAGEVIEWER')'
+	    breaksw
+	  endsw # $d:e (suffix)
+
+	end #foreach d (associated file)
+
+        if (! $first_image) then
+	  echo '::::::::::'  #separate the image information from the text
+        endif
+
+	cat $f  # output the text associated with the images
+	echo -n ''
+      end #foreach f (text file)
+      unset nonomatch
+    breaksw #-text
+
+    case '-cleanup':
+      #remove temporary text files
+      $rm $sourceDir/*.tmp
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #IMGTXT
+
+
+  default:
+    echo 'Sorry, I do not know how to get' $1
+    exit 1	
+endsw
+exit 0
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_get_merge 1 \*(Dt CITRI
+.SH NAME
+mg_get_merge \- output source texts for processing
+.SH SYNOPSIS
+.B mg_get_merge
+.I collection-name
+.if n .ti +9n
+[
+.BR \-init " |"
+.BR \-i " |"
+.BR \-text " |"
+.BR \-t " |"
+.BR \-cleanup " |"
+.B  \-c
+]
+.SH DESCRIPTION
+This program is the default one used by
+.BR mgmerge (1)
+to generate the source text for new documents to appended to
+an
+.BR mg (1)
+system database.
+Its function is exactly the same as
+.BR mg_get (1)
+except that it generates the 
+.I new 
+documents to be added to an
+.BR mg (1)
+system database that has already been built using
+.BR mgbuild(1).
+Any program may be used to generate the source
+text for
+.BR mgmerge (1),
+as long as it confirms to the interface specified
+here.
+.SH OPTIONS
+The
+.I collection-name
+must appear before any other option.  Only the first option has any
+significance.  If no option is specified,
+.B \-text
+is assumed.
+.TP "\w'\fB\-cleanup\fP and \fB\-c\fP'u+2n"
+.BR \-init " and " \-i
+The program is called once with this flag at the start of merging a
+collection.
+.TP
+.BR \-text " and " \-t
+The program is called with this flag multiple times during the
+merging of a collection.  The program outputs (on
+.IR stdout )
+the text of the collection.
+.I Documents
+within the collection are separated by ctrl-B's (ASCII code 2).
+.TP
+.BR \-cleanup " and " \-c
+The program is called once with this flag at the completion of
+merging a collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_get (1),
+.BR mgbuild (1),
+.BR mgmerge (1),
+.BR mg_text_merge (1),
+.BR mg_invf_merge (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mg_get_merge.sh	(revision 16561)
@@ -0,0 +1,313 @@
+#! /bin/csh -f
+###########################################################################
+#
+# mg_get_merge.sh -- script to get text for mgbuild
+# Copyright (C) 1994  Tim Bell, Shane Hudson (mods. for merging)
+#
+# 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.
+#
+#
+###########################################################################
+#
+# "mg_get_merge" formats the text for mgbuild by collecting the appropriate
+#   documents, and outputing them with control-B after each document.
+#   The name of the text must be supplied as an argument.
+#   mg_get is called with -i (init) first, then with -t (text) each time the 
+#   text is needed, then with -c (cleanup).
+#   e.g. mg_get alice -t
+#
+###########################################################################
+
+
+
+# The directory containing the data files is stored in the environment
+#   variable MGSAMPLE
+
+if ($?MGSAMPLE) then
+  set SampleData = $MGSAMPLE
+else
+  set SampleData = ./SampleData
+endif
+
+if (! $?MGIMAGEVIEWER) then
+  setenv MGIMAGEVIEWER "xv -"
+endif
+
+set rm = /bin/rm
+
+switch ($#argv)
+  case 1:
+  set flag = '-text'
+  breaksw
+
+  case 2:
+  set flag = $2
+  switch ($flag)
+     case '-i':
+	set flag = '-init'
+	breaksw
+     case '-t':
+	set flag = '-text'
+	breaksw
+     case '-c':
+	set flag = '-cleanup'
+	breaksw
+     case '-init':
+     case '-text':
+     case '-cleanup':
+     breaksw
+     default:
+     echo 'Usage: get <document> [-i | -t | -c]'
+     exit(1)
+     endsw
+  breaksw
+
+  default:
+  echo 'Usage: get <document> [-i | -t | -c]'
+  exit(1)
+  endsw
+
+set bindir = $0
+set bindir = $bindir:h
+
+switch ($1)
+  case alice:
+  switch ($flag)
+     case '-init':
+     breaksw
+
+     case '-text':
+	echo "This is a test document 1 2 3 4 5 6 7 8 9 "
+	echo ""
+	echo "A B C"
+     breaksw
+
+     case '-cleanup':
+     breaksw
+  endsw #flag
+  breaksw
+
+  case bible:
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text':
+       # use uncompressed file if it exists
+       # revelation.txt already contains '^B' characters between verses
+       if (-e $SampleData/bible/revelation.txt) then
+          cat $SampleData/bible/revelation.txt
+       else
+          uncompress <$SampleData/bible/revelation.txt.Z
+    breaksw #-text
+
+    case '-cleanup':
+    breaksw #-cleanup
+
+  endsw #flag
+  breaksw #bible
+
+
+
+  case mailfiles:
+# Takes a list of files that contain mail, and splits them up 
+#   by putting ^B after each message. Assumes that each message
+#   begins with a line '^From '.
+# If messages are already in individual files, use "get allfiles"
+#   rather than this one.
+# The EXTRA documents added are in files in the directory mailextra
+#### The variable 'mailfiles' contains a list of files to be split up
+  set mailfiles = '~/mbox ~/.sentmail'
+  set mailextra = '~/extra/*'
+
+  switch ($flag)
+    case '-init':
+    breaksw
+
+    case '-text':
+      foreach f ($mailextra)
+	awk '/^From /&&NR!=1{print ""} {print $0} END{print ""}' $f
+      end # foreach
+    breaksw #-text
+
+    case '-cleanup':
+    breaksw #-cleanup
+
+  endsw #flag
+  breaksw #mailfiles
+
+
+
+  case allfiles:
+# Recursively concatenates every file in every subdirectory of the given
+#   directory.
+
+# The variable 'directory' on the next line contains the directory to be used
+  set directory = ~/Mail
+
+# The date of the file ".mgbuildtime" in 'directory' is the last
+# build or merge. At the start of the merge ("-init" argument) the
+# file ".mgmergetime" is touched. Only files newer than ".mgbuildtime"
+# but older than ".mgmergetime" are output by mg_get_merge
+
+  switch ($flag)
+    case '-init':
+        touch $directory/.mgmergetime
+    breaksw
+
+    case '-text'
+      find $directory -type f -newer $directory/.mgbuildtime \! -newer $directory/.mgmergetime \! -name '.*' -exec echo '<' {} '>' \; -exec cat {} \; -exec echo -n '' \;
+    breaksw
+
+    case '-cleanup':
+        touch $directory/.mgmergetime
+        touch $directory/.mgbuildtime
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #allfiles
+
+
+
+
+  case davinci:
+# compress and index the DaVinci collection of text and images
+# (this code is suitable for all sorts of integrated collections
+#  of text and images)
+# It assumes that all files are in one directory ($sourceDir).
+# Files that are related have the same prefix. For example,
+#  monaLisa.img.pgm might be a gray-level image, and
+#  monaLisa.txt.txt would be a textual file describing the image.
+# The suffixes recognised are:
+#    .txt.txt	ascii text
+#    .txt.pbm	scanned text stored as a bilevel image
+#    .img.pbm	a black and white image (typically a line drawing)
+#    .img.pgm	a gray-scale image
+# In addition, if no corresponding ascii text file is found for
+#  a .pbm or .pgm file, then one is created with suffix .tmp.txt,
+#  and it stores the name of the image file (in principle it could
+#  store the OCR of a .txt.pbm file). At present the .tmp.txt files
+#  are deleted by the '-cleanup' option.
+  
+#### the next two 'set' statements define which directory the files
+#    come from, and where they will be stored. This is the only
+#    part of this code that is specific to the 'davinci' collection
+  set sourceDir = $SampleData/davinci
+  set targetDir = $MGDATA/davinci
+
+  switch ($flag)
+    case '-init':
+      mkdir $targetDir >&/dev/null	   #create the directory to store
+					   # compressed images in
+      $rm -rf $targetDir/* >&/dev/null #in case it already existed
+    
+      # process all pbm (black and white) images of text
+      foreach f ($sourceDir/*.ptm)
+	set base = $f:t
+	
+        $bindir/mgticbuild $targetDir/$base:r.ticlib.$$ $f
+        $bindir/mgticprune $targetDir/$base:r.ticlib.$$
+
+	$bindir/mgtic -L -e $targetDir/$base:r.ticlib.$$ $f >$targetDir/$base:r.tic
+	$rm $targetDir/$base:r.ticlib.$$
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do OCR here to get its contents
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '#######' $f > $r.tmp
+	endif
+      end
+      # process all pgm (gray scale) images
+      foreach f ($sourceDir/*.pgm)
+	set base = $f:t
+	$bindir/mgfelics -e $f >$targetDir/$base:r.flx
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do image recognition here to create one(!)
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '#######' $f > $r.tmp
+	endif
+      end
+      # process all pbm (black and white) images
+      foreach f ($sourceDir/*.pbm)
+	set base = $f:t
+	$bindir/mgbilevel -e $f >$targetDir/$base:r.blv
+	set r = $f:r	#root name of file
+	if (! (-e $r.txt || -e $r.tmp)) then
+	# creates a file; could do drawing recognition here to create one(!)
+	    echo "No corresponding txt file for" $f "- creating" $r.tmp
+	    echo '####### No corresponding text file available for this image' >$r.tmp
+	    echo 'Original image file name was' $f >> $r.tmp
+	endif
+      end
+    breaksw #-init
+
+    case '-text'
+      #output each text file and tmp.txt, with names of other associated files
+      foreach f ($sourceDir/*.txt $sourceDir/*.tmp)
+	set r = $f:r	#root name of file
+	set base = $r:t
+	set n = `/bin/ls $sourceDir/$base.* | wc -l`
+	if ($n != 1) then # more files than just the text one
+	 echo '::::::::::'  #separate the image information from the text
+	 echo 'Image(s) available:'
+	 foreach d ($targetDir/$base.*)
+	  switch ($d:e)	# work out decoding method from suffix
+            case 'tic':
+              echo 'MGDATA (mgtic -d ' $d '  | '$MGIMAGEVIEWER') '
+            breaksw
+            case 'flx':
+              echo 'MGDATA (mgfelics -d ' $d '  | '$MGIMAGEVIEWER')'
+            breaksw
+            case 'blv':
+              echo 'MGDATA (mgbilevel -d ' $d '  | '$MGIMAGEVIEWER')'
+	    breaksw
+	  endsw # $d:e (suffix)
+	 end #foreach d (associated file)
+	echo '::::::::::'  #separate the image information from the text
+	endif # if more than one file
+	cat $f  # output the text associated with the images
+	echo -n ''
+      end #foreach f (text file)
+    breaksw #-text
+
+    case '-cleanup':
+      #remove temporary text files
+      $rm $sourceDir/*.tmp
+    breaksw #-cleanup
+  endsw #flag
+  breaksw #davinci
+
+
+#---------------------------
+  case mods:
+# Splits the modifications file at the mod separator "--------"
+  switch ($flag)
+     case '-init':
+     breaksw
+ 
+     case '-text':
+       sed -e 's/^---.*//g' < ${SampleData}/MODIFICATIONS
+     breaksw
+ 
+     case '-cleanup':
+     breaksw
+  endsw #flag
+  breaksw #mods
+
+  default:
+    echo 'Sorry, I do not know how to get' $1
+    exit 1	
+endsw
+exit 0
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.1	(revision 16561)
@@ -0,0 +1,169 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbuild 1 \*(Dt CITRI
+.SH NAME
+mgbuild \- build an mg system database
+.SH SYNOPSIS
+.B mgbuild
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.BI \-d " mgdata dir"
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgbuild
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely build an
+.BR mg (1)
+system database ready for queries to be made
+by
+.BR mgquery.
+This program makes use of the
+.BR mg_get (1)
+script to obtain the text of the collection.
+.SH OPTIONS
+Options can occur in any order, but the collection name must be last.
+.TP "\w'\fIcollection-name\fP'u+2n"
+.BI \-c
+This specifies whether the
+.I get
+program is \*(lqcomplex\*(rq.  If a
+.I get
+program is \*(lqcomplex\*(rq, then it requires initialisation and
+cleanup with the
+.B \-i
+and
+.B \-c
+options.
+.TP
+.BI \-g " get"
+This specifies the program to use for getting the source text for the
+build.  If no
+.B \-g
+option is given, the default program
+.BR mg_get (1)
+is used.
+.TP
+.BI \-s " source"
+The
+.B mgbuild
+program consists of two parts.  The first part initializes variables
+to default values.  The second part uses these variables to control
+how the
+.BR mg (1)
+database is built.  This option specifies a program to execute between
+the first and second parts.  The details of what the variables are, and
+how they may be changed, are in comments in the
+.B mgbuild
+program.
+.TP
+.BI \-d " mgdata dir"
+Setting this option allows one to override the MGDATA environment variable.
+.TP
+.I collection-name
+This is the collection name, as required by the
+.BR mg_get (1)
+program.  It serves both as a
+.I case
+statement selector, and as the name of a subdirectory that holds the
+indexing files.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.chunk
+Inverted file chunk descriptor file.  When the inverted file is
+created, it is written in chunks that use no more than a set amount of
+memory.  This file describes those chunks.
+.TP
+.B *.invf.chunk.trans
+Word-occurrence-order to lexical-order translation file.  The
+.B *.invf.chunk
+file is written in word-occurrence order but is required by
+.B \-N2
+to be in lexical order.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for an order-preserving perfect hash function.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Text statistics.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild.sh	(revision 16561)
@@ -0,0 +1,339 @@
+#!/bin/csh -f
+###########################################################################
+#
+# mgbuild.sh -- Script used to build mg text collection.
+# Copyright (C) 1994  Neil Sharman
+#
+# 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.
+#
+#       @(#)mgbuild.sh	1.9 16 Mar 1994
+#
+###########################################################################
+#  mg.build
+# Creates a Full Text Retrieval database
+#
+#  @(#)mgbuild.sh	1.9 16 Mar 1994
+#
+
+set complex = ""
+
+# Parse the command line arguments
+while ($#argv >= 1)
+  if ("$1" == "-s") then
+    shift
+    if ($#argv >= 1) then
+      set source = $1
+      shift
+    endif
+  else if ("$1" == "-d") then
+    shift
+    if ($#argv >= 1) then
+      set mgdata = $1
+      shift
+    endif
+  else if ("$1" == "-g") then
+    shift
+    if ($#argv >= 1) then
+      set get = $1
+      shift
+    endif
+  else if ("$1" == "-c") then
+    set complex = "-text"
+    shift
+  else
+    if ($?text == "0") then
+      set text = $1
+    endif
+    shift
+  endif
+    
+end
+
+if ($?text == "0") then
+  set prog = $0
+  echo "USAGE:"
+  echo "     "$prog:t" [-s config-script] [-g get-program] [-d mgdata dir] [-c] source"
+  echo ""
+  echo " The config-script is only needed if a non-standard build is required."
+  echo " The get-program defaults to mg_get if not specified."
+  exit 1	
+endif
+
+set bindir = $0
+set bindir = $bindir:h
+
+# if $pipe == 1 then pipe in the source text using $get and $text otherwise
+# read the source text directly from the file names specified in $input_files
+set pipe = 1
+
+if ($?get == "0") then
+  set complex = "-text"
+  set get = $bindir/mg_get
+endif
+
+if ($?mgdata) then
+  setenv MGDATA $mgdata
+endif
+
+if (-e $MGDATA/${text}.chunks) then
+  set input_files = `cat $MGDATA/${text}.chunks`
+endif
+
+# Set the stemming method
+#   Bit 0 = case folding
+#   Bit 1 = S stemmer
+set stem_method = 3
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then build collection with full indexes to blocked file
+# overriding stem_method (stem_method will be set to 0)
+# Otherwise don't build with indexes
+set do_indexes = 0
+
+
+# If do_text == 1 then do the text component of building a mg database. 
+# Otherwise don't do the text component.
+set do_text = 1
+
+
+# If do_invf == 1 then do the inversion component of building a mg database. 
+# Otherwise don't do the inversion component.
+set do_invf = 1
+
+
+# If do_pass1 == 1 then do pass1 of building a mg database. 
+# Otherwise don't do pass1.
+set do_pass1 = 1
+
+# If do_pass2 == 1 then do pass2 of building a mg database. 
+# Otherwise don't do pass2.
+set do_pass2 = 1
+
+
+# $invf_mem specifies the amount of memory to use for the pass2 inversion
+set invf_mem = 32
+
+
+# $num_chunks specifies the number of interium chunks of inverted file that
+# may be written to disc before a merge into the invf file is done
+set num_chunks = 3
+
+# $invf_level specifies the level of the inverted file that will be generated.
+set invf_level = 2
+
+
+# If $strip_sgml == 1 then sgml tags are stripped from the inversion phase.
+# Otherwise sgml tags are kepted.
+set strip_sgml = 1
+
+
+# $trace specifies the interval between trace entries in Mb.
+# If this is not set no trace entries will be generated.
+set trace = 10
+
+
+# $weight_bits specifies the number of bits of precision bo be given to the 
+# approximate weights.
+set weight_bits = 6
+
+
+# $mcd specifies the commandline arguments for the mg_compression_dict program
+#### Note: Set -S so novel words can be encoded, as this will happen if
+#### mgmerge is used.
+set mcd = -S
+
+
+# Source the parameter file to modify parameters of the build.
+if ($?source) then
+  source ${source}
+endif
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then set stem_method = 0
+if ("$do_indexes" == "1") then
+  set stem_method = 0
+endif
+
+
+# Generate the collection name.
+set coll_name = ${text}
+if ("$invf_level" == "3") then
+  set coll_name = ${text}-p
+endif
+
+# Generate the directory for the collection.
+if (-e $MGDATA/${coll_name}) then
+else
+  mkdir $MGDATA/${coll_name}
+endif
+
+# Generate the base name for the collection.
+set bname = ${coll_name}/${coll_name}
+
+
+#build up the command lines for pass 1 and 2
+set pass1 = (-f ${bname} -${invf_level} -m ${invf_mem} -s ${stem_method})
+set pass2 = (-f ${bname} -${invf_level} -c ${num_chunks})
+
+if ($strip_sgml) then
+  set pass1 = (${pass1} -G)
+  set pass2 = (${pass2} -G)
+endif
+
+if ($?trace) then
+  set pass1 = (${pass1} -t ${trace})
+  set pass2 = (${pass2} -t ${trace})
+endif
+
+if ($do_text) then
+  set pass1 = (${pass1} -T1)
+  set pass2 = (${pass2} -T2)
+endif
+
+if ($do_invf) then
+  set pass1 = (${pass1} -I1)
+  set pass2 = (${pass2} -I2)
+endif
+  
+if ($?trace_name) then
+  set pass1 = (${pass1} -n ${trace_name})
+  set pass2 = (${pass2} -n ${trace_name})
+endif
+  
+if ($?comp_stats) then
+  set pass2 = (${pass2} -C ${comp_stats})
+endif
+
+echo "-----------------------------------"
+echo "`uname -n`, `date`"
+echo "${text} --> ${bname}"
+echo "-----------------------------------"
+if ($pipe) then
+  if ("$complex" != "") then
+    echo "$get $text -init"
+    $get $text -init
+    if ("$status" != "0") exit 1 
+    echo "-----------------------------------"
+  endif  
+endif
+
+if (${do_pass1}) then
+  if ($pipe) then
+    if ($?pass1filter) then
+      echo "$get $text $complex | $pass1filter | mg_passes ${pass1}"
+      $get $text $complex| $pass1filter | $bindir/mg_passes ${pass1}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass1}"
+      $get $text $complex| $bindir/mg_passes ${pass1}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass1} ${input_files}
+    $bindir/mg_passes ${pass1} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+
+  echo "mg_perf_hash_build -f ${bname}"
+  $bindir/mg_perf_hash_build -f ${bname}
+  if ("$status" != "0") exit 1 
+
+  echo "-----------------------------------"
+  echo "mg_compression_dict -f ${bname} ${mcd}"
+  $bindir/mg_compression_dict -f ${bname} ${mcd}
+  if ("$status" != "0") exit 1 
+  echo "-----------------------------------"
+endif
+
+if (${do_pass2}) then
+  if ($pipe) then
+    if ($?pass2filter) then
+      echo "$get $text $complex | $pass2filter | mg_passes ${pass2}"
+      $get $text $complex | $pass2filter | $bindir/mg_passes ${pass2}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass2}"
+      $get $text $complex | $bindir/mg_passes ${pass2}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass2} ${input_files}
+    $bindir/mg_passes ${pass2} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+endif
+
+echo "mg_weights_build -f ${bname} -b ${weight_bits}"
+$bindir/mg_weights_build -f ${bname} -b ${weight_bits}
+if ("$status" != "0") exit 1 
+
+echo "-----------------------------------"
+
+echo "mg_invf_dict -f ${bname} -b 4096"
+$bindir/mg_invf_dict -f ${bname} -b 4096
+if ("$status" != "0") exit 1 
+
+echo "-----------------------------------"
+
+# [RPAP - Jan 97: Stem Index Change]
+if ("$do_indexes" == "1") then
+  echo "mg_stem_idx -f ${bname} -b 4096 -s1"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s1
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${bname} -b 4096 -s2"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s2
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${bname} -b 4096 -s3"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s3
+  if ("$status" != "0") exit 1 
+
+  echo "-----------------------------------"
+endif
+
+echo "mgstat -f ${bname} -E"
+$bindir/mgstat -f ${bname} -E
+if ("$status" != "0") exit 1 
+
+if ($pipe) then
+  if ("$complex" != "") then
+    echo "-----------------------------------"
+    echo "$get $text -cleanup"
+    $get $text -cleanup
+    if ("$status" != "0") exit 1 
+  endif
+endif
+
+echo "-----------------------------------"
+echo "`uname -n`, `date`"
+echo "-----------------------------------"
+
+echo "-- The fast-loading compression dictionary has not been built."
+echo "-- If you wish to build it, execute the following command:"
+echo "-- "$bindir/mg_fast_comp_dict -f ${bname}
+
+echo "-----------------------------------"
+
+echo ""
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.1	(revision 16561)
@@ -0,0 +1,53 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgbuild_4 1 \*(Dt CITRI
+.SH NAME
+mgbuild_4 \- build an mg system database using 4 passes instead of 2
+.SH SYNOPSIS
+.B mgbuild_4
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.BI \-d " mgdata dir"
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgbuild_4
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely build an
+.BR mg (1)
+system database ready for queries to be made
+by
+.BR mgquery.
+This program makes use of the
+.BR mg_get (1)
+script to obtain the text of the collection.
+In 
+.BR mgbuild (1)
+the build is done by two passes over the input text. The first
+pass creates the compressed text dictionary and inverted file dictionary,
+the second pass creates the compressed text and inverted file. What 
+.BR mgbuild_4
+does is use a separate pass for each function. This will be slower but will
+not require as much RAM at one time. Therefore, if your build is failing
+due to running short of memory, then 
+.BR mgbuild_4
+is worth a try.
+.SH "SEE ALSO"
+.na
+.BR mgbuild (1)
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgbuild_4.sh	(revision 16561)
@@ -0,0 +1,435 @@
+#!/bin/csh -f
+###########################################################################
+#
+# mgbuild_4.sh -- Script used to build mg text collection in 4 passes.
+# Copyright (C) 1994  Neil Sharman; Modified by tes@kbs 1995
+#
+# 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.
+#
+#
+###########################################################################
+#
+# Does an mgbuild in 4 passes instead of 2 passes.
+# This is useful if one does not have as much RAM and a large collection.
+#
+
+set complex = ""
+
+# ************ parse the command line arguments ************
+
+while ($#argv >= 1)
+  if ("$1" == "-s" || "$1" == "--source") then
+    shift
+    if ($#argv >= 1) then
+      set source = $1
+      shift
+    endif
+  else if ("$1" == "-g" || "$1" == "--get") then
+    shift
+    if ($#argv >= 1) then
+      set get = $1
+      shift
+    endif
+  else if ("$1" == "-c" || "$1" == "--complex") then
+    set complex = "-text"
+    shift
+  else
+    if (! $?text ) then
+      set text = $1
+    endif
+    shift
+  endif
+    
+end
+
+if (! $?text ) then
+  set prog = $0
+  echo "USAGE:"
+  echo "     "$prog:t" [-s config-script] [-g get-program] [-c] source"
+  echo ""
+  echo " The config-script is only needed if a non-standard build is required."
+  echo " The get-program defaults to mg_get if not specified."
+  exit 1	
+endif
+
+set bindir = `which $0` # find which one we are executing
+set bindir = $bindir:h # use it as the bindir path
+
+# ************ set up shell variables ************
+# Override these by providing a script to source using the -s option
+
+# if $pipe == 1 then pipe in the source text using $get and $text otherwise
+# read the source text directly from the file names specified in $input_files
+set pipe = 1
+
+set get_options = ""
+
+
+if (! $?get ) then
+  set complex = "-text"
+  set get = $bindir/mg_get $get_options
+endif
+
+
+if (-e $MGDATA/${text}.chunks) then
+  set input_files = `cat $MGDATA/${text}.chunks`
+endif
+
+# Set the stemming method
+#   Bit 0 = case folding
+#   Bit 1 = S stemmer
+set stem_method = 3
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then build collection with full indexes to blocked file
+# overriding stem_method (stem_method will be set to 0)
+# Otherwise don't build with indexes
+set do_indexes = 0
+
+
+# whether to build weights or not
+set do_weights = 1
+
+# If do_pass1 == 1 then do pass1 of building a mg database. 
+# Otherwise don't do pass1.
+# Text 1st pass
+set do_pass1 = 1
+
+# If do_pass2 == 1 then do pass2 of building a mg database. 
+# Otherwise don't do pass2.
+# Text 2nd pass
+set do_pass2 = 1
+
+# If do_pass3 == 1 then do pass3 of building a mg database. 
+# Otherwise don't do pass3.
+# Invf 1st pass
+set do_pass3 = 1
+
+# If do_pass4 == 1 then do pass4 of building a mg database. 
+# Otherwise don't do pass4.
+# Invf 2nd pass
+set do_pass4 = 1
+
+# Buffer size used by mg_passes
+# Should be big enough to hold largest document
+# In Kilobytes
+set buf_size = 3072
+
+# $invf_mem specifies the amount of memory to use for the pass2 inversion
+set invf_mem = 5
+
+# $num_chunks specifies the number of interium chunks of inverted file that
+# may be written to disc before a merge into the invf file is done
+set num_chunks = 3
+
+# $invf_level specifies the level of the inverted file that will be generated.
+set invf_level = 2
+
+
+# If $strip_sgml == 1 then sgml tags are stripped from the inversion phase.
+# Otherwise sgml tags are kepted.
+set strip_sgml = 1
+
+
+# $trace specifies the interval between trace entries in Mb.
+# If this is not set no trace entries will be generated.
+set trace = 10
+
+
+# $weight_bits specifies the number of bits of precision bo be given to the 
+# approximate weights.
+set weight_bits = 6
+
+
+# $mcd specifies the commandline arguments for the mg_compression_dict program
+set mcd = -C
+
+
+# Source the parameter file to modify parameters of the build.
+if ($?source) then
+  source ${source}
+endif
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then set stem_method = 0
+if ("$do_indexes" == "1") then
+  set stem_method = 0
+endif
+
+
+# Generate the collection name.
+set coll_name = ${text}
+if ("$invf_level" == "3") then
+  set coll_name = ${text}-p
+endif
+
+# Generate the directory for the collection.
+if (-e $MGDATA/${coll_name}) then
+else
+  mkdir $MGDATA/${coll_name}
+endif
+
+# Generate the base name for the collection.
+set bname = ${coll_name}/${coll_name}
+
+# ************ set up pass parameters ************
+
+if ($do_pass1) then
+  set pass1_text = (-f ${bname} -T1)
+endif
+
+if ($do_pass2) then
+  set pass2_text = (-f ${bname} -T2)
+endif
+
+if ($do_pass3) then
+  set pass3_invf = (-f ${bname} -${invf_level} -m ${invf_mem} -s ${stem_method})
+  set pass3_invf = (${pass3_invf} -N1)
+endif
+
+if ($do_pass4) then
+  set pass4_invf = (-f ${bname} -${invf_level} -N2)
+endif
+
+if ($strip_sgml) then
+  set pass1_text = (${pass1_text} -G)
+  set pass2_text = (${pass2_text} -G)
+  set pass3_invf = (${pass3_invf} -G)
+  set pass4_invf = (${pass4_invf} -G)
+endif
+
+if ($?trace) then
+  set pass1_text = (${pass1_text} -t ${trace})
+  set pass2_text = (${pass2_text} -t ${trace})
+  set pass3_invf = (${pass3_invf} -t ${trace})
+  set pass4_invf = (${pass4_invf} -t ${trace})
+endif
+
+if ($?trace_name) then
+  set pass1_text = (${pass1_text} -n ${trace_name})
+  set pass2_text = (${pass2_text} -n ${trace_name})
+  set pass3_invf = (${pass3_invf} -n ${trace_name})
+  set pass4_invf = (${pass4_invf} -n ${trace_name})
+endif
+
+if ($buf_size) then
+  set pass1_text = (${pass1_text} -b ${buf_size})
+  set pass2_text = (${pass2_text} -b ${buf_size})
+  set pass3_invf = (${pass3_invf} -b ${buf_size})
+  set pass4_invf = (${pass4_invf} -b ${buf_size})
+endif
+  
+if ($?comp_stats) then
+  set pass2_text = (${pass2_text} -C ${comp_stats})
+endif
+
+echo "-----------------------------------"
+echo "`uname -n`, `date`"
+echo "${text} --> ${bname}"
+echo "-----------------------------------"
+
+# ************ init get ************
+
+if ($pipe) then
+  if ("$complex" != "") then
+    echo "$get $text -init"
+    $get $text -init
+    if ("$status" != "0") exit 1 
+    echo "-----------------------------------"
+  endif  
+endif
+
+# ************************************* T E X T **********************************
+
+# ************ pass 1_text ************
+
+if (${do_pass1}) then
+  if ($pipe) then
+    if ($?pass1filter) then
+      echo "$get $text $complex | $pass1filter | mg_passes ${pass1_text}"
+      $get $text $complex| $pass1filter | $bindir/mg_passes ${pass1_text}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass1_text}"
+      $get $text $complex| $bindir/mg_passes ${pass1_text}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass1_text} ${input_files}
+    $bindir/mg_passes ${pass1_text} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+endif
+
+# ************ compression dict  ************
+if ($do_pass2) then 
+  echo "mg_compression_dict -f ${bname} ${mcd}"
+  $bindir/mg_compression_dict -f ${bname} ${mcd}
+  if ("$status" != "0") exit 1 
+  echo "-----------------------------------"
+endif
+
+# ************ pass 2_text ************
+if (${do_pass2}) then
+  if ($pipe) then
+    if ($?pass2filter) then
+      echo "$get $text $complex | $pass2filter | mg_passes ${pass2_text}"
+      $get $text $complex| $pass2filter | $bindir/mg_passes ${pass2_text}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass2_text}"
+      $get $text $complex| $bindir/mg_passes ${pass2_text}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass2_text} ${input_files}
+    $bindir/mg_passes ${pass2_text} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+endif
+
+# ************************************* I N V F **********************************
+
+# ************ pass 3_invf ************
+if (${do_pass3}) then
+  if ($pipe) then
+    if ($?pass3filter) then
+      echo "$get $text $complex | $pass3filter | mg_passes ${pass3_invf}"
+      $get $text $complex| $pass3filter | $bindir/mg_passes ${pass3_invf}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass3_invf}"
+      $get $text $complex| $bindir/mg_passes ${pass3_invf}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass3_invf} ${input_files}
+    $bindir/mg_passes ${pass3_invf} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+endif
+
+# ************ perfect hash  ************
+if ($do_pass4) then
+  echo "mg_perf_hash_build -f ${bname}"
+  $bindir/mg_perf_hash_build -f ${bname}
+  if ("$status" != "0") exit 1 
+
+  echo "-----------------------------------"
+endif
+
+# ************ pass 4_invf ************
+if (${do_pass4}) then
+  if ($pipe) then
+    if ($?pass4filter) then
+      echo "$get $text $complex | $pass4filter | mg_passes ${pass4_invf}"
+      $get $text $complex| $pass4filter | $bindir/mg_passes ${pass4_invf}
+      if ("$status" != "0") exit 1 
+    else
+      echo "$get $text $complex | mg_passes ${pass4_invf}"
+      $get $text $complex| $bindir/mg_passes ${pass4_invf}
+      if ("$status" != "0") exit 1 
+    endif
+  else
+    echo mg_passes ${pass4_invf} ${input_files}
+    $bindir/mg_passes ${pass4_invf} ${input_files}
+    if ("$status" != "0") exit 1 
+  endif
+  echo "-----------------------------------"
+endif
+
+
+# ************ build invf dictionary ************
+
+if ($do_pass4) then
+  echo "mg_invf_dict -f ${bname} -b 4096"
+  $bindir/mg_invf_dict -f ${bname} -b 4096
+  if ("$status" != "0") exit 1 
+  echo "-----------------------------------"
+endif
+
+# ******** build indexes to stem dict *********
+
+# [RPAP - Jan 97: Stem Index Change]
+if ($do_pass4 && "$do_indexes" == "1") then
+  echo "mg_stem_idx -f ${bname} -b 4096 -s1"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s1
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${bname} -b 4096 -s2"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s2
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${bname} -b 4096 -s3"
+  $bindir/mg_stem_idx -f ${bname} -b 4096 -s3
+  if ("$status" != "0") exit 1 
+
+  echo "-----------------------------------"
+endif
+
+# ************ build weights ************
+
+if ($do_pass4 && $do_weights) then
+  echo "mg_weights_build -f ${bname} -b ${weight_bits} "
+  $bindir/mg_weights_build -f ${bname} -b ${weight_bits}
+  if ("$status" != "0") exit 1 
+  echo "-----------------------------------"
+endif
+
+# *******************************************************************************
+
+
+# ************ print out statistics ************
+if ($do_pass1 && $do_pass2 && $do_pass3 && $do_pass4) then
+  echo "mgstat -f ${bname} -E"
+  $bindir/mgstat -f ${bname} -E
+  if ("$status" != "0") exit 1 
+endif
+
+
+# ************ cleanup get ************
+
+if ($pipe) then
+  if ("$complex" != "") then
+    echo "-----------------------------------"
+    echo "$get $text -cleanup"
+    $get $text -cleanup
+    if ("$status" != "0") exit 1 
+  endif
+endif
+
+echo "-----------------------------------"
+echo "`uname -n`, `date`"
+echo "-----------------------------------"
+
+if ($do_pass2) then
+  echo "-- The fast-loading compression dictionary has not been built."
+  echo "-- If you wish to build it, execute the following command:"
+  echo "-- "$bindir/mg_fast_comp_dict -f ${bname}
+
+  echo "-----------------------------------"
+endif
+
+echo ""
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.1	(revision 16561)
@@ -0,0 +1,175 @@
+`.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgmerge 1 \*(Dt CITRI
+.SH NAME
+mgmerge \- update an mg system database with new documents
+.SH SYNOPSIS
+.B mgmerge
+[
+.B \-c
+]
+[
+.BI \-g " get"
+]
+[
+.BI \-s " source"
+]
+[
+.B \-S
+]
+[
+.B \-w
+]
+.I collection-name
+.SH DESCRIPTION
+.B mgmerge
+is a
+.B csh
+script that executes all the appropriate programs in the correct order
+to completely merge a current
+.BR mg (1)
+system database ready with some new documents, saving the need for a complete
+database rebuild with
+.B mgbuild.
+It does this by building a second
+.BR mg (1)
+system database from the new documents and then merging this database
+with the old one.
+This program makes use of the
+.BR mg_get_merge (1)
+script to obtain the text of the collection.
+.B mgmerge
+cannot edit or delete documents already in a database; only new documents
+can be added.
+.SH OPTIONS
+Options can occur in any order, but the collection name must be last.
+.TP "\w'\fIcollection-name\fP'u+2n"
+.BI \-c
+This specifies whether the
+.I get
+program is \*(lqcomplex\*(rq.  If a
+.I get
+program is \*(lqcomplex\*(rq, then it requires initialisation and
+cleanup with the
+.B \-i
+and
+.B \-c
+options.
+.TP
+.BI \-g " get"
+This specifies the program to use for getting the source text for the
+build.  If no
+.B \-g
+option is given, the default program
+.BR mg_get_merge (1)
+is used.
+.TP
+.BI \-s " source"
+The
+.B mgmerge
+program consists of two parts.  The first part initializes variables
+to default values.  The second part uses these variables to control
+how the
+.BR mg (1)
+database is built.  This option specifies a program to execute between
+the first and second parts.  The details of what the variables are, and
+how they may be changed, are in comments in the
+.B mgmerge
+program.
+If this option is used, the program should ideally be the same one that
+was called by
+.BR mgbuild (1)
+to build the database since some parameters need to be consistent between
+.B mgbuild
+and
+.B mgmerge.
+.TP
+.B \-S
+This option will cause a slow merge to be performed on the inverted files,
+where each inverted file entry is decoded and recoded.
+The default is a fast merge. Accumulated fast merges slowly degrade
+compression performance on the resulting inverted file so
+a periodic slow merge is recommended.
+.TP
+.B \-w
+Adding new documents can have an effect on the weight of the previous ones.
+By default the weights for documents already in the collection are not 
+recomputed since the change in their values is usually small.
+This option forces new weights to be recomputed.
+Periodic use of this option, as for the "-S" option, is recomended,
+otherwise query rankings may become inaccurate.
+.TP
+.I collection-name
+This is the collection name, as required by the
+.BR mg_get_merge (1)
+program.  It serves both as a
+.I case
+statement selector, and as the name of a subdirectory that holds the
+indexing files.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+Note that a temporary directory under the
+.B MGDATA
+directory is used to perform the merge.
+The default name for this directory is
+.BR MERGE .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Text statistics.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgbuild (1),
+.BR mg_get_merge (1),
+.BR mg_get (1),
+.BR mg_invf_merge (1),
+.BR mg_text_merge (1),
+.BR mg_query (1),
+.BR mg_weights_build (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/mgmerge.sh	(revision 16561)
@@ -0,0 +1,417 @@
+#!/bin/csh -f
+###########################################################################
+#
+# mgmerge.sh -- Script used to build mg text collection.
+# Copyright (C) 1994  Neil Sharman, Shane Hudson (mods. for merging)
+#
+# 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.
+#
+#
+###########################################################################
+
+set complex = ""
+set slow_merge = ""
+set guess_weights = "-w "
+# [RPAP - Feb 97: Level 3 Merge]
+set text_pass = 1
+
+# Parse the command line arguments
+while ($#argv >= 1)
+  if ("$1" == "-s") then
+    shift
+    if ($#argv >= 1) then
+      set source = $1
+      shift
+    endif
+  else if ("$1" == "-g") then
+    shift
+    if ($#argv >= 1) then
+      set get = $1
+      shift
+    endif
+  else if ("$1" == "-c") then
+    set complex = "-text"
+    shift
+  else if ("$1" == "-S") then
+# set slow merge option in mg_invf_merge
+    set slow_merge = "-s "
+    shift
+  else if ("$1" == "-w") then
+# turn weights guessing off (rebuild weights file from scratch)
+    set guess_weights = ""
+    shift
+
+# [RPAP - Feb 97: Level 3 Merge]
+  else if ("$1" == "-T") then
+# turn text pass off
+    set text_pass = 0
+    shift
+
+  else
+    if ($?text == "0") then
+      set text = $1
+    endif
+    shift
+  endif
+    
+end
+
+if ($?text == "0") then
+  set prog = $0
+  echo "USAGE:"
+  echo "     "$prog:t" [-s config-script] [-g get-program] [-c] source"
+  echo ""
+  echo " The config-script is only needed if a non-standard build is required."
+  echo " The get-program defaults to mg_get if not specified."
+  exit 1	
+endif
+
+set bindir = $0
+set bindir = $bindir:h
+
+# if $pipe == 1 then pipe in the source text using $get and $text otherwise
+# read the source text directly from the file names specified in $input_files
+set pipe = 1
+
+if ($?get == "0") then
+  set complex = "-text"
+  set get = $bindir/mg_get_merge
+endif
+
+if (-e $MGDATA/${text}.chunks) then
+  set input_files = `cat $MGDATA/${text}.chunks`
+endif
+
+# Set the inversion method this may be either "I" or "N"
+# N is memory-efficient, I is faster for small collections
+set invf_method = N
+
+
+# Set the stemming method
+#   Bit 0 = case folding
+#   Bit 1 = S stemmer
+set stem_method = 3
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then build collection with full indexes to blocked file
+# overriding stem_method (stem_method will be set to 0)
+# Otherwise don't build with indexes
+set do_indexes = 0
+
+
+# $invf_mem specifies the amount of memory to use for the pass2 inversion
+# This only has an effect if $invf_method is "N".
+set invf_mem = 32
+
+
+# $num_chunks specifies the number of interium chunks of inverted file that
+# may be written to disc before a merge into the invf file is done
+# This only has an effect if $invf_method is "N".
+set num_chunks = 3
+
+# $invf_level specifies the level of the inverted file that will be generated.
+# Note: The value *MUST* be the same as was used to build the collection
+# that is being merged.
+# In the current mg_invf_merge implementation the maximum level is 2. 
+# Paragraph-level inversion (invf_level = 3) is NOT supported.
+set invf_level = 2
+
+
+# If $strip_sgml == 1 then sgml tags are stripped from the inversion phase.
+# Otherwise sgml tags are kepted.
+set strip_sgml = 1
+
+
+# $trace specifies the interval between trace entries in Mb.
+# If this is not set no trace entries will be generated.
+set trace = 10
+
+
+# $weight_bits specifies the number of bits of precision bo be given to the 
+# approximate weights.
+set weight_bits = 6
+
+
+# $mcd specifies the commandline arguments for the mg_compression_dict program
+set mcd = -S
+
+# $merge_name is the name of the subdirectory under ${MGDATA} where
+# the mergeing steps will be performed. There should NOT be an mg
+# collection with this name! The default is "MERGE"
+set merge = "MERGE"
+
+# Source the parameter file to modify parameters of the build.
+if ($?source) then
+  source ${source}
+endif
+
+
+# [RPAP - Jan 97: Stem Index Change]
+# If do_indexes == 1 then set stem_method = 0
+if ($do_indexes) then
+  set stem_method = 0
+endif
+
+
+# Generate the collection name.
+set coll_name = ${text}
+
+# Generate the directory where merging will take place
+if (-e $MGDATA/${merge}) then
+else
+  mkdir $MGDATA/${merge}
+endif
+
+# Generate the base name for the collection.
+# [RPAP - Feb 97: Level 3 Merge]
+set bname = ${coll_name}
+
+# generate the old, new and merge names in the $merge directory
+set oldname = ${merge}/${coll_name}.old
+set newname = ${merge}/${coll_name}.new
+set mergename = ${merge}/${coll_name}
+
+# build up the command lines for pass 1 and 2 to build a
+# dummy collection for the new documents
+set pass1 = (-f ${newname} -${invf_level} -m ${invf_mem} -s ${stem_method})
+set pass2 = (-f ${newname} -${invf_level} -c ${num_chunks})
+
+if ($strip_sgml) then
+  set pass1 = (${pass1} -G)
+  set pass2 = (${pass2} -G)
+endif
+
+if ($?trace) then
+  set pass1 = (${pass1} -t ${trace})
+  set pass2 = (${pass2} -t ${trace})
+endif
+
+# Note that a -T1 pass isnt done since the old .text.stats file is used
+# as the compression model for compressing the new documents.
+
+# [RPAP - Feb 97: Level 3 Merge]
+# Do not perform -T2 pass if text_pass == 0
+if ($text_pass) then
+  set pass2 = (${pass2} -T2)
+endif
+
+set pass1 = (${pass1} -${invf_method}1)
+set pass2 = (${pass2} -${invf_method}2)
+
+
+if ($?trace_name) then
+  set pass1 = (${pass1} -n ${trace_name})
+  set pass2 = (${pass2} -n ${trace_name})
+endif
+  
+if ($?comp_stats) then
+  set pass2 = (${pass2} -C ${comp_stats})
+endif
+
+########################################################################
+# Here is where mgmerge goes to work.
+
+echo "--------------------------------------------------------------"
+echo "`uname -n`, `date`"
+echo "MGMERGE collection: ${bname}"
+echo "--------------------------------------------------------------"
+echo "FIRST PHASE: build new collection from new documents"
+echo "--------------------------------------------------------------"
+
+# move and rename the text dictionary file
+# [RPAP - Feb 97: Level 3 Merge] - only if performing text pass
+if ($text_pass) then
+  mv $MGDATA/${bname}.text.dict $MGDATA/${newname}.text.dict
+endif
+
+if ($pipe) then
+  if ("$complex" != "") then
+    echo ">> $get $text -init"
+    $get $text -init
+    if ("$status" != "0") exit 1 
+    echo "-----------------------------------"
+  endif  
+endif
+
+if ($pipe) then
+  if ($?pass1filter) then
+    echo ">> $get $text $complex | $pass1filter | mg_passes ${pass1}"
+    $get $text $complex| $pass1filter | $bindir/mg_passes ${pass1}
+    if ("$status" != "0") exit 1 
+  else
+    echo ">> $get $text $complex | mg_passes ${pass1}"
+    $get $text $complex| $bindir/mg_passes ${pass1}
+    if ("$status" != "0") exit 1 
+  endif
+else
+  echo ">> mg_passes ${pass1} ${input_files}"
+  $bindir/mg_passes ${pass1} ${input_files}
+  if ("$status" != "0") exit 1 
+endif
+echo "-----------------------------------"
+
+echo "mg_perf_hash_build -f ${newname}"
+$bindir/mg_perf_hash_build -f ${newname}
+if ("$status" != "0") exit 1 
+echo "-----------------------------------"
+
+if ($pipe) then
+  if ($?pass2filter) then
+    echo ">> $get $text $complex | $pass2filter | mg_passes ${pass2}"
+    $get $text $complex | $pass2filter | $bindir/mg_passes ${pass2}
+    if ("$status" != "0") exit 1 
+  else
+    echo ">> $get $text $complex | mg_passes ${pass2}"
+    $get $text $complex | $bindir/mg_passes ${pass2}
+    if ("$status" != "0") exit 1 
+  endif
+else
+  echo ">> mg_passes ${pass2} ${input_files}"
+  $bindir/mg_passes ${pass2} ${input_files}
+  if ("$status" != "0") exit 1 
+endif
+echo "-----------------------------------"
+
+if ($pipe) then
+  if ("$complex" != "") then
+    echo "-----------------------------------"
+    echo ">> $get $text -cleanup"
+    $get $text -cleanup
+    if ("$status" != "0") exit 1 
+  endif
+endif
+
+echo "---------------------------------------"
+date
+echo "--------------------------------------------------------------"
+echo "SECOND PHASE: merge the two collections"
+echo "--------------------------------------------------------------"
+
+# move the appropriate files from the base directory to the merge dir
+echo "(moving files....)"
+# [RPAP - Feb 97: Level 3 Merge]
+# Move files if doing text pass
+if ($text_pass) then
+  mv $MGDATA/${bname}.text       $MGDATA/${oldname}.text
+  mv $MGDATA/${bname}.text.idx   $MGDATA/${oldname}.text.idx
+else
+# Need this file if building weights
+  cp -d $MGDATA/${bname}.text.idx   $MGDATA/${mergename}.text.idx
+endif
+mv $MGDATA/${bname}.invf       $MGDATA/${oldname}.invf
+mv $MGDATA/${bname}.invf.idx   $MGDATA/${oldname}.invf.idx
+mv $MGDATA/${bname}.invf.dict  $MGDATA/${oldname}.invf.dict
+# the old weight file may be used for the new file
+mv $MGDATA/${bname}.weight     $MGDATA/${mergename}.weight
+# EOC: the old invf.paragraph file may be used for the new file for Level 3 Invf
+if (-e $MGDATA/${bname}.invf.paragraph) then
+  mv $MGDATA/${bname}.invf.paragraph  $MGDATA/${mergename}.invf.paragraph
+endif
+echo "---------------------------------------"
+
+# [RPAP - Feb 97: Level 3 Merge]
+# Only merge text if doing text pass
+if ($text_pass) then
+  echo ">> mg_text_merge -f ${mergename}"
+  $bindir/mg_text_merge -f $mergename
+  echo "---------------------------------------"
+endif
+
+echo ">> mg_invf_merge ${slow_merge}${guess_weights} -f ${mergename}"
+$bindir/mg_invf_merge ${slow_merge}${guess_weights} -f ${mergename}
+echo "---------------------------------------"
+
+#remove .weight file, so a new one will be created by mg_weights_build
+if ("$guess_weights" == "") then
+  echo "rm ${mergename}.weight"
+  rm $MGDATA/${mergename}.weight
+endif
+
+# mv $MGDATA/${mergename}.weight $MGDATA/${mergename}.w2
+
+echo ">> mg_weights_build -f ${mergename} -b ${weight_bits}"
+$bindir/mg_weights_build -f ${mergename} -b ${weight_bits}
+if ("$status" != "0") exit 1 
+
+echo "---------------------------------------"
+
+echo ">> mg_invf_dict -f ${mergename} -b 4096"
+$bindir/mg_invf_dict -f ${mergename} -b 4096
+
+# [RPAP - Jan 97: Stem Index Change]
+if ($do_indexes) then
+  echo "-----------------------------------"
+
+  echo "mg_stem_idx -f ${mergename} -b 4096 -s1"
+  $bindir/mg_stem_idx -f ${mergename} -b 4096 -s1
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${mergename} -b 4096 -s2"
+  $bindir/mg_stem_idx -f ${mergename} -b 4096 -s2
+  if ("$status" != "0") exit 1 
+
+  echo ""
+
+  echo "mg_stem_idx -f ${mergename} -b 4096 -s3"
+  $bindir/mg_stem_idx -f ${mergename} -b 4096 -s3
+  if ("$status" != "0") exit 1 
+endif
+
+echo "--------------------------------------------------------------"
+
+# move files back to base dir
+# [RPAP - Feb 97: Level 3 Merge]
+# mv the text files if doing text pass
+if ($text_pass) then
+  mv $MGDATA/${oldname}.text         $MGDATA/${bname}.text
+  mv $MGDATA/${mergename}.text.idx   $MGDATA/${bname}.text.idx
+  mv $MGDATA/${newname}.text.dict    $MGDATA/${bname}.text.dict
+endif
+mv $MGDATA/${mergename}.invf       $MGDATA/${bname}.invf
+mv $MGDATA/${mergename}.invf.idx   $MGDATA/${bname}.invf.idx
+mv $MGDATA/${mergename}.invf.dict  $MGDATA/${bname}.invf.dict
+mv $MGDATA/${mergename}.invf.dict.blocked  $MGDATA/${bname}.invf.dict.blocked
+
+# [RPAP - Jan 97: Stem Index Change]
+if ($do_indexes) then
+  mv $MGDATA/${mergename}.invf.dict.blocked.1 $MGDATA/${bname}.invf.dict.blocked.1
+  mv $MGDATA/${mergename}.invf.dict.blocked.2 $MGDATA/${bname}.invf.dict.blocked.2
+  mv $MGDATA/${mergename}.invf.dict.blocked.3 $MGDATA/${bname}.invf.dict.blocked.3
+endif
+
+mv $MGDATA/${mergename}.weight     $MGDATA/${bname}.weight
+mv $MGDATA/${mergename}.weight.approx $MGDATA/${bname}.weight.approx
+mv $MGDATA/${mergename}.text.idx.wgt $MGDATA/${bname}.text.idx.wgt
+
+# EOC: mv .invf.paragraph file if exists
+if (-e $MGDATA/${mergename}.invf.paragraph) then
+  mv $MGDATA/${mergename}.invf.paragraph   $MGDATA/${bname}.invf.paragraph
+endif
+
+# remove old files in the MERGE directory
+rm -f $MGDATA/${merge}/*
+
+echo "mgstat -f ${bname} -E"
+$bindir/mgstat -f ${bname} -E
+if ("$status" != "0") exit 1 
+
+echo "--------------------------------------------------------------"
+echo "`uname -n`, `date`"
+echo "--------------------------------------------------------------"
+
+echo ""
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.1	(revision 16561)
@@ -0,0 +1,130 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH xmg 1 \*(Dt CITRI
+.SH NAME
+xmg \- An X interface to mgquery
+.SH SYNOPSIS
+.B xmg
+[
+.I collection-name
+]
+.SH DESCRIPTION
+.B xmg
+is an X interface to the
+.BR mg (1)
+information retrieval system.
+.PP
+Type a query in the ``Query'' box.  The escape key clears the query text.
+.PP
+The first line or so of each matching document is listed.
+.PP
+Click on any line to show the complete document.
+.PP
+Shift-click to show the document in a new window.
+.PP
+Press
+.I Next
+and
+.I Previous
+to cycle through the list of documents (shift works here too).
+.PP
+If you change the query, existing windows continue to refer to the
+original query.
+.PP
+You can select either Boolean or ranked queries:
+.RS
+.TP "\w'\fIBoolean queries:\fP'u+2n"
+.I "Boolean queries:"
+use ``&'' for AND, ``|'' for OR, ``!'' for NOT, and parentheses for
+grouping.
+.TP
+.I "Ranked queries:"
+Documents are listed in order of relevance.  A weight is shown
+alongside each.  Weights are normalized to 100 for the most relevant
+document.  Choose
+.I "Approximate Ranking"
+for greater retrieval speed.
+.RE
+.PP
+In document windows (other than the main window):
+.RS
+.br
+Typing `n' = next document;
+.br
+Typing `N' = next document in new window;
+.br
+Typing `p' = previous document;
+.br
+Typing `P' = previous document in new window;
+.br
+Typing `q' = close window.
+.RE
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.TP
+.SB Xdefaults
+To override the default font options, use RESOURCE_MANAGER properties
+like the following in your
+.I .Xdefaults
+file:
+.IP
+.RS
+.nf
+.I "xmg.textfont: fixed"
+.I "xmg.buttonfont: fixed"
+.fi
+.RE
+.IP
+To override the default query type of ranked, use
+.IP
+.RS
+.I "xmg.querytype: boolean"
+.RE
+.IP
+To override the default collection name (the first one listed by
+.BR ls (1)
+from your MGDATA environment variable), use
+.IP
+.RS
+.I "xmg.collection: mail93"
+.RE
+.SH "SEE ALSO"
+.na
+.BR ls (1),
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/scripts/xmg.sh	(revision 16561)
@@ -0,0 +1,1376 @@
+#!/usr/local/bin/wish -f
+# Edit the line above to point to your version of wish
+#
+###############################################################################
+#
+# xmg -- X interface to the mg information retrieval system
+# Copyright (C) 1994  Bruce McKenzie, Ian Witten and Craig Nevill-Manning
+#
+# 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.
+#
+#       @(#)xmg.sh	1.4 25 Mar 1994
+#
+###############################################################################
+#
+# "xmg" is an X interface to the mg information retrieval system.
+#
+# Type a query in the "Query" box.
+# The first line or so of each matching document is listed.
+# Click on any line to show the complete document.
+# Shift-click to show the document in a new window.
+# Press Next and Previous to cycle through the list of documents (Shift works
+#   here too). 
+# If you change the query, existing windows continue to refer to the original
+#   query. 
+# 
+# You can select either Boolean or Ranked queries.
+# Boolean queries: use "&" for AND, "|" for OR, "!" for NOT, and parentheses
+# for grouping 
+# Ranked queries:  - documents are listed in order of relevance; a weight is
+# 		     shown alongside each 
+#                  - weights are normalized to 100 for the most relevant
+#		     document 
+#                  - choose Approximate Ranking for greater retrieval speed. 
+# 
+# In document windows (other than the main window): 
+#		Typing 'n' = next document
+#		Typing 'N' = next document in new window
+#		Typing 'p' = previous document
+#		Typing 'P' = previous document in new window
+#		Typing 'q' = close window
+#
+###############################################################################
+
+# Date: 13 March 1994
+# Authors: Bruce McKenzie, Ian Witten and Craig Nevill-Manning
+
+###############################################################################
+# Customizing xmg
+###############################################################################
+
+# To override the default font options, use RESOURCE_MANAGER properties like
+# the following in your .Xdefaults file
+#
+#	xmg.textfont: fixed
+#	xmg.buttonfont: fixed
+#
+# To override the default query type of ranked, use
+#
+#	xmg.querytype: boolean
+#
+# To override the default collection name (the first one listed by /bin/ls from
+# your MGDATA environment variable), use
+#
+#	xmg.collection: mail93
+
+###############################################################################
+# Global variables
+###############################################################################
+
+#   genuine constants:
+#
+#	env		the environment variables
+#	Terminator	terminator for mgquery
+#	MaxWindowHeight	the maximum height permitted for a document window
+#	ListBoxLength	the number of items in the document list
+#	HeadLength	the length document head displayed in the list box
+#	ImageString	the string at the start of a document associated
+#			with an image
+#
+#   things that rarely get changed:
+#
+#	querytype	the current query type (boolean, ranked, approx-ranked)
+#	booleanquery	says whether the query is boolean or ranked
+#	approxquery	says whether ranking is to be approximate or not
+#	collection	the name of the document collection being used
+#	fileId		file identifer for a pipe to the mgquery process
+#       errorOccurred	flag to say that there has been an I/O error on the
+#			current collection
+#
+#   things that reflect items in the main panel
+#
+#	query		the current query
+#	hits		number of hits for the current query
+#	display		shell command to display the image associated with
+#			the last document retrieved
+#	scrolledto	document prefixes have been obtained for all documents
+#			up to this position in the document list
+#
+#   static variable needed in just one procedure
+#
+#	top_rank	weight associated with the first document retrieved
+#	windowname	unique id for each document window
+
+###############################################################################
+# High-level procedures
+###############################################################################
+
+proc SaveDocument {w} {
+  set file [FSBox "Save document to file:"]
+
+  if {$file != ""} {
+    if [file exists $file] {
+      toplevel .r
+      wm title .r "\n"
+      .r configure -borderwidth 2 -relief raised 
+      focus .r
+      label .r.message -text "The file already exists"
+      button .r.replace -text "Replace" -width 10 -height 2 \
+          -command "destroy .r; WriteDocument $file \"w\" $w" 
+      button .r.append -text "Append" -width 10 -height 2 \
+          -command "destroy .r; WriteDocument $file \"a\" $w"
+      button .r.cancel -text "Cancel" -command {destroy .r} -width 10 -height 2
+      pack .r.message -side top -pady 10 -padx 20
+      pack .r.replace .r.append .r.cancel -side left -pady 10 -padx 20
+
+    } else {
+       WriteDocument $file "w" $w
+    }
+  }
+}
+
+proc Dialog {text command} {
+      if {![winfo exists .r] } { toplevel .r }
+      wm title .r "\n"
+      .r configure -borderwidth 2 -relief raised 
+      focus .r
+      label .r.message -text $text
+      button .r.ok -text "OK" -width 10 -height 2 \
+          -command "destroy .r $command"
+      pack .r.message -side top -pady 10 -padx 20
+      pack .r.ok -side bottom -pady 10 -padx 20
+}
+
+proc WriteDocument {file mode w} {
+    if {[catch {set f [open $file $mode]}] == 0} {
+        if [winfo exists $w] {
+            puts $f [$w get 0.0 end]
+        } else {
+            Dialog "Unable to save text" ""
+        }
+        flush $f
+        close $f
+    } else {
+        Dialog "Unable to open file for writing" "; SaveDocument $w"
+    }
+}
+
+# Send a query to mg and enter the response in the document list
+#
+proc sendQuery {window} {
+    global query hits querytype errorOccurred scrolledto
+
+    .m.t.q.retrieved.label configure -foreground Gray
+    .m.t.q.retrieved.hits configure -foreground Gray
+
+    sendto ".set query \"$querytype\""
+    sendto ".set mode \"docnums\""
+    foreach f "sdocnums sdocs srank" { $window.$f delete 0 end }
+    disableButton .m.d.buttons.previous
+    disableButton .m.d.buttons.next
+    if {[string index $query 0] == "." || [string length $query] > 255} {
+        set doclist ""
+    } else { set doclist [sendandget $query] }
+
+    if {$errorOccurred} {
+        enterText .m.d.text [mgqueryFailure]
+        .m.t.q.retrieved.label configure -foreground Black
+        .m.t.q.retrieved.hits configure -foreground Black 
+        update
+        return
+    }
+    sendto ".set query \"DocNums\""
+    sendto ".set mode  \"text\""
+    .m.t.q.retrieved.label configure -foreground Black
+    .m.t.q.retrieved.hits configure -foreground Black 
+    set hits [llength $doclist]
+    set i 0
+    global top_rank
+
+    scan [lindex $doclist 0] "%d %f %d" d rank size
+    set top_rank $rank
+    if {$top_rank == 0} {set top_rank 1}
+
+    while {$i < $hits} {
+	scan [lindex $doclist $i] "%d %f %d" d rank size
+        if {$rank == "Inf"} {set rank 1}
+	if {$querytype == "boolean"} {
+	    $window.srank insert end ""
+	} else {
+	    $window.srank insert end \
+		[format "%3.0f" [expr $rank / $top_rank * 99 + 1]]
+	}
+	$window.sdocnums insert end [format " %d" $d]
+	incr i
+    }
+
+    set scrolledto 0
+    scroll $window 0
+    bind $window.sdocs <ButtonRelease-1> \
+	"showDocument $window.sdocs %y .m.d \"$doclist\" \"$query\""
+    bind $window.sdocs <Shift-ButtonRelease-1> \
+	"showDocument $window.sdocs %y New \"$doclist\" \"$query\""
+    enterText .m.d.text ""
+}
+
+# Scrolling routine for the document list
+#
+proc scroll {window n} {
+    global hits scrolledto ListBoxLength Terminator fileId ImageString
+
+    $window.sdocnums yview $n
+    $window.sdocs yview $n
+    $window.srank yview $n
+    set last $n
+    if {$last <= 0} {set last 0}
+    incr last $ListBoxLength
+    if {$last > $hits} {set last $hits}
+
+#    .m config -cursor {watch}
+#    update
+    sendto ".set mode \"heads\""
+    sendto ".set terminator \"\""
+    while {$scrolledto < $last} {
+	if {[scan [$window.sdocnums get $scrolledto] "%d" d] == 1} {
+            sendto $d
+            gets $fileId line
+            set i [string first " " $line]
+            if {$i != -1} {set line [string range $line [expr $i + 1] end]}
+            if {[string compare $ImageString [string range $line 0 9]] == 0} {
+                set line [getImageDocumentHead $d]
+            }
+	    $window.sdocs insert $scrolledto $line
+	}
+	incr scrolledto
+    }
+    sendto ".set terminator \"$Terminator\\n\""
+    sendto ".set mode text"
+#   .m config -cursor {}
+}
+
+proc getImageDocumentHead {d} {
+    global HeadLength ImageString fileId
+
+    sendto ".set heads_length 1000"
+    sendto $d
+    gets $fileId line
+
+    set line [string range $line 11 end]
+
+    set s [string first $ImageString $line]
+    if {$s != -1} {
+        incr s 11
+        set line [string range $line $s [expr $s + $HeadLength]]
+    }
+  
+    sendto ".set heads_length $HeadLength"
+
+    return "* $line"
+}
+
+# Show document from the document list
+#
+proc showDocument {window y newWindow doclist query} {
+    showThisDocument [$window nearest $y] $doclist $newWindow $query
+}
+
+proc adjacentDocument {index doclist window q button} {
+    global ListBoxLength
+
+    showThisDocument $index $doclist $window $q
+
+    if {$window == ".m.d"} {
+      set first [.m.t.q.r.sdocs nearest 0]
+      if {$index < $first || $index >= $first + $ListBoxLength} {
+          scroll .m.t.q.r [expr $index - $ListBoxLength / 2]
+      }
+      .m.t.q.r.sdocs select from $index
+    } 
+}
+
+
+# Show document with this index in the document list
+#
+proc showThisDocument {index doclist window q} {
+    global collection MaxWindowHeight display windowname
+
+    set document ""
+    scan [lindex $doclist $index] "%d %f" document rank
+    if {$document == ""} { return }
+    set textlist [sendandget $document]
+	
+    if {[string length $display] != 0} { exec csh -c $display >& /dev/null & }
+	
+# throw away the first line
+    set text [join [lrange $textlist 1 end] "\n"]
+
+# if necessary, create a new window for this document
+    if {$window == "New"} {
+        .m.t.q.r.sdocs select clear
+	set window .m.document$windowname
+        incr windowname
+        set existing ""
+        set raised 0
+        foreach w [lrange [winfo children .m] 2 end] {
+           if {[lindex [$w.docnum configure -text] 4] == $index && \
+               [lindex [$w.query configure -text] 4] == $q} {
+              if {$raised} {
+                 closeDocumentWindow $w
+              } else {
+                  raise $w
+		  getFocus $w
+                  set window $w
+                  set raised 1
+              }
+           }
+        }
+  
+        if {!$raised} {
+   	    toplevel $window
+            $window configure -borderwidth 2 -relief raised
+            wm protocol $window WM_TAKE_FOCUS "getFocus $window"
+	    getFocus $window
+	    makeDocumentPanel $window
+            bind $window q "$window.buttons.close invoke"
+            label $window.docnum -text $index
+            label $window.query -text $q
+       }
+    }
+
+# configure the window for this document
+    set rows [expr [llength $textlist] - 1]
+    if {$window != ".m.d"} {
+	wm title $window "$collection \"$q\""
+	wm iconname $window "$document"
+	$window.text configure -height $rows
+	if {$rows > $MaxWindowHeight} {
+	    $window.text configure -height $MaxWindowHeight
+	}
+    }
+
+# show the document text in the window
+    enterText $window.text $text
+
+# configure the window's Previous and Next buttons
+    if {$index == 0} {
+        disableButton $window.buttons.previous
+    } else {
+	set newindex [expr $index-1]
+	bindBoth $window previous $newindex $doclist $q
+        bind $window p [bind $window.buttons.previous <1>]
+        bind $window P [bind $window.buttons.previous <Shift-1>]
+    }
+    if {$index == [expr [llength $doclist]-1]} {
+	disableButton $window.buttons.next 
+    } else {
+	set newindex [expr $index+1]
+	bindBoth $window next $newindex $doclist $q
+        bind $window n [bind $window.buttons.next <1>]
+        bind $window N [bind $window.buttons.next <Shift-1>]
+    }
+
+    if {$window != ".m.d"} { $window.docnum configure -text $index }
+
+    if {[string length $display] != 0} { 
+        set on "{
+            if \[winfo exists $window\] \{
+                 $window.buttons.image configure \
+                   -foreground Black
+                 update\}
+        }"
+        set off "{
+            if \[winfo exists $window\] \{
+                 $window.buttons.image configure \
+                   -foreground [lindex [.m.d configure -background] 4]
+                 update\}
+        }"
+        set t 0
+        while {$t < 5} {
+            after [expr $t * 1000] eval $on
+            after [expr $t * 1000 + 500] eval $off
+            incr t
+        }   
+    }
+}
+
+# Bind both Button-1 and Shift-Button-1 commands
+#
+proc bindBoth {window button index doclist query} {
+    $window.buttons.$button configure -state normal
+    bind $window.buttons.$button <Button-1> \
+	"adjacentDocument $index \"$doclist\" $window \"$query\" $button"
+    bind $window.buttons.$button <Shift-Button-1> \
+	"adjacentDocument $index \"$doclist\" New \"$query\" $button"
+}
+
+# Change the type of query
+#
+proc changeQuerytype {name element op} {
+    global querytype booleanquery approxquery
+
+    if {$booleanquery} {
+	.m.t.b.type.approx configure -state disabled
+	.m.t.q.retrieved.weight configure \
+             -foreground [lindex [.m configure -background] 4]
+	set querytype boolean
+    } else {
+	.m.t.b.type.approx configure -state normal
+	.m.t.q.retrieved.weight configure -foreground Black
+	if {$approxquery} {
+	    set querytype "approx-ranked"
+	} else {
+	    set querytype "ranked"
+	}
+    }
+}
+
+proc enterText {window text} {
+    $window configure -state normal
+    $window delete 0.0 end
+    $window insert 0.0 $text
+    $window configure -state disabled
+}
+
+###############################################################################
+# IO and initialization
+###############################################################################
+
+# Send a message to mg and return the reply
+#
+proc sendandget {message} {
+    global fileId Terminator display errorOccurred ImageString
+    set display ""
+
+    .m config -cursor {watch}
+    update idletasks
+    set reply ""
+    sendto "$message\n"
+    if {$message != "" && !$errorOccurred} {
+        gets $fileId line
+    	while {[string compare $Terminator $line] != 0} {
+            lappend reply $line;
+            gets $fileId line
+        }
+
+        if {[string compare $ImageString [lindex $reply 1]] == 0 } {
+          if {[string compare "MGDATA" [string range [lindex $reply 3] 0 5]] == 0} {
+             set display [string range [lindex $reply 3] 7 end];
+          }
+          set reply [lrange $reply 4 end]
+        }
+    }
+
+    .m config -cursor {}
+    return $reply
+}
+    
+# Send a message to mg
+#
+proc sendto {message} {
+    global fileId errorOccurred
+
+    puts $fileId $message
+    set errorOccurred [catch {flush $fileId}]
+}
+
+# Reinitialise mgquery to work with a new document collection
+#
+proc reInitMG {c} {
+    global fileId
+    catch {close $fileId}
+    initMG $c
+}
+
+# Initialize mgquery to work with a specified text collection
+#
+proc initMG {c} {
+    global query hits fileId collection Terminator env errorOccurred
+    global HeadLength
+
+    set collection $c
+    set query ""
+    set hits 0
+    set errorOccurred 0
+
+    foreach f "sdocs srank" { .m.t.q.r.$f delete 0 end }
+    enterText .m.d.text ""
+    .m.t.b.collect.m configure -text $collection
+    foreach w [winfo children .m] {
+	if {[string first ".m.document" $w] == 0} { closeDocumentWindow $w }
+    }
+
+    disableButton .m.d.buttons.previous 
+    disableButton .m.d.buttons.next
+
+    if {$c != ""} {
+        set f $env(MGDATA)/$c
+
+        if {[file exists $f] && [file readable $f]} {
+    	    set fileId [open "| mgquery $collection" RDWR]
+	    sendto ".set expert \"true\""
+	    sendto ".set terminator \"$Terminator\\n\""
+	    sendto ".set pager \"cat\""
+            sendto ".set heads_length $HeadLength"
+	    return
+        }
+    }
+    bind .m.t.q.e.entry <Return> {null}
+    enterText .m.d.text [initializationFailure]
+    update
+}
+
+# Routine to obtain values from RESOURCE_MANAGER
+#
+proc query_resource {option class default} {
+	set val [option get . $option $class]
+	if {[string length $val] == 0} { return $default }
+	return $val
+}
+
+
+proc disableButton {b} {
+    bind $b <Button-1> {null}
+    bind $b <Shift-Button-1> {null}
+    $b configure -state disabled
+}
+
+proc null {} {}
+
+###############################################################################
+# Setting up windows
+###############################################################################
+
+proc removeFromWindowList {window} {
+    global stacking
+
+    set i 0
+    while {$i < [llength $stacking]} {
+      if {$window == [lindex $stacking $i]} {
+         set stacking [lreplace $stacking $i $i]
+      }
+      incr i
+    }
+}
+
+proc getFocus {window} {
+    global stacking
+
+    focus $window
+    removeFromWindowList $window
+    set stacking [linsert $stacking 0 $window]
+}
+
+proc closeDocumentWindow {w} {
+   global stacking
+   removeFromWindowList $w
+   set t [lindex $stacking 0]
+
+   if {$t != ""} {
+     focus $t
+     raise $t
+  }
+  destroy $w
+}
+
+# Make a "document" panel, including Previous, Next, and Close buttons
+#
+proc makeDocumentPanel {window} {
+    frame $window.buttons
+    button $window.buttons.previous -text "Previous" -width 10 -state disabled
+    button $window.buttons.next -text "Next" -width 10 -state disabled
+    button $window.buttons.close -text "Close" -width 10 \
+	-command "closeDocumentWindow $window"
+    button $window.buttons.save -text "Save" -width 10 \
+        -command "SaveDocument $window.text"
+
+    label $window.buttons.image -text "Fetching image..." \
+            -foreground [lindex [$window configure -background] 4]
+    pack $window.buttons.previous $window.buttons.next -side left
+    pack $window.buttons.image -side left -padx 10
+    pack $window.buttons.close $window.buttons.save -side right 
+    pack $window.buttons -side top -fill x 
+
+    text $window.text -borderwidth 2 -relief raised \
+             -setgrid true -width 80 -state disabled
+
+    $window.text configure -yscrollcommand "$window.sy set" 
+    scrollbar $window.sy -relief flat -command "$window.text yview"
+    pack $window.sy -side right -fill y 
+    pack $window.text -side top -expand yes -fill both
+}
+
+# Make the "buttons" panel, with the query type and collection name
+#
+proc mkButtons {window} {
+    global collection booleanquery approxquery env
+    frame $window.type
+    label $window.type.label -text "Query type"
+
+    radiobutton $window.type.boolean -text "Boolean" -variable booleanquery \
+	  -value 1 -relief flat
+
+    radiobutton $window.type.ranked -text "Ranked" -variable booleanquery \
+	  -value 0 -relief flat
+
+    checkbutton $window.type.approx -text "Approximate" -variable approxquery \
+	  -relief flat
+
+    trace variable booleanquery w changeQuerytype
+    trace variable approxquery w changeQuerytype
+
+    pack $window.type.label   -side top -anchor w
+    pack $window.type.boolean -side top -anchor w -padx 12
+    pack $window.type.ranked  -side top -anchor w -padx 12
+    pack $window.type.approx  -side top -anchor w -padx 36
+
+    pack $window.type -side top -anchor w -pady 12
+
+    frame $window.collect
+    label $window.collect.label -text "Collection: "
+    pack $window.collect.label -side left -anchor w
+
+    getViewer
+
+    set dirs [getMGDATA]
+    set collection ""
+    if {$dirs != ""} { set collection [lindex $dirs 0] }
+   
+    menubutton $window.collect.m -menu $window.collect.m.menu \
+	-relief sunken
+    menu $window.collect.m.menu
+    foreach c $dirs {
+        if {[file readable $env(MGDATA)/$c] && 
+            [file isdirectory $env(MGDATA)/$c]} {
+    	    $window.collect.m.menu add command -label $c \
+	        -command "reInitMG $c"
+        }
+    }
+    pack $window.collect.m -side left
+    pack $window.collect -side bottom -anchor w
+
+
+}
+
+# Check that the MGDATA environment variable is set, and is non-null
+#
+proc getMGDATA {} {
+    global env
+
+    set MGDATA 0
+    foreach i [array names env] {
+	if {$i == "MGDATA"} {
+	    set dirs [exec /bin/ls $env(MGDATA)]
+	    if {[info exists dirs]} { return $dirs }
+	}
+    }
+
+    return ""
+}
+
+proc getViewer {} {
+    global env
+
+    set viewer 0
+    foreach i [array names env] {
+	if {$i == "MGIMAGEVIEWER"} {set viewer $env(MGIMAGEVIEWER)}
+    }
+
+    if {$viewer == 0} {set env(MGIMAGEVIEWER) "xloadimage -quiet -fit stdin"}
+}
+
+# Make the "query" panel, with the query and list of retrieved documents
+#
+proc mkQuery {window} {
+    global hits ListBoxLength
+
+# at the top of the "query" panel is the "entry" frame
+    frame $window.e
+    label $window.e.label -text "Query"
+    entry $window.e.entry -relief sunk -width 47 -textvariable query
+    pack $window.e.label -side left
+    pack $window.e.entry -side right -expand yes -fill x
+    focus default $window.e.entry
+    focus $window.e.entry
+
+    bind $window.e.entry <Control-Key> {null}
+    bind $window.e.entry <Return> "sendQuery $window.r"
+    bind $window.e.entry <Escape> { set query "" }
+    bind $window.e.entry <2> {
+	%W insert insert [selection get STRING]
+	tk_entrySeeCaret %W
+    }
+
+# next is the "retrieved" frame
+    frame $window.retrieved
+    label $window.retrieved.weight -text "Weight"
+    label $window.retrieved.label -text "   Documents retrieved: "
+    label $window.retrieved.hits -width 4 -relief sunk -textvariable hits
+    set documentsRetrieved 0
+    pack $window.retrieved.weight $window.retrieved.label \
+        $window.retrieved.hits -side left
+
+# next is the "results" frame (note that the docnums listbox doesn't appear on
+# the screen)
+    frame $window.r
+    scrollbar $window.r.sscroll -relief sunken -command "scroll $window.r"
+#    bind $window.r.sscroll <ButtonRelease-1> "updateSdocs $window.r.sdocs $window.r.sdocnums"
+    listbox $window.r.sdocnums
+    listbox $window.r.sdocs -relief sunken \
+            -setgrid 1 -geometry 50x$ListBoxLength
+    listbox $window.r.srank -yscroll "$window.r.sscroll set" \
+            -setgrid 1 -geometry 4x$ListBoxLength
+  
+    bind $window.r.srank <Any-1> {null}
+    bind $window.r.srank <Any-2> {null}
+    bind $window.r.srank <Any-3> {null}
+
+    bind $window.r.srank <Any-B1-Motion> {null}
+    bind $window.r.srank <Any-B2-Motion> {null}
+    bind $window.r.srank <Any-B3-Motion> {null}
+
+    bind $window.r.sdocs <Any-2> {null}
+    bind $window.r.sdocs <Any-B2-Motion> {null}
+
+    pack $window.r.sscroll -side right -fill y
+    pack $window.r.srank -side left 
+    pack $window.r.sdocs -side left -fill x -expand yes
+
+# pack them up
+    pack $window.e -side top -anchor e -pady 10 -fill x -expand yes
+    pack $window.retrieved -anchor w
+    pack $window.r -side bottom -anchor e -pady 10 -fill x -expand yes
+}
+
+# Make a "help" window
+#
+proc mkHelp {} {
+    if [winfo exists .help] { destroy .help }
+    toplevel .help
+    wm title .help "X interface to the mg information retrieval system"
+    wm iconname .help "About xmg"
+    frame .help.buttons
+    button .help.buttons.close -text Close -command "destroy .help" -width 10
+    button .help.buttons.help -text Help -width 10
+    button .help.buttons.conditions -text Conditions -width 10
+    button .help.buttons.warranty -text Warranty -width 10
+    button .help.buttons.bugs -text Bugs -width 10
+
+    frame .help.t
+    text .help.t.text -relief raised -bd 2 -setgrid true -height 24 -width 95
+    .help.t.text configure -yscrollcommand ".help.t.sy set" 
+    scrollbar .help.t.sy -relief flat -command ".help.t.text yview"
+    
+    pack .help.buttons.close .help.buttons.help -side right -fill x
+    pack .help.buttons.conditions .help.buttons.warranty \
+         .help.buttons.bugs -side left
+    pack .help.buttons -side bottom -fill x
+
+    pack .help.t.text -side left -expand yes -fill both
+    pack .help.t.sy -side right -fill y 
+    pack .help.t -side top -fill both -expand yes
+    enterText .help.t.text [HelpText]
+    grayButton help
+}
+
+proc grayButton {button} {
+    bind .help.buttons.help <1> "enterText .help.t.text \[HelpText\]
+         grayButton help" 
+    .help.buttons.help configure -state normal
+
+    bind .help.buttons.bugs <1> "enterText .help.t.text \[Bugs\]
+         grayButton bugs" 
+    .help.buttons.bugs configure -state normal
+
+    bind .help.buttons.warranty <1> "enterText .help.t.text \[Warranty\]
+         grayButton warranty"
+    .help.buttons.warranty configure -state normal
+
+    bind .help.buttons.conditions <1> "enterText .help.t.text \[Conditions\]
+         grayButton conditions" 
+    .help.buttons.conditions configure -state normal
+    disableButton .help.buttons.$button
+}
+
+# The text for errors in the MG environment variable
+#
+
+
+proc bookDetails {} {
+  return { \
+
+ This X interface was written by Bruce McKenzie, Ian Witten and Craig Nevill-Manning.  
+ For information on mg, see
+             Witten, I.H.,  Moffat, A. and Bell, T.C. (1994) "Managing gigabytes: compressing
+             and indexing documents and images." Van Nostrand Reinhold, New York.
+             ISBN 0-442-01863-0, US $54.95; call 1 (800) 544-0550 to order.
+}
+}
+
+proc initializationFailure {} {
+    return [format "%s\n%s" {\
+In order to run XMG you need to have already created and indexed some document collections,
+ and placed them in a directory whose name is given by the environment variable MGDATA.
+ Your MGDATA variable is unset or does not specify a directory with files in; therefore XMG
+ cannot proceed.
+} [bookDetails]]
+}
+
+proc mgqueryFailure {} {
+    return [format "%s\n%s" {\
+In order to run XMG you need to have already created and indexed some document collections,
+ and placed them in a directory whose name is given by the environment variable MGDATA.
+ There is a problem with the collection that you have chosen. Please re-index the collection before
+ you use it again.
+} [bookDetails]]
+
+}
+
+# The text for the help message
+#
+proc HelpText {} {
+    return [format "%s\n%s" {\
+This is an X interface to the mg information retrieval system.
+
+ Type a query in the "Query" box.
+ The first line or so of each matching document is listed. 
+ Click on any line to show the complete document.
+ Shift-click to show the document in its own window
+ Press Next and Previous to cycle through the list of documents (Shift works here too).
+ In document windows (other than the main window), typing n or p shows the next and previous 
+ documents; N or P creates a new window for them. q closes the most recently created window.
+ If you change the query, existing windows continue to refer to the original query.
+ 
+ You can select either Boolean or Ranked queries.
+ Boolean queries: use & for AND, | for OR, ! for NOT, and parentheses for grouping
+ Ranked queries:  documents are listed in order of relevance; a weight is shown alongside each
+                                  weights are normalized to 100 for the most relevant document
+                                  choose Approximate Ranking for greater retrieval speed. 
+ } [bookDetails]]
+}
+
+proc reformat {text} {
+    regsub -all "\n *\n *" $text "BLEQUEWFI" text
+    regsub -all "\[ \t\n\r\]+" $text " " text
+    regsub -all "BLEQUEWFI" $text "\n\n" text
+    return $text
+}
+
+proc Conditions {} {
+    return [reformat [join [sendandget ".conditions\nTHISWILLNEVERMATCHANYTHING\n"] "\n"]]
+}
+
+proc Warranty {} {
+    return [reformat [join [sendandget ".warranty\nTHISWILLNEVERMATCHANYTHING\n"] "\n"]]
+}
+
+proc Bugs {} {
+    return [reformat {
+Known shortcomings of xmg include:
+
+    Slow picture viewing.  Pictures are decompressed in their entirety before a window is created 
+        for the picture.  This often results in a long delay with no feedback. Moreover, when 
+        an image's document is displayed repeatedly, multiple identical images are shown.
+
+    Sequential viewing.  There is no way of going to the next document in the collection to see the
+        context of documents that have been retrieved.  This would be trivial to implement but
+        conflicts with the interpretation of the "previous" and "next" buttons.
+    }]
+}
+
+###############################################################################
+# The main program
+###############################################################################
+
+scan [info tclversion] "%d.%d" major minor
+
+if {$major < 7} {
+    puts "xmg requires version 7.0 of the tcl interpreter. You are using version $major.$minor"
+    exit
+}
+
+# Default font settings
+set defaulttextfont   -adobe-times-medium-r-normal--14-140-75-75-p-74-iso8859-1
+set defaultbuttonfont -adobe-times-bold-r-normal--14-140-75-75-p-77-iso8859-1
+set textfont   [query_resource textfont Textfont $defaulttextfont]
+set buttonfont [query_resource buttonfont Buttonfont $defaultbuttonfont]
+
+set MaxWindowHeight 10
+set HeadLength 100
+set scrolledto 0
+set ListBoxLength 12
+set windowname 0
+set Terminator "RECORD_END"
+set fileId ""
+set query ""
+set ImageString "::::::::::"
+set stacking ".m.t"
+
+tk_listboxSingleSelect Listbox
+option add *Text.wrap word
+
+option add *Text.font $textfont
+option add *Listbox.font $textfont
+option add *Entry.font $textfont
+
+option add *Button.font $buttonfont
+option add *Menubutton.font $buttonfont
+option add *Radiobutton.font $buttonfont
+option add *Checkbutton.font $buttonfont
+option add *Label.font $buttonfont
+option add *Menu.font $buttonfont
+
+wm title . "Managing Gigabytes"
+wm iconname . "xmg"
+frame .m -borderwidth 5 -relief raised
+
+# top part of frame
+frame .m.t
+
+# on the left side of the top part is the "buttons" panel
+frame .m.t.b
+mkButtons .m.t.b
+
+# on the right side is the "query" panel
+frame .m.t.q
+mkQuery .m.t.q
+
+set q [query_resource querytype Querytype approx-ranked]
+set booleanquery [expr \"$q\" == \"boolean\"]
+set approxquery [expr \"$q\" == \"approx-ranked\"]
+# the above lines will trigger changeQuerytype
+
+pack .m.t.b -side left -anchor nw
+pack .m.t.q -side right -anchor ne -fill both -expand yes
+
+# at the bottom is the "document" panel
+frame .m.d
+makeDocumentPanel .m.d
+
+.m.d.buttons.close configure -text "Exit" \
+    -command "destroy ."
+button .m.d.buttons.help -text "Help" -width 10 \
+	-command "mkHelp"
+
+pack .m.d.buttons.help -side right
+pack .m.t -side top -fill x -expand yes
+pack .m.d -side bottom -fill both -expand yes
+
+pack .m -fill both -expand yes
+wm geometry . +300+85
+
+set collection [query_resource collection Collection $collection]
+if {$argc>0} { set collection $argv }
+
+enterText .m.d.text [HelpText]
+
+initMG $collection
+
+###############################################################################
+###############################################################################
+# The following file selection box procedure was written by Andrew Donkin
+# of the University of Waikato. 
+###############################################################################
+
+proc FSBox { {fsBoxMessage "Select file:"}} {
+# xf ignore me 5
+  global fsBox
+
+  set fsBox(name) ""
+  set fsBox(internalPath) [pwd]
+  set fsBox(pattern) "*"
+  set fsBox(all) 0
+
+  # build widget structure
+
+  # start build of toplevel
+  if {"[info commands XFDestroy]" != ""} {
+    catch {XFDestroy .fsBox}
+  } {
+    catch {destroy .fsBox}
+  }
+  toplevel .fsBox -borderwidth 0
+  wm geometry .fsBox 350x300 
+  wm title .fsBox {File select box}
+  wm maxsize .fsBox 1000 1000
+  wm minsize .fsBox 100 100
+  # end build of toplevel
+
+  label .fsBox.message1 -anchor c -text "$fsBoxMessage"
+  frame .fsBox.frame1  -borderwidth 0  -relief raised
+
+  button .fsBox.frame1.ok  -text "OK"  -command "
+      global fsBox
+      set fsBox(name) \[.fsBox.file.file get\]
+      set fsBox(path) \[.fsBox.path.path get\]
+
+      set fsBox(internalPath) \[.fsBox.path.path get\]
+
+      if {\"\[info commands XFDestroy\]\" != \"\"} {
+        catch {XFDestroy .fsBox}
+      } {
+        catch {destroy .fsBox}
+      }"
+
+  button .fsBox.frame1.rescan -text "Rescan" -command {
+      global fsBox
+      FSBoxFSShow [.fsBox.path.path get] [.fsBox.pattern.pattern get] $fsBox(all)
+  }
+
+  button .fsBox.frame1.cancel -text "Cancel" -command "
+      global fsBox
+      set fsBox(name) {}
+      set fsBox(path) {}
+
+      if {\"\[info commands XFDestroy\]\" != \"\"} {
+        catch {XFDestroy .fsBox}
+      } {
+        catch {destroy .fsBox}
+      }"
+
+  frame .fsBox.path -borderwidth 0 -relief raised
+  frame .fsBox.path.paths 
+  label .fsBox.path.paths.paths -borderwidth 0 -text "Pathname:"
+
+  entry .fsBox.path.path -relief sunken
+  .fsBox.path.path insert 0 $fsBox(internalPath)
+  frame .fsBox.pattern -borderwidth 0 -relief raised
+  frame .fsBox.pattern.patterns 
+
+  label .fsBox.pattern.patterns.patterns -borderwidth 0 -text "Selection pattern:"
+  entry .fsBox.pattern.pattern -relief sunken
+  .fsBox.pattern.pattern insert 0 $fsBox(pattern)
+  
+  frame .fsBox.files -borderwidth 0 -relief raised
+  scrollbar .fsBox.files.vscroll -relief raised -command ".fsBox.files.files yview"
+  scrollbar .fsBox.files.hscroll -orient horiz -relief raised -command ".fsBox.files.files xview" 
+  listbox .fsBox.files.files -exportselection false -relief raised -xscrollcommand \
+        ".fsBox.files.hscroll set" -yscrollcommand ".fsBox.files.vscroll set"
+  frame .fsBox.file -borderwidth 0 -relief raised
+  label .fsBox.file.labelfile -text "Filename:"
+  entry .fsBox.file.file -relief sunken
+  focus .fsBox.file.file
+
+  .fsBox.file.file delete 0 end
+  .fsBox.file.file insert 0 $fsBox(name)
+  
+  checkbutton .fsBox.pattern.all -offvalue 0 -onvalue 1 -text "Show all files" -variable fsBox(all) -command {
+      global fsBox
+      FSBoxFSShow [.fsBox.path.path get] [.fsBox.pattern.pattern get] $fsBox(all)
+    }
+
+  FSBoxFSShow $fsBox(internalPath) $fsBox(pattern) $fsBox(all)
+
+  # bindings
+  bind .fsBox.files.files <Double-Button-1> "FSBoxFSFileSelectDouble %W %y"
+  bind .fsBox.files.files <ButtonPress-1> "FSBoxFSFileSelect %W %y"
+  bind .fsBox.files.files <Button1-Motion> "FSBoxFSFileSelect %W %y"
+  bind .fsBox.files.files <Shift-Button1-Motion> "FSBoxFSFileSelect %W %y"
+  bind .fsBox.files.files <Shift-ButtonPress-1> "FSBoxFSFileSelect %W %y"
+
+  bind .fsBox.path.path <Return> {
+    global fsBox
+    FSBoxFSShow [.fsBox.path.path get] [.fsBox.pattern.pattern get] $fsBox(all)
+    .fsBox.file.file icursor end
+    focus .fsBox.file.file}
+  catch "bind .fsBox.path.path <Up> {}"
+  bind .fsBox.path.path <Down> {
+    .fsBox.file.file icursor end
+    focus .fsBox.file.file}
+
+  bind .fsBox.file.file <Return> "
+    global fsBox
+    set fsBox(name) \[.fsBox.file.file get\]
+    set fsBox(path) \[.fsBox.path.path get\]
+    set fsBox(internalPath) \[.fsBox.path.path get\]
+
+    if {\"\[info commands XFDestroy\]\" != \"\"} {
+      catch {XFDestroy .fsBox}
+    } {
+      catch {destroy .fsBox}
+    }"
+  bind .fsBox.file.file <Up> {.fsBox.path.path icursor end
+    focus .fsBox.path.path}
+  bind .fsBox.file.file <Down> {.fsBox.pattern.pattern icursor end
+    focus .fsBox.pattern.pattern}
+
+  bind .fsBox.pattern.pattern <Return> {
+    global fsBox
+    FSBoxFSShow [.fsBox.path.path get] [.fsBox.pattern.pattern get] $fsBox(all)}
+  bind .fsBox.pattern.pattern <Up> {
+    .fsBox.file.file icursor end
+    focus .fsBox.file.file}
+  catch "bind .fsBox.pattern.pattern <Down> {}"
+
+  # packing
+  pack .fsBox.files.vscroll -side left -fill y 
+  pack .fsBox.files.hscroll -side bottom -fill x
+  pack .fsBox.files.files -side left -fill both -expand yes
+
+  pack .fsBox.file.labelfile -side left
+  pack .fsBox.file.file -side left -fill  both -expand yes
+
+  pack .fsBox.frame1.ok -side left -fill both -expand yes
+  pack .fsBox.frame1.rescan -side left -fill both -expand yes
+  pack .fsBox.frame1.cancel -side left -fill both -expand yes
+
+  pack .fsBox.path.paths.paths -side left
+
+  pack .fsBox.pattern.patterns.patterns -side left
+
+  pack .fsBox.path.paths -side left
+  pack .fsBox.path.path -side left -fill both -expand yes
+
+  pack .fsBox.pattern.patterns -side left
+  pack .fsBox.pattern.all -side right -fill both
+  pack .fsBox.pattern.pattern -side left -fill both -expand yes
+
+  pack .fsBox.message1 -side top -fill both
+  pack .fsBox.frame1 -side bottom -fill both
+  pack .fsBox.pattern -side bottom -fill both
+  pack .fsBox.file -side bottom -fill both
+  pack .fsBox.path -side bottom -fill both
+  pack .fsBox.files -side left -fill both -expand yes
+
+  update idletask
+  grab .fsBox
+  tkwait window .fsBox
+
+  if {"[string trim $fsBox(path)]" != "" ||
+      "[string trim $fsBox(name)]" != ""} {
+    if {"[string trimleft [string trim $fsBox(name)] /]" == ""} {
+      return [string trimright [string trim $fsBox(path)] /]
+    } {
+      return [string trimright [string trim $fsBox(path)] /]/[string trimleft [string trim $fsBox(name)] /]
+    }
+  }
+}
+
+proc FSBoxBindSelectOne { fsBoxW fsBoxY} {
+# xf ignore me 6
+
+  set fsBoxNearest [$fsBoxW nearest $fsBoxY]
+  if {$fsBoxNearest >= 0} {
+    $fsBoxW select from $fsBoxNearest
+    $fsBoxW select to $fsBoxNearest
+  }
+}
+
+proc FSBoxFSFileSelect { fsBoxW fsBoxY} {
+# xf ignore me 6
+  global fsBox
+
+  FSBoxBindSelectOne $fsBoxW $fsBoxY
+  set fsBoxNearest [$fsBoxW nearest $fsBoxY]
+  if {$fsBoxNearest >= 0} {
+    set fsBoxTmpEntry [$fsBoxW get $fsBoxNearest]
+    if {"[string index $fsBoxTmpEntry           [expr [string length $fsBoxTmpEntry]-1]]" == "/" ||
+        "[string index $fsBoxTmpEntry           [expr [string length $fsBoxTmpEntry]-1]]" == "@"} {
+      set fsBoxFileName [string range $fsBoxTmpEntry 0             [expr [string length $fsBoxTmpEntry]-2]]
+      if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]] &&
+          ![IsASymlink [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} {
+        set fsBoxFileName $fsBoxTmpEntry
+      }
+    } {
+      if {"[string index $fsBoxTmpEntry             [expr [string length $fsBoxTmpEntry]-1]]" == "*"} {
+        set fsBoxFileName [string range $fsBoxTmpEntry 0           [expr [string length $fsBoxTmpEntry]-2]]
+        if {![file executable $fsBox(internalPath)/$fsBoxFileName]} {
+          set fsBoxFileName $fsBoxTmpEntry
+        }
+      } {
+        set fsBoxFileName $fsBoxTmpEntry
+      }
+    }
+    if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} {
+      set fsBox(name) $fsBoxFileName
+      .fsBox.file.file delete 0 end
+      .fsBox.file.file insert 0 $fsBox(name)
+    }
+  }
+}
+
+proc FSBoxFSFileSelectDouble { fsBoxW fsBoxY} {
+# xf ignore me 6
+  global fsBox
+
+  FSBoxBindSelectOne $fsBoxW $fsBoxY
+  set fsBoxNearest [$fsBoxW nearest $fsBoxY]
+  if {$fsBoxNearest >= 0} {
+    set fsBoxTmpEntry [$fsBoxW get $fsBoxNearest]
+    if {"$fsBoxTmpEntry" == "../"} {
+      set fsBoxTmpEntry [string trimright [string trim $fsBox(internalPath)] "@/"]
+      if {"$fsBoxTmpEntry" == ""} {
+        return
+      }
+      FSBoxFSShow [file dirname $fsBoxTmpEntry]         [.fsBox.pattern.pattern get] $fsBox(all)
+      .fsBox.path.path delete 0 end
+      .fsBox.path.path insert 0 $fsBox(internalPath)
+    } {
+      if {"[string index $fsBoxTmpEntry [expr [string length $fsBoxTmpEntry]-1]]" == "/" ||
+          "[string index $fsBoxTmpEntry [expr [string length $fsBoxTmpEntry]-1]]" == "@"} {
+        set fsBoxFileName [string range $fsBoxTmpEntry 0               [expr [string length $fsBoxTmpEntry]-2]]
+        if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]] &&
+            ![IsASymlink [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} {
+          set fsBoxFileName $fsBoxTmpEntry
+        }
+      } {
+        if {"[string index $fsBoxTmpEntry [expr [string length $fsBoxTmpEntry]-1]]" == "*"} {
+          set fsBoxFileName [string range $fsBoxTmpEntry 0                 [expr [string length $fsBoxTmpEntry]-2]]
+          if {![file executable $fsBox(internalPath)/$fsBoxFileName]} {
+            set fsBoxFileName $fsBoxTmpEntry
+          }
+        } {
+          set fsBoxFileName $fsBoxTmpEntry
+        }
+      }
+      if {[IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} {
+        set fsBox(internalPath) "[string trimright $fsBox(internalPath) {/@}]/$fsBoxFileName"
+        FSBoxFSShow $fsBox(internalPath) [.fsBox.pattern.pattern get] $fsBox(all)
+        .fsBox.path.path delete 0 end
+        .fsBox.path.path insert 0 $fsBox(internalPath)
+      } {
+        set fsBox(name) $fsBoxFileName
+        set fsBox(path) $fsBox(internalPath)
+        if {"[info commands XFDestroy]" != ""} {
+          catch {XFDestroy .fsBox}
+        } {
+          catch {destroy .fsBox}
+        }
+      }
+    }
+  }
+}
+
+proc FSBoxFSShow { fsBoxPath fsBoxPattern fsBoxAll} {
+# xf ignore me 6
+  global fsBox
+
+  set fsBox(pattern) $fsBoxPattern
+
+  if {[file exists $fsBoxPath] && [file readable $fsBoxPath] &&
+      [IsADir $fsBoxPath]} {
+    set fsBox(internalPath) $fsBoxPath
+  } {
+    if {[file exists $fsBoxPath] && [file readable $fsBoxPath] &&
+        [IsAFile $fsBoxPath]} {
+      set fsBox(internalPath) [file dirname $fsBoxPath]
+      .fsBox.file.file delete 0 end
+      .fsBox.file.file insert 0 [file tail $fsBoxPath]
+      set fsBoxPath $fsBox(internalPath)
+    } {
+      while {"$fsBoxPath" != "" && "$fsBoxPath" != "/" &&
+             ![file isdirectory $fsBoxPath]} {
+        set fsBox(internalPath) [file dirname $fsBoxPath]
+         set fsBoxPath $fsBox(internalPath)
+      }
+    }
+  }
+  if {"$fsBoxPath" == ""} {
+    set fsBoxPath "/"
+    set fsBox(internalPath) "/"
+  }
+  .fsBox.path.path delete 0 end
+  .fsBox.path.path insert 0 $fsBox(internalPath)
+
+  if {[.fsBox.files.files size] > 0} {
+    .fsBox.files.files delete 0 end
+  }
+  if {$fsBoxAll} {
+    if {[catch "exec ls -F -a $fsBoxPath" fsBoxResult]} {
+      puts stderr "$fsBoxResult"
+    }
+  } {
+    if {[catch "exec ls -F $fsBoxPath" fsBoxResult]} {
+      puts stderr "$fsBoxResult"
+    }
+  }
+  set fsBoxElementList [lsort $fsBoxResult]
+
+  foreach fsBoxCounter [winfo children .fsBox.pattern.patterns.patterns] {
+    if {[string length [info commands XFDestroy]] > 0} {
+      catch {XFDestroy $fsBoxCounter}
+    } {
+      catch {destroy $fsBoxCounter}
+    }
+  }
+  menu .fsBox.pattern.patterns.patterns.menu
+
+  if {"$fsBoxPath" != "/"} {
+    .fsBox.files.files insert end "../"
+  }
+  foreach fsBoxCounter $fsBoxElementList {
+    if {[string match $fsBoxPattern $fsBoxCounter] ||
+        [IsADir [string trimright $fsBoxPath/$fsBoxCounter "/@"]]} {
+      if {"$fsBoxCounter" != "../" &&
+          "$fsBoxCounter" != "./"} {
+        .fsBox.files.files insert end $fsBoxCounter
+      }
+    }
+  }
+}
+
+proc IsADir { pathName} {
+# xf ignore me 5
+  if {[file isdirectory $pathName]} {
+    return 1
+  } {
+    catch "file type $pathName" fileType
+    if {"$fileType" == "link"} {
+      if {[catch "file readlink $pathName" linkName]} {
+        return 0
+      }
+      catch "file type $linkName" fileType
+      while {"$fileType" == "link"} {
+        if {[catch "file readlink $linkName" linkName]} {
+          return 0
+        }
+        catch "file type $linkName" fileType
+      }
+      return [file isdirectory $linkName]
+    }
+  }
+  return 0
+}
+
+proc IsAFile { fileName} {
+# xf ignore me 5
+  if {[file isfile $fileName]} {
+    return 1
+  } {
+    catch "file type $fileName" fileType
+    if {"$fileType" == "link"} {
+      if {[catch "file readlink $fileName" linkName]} {
+        return 0
+      }
+      catch "file type $linkName" fileType
+      while {"$fileType" == "link"} {
+        if {[catch "file readlink $linkName" linkName]} {
+          return 0
+        }
+        catch "file type $linkName" fileType
+      }
+      return [file isfile $linkName]
+    }
+  }
+  return 0
+}
+
+proc IsASymlink { fileName} {
+# xf ignore me 5
+  catch "file type $fileName" fileType
+  if {"$fileType" == "link"} {
+    return 1
+  }
+  return 0
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/Makefile.in	(revision 16561)
@@ -0,0 +1,387 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the MG system
+# Copyright (C) 1994  Neil Sharman;  1995 Tim Shimmin
+#
+# 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.
+#
+#       @(#)Makefile.in	1.10 22 Mar 1994
+#
+###########################################################################
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+subdir = src/text
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+AR = ar
+CC = @CC@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -fpic
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = ../../lib/libmglib.a @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+YACC = @YACC@
+
+##########################################
+# Uncomment for compiling for the nzdl project
+#
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -fpic -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM
+##########################################
+
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+ 
+COMPILE = $(CC) -c $(DEFS) $(TREC_MODE) $(READLINE) $(INCLUDES) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+ 
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+ 
+.SUFFIXES: ._c ._o
+.c._c:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $@
+._c._o:
+	@echo $(COMPILE) $<
+	@rm -f _$*.c
+	@ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+.c._o:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $*._c
+	@echo $(COMPILE) $*._c
+	@rm -f _$*.c
+	@ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+ 
+INCLUDES = -I../../../.. -I../.. -I../../lib -I.
+
+#############################################################################
+#
+# Uncomment (and possibly edit) the following lines if you wish
+# to use the GNU readline library for the input in mgquery
+# 
+#READLINE = -DGNU_READLINE -I/opt/gnu/include/readline -L/opt/gnu/lib
+#READLINE_LIBS = -lreadline -ltermcap
+#
+#
+#############################################################################
+
+#############################################################################
+#
+# When working with the trec collection this should be uncommented.
+#
+# TREC_MODE = -DTREC_MODE
+#
+#
+#############################################################################
+
+SOURCES = \
+  backend.c               locallib.c              mg_weights_build.c   \
+  bool_optimiser.c        mg.special.c            mgdictlist.c         \
+  bool_parser.c           mg_compression_dict.c   mgquery.c            \
+  bool_query.c            mg_errors.c             mgstat.c             \
+  bool_tester.c           mg_fast_comp_dict.c     mg_decompress_text.c \
+  bool_tree.c             mg_files.c              query.docnums.c      \
+  commands.c              mg_hilite_words.c       query.ranked.c       \
+  comp_dict.c             mg_invf_dict.c          read_line.c          \
+  environment.c           mg_invf_dump.c          stem_search.c        \
+  mg_invf_merge.c         stemmer.c               mg_stem_idx.c        \
+  mg_invf_rebuild.c       term_lists.c            mgstemidxlist.c      \
+  invf_get.c              mg_passes.c             text.pass1.c         \
+  ivf.pass1.c             mg_perf_hash_build.c    text.pass2.c         \
+  ivf.pass2.c             mg_text_estimate.c      text_get.c           \
+  lists.c                 mg_text_merge.c         weights.c            \
+  query_term_list.c       words.c                 mg_passes_4jni.c
+
+HEADERS = \
+  backend.h          conditions.h       locallib.h         term_lists.h \
+  bool_optimiser.h   environment.h      mg.h               text.h       \
+  bool_parser.h      globals.h          mg_errors.h        text_get.h   \
+  bool_query.h       hash.h             mg_files.h         warranty.h   \
+  bool_tree.h        help.mg.h          mg_merge.h         weights.h    \
+  build.h            invf.h             read_line.h        words.h      \
+  commands.h         invf_get.h         stem_search.h                   \
+  comp_dict.h        lists.h            stemmer.h                       \
+  query_term_list.h
+
+
+MISC          = Makefile.in help.mg.src bool_parser.y warranty.src conditions.src
+
+ALLFILES      =	$(SOURCES) $(HEADERS) $(MISC) $(MAN)
+
+DISTFILES     = $(ALLFILES) 
+
+EXEC          = mgquery mg_weights_build mgstat mg_invf_dump mg_invf_dict \
+                mg_invf_rebuild mgdictlist mg_passes mg_perf_hash_build \
+                mg_compression_dict mg_text_estimate mg_fast_comp_dict \
+                mg_hilite_words mg_invf_merge mg_text_merge bool_tester \
+                mg_stem_idx mgstemidxlist mg_decompress_text 
+
+MAN           = mgquery.1 mg_weights_build.1 mgstat.1 mg_invf_dump.1 mg_invf_dict.1 \
+                mg_invf_rebuild.1 mgdictlist.1 mg_passes.1 mg_perf_hash_build.1 \
+                mg_compression_dict.1 mg_text_estimate.1 mg_fast_comp_dict.1 \
+                mg_hilite_words.1 mg_invf_merge.1 mg_text_merge.1 \
+                mg_stem_idx.1 mgstemidxlist.1 mg_decompress_text.1 
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+all:	$(EXEC) libmgtext.a libmgpass.a
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+help.mg.h : help.mg.src
+	sed -e 's/\\/\\\\/g' -e 's/\"/\\\"/g' help.mg.src | $(AWK) '/^#/ { next; };{ if (x == 0) printf("static char *help_str[] = {\n\"%s\\n\"", $$0); else printf(",\n\"%s\\n\"", $$0); x = 1; }; END { printf("};\n"); }' - >help.mg.h
+
+warranty.h : warranty.src
+	sed  -e 's/\\/\\\\/g' -e 's/\"/\\\"/g' warranty.src | $(AWK) '/^#/ { next; };{ if (x == 0) printf("static char *warranty_str[] = {\n\"%s\\n\"", $$0); else printf(",\n\"%s\\n\"", $$0); x = 1; }; END { printf("};\n"); }' - >warranty.h
+
+conditions.h : conditions.src
+	sed -e 's/\\/\\\\/g' -e 's/\"/\\\"/g' conditions.src | $(AWK) '/^#/ { next; };{ if (x == 0) printf("static char *cond_str[] = {\n\"%s\\n\"", $$0); else printf(",\n\"%s\\n\"", $$0); x = 1; }; END { printf("};\n"); }' - >conditions.h
+
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+HILITE_OBJS = mg_hilite_words$o stemmer$o locallib$o words$o environment$o
+
+mg_hilite_words: $(HILITE_OBJS)
+	$(LINK) $(HILITE_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+bool_parser.c: bool_parser.y
+	$(YACC) $?
+	mv y.tab.c $@
+
+BOOL_OBJS = bool_tester$o bool_tree$o bool_parser$o bool_optimiser$o  \
+            term_lists$o stemmer$o stem_search$o mg_errors$o query_term_list$o \
+	    words$o environment$o
+
+bool_tester: $(BOOL_OBJS)
+	$(LINK) $(BOOL_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+QUERY_OBJS = \
+	mgquery$o locallib$o lists$o \
+	query.ranked$o query.docnums$o stem_search$o \
+	environment$o commands$o weights$o text_get$o stemmer$o \
+	read_line$o mg_errors$o backend$o invf_get$o \
+	term_lists$o bool_tree$o bool_parser$o bool_optimiser$o bool_query$o \
+	query_term_list$o words$o
+
+mgquery: $(QUERY_OBJS)	
+	$(LINK) $(READLINE) $(QUERY_OBJS) $(READLINE_LIBS) $(LIBS)
+
+commands$o : help.mg.h warranty.h conditions.h
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+PASSES_OBJS = \
+	mg_passes$o text.pass1$o comp_dict$o stemmer$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o environment$o
+
+mg_passes: $(PASSES_OBJS)
+	$(LINK) $(PASSES_OBJS) $(LIBS) 
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+WEIGHTS_BUILD_OBJS = mg_weights_build$o mg_files$o
+
+mg_weights_build: $(WEIGHTS_BUILD_OBJS)
+	$(LINK) $(WEIGHTS_BUILD_OBJS) $(LIBS)
+
+COMP_DICT_OBJS = mg_compression_dict$o mg_files$o locallib$o
+
+mg_compression_dict: $(COMP_DICT_OBJS)
+	$(LINK) $(COMP_DICT_OBJS) $(LIBS)
+
+FAST_COMP_OBJS = mg_fast_comp_dict$o mg_files$o locallib$o
+
+mg_fast_comp_dict: $(FAST_COMP_OBJS)
+	$(LINK) $(FAST_COMP_OBJS) $(LIBS)
+
+TEXT_EST_OBJS = mg_text_estimate$o locallib$o comp_dict$o mg_files$o
+
+mg_text_estimate: $(TEXT_EST_OBJS)
+	$(LINK) $(TEXT_EST_OBJS) $(LIBS)
+
+STAT_OBJS = mgstat$o mg_files$o locallib$o
+
+mgstat:	$(STAT_OBJS)
+	$(LINK) $(STAT_OBJS) $(LIBS)
+
+INVF_DUMP_OBJS = mg_invf_dump$o locallib$o mg_files$o
+
+mg_invf_dump: $(INVF_DUMP_OBJS)
+	$(LINK) $(INVF_DUMP_OBJS) $(LIBS)
+
+INVF_DICT_OBJS = mg_invf_dict$o mg_files$o locallib$o
+
+mg_invf_dict: $(INVF_DICT_OBJS)
+	$(LINK) $(INVF_DICT_OBJS) $(LIBS)
+
+INVF_REB_OBJS = mg_invf_rebuild$o locallib$o
+
+mg_invf_rebuild: $(INVF_REB_OBJS)
+	$(LINK) $(INVF_REB_OBJS) $(LIBS)
+
+DICTLIST_OBJS = mgdictlist$o locallib$o
+
+mgdictlist: $(DICTLIST_OBJS)
+	$(LINK) $(DICTLIST_OBJS) $(LIBS)
+
+PERF_HASH_OBJS = mg_perf_hash_build$o mg_files$o
+
+mg_perf_hash_build: $(PERF_HASH_OBJS)
+	$(LINK) $(PERF_HASH_OBJS) $(LIBS)
+
+STEM_IDX_OBJS = \
+                mg_stem_idx$o mg_files$o stemmer$o locallib$o \
+                mg_errors$o term_lists$o
+
+mg_stem_idx: $(STEM_IDX_OBJS)
+	$(LINK) $(STEM_IDX_OBJS) $(LIBS)
+
+STEM_IDX_LIST_OBJS = mgstemidxlist$o mg_files$o
+
+mgstemidxlist: $(STEM_IDX_LIST_OBJS)
+	$(LINK) $(STEM_IDX_LIST_OBJS) $(LIBS)
+
+DECOMPRESS_TEXT_OBJS = \
+                mg_decompress_text.o query_term_list.o mg_files.o mg_errors.o \
+                text_get.o locallib.o backend.o stem_search.o \
+                term_lists.o stemmer.o weights.o invf_get.o lists.o
+
+mg_decompress_text: $(DECOMPRESS_TEXT_OBJS)
+	$(LINK) $(DECOMPRESS_TEXT_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+TEXT_MERGE_OBJS = mg_text_merge$o mg_files$o locallib$o
+
+mg_text_merge: $(TEXT_MERGE_OBJS)
+	$(LINK) $(TEXT_MERGE_OBJS) $(LIBS)
+
+INVF_MERGE_OBJS = mg_invf_merge$o mg_files$o locallib$o
+
+mg_invf_merge: $(INVF_MERGE_OBJS)
+	$(LINK) $(INVF_MERGE_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+ansi2knr: ansi2knr.o
+	$(LINK) ansi2knr.o $(LIBS)
+
+install: execinstall #maninstall
+uninstall: execuninstall manuninstall
+
+execinstall: $(EXEC)
+	$(top_srcdir)/mkinstalldirs $(bindir); \
+	for name in $(EXEC); do \
+	  $(INSTALL_PROGRAM) $$name \
+	  $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+execuninstall:
+	for name in $(EXEC); do \
+	 rm -f $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+maninstall: $(MAN)
+	$(top_srcdir)/mkinstalldirs $(mandir); \
+	for f in $(MAN); do \
+	  $(INSTALL_DATA) $$f $(mandir)/$$f ;\
+	done
+
+manuninstall: $(MAN)
+	for f in $(MAN); do \
+	  rm -f $(mandir)/$$f ;\
+	done
+
+LIB_OBJS = \
+	query_term_list$o \
+	locallib$o lists$o \
+	query.ranked$o query.docnums$o stem_search$o \
+	environment$o commands$o weights$o text_get$o stemmer$o \
+	read_line$o mg_errors$o backend$o invf_get$o \
+	term_lists$o bool_tree$o bool_parser$o bool_optimiser$o \
+	bool_query$o words$o
+
+libmgtext.a: $(LIB_OBJS)
+	rm -f libmgtext.a
+	$(AR) cru libmgtext.a $(LIB_OBJS)
+	$(RANLIB) libmgtext.a
+
+JNI_LIB_OBJS = \
+	mg_passes_4jni$o text.pass1$o comp_dict$o stemmer$o \
+	text.pass2$o locallib$o  words$o environment$o\
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o 
+
+libmgpass.a: $(JNI_LIB_OBJS)
+	rm -f libmgpass.a
+	$(AR) cru libmgpass.a $(JNI_LIB_OBJS)
+	$(RANLIB) libmgpass.a
+
+mostlyclean:
+	rm -f *$o _*.c _*.o *._c *._o core core.*
+ 
+clean: mostlyclean
+	rm -f $(EXEC) libmgtext.a libmgpass.a
+ 
+distclean: clean
+	rm -f ansi2knr
+	rm -f Makefile
+ 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+ 
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+	  || cp -p $(srcdir)/$$file $(distdir); \
+	done
+  
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exc
+
+
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/backend.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/backend.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/backend.c	(revision 16561)
@@ -0,0 +1,838 @@
+/**************************************************************************
+ *
+ * backend.c -- Underlying routines for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+#include "filestats.h"
+#include "sptree.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+#include "mg_files.h"
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "stem_search.h"
+#include "invf_get.h"
+#include "text_get.h"
+#include "weights.h"
+#include "locallib.h"
+#include "mg_errors.h"
+
+
+static File *
+OpenFile (char *base, char *suffix, unsigned long magic, int *ok)
+{
+  char FileName[512];
+  File *F;
+  sprintf (FileName, "%s%s", base, suffix);
+  if (!(F = Fopen (FileName, "rb", 0)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      mg_errno = MG_NOFILE;
+      MgErrorData (FileName);
+      if (ok)
+	*ok = 0;
+      return (NULL);
+    }
+  if (magic)
+    {
+      unsigned long m;
+      if (fread ((char *) &m, sizeof (m), 1, F->f) == 0)
+	{
+	  mg_errno = MG_READERR;
+	  MgErrorData (FileName);
+	  if (ok)
+	    *ok = 0;
+	  Fclose (F);
+	  return (NULL);
+	}
+      NTOHUL(m);  /* [RPAP - Jan 97: Endian Ordering] */
+      if (m != magic)
+	{
+	  mg_errno = MG_BADMAGIC;
+	  MgErrorData (FileName);
+	  if (ok)
+	    *ok = 0;
+	  Fclose (F);
+	  return (NULL);
+	}
+    }
+  return (F);
+}
+
+
+static int 
+open_all_files (query_data * qd)
+{
+  int ok = 1;
+
+  qd->File_text = OpenFile (qd->textpathname, TEXT_SUFFIX, /* [RJM 06/97: text filename] */
+			    MAGIC_TEXT, &ok);
+  qd->File_fast_comp_dict = OpenFile (qd->textpathname, /* [RJM 06/97: text filename] */
+				      TEXT_DICT_FAST_SUFFIX, MAGIC_FAST_DICT, NULL);
+  if (!qd->File_fast_comp_dict)
+    {
+      qd->File_comp_dict = OpenFile (qd->textpathname, /* [RJM 06/97: text filename] */
+				     TEXT_DICT_SUFFIX, MAGIC_DICT, &ok);
+      qd->File_aux_dict = OpenFile (qd->textpathname, /* [RJM 06/97: text filename] */
+				    TEXT_DICT_AUX_SUFFIX, MAGIC_AUX_DICT, NULL);
+    }
+  else
+    qd->File_comp_dict = qd->File_aux_dict = NULL;
+
+  qd->File_stem = OpenFile (qd->pathname, INVF_DICT_BLOCKED_SUFFIX,
+			    MAGIC_STEM, &ok);
+
+  /* [RPAP - Jan 97: Stem Index Change]
+     These will fail if collection not built with stem indexes */
+  qd->File_stem1 = OpenFile (qd->pathname, INVF_DICT_BLOCKED_1_SUFFIX,
+			     MAGIC_STEM_1, NULL);
+  qd->File_stem2 = OpenFile (qd->pathname, INVF_DICT_BLOCKED_2_SUFFIX,
+			     MAGIC_STEM_2, NULL);
+  qd->File_stem3 = OpenFile (qd->pathname, INVF_DICT_BLOCKED_3_SUFFIX,
+			     MAGIC_STEM_3, NULL);
+
+  qd->File_invf = OpenFile (qd->pathname, INVF_SUFFIX,
+			    MAGIC_INVF, &ok);
+
+  /* These will fail if a level 1 inverted file was created because there 
+     will be no document weights */
+  qd->File_text_idx_wgt = OpenFile (qd->pathname, TEXT_IDX_WGT_SUFFIX,
+				    MAGIC_TEXI_WGT, NULL);
+  qd->File_weight_approx = OpenFile (qd->pathname, APPROX_WEIGHTS_SUFFIX,
+				     MAGIC_WGHT_APPROX, NULL);
+  if (qd->File_text_idx_wgt == NULL && qd->File_weight_approx == NULL)
+    qd->File_text_idx = OpenFile (qd->textpathname, /* [RJM 06/97: text filename] */
+				  TEXT_IDX_SUFFIX, MAGIC_TEXI, NULL);
+  else
+    qd->File_text_idx = NULL;
+
+
+  if (!ok)
+    {
+      Fclose (qd->File_text);
+      if (qd->File_fast_comp_dict)
+	Fclose (qd->File_fast_comp_dict);
+      if (qd->File_comp_dict)
+	Fclose (qd->File_comp_dict);
+      Fclose (qd->File_stem);
+
+      /* [RPAP - Jan 97: Stem Index Change] */
+      if (qd->File_stem1)
+	Fclose (qd->File_stem1);
+      if (qd->File_stem2)
+	Fclose (qd->File_stem2);
+      if (qd->File_stem3)
+	Fclose (qd->File_stem3);
+
+      Fclose (qd->File_invf);
+      if (qd->File_text_idx_wgt)
+	Fclose (qd->File_text_idx_wgt);
+      if (qd->File_weight_approx)
+	Fclose (qd->File_weight_approx);
+      if (qd->File_text_idx)
+	Fclose (qd->File_text_idx);
+      return (-1);
+    }
+  return (0);
+
+}
+
+static void 
+close_all_files (query_data * qd)
+{
+  Fclose (qd->File_text);
+  if (qd->File_fast_comp_dict)
+    Fclose (qd->File_fast_comp_dict);
+  if (qd->File_aux_dict)
+    Fclose (qd->File_aux_dict);
+  if (qd->File_comp_dict)
+    Fclose (qd->File_comp_dict);
+  Fclose (qd->File_stem);
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (qd->File_stem1)
+    Fclose (qd->File_stem1);
+  if (qd->File_stem2)
+    Fclose (qd->File_stem2);
+  if (qd->File_stem3)
+    Fclose (qd->File_stem3);
+
+  Fclose (qd->File_invf);
+  if (qd->File_text_idx_wgt)
+    Fclose (qd->File_text_idx_wgt);
+  if (qd->File_weight_approx)
+    Fclose (qd->File_weight_approx);
+  if (qd->File_text_idx)
+    Fclose (qd->File_text_idx);
+}
+
+/* If textname is equal to null then name will be used instead */
+/* [RJM 06/97: text filename] */
+query_data *
+InitQuerySystem (char *dir, char *name, char *textname, InitQueryTimes * iqt)
+{
+  query_data *qd;
+  char *s;
+
+  if (textname == NULL) textname = name; /* [RJM 06/97: text filename] */
+
+  if (!(qd = Xmalloc (sizeof (query_data))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  bzero ((char *) qd, sizeof (*qd));
+
+  qd->mem_in_use = qd->max_mem_in_use = 0;
+
+  qd->doc_pos = qd->buf_in_use = 0;
+  qd->TextBufferLen = 0;
+  qd->DL = NULL;
+
+  /* [RPAP - Feb 97: Term Frequency] */
+  qd->TL = NULL;
+  qd->QTL = NULL;
+
+  qd->TextBuffer = NULL;
+
+  qd->tot_hops_taken = 0;
+  qd->tot_num_of_ptrs = 0;
+  qd->tot_num_of_accum = 0;
+  qd->tot_num_of_terms = 0;
+  qd->tot_num_of_ans = 0;
+  qd->tot_text_idx_lookups = 0;
+
+  qd->hops_taken = 0;
+  qd->num_of_ptrs = 0;
+  qd->num_of_accum = 0;
+  qd->num_of_terms = 0;
+  qd->num_of_ans = 0;
+  qd->text_idx_lookups = 0;
+
+  qd->pathname = NULL; /* RJM 06/97: text filename] */
+  qd->textpathname = NULL; /* RJM 06/97: text filename] */
+
+  s = strrchr (dir, '/');
+  if (s && *(s + 1) == '\0')
+    {
+      /* [RJM 06/97: text filename] */
+      if (!(qd->pathname = Xmalloc (strlen (dir) + strlen (name) + 1)) ||
+	  !(qd->textpathname = Xmalloc (strlen (dir) + strlen (textname) + 1)))
+	{
+	  mg_errno = MG_NOMEM;
+	  if (qd->pathname) Xfree (qd->pathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+      sprintf (qd->pathname, "%s%s", dir, name);
+      sprintf (qd->textpathname, "%s%s", dir, textname); /* [RJM 06/97: text filename] */
+    }
+
+  else
+    {
+      /* [RJM 06/97: text filename] */
+      if (!(qd->pathname = Xmalloc (strlen (dir) + strlen (name) + 2)) ||
+	  !(qd->textpathname = Xmalloc (strlen (dir) + strlen (textname) + 2)))
+	{
+	  mg_errno = MG_NOMEM;
+	  if (qd->pathname) Xfree (qd->pathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+      if (dir == NULL || dir[0] == '\0') {
+	sprintf (qd->pathname, "%s", name);
+	sprintf (qd->textpathname, "%s", textname); /* [RJM 06/97: text filename] */
+      } else {
+	sprintf (qd->pathname, "%s%s", dir, name);
+	sprintf (qd->textpathname, "%s%s", dir, textname); /* [RJM 06/97: text filename] */
+      }
+#else
+      sprintf (qd->pathname, "%s/%s", dir, name);
+      sprintf (qd->textpathname, "%s/%s", dir, textname); /* [RJM 06/97: text filename] */
+#endif
+    }
+
+  if (open_all_files (qd) == -1)
+    {
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+
+  if (iqt)
+    GetTime (&iqt->Start);
+
+  /* Initialise the stemmed dictionary system */
+  if (!(qd->sd = ReadStemDictBlk (qd->File_stem)))
+    {
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if ((qd->sd->sdh.indexed & 7) && qd->File_stem1 && qd->File_stem2 && qd->File_stem3)
+    {
+      if (!(qd->sd->stem1 = ReadStemIdxBlk (qd->File_stem1)))
+	{
+	  FreeStemDict (qd->sd);
+	  close_all_files (qd);
+	  Xfree (qd->pathname);
+	  Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+      if (!(qd->sd->stem2 = ReadStemIdxBlk (qd->File_stem2)))
+	{
+	  FreeStemDict (qd->sd);
+	  close_all_files (qd);
+	  Xfree (qd->pathname);
+	  Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+      if (!(qd->sd->stem3 = ReadStemIdxBlk (qd->File_stem3)))
+	{
+	  FreeStemDict (qd->sd);
+	  close_all_files (qd);
+	  Xfree (qd->pathname);
+	  Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+      }
+  else if (qd->sd->sdh.indexed != 0)
+    {
+      FreeStemDict (qd->sd);
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+  else
+    {
+      if (qd->File_stem1)
+	Fclose (qd->File_stem1);
+      if (qd->File_stem2)
+	Fclose (qd->File_stem2);
+      if (qd->File_stem3)
+	Fclose (qd->File_stem3);
+      qd->File_stem1 = NULL;
+      qd->File_stem2 = NULL;
+      qd->File_stem3 = NULL;
+      qd->sd->stem1 = NULL;
+      qd->sd->stem2 = NULL;
+      qd->sd->stem3 = NULL;
+    }
+
+  if (iqt)
+    GetTime (&iqt->StemDict);
+  if (qd->File_weight_approx)
+    {
+      if (!(qd->awd = LoadDocWeights (qd->File_weight_approx,
+				      qd->sd->sdh.num_of_docs)))
+	{
+	  FreeStemDict (qd->sd);
+	  close_all_files (qd);
+	  Xfree (qd->pathname);
+	  Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+	  Xfree (qd);
+	  return (NULL);
+	}
+    }
+  else
+    qd->awd = NULL;
+
+
+  if (iqt)
+    GetTime (&iqt->ApproxWeights);
+
+  if (!(qd->cd = LoadCompDict (qd->File_comp_dict, qd->File_aux_dict,
+			       qd->File_fast_comp_dict)))
+    {
+      if (qd->awd)
+	FreeWeights (qd->awd);
+      FreeStemDict (qd->sd);
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+
+  if (iqt)
+    GetTime (&iqt->CompDict);
+
+  if (!(qd->id = InitInvfFile (qd->File_invf, qd->sd)))
+    {
+      FreeCompDict (qd->cd);
+      if (qd->awd)
+	FreeWeights (qd->awd);
+      FreeStemDict (qd->sd);
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+  if ((qd->File_text_idx_wgt == NULL || qd->File_weight_approx == NULL) &&
+      qd->id->ifh.InvfLevel >= 2)
+    {
+      FreeInvfData (qd->id);
+      FreeCompDict (qd->cd);
+      if (qd->awd)
+	FreeWeights (qd->awd);
+      FreeStemDict (qd->sd);
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      mg_errno = MG_INVERSION;
+      return (NULL);
+    }
+  if (iqt)
+    GetTime (&iqt->Invf);
+
+  if (!(qd->td = LoadTextData (qd->File_text, qd->File_text_idx_wgt,
+			       qd->File_text_idx)))
+    {
+      FreeInvfData (qd->id);
+      FreeCompDict (qd->cd);
+      if (qd->awd)
+	FreeWeights (qd->awd);
+      FreeStemDict (qd->sd);
+      close_all_files (qd);
+      Xfree (qd->pathname);
+      Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+      Xfree (qd);
+      return (NULL);
+    }
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) || defined(NZDL)
+
+/*
+
+This code is based on the TREC_MODE code below to read the .paragraph
+file to determine what document numbers correspond to what paragraphs.
+This code is more space efficient, reading in the .paragraph file
+into memory as an accumulate docnum array.  Eg.  the .paragraph may contain
+
+	[5 3 6 4 7 9 4]
+
+indicating the first document has 5 paragraphs, the next 3, etc.
+This will be stored in memory as
+
+	[0 5 8 14 18 25 34 38]
+
+so a binary search can be performed.  The first 0 is for convenience;
+it prevents testing boundary conditions.
+
+
+The TREC_MODE code does this differently; it stores the array
+
+	[1 1 1 1 1 2 2 2 3 3 3 3 3 3 ....]
+
+allowing directy paragraph to docnum conversion, at the expense
+of memory.
+
+*/ 
+  qd->paragraph = NULL;
+
+  if (qd->id->ifh.InvfLevel == 3)
+    {
+      unsigned long magic;
+      FILE *paragraph;
+      int i;
+      char paraFile[512];
+
+      sprintf(paraFile, "%s%s", qd->pathname, INVF_PARAGRAPH_SUFFIX);
+      paragraph = fopen(paraFile, "rb");
+      if (!paragraph)
+	FatalError(1, "Unable to open 'paraFile'.", paraFile);
+
+      fread((void *)&magic, sizeof(magic), 1, paragraph);
+      qd->paragraph = Xmalloc((qd->td->cth.num_of_docs+1)*sizeof(int));
+      qd->paragraph[0] = 0;
+      for (i = 1; i <= qd->td->cth.num_of_docs; i++)
+	{
+	  int count;
+
+	  if (fread((void *)&count, sizeof(count), 1, paragraph) != 1)
+	    FatalError(1, "Unexpected EOF while reading '%s'.", paraFile);
+	  NTOHSI(count);  /* [RPAP - Jan 97: Endian Ordering] */
+	  qd->paragraph[i] = qd->paragraph[i-1]+count;
+	}
+
+      fclose (paragraph); /* [RJM 07/98: Memory Leak] */
+    }
+  
+  
+#endif
+
+#ifdef TREC_MODE
+  {
+    extern char *trec_ids;
+    extern long *trec_paras;
+    int size;
+    char FileName[512];
+    FILE *f;
+    if (!strstr (qd->pathname, "trec"))
+      goto error;
+    sprintf (FileName, "%s%s", qd->pathname, ".DOCIDS");
+    if (!(f = fopen (FileName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+      {
+	Message ("Unable to open \"%s\"", FileName);
+	goto error;
+      }
+    fseek (f, 0, 2);
+    size = ftell (f);
+    fseek (f, 0, 0);
+    trec_ids = Xmalloc (size);
+    if (!trec_ids)
+      {
+	fclose (f);
+	goto error;
+      }
+    fread (trec_ids, 1, size, f);
+    fclose (f);
+    if (qd->id->ifh.InvfLevel == 3)
+      {
+	int i, d;
+	unsigned long magic;
+	trec_paras = Xmalloc (qd->sd->sdh.num_of_docs * sizeof (long));
+	if (!trec_paras)
+	  {
+	    Xfree (trec_ids);
+	    trec_ids = NULL;
+	    goto error;
+	  }
+	sprintf (FileName, "%s%s", qd->pathname, INVF_PARAGRAPH_SUFFIX);
+	if (!(f = fopen (FileName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+	  {
+	    Message ("Unable to open \"%s\"", FileName);
+	    goto error;
+	  }
+	if (fread ((char *) &magic, sizeof (magic), 1, f) != 1 ||
+	    NTOHUL(magic) != MAGIC_PARAGRAPH)  /* [RPAP - Jan 97: Endian Ordering] */
+	  {
+	    fclose (f);
+	    Message ("Bad magic number in \"%s\"", FileName);
+	    goto error;
+	  }
+
+	for (d = i = 0; i < qd->td->cth.num_of_docs; i++)
+	  {
+	    int count;
+	    if (fread ((char *) &count, sizeof (count), 1, f) != 1)
+	      {
+		fclose (f);
+		goto error;
+	      }
+	    NTOHSI(count);  /* [RPAP - Jan 97: Endian Ordering] */
+	    while (count--)
+	      trec_paras[d++] = i;
+	  }
+	fclose (f);
+      }
+    goto ok;
+  error:
+    if (trec_ids)
+      Xfree (trec_ids);
+    if (trec_paras)
+      Xfree (trec_paras);
+    trec_ids = NULL;
+    trec_paras = NULL;
+  ok:
+    ;
+  }
+#endif
+
+  if (iqt)
+    GetTime (&iqt->Text);
+
+  return (qd);
+}
+
+
+
+
+
+
+/*
+ * Change the amount of memory currently in use
+ *
+ */
+void 
+ChangeMemInUse (query_data * qd, long delta)
+{
+  qd->mem_in_use += delta;
+  if (qd->mem_in_use > qd->max_mem_in_use)
+    qd->max_mem_in_use = qd->mem_in_use;
+}
+
+
+void 
+FinishQuerySystem (query_data * qd)
+{
+/* [RJM 07/98: Memory Leak] */
+#if defined(PARADOCNUM) || defined(NZDL)
+  if (qd->paragraph != NULL) {
+    Xfree (qd->paragraph);
+    qd->paragraph = NULL;
+  }
+#endif
+
+  FreeTextData (qd->td);
+  FreeInvfData (qd->id);
+  FreeCompDict (qd->cd);
+  if (qd->awd)
+    FreeWeights (qd->awd);
+  FreeStemDict (qd->sd);
+  close_all_files (qd);
+  Xfree (qd->textpathname); /* [RJM 06/97: text filename] */
+  Xfree (qd->pathname);
+  FreeQueryDocs (qd);
+  if (qd->TL != NULL) FreeTermList(&qd->TL);         /* [RJM 07/98: Memory Leak] */
+  if (qd->QTL != NULL) FreeQueryTermList(&qd->QTL);  /* [RJM 07/98: Memory Leak] */
+  Xfree (qd);
+
+  /* other global stuff hanging around */
+  MgErrorDeinit ();
+}
+
+
+void 
+ResetFileStats (query_data * qd)
+{
+  ZeroFileStats (qd->File_text);
+  if (qd->File_comp_dict)
+    ZeroFileStats (qd->File_comp_dict);
+  if (qd->File_fast_comp_dict)
+    ZeroFileStats (qd->File_fast_comp_dict);
+  ZeroFileStats (qd->File_stem);
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (qd->File_stem1)
+    ZeroFileStats (qd->File_stem1);
+  if (qd->File_stem2)
+    ZeroFileStats (qd->File_stem2);
+  if (qd->File_stem3)
+    ZeroFileStats (qd->File_stem3);
+
+  ZeroFileStats (qd->File_invf);
+  if (qd->File_text_idx_wgt)
+    ZeroFileStats (qd->File_text_idx_wgt);
+  if (qd->File_weight_approx)
+    ZeroFileStats (qd->File_weight_approx);
+  if (qd->File_text_idx)
+    ZeroFileStats (qd->File_text_idx);
+}
+
+
+void 
+TransFileStats (query_data * qd)
+{
+  qd->File_text->Current = qd->File_text->Cumulative;
+  if (qd->File_comp_dict)
+    qd->File_comp_dict->Current = qd->File_comp_dict->Cumulative;
+  if (qd->File_fast_comp_dict)
+    qd->File_fast_comp_dict->Current = qd->File_fast_comp_dict->Cumulative;
+  qd->File_stem->Current = qd->File_stem->Cumulative;
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (qd->File_stem1)
+    qd->File_stem1->Current = qd->File_stem1->Cumulative;
+  if (qd->File_stem2)
+    qd->File_stem2->Current = qd->File_stem2->Cumulative;
+  if (qd->File_stem3)
+    qd->File_stem3->Current = qd->File_stem3->Cumulative;
+
+  qd->File_invf->Current = qd->File_invf->Cumulative;
+  if (qd->File_text_idx_wgt)
+    qd->File_text_idx_wgt->Current = qd->File_text_idx_wgt->Cumulative;
+  if (qd->File_weight_approx)
+    qd->File_weight_approx->Current = qd->File_weight_approx->Cumulative;
+  if (qd->File_text_idx)
+    qd->File_text_idx->Current = qd->File_text_idx->Cumulative;
+}
+
+
+void 
+FreeTextBuffer (query_data * qd)
+{
+  if (qd->TextBuffer)
+    {
+      Xfree (qd->TextBuffer);
+      ChangeMemInUse (qd, -qd->TextBufferLen);
+    }
+  qd->TextBuffer = NULL;
+  qd->TextBufferLen = 0;
+}
+
+void 
+FreeQueryDocs (query_data * qd)
+{
+  qd->doc_pos = 0;
+  qd->buf_in_use = 0;
+  if (qd->DL)
+    {
+      int i;
+      for (i = 0; i < qd->DL->num; i++)
+	if (qd->DL->DE[i].CompTextBuffer)
+	  {
+	    Xfree (qd->DL->DE[i].CompTextBuffer);
+	    qd->DL->DE[i].CompTextBuffer = NULL;
+	    ChangeMemInUse (qd, -qd->DL->DE[i].Len);
+	  }
+      Xfree (qd->DL);
+    }
+  qd->DL = NULL;
+  FreeTextBuffer (qd);
+}
+
+int 
+LoadCompressedText (query_data * qd, int max_mem)
+{
+  DocEntry *DE;
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return -1;
+
+  DE = &qd->DL->DE[qd->doc_pos];
+  if (!DE->CompTextBuffer)
+    {
+      int i;
+      DocEntry *de;
+      for (i = 0, de = qd->DL->DE; i < qd->DL->num; i++, de++)
+	if (de->CompTextBuffer)
+	  {
+	    Xfree (de->CompTextBuffer);
+	    de->CompTextBuffer = NULL;
+	    ChangeMemInUse (qd, -de->Len);
+	  }
+      if (LoadBuffers (qd, &qd->DL->DE[qd->doc_pos], max_mem,
+		       qd->DL->num - qd->doc_pos) == -1)
+	return -1;
+    }
+  return 0;
+}
+
+int 
+GetDocNum (query_data * qd)
+{
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return -1;
+  return qd->DL->DE[qd->doc_pos].DocNum;
+}
+
+DocEntry *
+GetDocChain (query_data * qd)
+{
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return NULL;
+  return &(qd->DL->DE[qd->doc_pos]);
+}
+
+float 
+GetDocWeight (query_data * qd)
+{
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return -1;
+  return qd->DL->DE[qd->doc_pos].Weight;
+}
+
+long 
+GetDocCompLength (query_data * qd)
+{
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return -1;
+  return qd->DL->DE[qd->doc_pos].Len;
+}
+
+
+u_char *
+GetDocText (query_data * qd, unsigned long *len)
+{
+  DocEntry *DE;
+  int ULen;
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return NULL;
+
+  DE = &qd->DL->DE[qd->doc_pos];
+
+  if (!DE->CompTextBuffer)
+    {
+      fprintf (stderr, "The compressed text buffer is NULL\n");
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  FreeTextBuffer (qd);
+
+  qd->TextBufferLen = (int) (qd->td->cth.ratio * 1.01 *
+			     DE->Len) + 100;
+  if (!(qd->TextBuffer = Xmalloc (qd->TextBufferLen)))
+    {
+      fprintf (stderr, "No memory for TextBuffer\n");
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  DecodeText (qd->cd, (u_char *) (DE->CompTextBuffer), DE->Len,
+	      (u_char *) (qd->TextBuffer), &ULen);
+  qd->TextBuffer[ULen] = '\0';
+
+  if (ULen >= qd->TextBufferLen)
+    {
+      fprintf (stderr, "%d >= %d\n", ULen, qd->TextBufferLen);
+      mg_errno = MG_BUFTOOSMALL;
+      return (NULL);
+    }
+
+  if (len)
+    *len = ULen;
+
+  return qd->TextBuffer;
+}
+
+int 
+NextDoc (query_data * qd)
+{
+  if (qd->DL == NULL || qd->doc_pos >= qd->DL->num)
+    return 0;
+  qd->doc_pos++;
+  return qd->doc_pos < qd->DL->num;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/backend.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/backend.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/backend.h	(revision 16561)
@@ -0,0 +1,261 @@
+/**************************************************************************
+ *
+ * backend.h -- Underlying routines and datastructures for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 BACKEND_H
+#define BACKEND_H
+
+#include "sysfuncs.h"
+
+#include "timing.h"
+#include "lists.h"
+#include "term_lists.h"
+#include "query_term_list.h"  /* [RPAP - Feb 97: Term Frequency] */
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+
+
+typedef struct invf_data
+  {
+    File *InvfFile;
+    unsigned long N;
+    unsigned long Nstatic;	/* N parameter for decoding inverted file entries */
+    struct invf_file_header ifh;
+  }
+invf_data;
+
+typedef struct text_data
+  {
+    File *TextFile;
+    File *TextIdxFile;
+    File *TextIdxWgtFile;
+    long current_pos;
+    struct
+      {
+	unsigned long Start;
+	float Weight;
+      }
+     *idx_data;
+    compressed_text_header cth;
+  }
+text_data;
+
+
+typedef struct auxiliary_dict
+  {
+    aux_frags_header afh[2];
+    u_char *word_data[2];
+    u_char **words[2];
+    int blk_start[2][33], blk_end[2][33];	/* blk_start and blk_end are required
+						   for the hybrid methods */
+  }
+auxiliary_dict;
+
+
+typedef struct compression_dict
+  {
+    compression_dict_header cdh;
+    comp_frags_header *cfh[2];
+    unsigned long MemForCompDict;
+    u_char ***values[2];
+    u_char *escape[2];
+    huff_data *chars_huff[2];
+    u_long **chars_vals[2];
+    huff_data *lens_huff[2];
+    u_long **lens_vals[2];
+    auxiliary_dict *ad;
+    int fast_loaded;
+  }
+compression_dict;
+
+
+typedef struct stemmed_idx   /* [RPAP - Jan 97: Stem Index Change] */
+  {
+    File *stem_idx_file;
+    struct stem_idx_header sih;
+    u_char **index;
+    unsigned long *pos;
+    int active;
+    u_char *buffer;
+    unsigned long MemForStemIdx;
+  }
+stemmed_idx;
+
+
+typedef struct stemmed_dict
+  {
+    File *stem_file;
+    struct stem_dict_header sdh;
+    u_char **index;
+    unsigned long *pos;
+    int active;
+    u_char *buffer;
+    unsigned long MemForStemDict;
+
+    /* [RPAP - Jan 97: Stem Index Change] */
+    stemmed_idx *stem1;
+    stemmed_idx *stem2;
+    stemmed_idx *stem3;
+  }
+stemmed_dict;
+
+
+typedef struct approx_weights_data
+  {
+    double L;
+    double B;
+    unsigned long *DocWeights;
+    char bits;
+    float *table;
+    unsigned long mask;
+    unsigned long MemForWeights;
+    unsigned long num_of_docs;
+  }
+approx_weights_data;
+
+
+typedef struct RankedQueryInfo
+  {
+    int QueryFreqs;
+    int Exact;           /* use exact weights for ranking or not */
+    long MaxDocsToRetrieve;	/* may be -1 for all */
+    long MaxParasToRetrieve;
+    int Sort;        
+    char AccumMethod;		/* 'A' = array,  'S' = splay tree,  'H' = hash_table */
+    long MaxAccums;		/* may be -1 for all */
+    long MaxTerms;		/* may be -1 for all */
+    int StopAtMaxAccum; /* Stop at maximum accumulator or not */
+    long HashTblSize;
+    char *skip_dump;
+  }
+RankedQueryInfo;
+
+
+
+typedef struct BooleanQueryInfo
+  {
+    long MaxDocsToRetrieve;
+  }
+BooleanQueryInfo;
+
+
+/* [TS:24/Aug/94] - maximum number of characters in term string */
+#define MAXTERMSTRLEN 1023
+
+typedef struct query_data
+  {
+    stemmed_dict *sd;
+    compression_dict *cd;
+    approx_weights_data *awd;
+    invf_data *id;
+    text_data *td;
+#if defined(PARADOCNUM) || defined(NZDL)
+    int *paragraph;
+#endif
+    char *pathname;
+    char *textpathname; /* [RJM 06/97: text filename] */
+    File *File_text;
+    File *File_comp_dict;
+    File *File_aux_dict;
+    File *File_fast_comp_dict;
+    File *File_text_idx_wgt;
+    File *File_text_idx;
+    File *File_stem;
+
+    /* [RPAP - Jan 97: Stem Index Change] */
+    File *File_stem1;
+    File *File_stem2;
+    File *File_stem3;
+
+    File *File_invf;
+    File *File_weight_approx;
+    unsigned long mem_in_use, max_mem_in_use;
+    unsigned long num_of_ptrs, tot_num_of_ptrs;
+    unsigned long num_of_terms, tot_num_of_terms;
+    unsigned long num_of_accum, tot_num_of_accum;
+    unsigned long num_of_ans, tot_num_of_ans;
+    unsigned long hops_taken, tot_hops_taken;
+    unsigned long text_idx_lookups, tot_text_idx_lookups;
+    unsigned long max_buffers;
+    unsigned doc_pos;
+    unsigned buf_in_use;
+    DocList *DL;
+    TermList *TL;		/* [TS:Oct/94] - so term list for query can easily be accessed */
+    u_char *TextBuffer;
+    int TextBufferLen;
+    QueryTermList *QTL;    /* [RPAP - Feb 97: Term Frequency] */
+  }
+query_data;
+
+
+
+typedef struct InitQueryTimes
+  {
+    ProgTime Start;
+    ProgTime StemDict;
+    ProgTime ApproxWeights;
+    ProgTime CompDict;
+    ProgTime Invf;
+    ProgTime Text;
+  }
+InitQueryTimes;
+
+
+/* [RJM 06/97: text filename] */
+query_data *InitQuerySystem (char *dir, char *name, char *textname, InitQueryTimes * iqt);
+
+void ChangeMemInUse (query_data * qd, long delta);
+
+void FinishQuerySystem (query_data * qd);
+
+void ResetFileStats (query_data * qd);
+
+void TransFileStats (query_data * qd);
+
+void RankedQuery (query_data * qd, char *Query, RankedQueryInfo * rqi);
+
+void BooleanQuery (query_data * qd, char *Query, BooleanQueryInfo * bqi,
+		   int stem_method);
+
+void DocnumsQuery (query_data * qd, char *QueryLine);
+
+void FreeTextBuffer (query_data * qd);
+
+void FreeQueryDocs (query_data * qd);
+
+int LoadCompressedText (query_data * qd, int max_mem);
+
+int GetDocNum (query_data * qd);
+
+float GetDocWeight (query_data * qd);
+
+long GetDocCompLength (query_data * qd);
+
+u_char *GetDocText (query_data * qd, unsigned long *len);
+
+DocEntry *GetDocChain (query_data * qd);
+
+int NextDoc (query_data * qd);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.c	(revision 16561)
@@ -0,0 +1,1111 @@
+/**************************************************************************
+ *
+ * bool_optimiser -- optimise boolean parse tree
+ * Copyright (C) 1994,95  Tim Shimmin
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:42  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:20  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1995/07/27  04:54:55  tes
+   * Committing optimiser before I add the code
+   * to find diff-nodes in any kind of tree.
+   *
+   * Revision 1.3  1994/10/20  00:02:55  tes
+   * Fixed bug with bool_optimiser:
+   *   - it couldn't handle "false | false"
+   *   - it couldn't handle "true & true"
+   * These would collapse into an empty list and cause an error.
+   *
+   * Revision 1.2  1994/10/18  06:11:02  tes
+   * The boolean optimiser seems to be modifying the parse tree
+   * like it is supposed to.
+   * Paragraph ranking now works without any text files if required to.
+   *
+   * Revision 1.1  1994/10/12  01:15:28  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "bool_tree.h"
+#include "term_lists.h"
+#include "messages.h"
+
+typedef int (*CompFunc) (const void *, const void *);
+typedef void (*TreeFunc) (bool_tree_node *);
+
+/* --- routines --- */
+static void PermeateNots (bool_tree_node * tree);
+static void DoubleNeg (bool_tree_node * not_tree);
+static void AndDeMorgan (bool_tree_node * not_tree);
+static void OrDeMorgan (bool_tree_node * not_tree);
+static int AndDistribute (bool_tree_node * tree);
+static void DNF_Distribute (bool_tree_node * tree);
+static void ReplaceWithChildren (bool_tree_node * tree);
+static void CollapseTree (bool_tree_node * tree);
+static void AndTrueFalse (bool_tree_node * and_tree);
+static void OrTrueFalse (bool_tree_node * or_tree);
+static void TF_Idempotent (bool_tree_node * tree);
+static void AndNotToDiff (bool_tree_node * and_tree, TreeFunc recurs_func);
+static void DiffDNF (bool_tree_node * or_node);
+static void DiffTree (bool_tree_node * tree);
+static void AndSort (bool_tree_node * and_tree, TermList * tl, CompFunc comp);
+static void AndOrSort (bool_tree_node * tree, TermList * tl);
+static void AndSortDNF (bool_tree_node * or_node, TermList * tl);
+static void MarkOrTerms (bool_tree_node * tree, TermList * tl);
+static int calc_sum_ft (bool_tree_node * term_tree, TermList * tl);
+static int AndOrSort_comp (const void *A, const void *B);
+static int AndSort_comp (const void *A, const void *B);
+
+/* =========================================================================
+ * Function: OptimiseBoolTree
+ * Description: 
+ *      For case 2:
+ *        Do three major steps:
+ *        (i) put into standard form
+ *            -> put into DNF (disjunctive normal form - or of ands)
+ *            Use DeMorgan's, Double-negative, Distributive rules
+ *        (ii) simplify
+ *             apply idempotency rules
+ *        (iii) ameliorate
+ *              convert &! to diff nodes, order terms by frequency,...
+ *     Could also do the matching idempotency laws i.e. ...
+ *     (A | A), (A | !A), (A & !A), (A & A), (A & (A | B)), (A | (A & B)) 
+ *     Job for future.... ;-) 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+OptimiseBoolTree (bool_tree_node * parse_tree, TermList * tl, int type)
+{
+  switch (type)
+    {
+    case 1:
+      /* --- sorts for and of or-terms --- */
+      PermeateNots (parse_tree);
+      TF_Idempotent (parse_tree);
+      CollapseTree (parse_tree);
+      DiffTree (parse_tree);
+      MarkOrTerms (parse_tree, tl);
+      AndOrSort (parse_tree, tl);
+      break;
+    case 2:
+      /* --- put into DNF --- */
+      PermeateNots (parse_tree);
+      TF_Idempotent (parse_tree);
+      DNF_Distribute (parse_tree);
+      CollapseTree (parse_tree);
+      AndSortDNF (parse_tree, tl);
+      DiffDNF (parse_tree);
+      break;
+    default:
+      /* --- do nothing --- */
+      break;
+    }				/*switch */
+}				/*OptimiseBoolTree */
+
+/* =========================================================================
+ * Function: DoubleNeg
+ * Description: 
+ *      !(!(a) = a
+ *      Assumes binary tree.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+static void
+DoubleNeg (bool_tree_node * not_tree)
+{
+  bool_tree_node *child = BOOL_CHILD (not_tree);
+  bool_tree_node *a = BOOL_CHILD (child);
+  bool_tree_node *sibling = BOOL_SIBLING (not_tree);
+
+  /* copy a into not tree */
+  bcopy (a, not_tree, sizeof (bool_tree_node));
+
+  BOOL_SIBLING (not_tree) = sibling;
+
+  Xfree (child);
+  Xfree (a);
+
+}
+
+/* =========================================================================
+ * Function: AndDeMorgan
+ * Description: 
+ *      DeMorgan's rule for 'not' of an 'and'  i.e. !(a & b) <=> (!a) | (!b)
+ *      Assumes Binary Tree
+ * Input: 
+ *      not of and tree
+ * Output: 
+ *      or of not trees
+ * ========================================================================= */
+
+static void
+AndDeMorgan (bool_tree_node * not_tree)
+{
+  bool_tree_node *child = BOOL_CHILD (not_tree);
+  bool_tree_node *a = BOOL_CHILD (child);
+  bool_tree_node *b = BOOL_SIBLING (a);
+  bool_tree_node *new_not = NULL;
+
+  /* change 'not' to 'or' */
+  BOOL_TAG (not_tree) = N_or;
+
+  /* change 'and' to 'not' */
+  BOOL_TAG (child) = N_not;
+
+  /* 'a' no longer has a sibling */
+  BOOL_SIBLING (a) = NULL;
+
+  /* 'a's old sibling is now a child of a new not */
+  new_not = CreateBoolTreeNode (N_not, b, NULL);
+  BOOL_SIBLING (child) = new_not;
+}
+
+/* =========================================================================
+ * Function: OrDeMorgan
+ * Description: 
+ *      DeMorgan's rule for 'not' of an 'or' i.e. !(a | b) <=> (!a) & (!b)
+ *      Assumes Binary Tree
+ * Input: 
+ *      not of and tree
+ * Output: 
+ *      or of not trees
+ * ========================================================================= */
+
+static void
+OrDeMorgan (bool_tree_node * not_tree)
+{
+  bool_tree_node *child = BOOL_CHILD (not_tree);
+  bool_tree_node *a = BOOL_CHILD (child);
+  bool_tree_node *b = BOOL_SIBLING (a);
+  bool_tree_node *new_not = NULL;
+
+  /* change 'not' to 'and' */
+  BOOL_TAG (not_tree) = N_and;
+
+  /* change 'or' to 'not' */
+  BOOL_TAG (child) = N_not;
+
+  /* 'a' no longer has a sibling */
+  BOOL_SIBLING (a) = NULL;
+
+  /* 'a's old sibling is now a child of a new not */
+  new_not = CreateBoolTreeNode (N_not, b, NULL);
+  BOOL_SIBLING (child) = new_not;
+}
+
+/* =========================================================================
+ * Function: PermeateNots
+ * Description: 
+ *      Use DeMorgan's and Double-negative
+ *      Assumes tree in binary form (i.e. No ands/ors collapsed)
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+PermeateNots (bool_tree_node * tree)
+{
+  if (!tree)
+    return;
+
+  if (BOOL_TAG (tree) == N_not)
+    {
+      switch (BOOL_TAG (BOOL_CHILD (tree)))
+	{
+	case N_not:
+	  DoubleNeg (tree);
+/** NOTE: I do not go on to do the children **/
+	  /* I permeate the nots on the tree here & leave it at that */
+	  /* In this case I do not know what type 'tree' will end up */
+	  PermeateNots (tree);
+	  return;
+	case N_and:
+	  AndDeMorgan (tree);
+	  break;
+	case N_or:
+	  OrDeMorgan (tree);
+	  break;
+	default:
+	  /* leave alone */
+	  break;
+	}
+    }
+
+  /* Permeate Nots for children */
+  if (BOOL_HAS_CHILD (tree))
+    {
+      PermeateNots (BOOL_CHILD (tree));
+      PermeateNots (BOOL_SIBLING (BOOL_CHILD (tree)));
+    }
+
+}
+
+/* =========================================================================
+ * Function: DNF_Distribute
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+
+#define ITER_LIMIT 500		/* if reach limit then probably in trouble */
+
+static void
+DNF_Distribute (bool_tree_node * tree)
+{
+  int i = 0;
+
+  /* multiple passes until no change */
+  /* not very smart :-( */
+  while (1)
+    {
+      if (AndDistribute (tree) == 0)
+	return;
+      if (i == ITER_LIMIT)
+	FatalError (1, "DNF infinite loop");
+      i++;
+    }				/*while */
+
+}
+
+/* =========================================================================
+ * Function: AndDistribute
+ * Description: 
+ *      (a | b) & A <=> (a & A) | (b & A)
+ * Input: 
+ *      binary tree of "AND" , "OR"s.
+ * Output: 
+ *      return 1 if changed the tree
+ *      return 0 if there was NO change (no distributive rule to apply)
+ * ========================================================================= */
+
+static int
+AndDistribute (bool_tree_node * tree)
+{
+  if (!tree)
+    return 0;
+
+  if (BOOL_TAG (tree) != N_and)
+    {
+      /* try the children if got some */
+      if (BOOL_TAG (tree) == N_or)
+	{
+	  bool_tree_node *left_child = BOOL_CHILD (tree);
+	  int left = AndDistribute (left_child);
+	  int right = AndDistribute (BOOL_SIBLING (left_child));
+	  return (left || right);
+	}
+
+      /* no children, then no change */
+      return 0;
+    }
+
+  /* AND case */
+  {
+    /* we have to check the children to see if can distribute */
+    bool_tree_node *left_child = BOOL_CHILD (tree);
+    bool_tree_node *right_child = BOOL_SIBLING (left_child);
+    int swap = 0;
+
+    /* If "or" on the right side then do a swap */
+    /* This is done for code simplicity */
+    if (BOOL_TAG (right_child) == N_or)
+      {
+	/* do a swap */
+	bool_tree_node *temp;
+	temp = right_child;
+	right_child = left_child;
+	left_child = temp;
+
+	BOOL_SIBLING (left_child) = right_child;
+	BOOL_SIBLING (right_child) = NULL;
+	BOOL_CHILD (tree) = left_child;
+
+	swap = 1;
+      }
+
+    /* do the actual distribution */
+    if (swap || BOOL_TAG (left_child) == N_or)
+      {
+	bool_tree_node *A = right_child;
+	bool_tree_node *A_copy = CopyBoolTree (A);
+	bool_tree_node *a = BOOL_CHILD (left_child);
+	bool_tree_node *b = BOOL_SIBLING (a);
+
+	/* then can distribute away ... */
+	BOOL_TAG (tree) = N_or;
+	BOOL_TAG (left_child) = N_and;
+	BOOL_SIBLING (left_child) = CreateBoolTreeNode (N_and, b, A);
+	BOOL_SIBLING (a) = A_copy;
+
+	AndDistribute (tree);
+	return 1;
+      }
+
+    /* if "and" of something other than "or" s then try the children */
+    {
+      int left = AndDistribute (left_child);
+      int right = AndDistribute (right_child);
+      return (left || right);
+    }
+  }				/* AND case */
+
+}				/*AndDistribute */
+
+/* =========================================================================
+ * Function: CollapseTree
+ * Description: 
+ *      Collapse "and" and "or"s.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+CollapseTree (bool_tree_node * tree)
+{
+  if (!tree)
+    return;
+
+  if ((BOOL_TAG (tree) == N_and) || (BOOL_TAG (tree) == N_or))
+    {
+      bool_tree_node *child = BOOL_CHILD (tree);
+      while (child)
+	{
+	  while (BOOL_TAG (child) == BOOL_TAG (tree))
+	    {
+	      ReplaceWithChildren (child);
+	    }
+	  CollapseTree (child);
+	  child = BOOL_SIBLING (child);
+	}
+    }
+
+  /* assume "not"s permeated */
+  return;
+}
+
+/* =========================================================================
+ * Function: ReplaceWithChildren
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+ReplaceWithChildren (bool_tree_node * tree)
+{
+  bool_tree_node *next = BOOL_SIBLING (tree);
+  bool_tree_node *child = BOOL_CHILD (tree);
+  bool_tree_node *last = LastBoolSibling (child);
+
+  bcopy ((char *) child, (char *) tree, sizeof (bool_tree_node));
+  Xfree (child);
+  BOOL_SIBLING (last) = next;
+
+}
+
+/* =========================================================================
+ * Function: TF_Idempotent
+ * Description: 
+ *      Applies Idempotency laws which refer to True or False (hence "TF")
+ *      uses the true or false idempotent rules
+ * Input: 
+ *      operates on n-ary trees (not restricted to binary)
+ * Output: 
+ * ========================================================================= */
+
+static void
+TF_Idempotent (bool_tree_node * tree)
+{
+  bool_tree_node *child = NULL;
+
+  while (tree)
+    {
+      if (BOOL_HAS_CHILD (tree))
+	{
+	  child = BOOL_CHILD (tree);
+	  TF_Idempotent (child);
+	}
+
+      /* --- process node --- */
+      switch (BOOL_TAG (tree))
+	{
+	case N_not:
+	  child = BOOL_CHILD (tree);
+
+	  /* if true then falsify */
+	  if (BOOL_TAG (child) == N_all)
+	    {
+	      BOOL_TAG (tree) = N_none;
+	      BOOL_CHILD (tree) = NULL;
+	      Xfree (child);
+	      break;
+	    }
+
+	  /* if false then trueify */
+	  if (BOOL_TAG (child) == N_none)
+	    {
+	      BOOL_TAG (tree) = N_all;
+	      BOOL_CHILD (tree) = NULL;
+	      Xfree (child);
+	      break;
+	    }
+
+	  break;
+	case N_and:
+	  AndTrueFalse (tree);
+	  break;
+	case N_or:
+	  OrTrueFalse (tree);
+	  break;
+
+	default:
+	  /* do nothing */
+	  break;
+	}			/*switch */
+
+      tree = BOOL_SIBLING (tree);
+    }				/*while */
+}				/*TF_Idempotent */
+
+/* =========================================================================
+ * Function: AndTrueFalse
+ * Description: 
+ *      If any "false" then falsify whole tree 
+ *      If any "true" then delete them
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+AndTrueFalse (bool_tree_node * and_tree)
+{
+  bool_tree_node *child = BOOL_CHILD (and_tree);
+
+  bool_tree_node *prev = NULL;
+
+  while (child)
+    {
+      if (BOOL_TAG (child) == N_none)
+	{
+	  FreeBoolTree (&(BOOL_CHILD (and_tree)));
+	  BOOL_TAG (and_tree) = N_none;
+	  return;
+	}
+
+      if (BOOL_TAG (child) == N_all)
+	{
+	  bool_tree_node *sibling = BOOL_SIBLING (child);
+
+	  /* --- link over child --- */
+	  if (!prev)
+	    {
+	      BOOL_CHILD (and_tree) = sibling;
+	    }
+	  else
+	    {
+	      BOOL_SIBLING (prev) = sibling;
+	    }
+
+	  Xfree (child);
+	  child = sibling;
+	}
+      else
+	{
+	  prev = child;
+	  child = BOOL_SIBLING (child);
+	}
+    }				/*while */
+
+  child = BOOL_CHILD (and_tree);
+
+  /* if no children ... */
+  /* => all have been true */
+  if (!child)
+    {
+      BOOL_TAG (and_tree) = N_all;
+      return;
+    }
+
+  /* If only one child ... */
+  if (!BOOL_SIBLING (child))
+    {
+      /* replace the parent with child - move it up */
+      bool_tree_node *save_sibling = BOOL_SIBLING (and_tree);
+      bcopy ((char *) child, (char *) and_tree, sizeof (bool_tree_node));
+      BOOL_SIBLING (and_tree) = save_sibling;
+      Xfree (child);
+    }
+
+}				/*AndTrueFalse */
+
+/* =========================================================================
+ * Function: OrTrueFalse
+ * Description: 
+ *      If any "true" then trueify whole tree 
+ *      If any "false" then delete them 
+ *      ***** NOTE: This is a reflection of AndTrueFalse *****
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+OrTrueFalse (bool_tree_node * or_tree)
+{
+  bool_tree_node *child = BOOL_CHILD (or_tree);
+
+  bool_tree_node *prev = NULL;
+
+  while (child)
+    {
+      if (BOOL_TAG (child) == N_all)
+	{
+	  FreeBoolTree (&(BOOL_CHILD (or_tree)));
+	  BOOL_TAG (or_tree) = N_all;
+	  return;
+	}
+
+      if (BOOL_TAG (child) == N_none)
+	{
+	  bool_tree_node *sibling = BOOL_SIBLING (child);
+	  if (!prev)
+	    {
+	      BOOL_CHILD (or_tree) = sibling;
+	    }
+	  else
+	    {
+	      BOOL_SIBLING (prev) = sibling;
+	    }
+	  Xfree (child);
+	  child = sibling;
+
+	}
+      else
+	{
+	  prev = child;
+	  child = BOOL_SIBLING (child);
+	}
+    }				/*while */
+
+  child = BOOL_CHILD (or_tree);
+
+  /* if no children ... */
+  /* => all have been false */
+  if (!child)
+    {
+      BOOL_TAG (or_tree) = N_none;
+      return;
+    }
+
+  /* If only one child ... */
+  if (!BOOL_SIBLING (child))
+    {
+      /* replace the parent with child - move it up */
+      bool_tree_node *save_sibling = BOOL_SIBLING (or_tree);
+      bcopy ((char *) child, (char *) or_tree, sizeof (bool_tree_node));
+      BOOL_SIBLING (or_tree) = save_sibling;
+      Xfree (child);
+    }
+
+}				/*AndTrueFalse */
+
+/* =========================================================================
+ * Function: AndNotToDiff
+ * Description: 
+ *      Convert an "And" tree with possibly some negated terms into
+ *      a "And - neg-terms". 
+ * Input: 
+ *      Expects "And" of only literals - as tree should be in DNF form.
+ *      This means that no recursion is necessary.
+ * Output: 
+ * ========================================================================= */
+
+static void
+AndNotToDiff (bool_tree_node * and_tree, TreeFunc recurs_func)
+{
+
+  /* -------------------------------------------------- */
+  /* --- Divide the children up into positive and negative literals --- */
+  /* Preserves ordering of children */
+  bool_tree_node *pos = NULL;
+  bool_tree_node *neg = NULL;
+  bool_tree_node *last_pos = NULL;
+  bool_tree_node *last_neg = NULL;
+
+  bool_tree_node *child = BOOL_CHILD (and_tree);
+  while (child)
+    {
+      bool_tree_node *next = BOOL_SIBLING (child);
+      if (BOOL_TAG (child) == N_not)
+	{
+	  /* add to end of neg list */
+	  if (!neg)
+	    neg = child;
+	  else
+	    BOOL_SIBLING (last_neg) = child;
+	  last_neg = child;
+	}
+      else
+	{
+	  /* add to end of pos list */
+	  if (!pos)
+	    pos = child;
+	  else
+	    BOOL_SIBLING (last_pos) = child;
+	  last_pos = child;
+	}
+      child = next;
+    }				/*while */
+  if (last_pos)
+    BOOL_SIBLING (last_pos) = NULL;
+  if (last_neg)
+    BOOL_SIBLING (last_neg) = NULL;
+  /* -------------------------------------------------- */
+
+  if (!neg)			/* no negtives => no subtraction */
+    {
+      /* fix up parent's child link */
+      BOOL_CHILD (and_tree) = pos;
+      return;
+    }
+
+  if (!pos)			/* no positives = all negatives */
+    {
+      /* Choose one of the negs for the left-hand-side of the subtraction */
+      /* In particular, choose the first */
+      pos = neg;
+      neg = BOOL_SIBLING (neg);
+      BOOL_SIBLING (pos) = NULL;
+    }
+
+  /* Get rid of all '!' from not nodes */
+  /* Make a list out of the children */
+  {
+    bool_tree_node *n = neg;
+    bool_tree_node *next = NULL;
+
+    neg = BOOL_CHILD (neg);
+    while (n)
+      {
+	bool_tree_node *c = BOOL_CHILD (n);
+	next = BOOL_SIBLING (n);
+	Xfree (n);
+	if (next)
+	  BOOL_SIBLING (c) = BOOL_CHILD (next);
+	n = next;
+      }
+  }
+
+  /* --- restructure the tree with "and" and "diff" --- */
+  BOOL_TAG (and_tree) = N_diff;
+  if (!BOOL_SIBLING (pos))	/* i.e. only one pos */
+    {
+      /* (A & !B & !C & !D & ...) ==> A - (B,C,D,...) */
+
+      BOOL_CHILD (and_tree) = pos;
+      BOOL_SIBLING (pos) = neg;
+    }
+  else
+    {
+      /* (A & !C & B & !D & ...) ==> (A & B & ....) - (C, D, ...) */
+
+      bool_tree_node *n = CreateBoolNode (N_and);
+      BOOL_CHILD (and_tree) = n;
+      BOOL_CHILD (n) = pos;
+      BOOL_SIBLING (n) = neg;
+    }				/*if */
+
+  if (recurs_func)
+    {
+      recurs_func (BOOL_CHILD (and_tree));
+    }
+
+}				/*AndNotToDiff */
+
+/* =========================================================================
+ * Function: DiffTree
+ * Description: 
+ *      Applies "AndNotToNeg" to each conjunct.
+ *      Unlike DiffDNF, this routine will search throughout the tree for
+ *      "and"-nodes. It will call AndNotToNeg with this as its recursive 
+ *      routine.
+ * Date: 27/July/95
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+DiffTree (bool_tree_node * tree)
+{
+  bool_tree_node *t = tree;
+
+  while (t)
+    {
+      if (BOOL_TAG (t) == N_and)
+	{
+	  AndNotToDiff (t, DiffTree);
+	}
+      t = BOOL_SIBLING (t);
+    }				/*while */
+
+}				/*DiffTree */
+
+/* =========================================================================
+ * Function: DiffDNF
+ * Description: 
+ *      Applies "AndNotToNeg" to each conjunct.
+ * Input: 
+ *      A DNF tree
+ * Output: 
+ *      A tree possibly with some diffs in it.
+ * ========================================================================= */
+
+static void
+DiffDNF (bool_tree_node * DNF_tree)
+{
+
+  if (BOOL_TAG (DNF_tree) == N_or)
+    {
+      bool_tree_node *child = BOOL_CHILD (DNF_tree);
+
+      while (child)
+	{
+	  if (BOOL_TAG (child) == N_and)
+	    {
+	      AndNotToDiff (child, NULL);
+	    }
+	  child = BOOL_SIBLING (child);
+	}
+    }
+  else if (BOOL_TAG (DNF_tree) == N_and)
+    {
+      AndNotToDiff (DNF_tree, NULL);
+    }
+}
+
+/* =========================================================================
+ * Function: AndSort_comp 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+/* Module variable, "term_list",  used as parameter for comparison function */
+/* The comparison function only allows certain fixed parameters */
+static TermList *term_list;
+
+static int
+AndSort_comp (const void *A, const void *B)
+{
+  const bool_tree_node *const *a = A;
+  const bool_tree_node *const *b = B;
+
+  if (BOOL_TAG (*a) == N_term && BOOL_TAG (*b) == N_term)
+    {
+      /* look up in term list */
+      int a_doc_count = GetNthWE (term_list, BOOL_TERM (*a))->doc_count;
+      int b_doc_count = GetNthWE (term_list, BOOL_TERM (*b))->doc_count;
+
+      return a_doc_count - b_doc_count;
+    }
+
+  if (BOOL_TAG (*a) == N_term && BOOL_TAG (*b) != N_term)
+    return -1;
+
+  if (BOOL_TAG (*a) != N_term && BOOL_TAG (*b) == N_term)
+    return 1;
+
+  return 0;
+}				/*AndSort_comp */
+
+/* =========================================================================
+ * Function: AndSort
+ * Description: 
+ *      Sort the list of nodes by increasing doc_count 
+ *      Using some Neil Sharman code - pretty straight forward.
+ *      Note: not-terms are sent to the end of the list
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+AndSort (bool_tree_node * and_tree, TermList * tl, CompFunc comp)
+{
+  bool_tree_node **list = NULL;
+  bool_tree_node *child = NULL;
+  int count = 0;
+  int i = 0;
+
+  if (BOOL_TAG (and_tree) != N_and)
+    return;
+
+  /* --- Convert linked list into an array for sorting --- */
+  child = BOOL_CHILD (and_tree);
+  count = 0;
+  while (child)
+    {
+      count++;
+      child = BOOL_SIBLING (child);
+    }
+
+  if (!(list = Xmalloc (sizeof (bool_tree_node *) * count)))
+    return;
+
+  child = BOOL_CHILD (and_tree);
+  for (i = 0; i < count; i++)
+    {
+      list[i] = child;
+      child = BOOL_SIBLING (child);
+    }
+  /* ----------------------------------------------------- */
+
+  term_list = tl;
+  qsort (list, count, sizeof (bool_tree_node *), comp);
+
+  /* --- Convert array back into linked list --- */
+  for (i = 0; i < count - 1; i++)
+    BOOL_SIBLING (list[i]) = list[i + 1];
+  BOOL_SIBLING (list[count - 1]) = NULL;
+  BOOL_CHILD (and_tree) = list[0];
+  Xfree (list);
+  /* ----------------------------------------------------- */
+
+}				/*AndSort */
+
+/* =========================================================================
+ * Function: AndSortDNF
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+
+static void
+AndSortDNF (bool_tree_node * DNF_tree, TermList * tl)
+{
+
+  if (BOOL_TAG (DNF_tree) == N_or)
+    {
+      bool_tree_node *child = BOOL_CHILD (DNF_tree);
+
+      while (child)
+	{
+	  if (BOOL_TAG (child) == N_and)
+	    {
+	      AndSort (child, tl, AndSort_comp);
+	    }
+	  child = BOOL_SIBLING (child);
+	}
+    }
+  else if (BOOL_TAG (DNF_tree) == N_and)
+    {
+      AndSort (DNF_tree, tl, AndSort_comp);
+    }
+}
+
+/* =========================================================================
+ * Function: MarkOrTerms
+ * Description: 
+ *      Mark or-nodes whose children are all terms.
+ *      Also store the sum of the ft's of these nodes.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+MarkOrTerms (bool_tree_node * tree, TermList * tl)
+{
+  if (BOOL_TAG (tree) == N_or)
+    {
+      int found_non_term = 0;
+      bool_tree_node *child = BOOL_CHILD (tree);
+
+      /* --- check to see if got all terms --- */
+      while (child)
+	{
+	  if (BOOL_TAG (child) != N_term)
+	    {
+	      found_non_term = 1;
+	    }
+	  if (BOOL_HAS_CHILD (tree))
+	    {
+	      MarkOrTerms (child, tl);
+	    }
+	  child = BOOL_SIBLING (child);
+	}			/*while */
+
+      if (!found_non_term)
+	{
+	  /* Must have all terms for the OR node */
+	  /* So change its tag and calculate its sum */
+	  BOOL_TAG (tree) = N_or_terms;
+	  BOOL_SUM_FT (tree) = calc_sum_ft (tree, tl);
+	}			/*if */
+      /* ------------------------------------- */
+
+    }				/*if OR node */
+
+  else if (BOOL_HAS_CHILD (tree))
+    {
+      bool_tree_node *child = BOOL_CHILD (tree);
+      while (child)
+	{
+	  MarkOrTerms (child, tl);
+	  child = BOOL_SIBLING (child);
+	}
+    }
+
+}				/*MarkOrTerms */
+
+/* =========================================================================
+ * Function: calc_sum_ft
+ * Description: 
+ *      Sums up the ft's of all the terms 
+ * Input: 
+ *      Assumes that the tree only contains terms in it 
+ * Output: 
+ *      the ft sum
+ * ========================================================================= */
+static int
+calc_sum_ft (bool_tree_node * term_tree, TermList * tl)
+{
+  int sum_ft = 0;
+  bool_tree_node *child = BOOL_CHILD (term_tree);
+  while (child)
+    {
+      sum_ft += GetNthWE (tl, BOOL_TERM (child))->doc_count;
+      child = BOOL_SIBLING (child);
+    }
+  return sum_ft;
+}
+
+/* =========================================================================
+ * Function: AndOrSort
+ * Description: 
+ *      This is like an AndSort of just terms. 
+ *      The difference is that we are now also 
+ *      considering sorting with <or of terms> by using the sum of f_t's.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+static void
+AndOrSort (bool_tree_node * tree, TermList * tl)
+{
+  if (BOOL_HAS_CHILD (tree))
+    {
+
+      if (BOOL_TAG (tree) == N_and)
+	{
+	  AndSort (tree, tl, AndOrSort_comp);
+	}
+
+      /* Check out any children down the tree */
+      /* Probably a waste of time */
+      {
+	bool_tree_node *child = BOOL_CHILD (tree);
+	while (child)
+	  {
+	    AndOrSort (child, tl);
+	    child = BOOL_SIBLING (child);
+	  }
+      }				/*do the tree */
+
+    }				/*if has children */
+}				/*AndOrSort */
+
+/* =========================================================================
+ * Function: AndOrSort_comp
+ * Description: 
+ *      Comparison function used to order "term"s and "or_terms"s.
+ *      It's purpose is to be used by a sort routine.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+static int
+AndOrSort_comp (const void *A, const void *B)
+{
+  const bool_tree_node *const *a = A;
+  const bool_tree_node *const *b = B;
+
+  if (BOOL_TAG (*a) == N_term && BOOL_TAG (*b) == N_term)
+    {
+      /* look up in term list */
+      int a_doc_count = GetNthWE (term_list, BOOL_TERM (*a))->doc_count;
+      int b_doc_count = GetNthWE (term_list, BOOL_TERM (*b))->doc_count;
+
+      return a_doc_count - b_doc_count;
+    }
+
+  if (BOOL_TAG (*a) == N_term && BOOL_TAG (*b) == N_or_terms)
+    {
+      /* look up in term list */
+      int a_doc_count = GetNthWE (term_list, BOOL_TERM (*a))->doc_count;
+      int b_doc_count = BOOL_SUM_FT (*b);
+
+      return a_doc_count - b_doc_count;
+    }
+
+  if (BOOL_TAG (*a) == N_or_terms && BOOL_TAG (*b) == N_term)
+    {
+      /* look up in term list */
+      int a_doc_count = BOOL_SUM_FT (*a);
+      int b_doc_count = GetNthWE (term_list, BOOL_TERM (*b))->doc_count;
+
+      return a_doc_count - b_doc_count;
+    }
+
+  if (BOOL_TAG (*a) == N_or_terms && BOOL_TAG (*b) == N_or_terms)
+    {
+      /* look up in term list */
+      int a_doc_count = BOOL_SUM_FT (*a);
+      int b_doc_count = BOOL_SUM_FT (*b);
+
+      return a_doc_count - b_doc_count;
+    }
+
+  if ((BOOL_TAG (*a) == N_term || BOOL_TAG (*a) == N_or_terms) &&
+      (BOOL_TAG (*b) != N_term && BOOL_TAG (*b) != N_or_terms))
+    return -1;
+
+  if ((BOOL_TAG (*a) != N_term && BOOL_TAG (*a) != N_or_terms) &&
+      (BOOL_TAG (*b) == N_term || BOOL_TAG (*b) == N_or_terms))
+    return 1;
+
+  return 0;
+}				/*AndOrSort_comp */
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_optimiser.h	(revision 16561)
@@ -0,0 +1,65 @@
+/**************************************************************************
+ *
+ * bool_optimiser -- optimise boolean parse tree
+ * Copyright (C) 1994  Tim Shimmin
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:42  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:23  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1995/03/14  05:15:21  tes
+   * Updated the boolean query optimiser to do different types of optimisation.
+   * A query environment variable "optimise_type" specifies which one is to be
+   * used. Type 1 is the new one which is faster than 2.
+   *
+   * Revision 1.1  1994/10/20  03:56:32  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/10/18  06:11:03  tes
+   * The boolean optimiser seems to be modifying the parse tree
+   * like it is supposed to.
+   * Paragraph ranking now works without any text files if required to.
+   *
+   * Revision 1.1  1994/10/12  01:15:29  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+#ifndef BOOL_OPTIMISER_H
+#define BOOL_OPTIMISER_H
+
+#include "bool_tree.h"
+#include "term_lists.h"
+
+void OptimiseBoolTree (bool_tree_node * parse_tree, TermList * term_list, int type);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.c	(revision 16561)
@@ -0,0 +1,1196 @@
+
+/*  A Bison parser, made from bool_parser.y
+ by  GNU Bison version 1.27
+  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define	TERM	257
+
+#line 24 "bool_parser.y"
+
+ 
+#include "sysfuncs.h"
+
+#include "messages.h"
+ 
+#include "memlib.h"
+#include "words.h"
+#include "stemmer.h"
+#include "term_lists.h"
+#include "bool_tree.h"
+/* [RPAP - Jan 97: Stem Index Change] */
+#include "backend.h"     /* for stemmed_dict def */
+#include "stem_search.h"
+
+#include "query_term_list.h"  /* [RPAP - Feb 97: Term Frequency] */
+
+/* --- routines --- */
+static int query_lex();
+static int yyerror(char *);
+#define yylex() query_lex(&ch_buf, end_buf)
+ 
+/* --- module variables --- */
+static char *ch_buf; /* ptr to the character query line buffer */
+static char *end_buf; /* ptr to the last character of the line buffer */
+static bool_tree_node *tree_base = NULL;
+static TermList **term_list;
+static int stemmer_num;
+static int stem_method;
+/* [RPAP - Jan 97: Stem Index Change] */
+stemmed_dict *p__sd;
+static int indexed;
+/* [RPAP - Feb 97: Term Frequency] */
+static QueryTermList **query_term_list;
+static int word_num;
+static u_long count;
+static u_long doc_count;
+static u_long invf_ptr;
+static u_long invf_len;
+
+#line 66 "bool_parser.y"
+typedef union {
+  char *text;
+  bool_tree_node *node;
+} YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define	YYFINAL		20
+#define	YYFLAG		-32768
+#define	YYNTBASE	11
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 257 ? yytranslate[x] : 16)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     8,     2,     2,     2,     2,     9,     2,     4,
+     5,     6,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     7,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,    10,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     3
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     2,     4,     8,    10,    12,    14,    17,    21,    24,
+    26,    30
+};
+
+static const short yyrhs[] = {    15,
+     0,     3,     0,     4,    15,     5,     0,     6,     0,     7,
+     0,    12,     0,     8,    13,     0,    14,     9,    13,     0,
+    14,    13,     0,    13,     0,    15,    10,    14,     0,    14,
+     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+    76,    80,    81,    82,    83,    86,    87,    90,    91,    92,
+    95,    96
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","TERM","'('",
+"')'","'*'","'_'","'!'","'&'","'|'","query","term","not","and","or", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    11,    12,    12,    12,    12,    13,    13,    14,    14,    14,
+    15,    15
+};
+
+static const short yyr2[] = {     0,
+     1,     1,     3,     1,     1,     1,     2,     3,     2,     1,
+     3,     1
+};
+
+static const short yydefact[] = {     0,
+     2,     0,     4,     5,     0,     6,    10,    12,     1,     0,
+     7,     0,     9,     0,     3,     8,    11,     0,     0,     0
+};
+
+static const short yydefgoto[] = {    18,
+     6,     7,     8,     9
+};
+
+static const short yypact[] = {    10,
+-32768,    10,-32768,-32768,    10,-32768,-32768,     2,    -9,    14,
+-32768,    10,-32768,    10,-32768,-32768,     2,     4,    15,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,    -5,   -12,    18
+};
+
+
+#define	YYLAST		24
+
+
+static const short yytable[] = {    11,
+    14,    17,    13,    19,     1,     2,    16,     3,     4,     5,
+    12,    13,     1,     2,    20,     3,     4,     5,    15,    10,
+     0,     0,     0,    14
+};
+
+static const short yycheck[] = {     5,
+    10,    14,     8,     0,     3,     4,    12,     6,     7,     8,
+     9,    17,     3,     4,     0,     6,     7,     8,     5,     2,
+    -1,    -1,    -1,    10
+};
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/usr/lib/bison.simple"
+/* This file comes from bison-1.27.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   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, 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+	 instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+		 and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		-2
+#define YYEOF		0
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT 	goto yyabortlab
+#define YYERROR		goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL		goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    { yychar = (token), yylval = (value);			\
+      yychar1 = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { yyerror ("syntax error: cannot back up"); YYERROR; }	\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+#ifndef YYPURE
+#define YYLEX		yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int	yychar;			/*  the lookahead symbol		*/
+YYSTYPE	yylval;			/*  the semantic value of the		*/
+				/*  lookahead symbol			*/
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;			/*  location data for the lookahead	*/
+				/*  symbol				*/
+#endif
+
+int yynerrs;			/*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;			/*  nonzero means print parse trace	*/
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks	*/
+
+#ifndef	YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+
+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1		/* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)	__builtin_memcpy(TO,FROM,COUNT)
+#else				/* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+
+
+#line 216 "/usr/lib/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;	/*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;		/*  lookahead token as an internal (translated) token number */
+
+  short	yyssa[YYINITDEPTH];	/*  the state stack			*/
+  YYSTYPE yyvsa[YYINITDEPTH];	/*  the semantic value stack		*/
+
+  short *yyss = yyssa;		/*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;	/*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];	/*  the location stack			*/
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;		/*  the variable used to return		*/
+				/*  semantic values from the action	*/
+				/*  routines				*/
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+	 the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+	 but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yyls1, size * sizeof (*yylsp),
+		 &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+	{
+	  yyerror("parser stack overflow");
+	  if (yyfree_stacks)
+	    {
+	      free (yyss);
+	      free (yyvs);
+#ifdef YYLSP_NEEDED
+	      free (yyls);
+#endif
+	    }
+	  return 2;
+	}
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+	yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+		   size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+		   size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+		   size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+	YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)		/* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;		/* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+	{
+	  fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+	  /* Give the individual parser a way to print the precise meaning
+	     of a token, for further debugging info.  */
+#ifdef YYPRINT
+	  YYPRINT (stderr, yychar, yylval);
+#endif
+	  fprintf (stderr, ")\n");
+	}
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+	       yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+	fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 1:
+#line 76 "bool_parser.y"
+{ tree_base = yyvsp[0].node;;
+    break;}
+case 2:
+#line 80 "bool_parser.y"
+{ yyval.node = CreateBoolTermNode(term_list, yyvsp[0].text, 1, word_num, count, doc_count, invf_ptr, invf_len, stemmer_num); ;
+    break;}
+case 3:
+#line 81 "bool_parser.y"
+{ yyval.node = yyvsp[-1].node; ;
+    break;}
+case 4:
+#line 82 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_all, NULL, NULL); ;
+    break;}
+case 5:
+#line 83 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_none, NULL, NULL); ;
+    break;}
+case 7:
+#line 87 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_not, yyvsp[0].node, NULL); ;
+    break;}
+case 8:
+#line 90 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_and, yyvsp[-2].node, yyvsp[0].node); ;
+    break;}
+case 9:
+#line 91 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_and, yyvsp[-1].node, yyvsp[0].node); ;
+    break;}
+case 11:
+#line 95 "bool_parser.y"
+{ yyval.node = CreateBoolTreeNode(N_or, yyvsp[-2].node, yyvsp[0].node); ;
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+#line 542 "/usr/lib/bison.simple"
+
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+	{
+	  int size = 0;
+	  char *msg;
+	  int x, count;
+
+	  count = 0;
+	  /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+	  for (x = (yyn < 0 ? -yyn : 0);
+	       x < (sizeof(yytname) / sizeof(char *)); x++)
+	    if (yycheck[x + yyn] == x)
+	      size += strlen(yytname[x]) + 15, count++;
+	  msg = (char *) malloc(size + 15);
+	  if (msg != 0)
+	    {
+	      strcpy(msg, "parse error");
+
+	      if (count < 5)
+		{
+		  count = 0;
+		  for (x = (yyn < 0 ? -yyn : 0);
+		       x < (sizeof(yytname) / sizeof(char *)); x++)
+		    if (yycheck[x + yyn] == x)
+		      {
+			strcat(msg, count == 0 ? ", expecting `" : " or `");
+			strcat(msg, yytname[x]);
+			strcat(msg, "'");
+			count++;
+		      }
+		}
+	      yyerror(msg);
+	      free(msg);
+	    }
+	  else
+	    yyerror ("parse error; also virtual memory exceeded");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+	YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;		/* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+#line 99 "bool_parser.y"
+
+ 
+/* Bison on one mips machine defined "const" to be nothing but
+   then did not undef it */
+#ifdef const
+#undef const
+#endif
+
+/**************************************************************************/ 
+
+
+/* =========================================================================
+ * Function: query_lex
+ * Description:
+ *      Hand written lexical analyser for the parser.
+ * Input:
+ *      ptr = ptr to a ptr into character query-line buffer
+ *      end = ptr to last char in buffer
+ * Output:
+ *      yylval.text = the token's text
+ * Notes:
+ *      does NOT produce WILD tokens at the moment
+ * ========================================================================= */
+ 
+/* [RPAP - Jan 97: Stem Index Change]
+   state mode:
+      0 = Read next token
+      1 = Output word
+      2 = Output '|' or ')'
+ */
+static int query_lex(char **ptr, const char *end)
+{
+  char *buf_ptr = *ptr;
+  static int mode = 0;
+  static int termnum = 0;
+  static TermList *Terms = NULL;
+
+  if (mode == 0)
+    {
+      /* jump over whitespace */
+      buf_ptr = skipspace(buf_ptr, end);
+ 
+      if (inaword(buf_ptr, end))
+	{
+	  static char word[MAXSTEMLEN + 1]; /* [RJM 07/98: Memory Leak] */
+	  char *sWord = Xmalloc(MAXSTEMLEN + 1);
+	  int stem_to_apply, method_using = -1;
+
+	  PARSE_STEM_WORD(word, buf_ptr, end);
+
+	  /* Extract any parameters */
+	  stem_to_apply = stem_method;
+	  while (buf_ptr <= end)
+	    {
+	      int stem_param, param_type;
+	      char param[MAXPARAMLEN + 1];
+
+	      param_type = 0;
+	      PARSE_OPT_TERM_PARAM (param, param_type, buf_ptr, end);
+	      if (!param_type)
+		break;
+
+	      if (param_type == STEMPARAM)
+		{
+		  stem_param = atoi (param);
+		  if (errno != ERANGE && indexed && stem_param >= 0 && stem_param <= 3)
+		    method_using = stem_to_apply = stem_param;
+		}
+	    }
+
+	  bcopy ((char *) word, (char *) sWord, *word + 1);
+	  stemmer (stem_to_apply, stemmer_num, sWord);
+
+	  if (stem_to_apply == 0 || !indexed || p__sd == NULL)
+	    {
+	      /* [RPAP - Feb 97: Term Frequency] */
+	      word_num = FindWord (p__sd, sWord, &count, &doc_count, &invf_ptr, &invf_len);
+	      if (word_num == -1)
+		count = doc_count = invf_ptr = invf_len = 0;
+	      AddQueryTerm (query_term_list, (u_char *) word, count, method_using);
+
+	      yylval.text = word;
+	      *ptr = buf_ptr; /* fix up ptr */
+	      Xfree (sWord);
+	      return TERM;
+	    }
+	  else
+	    {
+	      *ptr = buf_ptr; /* fix up ptr */
+	      termnum = 0;
+	      ResetTermList (&Terms);
+	      if (FindWords (p__sd, (u_char *) sWord, stem_to_apply, &Terms) > 0)
+		{
+		  /* [RPAP - Feb 97: Term Frequency] */ 
+		  int i, freq = 0;
+		  for (i = 0; i < Terms->num; i++)
+		    freq += Terms->TE[i].WE.count;
+		  AddQueryTerm (query_term_list, word, freq, method_using);
+
+		  Xfree (sWord);
+		  mode = 1;
+		  return '(';
+		}
+	      else
+		{
+		  /* Word does not exists - include in tree anyway */
+		  Xfree (sWord);
+
+		  /* [RPAP - Feb 97: Term Frequency] */
+		  word_num = -1;
+		  count = doc_count = invf_ptr = invf_len = 0;
+		  AddQueryTerm (query_term_list, (u_char *) word, count, method_using);
+
+		  yylval.text = word;
+		  return TERM;
+		}
+	    }
+	}
+      else /* NON-WORD */
+	{
+	  if (*buf_ptr == '\0')
+	    {
+	      /* return null-char if it is one */
+	      *ptr = buf_ptr; /* fix up ptr */
+	      return 0;
+	    }
+	  else
+	    {
+	      /* return 1st char, and delete from buffer */
+	      char c = *buf_ptr++;
+	      *ptr = buf_ptr; /* fix up ptr */
+	      return c;
+	    }
+	}
+    }
+  else if (mode == 1)
+    {
+      yylval.text = Terms->TE[termnum].Word;
+      
+      /* [RPAP - Feb 97: Term Frequency] */
+      word_num = Terms->TE[termnum].WE.word_num;
+      count = Terms->TE[termnum].WE.count;
+      doc_count = Terms->TE[termnum].WE.doc_count;
+      invf_ptr = Terms->TE[termnum].WE.invf_ptr;
+      invf_len = Terms->TE[termnum].WE.invf_len;
+
+      termnum++;
+      mode = 2;
+      return TERM;
+    }
+  else  /* mode == 2 */
+    {
+      if (termnum >= Terms->num)
+	{
+	  mode = 0;
+	  return ')';
+	}
+      else
+	{
+	  mode = 1;
+	  return '|';
+	}
+    }
+}/*query_lex*/
+
+/* =========================================================================
+ * Function: yyerror
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */ 
+static int yyerror(char *s)
+{
+  Message("%s", s);
+  return(1);
+}
+
+ 
+/* =========================================================================
+ * Function: ParseBool
+ * Description:
+ *      Parse a boolean query string into a term-list and a boolean parse tree
+ * Input:
+ *      query_line = query line string
+ *      query_len = query line length
+ *      the_stem_method = stem method id used for stemming
+ * Output:
+ *      the_term_list = the list of terms
+ *      res = parser result code
+ * ========================================================================= */
+ 
+bool_tree_node *
+ParseBool(char *query_line, int query_len,
+          TermList **the_term_list, int the_stemmer_num, int the_stem_method, int *res,
+	  stemmed_dict * the_sd, int is_indexed,   /* [RPAP - Jan 97: Stem Index Change] */
+	  QueryTermList **the_query_term_list)  /* [RPAP - Feb 97: Term Frequency] */
+{
+  /* global variables to be accessed by bison/yacc created parser */
+  term_list = the_term_list;
+  stemmer_num = the_stemmer_num;
+  stem_method = the_stem_method;
+  ch_buf = query_line;
+  end_buf = query_line + query_len;
+  p__sd = the_sd;   /* [RPAP - Jan 97: Stem Index Change] */
+  indexed = is_indexed;  /* [RPAP - Jan 97: Stem Index Change] */
+  query_term_list = the_query_term_list; /* [RPAP - Feb 97: Term Frequency] */
+
+  FreeBoolTree(&(tree_base));
+ 
+  ResetTermList(term_list);
+  ResetQueryTermList(query_term_list);  /* [RPAP - Feb 97: Term Frequency] */
+
+  *res = yyparse();
+ 
+  return tree_base;
+}
+ 
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.h	(revision 16561)
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * filename -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:43  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:40  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:26  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:56:33  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.1  1994/10/12  01:15:31  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+#ifndef BOOL_PARSER_H
+#define BOOL_PARSER_H
+
+#include "backend.h"
+#include "term_lists.h"
+#include "invf.h"
+
+bool_tree_node *ParseBool (char *query_line, int query_len,
+			   TermList ** the_term_list, int the_stemmer_num, 
+			   int the_stem_method, int *res,
+			   stemmed_dict * the_sd, int is_indexed,  /* [RPAP - Jan 97: Stem Index Change] */
+			   QueryTermList ** the_query_term_list);  /* [RPAP - Feb 97: Term Frequency] */
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.y
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.y	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_parser.y	(revision 16561)
@@ -0,0 +1,316 @@
+/**************************************************************************
+ *
+ * bool_parser - boolean query parser
+ * Copyright (C) 1994  Neil Sharman & Tim Shimmin
+ *
+ * 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.
+ *
+ *
+ **************************************************************************/
+ 
+/**************************************************************************/
+%{
+ 
+#include "sysfuncs.h"
+
+#include "messages.h"
+ 
+#include "memlib.h"
+#include "words.h"
+#include "stemmer.h"
+#include "term_lists.h"
+#include "bool_tree.h"
+/* [RPAP - Jan 97: Stem Index Change] */
+#include "backend.h"     /* for stemmed_dict def */
+#include "stem_search.h"
+
+#include "query_term_list.h"  /* [RPAP - Feb 97: Term Frequency] */
+
+/* --- routines --- */
+static int query_lex();
+static int yyerror(char *);
+#define yylex() query_lex(&ch_buf, end_buf)
+ 
+/* --- module variables --- */
+static char *ch_buf; /* ptr to the character query line buffer */
+static char *end_buf; /* ptr to the last character of the line buffer */
+static bool_tree_node *tree_base = NULL;
+static TermList **term_list;
+static int stemmer_num;
+static int stem_method;
+/* [RPAP - Jan 97: Stem Index Change] */
+stemmed_dict *p__sd;
+static int indexed;
+/* [RPAP - Feb 97: Term Frequency] */
+static QueryTermList **query_term_list;
+static int word_num;
+static u_long count;
+static u_long doc_count;
+static u_long invf_ptr;
+static u_long invf_len;
+%}
+ 
+ 
+%union {
+  char *text;
+  bool_tree_node *node;
+}
+ 
+%token <text> TERM
+%type <node> query term not and or
+ 
+%%
+ 
+query: or  { tree_base = $1;}
+;
+ 
+ 
+term:     TERM  { $$ = CreateBoolTermNode(term_list, $1, 1, word_num, count, doc_count, invf_ptr, invf_len, stemmer_num); }
+        | '(' or ')' { $$ = $2; }
+        | '*' { $$ = CreateBoolTreeNode(N_all, NULL, NULL); }
+        | '_' { $$ = CreateBoolTreeNode(N_none, NULL, NULL); }
+;
+ 
+not:      term
+        | '!' not { $$ = CreateBoolTreeNode(N_not, $2, NULL); }
+;
+ 
+and:      and '&' not { $$ = CreateBoolTreeNode(N_and, $1, $3); }
+        | and not { $$ = CreateBoolTreeNode(N_and, $1, $2); }
+        | not
+;
+ 
+or:       or '|' and { $$ = CreateBoolTreeNode(N_or, $1, $3); }
+        | and
+;
+ 
+%%
+ 
+/* Bison on one mips machine defined "const" to be nothing but
+   then did not undef it */
+#ifdef const
+#undef const
+#endif
+
+/**************************************************************************/ 
+
+
+/* =========================================================================
+ * Function: query_lex
+ * Description:
+ *      Hand written lexical analyser for the parser.
+ * Input:
+ *      ptr = ptr to a ptr into character query-line buffer
+ *      end = ptr to last char in buffer
+ * Output:
+ *      yylval.text = the token's text
+ * Notes:
+ *      does NOT produce WILD tokens at the moment
+ * ========================================================================= */
+ 
+/* [RPAP - Jan 97: Stem Index Change]
+   state mode:
+      0 = Read next token
+      1 = Output word
+      2 = Output '|' or ')'
+ */
+static int query_lex(char **ptr, const char *end)
+{
+  char *buf_ptr = *ptr;
+  static int mode = 0;
+  static int termnum = 0;
+  static TermList *Terms = NULL;
+
+  if (mode == 0)
+    {
+      /* jump over whitespace */
+      buf_ptr = skipspace(buf_ptr, end);
+
+      if (inaword(buf_ptr, end))
+	{
+	  static char word[MAXSTEMLEN + 1]; /* [RJM 07/98: Memory Leak] */
+	  char *sWord = Xmalloc(MAXSTEMLEN + 1);
+	  int stem_to_apply, method_using = -1;
+
+	  PARSE_STEM_WORD(word, buf_ptr, end);
+
+	  /* Extract any parameters */
+	  stem_to_apply = stem_method;
+	  while (buf_ptr <= end)
+	    {
+	      int stem_param, param_type;
+	      char param[MAXPARAMLEN + 1];
+
+	      param_type = 0;
+	      PARSE_OPT_TERM_PARAM (param, param_type, buf_ptr, end);
+	      if (!param_type)
+		break;
+
+	      if (param_type == STEMPARAM)
+		{
+		  stem_param = atoi (param);
+		  if (errno != ERANGE && indexed && stem_param >= 0 && stem_param <= 3)
+		    method_using = stem_to_apply = stem_param;
+		}
+	    }
+
+	  bcopy ((char *) word, (char *) sWord, *word + 1);
+	  stemmer (stem_to_apply, stemmer_num, sWord);
+
+	  if (stem_to_apply == 0 || !indexed || p__sd == NULL)
+	    {
+	      /* [RPAP - Feb 97: Term Frequency] */
+	      word_num = FindWord (p__sd, sWord, &count, &doc_count, &invf_ptr, &invf_len);
+	      if (word_num == -1)
+		count = doc_count = invf_ptr = invf_len = 0;
+	      AddQueryTerm (query_term_list, (u_char *) word, count, method_using);
+
+	      yylval.text = word;
+	      *ptr = buf_ptr; /* fix up ptr */
+	      Xfree (sWord);
+	      return TERM;
+	    }
+	  else
+	    {
+	      *ptr = buf_ptr; /* fix up ptr */
+	      termnum = 0;
+	      ResetTermList (&Terms);
+	      if (FindWords (p__sd, (u_char *) sWord, stem_to_apply, &Terms) > 0)
+		{
+		  /* [RPAP - Feb 97: Term Frequency] */ 
+		  int i, freq = 0;
+		  for (i = 0; i < Terms->num; i++)
+		    freq += Terms->TE[i].WE.count;
+		  AddQueryTerm (query_term_list, word, freq, method_using);
+
+		  Xfree (sWord);
+		  mode = 1;
+		  return '(';
+		}
+	      else
+		{
+		  /* Word does not exists - include in tree anyway */
+		  Xfree (sWord);
+
+		  /* [RPAP - Feb 97: Term Frequency] */
+		  word_num = -1;
+		  count = doc_count = invf_ptr = invf_len = 0;
+		  AddQueryTerm (query_term_list, (u_char *) word, count, method_using);
+
+		  yylval.text = word;
+		  return TERM;
+		}
+	    }
+	}
+      else /* NON-WORD */
+	{
+	  if (*buf_ptr == '\0')
+	    {
+	      /* return null-char if it is one */
+	      *ptr = buf_ptr; /* fix up ptr */
+	      return 0;
+	    }
+	  else
+	    {
+	      /* return 1st char, and delete from buffer */
+	      char c = *buf_ptr++;
+	      *ptr = buf_ptr; /* fix up ptr */
+	      return c;
+	    }
+	}
+    }
+  else if (mode == 1)
+    {
+      yylval.text = Terms->TE[termnum].Word;
+      
+      /* [RPAP - Feb 97: Term Frequency] */
+      word_num = Terms->TE[termnum].WE.word_num;
+      count = Terms->TE[termnum].WE.count;
+      doc_count = Terms->TE[termnum].WE.doc_count;
+      invf_ptr = Terms->TE[termnum].WE.invf_ptr;
+      invf_len = Terms->TE[termnum].WE.invf_len;
+
+      termnum++;
+      mode = 2;
+      return TERM;
+    }
+  else  /* mode == 2 */
+    {
+      if (termnum >= Terms->num)
+	{
+	  mode = 0;
+	  return ')';
+	}
+      else
+	{
+	  mode = 1;
+	  return '|';
+	}
+    }
+}/*query_lex*/
+
+/* =========================================================================
+ * Function: yyerror
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */ 
+static int yyerror(char *s)
+{
+  Message("%s", s);
+  return(1);
+}
+
+ 
+/* =========================================================================
+ * Function: ParseBool
+ * Description:
+ *      Parse a boolean query string into a term-list and a boolean parse tree
+ * Input:
+ *      query_line = query line string
+ *      query_len = query line length
+ *      the_stem_method = stem method id used for stemming
+ * Output:
+ *      the_term_list = the list of terms
+ *      res = parser result code
+ * ========================================================================= */
+ 
+bool_tree_node *
+ParseBool(char *query_line, int query_len,
+          TermList **the_term_list, int the_stemmer_num, int the_stem_method, int *res,
+	  stemmed_dict * the_sd, int is_indexed,   /* [RPAP - Jan 97: Stem Index Change] */
+	  QueryTermList **the_query_term_list)  /* [RPAP - Feb 97: Term Frequency] */
+{
+  /* global variables to be accessed by bison/yacc created parser */
+  term_list = the_term_list;
+  stemmer_num = the_stemmer_num;
+  stem_method = the_stem_method;
+  ch_buf = query_line;
+  end_buf = query_line + query_len;
+  p__sd = the_sd;   /* [RPAP - Jan 97: Stem Index Change] */
+  indexed = is_indexed;  /* [RPAP - Jan 97: Stem Index Change] */
+  query_term_list = the_query_term_list; /* [RPAP - Feb 97: Term Frequency] */
+
+  FreeBoolTree(&(tree_base));
+ 
+  ResetTermList(term_list);
+  ResetQueryTermList(query_term_list);  /* [RPAP - Feb 97: Term Frequency] */
+
+  *res = yyparse();
+ 
+  return tree_base;
+}
+ 
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.c	(revision 16561)
@@ -0,0 +1,353 @@
+/**************************************************************************
+ *
+ * bool_query.c -- Boolean query evaluation
+ * Copyright (C) 1994  Neil Sharman & Tim Shimmin
+ *
+ * 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.
+ *
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "filestats.h"
+#include "timing.h"
+#include "sptree.h"
+
+
+#include "mg.h"
+#include "text.h"
+#include "invf.h"
+#include "lists.h"
+#include "term_lists.h"
+#include "backend.h"
+#include "words.h"
+#include "stemmer.h"
+#include "stem_search.h"
+#include "invf_get.h"
+#include "text_get.h"
+#include "read_line.h"
+#include "bool_tree.h"
+#include "bool_parser.h"
+#include "bool_optimiser.h"
+#include "environment.h"
+
+
+/**********************
+  [RPAP - Feb 97: Term Frequency]
+
+  Shifted to bool_parser.y
+
+  *********************/
+
+/* =========================================================================
+ * Function: ReadInTermInfo
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+/**************************
+static void 
+ReadInTermInfo (query_data * qd)
+{
+  int i;
+  TermList *tl = qd->TL;
+
+  for (i = 0; i < tl->num; i++)
+    {
+      TermEntry *te = &(tl->TE[i]);
+      WordEntry *we = &(te->WE);
+
+       we->word_num = FindWord (qd->sd, te->Word,
+			       &we->count, &we->doc_count,
+			       &we->invf_ptr, &we->invf_len);
+      if (we->word_num == -1)
+	we->count = we->doc_count = 0;
+    }
+}
+
+***********************/
+
+
+
+/* =========================================================================
+ * Function: FindNoneTerms 
+ * Description: 
+ *      Set any terms which are not in dictionary to N_none (i.e. false)
+ *      Assumes a prior call to ReadInTermInfo
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void 
+FindNoneTerms (query_data * qd, bool_tree_node * tree)
+{
+  if (BOOL_HAS_CHILD (tree))
+    {
+      bool_tree_node *child = BOOL_CHILD (tree);
+      while (child)
+	{
+	  FindNoneTerms (qd, child);
+	  child = BOOL_SIBLING (child);
+	}
+    }
+  else if (BOOL_TAG (tree) == N_term)
+    {
+      int doc_count = qd->TL->TE[BOOL_TERM (tree)].WE.doc_count;
+      if (!doc_count)
+	BOOL_TAG (tree) = N_none;
+    }
+
+}				/*FindNoneTerms */
+
+/* =========================================================================
+ * Function: ReduceList
+ * Description: 
+ *      Go thru doc-set and get rid of all docs which are unmarked
+ *      The motivation for this routine is in the case of CNF queries.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+ReduceList (DocList * cset)
+{
+  DocEntry *src;
+  DocEntry *dest;
+
+  src = dest = cset->DE;
+  for (src = dest = cset->DE; src < cset->DE + cset->num; src++)
+    {
+      if (src->or_included)
+	{
+	  src->or_included = 0;
+	  *dest++ = *src;
+	}
+    }
+  cset->num = dest - cset->DE;
+
+}				/*ReduceList */
+
+/* =========================================================================
+ * Function: BooleanGet
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+static DocList *
+BooleanGet (query_data * qd, bool_tree_node * n,
+	    BooleanQueryInfo * bqi)
+{
+  if (n)
+    {
+      DocList *res = NULL;
+      switch (BOOL_TAG (n))
+	{
+	case N_all:
+	  Message ("Unexpected \"all\" node in the parse tree.");
+	  res = MakeDocList (0);
+	  break;
+	case N_not:
+	  Message ("Unexpected \"not\" node in the parse tree.");
+	  res = MakeDocList (0);
+	  break;
+	case N_none:
+	  {
+	    res = MakeDocList (0);
+	    break;
+	  }
+	case N_diff:
+	  {
+	    res = BooleanGet (qd, BOOL_CHILD (n), bqi);
+	    n = BOOL_SIBLING (BOOL_CHILD (n));
+	    while (n)
+	      {
+		bool_tree_node *next = BOOL_SIBLING (n);
+		if (BOOL_TAG (n) == N_term)
+		  {
+		    WordEntry *we = GetNthWE (qd->TL, BOOL_TERM (n));
+		    res = GetDocsOp (qd, we, op_diff_terms, res);
+		  }
+		else
+		  res = DiffLists (res, BooleanGet (qd, n, bqi));
+		n = next;
+	      }
+	    break;
+	  }
+	case N_and:
+	  {
+	    res = BooleanGet (qd, BOOL_CHILD (n), bqi);		/* initial fill of c-set */
+	    n = BOOL_SIBLING (BOOL_CHILD (n));
+	    while (n)
+	      {
+		bool_tree_node *next = BOOL_SIBLING (n);
+		switch (BOOL_TAG (n))
+		  {
+		  case N_term:
+		    {
+		      WordEntry *we = GetNthWE (qd->TL, BOOL_TERM (n));
+		      res = GetDocsOp (qd, we, op_and_terms, res);
+		    }
+		    break;
+		  case N_or_terms:
+		    {
+		      bool_tree_node *child = BOOL_CHILD (n);
+		      /* note: all children are terms - predetermined */
+		      while (child)
+			{
+			  WordEntry *we = GetNthWE (qd->TL, BOOL_TERM (child));
+			  res = GetDocsOp (qd, we, op_and_or_terms, res);
+			  child = BOOL_SIBLING (child);
+			}
+		      ReduceList (res);
+		    }
+		    break;
+		  default:
+		    res = IntersectLists (res, BooleanGet (qd, n, bqi));
+		  }		/*switch */
+		n = next;
+	      }
+	    break;
+	  }
+	case N_or:
+	case N_or_terms:
+	  {
+	    res = BooleanGet (qd, BOOL_CHILD (n), bqi);
+	    n = BOOL_SIBLING (BOOL_CHILD (n));
+	    while (n)
+	      {
+		bool_tree_node *next = BOOL_SIBLING (n);
+		res = MergeLists (res, BooleanGet (qd, n, bqi));
+		n = next;
+	      }
+	    break;
+	  }
+	case N_term:
+	  {
+	    res = GetDocsOp (qd, GetNthWE (qd->TL, BOOL_TERM (n)), op_term, NULL);
+	    break;
+	  }
+	}
+      return (res ? res : MakeDocList (0));
+    }
+  else
+    return MakeDocList (0);
+}
+
+/* =========================================================================
+ * Function: AdjustParaDocs
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+static int 
+DE_comp (void *a, void *b)
+{
+  return ((DocEntry *) a)->SeekPos - ((DocEntry *) b)->SeekPos;
+}
+
+static void
+AdjustParaDocs (query_data * qd, DocList * Docs)
+{
+  DocEntry *DE = Docs->DE;
+  int s, d;
+  Splay_Tree *SP;
+
+
+  /* Make the doc-list contain only references to unique
+   * real documents. So can get rid of cases where there
+   * are multiple paragraphs for a real document.
+   */
+
+  /* Note: [TS: 29/Sep/94] */
+  /* reads in seek-pos for docnums so can compare docs */
+  /* seek-pos used as real doc identifier */
+  /* whereas doc-num entries refer to paragraphs -- I think */
+  for (s = 0; s < Docs->num; s++)
+    FetchDocStart (qd, DE[s].DocNum, &DE[s].SeekPos, &DE[s].Len);
+
+  Message ("Original Number = %d\n", Docs->num);
+  SP = SP_createset (DE_comp);
+  d = 0;
+  for (s = 0; s < Docs->num; s++)
+    if (!SP_member (&DE[s], SP))
+      {
+	DE[d] = DE[s];
+	SP_insert (&DE[d], SP);
+	d++;
+      }
+  SP_freeset (SP);
+  Docs->num = d;
+  Message ("Final Number = %d\n", Docs->num);
+}
+
+/* =========================================================================
+ * Function: BooleanQuery 
+ * Description: 
+ *      The main control function for the boolean query.
+ * Input: 
+ * Output: 
+ *      qd->DL
+ * ========================================================================= */
+
+void 
+BooleanQuery (query_data * qd, char *Query, BooleanQueryInfo * bqi,
+	      int stem_method)
+{
+  bool_tree_node *tree;
+  DocList *Docs;
+  int res = 0;
+
+  tree = ParseBool (Query, MAXLINEBUFFERLEN, &(qd->TL), 
+		    qd->sd->sdh.stemmer_num, stem_method, &res,
+		    qd->sd, qd->sd->sdh.indexed,         /* [RPAP - Jan 97: Stem Index Change] */
+		    &(qd->QTL));                         /* [RPAP - Feb 97: Term Frequency] */
+
+  /* [TS:Aug/94] no longer tries to continue after parse failure */
+  if (res != 0)			/* failed the parse */
+    {
+      qd->DL = NULL;		/* have empty doc-list */
+      return;
+    }
+
+  /* [RPAP - Feb 97: Term Frequency] */
+  /*  ReadInTermInfo (qd); */
+
+  FindNoneTerms (qd, tree);
+
+  OptimiseBoolTree (tree, qd->TL, atoi (GetEnv ("optimise_type")));
+
+  qd->hops_taken = qd->num_of_ptrs = 0;
+  qd->num_of_terms = 0;
+
+  Docs = BooleanGet (qd, tree, bqi);
+
+/* [RPAP - Feb 97: NZDL Specifics] */
+#if !defined(PARABOOLEANMATCHES) && !defined(NZDL)
+  if (qd->id->ifh.InvfLevel == 3)
+    {
+      AdjustParaDocs (qd, Docs);
+    }
+#endif
+
+  if (bqi->MaxDocsToRetrieve != -1 && Docs->num > bqi->MaxDocsToRetrieve)
+    Docs->num = bqi->MaxDocsToRetrieve;
+
+  FreeQueryDocs (qd);
+
+  qd->DL = Docs;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_query.h	(revision 16561)
@@ -0,0 +1,55 @@
+/**************************************************************************
+ *
+ * bool_query.h -- boolean query processing
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:45  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:28  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:56:36  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.1  1994/10/12  01:15:33  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+#ifndef BOOL_QUERY_H
+#define BOOL_QUERY_H
+
+#include "backend.h"
+
+void BooleanQuery (query_data * qd, char *Query, BooleanQueryInfo * bqi,
+		   int stem_method);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tester.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tester.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tester.c	(revision 16561)
@@ -0,0 +1,185 @@
+/**************************************************************************
+ *
+ * bool_tester -- used to test bool_tree, bool_parser, bool_optimiser
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2000/03/05 22:58:24  sjboddie
+   compiler complained about using stdin and stdout to do global
+   initialization - variables were never changed so they were
+   replaced with #defines
+
+   Revision 1.1  1999/08/10 21:17:45  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:41  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:29  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1995/03/14  05:15:23  tes
+   * Updated the boolean query optimiser to do different types of optimisation.
+   * A query environment variable "optimise_type" specifies which one is to be
+   * used. Type 1 is the new one which is faster than 2.
+   *
+   * Revision 1.2  1994/10/18  06:11:05  tes
+   * The boolean optimiser seems to be modifying the parse tree
+   * like it is supposed to.
+   * Paragraph ranking now works without any text files if required to.
+   *
+   * Revision 1.1  1994/10/12  01:15:33  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "local_strings.h"
+#include "term_lists.h"
+#include "bool_tree.h"
+#include "bool_parser.h"
+#include "bool_optimiser.h"
+#include "query_term_list.h"  /* [RPAP - Feb 97: Term Frequency] */
+
+#define MAX_LINE_LEN  255
+#define STEMMER_NUM 0 /* Lovin's stemmer */
+#define STEM_METHOD 3
+
+/* Newer compilers complain when you use stdin and stdout to do global 
+initialization. Since they're static we'll just use #defines instead
+static FILE *file_in = stdin;
+static FILE *file_out = stdout;
+*/
+#define FILE_IN stdin
+#define FILE_OUT stdout
+
+static char line[MAX_LINE_LEN + 1];
+
+/* --- prototypes --- */
+static char *prompt (char *str);
+
+
+/* =========================================================================
+ * Function: main
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int main (int argc, char *argv[])
+{
+  bool_tree_node *tree = NULL;
+  TermList *term_list = NULL;
+  QueryTermList *query_term_list = NULL;  /* [RPAP - Feb 97: Term Frequency] */
+  int opt_type = 0;
+
+  while (1)
+    {
+      int res = 0;
+      int len = 0;
+
+      /* get a line */
+      if (!prompt ("Please enter boolean expression..."))
+	break;
+
+      len = strlen (line) - 1;	/* -1 => ignore the \n */
+
+      tree = ParseBool (line, len, &term_list, STEMMER_NUM, STEM_METHOD, &res,
+			NULL, 0,             /* [RPAP - Jan 97: Stem Index Change] */
+			&query_term_list);   /* [RPAP - Feb 97: Term Frequency] */
+
+      {
+	int done = 0;
+	while (!done)
+	  {
+	    if (!prompt ("Which type of optimisation 1 or 2 ?"))
+	      break;
+	    opt_type = atoi (line);
+	    done = (opt_type == 1 || opt_type == 2);
+	  }
+      }
+
+      if (!prompt ("Do you want to assign doc_counts to terms ?"))
+	break;
+
+      if (toupper (line[0]) == 'Y')
+	{
+	  /* cycle thru terms asking for doc_counts */
+	  int i;
+	  for (i = 0; i < term_list->num; i++)
+	    {
+	      char str[80];
+	      TermEntry *te = &(term_list->TE[i]);
+
+	      sprintf (str, "Please enter doc count for term '%s'",
+		       str255_to_string (te->Word, NULL));
+	      prompt (str);
+	      te->WE.doc_count = atoi (line);
+	      printf ("doc_count = %ld\n", te->WE.doc_count);
+	    }			/*for */
+	}			/*if */
+
+
+      if (res == 0)
+	{
+	  fprintf (FILE_OUT, "\n***Parsed Expression***\n");
+	  PrintBoolTree (tree, FILE_OUT);
+	  fputc ('\n', FILE_OUT);
+	  OptimiseBoolTree (tree, term_list, opt_type);
+	  fprintf (FILE_OUT, "\n***Optimised Expression ***\n");
+	  PrintBoolTree (tree, FILE_OUT);
+	  fputc ('\n', FILE_OUT);
+	}
+
+    }
+  return 0;
+}
+
+/* =========================================================================
+ * Function: 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static char *
+prompt (char *str)
+{
+  fprintf (FILE_OUT, "\n%s\n", str);
+  return fgets (line, MAX_LINE_LEN, FILE_IN);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.c	(revision 16561)
@@ -0,0 +1,292 @@
+/**************************************************************************
+ *
+ * bool_tree.c -- Boolean parse tree ADT
+ * Copyright (C) 1994  Tim Shimmin
+ *
+ * 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.
+ *
+ *       @(#)query.bool.y	1.9 16 Mar 1994
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "bool_tree.h"
+
+static char *RCSID = "$Id$";
+
+
+/* =========================================================================
+ * Function: CreateBoolNode
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+bool_tree_node *
+CreateBoolNode (N_Tag tag)
+{
+  bool_tree_node *n;
+  if (!(n = Xmalloc (sizeof (bool_tree_node))))
+    FatalError (1, "Can not create bool_tree_node for boolean query");
+  n->tag = tag;
+  BOOL_SIBLING (n) = NULL;
+  return n;
+}
+
+/* =========================================================================
+ * Function: 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+bool_tree_node *
+CreateBoolTermNode (TermList ** tl, char *text, int Count, int word_num,
+		    u_long count, u_long doc_count, u_long invf_ptr, u_long invf_len,  /* [RPAP - Feb 97: Term Frequency] */
+		    int stemmer_num) 
+{
+  bool_tree_node *n = NULL;
+
+  /* allocate bool_tree_node */
+  n = CreateBoolNode (N_term);
+
+  BOOL_TERM (n) = AddTerm (tl, (u_char *) text, Count, word_num, count, doc_count, 
+			   invf_ptr, invf_len, stemmer_num);  /* [RPAP - Feb 97: Term Frequency] */
+
+  return n;
+}
+
+
+/* =========================================================================
+ * Function: CreateTreeNode
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+bool_tree_node *
+CreateBoolTreeNode (N_Tag tag, bool_tree_node * left, bool_tree_node * right)
+{
+  bool_tree_node *n = CreateBoolNode (tag);
+
+  BOOL_CHILD (n) = left;
+  if (left)
+    BOOL_SIBLING (left) = right;
+  if (right)
+    BOOL_SIBLING (right) = NULL;
+
+  return (n);
+}
+
+/* =========================================================================
+ * Function: 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+EqualBoolNodes (bool_tree_node * a, bool_tree_node * b)
+{
+  if (BOOL_TAG (a) == N_term && BOOL_TAG (a) == N_term)
+    return BOOL_TERM (a) == BOOL_TERM (b);
+  return 0;
+}
+
+/* =========================================================================
+ * Function: FreeBoolTree
+ * Description: 
+ *      Notes: does not fix up any parent or sibling links around given tree
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void 
+FreeTree (bool_tree_node * tree)
+{
+  bool_tree_node *sibling;
+
+  while (tree)
+    {
+      if (BOOL_HAS_CHILD (tree))
+	FreeTree (BOOL_CHILD (tree));
+      sibling = BOOL_SIBLING (tree);
+      Xfree (tree);
+      tree = sibling;
+    }
+}
+
+void 
+FreeBoolTree (bool_tree_node ** the_tree)
+{
+  bool_tree_node *tree = *the_tree;
+
+  if (!tree)
+    return;
+
+  FreeTree (tree);
+  *the_tree = NULL;
+/*********************************/
+  /* THIS IS POTENTIALLY DANGEROUS */
+  /* FIX UP LATER */
+  /* Need to correct the links for parent/siblings */
+/*********************************/
+
+}
+
+/* =========================================================================
+ * Function: PrintBoolTree
+ * Description: 
+ *      Print out a text version of the tree to the file
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+PrintBinaryOp (bool_tree_node * tree, FILE * file, char op)
+{
+  bool_tree_node *child = NULL;
+  assert (BOOL_HAS_CHILD (tree));
+
+  child = BOOL_CHILD (tree);
+  while (child)
+    {
+      PrintBoolTree (child, file);
+      child = BOOL_SIBLING (child);
+      if (child)
+	fprintf (file, " %c ", op);
+    }
+}
+
+static void
+PrintUnaryOp (bool_tree_node * tree, FILE * file, char op)
+{
+  bool_tree_node *child = NULL;
+
+  fprintf (file, " %c ", op);
+  assert (BOOL_HAS_CHILD (tree));
+  child = BOOL_CHILD (tree);
+  PrintBoolTree (child, file);
+}
+
+void
+PrintBoolTree (bool_tree_node * tree, FILE * file)
+{
+  if (!tree)
+    return;
+
+  fprintf (file, "(");
+  switch (BOOL_TAG (tree))
+    {
+    case N_term:
+      fprintf (file, "term %d", BOOL_TERM (tree));
+      break;
+    case N_and:
+      PrintBinaryOp (tree, file, '&');
+      break;
+    case N_or:
+      PrintBinaryOp (tree, file, '|');
+      break;
+    case N_or_terms:
+      PrintBinaryOp (tree, file, '+');
+      break;
+    case N_diff:
+      PrintBinaryOp (tree, file, '-');
+      break;
+    case N_not:
+      PrintUnaryOp (tree, file, '!');
+      break;
+    case N_all:
+      fprintf (file, " TRUE ");
+      break;
+    case N_none:
+      fprintf (file, " FALSE ");
+      break;
+    default:
+      break;
+    }
+
+  fprintf (file, ")");
+}
+
+
+/* =========================================================================
+ * Function: CopyBoolTree
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+bool_tree_node *
+CopyBoolTree (bool_tree_node * tree)
+{
+  bool_tree_node *sibling = NULL;
+  bool_tree_node *tree_copy = NULL;
+  bool_tree_node *prev = NULL;
+
+  if (!tree)
+    return NULL;
+
+  sibling = tree;
+  prev = NULL;
+  while (sibling)
+    {
+      /* copy sibling */
+      bool_tree_node *sibling_copy = (bool_tree_node *) malloc (sizeof (bool_tree_node));
+      if (!sibling_copy)
+	FatalError (1, "No memory for bool tree copy");
+      bcopy ((char *) sibling, (char *) sibling_copy, sizeof (bool_tree_node));
+
+      if (BOOL_HAS_CHILD (sibling))
+	{
+	  BOOL_CHILD (sibling_copy) = CopyBoolTree (BOOL_CHILD (sibling));
+	}
+
+      /* do the link up of copies */
+      if (prev)
+	BOOL_SIBLING (prev) = sibling_copy;
+      else
+	tree_copy = sibling_copy;
+      prev = sibling_copy;
+
+      sibling = BOOL_SIBLING (sibling);
+    }
+  return tree_copy;
+}
+
+
+/* =========================================================================
+ * Function: LastBoolSibling
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+bool_tree_node *
+LastBoolSibling (bool_tree_node * tree)
+{
+
+  if (!tree)
+    return NULL;
+
+  do
+    {
+      tree = BOOL_SIBLING (tree);
+    }
+  while (BOOL_SIBLING (tree));
+
+  return tree;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/bool_tree.h	(revision 16561)
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * bool_tree.h -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:46  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:42  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:31  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1995/03/14  05:15:25  tes
+   * Updated the boolean query optimiser to do different types of optimisation.
+   * A query environment variable "optimise_type" specifies which one is to be
+   * used. Type 1 is the new one which is faster than 2.
+   *
+   * Revision 1.2  1994/10/18  06:11:07  tes
+   * The boolean optimiser seems to be modifying the parse tree
+   * like it is supposed to.
+   * Paragraph ranking now works without any text files if required to.
+   *
+   * Revision 1.1  1994/10/12  01:15:35  tes
+   * Found bugs in the existing boolean query optimiser.
+   * So decided to rewrite it.
+   * I accidentally deleted query.bool.y, but I have replaced it
+   * with bool_parser.y (which I have forgotten to add here ! ;-(
+   *
+ */
+
+#ifndef BOOL_TREE_H
+#define BOOL_TREE_H
+
+#include "sysfuncs.h"
+#include "term_lists.h"
+
+typedef enum
+  {
+    N_term, N_and, N_or, N_diff, N_none, N_all,
+    N_not, N_or_terms
+  }
+N_Tag;
+
+typedef struct bool_tree_node
+  {
+    N_Tag tag;
+    struct bool_tree_node *sibling;
+    union
+      {
+	struct bool_tree_node *child;
+	int term_id;
+      }
+    fields;
+    int sum_ft;			/* for or_terms nodes */
+  }
+bool_tree_node;
+
+
+#define BOOL_HAS_CHILD(n) (\
+			(n)->tag==N_and || \
+			(n)->tag==N_or || (n)->tag==N_or_terms || \
+			(n)->tag==N_not || (n)->tag==N_diff)
+#define BOOL_TAG(n) ((n)->tag)
+#define BOOL_SIBLING(n) ((n)->sibling)
+#define BOOL_TERM(n) ((n)->fields.term_id)
+#define BOOL_CHILD(n) ((n)->fields.child)
+#define BOOL_SUM_FT(n) ((n)->sum_ft)
+
+bool_tree_node *CreateBoolNode (N_Tag tag);
+bool_tree_node *CopyBoolTree (bool_tree_node * tree);
+bool_tree_node *CreateBoolTermNode (TermList ** tl, char *text, int Count, int word_num,
+				    u_long count, u_long doc_count, u_long invf_ptr, 
+				    u_long invf_len, int stemmer_num);
+bool_tree_node *CreateBoolTreeNode (N_Tag tag,
+			     bool_tree_node * left, bool_tree_node * right);
+void FreeBoolTree (bool_tree_node ** tree);
+void PrintBoolTree (bool_tree_node * tree, FILE * file);
+bool_tree_node *LastBoolSibling (bool_tree_node * tree);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/build.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/build.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/build.h	(revision 16561)
@@ -0,0 +1,171 @@
+/**************************************************************************
+ *
+ * build.h -- Global information for the passes of mg_passes
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ *
+ * 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 H_BUILD
+#define H_BUILD
+
+#include "longlong.h"
+
+#define	TERMPARAGRAPH	'\003'
+
+
+int init_special (char *file_name);
+int init_text_1 (char *file_name);
+int init_text_2 (char *file_name);
+int init_invf_1 (char *file_name);
+int init_invf_2 (char *file_name);
+int init_ivf_1 (char *file_name);
+int init_ivf_2 (char *file_name);
+/*
+ * file_name  IN      The name of the dictionary file
+ * 
+ * returns      COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot read file
+ */
+
+
+
+
+
+int process_special (u_char * s_in, int l_in);
+int process_text_1 (u_char * s_in, int l_in);
+int process_text_2 (u_char * s_in, int l_in);
+int process_invf_1 (u_char * s_in, int l_in);
+int process_invf_2 (u_char * s_in, int l_in);
+int process_ivf_1 (u_char * s_in, int l_in);
+int process_ivf_2 (u_char * s_in, int l_in);
+/*
+ * s_in               IN      The binary string to be compressed
+ * l_in         IN      The number of characters in s_in
+ *
+ * returns      COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot read file
+ *
+ * The calling routine is responsible for ensuring that s_out is long 
+ * enough.
+ */
+
+
+
+
+
+int done_special (char *filename);
+int done_text_1 (char *filename);
+int done_text_2 (char *filename);
+int done_invf_1 (char *filename);
+int done_invf_2 (char *filename);
+int done_ivf_1 (char *filename);
+int done_ivf_2 (char *filename);
+/*
+ * returns    COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot write file
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern char InvfLevel;
+/*
+ * This will determine the level of the inverted file it can take on the
+ * values 1, 2, or 3.
+ *
+ * Level 1: The inverted file contains only document numbers making it possible
+ *          to do only boolean queries.
+ * 
+ * Level 2: The inverted file also contains word counts per document making it
+ *          possible to do cosine ranked queries.
+ *
+ * Level 3: The inverted file contains word positions.
+ */
+
+
+extern unsigned long buf_size;
+/*
+ * The size of the document input buffer.
+ */
+
+extern unsigned long ChunkLimit;
+/*
+ * The maximum number of chunks that can be written to disk.
+ */
+
+extern unsigned long invf_buffer_size;
+/*
+ * The amount of memory to allocate to the invertion buffer.
+ */
+
+extern char SkipSGML;
+/*
+ * 1 if SGML tags are to be considered non-words when building the 
+ * inverted file.
+ */
+
+extern char MakeWeights;
+/*
+ * 1 if the weights file should be generated.
+ */
+
+extern FILE *Comp_Stats;
+/*
+ * Contains a file pointer to the file where compression stats should be sent
+ */
+
+extern int comp_stat_point;
+/*
+ * Generate a compression stat entry every comp_stat_point bytes 
+ */
+
+extern mg_ullong bytes_processed;
+/*
+ * The number of bytes processed. NOTE: This excludes document separators.
+ */
+
+extern mg_ullong bytes_received;
+/*
+ * The number of bytes processed. NOTE: This includes document separators.
+ */
+
+
+extern int stemmer_num;
+/*
+ * The stemmer to use for stemming words for the inverted file.
+ * see stemmer.h
+ */
+
+extern int stem_method;
+/*
+ * The method to use for stemming words for the inverted file.
+ * see stemmer.h
+ */
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/commands.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/commands.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/commands.c	(revision 16561)
@@ -0,0 +1,585 @@
+/**************************************************************************
+ *
+ * commands.c -- mgquery command processing functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:47  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:32  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1994/09/20  04:41:22  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+
+#include "environment.h"
+#include "mg.h"
+#include "locallib.h"
+#include "backend.h"		/* for qd */
+#include "term_lists.h"		/* for MAXTERMSTRLEN */
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+# define OPENPIPE        _popen
+# define CLOSEPIPE       _pclose
+#else
+# define OPENPIPE        popen
+# define CLOSEPIPE       pclose
+#endif
+
+extern char *PathName;
+extern FILE *OutFile, *InFile;
+extern int OutPipe, InPipe;
+extern int Quitting;
+
+char *CommandsErrorStr = NULL;
+
+/* [TS: June/95] */
+/* current qd for use by any commands */
+static query_data *the_qd = NULL;
+
+
+#include "help.mg.h"
+#include "warranty.h"
+#include "conditions.h"
+
+static void 
+CmdQuit (void)
+{
+  Quitting = 1;
+}
+
+static void 
+CmdPush (void)
+{
+  PushEnv ();
+}
+
+
+static void 
+CmdPop (void)
+{
+  PopEnv ();
+  if (EnvStackHeight () == 1)
+    PushEnv ();
+}
+
+static void 
+CmdHelp (void)
+{
+  FILE *more;
+  int i;
+
+  if (!(more = OPENPIPE (GetDefEnv ("pager", "more"), "w")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf (stderr, "ERROR : Unable to run more\n");
+      return;
+    }
+  for (i = 0; i < sizeof (help_str) / sizeof (help_str[0]); i++)
+    fputs (help_str[i], more);
+
+  CLOSEPIPE (more);  /* [RPAP - Feb 97: WIN32 Port] */
+}
+
+
+static void 
+CmdWarranty (void)
+{
+  FILE *more;
+  int i;
+
+  if (!(more = OPENPIPE (GetDefEnv ("pager", "more"), "w")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf (stderr, "ERROR : Unable to run more\n");
+      return;
+    }
+  for (i = 0; i < sizeof (warranty_str) / sizeof (warranty_str[0]); i++)
+    fputs (warranty_str[i], more);
+
+  CLOSEPIPE (more);  /* [RPAP - Feb 97: WIN32 Port] */
+}
+
+
+static void 
+CmdConditions (void)
+{
+  FILE *more;
+  int i;
+
+  if (!(more = OPENPIPE (GetDefEnv ("pager", "more"), "w")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf (stderr, "ERROR : Unable to run more\n");
+      return;
+    }
+  for (i = 0; i < sizeof (cond_str) / sizeof (cond_str[0]); i++)
+    fputs (cond_str[i], more);
+
+  CLOSEPIPE (more);
+}
+
+static void 
+CmdReset (void)
+{
+  while (EnvStackHeight () > 1)
+    PopEnv ();
+  PushEnv ();
+}
+
+static void 
+CmdDisplay (void)
+{
+  int i;
+  size_t l = 0;
+  char *name;
+  for (i = 0; (name = GetEnvName (i)) != NULL; i++)
+    if (strlen (name) > l)
+      l = strlen (name);
+
+  for (i = 0; (name = GetEnvName (i)) != NULL; i++)
+    printf ("%-*s = \"%s\"\n", (int) l, name, GetEnv (name));
+}
+
+static void 
+CmdQueryTerms (void)
+{
+  static char terms_str[MAXTERMSTRLEN + 1];
+
+  if (the_qd && the_qd->TL)
+    {
+      ConvertTermsToString (the_qd->TL, terms_str);
+      printf ("%s\n", terms_str);
+      fflush (stdout);
+    }
+
+}
+
+
+static void 
+CmdSet (char *name, char *value)
+{
+  if (!*name)
+    {
+      CommandsErrorStr = "Blank names are not permitted";
+      return;
+    }
+  switch (SetEnv (name, value, NULL))
+    {
+    case -1:
+      CommandsErrorStr = "Out of memory\n";
+      break;
+    case -2:
+      CommandsErrorStr = ConstraintErrorStr;
+      break;
+    }
+}
+
+static void 
+CmdUnset (char *name)
+{
+  if (!*name)
+    {
+      CommandsErrorStr = "Blank names are not permitted";
+      return;
+    }
+  if (UnsetEnv (name, 0) == -1)
+    CommandsErrorStr = "Specified name does not exist";
+}
+
+
+
+
+
+
+/*
+ * Do the .input command 
+ */
+static void 
+CmdInput (char *param1, char *param2)
+{
+  enum
+  {
+    READ, APPEND, PIPEFROM
+  }
+  mode;
+  FILE *NewInput;
+  char *buf, *s;
+
+  /* join the two parameters together */
+  buf = Xmalloc (strlen (param1) + strlen (param2) + 1);
+  strcpy (buf, param1);
+  strcat (buf, param2);
+  s = buf;
+
+
+  while (*s == ' ' || *s == '\t')
+    s++;
+
+  if (*s == '<')
+    {
+      s++;
+      mode = READ;
+    }
+  else if (*s == '|')
+    {
+      mode = PIPEFROM;
+      s++;
+    }
+  else
+    {
+      if (InPipe)
+	CLOSEPIPE (InFile);  /* [RPAP - Feb 97: WIN32 Port] */
+      else if (InFile != stdin)
+	fclose (InFile);
+      InPipe = 0;
+      InFile = stdin;
+      Xfree (buf);
+      return;
+    }
+
+  while (*s == ' ' || *s == '\t')
+    s++;
+
+  if (*s)
+    {
+      if (mode == READ)
+	NewInput = fopen (s, "r");
+      else
+	NewInput = OPENPIPE (s, "r");  /* [RPAP - Feb 97: WIN32 Port] */
+
+      if (NewInput)
+	{
+	  if (InPipe)
+	    CLOSEPIPE (InFile);  /* [RPAP - Feb 97: WIN32 Port] */
+	  else if (InFile != stdin)
+	    fclose (InFile);
+	  InPipe = (mode == PIPEFROM);
+	  InFile = NewInput;
+	}
+      else
+	CommandsErrorStr = "Bad input file/pipe";
+    }
+  else
+    {
+      if (InPipe)
+	CLOSEPIPE (InFile);  /* [RPAP - Feb 97: WIN32 Port] */
+      else if (InFile != stdin)
+	fclose (InFile);
+      InPipe = 0;
+      InFile = stdin;
+    }
+  Xfree (buf);
+}
+
+
+
+static void 
+CmdOutput (char *param1, char *param2)
+{
+  enum
+  {
+    WRITE, APPEND, PIPETO
+  }
+  mode;
+  char *buf, *s;
+  FILE *NewOutput;
+
+  /* join the two parameters together */
+  buf = Xmalloc (strlen (param1) + strlen (param2) + 1);
+  strcpy (buf, param1);
+  strcat (buf, param2);
+  s = buf;
+
+  while (*s == ' ' || *s == '\t')
+    s++;
+
+  if (*s == '>')
+    {
+      s++;
+      mode = APPEND;
+      if (*s == '>')
+	s++;
+      else
+	mode = WRITE;
+    }
+  else if (*s == '|')
+    {
+      mode = PIPETO;
+      s++;
+    }
+  else
+    {
+      if (OutPipe)
+	CLOSEPIPE (OutFile);  /* [RPAP - Feb 97: WIN32 Port] */
+      else if (OutFile != stdout)
+	fclose (OutFile);
+      OutPipe = 0;
+      OutFile = stdout;
+      Xfree (buf);
+      return;
+    }
+
+  while (*s == ' ' || *s == '\t')
+    s++;
+
+  if (*s)
+    {
+      if (mode == PIPETO)
+	NewOutput = OPENPIPE (s, "w");  /* [RPAP - Feb 97: WIN32 Port] */
+      else
+	NewOutput = fopen (s, mode == WRITE ? "w" : "a");
+      if (NewOutput)
+	{
+	  if (OutPipe)
+	    CLOSEPIPE (OutFile);  /* [RPAP - Feb 97: WIN32 Port] */
+	  else if (OutFile != stdout)
+	    fclose (OutFile);
+	  OutPipe = (mode == PIPETO);
+	  OutFile = NewOutput;
+	}
+      else
+	CommandsErrorStr = "Bad output file/pipe";
+    }
+  else
+    {
+      if (OutPipe)
+	CLOSEPIPE (OutFile);  /* [RPAP - Feb 97: WIN32 Port] */
+      else if (OutFile != stdout)
+	fclose (OutFile);
+      OutPipe = 0;
+      OutFile = stdout;
+    }
+  Xfree (buf);
+}
+
+
+
+static char *
+ParseArg (char *line, char *buffer)
+{
+  if (*line == '\"')
+    {
+      line++;
+      while (*line != '\"' && *line != '\0')
+	{
+	  if (*line == '\\')
+	    *buffer++ = *line++;
+	  if (*line != '\0')
+	    *buffer++ = *line++;
+	}
+      if (*line == '\"')
+	line++;
+    }
+  else
+    while (*line != ' ' && *line != '\t' && *line != '\0')
+      {
+	if (*line == '\\')
+	  line++;
+	if (*line != '\0')
+	  *buffer++ = *line++;
+      }
+  *buffer = '\0';
+  while (*line == ' ' || *line == '\t')
+    line++;
+  return (line);
+}
+
+
+
+char *
+ProcessCommands (char *line, query_data * qd)
+{
+  struct
+  {
+    char *Command;
+    void (*action) (void);
+    void (*action1) (char *);
+    void (*action2) (char *, char *);
+  }
+   *dc, dot_command[] =
+  {
+    {
+      "set", NULL, NULL, CmdSet
+    }
+    ,
+    {
+      "input", NULL, NULL, CmdInput
+    }
+    ,
+    {
+      "output", NULL, NULL, CmdOutput
+    }
+    ,
+    {
+      "unset", NULL, CmdUnset, NULL
+    }
+    ,
+    {
+      "help", CmdHelp, NULL, NULL
+    }
+    ,
+    {
+      "reset", CmdReset, NULL, NULL
+    }
+    ,
+    {
+      "display", CmdDisplay, NULL, NULL
+    }
+    ,
+    {
+      "push", CmdPush, NULL, NULL
+    }
+    ,
+    {
+      "quit", CmdQuit, NULL, NULL
+    }
+    ,
+    {
+      "pop", CmdPop, NULL, NULL
+    }
+    ,
+    {
+      "warranty", CmdWarranty, NULL, NULL
+    }
+    ,
+    {
+      "conditions", CmdConditions, NULL, NULL
+    }
+    ,
+    {
+      "queryterms", CmdQueryTerms, NULL, NULL
+    }
+  };
+
+
+  char command[512];
+  char param1[512], param2[512];
+  CommandsErrorStr = NULL;
+  the_qd = qd;			/* set globally to be accessed by command functions if needed */
+  while (*line == ' ' || *line == '\t')
+    line++;
+  while (*line == '.')
+    {
+      int i;
+      line++;
+      for (i = 0; isalpha (*line) && i < 512; i++, line++)
+	command[i] = tolower (*line);
+      while (*line == ' ' || *line == '\t')
+	line++;
+      if (i != 512)
+	{
+	  command[i] = '\0';
+	  for (i = 0, dc = dot_command; i < NUMOF (dot_command); i++, dc++)
+	    if (!strcmp (dc->Command, command))
+	      {
+		if (dc->action)
+		  dc->action ();
+		else if (dc->action1)
+		  {
+		    line = ParseArg (line, param1);
+		    dc->action1 (param1);
+		    break;
+		  }
+		else if (dc->action2)
+		  {
+		    line = ParseArg (line, param1);
+		    line = ParseArg (line, param2);
+		    dc->action2 (param1, param2);
+		    break;
+		  }
+		if (CommandsErrorStr)
+		  *line = '\0';
+		break;
+	      }
+	  if (i == NUMOF (dot_command))
+	    i = 100;
+	}
+      if (i == 100)
+	{
+	  CommandsErrorStr = "Illegal command";
+	  *line = '\0';
+	}
+      while (*line == ' ' || *line == '\t')
+	line++;
+    }
+  return (line);
+}
+
+
+
+
+
+void 
+read_mgrc_file (void)
+{
+  char line[1000];
+  char FileName[256];
+  int LineNum = 0;
+  FILE *mgrc;
+
+  strcpy (FileName, ".mgrc");
+  mgrc = fopen (FileName, "r");
+
+  if (!mgrc)
+    {
+      char *home_path = getenv ("HOME");
+      if (home_path)
+	{
+	  sprintf (FileName, "%s/.mgrc", home_path);
+	  mgrc = fopen (FileName, "r");
+	}
+    }
+
+  if (!mgrc)
+    return;
+
+  while (fgets (line, sizeof (line), mgrc))
+    {
+      char *s, linebuf[1000];
+      LineNum++;
+      if ((s = strchr (line, '\n')) != NULL)
+	*s = '\0';
+      strcpy (linebuf, line);
+      if ((s = strchr (line, '#')) != NULL)
+	*s = '\0';
+      if (!*line)
+	continue;
+      ProcessCommands (line, NULL);
+      if (CommandsErrorStr)
+	Message ("%s\n%s : ERROR in line %d of %s\n%s : %s\n",
+		 linebuf,
+		 msg_prefix, LineNum, FileName,
+		 msg_prefix, CommandsErrorStr);
+    }
+  fclose (mgrc);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/commands.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/commands.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/commands.h	(revision 16561)
@@ -0,0 +1,33 @@
+/**************************************************************************
+ *
+ * commands.h -- mgquery command processing functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 COMMANDS_H
+#define COMMANDS_H
+
+#include "backend.h"
+
+extern char *CommandsErrorStr;
+char *ProcessCommands (char *line, query_data * qd);
+void read_mgrc_file (void);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.c	(revision 16561)
@@ -0,0 +1,258 @@
+/**************************************************************************
+ *
+ * comp_dict.c -- Functions for loading the compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "huffman.h"
+#include "local_strings.h"
+#include "memlib.h"
+#include "messages.h"
+
+#include "mg.h"
+#include "hash.h"
+#include "text.h"
+#include "comp_dict.h"
+#include "locallib.h"
+#include "mg_files.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:48  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:34  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:41  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:24  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+compression_dict_header cdh;
+compressed_text_header cth;
+comp_frags_header cfh[2];
+
+dict_hash_table *ht[2];
+
+huff_data char_huff[2];
+huff_data lens_huff[2];
+u_long *char_codes[2], *lens_codes[2];
+u_long Words_disk = 0;
+u_long Chars_disk = 0;
+
+
+static dict_hash_table *
+ReadInWords (FILE * dict, comp_frags_header * cfh,
+	     int esc)
+{
+  int i;
+  u_char *allwords, *prev = NULL;
+  dict_hash_table *ht;
+  u_char **words;
+  u_long ht_size;
+
+  ht_size = prime (cfh->hd.num_codes * HASH_RATIO);
+  if (!(ht = Xmalloc (sizeof (dict_hash_table) +
+		      (ht_size - 1) * sizeof (ht->table[0]))))
+    {
+      Message ("no memory for hash_table\n");
+      return NULL;
+    }
+
+  ht->size = ht_size;
+  ht->hd = &cfh->hd;
+
+  if (!(ht->codes = Generate_Huffman_Codes (&cfh->hd, NULL)))
+    {
+      Message ("no memory for huffman codes\n");
+      return NULL;
+    }
+
+  if (!(ht->words = Xmalloc (sizeof (u_char *) * cfh->hd.num_codes)))
+    {
+      Message ("no memory for word pointers\n");
+      return NULL;
+    }
+  words = ht->words;
+
+  bzero ((char *) ht->table, ht_size * sizeof (ht->table[0]));
+
+  if (!(allwords = Xmalloc (sizeof (u_char) * cfh->uncompressed_size)))
+    {
+      Message ("no memory for words\n");
+      return NULL;
+    }
+
+  for (i = 0; i < cfh->hd.num_codes; i++, words++)
+    {
+      register int val, copy;
+      val = fgetc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      *words = allwords;
+
+      memcpy (allwords + 1, prev + 1, copy);
+      fread (allwords + copy + 1, sizeof (u_char), val, dict);
+      *allwords = val + copy;
+
+      Words_disk += val + 1;
+
+      /* insert into the hash table */
+      if (i < cfh->hd.num_codes - esc)
+	{
+	  register u_char **wptr;
+	  register int tsize = ht->size;
+	  register unsigned long hashval, step;
+
+	  HASH (hashval, step, allwords, tsize);
+
+	  wptr = ht->table[hashval];
+	  for (; wptr;)
+	    {
+	      hashval += step;
+	      if (hashval >= tsize)
+		hashval -= tsize;
+	      wptr = ht->table[hashval];
+	    }
+	  ht->table[hashval] = words;
+	}
+      prev = allwords;
+      allwords += *allwords + 1;
+    }
+  return ht;
+}
+
+
+
+int 
+LoadCompressionDictionary (char *dict_file_name)
+{
+  FILE *dict;
+  int which;
+  if (!(dict = open_named_file (dict_file_name, "rb", MAGIC_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return COMPERROR;
+
+  Words_disk = sizeof (u_long);
+
+  if (Read_cdh (dict, &cdh, NULL, &Words_disk) == -1)
+    goto error;
+
+  for (which = 0; which < 2; which++)
+    switch (cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  if (Read_cfh (dict, &cfh[which], NULL, &Words_disk) == -1)
+	    goto error;
+
+	  if (!(ht[which] = ReadInWords (dict, &cfh[which], 0)))
+	    goto error;
+
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    {
+	      if (Read_cfh (dict, &cfh[which], NULL, &Words_disk) == -1)
+		goto error;
+	      if (!(ht[which] = ReadInWords (dict, &cfh[which], 1)))
+		goto error;
+	    }
+	  else
+	    ht[which] = NULL;
+	  if (Read_Huffman_Data (dict, &char_huff[which], NULL,
+				 &Chars_disk) == -1)
+	    goto error;
+	  if (!(char_codes[which] =
+		Generate_Huffman_Codes (&char_huff[which], NULL)))
+	    goto error;
+	  if (Read_Huffman_Data (dict, &lens_huff[which], NULL,
+				 &Chars_disk) == -1)
+	    goto error;
+	  if (!(lens_codes[which] =
+		Generate_Huffman_Codes (&lens_huff[which], NULL)))
+	    goto error;
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    {
+	      if (Read_cfh (dict, &cfh[which], NULL, &Words_disk) == -1)
+		goto error;
+	      if (!(ht[which] = ReadInWords (dict, &cfh[which], 1)))
+		goto error;
+	    }
+	  else
+	    ht[which] = NULL;
+	  switch (cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      if (Read_Huffman_Data (dict, &char_huff[which], NULL,
+				     &Chars_disk) == -1)
+		goto error;
+	      if (!(char_codes[which] =
+		    Generate_Huffman_Codes (&char_huff[which], NULL)))
+		goto error;
+	      if (Read_Huffman_Data (dict, &lens_huff[which], NULL,
+				     &Chars_disk) == -1)
+		goto error;
+	      if (!(lens_codes[which] =
+		    Generate_Huffman_Codes (&lens_huff[which], NULL)))
+		goto error;
+	      break;
+	    case MG_NOVEL_BINARY:
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    case MG_NOVEL_HYBRID_MTF:
+	      break;
+	    default:
+	      FatalError (1, "Bad novel method");
+	    }
+	}
+	break;
+      default:
+	FatalError (1, "Bad dictionary kind\n");
+      }
+
+  return (COMPALLOK);
+
+
+error:
+  fclose (dict);
+  return (COMPERROR);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/comp_dict.h	(revision 16561)
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * comp_dict.h -- Functions for loading the compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+
+
+
+#define HASH_RATIO 2
+
+
+
+
+typedef struct dict_hash_table
+  {
+    u_long size;
+    huff_data *hd;
+    u_long *codes;
+    u_char **words;
+    u_char **table[1];
+  }
+dict_hash_table;
+
+
+
+
+extern compression_dict_header cdh;
+extern compressed_text_header cth;
+extern comp_frags_header cfh[2];
+
+extern dict_hash_table *ht[2];
+
+extern huff_data char_huff[2];
+extern huff_data lens_huff[2];
+extern u_long *char_codes[2], *lens_codes[2];
+extern u_long Words_disk;
+extern u_long Chars_disk;
+
+
+
+int LoadCompressionDictionary (char *dict_file_name);
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.h	(revision 16561)
@@ -0,0 +1,226 @@
+static char *cond_str[] = {
+"		    GNU GENERAL PUBLIC LICENSE\n",
+"   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n",
+"\n",
+"  0. This License applies to any program or other work which contains\n",
+"a notice placed by the copyright holder saying it may be distributed\n",
+"under the terms of this General Public License.  The \"Program\", below,\n",
+"refers to any such program or work, and a \"work based on the Program\"\n",
+"means either the Program or any derivative work under copyright law:\n",
+"that is to say, a work containing the Program or a portion of it,\n",
+"either verbatim or with modifications and/or translated into another\n",
+"language.  (Hereinafter, translation is included without limitation in\n",
+"the term \"modification\".)  Each licensee is addressed as \"you\".\n",
+"\n",
+"Activities other than copying, distribution and modification are not\n",
+"covered by this License; they are outside its scope.  The act of\n",
+"running the Program is not restricted, and the output from the Program\n",
+"is covered only if its contents constitute a work based on the\n",
+"Program (independent of having been made by running the Program).\n",
+"Whether that is true depends on what the Program does.\n",
+"\n",
+"  1. You may copy and distribute verbatim copies of the Program's\n",
+"source code as you receive it, in any medium, provided that you\n",
+"conspicuously and appropriately publish on each copy an appropriate\n",
+"copyright notice and disclaimer of warranty; keep intact all the\n",
+"notices that refer to this License and to the absence of any warranty;\n",
+"and give any other recipients of the Program a copy of this License\n",
+"along with the Program.\n",
+"\n",
+"You may charge a fee for the physical act of transferring a copy, and\n",
+"you may at your option offer warranty protection in exchange for a fee.\n",
+"\n",
+"  2. You may modify your copy or copies of the Program or any portion\n",
+"of it, thus forming a work based on the Program, and copy and\n",
+"distribute such modifications or work under the terms of Section 1\n",
+"above, provided that you also meet all of these conditions:\n",
+"\n",
+"    a) You must cause the modified files to carry prominent notices\n",
+"    stating that you changed the files and the date of any change.\n",
+"\n",
+"    b) You must cause any work that you distribute or publish, that in\n",
+"    whole or in part contains or is derived from the Program or any\n",
+"    part thereof, to be licensed as a whole at no charge to all third\n",
+"    parties under the terms of this License.\n",
+"\n",
+"    c) If the modified program normally reads commands interactively\n",
+"    when run, you must cause it, when started running for such\n",
+"    interactive use in the most ordinary way, to print or display an\n",
+"    announcement including an appropriate copyright notice and a\n",
+"    notice that there is no warranty (or else, saying that you provide\n",
+"    a warranty) and that users may redistribute the program under\n",
+"    these conditions, and telling the user how to view a copy of this\n",
+"    License.  (Exception: if the Program itself is interactive but\n",
+"    does not normally print such an announcement, your work based on\n",
+"    the Program is not required to print an announcement.)\n",
+"
+\n",
+"These requirements apply to the modified work as a whole.  If\n",
+"identifiable sections of that work are not derived from the Program,\n",
+"and can be reasonably considered independent and separate works in\n",
+"themselves, then this License, and its terms, do not apply to those\n",
+"sections when you distribute them as separate works.  But when you\n",
+"distribute the same sections as part of a whole which is a work based\n",
+"on the Program, the distribution of the whole must be on the terms of\n",
+"this License, whose permissions for other licensees extend to the\n",
+"entire whole, and thus to each and every part regardless of who wrote it.\n",
+"\n",
+"Thus, it is not the intent of this section to claim rights or contest\n",
+"your rights to work written entirely by you; rather, the intent is to\n",
+"exercise the right to control the distribution of derivative or\n",
+"collective works based on the Program.\n",
+"\n",
+"In addition, mere aggregation of another work not based on the Program\n",
+"with the Program (or with a work based on the Program) on a volume of\n",
+"a storage or distribution medium does not bring the other work under\n",
+"the scope of this License.\n",
+"\n",
+"  3. You may copy and distribute the Program (or a work based on it,\n",
+"under Section 2) in object code or executable form under the terms of\n",
+"Sections 1 and 2 above provided that you also do one of the following:\n",
+"\n",
+"    a) Accompany it with the complete corresponding machine-readable\n",
+"    source code, which must be distributed under the terms of Sections\n",
+"    1 and 2 above on a medium customarily used for software interchange; or,\n",
+"\n",
+"    b) Accompany it with a written offer, valid for at least three\n",
+"    years, to give any third party, for a charge no more than your\n",
+"    cost of physically performing source distribution, a complete\n",
+"    machine-readable copy of the corresponding source code, to be\n",
+"    distributed under the terms of Sections 1 and 2 above on a medium\n",
+"    customarily used for software interchange; or,\n",
+"\n",
+"    c) Accompany it with the information you received as to the offer\n",
+"    to distribute corresponding source code.  (This alternative is\n",
+"    allowed only for noncommercial distribution and only if you\n",
+"    received the program in object code or executable form with such\n",
+"    an offer, in accord with Subsection b above.)\n",
+"\n",
+"The source code for a work means the preferred form of the work for\n",
+"making modifications to it.  For an executable work, complete source\n",
+"code means all the source code for all modules it contains, plus any\n",
+"associated interface definition files, plus the scripts used to\n",
+"control compilation and installation of the executable.  However, as a\n",
+"special exception, the source code distributed need not include\n",
+"anything that is normally distributed (in either source or binary\n",
+"form) with the major components (compiler, kernel, and so on) of the\n",
+"operating system on which the executable runs, unless that component\n",
+"itself accompanies the executable.\n",
+"\n",
+"If distribution of executable or object code is made by offering\n",
+"access to copy from a designated place, then offering equivalent\n",
+"access to copy the source code from the same place counts as\n",
+"distribution of the source code, even though third parties are not\n",
+"compelled to copy the source along with the object code.\n",
+"
+\n",
+"  4. You may not copy, modify, sublicense, or distribute the Program\n",
+"except as expressly provided under this License.  Any attempt\n",
+"otherwise to copy, modify, sublicense or distribute the Program is\n",
+"void, and will automatically terminate your rights under this License.\n",
+"However, parties who have received copies, or rights, from you under\n",
+"this License will not have their licenses terminated so long as such\n",
+"parties remain in full compliance.\n",
+"\n",
+"  5. You are not required to accept this License, since you have not\n",
+"signed it.  However, nothing else grants you permission to modify or\n",
+"distribute the Program or its derivative works.  These actions are\n",
+"prohibited by law if you do not accept this License.  Therefore, by\n",
+"modifying or distributing the Program (or any work based on the\n",
+"Program), you indicate your acceptance of this License to do so, and\n",
+"all its terms and conditions for copying, distributing or modifying\n",
+"the Program or works based on it.\n",
+"\n",
+"  6. Each time you redistribute the Program (or any work based on the\n",
+"Program), the recipient automatically receives a license from the\n",
+"original licensor to copy, distribute or modify the Program subject to\n",
+"these terms and conditions.  You may not impose any further\n",
+"restrictions on the recipients' exercise of the rights granted herein.\n",
+"You are not responsible for enforcing compliance by third parties to\n",
+"this License.\n",
+"\n",
+"  7. If, as a consequence of a court judgment or allegation of patent\n",
+"infringement or for any other reason (not limited to patent issues),\n",
+"conditions are imposed on you (whether by court order, agreement or\n",
+"otherwise) that contradict the conditions of this License, they do not\n",
+"excuse you from the conditions of this License.  If you cannot\n",
+"distribute so as to satisfy simultaneously your obligations under this\n",
+"License and any other pertinent obligations, then as a consequence you\n",
+"may not distribute the Program at all.  For example, if a patent\n",
+"license would not permit royalty-free redistribution of the Program by\n",
+"all those who receive copies directly or indirectly through you, then\n",
+"the only way you could satisfy both it and this License would be to\n",
+"refrain entirely from distribution of the Program.\n",
+"\n",
+"If any portion of this section is held invalid or unenforceable under\n",
+"any particular circumstance, the balance of the section is intended to\n",
+"apply and the section as a whole is intended to apply in other\n",
+"circumstances.\n",
+"\n",
+"It is not the purpose of this section to induce you to infringe any\n",
+"patents or other property right claims or to contest validity of any\n",
+"such claims; this section has the sole purpose of protecting the\n",
+"integrity of the free software distribution system, which is\n",
+"implemented by public license practices.  Many people have made\n",
+"generous contributions to the wide range of software distributed\n",
+"through that system in reliance on consistent application of that\n",
+"system; it is up to the author/donor to decide if he or she is willing\n",
+"to distribute software through any other system and a licensee cannot\n",
+"impose that choice.\n",
+"\n",
+"This section is intended to make thoroughly clear what is believed to\n",
+"be a consequence of the rest of this License.\n",
+"
+\n",
+"  8. If the distribution and/or use of the Program is restricted in\n",
+"certain countries either by patents or by copyrighted interfaces, the\n",
+"original copyright holder who places the Program under this License\n",
+"may add an explicit geographical distribution limitation excluding\n",
+"those countries, so that distribution is permitted only in or among\n",
+"countries not thus excluded.  In such case, this License incorporates\n",
+"the limitation as if written in the body of this License.\n",
+"\n",
+"  9. The Free Software Foundation may publish revised and/or new versions\n",
+"of the General Public License from time to time.  Such new versions will\n",
+"be similar in spirit to the present version, but may differ in detail to\n",
+"address new problems or concerns.\n",
+"\n",
+"Each version is given a distinguishing version number.  If the Program\n",
+"specifies a version number of this License which applies to it and \"any\n",
+"later version\", you have the option of following the terms and conditions\n",
+"either of that version or of any later version published by the Free\n",
+"Software Foundation.  If the Program does not specify a version number of\n",
+"this License, you may choose any version ever published by the Free Software\n",
+"Foundation.\n",
+"\n",
+"  10. If you wish to incorporate parts of the Program into other free\n",
+"programs whose distribution conditions are different, write to the author\n",
+"to ask for permission.  For software which is copyrighted by the Free\n",
+"Software Foundation, write to the Free Software Foundation; we sometimes\n",
+"make exceptions for this.  Our decision will be guided by the two goals\n",
+"of preserving the free status of all derivatives of our free software and\n",
+"of promoting the sharing and reuse of software generally.\n",
+"\n",
+"			    NO WARRANTY\n",
+"\n",
+"  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n",
+"FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n",
+"OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n",
+"PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n",
+"OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n",
+"MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n",
+"TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n",
+"PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n",
+"REPAIR OR CORRECTION.\n",
+"\n",
+"  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n",
+"WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n",
+"REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n",
+"INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n",
+"OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n",
+"TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n",
+"YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n",
+"PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n",
+"POSSIBILITY OF SUCH DAMAGES.\n",
+"\n",
+"		     END OF TERMS AND CONDITIONS\n"};
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.src
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.src	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/conditions.src	(revision 16561)
@@ -0,0 +1,225 @@
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/environment.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/environment.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/environment.c	(revision 16561)
@@ -0,0 +1,691 @@
+/**************************************************************************
+ *
+ * environment.c -- mgquery environment functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.2  2004/04/25 23:01:18  kjdon
+   added a new -M option to mg_passes, allowing maxnumeric to be altered - made this change to keep gsdl3 mg inline with gsdl2 mg.
+
+   Revision 1.1  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:17:50  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:37  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.5  1995/03/14  05:15:26  tes
+   * Updated the boolean query optimiser to do different types of optimisation.
+   * A query environment variable "optimise_type" specifies which one is to be
+   * used. Type 1 is the new one which is faster than 2.
+   *
+   * Revision 1.4  1994/11/25  03:47:43  tes
+   * Committing files before adding the merge stuff.
+   *
+   * Revision 1.3  1994/10/20  03:56:43  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:25  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "locallib.h"
+#include "local_strings.h"
+#include "messages.h"
+
+#include "environment.h"
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+# define STRCASECMP      stricmp
+#else
+# define STRCASECMP      strcasecmp
+#endif
+
+typedef struct
+  {
+    char *Name, *Data;
+    char *(*Constraint) (char *Old, char *New);
+  }
+EEntry;
+
+typedef struct Env
+  {
+    EEntry *Environment;
+    int NumEnv;
+    struct Env *Next;
+  }
+Env;
+
+
+char *ConstraintErrorStr;
+
+static Env Base =
+{NULL, 0, NULL};
+
+
+/*
+ * Add name and data to the environment (data may be NULL)
+ * All names are converted to lower case
+ * returns 0 on success, -1 on memory failure or -2 on constraint failure
+ */
+int 
+SetEnv (char *name, char *data, char *(*Constraint) (char *, char *))
+{
+  int i;
+  EEntry *newenv, *env = Base.Environment;
+
+  if (data && *data == '\0')
+    data = NULL;
+
+  /* search for name in the environment */
+  for (i = 0; i < Base.NumEnv && STRCASECMP (name, env->Name); i++, env++);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* if not found the increase the environment size */
+  if (i >= Base.NumEnv)
+    {
+      if (Constraint)
+	{
+	  data = Constraint (NULL, data);
+	  if (!data)
+	    return (-2);
+	}
+      /* allocate memory for the new environment */
+      newenv = Base.Environment ?
+	Xrealloc (Base.Environment, sizeof (EEntry) * (Base.NumEnv + 1)) :
+	Xmalloc (sizeof (EEntry) * (Base.NumEnv + 1));
+      if (!newenv)
+	return (-1);
+      Base.Environment = newenv;
+      env = &newenv[Base.NumEnv];
+      env->Data = NULL;
+      env->Name = NULL;
+      env->Constraint = Constraint;
+      env->Name = Xstrdup (name);
+      if (!env->Name)
+	return (-1);
+      Base.NumEnv++;
+      for (i = 0; env->Name[i]; i++)
+	env->Name[i] = tolower (env->Name[i]);
+    }
+  if (env->Constraint)
+    {
+      data = env->Constraint (env->Data, data);
+      if (!data)
+	return (-2);
+    }
+  if (env->Data)
+    Xfree (env->Data);
+  env->Data = NULL;
+  if (data && *data != '\0')
+    {
+      env->Data = Xstrdup (data);
+      if (!env->Data)
+	return (-1);
+    }
+  return (0);
+}
+
+
+
+/*
+ * Returns the data associated with name. If Data = NULL then this routine
+ * returns a pointer to "". If the specified name does not exist then this 
+ * routine returns NULL.
+ */
+char *
+GetEnv (char *name)
+{
+  int i;
+  EEntry *env = Base.Environment;
+  /* search for name in the environment */
+  for (i = 0; i < Base.NumEnv && STRCASECMP (name, env->Name); i++, env++);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  if (i >= Base.NumEnv)
+    return (NULL);
+
+  return (env->Data ? env->Data : "");
+}
+
+/*
+ * Returns the data associated with name. If Data = NULL then this routine
+ * returns a pointer to "". If the specified name does not exist then this 
+ * routine returns default.
+ */
+char *
+GetDefEnv (char *name, char *def)
+{
+  char *data = GetEnv (name);
+  return (data ? data : def);
+}
+
+
+
+/*
+ * This function pushes the environment on to a stack and duplicated it in 
+ * the current stack.
+ *
+ */
+int 
+PushEnv (void)
+{
+  Env *env = Xmalloc (sizeof (Env));
+  int i;
+  *env = Base;
+  Base.Next = env;
+  Base.Environment = NULL;
+  Base.NumEnv = 0;
+  for (i = 0; i < env->NumEnv; i++)
+    if (SetEnv (env->Environment[i].Name, env->Environment[i].Data,
+		env->Environment[i].Constraint))
+      return (-1);
+  return (0);
+}
+
+int 
+PopEnv (void)
+{
+  Env *env;
+  int i;
+  if (!Base.Next)
+    return (-1);
+  for (i = 0; i < Base.NumEnv; i++)
+    {
+      Xfree (Base.Environment[i].Name);
+      if (Base.Environment[i].Data)
+	Xfree (Base.Environment[i].Data);
+    }
+  if (Base.Environment)
+    Xfree (Base.Environment);
+  env = Base.Next;
+  Base = *env;
+  Xfree (env);
+  return (0);
+}
+
+
+/*
+ * Count the number of environments on the stack, including the current one.
+ *
+ */
+int 
+EnvStackHeight (void)
+{
+  int i = 1;
+  Env *env = &Base;
+  while (env->Next)
+    {
+      i++;
+      env = env->Next;
+    }
+  return (i);
+}
+
+/*
+ * Delete environment variable name. If name does not exist it returns -1
+ * otherwise it returns 0
+ */
+int 
+UnsetEnv (char *name, int Force)
+{
+  int i;
+  EEntry *env = Base.Environment;
+  /* search for name in the environment */
+  for (i = 0; i < Base.NumEnv && STRCASECMP (name, env->Name); i++, env++);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  if (i >= Base.NumEnv ||
+      (Base.Environment[i].Constraint && !Force))
+    return (-1);
+  Xfree (Base.Environment[i].Name);
+  if (Base.Environment[i].Data)
+    Xfree (Base.Environment[i].Data);
+  for (i++; i < Base.NumEnv; i++)
+    Base.Environment[i - 1] = Base.Environment[i];
+  Base.NumEnv--;
+  if (Base.NumEnv == 0)
+    {
+      Xfree (Base.Environment);
+      Base.Environment = NULL;
+    }
+  return (0);
+}
+
+
+
+
+
+
+
+
+/*
+ * Returns the name of environment variable number i or NULL if i is greater 
+ * than or equal to the number of environment variables. Names start from
+ * zero. 
+ */
+char *
+GetEnvName (int i)
+{
+  return (i < Base.NumEnv ? Base.Environment[i].Name : NULL);
+}
+
+static char *BooleanStrs[] =
+{"false", "true", "no", "yes", "off", "on"};
+
+static char *
+BooleanCons (char *Old, char *New)
+{
+  int i;
+  int old = -1, new = -1;
+  if (Old)
+    for (i = 0; i < sizeof (BooleanStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (Old, BooleanStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  old = i;
+	  break;
+	}
+  if (New)
+    for (i = 0; i < sizeof (BooleanStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (New, BooleanStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  new = i;
+	  break;
+	}
+  if (new >= 0)
+    return (New);
+  if (!New)
+    return (BooleanStrs[Old ? old ^ 1 : 0]);
+  ConstraintErrorStr = "Invalid argument [true|false|yes|no|on|off] required";
+  return (NULL);
+}
+
+/*
+ * returns 0 or 1  for a boolean string
+ * or default on a error
+ */
+int 
+BooleanEnv (char *data, int def)
+{
+  int i;
+  if (!data)
+    return (def);
+  for (i = 0; i < sizeof (BooleanStrs) / sizeof (char *); i++)
+    if (!STRCASECMP (data, BooleanStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+      return (i & 1);
+  return (def);
+}
+
+/*
+ * returns the value for a integer string
+ * or default on a error
+ */
+long 
+IntEnv (char *data, long def)
+{
+  long val;
+  char *ptr;
+  if (!data)
+    return (def);
+  val = strtol (data, &ptr, 10);
+  return *ptr ? def : val;
+}
+
+static char *
+NumberCmp (char *num, int min, int max)
+{
+  char *err;
+  int val;
+  static char Err[100];
+  ConstraintErrorStr = "Not a valid number";
+  if (!num)
+    return (NULL);
+  val = strtol (num, &err, 10);
+  if (*err)
+    return (NULL);
+  sprintf (Err, "Not in legal range [%d <= num <= %d]", min, max);
+  ConstraintErrorStr = Err;
+  if (val < min || val > max)
+    return (NULL);
+  return (num);
+}
+
+/*ARGSUSED */
+static char *
+MaxDocsCons (char *Old, char *New)
+{
+  if (!STRCASECMP (New, "all"))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (New);
+  return (NumberCmp (New, 1, ((unsigned) (~0)) >> 1));
+}
+
+/*ARGSUSED */
+static char *
+MaxTermsCons (char *Old, char *New)
+{
+  if (!STRCASECMP (New, "all"))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (New);
+  return (NumberCmp (New, 1, ((unsigned) (~0)) >> 1));
+}
+
+/* ARGSUSED */
+static char *
+BufferCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 0, 16 * 1024 * 1024));
+}
+
+/* ARGSUSED */
+static char *
+MaxNodesCons (char *Old, char *New)
+{
+  if (!STRCASECMP (New, "all"))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (New);
+  return (NumberCmp (New, 8, 256 * 1024 * 1024));
+}
+
+/* ARGSUSED */
+static char *
+MaxHashCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 8, 256 * 1024 * 1024));
+}
+
+/* ARGSUSED */
+static char *
+MaxParasCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 1, 256 * 1024 * 1024));
+}
+
+/* ARGSUSED */
+static char *
+MaxHeadsCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 1, 1000));
+}
+
+/* ARGSUSED */
+char *
+OptimiseCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 0, 2));
+}
+
+/* ARGSUSED */
+char *
+MaxNumericCons (char *Old, char *New)
+{
+  return (NumberCmp (New, 4, 512));
+}
+
+
+/*
+ * Makes sure that New is a valid query type
+ *
+ */
+/* ARGSUSED */
+static char *
+QueryCons (char *Old, char *New)
+{
+  static char *QueryStrs[] =
+  {
+    "boolean", "ranked",
+    "docnums", "approx-ranked"};
+  int i;
+  int new = -1;
+  if (New)
+    for (i = 0; i < sizeof (QueryStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (New, QueryStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  new = i;
+	  break;
+	}
+  if (new >= 0)
+    return (New);
+  ConstraintErrorStr = "Invalid argument [boolean|ranked|docnums|approx-ranked] required";
+  return (NULL);
+}
+
+/*
+ * Makes sure that New is a valid accum type
+ *
+ */
+/* ARGSUSED */
+static char *
+AccumCons (char *Old, char *New)
+{
+  static char *AccumStrs[] =
+  {
+    "array", "splay_tree", "hash_table", "list"};
+  int i;
+  int new = -1;
+  if (New)
+    for (i = 0; i < sizeof (AccumStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (New, AccumStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  new = i;
+	  break;
+	}
+  if (new >= 0)
+    return (New);
+  ConstraintErrorStr = "Invalid argument "
+    "[array|splay_tree|hash_table|list] required";
+  return (NULL);
+}
+
+
+
+/*
+ * Makes sure that New is a valid output type type
+ *
+ */
+/* ARGSUSED */
+static char *
+OutputTypeCons (char *Old, char *New)
+{
+  static char *OutputTypeStrs[] =
+  {"text", "silent", "docnums",
+   "count", "heads", "hilite"
+#ifdef TREC_MODE
+   ,"extras_for_trec"
+#endif
+  };
+
+  int i;
+  int new = -1;
+  if (New)
+    for (i = 0; i < sizeof (OutputTypeStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (New, OutputTypeStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  new = i;
+	  break;
+	}
+  if (new >= 0)
+    return (New);
+  ConstraintErrorStr = "Invalid argument [text|silent|docnums|heads|count|hilite"
+#ifdef TREC_MODE
+    "|extras_for_trec"
+#endif
+    "] required";
+  return (NULL);
+}
+
+/*
+ * Makes sure that New is a valid query type
+ *
+ */
+/* ARGSUSED */
+char *
+HiliteStyleCons (char *Old, char *New)
+{
+  static char *StyleStrs[] =
+  {"bold", "underline"};
+  int i;
+  int new = -1;
+  if (New)
+    for (i = 0; i < sizeof (StyleStrs) / sizeof (char *); i++)
+      if (!STRCASECMP (New, StyleStrs[i]))  /* [RPAP - Feb 97: WIN32 Port] */
+	{
+	  new = i;
+	  break;
+	}
+  if (new >= 0)
+    return (New);
+  ConstraintErrorStr = "Invalid argument [bold|underline] required";
+  return (NULL);
+}
+
+/* =========================================================================
+ * Function: get_output_type
+ * Description:
+ *      Map a mode string onto a mode char e.g. OUTPUT_????
+ * Input:
+ * Output:
+ *      The mode char which is a subrange value.
+ * ========================================================================= */
+
+char
+get_output_type (void)
+{
+  char *env_str = GetDefEnv ("mode", "text");
+  char ch1 = toupper (env_str[0]);
+  char ch2 = toupper (env_str[1]);
+
+  switch (ch1)
+    {
+    case 'T':
+      return OUTPUT_TEXT;
+    case 'S':
+      return OUTPUT_SILENT;
+    case 'E':
+      return OUTPUT_EXTRAS;
+    case 'C':
+      return OUTPUT_COUNT;
+    case 'D':
+      return OUTPUT_DOCNUMS;
+    case 'H':
+      if (ch2 == 'E')
+	return OUTPUT_HEADERS;
+      else
+	return OUTPUT_HILITE;
+    default:
+      FatalError (1, "Problem in output type switch");
+    }
+
+  return '\0';			/*shouldn't reach here -- keep compiler happy */
+}
+
+/* =========================================================================
+ * Function: get_query_type
+ * Description:
+ *      Map a query type string onto a char.
+ * Input:
+ * Output:
+ *      Query type char.
+ * ========================================================================= */
+
+char
+get_query_type (void)
+{
+  char *env_str = GetDefEnv ("query", "boolean");
+  char ch1 = toupper (env_str[0]);
+
+  switch (ch1)
+    {
+    case 'R':
+      return QUERY_RANKED;
+    case 'A':
+      return QUERY_APPROX;
+    case 'B':
+      return QUERY_BOOLEAN;
+    case 'D':
+      return QUERY_DOCNUMS;
+    default:
+      FatalError (1, "Problem in query type switch");
+    }
+
+  return '\0';			/*shouldn't reach here */
+}
+
+
+
+/*
+ * This initialises certain environment variables
+ *
+ */
+void 
+InitEnv (void)
+{
+  SetEnv ("hilite_style", "underline", HiliteStyleCons);	/*[TS:Sep/94] */
+  SetEnv ("briefstats", "off", BooleanCons);
+  SetEnv ("diskstats", "off", BooleanCons);
+  SetEnv ("expert", "false", BooleanCons);
+  SetEnv ("mgdir", getenv ("MGDATA") ? getenv ("MGDATA") : ".", NULL);
+  SetEnv ("mgname", "", NULL);
+  SetEnv ("maxdocs", "all", MaxDocsCons);
+  SetEnv ("memstats", "off", BooleanCons);
+  SetEnv ("mode", "text", OutputTypeCons);
+  SetEnv ("pager", getenv ("PAGER") ? getenv ("PAGER") : "more", NULL);
+  SetEnv ("qfreq", "true", BooleanCons);
+  SetEnv ("query", "boolean", QueryCons);
+  SetEnv ("sizestats", "off", BooleanCons);
+  SetEnv ("timestats", "off", BooleanCons);
+  SetEnv ("verbatim", "off", BooleanCons);
+  SetEnv ("sorted_terms", "on", BooleanCons);
+  SetEnv ("accumulator_method", "array", AccumCons);
+  SetEnv ("stop_at_max_accum", "off", BooleanCons);
+  SetEnv ("buffer", "1048576", BufferCons);
+  SetEnv ("max_accumulators", "50000", MaxNodesCons);
+  SetEnv ("max_terms", "all", MaxTermsCons);
+  SetEnv ("maxparas", "1000", MaxParasCons);
+  SetEnv ("hash_tbl_size", "1000", MaxHashCons);
+  SetEnv ("skip_dump", "skips.%d", NULL);
+  SetEnv ("ranked_doc_sepstr", "---------------------------------- %n %w\\n", NULL);
+  SetEnv ("doc_sepstr", "---------------------------------- %n\\n", NULL);
+  SetEnv ("para_sepstr", "\\n######## PARAGRAPH %n ########\\n", NULL);
+  SetEnv ("para_start", "***** Weight = %w *****\\n", NULL);
+  SetEnv ("terminator", "", NULL);
+  SetEnv ("heads_length", "50", MaxHeadsCons);
+  SetEnv ("optimise_type", "1", OptimiseCons);	/*[TS:Mar/95] */
+  SetEnv ("casefold", "on", BooleanCons);  /* [RPAP - Jan 97: Stem Index Change] */
+  SetEnv ("stem", "on", BooleanCons);  /* [RPAP - Jan 97: Stem Index Change] */
+  SetEnv ("term_freq", "off", BooleanCons);  /* [RPAP - Feb 97: Term Frequency] */
+  SetEnv ("maxnumeric", "4", MaxNumericCons);  /* [sjboddie - Jun 2002: Max Numeric word length] */
+}
+
+
+void 
+UninitEnv (void)
+{
+  char *name;
+  while (PopEnv () == 0);
+  while ((name = GetEnvName (0)) != 0)
+    UnsetEnv (name, 1);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/environment.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/environment.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/environment.h	(revision 16561)
@@ -0,0 +1,61 @@
+/**************************************************************************
+ *
+ * environment.h -- mgquery environment functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 ENVIRONMENT_H
+#define ENVIRONMENT_H
+
+#define OUTPUT_TEXT     'T'
+#define OUTPUT_HEADERS  'H'
+#define OUTPUT_SILENT   'S'
+#define OUTPUT_EXTRAS   'E'
+#define OUTPUT_HILITE   'I'
+#define OUTPUT_COUNT    'C'
+#define OUTPUT_DOCNUMS  'D'
+
+#define QUERY_RANKED    'R'
+#define QUERY_APPROX    'A'
+#define QUERY_BOOLEAN   'B'
+#define QUERY_DOCNUMS   'D'
+
+
+char get_output_type (void);
+char get_query_type (void);
+
+extern char *ConstraintErrorStr;
+int SetEnv (char *name, char *data, char *(*Constraint) (char *, char *));
+char *GetEnv (char *name);
+char *GetDefEnv (char *name, char *def);
+int UnsetEnv (char *name, int Force);
+char *de_escape_string (char *s);
+int PushEnv (void);
+int PopEnv (void);
+int EnvStackHeight (void);
+char *GetEnvName (int i);
+int BooleanEnv (char *data, int def);
+long IntEnv (char *data, long def);
+
+void InitEnv (void);
+void UninitEnv (void);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/globals.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/globals.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/globals.h	(revision 16561)
@@ -0,0 +1,32 @@
+/**************************************************************************
+ *
+ * globals.h -- Some global stuff for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_GLOBALS
+#define H_GLOBALS
+
+
+extern FILE *OutFile;
+extern FILE *InFile;
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/hash.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/hash.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/hash.h	(revision 16561)
@@ -0,0 +1,44 @@
+/**************************************************************************
+ *
+ * hash.h -- Hash function
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_HASH
+#define H_HASH
+
+
+
+#define HASH(h, step, wd, tablesize)			\
+	do {						\
+		register u_char *p=wd;			\
+		register long len = *p++;		\
+		register unsigned long hval;		\
+		if (len>25) len = 25;			\
+		for (hval=0; len; --len)		\
+		  hval = (hval << 5) - hval + *p++;	\
+		step = hval % (tablesize - 2) + 1;	\
+		h = hval % tablesize;			\
+	} while (0)
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.h	(revision 16561)
@@ -0,0 +1,320 @@
+static char *help_str[] = {
+"\n",
+"			  HELP for mgquery\n",
+"		   ================================\n",
+"\n",
+"The text is a summary of the information in the \"mgquery\" manual pages.\n",
+"\n",
+"The input to 'mgquery' consists of a series of input lines. The backslash\n",
+"character (\"\\\") is used at the end of lines to indicate that input \n",
+"continues on the next line.\n",
+"\n",
+"Input lines on which the first character is a dot (\".\") are commands to\n",
+"the mgquery program. Input lines that do not start with a dot are queries.\n",
+"\n",
+"A query consists of two parts. One part is a boolean or ranked query that\n",
+"identifies documents. The second part is a post-processing pattern matching\n",
+"operation. Any text between the first speech mark (\") and the last speech\n",
+"mark is considered to the a post-processing pattern. \n",
+"\n",
+"\n",
+"The following command are available :-\n",
+"\n",
+"    .help	     - displays this text.\n",
+"    .quit            - quits the program.\n",
+"    .set name value  - sets parameter \"name\" to \"value\" . If the parameter\n",
+"                       is a boolean parameter and value is omitted the\n",
+"	    	       parameter will be inverted (i.e. if it is true it\n",
+"		       will change to false, if it is false it will change\n",
+"		       to true).\n",
+"    .unset name      - deletes parameter \"name\"\n",
+"    .reset           - sets all the parameters to their initial state.\n",
+"    .display         - displays the values of all the current parameters.\n",
+"    .push            - pushes the current parameters on to a stack.\n",
+"    .pop             - destroys the current parameters and pops a new set\n",
+"                       of parameters off the stack.\n",
+"    .output arg      - This is used to specify where to send the documents.\n",
+"                       Arg may one be of the following:\n",
+"		       > filename  : Send output to the specified file.\n",
+"		       >> filename : Append output to the specified file.\n",
+"                       | command   : The output is piped into command, \n",
+"                                     which is executed by sh.\n",
+"    .input arg       - This is used to specify where input comes from.\n",
+"                       Arg may one be of the following:\n",
+"		       < filename  : Get the input from the specified\n",
+"                                     file.\n",
+"                       | command   : The input comes from the standard\n",
+"                                     output of command, which is executed\n",
+"                                     by sh.\n",
+"\n",
+"On startup the mgquery program reads from the file .mgrc a sequence \n",
+"of commands (NOTE: The .mgrc file may not contain any queries). mgquery \n",
+"first looks for .mgrc in the current directory and then in the users home \n",
+"directory. Lines starting with a '#' in the .mgrc file are considered to\n",
+"be comments and are ignored.\n",
+"\n",
+"The following parameters (used in the .set and .unset commands) are \n",
+"predefined and have special significance :-\n",
+"\n",
+"accumulator_method = `array'\n",
+"	This parameter is used during ranking, and specifies how the\n",
+"	weight for each document should be accumulated. The following\n",
+"	methods are available `array', `splay_tree', `hash_table', and\n",
+"	`list'.\n",
+"\n",
+"briefstats = `off'\n",
+"	This is a boolean parameter that determines whether the the\n",
+"	totals for disk, memory and time usage statistics will be \n",
+"	displayed. at the end of each query. \n",
+"	NOTE: this takes precedence over the parameters \"diskstats\",\n",
+"	\"memstats\" and \"timestats\". This parameter may take the values \n",
+"	`yes', `no', `true', `false', `on' or `off'.\n",
+"\n",
+"buffer = `1048576'\n",
+"	When the documents are being read in they are read into a \n",
+"	buffer of this size and then displayed from this buffer. If \n",
+"        the documents are larger than this buffer the buffer is\n",
+"	expanded automatically. Having a large buffer gives a very\n",
+"	slight performance improvement because it allows the order of \n",
+"	disk operations to be optimised. The buffer size is measured\n",
+"	in bytes.\n",
+"\n",
+"diskstats = `off'\n",
+"	This is a boolean parameter that determines whether the disk\n",
+"	usage statistics for the preceding query will be displayed\n",
+"	after each query. This parameter may take the values `yes',\n",
+"	`no', `true', `false', `on' or `off'.\n",
+"\n",
+"doc_sepstr = `---------------------------------- %n\\n'\n",
+"	This specifies the string that will be used to separate \n",
+"	documents when they are displayed for `boolean' or `docnums'\n",
+"	queries. The standard C escape character sequences (see the \n",
+"	man page) may be used to place special characters in the \n",
+"	string. For example, a newline would the `\\n'. To include a `%'\n",
+"	use the sequence `%%'. To include the MG document number use\n",
+"	the sequence `%n'.\n",
+"\n",
+"expert = `false'\n",
+"	If this is true then a lot of the waffle that the program \n",
+"	spits out is suppressed. This parameter may take the values\n",
+"	`yes', `no', `true', `false', `on' or `off'.\n",
+"\n",
+"hash_tbl_size = `1000'\n",
+"	One of the options during ranking queries is to use a hash \n",
+"	table to accumulate the weights for each document. The hash \n",
+"	table is a simple chained type. This parameter specifies the \n",
+"	size of the hash table and may take any value between 8 and\n",
+"	268435456. \n",
+"\n",
+"heads_length = `50'\n",
+"	When the mode is `heads' this specifies the number of \n",
+"	characters that	will be output for each document.\n",
+"\n",
+"maxdocs = `all'\n",
+"	The maximum number of documents to display in response to a\n",
+"	query. This parameter may take on a numeric value between 1 \n",
+"	and 429467295 or the word `all'.\n",
+"\n",
+"maxparas = `1000'\n",
+"	The maximum number of paragraphs to identify during a ranked\n",
+"	query with paragraph indexing. After the paragraphs have been\n",
+"	identified the paragraphs are converted into documents, and \n",
+"	because some of the paragraphs may refer to the same documents \n",
+"	the final number of answers may be less that maxparas. The \n",
+"	maxdocs parameter will then be applied. This parameter may \n",
+"	take on a numeric value between 1 and 429467295.\n",
+"\n",
+"max_accumulators = `50000'\n",
+"	This parameter limits the number of different paragraph/\n",
+"	document numbers to be accumulated during ranked queries when \n",
+"	the parameter `accumulator_method` is set to `splay_tree',\n",
+"	`hash_table', or `list'.  This parameter may take any value \n",
+"	between 8 and 268435456.\n",
+"\n",
+"max_terms = `all'\n",
+"	This parameter limits the number of terms that will actually\n",
+"	be used during a ranked query. If more terms than the number\n",
+"	specified by max_terms are entered, then the extra terms will\n",
+"	be discarded. If `sorted_terms' is on then the limiting will \n",
+"	be done after the terms have been sorted. This parameter may\n",
+"	take any value between 1 and 429467295 or the word `all'.\n",
+"\n",
+"memstats = `off'\n",
+"	This is a boolean parameter that determines whether the memory \n",
+"	usage statistics for the preceding query will be displayed\n",
+"	after each query. This parameter may take the values `yes', \n",
+"	`no', `true', `false', `on' or `off'.\n",
+"\n",
+"mgdir = `.'\n",
+"	This specifies the directory where the MG files may be found.\n",
+"	If the environment variable `MGDATA' is set then `mgdir' is\n",
+"	initialised to the value in `MGDATA'.\n",
+"\n",
+"mgname = `'\n",
+"	This specifies the name of the MG database to process.\n",
+"\n",
+"mode = `text'\n",
+"	This specifies how documents should be displayed when they\n",
+"	are retrieved it may take four different values `text', \n",
+"	`docnums', `silent', `heads' or `count'. `text' displays \n",
+"	the contents of the document. `docnums' displays only the\n",
+"	document numbers. `Silent' retrieves all the documents but\n",
+"	displays nothing except how many documents were retrieved.\n",
+"	This mode is intended to be used in timing experiments. \n",
+"	`Heads` is used to print out the head of each document.\n",
+"	`Count' does the minimum amount of work required to determine\n",
+"	how many documents would be retrieved, but does not retrieve\n",
+"	them.\n",
+"\n",
+"pager = `more'\n",
+"	This is the name of the program that will be used to display\n",
+"	the help and the retrieved documents. If the environment \n",
+"	variable \"PAGER\" is defined then `pager' takes on that value.\n",
+"\n",
+"para_sepstr = `\\n######## PARAGRAPH %n ########\\n'\n",
+"	This specifies the string that will be used to separate \n",
+"	paragraphs. The standard C escape character sequences (see the \n",
+"	man page) may be used to place special characters in the \n",
+"	string. For example, a newline would the `\\n'. To include a `%'\n",
+"	use the sequence `%%'. To include the paragraph number within\n",
+"	the document use the sequence `%n'.\n",
+"\n",
+"para_start = `***** Weight = %w *****\\n'\n",
+"	This specifies the string that will be used at the head of \n",
+"	paragraphs for a paraghaph level index following a ranked query.\n",
+"	The standard C escape character sequences (see the man page)\n",
+"	may be used to place special characters in the string. For \n",
+"	example, a newline would the `\\n'. To include a `%' use the\n",
+"	sequence `%%'. To include the paragraph weight use the \n",
+"	sequence `%w'.\n",
+"\n",
+"qfreq = `true'\n",
+"	This determine whether the ranked queries will take into \n",
+"	account the number of times each query term is specified.\n",
+"	When this is `true' the number of times a term appears in\n",
+"	the query is used in the ranking. When this is `false' all \n",
+"	query term are assumed to occur only once. This parameter\n",
+"	may take the values `yes', `no', `true', `false', `on' or\n",
+"	`off'.\n",
+"\n",
+"query = `boolean'\n",
+"	This specifies the type of queries that are to be specified.\n",
+"	It can take four different values `boolean', `ranked', \n",
+"	`docnums' or `approx-ranked'. \n",
+"\n",
+"	`boolean' is for boolean queries. \n",
+"		The yacc grammar for boolean queries is as follows :-\n",
+"\n",
+"		query   : or;\n",
+" \n",
+"		or      : or '|' and\n",
+"		        | and ;	\n",
+" \n",
+"		and     : and '&' not\n",
+"        		| and not\n",
+"        		| not ;\n",
+"\n",
+"		not     : term\n",
+"		        | '!' not ;\n",
+" \n",
+"		term    : TERM\n",
+"		        | '(' or ')' ;\n",
+" \n",
+"\n",
+"\n",
+"\n",
+"	`ranked' and `approx-ranked' are for queries ranked by the\n",
+"		cosine measure. `approx-ranked' uses only the low\n",
+"		precision document lengths, and therefore only\n",
+"		produces an approximation to full cosine ranking.\n",
+"\n",
+"		query   : TERM\n",
+"      			| query TERM ;\n",
+"	\n",
+"	`docnums' allows the entry of document numbers. Multiple \n",
+"		numbers separated by spaces may be specified\n",
+"		or ranges separated by hyphens.\n",
+"\n",
+"\n",
+"		query   : range\n",
+"			| query range ;\n",
+"\n",
+"\n",
+"		range   : num\n",
+"			| num '-' num ;\n",
+"\n",
+"\n",
+"ranked_doc_sepstr = `---------------------------------- %n %w\\n'\n",
+"	This specifies the string that will be used to separate \n",
+"	documents when they are displayed for `ranked' or \n",
+"	`approx-ranked'	queries. The standard C escape character \n",
+"	sequences (see the man page) may be used to place special\n",
+"	characters in the string. For example, a newline would the\n",
+"	`\\n'. To include a `%' use the sequence `%%'. To include the\n",
+"	MG document number use the sequence `%n'. To include the\n",
+"	document weight use the sequence `%w'.\n",
+"\n",
+"sizestats = `false'\n",
+"	If this is true then various numbers are output at the end\n",
+"	of each query indicating what went on during the query. \n",
+"	This parameter may take the values `yes', `no', `true', \n",
+"	`false', `on' or `off'.\n",
+"\n",
+"skip_dump = `skips.%d'\n",
+"	If this parameter is set then during ranked queries on skipped\n",
+"	inverted files when `accumulator_method' is set to `splay_tree',\n",
+"	`hash_table', or `list` a file will be produced in the current\n",
+"	directory. The name of the file is the value of this parameter,\n",
+"	a `%d' in the file name will be replaced with the process id of\n",
+"	mgquery. This file will contain information about the usage of\n",
+"	skips during the query processing. This option is expensive; \n",
+"	use `.unset skip_dump' to obtain optimal performance.\n",
+"\n",
+"sorted_terms = `on'\n",
+"	This specifies whether of not the terms should be sorted into\n",
+"	decreasing occurrence in documents so that the least often\n",
+"	occurring terms are processed first when ranked queries are\n",
+"	being done. When this is true the terms are sorted. When this\n",
+"	is false the terms are not sorted and are instead processed in\n",
+"	order of occurrence. This parameter may take the values `yes',\n",
+"	`no', `true', `false', `on' or `off'.\n",
+"\n",
+"\n",
+"stop_at_max_accum = `on'\n",
+"	This specifies what should happen when the maximum number of\n",
+"	accumulators set by `max_accumulators' is reached. When this\n",
+"	is true the the processing of terms is stopped at the completion\n",
+"	of the current term. When this is false processing continues but\n",
+"	no new accumulators are created. This parameter may take the \n",
+"	values `yes', `no', `true', `false', `on' or `off'.\n",
+"\n",
+"terminator = `'\n",
+"	This specifies the string that will be output after the	last\n",
+"	document from the previous query has been output. The standard\n",
+"	C escape character sequences (see the man page) may be used to\n",
+"	place special characters in the string. For example, a newline\n",
+"	would the `\\n'. To include a `%' use the sequence `%%'.\n",
+"	\n",
+"\n",
+"timestats = `false'\n",
+"	If this is true then the time to process a query is displayed\n",
+"	in both real time and CPU time. This parameter may take the\n",
+"	values `yes', `no', `true', `false', `on' or `off'.\n",
+"\n",
+"verbatim = `off'\n",
+"	This is a boolean parameter that determines whether the program\n",
+"	should attempt to do a regular expression match on the retrieved\n",
+"	text. If verbatim is `on' and a post-processing strng is specified\n",
+"	with the query then the post-processing string will be searched for\n",
+"	in the documents just before they are displayed. If the string is\n",
+"	found the document will be displayed, if not the document will not\n",
+"	be displayed. If verbatim is `off' the post-processing string will\n",
+"	be considered a regular expression like in `vi' or `egrep'.\n",
+"	E.G. If verbatim is `on', \"and.*the\" will look for the 8 character\n",
+"	     sequence \"and.*the\". If verbatim is `off', \"and.*the\" will\n",
+"	     look for the sequence \"and\" followed somewhere later in the\n",
+"	     document by the sequence \"the\".\n",
+"	This parameter may take the values `yes', `no', `true', `false',\n",
+"	`on' or `off'.\n",
+"\n",
+"\n"};
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.src
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.src	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/help.mg.src	(revision 16561)
@@ -0,0 +1,347 @@
+###########################################################################
+#
+# help.mg.src -- Source for the help command
+# Copyright (C) 1994  Neil Sharman
+#
+# 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.
+#
+#       @(#)help.mg.src	1.8 21 Mar 1994
+#
+###########################################################################
+#
+# The help file for mgquery.  
+# Lines starting with '#' are treated as comments and are discarded when
+# the 'help.mg.h' file is produced
+#
+###########################################################################
+
+			  HELP for mgquery
+		   ================================
+
+The text is a summary of the information in the "mgquery" manual pages.
+
+The input to 'mgquery' consists of a series of input lines. The backslash
+character ("\") is used at the end of lines to indicate that input 
+continues on the next line.
+
+Input lines on which the first character is a dot (".") are commands to
+the mgquery program. Input lines that do not start with a dot are queries.
+
+A query consists of two parts. One part is a boolean or ranked query that
+identifies documents. The second part is a post-processing pattern matching
+operation. Any text between the first speech mark (") and the last speech
+mark is considered to the a post-processing pattern. 
+
+
+The following command are available :-
+
+    .help	     - displays this text.
+    .quit            - quits the program.
+    .set name value  - sets parameter "name" to "value" . If the parameter
+                       is a boolean parameter and value is omitted the
+	    	       parameter will be inverted (i.e. if it is true it
+		       will change to false, if it is false it will change
+		       to true).
+    .unset name      - deletes parameter "name"
+    .reset           - sets all the parameters to their initial state.
+    .display         - displays the values of all the current parameters.
+    .push            - pushes the current parameters on to a stack.
+    .pop             - destroys the current parameters and pops a new set
+                       of parameters off the stack.
+    .output arg      - This is used to specify where to send the documents.
+                       Arg may one be of the following:
+		       > filename  : Send output to the specified file.
+		       >> filename : Append output to the specified file.
+                       | command   : The output is piped into command, 
+                                     which is executed by sh.
+    .input arg       - This is used to specify where input comes from.
+                       Arg may one be of the following:
+		       < filename  : Get the input from the specified
+                                     file.
+                       | command   : The input comes from the standard
+                                     output of command, which is executed
+                                     by sh.
+
+On startup the mgquery program reads from the file .mgrc a sequence 
+of commands (NOTE: The .mgrc file may not contain any queries). mgquery 
+first looks for .mgrc in the current directory and then in the users home 
+directory. Lines starting with a '#' in the .mgrc file are considered to
+be comments and are ignored.
+
+The following parameters (used in the .set and .unset commands) are 
+predefined and have special significance :-
+
+accumulator_method = `array'
+	This parameter is used during ranking, and specifies how the
+	weight for each document should be accumulated. The following
+	methods are available `array', `splay_tree', `hash_table', and
+	`list'.
+
+briefstats = `off'
+	This is a boolean parameter that determines whether the the
+	totals for disk, memory and time usage statistics will be 
+	displayed. at the end of each query. 
+	NOTE: this takes precedence over the parameters "diskstats",
+	"memstats" and "timestats". This parameter may take the values 
+	`yes', `no', `true', `false', `on' or `off'.
+
+buffer = `1048576'
+	When the documents are being read in they are read into a 
+	buffer of this size and then displayed from this buffer. If 
+        the documents are larger than this buffer the buffer is
+	expanded automatically. Having a large buffer gives a very
+	slight performance improvement because it allows the order of 
+	disk operations to be optimised. The buffer size is measured
+	in bytes.
+
+diskstats = `off'
+	This is a boolean parameter that determines whether the disk
+	usage statistics for the preceding query will be displayed
+	after each query. This parameter may take the values `yes',
+	`no', `true', `false', `on' or `off'.
+
+doc_sepstr = `---------------------------------- %n\n'
+	This specifies the string that will be used to separate 
+	documents when they are displayed for `boolean' or `docnums'
+	queries. The standard C escape character sequences (see the 
+	man page) may be used to place special characters in the 
+	string. For example, a newline would the `\n'. To include a `%'
+	use the sequence `%%'. To include the MG document number use
+	the sequence `%n'.
+
+expert = `false'
+	If this is true then a lot of the waffle that the program 
+	spits out is suppressed. This parameter may take the values
+	`yes', `no', `true', `false', `on' or `off'.
+
+hash_tbl_size = `1000'
+	One of the options during ranking queries is to use a hash 
+	table to accumulate the weights for each document. The hash 
+	table is a simple chained type. This parameter specifies the 
+	size of the hash table and may take any value between 8 and
+	268435456. 
+
+heads_length = `50'
+	When the mode is `heads' this specifies the number of 
+	characters that	will be output for each document.
+
+maxdocs = `all'
+	The maximum number of documents to display in response to a
+	query. This parameter may take on a numeric value between 1 
+	and 429467295 or the word `all'.
+
+maxparas = `1000'
+	The maximum number of paragraphs to identify during a ranked
+	query with paragraph indexing. After the paragraphs have been
+	identified the paragraphs are converted into documents, and 
+	because some of the paragraphs may refer to the same documents 
+	the final number of answers may be less that maxparas. The 
+	maxdocs parameter will then be applied. This parameter may 
+	take on a numeric value between 1 and 429467295.
+
+max_accumulators = `50000'
+	This parameter limits the number of different paragraph/
+	document numbers to be accumulated during ranked queries when 
+	the parameter `accumulator_method` is set to `splay_tree',
+	`hash_table', or `list'.  This parameter may take any value 
+	between 8 and 268435456.
+
+max_terms = `all'
+	This parameter limits the number of terms that will actually
+	be used during a ranked query. If more terms than the number
+	specified by max_terms are entered, then the extra terms will
+	be discarded. If `sorted_terms' is on then the limiting will 
+	be done after the terms have been sorted. This parameter may
+	take any value between 1 and 429467295 or the word `all'.
+
+memstats = `off'
+	This is a boolean parameter that determines whether the memory 
+	usage statistics for the preceding query will be displayed
+	after each query. This parameter may take the values `yes', 
+	`no', `true', `false', `on' or `off'.
+
+mgdir = `.'
+	This specifies the directory where the MG files may be found.
+	If the environment variable `MGDATA' is set then `mgdir' is
+	initialised to the value in `MGDATA'.
+
+mgname = `'
+	This specifies the name of the MG database to process.
+
+mode = `text'
+	This specifies how documents should be displayed when they
+	are retrieved it may take four different values `text', 
+	`docnums', `silent', `heads' or `count'. `text' displays 
+	the contents of the document. `docnums' displays only the
+	document numbers. `Silent' retrieves all the documents but
+	displays nothing except how many documents were retrieved.
+	This mode is intended to be used in timing experiments. 
+	`Heads` is used to print out the head of each document.
+	`Count' does the minimum amount of work required to determine
+	how many documents would be retrieved, but does not retrieve
+	them.
+
+pager = `more'
+	This is the name of the program that will be used to display
+	the help and the retrieved documents. If the environment 
+	variable "PAGER" is defined then `pager' takes on that value.
+
+para_sepstr = `\n######## PARAGRAPH %n ########\n'
+	This specifies the string that will be used to separate 
+	paragraphs. The standard C escape character sequences (see the 
+	man page) may be used to place special characters in the 
+	string. For example, a newline would the `\n'. To include a `%'
+	use the sequence `%%'. To include the paragraph number within
+	the document use the sequence `%n'.
+
+para_start = `***** Weight = %w *****\n'
+	This specifies the string that will be used at the head of 
+	paragraphs for a paraghaph level index following a ranked query.
+	The standard C escape character sequences (see the man page)
+	may be used to place special characters in the string. For 
+	example, a newline would the `\n'. To include a `%' use the
+	sequence `%%'. To include the paragraph weight use the 
+	sequence `%w'.
+
+qfreq = `true'
+	This determine whether the ranked queries will take into 
+	account the number of times each query term is specified.
+	When this is `true' the number of times a term appears in
+	the query is used in the ranking. When this is `false' all 
+	query term are assumed to occur only once. This parameter
+	may take the values `yes', `no', `true', `false', `on' or
+	`off'.
+
+query = `boolean'
+	This specifies the type of queries that are to be specified.
+	It can take four different values `boolean', `ranked', 
+	`docnums' or `approx-ranked'. 
+
+	`boolean' is for boolean queries. 
+		The yacc grammar for boolean queries is as follows :-
+
+		query   : or;
+ 
+		or      : or '|' and
+		        | and ;	
+ 
+		and     : and '&' not
+        		| and not
+        		| not ;
+
+		not     : term
+		        | '!' not ;
+ 
+		term    : TERM
+		        | '(' or ')' ;
+ 
+
+
+
+	`ranked' and `approx-ranked' are for queries ranked by the
+		cosine measure. `approx-ranked' uses only the low
+		precision document lengths, and therefore only
+		produces an approximation to full cosine ranking.
+
+		query   : TERM
+      			| query TERM ;
+	
+	`docnums' allows the entry of document numbers. Multiple 
+		numbers separated by spaces may be specified
+		or ranges separated by hyphens.
+
+
+		query   : range
+			| query range ;
+
+
+		range   : num
+			| num '-' num ;
+
+
+ranked_doc_sepstr = `---------------------------------- %n %w\n'
+	This specifies the string that will be used to separate 
+	documents when they are displayed for `ranked' or 
+	`approx-ranked'	queries. The standard C escape character 
+	sequences (see the man page) may be used to place special
+	characters in the string. For example, a newline would the
+	`\n'. To include a `%' use the sequence `%%'. To include the
+	MG document number use the sequence `%n'. To include the
+	document weight use the sequence `%w'.
+
+sizestats = `false'
+	If this is true then various numbers are output at the end
+	of each query indicating what went on during the query. 
+	This parameter may take the values `yes', `no', `true', 
+	`false', `on' or `off'.
+
+skip_dump = `skips.%d'
+	If this parameter is set then during ranked queries on skipped
+	inverted files when `accumulator_method' is set to `splay_tree',
+	`hash_table', or `list` a file will be produced in the current
+	directory. The name of the file is the value of this parameter,
+	a `%d' in the file name will be replaced with the process id of
+	mgquery. This file will contain information about the usage of
+	skips during the query processing. This option is expensive; 
+	use `.unset skip_dump' to obtain optimal performance.
+
+sorted_terms = `on'
+	This specifies whether of not the terms should be sorted into
+	decreasing occurrence in documents so that the least often
+	occurring terms are processed first when ranked queries are
+	being done. When this is true the terms are sorted. When this
+	is false the terms are not sorted and are instead processed in
+	order of occurrence. This parameter may take the values `yes',
+	`no', `true', `false', `on' or `off'.
+
+
+stop_at_max_accum = `on'
+	This specifies what should happen when the maximum number of
+	accumulators set by `max_accumulators' is reached. When this
+	is true the the processing of terms is stopped at the completion
+	of the current term. When this is false processing continues but
+	no new accumulators are created. This parameter may take the 
+	values `yes', `no', `true', `false', `on' or `off'.
+
+terminator = `'
+	This specifies the string that will be output after the	last
+	document from the previous query has been output. The standard
+	C escape character sequences (see the man page) may be used to
+	place special characters in the string. For example, a newline
+	would the `\n'. To include a `%' use the sequence `%%'.
+	
+
+timestats = `false'
+	If this is true then the time to process a query is displayed
+	in both real time and CPU time. This parameter may take the
+	values `yes', `no', `true', `false', `on' or `off'.
+
+verbatim = `off'
+	This is a boolean parameter that determines whether the program
+	should attempt to do a regular expression match on the retrieved
+	text. If verbatim is `on' and a post-processing strng is specified
+	with the query then the post-processing string will be searched for
+	in the documents just before they are displayed. If the string is
+	found the document will be displayed, if not the document will not
+	be displayed. If verbatim is `off' the post-processing string will
+	be considered a regular expression like in `vi' or `egrep'.
+	E.G. If verbatim is `on', "and.*the" will look for the 8 character
+	     sequence "and.*the". If verbatim is `off', "and.*the" will
+	     look for the sequence "and" followed somewhere later in the
+	     document by the sequence "the".
+	This parameter may take the values `yes', `no', `true', `false',
+	`on' or `off'.
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/invf.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/invf.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/invf.h	(revision 16561)
@@ -0,0 +1,79 @@
+/**************************************************************************
+ *
+ * invf.h -- Data structures for inverted files
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_INVF
+#define H_INVF
+
+/* NOTE: This does not include the magic number */
+struct invf_dict_header
+  {
+    unsigned long lookback;
+    unsigned long dict_size;
+    unsigned long total_bytes;
+    unsigned long index_string_bytes;
+    double input_bytes; /* [RJM 07/97: 4G limit] */
+    unsigned long num_of_docs;
+    unsigned long static_num_of_docs;
+    unsigned long num_of_words;
+    unsigned long stemmer_num;
+    unsigned long stem_method;
+  };
+
+struct stem_dict_header
+  {
+    unsigned long lookback;
+    unsigned long block_size;
+    unsigned long num_blocks;
+    unsigned long blocks_start;
+    unsigned long index_chars;
+    unsigned long num_of_docs;
+    unsigned long static_num_of_docs;
+    unsigned long num_of_words;
+    unsigned long stemmer_num;
+    unsigned long stem_method;
+    unsigned long indexed;       /* [RPAP - Jan 97: Stem Index Change] */
+  };
+
+struct invf_file_header
+  {
+    unsigned long no_of_words;
+    unsigned long no_of_ptrs;
+    unsigned long skip_mode;
+    unsigned long params[16];
+    unsigned long InvfLevel;
+  };
+
+/* [RPAP - Jan 97: Stem Index Change] */
+struct stem_idx_header
+  {
+    unsigned long lookback;
+    unsigned long block_size;
+    unsigned long num_blocks;
+    unsigned long blocks_start;
+    unsigned long index_chars;
+    unsigned long num_of_words;
+  };
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.c	(revision 16561)
@@ -0,0 +1,1666 @@
+/**************************************************************************
+ *
+ * invf_get.c -- Inverted file reading routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "filestats.h"
+#include "timing.h"
+#include "bitio_gen.h"
+#include "bitio_m_mem.h"
+#include "bitio_m.h"
+#include "sptree.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "invf_get.h"
+#include "mg_errors.h"
+
+#define SKIPPING
+#define SHORT_CUT
+
+struct pool
+  {
+    int length, pos;
+    struct pool *next;
+    Invf_Doc_Entry ide[1];
+  };
+
+static int skip_header = 0;
+
+static char skip_header_text0[] =
+"#######################################################################\n"
+"#\n"
+"# 1\tWord number.\n"
+"# 2\tNumber of time the word occurs in the collection.\n"
+"# 3\tNumber of documents the word occurs in.\n"
+"# 4\tThe word.\n"
+"# 5\tNumber of docnum/word_count entries decoded.\n"
+"# 6\tNumber of hits.\n"
+"# 7\tNumber of entries in the splay tree or hash table.\n"
+"# 8\tAmount added to the accumulators while while processing this word.\n"
+"#\n"
+"#######################################################################\n";
+
+static char skip_header_text1[] =
+"#######################################################################\n"
+"#\n"
+"# 1\tWord number.\n"
+"# 2\tNumber of time the word occurs in the collection.\n"
+"# 3\tNumber of documents the word occurs in.\n"
+"# 4\tThe word.\n"
+"# 5\tNumber of skips taken in evaluating the inverted file entry.\n"
+"# 6\tNumber of docnum/word_count entries decoded.\n"
+"# 7\tNumber of hits.\n"
+"# 8\tNumber of entries in the splay tree or hash table.\n"
+"# 9\tAmount added to the accumulators while while processing this word.\n"
+"#\n"
+"#######################################################################\n";
+
+
+static char skip_header_text2[] =
+"#######################################################################\n"
+"#\n"
+"#  1\tWord number.\n"
+"#  2\tNumber of time the word occurs in the collection.\n"
+"#  3\tNumber of documents the word occurs in.\n"
+"#  4\tThe word.\n"
+"#  5\tSkip size.\n"
+"#  6\tNumber of skips taken in evaluating the inverted file entry.\n"
+"#  7\tNumber of docnum/word_count entries decoded.\n"
+"#  8\tNumber of hits.\n"
+"#  9\tNumber of entries in the splay tree or hash table.\n"
+"# 10\tAmount added to the accumulators while while processing this word.\n"
+"#\n"
+"#######################################################################\n";
+
+static char *skip_header_text[] =
+{skip_header_text0,
+ skip_header_text1,
+ skip_header_text2};
+
+
+
+
+
+
+
+
+/**************************************************************************
+ *
+ * Code for the hash table.
+ *
+ */
+
+static Hash_Table *
+HT_create (query_data * qd, unsigned max_entries,
+	   unsigned max_size)
+{
+  unsigned es, hts;
+  Hash_Table *HT;
+  if (max_entries == 0)
+    max_entries = 8192;
+  es = sizeof (Hash_Table) + sizeof (Invf_Doc_EntryH) * (max_entries - 1);
+  hts = sizeof (Invf_Doc_EntryH *) * max_size;
+
+  HT = Xmalloc (es);
+  if (!HT)
+    return NULL;
+
+  HT->HashTable = Xmalloc (hts);
+  if (!(HT->HashTable))
+    {
+      Xfree (HT);
+      return NULL;
+    }
+
+  ChangeMemInUse (qd, es + hts);
+  bzero ((char *) (HT->HashTable), hts);
+  HT->max = max_entries;
+  HT->num = 0;
+  HT->Suplimentary_Entries = NULL;
+  HT->Suplimentary_Size = 0;
+  HT->Suplimentary_Num = 0;
+  HT->Head = NULL;
+  HT->size = max_size;
+  return HT;
+}
+
+
+static void 
+HT_Sup_create (query_data * qd, Hash_Table * HT, unsigned max_size)
+{
+  int es = sizeof (Invf_Doc_EntryH) * max_size;
+  HT->Suplimentary_Entries = Xmalloc (es);
+  if (!HT->Suplimentary_Entries)
+    return;
+  HT->Suplimentary_Size = max_size;
+  HT->Suplimentary_Num = 0;
+  ChangeMemInUse (qd, es);
+}
+
+static void 
+HT_Sup_Add (Hash_Table * HT, unsigned long DN, float Sum)
+{
+  Invf_Doc_EntryH *IDEH;
+  IDEH = &HT->Suplimentary_Entries[HT->Suplimentary_Num++];
+  IDEH->next = NULL;
+  IDEH->IDE.DocNum = DN;
+  IDEH->IDE.Sum = Sum;
+}
+
+Invf_Doc_Entry *
+HT_find (Hash_Table * HT, unsigned long DN)
+{
+  Invf_Doc_EntryH *IDEH;
+  if (!HT->HashTable)
+    FatalError (1, "Accessing the hash_table through HT_find after HT_sort");
+  IDEH = HT->HashTable[DN % HT->size];
+  while (IDEH)
+    {
+      if (IDEH->IDE.DocNum == DN)
+	return &(IDEH->IDE);
+      IDEH = IDEH->next;
+    }
+  return NULL;
+}
+
+
+static int 
+HT_insert (Hash_Table * HT,
+	   unsigned long DN, float Sum)
+{
+  Invf_Doc_EntryH **IDEH;
+  if (HT->num == HT->max)
+    return 0;
+  IDEH = &(HT->HashTable[DN % HT->size]);
+  while (*IDEH)
+    IDEH = &((*IDEH)->next);
+  *IDEH = &(HT->IDEH[HT->num++]);
+  (*IDEH)->next = NULL;
+  (*IDEH)->IDE.DocNum = DN;
+  (*IDEH)->IDE.Sum = Sum;
+  return 1;
+}
+
+
+void 
+HT_free (query_data * qd, Hash_Table * HT)
+{
+  unsigned es;
+  es = sizeof (Hash_Table) + sizeof (Invf_Doc_EntryH) * HT->max +
+    sizeof (Invf_Doc_EntryH *) * HT->size;
+  if (HT->HashTable)
+    Xfree (HT->HashTable);
+  if (HT->Suplimentary_Entries)
+    {
+      Xfree (HT->Suplimentary_Entries);
+      es += sizeof (Invf_Doc_EntryH) * HT->Suplimentary_Size;
+    }
+  Xfree (HT);
+  ChangeMemInUse (qd, -es);
+}
+
+
+static int 
+IDEH_comp (const void *a, const void *b)
+{
+  const Invf_Doc_EntryH *A = a;
+  const Invf_Doc_EntryH *B = b;
+  return A->IDE.DocNum - B->IDE.DocNum;
+}
+
+static void 
+HT_sort (query_data * qd, Hash_Table * HT)
+{
+  int i;
+  Invf_Doc_EntryH *l, *r, **b;
+  unsigned es = sizeof (Invf_Doc_EntryH *) * HT->size;
+  if (HT->HashTable)
+    Xfree (HT->HashTable);
+  HT->size = 0;
+  HT->HashTable = NULL;
+  ChangeMemInUse (qd, -es);
+
+  qsort (HT->IDEH, HT->num, sizeof (Invf_Doc_EntryH), IDEH_comp);
+
+  for (i = 0; i < HT->num - 1; i++)
+    HT->IDEH[i].next = &HT->IDEH[i + 1];
+  HT->IDEH[i].next = NULL;
+  l = HT->IDEH;
+
+  if (HT->Suplimentary_Entries)
+    {
+      for (i = 0; i < HT->Suplimentary_Num - 1; i++)
+	HT->Suplimentary_Entries[i].next = &HT->Suplimentary_Entries[i + 1];
+      HT->Suplimentary_Entries[i].next = NULL;
+    }
+  r = HT->Suplimentary_Entries;
+
+  b = &HT->Head;
+
+  while (l || r)
+    {
+      if (r && (!l || (l->IDE.DocNum > r->IDE.DocNum)))
+	{
+	  *b = r;
+	  b = &r->next;
+	  r = *b;
+	}
+      else
+	{
+	  *b = l;
+	  b = &l->next;
+	  l = *b;
+	}
+    }
+  *b = NULL;
+}
+
+
+
+
+
+
+/**************************************************************************
+ *
+ * Code for the hash table.
+ *
+ */
+
+static List_Table *
+LT_create (query_data * qd, unsigned max_entries)
+{
+  unsigned es;
+  List_Table *LT;
+  if (max_entries == 0)
+    max_entries = 8192;
+  es = sizeof (List_Table) + sizeof (Invf_Doc_Entry) * (max_entries - 1);
+
+  LT = Xmalloc (es);
+  if (!LT)
+    return NULL;
+
+  ChangeMemInUse (qd, es);
+  LT->max = max_entries;
+  LT->num = 0;
+  return LT;
+}
+
+
+
+Invf_Doc_Entry *
+LT_find (List_Table * LT, unsigned long DN)
+{
+  int l = 0;
+  int r = LT->num - 1;
+  int m, c;
+  while (l <= r)
+    {
+      m = (l + r) / 2;
+      c = DN - LT->IDE[m].DocNum;
+      if (c < 0)
+	r = m - 1;
+      else if (c > 0)
+	l = m + 1;
+      else
+	return &LT->IDE[m];
+    }
+  return NULL;
+}
+
+static List_Table *
+LT_add (query_data * qd, List_Table * LT,
+	unsigned long DN, float Sum)
+{
+  Invf_Doc_Entry *ide;
+
+  /* increase the size of the list if there is not enough */
+  /* room for this entry */
+  if (LT->num == LT->max)
+    {
+      int old, new, max_entries;
+      old = sizeof (List_Table) + sizeof (Invf_Doc_Entry) * LT->max;
+      max_entries = LT->max + (LT->max >> 1);
+      new = sizeof (List_Table) + sizeof (Invf_Doc_Entry) * max_entries;
+
+      LT = Xrealloc (LT, new);
+      if (!LT)
+	return NULL;
+
+      ChangeMemInUse (qd, new - old);
+      LT->max = max_entries;
+    }
+
+  /* add the entry to the end of the list */
+  ide = &LT->IDE[LT->num];
+  ide->DocNum = DN;
+  ide->Sum = Sum;
+  LT->num++;
+
+  return LT;
+}
+
+
+void 
+LT_free (query_data * qd, List_Table * LT)
+{
+  unsigned es;
+  es = sizeof (List_Table) + sizeof (Invf_Doc_Entry) * LT->max;
+  Xfree (LT);
+  ChangeMemInUse (qd, -es);
+}
+
+
+static int 
+IDE_comp (const void *a, const void *b)
+{
+  const Invf_Doc_Entry *A = a;
+  const Invf_Doc_Entry *B = b;
+  return A->DocNum - B->DocNum;
+}
+
+static void 
+LT_sort (List_Table * LT)
+{
+  qsort (LT->IDE, LT->num, sizeof (Invf_Doc_Entry), IDE_comp);
+}
+
+
+/* [RJM 07/97: Ranked Required Terms] */
+/* accumulators with a sum less than or equal to 0.0 are to be deleted */
+static void 
+LT_pack (List_Table * LT)
+{
+  int s, d = 0;
+  for (s = 0; s < LT->num; s++) {
+      if (LT->IDE[s].Sum <= 0.0) {
+	/* ignore this accumulator */
+
+      } else if (d > 0 && LT->IDE[s].DocNum == LT->IDE[d-1].DocNum) {
+	LT->IDE[d-1].Sum += LT->IDE[s].Sum;
+
+      } else {
+	if (d != s) LT->IDE[d] = LT->IDE[s];
+	d++;
+      }
+  }
+
+  if (LT->num) LT->num = d;
+}
+
+
+/**************************************************************************/
+
+
+
+invf_data *
+InitInvfFile (File * InvfFile, stemmed_dict * sd)
+{
+  invf_data *id;
+  if (!(id = Xmalloc (sizeof (invf_data))))
+    {
+      mg_errno = MG_NOMEM;
+      return NULL;
+    }
+
+  id->InvfFile = InvfFile;
+
+  Fread (&id->ifh, sizeof (id->ifh), 1, InvfFile);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  {
+    int i;
+    
+    NTOHUL(id->ifh.no_of_words);
+    NTOHUL(id->ifh.no_of_ptrs);
+    NTOHUL(id->ifh.skip_mode);
+    for (i = 0; i < 16; i++)
+      NTOHUL(id->ifh.params[i]);
+    NTOHUL(id->ifh.InvfLevel);
+  }
+
+  id->N = sd->sdh.num_of_docs;
+  id->Nstatic = sd->sdh.static_num_of_docs;
+
+  return (id);
+}
+
+
+void 
+FreeInvfData (invf_data * id)
+{
+  Xfree (id);
+}
+
+/****************************************************************************/
+
+
+static void 
+CalcBlks (query_data * qd, WordEntry * we, int *blk,
+	  int *dn_blk, int *len_blk, int *k, int *p)
+{
+  *p = we->doc_count;
+
+  *blk = BIO_Bblock_Init (qd->id->Nstatic, *p);
+  switch (qd->id->ifh.skip_mode)
+    {
+    case 1:
+      {
+	unsigned long len;
+	if (*p <= qd->id->ifh.params[0])
+	  *k = 0;
+	else
+	  {
+	    *k = qd->id->ifh.params[0];
+	    len = BIO_Bblock_Bound (qd->id->N, *p);
+	    if (qd->id->ifh.InvfLevel >= 2)
+	      len += we->count;
+	    *dn_blk = BIO_Bblock_Init (qd->sd->sdh.num_of_docs, (*p + *k - 1) / *k);
+	    *len_blk = BIO_Bblock_Init (len, (*p + *k - 1) / *k);
+	  }
+	break;
+      }
+    case 2:
+      {
+	unsigned long len;
+	if (*p <= qd->id->ifh.params[1])
+	  *k = 0;
+	else
+	  {
+	    *k = (int) (2 * sqrt (((double) (*p)) / qd->id->ifh.params[0]));
+	    if (*k <= qd->id->ifh.params[1])
+	      *k = qd->id->ifh.params[1];
+	    len = BIO_Bblock_Bound (qd->id->N, *p);
+	    if (qd->id->ifh.InvfLevel >= 2)
+	      len += we->count;
+	    *dn_blk = BIO_Bblock_Init (qd->sd->sdh.num_of_docs,
+				       (*p + *k - 1) / *k);
+	    *len_blk = BIO_Bblock_Init (len, (*p + *k - 1) / *k);
+	  }
+	break;
+      }
+    default:
+      *k = 0;
+    }
+}
+
+
+/* =========================================================================
+ * Function: GetDocsOp
+ * Description: 
+ *      if op == op_term then 
+ *        look up term in invf and decode all its entries/documents
+ *      if op == op_and_terms then 
+ *        go thru L, testing if it exists in invf using skips  
+ *        if it does exist then overwrite the entry nearer the start of the list
+ *      if op == op_and_or_terms then
+ *        go thru L, testing if it exists in invf using skips  
+ *        BUT only mark ones which are found - keep list intact
+ *      if op == op_diff_terms then     >>>> NOT tested <<<<
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+DocList *
+GetDocsOp (query_data * qd, WordEntry * we, Op_types op, DocList * L)
+{
+  u_char *buffer;
+  DocEntry *src = NULL, *dest = NULL;
+  int i;			/* index counter into inverted list */
+  unsigned long pos;
+  int next_start, next_doc_num, next_i;
+  int block_counter;		/* within counter in block */
+  int dn_blk = 0, len_blk = 0;
+  int skip_size, have_skipping;
+  int blk;			/* bblock control parameter */
+  int ft;			/* The number of documents the word occurs in */
+  unsigned long CurrDoc = 0;	/* NOTE: Document numbers start at 1 */
+  float Weight;
+
+  /* ... AND empty */
+  if (op == op_and_terms && (!L || !L->num))
+    {
+      if (L)
+	Xfree (L);
+      return MakeDocList (0);
+    }
+
+  if (op == op_term)
+    L = MakeDocList (we->doc_count);
+
+  CalcBlks (qd, we, &blk, &dn_blk, &len_blk, &skip_size, &ft);
+  have_skipping = (skip_size != 0);
+  if (!(buffer = Xmalloc (we->invf_len)))
+    return MakeDocList (0);
+
+  ChangeMemInUse (qd, we->invf_len);
+
+  qd->num_of_terms++;
+
+  /* --- Read an inverted file entry for the given word --- */
+  Fseek (qd->File_invf, we->invf_ptr, 0);
+  Fread (buffer, sizeof (char), we->invf_len, qd->File_invf);
+
+  /* Read from the buffer */
+  DECODE_START (buffer, we->invf_len);
+
+  Weight = qd->sd->sdh.num_of_words / (100 * log2 ((float) we->count + 1));
+
+  if (op != op_term)
+    dest = src = L->DE;
+  i = pos = 0;
+  next_i = next_start = next_doc_num = 0;
+  block_counter = skip_size - 1;
+
+  /* Go thru the elements of an inverted list for a term */
+  /* May not go sequentially if skipping is done */
+  while (i < ft && ((op == op_term) || src - L->DE < L->num))
+    {
+      unsigned long diff, Temp;
+
+      if (have_skipping)
+	{
+
+	  /* --- Skip to next block --- */
+	  if (op != op_term && i + skip_size < ft &&
+	      src->DocNum > next_doc_num && next_doc_num >= 0)
+	    {
+	    ShortCut:
+	      i = next_i;
+	      DECODE_SEEK (next_start);
+	      pos = next_start;
+	      CurrDoc = next_doc_num;
+	      qd->hops_taken++;
+	      block_counter = skip_size - 1;
+	    }
+
+	  /* --- Decode the skip block header --- */
+	  if (block_counter == skip_size - 1)
+	    {
+	      if (i + skip_size < ft)
+		{
+		  /* --- Decode the skip block header --- */
+		  int doc_diff, pos_diff;
+		  BBLOCK_DECODE_L (doc_diff, dn_blk, pos);
+		  next_doc_num += doc_diff;
+		  next_i += skip_size;
+		  BBLOCK_DECODE_L (pos_diff, len_blk, pos);
+		  next_start = pos + pos_diff;
+		}
+	      else
+		next_doc_num = -1;
+	    }
+
+	  /* --- Do a skip if c-set doc is higher than block header doc --- */
+	  if (op != op_term && i + skip_size < ft &&
+	      src->DocNum > next_doc_num && next_doc_num >= 0)
+	    goto ShortCut;
+
+
+	}			/*if skipping */
+
+      /* --- increment CurrDoc --- */
+      if (have_skipping && block_counter == 0 && i != ft - 1)
+	CurrDoc = next_doc_num;
+      else
+	{
+	  BBLOCK_DECODE_L (diff, blk, pos);
+	  CurrDoc += diff;
+	}
+
+      /* --- If have f_dt's then eat them up --- */
+      if (qd->id->ifh.InvfLevel >= 2)
+	GAMMA_DECODE_L (Temp, pos);	/* discard word count */
+
+      /* --- Increment counters --- */
+      qd->num_of_ptrs++;
+      i++;
+      block_counter--;
+      if (block_counter < 0)	/* ??? Why would this happen ? */
+	block_counter = skip_size - 1;
+
+      switch (op)
+	{
+	case op_term:
+	  /* --- Add the doc from invf onto the c-set list --- */
+	  L->DE[L->num].DocNum = CurrDoc;
+	  L->DE[L->num].Weight = Weight;
+	  L->DE[L->num].SeekPos = 0;
+	  L->DE[L->num].Len = 0;
+	  L->DE[L->num].CompTextBuffer = NULL;
+	  L->DE[L->num].Next = NULL;
+	  L->DE[L->num].or_included = 0;
+	  L->num++;
+	  break;
+
+	case op_and_terms:
+	case op_and_or_terms:
+	  /* --- see if have a match and save it --- */
+
+	  while (src < (L->DE + L->num) && CurrDoc > src->DocNum)
+	    {
+	      src++;
+	    }
+	  /* Unless at end of list, assert(CurrDoc <= src->DocNum) */
+
+	  /* --- Accept the doc as a candidate --- */
+	  if (src < (L->DE + L->num) && CurrDoc == src->DocNum)
+	    {
+	      if (op == op_and_terms)
+		{
+		  *dest++ = *src;
+		}
+	      else
+		{
+		  src->or_included = 1;
+		}
+	      src++;		/* Move onto next one */
+	    }
+
+	  /* If the next one to try has already been tried then move on again */
+	  if (op == op_and_or_terms)
+	    {
+	      while (src < (L->DE + L->num) && src->or_included)
+		src++;
+	    }
+
+	  break;
+
+	case op_diff_terms:
+	  while (src < (L->DE + L->num) && src->DocNum < CurrDoc)
+	    *dest++ = *src++;
+	  if (src < (L->DE + L->num) && src->DocNum == CurrDoc)
+	    src++;
+	  break;
+
+	default:
+	  break;
+	}			/*switch */
+
+    }				/*while traversing invf */
+
+  if (op == op_and_terms || op == op_diff_terms)
+    {
+      if (op == op_diff_terms)
+	while (src - L->DE < L->num)
+	  *dest++ = *src++;
+
+      /* update size of c-set */
+      L->num = dest - L->DE;
+    }
+  DECODE_DONE
+
+    Xfree (buffer);
+  ChangeMemInUse (qd, -we->invf_len);
+
+  return (L);
+}
+
+
+
+
+
+
+
+float *
+CosineDecode (query_data * qd, TermList * Terms, RankedQueryInfo * rqi)
+{
+  float *AccumulatedWeights;
+  int j, num_docs, num_terms;
+  int seen_must_match = 0; /* [RJM 07/97: Ranked Required Terms] says whether to 'and' queries or not */
+
+  num_docs = qd->sd->sdh.num_of_docs;
+  /* Create the array for the documents */
+  if (!(AccumulatedWeights = Xmalloc (num_docs * sizeof (float))))
+      return (NULL);
+
+  bzero ((char *) AccumulatedWeights, sizeof (float) * num_docs);
+  ChangeMemInUse (qd, sizeof (float) * num_docs);
+
+  if (rqi->MaxTerms == -1)
+    num_terms = Terms->num;
+  else
+    num_terms = rqi->MaxTerms < Terms->num ? rqi->MaxTerms : Terms->num;
+
+
+  /* For each word in the list . . . */
+  for (j = 0; j < num_terms; j++)
+    {
+      u_char *buffer;
+      int i, kd;
+      unsigned long next_mjr_dn = 0;
+      unsigned long CurrDocNum = 0;	/* NOTE: Document numbers start at 1 */
+      unsigned long LastDocNum = 0; /* [RJM 07/97: Ranked Required Terms] */
+      unsigned long TempDocNum = 0; /* [RJM 07/97: Ranked Required Terms] */
+      double Wqt, WordLog;
+      int dn_blk = 0, len_blk = 0, k;
+      int blk;			/* bblock control parameter */
+      int p;			/* The number of documents the word occurs in */
+      int require_match = Terms->TE[j].require_match; /* [RJM 07/97: Ranked Required Terms] */
+      WordEntry *we = &Terms->TE[j].WE;
+      CalcBlks (qd, we, &blk, &dn_blk, &len_blk, &k, &p);
+      if (!(buffer = Xmalloc (we->invf_len)))
+	break;
+
+      ChangeMemInUse (qd, we->invf_len);
+
+      Fseek (qd->File_invf, we->invf_ptr, 0);
+      Fread (buffer, sizeof (char), we->invf_len, qd->File_invf);
+
+      /* Read from the buffer */
+      DECODE_START (buffer, we->invf_len);
+
+      WordLog = log ((double) num_docs / (double) we->max_doc_count);  /* [RPAP - Jan 97: Stem Index Change] */
+
+      Wqt = (rqi->QueryFreqs ? Terms->TE[j].Count : 1) * WordLog;
+
+      qd->num_of_terms++;
+
+      kd = 0;
+      for (i = 0; i < p; i++, kd++)
+	{
+	  int Count;		/* The number of times the word occurs in a document */
+	  double Wdt;
+	  unsigned long diff;
+	  if (kd == k)
+	    {
+	      kd = 0;
+	    }
+	  if (k && kd == 0)
+	    if (i + k < p)
+	      {
+		int temp;
+		BBLOCK_DECODE (next_mjr_dn, dn_blk);
+		next_mjr_dn += CurrDocNum;
+		BBLOCK_DECODE (temp, len_blk);
+	      }
+	  if (k && kd == k - 1 && i != p - 1)
+	    CurrDocNum = next_mjr_dn;
+	  else
+	    {
+	      BBLOCK_DECODE (diff, blk);
+	      CurrDocNum += diff;
+	    }
+	  if (qd->id->ifh.InvfLevel >= 2)
+	    GAMMA_DECODE (Count);
+	  else
+	    Count = (double) we->count / p;
+	  Wdt = Count * WordLog;
+
+	  /* [RJM 07/97: Ranked Required Terms] */
+	  /* zero the weights of intermediate documents if this term is required to match */
+	  if (require_match == 1) {
+	    for (TempDocNum = LastDocNum+1; TempDocNum < CurrDocNum; TempDocNum++)
+	      AccumulatedWeights[TempDocNum - 1] = 0.0;
+	  }
+
+	  /* [RJM 07/97: Ranked Required Terms] */
+	  /* if a term has been required to match before, the weight we are */
+	  /* adding to must be non-zero */
+	  if (!seen_must_match || (AccumulatedWeights[CurrDocNum - 1] > 0.0)) {
+	      AccumulatedWeights[CurrDocNum - 1] += Wqt * Wdt;
+	  }
+
+	  qd->num_of_ptrs++;
+	  LastDocNum = CurrDocNum; /* [RJM 07/97: Ranked Required Terms] */
+	}
+
+      DECODE_DONE
+
+	Xfree (buffer);
+      ChangeMemInUse (qd, -we->invf_len);
+
+      /* [RJM 07/97: Ranked Required Terms] */
+      /* zero the remaining weights if this term is required to match */
+      if (require_match == 1) {
+	for (TempDocNum = LastDocNum+1; TempDocNum <= num_docs; TempDocNum++)
+	  AccumulatedWeights[TempDocNum - 1] = 0.0;
+	seen_must_match = 1;
+      }
+    }
+  return AccumulatedWeights;
+}
+
+
+
+static int 
+doc_comp (void *a, void *b)
+{
+  return ((Invf_Doc_Entry *) a)->DocNum - ((Invf_Doc_Entry *) b)->DocNum;
+}
+
+
+static Invf_Doc_Entry *
+get_ide (query_data * qd, Invf_Doc_Entry_Pool * pool)
+{
+  struct pool *p = pool->pool;
+  if (!p || p->pos == p->length)
+    {
+      register unsigned size = sizeof (struct pool) +
+      (pool->pool_chunks - 1) * sizeof (Invf_Doc_Entry);
+      p = malloc (size);
+      if (!p)
+	return NULL;
+      ChangeMemInUse (qd, size);
+      p->length = pool->pool_chunks;
+      p->pos = 0;
+      p->next = pool->pool;
+      pool->pool = p;
+    }
+  return &(p->ide[p->pos++]);
+}
+
+
+void 
+free_ide_pool (query_data * qd, Invf_Doc_Entry_Pool * pool)
+{
+  while (pool->pool)
+    {
+      struct pool *p = pool->pool;
+      pool->pool = p->next;
+      ChangeMemInUse (qd, -(sizeof (struct pool) + (p->length - 1) *
+			    sizeof (Invf_Doc_Entry)));
+      free (p);
+    }
+  pool->pool = NULL;
+}
+
+
+FILE *
+open_skip (char *sd)
+{
+  char buf[256];
+  if (sd == NULL || *sd == '\0')
+    return NULL;
+  sprintf (buf, sd, getpid ());
+  return fopen (buf, "a");
+}
+
+
+
+Splay_Tree *
+CosineDecodeSplay (query_data * qd, TermList * Terms,
+		   RankedQueryInfo * rqi, Invf_Doc_Entry_Pool * pool)
+{
+  Splay_Tree *Doc_Set;
+  int j, num_docs, num_terms, MoreAccum;
+  int Anding = 0;
+  FILE *sk = open_skip (rqi->skip_dump);
+  u_char indent = 0;
+  if (sk)
+    {
+      if (!skip_header)
+	{
+	  skip_header = 1;
+	  fprintf (sk, "%s", skip_header_text[qd->id->ifh.skip_mode]);
+	  fprintf (sk, "\nSkipping method %ld\n", qd->id->ifh.skip_mode);
+	  switch (qd->id->ifh.skip_mode)
+	    {
+	    case 1:
+	      fprintf (sk, "Skipping is every %ld docnums\n",
+		       qd->id->ifh.params[0]);
+	      break;
+	    case 2:
+	      fprintf (sk, "Max nodes = %ld\nNo skips smaller or equal to %ld\n",
+		       qd->id->ifh.params[0], qd->id->ifh.params[1]);
+	      break;
+	    }
+	}
+      fprintf (sk, "\n\t\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      for (j = 0; j < Terms->num; j++)
+	if (Terms->TE[j].Word[0] > indent)
+	  indent = Terms->TE[j].Word[0];
+    }
+
+  num_docs = qd->sd->sdh.num_of_docs;
+  /* Create the array for the documents */
+  Doc_Set = SP_createset (doc_comp);
+
+  pool->pool = NULL;
+  pool->pool_chunks = 1024;
+
+  if (rqi->MaxTerms == -1)
+    num_terms = Terms->num;
+  else
+    num_terms = rqi->MaxTerms < Terms->num ? rqi->MaxTerms : Terms->num;
+
+  MoreAccum = 1;
+
+  /* For each word in the list . . . */
+  for (j = 0; j < num_terms; j++)
+    {
+      int Abort = 0;
+      u_char *buffer;
+      int pos, i;
+      unsigned long CurrDocNum = 0;	/* NOTE: Document numbers start at 1 */
+      double Wqt, WordLog;
+      int dn_blk = 0, len_blk = 0, k, kd;
+      int blk;			/* bblock control parameter */
+      int p;			/* The number of documents the word occurs in */
+      WordEntry *we = &Terms->TE[j].WE;
+      Invf_Doc_Entry *next = NULL;
+      int next_i, next_doc_num, next_start;
+      int skips_taken = 0, ptrs_dec = 0;
+      double total_added = 0.0;
+      int hits = 0;
+      if (sk)
+	{
+	  fprintf (sk, "%3d : %8ld %6ld \"%.*s\"%*s", j, we->count,
+		 we->doc_count, Terms->TE[j].Word[0], Terms->TE[j].Word + 1,
+		   indent - Terms->TE[j].Word[0], "");
+	}
+
+      CalcBlks (qd, we, &blk, &dn_blk, &len_blk, &k, &p);
+      if (!(buffer = Xmalloc (we->invf_len)))
+	break;
+
+      ChangeMemInUse (qd, we->invf_len);
+
+      if (sk && qd->id->ifh.skip_mode == 2)
+	fprintf (sk, "%4d", k);
+
+      Fseek (qd->File_invf, we->invf_ptr, 0);
+      Fread (buffer, sizeof (char), we->invf_len, qd->File_invf);
+
+      /* Read from the buffer */
+      DECODE_START (buffer, we->invf_len);
+
+      WordLog = log ((double) num_docs / (double) we->max_doc_count);  /* [RPAP - Jan 97: Stem Index Change] */
+
+      Wqt = (rqi->QueryFreqs ? Terms->TE[j].Count : 1) * WordLog;
+
+      qd->num_of_terms++;
+
+      if (rqi->MaxAccums != -1 && Doc_Set->no_of_items >= rqi->MaxAccums)
+	{
+	  MoreAccum = 0;
+	  Anding = 1;
+	  next = SP_get_first (Doc_Set);
+	}
+      i = pos = 0;
+      next_i = next_start = next_doc_num = 0;
+      kd = k - 1;
+      while (i < p && (!Anding || next))
+	{
+	  int Count;		/* The number of times the occurs in a document */
+	  double Wdt;
+	  unsigned long diff;
+	  Invf_Doc_Entry ent, *mem;
+	  if (k)
+	    {
+	      if (Anding && i + k < p && next->DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		{
+		  i = next_i;
+		  DECODE_SEEK (next_start);
+		  pos = next_start;
+		  CurrDocNum = next_doc_num;
+		  qd->hops_taken++;
+		  skips_taken++;
+		  kd = k - 1;
+		}
+	      if (kd == k - 1)
+		if (i + k < p)
+		  {
+		    int doc_diff, pos_diff;
+		    BBLOCK_DECODE_L (doc_diff, dn_blk, pos);
+		    next_doc_num += doc_diff;
+		    next_i += k;
+		    BBLOCK_DECODE_L (pos_diff, len_blk, pos);
+		    next_start = pos + pos_diff;
+		  }
+		else
+		  next_doc_num = -1;
+	      if (Anding && i + k < p && next->DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		continue;
+	    }
+	  if (k && kd == 0 && i != p - 1)
+	    CurrDocNum = next_doc_num;
+	  else
+	    {
+	      BBLOCK_DECODE_L (diff, blk, pos);
+	      CurrDocNum += diff;
+	    }
+	  qd->num_of_ptrs++;
+	  ptrs_dec++;
+	  if (qd->id->ifh.InvfLevel >= 2)
+	    GAMMA_DECODE_L (Count, pos);
+	  else
+	    Count = (double) we->count / p;
+	  i++;
+	  kd--;
+	  if (kd < 0)
+	    kd = k - 1;
+	  Wdt = Count * WordLog;
+
+
+	  if (Anding)
+	    {
+	      while (next && next->DocNum < CurrDocNum - 1)
+		next = SP_get_next (Doc_Set);
+	      if (next && next->DocNum == CurrDocNum - 1)
+		{
+		  next->Sum += Wqt * Wdt;
+		  total_added += Wqt * Wdt;
+		  hits++;
+		}
+	      if (next && next->DocNum <= CurrDocNum - 1)
+		next = SP_get_next (Doc_Set);
+	      goto NextDocNum;
+	    }
+	  ent.DocNum = CurrDocNum - 1;
+	  if ((mem = SP_member (&ent, Doc_Set)) == NULL)
+	    {
+	      hits++;
+	      if (rqi->MaxAccums != -1 &&
+		  Doc_Set->no_of_items >= rqi->MaxAccums &&
+		  !MoreAccum)
+		goto NextDocNum;
+	      if ((mem = get_ide (qd, pool)) == NULL)
+		return NULL;
+	      ChangeMemInUse (qd, sizeof (*mem));
+	      mem->DocNum = CurrDocNum - 1;
+	      mem->Sum = 0;
+	      ChangeMemInUse (qd, -Doc_Set->mem_in_use);
+	      if (SP_insert (mem, Doc_Set) == NULL)
+		{
+		  Message ("Unable to allocate memory for a splay node");
+		  Abort = 1;
+		  goto FastAbort;
+		}
+	      ChangeMemInUse (qd, Doc_Set->mem_in_use);
+	      if (rqi->MaxAccums != -1 &&
+		  Doc_Set->no_of_items >= rqi->MaxAccums &&
+		  rqi->StopAtMaxAccum)
+		Abort = 1;
+	    }
+
+	  mem->Sum += Wqt * Wdt;
+	  total_added += Wqt * Wdt;
+
+	NextDocNum:
+	  ;
+	}
+
+    FastAbort:
+
+      DECODE_DONE
+
+	Xfree (buffer);
+      ChangeMemInUse (qd, -we->invf_len);
+      if (sk)
+	{
+	  if (qd->id->ifh.skip_mode != 0)
+	    fprintf (sk, " %6d", skips_taken);
+	  fprintf (sk, " %6d %6d %6d%12.2f\n",
+		   ptrs_dec, hits, Doc_Set->no_of_items, total_added);
+	}
+      if (Abort)
+	break;
+    }
+  if (sk)
+    fclose (sk);
+  return Doc_Set;
+}
+
+
+
+
+Hash_Table *
+CosineDecodeHash (query_data * qd, TermList * Terms,
+		  RankedQueryInfo * rqi)
+{
+  int j, num_docs, num_terms, MoreAccum;
+  Hash_Table *HT;
+  int Anding = 0;
+  int Sorted = 0;
+  FILE *sk = open_skip (rqi->skip_dump);
+  u_char indent = 0;
+  if (sk)
+    {
+      if (!skip_header)
+	{
+	  skip_header = 1;
+	  fprintf (sk, "%s", skip_header_text[qd->id->ifh.skip_mode]);
+	  fprintf (sk, "\nSkipping method %ld\n", qd->id->ifh.skip_mode);
+	  switch (qd->id->ifh.skip_mode)
+	    {
+	    case 1:
+	      fprintf (sk, "Skipping is every %ld docnums\n",
+		       qd->id->ifh.params[0]);
+	      break;
+	    case 2:
+	      fprintf (sk, "Max nodes = %ld\nNo skips smaller or equal to %ld\n",
+		       qd->id->ifh.params[0], qd->id->ifh.params[1]);
+	      break;
+	    }
+	}
+      fprintf (sk, "\n\t\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      for (j = 0; j < Terms->num; j++)
+	if (Terms->TE[j].Word[0] > indent)
+	  indent = Terms->TE[j].Word[0];
+    }
+
+  num_docs = qd->sd->sdh.num_of_docs;
+  /* Create the array for the documents */
+  HT = HT_create (qd, rqi->MaxAccums, rqi->HashTblSize);
+  if (!HT)
+    return NULL;
+
+  if (rqi->MaxTerms == -1)
+    num_terms = Terms->num;
+  else
+    num_terms = rqi->MaxTerms < Terms->num ? rqi->MaxTerms : Terms->num;
+
+  MoreAccum = 1;
+
+  /* For each word in the list . . . */
+  for (j = 0; j < num_terms; j++)
+    {
+      int Abort = 0;
+      u_char *buffer;
+      int pos;
+      int i;
+      unsigned long CurrDocNum = 0;	/* NOTE: Document numbers start at 1 */
+      double Wqt, WordLog;
+      int dn_blk = 0, len_blk = 0, k, kd;
+      int blk;			/* bblock control parameter */
+      int p;			/* The number of documents the word occurs in */
+      WordEntry *we = &Terms->TE[j].WE;
+      Invf_Doc_EntryH *next = NULL;
+      int next_i, next_doc_num, next_start;
+      int skips_taken = 0, ptrs_dec = 0;
+      double total_added = 0.0;
+      int hits = 0;
+      if (sk)
+	{
+	  fprintf (sk, "%3d : %8ld %6ld \"%.*s\"%*s", j, we->count,
+		 we->doc_count, Terms->TE[j].Word[0], Terms->TE[j].Word + 1,
+		   indent - Terms->TE[j].Word[0], "");
+	}
+
+      CalcBlks (qd, we, &blk, &dn_blk, &len_blk, &k, &p);
+      if (!(buffer = Xmalloc (we->invf_len)))
+	break;
+
+      ChangeMemInUse (qd, we->invf_len);
+
+      if (sk && qd->id->ifh.skip_mode == 2)
+	fprintf (sk, "%4d", k);
+
+      Fseek (qd->File_invf, we->invf_ptr, 0);
+      Fread (buffer, sizeof (char), we->invf_len, qd->File_invf);
+
+      /* Read from the buffer */
+      DECODE_START (buffer, we->invf_len);
+
+      WordLog = log ((double) num_docs / (double) we->max_doc_count);  /* [RPAP - Jan 97: Stem Index Change] */
+
+      Wqt = (rqi->QueryFreqs ? Terms->TE[j].Count : 1) * WordLog;
+
+      qd->num_of_terms++;
+
+      if (rqi->MaxAccums != -1 && HT->num >= rqi->MaxAccums)
+	{
+	  MoreAccum = 0;
+	  Anding = 1;
+	  if (!Sorted)
+	    {
+	      HT_sort (qd, HT);
+	      Sorted = 1;
+	    }
+	  next = HT->Head;
+	}
+      i = pos = 0;
+      next_i = next_start = next_doc_num = 0;
+      kd = k - 1;
+      while (i < p && (!Anding || next))
+	{
+	  int Count;		/* The number of times the occurs in a document */
+	  double Wdt;
+	  unsigned long diff;
+	  Invf_Doc_Entry *mem;
+	  if (k)
+	    {
+	      if (Anding && i + k < p && next->IDE.DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		{
+#ifdef SHORT_CUT
+		ShortCut:
+#endif
+		  i = next_i;
+		  DECODE_SEEK (next_start);
+		  pos = next_start;
+		  CurrDocNum = next_doc_num;
+		  qd->hops_taken++;
+		  skips_taken++;
+		  kd = k - 1;
+		}
+	      if (kd == k - 1)
+		{
+		  if (i + k < p)
+		    {
+		      int doc_diff, pos_diff;
+		      BBLOCK_DECODE_L (doc_diff, dn_blk, pos);
+		      next_doc_num += doc_diff;
+		      next_i += k;
+		      BBLOCK_DECODE_L (pos_diff, len_blk, pos);
+		      next_start = pos + pos_diff;
+		    }
+		  else
+		    next_doc_num = -1;
+		}
+#ifdef SHORT_CUT
+	      if (Anding && i + k < p && next->IDE.DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		goto ShortCut;
+#else
+	      if (Anding && i + k < p && next->IDE.DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		continue;
+#endif
+	    }
+	  if (k && kd == 0 && i != p - 1)
+	    {
+	      CurrDocNum = next_doc_num;
+	    }
+	  else
+	    {
+	      BBLOCK_DECODE_L (diff, blk, pos);
+	      CurrDocNum += diff;
+	    }
+	  qd->num_of_ptrs++;
+	  ptrs_dec++;
+	  if (qd->id->ifh.InvfLevel >= 2)
+	    GAMMA_DECODE_L (Count, pos);
+	  else
+	    Count = (double) we->count / p;
+	  i++;
+	  kd--;
+	  if (kd < 0)
+	    kd = k - 1;
+
+
+	  if (Anding)
+	    {
+	      while (next && next->IDE.DocNum < CurrDocNum - 1)
+		next = next->next;
+	      if (next && next->IDE.DocNum == CurrDocNum - 1)
+		{
+		  Wdt = Count * WordLog;
+		  next->IDE.Sum += Wqt * Wdt;
+		  total_added += Wqt * Wdt;
+		  hits++;
+		}
+	      if (next && next->IDE.DocNum <= CurrDocNum - 1)
+		next = next->next;
+	      goto NextDocNum;
+	    }
+	  if ((mem = HT_find (HT, CurrDocNum - 1)) == NULL)
+	    {
+	      if (rqi->MaxAccums != -1 && HT->num >= rqi->MaxAccums &&
+		  !MoreAccum)
+		goto NextDocNum;
+	      Wdt = Count * WordLog;
+	      if (!HT_insert (HT, CurrDocNum - 1, Wqt * Wdt))
+		{
+		  if (!HT->Suplimentary_Entries)
+		    HT_Sup_create (qd, HT, we->doc_count - i + 1);
+		  HT_Sup_Add (HT, CurrDocNum - 1, Wqt * Wdt);
+		}
+	      if (rqi->MaxAccums != -1 && HT->num >= rqi->MaxAccums &&
+		  rqi->StopAtMaxAccum)
+		Abort = 1;
+	    }
+	  else
+	    {
+	      Wdt = Count * WordLog;
+	      mem->Sum += Wqt * Wdt;
+	      total_added += Wqt * Wdt;
+	      hits++;
+	    }
+
+	NextDocNum:
+	  ;
+	}
+
+      DECODE_DONE
+
+	Xfree (buffer);
+      ChangeMemInUse (qd, -we->invf_len);
+      if (sk)
+	{
+	  if (qd->id->ifh.skip_mode != 0)
+	    fprintf (sk, " %6d", skips_taken);
+	  fprintf (sk, " %6d %6d %6d%12.2f\n",
+	       ptrs_dec, hits, HT->num + HT->Suplimentary_Num, total_added);
+	}
+      if (Abort)
+	break;
+    }
+  if (sk)
+    fclose (sk);
+  if (!HT->Head)
+    HT_sort (qd, HT);
+  return HT;
+}
+
+
+
+List_Table *
+CosineDecodeList (query_data * qd, TermList * Terms,
+		  RankedQueryInfo * rqi)
+{
+  int j, num_docs, num_terms, MoreAccum;
+  List_Table *LT;
+  int Anding = 0;
+  int Sorted = 0;
+  int has_grown = 0; /* [RJM 07/97: Ranked Required Match] whether elements have been added lately */
+  FILE *sk = open_skip (rqi->skip_dump);
+  u_char indent = 0;
+  if (sk)
+    {
+      if (!skip_header)
+	{
+	  skip_header = 1;
+	  fprintf (sk, "%s", skip_header_text[qd->id->ifh.skip_mode]);
+	  fprintf (sk, "\nSkipping method %ld\n", qd->id->ifh.skip_mode);
+	  switch (qd->id->ifh.skip_mode)
+	    {
+	    case 1:
+	      fprintf (sk, "Skipping is every %ld docnums\n",
+		       qd->id->ifh.params[0]);
+	      break;
+	    case 2:
+	      fprintf (sk, "Max nodes = %ld\nNo skips smaller or equal to %ld\n",
+		       qd->id->ifh.params[0], qd->id->ifh.params[1]);
+	      break;
+	    }
+	}
+      fprintf (sk, "\n\t\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      for (j = 0; j < Terms->num; j++)
+	if (Terms->TE[j].Word[0] > indent)
+	  indent = Terms->TE[j].Word[0];
+    }
+
+  num_docs = qd->sd->sdh.num_of_docs;
+
+  /* Create the list for the documents */
+  LT = LT_create (qd, rqi->MaxAccums);
+  if (!LT)
+    return NULL;
+
+  if (rqi->MaxTerms == -1)
+    num_terms = Terms->num;
+  else
+    num_terms = rqi->MaxTerms < Terms->num ? rqi->MaxTerms : Terms->num;
+
+  MoreAccum = 1;
+
+  /* For each word in the list . . . */
+  for (j = 0; j < num_terms; j++)
+    {
+      int Abort = 0;
+      u_char *buffer;
+      int pos;
+      int i, n = 0;
+      unsigned long CurrDocNum = 0;	/* NOTE: Document numbers start at 1 */
+      unsigned long LastDocNum = 0;	/* [RJM 07/97: Ranked Required Match] */
+      int this_item = 0; /* [RJM 07/97: Ranked Required Match] points to the next possible LT to delete */
+      double Wqt, WordLog;
+      int dn_blk = 0, len_blk = 0, k, kd;
+      int blk;			/* bblock control parameter */
+      int p;			/* The number of documents the word occurs in */
+      int require_match = Terms->TE[j].require_match; /* [RJM 07/97: Ranked Required Match] */
+      WordEntry *we = &Terms->TE[j].WE;
+      Invf_Doc_Entry *next = NULL;
+      int next_i, next_doc_num, next_start;
+      int skips_taken = 0, ptrs_dec = 0;
+      double total_added = 0.0;
+      int hits = 0;
+
+      if (sk)
+	{
+	  fprintf (sk, "%3d : %8ld %6ld \"%.*s\"%*s", j, we->count,
+		 we->doc_count, Terms->TE[j].Word[0], Terms->TE[j].Word + 1,
+		   indent - Terms->TE[j].Word[0], "");
+	}
+
+      CalcBlks (qd, we, &blk, &dn_blk, &len_blk, &k, &p);
+      if (!(buffer = Xmalloc (we->invf_len)))
+	break;
+
+      ChangeMemInUse (qd, we->invf_len);
+
+      if (sk && qd->id->ifh.skip_mode == 2)
+	fprintf (sk, "%4d", k);
+
+      Fseek (qd->File_invf, we->invf_ptr, 0);
+      Fread (buffer, sizeof (char), we->invf_len, qd->File_invf);
+
+      /* Read from the buffer */
+      DECODE_START (buffer, we->invf_len);
+
+      WordLog = log ((double) num_docs / (double) we->max_doc_count);  /* [RPAP - Jan 97: Stem Index Change] */
+
+      Wqt = (rqi->QueryFreqs ? Terms->TE[j].Count : 1) * WordLog;
+
+      qd->num_of_terms++;
+
+      if (rqi->MaxAccums != -1 && LT->num >= rqi->MaxAccums)
+	MoreAccum = 0;
+
+      if ((rqi->MaxAccums != -1 && LT->num >= rqi->MaxAccums) || Anding)
+	{
+	  Anding = 1;
+	  if (!Sorted || has_grown)
+	    {
+	      LT_sort (LT);
+	      LT_pack (LT);
+	      Sorted = 1;
+	      has_grown = 0;
+	    }
+	  next = LT->IDE;
+	  n = 0;
+	}
+
+      /* [RJM 07/97: Ranked Required Terms] */
+      /* make sure that the document list is set up correctly for */
+      /* requiring a term to match */
+      if (require_match == 1) {
+	if (!Sorted || has_grown) {
+	  LT_sort (LT);
+	  LT_pack (LT);
+	  Sorted = 1;
+	  has_grown = 0;
+	}
+	/* this_item is already set to zero */
+      }
+
+      i = pos = 0;
+      next_i = next_start = next_doc_num = 0;
+      kd = k - 1;
+      while (i < p && (!Anding || n < LT->num))
+	{
+	  int Count;		/* The number of times the occurs in a document */
+	  double Wdt;
+	  unsigned long diff;
+	  Invf_Doc_Entry *mem;
+
+	  /* deal with skipping */
+	  if (k)
+	    {
+	      /* see if we should take a skip */
+	      if (Anding && i + k < p && next->DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		{
+		  i = next_i;
+		  DECODE_SEEK (next_start);
+		  pos = next_start;
+		  CurrDocNum = next_doc_num;
+		  qd->hops_taken++;
+		  skips_taken++;
+		  kd = k - 1;
+		}
+	      if (kd == k - 1)
+		if (i + k < p)
+		  {
+		    int doc_diff, pos_diff;
+		    BBLOCK_DECODE_L (doc_diff, dn_blk, pos);
+		    next_doc_num += doc_diff;
+		    next_i += k;
+		    BBLOCK_DECODE_L (pos_diff, len_blk, pos);
+		    next_start = pos + pos_diff;
+		  }
+		else
+		  next_doc_num = -1;
+
+	      /* should we take another skip? */
+	      if (Anding && i + k < p && next->DocNum > next_doc_num &&
+		  next_doc_num >= 0)
+		continue;
+	    }
+	  if (k && kd == 0 && i != p - 1)
+	    CurrDocNum = next_doc_num;
+	  else
+	    {
+	      BBLOCK_DECODE_L (diff, blk, pos);
+	      CurrDocNum += diff;
+	    }
+	  qd->num_of_ptrs++;
+	  ptrs_dec++;
+	  if (qd->id->ifh.InvfLevel >= 2)
+	    GAMMA_DECODE_L (Count, pos);
+	  else
+	    Count = (double) we->count / p;
+	  i++;
+	  kd--;
+	  if (kd < 0)
+	    kd = k - 1;
+	  Wdt = Count * WordLog;
+
+	  /* [RJM 07/97: Ranked Required Terms] */
+	  /* if this is a required match we must remove all accumulators */
+	  /* of intermediate documents */
+	  if (require_match == 1) {
+	    while (this_item < LT->num && LT->IDE[this_item].DocNum < CurrDocNum - 1) {
+	      if (LastDocNum==0 ||(LT->IDE[this_item].DocNum > LastDocNum - 1))
+		LT->IDE[this_item].Sum = -1000.0; /* mark for deletion */
+	      this_item++;
+	    }
+	  }
+
+	  if (Anding)
+	    {
+	      while (n < LT->num && next->DocNum < CurrDocNum - 1)
+		{
+		  next++;
+		  n++;
+		}
+	      if (n < LT->num && next->DocNum == CurrDocNum - 1)
+		{
+		  next->Sum += Wqt * Wdt;
+		  total_added += Wqt * Wdt;
+		  hits++;
+		}
+	      if (n < LT->num && next->DocNum <= CurrDocNum - 1)
+		{
+		  next++;
+		  n++;
+		}
+	      goto NextDocNum;
+	    }
+	  if (!Anding)
+	    {
+	      if (rqi->MaxAccums != -1 && LT->num >= rqi->MaxAccums &&
+		  !MoreAccum)
+		goto NextDocNum;
+	      LT = LT_add (qd, LT, CurrDocNum - 1, Wqt * Wdt);
+	      if (!LT)
+		{
+		  Abort = 1;
+		  goto FastAbort;
+		}
+
+	      /* the list of accumulators just got bigger */
+	      has_grown = 1;
+
+	      if (rqi->MaxAccums != -1 && LT->num >= rqi->MaxAccums &&
+		  rqi->StopAtMaxAccum)
+		Abort = 1;
+
+
+	    }
+	  else if ((mem = LT_find (LT, CurrDocNum - 1)))
+	    {
+	      mem->Sum += Wqt * Wdt;
+	      total_added += Wqt * Wdt;
+	      hits++;
+	    }
+
+	NextDocNum:
+
+	  LastDocNum = CurrDocNum;
+	}
+
+    FastAbort:
+
+      DECODE_DONE
+
+	Xfree (buffer);
+      ChangeMemInUse (qd, -we->invf_len);
+      if (sk)
+	{
+	  if (qd->id->ifh.skip_mode != 0)
+	    fprintf (sk, " %6d", skips_taken);
+	  fprintf (sk, " %6d %6d %6d%12.2f\n",
+		   ptrs_dec, hits, LT->num, total_added);
+	}
+      if (Abort)
+	break;
+
+      /* [RJM 07/97: Ranked Required Terms] */
+      /* remove the remaining accumulators if this term is required to match */
+      if (require_match == 1) {
+	while (this_item < LT->num) {
+	  if (LT->IDE[this_item].DocNum > LastDocNum - 1)
+	    LT->IDE[this_item].Sum = -1000.0; /* mark for deletion */
+	  this_item++;
+	}
+	Anding = 1; /* no more documents should be added */
+
+	/* Get rid of any documents which have been deleted */
+	LT_pack (LT);
+      }
+    }
+
+  if (sk)
+    fclose (sk);
+  return LT;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/invf_get.h	(revision 16561)
@@ -0,0 +1,103 @@
+/**************************************************************************
+ *
+ * invf_get.h -- Inverted file reading routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_INVF_GET
+#define H_INVF_GET
+
+typedef struct Invf_Doc_Entry
+  {
+    int DocNum;
+    float Sum;
+  }
+Invf_Doc_Entry;
+
+typedef struct Invf_Doc_Entry_Pool
+  {
+    void *pool;
+    unsigned pool_chunks;
+  }
+Invf_Doc_Entry_Pool;
+
+typedef struct Invf_Doc_EntryH
+  {
+    Invf_Doc_Entry IDE;
+    struct Invf_Doc_EntryH *next;
+  }
+Invf_Doc_EntryH;
+
+typedef struct Hash_Table
+  {
+    Invf_Doc_EntryH *Suplimentary_Entries;
+    int Suplimentary_Size;
+    int Suplimentary_Num;
+    unsigned max;
+    unsigned num;
+    unsigned size;
+    Invf_Doc_EntryH **HashTable;
+    Invf_Doc_EntryH *Head;
+    Invf_Doc_EntryH IDEH[1];
+  }
+Hash_Table;
+
+typedef struct List_Table
+  {
+    unsigned max;
+    unsigned num;
+    Invf_Doc_Entry IDE[1];
+  }
+List_Table;
+
+typedef enum
+  {
+    op_term, op_and_terms, op_and_or_terms, op_diff_terms
+  }
+Op_types;
+
+/*Invf_Doc_Entry *HT_find(Hash_Table *HT, unsigned long DN); */
+void HT_free (query_data * qd, Hash_Table * HT);
+void LT_free (query_data * qd, List_Table * LT);
+
+invf_data *InitInvfFile (File * InvfFile, stemmed_dict * sd);
+void FreeInvfData (invf_data * id);
+
+DocList *GetDocs (query_data * qd, WordEntry * we, BooleanQueryInfo * bqi);
+
+float *CosineDecode (query_data * qd, TermList * Terms, RankedQueryInfo * rqi);
+
+void free_ide_pool (query_data * qd, Invf_Doc_Entry_Pool * pool);
+
+Splay_Tree *CosineDecodeSplay (query_data * qd, TermList * Terms,
+			 RankedQueryInfo * rqi, Invf_Doc_Entry_Pool * pool);
+
+Hash_Table *CosineDecodeHash (query_data * qd, TermList * Terms,
+			      RankedQueryInfo * rqi);
+List_Table *CosineDecodeList (query_data * qd, TermList * Terms,
+			      RankedQueryInfo * rqi);
+
+
+DocList *GetDocsOp (query_data * qd, WordEntry * we, Op_types op, DocList * L);
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass1.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass1.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass1.c	(revision 16561)
@@ -0,0 +1,866 @@
+/**************************************************************************
+ *
+ * ivf.pass1.c -- Memory efficient pass 1 inversion
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include "local_strings.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "mg.h"
+#include "build.h"
+#include "locallib.h"
+#include "words.h"
+#include "stemmer.h"
+#include "hash.h"
+
+#include "longlong.h"
+
+/*
+   $Log$
+   Revision 1.2  2004/06/10 03:02:05  kjdon
+   fixed the bug that was causing it not to be able to create a second index using jni - basically had to reset all the static variables at the start of each pass. the tricky thing to find was the static variables in occur_to_lexical in ivf.pass2
+
+   Revision 1.1  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2001/09/21 12:46:42  kjm18
+   updated mg to be in line with mg_1.3f. Now uses long long for some variables
+   to enable indexing of very large collections.
+
+   Revision 1.1  1999/08/10 21:17:54  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1998/12/17 09:12:50  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:43  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:44  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.6  1995/01/16  03:57:05  tes
+   * Fixed bug for index_string_bytes which was calculated
+   * by adding the suffix lengths except for every lookback (block)
+   * number of words. This was incorrect as invf.dict is fully
+   * front coded - perhaps it was partially coded in the past.
+   * This would only affect a statistic - hence the reason why the
+   * bug could exist ;-)
+   *
+   * Revision 1.5  1994/11/29  00:31:59  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.3  1994/10/20  03:56:48  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:34  tes
+   * For version 1.1
+   *
+ */
+
+/*
+ *  Modified:
+ *   - long long bit counts for inverted file
+ *     (1999-08-05 Tim Bell <bhat@cs.mu.oz.au>)
+ */
+static char *RCSID = "$Id$";
+
+#define LOGLOOKBACK	2
+#define POOL_SIZE 1024*1024
+#define HASH_POOL_SIZE 8192
+#define INITIAL_HASH_SIZE 7927
+
+#define INIT_CHECK_FRAC 0.10
+#define CHECK_FRAC 0.75
+#define CHECK_CLOSE 0.999
+#define PARA_DIV 1.5
+#define NORM_DIV 1.0
+
+
+typedef struct hash_rec
+  {
+    unsigned long fcnt;		/* fragment frequency */
+    unsigned long lfcnt;	/* local fragment frequency */
+    unsigned long fnum;		/* last fragment to use stem */
+    unsigned long wcnt;		/* stem frequency */
+    unsigned long lwcnt;	/* local stem frequency */
+    u_char *word;
+  }
+hash_rec;
+
+static unsigned long words_read = 0, words_diff = 0, bytes_diff = 0;
+static unsigned long outputbytes = 0;
+static unsigned long inputbytes = 0;
+static unsigned long MaxMemInUse = 0;
+static unsigned long MemInUse = 0;
+static unsigned long ChunksWritten = 0;
+
+
+static FILE *ic;		/* The invf block file */
+static stdio_bitio_state sbs;
+
+static hash_rec **HashTable;
+static unsigned long HashSize=0;
+static unsigned long HashUsed=0;
+static u_char *Pool;
+static int PoolLeft=0;
+
+static hash_rec *hr_pool;
+static int hr_PoolLeft=0;
+
+static hash_rec **first_occr;
+static int max_first_occr=0;
+
+static mg_ullong L1_bits = 0;
+static mg_ullong L2_bits = 0;
+static mg_ullong L3_bits = 0;
+static unsigned long L1_ohead = 0;
+static unsigned long L2_ohead = 0;
+static unsigned long L3_ohead = 0;
+static unsigned long callnum = 0, lcallnum = 0, wordnum = 0, lwordnum = 0;
+static unsigned long ptrcnt = 0;
+static unsigned long checknum=0;
+static long max_mem = 0;
+
+
+static void 
+ChangeMem (int Change)
+{
+  MemInUse += Change;
+  if (MemInUse > MaxMemInUse)
+    MaxMemInUse = MemInUse;
+}
+
+void
+ResetStaticI1Vars () 
+{
+  words_read = 0;
+  words_diff = 0; 
+  bytes_diff = 0;
+  outputbytes = 0;
+  inputbytes = 0;
+  MaxMemInUse = 0;
+  MemInUse = 0;
+  ChunksWritten = 0;
+  
+  HashSize=0;
+  HashUsed=0;
+  PoolLeft=0;
+  
+  hr_PoolLeft=0;
+  
+  max_first_occr=0;
+  
+  L1_bits = 0;
+  L2_bits = 0;
+  L3_bits = 0;
+  L1_ohead = 0;
+  L2_ohead = 0;
+  L3_ohead = 0;
+  callnum = 0;
+  lcallnum = 0;
+  wordnum = 0;
+  lwordnum = 0;
+  ptrcnt = 0;
+  checknum=0;
+  max_mem = 0;
+  
+}
+int 
+init_ivf_1 (char *file_name)
+{
+  ResetStaticI1Vars();
+  if (!(ic = create_file (file_name, INVF_CHUNK_SUFFIX, "wb", MAGIC_CHUNK,
+			  MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+  fwrite ("    ", sizeof (u_long), 1, ic);	/* Space for the maxmem */
+  ENCODE_START (ic)
+    ENCODE_PAUSE (sbs)
+
+    if (!(Pool = Xmalloc (POOL_SIZE)))
+    {
+      Message ("Unable to allocate memory for pool");
+      return (COMPERROR);
+    }
+  PoolLeft = POOL_SIZE;
+  ChangeMem (POOL_SIZE);
+
+  if (!(hr_pool = Xmalloc (HASH_POOL_SIZE * sizeof (hash_rec))))
+    {
+      Message ("Unable to allocate memory for pool");
+      return (COMPERROR);
+    }
+  hr_PoolLeft = HASH_POOL_SIZE;
+  ChangeMem (HASH_POOL_SIZE * sizeof (hash_rec));
+
+  HashSize = INITIAL_HASH_SIZE;
+  HashUsed = 0;
+  if (!(HashTable = Xmalloc (sizeof (hash_rec *) * HashSize)))
+    {
+      Message ("Unable to allocate memory for table");
+      return (COMPERROR);
+    }
+  ChangeMem (sizeof (hash_rec *) * HashSize);
+  bzero ((char *) HashTable, sizeof (hash_rec *) * HashSize);
+  max_first_occr = 8192;
+  if (!(first_occr = Xmalloc (sizeof (hash_rec *) * max_first_occr)))
+    {
+      Message ("Unable to allocate memory for first_occur");
+      return (COMPERROR);
+    }
+  ChangeMem (sizeof (hash_rec *) * max_first_occr);
+
+  checknum = (invf_buffer_size * INIT_CHECK_FRAC) /
+    (InvfLevel == 3 ? PARA_DIV : NORM_DIV);
+
+  return (COMPALLOK);
+}
+
+
+
+static unsigned long 
+mem_reqd (void)
+{
+  register int i;
+  register unsigned long total = 0;
+/*  register unsigned long N = InvfLevel == 3 ? lwordnum : lcallnum; */
+  register unsigned long N = lcallnum;
+  for (i = 0; i < HashUsed; i++)
+    {
+      register hash_rec *ent = first_occr[i];
+/*      register unsigned long p = InvfLevel == 3 ? ent->lwcnt : ent->lfcnt; */
+      register unsigned long p = ent->lfcnt;
+      if (p)
+	total += BIO_Bblock_Bound (N, p);
+      if (InvfLevel >= 2)
+	total += ent->lwcnt;
+    }
+
+  total = (total + 7) >> 3;
+  return total;
+}
+
+static unsigned long 
+max_mem_reqd (void)
+{
+  register int i;
+  register unsigned long total = 0;
+/*  register unsigned long N = InvfLevel == 3 ? wordnum : callnum; */
+  register unsigned long N = callnum;
+  for (i = 0; i < HashUsed; i++)
+    {
+      register hash_rec *ent = first_occr[i];
+/*      register unsigned long p = InvfLevel == 3 ? ent->wcnt : ent->fcnt; */
+      register unsigned long p = ent->fcnt;
+      if (p)
+	total += BIO_Bblock_Bound (N, p);
+      if (InvfLevel >= 2)
+	total += ent->wcnt;
+    }
+
+  total = (total + 7) >> 3;
+  return total;
+}
+
+
+static void 
+dump_dict (unsigned long mem)
+{
+  int i;
+
+  ChunksWritten++;
+
+  ENCODE_CONTINUE (sbs)
+
+    GAMMA_ENCODE (lcallnum + 1);
+  if (mem > max_mem)
+    max_mem = mem;
+  GAMMA_ENCODE (mem + 1);
+  lwordnum = lcallnum = 0;
+  GAMMA_ENCODE (HashUsed + 1);
+  for (i = 0; i < HashUsed; i++)
+    {
+      hash_rec *ent = first_occr[i];
+      GAMMA_ENCODE (ent->lwcnt + 1);
+      if (ent->lwcnt >= 2)
+	GAMMA_ENCODE (ent->lfcnt);
+      ent->lwcnt = ent->lfcnt = 0;
+    }
+  ptrcnt = 0;
+  ENCODE_PAUSE (sbs)
+}
+
+
+
+static int 
+process_doc (u_char * s_in, int l_in)
+{
+  u_char *end = s_in + l_in - 1;
+
+  callnum++;
+  lcallnum++;
+  inputbytes += l_in;
+
+  if (!inaword (s_in, end))
+    if (SkipSGML)
+      PARSE_NON_STEM_WORD_OR_SGML_TAG (s_in, end);
+    else
+      PARSE_NON_STEM_WORD (s_in, end);
+  /*
+   ** Alternately parse off words and non-words from the input
+   ** stream beginning with a non-word. Each token is then
+   ** inserted into the set if it does not exist or has it's
+   ** frequency count incremented if it does.
+   */
+  while (s_in <= end)
+    {
+      u_char Word[MAXSTEMLEN + 1];
+
+      PARSE_STEM_WORD (Word, s_in, end);
+      
+      stemmer (stem_method, stemmer_num, Word);
+      if (SkipSGML)
+	PARSE_NON_STEM_WORD_OR_SGML_TAG (s_in, end);
+      else
+	PARSE_NON_STEM_WORD (s_in, end);
+
+      words_read++;
+      wordnum++;
+      lwordnum++;
+      /* Search the hash table for Word */
+      {
+	register unsigned long hashval, step;
+	register int hsize = HashSize;
+	HASH (hashval, step, Word, hsize);
+	for (;;)
+	  {
+	    register u_char *s1;
+	    register u_char *s2;
+	    register int len;
+	    register hash_rec *ent;
+	    ent = HashTable[hashval];
+	    if (!ent)
+	      {
+		int len = *Word + 1;
+		if (!hr_PoolLeft)
+		  {
+		    if (!(hr_pool = Xmalloc (HASH_POOL_SIZE *
+					     sizeof (hash_rec))))
+		      {
+			Message ("Unable to allocate memory for pool");
+			return (COMPERROR);
+		      }
+		    hr_PoolLeft = HASH_POOL_SIZE;
+		    ChangeMem (HASH_POOL_SIZE * sizeof (hash_rec));
+		  }
+		ent = hr_pool++;
+		hr_PoolLeft--;
+		if (len > PoolLeft)
+		  {
+		    if (!(Pool = Xmalloc (POOL_SIZE)))
+		      {
+			Message ("Unable to allocate memory for pool");
+			return (COMPERROR);
+		      }
+		    PoolLeft = POOL_SIZE;
+		    ChangeMem (POOL_SIZE);
+		  }
+		ent->wcnt = 1;
+		ent->fcnt = 1;
+		ent->lwcnt = 1;
+		ent->lfcnt = 1;
+		ent->fnum = callnum;
+		ent->word = Pool;
+		memcpy (Pool, Word, len);
+		Pool += len;
+		PoolLeft -= len;
+		if (HashUsed == max_first_occr - 1)
+		  {
+		    ChangeMem (-sizeof (hash_rec *) * max_first_occr);
+		    max_first_occr *= 2;
+		    if (!(first_occr = Xrealloc (first_occr, sizeof (hash_rec *) *
+						 max_first_occr)))
+		      {
+			Message ("Unable to allocate memory for first_occr");
+			return (COMPERROR);
+		      }
+		    ChangeMem (sizeof (hash_rec *) * max_first_occr);
+		  }
+		first_occr[HashUsed] = ent;
+		HashUsed++;
+		HashTable[hashval] = ent;
+		bytes_diff += Word[0];
+		break;
+	      }
+
+	    /* Compare the words */
+	    s1 = Word;
+	    s2 = ent->word;
+	    len = *s1 + 1;
+	    for (; len; len--)
+	      if (*s1++ != *s2++)
+		break;
+
+	    if (len)
+	      {
+		hashval = (hashval + step);
+		if (hashval >= hsize)
+		  hashval -= hsize;
+	      }
+	    else
+	      {
+		ent->wcnt++;
+		ent->lwcnt++;
+		if (callnum > ent->fnum)
+		  {
+		    ptrcnt++;
+		    ent->fcnt++;
+		    ent->lfcnt++;
+		    ent->fnum = callnum;
+		  }
+		break;
+	      }
+	  }
+      }
+
+      if (HashUsed >= HashSize >> 1)
+	{
+	  hash_rec **ht;
+	  unsigned long size;
+	  unsigned long i;
+	  size = prime (HashSize * 2);
+	  if (!(ht = Xmalloc (sizeof (hash_rec *) * size)))
+	    {
+	      Message ("Unable to allocate memory for table");
+	      return (COMPERROR);
+	    }
+	  bzero ((char *) ht, sizeof (hash_rec *) * size);
+	  ChangeMem (sizeof (hash_rec *) * size);
+
+	  for (i = 0; i < HashSize; i++)
+	    if (HashTable[i])
+	      {
+		register u_char *wptr;
+		hash_rec *ent;
+		register unsigned long hashval, step;
+
+		wptr = HashTable[i]->word;
+		HASH (hashval, step, wptr, size);
+		ent = ht[hashval];
+		while (ent)
+		  {
+		    hashval += step;
+		    if (hashval >= size)
+		      hashval -= size;
+		    ent = ht[hashval];
+		  }
+		ht[hashval] = HashTable[i];
+	      }
+	  Xfree (HashTable);
+	  ChangeMem (-sizeof (hash_rec *) * HashSize);
+	  HashTable = ht;
+	  HashSize = size;
+	}
+
+    }
+
+  if (ptrcnt >= checknum)
+    {
+      unsigned long mem;
+      /*fprintf(stderr, "Checking at %u . . . ", ptrcnt); */
+      mem = mem_reqd ();
+      if (mem >= invf_buffer_size * CHECK_CLOSE)
+	{
+	  /*fprintf(stderr, "Got a match\n"); */
+	  dump_dict (mem);
+	  checknum = (invf_buffer_size * INIT_CHECK_FRAC) /
+	    (InvfLevel == 3 ? PARA_DIV : NORM_DIV);
+	}
+      else
+	{
+	  checknum = checknum * ((CHECK_FRAC * (invf_buffer_size - mem)) / mem) +
+	    checknum;
+	  if (checknum <= ptrcnt)
+	    checknum = ptrcnt + 1;
+	  /*fprintf(stderr, "Next check at %u\n", checknum); */
+	}
+    }
+  return (COMPALLOK);
+
+}				/* encode */
+
+
+int 
+process_ivf_1 (u_char * s_in, int l_in)
+{
+  if (InvfLevel <= 2)
+    return process_doc (s_in, l_in);
+  else
+    {
+      int pos = 0;
+      u_char *start = s_in;
+      while (pos < l_in)
+	{
+	  if (s_in[pos] == TERMPARAGRAPH)
+	    {
+	      int len = pos + s_in + 1 - start;
+	      if (process_doc (start, len) != COMPALLOK)
+		return (COMPERROR);
+	      start = s_in + pos + 1;
+	    }
+	  pos++;
+	}
+      if (start < s_in + pos)
+	return process_doc (start, pos + s_in - start);
+    }
+  return COMPALLOK;
+}
+
+static int 
+PackHashTable (void)
+{
+  int s, d;
+  for (s = d = 0; s < HashSize; s++)
+    if (HashTable[s])
+      HashTable[d++] = HashTable[s];
+  ChangeMem (-sizeof (hash_rec *) * HashSize);
+  ChangeMem (sizeof (hash_rec *) * HashUsed);
+  if (!(HashTable = Xrealloc (HashTable, sizeof (hash_rec *) * HashUsed)))
+    {
+      Message ("Out of memory");
+      return COMPERROR;
+    }
+  HashSize = HashUsed;
+  return COMPALLOK;
+}
+
+
+
+
+
+static int 
+ent_comp (const void *A, const void *B)
+{
+  u_char *s1 = (*((hash_rec **) A))->word;
+  u_char *s2 = (*((hash_rec **) B))->word;
+
+  return (casecompare (s1, s2)); /* [RPAP - Jan 97: Stem Index Change] */
+}				/* stem_comp */
+
+
+
+
+
+/*
+ * void count_text()
+ *
+ * The maths used in this function is described in the paper "Coding for 
+ * Compression in Full-Text Retrieval Systems"
+ *
+ */
+static void 
+count_text ()
+{
+  int i;
+  mg_ullong oldL12_bits = 0;
+  for (i = 0; i < HashUsed; i++)
+    {
+      hash_rec *wrd = HashTable[i];
+      /* estimate size of a level 1 inverted file */
+      L1_bits += (int) (0.99 + wrd->fcnt * (1.6 + log2 (1.0 * callnum / wrd->fcnt)));
+      L1_ohead += BIO_Gamma_Length (wrd->fcnt);
+
+      L2_bits += BIO_Unary_Length (wrd->wcnt);
+      L2_ohead += BIO_Gamma_Length (wrd->wcnt - wrd->fcnt + 1);
+
+      L3_bits += (int) (0.99 + wrd->wcnt *
+		  (1.6 + log2 (1.0 * words_read / (wrd->wcnt + callnum))));
+      L3_ohead += 0;
+
+      /* check for overflow */
+      if (L1_bits + L2_bits < oldL12_bits) {
+	fprintf(stderr, "ERROR: Inverted file size will probably overflow %d byte unsigned integer\n", sizeof (mg_ullong));
+	fprintf(stderr, "       counter in pass 2.\n");
+	if (sizeof (mg_ullong) < 8) {
+	  fprintf(stderr, "       Try compiling with GCC to enable use of 8 bytes for this counter.\n");
+	}
+	fprintf(stderr, "       Build aborted.\n");
+	exit(1);
+      }
+      oldL12_bits = L1_bits + L2_bits;
+    }
+  L3_bits = (L3_bits + L2_bits + L1_bits + 7) / 8;
+  L3_ohead = (L3_ohead + L2_ohead + L1_ohead + 7) / 8;
+  L2_bits = (L2_bits + L1_bits + 7) / 8;
+  L2_ohead = (L2_ohead + L1_ohead + 7) / 8;
+  L1_bits = (L1_bits + 7) / 8;
+  L1_ohead = (L1_ohead + 7) / 8;
+}				/* count_text */
+
+
+
+/*
+ *    write_stem_file():
+ *              writes out the stemmed dictionary file
+ *              in the following format 
+ *                      lookback value (int)
+ *                      totalbytes value (int)
+ *                      indexstringbytes (int)
+ *                      for each word 
+ *                        wordlen (4 bits)
+ *                        prefix match (4 bits)
+ *                        word (wordlen bytes)
+ *                        word frequency (int)
+ *                        word count (int)
+ *
+ *      Accesses outside variables:     
+ *
+ *      Return value...:                
+ */
+
+static void 
+write_stem_file (char *file_name)
+{
+  long j;
+  struct invf_dict_header idh;
+  long lookback = (1 << LOGLOOKBACK);	/* ???? */
+  long totalbytes = 0;		/* The sum of the length of all words, including 
+				   the length byte */
+  long indexstringbytes = 0;	/* The amount of space required to store the 
+				   words in the diction, this takes into account 
+				   the prefixes */
+  u_char *lastword = NULL;	/* A pointer to the last word processed */
+  FILE *sp;
+
+
+  /* Calculate the size of certain things */
+  for (j = 0; j < HashSize; j++)
+    {
+      u_char *word = HashTable[j]->word;
+      indexstringbytes += word[0] + 2;
+      totalbytes += word[0] + 1;
+      if (lastword)
+	indexstringbytes -= prefixlen (lastword, word);
+      lastword = word;
+    }
+
+  lastword = NULL;
+
+  if (!(sp = create_file (file_name, INVF_DICT_SUFFIX, "wb", MAGIC_STEM_BUILD,
+			  MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL2(lookback, idh.lookback);
+  HTONUL2(HashSize, idh.dict_size);
+  HTONUL2(totalbytes, idh.total_bytes);
+  HTONUL2(indexstringbytes, idh.index_string_bytes);
+  HTOND2(inputbytes, idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  HTONUL2(callnum, idh.num_of_docs);
+  HTONUL2(callnum, idh.static_num_of_docs);
+  HTONUL2(words_read, idh.num_of_words);
+  HTONUL2(stemmer_num, idh.stemmer_num);
+  HTONUL2(stem_method, idh.stem_method);
+
+  fwrite ((char *) &idh, sizeof (idh), 1, sp);
+  outputbytes += sizeof (idh);
+
+  for (j = 0; j < HashSize; j++)
+    {
+      int i;
+      unsigned long wcnt, fcnt;  /* [RPAP - Jan 97: Endian Ordering] */
+      hash_rec *ent = HashTable[j];
+      if (lastword != NULL)
+	/* look for prefix match with prev string */
+	i = prefixlen (lastword, ent->word);
+      else
+	i = 0;
+      fputc (i, sp);
+      fputc (ent->word[0] - i, sp);
+      fwrite ((char *) ent->word + i + 1, sizeof (u_char), ent->word[0] - i, sp);
+      outputbytes += ent->word[0] - i + 1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL2(ent->fcnt, fcnt);
+      fwrite ((char *) &(fcnt), sizeof (fcnt), 1, sp);
+
+      outputbytes += sizeof (ent->fcnt);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL2(ent->wcnt, wcnt);
+      fwrite ((char *) &(wcnt), sizeof (wcnt), 1, sp);
+
+      outputbytes += sizeof (ent->wcnt);
+      lastword = ent->word;
+    }
+
+  fclose (sp);
+}				/* write_stem_file() */
+
+
+
+/*
+ *    write_codes():
+ *              calls functions to assign and write out codes and
+ *              then prints out stats about execution results.
+ *
+ *      Accesses outside variables:     z
+ *
+ *      Return value...:                z
+ */
+
+static void 
+write_codes (char *file_name)
+{
+  unsigned long dicts = 0;
+  outputbytes = 0;
+  write_stem_file (file_name);
+  dicts = outputbytes;
+
+#ifndef SILENT
+  Message ("Chunks written      : %d\n",
+	   ChunksWritten);
+  if (InvfLevel == 3)
+    Message ("Paragraphs          : %8d\n", callnum);
+  Message ("Peak memory usage   : %10.1f Mb\n",
+	   (double) MaxMemInUse / 1024.0 / 1024.0);
+  Message ("Stems size          : %10.1f kB  %5.2f%%\n",
+	   (double) dicts / 1024, (double) 100 * dicts / inputbytes);
+  Message ("Lev 1 inverted file : %10.1f kB  %5.2f%%\n",
+	   (double) (L1_bits + L1_ohead) / 1024,
+	   (double) 100 * (L1_bits + L1_ohead) / inputbytes);
+  Message ("Lev 2 inverted file : %10.1f kB  %5.2f%%\n",
+	   (double) (L2_bits + L2_ohead) / 1024,
+	   (double) 100 * (L2_bits + L2_ohead) / inputbytes);
+  Message ("Lev 3 inverted file : %10.1f kB  %5.2f%%\n",
+	   (double) (L3_bits + L3_ohead) / 1024,
+	   (double) 100 * (L3_bits + L3_ohead) / inputbytes);
+  Message ("Record addresses    : %10.1f kB  %5.2f%%\n",
+	   (double) words_diff * 4 / 1024, (double) 100 * words_diff * 4 / inputbytes);
+#endif
+}				/* write_codes() */
+
+
+
+void 
+write_num_file (char *file_name)
+{
+
+  int i;
+  FILE *f;
+
+  for (i = 0; i < HashSize; i++)
+    HashTable[i]->fnum = i;
+
+  if (!(f = create_file (file_name, INVF_CHUNK_TRANS_SUFFIX, "wb",
+			 MAGIC_CHUNK_TRANS, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return;
+
+#if 1
+  ENCODE_START (f)
+
+    for (i = 0; i < HashSize; i++)
+    BINARY_ENCODE (first_occr[i]->fnum + 1, HashSize + 1);
+
+  ENCODE_DONE;
+#else
+  for (i = 0; i < HashSize; i++)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      unsigned long fnum;
+      HTONUL2(first_occur[i]->fnum, fnum);
+      fwrite ((char *) &fnum, sizeof (unsigned long), 1, f);
+    }
+#endif
+
+  fclose (f);
+}
+
+int 
+done_ivf_1 (char *FileName)
+{
+  char *temp_str = msg_prefix;
+  msg_prefix = "ivf.pass1";
+#ifndef SILENT
+  Message ("Mem reqd for 1 chunk: %8u bytes\n",
+	   max_mem_reqd ());
+#endif
+  if (lcallnum)
+    dump_dict (mem_reqd ());
+
+  if (PackHashTable () == COMPERROR)
+    return COMPERROR;
+
+  qsort (HashTable, HashUsed, sizeof (hash_rec *), ent_comp);
+
+  count_text ();
+  write_codes (FileName);
+
+  ENCODE_CONTINUE (sbs)
+    GAMMA_ENCODE (1);
+  ENCODE_DONE
+    fseek (ic, sizeof (long), 0);
+  
+  HTONSL(max_mem);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&max_mem, sizeof (max_mem), 1, ic);
+  NTOHSL(max_mem);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  fclose (ic);
+
+  write_num_file (FileName);
+
+  msg_prefix = temp_str;
+
+  /* free the memory */
+  Xfree(HashTable);
+  if (Pool) {
+    Pool -= (POOL_SIZE-PoolLeft);
+    Xfree(Pool);
+  }
+  if (hr_pool) {
+    hr_pool -= (HASH_POOL_SIZE-hr_PoolLeft);
+    Xfree(hr_pool);
+  }
+  if (first_occr) {
+    Xfree(first_occr);
+  }
+
+  return (COMPALLOK);
+}				/* done_encode */
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass2.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass2.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/ivf.pass2.c	(revision 16561)
@@ -0,0 +1,1191 @@
+/**************************************************************************
+ *
+ * ivf.pass2.c -- Memory efficient pass 2 inversion
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.3  2004/06/10 05:07:43  kjdon
+   have to declare vars before calling functions!!
+
+   Revision 1.2  2004/06/10 03:02:05  kjdon
+   fixed the bug that was causing it not to be able to create a second index using jni - basically had to reset all the static variables at the start of each pass. the tricky thing to find was the static variables in occur_to_lexical in ivf.pass2
+
+   Revision 1.1  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2001/09/21 12:46:42  kjm18
+   updated mg to be in line with mg_1.3f. Now uses long long for some variables
+   to enable indexing of very large collections.
+
+ * Revision 1.2  1997/08/02  05:01:57  wew
+ * changed literal values of 32 for the bit size of magic numbers of
+ * files to sizeof (unsigned long) * 8, increased the gap at the start
+ * of the invf during processing to 200 bytes
+
+   Revision 1.1  1999/08/10 21:17:54  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1998/12/17 09:12:51  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:43  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:45  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:49  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:35  tes
+   * For version 1.1
+   *
+ */
+
+/*
+ *  Modified:
+ *   - long long disk pointers and bit counts for inverted file
+ *     (1999-08-03 Tim Bell <bhat@cs.mu.oz.au>)
+ *     Code provided by Owen de Kretser <oldk@cs.mu.oz.au>
+ */
+
+static char *RCSID = "$Id$";
+
+#include "local_strings.h"
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "stemmer.h"
+#include "perf_hash.h"
+#include "bitio_m.h"
+#include "bitio_m_mems.h"
+#include "bitio_gen.h"
+#include "bitio_random.h"
+#include "bitio_stdio.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "locallib.h"
+#include "mg.h"
+#include "build.h"
+#include "words.h"
+#include "hash.h"
+
+#include "longlong.h"
+
+#ifdef USE_LONG_LONG
+#define BIO_Random_Seek_X BIO_Random_Seek_LL
+#define BIO_Random_Tell_X BIO_Random_Tell_LL
+#else
+#define BIO_Random_Seek_X BIO_Random_Seek
+#define BIO_Random_Tell_X BIO_Random_Tell
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+#include <io.h>
+#endif
+
+#ifndef RND_BUF_SIZE
+#define RND_BUF_SIZE 8*1024
+/*#define RND_BUF_SIZE 128 */
+#endif
+
+#define print_fsize(file)\
+do\
+  {\
+    struct stat file_state;\
+    fstat(fileno(invf_out), &file_state);\
+    Message("len(invf) = %ld", file_state.st_size);\
+  }while(0)
+
+typedef struct word_rec
+  {
+    unsigned long ptr;
+    unsigned long last;
+  }
+word_rec;
+
+typedef struct invf_state_rec
+  {
+    mg_ullong Disk_Ptr;
+    mg_ullong Disk_Last;
+    unsigned long Disk_B;
+  }
+invf_state_rec;
+
+typedef struct chunk
+  {
+    unsigned long start_doc;
+    unsigned long params_pos;
+    unsigned long disk_pos;
+    unsigned long N;
+  }
+chunk;
+
+
+static FILE *dict;		/* Stemmed dictionary file */
+static FILE *hash;		/* Stemmed dictionary hash file */
+static FILE *invf;		/* Inverted file */
+static FILE *invf_in;		/* Inverted file */
+static FILE *invf_out;		/* Inverted file */
+static FILE *invf_idx;		/* Inverted index file */
+static FILE *count;		/* Count file */
+static FILE *count_trans;	/* Count translation file */
+static FILE *invf_state;	/* Inverted file State */
+static FILE *chunk_state;	/* Chunk state */
+static FILE *chunks;		/* Chunk state */
+static FILE *invf_para = NULL;	/* Paragraph counts file */
+static FILE *weights = NULL;	/* Weights file */
+
+static stdio_bitio_state sbs;
+static random_bitio_state crbs;
+static chunk *chunk_data = NULL;
+static random_bitio_state rbs, rbsp;
+
+static int docs_left = 0, next_docs_left = 0;
+static unsigned long N;
+
+static word_rec *WordRecs;
+static u_char *lg_bs;
+static float *idf = NULL;
+
+static char *MemoryBuffer = NULL;
+static unsigned long MemBufSize;
+static unsigned long BufToUse;
+static struct invf_dict_header idh;
+
+static perf_hash_data *phd;
+
+static unsigned long *word_list = NULL;
+static unsigned long wl_size = 0;
+
+static unsigned long dict_size;
+static unsigned long no_of_ptrs = 0;
+static unsigned long chunks_read = 0;
+static unsigned long Disk_pos = 0;
+static unsigned long callnum = 0;
+static unsigned long wordnum = 0;
+
+static unsigned long totalIbytes = 0;
+static unsigned long totalDbytes = 0;
+static unsigned long totalHbytes = 0;
+
+static unsigned long MemInUse = 0;
+static unsigned long MaxMemInUse = 0;
+static unsigned long max_buffer_len;
+
+void 
+ChangeMemInUse (int mem)
+{
+  MemInUse += mem;
+  if (MemInUse > MaxMemInUse)
+    MaxMemInUse = MemInUse;
+}
+
+void 
+ResetStaticI2Vars() 
+{
+  docs_left = 0;
+  next_docs_left = 0;
+  N = 0;
+  MemBufSize=0;
+  BufToUse=0;
+  memset(&idh, 0, sizeof(idh));
+  wl_size = 0;
+  
+  dict_size = 0;
+  no_of_ptrs = 0;
+  chunks_read = 0;
+  Disk_pos = 0;
+  callnum = 0;
+  wordnum = 0;
+  
+  totalIbytes = 0;
+  totalDbytes = 0;
+  totalHbytes = 0;
+  
+  MemInUse = 0;
+  MaxMemInUse = 0;
+  max_buffer_len = 0;
+
+}
+
+
+static int 
+open_files (char *file_name)
+{
+  char FName[200];
+
+  if (!(dict = open_file (file_name, INVF_DICT_SUFFIX, "rb",
+			  MAGIC_STEM_BUILD, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+
+  if (!(hash = open_file (file_name, INVF_DICT_HASH_SUFFIX, "rb",
+			  MAGIC_HASH, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+
+  if (!(count = open_file (file_name, INVF_CHUNK_SUFFIX, "rb",
+			   MAGIC_CHUNK, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+  fread (&max_buffer_len, sizeof (max_buffer_len), 1, count);
+  NTOHUL(max_buffer_len);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  BIO_Stdio_Decode_Start (count, &sbs);
+  next_docs_left = BIO_Stdio_Gamma_Decode (&sbs, NULL) - 1;
+
+  if (!(count_trans = open_file (file_name, INVF_CHUNK_TRANS_SUFFIX, "rb",
+				 MAGIC_CHUNK_TRANS, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+
+  if (!(invf = create_file (file_name, INVF_SUFFIX, "w+b",
+			    MAGIC_INVF, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+  fflush (invf);
+  if (!(invf_in = open_file (file_name, INVF_SUFFIX, "rb",
+			     MAGIC_INVF, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+  if (!(invf_out = create_file (file_name, INVF_SUFFIX, "wb",
+				MAGIC_INVF, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+  BIO_Random_Start (invf, RND_BUF_SIZE, &rbs);
+  BIO_Random_Start (invf, RND_BUF_SIZE, &rbsp);
+  ChangeMemInUse (RND_BUF_SIZE * 2);
+
+  if (!(invf_idx = create_file (file_name, INVF_IDX_SUFFIX, "wb",
+				MAGIC_INVI, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return (COMPERROR);
+
+  if (InvfLevel == 3)
+    if (!(invf_para = create_file (file_name, INVF_PARAGRAPH_SUFFIX, "wb",
+				   MAGIC_PARAGRAPH, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+      return (COMPERROR);
+
+  sprintf (FName, FILE_NAME_FORMAT ".%ld", get_basepath (), file_name,
+	   ".invf.state", (long) getpid ());  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(invf_state = fopen (FName, "w+b")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      Message ("Unable to create \"%s\"", FName);
+      return (COMPERROR);
+    }
+  unlink (FName);
+
+  sprintf (FName, FILE_NAME_FORMAT ".%ld", get_basepath (), file_name,
+	   ".chunk.state", (long) getpid ());  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(chunk_state = fopen (FName, "w+b")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      Message ("Unable to create \"%s\"", FName);
+      return (COMPERROR);
+    }
+  unlink (FName);
+  BIO_Random_Start (chunk_state, RND_BUF_SIZE, &crbs);
+  ChangeMemInUse (RND_BUF_SIZE);
+
+  sprintf (FName, FILE_NAME_FORMAT ".%ld", get_basepath (), file_name,
+	   ".chunks", (long) getpid ());  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(chunks = fopen (FName, "w+b")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      Message ("Unable to create \"%s\"", FName);
+      return (COMPERROR);
+    }
+  unlink (FName);
+
+  return (COMPALLOK);
+}
+
+
+
+
+
+#define ISR_CACHE 1024
+#define ISR_ENTRY_SIZE (sizeof(mg_ullong)*2 + sizeof(unsigned long))
+
+invf_state_rec *
+in_cache (int pos)
+{
+  static char isr_data[ISR_CACHE * ISR_ENTRY_SIZE];
+  static invf_state_rec isr;
+  static int isr_base = 0, isr_num = -1, isr_pos = -1;
+  if (isr_pos >= 0)
+    bcopy ((char *) &isr, &isr_data[isr_pos * ISR_ENTRY_SIZE], ISR_ENTRY_SIZE);
+  if (pos < isr_base || pos >= isr_base + isr_num)
+    {
+      if (isr_num >= 0)
+	{
+	  fseek (invf_state, isr_base * ISR_ENTRY_SIZE, 0);
+	  fwrite (isr_data, 1, ISR_ENTRY_SIZE * isr_num, invf_state);
+	}
+      isr_base = pos;
+      fseek (invf_state, isr_base * ISR_ENTRY_SIZE, 0);
+      fread (isr_data, 1, ISR_ENTRY_SIZE * ISR_CACHE, invf_state);
+      isr_num = ISR_CACHE;
+    }
+  isr_pos = pos - isr_base;
+  bcopy (&isr_data[isr_pos * ISR_ENTRY_SIZE], (char *) &isr, ISR_ENTRY_SIZE);
+  return &isr;
+}
+
+
+
+
+
+unsigned long 
+occur_to_lexical (long occ, int clear_state)
+{
+  static long pos = -1;
+  static random_bitio_state rbs;
+  static int val = 0;
+  if (clear_state) {
+    pos = -1;
+    val = 0;
+    return 0;
+  }
+  if (pos == -1)
+    {
+      BIO_Random_Start (count_trans, RND_BUF_SIZE, &rbs);
+      pos = 0x7fffffff;
+    }
+  if (occ < pos)
+    {
+      if (occ == -1)
+	{
+	  BIO_Random_Done (&rbs);
+	  return 0;
+	}
+      BIO_Random_Seek_X (sizeof (unsigned long) * 8, &rbs);
+      pos = 0;
+    }
+  while (pos <= occ)
+    {
+      val = BIO_Random_Binary_Decode (dict_size + 1, &rbs, NULL) - 1;
+      pos++;
+    }
+  return (val);
+}
+
+
+void 
+add_chunk_state (unsigned long pos, unsigned long start_doc,
+		 unsigned long N)
+{
+  chunk_data[chunks_read].params_pos = pos;
+  chunk_data[chunks_read].start_doc = start_doc;
+  chunk_data[chunks_read].N = N;
+  chunks_read++;
+}
+
+
+int 
+init_ivf_2 (char *file_name)
+{
+  u_char prev[MAXSTEMLEN + 1];
+  int i;
+  mg_ullong totalIbits;
+  mg_ullong lasttotalIbits;
+  double logN = 0.0;
+
+  ResetStaticI2Vars(); /* clear the global statics */
+  occur_to_lexical(0, 1); /* clear the statics in here*/
+
+  if (open_files (file_name) == COMPERROR)
+    return COMPERROR;
+
+
+  /* Read in the stemmed dictionary file header */
+  fread ((char *) &idh, sizeof (idh), 1, dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  dict_size = idh.dict_size;
+
+  N = idh.num_of_docs;
+
+  if (!(phd = read_perf_hash_data (hash)))
+    {
+      Message ("Unable to read in hash data");
+      return COMPERROR;
+    }
+  totalHbytes = sizeof (perf_hash_data) + sizeof (u_char) * 256 +
+    sizeof (int) * (phd->MAX_N + 1) + sizeof (int *) * 3 * phd->MAX_CH +
+    sizeof (long) * phd->MAX_CH * phd->MAX_L;
+
+  if (!(WordRecs = Xmalloc (sizeof (word_rec) * idh.dict_size)))
+    {
+      Message ("No memory for word entries");
+      return COMPERROR;
+    }
+  totalDbytes += sizeof (word_rec) * idh.dict_size;
+  /* separate storage for the log(b) values, one byte each */
+  if (!(lg_bs = Xmalloc (sizeof (u_char) * idh.dict_size)))
+    {
+      Message ("No memory for lg b's");
+      return COMPERROR;
+    }
+  totalDbytes += sizeof (u_char) * idh.dict_size;
+
+  if (MakeWeights)
+    {
+      /* separate storage for the idf values, one single each */
+      if (!(idf = Xmalloc (sizeof (float) * idh.dict_size)))
+	{
+	  Message ("No memory for idf's");
+	  return COMPERROR;
+	}
+      totalDbytes += sizeof (float) * idh.dict_size;
+
+      if (!(weights = create_file (file_name, WEIGHTS_SUFFIX, "wb",
+				 MAGIC_WGHT, MG_CONTINUE))) { /* [RPAP - Feb 97: WIN32 Port] */
+	Message ("Couldn't open weights file for writing");
+	return (COMPERROR);
+      }
+    }
+  else
+    {
+      unlink (make_name (file_name, WEIGHTS_SUFFIX, NULL));
+    }
+
+  chunk_data = Xmalloc (sizeof (chunk) * (ChunkLimit + 2));
+  totalDbytes += sizeof (chunk) * (ChunkLimit + 2);
+
+  totalIbits = sizeof (unsigned long) * 8;		/* The magic number */
+  totalIbits += 8 * 200;	/* A 200 byte gap */
+
+  if (MakeWeights)
+    {
+      wl_size = 1024;
+      if (!(word_list = Xmalloc (sizeof (*word_list) * wl_size)))
+	{
+	  Message ("No memory for word_list");
+	  return COMPERROR;
+	}
+
+      logN = log ((double) N);
+    }
+
+  for (i = 0; i < idh.dict_size; i++)
+    {
+      invf_state_rec *isr;
+      register unsigned long copy, suff, p;
+      unsigned long fcnt, wcnt;
+
+      lasttotalIbits = totalIbits;
+      
+      copy = fgetc (dict);
+      suff = fgetc (dict);
+      *prev = copy + suff;
+      fread (prev + copy + 1, sizeof (u_char), suff, dict);
+
+      fread ((char *) &fcnt, sizeof (fcnt), 1, dict);
+      fread ((char *) &wcnt, sizeof (wcnt), 1, dict);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      WordRecs[i].last = 0;
+      WordRecs[i].ptr = 0;
+
+      p = fcnt;
+
+      if (MakeWeights)
+	idf[i] = logN - log ((double) fcnt);
+
+
+      isr = in_cache (i);
+
+      isr->Disk_Last = 0;
+      isr->Disk_Ptr = totalIbits;
+
+      isr->Disk_B = BIO_Bblock_Init (N, p);
+
+      totalIbits += BIO_Bblock_Bound_b (N, p, isr->Disk_B);
+
+      if (InvfLevel >= 2)
+	totalIbits += BIO_Gamma_Bound (wcnt, fcnt);
+
+#ifdef USE_LONG_LONG
+      totalIbits = (totalIbits + 7ull) & 0xfffffffffffffff8ull;
+#else
+      totalIbits = (totalIbits + 7ul) & 0xfffffff8ul;
+#endif
+
+      if (totalIbits < lasttotalIbits) {
+	fprintf(stderr, "ERROR: The totalIbits counter (%d byte unsigned integer) has overflowed.\n", sizeof (mg_ullong));
+	if (sizeof (mg_ullong) < 8) {
+	  fprintf(stderr, "       Try compiling with GCC to enable use of 8 bytes for this counter.\n");
+	}
+	fprintf(stderr, "       Build aborted.\n");
+	exit(1);
+      }
+    }
+
+
+  /* now convert to bytes, and actually get the space */
+#ifdef USE_LONG_LONG
+  totalIbytes = (totalIbits + 7ull) >> 3ull;
+#else
+  totalIbytes = (totalIbits + 7ul) >> 3ul;
+#endif
+  return (COMPALLOK);
+
+}
+
+
+
+
+
+static void 
+LoadCounts (void)
+{
+  unsigned long numwords, i, last_total;
+  static unsigned long local_N = 0;
+  unsigned long totalIbits, crbs_pos;
+  word_rec *wr;
+  unsigned long *counts;
+
+  if (MemoryBuffer == NULL)
+    {
+      MemBufSize = sizeof (unsigned long) * dict_size;
+      if (max_buffer_len > MemBufSize) 
+	MemBufSize = max_buffer_len;
+      if (!(MemoryBuffer = Xmalloc (MemBufSize)))
+	FatalError (1, "Unable to allocate memory for buffer");
+      ChangeMemInUse (MemBufSize);
+    }
+  counts = (unsigned long *) MemoryBuffer;
+/*   bzero ((char *) counts, sizeof (unsigned long) * dict_size); */
+  bzero ((char *) counts, MemBufSize);
+  docs_left = next_docs_left; 
+  if (!docs_left)
+    FatalError (1, "The number of docs in the current chunk is 0");
+
+  BufToUse = BIO_Stdio_Gamma_Decode (&sbs, NULL) - 1;
+
+  numwords = BIO_Stdio_Gamma_Decode (&sbs, NULL) - 1;
+
+  local_N = docs_left;
+
+  for (wr = WordRecs, i = 0; i < dict_size; i++, wr++)
+    wr->ptr = 0; 
+
+  bzero ((char *) lg_bs, dict_size);
+
+  for (i = 0; i < numwords; i++)
+    {
+      unsigned long word_num, wcnt, fcnt, p;
+      word_num = occur_to_lexical (i,0);
+      
+      wr = &WordRecs[word_num];
+      
+      wcnt = BIO_Stdio_Gamma_Decode (&sbs, NULL) - 1;
+      if (wcnt >= 2)
+	fcnt = BIO_Stdio_Gamma_Decode (&sbs, NULL);
+      else
+	fcnt = wcnt;
+
+
+      p = fcnt;
+      if (wcnt)
+	{
+	  register unsigned long length;
+	  counts[word_num] = p;
+	  length = BIO_Bblock_Bound (local_N, p);
+	  if (InvfLevel >= 2)
+	    length += wcnt;
+	  wr->ptr = length;
+	  lg_bs[word_num] = floorlog_2 (BIO_Bblock_Init_W (local_N, p));
+	}
+
+
+  }  
+  crbs_pos = BIO_Random_Tell (&crbs);
+  totalIbits = 0;
+  last_total = 0;
+  for (wr = WordRecs, i = 0; i < dict_size; i++, wr++)
+    {
+      register unsigned long length;
+      length = wr->ptr;
+      wr->last = callnum;
+      BIO_Random_Gamma_Encode (counts[i] + 1, &crbs, NULL);
+      if (counts[i])
+	{
+	  if (i)
+	    BIO_Random_Delta_Encode (totalIbits - last_total + 1, &crbs, NULL);
+	  else
+	    BIO_Random_Delta_Encode (1, &crbs, NULL);
+
+	  last_total = totalIbits;
+	}
+      wr->ptr = totalIbits;
+      totalIbits += length;
+    }
+  add_chunk_state (crbs_pos, callnum, local_N);
+
+  if ((totalIbits + 7ul) >> 3ul > BufToUse)
+    FatalError (1, "Pointers exceed buffer size");
+
+  next_docs_left = BIO_Stdio_Gamma_Decode (&sbs, NULL) - 1;
+}
+
+
+
+
+static void 
+DumpChunk (void)
+{
+  chunk_data[chunks_read - 1].disk_pos = Disk_pos << 3;
+  fseek (chunks, Disk_pos, 0);
+  fwrite (MemoryBuffer, sizeof (char), BufToUse, chunks);
+  Disk_pos += BufToUse;
+}
+
+
+
+
+static void 
+DiskMerge (void)
+{
+  random_bitio_state *rbsi;
+  random_bitio_state *chks = NULL;
+  unsigned long *chunk_ptrs;
+  int i;
+
+  BIO_Random_Flush (&crbs);
+
+  chunk_ptrs = Xmalloc (chunks_read * sizeof (unsigned long));
+  ChangeMemInUse (chunks_read * sizeof (unsigned long));
+  bzero ((char *) chunk_ptrs, chunks_read * sizeof (unsigned long));
+
+  rbsi = Xmalloc (chunks_read * sizeof (random_bitio_state));
+  ChangeMemInUse (chunks_read * sizeof (random_bitio_state));
+  for (i = 0; i < chunks_read; i++)
+    {
+      rbsi[i] = crbs;
+      rbsi[i].Buf = Xmalloc (rbsi[i].len);
+      ChangeMemInUse (rbsi[i].len);
+      bcopy ((char *) (crbs.Buf), (char *) (rbsi[i].Buf), rbsi[i].len);
+      BIO_Random_Seek (chunk_data[i].params_pos, &rbsi[i]);
+    }
+
+  if (chunks_read > 1)
+    {
+      int j;
+      chks = Xmalloc ((chunks_read - 1) * sizeof (random_bitio_state));
+      ChangeMemInUse ((chunks_read - 1) * sizeof (random_bitio_state));
+      BIO_Random_Start (chunks, RND_BUF_SIZE, &chks[0]);
+      ChangeMemInUse (RND_BUF_SIZE);
+      for (j = 1; j < chunks_read - 1; j++)
+	{
+	  chks[j] = chks[0];
+	  chks[j].Buf = Xmalloc (chks[0].len);
+	  ChangeMemInUse (chks[0].len);
+	  bcopy ((char *) (chks[0].Buf), (char *) (chks[j].Buf), chks[0].len);
+	}
+    }
+  for (i = 0; i < dict_size; i++)
+    {
+      int j;
+      invf_state_rec *isr = in_cache (i);
+      register int B;
+
+      BIO_Random_Seek_X (isr->Disk_Ptr, &rbs);	/* Position in invf file */
+
+      B = isr->Disk_B;
+
+      for (j = 0; j < chunks_read; j++)
+	{
+	  int p;
+	  p = BIO_Random_Gamma_Decode (&rbsi[j], NULL) - 1;
+
+	  if (p)
+	    {
+	      int ptr, b;
+	      chunk_ptrs[j] += BIO_Random_Delta_Decode (&rbsi[j], NULL) - 1;
+	      ptr = chunk_ptrs[j];
+	      b = 1 << floorlog_2 (BIO_Bblock_Init_W (chunk_data[j].N, p));
+
+	      if (j == chunks_read - 1)
+		{
+		  int k, CurrDoc;
+		  DECODE_START ((u_char *) MemoryBuffer, ptr)
+		    CurrDoc = isr->Disk_Last;
+		  for (k = 0; k < p; k++)
+		    {
+		      register unsigned long x, tf;
+		      BBLOCK_DECODE (x, b);
+		      if (k == 0)
+			x = x + chunk_data[j].start_doc - isr->Disk_Last;
+		      CurrDoc += x;
+		      BIO_Random_Bblock_Encode (x, B, &rbs, NULL);
+		      if (InvfLevel >= 2)
+			{
+			  UNARY_DECODE (tf);
+			  BIO_Random_Gamma_Encode (tf, &rbs, NULL);
+			}
+		    }
+		  DECODE_DONE
+		    isr->Disk_Last = CurrDoc;
+		}
+	      else
+		{
+		  int k, CurrDoc;
+		  random_bitio_state *Chks = chks + j;
+		  BIO_Random_Seek (chunk_data[j].disk_pos + ptr, Chks);
+		  CurrDoc = isr->Disk_Last;
+		  for (k = 0; k < p; k++)
+		    {
+		      register unsigned long x, tf;
+		      x = BIO_Random_Bblock_Decode (b, Chks, NULL);
+		      if (k == 0)
+			x = x + chunk_data[j].start_doc - isr->Disk_Last;
+		      CurrDoc += x;
+		      BIO_Random_Bblock_Encode (x, B, &rbs, NULL);
+		      if (InvfLevel >= 2)
+			{
+			  tf = BIO_Random_Unary_Decode (Chks, NULL);
+			  BIO_Random_Gamma_Encode (tf, &rbs, NULL);
+			}
+		    }
+		  isr->Disk_Last = CurrDoc;
+		}
+	    }
+	}
+
+      isr->Disk_Ptr = BIO_Random_Tell_X (&rbs);
+
+    }
+  if (chunks_read > 1)
+    {
+      int j;
+      for (j = 0; j < chunks_read - 1; j++)
+	{
+	  Xfree (chks[j].Buf);
+	  ChangeMemInUse (-chks[j].len);
+	}
+      Xfree (chks);
+      ChangeMemInUse (-(chunks_read - 1) * sizeof (random_bitio_state));
+    }
+
+  for (i = 0; i < chunks_read; i++)
+    {
+      Xfree (rbsi[i].Buf);
+      ChangeMemInUse (-rbsi[i].len);
+    }
+  Xfree (rbsi);
+  ChangeMemInUse (-chunks_read * sizeof (random_bitio_state));
+/*   chunks_read = 0; */
+  Xfree (chunk_ptrs);
+  ChangeMemInUse (-chunks_read * sizeof (unsigned long));
+  chunks_read = 0;
+  Disk_pos = 0;
+  BIO_Random_Seek (0, &crbs);
+}
+
+static void 
+MergeIn (void)
+{
+  static int disk_chunks = 0;
+  static header = 0;
+  if (!header)
+    {
+      fprintf (stderr, "ivf.pass2 : ");
+      header = 1;
+    }
+  if (disk_chunks == ChunkLimit || next_docs_left == 0)
+    {
+      fprintf (stderr, "M");
+      DiskMerge ();
+      disk_chunks = 0;
+    }
+  else
+    {
+      fprintf (stderr, "-");
+      DumpChunk ();
+      disk_chunks++;
+    }
+  if (next_docs_left == 0)
+    fprintf (stderr, "\n");
+}
+
+
+static int 
+wl_comp (const void *a, const void *b)
+{
+  return *((int *) a) - *((int *) b);
+}
+
+static int 
+process_doc (u_char * s_in, int l_in)
+{
+  int res;
+  u_char *end = s_in + l_in - 1;
+  unsigned long tocode;
+  unsigned long wl_pos = 0;
+
+  if (!docs_left)
+    LoadCounts ();
+
+  callnum++;
+
+  if (!inaword (s_in, end))
+    if (SkipSGML)
+      PARSE_NON_STEM_WORD_OR_SGML_TAG (s_in, end);
+    else
+      PARSE_NON_STEM_WORD (s_in, end);
+
+  while (s_in <= end)
+    {
+      u_char Word[MAXSTEMLEN + 1];
+
+      PARSE_STEM_WORD (Word, s_in, end);
+      stemmer (idh.stem_method, idh.stemmer_num, Word);
+      if (SkipSGML)
+	PARSE_NON_STEM_WORD_OR_SGML_TAG (s_in, end);
+      else
+	PARSE_NON_STEM_WORD (s_in, end);
+
+      if (*Word == 0)
+	continue;
+
+      res = perf_hash (phd, Word);
+
+      {
+	word_rec *arr = &WordRecs[res];
+	int b = 1 << lg_bs[res];
+	wordnum++;
+
+	tocode = callnum;
+
+	ENCODE_START ((u_char *) MemoryBuffer, arr->ptr)
+
+	  if (tocode > arr->last)
+	  {
+	    register int x;
+	    x = tocode - arr->last - 1;
+	    BBLOCK_ENCODE (x + 1, b);
+	    if (InvfLevel >= 2)
+	      ENCODE_BIT (1);
+	    no_of_ptrs++;
+	    arr->last = tocode;
+	  }
+	else if (InvfLevel >= 2)
+	  {
+	    __pos--;
+	    ENCODE_BIT (0);
+	    ENCODE_BIT (1);
+	  }
+	arr->ptr = __pos;
+	ENCODE_DONE
+      }
+
+      if (MakeWeights)
+	{
+	  if (wl_pos >= wl_size)
+	    {
+	      wl_size += (wl_size >> 1);
+	      word_list = Xrealloc (word_list, sizeof (*word_list) * wl_size);
+	    }
+	  word_list[wl_pos++] = res;
+	}
+    }
+  if (MakeWeights)
+    {
+      float doc_weight = 0.0;
+      if (wl_pos)
+	{
+	  unsigned long *wl = word_list;
+	  unsigned long i, count, val;
+	  qsort (wl, wl_pos, sizeof (*wl), wl_comp);
+	  count = 1;
+	  val = *wl++;
+	  for (i = 1; i <= wl_pos; i++, wl++)
+	    if (i == wl_pos || val != *wl)
+	      {
+		double weight = count * idf[val];
+		doc_weight += weight * weight;
+		count = 1;
+		val = *wl;
+	      }
+	    else
+	      count++;
+	}
+      HTONF(doc_weight);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite ((char *) &doc_weight, sizeof (doc_weight), 1, weights);
+    }
+  docs_left--;
+  if (!docs_left)
+    MergeIn ();
+
+  return COMPALLOK;
+}
+
+int 
+process_ivf_2 (u_char * s_in, int l_in)
+{
+  if (InvfLevel <= 2)
+    return process_doc (s_in, l_in);
+  else
+    {
+      int count = 0;
+      int pos = 0;
+      u_char *start = s_in;
+      while (pos < l_in)
+	{
+	  if (s_in[pos] == TERMPARAGRAPH)
+	    {
+	      int len = pos + s_in + 1 - start;
+	      if (process_doc (start, len) != COMPALLOK)
+		return (COMPERROR);
+	      start = s_in + pos + 1;
+	      count++;
+	    }
+	  pos++;
+	}
+      if (start < s_in + pos)
+	{
+	  if (process_doc (start, pos + s_in - start) != COMPALLOK)
+	    return (COMPERROR);
+	  count++;
+	}
+      HTONSI(count);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite ((char *) &count, sizeof (count), 1, invf_para);
+    }
+  return COMPALLOK;
+}
+
+
+
+
+
+static void 
+stats (unsigned long len)
+{
+#ifndef SILENT
+  fseek (count, 0, 2);
+  fseek (count_trans, 0, 2);
+  fseek (invf_state, 0, 2);
+  fseek (invf, 0, 0);
+  fseek (invf, 0, 2);
+  fseek (chunks, 0, 2);
+  fseek (chunk_state, 0, 2);
+  Message ("File sizes\n");
+  Message ("  Chunk desc    : %10u bytes\n", ftell (count));
+  Message ("  Chunk trans   : %10u bytes\n", ftell (count_trans));
+  Message ("  Chunks        : %10u bytes\n", ftell (chunks));
+  Message ("  Chunk state   : %10u bytes\n", ftell (chunk_state));
+  Message ("  Invf state    : %10u bytes\n", ftell (invf_state));
+  Message ("  Peak invf     : %10u bytes\n", len);
+  Message ("  Final invf    : %10u bytes\n", ftell (invf));
+  Message ("Peak disk usage : %10.2f %%\n",
+	   (double) (ftell (count) + ftell (count_trans) +
+		     ftell (invf_state) + ftell (chunks) +
+		     ftell (chunk_state) + len) / ftell (invf) * 100.0);
+#endif
+}
+
+
+/* ARGSUSED */
+int 
+done_ivf_2 (char *FileName)
+{
+  long i;
+  mg_ullong totalIbits;
+  unsigned long invf_len;
+  unsigned long bytes_output;
+  struct invf_file_header ifh;
+
+  if (weights)
+    fclose (weights);
+  if (invf_para)
+    fclose (invf_para);
+
+  free_perf_hash (phd);
+  phd = NULL;
+
+  Xfree (MemoryBuffer);
+  MemoryBuffer = NULL;
+  ChangeMemInUse (-MemBufSize);
+
+  BIO_Random_Done (&rbs);
+  BIO_Random_Done (&rbsp);
+  fflush (invf);
+
+  fseek (invf, 0, 2);
+  invf_len = ftell (invf);
+
+  fseek (invf_out, sizeof (long), 0);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL2(dict_size, ifh.no_of_words);
+  HTONUL2(no_of_ptrs, ifh.no_of_ptrs);
+  ifh.skip_mode = 0;
+  bzero ((char *) ifh.params, sizeof (ifh.params));
+  HTONUL2(InvfLevel, ifh.InvfLevel);
+  fwrite ((char *) &ifh, sizeof (ifh), 1, invf_out);
+
+  bytes_output = ftell (invf_out);
+
+  totalIbits = sizeof (unsigned long) * 8;		/* The magic number */
+  totalIbits += 8 * 200;	/* A 200 byte gap */
+
+  /* find the right place in the file to start reading p values */
+  fseek (dict, sizeof (unsigned long) + sizeof (struct invf_dict_header), 0);
+  for (i = 0; i < dict_size; i++)
+    {
+      invf_state_rec *isr;
+      unsigned long fcnt, wcnt, s, e;
+      register unsigned long p;
+      u_char dummy1, dummy2[MAXSTEMLEN + 1];
+
+      /* output location to the invf_idx  */
+      HTONUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite ((char *) &bytes_output, sizeof (bytes_output), 1, invf_idx);
+      NTOHUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+
+      /* read an entry for a word, just to get p value */
+      dummy1 = fgetc (dict);
+      dummy1 = fgetc (dict);
+      fread (dummy2, sizeof (u_char), dummy1, dict);
+      fread ((char *) &fcnt, sizeof (fcnt), 1, dict);
+      fread ((char *) &wcnt, sizeof (wcnt), 1, dict);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      p = fcnt;
+
+      isr = in_cache (i);
+
+      e = (isr->Disk_Ptr + 7ul) >> 3ul;
+      s = totalIbits >> 3;
+
+      fseek (invf_in, s, 0);
+      while (s < e)
+	{
+	  u_char c = getc (invf_in);
+	  if (s == e - 1)
+	    {
+	      u_char ands[8] =
+	      {0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
+	      c &= ands[isr->Disk_Ptr & 7ul];
+	    }
+	  putc (c, invf_out);
+	  bytes_output++;
+	  s++;
+	}
+
+      totalIbits += BIO_Bblock_Bound_b (N, p, isr->Disk_B);
+      if (InvfLevel >= 2)
+	totalIbits += BIO_Gamma_Bound (wcnt, fcnt);
+#ifdef USE_LONG_LONG
+      totalIbits = (totalIbits + 7ull) & 0xfffffffffffffff8ull;
+#else
+      totalIbits = (totalIbits + 7ul) & 0xfffffff8ul;
+#endif
+
+    }
+
+  fclose (invf_in);
+
+  /* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  if (!(_chsize (_fileno (invf_out), bytes_output)))
+    Message ("Could not truncate invf.");
+#else
+  ftruncate (fileno (invf_out), bytes_output);
+#endif
+
+  fclose (invf_out);
+
+  HTONUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite ((char *) &bytes_output, sizeof (bytes_output), 1, invf_idx);
+  NTOHUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  fclose (invf_idx);
+
+#ifndef SILENT
+  {
+    char *temp_str = msg_prefix;
+    unsigned long total;
+    msg_prefix = "ivf.pass2";
+    stats (invf_len);
+    Message ("Pass two data structures      : %6.3f Mbyte\n",
+	     (double) totalDbytes / 1024 / 1024);
+    total = totalDbytes;
+    Message ("Pass two hash structure(s)    : %6.3f Mbyte\n",
+	     (double) totalHbytes / 1024 / 1024);
+    total += totalHbytes;
+    Message ("Peak extra memory in use      : %6.3f Mbyte\n",
+	     (double) MaxMemInUse / 1024 / 1024);
+    total += MaxMemInUse;
+    Message ("Peak total memory in use      : %6.3f Mbyte\n",
+	     (double) total / 1024 / 1024);
+    msg_prefix = temp_str;
+  }
+#endif
+
+  Xfree(chunk_data);
+  chunk_data = NULL;
+  Xfree (WordRecs);
+  WordRecs = NULL;
+  Xfree (lg_bs);
+  lg_bs = NULL;
+  Xfree (idf);
+  idf = NULL;
+  Xfree (word_list);
+  word_list = NULL;
+  /* Free the memory allocated for the BIO_Random */
+  occur_to_lexical (-1,1);
+
+  BIO_Random_Done (&crbs);
+
+  fclose (invf);
+  fclose (dict);
+  fclose (hash);
+  fclose (count);
+  fclose (count_trans);
+  fclose (chunk_state);
+  fclose (chunks);
+  fclose (invf_state);
+  return (COMPALLOK);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/lists.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/lists.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/lists.c	(revision 16561)
@@ -0,0 +1,217 @@
+/**************************************************************************
+ *
+ * lists.c -- List processing functions for boolean queries
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+
+#include "lists.h"
+#include "local_strings.h"
+#include "locallib.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:01  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1999/07/01 09:30:48  rjmcnab
+   Changes for better match reporting.
+
+   Revision 1.1  1998/11/17 09:34:47  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:50  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:37  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+
+
+
+/*
+ * Make a DocList large enough to hold num entries 
+ */
+DocList *
+MakeDocList (int num)
+{
+  DocList *d;
+  if (!(d = Xmalloc (sizeof (DocList) + sizeof (DocEntry) * (num - 1))))
+    return (NULL);
+  d->num = 0;
+  d->total_retrieved = 0;
+  d->is_approx = 0;
+  return (d);
+}
+
+
+/*
+ * Change the size of a DocList so that it can hold num entries 
+ */
+DocList *
+ResizeDocList (DocList * d, int num)
+{
+  if (!(d = Xrealloc (d, sizeof (DocList) + sizeof (DocEntry) * (num - 1))))
+    return (NULL);
+  return (d);
+}
+
+
+/* IntersectLists () */
+/* Returns the intersection of List1 and List2 by deleting from List1 all */
+/* entries not in List2.  All of List2 is freed, and the nodes of List1 not */
+/* included in the resultant list are freed. */
+/* On exit the intersection of the lists is returned, or NULL on empty */
+/* intersection. */
+/* Assumes that both lists are ordered on increasing Value. */
+DocList *
+IntersectLists (DocList * List1, DocList * List2)
+{
+  DocEntry *s1, *d1, *s2;
+  if (!List2 || !List1)
+    {
+      if (List1)
+	Xfree (List1);
+      if (List2)
+	Xfree (List2);
+      return (MakeDocList (0));
+    }
+  d1 = s1 = List1->DE;
+  s2 = List2->DE;
+  while (s1 - List1->DE < List1->num && s2 - List2->DE < List2->num)
+    {
+      if (s1->DocNum == s2->DocNum)
+	*d1++ = *s1++;
+      if (s1 - List1->DE < List1->num)
+	while (s2 - List2->DE < List2->num && s2->DocNum < s1->DocNum)
+	  s2++;
+      if (s2 - List2->DE < List2->num)
+	while (s1 - List1->DE < List1->num && s1->DocNum < s2->DocNum)
+	  s1++;
+    }
+  List1->num = d1 - List1->DE;
+  Xfree (List2);
+  return (List1);
+}
+
+
+/* DiffLists () */
+/* Returns the difference of List1 and List2 by deleting from List1 all */
+/* entries in List2.  All of List2 is freed, and the nodes of List1 not */
+/* included in the resultant list are freed. */
+/* On exit the differrence of the lists is returned, or NULL on empty */
+/* intersection. */
+/* Assumes that both lists are ordered on increasing Value. */
+DocList *
+DiffLists (DocList * List1, DocList * List2)
+{
+  DocEntry *s1, *d1, *s2;
+  if (!List1)
+    {
+      if (List2)
+	Xfree (List2);
+      return (MakeDocList (0));
+    }
+  if (!List2)
+    return (List1);
+  d1 = s1 = List1->DE;
+  s2 = List2->DE;
+  while (s1 - List1->DE < List1->num)
+    {
+      if (s2 - List2->DE == List2->num)
+	*d1++ = *s1++;
+      else
+	{
+	  if (s1->DocNum == s2->DocNum)
+	    {
+	      s1++;
+	      s2++;
+	    }
+	  else if (s1->DocNum < s2->DocNum)
+	    *d1++ = *s1++;
+	  else
+	    s2++;
+	}
+    }
+  List1->num = d1 - List1->DE;
+  Xfree (List2);
+  return (List1);
+}
+
+/* MergeLists () */
+/* Finds the union of two lists and returns the head of the new list formed. */
+/* Assumes that both lists are ordered on increasing Value. */
+DocList *
+MergeLists (DocList * List1, DocList * List2)
+{
+  DocList *NewList;
+  DocEntry *d, *s1, *s2;
+  if (!List1)
+    {
+      if (List2)
+	return (List2);
+      else
+	return (MakeDocList (0));
+    }
+  if (!List2)
+    return (List1);
+  if (!(NewList = MakeDocList (List1->num + List2->num)))
+    {
+      Xfree (List1);
+      Xfree (List2);
+      return (NULL);
+    }
+
+  s1 = List1->DE;
+  s2 = List2->DE;
+  d = NewList->DE;
+  while (s1 - List1->DE < List1->num || s2 - List2->DE < List2->num)
+    {
+      if (s1 - List1->DE >= List1->num)
+	*d++ = *s2++;
+      else if (s2 - List2->DE >= List2->num)
+	*d++ = *s1++;
+      else if (s1->DocNum == s2->DocNum)
+	{
+	  *d = *s1++;
+	  (d++)->Weight += (s2++)->Weight;
+	}
+      else if (s1->DocNum < s2->DocNum)
+	*d++ = *s1++;
+      else
+	*d++ = *s2++;
+    }
+  NewList->num = d - NewList->DE;
+  Xfree (List1);
+  Xfree (List2);
+  return (NewList);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/lists.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/lists.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/lists.h	(revision 16561)
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * lists.h -- List processing functions for boolean queries
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ *
+ * 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 H_LISTS
+#define H_LISTS
+
+
+
+typedef struct DocEntry
+  {
+    float Weight;
+    int DocNum;
+    unsigned long SeekPos;	/* position in the text file in bytes */
+    unsigned long Len;		/* length of the document in bytes */
+    char *CompTextBuffer;
+    struct DocEntry *Next;
+    short or_included;		/*[TS:Mar/95] whether included in an AND of ORed-terms */
+  }
+DocEntry;
+
+typedef struct DocList
+  {
+    int num;
+    int total_retrieved; /* valid if > 0 */
+    int is_approx;       /* whether total_retrieved is approximate */
+    DocEntry DE[1];
+  }
+DocList;
+
+DocList *MakeDocList (int num);
+DocList *ResizeDocList (DocList * d, int num);
+DocList *IntersectLists (DocList * List1, DocList * List2);
+DocList *DiffLists (DocList * List1, DocList * List2);
+DocList *MergeLists (DocList * List1, DocList * List2);
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.c	(revision 16561)
@@ -0,0 +1,307 @@
+/**************************************************************************
+ *
+ * locallib.c -- Misc functions
+ * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "bitio_gen.h"
+#include "huffman.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "locallib.h"
+#include "text.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:02  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:49  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:51  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:38  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+int 
+vecentropy (int *A, int n)
+{
+  int total = 0, i;
+  double bits = 0.0;
+
+  for (i = 0; i < n; i++)
+    total += A[i];
+  for (i = 0; i < n; i++)
+    if (A[i])
+      bits += A[i] * log2 (1.0 * total / A[i]);
+  return ((int) (bits + 0.9999));
+
+}
+
+static void 
+siftdown (int *A, int i, int n)
+{
+  int ms, t;
+
+  while (2 * i + 1 <= n - 1)
+    {
+      if (2 * i + 1 == n - 1 || A[2 * i + 1] < A[2 * i + 2])
+	ms = 2 * i + 1;
+      else
+	ms = 2 * i + 2;
+      if (A[i] > A[ms])
+	{
+	  t = A[i];
+	  A[i] = A[ms];
+	  A[ms] = t;
+	}
+      i = ms;
+    }
+}
+
+static void 
+buildheap (int *A, int n)
+{
+  int i;
+  for (i = n / 2 - 1; i >= 0; i--)
+    siftdown (A, i, n);
+}
+
+int 
+huffcodebits (unsigned long *A, int n)
+{
+  int i, j, tot = 0, bits = 0, v1, v2, *B;
+
+  B = (int *) Xmalloc (n * sizeof (int));
+  j = 0;
+  for (i = 0; i < n; i++)
+    {
+      if (A[i])
+	{
+	  tot += (B[j++] = A[i]);
+	}
+    }
+  n = j;
+
+  if (n == 0 || tot == 0)
+    return (0);
+
+  buildheap (B, n);
+  while (n > 1)
+    {
+      v1 = B[0];
+      B[0] = B[n - 1];
+      n--;
+      siftdown (B, 0, n);
+      v2 = B[0];
+      B[0] = v1 + v2;
+      siftdown (B, 0, n);
+      bits += v1 + v2;
+    }
+  Xfree (B);
+  return (bits);
+}
+
+
+int 
+modelbits (unsigned long *A, int n)
+{
+  int i, bits = 0, last, N = 0;
+
+  last = -1;
+  for (i = 0; i < n; i++)
+    {
+      if (A[i])
+	{
+	  bits += BIO_Gamma_Length (i - last) + BIO_Gamma_Length (A[i]);
+	  last = i;
+	  N++;
+	}
+    }
+  if (N == 0)
+    return (0);
+  bits += BIO_Gamma_Length (N);
+  return (bits);
+}
+
+
+
+
+
+/* Find the next prime number larger than p */
+int 
+prime (int p)
+{
+  int limit;
+  p += (p & 1) + 1;
+  limit = (int) sqrt ((double) p) + 1;
+  do
+    {
+      int j;
+      while (limit * limit < p)
+	limit++;
+      for (j = 3; j <= limit && p % j; j += 2)
+	;			/* NULLBODY */
+      if (j > limit)
+	break;
+      p += 2;
+    }
+  while (1);
+  return (p);
+}
+
+
+
+int 
+Read_cdh (FILE * f, compression_dict_header * cdh, u_long * mem,
+	  u_long * disk)
+{
+  if (disk)
+    (*disk) += sizeof (*cdh);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  if (fread (cdh, sizeof (*cdh), 1, f) == 1)
+    {
+      int i;
+      NTOHUL(cdh->dict_type);
+      NTOHUL(cdh->novel_method);
+      for (i = 0; i < TEXT_PARAMS; i++)
+	NTOHUL(cdh->params[i]);
+      NTOHUL(cdh->num_words[0]);
+      NTOHUL(cdh->num_words[1]);
+      NTOHUL(cdh->num_word_chars[0]);
+      NTOHUL(cdh->num_word_chars[1]);
+      NTOHUL(cdh->lookback);
+      return 0;
+    }
+  else
+    return -1;
+}
+
+
+int 
+F_Read_cdh (File * f, compression_dict_header * cdh, u_long * mem,
+	    u_long * disk)
+{
+  if (disk)
+    (*disk) += sizeof (*cdh);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  if (Fread (cdh, sizeof (*cdh), 1, f) == 1)
+    {
+      int i;
+      NTOHUL(cdh->dict_type);
+      NTOHUL(cdh->novel_method);
+      for (i = 0; i < TEXT_PARAMS; i++)
+	NTOHUL(cdh->params[i]);
+      NTOHUL(cdh->num_words[0]);
+      NTOHUL(cdh->num_words[1]);
+      NTOHUL(cdh->num_word_chars[0]);
+      NTOHUL(cdh->num_word_chars[1]);
+      NTOHUL(cdh->lookback);
+      return 0;
+    }
+  else
+    return -1;
+}
+
+
+int 
+Read_cfh (FILE * f, comp_frags_header * cfh, u_long * mem, u_long * disk)
+{
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (Read_Huffman_Data (f, &cfh->hd, mem, disk) == -1)
+    return -1;
+  if (fread (&cfh->uncompressed_size,
+	     sizeof (cfh->uncompressed_size), 1, f) != 1)
+    return -1;
+  NTOHUL(cfh->uncompressed_size);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (disk)
+    (*disk) += sizeof (cfh->uncompressed_size);
+
+  bzero ((char *) cfh->huff_words_size, sizeof (cfh->huff_words_size));
+
+  if (fread (cfh->huff_words_size + cfh->hd.mincodelen,
+	     sizeof (*cfh->huff_words_size),
+	     cfh->hd.maxcodelen - cfh->hd.mincodelen + 1, f) !=
+      cfh->hd.maxcodelen - cfh->hd.mincodelen + 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = cfh->hd.mincodelen; i < cfh->hd.maxcodelen + 1; i++)
+    NTOHUL(cfh->huff_words_size[i]);
+
+  if (disk)
+    (*disk) += sizeof (*cfh->huff_words_size) *
+      (cfh->hd.maxcodelen - cfh->hd.mincodelen + 1);
+  return 0;
+}
+
+
+
+int 
+F_Read_cfh (File * f, comp_frags_header * cfh, u_long * mem, u_long * disk)
+{
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (F_Read_Huffman_Data (f, &cfh->hd, mem, disk) == -1)
+    return -1;
+  if (Fread (&cfh->uncompressed_size,
+	     sizeof (cfh->uncompressed_size), 1, f) != 1)
+    return -1;
+  NTOHUL(cfh->uncompressed_size);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (disk)
+    (*disk) += sizeof (cfh->uncompressed_size);
+
+  bzero ((char *) cfh->huff_words_size, sizeof (cfh->huff_words_size));
+
+  if (Fread (cfh->huff_words_size + cfh->hd.mincodelen,
+	     sizeof (*cfh->huff_words_size),
+	     cfh->hd.maxcodelen - cfh->hd.mincodelen + 1, f) !=
+      cfh->hd.maxcodelen - cfh->hd.mincodelen + 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = cfh->hd.mincodelen; i < cfh->hd.maxcodelen + 1; i++)
+    NTOHUL(cfh->huff_words_size[i]);
+
+  if (disk)
+    (*disk) += sizeof (*cfh->huff_words_size) *
+      (cfh->hd.maxcodelen - cfh->hd.mincodelen + 1);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/locallib.h	(revision 16561)
@@ -0,0 +1,52 @@
+/**************************************************************************
+ *
+ * locallib.h -- Misc functions
+ * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
+ *
+ * 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 LOCALLIB_H
+#define LOCALLIB_H
+
+#include "sysfuncs.h"
+
+#include "text.h"
+
+#define NUMOF(a) (sizeof(a)/sizeof((a)[0]))
+
+int vecentropy (int *A, int n);
+
+int huffcodebits (unsigned long *A, int n);
+
+int modelbits (unsigned long *A, int n);
+
+int prime (int p);
+
+
+int Read_cdh (FILE * f, compression_dict_header * cdh, u_long * mem, u_long * disk);
+
+int Read_cfh (FILE * f, comp_frags_header * cfh, u_long * mem, u_long * disk);
+
+int F_Read_cdh (File * f, compression_dict_header * cdh, u_long * mem,
+		u_long * disk);
+
+int F_Read_cfh (File * f, comp_frags_header * cfh, u_long * mem, u_long * disk);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg.h	(revision 16561)
@@ -0,0 +1,42 @@
+/**************************************************************************
+ *
+ * mg.h -- General header file
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ *
+ * 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 H_MG
+#define H_MG
+
+
+#define COMPALLOK	(0)
+#define COMPERROR	(-1)
+
+#define MAXSTRING       3000000
+#define BUFFERSIZE      (MAXSTRING+30000)
+
+
+
+
+#define	MAXLN	65536
+#define WORDS           1
+#define NONWORDS        0
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg.special.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg.special.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg.special.c	(revision 16561)
@@ -0,0 +1,118 @@
+/**************************************************************************
+ *
+ * mg.special.c -- Special pass for mg_passes
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "mg.h"
+#include "build.h"
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:03  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:34:51  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:53  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:44  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+FILE *fp;
+
+
+int 
+init_special (char *FileName)
+{
+  char FName[200];
+  sprintf (FName, "%s%s", FileName, ".docnums");
+  if (!(fp = fopen (FName, "w")))
+    return (COMPERROR);
+  return (COMPALLOK);
+}
+
+
+
+
+int 
+process_special (u_char * s_in, int l_in)
+{
+  static int un = 1;
+  char *s_done = (char *) (s_in + l_in);
+  for (; (char *) s_in != s_done; s_in++)
+    if (*s_in == '<')
+      {
+	if (strncmp ((char *) s_in, "<DOCNO>", 7) == 0)
+	  {
+	    char *s_end = (char *) (s_in + 1);
+	    for (; s_end != s_done; s_end++)
+	      if (*s_end == '<')
+		if (strncmp (s_end, "</DOCNO>", 8) == 0)
+		  break;
+	    if (s_end == s_done)
+	      {
+		fprintf (fp, "NO DOC ID %d\n", un++);
+		return COMPALLOK;
+	      }
+	    s_in += 7;
+	    while (s_end != (char *) s_in &&
+		   (*(s_end - 1) == ' ' || *(s_end - 1) == '\n'))
+	      s_end--;
+	    while ((char *) s_in != s_end &&
+		   (*s_in == ' ' || *s_in == '\n'))
+	      s_in++;
+	    for (; (char *) s_in != s_end; s_in++)
+	      putc (*s_in, fp);
+	    putc ('\n', fp);
+	    return (COMPALLOK);
+	  }
+      }
+  fprintf (fp, "NO DOC ID %d\n", un++);
+  return (COMPALLOK);
+}
+
+
+
+
+
+
+
+
+int 
+done_special (char *FileName)
+{
+  fclose (fp);
+  return COMPALLOK;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.1	(revision 16561)
@@ -0,0 +1,219 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_compression_dict 1 \*(Dt CITRI
+.SH NAME
+mg_compression_dict \- build a compression dictionary.
+.SH SYNOPSIS
+.B mg_compression_dict
+[
+.B \-h
+]
+[
+.BR \-C " |"
+.BR \-P " |"
+.B  \-S
+]
+.if n .ti +9n
+[
+.BR \-0 " |"
+.BR \-1 " |"
+.BR \-2 " |"
+.B  \-3
+]
+[
+.BR \-H " |"
+.BR \-B " |"
+.BR \-D " |"
+.BR \-Y " |"
+.B  \-M
+]
+.if n .ti +9n
+.if t .ti +.5i
+[
+.BI \-l " lookback"
+]
+[
+.BI \-k " mem"
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_compression_dict
+builds a compression dictionary based on the statistics gathered
+during the first pass over the text.  The options to the program are
+mainly concerned with limiting the amount of memory the dictionary
+will use and with how the text compressor will cope with any novel
+words found during the compression phase.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-C
+Build a complete dictionary from the statistics file.  If during the
+text compression phase a novel word is found, then the compressor will
+produce an error message and stop.
+.TP
+.B \-P
+Build a partial dictionary from the statistics file.  This dictionary
+assumes that the statistics file are based on the entire text.  The
+statistics of words not includes in the dictionary are used to
+calculate the escape probability.  If novel words are being coded
+character by character, then there may not be a Huffman code for every
+possible character.  This means that the compressor may fail if a novel
+word contains a novel character.
+.TP
+.B \-S
+Build a seed dictionary from the statistics file.  This dictionary
+assumes that the statistics file is based on only a portion of the
+text to be compressed.  The probability of a novel word is based on the
+number of words that have only occurred once.  If novel words are being
+coded character by character, then the Huffman codes for characters are
+based on the frequency of characters in the dictionary.
+.TP
+.B \-0
+All words from the statistics file are included in the built
+dictionary.
+.TP
+.B \-1
+Words are included in the dictionary until the dictionary reaches the
+desired size.  Words are selected for the dictionary based on the order
+they occurred in the source text.
+.TP
+.B \-2
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.
+.TP
+.B \-3
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.  Words are the shuffled back and forth between the
+`keep' and `discard' lists to find the `optimal' set of words that
+should be in the dictionary.
+.TP
+.B \-H
+This specifies that novel words will be coded character by character
+using Huffman codes.
+.TP
+.B \-B
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using binary codes.  The binary code represents their occurrence
+position in the auxiliary dictionary.
+.TP
+.B \-D
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using delta codes.  The delta code represents their occurrence position
+in the auxiliary dictionary.
+.TP
+.B \-Y
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This generally
+produces better compression than
+.B \-B
+or
+.BR \-D .
+.TP
+.B \-M
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This method is
+adaptive within documents, and generally produces better compression
+than
+.BR \-B ,
+.B \-D
+or
+.BR \-Y .
+.TP
+.BI \-l " lookback"
+The generated dictionary is designed to be front coded when it is
+loaded into memory.  Under normal circumstances, a front-coded
+dictionary would require scanning from the beginning in order to find
+any particular word.  However, every
+.I lookback
+words in the dictionary, the whole word is stored and a pointer to that
+word maintained.  E.g., if
+.I lookback
+is 4, then every fourth word is stored in its entirety.
+.TP
+.BI \-k " mem"
+This limits the amount of memory to use for the generated
+dictionary.  Words are selected for the dictionary based of the text
+statistics, and whether
+.BR \-0 , " \-1" , " \-2"
+or
+.B \-3
+is specified.  The memory is calculated assuming a lookback of 0,
+irrespective of what actual lookback is specified.  This means that if
+a non-zero lookback is given, the dictionary will actually occupy
+less space than specified by
+.BR \-k .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.stats
+Statistics about the source text.
+.TP
+.B *.text.dict
+Compression dictionary for the source text.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_compression_dict.c	(revision 16561)
@@ -0,0 +1,1087 @@
+/**************************************************************************
+ *
+ * mg_compression_dict.c -- Routines for reading the compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_gen.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "timing.h"
+#include "mgheap.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "invf.h"
+#include "text.h"
+#include "words.h"
+#include "mg.h"
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  1999/12/10 15:04:25  cs025
+   Incorrect use of NTOHUL on a double value (num_bytes).
+
+   Revision 1.1  1999/08/10 21:18:04  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.4  1999/01/15 03:05:51  rjmcnab
+
+   Renamed lib/heap to be lib/mgheap (it was causing some problems with
+   some versions of the STL libraries which contained a heap.h)
+
+   Revision 1.3  1998/12/17 09:12:52  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:44  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:52  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:54  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:45  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+/* #define DEBUG1 */
+
+/* #define DEBUG */
+
+#define is_power_of_two(a) ((a) != 0 && (((a) & ((a)-1)) == 0))
+
+#define MAX_RECALCULATIONS 100
+
+typedef struct WordData
+  {
+    u_long freq, occur_num;
+    float saving;
+    float char_bit_cost;
+    u_long num_trans;
+    u_char *word;
+  }
+WordData;
+
+static WordData *Words[2];
+static u_long Num[2];
+static u_long chars[2];
+
+#define KIND(p) (((p) >= Words[0] && (p) < Words[0] + Num[0]) ? 0 : 1)
+#define IsWord(p) (((p) >= Words[1] && (p) < Words[1] + Num[1]) ? 1 : 0)
+#define IsNonWord(p) (((p) >= Words[0] && (p) < Words[0] + Num[0]) ? 1 : 0)
+
+
+typedef struct DictData
+  {
+    WordData **wd;
+    u_long num_wds;
+    u_long chars;
+  }
+DictData;
+
+typedef DictData DictInfo[2];
+
+static DictInfo keep, discard, all;
+static compression_stats_header csh;
+
+static char *file_name = "";
+static u_long novel_method = MG_NOVEL_HUFFMAN_CHARS;
+
+
+static void ReadInWords (char *);
+static void Select_on (int k, heap_comp hc);
+static void Method3 (int k);
+static void Select_all (void);
+static u_long WriteOutWords (char *, u_long, int);
+static int DecFreqIncWL (void *a, void *b);
+static int OccuranceOrder (void *a, void *b);
+
+
+
+int main (int argc, char **argv)
+{
+  ProgTime StartTime;
+  int ch;
+  char type = 'C';
+  char mode = '0';
+  int lookback = 2;
+  double k = 0;
+  u_long mem_reqd;
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "0123CPSf:d:l:hk:HBDYM")) != -1)
+    switch (ch)
+      {
+      case 'H':
+	novel_method = MG_NOVEL_HUFFMAN_CHARS;
+	break;
+      case 'B':
+	novel_method = MG_NOVEL_BINARY;
+	break;
+      case 'D':
+	novel_method = MG_NOVEL_DELTA;
+	break;
+      case 'Y':
+	novel_method = MG_NOVEL_HYBRID;
+	break;
+      case 'M':
+	novel_method = MG_NOVEL_HYBRID_MTF;
+	break;
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'C':
+      case 'P':
+      case 'S':
+	type = ch;
+	break;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+	mode = ch;
+	break;
+      case 'l':
+	lookback = atoi (optarg);
+	if (!is_power_of_two (lookback))
+	  FatalError (1, "The lookback value must be a power of 2");
+	lookback = floorlog_2 (lookback);
+	break;
+      case 'k':
+	k = atof (optarg) * 1024;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-l lookback] [-f input_file]"
+	"[-d data directory] [-h] [-0|-1|-2|-3] [-C|-P|-S] [-k mem (Kb)]\n",
+		 argv[0]);
+	exit (1);
+      }
+  GetTime (&StartTime);
+
+  ReadInWords (file_name);
+
+  if (type == 'C')
+    {
+      Select_all ();
+      mem_reqd = WriteOutWords (file_name, MG_COMPLETE_DICTIONARY, lookback);
+    }
+  else
+    {
+      switch (mode)
+	{
+	case '0':
+	  Select_all ();
+	  break;
+	case '1':
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+	  Select_on ((int) k, OccuranceOrder);
+	  break;
+	case '2':
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+	  Select_on ((int) k, DecFreqIncWL);
+	  break;
+	case '3':
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+	  Method3 ((int) k);
+	  break;
+	}
+      if (type == 'P')
+	{
+	  mem_reqd = WriteOutWords (file_name, MG_PARTIAL_DICTIONARY, lookback);
+	}
+      else
+	{
+	  mem_reqd = WriteOutWords (file_name, MG_SEED_DICTIONARY, lookback);
+	}
+    }
+
+  Message ("Num words           : %8u -> %8u\n", Num[1], keep[1].num_wds);
+  Message ("Num non-words       : %8u -> %8u\n", Num[0], keep[0].num_wds);
+  Message ("Chars of words      : %8u -> %8u\n", chars[1], keep[1].chars);
+  Message ("Chars of non-words  : %8u -> %8u\n", chars[0], keep[0].chars);
+  Message ("Mem usage           : %8u -> %8u\n",
+	   (Num[0] + Num[1]) * sizeof (char *) + chars[0] + chars[1],
+	     (keep[0].num_wds + keep[1].num_wds) * sizeof (char *) +
+	   keep[0].chars + keep[1].chars);
+  Message ("Actual mem required : %8u\n", mem_reqd);
+
+  Message ("%s", ElapsedTime (&StartTime, NULL));
+  return 0;
+}
+
+
+
+
+static void 
+ReadInWords (char *filename)
+{
+  FILE *f;
+  unsigned long i;
+  f = open_file (filename, TEXT_STATS_DICT_SUFFIX, "rb",
+		 MAGIC_STATS_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread (&csh, sizeof (csh), 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes);
+
+  for (i = 0; i < 2; i++)
+    {
+      frags_stats_header fsh;
+      char *buf;
+      WordData *wd;
+      int j;
+      chars[i] = 0;
+
+      fread (&fsh, sizeof (fsh), 1, f);
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fsh.num_frags);
+      NTOHUL(fsh.mem_for_frags);
+
+      Num[i] = all[i].num_wds = fsh.num_frags;
+
+      /* The +1 on the following line is to leave room for the esc code. */
+      all[i].wd = Xmalloc (sizeof (WordData *) * Num[i] + 1);
+
+      buf = Xmalloc (fsh.mem_for_frags);
+      wd = Words[i] = Xmalloc (sizeof (WordData) * Num[i]);
+      for (j = 0; j < Num[i]; j++, wd++)
+	{
+	  int len;
+	  fread (&wd->freq, sizeof (wd->freq), 1, f);
+	  NTOHUL(wd->freq);  /* [RPAP - Jan 97: Endian Ordering] */
+	  fread (&wd->occur_num, sizeof (wd->occur_num), 1, f);
+	  NTOHUL(wd->occur_num);  /* [RPAP - Jan 97: Endian Ordering] */
+	  len = fgetc (f);
+	  wd->word = (u_char *) buf;
+	  *buf++ = len;
+	  fread (buf, len, 1, f);
+	  buf += len;
+	  all[i].wd[j] = wd;
+	}
+      chars[i] = fsh.mem_for_frags - fsh.num_frags;
+    }
+  fclose (f);
+}
+
+
+static void 
+Alloc_keep_discard (void)
+{
+  keep[0].num_wds = 0;
+  keep[0].wd = Xmalloc ((Num[0] + 1) * sizeof (WordData *));
+  keep[1].num_wds = 0;
+  keep[1].wd = Xmalloc ((Num[1] + 1) * sizeof (WordData *));
+  discard[0].num_wds = 0;
+  discard[0].wd = Xmalloc ((Num[0] + 1) * sizeof (WordData *));
+  discard[1].num_wds = 0;
+  discard[1].wd = Xmalloc ((Num[1] + 1) * sizeof (WordData *));
+}
+
+
+static int 
+sort_comp (const void *a, const void *b)
+{
+  WordData *aa = *(WordData **) a;
+  WordData *bb = *(WordData **) b;
+  return casecompare (aa->word, bb->word);  /* [RPAP - Jan 97: Stem Index Change] */
+}
+
+
+
+static void 
+SortAndCount_DictData (DictData * dd)
+{
+  int i;
+  WordData **wd;
+  qsort (dd->wd, dd->num_wds, sizeof (WordData *), sort_comp);
+  dd->chars = 0;
+  wd = dd->wd;
+  for (i = 0; i < dd->num_wds; i++, wd++)
+    dd->chars += (*wd)->word[0];
+}
+
+
+static void 
+Select_all (void)
+{
+  int i;
+  Alloc_keep_discard ();
+  keep[0].num_wds = Num[0];
+  for (i = 0; i < Num[0]; i++)
+    keep[0].wd[i] = Words[0] + i;
+  keep[1].num_wds = Num[1];
+  for (i = 0; i < Num[1]; i++)
+    keep[1].wd[i] = Words[1] + i;
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+}
+
+
+
+
+static int 
+DecFreqIncWL (void *a, void *b)
+{
+  WordData *aa = *(WordData **) a;
+  WordData *bb = *(WordData **) b;
+  if (aa->freq > bb->freq)
+    return -1;
+  else if (aa->freq < bb->freq)
+    return 1;
+  else
+    return bb->word[0] - aa->word[0];
+}
+
+
+static int 
+OccuranceOrder (void *a, void *b)
+{
+  WordData *aa = *(WordData **) a;
+  WordData *bb = *(WordData **) b;
+  if (aa->occur_num > bb->occur_num)
+    return 1;
+  else if (aa->occur_num < bb->occur_num)
+    return -1;
+  else
+    return 0;
+}
+
+
+static void 
+Select_on (int k, heap_comp hc)
+{
+  int i, num, mem;
+  WordData **wd;
+
+  Alloc_keep_discard ();
+
+  num = Num[0] + Num[1];
+  wd = Xmalloc (num * sizeof (WordData *));
+  for (i = 0; i < Num[0]; i++)
+    wd[i] = Words[0] + i;
+  for (i = 0; i < Num[1]; i++)
+    wd[i + Num[0]] = Words[1] + i;
+
+  heap_build (wd, sizeof (*wd), num, hc);
+
+  mem = 0;
+  while (k > mem && num)
+    {
+      int idx;
+      WordData *word = wd[0];
+#ifdef DEBUG
+      fprintf (stderr, "%4d:%4d:%8d :%8d :%8d : \"%s\"\n",
+	       keep[0].num_wds, keep[1].num_wds,
+	       mem, word->freq, word->occur_num, word2str (word->word));
+#endif
+      mem += sizeof (u_char *) + word->word[0];
+      heap_deletehead (wd, sizeof (*wd), &num, hc);
+      idx = KIND (word);
+      keep[idx].wd[keep[idx].num_wds++] = word;
+    }
+
+  for (i = 0; i < num; i++)
+    {
+      WordData *word = wd[i];
+      int idx = KIND (word);
+      discard[idx].wd[discard[idx].num_wds++] = word;
+    }
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+  SortAndCount_DictData (&discard[0]);
+  SortAndCount_DictData (&discard[1]);
+
+  assert (keep[0].num_wds + discard[0].num_wds == Num[0]);
+  assert (keep[1].num_wds + discard[1].num_wds == Num[1]);
+
+}
+
+
+
+static int 
+BigSaving (void *a, void *b)
+{
+  WordData *aa = *(WordData **) a;
+  WordData *bb = *(WordData **) b;
+  return (aa->saving > bb->saving) ? -1 : (aa->saving < bb->saving);
+}
+
+static int 
+SmallSaving (void *a, void *b)
+{
+  WordData *aa = *(WordData **) a;
+  WordData *bb = *(WordData **) b;
+  return (aa->saving < bb->saving) ? -1 : (aa->saving > bb->saving);
+}
+
+
+static void 
+CalcCharCounts (WordData ** wd, int num,
+		char *char_lens[2], char *len_lens[2],
+		u_long escape[2])
+{
+  long char_freqs[2][256];
+  long len_freqs[2][16];
+  huff_data hd;
+  int i;
+  escape[0] = 0;
+  escape[1] = 0;
+  bzero ((char *) char_freqs, sizeof (char_freqs));
+  bzero ((char *) len_freqs, sizeof (len_freqs));
+  for (i = 0; i < num; i++, wd++)
+    {
+      u_long freq = (*wd)->freq;
+      u_char *buf = (*wd)->word;
+      int len = *buf++;
+      int idx = KIND (*wd);
+      len_freqs[idx][len] += freq;
+      escape[idx] += freq;
+      for (; len; len--, buf++)
+	char_freqs[idx][(u_long) (*buf)] += freq;
+    }
+  Generate_Huffman_Data (256, char_freqs[0], &hd, NULL);
+  char_lens[0] = hd.clens;
+  Generate_Huffman_Data (256, char_freqs[1], &hd, NULL);
+  char_lens[1] = hd.clens;
+
+  Generate_Huffman_Data (16, len_freqs[0], &hd, NULL);
+  len_lens[0] = hd.clens;
+  Generate_Huffman_Data (16, len_freqs[1], &hd, NULL);
+  len_lens[1] = hd.clens;
+}
+
+
+
+
+
+
+
+
+
+void 
+CalcBitCost (WordData ** discard_word, int discard_num,
+	     WordData ** keep_word, int keep_num, double freqs_trans[2],
+	     u_long escape[2], int num_trans)
+{
+  char *char_lens[2];
+  char *len_lens[2];
+  double esc[2];
+  int j;
+  CalcCharCounts (discard_word, discard_num, char_lens, len_lens, escape);
+  esc[0] = -log2 (escape[0] / (freqs_trans[0] + escape[0]));
+  esc[1] = -log2 (escape[1] / (freqs_trans[1] + escape[1]));
+  for (j = 0; j < discard_num; j++, discard_word++)
+    {
+      float cbc, wbc;
+      u_char *buf = (*discard_word)->word;
+      int len = *buf++;
+      u_long freq = (*discard_word)->freq;
+      int idx = KIND (*discard_word);
+
+      cbc = len_lens[idx][len];
+      for (; len; len--, buf++)
+	cbc += char_lens[idx][(u_long) (*buf)];
+
+      (*discard_word)->char_bit_cost = (cbc + esc[idx]) * freq;
+
+      wbc = -log2 (freq / (freqs_trans[idx] + escape[idx])) * freq;
+
+      (*discard_word)->saving = ((*discard_word)->char_bit_cost - wbc) /
+	(sizeof (char *) + (*discard_word)->word[0]);
+
+      (*discard_word)->num_trans = num_trans;
+    }
+  for (j = 0; j < keep_num; j++, keep_word++)
+    {
+      float cbc, wbc;
+      u_char *buf = (*keep_word)->word;
+      int len = *buf++;
+      u_long freq = (*keep_word)->freq;
+      int idx = KIND (*keep_word);
+
+      cbc = len_lens[idx][len];
+      for (; len; len--, buf++)
+	cbc += char_lens[idx][(u_long) (*buf)];
+
+      (*keep_word)->char_bit_cost = (cbc + esc[idx]) * freq;
+
+      wbc = -log2 (freq / (freqs_trans[idx] + escape[idx])) * freq;
+
+      (*keep_word)->saving = ((*keep_word)->char_bit_cost - wbc) /
+	(sizeof (char *) + (*keep_word)->word[0]);
+
+      (*keep_word)->num_trans = num_trans;
+    }
+  Xfree (char_lens[0]);
+  Xfree (char_lens[1]);
+  Xfree (len_lens[0]);
+  Xfree (len_lens[1]);
+}
+
+
+
+static void 
+Method3 (int k)
+{
+  int i, keep_num, discard_num, mem, num_trans, recalc_reqd;
+  int keep_to_discard = 0;
+  int discard_to_keep = 0;
+  int recalcs = 0;
+  double freqs_trans[2], total;
+  u_long escape[2];
+  WordData **keep_heap, **discard_heap;
+
+  freqs_trans[0] = freqs_trans[1] = 0;
+  num_trans = 0;
+
+  discard_num = Num[0] + Num[1];
+  discard_heap = Xmalloc (discard_num * sizeof (WordData *));
+
+  keep_num = 0;
+  keep_heap = Xmalloc (discard_num * sizeof (WordData *));
+
+
+  for (i = 0; i < Num[0]; i++)
+    discard_heap[i] = Words[0] + i;
+  for (i = 0; i < Num[1]; i++)
+    discard_heap[i + Num[0]] = Words[1] + i;
+
+  heap_build (discard_heap, sizeof (*discard_heap), discard_num, DecFreqIncWL);
+
+  mem = 0;
+  while (k > mem && discard_num)
+    {
+      WordData *word = discard_heap[0];
+      mem += sizeof (u_char *) + word->word[0];
+      heap_deletehead (discard_heap, sizeof (word), &discard_num, DecFreqIncWL);
+      keep_heap[keep_num++] = word;
+      freqs_trans[KIND (word)] += word->freq;
+      num_trans++;
+    }
+
+  CalcBitCost (discard_heap, discard_num, keep_heap, keep_num,
+	       freqs_trans, escape, num_trans);
+  heap_build (discard_heap, sizeof (*discard_heap), discard_num, BigSaving);
+  heap_build (keep_heap, sizeof (*keep_heap), keep_num, SmallSaving);
+
+  total = 0;
+  recalc_reqd = 0;
+  while (keep_num && discard_num)
+    {
+      if ((keep_num && keep_heap[0]->num_trans < num_trans) ||
+	  (discard_num && discard_heap[0]->num_trans < num_trans))
+	{
+	  if (keep_num && keep_heap[0]->num_trans < num_trans)
+	    {
+	      WordData *word = keep_heap[0];
+	      int idx = KIND (word);
+	      float wbc;
+#ifdef DEBUG1
+	      fprintf (stderr, "KEEP \"%s\" %.2f ->", word2str (word->word),
+		       word->saving);
+#endif
+	      wbc = -log2 (word->freq / (freqs_trans[idx] + escape[idx])) *
+		word->freq;
+	      word->saving = (word->char_bit_cost - wbc) /
+		(sizeof (char *) + word->word[0]);
+#ifdef DEBUG1
+	      fprintf (stderr, " %.2f\n", word->saving);
+#endif
+	      word->num_trans = num_trans;
+	      heap_changedhead (keep_heap, sizeof (word), keep_num, SmallSaving);
+	    }
+
+	  if (discard_num && discard_heap[0]->num_trans < num_trans)
+	    {
+	      WordData *word = discard_heap[0];
+	      int idx = KIND (word);
+	      float wbc;
+#ifdef DEBUG1
+	      fprintf (stderr, "DISCARD \"%s\" %.2f ->", word2str (word->word),
+		       word->saving);
+#endif
+	      wbc = -log2 (word->freq / (freqs_trans[idx] + escape[idx])) *
+		word->freq;
+	      word->saving = (word->char_bit_cost - wbc) /
+		(sizeof (char *) + word->word[0]);
+#ifdef DEBUG1
+	      fprintf (stderr, " %.2f\n", word->saving);
+#endif
+	      word->num_trans = num_trans;
+	      heap_changedhead (discard_heap, sizeof (word),
+				discard_num, BigSaving);
+	    }
+	}
+      else if (keep_heap[0]->saving < discard_heap[0]->saving)
+	{
+	  assert (keep_num && discard_num);
+	  if (keep_num && mem + sizeof (char *) + discard_heap[0]->word[0] > k)
+	    {
+	      /* Transfer the word at the top of the keep heap to the
+	         discard heap. */
+	      WordData *word = keep_heap[0];
+	      int idx = KIND (word);
+	      heap_deletehead (keep_heap, sizeof (word), &keep_num, SmallSaving);
+	      discard_heap[discard_num] = word;
+	      heap_additem (discard_heap, sizeof (word), &discard_num,
+			    BigSaving);
+	      freqs_trans[idx] -= word->freq;
+	      mem -= sizeof (u_char *) + word->word[0];
+	      num_trans++;
+	      total += word->saving;
+	      keep_to_discard++;
+#ifdef DEBUG
+	      fprintf (stderr,
+		       "KEEP -> DISCARD %8d :%8d :%8.0f :%8.0f : \"%s\"\n",
+		       mem, word->freq, word->char_bit_cost,
+		       word->saving, word2str (word->word));
+#endif
+	    }
+	  else
+	    {
+	      /* Transfer the word at the top of the discard heap to the
+	         keep heap. */
+	      WordData *word = discard_heap[0];
+	      int idx = KIND (word);
+	      heap_deletehead (discard_heap, sizeof (word),
+			       &discard_num, BigSaving);
+	      keep_heap[keep_num] = word;
+	      heap_additem (keep_heap, sizeof (word), &keep_num, SmallSaving);
+	      freqs_trans[idx] += word->freq;
+	      mem += sizeof (u_char *) + word->word[0];
+	      num_trans++;
+	      total += word->saving;
+	      discard_to_keep++;
+#ifdef DEBUG
+	      fprintf (stderr,
+		       "DISCARD -> KEEP %8d :%8d :%8.0f :%8.0f : \"%s\"\n",
+		       mem, word->freq, word->char_bit_cost,
+		       word->saving, word2str (word->word));
+#endif
+	    }
+
+	  recalc_reqd = 1;
+
+	}
+      else
+	{
+	  if (recalc_reqd == 0)
+	    break;
+#ifdef DEBUG1
+	  fprintf (stderr, "--------------\n");
+#endif
+	  if (recalcs == MAX_RECALCULATIONS)
+	    break;
+	  CalcBitCost (discard_heap, discard_num, keep_heap, keep_num,
+		       freqs_trans, escape, num_trans);
+	  heap_build (discard_heap, sizeof (*discard_heap),
+		      discard_num, BigSaving);
+	  heap_build (keep_heap, sizeof (keep_heap), keep_num, SmallSaving);
+	  recalc_reqd = 0;
+	  recalcs++;
+	}
+    }
+
+  Alloc_keep_discard ();
+
+  for (i = 0; i < discard_num; i++)
+    {
+      WordData *word = discard_heap[i];
+      int idx = KIND (word);
+      assert (IsWord (word) || IsNonWord (word));
+      discard[idx].wd[discard[idx].num_wds++] = word;
+    }
+  for (i = 0; i < keep_num; i++)
+    {
+      WordData *word = keep_heap[i];
+      int idx = KIND (word);
+      assert (IsWord (word) || IsNonWord (word));
+      keep[idx].wd[keep[idx].num_wds++] = word;
+    }
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+  SortAndCount_DictData (&discard[0]);
+  SortAndCount_DictData (&discard[1]);
+
+  assert (keep[0].num_wds + discard[0].num_wds == Num[0]);
+  assert (keep[1].num_wds + discard[1].num_wds == Num[1]);
+  Message ("Keep -> Discard        : %8d", keep_to_discard);
+  Message ("Discard -> Keep        : %8d", discard_to_keep);
+  Message ("Huffman Recalculations : %8d", recalcs);
+  if (recalcs == MAX_RECALCULATIONS)
+    Message ("WARNING: The number of recalculations == %d", MAX_RECALCULATIONS);
+}
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************
+ *****									*****
+ *****			Dictionary saving code				*****
+ *****									*****
+ ****************************************************************************/
+
+
+
+static void 
+Write_cdh (FILE * f, compression_dict_header * cdh)
+{
+  /* [RPAP - Jan 97: Endian Ordering] */
+  int i;
+  compression_dict_header tmp = *cdh;
+  HTONUL(tmp.dict_type);
+  HTONUL(tmp.novel_method);
+  for (i = 0; i < TEXT_PARAMS; i++)
+    HTONUL(tmp.params[i]);
+  HTONUL(tmp.num_words[0]);
+  HTONUL(tmp.num_words[1]);
+  HTONUL(tmp.num_word_chars[0]);
+  HTONUL(tmp.num_word_chars[1]);
+  HTONUL(tmp.lookback);
+
+  fwrite (&tmp, sizeof (tmp), 1, f);
+}
+
+
+static void 
+Write_words (FILE * f, DictData * dd)
+{
+  int i;
+  u_char *curr, *prev = NULL;
+  for (i = 0; i < dd->num_wds; i++)
+    {
+      int len;
+      curr = dd->wd[i]->word;
+      if (prev)
+	/* look for prefix match with prev string */
+	len = prefixlen (prev, curr);
+      else
+	len = 0;
+      fputc ((len << 4) + (curr[0] - len), f);
+      fwrite (curr + len + 1, sizeof (u_char), curr[0] - len, f);
+      prev = curr;
+    }
+
+}
+
+
+static int 
+Uncompressed_size (DictData * dd)
+{
+  int i, us;
+  for (us = i = 0; i < dd->num_wds; i++)
+    us += dd->wd[i]->word[0];
+  return us;
+}
+
+
+static u_long 
+Write_data (FILE * f, DictData * dd, int lookback)
+{
+  u_long mem_reqd;
+  huff_data *hd;
+  int i; 
+  u_long us = dd->num_wds;
+  long *freqs;
+  u_long huff_words_size[MAX_HUFFCODE_LEN + 1];
+  u_long lencounts[MAX_HUFFCODE_LEN + 1];
+  u_char *lastword[MAX_HUFFCODE_LEN + 1];
+
+  if (!(freqs = Xmalloc ((dd->num_wds) * sizeof (*freqs))))
+    FatalError (1, "Unable to allocate memory for freqs");
+
+  for (i = 0; i < dd->num_wds; i++)
+    {
+      freqs[i] = dd->wd[i]->freq;
+      us += dd->wd[i]->word[0];
+    }
+
+  if (!(hd = Generate_Huffman_Data (dd->num_wds, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  Xfree (freqs), freqs = NULL;
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+  HTONUL(us);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&us, sizeof (us), 1, f);
+  NTOHUL(us);  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+/* Calculate the amount of memory that will be required to store the text for
+   each different huffman code len. Every 1<<lookback words for each different
+   codelen length will not be prefixed by previous strings. */
+
+
+  bzero ((char *) &huff_words_size, sizeof (huff_words_size));
+  bzero ((char *) &lencounts, sizeof (lencounts));
+
+  mem_reqd = 0;
+
+  for (i = 0; i < dd->num_wds; i++)
+    {
+      int codelen = hd->clens[i];
+      u_char *word = dd->wd[i]->word;
+
+      if (!codelen)
+	FatalError (1, "The length of a code for a word was zero");
+
+      huff_words_size[codelen] += word[0] + 1;
+      mem_reqd += word[0] + (lookback != 0);
+#if 0
+      if ((lencounts[codelen] & ((1 << lookback) - 1)) == 0)
+	lastword[codelen] = word;
+      else
+	huff_words_size[codelen] -= prefixlen (lastword[codelen], word);
+#else
+      if ((lencounts[codelen] & ((1 << lookback) - 1)) != 0)
+	{
+	  int save = prefixlen (lastword[codelen], word);
+	  mem_reqd -= save;
+	  huff_words_size[codelen] -= save;
+	}
+      else
+	{
+	  mem_reqd += sizeof (u_char *);
+	}
+      lastword[codelen] = word;
+#endif
+      lencounts[codelen]++;
+    }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = hd->mincodelen; i < hd->maxcodelen + 1; i++)
+    HTONUL(huff_words_size[i]);
+
+  fwrite (huff_words_size + hd->mincodelen, sizeof (*huff_words_size),
+	  hd->maxcodelen - hd->mincodelen + 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = hd->mincodelen; i < hd->maxcodelen + 1; i++)
+    NTOHUL(huff_words_size[i]);
+
+  Write_words (f, dd);
+
+  Xfree (hd->clens);
+  Xfree (hd);
+
+  return mem_reqd;
+}
+
+
+
+static void 
+Write_charfreqs (FILE * f, DictData * dd, int words,
+		 int zero_freq_permitted)
+{
+  int j;
+  long freqs[256];
+  WordData **wd = dd->wd;
+  huff_data *hd;
+
+  bzero ((char *) freqs, sizeof (freqs));
+
+  for (j = 0; j < dd->num_wds; j++, wd++)
+    {
+      u_char *buf = (*wd)->word;
+      int len = *buf++;
+      for (; len; len--, buf++)
+	freqs[(u_long) (*buf)] += (*wd)->freq;
+    }
+
+  if (!zero_freq_permitted)
+    for (j = 0; j < 256; j++)
+      if (!freqs[j] && PESINAWORD (j) == words)
+	freqs[j] = 1;
+
+  if (!(hd = Generate_Huffman_Data (256, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+  Xfree (hd->clens);
+  Xfree (hd);
+}
+
+
+
+
+static void 
+Write_wordlens (FILE * f, DictData * dd, int zero_freq_permitted)
+{
+  int j;
+  long freqs[16];
+  WordData **wd = dd->wd;
+  huff_data *hd;
+
+  bzero ((char *) freqs, sizeof (freqs));
+
+  for (j = 0; j < dd->num_wds; j++, wd++)
+    freqs[(*wd)->word[0]] += (*wd)->freq;
+
+  if (!zero_freq_permitted)
+    for (j = 0; j < 16; j++)
+      if (!freqs[j])
+	freqs[j] = 1;
+
+  if (!(hd = Generate_Huffman_Data (16, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+
+  Xfree (hd->clens);
+  Xfree (hd);
+}
+
+
+
+static u_long 
+WriteOutWords (char *file_name, u_long type, int lookback)
+{
+  FILE *f;
+  int i;
+  u_long mem_reqd = 0;
+
+  compression_dict_header cdh;
+  f = create_file (file_name, TEXT_DICT_SUFFIX, "w+b",
+		   MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  bzero((char *) &cdh, sizeof(cdh));
+
+  cdh.dict_type = type;
+  cdh.novel_method = (type == MG_SEED_DICTIONARY) ? novel_method :
+    MG_NOVEL_HUFFMAN_CHARS;
+
+  cdh.num_words[1] = keep[1].num_wds;
+  cdh.num_words[0] = keep[0].num_wds;
+  cdh.num_word_chars[1] = Uncompressed_size (&keep[1]);
+  cdh.num_word_chars[0] = Uncompressed_size (&keep[0]);
+  cdh.lookback = lookback;
+
+  Write_cdh (f, &cdh);
+
+  for (i = 0; i < 2; i++)
+    switch (type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  mem_reqd += Write_data (f, &keep[i], lookback);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (keep[i].num_wds)
+	    {
+	      int j;
+	      WordData esc;
+	      esc.freq = 0;
+	      esc.word = (u_char *) "";
+	      keep[i].wd[keep[i].num_wds++] = &esc;
+	      for (j = 0; j < discard[i].num_wds; j++)
+		esc.freq += discard[i].wd[j]->freq;
+	      if (!esc.freq)
+		esc.freq++;
+	      mem_reqd += Write_data (f, &keep[i], lookback);
+	    }
+	  Write_charfreqs (f, &discard[i], i, 1);
+	  Write_wordlens (f, &discard[i], 1);
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (keep[i].num_wds)
+	    {
+	      int j;
+	      WordData esc;
+	      esc.freq = 0;
+	      esc.word = (u_char *) "";
+	      keep[i].wd[keep[i].num_wds++] = &esc;
+	      for (j = 0; j < all[i].num_wds; j++)
+		if (all[i].wd[j]->freq == 1)
+		  esc.freq++;
+	      if (!esc.freq)
+		esc.freq++;
+	      mem_reqd += Write_data (f, &keep[i], lookback);
+	    }
+	  switch (novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      Write_charfreqs (f, &all[i], i, 0);
+	      Write_wordlens (f, &all[i], 0);
+	      break;
+	    case MG_NOVEL_BINARY:
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    case MG_NOVEL_HYBRID_MTF:
+	      break;
+	    default:
+	      FatalError (1, "Bad novel method");
+	    }
+	}
+	break;
+      }
+  fclose (f);
+  return mem_reqd;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.1	(revision 16561)
@@ -0,0 +1,78 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_decompress_text 1 \*(Dt CITRI
+.SH NAME
+mg_decompress_text \- decompresses the compressed text of a collection
+.SH SYNOPSIS
+.B mg_decompress_text
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_decompress_text
+decompresses the compressed text of a collection, inserts paragraph and
+document markers, writing the output to
+.IR stdout ".  "
+This program was created
+to be used as a type of
+.BR mg_get (1).
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.text
+Compressed documents.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.invf.paragraph
+Paragraph counts for documents.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_passes (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_decompress_text.c	(revision 16561)
@@ -0,0 +1,167 @@
+#include "sysfuncs.h"
+
+#include "filestats.h"
+#include "netorder.h"
+#include "memlib.h"
+#include "messages.h"
+#include "huffman.h"
+#include "bitio_m_mem.h"
+#include "bitio_m.h"
+#include "bitio_stdio.h"
+#include "huffman_stdio.h"
+
+#include "mg_files.h"
+#include "mg.h"
+#include "backend.h"  /* compressed_dict struct */
+#include "text_get.h"
+#include "words.h"
+
+#define EOD         '\002'
+#define EOP         '\003'
+
+/* These are defined in mgquery, and they need to be here for backend.o */
+/* Messy I know but this was meant to be a quick hack :-) */
+#if defined(PARADOCNUM) || defined(NZDL)
+int *Paragraph = NULL;
+int Documents = 0;
+#endif
+
+int
+main (int argc, char **argv)
+{
+  FILE *text, *text_idx, *para;
+  File *text_comp_dict = NULL, *text_aux_dict = NULL, *text_fast_comp_dict = NULL;
+
+  int ch;
+  unsigned long start, finish, len;
+  u_long total_chars = 0;
+  u_char *c_buffer = 0;
+  int buf_len = -1;
+  u_char *uc_buffer = 0;
+  int ULen;
+  compression_dict *cd = NULL;
+  compressed_text_header cth;
+  u_long doc;
+  int count;
+  char *filename = "";
+  char path[512];
+  opterr = 0;
+  msg_prefix = argv[0];
+
+  while ((ch = getopt (argc, argv, "f:d:h")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	filename = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-h] [-d directory] -f name\n",
+		 argv[0]);
+	exit (1);
+      }
+
+  /* Open files */
+  text = open_file (filename, TEXT_SUFFIX, "rb", MAGIC_TEXT, MG_ABORT);
+  fread ((char *)&cth, sizeof (cth), 1, text);
+
+  text_idx = open_file (filename, TEXT_IDX_SUFFIX, "rb", MAGIC_TEXI, MG_ABORT);
+  fread ((char *)&cth, sizeof (cth), 1, text_idx);
+
+  NTOHUL(cth.num_of_docs);
+  NTOHD(cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(cth.num_of_words);
+  NTOHUL(cth.length_of_longest_doc);
+  NTOHD(cth.ratio);
+
+  para = open_file (filename, INVF_PARAGRAPH_SUFFIX, "rb", MAGIC_PARAGRAPH, MG_CONTINUE);
+
+  sprintf (path, FILE_NAME_FORMAT, get_basepath(), filename, TEXT_DICT_FAST_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(text_fast_comp_dict = Fopen (path, "rb", MAGIC_FAST_DICT)))
+    {
+      sprintf (path, FILE_NAME_FORMAT, get_basepath(), filename, TEXT_DICT_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+      text_comp_dict = Fopen (path, "rb", MAGIC_DICT);
+      if (!text_comp_dict)
+	FatalError (1, "Could not open compressed dictionary");
+      sprintf (path, FILE_NAME_FORMAT, get_basepath(), filename, TEXT_DICT_AUX_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+      text_aux_dict = Fopen (path, "rb", MAGIC_AUX_DICT);
+    }
+
+  cd = LoadCompDict (text_comp_dict, text_aux_dict, text_fast_comp_dict);
+
+
+  /* Uncompress text */
+
+  doc = 1;
+  fread ((char *)&start, sizeof (start), 1, text_idx);
+  if (para)
+    fread ((char *)&count, sizeof (count), 1, para);
+  else
+    count = 1;
+
+  for (;;)
+    {
+      fread ((char *) &finish, sizeof (finish), 1, text_idx);
+      len = finish - start;
+      
+      if ((int) len > buf_len)
+	{
+	  if (c_buffer)
+	    {
+	      Xfree (c_buffer);
+	      Xfree (uc_buffer);
+	    }
+	  if (!(c_buffer = Xmalloc (len)))
+	    FatalError (1, "Cannot allocate memory for compressed buffer");
+	  if (!(uc_buffer = Xmalloc ((int) (cth.ratio * 1.01 * len) + 100)))
+	    FatalError (1, "Cannot allocate memory for uncompressed buffer");
+	  buf_len = len;
+	}
+
+      fread (c_buffer, 1, len, text);
+      DecodeText (cd, c_buffer, len, uc_buffer, &ULen);
+      fwrite (uc_buffer, ULen, sizeof (u_char), stdout);
+      fflush (stdout);
+
+      total_chars += ULen;
+
+      if (++doc > cth.num_of_docs)
+	{
+	  if (para)
+	    fputc (EOP, stdout);
+	  fputc (EOD, stdout);
+	  fflush (stdout);
+	  break;
+	}
+
+      if (!--count)
+	{
+	  /* End of document */
+	  fputc (EOD, stdout);
+	  if (para)
+	    fread ((char *)&count, sizeof (count), 1, para);
+	  else
+	    count = 1;
+	}
+      else
+	fputc (EOP, stdout);
+      fflush (stdout);
+      start = finish;
+    }
+
+  /* Close files */
+
+  fclose (text);
+  fclose (text_idx);
+  if (para)
+    fclose (para);
+
+#if 0
+  printf ("\n#Total chars output = %lu\n", total_chars);
+#endif
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.c	(revision 16561)
@@ -0,0 +1,73 @@
+/**************************************************************************
+ *
+ * mg_errors.c -- Error related stuff for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+#include "mg_errors.h"
+
+
+
+int mg_errno = MG_NOERROR;
+
+char *mg_errorstrs[] =
+{
+  "No error",
+  "Out of memory",
+  "File \"%s\" not found",
+  "Bad magic number in \"%s\"",
+  "Error reading \"%s\"",
+  "MG_BUFTOOSMALL",
+  "Files required for level 2 and 3 inversion are missing"};
+
+
+/* [RJM 07/98: Memory Leak] -- rest of file */
+
+static char null_data[] = "";
+char *mg_error_data = null_data;
+
+
+void MgErrorData (char *s)
+{
+  /* free the current error string, unless it is the null string */
+  if ((mg_error_data != NULL) && (mg_error_data != null_data)) {
+    Xfree (mg_error_data);
+    mg_error_data = null_data;
+  }
+
+  /* make a copy of the string */
+  mg_error_data = Xstrdup (s);
+
+  /* if the alloc failed set the error to the null string */
+  if (!mg_error_data) mg_error_data = null_data;
+}
+
+
+void MgErrorDeinit (void)
+{
+  /* free the current error string, unless it is the null string */
+  if ((mg_error_data != NULL) && (mg_error_data != null_data)) {
+    Xfree (mg_error_data);
+    mg_error_data = null_data;
+  }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_errors.h	(revision 16561)
@@ -0,0 +1,39 @@
+/**************************************************************************
+ *
+ * mg_errors.h -- Error related stuff for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+
+extern int mg_errno;
+extern char *mg_error_data;
+extern char *mg_errorstrs[];
+
+
+#define MG_NOERROR 0
+#define MG_NOMEM 1
+#define MG_NOFILE 2
+#define MG_BADMAGIC 3
+#define MG_READERR 4
+#define MG_BUFTOOSMALL 5
+#define MG_INVERSION 6
+
+void MgErrorData (char *s);
+void MgErrorDeinit (void);
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_fast_comp_dict 1 \*(Dt CITRI
+.SH NAME
+mg_fast_comp_dict \- build a fast loading compression dictionary
+.SH SYNOPSIS
+.B mg_fast_comp_dict
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_fast_comp_dict
+builds a fast-loading compression dictionary from the standard
+compression dictionary.  The new dictionary has the property that
+.BR mgquery (1)
+can load it in very quickly, making the startup time for
+.BR mgquery (1)
+much shorter.  The new dictionary is larger on disk than the original,
+but they are no different when it comes to memory usage.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.dict
+Compression dictionary.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_fast_comp_dict.c	(revision 16561)
@@ -0,0 +1,801 @@
+/**************************************************************************
+ *
+ * mg_fast_comp_dict.c -- Program to generate a fast compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:06  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:44  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:34:57  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:55  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:47  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "filestats.h"
+#include "huffman.h"
+#include "timing.h"
+#include "messages.h"
+#include "memlib.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "local_strings.h"
+#include "mg.h"
+#include "text.h"
+#include "invf.h"
+#include "lists.h"
+#include "backend.h"
+#include "mg_files.h"
+#include "locallib.h"
+#include "words.h"
+
+
+
+#define ALIGN_SIZE(p,t) (((p) + (sizeof(t)-1)) & (~(sizeof(t)-1)))
+
+#define WORDNO(p, base) ((((char*)(p))-((char*)(base)))/sizeof(u_char*))
+#define FIXUP(p) (fixup[WORDNO(p,buffer)/8] |= (1<<(WORDNO(p, buffer) & 7)))
+
+#define IS_FIXUP(p) ((fixup[WORDNO(p,buffer)/8] & (1<<(WORDNO(p, buffer) & 7))) != 0)
+
+#define FIXUP_VALS(vals) do {						\
+	int i;								\
+	for (i=0; i < MAX_HUFFCODE_LEN+1; i++)				\
+	  FIXUP(&vals[i]);						\
+      } while(0)
+
+
+
+static u_long mem_for_comp_dict (char *filename);
+static void load_comp_dict (char *filename);
+static void save_fast_dict (char *filename);
+static void unfixup_buffer (void);
+
+
+static void *buffer;
+static void *cur;
+static u_char *fixup;
+static u_long mem, fixup_mem;
+
+int main (int argc, char **argv)
+{
+  ProgTime StartTime;
+  int ch;
+  char *filename = "";
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "f:d:h")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	filename = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file] [-d data directory] [-h]\n",
+		 argv[0]);
+	exit (1);
+      }
+  GetTime (&StartTime);
+
+  mem = mem_for_comp_dict (filename);
+
+  fixup_mem = (ALIGN_SIZE (mem, u_char *) / sizeof (u_char *) + 7) / 8;
+
+  cur = buffer = Xmalloc (mem);
+  bzero (buffer, mem);
+  fixup = Xmalloc (fixup_mem);
+  bzero (fixup, fixup_mem);
+
+  Message ("Estimated memory for fast_dict %u", mem);
+  Message ("Estimated memory for fixups %u", fixup_mem);
+
+  load_comp_dict (filename);
+
+  Message ("Actual memory for fast_dict %u", (char *) cur - (char *) buffer);
+
+  if ((u_long) cur > (u_long) buffer + mem)
+    FatalError (1, "The buffer was not big enough for the dictionary");
+
+  {
+    /* [RPAP - Jan 97: Endian Ordering] */
+    compression_dict *cd = (compression_dict *) buffer;
+    int i, which;
+
+    /* cfh */
+    for (which = 0; which <= 1; which++)
+      {
+	int j;
+	
+	HTONSI(cd->cfh[which]->hd.num_codes);
+	HTONSI(cd->cfh[which]->hd.mincodelen);
+	HTONSI(cd->cfh[which]->hd.maxcodelen);
+	for (j = 0; j < MAX_HUFFCODE_LEN + 1; j++)
+	  {
+	    HTONSI(cd->cfh[which]->hd.lencount[j]);
+	    HTONUL(cd->cfh[which]->hd.min_code[j]);
+	  }
+	HTONUL(cd->cfh[which]->uncompressed_size);
+	for (j = 0; j < MAX_HUFFCODE_LEN + 1; j++)
+	  HTONUL(cd->cfh[which]->huff_words_size[j]);
+      }
+    HTONUL(cd->MemForCompDict);
+    /* ad */
+    if (cd->cdh.novel_method == MG_NOVEL_BINARY ||
+	cd->cdh.novel_method == MG_NOVEL_DELTA ||
+	cd->cdh.novel_method == MG_NOVEL_HYBRID ||
+	cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+      for (which = 0; which <= 1; which++)
+	{
+	  int j;
+	  
+	  HTONUL(cd->ad->afh[which].num_frags);
+	  HTONUL(cd->ad->afh[which].mem_for_frags);
+	  for (j = 0; j < 33; j++)
+	    {
+	      HTONSI(cd->ad->blk_start[which][j]);
+	      HTONSI(cd->ad->blk_end[which][j]);
+	    }
+	}
+    /* cdh */
+    HTONUL(cd->cdh.dict_type);
+    HTONUL(cd->cdh.novel_method);
+    for (i = 0; i < TEXT_PARAMS; i++)
+      HTONUL(cd->cdh.params[which]);
+    HTONUL(cd->cdh.num_words[0]);
+    HTONUL(cd->cdh.num_words[1]);
+    HTONUL(cd->cdh.num_word_chars[0]);
+    HTONUL(cd->cdh.num_word_chars[1]);
+    HTONUL(cd->cdh.lookback);
+    HTONSI(cd->fast_loaded);
+  }
+
+  unfixup_buffer ();
+
+  save_fast_dict (filename);
+
+  Message ("%s", ElapsedTime (&StartTime, NULL));
+  return 0;
+}
+
+
+
+static void 
+unfixup_buffer ()
+{
+  u_long *p;
+  for (p = buffer; (u_long) p < (u_long) cur; p++)
+    {
+      if (IS_FIXUP (p))
+	*p = *p - (u_long) buffer;
+    }
+}
+
+
+
+
+static u_long 
+mem_for_aux_dict (compression_dict_header * cdh, char *filename)
+{
+  int i;
+  u_long mem = sizeof (auxiliary_dict);
+  FILE *aux;
+
+  aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
+		   MAGIC_AUX_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  for (i = 0; i <= 1; i++)
+    {
+      aux_frags_header afh;
+      fread (&afh, sizeof (afh), 1, aux);
+      NTOHUL(afh.num_frags);  /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(afh.mem_for_frags);  /* [RPAP - Jan 97: Endian Ordering] */
+      mem += afh.num_frags * sizeof (u_char *);
+      mem = ALIGN_SIZE (mem + afh.mem_for_frags, u_char *);
+      fseek (aux, afh.mem_for_frags, SEEK_CUR);
+    }
+  fclose (aux);
+
+  return mem;
+}
+
+
+
+static u_long 
+mem_for_words (FILE * dict, compression_dict_header * cdh,
+	       comp_frags_header * cfh)
+{
+  u_long mem = 0;
+  u_long i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+
+  lookback = cdh->lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; i++)
+    {
+      ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+      mem_reqd += cfh->huff_words_size[i];
+    }
+
+  mem += ptrs_reqd * sizeof (u_char *);
+  mem += (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **);
+  mem += mem_reqd;
+
+  for (i = 0; i < cfh->hd.num_codes; i++)
+    {
+      register int val;
+      for (val = getc (dict) & 0xf; val; val--)
+	getc (dict);
+    }
+  return ALIGN_SIZE (mem, u_char *);
+}
+
+
+
+
+static u_long 
+mem_for_comp_dict (char *filename)
+{
+  u_long mem = sizeof (compression_dict);
+  compression_dict_header cdh;
+  comp_frags_header cfh;
+  FILE *dict;
+  int which;
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
+		    MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread (&cdh, sizeof (cdh), 1, dict);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cdh.dict_type);
+  NTOHUL(cdh.novel_method);
+  for (i = 0; i < TEXT_PARAMS; i++)
+    NTOHUL(cdh.params[i]);
+  NTOHUL(cdh.num_words[0]);
+  NTOHUL(cdh.num_words[1]);
+  NTOHUL(cdh.num_word_chars[0]);
+  NTOHUL(cdh.num_word_chars[1]);
+  NTOHUL(cdh.lookback);
+
+  for (which = 0; which < 2; which++)
+    switch (cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  mem += sizeof (comp_frags_header);
+	  Read_cfh (dict, &cfh, NULL, NULL);
+
+	  /* Don't need to count the space for the clens of the huffman data */
+
+	  mem += mem_for_words (dict, &cdh, &cfh);
+	  if (cfh.hd.clens)
+	    Xfree (cfh.hd.clens);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  huff_data hd;
+	  if (cdh.num_words[which])
+	    {
+	      mem += sizeof (comp_frags_header);
+	      Read_cfh (dict, &cfh, NULL, NULL);
+
+	      /* Don't need to count the space for the clens of the
+	         huffman data */
+
+	      mem += mem_for_words (dict, &cdh, &cfh);
+	      if (cfh.hd.clens)
+		Xfree (cfh.hd.clens);
+
+	    }
+
+	  mem += sizeof (hd);
+	  Read_Huffman_Data (dict, &hd, NULL, NULL);
+	  if (hd.clens)
+	    Xfree (hd.clens);
+	  mem += hd.num_codes * sizeof (unsigned long);
+	  mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+	  mem += sizeof (hd);
+	  Read_Huffman_Data (dict, &hd, NULL, NULL);
+	  if (hd.clens)
+	    Xfree (hd.clens);
+	  mem += hd.num_codes * sizeof (unsigned long);
+	  mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  huff_data hd;
+	  if (cdh.num_words[which])
+	    {
+	      mem += sizeof (comp_frags_header);
+	      Read_cfh (dict, &cfh, NULL, NULL);
+
+	      /* Don't need to count the space for the clens of the
+	         huffman data */
+
+	      mem += mem_for_words (dict, &cdh, &cfh);
+	      if (cfh.hd.clens)
+		Xfree (cfh.hd.clens);
+
+	    }
+	  switch (cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      mem += sizeof (hd);
+	      Read_Huffman_Data (dict, &hd, NULL, NULL);
+	      if (hd.clens)
+		Xfree (hd.clens);
+	      mem += hd.num_codes * sizeof (unsigned long);
+	      mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+	      mem += sizeof (hd);
+	      Read_Huffman_Data (dict, &hd, NULL, NULL);
+	      if (hd.clens)
+		Xfree (hd.clens);
+	      mem += hd.num_codes * sizeof (unsigned long);
+	      mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+	      break;
+	    case MG_NOVEL_BINARY:
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    case MG_NOVEL_HYBRID_MTF:
+	      break;
+	    }
+	  break;
+	}
+      }
+  fclose (dict);
+
+  if (cdh.novel_method == MG_NOVEL_BINARY ||
+      cdh.novel_method == MG_NOVEL_DELTA ||
+      cdh.novel_method == MG_NOVEL_HYBRID ||
+      cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    mem += mem_for_aux_dict (&cdh, filename);
+
+  return ALIGN_SIZE (mem, u_char *);
+}
+
+
+
+void *
+getmem (u_long size, int align)
+{
+  void *res;
+  cur = (void *) (((u_long) cur + (align - 1)) & (~(align - 1)));
+  res = cur;
+  cur = (char *) cur + size;
+  if ((long) cur > (long) buffer + mem)
+    FatalError (1, "The buffer was not big enough for the dictionary");
+  return res;
+}
+
+
+
+
+
+
+
+
+
+
+
+static auxiliary_dict *
+LoadAuxDict (compression_dict_header * cdh,
+	     char *filename)
+{
+  auxiliary_dict *ad;
+  int i;
+  FILE *aux;
+
+  aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
+		   MAGIC_AUX_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  ad = getmem (sizeof (auxiliary_dict), sizeof (u_char *));
+
+  for (i = 0; i <= 1; i++)
+    {
+      int j;
+      u_char *pos;
+
+      fread (&ad->afh[i], sizeof (aux_frags_header), 1, aux);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(ad->afh[i].num_frags);
+      NTOHUL(ad->afh[i].mem_for_frags);
+
+      ad->word_data[i] = getmem (ad->afh[i].mem_for_frags, 1);
+      FIXUP (&ad->word_data[i]);
+
+      ad->words[i] = getmem (ad->afh[i].num_frags * sizeof (u_char *),
+			     sizeof (u_char *));
+      FIXUP (&ad->words[i]);
+
+      fread (ad->word_data[i], ad->afh[i].mem_for_frags, sizeof (u_char), aux);
+
+      pos = ad->word_data[i];
+      for (j = 0; j < ad->afh[i].num_frags; j++)
+	{
+	  ad->words[i][j] = pos;
+	  FIXUP (&ad->words[i][j]);
+	  pos += *pos + 1;
+	}
+      if (cdh->novel_method == MG_NOVEL_HYBRID ||
+	  cdh->novel_method == MG_NOVEL_HYBRID_MTF)
+	{
+	  int num;
+	  num = 1;
+	  ad->blk_start[i][0] = 0;
+	  ad->blk_end[i][0] = cdh->num_words[i] - 1;
+	  while (num < 33)
+	    {
+	      ad->blk_start[i][num] = ad->blk_end[i][num - 1] + 1;
+	      ad->blk_end[i][num] = ad->blk_start[i][num] +
+		(ad->blk_end[i][num - 1] - ad->blk_start[i][num - 1]) * 2;
+	      num++;
+	    }
+	}
+    }
+  fclose (aux);
+  return (ad);
+}
+
+
+
+
+
+static u_char ***
+ReadInWords (FILE * dict, compression_dict * cd,
+	     comp_frags_header * cfh, u_char ** escape)
+{
+  int i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+  int num_set[MAX_HUFFCODE_LEN + 1];
+  u_char *next_word[MAX_HUFFCODE_LEN + 1];
+  u_char **vals;
+  u_char ***values;
+  u_char word[MAXWORDLEN + 1];
+  u_char last_word[MAX_HUFFCODE_LEN + 1][MAXWORDLEN + 1];
+
+  lookback = cd->cdh.lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; i++)
+    {
+      ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+      mem_reqd += cfh->huff_words_size[i];
+    }
+
+  vals = getmem (ptrs_reqd * sizeof (*vals), sizeof (u_char *));
+
+  values = getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (u_char **), sizeof (u_char **));
+
+  next_word[0] = getmem (mem_reqd, sizeof (char));
+
+  cd->MemForCompDict += ptrs_reqd * sizeof (*vals) +
+    (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **) +
+    mem_reqd;
+
+  values[0] = vals;
+  FIXUP (&values[0]);
+  values[0][0] = next_word[0];
+  FIXUP (&values[0][0]);
+  for (i = 1; i <= cfh->hd.maxcodelen; i++)
+    {
+      int next_start = (values[i - 1] - vals) +
+      ((cfh->hd.lencount[i - 1] + ((1 << lookback) - 1)) >> lookback);
+      values[i] = &vals[next_start];
+      FIXUP (&values[i]);
+      next_word[i] = next_word[i - 1] + cfh->huff_words_size[i - 1];
+      values[i][0] = next_word[i];
+      FIXUP (&values[i][0]);
+    }
+
+  bzero ((char *) num_set, sizeof (num_set));
+
+  for (i = 0; i < cfh->hd.num_codes; i++)
+    {
+      register int val, copy;
+      register int len = cfh->hd.clens[i];
+      val = getc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      fread (word + copy + 1, sizeof (u_char), val, dict);
+      *word = val + copy;
+
+      if ((num_set[len] & ((1 << lookback) - 1)) == 0)
+	{
+	  values[len][num_set[len] >> lookback] = next_word[len];
+	  FIXUP (&values[len][num_set[len] >> lookback]);
+	  memcpy (next_word[len], word, *word + 1);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    {
+	      *escape = next_word[len];
+	      FIXUP (escape);
+	    }
+	  next_word[len] += *word + 1;
+	}
+      else
+	{
+	  copy = prefixlen (last_word[len], word);
+	  memcpy (next_word[len] + 1, word + copy + 1, *word - copy);
+	  *next_word[len] = (copy << 4) + (*word - copy);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    {
+	      *escape = next_word[len];
+	      FIXUP (escape);
+	    }
+	  next_word[len] += (*word - copy) + 1;
+	}
+      memcpy (last_word[len], word, *word + 1);
+      num_set[len]++;
+    }
+  if (cfh->hd.clens)
+    Xfree (cfh->hd.clens);
+  cfh->hd.clens = NULL;
+  return values;
+}
+
+
+static unsigned long **
+Generate_Fast_Huffman_Vals (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *fcode[MAX_HUFFCODE_LEN + 1];
+  unsigned long **values;
+  unsigned long *vals;
+
+  if (!data)
+    return (NULL);
+  vals = getmem (data->num_codes * sizeof (*vals), sizeof (long *));
+  values = getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *),
+		   sizeof (long *));
+
+  bzero ((char *) values, (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *));
+
+  if (mem)
+    *mem += data->num_codes * sizeof (*vals) +
+      (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+  fcode[0] = values[0] = &vals[0];
+  FIXUP (&values[0]);
+  for (i = 1; i <= data->maxcodelen; i++)
+    {
+      fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]];
+      FIXUP (&values[i]);
+    }
+
+  for (i = 0; i < data->num_codes; i++)
+    if (data->clens[i])
+      *fcode[(int) (data->clens[i])]++ = i;
+  return (values);
+}
+
+
+
+static void 
+load_comp_dict (char *filename)
+{
+  FILE *dict;
+  int which;
+  compression_dict *cd;
+
+  cd = getmem (sizeof (compression_dict), sizeof (u_char *));
+  cd->fast_loaded = 1;
+
+  dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
+		    MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  Read_cdh (dict, &cd->cdh, NULL, NULL);
+
+  for (which = 0; which < 2; which++)
+    switch (cd->cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  cd->cfh[which] = getmem (sizeof (*cd->cfh[which]), sizeof (u_char *));
+	  cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	  Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL);
+
+	  cd->values[which] = ReadInWords (dict, cd, cd->cfh[which], NULL);
+	  FIXUP (&cd->cfh[which]);
+	  FIXUP (&cd->values[which]);
+	  cd->escape[which] = NULL;
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  huff_data *hd;
+	  u_long **vals;
+	  if (cd->cdh.num_words[which])
+	    {
+	      cd->cfh[which] = getmem (sizeof (*cd->cfh[which]), sizeof (u_char *));
+	      cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	      Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL);
+	      cd->values[which] = ReadInWords (dict, cd, cd->cfh[which],
+					       &cd->escape[which]);
+	      FIXUP (&cd->cfh[which]);
+	      FIXUP (&cd->values[which]);
+	      FIXUP (&cd->escape[which]);
+	    }
+
+	  hd = getmem (sizeof (huff_data), sizeof (char *));
+	  cd->MemForCompDict += sizeof (huff_data);
+	  Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
+	  vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
+	  cd->chars_huff[which] = hd;
+	  FIXUP (&cd->chars_huff[which]);
+	  cd->chars_vals[which] = vals;
+	  FIXUP (&cd->chars_vals[which]);
+	  if (hd->clens)
+	    Xfree (hd->clens);
+	  hd->clens = NULL;
+
+	  hd = getmem (sizeof (huff_data), sizeof (char *));
+	  cd->MemForCompDict += sizeof (huff_data);
+	  Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
+	  vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
+	  cd->lens_huff[which] = hd;
+	  FIXUP (&cd->lens_huff[which]);
+	  cd->lens_vals[which] = vals;
+	  FIXUP (&cd->lens_vals[which]);
+	  if (hd->clens)
+	    Xfree (hd->clens);
+	  hd->clens = NULL;
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  huff_data *hd;
+	  u_long **vals;
+	  if (cd->cdh.num_words[which])
+	    {
+	      cd->cfh[which] = getmem (sizeof (*cd->cfh[which]), sizeof (u_char *));
+	      cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	      Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL);
+	      cd->values[which] = ReadInWords (dict, cd, cd->cfh[which],
+					       &cd->escape[which]);
+	      FIXUP (&cd->cfh[which]);
+	      FIXUP (&cd->values[which]);
+	      FIXUP (&cd->escape[which]);
+	    }
+	  switch (cd->cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      hd = getmem (sizeof (huff_data), sizeof (char *));
+	      cd->MemForCompDict += sizeof (huff_data);
+	      Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
+	      vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
+	      cd->chars_huff[which] = hd;
+	      FIXUP (&cd->chars_huff[which]);
+	      cd->chars_vals[which] = vals;
+	      FIXUP (&cd->chars_vals[which]);
+	      if (hd->clens)
+		Xfree (hd->clens);
+	      hd->clens = NULL;
+
+	      hd = getmem (sizeof (huff_data), sizeof (char *));
+	      cd->MemForCompDict += sizeof (huff_data);
+	      Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
+	      vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
+	      cd->lens_huff[which] = hd;
+	      FIXUP (&cd->lens_huff[which]);
+	      cd->lens_vals[which] = vals;
+	      FIXUP (&cd->lens_vals[which]);
+	      if (hd->clens)
+		Xfree (hd->clens);
+	      hd->clens = NULL;
+	      break;
+	    case MG_NOVEL_BINARY:
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    case MG_NOVEL_HYBRID_MTF:
+	      break;
+	    }
+	  break;
+	}
+      }
+  fclose (dict);
+
+
+  if (cd->cdh.novel_method == MG_NOVEL_BINARY ||
+      cd->cdh.novel_method == MG_NOVEL_DELTA ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    {
+      cd->ad = LoadAuxDict (&cd->cdh, filename);
+      FIXUP (&cd->ad);
+    }
+}
+
+
+static void 
+save_fast_dict (char *filename)
+{
+  FILE *fdict;
+
+  fdict = create_file (filename, TEXT_DICT_FAST_SUFFIX, "wb",
+		       MAGIC_FAST_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  {
+    u_long *p;
+    for (p = buffer; (u_long) p < (u_long) cur; p++)
+      {
+	if (IS_FIXUP (p))
+	  HTONUL(*p);
+      }
+  }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(mem);
+  HTONUL(fixup_mem);
+
+  fwrite (&mem, sizeof (mem), 1, fdict);
+  fwrite (&fixup_mem, sizeof (fixup_mem), 1, fdict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(mem);
+  NTOHUL(fixup_mem);
+
+  fwrite (buffer, sizeof (u_char), mem, fdict);
+  fwrite (fixup, sizeof (u_char), fixup_mem, fdict);
+
+  fclose (fdict);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.c	(revision 16561)
@@ -0,0 +1,261 @@
+/**************************************************************************
+ *
+ * mg_files.c -- Routines for handling files for the auxillary programs
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+
+
+/* This must contain a valid path without a trailing slash */
+static char *basepath = NULL;
+
+
+
+/* This sets the base path for all file operations */
+void 
+set_basepath (const char *bp)
+{
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  basepath = "";
+#else
+  char *s;
+  /* Free the memory for the base path if it has already been allocated */
+  if (basepath)
+    {
+      Xfree (basepath);
+      basepath = NULL;
+    }
+
+  if (!bp || *bp == '\0')
+    bp = ".";
+
+  s = strrchr (bp, '/');
+  if (s && *(s + 1) == '\0')
+    {
+      basepath = Xmalloc (strlen (bp) + 2);
+      if (!basepath)
+	return;
+      strcpy (basepath, bp);
+      strcat (basepath, ".");
+    }
+  else
+    basepath = Xstrdup (bp);
+#endif
+}
+
+
+
+/* return the currently defined basepath */
+char *
+get_basepath (void)
+{
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  return basepath;
+}
+
+
+
+/* This generates the name of a file. It places the name in the buffer
+   specified or if that is NULL it uses a static buffer. */
+char *
+make_name (const char *name, const char *suffix, char *buffer)
+{
+  static char path[512];
+  if (!buffer)
+    buffer = path;
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (buffer, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return buffer;
+}
+
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+open_named_file (const char *name, const char *mode,
+		 u_long magic_num, int err_mode)
+{
+  unsigned long magic;
+  FILE *f = NULL;
+  char *err;
+  f = fopen (name, mode);
+
+  if (!f)
+    {
+      err = "Unable to open \"%s\"";
+      goto error;
+    }
+
+  if (magic_num)
+    {
+      if (fread (&magic, sizeof (magic), 1, f) != 1)
+	{
+	  err = "No magic number \"%s\"";
+	  goto error;
+	}
+
+      NTOHUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+
+      if (!IS_MAGIC (magic))
+	{
+	  err = "No MG magic number \"%s\"";
+	  goto error;
+	}
+
+      if (magic != magic_num)
+	{
+	  err = "Wrong MG magic number \"%s\"";
+	  goto error;
+	}
+    }
+  return f;
+
+error:
+  if (f)
+    fclose (f);
+  if (err_mode == MG_ABORT)
+    FatalError (1, err, name);
+  if (err_mode == MG_MESSAGE)
+    Message (err, name);
+  return NULL;
+}
+
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+open_file (const char *name, const char *suffix, const char *mode,
+	   u_long magic_num, int err_mode)
+{
+  char path[512];
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (path, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return open_named_file (path, mode, magic_num, err_mode);
+}
+
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+create_named_file (const char *name, const char *mode,
+		   u_long magic_num, int err_mode)
+{
+  FILE *f = NULL;
+  char *err;
+  f = fopen (name, mode);
+
+  if (!f)
+    {
+      err = "Unable to open \"%s\"";
+      goto error;
+    }
+
+  if (magic_num)
+    HTONUL(magic_num);  /* [RPAP - Jan 97: Endian Ordering] */
+    if (fwrite (&magic_num, sizeof (magic_num), 1, f) != 1)
+      {
+	err = "Couldn't write magic number \"%s\"";
+	goto error;
+      }
+
+  return f;
+
+error:
+  if (f)
+    fclose (f);
+  if (err_mode == MG_ABORT)
+    FatalError (1, err, name);
+  if (err_mode == MG_MESSAGE)
+    Message (err, name);
+  return NULL;
+}
+
+
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+create_file (const char *name, const char *suffix, const char *mode,
+	     u_long magic_num, int err_mode)
+{
+  char path[512];
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (path, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return create_named_file (path, mode, magic_num, err_mode);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_files.h	(revision 16561)
@@ -0,0 +1,329 @@
+/**************************************************************************
+ *
+ * mg_files.h -- Routines for handling files for the auxillary programs
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 MG_FILES_H
+#define MG_FILES_H
+
+#include "sysfuncs.h"
+
+/* Magic numbers for the different types of files */
+
+#define GEN_MAGIC(a,b,c,d) ((unsigned long)(((a)<<24) + ((b)<<16) + \
+					    ((c)<<8) + (d)))
+
+#define MAGIC_XXXX		GEN_MAGIC('M','G', 0 , 0)
+#define MAGIC_STATS_DICT	GEN_MAGIC('M','G','S','D')
+#define MAGIC_AUX_DICT		GEN_MAGIC('M','G','A','D')
+#define MAGIC_FAST_DICT		GEN_MAGIC('M','G','F','D')
+#define MAGIC_DICT		GEN_MAGIC('M','G','D', 0 )
+#define MAGIC_STEM_BUILD	GEN_MAGIC('M','G','S', 0 )
+#define MAGIC_HASH		GEN_MAGIC('M','G','H', 0 )
+#define MAGIC_STEM		GEN_MAGIC('M','G','s', 0 )
+#define MAGIC_CHUNK		GEN_MAGIC('M','G','C', 0 )
+#define MAGIC_CHUNK_TRANS	GEN_MAGIC('M','G','c', 0 )
+#define MAGIC_TEXT		GEN_MAGIC('M','G','T', 0 )
+#define MAGIC_TEXI		GEN_MAGIC('M','G','t', 0 )
+#define MAGIC_TEXI_WGT		GEN_MAGIC('M','G','t','W')
+#define MAGIC_INVF		GEN_MAGIC('M','G','I', 0 )
+#define MAGIC_INVI		GEN_MAGIC('M','G','i', 0 )
+#define MAGIC_WGHT		GEN_MAGIC('M','G','W', 0 )
+#define MAGIC_WGHT_APPROX	GEN_MAGIC('M','G','w', 0 )
+#define MAGIC_PARAGRAPH		GEN_MAGIC('M','G','P', 0 )
+/* [RPAP - Jan 97: Stem Index Change] */
+#define MAGIC_STEM_1            GEN_MAGIC('M','G','s','1')
+#define MAGIC_STEM_2            GEN_MAGIC('M','G','s','2')
+#define MAGIC_STEM_3            GEN_MAGIC('M','G','s','3')
+
+#define IS_MAGIC(a) ((((u_long)(a)) & 0xffff0000) == MAGIC_XXXX)
+
+
+/* err_mode values for open_file and create_file */
+#define MG_ABORT 0
+#define MG_MESSAGE 1
+#define MG_CONTINUE 2
+
+
+
+
+
+/* File suffixes */
+
+
+/* The compression dictionary built by txt.pass1 */
+#ifdef SHORT_SUFFIX
+# define TEXT_STATS_DICT_SUFFIX	 ".tsd"
+#else
+# define TEXT_STATS_DICT_SUFFIX	 ".text.stats"
+#endif
+
+/* The compression dictionary built by text.pass1 and comp_dict.process */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_SUFFIX	 ".td"
+#else
+# define TEXT_DICT_SUFFIX	 ".text.dict"
+#endif
+
+/* The compression dictionary built by mg_make_fast_dict */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_FAST_SUFFIX	 ".tdf"
+#else
+# define TEXT_DICT_FAST_SUFFIX	 ".text.dict.fast"
+#endif
+
+/* The auxilary dictionary built by text.pass2 */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_AUX_SUFFIX	 ".tda"
+#else
+# define TEXT_DICT_AUX_SUFFIX	 ".text.dict.aux"
+#endif
+
+/* The compressed text build by text.pass2 */
+#ifdef SHORT_SUFFIX
+# define TEXT_SUFFIX		 ".t"
+#else
+# define TEXT_SUFFIX		 ".text"
+#endif
+
+/* The combined compressed text index and document weight file */
+#ifdef SHORT_SUFFIX
+# define TEXT_IDX_WGT_SUFFIX	 ".tiw"
+#else
+# define TEXT_IDX_WGT_SUFFIX	 ".text.idx.wgt"
+#endif
+
+/* The compressed text index file */
+#ifdef SHORT_SUFFIX
+# define TEXT_IDX_SUFFIX	 	".ti"
+#else
+# define TEXT_IDX_SUFFIX	 	".text.idx"
+#endif
+
+/* The dictionary of stemmed words build by invf.pass1 and ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_SUFFIX         ".id"
+#else
+# define INVF_DICT_SUFFIX         ".invf.dict"
+#endif
+
+/* The dictionary of stemmed words build by stem.process */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_SUFFIX ".idb"
+#else
+# define INVF_DICT_BLOCKED_SUFFIX ".invf.dict.blocked"
+#endif
+
+/* The exact document weights file build by make.weights, invf.pass2,
+   or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define WEIGHTS_SUFFIX           ".w"
+#else
+# define WEIGHTS_SUFFIX           ".weight"
+#endif
+
+/* The approximate weights file built by make.weights */
+#ifdef SHORT_SUFFIX
+# define APPROX_WEIGHTS_SUFFIX    ".wa"
+#else
+# define APPROX_WEIGHTS_SUFFIX    ".weight.approx"
+#endif
+
+/* The inverted file build by invf.pass2 or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define INVF_SUFFIX              ".i"
+#else
+# define INVF_SUFFIX              ".invf"
+#endif
+
+/* The inverted file index build by invf.pass2 or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define INVF_IDX_SUFFIX          ".ii"
+#else
+# define INVF_IDX_SUFFIX          ".invf.idx"
+#endif
+
+/* The inverted file chunk descriptor built by ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_CHUNK_SUFFIX        ".ic"
+#else
+# define INVF_CHUNK_SUFFIX        ".invf.chunk"
+#endif
+
+/* The word index translation file built by ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_CHUNK_TRANS_SUFFIX  ".ict"
+#else
+# define INVF_CHUNK_TRANS_SUFFIX  ".invf.chunk.trans"
+#endif
+
+/* The hashed stemmed dictionary built by make.perf_hash */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_HASH_SUFFIX    ".idh"
+#else
+# define INVF_DICT_HASH_SUFFIX    ".invf.dict.hash"
+#endif
+
+/* The paragraph descriptior file built by invf.pass1 or ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_PARAGRAPH_SUFFIX    ".ip"
+#else
+# define INVF_PARAGRAPH_SUFFIX    ".invf.paragraph"
+#endif
+
+/* The trace file build by mg.builder. */
+#ifdef SHORT_SUFFIX
+# define TRACE_SUFFIX             ".trc"
+#else
+# define TRACE_SUFFIX             ".trace"
+#endif
+
+/* The compression stats file build by mg.builder. */
+#ifdef SHORT_SUFFIX
+# define COMPRESSION_STATS_SUFFIX ".cs"
+#else
+# define COMPRESSION_STATS_SUFFIX ".compression.stats"
+#endif
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* The casefolded index into the stemmed dictionary */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_1_SUFFIX ".ib1"
+#else
+# define INVF_DICT_BLOCKED_1_SUFFIX ".invf.dict.blocked.1"
+#endif
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* The stemmed index into the stemmed dictionary */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_2_SUFFIX ".ib2"
+#else
+# define INVF_DICT_BLOCKED_2_SUFFIX ".invf.dict.blocked.2"
+#endif
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* The casefolded and stemmed  index into the stemmed dictionary */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_3_SUFFIX ".ib3"
+#else
+# define INVF_DICT_BLOCKED_3_SUFFIX ".invf.dict.blocked.3"
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+#define FILE_NAME_FORMAT "%s%s%s"
+#else
+#define FILE_NAME_FORMAT "%s/%s%s"
+#endif
+
+
+/* This sets the base path for all file operations */
+void set_basepath (const char *bp);
+
+
+/* return the currently defined basepath */
+char *get_basepath (void);
+
+
+
+
+/* This generates the name of a file. It places the name in the buffer
+   specified or if that is NULL it uses a static buffer. */
+char *make_name (const char *name, const char *suffix, char *buffer);
+
+
+
+
+
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *open_named_file (const char *name, const char *mode,
+		       u_long magic_num, int err_mode);
+
+
+
+
+/* This will open the specified file and check its magic number.
+
+   err_mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *open_file (const char *name, const char *suffix, const char *mode,
+		 u_long magic_num, int err_mode);
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *create_named_file (const char *name, const char *mode,
+			 u_long magic_num, int err_mode);
+
+
+
+/* This will create the specified file and set its magic number.
+
+   err_mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *create_file (const char *name, const char *suffix, const char *mode,
+		   u_long magic_num, int err_mode);
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.1	(revision 16561)
@@ -0,0 +1,95 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_hilite_words 1 \*(Dt CITRI
+.SH NAME
+mg_hilite_words \- display text and highlight particular words which it contains
+.SH SYNOPSIS
+.B mg_hilite_words
+.if n .ti +9n
+[
+.B --stem_method [0-3]
+]
+.if n .ti +9n
+[
+.B --stemmer [english|lovin|french|simplefrench]
+]
+.if n .ti +9n
+[
+.B --style [bold|underline|italic|emphasis|strong]
+]
+.if n .ti +9n
+[
+.B --pager [less|more|html|???]
+]
+.if n .ti +9n
+.I list-of-words-to-highlight
+
+.SH DESCRIPTION
+.B mg_hilite_words
+reads text from stdin and outputs it into a pager such as
+less. It's command arguments include a list of stemmed words
+which if extracted from the text will be highlighted on the output
+using a highlighting style.
+
+It has been extended to output the highlighted text using html
+straight to stdout.
+
+.SH OPTIONS
+Options may appear in any order, but the
+.IR list-of-words-to-highlight ,
+must be last.
+.TP
+.B --stem_method [0-3]
+This option allows one to set the stemming method for the extraction of
+the words from the incoming text.
+This should match the way the inverted file index was created.
+The default is 3, which is fold and stem.
+.TP
+.B --stemmer [english|lovin|french|simplefrench]
+This option allows you to select the stemmer to use.  The
+default is the English stemmer.
+.TP
+.B --style [bold|underline|italic|emphasis|strong]
+This option sets the highlighting style.
+.IR Bold 
+and 
+.IR underline
+are utilised by the pager or for html output.
+.IR Italic,
+.IR emphasis,
+and
+.IR strong
+can only be used for html output.
+.TP
+.B --pager [less|more|html|???]
+One can use the standard 
+.IR less
+or
+.IR more
+pages for displaying the highlighted text.
+Or one can generate highlighted html using the paging setting of
+.IR html.
+One can also specify a custom pager of their own choosing (i.e. ???).
+.TP
+.B list-of-words-to-highlight
+Each word is taken as a command line argument.
+Each word should already be stemmed and folded according to
+the stem option.
+.SH USAGE
+This program was written primarily to be called by 
+.IR mgquery(1)
+.
+When the user selects the mode of "hilite" then this program
+is called for displaying the query-result documents instead of
+just the pager.
+.SH "SEE ALSO"
+.na
+.BR mgquery (1).
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_hilite_words.c	(revision 16561)
@@ -0,0 +1,693 @@
+/**************************************************************************
+ *
+ * mg_hilite_words -- display text and highlight particular words which 
+ *                    it contains
+ * Copyright (C) 1994 Tim Shimmin
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "getopt.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "stemmer.h"
+#include "words.h"
+
+
+/*
+ * Description
+ * -----------
+ *   Hilite_words reads text from stdin and outputs it into a pager such as
+ *   less. It's command arguments include a list of stemmed words 
+ *   which if extracted from the text will be highlighted on the output.
+ *
+ * Implementation
+ * --------------
+ *   Extracting of words - using PARSE_STEM_WORD & stemmer
+ *   
+ *   Highlighting of words - for standard pager:
+ *                             using back-space character code
+ *                             e.g. bolded a = "a\ba", underlined a = "_\ba"
+ *                         - for pager==html:
+ *                             use some of the standard html character 
+ *                             formatting tags
+ *
+ *   Storage of words - using hashtable (set) of size equalling a constant
+ *                      times the number of words
+ * 
+ * Usage
+ * -----
+ *   mg_hilite_words --stem_method [0-3] 
+ *                   --style [bold|underline|italic|emphasis|strong]
+ *                   --pager [less|more|html|???]
+ *                   --terminator [terminator-string]
+ *                   list-of-words-to-highlight
+ */
+
+/*
+ * Modifications:
+ *
+ * 21/Apr/95: To handle outputting html tags to stdout
+ *
+ */
+
+/* --- constants --- */
+
+/* highlighting styles */
+#define HILITE_MAX      5	/* the number of styles */
+#define BOLD 		0
+#define UNDERLINE	1
+#define ITALIC     	2
+#define EMPHASIS     	3
+#define STRONG     	4
+
+/* maximum length of line buffer */
+#define MAX_LINE_BUFFER 200
+
+/* set pager to this to get html hilited text to stdout */
+#define HTML_OUT "html"
+
+/* output types */
+#define PAGER 0
+#define HTML  1
+
+/* --- types --- */
+
+typedef u_char *Word;
+
+/* --- globals --- */
+
+/* keep in synch with constants */
+static char *hilite_names[] =
+{"bold", "underline", "italic", "emphasis", "strong"};
+static char *hilite_tags[] =
+{"B", "", "I", "EM", "STRONG"};
+static short hilite_style = BOLD;
+static char *pager = "less";
+static int stemmer_num = 0;	/* Lovin's stemmer */
+static int stem_method = 3;	/* fold & stem */
+static char **word_list;
+static int num_of_words = 0;
+static int output_type = PAGER;
+static char *terminator = NULL;
+
+/* --- prototypes --- */
+
+static Word copy_c_word (char *w);
+static void process_words (char **words, int num_of_words);
+static int get_line (u_char * line, int n, FILE * stream);
+static void process_text (FILE * input_file, FILE * output_file);
+static void copy_word (u_char * w1, u_char * w2);
+static void process_buffer (u_char * s_in, int len, FILE * output_file);
+static void output_word (u_char * s_start, u_char * s_finish, FILE * output_file);
+static void output_hilite_word (u_char * s_start, u_char * s_finish,
+				FILE * output_file);
+static void process_args (int argc, char *argv[]);
+static void print_line (u_char * line, int n, FILE * stream);
+
+
+/******************************** word set ************************************/
+
+#include "hash.h"
+#include "locallib.h"
+
+#define SIZE_FACTOR 2
+
+typedef Word *WordSet;
+static WordSet set_of_words = NULL;
+static int hash_size = 0;
+
+/* prototypes - set routines */
+static void set_create (int num_of_words);
+static void set_add (Word word);
+static int set_member (Word word);
+static void set_print (void);
+
+/* =========================================================================
+ * Function: set_print
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+set_print (void)
+{
+  int i = 0;
+
+  for (i = 0; i < hash_size; i++)
+    {
+      Word word = set_of_words[i];
+
+      if (word)
+	{
+	  int len = *word++;
+
+	  fprintf (stderr, "[%d] = ", i);
+	  while (len--)
+	    fputc (*word++, stderr);
+	  fputc ('\n', stderr);
+	}
+    }
+
+}
+
+
+/* =========================================================================
+ * Function: set_create
+ * Description: 
+ *      Allocate memory for the set
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+set_create (int num_of_words)
+{
+  WordSet set;
+
+  hash_size = prime (num_of_words * SIZE_FACTOR);
+  set = (WordSet) malloc (hash_size * sizeof (Word));
+  if (!set)
+    FatalError (1, "Runout of memory for word hashtable");
+  bzero ((char *) set, hash_size * sizeof (Word));  /* [RPAP - Feb 97: WIN32 Port] */
+
+  set_of_words = set;
+
+}
+
+/* =========================================================================
+ * Function: set_add
+ * Description: 
+ *      Add a string element to the set.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+set_add (Word word)
+{
+  int hash_val;
+  int hash_step;
+
+  HASH (hash_val, hash_step, word, hash_size);
+
+  /* loop around in case of collisions and need to step */
+  while (1)
+    {
+
+      Word entry = set_of_words[hash_val];
+
+      /* if doesn't exist then */
+      if (!entry)
+	{
+	  set_of_words[hash_val] = word;
+	  break;
+	}
+
+      /* if we have a matching word */
+      if (compare (entry, word) == 0)
+	break;
+
+      /* if collides with a different word */
+      hash_val = (hash_val + hash_step) % hash_size;
+
+    }
+}
+
+
+/* =========================================================================
+ * Function: set_member
+ * Description: 
+ *      Tests whether a string is a member of the set
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static int
+set_member (Word word)
+{
+  int hash_val;
+  int hash_step;
+
+  HASH (hash_val, hash_step, word, hash_size);
+
+  /* loop around in case of collisions and need to step */
+  while (1)
+    {
+
+      Word entry = set_of_words[hash_val];
+
+      /* if doesn't exist then */
+      if (!entry)
+	return 0;
+
+      /* if we have a matching word */
+      if (compare (entry, word) == 0)
+	return 1;
+
+      /* if collides with a different word */
+      hash_val = (hash_val + hash_step) % hash_size;
+
+    }
+}
+
+/******************************** end of word set ******************************/
+
+
+
+/* =========================================================================
+ * Function: copy_c_word
+ * Description: 
+ *      Allocate enough memory and copy word over
+ * Input: 
+ *      w = null terminated string (c_word)
+ * Output: 
+ *      word with length in 1st byte
+ * ========================================================================= */
+
+static Word
+copy_c_word (char *w)
+{
+  int len = strlen (w);
+  Word w_copy = (Word) malloc (len + 1);
+  Word w_ptr = NULL;
+  int j = 0;
+
+  if (!w_copy)
+    FatalError (1, "Not enough memory to copy a word");
+
+  w_copy[0] = len;
+  w_ptr = w_copy + 1;
+  for (j = 0; j < len; j++)
+    *w_ptr++ = *w++;
+
+  return w_copy;
+}
+
+/* =========================================================================
+ * Function: process_words
+ * Description: 
+ *      Go through the stemmed words and add to word set
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+process_words (char **words, int num_of_words)
+{
+  int i = 0;
+
+  set_create (num_of_words);
+
+  for (i = 0; i < num_of_words; i++)
+    {
+      Word word = copy_c_word (words[i]);
+      set_add (word);
+    }
+
+}
+
+/* =========================================================================
+ * Function: get_line
+ * Description: 
+ *      Equivalent of fgets for u_char*.
+ *      But returns length of read-in line.
+ *      Expects to see a '\n' before an EOF
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static int
+get_line (u_char * line, int n, FILE * stream)
+{
+  int i = 0;
+  int ch = '\0';
+
+  while (1)
+    {
+      if (i == n)
+	return i;
+
+      ch = fgetc (stream);
+
+      if (ch == EOF)
+	{
+	  if (!feof (stream))
+	    FatalError (1, "Error on reading a line from stdin");
+	  return EOF;
+	}
+
+      if (ch == '\n')
+	return i;
+
+      *line++ = ch;
+      i++;
+    }
+
+}
+
+/* =========================================================================
+ * Function: print_line
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+print_line (u_char * line, int n, FILE * stream)
+{
+
+  while (n--)
+    {
+      fputc (*line++, stream);
+    }
+  fputc ('\n', stream);
+
+}
+
+/* =========================================================================
+ * Function: process_text
+ * Description: 
+ *      Go through the text from input_file and highlight to output_file 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+process_text (FILE * input_file, FILE * output_file)
+{
+  static u_char line_buffer[MAX_LINE_BUFFER];
+
+
+  while (1)
+    {
+      int len = get_line (line_buffer, MAX_LINE_BUFFER, input_file);
+
+      if (len == EOF)
+	break;
+      process_buffer (line_buffer, len, output_file);
+
+    }
+}
+
+/* =========================================================================
+ * Function: copy_word
+ * Description: 
+ *      Copies w2 into w1. Assumes both have storage allocated.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+copy_word (u_char * w1, u_char * w2)
+{
+  int i;
+  int len = w2[0];
+
+  for (i = 0; i <= len; i++)
+    *w1++ = *w2++;
+
+}
+
+/* =========================================================================
+ * Function: process_buffer
+ * Description: 
+ *      Parse & stem words of line buffer
+ *      Based on the usage of PARSEing in other mg files.
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+static void
+process_buffer (u_char * s_in, int len, FILE * output_file)
+{
+  u_char *end = s_in + len - 1;
+  u_char *s_start = NULL;
+
+  if (!inaword (s_in, end))
+    {
+      s_start = s_in;
+      PARSE_NON_STEM_WORD (s_in, end);
+      output_word (s_start, s_in - 1, output_file);
+    }
+
+  while (s_in <= end)
+    {
+      u_char word[MAXSTEMLEN + 1];
+
+      s_start = s_in;
+      PARSE_STEM_WORD (word, s_in, end);
+
+      stemmer (stem_method, stemmer_num, word);
+
+      if (set_member (word))	/* output with highlighting */
+	{
+	  output_hilite_word (s_start, s_start + word[0] - 1, output_file);
+	  s_start += word[0];	/* step over hilited output */
+	}
+      output_word (s_start, s_in - 1, output_file);
+
+      s_start = s_in;
+      PARSE_NON_STEM_WORD (s_in, end);
+      output_word (s_start, s_in - 1, output_file);
+
+    }				/*while */
+
+  fputc ('\n', output_file);
+  fflush (output_file);
+
+}				/*process_buffer */
+
+/* =========================================================================
+ * Function: output_word
+ * Description: 
+ *      Output a word which lies from s_start to s_finish in buffer
+ * Input: 
+ *      s_start = ptr to 1st char
+ *      s_finish = ptr to last char
+ * Output: 
+ * ========================================================================= */
+
+static void
+output_word (u_char * s_start, u_char * s_finish, FILE * output_file)
+{
+  while (s_start <= s_finish)
+    {
+      fputc (*s_start++, output_file);
+    }
+}
+
+
+/* =========================================================================
+ * Function: output_hilite_word
+ * Description: 
+ *      Highlight a word (with length in 1st byte)
+ *      Pager highlighting:
+ *        Highlighting is either by bolding or underlining using
+ *        the method used by UNIX utilities More(1) and Less(1)
+ *      HTML highlighting:
+ *        use the appropriate start and end tags around the word
+ * ========================================================================= */
+
+static void
+output_hilite_word (u_char * s_start, u_char * s_finish, FILE * output_file)
+{
+
+  if (output_type == HTML)
+    {
+      char *hilite_tag = hilite_tags[hilite_style];
+
+      /* print start tag */
+      fprintf (output_file, "<%s>", hilite_tag);
+
+      output_word (s_start, s_finish, output_file);
+
+      /* print end tag */
+      fprintf (output_file, "</%s>", hilite_tag);
+    }
+
+  else
+    /* PAGER */
+    {
+      /* use backspaces around each letter */
+      while (s_start <= s_finish)
+	{
+	  switch (hilite_style)
+	    {
+	    case BOLD:
+	      fputc (*s_start, output_file);
+	      fputc ('\b', output_file);
+	      fputc (*s_start, output_file);
+	      break;
+	    case UNDERLINE:
+	      fputc ('_', output_file);
+	      fputc ('\b', output_file);
+	      fputc (*s_start, output_file);
+	      break;
+	    default:
+	      fputc (*s_start, output_file);
+	    }
+	  s_start++;
+	}			/*while */
+    }
+}
+
+/* =========================================================================
+ * Function: process_args
+ * Description: 
+ *      sets the global variables:
+ *              hilite_style, pager, num_of_words, word_list
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+struct option long_opts[] =
+{
+  {"style", required_argument, 0, 's'},
+  {"terminator", required_argument, 0, 't'},
+  {"pager", required_argument, 0, 'p'},
+  {"stem_method", required_argument, 0, 'm'},
+  {"stemmer", required_argument, 0, 'a'},
+  {0, 0, 0, 0}
+};
+
+static void
+process_args (int argc, char *argv[])
+{
+  int ch;
+
+
+  opterr = 0;
+  while ((ch = getopt_long (argc, argv, "s:p:t:m:a:", long_opts, (int *) 0)) != -1)
+    {
+      switch (ch)
+	{
+	case 's':
+	  {
+	    int i;
+	    for (i = 0; i < HILITE_MAX; i++)
+	      if (strcmp (optarg, hilite_names[i]) == 0)
+		break;
+
+	    if (i < HILITE_MAX)
+	      hilite_style = i;
+	  }
+	  break;
+	case 'a':
+	  stemmer_num = stemmernumber (optarg);
+	  break;
+	case 't':
+	  terminator = optarg;
+	  break;
+	case 'm':
+	  stem_method = atoi (optarg);
+	  break;
+	case 'p':
+	  pager = optarg;
+	  break;
+	default:
+	  FatalError (1, "Usage: \n"
+		      "mg_hilite_words --stem_method [0-3]\n"
+	  "                --stemmer [english|lovin|french|simplefrench]\n"
+	  "                --style [bold|underline|italic|emphasis|strong]\n"
+		      "                --pager [less|more|html|???]\n");
+	}
+    }
+
+  num_of_words = argc - optind;
+
+  word_list = &argv[optind];
+
+  /* fix up output type */
+  if (strcmp (pager, HTML_OUT) == 0)
+    output_type = HTML;
+  else
+    output_type = PAGER;
+
+}
+
+/* =========================================================================
+ * Function: main
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+main (int argc, char *argv[])
+{
+  FILE *output = NULL;
+
+  process_args (argc, argv);
+
+  /* set output file */
+  if (output_type == PAGER)
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+    output = _popen (pager, "w");
+#else
+    output = popen (pager, "w");
+#endif
+  else
+    output = stdout;
+
+  if (!output)
+    FatalError (1, "Unable to run \"%s\"\n", pager);
+
+  if (num_of_words < 1)
+    {
+      int ch;
+
+      /* just echo the input */
+      /* better not to call this program at all ;-) */
+      while ((ch = fgetc (stdin)) != EOF)
+	{
+	  fputc (ch, output);
+	}
+    }
+  else
+    {
+      /* set up hash table for words */
+      process_words (word_list, num_of_words);
+
+
+      /* Go thru lines of text from stdin and 
+       * output words with hilite info if
+       * words parse into existence in the hash table 
+       */
+      process_text (stdin, output);
+    }
+
+  if (terminator)
+    fprintf (output, "%s\n", terminator);
+
+  if (output != stdout)
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+    _pclose (output);
+#else
+    pclose (output);
+#endif
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.1	(revision 16561)
@@ -0,0 +1,104 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_dict 1 \*(Dt CITRI
+.SH NAME
+mg_invf_dict \- build an `on-disk' stemmed dictionary
+.SH SYNOPSIS
+.B mg_invf_dict
+[
+.B \-h
+]
+[
+.B \-F
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_invf_dict
+builds an `on-disk' stemmed dictionary from the
+compressed stemmed dictionary that the
+.BR mgquery (1)
+program can access.  Each entry in the dictionary contains the disk
+pointer to the associated inverted file entry, and various other pieces
+of information about the stemmed word.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " num"
+The dictionary is stored in blocks on disk; this option is used to set
+the block size.  The default is 4-KB blocks.
+.TP
+.B \-F
+This option causes the disk blocks to be aligned on the appropriate
+boundaries.  For example, if 4-KB blocks are used, they will be aligned
+in the file on 4-KB boundaries.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dict.c	(revision 16561)
@@ -0,0 +1,347 @@
+/**************************************************************************
+ *
+ * mg_invf_dict.c -- Program to build the blocked stemmed dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "locallib.h"
+#include "words.h"
+#include "mg.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:08  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:45  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:03  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1994/11/29  00:32:00  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.3  1994/10/20  03:56:56  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:49  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+int block_size = 1024 * 4;
+
+int force = 0;
+
+static void process_files (char *filename);
+
+int main (int argc, char **argv)
+{
+  char *file_name = "";
+  int ch;
+  msg_prefix = argv[0];
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "f:d:b:Fh")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'b':
+	block_size = atoi (optarg);
+	break;
+      case 'F':
+	force = 1;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file]"
+		 "[-d data directory] [-b num] [-F] [-h]\n", argv[0]);
+	exit (1);
+      }
+
+  process_files (file_name);
+  return 0;
+}
+
+
+
+
+static void 
+process_files (char *filename)
+{
+  FILE *id, *idb, *tmp, *ii;
+  unsigned long i, pos, num, First_word, invf_ptr, invf_len;
+  unsigned long last_ptr = 0;
+  char *FName;
+  struct invf_dict_header idh;
+  struct stem_dict_header sdh;
+  u_char prev[MAXSTEMLEN + 1];
+  u_char *buffer;
+  unsigned short *pointers;
+  int buf_in_use;
+  unsigned short ptrs_in_use, word_num;
+
+  id = open_file (filename, INVF_DICT_SUFFIX, "rb", MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  ii = open_file (filename, INVF_IDX_SUFFIX, "rb", MAGIC_INVI, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  idb = create_file (filename, INVF_DICT_BLOCKED_SUFFIX, "w+b", MAGIC_STEM,
+		     MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  FName = make_name (filename, ".tmp", NULL);
+  if (!(tmp = fopen (FName, "w+b")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open \"%s\".\n", FName);
+
+  /* Delete the file now */
+  unlink (FName);
+
+  fread (&idh, sizeof (idh), 1, id);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  sdh.lookback = idh.lookback;
+  sdh.block_size = block_size;
+  sdh.num_blocks = 0;
+  sdh.index_chars = 0;
+  sdh.blocks_start = 0;
+  sdh.num_of_docs = idh.num_of_docs;
+  sdh.static_num_of_docs = idh.static_num_of_docs;
+  sdh.num_of_words = idh.num_of_words;
+  sdh.stemmer_num = idh.stemmer_num;
+  sdh.stem_method = idh.stem_method;
+  sdh.indexed = 0;  /* [RPAP - Jan 97: Stem Index Change] */
+
+  fwrite (&sdh, sizeof (sdh), 1, idb);
+
+  if (!(buffer = Xmalloc (block_size + 512)))
+    FatalError (1, "Unable to allocate memory for \"buffer\"\n");
+  if (!(pointers = Xmalloc (block_size + 512)))
+    FatalError (1, "Unable to allocate memory for \"buffer\"\n");
+
+  buf_in_use = 0;
+  pos = 0;
+  word_num = 0;
+  ptrs_in_use = 0;
+  First_word = 0;
+  for (i = 0; i < idh.dict_size; i++)
+    {
+      register unsigned long extra, copy, suff;
+      unsigned long wcnt, fcnt;
+
+      /* build a new word on top of prev */
+      copy = getc (id);
+      suff = getc (id);
+      *prev = copy + suff;
+      fread (prev + copy + 1, sizeof (u_char), suff, id);
+
+      /* read other data, but no need to store it */
+      fread (&fcnt, sizeof (fcnt), 1, id);
+      fread (&wcnt, sizeof (wcnt), 1, id);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      /* read in the inverted file position */
+      fread (&invf_ptr, sizeof (invf_ptr), 1, ii);
+      NTOHUL(invf_ptr);  /* [RPAP - Jan 97: Endian Ordering] */
+      if (word_num % idh.lookback == 0)
+	extra = copy + sizeof (*pointers);
+      else
+	extra = 0;
+      if ((ptrs_in_use + 1) * sizeof (*pointers) + sizeof (ptrs_in_use) + extra +
+	  buf_in_use + suff + 1 + sizeof (fcnt) + sizeof (wcnt) +
+	  sizeof (First_word) + sizeof (invf_ptr) + sizeof (invf_len) > block_size)
+	{
+	  int chunk;
+	  invf_len = invf_ptr - last_ptr;
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  HTONUL(First_word);
+	  HTONUL(invf_len);
+	  HTONUS(word_num);
+
+	  fwrite (&First_word, sizeof (First_word), 1, tmp);
+	  fwrite (&invf_len, sizeof (invf_len), 1, tmp);
+	  fwrite (&word_num, sizeof (word_num), 1, tmp);
+	  fwrite (pointers, sizeof (*pointers), ptrs_in_use, tmp);
+	  fwrite (buffer, sizeof (u_char), buf_in_use, tmp);
+	  bzero ((char *) buffer, block_size);
+	  chunk = buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	    sizeof (ptrs_in_use) + sizeof (First_word) + sizeof (invf_len);
+	  if (force && chunk < block_size)
+	    {
+	      fwrite (buffer, sizeof (u_char), block_size - chunk, tmp);
+	      chunk = block_size;
+	    }
+
+	  pos += chunk;
+
+	  buf_in_use = 0;
+	  word_num = 0;
+	  ptrs_in_use = 0;
+	  sdh.num_blocks++;
+	}
+
+      if (word_num % idh.lookback == 0)
+	{
+	  HTONUS2(buf_in_use, pointers[ptrs_in_use++]);  /* [RPAP - Jan 97: Endian Ordering] */
+	  suff += copy;
+	  copy = 0;
+	}
+      buffer[buf_in_use++] = copy;
+      buffer[buf_in_use++] = suff;
+      bcopy ((char *) (prev + copy + 1), (char *) (buffer + buf_in_use), suff);
+      buf_in_use += suff;
+      HTONUL(fcnt);  /* [RPAP - Jan 97: Endian Ordering] */
+      bcopy ((char *) &fcnt, (char *) (buffer + buf_in_use), sizeof (fcnt));
+      buf_in_use += sizeof (fcnt);
+      HTONUL(wcnt);  /* [RPAP - Jan 97: Endian Ordering] */
+      bcopy ((char *) &wcnt, (char *) (buffer + buf_in_use), sizeof (wcnt));
+      buf_in_use += sizeof (wcnt);
+      last_ptr = invf_ptr;
+      HTONUL(invf_ptr);  /* [RPAP - Jan 97: Endian Ordering] */
+      bcopy ((char *) &invf_ptr, (char *) (buffer + buf_in_use), sizeof (invf_ptr));
+      NTOHUL(invf_ptr);  /* [RPAP - Jan 97: Endian Ordering] */
+      buf_in_use += sizeof (invf_ptr);
+      if (buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	  sizeof (ptrs_in_use) > block_size)
+	FatalError (1, "Fatal Internal Error # 34876234\n");
+      if (word_num == 0)
+	{
+	  fwrite (prev, sizeof (u_char), *prev + 1, idb);
+	  HTONUL(pos);  /* [RPAP - Jan 97: Endian Ordering] */
+	  fwrite (&pos, sizeof (pos), 1, idb);
+	  NTOHUL(pos);  /* [RPAP - Jan 97: Endian Ordering] */
+	  sdh.index_chars += *prev + 1;
+	  First_word = i;
+	}
+      word_num++;
+    }
+  if (buf_in_use)
+    {
+      int chunk;
+      fread (&invf_ptr, sizeof (invf_ptr), 1, ii);
+      NTOHUL(invf_ptr);  /* [RPAP - Jan 97: Endian Ordering] */
+      invf_len = invf_ptr - last_ptr;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(First_word);
+      HTONUL(invf_len);
+      HTONUS(word_num);
+
+      fwrite (&First_word, sizeof (First_word), 1, tmp);
+      fwrite (&invf_len, sizeof (invf_len), 1, tmp);
+      fwrite (&word_num, sizeof (word_num), 1, tmp);
+      fwrite (pointers, sizeof (*pointers), ptrs_in_use, tmp);
+      fwrite (buffer, sizeof (u_char), buf_in_use, tmp);
+      bzero ((char *) buffer, block_size);
+      chunk = buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	sizeof (ptrs_in_use) + sizeof (First_word) + sizeof (invf_len);
+      if (force && chunk < block_size)
+	{
+	  fwrite (buffer, sizeof (u_char), block_size - chunk, tmp);
+	  chunk = block_size;
+	}
+
+      sdh.num_blocks++;
+    }
+  fclose (id);
+  fclose (ii);
+
+  rewind (tmp);
+  sdh.blocks_start = sdh.index_chars + sizeof (u_long) + sizeof (sdh) +
+    sdh.num_blocks * sizeof (pos);
+  if (force)
+    {
+      int amount;
+      amount = sdh.blocks_start % block_size;
+      if (amount != 0)
+	{
+	  bzero ((char *) buffer, block_size);
+	  fwrite (buffer, sizeof (u_char), block_size - amount, idb);
+	  sdh.blocks_start += block_size - amount;
+	}
+    }
+
+  while ((num = fread (buffer, sizeof (u_char), block_size, tmp)) != 0)
+    fwrite (buffer, sizeof (u_char), num, idb);
+  fclose (tmp);
+
+  /* skip over the magic number */
+  fseek (idb, sizeof (u_long), 0);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(sdh.lookback);
+  HTONUL(sdh.block_size);
+  HTONUL(sdh.num_blocks);
+  HTONUL(sdh.blocks_start);
+  HTONUL(sdh.index_chars);
+  HTONUL(sdh.num_of_docs);
+  HTONUL(sdh.static_num_of_docs);
+  HTONUL(sdh.num_of_words);
+  HTONUL(sdh.stemmer_num);
+  HTONUL(sdh.stem_method);
+  HTONUL(sdh.indexed);
+
+  fwrite (&sdh, sizeof (sdh), 1, idb);
+  fclose (idb);
+
+
+  Message ("Block size = %d\n", block_size);
+  Message ("Number of blocks written = %d\n", NTOHUL(sdh.num_blocks));
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.1	(revision 16561)
@@ -0,0 +1,142 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_dump 1 \*(Dt CITRI
+.SH NAME
+mg_invf_dump \- Dump out an inverted file in ASCII
+.SH SYNOPSIS
+.B mg_invf_dump
+[
+.B \-h
+]
+[
+.B \-b
+]
+[
+.B \-w
+]
+[
+.B \-t
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program dumps out an inverted file produced by the
+.BR mg (1)
+system as ASCII numbers.  This program could be used in conjunction
+with
+.BR mgdictlist (1)
+to write simple shell programs that work with the inverted files.  The
+output from the program looks something like this:
+.LP
+.RS
+.ft 3
+.nf
+6 337
+1
+ 1 1
+1
+ 5 1
+1
+ 2 1
+1
+ 1 1
+4
+ 1 3
+ 2 7
+ 3 2
+ 5 2
+1
+ 1 1
+ . . .
+.fi
+.ft
+.RE
+The first number (6) is the number of documents in the collection.  It
+is followed by the number of different terms in the collection.  For
+each term, the number of different documents it occurs in follows. The
+document numbers that the term occurs in are listed next.  If
+.B \-w
+is specified, then the number of times the term occurred in the
+document is printed alongside the document number.
+If
+.B \-t
+is specified, the term itself is also displayed in quotes.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-b
+This option will cause the output from the program to be in fixed-size
+binary numbers, rather than in ASCII.
+.TP
+.B \-w
+If the inverted file is a level-2 or level-3 inverted file, this causes
+the word counts per document to be output.
+.TP
+.B \-t
+This option causes each term to be displayed along with the number of
+documents it appears in. The term is printed in quotes,
+as in "the".
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf.dict
+The compressed stemmed dictionary.
+.TP
+.B *.invf.ORG
+The original inverted file saved by
+.BR mg_invf_rebuild .
+.TP
+.B *.invf
+The inverted file.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_dump.c	(revision 16561)
@@ -0,0 +1,229 @@
+/**************************************************************************
+ *
+ * mg_invf_dump.c -- Program to dump uot an inverted fil
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "messages.h"
+#include "timing.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "words.h"
+#include "invf.h"
+
+extern unsigned long S_btg;
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2001/06/12 01:41:39  kjm18
+   changed the help message
+
+   Revision 1.1  1999/08/10 21:18:09  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:46  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:05  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/11/29  00:32:01  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.2  1994/09/20  04:41:50  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+
+static void process_files (char *filename);
+
+int binary = 0;
+int word_counts = 0;
+int term_dump = 0;
+
+
+int main (int argc, char **argv)
+{
+  ProgTime start;
+  char *dir_name, *file_name = "";
+  int ch;
+  msg_prefix = argv[0];
+  dir_name = getenv ("MGDATA");
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "hbwtf:d:")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+        set_basepath(optarg);
+	break;
+      case 'b':
+	binary = 1;
+	break;
+      case 'w':
+	word_counts = 1;
+	break;
+      case 't':
+	term_dump = 1;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-h] [-b] [-w] [-t] [-f input_file]"
+		 "[-d data directory]\n"
+		 "(b - binary mode, w - wordcount, t - term dump)\n", argv[0]);
+	exit (1);
+      }
+  GetTime (&start);
+  process_files (file_name);
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  return 0;
+}
+
+static void 
+process_files (char *name)
+{
+  unsigned long N, Nstatic, k;
+  FILE *dict;
+  FILE *invf;
+  struct invf_dict_header idh;
+  struct invf_file_header ifh;
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  dict = open_file (name, INVF_DICT_SUFFIX, "rb", MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread ((char *) &idh, sizeof (idh), 1, dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  if (!(invf = open_file (name, INVF_SUFFIX ".ORG", "rb", MAGIC_INVF, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    invf = open_file (name, INVF_SUFFIX, "rb", MAGIC_INVF, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread ((char *) &ifh, sizeof (ifh), 1, invf);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(ifh.no_of_words);
+  NTOHUL(ifh.no_of_ptrs);
+  NTOHUL(ifh.skip_mode);
+  for (i = 0; i < 16; i++)
+    NTOHUL(ifh.params[i]);
+  NTOHUL(ifh.InvfLevel);
+
+  if (ifh.skip_mode != 0)
+    FatalError (1, "The invf file contains skips. Unable to dump.");
+
+  DECODE_START (invf)
+    N = idh.num_of_docs;
+  Nstatic = idh.static_num_of_docs;
+  if (binary)
+    {
+      fwrite ((char *) &N, sizeof (N), 1, stdout);
+      fwrite ((char *) &ifh.no_of_words, sizeof (ifh.no_of_words), 1, stdout);
+    }
+  else
+    printf ("%ld %ld\n", N, ifh.no_of_words);
+  for (k = 0; k < ifh.no_of_words; k++)
+    {
+      int i, blk, doc;
+      register unsigned long suff, prefix;
+      unsigned long fcnt, wcnt;
+      char term[MAXSTEMLEN + 1];
+
+      prefix = fgetc (dict);
+      suff = fgetc (dict);
+      fread (&term[prefix], sizeof (char), suff, dict);
+      fread ((char *) &fcnt, sizeof (fcnt), 1, dict);
+      fread ((char *) &wcnt, sizeof (wcnt), 1, dict);
+      term[prefix + suff] = '\0';
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      if (binary)
+	fwrite ((char *) &fcnt, sizeof (fcnt), 1, stdout);
+      else
+	{
+	  if (term_dump)
+	    printf ("%ld    \"%s\"\n", fcnt, term);
+	  else
+	    printf ("%ld\n", fcnt);
+	}
+      blk = BIO_Bblock_Init (Nstatic, fcnt);
+      for (doc = i = 0; i < fcnt; i++)
+	{
+	  int num;
+	  BBLOCK_DECODE (num, blk);
+	  doc += num;
+	  if (binary)
+	    fwrite ((char *) &doc, sizeof (doc), 1, stdout);
+	  else
+	    printf (" %d", doc);
+	  if (ifh.InvfLevel >= 2)
+	    {
+	      int count;
+	      GAMMA_DECODE (count);
+	      if (word_counts)
+		if (binary)
+		  fwrite ((char *) &count, sizeof (count), 1, stdout);
+		else
+		  printf (" %d", count);
+	    }
+	  if (!binary)
+	    putchar ('\n');
+	}
+      while (__btg)
+	DECODE_BIT;
+    }
+  DECODE_DONE
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_merge 1 \*(Dt CITRI
+.SH NAME
+mg_invf_merge \- Merge two inverted files and stemmed dictionaries
+.SH SYNOPSIS
+.B mg_invf_merge
+[
+.B \-h
+]
+[
+.B \-s
+]
+[
+.B \-w
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program takes two
+.BR mg (1)
+database compressed text files and appends the second to
+the first.
+It is part of the
+.BR mgmerge (1)
+addition to the
+.BR mg (1)
+package.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-s
+This option will cause a slow merge to be performed on the inverted files,
+where each inverted file entry is decoded and recoded.
+The default is a fast merge. Accumulated fast merges slowly degrade
+compression performance on the resulting inverted file so
+a periodic slow merge is recommended.
+.TP
+.B \-w
+By default the database .weights file is updated with
+new weights for the new documents in
+.B mg_text_merge.
+The
+.B -w
+option turns this step off.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.old.invf.dict
+The old compressed stemmed dictionary.
+.TP
+.B *.new.invf.dict
+The new compressed stemmed dictionary.
+.TP
+.B *.invf.dict
+The resulting merged compressed stemmed dictionary.
+.TP
+.B *.old.invf.ORG
+The original inverted file saved by
+.BR mg_invf_rebuild .
+.TP
+.B *.old.invf
+The old inverted file.
+.TP
+.B *.new.invf
+The new inverted file.
+.TP
+.B *.invf
+The resulting merged inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgmerge (1),
+.BR mg_get_merge (1),
+.BR mg_text_merge (1),
+.BR mg_invf_rebuild (1),
+.BR mg_invf_dict (1),
+.BR mg_weights_build (1),
+.BR mgquery (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_merge.c	(revision 16561)
@@ -0,0 +1,852 @@
+/**************************************************************************
+ *
+ * mg_invf_merge.c -- description
+ * Copyright (C) 1995  Shane Hudson (shane@cosc.canterbury.ac.nz)
+ *
+ * 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.
+ *
+ * Last edited:  31 March, 1995 
+ * $Id$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "locallib.h"
+#include "local_strings.h"
+#include "messages.h"
+#include "timing.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "bitio_m.h"
+#include "bitio_gen.h"
+#include "bitio_stdio.h"
+
+#include "mg.h"
+#include "mg_merge.h"
+#include "mg_files.h"
+#include "invf.h"
+#include "words.h"
+
+typedef char FileName[256];
+
+
+/*
+ *  The merge_info struct contains all the data for an inverted file and
+ *  lexicon. We declare 3 merge_info variables, m[OLD] m[NEW] and m[MERGE].
+ */
+typedef struct merge_info_type
+  {
+    FileName fname;
+    FILE *invf;
+    FILE *dict;
+    FILE *idx;
+    FILE *para;  /* [RPAP - Feb 97: Level 3 Merge] */
+    struct invf_dict_header idh;
+    struct invf_file_header ifh;
+    u_long nDocs;
+
+    /* Lexicon processing variables */
+    u_long fcnt, wcnt;
+    int suff, pref;
+    unsigned char term[MAXSTEMLEN + 1];
+    u_long term_count;
+    int done;
+
+    /* Variables used for merging */
+    struct stdio_bitio_state sbs;	/* for bit-based I/O on inverted files */
+    u_long nTerms;		/* = number of invf entries */
+    int *fcntlist;		/* store fcnt values read in from the lexicons */
+
+  }
+merge_info;
+
+
+/***************************************************
+ **** GLOBALS ****
+ ***************************************************/
+
+merge_info m[3];		/*  m[OLD], m[NEW] and m[MERGE] */
+u_long magicsize;		/* how big is magic number? currently always 4 */
+u_long Nstatic;			/* static # of documents in file */
+
+float *DocWeightBuffer;
+int weightOption = 0;		/* true if weights file is to be appended */
+
+/*** Globals for processing the lexicons -- see readTerm() and writeTerm() 
+ ***/
+ProgTime start;			/* for printing elapsed time */
+unsigned char prevTerm[MAXSTEMLEN + 1];
+
+/*** Globals for merging -- see processEntry() 
+ ***/
+char *mergedata;		/* stores value OLD/NEW/MERGE for each term */
+int fastMerge = 1;		/* True if fast merge selected. Default = true */
+
+u_long oldIdxValue;		/* stores last read value from m[OLD].idx
+				   Used to calculate entry lengths */
+u_long bytes_output;		/* for writing .invf.idx file */
+u_long oldOnlyBits = 0;		/* entries in old only */
+u_long oldMergeBits = 0;	/* entries in old that are merged with 
+				   a new entry */
+
+/**************************************************************************/
+
+
+/*=======================================================================
+ * init_merge_invf(): open files, initialise globals, etc
+ *=======================================================================*/
+int 
+init_merge_invf ()
+{
+  prevTerm[0] = prevTerm[1] = 0;
+
+    /***
+     *  open .dict files
+     ***/
+  m[OLD].dict = open_file (m[OLD].fname, INVF_DICT_SUFFIX, "rb",
+			   MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  magicsize = ftell (m[OLD].dict);
+  fread (&(m[OLD].idh), sizeof (m[OLD].idh), 1, m[OLD].dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[OLD].idh.lookback);
+  NTOHUL(m[OLD].idh.dict_size);
+  NTOHUL(m[OLD].idh.total_bytes);
+  NTOHUL(m[OLD].idh.index_string_bytes);
+  NTOHD(m[OLD].idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(m[OLD].idh.num_of_docs);
+  NTOHUL(m[OLD].idh.static_num_of_docs);
+  NTOHUL(m[OLD].idh.num_of_words);
+  NTOHUL(m[OLD].idh.stemmer_num);
+  NTOHUL(m[OLD].idh.stem_method);
+
+  m[NEW].dict = open_file (m[NEW].fname, INVF_DICT_SUFFIX, "rb",
+			   MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  fread (&(m[NEW].idh), sizeof (m[NEW].idh), 1, m[NEW].dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[NEW].idh.lookback);
+  NTOHUL(m[NEW].idh.dict_size);
+  NTOHUL(m[NEW].idh.total_bytes);
+  NTOHUL(m[NEW].idh.index_string_bytes);
+  NTOHD(m[NEW].idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(m[NEW].idh.num_of_docs);
+  NTOHUL(m[NEW].idh.static_num_of_docs);
+  NTOHUL(m[NEW].idh.num_of_words);
+  NTOHUL(m[NEW].idh.stemmer_num);
+  NTOHUL(m[NEW].idh.stem_method);
+
+  m[MERGE].dict = create_file (m[MERGE].fname, INVF_DICT_SUFFIX, "wb",
+			       MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* write space for header */
+  fwrite ((char *) &(m[OLD].idh), sizeof (m[OLD].idh), 1, m[MERGE].dict);
+
+  m[OLD].nDocs = m[OLD].idh.num_of_docs;
+  m[NEW].nDocs = m[NEW].idh.num_of_docs;
+  Nstatic = m[OLD].idh.static_num_of_docs;
+  m[OLD].nTerms = m[OLD].idh.dict_size;
+  m[NEW].nTerms = m[NEW].idh.dict_size;
+  m[MERGE].nDocs = m[OLD].nDocs + m[NEW].nDocs;
+  m[MERGE].nTerms = 0;
+
+  /* Set up weight buffer for weights of new documents */
+  if (!(DocWeightBuffer = Xmalloc (m[NEW].nDocs * sizeof (*DocWeightBuffer))))
+    {
+      Message ("Insufficient memory\n");
+      return COMPERROR;
+    }
+  bzero ((char *) DocWeightBuffer, m[NEW].nDocs * sizeof (*DocWeightBuffer));
+
+    /***
+     *  open .invf files
+     *  Try ".ORG" extension first for m[OLD].invf since an inverted file
+     *  with skips may have been created.
+     ****/
+  if (!(m[OLD].invf = open_file (m[OLD].fname, INVF_SUFFIX ".ORG", "rb",
+				 MAGIC_INVF, MG_CONTINUE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    m[OLD].invf = open_file (m[OLD].fname, INVF_SUFFIX, "rb",
+			     MAGIC_INVF, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  fread (&(m[OLD].ifh), sizeof (m[OLD].ifh), 1, m[OLD].invf);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[OLD].ifh.no_of_words);
+  NTOHUL(m[OLD].ifh.no_of_ptrs);
+  NTOHUL(m[OLD].ifh.skip_mode);
+  {
+    int i;
+    for (i = 0; i < 16; i++)
+      NTOHUL(m[OLD].ifh.params[i]);
+  }
+  NTOHUL(m[OLD].ifh.InvfLevel);
+
+  m[NEW].invf = open_file (m[NEW].fname, INVF_SUFFIX, "rb",
+			   MAGIC_INVF, MG_ABORT);
+  fread (&(m[NEW].ifh), sizeof (m[NEW].ifh), 1, m[NEW].invf);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[NEW].ifh.no_of_words);
+  NTOHUL(m[NEW].ifh.no_of_ptrs);
+  NTOHUL(m[NEW].ifh.skip_mode);
+  {
+    int i;
+    for (i = 0; i < 16; i++)
+      NTOHUL(m[NEW].ifh.params[i]);
+  }
+  NTOHUL(m[NEW].ifh.InvfLevel);
+
+  if (m[OLD].ifh.skip_mode != 0)
+    FatalError (1, "The old invf file contains skips. Unable to merge.");
+
+  if (m[OLD].ifh.InvfLevel != m[NEW].ifh.InvfLevel)
+    FatalError (1, "The two invf files have different inversion levels!");
+
+  /********** [RPAP - Feb 97: Level 3 Merge]
+
+  if ((m[OLD].ifh.InvfLevel > 2) || (m[NEW].ifh.InvfLevel > 2))
+    FatalError (1, "mgmerge cannot handle level 3 inverted files!");
+
+    *************/
+
+  m[MERGE].invf = create_file (m[MERGE].fname, INVF_SUFFIX, "wb",
+			       MAGIC_INVF, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* write space for header in inverted file */
+  m[MERGE].ifh.no_of_words = m[MERGE].ifh.no_of_ptrs = 0;
+  m[MERGE].ifh.skip_mode = 0;
+  m[MERGE].ifh.InvfLevel = 2;
+  {
+    int i;
+    for (i = 0; i < 16; i++)
+      m[MERGE].ifh.params[i] = 0;
+  }
+  fwrite ((char *) &(m[MERGE].ifh), sizeof (m[MERGE].ifh), 1, m[MERGE].invf);
+
+    /***
+     *  open .invf.idx files
+     ***/
+  m[OLD].idx = open_file (m[OLD].fname, INVF_IDX_SUFFIX, "rb",
+			  MAGIC_INVI, MG_ABORT);
+  fread (&oldIdxValue, sizeof (u_long), 1, m[OLD].idx);
+  NTOHUL(oldIdxValue);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  m[NEW].idx = open_file (m[NEW].fname, INVF_IDX_SUFFIX, "rb",
+			  MAGIC_INVI, MG_ABORT);
+
+  m[MERGE].idx = create_file (m[MERGE].fname, INVF_IDX_SUFFIX, "wb",
+			      MAGIC_INVI, MG_ABORT);
+
+
+  /* [RPAP - Feb 97: Level 3 Merge] */
+
+    /***
+     *  open .invf.parargraph files
+     ***/
+  if (m[OLD].ifh.InvfLevel == 3)
+    {
+      m[OLD].para = NULL;
+      m[NEW].para = open_file (m[NEW].fname, INVF_PARAGRAPH_SUFFIX, "rb",
+			       MAGIC_PARAGRAPH, MG_ABORT);
+      m[MERGE].para = open_file (m[MERGE].fname, INVF_PARAGRAPH_SUFFIX, "rb+",
+				 MAGIC_PARAGRAPH, MG_ABORT);
+    }
+  else
+    {
+      m[OLD].para = NULL;
+      m[NEW].para = NULL;
+      m[MERGE].para = NULL;
+    }
+
+  return OK;
+}
+
+
+/*=======================================================================
+ * procEntry_slow(): merge an entry, slow method
+ *=======================================================================*/
+void 
+procEntry_slow (int tnum, int type, u_long oldN, u_long newN, u_long mergeN)
+/* 
+ *  Process an invf entry. 
+ *  Slow Merge. (Always decode/re-encode).
+ *
+ *  tnum is the entry number, type = OLD or NEW or MERGE.
+ *  oldN, newN and mergeN are the N parameters for Bblock coding.
+ *  They are passed so other procEntry_fast can call this one
+ *  when a decode/recode is needed to save repeating code.
+ */
+{
+  int doc = 0;
+  int fcntOLD = m[OLD].fcntlist[tnum];
+  int fcntNEW = m[NEW].fcntlist[tnum];
+  int inblk, outblk;
+  u_long inbits = 0, outbits = 0;
+
+  /* write .invf.idx pointer */
+  HTONUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&bytes_output, sizeof (u_long), 1, m[MERGE].idx);
+  NTOHUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  outblk = BIO_Bblock_Init (mergeN, (fcntOLD + fcntNEW));
+
+/*** OLD entry -> merge entry ***/
+  if (type != NEW)
+    {
+      int i;
+      inblk = BIO_Bblock_Init (oldN, fcntOLD);
+      for (i = 0; i < fcntOLD; i++)
+	{
+	  int num;
+	  num = BIO_Stdio_Bblock_Decode (inblk, &(m[OLD].sbs), &inbits);
+	  doc += num;
+	  BIO_Stdio_Bblock_Encode (num, outblk, &(m[MERGE].sbs), &outbits);
+	  if (m[OLD].ifh.InvfLevel >= 2)	/* frequencies */
+	    {
+	      num = BIO_Stdio_Gamma_Decode (&(m[OLD].sbs), &inbits);
+	      BIO_Stdio_Gamma_Encode (num, &(m[MERGE].sbs), &outbits);
+	    }
+	}
+      if (type == OLD)
+	oldOnlyBits += (inbits + m[OLD].sbs.Btg);
+      else
+	oldMergeBits += (inbits + m[OLD].sbs.Btg);
+    }
+
+/*** NEW entry -> merge entry ***/
+  if (type != OLD)
+    {
+      int i;
+      int offset;
+      double logN = log ((double) m[MERGE].nDocs);
+      double idf = logN - log ((double) (fcntOLD + fcntNEW));
+
+      inblk = BIO_Bblock_Init (newN, fcntNEW);
+      offset = (m[OLD].nDocs - doc);	/* amount to be added to 1ST pointer */
+      for (i = 0; i < fcntNEW; i++)
+	{
+	  register int num;
+	  register double weight;
+	  num = BIO_Stdio_Bblock_Decode (inblk, &(m[NEW].sbs), &inbits);
+	  if (i == 0)
+	    num += offset;
+	  doc += num;
+	  BIO_Stdio_Bblock_Encode (num, outblk, &(m[MERGE].sbs), &outbits);
+	  if (m[OLD].ifh.InvfLevel >= 2)	/* frequencies */
+	    {
+	      num = BIO_Stdio_Gamma_Decode (&(m[NEW].sbs), &inbits);
+	      BIO_Stdio_Gamma_Encode (num, &(m[MERGE].sbs), &outbits);
+	      weight = num * idf;
+	      DocWeightBuffer[doc - m[OLD].nDocs - 1] += weight * weight;
+	    }
+	}
+    }
+/*** Now the padding bits ***/
+  while (m[OLD].sbs.Btg)
+    BIO_Stdio_Decode_Bit (&(m[OLD].sbs));
+  while (m[NEW].sbs.Btg)
+    BIO_Stdio_Decode_Bit (&(m[NEW].sbs));
+  while (m[MERGE].sbs.Btg != 8)
+    {
+      BIO_Stdio_Encode_Bit (0, &(m[MERGE].sbs));
+      outbits++;
+    }
+  bytes_output += (outbits >> 3);
+  return;
+}
+
+
+/*=======================================================================
+ * procEntry_fast(): merge an entry, WITHOUT decoding if it's in the
+ *                   old inverted file only
+ *=======================================================================*/
+void 
+procEntry_fast (int tnum, int type)
+/*
+ *  Merge an invf entry. 
+ *  Faster Method : Copy (don't re-code) entries for terms only in IFold.
+ *  The N parameter is Nstatic, from m[OLD].idh.static_num_of_docs.
+ *  If entry type isnt OLD, just call procEntry_slow()
+ */
+{
+  u_long newIdxValue, len = 0;
+
+  /* Calculate IFold entry length in bytes if not NEW type */
+  if (type != NEW)		/* read in an index number from m[OLD].idx */
+    {
+      fread (&newIdxValue, sizeof (u_long), 1, m[OLD].idx);
+      NTOHUL(newIdxValue);  /* [RPAP - Jan 97: Endian Ordering] */
+      len = newIdxValue - oldIdxValue;
+      oldIdxValue = newIdxValue;
+    }
+
+  if (type != OLD)		/* must decode/recode */
+    procEntry_slow (tnum, type, Nstatic, m[NEW].nDocs, Nstatic);
+  else
+    {
+      /* write .invf.idx pointer */
+      HTONUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite (&bytes_output, sizeof (u_long), 1, m[MERGE].idx);
+      NTOHUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+
+      /* copy entry */
+      {
+	int i;
+	char c;
+	for (i = 0; i < len; i++)
+	  {
+	    c = fgetc (m[OLD].invf);
+	    fputc (c, m[MERGE].invf);
+	  }
+      }
+      bytes_output += len;
+      oldOnlyBits += (len * 8);
+    }
+  return;
+}
+
+
+/*=======================================================================
+ * processEntry(): merge an entry. Call either procEntry_slow() or
+ *                 procEntry_fast.
+ *=======================================================================*/
+void 
+processEntry (int tnum, int type)
+/* 
+ *  Read an entry from IFold and/or IFnew, and write to IFmerge.
+ *  tnum is the term number, type is OLD or NEW or MERGE
+ */
+{
+  if (fastMerge)
+    procEntry_fast (tnum, type);
+  else
+    procEntry_slow (tnum, type, Nstatic, m[NEW].nDocs, m[MERGE].nDocs);
+  return;
+}
+
+
+/*=======================================================================
+ * readTerm()
+ *=======================================================================*/
+void 
+readTerm (int x)
+/*
+ *  Read the next term from the appropriate ".invf.dict" file.
+ *  x should be either OLD or NEW.
+ *  Sets m[x].done to true when that lexicon has been completely read.
+ */
+{
+  int i;
+
+  if (m[x].term_count >= m[x].nTerms)
+    {
+      m[x].term[0] = 0;
+      m[x].done = 1;
+      return;
+    }
+  m[x].pref = fgetc (m[x].dict);
+  m[x].suff = fgetc (m[x].dict);
+  m[x].term[0] = m[x].pref + m[x].suff;
+
+  for (i = 0; i < m[x].suff; i++)
+    {
+      m[x].term[m[x].pref + i + 1] = fgetc (m[x].dict);
+    }
+  m[x].term_count = m[x].term_count + 1;
+
+  fread ((char *) &(m[x].fcnt), sizeof (u_long), 1, m[x].dict);
+  fread ((char *) &(m[x].wcnt), sizeof (u_long), 1, m[x].dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[x].fcnt);
+  NTOHUL(m[x].wcnt);
+
+  return;
+}
+
+
+/*=======================================================================
+ * writeTerm()
+ *=======================================================================*/
+void 
+writeTerm (int x)
+/*
+ *  Write the current term to m[MERGE].dict file.
+ *  x is OLD, NEW or MERGE.
+ */
+{
+  unsigned char i, prefix, suffix;
+  if (x == MERGE)
+    {
+      m[MERGE].fcnt = m[OLD].fcnt + m[NEW].fcnt;
+      m[MERGE].wcnt = m[OLD].wcnt + m[NEW].wcnt;
+      m[MERGE].pref = m[OLD].pref;
+      m[MERGE].suff = m[OLD].suff;
+      memcpy (m[MERGE].term, m[OLD].term, m[OLD].term[0] + 1);
+    }
+  if (x == OLD)
+    {
+      m[MERGE].fcnt = m[OLD].fcnt;
+      m[MERGE].wcnt = m[OLD].wcnt;
+    }
+  if (x == NEW)
+    {
+      m[MERGE].fcnt = m[NEW].fcnt;
+      m[MERGE].wcnt = m[NEW].wcnt;
+    }
+  prefix = prefixlen (prevTerm, m[x].term);
+  suffix = m[x].term[0] - prefix;
+  fputc (prefix, m[MERGE].dict);	/* prefix length */
+  fputc (suffix, m[MERGE].dict);	/* suffix length */
+  for (i = 0; i < suffix; i++)
+    fputc (m[x].term[prefix + i + 1], m[MERGE].dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(m[MERGE].fcnt);
+  HTONUL(m[MERGE].wcnt);
+
+  fwrite ((char *) &(m[MERGE].fcnt), sizeof (m[MERGE].fcnt), 1, m[MERGE].dict);
+  fwrite ((char *) &(m[MERGE].wcnt), sizeof (m[MERGE].wcnt), 1, m[MERGE].dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(m[MERGE].fcnt);
+  NTOHUL(m[MERGE].wcnt);
+
+  memcpy (prevTerm, m[x].term, prefix + suffix + 1);
+  m[MERGE].idh.total_bytes += m[x].term[0] + 1;
+  m[MERGE].idh.index_string_bytes += m[x].term[0] + 2 - prefix;
+  fflush (m[MERGE].dict);
+}
+
+
+/*=======================================================================
+ * process_merge_invf(): The main loop
+ *=======================================================================*/
+int 
+process_merge_invf (void)
+/*
+ *  The main function to merge the lexicons and inverted files.
+ */
+{
+/*** Some extra Initialisation stuff first ***/
+
+  m[OLD].term[0] = m[NEW].term[0] = '\0';
+  m[OLD].term_count = m[NEW].term_count = 0;
+  m[OLD].done = m[NEW].done = 0;
+  m[MERGE].idh.total_bytes = m[MERGE].idh.index_string_bytes = 0;
+
+/*** malloc arrays: mergedata, m[OLD].fcntlist, m[NEW].fcntlist ***/
+  mergedata = malloc ((m[OLD].nTerms + m[NEW].nTerms) * sizeof (char));
+  if (mergedata == 0)
+    {
+      fprintf (stderr, "MALLOC error!\n");
+      exit (1);
+    }
+  {
+    int i;
+    for (i = 0; i < 2; i++)
+      {
+	m[i].fcntlist = malloc ((m[OLD].nTerms + m[NEW].nTerms) * sizeof (int));
+	if (m[i].fcntlist == 0)
+	  {
+	    fprintf (stderr, "MALLOC error!\n");
+	    exit (1);
+	  }
+      }
+  }
+
+/*========================*/
+/***    LEXICON PASS    ***/
+/*========================*/
+
+/*** read first terms ***/
+  readTerm (OLD);
+  readTerm (NEW);
+
+  m[MERGE].nTerms = 0;
+  while (m[OLD].done == 0 || m[NEW].done == 0)
+    {
+      int i;
+      if (m[OLD].done)
+	i = 1; /* NEW will always be greater */
+      else if (m[NEW].done)
+	i = -1; /* OLD will always be greater */
+      else
+	i = casecompare (m[OLD].term, m[NEW].term);
+
+      if (i < 0)
+	{			/* term in OLD only */
+	  mergedata[m[MERGE].nTerms] = (char) OLD;
+	  m[OLD].fcntlist[m[MERGE].nTerms] = m[OLD].fcnt;
+	  m[NEW].fcntlist[m[MERGE].nTerms] = 0;
+	  m[MERGE].nTerms++;
+	  writeTerm (OLD);
+	  readTerm (OLD);
+	}
+      if (i == 0)
+	{			/* term in both lexions */
+	  mergedata[m[MERGE].nTerms] = (char) MERGE;
+	  m[OLD].fcntlist[m[MERGE].nTerms] = m[OLD].fcnt;
+	  m[NEW].fcntlist[m[MERGE].nTerms] = m[NEW].fcnt;
+	  m[MERGE].nTerms++;
+	  writeTerm (MERGE);
+	  readTerm (OLD);
+	  readTerm (NEW);
+	}
+      if (i > 0)
+	{			/* term in NEW only */
+	  mergedata[m[MERGE].nTerms] = (char) NEW;
+	  m[NEW].fcntlist[m[MERGE].nTerms] = m[NEW].fcnt;
+	  m[OLD].fcntlist[m[MERGE].nTerms] = 0;
+	  m[MERGE].nTerms++;
+	  writeTerm (NEW);
+	  readTerm (NEW);
+	}
+    }/*while*/
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  /* print some results about terms */
+  {
+    fprintf (stderr, "   Terms: OLD = %ld,  NEW = %ld,  MERGE = %ld\n",
+	     m[OLD].nTerms, m[NEW].nTerms, m[MERGE].nTerms);
+    fprintf (stderr, "   In OLD only: %ld,  In NEW only: %ld,  BOTH: %ld\n",
+	     (m[MERGE].nTerms - m[NEW].nTerms),
+	     (m[MERGE].nTerms - m[OLD].nTerms),
+	     (m[OLD].nTerms + m[NEW].nTerms - m[MERGE].nTerms));
+  }
+
+
+/*========================*/
+/***      INVF PASS     ***/
+/*========================*/
+
+  bytes_output = ftell (m[MERGE].invf);
+  BIO_Stdio_Decode_Start (m[OLD].invf, &m[OLD].sbs);
+  BIO_Stdio_Decode_Start (m[NEW].invf, &m[NEW].sbs);
+  BIO_Stdio_Encode_Start (m[MERGE].invf, &m[MERGE].sbs);
+  {
+    int i;
+    for (i = 0; i < m[MERGE].nTerms; i++)
+      {
+	processEntry (i, mergedata[i]);
+      }
+    /* write final .invf.idx pointer */
+    HTONUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+    fwrite (&bytes_output, sizeof (u_long), 1, m[MERGE].idx);
+    NTOHUL(bytes_output);  /* [RPAP - Jan 97: Endian Ordering] */
+
+    fprintf (stderr, "Old invf: old only bits: %ld, merged bits: %ld\n",
+	     oldOnlyBits, oldMergeBits);
+  }
+
+  return OK;
+}
+
+
+/*=======================================================================
+ * done_merge_invf(): write headers, close files, append weights to
+ *                     weights file
+ *=======================================================================*/
+int 
+done_merge_invf (void)
+{
+/*** write new lexicon header ***/
+  m[MERGE].idh.dict_size = m[MERGE].nTerms;
+  m[MERGE].idh.lookback = m[OLD].idh.lookback;
+  m[MERGE].idh.input_bytes = m[OLD].idh.input_bytes + m[NEW].idh.input_bytes;
+  m[MERGE].idh.total_bytes = m[OLD].idh.total_bytes + m[NEW].idh.total_bytes;
+  m[MERGE].idh.num_of_docs = m[MERGE].nDocs;
+  m[MERGE].idh.num_of_words = m[OLD].idh.num_of_words + m[NEW].idh.num_of_words;
+  m[MERGE].idh.stemmer_num = m[OLD].idh.stemmer_num;
+  m[MERGE].idh.stem_method = m[OLD].idh.stem_method;
+
+  /* if fastMerge, static num of docs stays the same! */
+  if (fastMerge)
+    m[MERGE].idh.static_num_of_docs = m[OLD].idh.static_num_of_docs;
+  else
+    m[MERGE].idh.static_num_of_docs = m[MERGE].nDocs;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(m[MERGE].idh.lookback);
+  HTONUL(m[MERGE].idh.dict_size);
+  HTONUL(m[MERGE].idh.total_bytes);
+  HTONUL(m[MERGE].idh.index_string_bytes);
+  HTOND(m[MERGE].idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  HTONUL(m[MERGE].idh.num_of_docs);
+  HTONUL(m[MERGE].idh.static_num_of_docs);
+  HTONUL(m[MERGE].idh.num_of_words);
+  HTONUL(m[MERGE].idh.stemmer_num);
+  HTONUL(m[MERGE].idh.stem_method);
+
+  fseek (m[MERGE].dict, magicsize, 0);
+  fwrite (&(m[MERGE].idh), sizeof (struct invf_dict_header), 1, m[MERGE].dict);
+
+/*** write new inverted file header ***/
+  m[MERGE].ifh.no_of_words = m[MERGE].nTerms;
+  m[MERGE].ifh.no_of_ptrs = m[OLD].ifh.no_of_ptrs + m[NEW].ifh.no_of_ptrs;
+  m[MERGE].ifh.skip_mode = m[OLD].ifh.skip_mode;
+  m[MERGE].ifh.InvfLevel = m[OLD].ifh.InvfLevel;
+
+  /* ifh.params[16] -- I have NO IDEA what these do!!!!! */
+  /* only set if a inverted file is in skipped format, I think */
+  {
+    int i;
+    for (i = 0; i < 16; i++)
+      m[MERGE].ifh.params[i] = m[OLD].ifh.params[i];
+  }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(m[MERGE].ifh.no_of_words);
+  HTONUL(m[MERGE].ifh.no_of_ptrs);
+  HTONUL(m[MERGE].ifh.skip_mode);
+  {
+    int i;
+    for (i = 0; i < 16; i++)
+      HTONUL(m[MERGE].ifh.params[i]);
+  }
+  HTONUL(m[MERGE].ifh.InvfLevel);
+
+  fseek (m[MERGE].invf, magicsize, 0);	/* go to start of header */
+  fwrite (&(m[MERGE].ifh), sizeof (struct invf_file_header), 1, m[MERGE].invf);
+
+/*** Write new document weights --- leave old weights alone ***/
+  if ((weightOption) && (m[OLD].ifh.InvfLevel >= 2))
+    {
+      FILE *weightfile = open_file (m[MERGE].fname, WEIGHTS_SUFFIX, "r+b",
+				    MAGIC_WGHT, MG_CONTINUE);  /* [RPAP - Feb 97: WIN32 Port] */
+      if (weightfile)
+	{
+	  u_long i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+	  fprintf (stderr, "writing new weights: Nnew = %ld\n", m[NEW].nDocs);
+	  fseek (weightfile, 0, 2);	/* end of file */
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  for (i = 0; i < m[NEW].nDocs; i++)
+	    HTONF(DocWeightBuffer[i]);
+
+	  fwrite ((char *) DocWeightBuffer, sizeof (float), m[NEW].nDocs,
+		  weightfile);
+	}
+      fclose (weightfile);
+    }
+
+
+  /* [RPAP - Feb 97: Level 3 Merge] */
+  if (m[OLD].ifh.InvfLevel == 3)
+    {
+      /* Update paragraph file */
+      int buffer_size = 1000;
+      u_char *buffer;
+      int num;
+
+      if (!(buffer = (u_char *) Xmalloc (buffer_size)))
+	FatalError (1, "Could not allocate memory for paragraph buffer");
+
+      fseek (m[MERGE].para, 0, SEEK_END);
+      while ((num = fread ((char *) buffer, sizeof (*buffer), buffer_size, m[NEW].para)) != 0)
+	fwrite ((char *) buffer, sizeof (*buffer), num, m[MERGE].para);
+
+      Xfree (buffer);
+    }
+
+/*** close files ***/
+  fclose (m[MERGE].invf);
+  fclose (m[OLD].invf);
+  fclose (m[NEW].invf);
+  fclose (m[MERGE].dict);
+  fclose (m[OLD].dict);
+  fclose (m[NEW].dict);
+  fclose (m[MERGE].idx);
+  fclose (m[OLD].idx);
+  fclose (m[NEW].idx);
+
+  /* [RPAP - Feb 97: Level 3 Merge] */
+  if (m[OLD].ifh.InvfLevel == 3)
+    {
+      fclose (m[NEW].para);
+      fclose (m[MERGE].para);
+    }
+
+  return OK;
+}
+
+
+/*=======================================================================
+ * usage()
+ *=======================================================================*/
+void 
+usage (char *progname)
+{
+  fprintf (stderr, "usage:  %s [-s] [-w] [-d directory] -f mg_collection\n",
+	   progname);
+  fprintf (stderr, "  example: %s -s -w -f MERGE/mailfiles\n", progname);
+  exit (1);
+}
+
+
+/*=======================================================================
+ * main()
+ *=======================================================================*/
+int 
+main (int argc, char *argv[])
+{
+  int ch;
+  char *progname;
+  progname = argv[0];
+  m[MERGE].fname[0] = '\0';
+
+  /* message and timing information */
+  msg_prefix = argv[0];
+
+  while ((ch = getopt (argc, argv, "f:d:swh")) != -1)
+    switch (ch)
+      {
+      case 'f':
+	strcpy (m[MERGE].fname, optarg);
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 's':
+	fastMerge = 0;
+	break;
+      case 'w':
+	weightOption = 1;
+	break;
+      case 'h':
+      case '?':
+      default:
+	usage (progname);
+      }
+
+  if (m[MERGE].fname[0] == '\0')
+    usage (progname);
+  strcpy (m[OLD].fname, m[MERGE].fname);
+  strcat (m[OLD].fname, ".old");
+  strcpy (m[NEW].fname, m[MERGE].fname);
+  strcat (m[NEW].fname, ".new");
+
+  GetTime (&start);
+  init_merge_invf ();
+  process_merge_invf ();
+  done_merge_invf ();
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  exit (0);
+}
+
+/*************************************************************************/
+/*   EOF: mg_invf_merge_new.c                                            */
+/*************************************************************************/
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.1	(revision 16561)
@@ -0,0 +1,156 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_invf_rebuild 1 \*(Dt CITRI
+.SH NAME
+mg_invf_rebuild \- Rebuild an mg inverted file with or without skips.
+.SH SYNOPSIS
+.B mg_invf_rebuild
+.RB [ \-h ]
+.if n .ti +9n
+[
+.BR \-0 " |"
+.B \-1
+[
+.BI \-k " num"
+] |
+.B \-2
+[
+.BI \-s " num"
+]
+[
+.BI \-m " num"
+]
+]
+.if n .ti +9n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_invf_rebuild
+builds a new inverted file, with or without skipping, from an old
+inverted file.  This file uses
+.I *.invf.ORG
+and
+.I *.invf.idx.ORG
+as the
+source from which it builds
+.I *.invf
+and
+.IR *.invf.idx .
+If
+.I *.invf.ORG
+or
+.I *.invf.idx.ORG
+do not exist, the program renames
+.I *.invf
+or
+.I *.invf.idx
+to
+.I *.invf.ORG
+or
+.I *.invf.idx.ORG
+as appropriate.  The old inverted file may contain skipping.  This
+means that it is possible to delete the
+.I *.ORG
+files
+after the new inverted file is built.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-m\fP \fInum\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-0
+This generates a non-skipped inverted file.  This option is normally
+only needed if the
+.I *.ORG
+files have been deleted.
+.TP
+.B \-1
+This generates a skipped inverted file.  The
+.BI \-k " num"
+argument specifies the number of pointers hopped over with each skip.
+.TP
+.B \-2
+This option generates a skipped inverted file.  The skipped inverted
+file is built so that it is `optimal' for ranking using a specific
+number of accumulators.  Each term in the inverted file has a
+different skip length.  The arguments
+.BR \-s " and " \-m
+control the sizes of the skips.
+.TP
+.BI \-k " num"
+This specifies the number of pointers that should be hopped over with
+each skip.  This option is only valid if
+.B \-1
+is specified.
+.TP
+.BI \-m " num"
+This specifies the intended number of accumulators that will be used
+when ranking queries are done on the collection.
+.TP
+.BI \-s " num"
+This specifies the minimum size for skips.  If the calculation of the
+optimal skip size results in a number smaller than
+.IR num ,
+the skip size is set to
+.IR num .
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.ORG
+Original inverted file.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.invf.idx.ORG
+The original index into the inverted file.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_invf_rebuild.c	(revision 16561)
@@ -0,0 +1,567 @@
+/**************************************************************************
+ *
+ * mg_invf_rebuild.c -- Program to rebuild an inverted file with skipping
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "locallib.h"
+#include "words.h"
+#include "mg.h"
+
+#define LEN 40
+#define WRD 1
+
+typedef struct invf_info
+  {
+    unsigned long doc_num, count, bits_so_far, count_bits;
+  }
+invf_info;
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:11  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:46  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:10  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1994/11/29  00:32:03  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.3  1994/10/20  03:56:56  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:51  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+static char pathname[256];
+
+static void process_files (char *filename);
+
+static int k = -1;
+static int mode = 0;
+static int max_nodes = -1;
+static int mins = -1;
+
+void 
+usage (char *pgname)
+{
+  fprintf (stderr, "usage: %s [-f input_file]"
+       "[-d data directory] -0|-1|-2 [-k num] [-m num] [-s num]\n", pgname);
+  printf ("Mode 0: (the default)\n");
+  printf ("\tk, m, and s have no meaning and if specified produce an error\n");
+  printf ("Mode 1:\n");
+  printf ("\tm, and s have no meaning and if specified produce an error\n");
+  printf ("\tk The size of skips\n");
+  printf ("Mode 2:\n");
+  printf ("\tk has no meaning and if specified will produce an error\n");
+  printf ("\tm is the the number of accumulators that will be used for the\n");
+  printf ("\t  ranking. The program builds an inverted file that is \n");
+  printf ("\t  \"optimal\" for that number of accumulators.\n");
+  printf ("\ts is the minimum size for skips.\n");
+  exit (1);
+}
+
+
+
+int main (int argc, char **argv)
+{
+  ProgTime start;
+  char *dir_name, *file_name = "";
+  int ch;
+  msg_prefix = argv[0];
+  dir_name = getenv ("MGDATA");
+  strcpy (pathname, dir_name ? dir_name : ".");
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "012hf:d:k:b:s:m:")) != -1)
+    switch (ch)
+      {
+      case '0':
+	mode = 0;
+	break;
+      case '1':
+	mode = 1;
+	break;
+      case '2':
+	mode = 2;
+	break;
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	strcpy (pathname, optarg);
+	break;
+      case 'k':
+	k = atoi (optarg);
+	break;
+      case 'm':
+	max_nodes = atoi (optarg);
+	break;
+      case 's':
+	mins = atoi (optarg);
+	if (mins < 1)
+	  FatalError (1, "The number for the -m option must be greater"
+		      " than or equal to 1");
+	break;
+      case 'h':
+      case '?':
+	usage (argv[0]);
+      }
+
+  if ((mode == 0 && (k != -1 || max_nodes != -1 || mins != -1)) ||
+      (mode == 1 && (max_nodes != -1 || mins != -1)) ||
+      (mode == 2 && (k != -1)))
+    {
+      Message ("Illegal parameters for mode");
+      usage (argv[0]);
+    }
+
+  if (mode == 1 && k == -1)
+    {
+      k = 8;
+      Message ("k is required for mode 1: defaulting k to %d", k);
+    }
+  if (mode == 2 && max_nodes == -1)
+    {
+      max_nodes = 1024;
+      Message ("m is required for mode 2: defaulting m to %d", max_nodes);
+    }
+  if (mode == 2 && mins == -1)
+    {
+      mins = 1;
+      Message ("s is required for mode 2: defaulting s to %d", mins);
+    }
+
+
+  GetTime (&start);
+  process_files (file_name);
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  Message ("**** Don\'t forget to rebuild the stemmed dictionary with mg_invf_dict. ****\n");
+  Message ("**** If the collection was built with stem indexes don\'t forget to     ****\n");
+  Message ("**** rebuild them with mg_stem_idx.                                     ****\n");
+
+  return 0;
+}
+
+
+
+
+
+static void 
+process_files (char *filename)
+{
+  FILE *in, *out, *idx, *odx, *dict;
+  unsigned long magic, outmode, N, in_k, out_k;
+  unsigned long bits_out, bytes_out, i, j;
+  stdio_bitio_state out_buf, in_buf;
+  struct invf_dict_header idh;
+  struct invf_file_header ifh_in, ifh_out;
+  char FName[256];
+
+  outmode = mode;
+
+  /* open .invf.ORG, rename .invf if have to */
+  sprintf (FName, FILE_NAME_FORMAT ".ORG", pathname, filename, INVF_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(in = fopen (FName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      char fname[256];
+      sprintf (fname, FILE_NAME_FORMAT, pathname, filename, INVF_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+      rename (fname, FName);
+      if (!(in = fopen (FName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+	FatalError (1, "Unable to open \"%s\".\n", FName);
+    }
+  else
+    {
+      char fname[256];
+      sprintf (fname, FILE_NAME_FORMAT, pathname, filename, INVF_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+      unlink (fname);
+    }
+  Message ("Opening \"%s\"\n", FName);
+
+  /* check the magic number for .invf.ORG */
+  if (fread (&magic, sizeof (magic), 1, in) != 1 || NTOHUL(magic) != MAGIC_INVF)  /* [RPAP - Jan 97: Endian Ordering] */
+    FatalError (1, "Bad magic number in \"%s\".\n", FName);
+
+
+  /* open .invf.idx.ORG, rename .invf.idx if have to */
+  sprintf (FName, FILE_NAME_FORMAT ".ORG", pathname, filename, INVF_IDX_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  if (!(idx = fopen (FName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      char fname[256];
+      sprintf (fname, FILE_NAME_FORMAT, pathname, filename, INVF_IDX_SUFFIX);
+      rename (fname, FName);
+      if (!(idx = fopen (FName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+	FatalError (1, "Unable to open \"%s\".\n", FName);
+    }
+  else
+    {
+      char fname[256];
+      sprintf (fname, FILE_NAME_FORMAT, pathname, filename, INVF_IDX_SUFFIX);
+      unlink (fname);
+    }
+  Message ("Opening \"%s\"\n", FName);
+
+  /* check the magic number for .invf.idx.ORG */
+  if (fread (&magic, sizeof (magic), 1, idx) != 1 || NTOHUL(magic) != MAGIC_INVI)  /* [RPAP - Jan 97: Endian Ordering] */
+    FatalError (1, "Bad magic number in \"%s\".\n", FName);
+
+  sprintf (FName, FILE_NAME_FORMAT, pathname, filename, INVF_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  Message ("Creating \"%s\"\n", FName);
+  if (!(out = fopen (FName, "wb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open \"%s\".\n", FName);
+
+  sprintf (FName, FILE_NAME_FORMAT, pathname, filename, INVF_IDX_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  Message ("Creating \"%s\"\n", FName);
+  if (!(odx = fopen (FName, "wb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open \"%s\".\n", FName);
+
+  sprintf (FName, FILE_NAME_FORMAT, pathname, filename, INVF_DICT_SUFFIX);  /* [RPAP - Feb 97: WIN32 Port] */
+  Message ("Opening \"%s\"\n", FName);
+  if (!(dict = fopen (FName, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open \"%s\".\n", FName);
+
+  if (fread (&magic, sizeof (magic), 1, dict) != 1 || NTOHUL(magic) != MAGIC_STEM_BUILD)  /* [RPAP - Jan 97: Endian Ordering] */
+    FatalError (1, "Bad magic number in \"%s\".\n", FName);
+
+  fread ((char *) &idh, sizeof (idh), 1, dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  HTONUL2(MAGIC_INVF, magic);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite ((char *) &magic, sizeof (magic), 1, out);
+
+  fread ((char *) &ifh_in, sizeof (ifh_in), 1, in);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(ifh_in.no_of_words);
+  NTOHUL(ifh_in.no_of_ptrs);
+  NTOHUL(ifh_in.skip_mode);
+  for (i = 0; i < 16; i++)
+    NTOHUL(ifh_in.params[i]);
+  NTOHUL(ifh_in.InvfLevel);
+
+  ifh_out = ifh_in;
+  ifh_out.skip_mode = outmode;
+  bzero ((char *) ifh_out.params, sizeof (ifh_out.params));
+  switch (outmode)
+    {
+    case 0:
+      break;
+    case 1:
+      ifh_out.params[0] = k;
+      break;
+    case 2:
+      ifh_out.params[0] = max_nodes;
+      ifh_out.params[1] = mins;
+      break;
+    }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(ifh_out.no_of_words);
+  HTONUL(ifh_out.no_of_ptrs);
+  HTONUL(ifh_out.skip_mode);
+  for (i = 0; i < 16; i++)
+    HTONUL(ifh_out.params[i]);
+  HTONUL(ifh_out.InvfLevel);
+
+  fwrite ((char *) &ifh_out, sizeof (ifh_out), 1, out);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(ifh_out.no_of_words);
+  NTOHUL(ifh_out.no_of_ptrs);
+  NTOHUL(ifh_out.skip_mode);
+  for (i = 0; i < 16; i++)
+    NTOHUL(ifh_out.params[i]);
+  NTOHUL(ifh_out.InvfLevel);
+
+  Message ("The file is a level %d inverted file.\n", ifh_in.InvfLevel);
+
+  bits_out = ftell (out) * 8;
+
+
+  HTONUL2(MAGIC_INVI, magic);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite ((char *) &magic, sizeof (magic), 1, odx);
+
+  DECODE_START (in)
+    DECODE_PAUSE (in_buf)
+
+    ENCODE_START (out)
+    ENCODE_PAUSE (out_buf)
+
+    N = idh.num_of_docs;
+
+  for (i = 0; i < ifh_in.no_of_words; i++)
+    {
+      unsigned long blk, p;
+      unsigned long odn_blk = 0, olen_blk = 0;
+      unsigned long idn_blk = 0, ilen_blk = 0;
+      register unsigned long suff;
+      unsigned long fcnt, wcnt, doc_num, bits_so_far, last_major;
+      unsigned long next_mjr_dn, kd;
+      char dummy2[MAXSTEMLEN + 1];
+      invf_info *ii;
+
+      fgetc (dict);
+      suff = fgetc (dict);
+      fread (dummy2, sizeof (u_char), suff, dict);
+      fread ((char *) &fcnt, sizeof (fcnt), 1, dict);
+      fread ((char *) &wcnt, sizeof (wcnt), 1, dict);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      HTONUL2(bits_out >> 3, bytes_out);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite ((char *) &bytes_out, sizeof (bytes_out), 1, odx);
+      NTOHUL(bytes_out);  /* [RPAP - Jan 97: Endian Ordering] */
+
+      p = fcnt;
+      blk = BIO_Bblock_Init (idh.static_num_of_docs, p);
+      switch (outmode)
+	{
+	case 1:
+	  {
+	    unsigned long len;
+	    if (p <= ifh_out.params[0])
+	      out_k = 0;
+	    else
+	      {
+		out_k = ifh_out.params[0];
+		len = BIO_Bblock_Bound (N, p);
+		if (ifh_in.InvfLevel >= 2)
+		  len += wcnt;
+		odn_blk = BIO_Bblock_Init (idh.num_of_docs, (p + out_k - 1) / out_k);
+		olen_blk = BIO_Bblock_Init (len, (p + out_k - 1) / out_k);
+	      }
+	    break;
+	  }
+	case 2:
+	  {
+	    unsigned long len;
+	    if (p <= mins)
+	      out_k = 0;
+	    else
+	      {
+		out_k = (int) (2 * sqrt ((double) p / max_nodes));
+		if (out_k <= mins)
+		  out_k = mins;
+		len = BIO_Bblock_Bound (N, p);
+		if (ifh_in.InvfLevel >= 2)
+		  len += wcnt;
+		odn_blk = BIO_Bblock_Init (idh.num_of_docs,
+					   (p + out_k - 1) / out_k);
+		olen_blk = BIO_Bblock_Init (len, (p + out_k - 1) / out_k);
+	      }
+	    break;
+	  }
+	default:
+	  out_k = 0;
+	}
+
+      switch (ifh_in.skip_mode)
+	{
+	case 1:
+	  {
+	    unsigned long len;
+	    if (p <= ifh_in.params[0])
+	      in_k = 0;
+	    else
+	      {
+		in_k = ifh_in.params[0];
+		len = BIO_Bblock_Bound (N, p);
+		if (ifh_in.InvfLevel >= 2)
+		  len += wcnt;
+		idn_blk = BIO_Bblock_Init (idh.num_of_docs, (p + in_k - 1) / in_k);
+		ilen_blk = BIO_Bblock_Init (len, (p + in_k - 1) / in_k);
+	      }
+	    break;
+	  }
+	case 2:
+	  {
+	    unsigned long len;
+	    if (p <= ifh_in.params[1])
+	      {
+		in_k = 0;
+	      }
+	    else
+	      {
+		in_k = (int) (2 * sqrt ((double) p / ifh_in.params[0]));
+		if (in_k <= ifh_in.params[1])
+		  in_k = ifh_in.params[1];
+		len = BIO_Bblock_Bound (N, p);
+		if (ifh_in.InvfLevel >= 2)
+		  len += wcnt;
+		idn_blk = BIO_Bblock_Init (idh.num_of_docs,
+					   (p + in_k - 1) / in_k);
+		ilen_blk = BIO_Bblock_Init (len, (p + in_k - 1) / in_k);
+	      }
+	    break;
+	  }
+	default:
+	  in_k = 0;
+	}
+
+      if (!(ii = Xmalloc (sizeof (invf_info) * p)))
+	FatalError (1, "Unable to allocate memory for \"ii\"\n");
+
+      doc_num = bits_so_far = 0;
+      next_mjr_dn = 0;
+      kd = 0;
+      DECODE_CONTINUE (in_buf)
+	for (j = 0; j < p; j++, kd++)
+	{
+	  unsigned long doc_diff, count = 0;
+	  if (kd == in_k)
+	    kd = 0;
+	  if (in_k && kd == 0 && j + in_k < p)
+	    {
+	      int temp;
+	      BBLOCK_DECODE (next_mjr_dn, idn_blk);
+	      next_mjr_dn += doc_num;
+	      BBLOCK_DECODE (temp, ilen_blk);
+	    }
+	  ii[j].bits_so_far = bits_so_far;
+	  if (in_k && kd == in_k - 1 && j != p - 1)
+	    {
+	      int count;
+	      BBLOCK_LENGTH (next_mjr_dn - doc_num, blk, count);
+	      bits_so_far += count;
+	      doc_num = next_mjr_dn;
+	    }
+	  else
+	    {
+	      BBLOCK_DECODE_L (doc_diff, blk, bits_so_far);
+	      doc_num += doc_diff;
+	    }
+	  ii[j].doc_num = doc_num;
+	  if (ifh_in.InvfLevel >= 2)
+	    {
+	      int count_bits = 0;
+	      GAMMA_DECODE_L (count, count_bits);
+	      ii[j].count_bits = count_bits;
+	      bits_so_far += count_bits;
+	      ii[j].count = count;
+	    }
+	}
+
+      /* read till a byte boundary */
+      while (__btg)
+	{
+	  DECODE_BIT;
+	  bits_so_far++;
+	}
+
+      DECODE_PAUSE (in_buf)
+
+	doc_num = bits_so_far = 0;
+      last_major = 0;
+      kd = 0;
+      ENCODE_CONTINUE (out_buf)
+	for (j = 0; j < p; j++, kd++)
+	{
+	  if (kd == out_k)
+	    kd = 0;
+	  if (out_k && kd == 0)
+	    {
+	      if (j + out_k < p)
+		{
+		  int num = ii[j + out_k - 1].doc_num - last_major;
+		  BBLOCK_ENCODE_L (num, odn_blk, bits_out);
+		  last_major = ii[j + out_k - 1].doc_num;
+
+		  num = ii[j + out_k - 1].bits_so_far + ii[j + out_k - 1].count_bits -
+		    bits_so_far;
+		  BBLOCK_ENCODE_L (num, olen_blk, bits_out);
+		  bits_so_far = ii[j + out_k].bits_so_far;
+		}
+	    }
+	  if (!(out_k && kd == out_k - 1 && j != p - 1))
+	    BBLOCK_ENCODE_L (ii[j].doc_num - doc_num, blk, bits_out);
+	  doc_num = ii[j].doc_num;
+	  if (ifh_in.InvfLevel >= 2)
+	    GAMMA_ENCODE_L (ii[j].count, bits_out);
+	}
+
+      /* write till a byte boundary */
+      while (__btg != 8)
+	{
+	  ENCODE_BIT (0);
+	  bits_out++;
+	}
+      ENCODE_PAUSE (out_buf)
+
+	Xfree (ii);
+
+    }
+  ENCODE_CONTINUE (out_buf)
+    ENCODE_DONE
+
+    HTONUL2(bits_out >> 3, bytes_out);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite ((char *) &bytes_out, sizeof (bytes_out), 1, odx);
+  NTOHUL(bytes_out);
+
+  fclose (idx);
+  fclose (odx);
+  fclose (in);
+  fclose (out);
+  fclose (dict);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_merge.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_merge.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_merge.h	(revision 16561)
@@ -0,0 +1,37 @@
+/**************************************************************************
+ *
+ * mgmerge.h --- mgmerge #defines, etc
+ * Copyright (C) 1995  Shane Hudson (shane@cosc.canterbury.ac.nz)
+ *
+ * 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$
+ * Last edtited: November 12 1994
+ *
+ **************************************************************************/
+
+#ifndef MGMERGE_H
+#define MGMERGE_H
+
+typedef unsigned char byte;
+
+#define OLD 0
+#define NEW 1
+#define MERGE 2
+
+#define OK 0
+#define ERROR_MGMERGE 1
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.1	(revision 16561)
@@ -0,0 +1,441 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_passes 1 \*(Dt CITRI
+.SH NAME
+mg_passes \- builds mg databases
+.SH SYNOPSIS
+.B mg_passes
+[
+.B \-h
+]
+[
+.B \-G
+]
+[
+.B \-S
+]
+[
+.B \-D
+]
+[
+.B \-W
+]
+.if n .ti +9n
+[
+.BR \-1 " |"
+.BR \-2 " |"
+.B  \-3
+]
+[
+.BI \-C " compstatpoint"
+]
+.if n .ti +9n
+[
+.BI \-n " tracename"
+]
+.if t .ti +.5i
+[
+.BI \-b " bufsize"
+]
+[
+.BI \-m " memlimit"
+]
+.if n .ti +9n
+[
+.BI \-c " numchunks"
+]
+[
+.BI \-a " stemmer"
+]
+[
+.BI \-s " stemmethod"
+]
+[
+.BI \-t " tracepos"
+]
+.if n .ti +9n
+[
+.B \-T1
+]
+[
+.B \-T2
+]
+.if t .ti +.5i
+[
+.B \-I1
+]
+[
+.B \-I2
+]
+.if n .ti +9n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+[
+.I filename(s)
+]
+.SH DESCRIPTION
+.B mg_passes
+is the program that does most of the work when building
+.BR mg (1)
+database systems.  The input documents can come from either
+.I stdin
+or from a list of files on the command line.  Individual documents
+must be separated with control-B characters.  In general,
+.B mg_passes
+must be run twice to build a database, first with the
+.B \-T1
+and
+.B \-I1
+options, and second with the
+.B \-T2
+and
+.B \-I2
+options.  Several other programs must be run in order to get an
+.BR mg (1)
+database that is ready for the
+.BR mgquery (1)
+program.  The
+.SB EXAMPLE
+section below gives an example of how to build a complete
+.BR mg (1)
+database.
+.SH OPTIONS
+Options may appear in any order, but the
+.IR filename(s) ,
+if specified, must be last.
+.TP "\w'\fB\-C\fP \fIcompstatpoint\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-G
+Treat SGML tags as non-words when building the inverted file.  An SGML
+tag is anything between angle brackets, i.e., `<' and `>'.
+.TP
+.B \-S
+This option causes a special pass to be executed.  It is up to a user
+to modify
+.I mg.special.c
+in the source code to do something with the documents it is given.
+.TP
+.B \-D
+If
+.B mg_passes
+fails, then print the document that caused the failure to the trace
+file if tracing is active, or to
+.I stderr
+if it is not.
+.TP
+.B \-W
+This option enables the generation of the weights file when
+.B \-I2
+is specified.  It causes
+.B \-I2
+to use a little more memory and CPU.
+.TP
+.B \-1
+Produce a level-1 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+A level-1 inverted file makes it possible for
+.BR mgquery (1)
+to do Boolean queries.  Ranked queries can still be done,
+although the quality of the ranking is abysmal.
+.TP
+.B \-2
+Produce a level-2 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+This is the default when neither
+.BR \-1 ", " "\-2 " "or " \-3
+is specified.
+A level-2 inverted file makes it possible for
+.BR mgquery (1)
+to do Boolean queries and cosine-ranked queries.
+.TP
+.B \-3
+Produce a level-3 inverted file.  This option is only useful when
+specified with
+.BR "\-I1 ".
+This has been implemented to enable paragraph-level inversion.
+Paragraphs are delimited by control-C characters in the source text.
+.TP
+.BI \-C " compstatpoint"
+This option causes statistics on the compression performance to be
+output to a file called
+.IR *.compression.stats .
+.I compstatpoint
+specifies the interval between outputting each line of statistics.  The
+units of
+.I compstatpoint
+are kilobytes of source text.  E.g., if
+.I compstatpoint
+is 10, then a line is output to the file every 10 KB of input
+source.  Each line of the file consists of 4 numbers The first number
+is the amount of input text, in bytes, processed so far.  The second
+number is the amount of input text, in bytes, processed since the
+last line was output to the file.  The third number is the number of
+output bytes generated since the last line was output to the file, and
+the fourth number is the compression achieved since the last line was
+output, i.e., the third number divided by the second number.
+.TP
+.BI \-n " tracename"
+This specifies the filename to use for the trace log, if tracing is
+enabled using the
+.B \-t
+option.  If
+.BI \-n " tracename"
+is not given and tracing is enabled, a default trace filename will be
+used.
+.TP
+.BI \-s " stemmethod"
+This specifies the method to use to \*(lqstem\*(rq the words in the
+inverted file dictionary.  This is a bit mask specifying the
+operations to do on words as they are parsed out of the text, where
+bit number 0 is the low-order (rightmost) bit.  Bit 0 does case
+folding, and bit 1 does simple stemming, so the value 3 for
+.I stemmethod
+does both case folding and stemming.
+.TP
+.BI \-a " stemmer"
+This specifies the stemmer to use when stemming words.  This
+is a description of the language the stemmer is intended for
+or a description of the stemmer.  Valid options include:
+english, lovin, french, and simplefrench.
+.TP
+.BI \-b " bufsize"
+Specify the size of the document buffer in kilobytes.  If any document
+is larger than
+.IR bufsize ,
+the program will abort with an error message.  This should probably be
+replaced with some system which automatically increases the buffer
+size as required.  The default size is 3072 KB (3 MB).
+.TP
+.BI \-m " memlimit"
+Maximum amount of memory to use for the pass-2 file inversion in
+megabytes.  This option is only useful when used in conjunction with
+the option
+.BR \-I1 .
+The larger this value, the faster the pass-2 inversion will proceed.
+The default value is 5 MB.
+.TP
+.BI \-c " numchunks"
+The maximum number of inversion chunks to write to disk.  Each chunk
+will be approximately as large as
+.IR memlimit .
+This option is only useful when used in conjunction with the option
+.BR \-I2 .
+The larger this value, the faster the pass-2 inversion will proceed.
+The default value is 5 MB.
+.TP
+.BI \-t " tracepos"
+This option activates tracing.  A line will be generated in the
+trace file for every
+.I tracepos
+input bytes processed.  The default name for the trace file can be
+overridden using the
+.BI \-n " tracename"
+option.
+.TP
+.B \-T1
+Generate the
+.I *.text.stats
+file.
+.TP
+.B \-T2
+Generate the
+.IR *.text ,
+.IR *.text.idx ,
+and possibly the
+.I *.text.dict.aux
+files.  Using this option requires that the
+.I *.text.dict
+file be present.
+.TP
+.B \-I1
+Generate the
+.IR *.invf.dict ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files.
+.TP
+.B \-I2
+Generate the
+.I *.invf
+and
+.I *.invf.idx
+files.  Using this option requires
+that the
+.IR *.invf.dict.hash ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files
+be present.  The
+.I *.invf.dict.hash
+file is generated by
+.BR mg_perf_hash_build (1)
+from the
+.I *.invf.dict.build
+file.  If the
+.B \-W
+option is specified, the
+.I *.weight
+file will also be generated.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is to be
+written.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection that will be
+created.
+.TP
+.I filename(s)
+This specifies the source text. If this is not specified, then the
+program expects the source text from
+.IR stdin .
+.SH EXAMPLE
+What follows is a UNIX
+.BR csh (1)
+script as an example of how to build an
+.BR mg (1)
+document collection.
+.LP
+.nf
+.DT
+.ft B
+.I #! /bin/csh
+.I
+# The first argument on the command line specifies the
+.I
+#   source of the text
+set source = ($1)
+.PP
+.I
+# The second argument is the name of the collection
+set text = ($2)
+.PP
+.I
+# Create *.text.stats,  *.invf.dict.build,
+.I
+#   *.invf.chunk and *.invf.chunks.trans
+${source} | mg_passes -T1 -I1 -m 1 -t 1 -f ${text}
+.PP
+.I
+# Create *.text.dict
+mg_compression_dict -f ${text}
+.PP
+.I
+# Create *.invf.dict.hash
+mg_perf_hash_build -f ${text}
+.PP
+.I
+# Create *.text,  *.text.idx,
+.I
+#   *.invf and *.invf.idx
+${source} | mg_passes -T2 -I2 -c 2 -t 1 -f ${text}
+.PP
+.I
+# Create *.text.idx.wgt and *.weight.approx
+mg_weights_build -f ${text} -b 8
+.PP
+.I
+# Create *.invf.dict
+mg_invf_dict -f ${text} -b 4096
+.PP
+.I
+# Create *.text.dict
+mg_fast_comp_dict -f ${text}
+.ft R
+.fi
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.chunk
+Inverted file chunk descriptor file.  When the inverted file is
+created it is created in chunks that use no more than a set amount of
+memory.  This file describes those chunks.
+.TP
+.B *.invf.chunk.trans
+Word-occurrence-order to lexical-order translation file.  The
+.B *.invf.chunk
+file is written in word-occurrence order but is required by
+.B \-I2
+to be in lexical order.
+.TP
+.B *.invf.dict.build
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for an order-preserving perfect hash function.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.trace
+The default trace file.
+.TP
+.B *.compression.stats
+Statistics about the compression of the text.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes.c	(revision 16561)
@@ -0,0 +1,641 @@
+/**************************************************************************
+ *
+ * mg_passes.c -- Driver for the various passes
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#ifdef HAVE_MALLINFO
+# include <malloc.h> 
+#endif
+#include <stdlib.h>
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+
+#include "longlong.h"
+
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "text.h"
+#include "stemmer.h"
+
+#include "words.h"
+
+/*
+   $Log$
+   Revision 1.3  2004/11/29 03:15:13  kjdon
+   added some changes made by Emanuel Dejanu (Simple Words)
+
+   Revision 1.2  2004/04/25 23:01:18  kjdon
+   added a new -M option to mg_passes, allowing maxnumeric to be altered - made this change to keep gsdl3 mg inline with gsdl2 mg.
+
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.3  2001/09/21 12:46:42  kjm18
+   updated mg to be in line with mg_1.3f. Now uses long long for some variables
+   to enable indexing of very large collections.
+
+   Revision 1.2  2001/06/12 23:23:42  jrm21
+   fixed a bug where mg_passes segfaults when trying to print the usage message.
+
+   Revision 1.1  1999/08/10 21:18:12  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1998/12/17 09:12:53  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:47  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:13  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:57  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:52  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#define MAX_PASSES 5
+
+#define SPECIAL 1
+#define TEXT_PASS_1 2
+#define TEXT_PASS_2 4
+#define IVF_PASS_1 8
+#define IVF_PASS_2 16
+
+#define MIN_BUF 8192
+#define	TERMRECORD	'\002'
+
+unsigned long buf_size = 3 * 1024 * 1024;	/* 3Mb */
+unsigned long invf_buffer_size = 5 * 1024 * 1024;	/* 5Mb */
+unsigned long ChunkLimit = 0;
+char InvfLevel = 2;
+char SkipSGML = 0;
+char MakeWeights = 0;
+FILE *Comp_Stats = NULL;
+int comp_stat_point = 0;
+mg_ullong bytes_processed = 0;
+mg_ullong bytes_received = 0;
+int stemmer_num = 0; /* default to the lovin stemmer */
+int stem_method = 0;
+
+static char Passes = 0;
+static unsigned long trace = 0;
+static int Dump = 0;
+static char **files = NULL;
+static int num_files = 0;
+static char *trace_name = NULL;
+
+
+typedef struct pass_data
+  {
+    char *name;
+    int (*init) (char *);
+    int (*process) (u_char *, int);
+    int (*done) (char *);
+#ifdef HAVE_TIMES
+    clock_t init_time;
+    clock_t process_time;
+    clock_t done_time;
+#else
+    struct timeval init_time;
+    struct timeval process_time;
+    struct timeval done_time;
+#endif
+  }
+pass_data;
+
+#ifdef HAVE_TIMES
+#define NULL_TIMES 0, 0, 0
+#else
+#define NULL_TIMES {0, 0}, {0, 0}, {0, 0}
+#endif
+
+static pass_data PassData[MAX_PASSES] =
+{
+  {"special", init_special, process_special, done_special, NULL_TIMES},
+  {"text.pass1", init_text_1, process_text_1, done_text_1, NULL_TIMES},
+  {"text.pass2", init_text_2, process_text_2, done_text_2, NULL_TIMES},
+  {"ivf.pass1", init_ivf_1, process_ivf_1, done_ivf_1, NULL_TIMES},
+  {"ivf.pass2", init_ivf_2, process_ivf_2, done_ivf_2, NULL_TIMES},
+};
+
+static char *usage_str = "\nUSAGE:\n"
+"  %s [-h] [-G] [-D] [-1|-2|-3] [-T1] [-T2] [-I1] [-I2] [-N1]\n"
+"  %*s [-N2] [-W] [-S] [-b buffer-size] [-d dictionary-directory]\n"
+"  %*s [-t trace-point Mb] [-m invf-memory] [-c chunk-limit]\n"
+"  %*s [-n trace-name] [-C comp-stat-size] [-s stem_method]\n"
+"  %*s [-a stemmer] [-M max-numeric] -f doc-collection-name\n";
+
+
+static void 
+usage (char *err)
+{
+  if (err)
+    Message (err);
+  fprintf (stderr, usage_str, msg_prefix, strlen (msg_prefix), "",
+	   strlen (msg_prefix), "",strlen (msg_prefix), "",
+	   strlen (msg_prefix),"");
+  exit (1);
+}
+
+
+
+
+#if 0
+static char *
+str_comma (unsigned long u)
+{
+  static char buf[20];
+  unsigned long a, b, c, d;
+  a = u / 1000000000;
+  u -= a * 1000000000;
+  b = u / 1000000;
+  u -= b * 1000000;
+  c = u / 1000;
+  u -= c * 1000;
+  d = u;
+
+  if (a)
+    sprintf (buf, "%u,%03u,%03u,%03u", a, b, c, d);
+  else if (b)
+    sprintf (buf, "%u,%03u,%03u", b, c, d);
+  else if (c)
+    sprintf (buf, "%u,%03u", c, d);
+  else
+    sprintf (buf, "%u", d);
+  return (buf);
+}
+#endif
+
+
+
+
+int 
+open_next_file (int in_fd)
+{
+  if (in_fd > 0)
+    close (in_fd);
+  if (num_files == 0)
+    return (-1);
+  if ((in_fd = open (files[0], O_RDONLY)) == -1)
+    FatalError (1, "Cannot open %s", files[0]);
+  files++;
+  num_files--;
+  return (in_fd);
+}
+
+
+static void 
+driver (int in_fd, FILE * Trace, char *file_name)
+{
+  int pass, num = 1;
+
+  char *buffer = Xmalloc (buf_size);
+  unsigned long num_docs = 0;
+  unsigned long block_bytes = 0;
+  register int buf_left = buf_size;
+  register char *look_pos = buffer;
+  register char *end_pos = buffer;
+
+  ProgTime StartTime, InitTime, ProcTime, DoneTime;
+
+  GetTime (&StartTime);
+
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	pass_data *pd = &PassData[pass];
+#ifdef HAVE_TIMES
+	struct tms tims;
+	times (&tims);
+	pd->init_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	struct rusage ru;
+
+	getrusage (RUSAGE_SELF, &ru);
+	pd->init_time.tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->init_time.tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+	if (pd->init (file_name) == COMPERROR)
+	  FatalError (1, "Error during init of \"%s\"", pd->name);
+
+#ifdef HAVE_TIMES
+	times (&tims);
+	pd->init_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	getrusage (RUSAGE_SELF, &ru);
+	pd->init_time.tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->init_time.tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+	time_normalise (&pd->init_time);
+#endif
+      }
+
+  GetTime (&InitTime);
+  for (;;)
+    {
+      int len = 0;
+      char *base = look_pos;
+
+      while (look_pos != end_pos && *look_pos != TERMRECORD)
+	look_pos++;
+
+      while (look_pos == end_pos)
+	{
+	  if (buf_left < MIN_BUF)
+	    {
+	      bcopy (base, buffer, end_pos - base);
+	      look_pos = buffer + (end_pos - base);
+	      buf_left = buf_size - (end_pos - base);
+	      end_pos = look_pos;
+	      base = buffer;
+	    }
+	  if (buf_left)
+	    {
+    	      num = read (in_fd, end_pos, buf_left);
+	      if (num < 0) num = 0; /* RJM - quick hack :-) */
+	      if (num == 0)
+		if ((in_fd = open_next_file (in_fd)) != -1)
+		  num = read (in_fd, end_pos, buf_left);
+	      bytes_received += num;
+	      buf_left -= num;
+	      end_pos += num;
+	    }
+	  while (look_pos < end_pos && *look_pos != TERMRECORD)
+	    look_pos++;
+	  if (buf_left == 0 && base == buffer && look_pos == end_pos)
+	    {
+	      Message ("Unable to find document terminator (i.e ^B)"
+		       " in the document");
+	      FatalError (1, "The document is in excess of %d chars long",
+			  look_pos - base);
+	    }
+	  if (!num)
+	    break;
+	}
+      len = look_pos++ - base;
+
+      if (!num && base == end_pos)
+	break;
+
+      bytes_processed += len;
+
+#ifndef QUIET
+      if (!len)
+	Message ("Warning : Processing zero length document");
+#endif
+
+      for (pass = 0; pass < MAX_PASSES; pass++)
+	if (Passes & (1 << pass))
+	  {
+	    register pass_data *pd = &PassData[pass];
+
+#ifdef HAVE_TIMES
+	    struct tms tims;
+	    times (&tims);
+	    pd->process_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	    struct rusage ru;
+	    register struct timeval *tv = &pd->process_time;
+
+	    getrusage (RUSAGE_SELF, &ru);
+	    tv->tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	    tv->tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+
+	    if (pd->process ((u_char *) base, len) == COMPERROR)
+	      {
+		Message ("Error during processing of \"%s\"", pd->name);
+		if (Dump || Trace)
+		  {
+		    int i;
+		    FILE *f = Trace ? Trace : stderr;
+		    fprintf (f, "-=- * -=- * -=- * -=- * -=- * -=- * -=-\n");
+		    for (i = 0; i < len; i++)
+		      {
+			char ch = base[i];
+			if (ch == '\1' || ch == '\2')
+			  ch = '\n';
+			putc (ch, f);
+		      }
+		    fprintf (f, "-=- * -=- * -=- * -=- * -=- * -=- * -=-\n");
+		  }
+		if (Trace)
+		  fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+			   bytes_processed, num_docs,
+			   ElapsedTime (&StartTime, NULL));
+		exit (1);
+	      }
+
+#ifdef HAVE_TIMES
+	    times (&tims);
+	    pd->process_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	    getrusage (RUSAGE_SELF, &ru);
+	    tv->tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	    tv->tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+	  }
+      num_docs++;
+      if (Trace)
+	{
+	  block_bytes += (look_pos - base);
+	  if (block_bytes >= trace)
+	    {
+#ifdef HAVE_MALLINFO
+	      struct mallinfo mi;
+	      mi = mallinfo ();
+	      block_bytes -= trace;
+	      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs |%7.3f Mb | %s\n",
+		       bytes_processed, num_docs, mi.arena / 1024.0 / 1024.0,
+		       ElapsedTime (&StartTime, NULL));
+#else
+	      block_bytes -= trace;
+	      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+		       bytes_processed, num_docs,
+		       ElapsedTime (&StartTime, NULL));
+#endif
+	    }
+	}
+      if (!num && look_pos - 1 == end_pos)
+	break;
+    }
+
+#ifndef HAVE_TIMES
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      time_normalise (&PassData[pass].process_time);
+#endif
+
+  GetTime (&ProcTime);
+
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	pass_data *pd = &PassData[pass];
+#ifdef HAVE_TIMES
+	struct tms tims;
+	times (&tims);
+	pd->done_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	struct rusage ru;
+
+	getrusage (RUSAGE_SELF, &ru);
+	pd->done_time.tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->done_time.tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+	if (pd->done (file_name) == COMPERROR)
+	  FatalError (1, "Error during done of \"%s\"", pd->name);
+
+#ifdef HAVE_TIMES
+	times (&tims);
+	pd->done_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	getrusage (RUSAGE_SELF, &ru);
+	pd->done_time.tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->done_time.tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+	time_normalise (&pd->done_time);
+#endif
+      }
+  if (Trace)
+    {
+#ifdef HAVE_MALLINFO
+      struct mallinfo mi;
+      mi = mallinfo ();
+      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs |%7.3f Mb | %s\n",
+	       bytes_processed, num_docs, mi.arena / 1024.0 / 1024.0,
+	       ElapsedTime (&StartTime, NULL));
+#else
+      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+	       bytes_processed, num_docs,
+	       ElapsedTime (&StartTime, NULL));
+#endif
+    }
+
+  GetTime (&DoneTime);
+
+  Message ("");
+  Message ("%10s      :    init         process        done", "");
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	pass_data *pd = &PassData[pass];
+	char it[15], pt[15], dt[15];
+#ifdef HAVE_TIMES
+	strcpy (it, cputime_string (pd->init_time));
+	strcpy (pt, cputime_string (pd->process_time));
+	strcpy (dt, cputime_string (pd->done_time));
+#else
+	strcpy (it, cputime_string (&pd->init_time));
+	strcpy (pt, cputime_string (&pd->process_time));
+	strcpy (dt, cputime_string (&pd->done_time));
+#endif
+	Message ("%-10s      : %s   %s   %s", pd->name, it, pt, dt);
+      }
+  Message ("");
+  Message ("Init time       : %s", ElapsedTime (&StartTime, &InitTime));
+  Message ("Process time    : %s", ElapsedTime (&InitTime, &ProcTime));
+  Message ("Done time       : %s", ElapsedTime (&ProcTime, &DoneTime));
+  Message ("Total time      : %s", ElapsedTime (&StartTime, &DoneTime));
+  Message ("Documents       : %u", num_docs);
+  Message ("Bytes received  : %" ULL_FS, bytes_received);
+  Message ("Bytes processed : %" ULL_FS, bytes_processed);
+  Message ("Process Rate    : %.1f kB per cpu second",
+   (double) bytes_processed / (ProcTime.CPUTime - InitTime.CPUTime) / 1024);
+  free (buffer);
+}
+
+
+
+int main (int argc, char **argv)
+{
+  int ch, in_fd;
+  char *filename = NULL;
+  FILE *Trace = NULL;
+
+  msg_prefix = argv[0];
+
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "hC:WGSD123f:d:b:T:I:t:m:N:c:n:s:a:M:")) != -1)
+    {
+      switch (ch)
+	{
+	case 'G':
+	  SkipSGML = 1;
+	  break;
+	case 'S':
+	  Passes |= SPECIAL;
+	  break;
+	case '1':
+	  InvfLevel = 1;
+	  break;
+	case '2':
+	  InvfLevel = 2;
+	  break;
+	case '3':
+	  InvfLevel = 3;
+	  break;
+	case 'f':
+	  filename = optarg;
+	  break;
+	case 'n':
+	  trace_name = optarg;
+	  break;
+	case 'D':
+	  Dump = 1;
+	  break;
+	case 'W':
+	  MakeWeights = 1;
+	  break;
+	case 'd':
+	  set_basepath (optarg);
+	  break;
+	case 'a':
+	  stemmer_num = stemmernumber (optarg);
+	  break;
+	case 's':
+	  stem_method = atoi (optarg) & STEMMER_MASK;
+	  break;
+	case 'b':
+	  buf_size = atoi (optarg) * 1024;
+	  break;
+	case 'C':
+	  comp_stat_point = atoi (optarg) * 1024;
+	  break;
+	case 'c':
+	  ChunkLimit = atoi (optarg);
+	  break;
+	case 'm':
+	  invf_buffer_size = (int) (atof (optarg) * 1024 * 1024);
+	  break;
+	case 'I':
+	case 'N': /* N kept for compatability */
+	  if (*optarg == '1')
+	    Passes |= IVF_PASS_1;
+	  else if (*optarg == '2')
+	    Passes |= IVF_PASS_2;
+	  else
+	    usage ("Invalid pass number");
+	  break;
+	case 'T':
+	  if (*optarg == '1')
+	    Passes |= TEXT_PASS_1;
+	  else if (*optarg == '2')
+	    Passes |= TEXT_PASS_2;
+	  else
+	    usage ("Invalid pass number");
+	  break;
+	case 't':
+	  trace = (unsigned long) (atof (optarg) * 1024 * 1024);
+	  break;
+	case 'M':
+	  SetEnv ("maxnumeric", optarg, NULL);
+	  break;
+	case 'h':
+	case '?':
+	  usage (NULL);
+	}
+    }
+
+  if (!filename || *filename == '\0')
+    FatalError (1, "A document collection name must be specified.");
+
+  if (buf_size < MIN_BUF)
+    FatalError (1, "The buffer size must exceed 1024 bytes.");
+
+  if ((Passes & (IVF_PASS_1 | IVF_PASS_2)) == (IVF_PASS_1 | IVF_PASS_2))
+    FatalError (1, "I1 and I2 cannot be done simultaneously.");
+
+  if ((Passes & (TEXT_PASS_1 | TEXT_PASS_2)) == (TEXT_PASS_1 | TEXT_PASS_2))
+    FatalError (1, "T1 and T2 cannot be done simultaneously.");
+
+  if (!Passes)
+    FatalError (1, "S, T1, T2, I1 or I2 must be specified.");
+
+  if (optind < argc)
+    {
+      if ((in_fd = open (argv[optind], O_RDONLY)) == -1)
+	FatalError (1, "Cannot open %s", argv[optind]);
+      files = &argv[optind + 1];
+      num_files = argc - (optind + 1);
+    }
+  else
+    in_fd = 0;			/* stdin */
+
+
+  if (trace)
+    {
+      if (!trace_name)
+	trace_name = make_name (filename, TRACE_SUFFIX, NULL);
+      if (!(Trace = fopen (trace_name, "a")))
+	Message ("Unable to open \"%s\". No tracing will be done.", trace_name);
+      else
+	setbuf (Trace, NULL);
+    }
+  else
+    Trace = NULL;
+
+  if (comp_stat_point)
+    {
+      char *name = make_name (filename, COMPRESSION_STATS_SUFFIX, NULL);
+      if (!(Comp_Stats = fopen (name, "wb")))  /* [RPAP - Feb 97: WIN32 Port] */
+	Message ("Unable to open \"%s\". No comp. stats. will be generated.",
+		 name);
+    }
+
+
+  if (Trace)
+    {
+      int i;
+      fprintf (Trace, "\n\n\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      for (i = 0; i < argc; i++)
+	fprintf (Trace, "%s ", argv[i]);
+      fprintf (Trace, "\n\n");
+    }
+
+  driver (in_fd, Trace, filename);
+
+  if (Trace)
+    fclose (Trace);
+
+  if (Comp_Stats)
+    fclose (Comp_Stats);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.c	(revision 16561)
@@ -0,0 +1,546 @@
+/**************************************************************************
+ *
+ * mg_passes_4jni.c -- Driver for the various passes
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+/* this needs to come first */
+#include "sysfuncs.h"
+
+#ifdef HAVE_MALLINFO
+# include <malloc.h> 
+#endif
+
+#include "mg_passes_4jni.h"
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+
+#include "longlong.h"
+#include "stemmer.h"
+
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "text.h"
+
+#include "words.h"
+#include "environment.h"
+
+static char *RCSID = "$Id$";
+
+#define MAX_PASSES 5
+
+#define SPECIAL 1
+#define TEXT_PASS_1 2
+#define TEXT_PASS_2 4
+#define IVF_PASS_1 8
+#define IVF_PASS_2 16
+
+#define MIN_BUF 8192
+#define	TERMRECORD	'\002'
+
+unsigned long buf_size = 3 * 1024 * 1024;	/* 3Mb */
+unsigned long invf_buffer_size = 5 * 1024 * 1024;	/* 5Mb */
+unsigned long ChunkLimit = 0;
+char InvfLevel = 2;
+char SkipSGML = 0;
+char MakeWeights = 0;
+FILE *Comp_Stats = NULL;
+int comp_stat_point = 0;
+mg_ullong bytes_processed = 0;
+mg_ullong bytes_received = 0;
+int stemmer_num = 0; /* default to the lovin stemmer */
+int stem_method = 0;
+FILE * Trace;
+char * filename;
+unsigned long num_docs = 0;
+unsigned long block_bytes = 0;
+
+static char Passes = 0;
+static unsigned long trace = 0;
+static int Dump = 0;
+static char **files = NULL;
+static int num_files = 0;
+static char *trace_name = NULL;
+
+int mg_passes_exit_value = 0;
+
+typedef struct pass_data
+  {
+    char *name;
+    int (*init) (char *);
+    int (*process) (u_char *, int);
+    int (*done) (char *);
+#ifdef HAVE_TIMES
+    clock_t init_time;
+    clock_t process_time;
+    clock_t done_time;
+#else
+    struct timeval init_time;
+    struct timeval process_time;
+    struct timeval done_time;
+#endif
+  }
+pass_data;
+
+#ifdef HAVE_TIMES
+#define NULL_TIMES 0, 0, 0
+#else
+#define NULL_TIMES {0, 0}, {0, 0}, {0, 0}
+#endif
+
+static pass_data PassData[MAX_PASSES] =
+{
+  {"special", init_special, process_special, done_special, NULL_TIMES},
+  {"text.pass1", init_text_1, process_text_1, done_text_1, NULL_TIMES},
+  {"text.pass2", init_text_2, process_text_2, done_text_2, NULL_TIMES},
+  {"ivf.pass1", init_ivf_1, process_ivf_1, done_ivf_1, NULL_TIMES},
+  {"ivf.pass2", init_ivf_2, process_ivf_2, done_ivf_2, NULL_TIMES},
+};
+
+
+/* clear all the settings from one mg_passes run to the next */
+void clear_variables() {
+
+  buf_size = 3 * 1024 * 1024;	/* 3Mb */
+  invf_buffer_size = 5 * 1024 * 1024;	/* 5Mb */
+  ChunkLimit = 0;
+  InvfLevel = 2;
+  SkipSGML = 0;
+  MakeWeights = 0;
+  Comp_Stats = NULL;
+  comp_stat_point = 0;
+  bytes_processed = 0;
+  bytes_received = 0;
+  stemmer_num = 0; /* default to the lovin stemmer */
+  stem_method = 0;
+  Trace = NULL;
+  filename = NULL;
+  num_docs = 0;
+  block_bytes = 0;
+  
+  Passes = 0;
+  trace = 0;
+  Dump = 0;
+  files = NULL;
+  num_files = 0;
+  trace_name = NULL;
+  
+  mg_passes_exit_value = 0;
+}
+
+/* ################################################## */
+/* the following are methods to set all the variables that used to be
+   set by command line args */
+
+/* -S, -T1, -T2, -I1, -I2, args to mg_passes */
+void add_pass (char pass_type, char pass_num) {
+
+  switch(pass_type) {
+  case 'S':
+    Passes |= SPECIAL;
+    break;
+  case 'I':
+  case 'N':
+    if (pass_num == '1')
+      Passes |= IVF_PASS_1;
+    else if (pass_num == '2')
+      Passes |= IVF_PASS_2;
+    else
+      fprintf(stderr, "Invalid pass number %c for pass type %c\n", pass_num, pass_type);
+    break;
+  case 'T':
+    if (pass_num == '1')
+      Passes |= TEXT_PASS_1;
+    else if (pass_num == '2')
+      Passes |= TEXT_PASS_2;
+    else
+      fprintf(stderr, "Invalid pass number %c for pass type %c\n", pass_num, pass_type);
+    break;
+  }
+}
+
+/* -D arg to mg_passes */
+void dump_failed_document(int dump) {
+  Dump = dump;
+}
+
+/* -G arg to mg_passes */
+void ignore_sgml_tags(int ignore) {
+  if (ignore) {
+    SkipSGML = 1;
+  } else {
+    SkipSGML = 0;
+  }
+}
+
+/* -b arg to mg_passes */
+void set_buffer_size(long size) {
+  buf_size = size * 1024;
+  if (buf_size <  MIN_BUF) {
+    buf_size = MIN_BUF;
+  }
+}
+
+/* -c arg to mg_passes */
+void set_chunk_limit(long chunk_limit) {
+  ChunkLimit = chunk_limit;
+}
+
+/* -C arg to mg_passes */
+void set_comp_stat_point(int stat_point) {
+  comp_stat_point = stat_point * 1024;
+}
+
+/* -f arg to mg_passes */
+void set_filename(const char * filen) {
+  if (filename) {
+    Xfree (filename);
+    filename = NULL;
+  }
+  filename = Xstrdup (filen);  
+}
+
+/* -m arg to mg_passes */
+void set_inversion_limit(int limit) {
+  invf_buffer_size = limit * 1024 * 1024;
+}
+
+/* -1, -2, -3 args to mg_passes */
+void set_invf_level(char level) {
+  switch (level) {
+  case '1':
+    InvfLevel = 1;
+    break;
+  case '2':
+    InvfLevel = 2;
+    break;
+  case '3':
+    InvfLevel = 3;
+    break;
+  }
+}
+
+/* -W arg to mg_passes */
+void set_make_weights(int make_w) {
+  MakeWeights = make_w;
+}
+
+/* -M arg to mg_passes */
+void set_max_numeric(int max_numeric) {
+  char data[99];
+  sprintf(data, "%d", max_numeric);
+  SetEnv ("maxnumeric", data, NULL);
+}
+
+/* -a, -s args to mg_passes */
+void set_stem_options(const char * stemmer, int method) {
+  stemmer_num = stemmernumber (stemmer);
+  stem_method = method & STEMMER_MASK;
+}
+
+/* -t arg to mg_passes */
+void set_trace_point(int tracepos) {
+  trace = (unsigned long) (tracepos * 1024 * 1024);
+}
+
+/* -n arg to mg_passes */
+void set_trace_file(const char * filen) {
+  if (trace_name) {
+    Xfree (trace_name);
+    trace_name = NULL;
+  }
+  trace_name = Xstrdup (filen);
+}
+
+/* ############################################### */
+/* The old driver method has been split into 3:
+init_driver, process_document (called numdocs times), 
+finalise_driver.
+The above set vars methods should all be called before init_driver.
+*/
+
+
+ProgTime StartTime, InitTime, ProcTime, DoneTime;
+
+void 
+init_driver ()
+{
+  int pass;
+  if (!filename || *filename == '\0') {
+    mg_passes_exit_value = 1;
+    FatalError (1, "A document collection name must be specified.");
+  }
+
+  if ((Passes & (IVF_PASS_1 | IVF_PASS_2)) == (IVF_PASS_1 | IVF_PASS_2)) {
+    mg_passes_exit_value = 1;
+    FatalError (1, "I1 and I2 cannot be done simultaneously.");
+
+  }
+  if ((Passes & (TEXT_PASS_1 | TEXT_PASS_2)) == (TEXT_PASS_1 | TEXT_PASS_2)) {
+    mg_passes_exit_value = 1;
+    FatalError (1, "T1 and T2 cannot be done simultaneously.");
+  }
+  if (!Passes) {
+    mg_passes_exit_value = 1;
+    FatalError (1, "S, T1, T2, I1 or I2 must be specified.");
+  }
+  if (trace)
+    {
+      if (!trace_name)
+	trace_name = make_name (filename, TRACE_SUFFIX, NULL);
+      if (!(Trace = fopen (trace_name, "a")))
+	Message ("Unable to open \"%s\". No tracing will be done.", trace_name);
+      else
+	setbuf (Trace, NULL);
+    }
+  else
+    Trace = NULL;
+
+  if (comp_stat_point)
+    {
+      char *name = make_name (filename, COMPRESSION_STATS_SUFFIX, NULL);
+      if (!(Comp_Stats = fopen (name, "wb")))  /* [RPAP - Feb 97: WIN32 Port] */
+	Message ("Unable to open \"%s\". No comp. stats. will be generated.",
+		 name);
+    }
+
+  if (Trace)
+    {
+      fprintf (Trace, "\n\n\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      /* print out the args to mg_passes */
+      fprintf (Trace, "\n\n");
+    }
+
+
+  GetTime (&StartTime);
+  
+  for (pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass)) {
+      pass_data *pd = &PassData[pass];
+#ifdef HAVE_TIMES
+      struct tms tims;
+      times (&tims);
+      pd->init_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+      struct rusage ru;
+      
+      getrusage (RUSAGE_SELF, &ru);
+      pd->init_time.tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+      pd->init_time.tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+      if (pd->init (filename) == COMPERROR) {
+	mg_passes_exit_value = 1;
+	FatalError (1, "Error during init of \"%s\"", pd->name);
+      }
+#ifdef HAVE_TIMES
+      times (&tims);
+      pd->init_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+      getrusage (RUSAGE_SELF, &ru);
+      pd->init_time.tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+      pd->init_time.tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+      time_normalise (&pd->init_time);
+#endif
+    }
+  }
+  GetTime (&InitTime);
+}
+
+
+void process_document(const u_char *buffer, int len) {
+  int pass;
+  bytes_processed += len;
+
+#ifndef QUIET
+  if (!len)
+    Message ("Warning : Processing zero length document");
+#endif
+  
+  for (pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass))
+      {
+	register pass_data *pd = &PassData[pass];
+	
+#ifdef HAVE_TIMES
+	struct tms tims;
+	times (&tims);
+	pd->process_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	struct rusage ru;
+	register struct timeval *tv = &pd->process_time;
+	
+	getrusage (RUSAGE_SELF, &ru);
+	tv->tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	tv->tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+	if (pd->process ((u_char *) buffer, len) == COMPERROR)
+	  {
+	    Message ("Error during processing of \"%s\"", pd->name);
+	    if (Dump || Trace)
+	      {
+		int i;
+		FILE *f = Trace ? Trace : stderr;
+		fprintf (f, "-=- * -=- * -=- * -=- * -=- * -=- * -=-\n");
+		for (i = 0; i < len; i++)
+		  {
+		    char ch = buffer[i];
+		    if (ch == '\1' || ch == '\2')
+		      ch = '\n';
+		    putc (ch, f);
+		  }
+		fprintf (f, "-=- * -=- * -=- * -=- * -=- * -=- * -=-\n");
+	      }
+	    if (Trace)
+	      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+		       bytes_processed, num_docs,
+		       ElapsedTime (&StartTime, NULL));
+	    exit (1);
+	  }
+#ifdef HAVE_TIMES
+	times (&tims);
+	pd->process_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	getrusage (RUSAGE_SELF, &ru);
+	tv->tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	tv->tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+      }
+  }
+  num_docs++;
+  if (Trace)
+    {
+      block_bytes += len;
+      if (block_bytes >= trace)
+	{
+#ifdef HAVE_MALLINFO
+	  struct mallinfo mi;
+	  mi = mallinfo ();
+	  block_bytes -= trace;
+	  fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs |%7.3f Mb | %s\n",
+		   bytes_processed, num_docs, mi.arena / 1024.0 / 1024.0,
+		   ElapsedTime (&StartTime, NULL));
+#else
+	  block_bytes -= trace;
+	  fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+		   bytes_processed, num_docs,
+		   ElapsedTime (&StartTime, NULL));
+#endif
+	}
+    }
+}
+
+void finalise_driver() {
+  int pass;
+#ifndef HAVE_TIMES
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      time_normalise (&PassData[pass].process_time);
+#endif
+
+  GetTime (&ProcTime);
+
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	pass_data *pd = &PassData[pass];
+#ifdef HAVE_TIMES
+	struct tms tims;
+	times (&tims);
+	pd->done_time -= tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	struct rusage ru;
+	
+	getrusage (RUSAGE_SELF, &ru);
+	pd->done_time.tv_sec -= ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->done_time.tv_usec -= ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+#endif
+	if (pd->done (filename) == COMPERROR) {
+	  mg_passes_exit_value = 1;
+	  FatalError (1, "Error during done of \"%s\"", pd->name);
+	}
+#ifdef HAVE_TIMES
+	times (&tims);
+	pd->done_time += tims.tms_utime + tims.tms_stime;
+#elif defined(HAVE_GETRUSAGE)  /* [RPAP - Feb 97: WIN32 Port] */
+	getrusage (RUSAGE_SELF, &ru);
+	pd->done_time.tv_sec += ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
+	pd->done_time.tv_usec += ru.ru_utime.tv_usec + ru.ru_stime.tv_usec;
+	time_normalise (&pd->done_time);
+#endif
+      }
+  if (Trace)
+    {
+#ifdef HAVE_MALLINFO
+      struct mallinfo mi;
+      mi = mallinfo ();
+      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs |%7.3f Mb | %s\n",
+	       bytes_processed, num_docs, mi.arena / 1024.0 / 1024.0,
+	       ElapsedTime (&StartTime, NULL));
+#else
+      fprintf (Trace, "%11" ULL_FS " bytes |%7lu docs | %s\n",
+	       bytes_processed, num_docs,
+	       ElapsedTime (&StartTime, NULL));
+#endif
+    }
+
+  GetTime (&DoneTime);
+
+  Message ("");
+  Message ("%10s      :    init         process        done", "");
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	pass_data *pd = &PassData[pass];
+	char it[15], pt[15], dt[15];
+#ifdef HAVE_TIMES
+	strcpy (it, cputime_string (pd->init_time));
+	strcpy (pt, cputime_string (pd->process_time));
+	strcpy (dt, cputime_string (pd->done_time));
+#else
+	strcpy (it, cputime_string (&pd->init_time));
+	strcpy (pt, cputime_string (&pd->process_time));
+	strcpy (dt, cputime_string (&pd->done_time));
+#endif
+	Message ("%-10s      : %s   %s   %s", pd->name, it, pt, dt);
+      }
+  Message ("");
+  Message ("Init time       : %s", ElapsedTime (&StartTime, &InitTime));
+  Message ("Process time    : %s", ElapsedTime (&InitTime, &ProcTime));
+  Message ("Done time       : %s", ElapsedTime (&ProcTime, &DoneTime));
+  Message ("Total time      : %s", ElapsedTime (&StartTime, &DoneTime));
+  Message ("Documents       : %u", num_docs);
+  Message ("Bytes received  : %" ULL_FS, bytes_received);
+  Message ("Bytes processed : %" ULL_FS, bytes_processed);
+  Message ("Process Rate    : %.1f kB per cpu second",
+   (double) bytes_processed / (ProcTime.CPUTime - InitTime.CPUTime) / 1024);
+  //free (buffer);
+
+  if (Trace)
+    fclose (Trace);
+
+  if (Comp_Stats)
+    fclose (Comp_Stats);
+
+}
+
+int get_exit_value() {
+  return mg_passes_exit_value;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_passes_4jni.h	(revision 16561)
@@ -0,0 +1,86 @@
+/**************************************************************************
+ *
+ * mg_passes_4jni.h -- modified mg_passes for use from jni
+ * Copyright (C) 2004 New Zealand Digital Library, http://www.nzdl.org
+ *
+ * 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 H_MG_PASSES_4JNI
+#define H_MG_PASSES_4JNI
+
+/* clear all the settings from one mg_passes run to the next */
+void clear_variables();
+
+/* the following are methods to set all the variables that used to be
+   set by command line args */
+
+/* -S, -T1, -T2, -I1, -I2, args to mg_passes */
+void add_pass(char pass_type, char pass_num);
+
+/* -D arg to mg_passes */
+void dump_failed_document(int dump);
+
+/* -G arg to mg_passes */
+void ignore_sgml_tags(int ignore);
+
+/* -b arg to mg_passes */
+void set_buffer_size(long size);
+
+/* -c arg to mg_passes */
+void set_chunk_limit(long chunk_limit);
+
+/* -C arg to mg_passes */
+void set_comp_stat_point(int stat_point);
+
+/* -f arg to mg_passes */
+void set_filename(const char * filen);
+
+/* -m arg to mg_passes */
+void set_inversion_limit(int limit);
+
+/* -1, -2, -3 args to mg_passes */
+void set_invf_level(char level);
+
+/* -W arg to mg_passes */
+void set_make_weights(int make_w);
+
+/* -M arg to mg_passes */
+void set_max_numeric(int max_numeric);
+
+/* -a, -s args to mg_passes */
+void set_stem_options(const char * stemmer, int method);
+
+/* -t arg to mg_passes */
+void set_trace_point(int tracepos);
+
+/* -n arg to mg_passes */
+void set_trace_file(const char * filen);
+
+/* The old driver method has been split into 3:
+init_driver, process_document (called numdocs times), 
+finalise_driver.
+The above set vars methods should all be called before init_driver.
+*/
+
+void init_driver ();
+
+void process_document(const u_char *buffer, int len);
+
+void finalise_driver();
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.1	(revision 16561)
@@ -0,0 +1,89 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_perf_hash_build 1 \*(Dt CITRI
+.SH NAME
+mg_perf_hash_build \- generate an order-preserving hash function for the stemmed dictionary
+.SH SYNOPSIS
+.B mg_perf_hash_build
+[
+.B \-h
+]
+[
+.BI \-r " rndseed"
+]
+[
+.BI \-d  " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_perf_hash_build
+generates an order-preserving hashing function from the compressed
+stemmed dictionary and writes it out to disk.
+.BR mg_passes (1)
+will make use of the hash function when it builds the inverted file.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-r " rndseed"
+This specifies the random seed to be used in the generation of the
+hash function; it must be an integer.  If this is not specified, the
+current time will be used as the random seed.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf.dict
+Stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for a order-preserving perfect hash function.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_perf_hash_build.c	(revision 16561)
@@ -0,0 +1,186 @@
+/**************************************************************************
+ *
+ * mg_perf_hash_build.c -- Program to build a perfect hash function
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+#include "local_strings.h"
+#include "perf_hash.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "locallib.h"
+#include "words.h"
+#include "mg.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:13  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:47  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:15  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:58  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:53  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+
+#define POOL_SIZE 1024*1024
+
+static void process_files (char *filename);
+
+int r = -1;
+
+int main (int argc, char **argv)
+{
+  ProgTime start;
+  char *file_name = "";
+  int ch;
+  msg_prefix = argv[0];
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "f:d:r:h")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'r':
+	r = atoi (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file]"
+		 "[-d data directory] [-r random seed] [-h]\n", argv[0]);
+	exit (1);
+      }
+
+  GetTime (&start);
+  process_files (file_name);
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  return 0;
+}
+
+
+
+
+
+static void 
+process_files (char *filename)
+{
+  FILE *dict, *hash;
+  unsigned long i;
+  u_char prev[MAXSTEMLEN + 1];
+  struct invf_dict_header idh;
+  perf_hash_data *phd;
+  u_char *pool;
+  int pool_left;
+  u_char **starts;
+
+
+  dict = open_file (filename, INVF_DICT_SUFFIX, "rb",
+		    MAGIC_STEM_BUILD, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread ((char *) &idh, sizeof (idh), 1, dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  hash = create_file (filename, INVF_DICT_HASH_SUFFIX, "wb",
+		      MAGIC_HASH, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  if (!(pool = Xmalloc (POOL_SIZE)))
+    FatalError (1, "Out of memory");
+  pool_left = POOL_SIZE;
+
+  if (!(starts = Xmalloc (sizeof (u_char *) * idh.dict_size)))
+    FatalError (1, "Out of memory");
+
+  for (i = 0; i < idh.dict_size; i++)
+    {
+      register unsigned long copy, suff, l;
+      unsigned long wcnt, fcnt;
+
+      /* build a new word on top of prev */
+      copy = getc (dict);
+      suff = getc (dict);
+      *prev = copy + suff;
+      fread (prev + copy + 1, sizeof (u_char), suff, dict);
+
+      /* read other data, but no need to store it */
+      fread (&fcnt, sizeof (fcnt), 1, dict);
+      fread (&wcnt, sizeof (wcnt), 1, dict);
+
+      l = *prev + 1;
+      if (pool_left < l)
+	{
+	  pool = Xmalloc (POOL_SIZE);
+	  pool_left = POOL_SIZE;
+	}
+      starts[i] = pool;
+      bcopy ((char *) prev, (char *) pool, l);
+      pool += l;
+      pool_left -= l;
+    }
+  if (!(phd = gen_hash_func (idh.dict_size, starts, r)))
+    FatalError (1, "Unable to generate hash function");
+  if (write_perf_hash_data (hash, phd) == -1)
+    FatalError (1, "Unable to write hash function");
+  fclose (dict);
+  fclose (hash);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.1	(revision 16561)
@@ -0,0 +1,95 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_stem_idx 1 \*(Dt CITRI
+.SH NAME
+mg_stem_idx \- builds a stem index file
+.SH SYNOPSIS
+.B mg_stem_idx
+[
+.B \-h
+]
+[
+.B \-F
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.B \-s 1|2|3
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_stem_idx
+generates a stem index file for a collection that is to be built with
+stem indexes.  This program should be called three times: once for each
+.B -s
+parameter.  It uses the stemmed dictionary to create the stem index which
+contains pointers into the stemmed dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " num"
+The dictionary is stored in blocks on disk; this option is used to set
+the block size.  The default is 4-KB blocks.
+.TP
+.B \-F
+This option causes the disk blocks to be aligned on the appropriate
+boundaries.  For example, if 4-KB blocks are used, they will be aligned
+in the file on 4-KB boundaries.
+.TP
+.B -s 1|2|3
+The stem method to apply for the stem index.
+.br
+1 = casefolded and non-stemmed
+.br
+2 = non-casefolded and stemmed
+.br
+3 = casefolded and stemmed
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.invf.dict.blocked
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked.1
+Stem index with stem index method 1.
+.TP
+.B *.invf.dict.blocked.2
+Stem index with stem index method 2.
+.TP
+.B *.invf.dict.blocked.3
+Stem index with stem index method 3.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_invf_dict (1),
+.BR mgstemidxlist (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_stem_idx.c	(revision 16561)
@@ -0,0 +1,892 @@
+/**************************************************************************
+ *
+ * mg_stem_idx.c -- Memory efficient stem index builder
+ * Copyright (C) 1997  Ross Peeters
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "filestats.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "invf.h"
+#include "mg.h"
+#include "locallib.h"
+#include "backend.h"     /* For struct stemmed_dict */
+#include "words.h"
+#include "stemmer.h"
+#include "hash.h"
+#include "local_strings.h"
+
+typedef struct PosEntry
+{
+  unsigned int num_cases;
+  unsigned int blk;
+  unsigned short blk_index;
+  unsigned short offset;
+}
+PosEntry;
+
+
+typedef struct PosList
+{
+  unsigned int list_size;
+  unsigned int num_entries;
+  PosEntry PE[1];
+}
+PosList;
+
+
+typedef struct idx_hash_rec
+{
+  u_char *word;
+  PosList *PL;
+}
+idx_hash_rec;
+
+
+#define POOL_SIZE 1024*1024
+#define HASH_POOL_SIZE 8192
+#define INITIAL_HASH_SIZE 7927
+
+
+static unsigned long MaxMemInUse = 0;
+static unsigned long MemInUse = 0;
+
+static idx_hash_rec **IdxHashTable;
+static unsigned long IdxHashSize;
+static unsigned long IdxHashUsed;
+static u_char *IdxPool;
+static int IdxPoolLeft;
+
+static idx_hash_rec *idx_hr_pool;
+static int idx_hr_PoolLeft;
+
+static idx_hash_rec **idx_first_occr;
+static int idx_max_first_occr;
+
+int block_size = 1024 * 4;
+
+int force = 0;
+
+static long lookback = 4;
+
+static void 
+ChangeMem (int Change)
+{
+  MemInUse += Change;
+  if (MemInUse > MaxMemInUse)
+    MaxMemInUse = MemInUse;
+}
+
+
+/* =========================================================================
+ * Function: MakePosList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+PosList *
+MakePosList (int n)
+{
+  PosList *pl;
+  int list_size = (n == 0 ? 1 : n);	/* always allocate at least one node */
+
+  pl = Xmalloc (sizeof (PosList) + (list_size - 1) * sizeof (PosEntry));
+  if (!pl)
+    FatalError (1, "Unable to allocate term list");
+  ChangeMem (sizeof (PosList) + (list_size - 1) * sizeof (PosEntry));
+
+  pl->num_entries = n;
+  pl->list_size = list_size;
+
+  return pl;
+}
+
+/* =========================================================================
+ * Function: ResizePosList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+#define GROWTH_FACTOR 2
+#define MIN_SIZE 2
+
+static void
+ResizePosList (PosList ** pos_list)
+{
+  PosList *pl = *pos_list;
+
+  ChangeMem (-(sizeof (PosList) + (pl->list_size - 1) * sizeof (PosEntry)));
+  if (pl->num_entries > pl->list_size)
+    {
+      if (pl->list_size)
+	pl->list_size *= GROWTH_FACTOR;
+      else
+	pl->list_size = MIN_SIZE;
+    }
+  pl = Xrealloc (pl, sizeof (PosList) + (pl->list_size - 1) * sizeof (PosEntry));
+
+  if (!pl)
+    FatalError (1, "Unable to resize pos list");
+  ChangeMem (sizeof (PosList) + (pl->list_size - 1) * sizeof (PosEntry));
+
+  *pos_list = pl;
+}
+
+
+/* =========================================================================
+ * Function: FreePosList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+void
+FreePosList (PosList ** the_pl)
+{
+  PosList *pl = *the_pl;
+
+  ChangeMem (-(sizeof(PosList) + sizeof (PosEntry) * (pl->list_size - 1)));
+  Xfree (pl);
+
+  *the_pl = NULL;
+}
+
+
+/* =========================================================================
+ * Function: ResetPosList
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+ResetPosList (PosList ** pl)
+{
+  if (*pl)
+    FreePosList (pl);
+  *pl = MakePosList (0);
+}
+
+/* =========================================================================
+ * Function: AddPosEntry
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+AddPosEntry (PosList ** pos_list, PosEntry * pe)
+{
+  PosList *pl = *pos_list;
+
+  pl->num_entries++;
+  ResizePosList (pos_list);
+  pl = *pos_list;
+
+  /* copy the structure contents */
+  bcopy ((char *) pe, (char *) &(pl->PE[pl->num_entries - 1]), sizeof (PosEntry));
+
+  return pl->num_entries - 1;
+}
+
+
+/* Modified from stem_search.c */
+stemmed_dict *
+ReadStemDictBlk (File * stem_file)
+{
+  unsigned long i;
+  stemmed_dict *sd;
+  u_char *buffer;
+
+  if (!(sd = Xmalloc (sizeof (stemmed_dict))))
+    FatalError (1, "Could not allocate memory for stemmed dict");
+
+
+  sd->stem_file = stem_file;
+  sd->MemForStemDict = 0;
+
+  Fread (&sd->sdh, sizeof (sd->sdh), 1, stem_file);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(sd->sdh.lookback);
+  NTOHUL(sd->sdh.block_size);
+  NTOHUL(sd->sdh.num_blocks);
+  NTOHUL(sd->sdh.blocks_start);
+  NTOHUL(sd->sdh.index_chars);
+  NTOHUL(sd->sdh.num_of_docs);
+  NTOHUL(sd->sdh.static_num_of_docs);
+  NTOHUL(sd->sdh.num_of_words);
+  NTOHUL(sd->sdh.stemmer_num);
+  NTOHUL(sd->sdh.stem_method);
+  NTOHUL(sd->sdh.indexed);
+
+  if (!(buffer = Xmalloc (sd->sdh.index_chars)))
+    {
+      Xfree (sd);
+      FatalError (1, "Could not allocate memory for stemmed dict");
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.index_chars;
+
+  if (!(sd->index = Xmalloc (sd->sdh.num_blocks * sizeof (*sd->index))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      FatalError (1, "Could not allocate memory for stemmed dict");
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.num_blocks * sizeof (*sd->index);
+
+  if (!(sd->pos = Xmalloc (sd->sdh.num_blocks * sizeof (*sd->pos))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      Xfree (sd->index);
+      FatalError (1, "Could not allocate memory for stemmed dict");
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.num_blocks * sizeof (*sd->pos);
+
+  if (!(sd->buffer = Xmalloc (sd->sdh.block_size * sizeof (*sd->buffer))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      Xfree (sd->index);
+      Xfree (sd->buffer);
+      FatalError (1, "Could not allocate memory for stemmed dict");
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.block_size * sizeof (*sd->buffer);
+
+  sd->active = -1;
+
+  for (i = 0; i < sd->sdh.num_blocks; i++)
+    {
+      register u_char len;
+      sd->index[i] = buffer;
+      len = Getc (stem_file);
+      *buffer++ = len;
+      Fread (buffer, sizeof (u_char), len, stem_file);
+      buffer += len;
+      Fread (&sd->pos[i], sizeof (*sd->pos), 1, stem_file);
+      NTOHUL(sd->pos[i]);  /* [RPAP - Jan 97: Endian Ordering] */
+    }
+
+  return sd;
+}
+
+
+void
+mg_init_process ()
+{
+  /* Allocate memory for idx hash table */
+
+  if (!(IdxPool = Xmalloc (POOL_SIZE)))
+    FatalError (1, "Unable to allocate memory for idx pool");
+  IdxPoolLeft = POOL_SIZE;
+  ChangeMem (POOL_SIZE);
+
+  if (!(idx_hr_pool = Xmalloc (HASH_POOL_SIZE * sizeof (idx_hash_rec))))
+    FatalError (1, "Unable to allocate memory for idx pool");
+  idx_hr_PoolLeft = HASH_POOL_SIZE;
+  ChangeMem (HASH_POOL_SIZE * sizeof (idx_hash_rec));
+
+  IdxHashSize = INITIAL_HASH_SIZE;
+  IdxHashUsed = 0;
+  if (!(IdxHashTable = Xmalloc (sizeof (idx_hash_rec *) * IdxHashSize)))
+    FatalError (1, "Unable to allocate memory for idx table");
+  ChangeMem (sizeof (idx_hash_rec *) * IdxHashSize);
+  bzero ((char *) IdxHashTable, sizeof (idx_hash_rec *) * IdxHashSize);
+  idx_max_first_occr = 8192;
+  if (!(idx_first_occr = Xmalloc (sizeof (idx_hash_rec *) * idx_max_first_occr)))
+    FatalError (1, "Unable to allocate memory for idx_first_occur");
+  ChangeMem (sizeof (idx_hash_rec *) * idx_max_first_occr);
+}
+
+
+void
+PackIdxHashTable (void)
+{
+  int s, d;
+  for (s = d = 0; s < IdxHashSize; s++)
+    if (IdxHashTable[s])
+      IdxHashTable[d++] = IdxHashTable[s];
+  ChangeMem (-sizeof (idx_hash_rec *) * IdxHashSize);
+  ChangeMem (sizeof (idx_hash_rec *) * IdxHashUsed);
+  if (!(IdxHashTable = Xrealloc (IdxHashTable, sizeof (idx_hash_rec *) * IdxHashUsed)))
+    FatalError (1, "Out of memory");
+  IdxHashSize = IdxHashUsed;
+}
+
+
+void
+process_stem_dict (stemmed_dict * sd, int stem_method)
+{
+  int block;
+  short blk_index = -1;
+  int wordnum = -1;
+  PosEntry *prevPE = NULL;
+  idx_hash_rec *prevIdx = NULL;
+  u_char word[MAXSTEMLEN + 1];
+  u_char prev[MAXSTEMLEN + 1];
+
+  /* For each block in stem dict... */
+  for (block = 0; block < sd->sdh.num_blocks; block++)
+    {
+      register unsigned int res;
+      int num_indexes;
+      unsigned long *first_word, *last_invf_len;
+      unsigned short *num_words;
+      u_char *base;
+      unsigned short *index;
+
+      /* Read block into buffer */
+      Fseek (sd->stem_file, sd->pos[block] + sd->sdh.blocks_start, 0);
+      Fread (sd->buffer, sd->sdh.block_size, sizeof (u_char), sd->stem_file);
+      sd->active = sd->pos[block];
+
+      /* Move through block header */
+      first_word = (unsigned long *) (sd->buffer);
+      NTOHUL(*first_word);  /* [RPAP - Jan 97: Endian Ordering] */
+      last_invf_len = (unsigned long *) (first_word + 1);
+      NTOHUL(*last_invf_len);  /* [RPAP - Jan 97: Endian Ordering] */
+      num_words = (unsigned short *) (last_invf_len + 1);
+      NTOHUS(*num_words);  /* [RPAP - Jan 97: Endian Ordering] */
+      index = num_words + 1;
+      num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+
+      {
+	/* [RPAP - Jan 97: Endian Ordering] */
+	int i;
+	for (i = 0; i < num_indexes; i++)
+	  NTOHUS(index[i]);
+      }
+
+      base = (u_char *) (index + num_indexes);
+      blk_index = -1;
+
+      /* For each word in block... */
+      for (res = 0; res < *num_words; res++)
+	{
+	  unsigned copy, suff;
+	  register idx_hash_rec *idx_ent = 0;
+
+	  /* Update blk_index */
+	  if (!(res % sd->sdh.lookback))
+	    blk_index++;
+
+	  copy = *base++;
+	  suff = *base++;
+	  bcopy ((char *) base, (char *) (prev + copy + 1), suff);
+	  base += suff;
+	  *prev = copy + suff;
+
+	  /* Skip irrelevant word info */
+	  base += sizeof (u_long);
+	  base += sizeof (u_long);
+	  base += sizeof (u_long);
+
+	  /* Stem word */
+	  bcopy ((char *) prev, (char *) word, *prev + 1);
+	  stemmer (stem_method, sd->sdh.stemmer_num, word);
+
+	  /* Check if word follows straight on from previous word */
+	  if (prevIdx && !compare (word, prevIdx->word))
+	    prevPE->num_cases++;
+
+	  else 
+	    {
+	      /* Search the idx hash table for word */
+	      register unsigned long hashval, step;
+	      register int hsize = IdxHashSize;
+	      HASH (hashval, step, word, hsize);
+	      for (;;)
+		{
+		  register u_char *s1;
+		  register u_char *s2;
+		  register int len;
+		  idx_ent = IdxHashTable[hashval];
+		  if (!idx_ent)
+		    {
+		      /* Create a next entry in the hash table */
+		      int len = *word + 1;
+		      if (!idx_hr_PoolLeft)
+			{
+			  if (!(idx_hr_pool = Xmalloc (HASH_POOL_SIZE *
+						       sizeof (idx_hash_rec))))
+			    FatalError (1, "Unable to allocate memory for pool");
+			  idx_hr_PoolLeft = HASH_POOL_SIZE;
+			  ChangeMem (HASH_POOL_SIZE * sizeof (idx_hash_rec));
+			}
+		      idx_ent = idx_hr_pool++;
+		      idx_hr_PoolLeft--;
+		      if (len > IdxPoolLeft)
+			{
+			  if (!(IdxPool = Xmalloc (POOL_SIZE)))
+			    FatalError (1, "Unable to allocate memory for pool");
+			  IdxPoolLeft = POOL_SIZE;
+			  ChangeMem (POOL_SIZE);
+			}
+		      wordnum++;
+
+		      idx_ent->word = IdxPool;
+		      idx_ent->PL = MakePosList (0);
+		      {
+			PosEntry PE;
+			PE.num_cases = 1;
+			PE.blk = block;
+			PE.blk_index = (unsigned short) blk_index;
+			PE.offset = res % sd->sdh.lookback;
+			AddPosEntry (&(idx_ent->PL), &PE);
+		      }
+		      prevIdx = idx_ent;
+		      prevPE = &(idx_ent->PL->PE[idx_ent->PL->num_entries - 1]);
+
+		      bcopy ((char *) word, (char *) IdxPool, len);
+		      IdxPool += len;
+		      IdxPoolLeft -= len;
+		      if (IdxHashUsed == idx_max_first_occr - 1)
+			{
+			  ChangeMem (-sizeof (idx_hash_rec *) * idx_max_first_occr);
+			  idx_max_first_occr *= 2;
+			  if (!(idx_first_occr = Xrealloc (idx_first_occr, sizeof (idx_hash_rec *) *
+							   idx_max_first_occr)))
+			    FatalError (1, "Unable to allocate memory for idx_first_occr");
+			  ChangeMem (sizeof (idx_hash_rec *) * idx_max_first_occr);
+			}
+		      idx_first_occr[IdxHashUsed] = idx_ent;
+		      IdxHashUsed++;
+		      IdxHashTable[hashval] = idx_ent;
+		      break;
+		    } /* if (!idx_ent) */
+		  
+		  /* Compare the words */
+		  s1 = word;
+		  s2 = idx_ent->word;
+		  len = *s1 + 1;
+		  for (; len; len--)
+		    if (*s1++ != *s2++)
+		      break;
+		  
+		  if (len)
+		    {
+		      /* Entry is not the right one - move to next hash index */
+		      hashval = (hashval + step);
+		      if (hashval >= hsize)
+			hashval -= hsize;
+		    }
+		  else
+		    {
+		      /* Entry is correct - added PosEntry to word */
+		      PosEntry PE;
+		      PE.num_cases = 1;
+		      PE.blk = block;
+		      PE.blk_index = (unsigned short) blk_index;
+		      PE.offset = res % sd->sdh.lookback;
+		      AddPosEntry (&(idx_ent->PL), &PE);
+		      prevIdx = idx_ent;
+		      prevPE = &(idx_ent->PL->PE[idx_ent->PL->num_entries - 1]);
+		      break;
+		    }
+		} /* for (;;) */
+	    } /* else (the word does not follow on from previous one) */
+
+	  if (IdxHashUsed >= IdxHashSize >> 1)
+	    {
+	      idx_hash_rec **ht;
+	      unsigned long size;
+	      unsigned long i;
+	      size = prime (IdxHashSize * 2);
+	      if (!(ht = Xmalloc (sizeof (idx_hash_rec *) * size)))
+		FatalError (1, "Unable to allocate memory for idx table");
+	      bzero ((char *) ht, sizeof (idx_hash_rec *) * size);
+	      ChangeMem (sizeof (idx_hash_rec *) * size);
+
+	      for (i = 0; i < IdxHashSize; i++)
+		if (IdxHashTable[i])
+		  {
+		    register u_char *wptr;
+		    idx_hash_rec *ent;
+		    register unsigned long hashval, step;
+		    
+		    wptr = IdxHashTable[i]->word;
+		    HASH (hashval, step, wptr, size);
+		    ent = ht[hashval];
+		    while (ent)
+		      {
+			hashval += step;
+			if (hashval >= size)
+			  hashval -= size;
+			ent = ht[hashval];
+		      }
+		    ht[hashval] = IdxHashTable[i];
+		  }
+	      Xfree (IdxHashTable);
+	      ChangeMem (-sizeof (idx_hash_rec *) * IdxHashSize);
+	      IdxHashTable = ht;
+	      IdxHashSize = size;
+	    }
+	  
+	} /* end for each word */
+
+    } /* end for each block */
+
+}
+ 
+static int 
+idx_comp (const void *A, const void *B)
+{
+  u_char *s1 = (*((idx_hash_rec **) A))->word;
+  u_char *s2 = (*((idx_hash_rec **) B))->word;
+  return (casecompare (s1, s2));
+}
+
+
+void
+save_idx (char * filename, int stem_method)
+{
+  char *FName;
+  unsigned long i, j, pos, First_word, num;
+  struct stem_idx_header sih;
+  u_char *buffer, *last_word = NULL;
+  unsigned short *pointers;
+  int buf_in_use;
+  unsigned short ptrs_in_use, word_num;
+  FILE *idbi = NULL, *tmp = NULL;
+
+  FName = make_name (filename, ".tmp", NULL);
+  if (!(tmp = fopen (FName, "w+b")))
+    FatalError (1, "Unable to open \"%s\".\n", FName);
+
+  /* Delete the file now */
+  unlink (FName);
+
+  /* Create appropriate stem index file */
+  switch (stem_method)
+    {
+    case (1):
+      {
+	idbi = create_file (filename, INVF_DICT_BLOCKED_1_SUFFIX, "wb", MAGIC_STEM_1,
+			    MG_ABORT);
+	break;
+      }
+
+    case (2):
+      {
+	idbi = create_file (filename, INVF_DICT_BLOCKED_2_SUFFIX, "wb", MAGIC_STEM_2,
+			    MG_ABORT);
+	break;
+      }
+    case (3):
+      {
+	idbi = create_file (filename, INVF_DICT_BLOCKED_3_SUFFIX, "wb", MAGIC_STEM_3,
+			    MG_ABORT);
+	break;
+      }
+    }
+
+  if (!idbi)
+    FatalError (1, "Could NOT create .invf.blocked.%d file", stem_method);
+
+  PackIdxHashTable();
+  qsort (IdxHashTable, IdxHashUsed, sizeof (idx_hash_rec *), idx_comp);
+
+  sih.lookback = lookback;
+  sih.block_size = block_size;
+  sih.num_blocks = 0;
+  sih.blocks_start = 0;
+  sih.index_chars = 0;
+  sih.num_of_words = IdxHashUsed;
+
+  fwrite ((char *) &sih, sizeof (sih), 1, idbi);
+
+  if (!(buffer = Xmalloc (block_size + 512)))
+    FatalError (1, "Unable to allocate memory for \"buffer\"\n");
+  if (!(pointers = Xmalloc (block_size + 512)))
+    FatalError (1, "Unable to allocate memory for \"buffer\"\n");
+
+  buf_in_use = 0;
+  pos = 0;
+  word_num = 0;
+  ptrs_in_use = 0;
+  First_word = 0;
+
+  /* For each word in the hashtable... */
+  for (i = 0; i < IdxHashUsed; i++)
+    {
+      register unsigned long extra, copy, suff;
+      register struct idx_hash_rec *ent = IdxHashTable[i];
+
+      /* build a new word on top of prev */
+      if (last_word != NULL)
+	copy = prefixlen (last_word, ent->word);
+      else
+	copy = 0;
+      suff = *(ent->word) - copy;
+      last_word = ent->word;
+
+      if (word_num % sih.lookback == 0)
+	/* Will need copy chars to add + a pointer in index */
+	extra = copy + sizeof (*pointers);
+      else
+	extra = 0;
+      if ((ptrs_in_use + 1) * sizeof (*pointers) + sizeof (ptrs_in_use) + extra +
+	  buf_in_use + sizeof (First_word) + suff + 1 + sizeof (ent->PL->num_entries) + 
+	  ent->PL->num_entries * sizeof (PosEntry) > block_size)
+	{
+	  /* Dump buffer to tmp file */
+	  int chunk;
+	  HTONUL(First_word);  /* [RPAP - Jan 97: Endian Ordering] */
+	  HTONUS(word_num);  /* [RPAP - Jan 97: Endian Ordering] */
+	  fwrite (&First_word, sizeof (First_word), 1, tmp);
+	  fwrite (&word_num, sizeof (word_num), 1, tmp);
+	  fwrite (pointers, sizeof (*pointers), ptrs_in_use, tmp);
+	  fwrite (buffer, sizeof (u_char), buf_in_use, tmp);
+	  bzero ((char *) buffer, block_size);
+	  chunk = buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	    sizeof (ptrs_in_use) + sizeof (First_word);
+	  if (force && chunk < block_size)
+	    {
+	      fwrite (buffer, sizeof (u_char), block_size - chunk, tmp);
+	      chunk = block_size;
+	    }
+
+	  pos += chunk;
+
+	  buf_in_use = 0;
+	  word_num = 0;
+	  ptrs_in_use = 0;
+	  sih.num_blocks++;
+
+	  /* Check that entry will fit into new block */
+	  if (sizeof (*pointers) + sizeof (ptrs_in_use) + extra + sizeof (First_word) +
+	      suff + 1 + sizeof (ent->PL->num_entries) + 
+	      ent->PL->num_entries * sizeof (PosEntry) > block_size)
+	    FatalError (1, "Block size to small");
+
+	} /* if size > block_size */
+
+      if (word_num % sih.lookback == 0)
+	{
+	  HTONUS2(buf_in_use, pointers[ptrs_in_use++]);  /* [RPAP - Jan 97: Endian Ordering] */
+	  suff += copy;
+	  copy = 0;
+	}
+
+      /* Output Word information */
+      buffer[buf_in_use++] = copy;
+      buffer[buf_in_use++] = suff;
+      bcopy ((char *) (ent->word + copy + 1), (char *) (buffer + buf_in_use), suff);
+      buf_in_use += suff;
+      HTONUI(ent->PL->num_entries);  /* [RPAP - Jan 97: Endian Ordering] */
+      bcopy ((char *) &(ent->PL->num_entries), (char *) (buffer + buf_in_use), sizeof (ent->PL->num_entries));
+      NTOHUI(ent->PL->num_entries);  /* [RPAP - Jan 97: Endian Ordering] */
+      buf_in_use += sizeof (ent->PL->num_entries);
+
+      for (j = 0; j < ent->PL->num_entries; j++)
+	{
+	  register PosEntry *pe = &(ent->PL->PE[j]);
+	  HTONUI(pe->num_cases);  /* [RPAP - Jan 97: Endian Ordering] */
+	  bcopy ((char *) &(pe->num_cases), (char *) (buffer + buf_in_use), sizeof (pe->num_cases));
+	  buf_in_use += sizeof (pe->num_cases);
+	  HTONUI(pe->blk);  /* [RPAP - Jan 97: Endian Ordering] */
+	  bcopy ((char *) &(pe->blk), (char *) (buffer + buf_in_use), sizeof (pe->blk));
+	  buf_in_use += sizeof (pe->blk);
+	  HTONUS(pe->blk_index);  /* [RPAP - Jan 97: Endian Ordering] */
+	  bcopy ((char *) &(pe->blk_index), (char *) (buffer + buf_in_use), sizeof (pe->blk_index));
+	  buf_in_use += sizeof (pe->blk_index);
+	  HTONUS(pe->offset);  /* [RPAP - Jan 97: Endian Ordering] */
+	  bcopy ((char *) &(pe->offset), (char *) (buffer + buf_in_use), sizeof (pe->offset));
+	  buf_in_use += sizeof (pe->offset);
+	}
+
+      if (buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	  sizeof (ptrs_in_use) > block_size)
+	FatalError (1, "Fatal Internal Error # 64209258\n");
+
+      if (word_num == 0) /* the first word in each block - word_num set to 0 when buffer written to tmp file */
+	{
+	  /* Write word to main index */
+	  fwrite (ent->word, sizeof (u_char), *(ent->word) + 1, idbi);
+	  HTONUL(pos);  /* [RPAP - Jan 97: Endian Ordering] */
+	  fwrite (&pos, sizeof (pos), 1, idbi);
+	  NTOHUL(pos);  /* [RPAP - Jan 97: Endian Ordering] */
+	  sih.index_chars += *(ent->word) + 1;
+	  First_word = i;
+	}
+      word_num++;
+    } /* end for each word */ /* for each hash entry */
+
+  if (buf_in_use)
+    {
+      /* Write last buffer to tmp file */
+      int chunk;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(First_word);
+      HTONUS(word_num);
+
+      fwrite (&First_word, sizeof (First_word), 1, tmp);
+      fwrite (&word_num, sizeof (word_num), 1, tmp);
+      fwrite (pointers, sizeof (*pointers), ptrs_in_use, tmp);
+      fwrite (buffer, sizeof (u_char), buf_in_use, tmp);
+      bzero ((char *) buffer, block_size);
+      chunk = buf_in_use + ptrs_in_use * sizeof (*pointers) +
+	sizeof (ptrs_in_use) + sizeof (First_word);
+      if (force && chunk < block_size)
+	{
+	  fwrite (buffer, sizeof (u_char), block_size - chunk, tmp);
+	  chunk = block_size;
+	}
+
+      sih.num_blocks++;
+    }
+
+  rewind (tmp);
+  sih.blocks_start = sih.index_chars + sizeof (u_long) + sizeof (sih) +
+    sih.num_blocks * sizeof (pos);
+  if (force)
+    {
+      int amount;
+      amount = sih.blocks_start % block_size;
+      if (amount != 0)
+	{
+	  bzero ((char *) buffer, block_size);
+	  fwrite (buffer, sizeof (u_char), block_size - amount, idbi);
+	  sih.blocks_start += block_size - amount;
+	}
+    }
+
+  while ((num = fread (buffer, sizeof (u_char), block_size, tmp)) != 0)
+    fwrite (buffer, sizeof (u_char), num, idbi);
+  fclose (tmp);
+
+  /* skip over the magic number */
+  fseek (idbi, sizeof (u_long), 0);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(sih.lookback);
+  HTONUL(sih.block_size);
+  HTONUL(sih.num_blocks);
+  HTONUL(sih.blocks_start);
+  HTONUL(sih.index_chars);
+  HTONUL(sih.num_of_words);
+
+  fwrite (&sih, sizeof (sih), 1, idbi);
+  fclose (idbi);
+
+#ifndef SILENT
+  Message ("Stem %d:\n", stem_method);
+  Message ("     Block size   : %10d\n", block_size);
+  Message ("     Num_blocks   : %10d\n", NTOHUL(sih.num_blocks));  /* [RPAP - Jan 97: Endian Ordering] */
+  Message ("     Max mem used : %10.1f Mb\n", (double) MaxMemInUse / 1024.0 / 1024.0);
+  Message ("     Num_of_words : %10d\n", NTOHUL(sih.num_of_words));  /* [RPAP - Jan 97: Endian Ordering] */
+#endif
+}
+
+
+void
+UpdateStemDict (char * filename, int stem_method)
+{
+  FILE *idb;
+  struct stem_dict_header sdh;
+
+  if (!(idb = open_file (filename, INVF_DICT_BLOCKED_SUFFIX, "r+b",
+			 MAGIC_STEM, MG_CONTINUE)))
+    FatalError (1, "Could not update stemmed dict");
+
+  fread ((char *) &sdh, sizeof (sdh), 1, idb);
+  NTOHUL(sdh.indexed);  /* [RPAP - Jan 97: Endian Ordering] */
+  sdh.indexed |= 1 << (stem_method - 1);
+  HTONUL(sdh.indexed);
+  fseek (idb, sizeof (u_long), 0);
+  fwrite ((char *) &sdh, sizeof (sdh), 1, idb);
+  fclose (idb);
+}
+
+
+
+/* Main */
+int main (int argc, char **argv)
+{
+  File *idb;    /* File to .invf.dict.blocked */
+  char *filename = "";
+  stemmed_dict *sd;   /* Stemmed dictionary */
+  int ch;
+  char path[512];
+  int stem_method = 0;
+
+  msg_prefix = argv[0];
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "f:d:b:hFs:")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	filename = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'b':
+	block_size = atoi (optarg);
+	break;
+      case 'F':
+	force = 1;
+	break;
+      case 's':
+	stem_method = atoi (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-d directory] "
+		 "[-b num] [-F] [-h] -s 1|2|3 -f name\n", argv[0]);
+	exit (1);
+      }
+
+  if (stem_method < 1 || stem_method > 3)
+    FatalError (1, "Stem method must be 1, 2 or 3");
+
+  /* Open required stem dict file */
+  sprintf (path, FILE_NAME_FORMAT, get_basepath (), filename, INVF_DICT_BLOCKED_SUFFIX);
+  if (!(idb = Fopen (path, "rb", MAGIC_STEM)))
+    FatalError (1, "Unable to open \"%s\"", path);
+
+  /* Read in idb header and index to blocks */
+  if (!(sd = ReadStemDictBlk (idb)))
+    FatalError (1, "Could not read stemmed dictionary");
+
+  /* Process stemmed dictionary */
+  mg_init_process ();
+  process_stem_dict (sd, stem_method);
+  save_idx (filename, stem_method);
+ 
+  /* Close stemmed dict */
+  Fclose (idb);
+
+  /* Update stemmed dict */
+  UpdateStemDict (filename, stem_method);
+
+  return 0;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_text_estimate 1 \*(Dt CITRI
+.SH NAME
+mg_text_estimate \- Estimate the size of the compressed text based on the text statistics and a compression dictionary.
+.SH SYNOPSIS
+.B mg_text_estimate
+[
+.B \-h
+]
+[
+.BR \-H " |"
+.BR \-B " |"
+.BR \-D " |"
+.BR \-Y " |"
+.B \-M
+]
+.if n .ti +9n
+.I stats-dict
+.I compression-dict
+.SH DESCRIPTION
+This program estimates the size of the compressed text that would be
+generated if text with certain statistics were compressed with a
+particular dictionary.  This program has no real use in the
+.BR mg (1)
+system, except for experimenting with the
+.BR mg_compression_dict (1)
+program.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-m\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-H
+This specifies that novel words will be coded character by character
+using Huffman codes.
+.TP
+.B \-B
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using binary codes.  The binary code represents their occurrence
+position in the auxiliary dictionary.
+.TP
+.B \-D
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using delta codes.  The delta code represents their occurrence position
+in the auxiliary dictionary.
+.TP
+.B \-Y
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This generally
+produces better compression than
+.B \-B
+or
+.BR \-D .
+.TP
+.B \-M
+This specifies that the size of an auxiliary dictionary will be
+estimated.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This method is
+adaptive within documents, and generally produces better compression
+than
+.BR \-B ,
+.B \-D
+or
+.BR \-Y .
+.SH FILES
+.TP 20
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_estimate.c	(revision 16561)
@@ -0,0 +1,584 @@
+/**************************************************************************
+ *
+ * mg_text_estimate.c -- Program to estimate the compressed text size
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_gen.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "timing.h"
+#include "mgheap.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "invf.h"
+#include "text.h"
+#include "words.h"
+#include "mg.h"
+#include "comp_dict.h"
+#include "hash.h"
+
+
+
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:14  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.4  1999/01/15 03:05:51  rjmcnab
+
+   Renamed lib/heap to be lib/mgheap (it was causing some problems with
+   some versions of the STL libraries which contained a heap.h)
+
+   Revision 1.3  1998/12/17 09:12:53  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:48  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:19  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:59  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:54  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+typedef struct stats_t
+  {
+    u_long freq, occur;
+    u_long len;
+  }
+stats_t;
+
+static u_long huff_count = 0;
+static u_long escape_count = 0;
+
+
+char mode = 'H';
+
+/* [RJM 07/97: 4G limit] */
+static double ReadInWordsStandard (char *name, double *num_bytes);
+static double ReadInWordsSpecial (char *name, double *num_bytes,
+				  double *num_extra_bytes,
+				  u_long *aux_compressed);
+
+
+
+
+int main (int argc, char **argv)
+{
+  char *stats_dict = NULL, *comp_dict = NULL;
+  ProgTime StartTime;
+  double num_bytes, num_extra_bytes = 0; /* [RJM 07/97: 4G limit] */
+  u_long aux_compressed;
+  int ch;
+  double bytes;
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "BMDYHh")) != -1)
+    switch (ch)
+      {
+      case 'B':
+	Message ("The compressed text size for this method cannot be\n"
+		 "estimated exactly, so an upper bound will be calculated.");
+	mode = ch;
+	break;
+      case 'M':
+	Message ("The compressed text size for this method cannot be\n"
+		 "estimated exactly, so an upper bound will be calculated.");
+	mode = 'Y';
+	break;
+      case 'D':
+      case 'Y':
+      case 'H':
+	mode = ch;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-h] [B|M|D|Y|H] stats_dict comp_dict\n",
+		 argv[0]);
+	exit (1);
+      }
+
+  if (optind < argc)
+    stats_dict = argv[optind++];
+  else
+    stats_dict = NULL;
+  if (optind < argc)
+    comp_dict = argv[optind++];
+  else
+    {
+      fprintf (stderr, "usage: %s [-h] [B|M|D|Y|H] stats_dict comp_dict\n",
+	       argv[0]);
+      exit (1);
+    }
+
+
+
+  GetTime (&StartTime);
+
+  if (LoadCompressionDictionary (comp_dict) == COMPERROR)
+    exit (1);
+
+  if (mode == 'H')
+    bytes = ReadInWordsStandard (stats_dict, &num_bytes);
+  else
+    bytes = ReadInWordsSpecial (stats_dict, &num_bytes, &num_extra_bytes,
+				&aux_compressed);
+
+
+  Message ("Compressed Text %.2f Mb ( %.0f bytes   %0.3f %%)",
+	   bytes / 1024.0 / 1024, bytes, bytes * 100 / num_bytes);
+  Message ("Words portion of the dictionary %.2f Mb ( %.0f bytes   %0.3f %%)",
+	   Words_disk / 1024.0 / 1024, Words_disk * 1.0,
+	   (Words_disk * 100.0) / num_bytes);
+  if (mode == 'H')
+    Message ("Huffman info for chars in dictionary %.2f Mb ( %.0f bytes   %0.3f %%)",
+	     Chars_disk / 1024.0 / 1024, Chars_disk * 1.0,
+	     (Chars_disk * 100.0) / num_bytes);
+  else
+    {
+      Message ("Aux dictionary (Uncompressed) %.2f Mb ( %.0f bytes   %0.3f %%)",
+	       num_extra_bytes / 1024.0 / 1024, num_extra_bytes * 1.0,
+	       (num_extra_bytes * 100.0) / num_bytes);
+      Message ("Aux dictionary (Compressed)   %.2f Mb ( %.0f bytes   %0.3f %%)",
+	       aux_compressed / 1024.0 / 1024, aux_compressed * 1.0,
+	       (aux_compressed * 100.0) / num_bytes);
+    }
+  Message ("Words and non-words in the text %u", huff_count + escape_count);
+  Message ("Words and non-words coded by escapes %u ( %0.3f %%)", escape_count,
+	   escape_count * 100.0 / huff_count);
+  Message ("%s", ElapsedTime (&StartTime, NULL));
+  return 0;
+}
+
+
+
+
+
+static double 
+ReadInWordsStandard (char *name, double *num_bytes)
+{
+  compression_stats_header csh;
+  FILE *f;
+  u_long which;
+  double bytes, bits = 0;
+
+  f = open_named_file (name, "rb", MAGIC_STATS_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread (&csh, sizeof (csh), 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  for (which = 0; which < 2; which++)
+    {
+      frags_stats_header fsh;
+      int j;
+      fread (&fsh, sizeof (fsh), 1, f);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fsh.num_frags);
+      NTOHUL(fsh.mem_for_frags);
+
+      for (j = 0; j < fsh.num_frags; j++)
+	{
+	  int len, freq, res, occur_num;
+	  u_char Word[16];
+	  fread (&freq, sizeof (freq), 1, f);
+	  fread (&occur_num, sizeof (occur_num), 1, f);
+	  len = fgetc (f);
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  NTOHSI(freq);
+	  NTOHSI(occur_num);
+
+	  Word[0] = len;
+	  fread (Word + 1, len, 1, f);
+
+	  /* Search the hash table for Word */
+	  if (ht[which])
+	    {
+	      register unsigned long hashval, step;
+	      register int tsize = ht[which]->size;
+	      register u_char **wptr;
+	      HASH (hashval, step, Word, tsize);
+	      for (;;)
+		{
+		  register u_char *s1;
+		  register u_char *s2;
+		  register int len;
+		  wptr = ht[which]->table[hashval];
+		  if (wptr == NULL)
+		    {
+		      res = COMPERROR;
+		      break;
+		    }
+
+		  /* Compare the words */
+		  s1 = Word;
+		  s2 = *wptr;
+		  len = *s1 + 1;
+		  for (; len; len--)
+		    if (*s1++ != *s2++)
+		      break;
+
+		  if (len)
+		    {
+		      hashval += step;
+		      if (hashval >= tsize)
+			hashval -= tsize;
+		    }
+		  else
+		    {
+		      res = ht[which]->table[hashval] - ht[which]->words;
+		      break;
+		    }
+		}
+	    }
+	  else
+	    res = COMPERROR;
+
+	  /* Check that the word was found in the dictionary */
+	  if (res == COMPERROR)
+	    {
+	      escape_count += freq;
+	      if (cdh.dict_type == MG_COMPLETE_DICTIONARY)
+		Message ("Unknown word \"%.*s\"\n", *Word, Word + 1);
+	      if (cdh.dict_type == MG_PARTIAL_DICTIONARY)
+		{
+		  unsigned i;
+		  if (ht[which])
+		    {
+		      res = ht[which]->hd->num_codes - 1;
+		      bits += ht[which]->hd->clens[res] * freq;
+		    }
+		  bits += lens_huff[which].clens[Word[0]] * freq;
+		  for (i = 0; i < Word[0]; i++)
+		    bits += char_huff[which].clens[Word[i + 1]] * freq;
+		}
+	      if (cdh.dict_type == MG_SEED_DICTIONARY)
+		{
+		  unsigned i;
+		  if (ht[which])
+		    {
+		      res = ht[which]->hd->num_codes - 1;
+		      bits += ht[which]->hd->clens[res] * freq;
+		    }
+		  bits += lens_huff[which].clens[Word[0]] * freq;
+		  for (i = 0; i < Word[0]; i++)
+		    bits += char_huff[which].clens[Word[i + 1]] * freq;
+		}
+	    }
+	  else
+	    {
+	      bits += ht[which]->hd->clens[res] * freq;
+	      huff_count += freq;
+	    }
+	}
+    }
+  fclose (f);
+
+
+  /*
+   * The 5.5 bits added on per document consist of
+   *   1 bit for the word/non-word flag at the start of the document.
+   *   1 bit on average for document termination.
+   *   3.5 bits on average for padding to a byte boundary.
+   */
+  bits += 5.5 * csh.num_docs;
+
+  bytes = bits / 8 + sizeof (compressed_text_header) + sizeof (u_long);
+
+  *num_bytes = csh.num_bytes;
+
+  return bytes;
+}
+
+
+static int 
+stats_comp (const void *a, const void *b)
+{
+  return ((stats_t *) a)->occur - ((stats_t *) b)->occur;
+}
+
+
+
+
+
+static double 
+ReadInWordsSpecial (char *name, double *num_bytes,
+		    double *num_extra_bytes,
+		    u_long *aux_compressed)
+{
+  compression_stats_header csh;
+  FILE *f;
+  u_long magic, which;
+  double bytes, bits = 0;
+  double size[2];
+
+  if (!(f = fopen (name, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open file \"%s\"\n", name);
+
+  if (fread (&magic, sizeof (magic), 1, f) != 1)
+    FatalError (1, "Unable to read magic number");
+
+  NTOHUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (magic != MAGIC_STATS_DICT)
+    FatalError (1, "Bad magic number");
+
+  fread (&csh, sizeof (csh), 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  for (which = 0; which < 2; which++)
+    {
+      stats_t *stats;
+      frags_stats_header fsh;
+      int j, num = 0;
+      long lens[16], chars[256];
+
+      bzero ((char *) lens, sizeof (lens));
+      bzero ((char *) chars, sizeof (chars));
+
+      fread (&fsh, sizeof (fsh), 1, f);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fsh.num_frags);
+      NTOHUL(fsh.mem_for_frags);
+
+      stats = Xmalloc (fsh.num_frags * sizeof (*stats));
+      if (!stats)
+	FatalError (1, "Unable to allocate memory for stats");
+      for (j = 0; j < fsh.num_frags; j++)
+	{
+	  int len, res;
+	  u_char Word[16];
+	  fread (&stats[num].freq, sizeof (stats[num].freq), 1, f);
+	  fread (&stats[num].occur, sizeof (stats[num].occur), 1, f);
+	  len = fgetc (f);
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  NTOHSI(stats[num].freq);
+	  NTOHSI(stats[num].occur);
+
+	  stats[num].len = len + 1;
+
+	  Word[0] = len;
+	  fread (Word + 1, len, 1, f);
+
+	  /* Search the hash table for Word */
+	  if (ht[which])
+	    {
+	      register unsigned long hashval, step;
+	      register int tsize = ht[which]->size;
+	      register u_char **wptr;
+	      HASH (hashval, step, Word, tsize);
+	      for (;;)
+		{
+		  register u_char *s1;
+		  register u_char *s2;
+		  register int len;
+		  wptr = ht[which]->table[hashval];
+		  if (wptr == NULL)
+		    {
+		      res = COMPERROR;
+		      break;
+		    }
+
+		  /* Compare the words */
+		  s1 = Word;
+		  s2 = *wptr;
+		  len = *s1 + 1;
+		  for (; len; len--)
+		    if (*s1++ != *s2++)
+		      break;
+
+		  if (len)
+		    {
+		      hashval += step;
+		      if (hashval >= tsize)
+			hashval -= tsize;
+		    }
+		  else
+		    {
+		      res = ht[which]->table[hashval] - ht[which]->words;
+		      break;
+		    }
+		}
+	    }
+	  else
+	    res = COMPERROR;
+
+	  assert (stats[num].freq > 0);
+
+	  /* Check that the word was found in the dictionary */
+	  if (res == COMPERROR)
+	    {
+	      if (cdh.dict_type == MG_COMPLETE_DICTIONARY)
+		Message ("Unknown word \"%.*s\"\n", *Word, Word + 1);
+	      if ((cdh.dict_type == MG_PARTIAL_DICTIONARY ||
+		   cdh.dict_type == MG_SEED_DICTIONARY) &&
+		  ht[which])
+		{
+		  int k;
+		  res = ht[which]->hd->num_codes - 1;
+		  bits += ht[which]->hd->clens[res] * stats[num].freq;
+		  lens[Word[0]]++;
+		  for (k = 0; k < (int) Word[0]; k++)
+		    chars[Word[k + 1]]++;
+		}
+	      num++;
+	    }
+	  else
+	    {
+	      bits += ht[which]->hd->clens[res] * stats[num].freq;
+	    }
+	}
+
+      {
+	int j, num_buckets = 0;
+	int start[10], end[10];
+	long flens[16], fchars[256];
+
+	for (j = 0; j < 256; j++)
+	  if (!chars[j] && PESINAWORD (j) == which)
+	    fchars[j] = 1;
+	  else
+	    fchars[j] = chars[j];
+	for (j = 0; j < 16; j++)
+	  if (!lens[j])
+	    flens[j] = 1;
+	  else
+	    flens[j] = lens[j];
+
+	size[which] = Calculate_Huffman_Size (16, flens, lens) +
+	  Calculate_Huffman_Size (256, fchars, chars);
+
+
+	qsort (stats, num, sizeof (*stats), stats_comp);
+
+
+	if (mode == 'Y')
+	  {
+	    int k;
+	    num_buckets = 1;
+	    start[0] = 0;
+	    end[0] = cdh.num_words[which] - 1;
+	    Message ("There are %d novel %swords", num, which ? "" : "non-");
+	    while (end[num_buckets - 1] < num)
+	      {
+		start[num_buckets] = end[num_buckets - 1] + 1;
+		end[num_buckets] = start[num_buckets] +
+		  (end[num_buckets - 1] - start[num_buckets - 1]) * 2;
+		num_buckets++;
+	      }
+	    for (k = 0; k < num_buckets; k++)
+	      Message ("Bucket %d   %d <= x <= %d\n", k + 1, start[k], end[k]);
+	  }
+
+	for (j = 0; j < num; j++)
+	  {
+	    (*num_extra_bytes) += stats[j].len;
+	    switch (mode)
+	      {
+	      case 'D':
+		bits += BIO_Delta_Length (j + 1) * stats[j].freq;
+		break;
+	      case 'B':
+		bits += BIO_Binary_Length (j + 1, num) * stats[j].freq;
+		break;
+	      case 'Y':
+		{
+		  int k = 0;
+		  while (j > end[k])
+		    k++;
+		  if (!(j - start[k] + 1 >= 1 &&
+			j - start[k] + 1 <= end[k] - start[k] + 1))
+		    {
+		      Message ("k = %d, j = %d", k, j);
+		      assert (j - start[k] + 1 >= 1 &&
+			      j - start[k] + 1 <= end[k] - start[k] + 1);
+		    }
+		  else
+		    {
+		      bits += (BIO_Gamma_Length (k + 1) +
+			       BIO_Binary_Length (j - start[k] + 1,
+						  end[k] - start[k] + 1)) *
+			stats[j].freq;
+		    }
+		}
+	      }
+	  }
+	if (mode == 'B' && num)
+	  bits += BIO_Delta_Length (num) * csh.num_docs;
+      }
+      Xfree (stats);
+    }
+  fclose (f);
+
+  *aux_compressed = (size[0] + size[1]) / 8;
+
+  /*
+   * The 5.5 bits added on per document consist of
+   *   1 bit for the word/non-word flag at the start of the document.
+   *   1 bit on average for document termination.
+   *   3.5 bits on average for padding to a byte boundary.
+   */
+  bits += 5.5 * csh.num_docs;
+
+  bytes = bits / 8 + sizeof (compressed_text_header) + sizeof (u_long);
+
+  *num_bytes = csh.num_bytes;
+
+  return bytes;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.1	(revision 16561)
@@ -0,0 +1,76 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_text_merge 1 \*(Dt CITRI
+.SH NAME
+mg_text_merge \- Append one compressed text file to another
+.SH SYNOPSIS
+.B mg_text_merge
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+This program takes two
+.BR mg (1)
+database inverted files and stemmed
+dictionaries, named "old" and "new" respectively, and creates
+a merged stemmed dictionary and inverted file.
+It is part of the
+.BR mgmerge (1)
+addition to the
+.BR mg (1)
+package.
+The two compressed text files are
+.IR "name" .old.text
+and
+.IR "name" .new.text.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.old.text
+The old compressed text.
+.TP
+.B *.new.text
+The new compressed text that will be appended
+.TP
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mgmerge (1),
+.BR mg_get_merge (1),
+.BR mg_invf_merge (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mgquery (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_text_merge.c	(revision 16561)
@@ -0,0 +1,249 @@
+/**************************************************************************
+ *
+ * mg_text_merge.c --- merge  *.text, *.text.idx files
+ *                     part of the mgmerge utility
+ * Copyright (C) 1995 Shane Hudson (shane@cosc.canterbury.ac.nz)
+ *
+ * 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$
+ * Last edited:  November 11 1994
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "messages.h"
+#include "timing.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "locallib.h"
+#include "mg.h"
+#include "mg_merge.h"
+#include "mg_files.h"
+#include "text.h"
+
+
+/**** GLOBALS ****/
+FILE *text[3], *idx[3];
+
+typedef char FileName[256];
+FileName old_name, new_name, merge_name;
+
+long magicsize;			/* == where the header in a file begins */
+
+compressed_text_header cth[3];
+
+/*=======================================================================
+ * init_merge_text(): open files, set up global variables, etc
+ *=======================================================================*/
+int 
+init_merge_text ()
+{
+
+  /* open .text files */
+  text[OLD] = open_file (old_name, TEXT_SUFFIX, "r+b",
+			 MAGIC_TEXT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  magicsize = ftell (text[OLD]);
+  fread (&cth[OLD], sizeof (cth[OLD]), 1, text[OLD]);
+
+  text[NEW] = open_file (new_name, TEXT_SUFFIX, "rb+",
+			 MAGIC_TEXT, MG_ABORT);
+  fread (&cth[NEW], sizeof (cth[NEW]), 1, text[NEW]);
+
+  /* open .text.idx files */
+  idx[OLD] = open_file (old_name, TEXT_IDX_SUFFIX, "rb+",
+			MAGIC_TEXI, MG_ABORT);
+  fread (&cth[OLD], sizeof (cth[OLD]), 1, idx[OLD]);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cth[OLD].num_of_docs);
+  NTOHD(cth[OLD].num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(cth[OLD].num_of_words);
+  NTOHUL(cth[OLD].length_of_longest_doc);
+  NTOHD(cth[OLD].ratio);
+
+  idx[NEW] = open_file (new_name, TEXT_IDX_SUFFIX, "rb+",
+			MAGIC_TEXI, MG_ABORT);
+  fread (&cth[NEW], sizeof (cth[NEW]), 1, idx[NEW]);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cth[NEW].num_of_docs);
+  NTOHD(cth[NEW].num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(cth[NEW].num_of_words);
+  NTOHUL(cth[NEW].length_of_longest_doc);
+  NTOHD(cth[NEW].ratio);
+
+  idx[MERGE] = create_file (merge_name, TEXT_IDX_SUFFIX, "wb",
+			    MAGIC_TEXI, MG_ABORT);
+  return OK;
+}
+
+
+/*=======================================================================
+ * process_merge_text(): merge the files
+ *=======================================================================*/
+int 
+process_merge_text (void)
+{
+  int i;
+  u_long data, offset;
+  byte c;
+
+  /* update and write merged header to .text and .text.idx files */
+  /* they have the exact same header */
+  cth[MERGE].num_of_docs = cth[OLD].num_of_docs
+    + cth[NEW].num_of_docs;
+  cth[MERGE].num_of_bytes = cth[OLD].num_of_bytes
+    + cth[NEW].num_of_bytes;
+  cth[MERGE].num_of_words = cth[OLD].num_of_words;
+  cth[MERGE].length_of_longest_doc =
+    (cth[OLD].length_of_longest_doc > cth[NEW].length_of_longest_doc
+     ? cth[OLD].length_of_longest_doc
+     : cth[NEW].length_of_longest_doc);
+  cth[MERGE].ratio = ((cth[OLD].num_of_bytes * cth[OLD].ratio) +
+		      (cth[NEW].num_of_bytes * cth[NEW].ratio))
+    / cth[MERGE].num_of_bytes;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(cth[MERGE].num_of_docs);
+  HTOND(cth[MERGE].num_of_bytes); /* [RJM 07/97: 4G limit] */
+  HTONUL(cth[MERGE].num_of_words);
+  HTONUL(cth[MERGE].length_of_longest_doc);
+  HTOND(cth[MERGE].ratio);
+
+  fwrite (&cth[MERGE], sizeof (cth[MERGE]), 1, idx[MERGE]);
+  fseek (text[OLD], magicsize, 0);
+  fwrite (&cth[MERGE], sizeof (cth[MERGE]), 1, text[OLD]);
+
+  /*
+   *  Update *.text.idx:  need to know where each new doc starts
+   *  in the appended .text file
+   */
+  for (i = 0; i < cth[OLD].num_of_docs; i++)
+    {
+      fread (&data, sizeof (u_long), 1, idx[OLD]);
+      fwrite (&data, sizeof (u_long), 1, idx[MERGE]);
+    }
+
+  /* offset is the amount to add to each entry from idx[NEW] */
+  fread (&offset, sizeof (u_long), 1, idx[OLD]);
+  NTOHUL(offset);  /* [RPAP - Jan 97: Endian Ordering] */
+  offset -= (4 + sizeof (cth[OLD]));	/* 4 for the magic number */
+
+  for (i = 0; i < cth[NEW].num_of_docs; i++)
+    {
+      fread (&data, sizeof (u_long), 1, idx[NEW]);
+      NTOHUL(data);  /* [RPAP - Jan 97: Endian Ordering] */
+      data += offset;
+      HTONUL(data);  /* [RPAP - Jan 97: Endian Ordering] */
+      fwrite (&data, sizeof (u_long), 1, idx[MERGE]);
+    }
+  /* write last u_long in idx[MERGE] (= length of file) */
+  fread (&data, sizeof (u_long), 1, idx[NEW]);
+  NTOHUL(data);  /* [RPAP - Jan 97: Endian Ordering] */
+  data += offset;
+  HTONUL(data);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&data, sizeof (u_long), 1, idx[MERGE]);
+
+/******* update .text *******/
+  /* simply cat's the files together, except for the headers 
+   * and magic numbers, of course
+   */
+  fseek (text[OLD], 0L, 2);
+  while (!feof (text[NEW]))
+    {
+      fread (&c, sizeof (c), 1, text[NEW]);
+      if (!feof (text[NEW]))
+	fwrite (&c, sizeof (c), 1, text[OLD]);
+    }
+
+  return OK;
+}
+
+
+
+/*=======================================================================
+ * done_merge_text(): close files.
+ *=======================================================================*/
+int 
+done_merge_text (void)
+{
+  fclose (idx[MERGE]);
+  fclose (text[OLD]);
+  fclose (idx[OLD]);
+  fclose (text[NEW]);
+  fclose (idx[NEW]);
+
+  fprintf (stderr, "mg_text_merge: %ld documents added to %s\n",
+	   cth[NEW].num_of_docs, merge_name);
+
+  return OK;
+}
+
+
+/*=======================================================================
+ * usage()
+ *=======================================================================*/
+void 
+usage (char *progname)
+{
+  fprintf (stderr, "usage:  %s -f collection_name\n", progname);
+  exit (1);
+}
+
+
+/*=======================================================================
+ * main()
+ *=======================================================================*/
+int main (int argc, char *argv[])
+{
+  char *progname;
+  ProgTime start;
+  int ch;			/* for command line processing */
+
+  progname = argv[0];
+  msg_prefix = argv[0];
+  merge_name[0] = '\0';
+
+  while ((ch = getopt (argc, argv, "f:d:h")) != -1)
+    switch (ch)
+      {
+      case 'f':
+	strcpy (merge_name, optarg);
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'h':
+      case '?':
+      default:
+	usage (progname);
+      }
+
+  if (merge_name[0] == '\0')
+    usage (progname);
+  strcpy (old_name, merge_name);
+  strcat (old_name, ".old");
+  strcpy (new_name, merge_name);
+  strcat (new_name, ".new");
+
+  GetTime (&start);
+  init_merge_text ();
+  process_merge_text ();
+  done_merge_text ();
+  Message ("%s\n", ElapsedTime (&start, NULL));
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.1	(revision 16561)
@@ -0,0 +1,112 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mg_weights_build 1 \*(Dt CITRI
+.SH NAME
+mg_weights_build \- builds the approximate weights file
+.SH SYNOPSIS
+.B mg_weights_build
+[
+.B \-h
+]
+[
+.B \-s
+]
+[
+.BI \-b " num"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mg_weights_build
+is the program that generates the approximate weights file and the
+interleaved text index and exact weights file.  To do this, it uses
+the text index and either the exact weights file, or the inverted file
+and stemmed dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-s
+Save the weights generated from the
+.I *.invf
+and
+.I *.invf.dict
+file in a
+.I *.weight
+file.
+.TP
+.BI \-b " num"
+This specifies the number of bits of precision that should be used for
+the approximate weights.  If not specified, it defaults to 8.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mg_weights_build.c	(revision 16561)
@@ -0,0 +1,514 @@
+/**************************************************************************
+ *
+ * mg_weights_build.c -- Program to build the document weights file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_gen.h"
+#include "bitio_m.h"
+#include "bitio_m_stdio.h"
+#include "timing.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "invf.h"
+#include "text.h"
+#include "words.h"
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+/*
+   $Log$
+   Revision 1.2  2004/05/24 21:12:18  kjdon
+   changed a message
+
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:16  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:49  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:22  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1994/11/29  00:32:05  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.3  1994/10/20  03:57:00  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:55  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+unsigned char bits = 8;
+static char *file_name = "";
+static char *text_file_name = "";
+static unsigned long NumPara = 0;
+static unsigned long StaticNumOfDocs = 0;
+
+unsigned long get_NumPara (void);
+unsigned long get_StaticNumOfDocs (void);
+void GenerateWeights (void);
+void Make_weight_approx (void);
+void Make_text_idx_wgt (void);
+
+
+int main (int argc, char **argv)
+{
+  ProgTime StartTime;
+  int ch;
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "f:t:d:b:sh")) != -1) /* [RJM 10/98 - Text Filename] */
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	file_name = optarg;
+	if (strlen(text_file_name) == 0) text_file_name = optarg;
+	break;
+      /* [RJM 10/98 - Text Filename] */
+      case 't':		/* text input file */
+	text_file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'b':
+	bits = atoi (optarg);
+	if (bits > 32)
+	  {
+	    fprintf (stderr, "b may only take values 0-32\n");
+	    exit (1);
+	  }
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file]"
+		 "[-d data directory] [-b bits] [-s] [-h]\n", argv[0]);
+	exit (1);
+      }
+  GetTime (&StartTime);
+
+  GenerateWeights ();
+
+  Make_weight_approx ();
+
+  Make_text_idx_wgt ();
+
+  Message ("%s", ElapsedTime (&StartTime, NULL));
+
+  return 0;
+}
+
+
+
+
+unsigned long 
+get_NumPara (void)
+{
+  struct invf_dict_header idh;
+  FILE *invf_dict;
+  if (NumPara)
+    return (NumPara);
+  invf_dict = open_file (file_name, INVF_DICT_SUFFIX, "rb", MAGIC_STEM_BUILD,
+			 MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  fread ((char *) &idh, sizeof (idh), 1, invf_dict);
+  fclose (invf_dict);
+  NTOHUL2(idh.num_of_docs, NumPara);  /* [RPAP - Jan 97: Endian Ordering] */
+  return NumPara;
+}
+
+
+
+unsigned long 
+get_StaticNumOfDocs (void)
+/* the static number of documents is the N parameter used to
+ * decode document gaps in the inverted file encoded using
+ * the Bblock method.
+ */
+{
+  struct invf_dict_header idh;
+  FILE *invf_dict;
+  if (StaticNumOfDocs)
+    return (StaticNumOfDocs);
+  invf_dict = open_file (file_name, INVF_DICT_SUFFIX, "rb", MAGIC_STEM_BUILD,
+			 MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  fread ((char *) &idh, sizeof (idh), 1, invf_dict);
+  fclose (invf_dict);
+  NTOHUL2(idh.static_num_of_docs, StaticNumOfDocs);  /* [RPAP - Jan 97: Endian Ordering] */
+  return StaticNumOfDocs;
+}
+
+
+
+void GenerateWeights (void) {
+  FILE *dict, *invf, *f, *idx;
+  struct invf_dict_header idh;
+  struct invf_file_header ifh;
+  int i;
+  double logN;
+  float *DocWeights;
+
+  /* make sure the globals NumPara and StaticNumOfDocs are loaded */
+  get_NumPara ();
+  get_StaticNumOfDocs ();
+
+  /* check to see if the weights file has already been built */
+  if ((f = open_file (file_name, WEIGHTS_SUFFIX, "rb", MAGIC_WGHT,
+		      MG_CONTINUE)) != NULL) {
+      fclose (f);
+      return;
+  }
+  Message ("The file \"%s.weight\" does not exist.", file_name);
+  Message ("Building the weight data from the file \"%s.invf\".", file_name);
+
+  logN = log ((double) NumPara);
+
+  /* allocate memory for the weights */
+  if (!(DocWeights = Xmalloc (sizeof (float) * (NumPara + 1))))
+      FatalError (1, "No memory for doc weights");
+  bzero ((char *) DocWeights, sizeof (float) * (NumPara + 1));
+
+  /* open the .invf.dict file and read in its header */
+  dict = open_file (file_name, INVF_DICT_SUFFIX, "rb", MAGIC_STEM_BUILD,
+		    MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  fread ((char *) &idh, sizeof (idh), 1, dict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  /* open .invf.idx */
+  idx = open_file (file_name, INVF_IDX_SUFFIX, "rb", MAGIC_INVI,
+		   MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* open .invf and read in its header */
+  invf = open_file (file_name, INVF_SUFFIX, "rb", MAGIC_INVF,
+		    MG_ABORT);
+  fread ((char *) &ifh, sizeof (ifh), 1, invf);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(ifh.no_of_words);
+  NTOHUL(ifh.no_of_ptrs);
+  NTOHUL(ifh.skip_mode);
+  for (i = 0; i <= 15; i++)
+    NTOHUL(ifh.params[i]);
+  NTOHUL(ifh.InvfLevel);
+
+  /* make sure the inverted file does not contain skips and is not level 1 */
+  if (ifh.skip_mode != 0)
+    FatalError (0, "Can\'t make weights file from a skipped inverted file.");
+  if (ifh.InvfLevel == 1)
+    FatalError (0, "Can\'t make weights file from level 1 inverted file.");
+
+  DECODE_START (invf)
+
+    /* process each word adding its contributions to the document weights */
+    for (i = 0; i < ifh.no_of_words; i++)
+    {
+      u_char dummy1, dummy2[MAXSTEMLEN + 1];
+      unsigned long fcnt, wcnt, blk, CurrDoc, p, j;
+      float idf;
+
+      /* give a little feedback every 4096 words */
+      if ((i & 0xfff) == 0)
+	fprintf (stderr, ".");
+
+      /* read an entry for a word, just to get p value */
+      dummy1 = fgetc (dict);
+      dummy1 = fgetc (dict);
+      fread (dummy2, sizeof (u_char), dummy1, dict);
+      fread ((char *) &fcnt, sizeof (fcnt), 1, dict);
+      fread ((char *) &wcnt, sizeof (wcnt), 1, dict);
+
+      dummy2[dummy1] = '\0';
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      p = fcnt;
+
+      idf = logN - log ((double) fcnt);
+      blk = BIO_Bblock_Init (StaticNumOfDocs, p);
+      CurrDoc = 0;
+
+      /* check the inverted file index entry for this word */
+      {
+	unsigned long loc;
+	fread ((char *) &loc, sizeof (loc), 1, idx);
+	NTOHUL(loc);  /* [RPAP - Jan 97: Endian Ordering] */
+	if (ftell (invf) != loc)
+	  {
+	    FatalError (1, "Word %d  %d != %d", i, ftell (invf), loc);
+	  }
+      }
+
+      for (j = 0; j < p; j++)
+	{
+	  unsigned long x, tf;
+	  BBLOCK_DECODE (x, blk);
+	  CurrDoc += x;
+
+	  if (CurrDoc > idh.num_of_docs) {
+	    FatalError (1, "CurrDoc = %d, number of documents = %d", 
+			CurrDoc, idh.num_of_docs);
+	  }	  
+
+	  if (ifh.InvfLevel >= 2)
+	    {
+	      double weight;
+	      GAMMA_DECODE (tf);
+	      weight = tf * idf;
+	      DocWeights[CurrDoc - 1] += weight * weight;
+	    }
+	}
+      
+      while (__btg)
+	DECODE_BIT;
+    }
+
+  DECODE_DONE
+
+  fclose (dict);
+  fclose (invf);
+  fprintf (stderr, "\n");
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < NumPara; i++)
+    HTONF(DocWeights[i]);
+
+  f = create_file (file_name, WEIGHTS_SUFFIX, "wb", MAGIC_WGHT,
+		   MG_ABORT);
+
+  fwrite ((char *) DocWeights, sizeof (float), NumPara, f);
+  fclose (f);
+  Xfree (DocWeights);
+}
+
+
+
+
+
+
+
+
+
+
+
+void 
+Make_weight_approx (void)
+{
+  int i, pos, max;
+  unsigned long buf;
+  double U, L, B;
+  FILE *approx, *exact;
+
+  exact = open_file (file_name, WEIGHTS_SUFFIX, "rb", MAGIC_WGHT,
+		     MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* calculate U and L */
+  L = 1e300;
+  U = 0;
+  for (i = 0; i < NumPara; i++)
+    {
+      float wgt;
+      fread ((char *) &wgt, sizeof (wgt), 1, exact);
+      NTOHF(wgt);  /* [RPAP - Jan 97: Endian Ordering] */
+      wgt = sqrt (wgt);
+      if (wgt > U)
+	U = wgt;
+      if (wgt > 0 && wgt < L)
+	L = wgt;
+
+    }
+  fseek (exact, sizeof (u_long), SEEK_SET);
+
+  B = pow (U / L, pow (2.0, -(double) bits));
+
+  fprintf (stderr, "L = %f\n", L);
+  fprintf (stderr, "U = %f\n", U);
+  fprintf (stderr, "B = %f\n", B);
+
+
+
+  approx = create_file (file_name, APPROX_WEIGHTS_SUFFIX, "wb",
+			MAGIC_WGHT_APPROX, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fwrite ((char *) &bits, sizeof (bits), 1, approx);
+  HTOND(L);  /* [RPAP - Jan 97: Endian Ordering] */
+  HTOND(B);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite ((char *) &L, sizeof (L), 1, approx);
+  fwrite ((char *) &B, sizeof (B), 1, approx);
+  NTOHD(L);  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHD(B);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  max = bits == 32 ? 0xffffffff : (1 << bits) - 1;
+  for (buf = pos = i = 0; i < NumPara; i++)
+    {
+      unsigned long fx;
+      float wgt;
+      fread ((char *) &wgt, sizeof (wgt), 1, exact);
+      NTOHF(wgt);  /* [RPAP - Jan 97: Endian Ordering] */
+      wgt = sqrt (wgt);
+      if (wgt == 0)
+	{
+	  wgt = L;
+#ifndef QUIET
+	  Message ("Warning: Document %d had a weight of 0.", i);
+#endif
+	}
+      fx = (int) floor (log (wgt / L) / log (B));
+
+      if (fx > max)
+	fx = max;
+
+      buf |= (fx << pos);
+      pos += bits;
+
+      if (pos >= MAXBITS)
+	{
+	  HTONUL(buf);
+	  fwrite ((char *) &buf, sizeof (buf), 1, approx);
+	  buf = fx >> (bits - (pos - MAXBITS));
+	  pos = pos - MAXBITS;
+	}
+    }
+  if (pos > 0)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(buf);
+      fwrite ((char *) &buf, sizeof (buf), 1, approx);
+    }
+
+  fclose (approx);
+  fclose (exact);
+}
+
+
+
+
+
+void 
+Make_text_idx_wgt (void)
+{
+  compressed_text_header cth;
+  int i;
+  FILE *idx_wgt, *idx, *para, *exact;
+
+  idx_wgt = create_file (file_name, TEXT_IDX_WGT_SUFFIX, "wb", MAGIC_TEXI_WGT,
+			 MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  /* [RJM 10/98 - Text Filename] */
+  idx = open_file (text_file_name, TEXT_IDX_SUFFIX, "rb", MAGIC_TEXI,
+		   MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+  if (fread (&cth, sizeof (cth), 1, idx) != 1)
+    FatalError (1, "Unable to read header of index file");
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cth.num_of_docs);
+  NTOHD(cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(cth.num_of_words);
+  NTOHUL(cth.length_of_longest_doc);
+  NTOHD(cth.ratio);
+
+  exact = open_file (file_name, WEIGHTS_SUFFIX, "rb", MAGIC_WGHT,
+		     MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  get_NumPara ();
+  if (cth.num_of_docs != NumPara)
+    {
+      Message ("The number of documents %d does not equal "
+	       "the number of paragraphs %d.", cth.num_of_docs, NumPara);
+      Message ("Using the \"%s.invf.paragraph\" file\n", file_name);
+      para = open_file (file_name, INVF_PARAGRAPH_SUFFIX, "rb", MAGIC_PARAGRAPH,
+			MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+    }
+  else
+    para = NULL;
+
+  {
+    struct
+      {
+	unsigned long Start;
+	float Weight;
+      }
+    data;
+    for (i = 0; i < cth.num_of_docs; i++)
+      {
+	int count;
+	fread ((char *) &data.Start, sizeof (unsigned long), 1, idx);
+	if (para && i < cth.num_of_docs)
+	  {
+	    /* [RPAP - Jan 97: Endian Ordering] */
+	    fread ((char *) &count, sizeof (count), 1, para);
+	    NTOHSI(count);
+	  }
+	else
+	  count = 1;
+	while (count--)
+	  {
+	    fread ((char *) &data.Weight, sizeof (float), 1, exact);
+	    NTOHF(data.Weight);  /* [RPAP - Jan 97: Endian Ordering] */
+	    data.Weight = sqrt (data.Weight);
+	    HTONF(data.Weight);  /* [RPAP - Jan 97: Endian Ordering] */
+	    fwrite ((char *) &data, sizeof (data), 1, idx_wgt);
+	  }
+      }
+    /* Write out the extra entry for the idx file */
+    fread ((char *) &data.Start, sizeof (unsigned long), 1, idx);
+    data.Weight = 0;
+    fwrite((char*)&data, sizeof(data), 1, idx_wgt);
+  }
+
+  fclose (idx_wgt);
+  fclose (idx);
+  fclose (exact);
+  if (para)
+    fclose (para);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.1	(revision 16561)
@@ -0,0 +1,72 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgdictlist 1 \*(Dt CITRI
+.SH NAME
+mgdictlist \- list the contents of a dictionary
+.SH SYNOPSIS
+.B mgdictlist
+[
+.B \-q
+]
+.I filename
+.SH DESCRIPTION
+.B mgdictlist
+lists the contents of either a compressed compression dictionary, a
+text statistics file, or a compressed stemmed dictionary.  Unlike all
+the other programs in the
+.BR mg (1)
+suite, it is necessary to specify the complete pathname of the
+dictionary to list.
+.SH OPTIONS
+The
+.I filename
+must be last.
+.TP "\w'\fIfilename\fP'u+2n"
+.B \-q
+Generate an abbreviated output (machine-readable rather than
+human-readable).
+.TP
+.I filename
+This is the name of the dictionary to list.
+.B mgdictlist
+automatically detects whether the file is a compressed compression
+dictionary, text statistics file, or a compressed stemmed dictionary.
+.SH FILES
+.TP 20
+.B *.text.dict
+Text statistics file.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.invf.build
+Compressed stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgdictlist.c	(revision 16561)
@@ -0,0 +1,408 @@
+/**************************************************************************
+ *
+ * mgdictlist.c -- Program to list a dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "messages.h"
+#include "memlib.h"
+#include "local_strings.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "text.h"
+#include "invf.h"
+#include "locallib.h"
+#include "words.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:17  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:49  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:24  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1994/11/29  00:32:07  tes
+   * Committing the new merged files and changes.
+   *
+   * Revision 1.3  1994/10/20  03:57:01  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:56  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+int quick = 0;
+int no_of_words[2];
+u_long maxcodelen[2];
+
+char *dictname = "";
+
+
+
+
+void 
+DumpStemDict (FILE * f)
+{
+  struct invf_dict_header idh;
+  int i;
+  u_char prev[MAXSTEMLEN + 1];
+
+  fread (&idh, sizeof (idh), 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(idh.lookback);
+  NTOHUL(idh.dict_size);
+  NTOHUL(idh.total_bytes);
+  NTOHUL(idh.index_string_bytes);
+  NTOHD(idh.input_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(idh.num_of_docs);
+  NTOHUL(idh.static_num_of_docs);
+  NTOHUL(idh.num_of_words);
+  NTOHUL(idh.stemmer_num);
+  NTOHUL(idh.stem_method);
+
+  if (quick)
+    printf ("%ld\n", idh.dict_size);
+  else
+    {
+      printf ("# lookback           = %lu\n", idh.lookback);
+      printf ("# dict size          = %lu\n", idh.dict_size);
+      printf ("# total bytes        = %lu\n", idh.total_bytes);
+      printf ("# index string bytes = %lu\n", idh.index_string_bytes);
+      printf ("# input bytes        = %.0f\n", idh.input_bytes); /* [RJM 07/97: 4G limit] */
+      printf ("# num of docs        = %lu\n", idh.num_of_docs);
+      printf ("# static num of docs = %lu\n", idh.static_num_of_docs);
+      printf ("# num of words       = %lu\n", idh.num_of_words);
+      printf ("#\n");
+    }
+
+  for (i = 0; i < idh.dict_size; i++)
+    {
+      register unsigned long copy, suff;
+      unsigned long wcnt, fcnt;
+
+      /* build a new word on top of prev */
+      copy = getc (f);
+      suff = getc (f);
+      *prev = copy + suff;
+      fread (prev + copy + 1, sizeof (u_char), suff, f);
+
+      /* read other data, but no need to store it */
+      fread (&fcnt, sizeof (fcnt), 1, f);
+      fread (&wcnt, sizeof (wcnt), 1, f);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fcnt);
+      NTOHUL(wcnt);
+
+      if (!quick)
+	{
+	  printf ("%d: %8ld ", i, wcnt);
+	  printf ("/ %5ld ", fcnt);
+	  printf ("%2d %2ld\t\"", *prev, copy);
+	}
+      printf ("%s", word2str (prev));
+      if (quick)
+	printf (" %ld %ld\n", wcnt, fcnt);
+      else
+	{
+	  putchar ('"');
+	  putchar ('\n');
+	}
+    }
+}
+
+
+
+
+void 
+ReadInWords (FILE * f)
+{
+  comp_frags_header cfh;
+  u_long *codes;
+  u_char prev[MAXSTEMLEN + 1];
+  int i;
+
+  if (Read_cfh (f, &cfh, NULL, NULL) == -1)
+    FatalError (1, "Unable to read in the dictionary");
+
+  printf ("#\n");
+  printf ("#   max code len       = %u\n", cfh.hd.maxcodelen);
+  printf ("#   total bytes        = %lu\n", cfh.uncompressed_size);
+  printf ("#\n");
+
+  if (!(codes = Generate_Huffman_Codes (&cfh.hd, NULL)))
+    FatalError (1, "no memory for huffman codes\n");
+
+  for (i = 0; i < cfh.hd.num_codes; i++)
+    {
+      register int val, copy, j, k;
+      char code[33];
+      val = fgetc (f);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      fread (prev + copy + 1, sizeof (u_char), val, f);
+      *prev = val + copy;
+
+      for (k = 0, j = cfh.hd.clens[i] - 1; j >= 0; j--, k++)
+	code[k] = '0' + ((codes[i] >> j) & 1);
+      code[k] = '\0';
+
+      printf ("%d: %2d : %*s : \"%s\"\n", i, cfh.hd.clens[i],
+	      cfh.hd.maxcodelen, code, word2str (prev));
+    }
+  Xfree (codes);
+  Xfree (cfh.hd.clens);
+}
+
+
+void 
+ReadCharHuffman (FILE * f, char *title)
+{
+  int i;
+  huff_data hd;
+  u_long *codes;
+
+  if (Read_Huffman_Data (f, &hd, NULL, NULL) == -1)
+    FatalError (1, "Unable to read huffman data");
+
+  if (!(codes = Generate_Huffman_Codes (&hd, NULL)))
+    FatalError (1, "no memory for huffman codes\n");
+
+  printf ("#\n# %s\n#\n", title);
+  for (i = 0; i < hd.num_codes; i++)
+    if (hd.clens[i])
+      {
+	int j, k;
+	char code[33];
+	for (k = 0, j = hd.clens[i] - 1; j >= 0; j--, k++)
+	  code[k] = '0' + ((codes[i] >> j) & 1);
+	code[k] = '\0';
+	printf ("%2d : %*s : \"%s\"\n", hd.clens[i],
+		hd.maxcodelen, code, char2str (i));
+      }
+  Xfree (codes);
+  Xfree (hd.clens);
+}
+
+
+void 
+ReadLenHuffman (FILE * f, char *title)
+{
+  int i;
+  huff_data hd;
+  u_long *codes;
+
+  if (Read_Huffman_Data (f, &hd, NULL, NULL) == -1)
+    FatalError (1, "Unable to read huffman data");
+
+  if (!(codes = Generate_Huffman_Codes (&hd, NULL)))
+    FatalError (1, "no memory for huffman codes\n");
+
+  printf ("#\n# %s\n#\n", title);
+  for (i = 0; i < hd.num_codes; i++)
+    if (hd.clens[i])
+      {
+	int j, k;
+	char code[33];
+	for (k = 0, j = hd.clens[i] - 1; j >= 0; j--, k++)
+	  code[k] = '0' + ((codes[i] >> j) & 1);
+	code[k] = '\0';
+	printf ("%2d : %*s : %d\n", hd.clens[i],
+		hd.maxcodelen, code, i);
+      }
+  Xfree (codes);
+  Xfree (hd.clens);
+}
+
+
+
+
+
+void 
+DumpTextDict (FILE * f)
+{
+  struct compression_dict_header cdh;
+  int which;
+
+  if (Read_cdh (f, &cdh, NULL, NULL) == -1)
+    FatalError (1, "Unable to read dictionary header");
+  switch (cdh.dict_type)
+    {
+    case MG_COMPLETE_DICTIONARY:
+      printf ("# COMPLETE DICTIONARY\n");
+      break;
+    case MG_PARTIAL_DICTIONARY:
+      printf ("# PARTIAL DICTIONARY\n");
+      break;
+    case MG_SEED_DICTIONARY:
+      printf ("# SEED DICTIONARY\n");
+      break;
+    }
+  printf ("# num words          = %lu\n", cdh.num_words[1]);
+  printf ("# num word chars     = %lu\n", cdh.num_word_chars[1]);
+  printf ("# num non-words      = %lu\n", cdh.num_words[0]);
+  printf ("# num non-word chars = %lu\n", cdh.num_word_chars[0]);
+  printf ("# lookback           = %lu\n", cdh.lookback);
+
+  for (which = 0; which < 2; which++)
+    switch (cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  ReadInWords (f);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    ReadInWords (f);
+
+	  ReadCharHuffman (f, "Characters");
+	  ReadLenHuffman (f, "Lengths");
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    ReadInWords (f);
+
+	  ReadCharHuffman (f, "Characters");
+	  ReadLenHuffman (f, "Lengths");
+	}
+	break;
+      }
+}
+
+
+
+
+void 
+DumpStatsDict (FILE * f)
+{
+  int i;
+  compression_stats_header csh;
+
+  fread (&csh, sizeof (csh), 1, f);
+
+  for (i = 0; i < 2; i++)
+    {
+      int j;
+      frags_stats_header fsh;
+
+      fread (&fsh, sizeof (fsh), 1, f);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fsh.num_frags);
+      NTOHUL(fsh.mem_for_frags);
+
+      if (!quick)
+	printf ("#\n# num %9s      = %lu\n#\n", i ? "words" : "non-words",
+		fsh.num_frags);
+
+      for (j = 0; j < fsh.num_frags; j++)
+	{
+	  u_char Word[16];
+	  u_long freq, occur_num;
+
+	  fread (&freq, sizeof (freq), 1, f);
+	  fread (&occur_num, sizeof (occur_num), 1, f);
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  NTOHUL(freq);
+	  NTOHUL(occur_num);
+
+	  Word[0] = fgetc (f);
+	  fread (Word + 1, Word[0], 1, f);
+	  printf ("%d: %7ld : %7ld : \"%s\"\n", j, freq,
+		  occur_num, word2str (Word));
+	}
+    }
+}
+
+
+int main (int argc, char **argv)
+{
+  FILE *fp;
+  unsigned long magic = 0;
+
+  if (argc < 2)
+    FatalError (1, "A file name must be specified");
+  dictname = argv[1];
+  if (strcmp (dictname, "-q") == 0)
+    {
+      quick = 1;
+      if (argc < 3)
+	FatalError (1, "A file name must be specified");
+      dictname = argv[2];
+    }
+  if (!(fp = fopen (dictname, "rb")))  /* [RPAP - Feb 97: WIN32 Port] */
+    FatalError (1, "Unable to open \"%s\"", dictname);
+
+  fread (&magic, sizeof (magic), 1, fp);
+
+  NTOHUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  switch (magic)
+    {
+    case MAGIC_STEM_BUILD:
+      if (!quick)
+	printf ("# Contents of STEM file \"%s\"\n#\n", dictname);
+      DumpStemDict (fp);
+      break;
+    case MAGIC_DICT:
+      if (!quick)
+	printf ("# Contents of DICT file \"%s\"\n#\n", dictname);
+      DumpTextDict (fp);
+      break;
+    case MAGIC_STATS_DICT:
+      if (!quick)
+	printf ("# Contents of STATS file \"%s\"\n#\n", dictname);
+      DumpStatsDict (fp);
+      break;
+    default:
+      FatalError (1, "Bad magic number. \"%s\" cannot be dumped", dictname);
+    }
+  fclose (fp);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgpass.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgpass.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgpass.c	(revision 16561)
@@ -0,0 +1,510 @@
+/**************************************************************************
+ *
+ * mgpass.cpp -- Driver for the various passes -
+
+	V1 -  removed all the pipe processing and replaced with
+	      code to directly explore a directory of files.
+	V2 -  rebuilt to extract non text files from web browser
+	      'catch' file.  Also to display progress count
+			   GH/WJR
+
+ * Copyright (C) 1994  Neil Sharman, ..
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "memlib.h"
+#include "messages.h"
+
+#include "longlong.h"
+
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "text.h"
+#include "stemmer.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:23  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2001/09/20 02:32:00  cs025
+   a few fixes for compiling under windows with new long long stuff in mg
+
+   Revision 1.1  1999/08/10 21:17:39  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.5  1998/12/02 01:32:40  sjboddie
+   fixed 1 missing semi-colon..
+
+   Revision 1.4  1998/12/02 00:57:10  rjmcnab
+
+   Fixed to compile with the changes to the stemming.
+
+   Revision 1.3  1998/11/24 03:39:23  rjmcnab
+
+   Fixed a small error in the windows compilation
+
+   Revision 1.2  1998/11/24 01:29:39  rjmcnab
+
+   Fixed a few problems with the windows build
+
+   Revision 1.1  1998/11/17 09:34:12  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:56:57  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:52  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#define MAX_PASSES 5
+
+#define SPECIAL 1
+#define TEXT_PASS_1 2
+#define TEXT_PASS_2 4
+#define IVF_PASS_1 8
+#define IVF_PASS_2 16
+
+#define MIN_BUF 8192
+#define path_length 256
+
+unsigned long buf_size = 3 * 1024 * 1024;       /* 3Mb */
+unsigned long invf_buffer_size = 5 * 1024 * 1024;       /* 5Mb */
+unsigned long ChunkLimit = 0;
+char InvfLevel = 2;
+char SkipSGML = 0;
+char MakeWeights = 0;
+FILE *Comp_Stats = NULL;
+int comp_stat_point = 0;
+mg_ullong bytes_processed = 0;
+int num_docs = 0;
+mg_ullong bytes_received = 0;
+int stem_method = 0;
+int stemmer_num = 0; /* default to the Lovin stemmer */
+
+static char Passes = 0;
+static unsigned long trace = 0;
+static int Dump = 0;
+static char **files = NULL;
+static int num_files = 0;
+static char *trace_name = NULL;
+
+static char dirname[path_length], wildname[path_length];
+static int by_para = 0, recurse = 0, html_catch = 0;
+static char *buffer;
+char *line_start, *data_end, *base, *scan;
+
+typedef struct pass_data
+  {
+    char *name;
+    int (*init) (char *);
+    int (*process) (u_char *, int);
+    int (*done) (char *);
+  }
+pass_data;
+
+static pass_data PassData[MAX_PASSES] =
+{
+  {"special", init_special, process_special, done_special},
+  {"text.pass1", init_text_1, process_text_1, done_text_1},
+  {"text.pass2", init_text_2, process_text_2, done_text_2},
+  {"ivf.pass1", init_ivf_1, process_ivf_1, done_ivf_1},
+  {"ivf.pass2", init_ivf_2, process_ivf_2, done_ivf_2},
+};
+
+static char *usage_str = "\nUSAGE:\n"
+"  %s [-h] [-G] [-D] [-1|-2|-3] [-T1] [-T2] [-I1] [-I2] [-N1]\n"
+"  %*s [-N2] [-W] [-S] [-b buffer-size] [-d dictionary-directory]\n"
+"  %*s [-t trace-point Mb] [-m invf-memory] [-c chunk-limit]\n"
+"  %*s [-n trace-name] [-C comp-stat-size] [-s stem_method] -f doc-collection-name\n"
+"  %*s [source directory\\] [source file]\n";
+
+static void usage (char *err)
+{
+  if (err) Message (err);
+  fprintf (stderr, usage_str, msg_prefix, strlen (msg_prefix), "",
+    strlen (msg_prefix), "", strlen (msg_prefix), "");
+  exit (1);
+}
+
+void do_process(char *buffer, int num)
+{
+  int pass;
+  if (num == 0) Message ("Warning : Processing zero length document");
+  num_docs++;
+  bytes_processed += num;
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+	if (PassData[pass].process((u_char *)buffer, num) == COMPERROR)
+	   FatalError(1, "Error during file processing");
+}
+
+int refill(int in_fd)
+{
+  int num, bitleft;
+  bitleft = data_end - base;
+  memmove(buffer, base, bitleft);
+  if (buf_size - bitleft < MIN_BUF)
+    FatalError(1, "Paragraph too big for buffer");
+  num = read(in_fd, &buffer[bitleft], buf_size - bitleft);
+  line_start -= (base - buffer);
+  scan -= (base - buffer);
+  base = buffer;
+  data_end = buffer + bitleft + num;
+  if (num > 0) return 1; else return 0;
+}
+
+char *scanpara(int in_fd)
+{
+  int num, blank, in_blank, at_end;
+  num = read(in_fd,buffer,buf_size);
+  in_blank = 1;  base = buffer;  line_start = buffer;  scan = buffer;
+  data_end = buffer + num;  at_end = 0;
+  for (;;) {
+    blank = 1;  line_start = scan;                   // Get a line
+    while (scan < data_end  &&  *scan != '\n')
+      if (*scan++ > ' ') blank = 0;
+    if (scan >= data_end) {
+      if (refill(in_fd)) {
+	while (scan < data_end  &&  *scan != '\n')
+	  if (*scan++ > ' ') blank = 0;
+	if (scan < data_end) scan++;
+	}
+      else at_end = 1;
+      }
+    else scan++;
+    if (line_start < scan) {                         // If we have a line
+      if (in_blank) {
+	if (!blank) { in_blank = 0; base = line_start; }
+	}
+      else {
+	if (blank) {
+	  do_process(base, line_start - base);
+	  in_blank = 1;  base = line_start;
+	  }
+	}
+      }
+    if (at_end) break;
+    }
+  if (in_blank) base = scan;
+  if (scan + 2 <= buffer + buf_size) {
+    *scan++ = 26;  *scan++ = 10;
+    }
+  if (base < scan) do_process(base, scan - base);
+  return NULL;
+}
+
+char *scanfile(int in_fd)
+{
+  int num = read(in_fd,buffer,buf_size);  /*expect to read the whole file*/
+  if (num < 0) return "file locked";
+  if (num >= buf_size-1) return "file too long";
+  do_process(buffer, num);
+  return NULL;
+}
+
+void search(char *dname, char *fname)
+{
+  long dirtag;  struct _finddata_t dirinfo;  int in_fd;  char *res;
+  char search_name[path_length], found_name[path_length];
+
+  sprintf(search_name, "%s%s", dname, fname);    /*Scan files*/
+  dirtag = _findfirst(search_name, &dirinfo);
+  if (dirtag >= 0) {
+    do {
+      if ((dirinfo.attrib & (_A_SUBDIR | _A_HIDDEN | _A_SYSTEM)) == 0) {
+	sprintf(found_name,"%s%s",dname,dirinfo.name);
+	in_fd = open(found_name,O_RDONLY|O_BINARY);
+	if (in_fd >= 0) {
+	  if (by_para)
+	    res = scanpara(in_fd);
+	  else
+	    res = scanfile(in_fd);
+	  if (res != NULL) {
+	    Message("Error %s in processing file %s\n", res, found_name);
+	    exit(1);
+	    }
+	  close(in_fd);
+	  }
+	}
+      } while (_findnext(dirtag, &dirinfo) == 0);
+    _findclose(dirtag);
+    }
+
+  if (recurse == 0) return;
+
+  sprintf(search_name, "%s*.*", dname);          /*Look for subdirs*/
+  dirtag = _findfirst(search_name, &dirinfo);
+  if (dirtag < 0) return;
+  do {
+    if ( ((dirinfo.attrib & (_A_HIDDEN | _A_SYSTEM)) == 0)  &&
+	 ((dirinfo.attrib & _A_SUBDIR) != 0)  &&
+	 strcmp(dirinfo.name,".") != 0 &&
+	 strcmp(dirinfo.name,"..") != 0) {
+      sprintf(found_name,"%s%s",dname,dirinfo.name);
+      strcat(found_name,"\\");
+      search(found_name,fname);
+      }
+    } while (_findnext(dirtag, &dirinfo) == 0);
+  _findclose(dirtag);
+}
+
+static int toobig(int n)
+{
+  if (n > path_length) {
+    printf("Cannot handle urls > %d characters in length\n", path_length);
+    exit(1);
+    }
+  return 0;
+}
+
+void scan_catch(char *dname, char *fname)
+{
+  int in_fd, urllen, conlen, filesize;  char catch_name[path_length];
+  int filecount = 0;
+  enum { filekind_redirected, filekind_text, filekind_other } filekind;
+  sprintf(catch_name, "%s%s", dname, fname);
+  in_fd = open(catch_name,O_RDONLY|O_BINARY);
+  if (in_fd < 0)
+    FatalError(1, "Couldn't open catch file \"%s\"", catch_name);
+  for (;;) {
+    filecount++;  if (filecount%100 == 0) { printf("%d\r", filecount);  fflush(stdout); }
+    if (read(in_fd, &urllen, sizeof(int)) != sizeof(int) || toobig(urllen) ||
+	read(in_fd, catch_name, urllen) != urllen ||
+	read(in_fd, &conlen, sizeof(int)) != sizeof(int) || toobig(conlen) ||
+	read(in_fd, catch_name, conlen) != conlen) break;
+    if (conlen >= 1 && *catch_name == '@')
+      filekind = filekind_redirected;
+    else {
+      if (conlen >= 4 && strncmp(catch_name, "text", 4) == 0)
+	filekind = filekind_text;
+      else
+	filekind = filekind_other;
+      if (read(in_fd, &filesize, sizeof(int)) != sizeof(int))
+	FatalError(1, "File read failed for size field");
+      if (filesize > buf_size)
+	FatalError(1, "File too large (%d > %d)", filesize, buf_size);
+      if (read(in_fd, buffer, filesize) != filesize)
+	FatalError(1, "Failed to read file data");
+      }
+    if (filekind == filekind_text) do_process(buffer, filesize);
+    }
+  close(in_fd);
+}
+
+static void driver (FILE * Trace, char *file_name)
+{
+  int pass;
+
+  buffer = (char *)Xmalloc (buf_size);
+
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	if (PassData[pass].init (file_name) == COMPERROR)
+	  FatalError (1, "Error during init of \"%s\"",PassData[pass].name);
+      }
+
+  if (html_catch == 0) search(dirname, wildname);
+		  else scan_catch(dirname, wildname);
+
+  for (pass = 0; pass < MAX_PASSES; pass++)
+    if (Passes & (1 << pass))
+      {
+	if (PassData[pass].done (file_name) == COMPERROR)
+	  FatalError (1, "Error during done of \"%s\"", PassData[pass].name);
+      }
+
+
+  free (buffer);
+}
+
+void main (int argc, char **argv)
+{
+  int ch;
+  char *filename = NULL;
+  FILE *Trace = NULL;
+
+  msg_prefix = argv[0];
+
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "hC:WHGpSD123f:d:b:T:I:t:m:N:c:n:s:")) != -1)
+    {
+      switch (ch)
+	{
+	case 'H':
+	  html_catch = 1;
+	  break;
+	case 'G':
+	  SkipSGML = 1;
+	  break;
+	case 'p':
+	  by_para = 1;
+	  break;
+	case 'S':
+	  Passes |= SPECIAL;
+	  break;
+	case '1':
+	  InvfLevel = 1;
+	  break;
+	case '2':
+	  InvfLevel = 2;
+	  break;
+	case '3':
+	  InvfLevel = 3;
+	  break;
+	case 'f':
+	  filename = optarg;
+	  break;
+	case 'n':
+	  trace_name = optarg;
+	  break;
+	case 'D':
+	  Dump = 1;
+	  break;
+	case 'W':
+	  MakeWeights = 1;
+	  break;
+	case 'd':
+	  set_basepath (optarg);
+	  break;
+	case 's':
+	  stem_method = atoi (optarg) & STEMMER_MASK;
+	  break;
+	case 'b':
+	  buf_size = atoi (optarg) * 1024;
+	  break;
+	case 'C':
+	  comp_stat_point = atoi (optarg) * 1024;
+	  break;
+	case 'c':
+	  ChunkLimit = atoi (optarg);
+	  break;
+	case 'm':
+	  invf_buffer_size = (int) (atof (optarg) * 1024 * 1024);
+	  break;
+	case 'I':
+	case 'N': /* N kept for compatability */
+	  if (*optarg == '1')
+	    Passes |= IVF_PASS_1;
+	  else if (*optarg == '2')
+	    Passes |= IVF_PASS_2;
+	  else
+	    usage ("Invalid pass number");
+	  break;
+	case 'T':
+	  if (*optarg == '1')
+	    Passes |= TEXT_PASS_1;
+	  else if (*optarg == '2')
+	    Passes |= TEXT_PASS_2;
+	  else
+	    usage ("Invalid pass number");
+	  break;
+	case 't':
+	  trace = (unsigned long) (atof (optarg) * 1024 * 1024);
+	  break;
+	case 'h':
+	case '?':
+	  usage (NULL);
+	}
+    }
+
+  if (!filename || *filename == '\0')
+    FatalError (1, "A document collection name must be specified.");
+
+  if (buf_size < MIN_BUF)
+    FatalError (1, "The buffer size must exceed 1024 bytes.");
+
+  if ((Passes & (IVF_PASS_1 | IVF_PASS_2)) == (IVF_PASS_1 | IVF_PASS_2))
+    FatalError (1, "I1 and I2 cannot be done simultaneously.");
+
+  if ((Passes & (TEXT_PASS_1 | TEXT_PASS_2)) == (TEXT_PASS_1 | TEXT_PASS_2))
+    FatalError (1, "T1 and T2 cannot be done simultaneously.");
+
+  if (!Passes)
+    FatalError (1, "S, T1, T2, I1 or I2 must be specified.");
+
+  if (argc - optind == 1) {
+    strcpy(dirname,"");
+    strcpy(wildname,argv[optind]);
+    }
+  else if (argc - optind == 2) {
+    strcpy(dirname,argv[optind]);
+    strcpy(wildname,argv[optind+1]);
+    }
+  else FatalError(1, "Finder code requires directory and filespec.");
+
+  if (strrchr(wildname,'*') != NULL  ||  strrchr(wildname,'?') != NULL)
+    recurse = 1;
+
+  if (trace)
+    {
+      if (!trace_name)
+	trace_name = make_name (filename, TRACE_SUFFIX, NULL);
+      if (!(Trace = fopen (trace_name, "a")))
+	Message ("Unable to open \"%s\". No tracing will be done.", trace_name);
+      else
+	setbuf (Trace, NULL);
+    }
+  else
+    Trace = NULL;
+
+  if (comp_stat_point)
+    {
+      char *name = make_name (filename, COMPRESSION_STATS_SUFFIX, NULL);
+      if (!(Comp_Stats = fopen (name, "wb")))
+	Message ("Unable to open \"%s\". No comp. stats. will be generated.",
+		 name);
+    }
+
+
+  if (Trace)
+    {
+      int i;
+      fprintf (Trace, "\n\n\t\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+      for (i = 0; i < argc; i++)
+	fprintf (Trace, "%s ", argv[i]);
+      fprintf (Trace, "\n\n");
+    }
+
+  driver (Trace, filename);
+
+  if (Trace)
+    fclose (Trace);
+
+  if (Comp_Stats)
+    fclose (Comp_Stats);
+
+  exit (0);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.1	(revision 16561)
@@ -0,0 +1,693 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgquery 1 \*(Dt CITRI
+.SH NAME
+mgquery \- query program for the mg system
+.SH SYNOPSIS
+.B mgquery
+[
+.B \-h
+]
+[
+.B \-D
+]
+[
+.BI \-f " name"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +9n
+[
+.I collection-name
+]
+.SH DESCRIPTION
+.B mgquery
+enables users to make Boolean or ranked queries from a data base
+generated by the
+.BR mg (1)
+system.  It accepts queries from
+.I stdin
+and sends the retrieved documents to
+.IR stdout .
+Information on the resource usage of
+.B mgquery
+as it processes queries can be obtained interactively.
+.SH OPTIONS
+Options may appear in any order, but the
+.IR collection-name ,
+if specified, must be last.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-D
+This option causes the entire text to be decompressed and sent to
+.IR stdout .
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection that will be
+used.  If a collection with the specified base
+.I name
+does not exist, an error message will be displayed and
+.B mgquery
+will exit.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.SH USAGE
+Prior to processing the command line arguments, the
+.B mgquery
+program attempts to read in a startup script called
+.IR ./.mgrc .
+If that fails, it attempts to read in the file
+.IR $HOME/.mgrc .
+The startup file can only contain commands\(emno queries are
+permitted in the
+.I .mgrc
+file.  Lines starting with \*(lq\fB#\fP\*(rq in the file are comments.
+The most common use for the
+.I .mgrc
+file is to personalise the initial values of the predefined parameters
+with
+.B .set
+commands.
+.LP
+The input to
+.B mgquery
+consists of a series of input lines.  The backslash
+character
+.RB (\*(lq \e \*(rq)
+is used at the end of lines to indicate
+that input continues on the next line.
+.LP
+Input lines on which the first character is a dot
+.RB (\*(lq . \*(rq)
+are commands to the
+.B mgquery
+program.  Input lines that do not start with a dot are queries.
+.LP
+A query consists of two parts.  One part is a Boolean or ranked query
+that identifies documents.  The second part is a post-processing
+pattern matching operation.  Any text between the first speech mark
+(\*(lq) and the last speech mark (\*(rq) is considered to be a
+post-processing pattern.
+.SH COMMANDS
+The
+.B mgquery
+program can accept the following commands.
+.TP 17
+.B .help
+Display several pages of help text.
+.TP
+.B .quit
+Quit the program.
+.TP
+.B .warranty
+Display the
+.BR mg (1)
+warranty.
+.TP
+.B .conditions
+Display the conditions of use and distribution of
+.BR mg (1).
+.TP
+.BI ".set " "name value"
+Set the parameter
+.I name
+to the specified
+.IR value .
+If the parameter is a Boolean
+.I value
+and the
+.I value
+is omitted, the parameter will be inverted (i.e., if it was
+.IR true ,
+then it will change to
+.IR false ;
+if it was
+.IR false ,
+then it will change to
+.IR true ).
+.TP
+.BI ".unset " name
+Delete the parameter
+.I name
+from the currently-defined parameters.
+.TP
+.B .reset
+Reset the parameters to the state that they had after the processing
+of the
+.B mgquery
+command line.
+.TP
+.B .display
+Display the values of all the currently-defined parameters.
+.TP
+.B .push
+Push the currently-defined parameters onto a stack.
+.TP
+.B .pop
+Pops a set of parameters off the stack, replacing the currently-defined
+ones.
+.TP
+.BI ".output " arg
+This is used to specify where to send the text of the documents.  Once
+the
+.B .output
+command is specified, all subsequent output will be sent to the place
+specified by
+.IR arg .
+If
+.I arg
+is not specified subsequent output will be directed to
+.IR stdout .
+.I Arg
+may be any of the following.
+.RS
+.TP 13
+.BI "> " filename
+Send output to the specified file.
+.TP
+.BI ">> " filename
+Append output to the specified file.
+.TP
+.BI "| " command
+Pipe the output to
+.IR command ,
+which is executed by
+.IR sh .
+.RE
+.TP
+.BI ".input " arg
+This is used to specify where input (queries and commands) comes
+from.  Once the
+.B .input
+command is specified all subsequent input will be come from the place
+specified by
+.IR arg .
+If
+.I arg
+is not specified subsequent input will come from
+.IR stdin .
+.RS
+.TP 13
+.BI "< " filename
+Get input from the specified file.
+.TP
+.BI "| " command
+The input comes from the standard output of
+.IR command ,
+which is executed by
+.IR sh .
+.RE
+.SH PARAMETERS
+The following parameters are predefined and have special
+significance.  Each parameter will be followed by its default
+value.  Parameters are initialised before the
+.I .mgrc
+file is read or the command line arguments are processed.
+.TP 17
+.BI accumulator_method " `array'"
+This parameter is used during ranking, and specifies how the
+weight for each document should be accumulated.  The following
+methods are available:
+.IR array ,
+.IR splay_tree ,
+.IR hash_table ,
+and
+.IR list .
+.TP
+.BI briefstats " `off'"
+This is a Boolean parameter that determines whether the
+totals for disk, memory and time usage statistics will be
+displayed at the end of each query.
+.IR Note :
+this takes precedence over the parameters
+.BR diskstats ,
+.BR memstats " and " timestats .
+This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI buffer " `1048576'"
+When the documents are being read in, they are read into a
+buffer of this size and then displayed from this buffer.  If
+the documents are larger than this buffer, the buffer is
+expanded automatically.  Having a large buffer gives a very
+slight performance improvement, because it allows the order of
+disk operations to be optimised.  The buffer size is measured
+in bytes.
+.TP
+.BI diskstats " `off'"
+This is a Boolean parameter that determines whether the disk
+usage statistics for the preceding query will be displayed
+after each query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI doc_sepstr " `---------------------------------- %n\en\'"
+This specifies the string that will be used to separate
+documents when they are displayed for `Boolean' or `docnums'
+queries.  The standard C escape character sequences
+may be used to place special characters in the
+string.  For example, a newline would be `\en'.  To include a `%',
+use the sequence `%%'.  To include the
+.BR mg (1)
+document number, use the sequence `%n'.  The following escape character
+sequences are available
+.nf
+.ta 1.7iL
+.B Sequence	Meaning
+`\e\e'	backslash
+`\eb'	backspace
+`\ef'	formfeed
+`\en'	newline
+`\er'	carriage return
+`\et'	tab
+`\e"'	speech marks
+`\e''	quote mark
+`\ex\fIhh\fP'	ASCII code in hexadecimal
+`\ennn'	ASCII code in octal
+.fi
+.TP
+.BI expert " `false'"
+If this is
+.IR true ,
+then much of the dialogue output is suppressed.  This parameter may
+take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI hash_tbl_size " `1000'"
+One of the options during ranking queries is to use a hash
+table to accumulate the weights for each document.  The hash
+table is a simple chained type.  This parameter specifies the
+size of the hash table and may take any value between 8 and
+268435456 (2^28).
+.TP
+.BI heads_length " `50'"
+When the mode is
+.BR heads ,
+this specifies the number of characters that will be output for each
+document.
+.TP
+.BI maxdocs " `all'"
+The maximum number of documents to display in response to a
+query.  This parameter may take on a numeric value between 1
+and 429467295 (2^32 - 1) or the word
+.IR all .
+.TP
+.BI maxparas " `1000'"
+The maximum number of paragraphs to identify during a ranked
+query with paragraph indexing.  After the paragraphs have been
+identified, the paragraphs are converted into documents, and
+because some of the paragraphs may refer to the same documents
+the final number of answers may be less than
+.BR maxparas .
+The
+.B maxdocs
+parameter will then be applied.  This parameter may take on a numeric
+value between 1 and 429467295 (2^32 - 1).
+.TP
+.BI max_accumulators " `50000'"
+This parameter limits the number of different paragraph and
+document numbers to be accumulated during ranked queries when
+the parameter
+.B accumulator_method
+is set to
+.IR splay_tree ,
+.IR hash_table ,
+or
+.IR list .
+This parameter may take any value between 8 and 268435456 (2^28).
+.TP
+.BI max_terms " `all'"
+This parameter limits the number of terms that will actually
+be used during a ranked query.  If more terms than the number
+specified by
+.B max_terms
+are entered, then the extra terms will be discarded.  If
+.B sorted_terms
+is on, then the limiting will be done after the terms have been
+sorted.  This parameter may take any value between 1 and 429467295
+(2^32 - 1), or the word
+.IR all.
+.TP
+.BI memstats " `off'"
+This is a Boolean parameter that determines whether the memory
+usage statistics for the preceding query will be displayed
+after each query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI mgdir " `.'"
+This is set to the directory where the
+.BR mg (1)
+data files may be found.  If
+the environment variable
+.B MGDATA
+exists, then this is instead initialised to the value of
+.BR MGDATA .
+The value of this parameter may be changed, either in the
+.I .mgrc
+file with a
+.BI ".set mgdir "directory
+command, or from the command line using the
+.BI \-d " directory"
+option.  Once the \*(lq\fB>\fP\*(rq prompt appears, changing this
+parameter will have no effect.
+.TP
+.BI mgname " `bible'"
+This is set to the name of the
+.BR mg (1)
+collection that is to be used for the session.  The value of this
+parameter may be changed, either in the
+.I .mgrc
+file with a
+.BI ".set mgname "name
+command, or from the command line using the
+.BI \-f " name"
+option.  Once the \*(lq\fB>\fP\*(rq prompt appears, changing this
+parameter will have no effect.
+.TP
+.BI mode " `text'"
+This specifies how documents should be displayed when they
+are retrieved.  It may take six different values:
+.IR text ,
+.IR hilite ,
+.IR docnums ,
+.IR heads ,
+.IR silent ,
+or
+.IR count .
+.I text
+displays the contents of the document.
+.I hilite
+displays the contents of the document and highlights any of the 
+stemmed query terms.
+.I docnums
+displays only the document numbers.
+.I heads
+is used to print out the head of each document.
+.I silent
+retrieves all the documents but displays nothing except how many
+documents were retrieved.  This mode is intended to be used in timing
+experiments.
+.I count
+does the minimum
+amount of work required to determine how many documents would
+be retrieved, but does not retrieve them.
+.TP
+.BI optimise_type " `1'"
+There are three types of boolean query optimisation (parse tree
+rearrangement). Type 0 leaves parse tree unaltered. Type 1 optimises
+for AND of terms and AND of OR of terms. Type 2 converts the tree
+into DNF (an experiment :-).
+.TP
+.BI pager " `more'"
+This is the name of the program that will be used to display
+the help and the retrieved documents.  If the environment
+variable
+.B PAGER
+is defined, then
+.B pager
+takes on that value.
+.TP
+.BI hilite_style " `bold'"
+This specifies the type of highlighting method.
+It may take one of two different values:
+.IR bold,
+or
+.IR underline.
+.TP
+.BI para_sepstr " `\en######## PARAGRAPH %n ########\en'"
+This specifies the string that will be used to separate paragraphs.
+The standard C escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written
+as `\en'.  To include a `%', use the sequence `%%'.  To include the
+paragraph number within the document, use the sequence `%n'.
+.TP
+.BI para_start " `***** Weight = %w *****\en'"
+This specifies the string that will be used at the head of paragraphs
+for a paragraph-level index following a ranked query.  The standard
+C-language escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written as
+`\en'.  To include a `%', use the sequence `%%'.  To include the
+paragraph weight, use the sequence `%w'.
+.TP
+.BI qfreq " `true'"
+This determine whether the ranked queries will take into account the
+number of times each query term is specified.  When this is
+.IR true ,
+the number of times a term appears in the query is used in the
+ranking.  When this is
+.IR false ,
+all query terms are assumed to occur only once.  This parameter may
+take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI query  " `Boolean'"
+This specifies the type of queries that are to be specified.
+It can take four different values:
+.IR Boolean ,
+.IR ranked ,
+.IR docnums " or " approx-ranked.
+.I Boolean
+is for Boolean queries.
+The
+.BR yacc (1)
+grammar for Boolean queries is as follows.
+.IP
+.nf
+        query   : or;
+.IP
+        or      : or '|' and
+                | and ;
+.IP
+        and     : and '&' not
+                | and not
+                | not ;
+.IP
+        not     : term
+                | '!' not ;
+.IP
+        term    : TERM
+                | '(' or ')' ;
+.fi
+.IP
+.IR ranked " and " approx-ranked
+are for queries ranked by the cosine measure.
+.I approx-ranked
+uses only the low-precision document lengths, and therefore only
+produces an approximation to full cosine ranking.
+.IP
+.nf
+        query   : TERM
+                | query TERM ;
+.fi
+.IP
+.I docnums
+allows the entry of document numbers.  Multiple numbers separated by
+spaces may be specified, or ranges separated by hyphens.
+.IP
+.nf
+        query   : range
+                | query range ;
+.IP
+        range   : num
+                | num '-' num ;
+.fi
+.TP
+.BI ranked_doc_sepstr " `-------------------------------- %n %w\en'"
+This specifies the string that will be used to separate documents when
+they are displayed for `ranked' or `approx-ranked' queries.  The
+standard C escape character sequences may be used to place special
+characters in the string.  For example, a newline would be written as
+`\en'.  To include a `%', use the sequence `%%'.  To include the
+.BR mg (1)
+document number, use the sequence `%n'.  To include the document
+weight, use the sequence `%w'.
+.TP
+.BI sizestats " `false'"
+If this is
+.IR true ,
+then various numbers are output at the end of each query indicating
+what went on during the query.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI skip_dump " `skips.%d'"
+If this parameter is set, then a file will be produced in the current
+directory during ranked queries on skipped inverted files when
+.B accumulator_method
+is set to
+.IR splay_tree ,
+.IR hash_table ,
+or
+.IR list .
+The name of the file is the value of this parameter.  A `%d' in the
+file name will be replaced with the process id of
+.BR mgquery .
+This file will contain information about the usage of skips during the
+query processing.  This option is expensive; use
+.B .unset skip_dump
+to obtain optimal performance.
+.TP
+.BI sorted_terms " `on'"
+This specifies whether or not the terms should be sorted into
+decreasing occurrence in documents so that the least-often occurring
+terms are processed first when ranked queries are being done.  When
+this is
+.IR true ,
+the terms are sorted.  When this is
+.IR false ,
+the terms are not sorted, and are instead processed in order of
+occurrence.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI stop_at_max_accum " `on'"
+This specifies what should happen when the maximum number of
+accumulators set by
+.B max_accumulators
+is reached.  When this is
+.IR true ,
+the processing of terms is stopped at the completion of the current
+term.  When this is
+.IR false ,
+processing continues but no new accumulators are created.  This
+parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI terminator " `'"
+This specifies the string that will be output after the last document
+from the previous query has been output.  The standard C escape
+character sequences may be used to place special characters in the
+string.  For example, a newline would be written as `\en'.  To include
+a `%', use the sequence `%%'.
+.TP
+.BI timestats " `false'"
+If this is
+.IR true ,
+then the time to process a query is displayed in both real time and
+CPU time.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.TP
+.BI verbatim " `off'"
+This is a Boolean parameter that determines whether the program
+should attempt to do a regular-expression match on the retrieved
+text.  If verbatim is
+.I on
+and a post-processing string is specified with the query, then the
+post-processing string will be searched for in the documents just
+before they are displayed.  If the string is found, the document will
+be displayed; if not, the document will not be displayed.  If verbatim
+is
+.IR off ,
+the post-processing string will be considered a regular expression
+as in
+.BR egrep (1)
+or
+.BR vi (1).
+E.g., if verbatim is
+.I on,
+\*(lq\fBand.*the\fP\*(rq will look for the 8-character sequence
+\*(lq\fBand.*the\fP\*(rq.  If verbatim is
+.IR off ,
+\*(lq\fBand.*the\fP\*(rq will look for the sequence
+\*(lq\fBand\fP\*(rq followed somewhere later in the document by the
+sequence \*(lq\fBthe\fP\*(rq.  This parameter may take the values
+.IR yes ", " no ", "
+.IR true ", " false ", "
+.IR on " or " off .
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.I .mgrc
+.B mgquery
+startup file
+.TP
+.B help.mg
+Help file for
+.BR mgquery .
+The contents of this file is displayed with the
+.B .help
+command.
+.TP
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+The `on-disk' stemmed dictionary.
+.TP
+.B *.text
+Compressed documents.
+.TP
+.B *.text.dict
+Compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR egrep (1),
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgstat (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1),
+.BR vi (1),
+.BR yacc (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgquery.c	(revision 16561)
@@ -0,0 +1,1502 @@
+/**************************************************************************
+ *
+ * mgquery.c -- The   M G Q U E R Y   program
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.2  2004/06/22 04:17:51  kjdon
+   fixed a couple of compiler warnings
+
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:18  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1999/01/08 00:33:46  rjmcnab
+
+   Enabled mg and the library software to read in more than one index
+   at a time.
+
+   Revision 1.2  1998/11/25 07:55:49  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:29  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:57:02  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:41:58  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#if defined(HAVE_SYS_PROCFS_H) && defined(HAVE_PR_BRKSIZE) && \
+    (__STDC__ == 0)
+/* STDC test was included to allow cc -Xc on SunOS 5 to work */
+#define USE_PROCESS_MEM
+#endif
+
+#ifdef USE_PROCESS_MEM
+# include <sys/procfs.h>
+#endif
+
+#ifdef HAVE_GETRUSAGE
+# ifdef HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# endif
+# include <sys/resource.h>
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+# include "getpagesize.h"
+#endif
+ 
+#if WITH_REGEX
+# include <regex.h>
+#else
+# include <rx.h>
+#endif
+
+
+#include <stdarg.h>
+#include <signal.h>
+
+#include "messages.h"
+#include "timing.h"
+#include "memlib.h"
+#include "local_strings.h"  /* [RPAP - Feb 97: Term Frequency] */
+
+#include "filestats.h"
+#include "invf.h"
+#include "text.h"
+#include "mg.h"
+#include "lists.h"
+#include "backend.h"
+#include "environment.h"
+#include "globals.h"
+#include "read_line.h"
+#include "mg_errors.h"
+#include "commands.h"
+#include "text_get.h"
+#include "term_lists.h"
+#include "query_term_list.h"
+
+
+FILE *OutFile = NULL, *InFile = NULL;
+int OutPipe = 0, InPipe = 0;
+int Quitting = 0;
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) ||  defined(NZDL)
+int GetDocNumFromParaNum(query_data *qd, int paranum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+  int low = 1, high = Documents;
+  int mid = (low+high)/2;
+
+  while ((mid = (low+high)/2) >=1 && mid <= Documents)
+    {
+      if (paranum > Paragraph[mid])
+	low = mid+1;
+      else if (paranum <= Paragraph[mid-1])
+	high = mid-1;
+      else
+	return mid;
+    }
+  FatalError(1, "Bad paragraph number.\n");
+}
+#endif
+
+#ifdef TREC_MODE
+char *trec_ids = NULL;
+long *trec_paras = NULL;
+#endif
+
+static volatile int PagerRunning = 0;
+static volatile int Ctrl_C = 0;
+
+
+/*****************************************************************************/
+
+typedef enum
+  {
+    S_Time, S_Mem, S_Size, S_File
+  }
+S_Type;
+
+static struct Stat
+  {
+    S_Type typ;
+    char *name;
+    char *text;
+  }
+ *Stats = NULL;
+static int NumStats = 0;
+
+static void 
+Clear_Stats (void)
+{
+  if (Stats)
+    {
+      int i;
+      for (i = 0; i < NumStats; i++)
+	{
+	  if (Stats[i].name)
+	    Xfree (Stats[i].name);
+	  if (Stats[i].text)
+	    Xfree (Stats[i].text);
+	}
+      Xfree (Stats);
+      Stats = NULL;
+      NumStats = 0;
+    }
+}
+
+static void 
+Add_Stats (S_Type typ, char *name, char *fmt,...)
+{
+  char buf[1024];
+  va_list args;
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  if (Stats)
+    Stats = Xrealloc (Stats, (++NumStats) * sizeof (*Stats));
+  else
+    Stats = Xmalloc ((++NumStats) * sizeof (*Stats));
+  Stats[NumStats - 1].typ = typ;
+  Stats[NumStats - 1].name = Xstrdup (name);
+  Stats[NumStats - 1].text = Xstrdup (buf);
+}
+
+static void 
+Display_Stats (FILE * f)
+{
+  static char *sep = "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="
+  "-=-=-=-=-=-=-=-=-=-=-";
+  char *names[] =
+  {"Time:  ", "Memory:", "Sizes: ", "Disk:  ", "       "};
+  int i, last_typ = -1;
+  size_t len = 0;
+  if (NumStats == 0)
+    return;
+  fprintf (f, "%s\n", sep);
+  for (i = 0; i < NumStats; i++)
+    if (strlen (Stats[i].name) > len)
+      len = strlen (Stats[i].name);
+  for (i = 0; i < NumStats; i++)
+    {
+      int typ = 4;
+      if (Stats[i].typ != last_typ)
+	typ = last_typ = Stats[i].typ;
+      fprintf (f, "%s %-*s %s\n", names[typ], (int) len, Stats[i].name, Stats[i].text);
+    }
+  fprintf (f, "%s\n", sep);
+}
+
+/*****************************************************************************/
+
+
+static void 
+QueryTimeStats (ProgTime * Start, ProgTime * invf, ProgTime * text)
+{
+  if (!BooleanEnv (GetEnv ("briefstats"), 0))
+    {
+      Add_Stats (S_Time, "invf", ElapsedTime (Start, invf));
+      Add_Stats (S_Time, "text", ElapsedTime (invf, text));
+    }
+  Add_Stats (S_Time, "total", ElapsedTime (Start, text));
+}
+
+static void 
+StartUpTimeStats (InitQueryTimes * iqt)
+{
+  if (!BooleanEnv (GetEnv ("briefstats"), 0))
+    {
+      Add_Stats (S_Time, "dict [stem]", ElapsedTime (&iqt->Start,
+						     &iqt->StemDict));
+      Add_Stats (S_Time, "weights", ElapsedTime (&iqt->StemDict,
+						 &iqt->ApproxWeights));
+      Add_Stats (S_Time, "dict [text]", ElapsedTime (&iqt->ApproxWeights,
+						     &iqt->CompDict));
+      Add_Stats (S_Time, "Inverted", ElapsedTime (&iqt->CompDict,
+						  &iqt->Invf));
+      Add_Stats (S_Time, "Compressed", ElapsedTime (&iqt->Invf,
+						    &iqt->Text));
+    }
+  Add_Stats (S_Time, "total", ElapsedTime (&iqt->Start, &iqt->Text));
+}
+
+
+
+
+#ifdef USE_PROCESS_MEM
+static u_long 
+process_mem (void)
+{
+  prstatus_t pr;
+  static int fd = -1;
+  if (fd == -1)
+    {
+      char buf[128];
+      sprintf (buf, "/proc/%ld", (long) getpid ());
+      fd = open (buf, O_RDONLY);
+    }
+  if (fd == -1 || ioctl (fd, PIOCSTATUS, &pr) == -1)
+    return 0;
+  return pr.pr_brksize;
+}
+#endif
+
+
+
+
+static void 
+MemStats (query_data * qd)
+{
+  if (!BooleanEnv (GetEnv ("briefstats"), 0))
+    {
+#ifdef HAVE_GETRUSAGE
+      struct rusage rusage;
+      getrusage (RUSAGE_SELF, &rusage);
+
+      Add_Stats (S_Mem, "process mem", "%7.3f Mb",
+	    (double) (rusage.ru_maxrss * getpagesize () / 1024.0 / 1024.0));
+#endif
+#ifdef USE_PROCESS_MEM
+      Add_Stats (S_Mem, "process mem", "%7.3f Mb",
+		 (double) (process_mem () / 1024.0 / 1024.0));
+#endif
+      Add_Stats (S_Mem, "dict [stem]", "%7.1f kB",
+		 (double) qd->sd->MemForStemDict / 1024);
+      Add_Stats (S_Mem, "dict [text]", "%7.1f kB",
+		 (double) qd->cd->MemForCompDict / 1024);
+      if (qd->awd)
+	Add_Stats (S_Mem, "weights", "%7.1f kB",
+		   (double) qd->awd->MemForWeights / 1024);
+    }
+  if (qd->awd)
+    Add_Stats (S_Mem, "total [peak]", "%7.1f kB",
+	       (double) (qd->max_mem_in_use + qd->sd->MemForStemDict +
+		   qd->cd->MemForCompDict + qd->awd->MemForWeights) / 1024);
+  else
+    Add_Stats (S_Mem, "total [peak]", "%7.1f kB",
+	       (double) (qd->max_mem_in_use + qd->sd->MemForStemDict +
+			 qd->cd->MemForCompDict) / 1024);
+
+}
+
+
+
+static void 
+SizeStats (query_data * qd)
+{
+  Add_Stats (S_Size, "skips", "%7d", qd->hops_taken);
+  Add_Stats (S_Size, "pointers", "%7d", qd->num_of_ptrs);
+  Add_Stats (S_Size, "accumulators", "%7d", qd->num_of_accum);
+  Add_Stats (S_Size, "terms", "%7d", qd->num_of_terms);
+  Add_Stats (S_Size, "answers", "%7d", qd->num_of_ans);
+  Add_Stats (S_Size, "index lookups", "%7d", qd->text_idx_lookups);
+}
+
+static void 
+TotalSizeStats (query_data * qd)
+{
+  Add_Stats (S_Size, "skips", "%7d", qd->tot_hops_taken);
+  Add_Stats (S_Size, "pointers", "%7d", qd->tot_num_of_ptrs);
+  Add_Stats (S_Size, "accumulators", "%7d", qd->tot_num_of_accum);
+  Add_Stats (S_Size, "terms", "%7d", qd->tot_num_of_terms);
+  Add_Stats (S_Size, "answers", "%7d", qd->tot_num_of_ans);
+  Add_Stats (S_Size, "index lookups", "%7d", qd->tot_text_idx_lookups);
+}
+
+
+static void 
+StatFile (File * F)
+{
+  static unsigned long NumBytes = 0, NumSeeks = 0, NumReads = 0;
+  if (F) 
+    {
+      if ((int) F != -1)
+	{
+	  if (!BooleanEnv (GetEnv ("briefstats"), 0))
+	    Add_Stats (S_File, F->name, "%7.1f kB (%3d seeks, %7d reads)",
+		       (double) F->Current.NumBytes / 1024, F->Current.NumSeeks,
+		       F->Current.NumReads);
+	  NumBytes += F->Current.NumBytes;
+	  NumSeeks += F->Current.NumSeeks;
+	  NumReads += F->Current.NumReads;
+	}
+      else
+	{
+	  Add_Stats (S_File, "total", "%7.1f kB (%3d seeks, %7d reads)",
+		     (double) NumBytes / 1024, NumSeeks, NumReads);
+	  NumSeeks = NumReads = NumBytes = 0;
+	}
+    }
+}
+
+
+static void 
+File_Stats (query_data * qd)
+{
+  StatFile (qd->File_comp_dict);
+  StatFile (qd->File_fast_comp_dict);
+  StatFile (qd->File_text_idx_wgt);
+  StatFile (qd->File_text);
+  StatFile (qd->File_stem);
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (qd->sd->sdh.indexed)
+    {
+      StatFile (qd->File_stem1);
+      StatFile (qd->File_stem2);
+      StatFile (qd->File_stem3);
+    }
+
+  StatFile (qd->File_invf);
+  StatFile (qd->File_weight_approx);
+  StatFile (qd->File_text_idx);
+  StatFile ((File *) (-1));
+}
+
+
+char *
+get_query (query_data * qd)
+{
+  char *line, *LinePtr;
+  WritePrompt ();
+  do
+    {
+      do
+	{
+	  line = GetMultiLine ();
+	  if (line == NULL)
+	    {
+	      if (stdin == InFile)
+		return (NULL);	/* EOF */
+	      if (InPipe)
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+		_pclose (InFile);
+#else
+		pclose (InFile);
+#endif
+	      else
+		fclose (InFile);
+	      InPipe = 0;
+	      InFile = stdin;
+	    }
+	}
+      while (line == NULL);
+      LinePtr = ProcessCommands (line, qd);
+      if (CommandsErrorStr)
+	fprintf (stderr, "%s\n", CommandsErrorStr);
+    }
+  while (*LinePtr == '\0' && !Quitting);
+  return (LinePtr);
+}
+
+
+/* This is executed when a SIGPIPE is detected
+   i.e. If some one quits out of the PAGER, this is executed */
+#ifdef HAVE_SIGCONTEXT
+static RETSIGTYPE 
+SIGPIPE_handler (int sig, int code,
+		 struct sigcontext *scp, char *addr)
+#else
+static RETSIGTYPE 
+SIGPIPE_handler (int sig)
+#endif
+{
+  /* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  signal (sig, SIG_IGN);
+#else
+  signal (SIGPIPE, SIG_IGN);
+#endif
+  PagerRunning = 0;
+}
+
+/* This is executed when a SIGINT (i.e. CTRL-C) is detected */
+#ifdef  HAVE_SIGCONTEXT
+static RETSIGTYPE 
+SIGINT_handler (int sig, int code,
+		struct sigcontext *scp, char *addr)
+#else
+static RETSIGTYPE 
+SIGINT_handler (int sig)
+#endif
+{
+  Ctrl_C = 1;
+}
+
+
+
+static char *post_proc = NULL;
+
+
+
+void 
+GetPostProc (char *line)
+{
+  char *start, *finish;
+  if (post_proc)
+    {
+      Xfree (post_proc);
+      post_proc = NULL;
+    }
+  start = strchr (line, '\"');
+  finish = strrchr (line, '\"');
+  if (start != finish)
+    {
+      /* found a pattern */
+      *finish = '\0';
+      post_proc = Xstrdup (start + 1);
+      strcpy (start, finish + 1);
+      if (BooleanEnv (GetEnv ("verbatim"), 1) == 0)
+	{
+	  char *s;
+	  s = re_comp (post_proc);
+	  if (!s)
+	    {
+	      Xfree (post_proc);
+	      post_proc = NULL;
+	    }
+	}
+    }
+  else if (start != NULL)
+    {
+      /* found a single speech mark. Delete It. */
+      strcpy (start, start + 1);
+    }
+}
+
+int 
+PostProc (char *UDoc, int verbatim)
+{
+  if (!post_proc)
+    return 1;
+
+  if (verbatim) {
+    return (strstr (UDoc, post_proc) != NULL);
+  }
+  return re_exec ((char *) UDoc);
+}
+
+
+
+static DocEntry *
+in_chain (int para, int ip, DocEntry * dc)
+{
+  while (dc)
+    {
+      if (dc->DocNum - ip == para)
+	return dc;
+      dc = dc->Next;
+    }
+  return NULL;
+}
+
+/* num should be greater than or equal to 1 */
+int 
+RawDocOutput (query_data * qd, u_long num, FILE * Output)
+{
+  static u_long last_pos = 0;
+  static u_char *c_buffer = 0;
+  static int buf_len = -1;
+  static u_char *uc_buffer = 0;
+  u_long pos, len;
+  int ULen;
+
+  FetchDocStart (qd, num, &pos, &len);
+
+  if ((int) len > buf_len)
+    {
+      if (c_buffer)
+	{
+	  Xfree (c_buffer);
+	  Xfree (uc_buffer);
+	}
+      if (!(c_buffer = Xmalloc (len)))
+	return -1;
+      if (!(uc_buffer = Xmalloc ((int) (qd->td->cth.ratio * 1.01 *
+					len) + 100)))
+	return -1;
+      buf_len = len;
+    }
+  if (last_pos != pos)
+    Fseek (qd->td->TextFile, pos, 0);
+  Fread (c_buffer, 1, len, qd->td->TextFile);
+  last_pos = pos + len;
+  DecodeText (qd->cd, c_buffer, len, uc_buffer, &ULen);
+  fwrite (uc_buffer, ULen, sizeof (u_char), Output);
+  return 0;
+}
+
+
+void 
+StringOut (FILE * Output, char *string,
+	   int intvalid, unsigned long intval,
+	   int floatvalid, double floatval)
+{
+  char *s;
+  for (s = string; *s; s++)
+    if (*s == '%' &&
+	(*(s + 1) == 'n' || *(s + 1) == 'w' || *(s + 1) == '%'))
+      {
+	s++;
+	switch (*s)
+	  {
+	  case 'n':
+	    if (intvalid)
+	      fprintf (Output, "%lu", intval);
+	    else
+	      fprintf (Output, "%%n");
+	    break;
+	  case 'w':
+	    if (floatvalid)
+	      fprintf (Output, "%f", floatval);
+	    else
+	      fprintf (Output, "%%w");
+	    break;
+	  case '%':
+	    fputc ('%', Output);
+	  }
+      }
+    else
+      fputc (*s, Output);
+}
+
+
+void 
+HeaderOut (FILE * Output, u_char * UDoc, unsigned long ULen, int heads_length)
+{
+  int i, space = 1, num = 0;
+  for (i = 0; i < ULen && num < heads_length; i++)
+    {
+      char c = UDoc[i];
+      if (c == '\02')
+	break;
+
+      if (isspace (c) || c == '\01' || c == '\03')
+	{
+	  if (!space)
+	    {
+	      fputc (' ', Output);
+	      num++;
+	    }
+	  space = 1;
+	}
+      else
+	{
+	  space = 0;
+	  fputc (c, Output);
+	  num++;
+	}
+    }
+}
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) || defined(NZDL)
+void PrintDocNum(FILE *output, char query_type,
+		 int docnum, int indexnum, float weight)
+{
+  if (query_type == 'R' || query_type == 'A')
+    fprintf(output, "%7d.%-7d  %6.4f\n", docnum, indexnum, weight);
+  else
+    fprintf(output, "%7d.%-7d\n", docnum, indexnum);
+}
+#endif
+
+static int 
+ProcessDocs (query_data * qd, int num, int verbatim,
+	     char OutputType, FILE * Output)
+{
+  int max_buf = 0;
+  int DocCount = 0;
+  char *doc_sepstr = NULL;
+  char *para_sepstr = NULL;
+  char *para_start = NULL;
+  int heads_length = atoi (GetDefEnv ("heads_length", "50"));
+  char QueryType = get_query_type ();
+  int need_text = (OutputType == OUTPUT_TEXT || OutputType == OUTPUT_HILITE ||
+		   OutputType == OUTPUT_HEADERS || OutputType == OUTPUT_SILENT || 
+		   post_proc); /* [RJM June 1997 -- fixing post retrieval scan] */
+
+  if (OutputType == OUTPUT_TEXT || OutputType == OUTPUT_HILITE)
+    {
+      if (QueryType == QUERY_APPROX || QueryType == QUERY_RANKED)
+	{
+	  doc_sepstr = de_escape_string (
+				    Xstrdup (GetDefEnv ("ranked_doc_sepstr",
+			   "---------------------------------- %n %w\\n")));
+	}
+      else
+	{
+	  doc_sepstr = de_escape_string (
+					  Xstrdup (GetDefEnv ("doc_sepstr",
+			      "---------------------------------- %n\\n")));
+	}
+      para_sepstr = de_escape_string (
+				       Xstrdup (GetDefEnv ("para_sepstr",
+				  "\\n######## PARAGRAPH %n ########\\n")));
+
+      para_start = de_escape_string (
+				      Xstrdup (GetDefEnv ("para_start",
+					    "***** Weight = %w *****\\n")));
+    }
+
+  if (need_text)
+    {
+      max_buf = atoi (GetDefEnv ("buffer", "1048576"));
+    }
+
+  do
+    {
+      u_char *UDoc = NULL;
+      unsigned long ULen;
+
+      if (need_text)
+	{
+	  /* load the compressed text */
+	  if (LoadCompressedText (qd, max_buf))
+	    {
+	      Message ("Unable to load compressed text.");
+	      FatalError (1, "This is probably due to lack of memory.");
+	    }
+
+	  /* uncompress the loaded text */
+	  UDoc = GetDocText (qd, &ULen);
+	  if (UDoc == NULL)
+	    FatalError (1, "UDoc is unexpectedly NULL");
+	}
+
+      if (!UDoc || PostProc ((char *) UDoc, verbatim))
+	{
+	  switch (OutputType)
+	    {
+	    case OUTPUT_COUNT:
+	    case OUTPUT_SILENT:
+	      break;
+	    case OUTPUT_DOCNUMS:	/* This prints out the docnums string */
+	      if (PagerRunning)
+		{
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) || defined(NZDL)
+		  int doc_num = GetDocNum(qd);
+
+		  if (qd->paragraph)
+		    {
+		      if (qd->id->ifh.InvfLevel == 3 &&
+			  (QueryType == 'R' || QueryType == 'A'))
+			{
+			  /* Print weights for each paragraph in document */
+			  
+
+			  int true_doc_num = GetDocNumFromParaNum(qd, doc_num);
+
+			  /* Get number of paragraphs in this document */
+
+			  int num_paragraphs = 
+			    qd->paragraph[true_doc_num]-qd->paragraph[true_doc_num-1];
+
+			  int init_para = FetchInitialParagraph(qd->td,
+								doc_num);
+			  DocEntry *de, *doc_chain = GetDocChain(qd);
+			  int i;
+
+			  for (i = 0; i < num_paragraphs; i++)
+			    {
+			      if ((de = in_chain(i, init_para, doc_chain)))
+				PrintDocNum(Output, QueryType,
+					    true_doc_num, init_para+i,
+					    de->Weight);
+			    }
+			}
+		      else
+			PrintDocNum(Output, QueryType,
+				    GetDocNumFromParaNum(qd, GetDocNum(qd)),
+				    GetDocNum(qd),
+				    GetDocWeight(qd));
+		    }
+		  else
+		    {
+		      PrintDocNum(Output, QueryType,
+				  doc_num, doc_num, GetDocWeight(qd));
+		    }
+#else
+		fprintf (Output, "%7d   %6.4f   %7lu\n", GetDocNum (qd),
+			 GetDocWeight (qd), GetDocCompLength (qd));
+#endif
+		}
+	      break;
+	    case OUTPUT_HEADERS:	/* This prints out the headers of the documents */
+	      if (PagerRunning)
+		fprintf (Output, "%d ", GetDocNum (qd));
+	      HeaderOut (Output, UDoc, ULen, heads_length);
+	      if (PagerRunning)
+		fputc ('\n', Output);
+	      break;
+#if TREC_MODE
+	    case OUTPUT_EXTRAS:	/* This prints out the docnums string */
+	      if (PagerRunning && trec_ids)
+		{
+		  long DN, PN = GetDocNum (qd) - 1;
+		  if (trec_paras)
+		    DN = trec_paras[PN];
+		  else
+		    DN = PN;
+		  fprintf (Output, "%-14.14s  %8ld  %10.5f\n",
+			   &trec_ids[DN * 14], PN + 1, GetDocWeight (qd));
+		}
+	      break;
+#endif
+	    case OUTPUT_TEXT:
+	    case OUTPUT_HILITE:
+	      {
+		int j, para = -1, curr_para = 0;
+		int init_para = -1;
+		DocEntry *de, *doc_chain = NULL;
+		register char ch = ' ';
+		register char lch = '\n';
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) || defined(NZDL)
+		if (qd->id->ifh.InvfLevel == 3)
+		  {
+		    init_para = FetchInitialParagraph(qd->td, GetDocNum(qd));
+		    
+		    StringOut(Output, para_sepstr, 
+			      1, init_para+curr_para,
+			      0, 0);
+
+		  }
+		else
+		  StringOut(Output, doc_sepstr, 
+			    1, GetDocNum(qd),
+			    QueryType == 'A' || QueryType == 'R',
+			    GetDocWeight(qd));
+
+#else
+		int p_on = 0;
+
+		if (PagerRunning)
+		  {
+		    StringOut (Output, doc_sepstr,
+			       1, GetDocNum (qd),
+			       QueryType == 'A' || QueryType == 'R',
+			       GetDocWeight (qd));
+		  }
+		if (qd->id->ifh.InvfLevel == 3)
+		  {
+		    init_para = FetchInitialParagraph (qd->td, GetDocNum (qd));
+		    doc_chain = GetDocChain (qd);
+		    para = GetDocNum (qd) - init_para;
+
+		    StringOut (Output, para_sepstr,
+			       1, curr_para + 1,
+			       0, 0);
+
+		    if ((de = in_chain (0, init_para, doc_chain)))
+		      StringOut (Output, para_start,
+				 0, 0,
+				 1, de->Weight);
+
+		    if (doc_chain->DocNum - init_para == 0)
+		      p_on = 1;
+		  }
+#endif
+		for (j = 0; j < ULen; j++)
+		  {
+		    ch = UDoc[j];
+		    switch (ch)
+		      {
+		      case '\02':
+			break;
+		      case '\01':
+			ch = '\n';
+		      case '\03':
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(PARADOCNUM) || defined(NZDL)
+			/* print paragraph numbers only if this is
+			   a level 3 index */
+			if (qd->id->ifh.InvfLevel == 3)
+			  {
+			    curr_para++;
+			    StringOut(Output, para_sepstr, 
+				      1, init_para+curr_para,
+				      0, 0);
+			  }
+#else
+			p_on = 0;
+			curr_para++;
+			StringOut (Output, para_sepstr,
+				   1, curr_para + 1,
+				   0, 0);
+			lch = *(strchr (para_sepstr, '\0') - 1);
+			if ((de = in_chain (curr_para, init_para, doc_chain)))
+			  StringOut (Output, para_start,
+				     0, 0,
+				     1, de->Weight);
+			if (doc_chain &&
+			    doc_chain->DocNum - init_para == curr_para)
+			  p_on = 1;
+#endif
+			break;
+		      default:
+			{
+			  if (PagerRunning)
+			    {
+			      fputc (ch, Output);
+/* [RPAP - Feb 97: NZDL Additions] */
+#if !defined(PARADOCNUM) && !defined(NZDL)
+			      if (p_on && isprint (ch))
+				{
+				  fputc ('\b', Output);
+				  fputc ('_', Output);
+				}
+#endif
+			    }
+
+			  lch = ch;
+			}
+		      }
+		  }
+		if (PagerRunning && lch != '\n')
+		  fputc ('\n', Output);
+/* [RPAP - Feb 97: NZDL Additions] */
+#if !defined(PARADOCNUM) && !defined(NZDL)
+		p_on = 0;
+#endif
+	      }
+	    }
+	  if (PagerRunning)
+	    fflush (Output);
+
+	  DocCount++; /* moved within if statement [RJM June 1997 -- fixing post retrieval scan] */
+	}
+    }
+  while (NextDoc (qd) && PagerRunning && (!Ctrl_C));
+
+  if (need_text)
+    {
+      FreeTextBuffer (qd);
+    }
+
+  if (OutputType == OUTPUT_TEXT || OutputType == OUTPUT_HILITE)
+    {
+      Xfree (doc_sepstr);
+      Xfree (para_sepstr);
+      Xfree (para_start);
+    }
+
+  return (DocCount);
+}
+
+
+void 
+output_terminator (FILE * out)
+{
+  char *terminator = Xstrdup (GetDefEnv ("terminator", ""));
+  de_escape_string (terminator);
+  fputs (terminator, out);
+  Xfree (terminator);
+}
+
+
+
+
+/* MoreDocs () */
+/* Displays all documents in list  DocList. */
+/* Documents are fetched, then decompressed and displayed according to the */
+/* format implied in  FormString(). */
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+# define HILITE_PAGER "mg_hilite_words.exe"
+#else
+# define HILITE_PAGER "mg_hilite_words"
+#endif
+
+#define MAX_HILITE_PAGER_STR 80	/* for command & its options */
+
+static void 
+MoreDocs (query_data * qd, char *Query, char OutputType)
+{
+  static char terms_str[MAXTERMSTRLEN + 1];
+  int DocCount = 0;		/* number of actual matches */
+  FILE *Output = NULL;
+  int using_pipe = 0;
+  char *pager = NULL;
+
+  Ctrl_C = 0;
+
+  qd->num_of_ans = qd->DL->num;
+
+  /* [RPAP - Feb 97: WIN32 Port] */
+#ifndef __WIN32__
+  signal (SIGPIPE, SIGPIPE_handler);
+#endif
+  signal (SIGINT, SIGINT_handler);
+
+  PagerRunning = 1;
+  if (isatty (fileno (OutFile)) && GetEnv ("pager") &&
+      OutputType != OUTPUT_HILITE &&
+      OutputType != OUTPUT_SILENT && OutputType != OUTPUT_COUNT)
+    {
+      pager = GetEnv ("pager");
+    }
+  else if (isatty (fileno (OutFile)) && OutputType == OUTPUT_HILITE)
+    {
+      /* concat the pager and its word argument strings */
+      ConvertTermsToString (qd->TL, terms_str);
+      pager = Xmalloc (MAX_HILITE_PAGER_STR + strlen (terms_str) + 1);
+      if (!pager)
+	{
+	  fprintf (stderr, "Unable to allocate memory for highlighting\n");
+	  return;
+	}
+      sprintf (pager, "%s --style=%s --pager=%s --stem_method=%ld --stemmer=%ld %s",
+	       HILITE_PAGER,
+	       GetEnv ("hilite_style"),
+	       GetEnv ("pager"),
+	       qd->sd->sdh.stem_method,
+	       qd->sd->sdh.stemmer_num,
+	       terms_str);
+
+    }
+  else
+    {
+      Output = OutFile;
+    }
+
+/* [RPAP - Feb 97: NZDL Additions] */
+#if defined(OUTPUTSTEMMEDWORDS) || defined(NZDL)
+  if (!isatty(fileno(OutFile)) && get_query_type() != QUERY_DOCNUMS)
+    {
+      ConvertTermsToString(qd->TL, terms_str);
+      fprintf(Output, "%s\n", terms_str);
+    }
+#endif
+  if (pager)
+    {
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+      Output = _popen (pager, "w");
+#else
+      Output = popen (pager, "w");
+#endif
+      using_pipe = (Output != NULL);
+      if (!using_pipe)
+	{
+	  fprintf (stderr, "Unable to run \"%s\"\n", pager);
+	  return;
+	}
+    }
+
+
+  if (qd->DL->num > 0)
+    {
+      if (OutputType == OUTPUT_COUNT && !post_proc)
+	DocCount = qd->DL->num;
+      else {
+	DocCount = ProcessDocs (qd, qd->DL->num,
+				BooleanEnv (GetEnv ("verbatim"), 1),
+				OutputType, Output);
+      }
+    }
+
+  if (PagerRunning)
+    {
+      output_terminator (Output);
+      fflush (Output);
+    }
+
+  if (OutputType == OUTPUT_HILITE && pager)
+    free (pager);		/* as needed to malloc to create the pager string */
+
+  if (using_pipe)
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+    _pclose (Output);
+#else
+    pclose (Output);
+#endif
+
+  if (qd->DL->num == 0)
+    fprintf (stderr, "No entries correspond to that query.\n");
+  else
+    {
+      if (OutputType == OUTPUT_COUNT)
+	fprintf (stderr, "%d documents match.\n", DocCount);
+      else
+	fprintf (stderr, "%d documents retrieved.\n", DocCount);
+    }
+
+  signal (SIGINT, SIG_DFL);
+}
+
+
+void 
+start_up_stats (query_data * qd, InitQueryTimes iqt)
+{
+  Clear_Stats ();
+  if (BooleanEnv (GetEnv ("timestats"), 0) ||
+      BooleanEnv (GetEnv ("briefstats"), 0))
+    StartUpTimeStats (&iqt);
+
+  if (BooleanEnv (GetEnv ("diskstats"), 0) ||
+      BooleanEnv (GetEnv ("briefstats"), 0))
+    File_Stats (qd);
+
+  if (BooleanEnv (GetEnv ("memstats"), 0) ||
+      BooleanEnv (GetEnv ("briefstats"), 0))
+    MemStats (qd);
+
+}
+
+
+void 
+shut_down_stats (query_data * qd, ProgTime * start,
+		 ProgTime * invf, ProgTime * text)
+{
+  Clear_Stats ();
+  if (BooleanEnv (GetEnv ("timestats"), 0) ||
+      BooleanEnv (GetEnv ("briefstats"), 0))
+    QueryTimeStats (start, invf, text);
+
+  if (BooleanEnv (GetEnv ("diskstats"), 0) ||
+      BooleanEnv (GetEnv ("briefstats"), 0))
+    {
+      TransFileStats (qd);
+      File_Stats (qd);
+    }
+
+  if (BooleanEnv (GetEnv ("sizestats"), 0))
+    TotalSizeStats (qd);
+}
+
+
+
+char *wordfreqword2str (u_char * s)
+{
+  static char buf[1024];
+  int i, len = (int) *s++;
+
+  for (i = 0; i < len; i++)
+    {
+      buf[i] = (char)s[i];
+    }
+  buf[len] = '\0';
+
+  return buf;
+}
+
+
+/* [RPAP - Feb 97: Term Frequency] */
+/*********************************
+ * PrintQueryTermFreq
+ *
+ * Prints the query terms and their respective frequencies within the collection
+ *********************************/
+void
+PrintQueryTermFreqs (QueryTermList *qtl)
+{
+  int i;
+
+  /* Print the number of terms */
+  fprintf (OutFile, "%d\n", qtl->num);
+
+  /* Print the terms and their respective frequency within the collection */
+  for (i = 0; i < qtl->num; i++)
+    if (qtl->QTE[i].stem_method == -1)
+      /* Using default stem method - don't print stem method beside term */
+      fprintf (OutFile, "%s %d\n", wordfreqword2str (qtl->QTE[i].Term), qtl->QTE[i].Count);
+    else
+      /* Term was forced with a stem, print stem method with term */
+      fprintf (OutFile, "%s#%d %d\n", wordfreqword2str (qtl->QTE[i].Term), qtl->QTE[i].stem_method, qtl->QTE[i].Count);
+}
+
+
+void 
+query (void)
+{
+  ProgTime TotalStartTime, TotalInvfTime, TotalTextTime;
+  InitQueryTimes iqt;
+  query_data *qd;
+
+  TotalStartTime.RealTime = TotalStartTime.CPUTime = 0;
+  TotalInvfTime.RealTime = TotalInvfTime.CPUTime = 0;
+  TotalTextTime.RealTime = TotalTextTime.CPUTime = 0;
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  qd = InitQuerySystem (GetDefEnv ("mgdir", ".\\"),
+                        GetDefEnv ("mgname", ""),
+			GetDefEnv ("textname", NULL), /* [RJM 06/97: text filename] */
+			&iqt);
+#else
+  qd = InitQuerySystem (GetDefEnv ("mgdir", "./"),
+                        GetDefEnv ("mgname", ""),
+			GetDefEnv ("textname", NULL), /* [RJM 06/97: text filename] */
+                        &iqt);
+#endif
+
+  if (!qd)
+    FatalError (1, mg_errorstrs[mg_errno], mg_error_data);
+  start_up_stats (qd, iqt);
+
+
+  while (1)
+    {
+      ProgTime StartTime, InvfTime, TextTime;
+      char QueryType;
+      char OutputType;
+      char *line;
+      ResetFileStats (qd);
+      qd->max_mem_in_use = qd->mem_in_use = 0;
+
+      qd->tot_hops_taken += qd->hops_taken;
+      qd->tot_num_of_ptrs += qd->num_of_ptrs;
+      qd->tot_num_of_accum += qd->num_of_accum;
+      qd->tot_num_of_terms += qd->num_of_terms;
+      qd->tot_num_of_ans += qd->num_of_ans;
+      qd->tot_text_idx_lookups += qd->text_idx_lookups;
+      qd->hops_taken = qd->num_of_ptrs = 0;
+      qd->num_of_accum = qd->num_of_ans = qd->num_of_terms = 0;
+      qd->text_idx_lookups = 0;
+
+      Display_Stats (stderr);
+      Clear_Stats ();
+      line = get_query (qd);
+      if (!line || Quitting)
+	break;
+
+      GetPostProc (line);
+
+      GetTime (&StartTime);
+
+      FreeQueryDocs (qd);
+
+      QueryType = get_query_type ();
+      OutputType = get_output_type ();
+      /* No point in hiliting words on a docnum query */
+      if (OutputType == OUTPUT_HILITE && QueryType == QUERY_DOCNUMS)
+	OutputType = OUTPUT_TEXT;
+
+      switch (QueryType)
+	{
+	case QUERY_BOOLEAN:
+	  {
+	    char *maxdocs;
+	    BooleanQueryInfo bqi;
+	    maxdocs = GetDefEnv ("maxdocs", "all");
+	    bqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
+	    /* [RPAP - Jan 97: Stem Index Change] */
+	    if (qd->sd->sdh.indexed)
+	      BooleanQuery (qd, line, &bqi, (BooleanEnv (GetEnv ("casefold"), 0) |
+					     (BooleanEnv (GetEnv ("stem"), 0) << 1)));
+	    else
+	      BooleanQuery (qd, line, &bqi, qd->sd->sdh.stem_method);
+
+	    break;
+	  }
+	case QUERY_APPROX:
+	case QUERY_RANKED:
+	  {
+	    char *maxdocs;
+	    char *maxterms;
+	    char *maxaccum;
+	    RankedQueryInfo rqi;
+	    maxdocs = GetDefEnv ("maxdocs", "all");
+	    maxterms = GetDefEnv ("max_terms", "all");
+	    maxaccum = GetDefEnv ("max_accumulators", "all");
+	    rqi.Sort = BooleanEnv (GetEnv ("sorted_terms"), 0);
+	    rqi.QueryFreqs = BooleanEnv (GetEnv ("qfreq"), 1);
+	    rqi.Exact = QueryType == QUERY_RANKED;
+	    rqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
+	    rqi.MaxTerms = strcmp (maxterms, "all") ? atoi (maxterms) : -1;
+	    rqi.MaxParasToRetrieve = rqi.MaxDocsToRetrieve;
+	    if (qd->id->ifh.InvfLevel == 3 && GetEnv ("maxparas"))
+	      rqi.MaxParasToRetrieve = atoi (GetEnv ("maxparas"));
+	    rqi.AccumMethod = toupper (*GetDefEnv ("accumulator_method", "A"));
+	    rqi.MaxAccums = strcmp (maxaccum, "all") ? atoi (maxaccum) : -1;
+	    rqi.HashTblSize = IntEnv (GetEnv ("hash_tbl_size"), 1000);
+	    rqi.StopAtMaxAccum = BooleanEnv (GetEnv ("stop_at_max_accum"), 0);
+	    rqi.skip_dump = GetEnv ("skip_dump");
+	    RankedQuery (qd, line, &rqi);
+	    break;
+	  }
+	case QUERY_DOCNUMS:
+	  {
+	    DocnumsQuery (qd, line);
+	    break;
+	  }
+	}
+
+      GetTime (&InvfTime);
+
+      /* [RPAP - Feb 97: Term Frequency] */
+      if (qd->QTL && BooleanEnv (GetEnv ("term_freq"), 0))
+	PrintQueryTermFreqs (qd->QTL);
+
+      if (qd->DL)
+	MoreDocs (qd, line, OutputType);
+
+      GetTime (&TextTime);
+
+      if (BooleanEnv (GetEnv ("timestats"), 0) ||
+	  BooleanEnv (GetEnv ("briefstats"), 0))
+	QueryTimeStats (&StartTime, &InvfTime, &TextTime);
+
+      if (BooleanEnv (GetEnv ("diskstats"), 0) ||
+	  BooleanEnv (GetEnv ("briefstats"), 0))
+	File_Stats (qd);
+
+      if (BooleanEnv (GetEnv ("memstats"), 0) ||
+	  BooleanEnv (GetEnv ("briefstats"), 0))
+	MemStats (qd);
+
+      if (BooleanEnv (GetEnv ("sizestats"), 0))
+	SizeStats (qd);
+
+      TotalInvfTime.RealTime += InvfTime.RealTime - StartTime.RealTime;
+      TotalInvfTime.CPUTime += InvfTime.CPUTime - StartTime.CPUTime;
+      TotalTextTime.RealTime += TextTime.RealTime - StartTime.RealTime;
+      TotalTextTime.CPUTime += TextTime.CPUTime - StartTime.CPUTime;
+    }
+
+  if (isatty (fileno (InFile)) && !Quitting)
+    fprintf (stderr, "\n");
+
+  shut_down_stats (qd, &TotalStartTime, &TotalInvfTime, &TotalTextTime);
+
+  Display_Stats (stderr);
+
+}
+
+
+void 
+search_for_collection (char *name)
+{
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  char *dir = GetDefEnv ("mgdir", ".\\");
+#else
+  char *dir = GetDefEnv ("mgdir", "./");
+#endif
+  char buffer[512];
+  struct stat stat_buf;
+  if (strrchr (dir, '/') && *(strrchr (dir, '/') + 1) != '\0')
+    {
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+      sprintf (buffer, "%s", dir);
+#else
+      sprintf (buffer, "%s/", dir);
+#endif
+      SetEnv ("mgdir", buffer, NULL);
+      dir = GetEnv ("mgdir");
+    }
+
+  sprintf (buffer, "%s.text", name);
+  if (stat (buffer, &stat_buf) != -1)
+    {
+      if ((stat_buf.st_mode & S_IFREG) != 0)
+	{
+	  /* The name is a directory */
+	  SetEnv ("mgname", name, NULL);
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+          SetEnv ("mgdir", ".\\", NULL);
+#else
+	  SetEnv ("mgdir", "./", NULL);
+#endif
+	  return;
+	}
+    }
+
+  sprintf (buffer, "%s%s", dir, name);
+  if (stat (buffer, &stat_buf) != -1)
+    {
+      if ((stat_buf.st_mode & S_IFDIR) != 0)
+	{
+	  /* The name is a directory */
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+          sprintf (buffer, "%s%s", name, name);
+#else
+	  sprintf (buffer, "%s/%s", name, name);
+#endif
+	  SetEnv ("mgname", buffer, NULL);
+	  return;
+	}
+    }
+
+  /* Look in the current directory last */
+  if (stat (name, &stat_buf) != -1)
+    {
+      if ((stat_buf.st_mode & S_IFDIR) != 0)
+	{
+	  /* The name is a directory */
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+          sprintf (buffer, "%s%s", name, name);
+          SetEnv ("mgdir", ".\\", NULL);
+#else
+	  sprintf (buffer, "%s/%s", name, name);
+          SetEnv ("mgdir", "./", NULL);
+#endif
+	  SetEnv ("mgname", buffer, NULL);
+	  return;
+	}
+    }
+
+  SetEnv ("mgname", name, NULL);
+}
+
+/* main () */
+/* Initialises global variables based on command line switches, and opens */
+/* files.  Then calls  query ()  to perform the querying. */
+int main (int argc, char **argv)
+{
+  ProgTime StartTime;
+  int decomp = 0;
+  int ch;
+
+  msg_prefix = argv[0];
+  GetTime (&StartTime);
+
+  /* Initialise the environment with default values */
+
+  InitEnv ();
+
+  read_mgrc_file ();
+
+  OutFile = stdout;
+  InFile = stdin;
+
+  opterr = 0;
+  /* [RJM 06/97: text filename] */
+  while ((ch = getopt (argc, argv, "Df:d:t:h")) != -1) {
+    switch (ch) {
+      case 'f':
+	SetEnv ("mgname", optarg, NULL);
+	break;
+      case 'd':
+	SetEnv ("mgdir", optarg, NULL);
+	break;
+      case 't': /* [RJM 06/97: text filename] */
+	SetEnv ("textname", optarg, NULL);
+	break;
+      case 'D':
+	decomp = 1;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-D] [-f base name of collection] "
+		 "[-t base name of files for text] " /* [RJM 06/97: text filename] */
+		 "[-d data directory] [collection]\n", argv[0]);
+	exit (1);
+    }      
+  }
+
+  PushEnv ();
+
+  if (decomp == 0)
+    {
+
+      Init_ReadLine ();
+
+      /* write a first prompt, let the user start thinking */
+      if (!BooleanEnv (GetEnv ("expert"), 0) && isatty (fileno (InFile)))
+	{
+	  fprintf (stderr, "\n\n\t     FULL TEXT RETRIEVAL QUERY PROGRAM\n");
+	  fprintf (stderr, "%24s%s\n\n", "", *"21 Mar 1994" == '%' ? __DATE__ : "21 Mar 1994");
+	  fprintf (stderr, "\n");
+	  fprintf (stderr, "  mgquery version " VERSION ", Copyright (C) 1994 Neil Sharman\n");
+	  fprintf (stderr, "  mgquery comes with ABSOLUTELY NO WARRANTY; for details type `.warranty'\n");
+	  fprintf (stderr, "  This is free software, and you are welcome to redistribute it\n");
+	  fprintf (stderr, "  under certain conditions; type `.conditions' for details.\n");
+	  fprintf (stderr, "\n");
+	}
+    }
+  if (optind < argc)
+    search_for_collection (argv[optind]);
+
+  if (decomp == 0)
+    {
+      query ();
+    }
+  else
+    {
+      int i;
+      InitQueryTimes iqt;
+      query_data *qd;
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+      qd = InitQuerySystem (GetDefEnv ("mgdir", ".\\"),
+                            GetDefEnv ("mgname", ""),
+                            GetDefEnv ("textname", NULL), /* [RJM 06/97: text filename] */
+                            &iqt);
+#else
+      qd = InitQuerySystem (GetDefEnv ("mgdir", "./"),
+			    GetDefEnv ("mgname", ""),
+                            GetDefEnv ("textname", NULL), /* [RJM 06/97: text filename] */
+			    &iqt);
+#endif
+      if (!qd)
+	FatalError (1, mg_errorstrs[mg_errno], mg_error_data);
+
+
+      start_up_stats (qd, iqt);
+
+      Display_Stats (stderr);
+      for (i = 0; i < qd->td->cth.num_of_docs; i++)
+	{
+	  RawDocOutput (qd, i + 1, stdout);
+	  putc ('\2', stdout);
+	}
+      Message ("%s", ElapsedTime (&StartTime, NULL));
+
+      FinishQuerySystem (qd);
+    }
+
+  UninitEnv ();
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.1	(revision 16561)
@@ -0,0 +1,131 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgstat 1 \*(Dt CITRI
+.SH NAME
+mgstat \- print out statistics about a document collection
+.SH SYNOPSIS
+.B mgstat
+[
+.B \-h
+]
+[
+.B \-E
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgstat
+prints out various statistics about an existing
+.BR mg (1)
+document collection.  Depending on the size of the collection, sizes
+will be printed in either kilobytes or megabytes.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stdout .
+.TP
+.B \-E
+This option forces sizes to be printed in bytes rather than kilobytes
+or megabytes.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text
+Compressed documents.
+.TP
+.B *.invf
+Inverted file.
+.TP
+.B *.text.idx.wgt
+Interleaved index into the compressed documents and document weights.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.TP
+.B *.invf.dict.blocked
+Compressed stemmed dictionary and index into the inverted file merged
+into an inverted file.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.invf.chunk
+Maps stemmed terms from occurrence order to lexical order.
+.TP
+.B *.invf.chunk.trans
+Describes where the source text is broken up into chunks for the
+inversion pass.
+.TP
+.B *.invf.dict.hash
+A perfect hash function for the terms in the stemmed dictionary.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_compression_dict (1),
+.BR mg_fast_comp_dict (1),
+.BR mg_get (1),
+.BR mg_invf_dict (1),
+.BR mg_invf_dump (1),
+.BR mg_invf_rebuild (1),
+.BR mg_passes (1),
+.BR mg_perf_hash_build (1),
+.BR mg_text_estimate (1),
+.BR mg_weights_build (1),
+.BR mgbilevel (1),
+.BR mgbuild (1),
+.BR mgdictlist (1),
+.BR mgfelics (1),
+.BR mgquery (1),
+.BR mgtic (1),
+.BR mgticbuild (1),
+.BR mgticdump (1),
+.BR mgticprune (1),
+.BR mgticstat (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgstat.c	(revision 16561)
@@ -0,0 +1,406 @@
+/**************************************************************************
+ *
+ * mgstat.c -- Program to generate statistics on a text collection
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "mg.h"
+#include "words.h"
+#include "invf.h"
+#include "text.h"
+#include "longlong.h"
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2001/09/21 12:46:42  kjm18
+   updated mg to be in line with mg_1.3f. Now uses long long for some variables
+   to enable indexing of very large collections.
+
+   Revision 1.1  1999/08/10 21:18:19  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:50  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:31  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1994/09/20  04:41:59  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+char *search_for_collection (char *name);
+int process_file (char *, char *, int);
+void ProcessDict (char *);
+void ProcessStem (char *);
+long ProcessStemBlk (char *name);  /* [RPAP - Jan 97: Stem Index Change] */
+
+static mg_ullong inputbytes = 0;
+static mg_ullong total = 0;
+
+
+
+
+
+int main (int argc, char **argv)
+{
+  mg_ullong sub_total;
+  int fast;
+  char *file_name = "";
+  int ch;
+  int exact = 0;
+  long indexed = 0; /* [RPAP - Jan 97: Stem Index Change] */
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "Ehf:d:")) != -1)
+    switch (ch)
+      {
+      case 'E':
+	exact = 1;
+	break;
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file]"
+		 "[-d data directory] [-h]\n", argv[0]);
+	exit (1);
+      }
+
+  if (optind < argc)
+    file_name = search_for_collection (argv[optind]);
+
+  ProcessDict (file_name);
+  ProcessStem (file_name);
+  indexed = ProcessStemBlk (file_name); /* [RPAP - Jan 97: Stem Index Change] */
+
+  {
+    char Name[256];
+    char *s;
+    sprintf (Name, FILE_NAME_FORMAT, get_basepath (), file_name, "");
+    s = strrchr (Name, '/');
+    if (s)
+      *s = '\0';
+    printf ("\nThe collection is in \"%s\"\n", Name);
+  }
+
+
+  printf ("\n\t\tFiles required by mgquery\n");
+  process_file (file_name, TEXT_SUFFIX, exact);
+  process_file (file_name, INVF_SUFFIX, exact);
+  process_file (file_name, TEXT_IDX_WGT_SUFFIX, exact);
+  process_file (file_name, APPROX_WEIGHTS_SUFFIX, exact);
+  process_file (file_name, INVF_DICT_BLOCKED_SUFFIX, exact);
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (indexed)
+    {
+      process_file (file_name, INVF_DICT_BLOCKED_1_SUFFIX, exact);
+      process_file (file_name, INVF_DICT_BLOCKED_2_SUFFIX, exact);
+      process_file (file_name, INVF_DICT_BLOCKED_3_SUFFIX, exact);
+    }
+
+  fast = process_file (file_name, TEXT_DICT_FAST_SUFFIX, exact);
+  if (!fast)
+    {
+      process_file (file_name, TEXT_DICT_SUFFIX, exact);
+      process_file (file_name, TEXT_DICT_AUX_SUFFIX, exact);
+    }
+
+
+  process_file (NULL, "SUB TOTAL", exact);
+  sub_total = total;
+  total = 0;
+
+  printf ("\n\t\tFiles NOT required by mgquery\n");
+  if (fast)
+    {
+      process_file (file_name, TEXT_DICT_SUFFIX, exact);
+      process_file (file_name, TEXT_DICT_AUX_SUFFIX, exact);
+    }
+  process_file (file_name, INVF_DICT_SUFFIX, exact);
+  process_file (file_name, INVF_IDX_SUFFIX, exact);
+  process_file (file_name, TEXT_STATS_DICT_SUFFIX, exact);
+  process_file (file_name, TEXT_IDX_SUFFIX, exact);
+  process_file (file_name, WEIGHTS_SUFFIX, exact);
+  process_file (file_name, INVF_CHUNK_SUFFIX, exact);
+  process_file (file_name, INVF_CHUNK_TRANS_SUFFIX, exact);
+  process_file (file_name, INVF_DICT_HASH_SUFFIX, exact);
+  process_file (file_name, INVF_PARAGRAPH_SUFFIX, exact);
+  process_file (NULL, "SUB TOTAL", exact);
+  total += sub_total;
+  printf ("\n");
+  process_file (NULL, "TOTAL", exact);
+  return 0;
+
+}
+
+
+
+char *
+search_for_collection (char *name)
+{
+  char *dir = get_basepath ();
+  static char buffer[512];
+  struct stat stat_buf;
+
+  /* Look in the current directory first */
+  if (stat (name, &stat_buf) != -1)
+    {
+      if (S_ISDIR(stat_buf.st_mode))
+	{
+	  /* The name is a directory */
+	  sprintf (buffer, "%s/%s", name, name);
+	  set_basepath (".");
+	  return buffer;
+	}
+    }
+
+  sprintf (buffer, "%s.text", name);
+  if (stat (buffer, &stat_buf) != -1)
+    {
+      if (S_ISREG(stat_buf.st_mode))
+	{
+	  /* The name is a directory */
+	  set_basepath (".");
+	  return name;
+	}
+    }
+  sprintf (buffer, "%s/%s", dir, name);
+  if (stat (buffer, &stat_buf) != -1)
+    {
+      if (S_ISDIR(stat_buf.st_mode))
+	{
+	  /* The name is a directory */
+	  sprintf (buffer, "%s/%s", name, name);
+	  return buffer;
+	}
+    }
+  return name;
+}
+
+
+
+
+
+
+void 
+ProcessDict (char *name)
+{
+  FILE *f;
+  compression_dict_header cdh;
+  int have_cdh = 0;
+  compressed_text_header cth;
+  int have_cth = 0;
+
+  if ((f = open_file (name, TEXT_DICT_SUFFIX, "rb", MAGIC_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      Read_cdh (f, &cdh, NULL, NULL);
+      fclose (f);
+      have_cdh = 1;
+    }
+
+  if ((f = open_file (name, TEXT_SUFFIX, "rb", MAGIC_TEXT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fread ((char *) &cth, sizeof (cth), 1, f);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(cth.num_of_docs);
+      NTOHD(cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+      NTOHUL(cth.num_of_words);
+      NTOHUL(cth.length_of_longest_doc);
+      NTOHD(cth.ratio);
+
+      fclose (f);
+      have_cth = 1;
+    }
+
+  if (have_cth)
+    {
+      inputbytes = cth.num_of_bytes;
+      printf ("Input bytes                        : %10" ULL_FS ", %8.2f Mbyte\n",
+	      cth.num_of_bytes, (double) cth.num_of_bytes / 1024 / 1024);
+      printf ("Documents                          : %10lu\n", cth.num_of_docs);
+      printf ("Words in collection [dict]         : %10lu\n", cth.num_of_words);
+      printf ("Longest doc in collection [dict]   : %10lu characters\n",
+	      cth.length_of_longest_doc);
+      printf ("Maximum ratio                      : %10.2f\n", cth.ratio);
+    }
+
+  if (have_cdh)
+    {
+      printf ("Words in dict                      : %10lu\n", cdh.num_words[1]);
+      printf ("Non-words in dict                  : %10lu\n", cdh.num_words[0]);
+      printf ("Total chars of distinct words      : %10lu\n", cdh.num_word_chars[1]);
+      printf ("Total chars of distinct non-words  : %10lu\n", cdh.num_word_chars[0]);
+    }
+
+}
+
+
+
+
+
+
+
+
+void 
+ProcessStem (char *name)
+{
+  FILE *f;
+  struct invf_dict_header idh;
+
+  if (!(f = open_file (name, INVF_DICT_SUFFIX, "rb",
+		       MAGIC_STEM_BUILD, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return;
+  fread ((char *) &idh, sizeof (idh), 1, f);
+  printf ("Words in collection [stem]         : %10ld\n", NTOHUL(idh.num_of_words));  /* [RPAP - Jan 97: Endian Ordering] */
+  printf ("Words in stem                      : %10ld\n", NTOHUL(idh.dict_size));  /* [RPAP - Jan 97: Endian Ordering] */
+  printf ("Indexed fragments                  : %10ld\n", NTOHUL(idh.num_of_docs));  /* [RPAP - Jan 97: Endian Ordering] */
+  printf ("Total chars of stem words          : %10ld\n", NTOHUL(idh.total_bytes));  /* [RPAP - Jan 97: Endian Ordering] */
+  fclose (f);
+}
+
+
+
+
+
+/* [RPAP - Jan 97: Stem Index Change] */
+long 
+ProcessStemBlk (char *name)
+{
+  FILE *f;
+  struct stem_dict_header sdh;
+
+  if (!(f = open_file (name, INVF_DICT_BLOCKED_SUFFIX, "rb",
+		       MAGIC_STEM, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return 0;
+  fread ((char *) &sdh, sizeof (sdh), 1, f);
+  fclose (f);
+  return NTOHUL(sdh.indexed);  /* [RPAP - Jan 97: Endian Ordering] */
+}
+
+
+
+
+int 
+process_file (char *name, char *ext, int exact)
+{
+  static double scale = 0;
+  static char *units;
+  struct stat buf;
+  if (scale == 0)
+    {
+      /* So can get output in Mb or Kb, */
+      /* will divide by a scale of 1024(Kb) or 1024*1024(Mb) */
+      /* Note: if inputbytes==0, then use 1024 as default */
+      scale = inputbytes > 10 * 1000 * 1000 ? 1024 * 1024 : 1024;
+      units = scale == 1024 ? "Kb" : "Mb";
+    }
+  if (name)
+    {
+      char Name[256];
+      sprintf (Name, FILE_NAME_FORMAT, get_basepath (), name, ext);
+      if (!stat (Name, &buf))
+	{
+	  char fname[256];
+	  char *nam = strrchr (name, '/');
+	  nam = nam ? nam + 1 : name;
+	  sprintf (fname, "%s%s", nam, ext);
+
+	  if (inputbytes == 0.0)
+	    {
+	      if (exact)
+		printf ("%s%*s: %10ld bytes   %7.3f%%\n", fname,
+			35 - (int) strlen (fname), "",
+			buf.st_size,
+			100.0 * buf.st_size / inputbytes);
+	      else
+		printf ("%s%*s: %8.2f %s   %7.3f%%\n", fname,
+			35 - (int) strlen (fname), "",
+			buf.st_size / scale,
+			units,
+			100.0 * buf.st_size / inputbytes);
+	    }
+	  else
+	    {
+	      if (exact)
+		printf ("%s%*s: %10ld bytes\n", fname,
+			35 - (int) strlen (fname), "",
+			buf.st_size);
+	      else
+		printf ("%s%*s: %8.2f %s\n", fname,
+			35 - (int) strlen (fname), "",
+			buf.st_size / scale,
+			units);
+	    }
+	  total += buf.st_size;
+	  return 1;
+	}
+      else
+	return 0;
+    }
+  else
+    {
+      if (inputbytes == 0.0)
+	{
+	  if (exact)
+	    printf ("%-34s : %10" ULL_FS " bytes   %7.3f%%\n", ext,
+		    total,
+		    100.0 * total / inputbytes);
+	  else
+	    printf ("%-34s : %8.2f %s   %7.3f%%\n", ext,
+		    total / scale, units,
+		    100.0 * total / inputbytes);
+	}
+      else
+	{
+	  if (exact)
+	    printf ("%-34s : %10" ULL_FS " bytes\n", ext, total);
+	  else
+	    printf ("%-34s : %8.2f %s\n", ext,
+		    total / scale, units);
+	}
+    }
+  return 1;
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.1	(revision 16561)
@@ -0,0 +1,86 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.Id $Id$
+.\"------------------------------------------------------------
+.TH mgstemidxlist 1 \*(Dt CITRI
+.SH NAME
+mgstemidxlist \- dumps the stem index file to STDOUT in ASCII format
+.SH SYNOPSIS
+.B mgstemidxlist
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.B \-s 1|2|3
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgstemidxlist
+is a utility program which passes through the stem index and displays
+the information.  (This program was used for debugging but is included
+for completeness!)  The header information is printed out, then for each
+block in the file the block header is displayed.  For each word in the
+stem index, the output is formatted as:
+
+.RS
+.I number_of_references \*(lqword\*(rq
+.br
+.RS
+.I "->  number_of_cases block 3-in-4_block offset"
+.br
+.I "->  ..."
+.RE
+.RE
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B -s 1|2|3
+The stem index file with the associated stem method.
+.br
+1 = casefolded and non-stemmed
+.br
+2 = non-casefolded and stemmed
+.br
+3 = casefolded and stemmed
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the
+.BR mg (1)
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 25
+.B *.invf.dict.blocked.1
+Stem index with stem index method 1.
+.TP
+.B *.invf.dict.blocked.2
+Stem index with stem index method 2.
+.TP
+.B *.invf.dict.blocked.3
+Stem index with stem index method 3.
+.SH "SEE ALSO"
+.na
+.BR mg (1),
+.BR mg_invf_dict (1),
+.BR mg_stem_idx (1).
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/mgstemidxlist.c	(revision 16561)
@@ -0,0 +1,245 @@
+/**************************************************************************
+ *
+ * mgstemidxlist.c -- Text dumper for the stem indexes
+ * Copyright (C) 1997  Ross Peeters
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "messages.h"
+#include "memlib.h"
+#include "local_strings.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "mg.h"
+#include "invf.h"
+#include "words.h"
+#include "backend.h"
+
+void
+read_3_in_4 (FILE * idbi)
+{
+  unsigned long i;
+  stemmed_idx *si;
+  u_char *buffer;
+  int block = 0;
+
+  if (!(si = Xmalloc (sizeof (stemmed_idx))))
+    {
+      return;
+    }
+
+  si->MemForStemIdx = 0;
+
+  fread (&(si->sih), sizeof (si->sih), 1, idbi);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(si->sih.lookback);
+  NTOHUL(si->sih.block_size);
+  NTOHUL(si->sih.num_blocks);
+  NTOHUL(si->sih.blocks_start);
+  NTOHUL(si->sih.index_chars);
+  NTOHUL(si->sih.num_of_words);
+
+  if (!(buffer = Xmalloc (si->sih.index_chars)))
+    {
+      Xfree (si);
+      return;
+    };
+  si->MemForStemIdx += si->sih.index_chars;
+
+  if (!(si->index = Xmalloc (si->sih.num_blocks * sizeof (*si->index))))
+    {
+      Xfree (si);
+      Xfree (buffer);
+      return;
+    };
+  si->MemForStemIdx += si->sih.num_blocks * sizeof (*si->index);
+
+  if (!(si->pos = Xmalloc (si->sih.num_blocks * sizeof (*si->pos))))
+    {
+      Xfree (si->index);
+      Xfree (si);
+      Xfree (buffer);
+      return;
+    };
+  si->MemForStemIdx += si->sih.num_blocks * sizeof (*si->pos);
+
+  if (!(si->buffer = Xmalloc (si->sih.block_size * sizeof (*si->buffer))))
+    {
+      Xfree (buffer);
+      Xfree (si->index);
+      Xfree (si->buffer);
+      Xfree (si);
+      return;
+    };
+  si->MemForStemIdx += si->sih.block_size * sizeof (*si->buffer);
+
+  si->active = -1;
+
+  for (i = 0; i < si->sih.num_blocks; i++)
+    {
+      register u_char len;
+      si->index[i] = buffer;
+      len = fgetc (idbi);
+      *buffer++ = len;
+      fread (buffer, sizeof (u_char), len, idbi);
+      buffer += len;
+      fread (&si->pos[i], sizeof (*si->pos), 1, idbi);
+      NTOHUL(si->pos[i]);  /* [RPAP - Jan 97: Endian Ordering] */
+    }
+
+  printf ("# lookback     = %lu\n", si->sih.lookback);
+  printf ("# block_size   = %lu\n", si->sih.block_size);
+  printf ("# num_blocks   = %lu\n", si->sih.num_blocks);
+  printf ("# blocks_start = %lu\n", si->sih.blocks_start);
+  printf ("# index_chars  = %lu\n", si->sih.index_chars);
+  printf ("# num_of_words = %lu\n", si->sih.num_of_words);
+
+  block = 0;
+  while (block < si->sih.num_blocks)
+    {
+      unsigned long *first_word;
+      unsigned short *num_words;
+      unsigned short *index;
+      long res;
+      u_char *base;
+      int num_indexes;
+
+      /* Read in next block */
+      fseek (idbi, si->pos[block] + si->sih.blocks_start, 0);
+      fread (si->buffer, si->sih.block_size, sizeof (u_char), idbi);
+      si->active = si->pos[block];
+
+      first_word = (unsigned long *) (si->buffer);
+      NTOHUL(*first_word);  /* [RPAP - Jan 97: Endian Ordering] */
+      num_words = (unsigned short *) (first_word + 1);
+      NTOHUS(*num_words);  /* [RPAP - Jan 97: Endian Ordering] */
+      index = num_words + 1;
+      num_indexes = ((*num_words - 1) / si->sih.lookback) + 1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = 0; i < num_indexes; i++)
+	NTOHUS(index[i]);
+
+      base = (u_char *) (index + num_indexes);
+      base += index[0];
+
+      printf ("\n# block      = %d\n", block);
+      printf ("# first_word = %lu\n", *first_word);
+      printf ("# num_words  = %u\n", *num_words);
+
+      res = 0;
+      while (res < *num_words)
+	{
+	  unsigned copy, suff;
+	  u_char prev[MAXSTEMLEN + 1];
+	  unsigned int num_entries, num_cases, blk;
+	  unsigned short blk_index, offset;
+
+	  /* Read word entry */
+	  copy = *base++;
+	  suff = *base++;
+	  bcopy ((char *) base, (char *) (prev + copy + 1), suff);
+	  *prev = copy + suff;
+	  base += suff;
+	  bcopy ((char *) base, (char *) &num_entries, sizeof (num_entries));
+	  base += sizeof (num_entries);
+	  NTOHUI(num_entries);  /* [RPAP - Jan 97: Endian Ordering] */
+	  printf ("%u \"%s\"\n", num_entries, word2str (prev));
+
+	  /* For all the PosEntries for the word... */
+	  for (i = 0; i < num_entries; i++)
+	    {
+	      bcopy ((char *) base, (char *) &num_cases, sizeof (num_cases));
+	      NTOHUI(num_cases);  /* [RPAP - Jan 97: Endian Ordering] */
+	      base += sizeof (num_cases);
+	      bcopy ((char *) base, (char *) &blk, sizeof (blk));
+	      NTOHUI(blk);  /* [RPAP - Jan 97: Endian Ordering] */
+	      base += sizeof (blk);
+	      bcopy ((char *) base, (char *) &blk_index, sizeof (blk_index));
+	      NTOHUS(blk_index);  /* [RPAP - Jan 97: Endian Ordering] */
+	      base += sizeof (blk_index);
+	      bcopy ((char *) base, (char *) &offset, sizeof (offset));
+	      NTOHUS(offset);  /* [RPAP - Jan 97: Endian Ordering] */
+	      base += sizeof (offset);
+
+	      printf ("    -> %4u %4u %4u %4u\n", num_cases, blk, blk_index, offset);
+	    }
+	  res++;
+	}
+      block++;
+    }
+  fclose (idbi);
+}
+
+int
+main (int argc, char **argv)
+{
+  FILE *idbi;
+  char *filename = "";
+  int ch;
+  int stem_method = 0;
+
+  msg_prefix = argv[0];
+  opterr = 0;
+  while ((ch = getopt (argc, argv, "f:d:hs:")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	filename = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 's':
+	stem_method = atoi (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-d data directory] [-h] -s 1|2|3 -f name\n", argv[0]);
+	exit (1);
+      }
+
+  /* Open required files */
+  switch (stem_method)
+    {
+    case (1):
+      idbi = open_file (filename, INVF_DICT_BLOCKED_1_SUFFIX, "rb", MAGIC_STEM_1,
+			MG_ABORT);
+      break;
+    case (2):
+      idbi = open_file (filename, INVF_DICT_BLOCKED_2_SUFFIX, "rb", MAGIC_STEM_2,
+			MG_ABORT);
+      break;
+    case (3):
+      idbi = open_file (filename, INVF_DICT_BLOCKED_3_SUFFIX, "rb", MAGIC_STEM_3,
+			MG_ABORT);
+      break;
+    default:
+      FatalError (1, "Stem method must be 1, 2 or 3\n");
+    }
+
+  if (!idbi)
+    FatalError (1, "Could NOT open file");
+
+  read_3_in_4 (idbi);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/query.docnums.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/query.docnums.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/query.docnums.c	(revision 16561)
@@ -0,0 +1,100 @@
+/**************************************************************************
+ *
+ * query.docnums.c -- Docnums query evaluation
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "filestats.h"
+#include "timing.h"
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+
+
+static DocList *
+AddToDocList (DocList * Docs, int res)
+{
+  int j;
+  /* Search the list for the word */
+  for (j = 0; j < Docs->num; j++)
+    if (Docs->DE[j].DocNum == res)
+      break;
+
+  /* Increment the weight if the word is in the list */
+  if (j < Docs->num)
+    Docs->DE[j].Weight++;
+  else
+    {
+      /* Create a new entry in the list for the new document */
+      Docs = ResizeDocList (Docs, Docs->num + 1);
+      Docs->DE[Docs->num].DocNum = res;
+      Docs->DE[Docs->num].Weight = 1;
+      Docs->DE[Docs->num].SeekPos = 0;
+      Docs->DE[Docs->num].Len = 0;
+      Docs->DE[Docs->num].CompTextBuffer = NULL;
+      Docs->DE[Docs->num].Next = NULL;
+      Docs->num++;
+    }
+  return (Docs);
+}
+
+void 
+DocnumsQuery (query_data * qd, char *QueryLine)
+{
+  DocList *Docs = MakeDocList (0);
+  unsigned long start = 0, finish;
+  int range = 0; /* is there a range or not ? */
+
+  /* find the start of the first word */
+  while (*QueryLine)
+    {
+      char *end;
+      finish = strtol (QueryLine, &end, 10);
+      if (end != (char *) QueryLine && finish >= 1 &&
+	  finish <= qd->sd->sdh.num_of_docs)
+	{
+	  unsigned long i;
+	  if (!range)
+	    start = finish;
+	  else
+	    range = 0;
+	  for (i = start; i <= finish; i++)
+	    Docs = AddToDocList (Docs, i);
+	  while (*end == ' ' || *end == '\t' || *end == '\n')
+	    end++;
+	  if (*end == '-')
+	    {
+	      start = finish;
+	      range = 1;
+	      end++;
+	    }
+	}
+      else
+	break;
+      QueryLine = end;
+    }
+  FreeQueryDocs (qd);
+
+  qd->DL = Docs;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/query.ranked.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/query.ranked.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/query.ranked.c	(revision 16561)
@@ -0,0 +1,994 @@
+/**************************************************************************
+ *
+ * query.ranked.c -- Ranked query evaluation
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "filestats.h"
+#include "messages.h"
+#include "timing.h"
+#include "sptree.h"
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "stem_search.h"
+#include "weights.h"
+#include "text_get.h"
+#include "invf_get.h"
+#include "words.h"
+#include "stemmer.h"
+#include "locallib.h"
+#include "environment.h"
+#include "term_lists.h"
+#include "local_strings.h"
+#include "query_term_list.h"  /* [RPAP - Feb 97: Term Frequency] */
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:20  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:50  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:34  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.4  1994/11/25  03:47:46  tes
+   * Committing files before adding the merge stuff.
+   *
+   * Revision 1.3  1994/10/20  03:57:03  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:42:04  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+/*************************************************************************/
+
+typedef struct HeapEntry
+  {
+    float Weight;
+    float *OrgWeight;
+    int DocNum;
+    u_long SeekPos;		/* position in the text file in bytes */
+    u_long Len;			/* length of the document in bytes */
+  }
+HeapEntry;
+
+typedef int (*HeapComp) (HeapEntry *, HeapEntry *);
+
+typedef struct Heap
+  {
+    int NumItems;
+    int MaxSize;
+    HeapComp HC;
+    HeapEntry HE[1];
+  }
+Heap;
+
+
+Heap *
+Heap_Make (int size, HeapComp hc)
+{
+  Heap *H;
+  H = Xmalloc (sizeof (Heap) + (size - 1) * sizeof (HeapEntry));
+  if (!H)
+    return NULL;
+  H->NumItems = 0;
+  H->MaxSize = size;
+  H->HC = hc;
+  return H;
+}
+
+int 
+Heap_Size (Heap * H)
+{
+  return sizeof (Heap) + H->MaxSize * sizeof (HeapEntry);
+}
+
+HeapEntry *
+Heap_GetHead (Heap * H)
+{
+  if (H && H->NumItems)
+    return &H->HE[0];
+  else
+    return NULL;
+}
+
+
+
+void 
+Heap_Heapify (Heap * H, int i)
+{
+  register int curr, child;
+  curr = i;
+  child = curr * 2;
+  while (child <= H->NumItems)
+    {
+      if (child < H->NumItems && H->HC (&H->HE[child], &H->HE[child - 1]) > 0)
+	child++;
+      if (H->HC (&H->HE[curr - 1], &H->HE[child - 1]) < 0)
+	{
+	  HeapEntry temp = H->HE[child - 1];
+	  H->HE[child - 1] = H->HE[curr - 1];
+	  H->HE[curr - 1] = temp;
+	  curr = child;
+	  child = 2 * child;
+	}
+      else
+	break;
+    }
+}
+
+
+void 
+Heap_Build (Heap * H)
+{
+  register int i;
+  for (i = H->NumItems / 2; i > 0; i--)
+    Heap_Heapify (H, i);
+}
+
+
+void 
+Heap_Sort (Heap * H)
+{
+  register int i;
+  for (i = H->NumItems; i > 1; i--)
+    {
+      HeapEntry temp = H->HE[0];
+      H->HE[0] = H->HE[i - 1];
+      H->HE[i - 1] = temp;
+      H->NumItems--;
+      Heap_Heapify (H, 1);
+    }
+}
+
+
+void 
+Heap_DeleteHead (Heap * H)
+{
+  H->HE[0] = H->HE[--H->NumItems];
+  Heap_Heapify (H, 1);
+}
+
+int 
+Heap_Lesser (HeapEntry * a, HeapEntry * b)
+{
+  return (a->Weight > b->Weight ? -1 :
+	  (a->Weight == b->Weight ? 0 : 1));
+}
+
+
+int 
+Heap_Greater (HeapEntry * a, HeapEntry * b)
+{
+  return (a->Weight > b->Weight ? 1 :
+	  (a->Weight == b->Weight ? 0 : -1));
+}
+
+int 
+Make_Exact_Root (query_data * qd, Heap * H)
+{
+  int num = 0;
+  HeapEntry *he = H->HE;
+  while (he->SeekPos == 0)
+    {
+      he->Weight = he->Weight *
+	GetLowerApproxDocWeight (qd->awd, he->DocNum - 1) /
+	FetchDocStart (qd, he->DocNum, &he->SeekPos, &he->Len);
+      Heap_Heapify (H, 1);
+      num++;
+    }
+  return num;
+}
+
+void 
+Heap_Dump (Heap * H, int num)
+{
+  int i, l, lines, r;
+  if (num > H->NumItems)
+    num = H->NumItems;
+  lines = (num + 3) / 4;
+  for (l = 0; l < lines; l++)
+    for (r = 0; r < 4; r++)
+      {
+	i = lines * r + l;
+	if (i < num)
+	  fprintf (stderr, "[%2d] %7.4f", i, H->HE[i].Weight);
+	fprintf (stderr, r == 3 ? "\n" : "        ");
+      }
+  fprintf (stderr, "\n");
+}
+
+/*************************************************************************/
+
+
+
+
+
+
+int 
+doc_count_comp (const void *A, const void *B)
+{
+  const TermEntry *a = A;
+  const TermEntry *b = B;
+  return (a->WE.doc_count - b->WE.doc_count);
+}
+
+/* =========================================================================
+ * Function: ParseRankedQuery
+ * Description:
+ *      Takes a string query line and extracts the terms in the query
+ *      which exist in the stemmed dictionary.
+ *      Optionally sorts the terms into ascending order by doc. number.
+ * Input:
+ *      stemmed dictionary, query line, sort-flag
+ * Output:
+ *      A list of terms.
+ * ========================================================================= */
+
+/* [RPAP - Jan 97: Stem Index Change] */
+static TermList *
+ParseRankedQuery (stemmed_dict * sd, char *QueryLine, int Sort, int indexed,
+		  QueryTermList **query_term_list)  /* [RPAP - Feb 97: Term Frequency] */
+{
+  u_char Word[MAXSTEMLEN + 1];
+  u_char sWord[MAXSTEMLEN + 1];
+  u_char *end, *s_in;
+  int default_stem_method = 0;
+  TermList *Terms = MakeTermList(0);
+
+  s_in = (u_char *) QueryLine;
+  end = s_in + strlen ((char *) s_in) - 1;
+  *query_term_list = MakeQueryTermList(0);  /* [RPAP - Feb 97: Term Frequency] */
+
+  if (indexed)
+    default_stem_method = BooleanEnv (GetEnv ("casefold"), 0) | (BooleanEnv (GetEnv ("stem"), 0) << 1);
+  else
+    default_stem_method = sd->sdh.stem_method;
+
+  while (s_in <= end)
+    {
+      int j;
+      long num_entries, word_num;
+      unsigned long count, doc_count, invf_ptr, invf_len;
+      int weight_to_apply, stem_to_apply;
+      int method_using = -1;
+
+      /* 0=optional, 1=mustmatch */
+      int require_match = 0; /* [RJM 07/97: Ranked Required Terms] */
+
+      /* Skip over the non word separator taking note of any parameters */
+      PARSE_RANKED_NON_STEM_WORD (require_match, s_in, end); /* [RJM 07/97: Ranked Required Terms] */
+      if (s_in > end) break;
+
+      /* Get a word and stem it */
+      PARSE_STEM_WORD (Word, s_in, end);
+
+      /* Extract any parameters */
+      weight_to_apply = 1;
+      stem_to_apply = default_stem_method;
+      while (s_in <= end)
+	{
+	  int stem_param, weight_param, param_type;
+	  char param[MAXPARAMLEN + 1];
+
+	  param_type = 0;
+	  PARSE_OPT_TERM_PARAM (param, param_type, s_in, end);
+	  if (!param_type)
+	    break;
+
+	  switch (param_type)
+	    {
+	    case (WEIGHTPARAM):
+	      weight_param = atoi (param);
+	      if (errno != ERANGE && weight_param > 0)
+		weight_to_apply = weight_param;
+	      break;
+
+	    case (STEMPARAM):
+	      stem_param = atoi (param);
+	      if (errno != ERANGE && indexed && stem_param >= 0 && stem_param <= 3)
+		method_using = stem_to_apply = stem_param;
+	      break;
+	    }
+	}
+
+      bcopy ((char *) Word, (char *) sWord, *Word + 1);
+      stemmer (stem_to_apply, sd->sdh.stemmer_num, sWord);
+
+      if (!indexed || stem_to_apply == 0)
+	{
+	  /* Look for the word in the already identified terms */
+	  for (j = 0; j < Terms->num; j++)
+	    if (compare (Terms->TE[j].Word, Word) == 0)
+	      break;
+
+	  /* Increment the weight if the word is in the list */
+	  /* Update the require match attribute */
+	  if (j < Terms->num)
+	    {
+	      Terms->TE[j].Count = ((Terms->TE[j].Count + weight_to_apply > INT_MAX) ?
+				    INT_MAX : (Terms->TE[j].Count + weight_to_apply));
+	      Terms->TE[j].require_match = require_match; /* [RJM 07/97: Ranked Require match] */
+	      AddQueryTerm (query_term_list, Word, Terms->TE[j].WE.count, method_using);  /* [RPAP - Feb 97: Term Frequency] */
+	    }
+	  else
+	    {
+	      /* Look for it in the stemmed dictionary */
+	      if ((word_num = FindWord (sd, sWord, &count, &doc_count,
+					&invf_ptr, &invf_len)) != -1)
+		{
+		  /* Search the list for the word */
+		  for (j = 0; j < Terms->num; j++)
+		    if (Terms->TE[j].WE.word_num == word_num)
+		      break;
+
+		  /* Increment the weight if the word is in the list */
+		  if (j < Terms->num)
+		    {
+		      Terms->TE[j].Count = ((Terms->TE[j].Count + weight_to_apply > INT_MAX) ?
+					    INT_MAX : (Terms->TE[j].Count + weight_to_apply));
+		      Terms->TE[j].require_match = require_match; /* [RJM 07/97: Ranked Require match] */
+		      AddQueryTerm (query_term_list, Word, Terms->TE[j].WE.count, method_using);  /* [RPAP - Feb 97: Term Frequency] */
+		    }
+		  else
+		    {
+		      /* Create a new entry in the list for the new word */
+		      TermEntry te;
+
+		      te.WE.word_num = word_num;
+		      te.WE.count = count;
+		      te.WE.doc_count = doc_count;
+		      te.WE.max_doc_count = doc_count;
+		      te.WE.invf_ptr = invf_ptr;
+		      te.WE.invf_len = invf_len;
+		      te.Count = weight_to_apply;
+		      te.Word = copy_string (Word);
+		      if (!te.Word)
+			FatalError (1, "Could NOT create memory to add term");
+		      te.Stem = NULL;
+		      te.require_match = require_match;
+
+		      AddTermEntry (&Terms, &te);
+
+		      /* [RPAP - Feb 97: Term Frequency] */
+		      AddQueryTerm (query_term_list, Word, count, method_using);
+		    }
+		}
+	      /* [RPAP - Feb 97: Term Frequency] */
+	      else
+		AddQueryTerm (query_term_list, Word, 0, method_using);
+	    }
+	}
+      else
+	{
+	  int total_count = 0;  /* [RPAP - Feb 97: Term Frequency] */
+	  TermList *tempList = MakeTermList (0);
+	  if ((num_entries = FindWords (sd, sWord, stem_to_apply, &tempList)) > 0)
+	    {
+	      int i;
+	      u_long max_doc_count = 0;
+
+	      /* get the maximum doc count */
+	      for (i = 0; i < tempList->num; i++)
+		{
+		  if (tempList->TE[i].WE.doc_count > max_doc_count)
+		    max_doc_count = tempList->TE[i].WE.doc_count;
+		  total_count += tempList->TE[i].WE.count;  /* [RPAP - Feb 97: Term Frequency] */
+		}
+
+	      for (i = 0; i < tempList->num; i++)
+		{
+		  /* Look for the word(s) in the already identified terms */
+		  for (j = 0; j < Terms->num; j++) 
+		    {
+		      if (compare (Terms->TE[j].Word, tempList->TE[i].Word) == 0)
+			{
+			  /* found the word */
+			  /* Modify weight */
+			  Terms->TE[j].Count = ((Terms->TE[j].Count + weight_to_apply > INT_MAX) ?
+						INT_MAX : (Terms->TE[j].Count + weight_to_apply));
+			  if (Terms->TE[j].WE.max_doc_count < max_doc_count)
+			    Terms->TE[j].WE.max_doc_count = max_doc_count;
+			  break;
+			}
+		    }
+
+		  if (j == Terms->num)
+		    {
+		      /* word was not found */
+		      tempList->TE[i].WE.max_doc_count = max_doc_count;
+		      tempList->TE[i].Count = weight_to_apply;
+
+		      /* We cannot require a term to match if it is expanded */
+		      /* into multiple terms :-( */
+		      tempList->TE[i].require_match = 0; /* [RJM 07/97: Ranked Required Terms] */
+
+		      AddTermEntry (&Terms, &tempList->TE[i]);
+		    }
+		}
+	    }
+	  /* [RPAP - Feb 97: Term Frequency] */
+	  AddQueryTerm (query_term_list, Word, total_count, method_using);
+
+	  if (tempList != NULL) Xfree(tempList); /* [RJM 07/98: Memory Leak] */
+	} /* end indexed */
+    } /* end while */
+
+  if (Sort)
+    /* Sort the terms in ascending order by doc_count */
+    qsort (Terms->TE, Terms->num, sizeof (TermEntry), doc_count_comp);
+  return (Terms);
+}
+
+
+
+
+static int 
+DE_comp (void *a, void *b)
+{
+  return ((DocEntry *) a)->SeekPos - ((DocEntry *) b)->SeekPos;
+}
+
+
+
+
+
+/*
+ * This function is given a list of term numbers and it returns a list
+ * of document numbers based on the cosine document weighting system.
+ * This puts the entries in an array.
+ * inverted file.
+ * If MaxDocs == -1 then it means all 
+ */
+static DocList *
+CosineGet (query_data * qd, TermList * Terms, RankedQueryInfo * rqi) {
+  DocList *Docs;
+  float *AccumulatedWeights = NULL;
+  Splay_Tree *ST = NULL;
+  Splay_Tree *Para_ST = NULL;
+  Hash_Table *HT = NULL;
+  List_Table *LT = NULL;
+  Heap *H;
+  HeapEntry *he;
+  register float *fptr = NULL;
+  register Invf_Doc_Entry *ide = NULL;
+  register Invf_Doc_EntryH *ideh = NULL;
+  int BackEnd, NumExact, MaxExact, NumParas;
+  int MaxDocs = 0, MaxParas = 0;
+  int i;
+  Invf_Doc_Entry_Pool ide_pool;
+  ide_pool.pool = NULL;
+
+  qd->hops_taken = qd->num_of_ptrs = qd->num_of_accum = 0;
+
+  switch (rqi->AccumMethod)
+    {
+    case 'S':
+      ST = CosineDecodeSplay (qd, Terms, rqi, &ide_pool);
+      if (!ST)
+	return NULL;
+      break;
+    case 'A':
+      AccumulatedWeights = CosineDecode (qd, Terms, rqi);
+      if (!AccumulatedWeights)
+	return NULL;
+      break;
+    case 'H':
+      HT = CosineDecodeHash (qd, Terms, rqi);
+      if (!HT)
+	return NULL;
+      break;
+    case 'L':
+      LT = CosineDecodeList (qd, Terms, rqi);
+      if (!LT)
+	return NULL;
+      break;
+    }
+
+#if 0
+  if (rqi->UseSplayTree)
+    {
+
+      AccumulatedWeights = CosineDecode (qd, Terms, rqi);
+      fptr = AccumulatedWeights;
+      ide = SP_get_first (ST);
+      for (i = 0; i < qd->sd->sdh.num_of_docs; i++)
+	{
+	  if (AccumulatedWeights[i] != 0)
+	    {
+	      if (i != ide->DocNum)
+		fprintf (stderr, "Sum mismatch for %d %f %d %f\n", i + 1,
+			 AccumulatedWeights[i], ide->DocNum + 1, ide->Sum);
+	      ide = SP_get_next (ST);
+	    }
+	}
+    }
+#endif
+
+  switch (rqi->AccumMethod)
+    {
+    case 'S':
+      MaxParas = ST->no_of_items;
+      break;
+    case 'A':
+      {				/* count the number of non-zero document weights */
+	register int i = qd->sd->sdh.num_of_docs;
+	register float *d;
+	MaxParas = 0;
+	for (d = AccumulatedWeights; i; i--, d++)
+	  if (*d)
+	    MaxParas++;
+      }
+      break;
+    case 'H':
+      MaxParas = HT->num + HT->Suplimentary_Num;
+      break;
+    case 'L':
+      MaxParas = LT->num;
+      break;
+    }
+
+  if (rqi->MaxParasToRetrieve != -1 && MaxParas > rqi->MaxParasToRetrieve)
+    MaxParas = rqi->MaxParasToRetrieve;
+  MaxDocs = MaxParas;
+
+  /* Allocate memory for the heap */
+  Docs = MakeDocList (MaxDocs);
+  ChangeMemInUse (qd, sizeof (DocEntry) * MaxDocs);
+
+  H = Heap_Make (MaxDocs, Heap_Lesser);
+
+
+  /* Get the sums from the array divide the sums by the 
+     document weights which we retrieve from the ".idx.wgt" file and put
+     the resulting data into a heap */
+
+
+  he = H->HE;
+  H->NumItems = MaxDocs;
+  switch (rqi->AccumMethod)
+    {
+    case 'S':
+      {
+	ide = SP_get_first (ST);
+	for (i = 0; i < H->NumItems; i++, ide = SP_get_next (ST), he++)
+	  {
+	    he->DocNum = ide->DocNum + 1;
+	    he->OrgWeight = &ide->Sum;
+	    qd->num_of_accum++;
+	  }
+      }
+      break;
+    case 'A':
+      {
+	fptr = AccumulatedWeights;
+	for (i = 0; i < H->NumItems; i++, fptr++, he++)
+	  {
+	    he->DocNum = i + 1;
+	    he->OrgWeight = fptr;
+	    if (*fptr)
+	      qd->num_of_accum++;
+	  }
+      }
+      break;
+    case 'H':
+      {
+	ideh = HT->Head;
+	for (i = 0; i < H->NumItems; i++, ideh = ideh->next, he++)
+	  {
+	    he->DocNum = ideh->IDE.DocNum + 1;
+	    he->OrgWeight = &ideh->IDE.Sum;
+	    qd->num_of_accum++;
+	  }
+      }
+      break;
+    case 'L':
+      {
+	ide = LT->IDE;
+	for (i = 0; i < H->NumItems; i++, ide++, he++)
+	  {
+	    he->DocNum = ide->DocNum + 1;
+	    he->OrgWeight = &ide->Sum;
+	    qd->num_of_accum++;
+	  }
+      }
+      break;
+    }
+
+  he = H->HE;
+  for (i = 0; i < H->NumItems; i++, he++)
+    {
+      *he->OrgWeight /= GetLowerApproxDocWeight (qd->awd, he->DocNum - 1);
+      he->Weight = *he->OrgWeight;
+      *he->OrgWeight = 0;
+      he->SeekPos = he->Len = 0;
+    }
+
+  Heap_Build (H);
+
+  he = H->HE;
+  switch (rqi->AccumMethod)
+    {
+    case 'S':
+      {
+	for (i = MaxDocs; i < ST->no_of_items; i++, ide = SP_get_next (ST))
+	  {
+	    ide->Sum /= GetLowerApproxDocWeight (qd->awd, ide->DocNum);
+	    qd->num_of_accum++;
+	    if (ide->Sum <= he->Weight)
+	      continue;
+	    *he->OrgWeight = he->Weight;
+	    he->DocNum = ide->DocNum + 1;
+	    he->Weight = ide->Sum;
+	    he->OrgWeight = &ide->Sum;
+	    *he->OrgWeight = 0;
+	    Heap_Heapify (H, 1);
+	  }
+      }
+      break;
+    case 'A':
+      {
+	for (i = MaxDocs; i < qd->sd->sdh.num_of_docs; i++, fptr++)
+	  {
+	    if (!*fptr)
+	      continue;
+	    qd->num_of_accum++;
+	    *fptr /= GetLowerApproxDocWeight (qd->awd, i);
+	    if (*fptr <= he->Weight)
+	      continue;
+	    *he->OrgWeight = he->Weight;
+	    he->DocNum = i + 1;
+	    he->Weight = *fptr;
+	    he->OrgWeight = fptr;
+	    *he->OrgWeight = 0;
+	    Heap_Heapify (H, 1);
+	  }
+      }
+      break;
+    case 'H':
+      {
+	for (; ideh; ideh = ideh->next)
+	  {
+	    qd->num_of_accum++;
+	    ideh->IDE.Sum /=
+	      GetLowerApproxDocWeight (qd->awd, ideh->IDE.DocNum);
+	    if (ideh->IDE.Sum <= he->Weight)
+	      continue;
+	    *he->OrgWeight = he->Weight;
+	    he->DocNum = ideh->IDE.DocNum + 1;
+	    he->Weight = ideh->IDE.Sum;
+	    he->OrgWeight = &ideh->IDE.Sum;
+	    *he->OrgWeight = 0;
+	    Heap_Heapify (H, 1);
+	  }
+      }
+      break;
+    case 'L':
+      {
+	for (i = MaxDocs; i < LT->num; i++, ide++)
+	  {
+	    qd->num_of_accum++;
+	    ide->Sum /=
+	      GetLowerApproxDocWeight (qd->awd, ide->DocNum);
+	    if (ide->Sum <= he->Weight)
+	      continue;
+	    *he->OrgWeight = he->Weight;
+	    he->DocNum = ide->DocNum + 1;
+	    he->Weight = ide->Sum;
+	    he->OrgWeight = &ide->Sum;
+	    *he->OrgWeight = 0;
+	    Heap_Heapify (H, 1);
+	  }
+      }
+      break;
+    }
+
+
+  if (rqi->Exact && qd->id->ifh.InvfLevel != 3)
+    {
+      HeapEntry *he = H->HE;
+
+      for (i = 0; i < H->NumItems; i++, he++)
+	{
+	  he->Weight = he->Weight *
+	    GetLowerApproxDocWeight (qd->awd, he->DocNum - 1) /
+	    FetchDocStart (qd, he->DocNum, &he->SeekPos, &he->Len);
+	}
+
+      Heap_Build (H);
+
+      he = H->HE;
+
+      switch (rqi->AccumMethod)
+	{
+	case 'S':
+	  {
+	    ide = SP_get_first (ST);
+	    for (i = 0; i < ST->no_of_items; i++, ide = SP_get_next (ST))
+	      {
+		u_long SeekPos, Len;
+		float Weight;
+		if (!ide->Sum)
+		  continue;
+		if (ide->Sum <= he->Weight)
+		  continue;
+		Weight = ide->Sum *
+		  GetLowerApproxDocWeight (qd->awd, ide->DocNum) /
+		  FetchDocStart (qd, ide->DocNum + 1, &SeekPos, &Len);
+		if (Weight <= he->Weight)
+		  continue;
+		he->DocNum = ide->DocNum + 1;
+		he->OrgWeight = &ide->Sum;
+		he->Weight = Weight;
+		he->SeekPos = SeekPos;
+		he->Len = Len;
+		ide->Sum = 0;
+		Heap_Heapify (H, 1);
+	      }
+	  }
+	  break;
+
+	  /* up to here */
+
+	case 'A':
+	  {
+	    fptr = AccumulatedWeights;
+	    for (i = 0; i < qd->sd->sdh.num_of_docs; i++, fptr++)
+	      {
+		u_long SeekPos, Len;
+		float Weight;
+		if (!*fptr)
+		  continue;
+		if (*fptr <= he->Weight)
+		  continue;
+		Weight = *fptr *
+		  GetLowerApproxDocWeight (qd->awd, i) /
+		  FetchDocStart (qd, i + 1, &SeekPos, &Len);
+		if (Weight <= he->Weight)
+		  continue;
+		he->DocNum = i + 1;
+		he->OrgWeight = fptr;
+		he->Weight = Weight;
+		he->SeekPos = SeekPos;
+		he->Len = Len;
+		*fptr = 0;
+		Heap_Heapify (H, 1);
+	      }
+	  }
+	  break;
+	case 'H':
+	  {
+	    ideh = HT->Head;
+	    for (ideh = HT->Head; ideh; ideh = ideh->next)
+	      {
+		u_long SeekPos, Len;
+		float Weight;
+		if (!ideh->IDE.Sum)
+		  continue;
+		if (ideh->IDE.Sum <= he->Weight)
+		  continue;
+		Weight = ideh->IDE.Sum *
+		  GetLowerApproxDocWeight (qd->awd, ideh->IDE.DocNum) /
+		  FetchDocStart (qd, ideh->IDE.DocNum + 1, &SeekPos, &Len);
+		if (Weight <= he->Weight)
+		  continue;
+		he->DocNum = ideh->IDE.DocNum + 1;
+		he->OrgWeight = &ideh->IDE.Sum;
+		he->Weight = Weight;
+		he->SeekPos = SeekPos;
+		he->Len = Len;
+		ideh->IDE.Sum = 0;
+		Heap_Heapify (H, 1);
+	      }
+	  }
+	  break;
+	case 'L':
+	  {
+	    ide = LT->IDE;
+	    for (i = 0; i < LT->num; i++, ide++)
+	      {
+		u_long SeekPos, Len;
+		float Weight;
+		if (!ide->Sum)
+		  continue;
+		if (ide->Sum <= he->Weight)
+		  continue;
+		Weight = ide->Sum *
+		  GetLowerApproxDocWeight (qd->awd, ide->DocNum) /
+		  FetchDocStart (qd, ide->DocNum + 1, &SeekPos, &Len);
+		if (Weight <= he->Weight)
+		  continue;
+		he->DocNum = ide->DocNum + 1;
+		he->OrgWeight = &ide->Sum;
+		he->Weight = Weight;
+		he->SeekPos = SeekPos;
+		he->Len = Len;
+		ide->Sum = 0;
+		Heap_Heapify (H, 1);
+	      }
+	  }
+	  break;
+	}
+    }
+
+
+
+  H->HC = Heap_Greater;
+  Heap_Build (H);
+
+
+  MaxDocs = H->NumItems;
+  if (rqi->MaxDocsToRetrieve != -1 && MaxDocs > rqi->MaxDocsToRetrieve)
+    MaxDocs = rqi->MaxDocsToRetrieve;
+
+  /* Alarm */
+
+  he = H->HE;
+  BackEnd = H->NumItems - 1;
+  NumExact = 0;
+  MaxExact = H->NumItems;
+  NumParas = 0;
+  Para_ST = SP_createset (DE_comp);
+  while (H->NumItems && Docs->num < MaxDocs)
+    {
+      DocEntry DocEnt;
+      DocEntry *mem;
+
+      if (rqi->Exact)
+	{
+	  if (H->HE[0].SeekPos == 0)
+	    NumExact += Make_Exact_Root (qd, H);
+	}
+      else
+	FetchDocStart (qd, he->DocNum, &he->SeekPos, &he->Len);
+
+      NumParas++;
+
+      DocEnt.DocNum = he->DocNum;
+      DocEnt.Weight = he->Weight;
+      DocEnt.Len = he->Len;
+      DocEnt.SeekPos = he->SeekPos;
+      DocEnt.CompTextBuffer = NULL;
+      DocEnt.Next = NULL;
+
+      Heap_DeleteHead (H);
+
+      if (!(mem = SP_member (&DocEnt, Para_ST)))
+	{
+	  Docs->DE[Docs->num] = DocEnt;
+	  SP_insert (&Docs->DE[Docs->num], Para_ST);
+	  Docs->num++;
+	}
+      else
+	{
+	  DocEnt.Next = mem->Next;
+	  Docs->DE[BackEnd] = DocEnt;
+	  mem->Next = &Docs->DE[BackEnd--];
+	}
+    }
+  SP_freeset (Para_ST);
+
+  if (qd->id->ifh.InvfLevel == 3)
+    {
+      Message ("%d Paragraphs were required to get %d documents",
+	       NumParas, Docs->num);
+      if (NumExact == MaxExact)
+	{
+	  Message ("The exact weights of all %d paragraphs had to be calculated", MaxExact);
+	  Message ("to obtain %d paragraphs. This may mean that the the documents", NumParas);
+	  Message ("returned do not necessarly represent an exact cosine ranking.");
+	  Message ("This problem may be corrected by increasing \'maxparas\'.");
+	}
+    }
+#if 0
+  {
+    int i;
+    FILE *f = fopen ("top.paras", "w");
+    fprintf (f, "=========================\nTop Paragraphs\n");
+    for (i = 0; i < Docs->num; i++)
+      {
+	DocEntry *d;
+	fprintf (f, "<%d(%f)>  ", Heap[i].DocNum, Heap[i].Weight);
+	for (d = Heap[i].Next; d; d = d->Next)
+	  fprintf (f, "%d(%f)  ", d->DocNum, d->Weight);
+	fprintf (f, "\n");
+      }
+    fprintf (f, "=========================\n");
+    fclose (f);
+  }
+#endif
+
+  if (AccumulatedWeights)
+    {
+      Xfree (AccumulatedWeights);
+      ChangeMemInUse (qd, -sizeof (float) * qd->sd->sdh.num_of_docs);
+    }
+  if (ST)
+    {
+      int mem = ST->mem_in_use;
+      SP_freeset (ST);
+      ChangeMemInUse (qd, -mem);
+      free_ide_pool (qd, &ide_pool);
+    }
+  if (HT)
+    HT_free (qd, HT);
+
+  if (LT)
+    LT_free (qd, LT);
+
+  if (H) Xfree (H); /* [RJM 07/98: Memory Leak] */
+
+  return (Docs);
+}
+
+
+
+
+/* if MaxDocs == -1 it means all */
+void 
+RankedQuery (query_data *qd, char *Query, RankedQueryInfo *rqi)
+{
+  DocList *dl;
+
+  if (qd->TL)
+    FreeTermList (&(qd->TL));
+
+  /* [RPAP - Feb 97: Term Frequency] */
+  if (qd->QTL)
+    FreeQueryTermList (&(qd->QTL));
+
+  qd->TL = ParseRankedQuery (qd->sd, Query, rqi->Sort, qd->sd->sdh.indexed,  /* [RPAP - Jan 97: Stem Index Change] */
+			     &(qd->QTL));  /* [RPAP - Feb 97: Term Frequency] */
+
+  /*  PrintTermList (qd->TL, stderr); */
+
+  dl = CosineGet (qd, qd->TL, rqi);
+
+  if (!dl)
+    FatalError (1, "Out of memory\n");
+
+  FreeQueryDocs (qd);
+
+  qd->DL = dl;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.c	(revision 16561)
@@ -0,0 +1,228 @@
+/**************************************************************************
+ *
+ * filename -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:21  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:35:36  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:57:07  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+ */
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "local_strings.h"
+#include "query_term_list.h"
+#include "term_lists.h"  /* for MAXTERMSTRLEN */
+#include "messages.h"
+
+
+/* =========================================================================
+ * Function: MakeQueryTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+QueryTermList *
+MakeQueryTermList (int n)
+{
+  QueryTermList *t;
+  int list_size = (n == 0 ? 1 : n);	/* always allocate at least one node */
+
+  t = Xmalloc (sizeof (QueryTermList) + (list_size - 1) * sizeof (QueryTermEntry));
+  if (!t)
+    FatalError (1, "Unable to allocate query term list");
+
+  t->num = n;
+  t->list_size = list_size;
+
+  return t;
+}
+
+/* =========================================================================
+ * Function: ResizeQueryTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+#define GROWTH_FACTOR 2
+#define MIN_SIZE 2
+
+static void
+ResizeQueryTermList (QueryTermList ** query_list)
+{
+  QueryTermList *qtl = *query_list;
+
+  if (qtl->num > qtl->list_size)
+    {
+      if (qtl->list_size)
+	qtl->list_size *= GROWTH_FACTOR;
+      else
+	qtl->list_size = MIN_SIZE;
+    }
+  qtl = Xrealloc (qtl, sizeof (QueryTermList) + (qtl->list_size - 1) * sizeof (QueryTermEntry));
+
+  if (!qtl)
+    FatalError (1, "Unable to resize query term list");
+
+  *query_list = qtl;
+}
+
+/* =========================================================================
+ * Function: ConvertQueryTermsToString
+ * Description:
+ *      Convert term list into null-terminated string
+ * Input:
+ *      query_term_list = query list
+ * Output:
+ *      str = term string
+ * ========================================================================= */
+
+void
+ConvertQueryTermsToString (QueryTermList * query_term_list, char *str)
+{
+  int i = 0;
+  int total_len = 0;
+
+  /* terms_str should be preallocated */
+  if (!str)
+    return;
+
+  for (i = 0; i < query_term_list->num; i++)
+    {
+      unsigned char *word = query_term_list->QTE[i].Term;
+      int len = word[0];
+      total_len += len + 1;	/* +1 for space */
+      if (query_term_list->QTE[i].stem_method >= 0)
+	total_len += 2;         /* '#' and the stem number */
+      if (total_len > MAXTERMSTRLEN)
+	break;
+      strncpy (str, (char *) word + 1, len);
+      str += len;
+      if (query_term_list->QTE[i].stem_method >= 0)
+	{
+	  *str++ = '#';
+	  *str++ = '0' + query_term_list->QTE[i].stem_method;
+	}
+      if (i != (query_term_list->num) - 1)
+	{
+	  *str = ' ';
+	  str++;		/* add space gap */
+	}
+
+    }
+  *str = '\0';
+}
+
+/* =========================================================================
+ * Function: ResetTermList
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+ResetQueryTermList (QueryTermList ** qtl)
+{
+  if (*qtl)
+    FreeQueryTermList (qtl);
+  *qtl = MakeQueryTermList (0);
+}
+
+/* =========================================================================
+ * Function: AddQueryTermEntry
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+AddQueryTermEntry (QueryTermList ** query_term_list, QueryTermEntry * qte)
+{
+  QueryTermList *qtl = *query_term_list;
+
+  qtl->num++;
+  ResizeQueryTermList (query_term_list);
+  qtl = *query_term_list;
+
+  /* copy the structure contents */
+  bcopy ((char *) qte, (char *) &(qtl->QTE[qtl->num - 1]), sizeof (QueryTermEntry));
+
+  return qtl->num - 1;
+}
+
+
+
+/* =========================================================================
+ * Function: AddQueryTerm
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+AddQueryTerm (QueryTermList ** query_term_list, u_char * Term, int Count, int stem_method)
+{
+  /* Create a new entry in the list for the new word */
+  QueryTermEntry qte;
+
+  qte.Count = Count;
+  qte.stem_method = stem_method;
+  qte.Term = copy_string (Term);
+  if (!qte.Term)
+    FatalError (1, "Could NOT create memory to add term");
+
+  return AddQueryTermEntry (query_term_list, &qte);
+}
+
+/* =========================================================================
+ * Function: FreeQueryTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+void
+FreeQueryTermList (QueryTermList ** the_qtl)
+{
+  int j;
+  QueryTermList *qtl = *the_qtl;
+
+  for (j = 0; j < qtl->num; j++)
+    if (qtl->QTE[j].Term)
+      Xfree (qtl->QTE[j].Term);
+  Xfree (qtl);
+
+  *the_qtl = NULL;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/query_term_list.h	(revision 16561)
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * term_lists.h -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:21  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:35:37  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:57:08  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+ */
+
+#ifndef QUERY_TERM_LIST_H
+#define QUERY_TERM_LIST_H
+
+#include "sysfuncs.h"
+
+typedef struct QueryTermEntry
+  {
+    int Count;			/* The number of times the word occurs in the query */
+    u_char *Term;		/* The query term */
+    int stem_method;            /* The steming method to apply to the term : -1 is default */
+  }
+QueryTermEntry;
+
+typedef struct QueryTermList
+  {
+    int list_size;
+    int num;
+    QueryTermEntry QTE[1];
+  }
+QueryTermList;
+
+/* --- prototypes --- */
+void ConvertQueryTermsToString (QueryTermList * query_term_list, char *str);
+int AddQueryTerm (QueryTermList ** query_term_list, u_char * Term, int Count, int stem_method);
+void ResetQueryTermList (QueryTermList ** tl);
+void FreeQueryTermList (QueryTermList ** the_qtl);
+QueryTermList *MakeQueryTermList (int n);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.c	(revision 16561)
@@ -0,0 +1,214 @@
+/**************************************************************************
+ *
+ * read_line.c -- Input line reading routines for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+#ifdef GNU_READLINE
+#include "readline.h"
+#include "chardefs.h"
+#include "history.h"
+#endif
+
+
+#include "globals.h"
+#include "environment.h"
+#include "read_line.h"
+
+
+
+#ifdef GNU_READLINE
+int rl_bind_key (int, int (*)());
+
+
+void 
+Init_ReadLine (void)
+{
+  /* Make TAB just insert itself rather than do a file name completion */
+  rl_bind_key (TAB, rl_insert);
+}
+#else
+
+static FILE *rl_instream = NULL;
+static FILE *rl_outstream = NULL;
+
+void 
+Init_ReadLine (void)
+{
+  rl_instream = stdin;
+  rl_outstream = stdout;
+}
+
+static char *
+readline (char *pmt)
+{
+  static char buf[MAXLINEBUFFERLEN + 1];
+  char *s;
+
+  fprintf (rl_outstream, "%s", pmt);
+  s = fgets (buf, sizeof (buf), rl_instream);
+  if (s)
+    {
+      char *s1 = strrchr (s, '\n');
+      if (s1 && *(s1 + 1) == '\0')
+	*s1 = '\0';
+    }
+  return s ? Xstrdup (s) : NULL;
+}
+#endif
+
+/* WritePrompt() */
+/* Write out a prompt if user is a TTY */
+void 
+WritePrompt (void)
+{
+  if (isatty (fileno (InFile)))
+    {
+      if (!BooleanEnv (GetEnv ("expert"), 0))
+	fprintf (stderr, "Enter a command or query (.quit to terminate, .help for assistance).\n");
+    }
+}
+
+#ifdef GNU_READLINE
+static void memory_error_and_abort ();
+
+char *
+xmalloc (bytes)
+     int bytes;
+{
+  char *temp = (char *) Xmalloc (bytes);
+
+  if (!temp)
+    memory_error_and_abort ();
+  return (temp);
+}
+
+char *
+xrealloc (pointer, bytes)
+     char *pointer;
+     int bytes;
+{
+  char *temp;
+
+  if (!pointer)
+    temp = (char *) xmalloc (bytes);
+  else
+    temp = (char *) Xrealloc (pointer, bytes);
+
+  if (!temp)
+    memory_error_and_abort ();
+
+  return (temp);
+}
+
+static void
+memory_error_and_abort ()
+{
+  fprintf (stderr, "history: Out of virtual memory!\n");
+  abort ();
+}
+#endif
+
+
+/*
+ * This routine returns a pointer to the users entered line 
+ *
+ */
+static char *
+GetLine (char *pmt)
+{
+  static char *the_line = NULL;
+  if (the_line)
+    Xfree (the_line);
+  the_line = NULL;
+  rl_instream = InFile;
+  if (isatty (fileno (InFile)))
+    {
+      fputc ('\r', stderr);
+      if (!isatty (fileno (OutFile)))
+	rl_outstream = stderr;
+      else
+	rl_outstream = OutFile;
+      the_line = readline (pmt);
+    }
+  else
+    {
+      if (isatty (fileno (OutFile)))
+	{
+	  the_line = readline (pmt);
+	  fprintf (stderr, "%s\n", the_line ? the_line : "");
+	}
+      else
+	{
+	  the_line = readline ("");
+	  if (the_line)
+	    fprintf (stderr, "%s%s\n", pmt, the_line);
+	}
+    }
+#ifdef GNU_READLINE
+  if (the_line && *the_line)
+    add_history (the_line);
+#endif
+  return (the_line);
+}
+
+
+
+
+
+
+char *
+GetMultiLine (void)
+{
+  static char *line = NULL;
+  char *s;
+  if (line)
+    Xfree (line);
+  line = NULL;
+  if (!(s = GetLine ("> ")))
+    return (NULL);
+  if (!(line = Xstrdup (s)))
+    {
+      fprintf (stderr, "Unable to allocate memory for the line\n");
+      abort ();
+    }
+
+  while ((s = strrchr (line, '\\')) && *(s + 1) == '\0')
+    {
+      char *new;
+      *strrchr (line, '\\') = '\0';
+      if (!(s = GetLine ("? ")))
+	return (NULL);
+      if (!(new = Xmalloc (strlen (line) + strlen (s) + 2)))
+	{
+	  fprintf (stderr, "Unable to allocate memory for the line\n");
+	  abort ();
+	}
+      strcpy (new, line);
+      strcat (new, "\n");
+      strcat (new, s);
+      Xfree (line);
+      line = new;
+    }
+  return (line);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/read_line.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**************************************************************************
+ *
+ * read_line.h -- Input line reading routines for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 READ_LINE_H
+#define READ_LINE_H
+
+#define MAXLINEBUFFERLEN  65535
+
+void Init_ReadLine (void);
+void WritePrompt (void);
+char *GetMultiLine (void);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.c	(revision 16561)
@@ -0,0 +1,775 @@
+/**************************************************************************
+ *
+ * stem_search.c -- Functions for searching the blocked stemmed dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "filestats.h"
+#include "timing.h"
+#include "local_strings.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "words.h"
+#include "locallib.h"
+#include "stem_search.h"
+#include "mg_errors.h"
+#include "term_lists.h"
+#include "stemmer.h"
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:22  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1999/07/02 00:18:55  rjmcnab
+   Changed so FindWords could be used in new ways.
+
+   Revision 1.2  1998/11/25 07:55:51  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:39  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:57:04  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:42:08  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+stemmed_dict *
+ReadStemDictBlk (File * stem_file)
+{
+  unsigned long i;
+  stemmed_dict *sd;
+  u_char *buffer;
+
+  if (!(sd = Xmalloc (sizeof (stemmed_dict))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  sd->stem_file = stem_file;
+  sd->MemForStemDict = 0;
+
+  Fread (&sd->sdh, sizeof (sd->sdh), 1, stem_file);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(sd->sdh.lookback);
+  NTOHUL(sd->sdh.block_size);
+  NTOHUL(sd->sdh.num_blocks);
+  NTOHUL(sd->sdh.blocks_start);
+  NTOHUL(sd->sdh.index_chars);
+  NTOHUL(sd->sdh.num_of_docs);
+  NTOHUL(sd->sdh.static_num_of_docs);
+  NTOHUL(sd->sdh.num_of_words);
+  NTOHUL(sd->sdh.stem_method);
+  NTOHUL(sd->sdh.indexed);
+
+  if (!(buffer = Xmalloc (sd->sdh.index_chars)))
+    {
+      Xfree (sd);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.index_chars;
+
+  if (!(sd->index = Xmalloc (sd->sdh.num_blocks * sizeof (*sd->index))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.num_blocks * sizeof (*sd->index);
+
+  if (!(sd->pos = Xmalloc (sd->sdh.num_blocks * sizeof (*sd->pos))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      Xfree (sd->index);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.num_blocks * sizeof (*sd->pos);
+
+  if (!(sd->buffer = Xmalloc (sd->sdh.block_size * sizeof (*sd->buffer))))
+    {
+      Xfree (sd);
+      Xfree (buffer);
+      Xfree (sd->index);
+      Xfree (sd->buffer);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  sd->MemForStemDict += sd->sdh.block_size * sizeof (*sd->buffer);
+
+  sd->active = -1;
+
+  for (i = 0; i < sd->sdh.num_blocks; i++)
+    {
+      register u_char len;
+      sd->index[i] = buffer;
+      len = Getc (stem_file);
+      *buffer++ = len;
+      Fread (buffer, sizeof (u_char), len, stem_file);
+      buffer += len;
+      Fread (&sd->pos[i], sizeof (*sd->pos), 1, stem_file);
+      NTOHUL(sd->pos[i]); /* [RPAP - Jan 97: Endian Ordering] */
+    }
+
+  mg_errno = MG_NOERROR;
+
+  /*  fprintf (stderr, "mem for stem dict = %i\n", sd->MemForStemDict); */
+
+  return sd;
+}
+
+
+/* [RPAP - Jan 97: Stem Index Change] */
+stemmed_idx *
+ReadStemIdxBlk (File * stem_idx_file)
+{
+  unsigned long i;
+  stemmed_idx *si;
+  u_char *buffer;
+
+  if (!(si = Xmalloc (sizeof (stemmed_idx))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  si->stem_idx_file = stem_idx_file;
+  si->MemForStemIdx = 0;
+
+  Fread (&si->sih, sizeof (si->sih), 1, stem_idx_file);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(si->sih.lookback);
+  NTOHUL(si->sih.block_size);
+  NTOHUL(si->sih.num_blocks);
+  NTOHUL(si->sih.blocks_start);
+  NTOHUL(si->sih.index_chars);
+  NTOHUL(si->sih.num_of_words);
+
+  if (!(buffer = Xmalloc (si->sih.index_chars)))
+    {
+      Xfree (si);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  si->MemForStemIdx += si->sih.index_chars;
+
+  if (!(si->index = Xmalloc (si->sih.num_blocks * sizeof (*si->index))))
+    {
+      Xfree (si);
+      Xfree (buffer);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  si->MemForStemIdx += si->sih.num_blocks * sizeof (*si->index);
+
+  if (!(si->pos = Xmalloc (si->sih.num_blocks * sizeof (*si->pos))))
+    {
+      Xfree (si->index);
+      Xfree (si);
+      Xfree (buffer);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  si->MemForStemIdx += si->sih.num_blocks * sizeof (*si->pos);
+
+  if (!(si->buffer = Xmalloc (si->sih.block_size * sizeof (*si->buffer))))
+    {
+      Xfree (buffer);
+      Xfree (si->index);
+      Xfree (si->buffer);
+      Xfree (si);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    };
+  si->MemForStemIdx += si->sih.block_size * sizeof (*si->buffer);
+
+  si->active = -1;
+
+  for (i = 0; i < si->sih.num_blocks; i++)
+    {
+      register u_char len;
+      si->index[i] = buffer;
+      len = Getc (stem_idx_file);
+      *buffer++ = len;
+      Fread (buffer, sizeof (u_char), len, stem_idx_file);
+      buffer += len;
+      Fread (&si->pos[i], sizeof (*si->pos), 1, stem_idx_file);
+      NTOHUL(si->pos[i]);  /* [RPAP - Jan 97: Endian Ordering] */
+    }
+  mg_errno = MG_NOERROR;
+
+  /*  fprintf (stderr, "mem for stem idx = %i\n", si->MemForStemIdx); */
+
+  return si;
+}
+
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* word should be appropriately stemed */
+static int
+GetIdxBlock (stemmed_idx * si, u_char * word)
+{
+  register int lo = 0, hi = si->sih.num_blocks - 1;
+  register int mid = 0, c = 0;
+
+  while (lo <= hi)
+    {
+      mid = (lo + hi) / 2;
+      c = casecompare (word, si->index[mid]);
+      if (c < 0)
+	hi = mid - 1;
+      else if (c > 0)
+	lo = mid + 1;
+      else
+	return mid;
+    }
+  return hi < 0 ? 0 : (c < 0 ? mid - 1 : mid);
+}
+
+
+static int 
+GetBlock (stemmed_dict * sd, u_char * Word)
+{
+  register int lo = 0, hi = sd->sdh.num_blocks - 1;
+  register int mid = 0, c = 0;
+  while (lo <= hi)
+    {
+      mid = (lo + hi) / 2;
+      c = casecompare (Word, sd->index[mid]);  /* [RPAP - Jan 97: Stem Index Change] */
+      if (c < 0)
+	hi = mid - 1;
+      else if (c > 0)
+	lo = mid + 1;
+      else
+	return mid;
+    }
+  return hi < 0 ? 0 : (c < 0 ? mid - 1 : mid);
+}
+
+
+/*
+ * This function looks up a word in the stemmed dictionary, it returns -1
+ * if the word cound not be found, and 0 if it successfully finds the word.
+ * If count is non-null the ulong it is pointing to is set to the number of 
+ * occurances of the stemmed word in the collection. i.e wcnt.
+ * If doc_count is non-null the ulong it is pointing to is set to the number
+ * of documents that the word occurs in. i.e fcnt
+ * If invf_ptr is non-null the ulong it is pointing to is set to the position
+ * of the inverted file where the entry for this word start.
+ */
+int 
+FindWord (stemmed_dict * sd, u_char * Word, unsigned long *count,
+	  unsigned long *doc_count, unsigned long *invf_ptr,
+	  unsigned long *invf_len)
+{
+  register int lo, hi, mid, c;
+  register unsigned int res;
+  int block, num_indexes;
+  unsigned long *first_word, *last_invf_len;
+  unsigned short *num_words;
+  u_char *base;
+  unsigned short *index;
+  u_char prev[MAXSTEMLEN + 1];
+
+  block = GetBlock (sd, Word);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  if (sd->active != sd->pos[block])
+    {
+      int i;
+
+      Fseek (sd->stem_file, sd->pos[block] + sd->sdh.blocks_start, 0);
+      Fread (sd->buffer, sd->sdh.block_size, sizeof (u_char), sd->stem_file);
+      sd->active = sd->pos[block];
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      first_word = (unsigned long *) (sd->buffer);
+      NTOHUL(*first_word);
+      last_invf_len = (unsigned long *) (first_word + 1);
+      NTOHUL(*last_invf_len);
+      num_words = (unsigned short *) (last_invf_len + 1);
+      NTOHUS(*num_words);
+      index = num_words + 1;
+      num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+
+      for (i = 0; i < num_indexes; i++)
+	NTOHUS(index[i]);
+    }
+  else
+    {
+      first_word = (unsigned long *) (sd->buffer);
+      last_invf_len = (unsigned long *) (first_word + 1);
+      num_words = (unsigned short *) (last_invf_len + 1);
+      index = num_words + 1;
+      num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+    }
+  base = (u_char *) (index + num_indexes);
+
+  lo = 0;
+  hi = num_indexes - 1;
+  while (lo <= hi)
+    {
+      mid = (lo + hi) / 2;
+      c = casecompare (Word, base + index[mid] + 1);  /* [RPAP - Jan 97: Stem Index Change] */
+      if (c < 0)
+	hi = mid - 1;
+      else if (c > 0)
+	lo = mid + 1;
+      else
+	{
+	  hi = mid;
+	  break;
+	}
+    }
+  if (hi < 0)
+    hi = 0;
+
+  res = hi * sd->sdh.lookback;
+  base += index[hi];
+
+  for (;;)
+    {
+      unsigned copy, suff;
+      unsigned long invfp;
+      if (res >= *num_words)
+	return (-1);
+      copy = *base++;
+      suff = *base++;
+      bcopy ((char *) base, (char *) (prev + copy + 1), suff);
+      base += suff;
+      *prev = copy + suff;
+
+      c = casecompare (Word, prev);   /* [RPAP - Jan 97: Stem Index Change] */
+      if (c < 0)
+	return (-1);
+
+      if (c == 0 && doc_count)
+	{
+	  bcopy ((char *) base, (char *) doc_count, sizeof (*doc_count));
+	  NTOHUL(*doc_count);  /* [RPAP - Jan 97: Endian Ordering] */
+	}
+      base += sizeof (*doc_count);
+
+      if (c == 0 && count)
+	{
+	  bcopy ((char *) base, (char *) count, sizeof (*count));
+	  NTOHUL(*count);  /* [RPAP - Jan 97: Endian Ordering] */
+	}
+      base += sizeof (*count);
+
+      if (c == 0 && invf_ptr)
+	{
+	  bcopy ((char *) base, (char *) &invfp, sizeof (invf_ptr));
+	  NTOHUL(invfp);  /* [RPAP - Jan 97: Endian Ordering] */
+	  *invf_ptr = invfp;
+	}
+      base += sizeof (*invf_ptr);
+
+      if (c == 0)
+	{
+	  /* Calculate invf_len is necessary */
+	  unsigned long next_invfp;
+	  if (!invf_len)
+	    return (*first_word + res);
+
+	  /* If the current word is the last word of the block the get the 
+	     length from last_invf_len */
+	  if (res == *num_words - 1)
+	    {
+	      *invf_len = *last_invf_len;
+	      return (*first_word + res);
+	    }
+
+	  /* Skip over most of the next word to get to the invf_ptr */
+	  base++;
+	  suff = *base++;
+	  base += suff + sizeof (unsigned long) * 2;
+	  bcopy ((char *) base, (char *) &next_invfp, sizeof (next_invfp));
+	  NTOHUL(next_invfp);  /* [RPAP - Jan 97: Endian Ordering] */
+	  *invf_len = next_invfp - invfp;
+	  return (*first_word + res);
+	}
+      res++;
+    }
+}
+
+
+/* [RPAP - Jan 97: Stem Index Change] */
+int 
+FindWords (stemmed_dict * sd, u_char * sWord, int stem_method, TermList ** tl)
+{
+  register unsigned int res;
+  unsigned int idx_res;
+  unsigned copy, suff;
+  int j, k;
+
+  int block, num_indexes;
+  unsigned long *first_word, *last_invf_len;
+  unsigned short *num_words;
+  u_char *base;
+  unsigned short *index;
+  u_char prev[MAXSTEMLEN + 1];
+
+  int idx_block, idx_num_indexes;
+  unsigned long *idx_first_word;
+  unsigned short *idx_num_words;
+  u_char *idx_base;
+  unsigned short *idx_index;
+  u_char idx_prev[MAXSTEMLEN + 1];
+
+  unsigned int num_entries, num_cases;
+  unsigned short blk_index, offset;
+  stemmed_idx * si = NULL;
+
+  /* handle stem_method 0 seperately */
+  if (stem_method == 0) {
+    TermEntry te;
+    if ((te.WE.word_num = FindWord (sd, sWord, &te.WE.count, &te.WE.doc_count,
+				   &te.WE.invf_ptr, &te.WE.invf_len)) != -1) {
+      te.WE.max_doc_count = te.WE.doc_count;
+
+      te.Count = 1;
+      te.Word = copy_string (sWord);
+      if (!te.Word)
+	FatalError (1, "Could NOT create memory to add term");
+      te.Stem = copy_string (sWord);
+      if (!te.Stem)
+	FatalError (1, "Could NOT create memory to add term");
+      /*      te.query_mask = NULL;*/
+      
+      AddTermEntry (tl, &te);
+      return (*tl)->num;
+      
+    } else {
+      /* didn't match */
+      return 0;
+    }
+  }
+  
+  if (stem_method == 1)
+    si = sd->stem1;
+  else if (stem_method == 2)
+    si = sd->stem2;
+  else
+    si = sd->stem3;
+
+  /* Locate block */
+  idx_block = GetIdxBlock (si, sWord);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  if (si->active != si->pos[idx_block])
+    {
+      Fseek (si->stem_idx_file, si->pos[idx_block] + si->sih.blocks_start, 0);
+      Fread (si->buffer, si->sih.block_size, sizeof (u_char), si->stem_idx_file);
+      si->active = si->pos[idx_block];
+
+      idx_first_word = (unsigned long *) (si->buffer);
+      NTOHUL(*idx_first_word);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_num_words = (unsigned short *) (idx_first_word + 1);
+      NTOHUS(*idx_num_words);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_index = idx_num_words + 1;
+      idx_num_indexes = ((*idx_num_words - 1) / si->sih.lookback) + 1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < idx_num_indexes; j++)
+	NTOHUS(idx_index[j]);
+    }
+  else
+    {
+      idx_first_word = (unsigned long *) (si->buffer);
+      idx_num_words = (unsigned short *) (idx_first_word + 1);
+      idx_index = idx_num_words + 1;
+      idx_num_indexes = ((*idx_num_words - 1) / si->sih.lookback) + 1;
+    }
+  idx_base = (u_char *) (idx_index + idx_num_indexes);
+  
+  {
+    /* Locate 3-in-4 block */
+    register int lo, hi, mid, c;
+    lo = 0;
+    hi = idx_num_indexes - 1;
+    while (lo <= hi)
+      {
+	mid = (lo + hi) / 2;
+	c = casecompare (sWord, idx_base + idx_index[mid] + 1);
+	if (c < 0)
+	  hi = mid - 1;
+	else if (c > 0)
+	  lo = mid + 1;
+	else
+	  {
+	    hi = mid;
+	    break;
+	  }
+      }
+    if (hi < 0)
+      hi = 0;
+
+    idx_res = hi * si->sih.lookback;
+    idx_base += idx_index[hi];
+  }
+
+  /* Locate actual word entry */
+  for (;;)
+    {
+      int c;
+      if (idx_res >= *idx_num_words)
+	return (-1);
+      copy = *idx_base++;
+      suff = *idx_base++;
+      bcopy ((char *) idx_base, (char *) (idx_prev + copy + 1), suff);
+      idx_base += suff;
+      *idx_prev = copy + suff;
+
+      c = casecompare (sWord, idx_prev);
+      if (c < 0)
+	return (-1);
+     
+      bcopy ((char *) idx_base, (char *) &num_entries, sizeof (num_entries));
+      NTOHUI(num_entries);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_base += sizeof (num_entries);
+      
+      if (c > 0)
+	idx_base += num_entries * (sizeof (num_cases) + sizeof (block) + 
+				   sizeof (blk_index) + sizeof (offset));
+
+      else
+	break;
+
+      idx_res++;
+    }
+
+  for (k = 0; k < num_entries; k++)
+    {
+      unsigned copy, suff;
+      unsigned long invfp;
+      /* Read next stem index pos */
+      bcopy ((char *) idx_base, (char *) &num_cases, sizeof (num_cases));
+      NTOHUI(num_cases);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_base += sizeof (num_cases);
+      bcopy ((char *) idx_base, (char *) &block, sizeof (block));
+      NTOHUI(block);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_base += sizeof (block);
+      bcopy ((char *) idx_base, (char *) &blk_index, sizeof (blk_index));
+      NTOHUS(blk_index);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_base += sizeof (blk_index);
+      bcopy ((char *) idx_base, (char *) &offset, sizeof (offset));
+      NTOHUS(offset);  /* [RPAP - Jan 97: Endian Ordering] */
+      idx_base += sizeof (offset);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      if (sd->active != sd->pos[block])
+	{
+	  Fseek (sd->stem_file, sd->pos[block] + sd->sdh.blocks_start, 0);
+	  Fread (sd->buffer, sd->sdh.block_size, sizeof (u_char), sd->stem_file);
+	  sd->active = sd->pos[block];
+
+	  first_word = (unsigned long *) (sd->buffer);
+	  NTOHUL(*first_word);  /* [RPAP - Jan 97: Endian Ordering] */
+	  last_invf_len = (unsigned long *) (first_word + 1);
+	  NTOHUL(*last_invf_len);  /* [RPAP - Jan 97: Endian Ordering] */
+	  num_words = (unsigned short *) (last_invf_len + 1);
+	  NTOHUS(*num_words);  /* [RPAP - Jan 97: Endian Ordering] */
+	  index = num_words + 1;
+	  num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+
+	  /* [RPAP - Jan 97: Endian Ordering] */
+	  for (j = 0; j < num_indexes; j++)
+	    NTOHUS(index[j]);
+	}
+      else
+	{
+	  first_word = (unsigned long *) (sd->buffer);
+	  last_invf_len = (unsigned long *) (first_word + 1);
+	  num_words = (unsigned short *) (last_invf_len + 1);
+	  index = num_words + 1;
+	  num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+	}
+      base = (u_char *) (index + num_indexes);
+      
+      res = blk_index * sd->sdh.lookback;
+      base += index[blk_index];
+      
+      for (j = 0; j < offset; j++)
+	{
+	  copy = *base++;
+	  suff = *base++;
+	  bcopy ((char *) base, (char *) (prev + copy + 1), suff);
+	  base += suff;
+	  *prev = copy + suff;
+	  base += sizeof (unsigned long);   /* skip doc_count */
+	  base += sizeof (unsigned long);   /* skip count */
+	  base += sizeof (unsigned long);   /* skip invf_ptr */
+	  res++;
+	}
+
+      for (j = 0; j < num_cases; j++)
+	{
+	  TermEntry te;
+	  
+	  if (res >= *num_words)
+	    return (-1);
+	  copy = *base++;
+	  suff = *base++;
+	  bcopy ((char *) base, (char *) (prev + copy + 1), suff);
+	  base += suff;
+	  *prev = copy + suff;
+	      
+	  te.Word = copy_string (prev);
+	  if (!te.Word)
+	    FatalError (1, "Could NOT create memory to add term");
+	  te.Stem = copy_string (prev);
+	  if (!te.Stem)
+	    FatalError (1, "Could NOT create memory to add term");
+	  stemmer (2, sd->sdh.stemmer_num, te.Stem);
+
+	  te.Count = 1;
+	  te.WE.word_num = *first_word + res;
+	  bcopy ((char *) base, (char *) &te.WE.doc_count, sizeof (te.WE.doc_count));
+	  NTOHUL(te.WE.doc_count);  /* [RPAP - Jan 97: Endian Ordering] */
+	  te.WE.max_doc_count = te.WE.doc_count;
+	  base += sizeof (te.WE.doc_count);
+	      
+	  bcopy ((char *) base, (char *) &te.WE.count, sizeof (te.WE.count));
+	  NTOHUL(te.WE.count);
+	  base += sizeof (te.WE.count);
+	      
+	  bcopy ((char *) base, (char *) &invfp, sizeof (te.WE.invf_ptr));
+	  NTOHUL(invfp);  /* [RPAP - Jan 97: Endian Ordering] */
+	  te.WE.invf_ptr = invfp;
+	  base += sizeof (te.WE.invf_ptr);
+		
+	  /* If the current word is the last word of the block the get the 
+	     length from last_invf_len */
+	  if (res == *num_words - 1)
+	    te.WE.invf_len = *last_invf_len;
+	  else
+	    {
+	      unsigned long next_invfp;
+	      u_char *oldbase = base;
+		  
+	      /* Skip over most of the next word to get to the invf_ptr */
+	      base++;
+	      suff = *base++;
+	      base += suff + sizeof (unsigned long) * 2;
+	      bcopy ((char *) base, (char *) &next_invfp, sizeof (next_invfp));
+	      NTOHUL(next_invfp);  /* [RPAP - Jan 97: Endian Ordering] */
+	      te.WE.invf_len = next_invfp - invfp;
+	      base = oldbase;
+	    }
+	      
+	  /* Add term entry to term list */
+	  /*	  te.query_mask = NULL;*/
+	  AddTermEntry (tl, &te);
+	      
+	  if (res == *num_words - 1 && j + 1 < num_cases)
+	    {
+	      int ii;
+	      /* Read in next block */
+	      block++;
+	      Fseek (sd->stem_file, sd->pos[block] + sd->sdh.blocks_start, 0);
+	      Fread (sd->buffer, sd->sdh.block_size, sizeof (u_char), sd->stem_file);
+	      sd->active = sd->pos[block];
+
+	      first_word = (unsigned long *) (sd->buffer);
+	      NTOHUL(*first_word);  /* [RPAP - Jan 97: Endian Ordering] */
+	      last_invf_len = (unsigned long *) (first_word + 1);
+	      NTOHUL(*last_invf_len);  /* [RPAP - Jan 97: Endian Ordering] */
+	      num_words = (unsigned short *) (last_invf_len + 1);
+	      NTOHUS(*num_words);  /* [RPAP - Jan 97: Endian Ordering] */
+	      index = num_words + 1;
+	      num_indexes = ((*num_words - 1) / sd->sdh.lookback) + 1;
+
+	      /* [RPAP - Jan 97: Endian Ordering] */
+	      for (ii = 0; ii < num_indexes; ii++)
+		NTOHUS(index[ii]);
+
+	      base = (u_char *) (index + num_indexes);
+	      base += index[0];
+	      res = 0;
+	      blk_index = 0;
+	    }
+	  else
+	    res++;
+	} /* end for num_cases */
+    } /* end for num_entries */
+  return (*tl)->num;
+}
+
+
+void 
+FreeStemDict (stemmed_dict * sd)
+{
+  /* [RPAP - Jan 97: Stem Index Change] */
+  if (sd->stem1)
+    FreeStemIdx (sd->stem1);
+  if (sd->stem2)
+    FreeStemIdx (sd->stem2);
+  if (sd->stem3)
+    FreeStemIdx (sd->stem3);
+
+  Xfree (sd->index[0]);
+  Xfree (sd->index);
+  Xfree (sd->buffer);
+  Xfree (sd->pos);
+  Xfree (sd);
+}
+
+/* [RPAP - Jan 97: Stem Index Change] */
+void
+FreeStemIdx (stemmed_idx * si)
+{
+  Xfree (si->index[0]);
+  Xfree (si->index);
+  Xfree (si->buffer);
+  Xfree (si->pos);
+  Xfree (si);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/stem_search.h	(revision 16561)
@@ -0,0 +1,43 @@
+/**************************************************************************
+ *
+ * stem_search.h -- Functions for searching the blocked stemmed dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_STEM_SEARCH
+#define H_STEM_SEARCH
+
+stemmed_dict *ReadStemDictBlk (File * stem_file);
+
+stemmed_idx *ReadStemIdxBlk (File * stem_idx_file);
+
+int FindWord (stemmed_dict * sd, u_char * Word, unsigned long *count,
+	      unsigned long *doc_count, unsigned long *invf_ptr,
+	      unsigned long *invf_len);
+
+/* [RPAP - Jan 97: Stem Index Change] */
+int FindWords (stemmed_dict * sd, u_char * sWord, int stem_method, TermList ** tl);
+
+void FreeStemDict (stemmed_dict * sd);
+
+/* [RPAP - Jan 97: Stem Index Change] */
+void FreeStemIdx (stemmed_idx * si);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.c	(revision 16561)
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * stemmer.c -- The stemmer/case folder
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "stemmer.h"
+
+#include "lovinstem.h"
+#include "simplefrenchstem.h"
+#include "unitool.h"
+
+/*
+   $Log$
+   Revision 1.2  2004/06/22 04:17:06  kjdon
+   make stemmerdescription a const
+
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:23  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.3  1998/12/17 09:12:54  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.2  1998/11/25 07:55:51  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:42  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:57:05  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:42:10  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+#define LOVINSTEMMER        0
+#define SIMPLEFRENCHSTEMMER 1
+
+
+/* decode the utf-8 encoded unicode, casefold and then recode 
+ * making sure the final length doesn't exceed the original
+ * length */
+static void unicode_casefold (u_char *word) {
+  unsigned short out[256]; /* temp space */
+  int i;
+  int len;
+
+  /* decode */
+  utf8_word_to_unicode (word, out, 255);
+  len = out[0];
+
+  /* casefold and simplify-fold */
+  for (i=0; i<len; i++) {
+    out[i+1] = unicode_tosimplified(unicode_tolower(out[i+1]));
+  }
+
+  /* re-code */
+  unicode_to_utf8_word (out, word, word[0]+1);
+}
+
+
+int stemmernumber (const u_char *stemmerdescription) {
+  u_char descript[MAX_STEM_DESCRIPTION_LEN];
+  int i;
+
+  /* copy and case-fold the description */
+  for (i=0; (stemmerdescription[i] != '\0') && 
+	 (i < MAX_STEM_DESCRIPTION_LEN-1); i++)
+    descript[i] = tolower (stemmerdescription[i]);
+  descript[i] = '\0';
+
+  /* map the description to its number */
+
+  if ((strcmp (descript, "0") == 0) ||
+      (strcmp (descript, "english") == 0) ||
+      (strcmp (descript, "lovin") == 0))
+    return LOVINSTEMMER;
+
+  if ((strcmp (descript, "1") == 1) ||
+      (strcmp (descript, "french") == 0) ||
+      (strcmp (descript, "simplefrench") == 0))
+    return SIMPLEFRENCHSTEMMER;
+
+  return -1;
+}
+
+
+
+/*
+ * Method 0 - Do not stem or case fold.
+ * Method 1 - Case fold.
+ * Method 2 - Stem.
+ * Method 3 - Case fold and stem.
+ *
+ * The stemmer number should be obtained using
+ * the stemmernumber function above.
+ */
+void 
+stemmer (int method, int stemmer, u_char *word) {
+  if (method & 1) {
+    unicode_casefold (word);
+  }
+
+  if (method & 2) {
+    switch (stemmer) {
+    case LOVINSTEMMER: lovinstem (word);
+      break;
+    case SIMPLEFRENCHSTEMMER: simplefrenchstem (word);
+      break;
+    }
+  }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/stemmer.h	(revision 16561)
@@ -0,0 +1,64 @@
+/**************************************************************************
+ *
+ * stemmer.h -- The stemmer/case folder
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 STEMMER_H
+#define STEMMER_H
+
+#include "sysfuncs.h"
+
+#define STEMMER_MASK 3
+#define MAX_STEM_DESCRIPTION_LEN 16
+
+/* stemmernumber will return the stemmer for
+ * a description of the stemmer. Stemmer descriptions
+ * are not case sensitive. Valid descriptions are:
+ *
+ *   'English'
+ *   'Lovin'
+ *   'French'
+ *   'SimpleFrench'
+ *
+ * More than one description might result in the same
+ * stemmer number (for example, for stemming 'English'
+ * we currently use the 'Lovin' stemmer).
+ *
+ * stemmerdescription is a normal C, null-terminated,
+ * string.
+ *
+ * stemmernumber will return -1 if it doesn't know the
+ * stemmer description.
+ */
+int stemmernumber (const u_char *stemmerdescription);
+
+/*
+ * Method 0 - Do not stem or case fold.
+ * Method 1 - Case fold.
+ * Method 2 - Stem.
+ * Method 3 - Case fold and stem.
+ *
+ * The stemmer number should be obtained using function
+ * stemmernumber above.
+ */
+void stemmer (int method, int stemmer, u_char * word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.c	(revision 16561)
@@ -0,0 +1,334 @@
+/**************************************************************************
+ *
+ * filename -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:24  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:52  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:43  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:57:07  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "local_strings.h"
+#include "term_lists.h"
+#include "messages.h"
+#include "stemmer.h"
+
+TermList *query_term_list = NULL;
+
+/* =========================================================================
+ * Function: MakeTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+TermList *
+MakeTermList (int n)
+{
+  TermList *t;
+  int list_size = (n == 0 ? 1 : n);	/* always allocate at least one node */
+
+  t = Xmalloc (sizeof (TermList) + (list_size - 1) * sizeof (TermEntry));
+  if (!t)
+    FatalError (1, "Unable to allocate term list");
+
+  t->num = n;
+  t->list_size = list_size;
+
+  return t;
+}
+
+/* =========================================================================
+ * Function: ResizeTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+#define GROWTH_FACTOR 2
+#define MIN_SIZE 2
+
+static void
+ResizeTermList (TermList ** term_list)
+{
+  TermList *tl = *term_list;
+
+  if (tl->num > tl->list_size)
+    {
+      if (tl->list_size)
+	tl->list_size *= GROWTH_FACTOR;
+      else
+	tl->list_size = MIN_SIZE;
+    }
+  tl = Xrealloc (tl, sizeof (TermList) + (tl->list_size - 1) * sizeof (TermEntry));
+
+  if (!tl)
+    FatalError (1, "Unable to resize term list");
+
+  *term_list = tl;
+}
+
+/* =========================================================================
+ * Function: ConvertTermsToString
+ * Description:
+ *      Convert term list into null-terminated string
+ * Input:
+ *      query_term_list = term list
+ * Output:
+ *      str = term string
+ * ========================================================================= */
+
+void
+ConvertTermsToString (TermList * query_term_list, char *str)
+{
+  int i = 0;
+  int total_len = 0;
+
+  /* terms_str should be preallocated */
+  if (!str)
+    return;
+
+  for (i = 0; i < query_term_list->num; i++)
+    {
+      unsigned char *word = query_term_list->TE[i].Word;
+      int len = word[0];
+      total_len += len + 1;	/* +1 for space */
+      if (total_len > MAXTERMSTRLEN)
+	break;
+      strncpy (str, (char *) word + 1, len);
+      str += len;
+      if (i != (query_term_list->num) - 1)
+	{
+	  *str = ' ';
+	  str++;		/* add space gap */
+	}
+
+    }
+  *str = '\0';
+}
+
+/* =========================================================================
+ * Function: ResetTermList
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+ResetTermList (TermList ** tl)
+{
+  if (*tl)
+    FreeTermList (tl);
+  *tl = MakeTermList (0);
+}
+
+/* =========================================================================
+ * Function: AddTermEntry
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+AddTermEntry (TermList ** query_term_list, TermEntry * te)
+{
+  TermList *tl = *query_term_list;
+
+  tl->num++;
+  ResizeTermList (query_term_list);
+  tl = *query_term_list;
+
+  /* copy the structure contents */
+  bcopy ((char *) te, (char *) &(tl->TE[tl->num - 1]), sizeof (TermEntry));
+
+  return tl->num - 1;
+}
+
+
+
+/* =========================================================================
+ * Function: AddTerm
+ * Description: Used in boolean parser - see bool_tree   [RPAP - Feb 97: Term Frequency] 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+int
+AddTerm (TermList ** query_term_list, u_char * Word, int Count, int word_num,
+	 u_long count, u_long doc_count, u_long invf_ptr, u_long invf_len, int stemmer_num)  /* [RPAP - Feb 97: Term Frequency] */
+{
+  int j;
+  TermList *tl = *query_term_list;
+
+  /* Look for the word in the already identified terms */
+  for (j = 0; j < tl->num; j++)
+    {
+      TermEntry *te = &(tl->TE[j]);
+      if (compare (te->Word, Word) == 0)
+	{
+	  te->Count++;
+	  return j;
+	}
+    }
+
+
+  {
+    /* Create a new entry in the list for the new word */
+    TermEntry te;
+
+    /* [RPAP - Feb 97: Term Frequency] */
+    te.WE.word_num = word_num;
+    te.WE.count = count;
+    te.WE.doc_count = doc_count;
+    te.WE.max_doc_count = doc_count;   /* [RPAP - Jan 97: Stem Index Change] */
+    te.WE.invf_ptr = invf_ptr;
+    te.WE.invf_len = invf_len;
+    te.Count = Count;
+    te.Word = copy_string (Word);
+    if (!te.Word)
+      FatalError (1, "Could NOT create memory to add term");
+
+    /* [RPAP - Jan 97: Stem Index Change] */
+    te.Stem = copy_string (Word);
+    if (!te.Stem)
+      FatalError (1, "Could NOT create memory to add term");
+    stemmer (2, stemmer_num, te.Stem);
+
+    te.require_match = 0;  /* [RJM - 07/97: Ranked Required Terms] */
+
+    return AddTermEntry (query_term_list, &te);
+  }
+
+}
+
+/* =========================================================================
+ * Function: FreeTermList
+ * Description:
+ * Input:
+ * Output:
+ * ========================================================================= */
+
+void
+FreeTermList (TermList ** the_tl)
+{
+  int j;
+  TermList *tl = *the_tl;
+
+  for (j = 0; j < tl->num; j++)
+    {
+      if (tl->TE[j].Word)
+	Xfree (tl->TE[j].Word);
+      /* [RPAP - Jan 97: Stem Index Change] */
+      if (tl->TE[j].Stem)
+	Xfree (tl->TE[j].Stem);
+    }
+  Xfree (tl);
+
+  *the_tl = NULL;
+}
+
+/* =========================================================================
+ * Function: PrintWordEntry
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+PrintWordEntry (WordEntry * we, FILE * file)
+{
+  fprintf (file, "we->word_num = %d\n", we->word_num);
+  fprintf (file, "we->count = %ld\n", we->count);
+  fprintf (file, "we->doc_count = %ld\n", we->doc_count);
+  fprintf (file, "we->max_doc_count = %ld\n", we->max_doc_count);
+  fprintf (file, "we->invf_ptr = %ld\n", we->invf_ptr);
+  fprintf (file, "we->invf_len = %ld\n", we->invf_len);
+}
+
+/* =========================================================================
+ * Function: PrintTermEntry
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+PrintTermEntry (TermEntry * te, FILE * file)
+{
+
+  fprintf (file, "Term Entry\n");
+  fprintf (file, "te->Count = %d\n", te->Count);
+  fprintf (file, "te->Word = %s\n", str255_to_string (te->Word, NULL));
+  if (te->Stem != NULL)
+    fprintf (file, "te->Stem = %s\n", str255_to_string (te->Stem, NULL));  /* [RPAP - Jan 97: Stem Index Change] */
+  fprintf (file, "te->require_match = %i\n", te->require_match);  /* [RJM 07/97: Ranked Required Terms] */
+  PrintWordEntry (&(te->WE), file);
+
+}
+
+/* =========================================================================
+ * Function: PrintTermList
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+void
+PrintTermList (TermList * tl, FILE * file)
+{
+  int i;
+
+  fprintf (file, "Term List\n");
+  fprintf (file, "tl->list_size = %d\n", tl->list_size);
+  fprintf (file, "tl->num = %d\n", tl->num);
+
+  for (i = 0; i < tl->num; i++)
+    {
+      fprintf (file, "[%d]\n", i);
+      PrintTermEntry (&(tl->TE[i]), file);
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/term_lists.h	(revision 16561)
@@ -0,0 +1,105 @@
+/**************************************************************************
+ *
+ * term_lists.h -- description
+ * Copyright (C) 1994  Authors
+ *
+ * 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  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:24  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/11/25 07:55:52  rjmcnab
+
+   Modified mg to that you can specify the stemmer you want
+   to use via a command line option. You specify it to
+   mg_passes during the build process. The number of the
+   stemmer that you used is stored within the inverted
+   dictionary header and the stemmed dictionary header so
+   the correct stemmer is used in later stages of building
+   and querying.
+
+   Revision 1.1  1998/11/17 09:35:44  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.1  1994/10/20  03:57:08  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+ */
+
+#ifndef TERM_LISTS_H
+#define TERM_LISTS_H
+
+#include "sysfuncs.h"
+
+#define MAXTERMSTRLEN 1023	/* maximum number of characters in term string */
+
+typedef struct WordEntry
+  {
+    int word_num;		/* Unique number for each different word */
+    u_long count;		/* Number of times the word occurs in the text */
+    u_long doc_count;		/* Number of documents that contain the word */
+    u_long max_doc_count;       /* [RPAP - Jan 97: Stem Index Change] */
+    u_long invf_ptr;		/* This is a byte position of the  
+				   inverted file entry corresponding to the word */
+    u_long invf_len;		/* This is the length of the inverted 
+				   file entry in bytes */
+  }
+WordEntry;
+
+typedef struct TermEntry
+  {
+    WordEntry WE;
+    int Count;			/* The number of times the word occurs in the query */
+    u_char *Word;		/* The word. */
+    u_char *Stem;               /* [RPAP - Jan 97: Stem Index Change] - The stemmed non-cf version of Word */
+    int require_match;          /* [RJM 07/97: Ranked Required Terms] 0=optional match, 1=must match */
+  }
+TermEntry;
+
+typedef struct TermList
+  {
+    int list_size;
+    int num;
+    TermEntry TE[1];
+  }
+TermList;
+
+#define GetNthWE(term_list, n) (&((term_list)->TE[(n)].WE))
+
+/* --- prototypes --- */
+void ConvertTermsToString (TermList * query_term_list, char *str);
+int AddTermEntry (TermList ** query_term_list, TermEntry * te);
+int AddTerm (TermList ** query_term_list, u_char * Word, int Count, int word_num,
+	     u_long count, u_long doc_count, u_long invf_ptr, u_long invf_len, /* [RPAP - Feb 97: Term Frequency] */
+	     int stemmer_num);  
+void ResetTermList (TermList ** tl);
+void FreeTermList (TermList ** the_tl);
+void PrintWordEntry (WordEntry * we, FILE * file);
+void PrintTermEntry (TermEntry * te, FILE * file);
+void PrintTermList (TermList * tl, FILE * file);
+TermList *MakeTermList (int n);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/text.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/text.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/text.h	(revision 16561)
@@ -0,0 +1,176 @@
+/**************************************************************************
+ *
+ * text.h -- Header file for compression related stuff
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_TEXT
+#define H_TEXT
+
+#include "huffman.h"
+
+
+
+
+
+/*****************************************************************************
+ *
+ * There are several different methods of compressing the text of the
+ * database. The following defines list the different methods of text
+ * compression
+ *
+ */
+
+
+/* The dictionary contains all the fragments that occur in the collection
+   i.e. escapes are not possible */
+#define MG_COMPLETE_DICTIONARY 0
+
+
+/* Certain words have been deleted from the dictionary. The words deleted
+   have been used to create the frequency huffman codes of the characters. 
+   This dictionary has an escape code and may be used to compress novel
+   words. This dictionary may fail if there is a novel character. */
+#define MG_PARTIAL_DICTIONARY 1
+
+
+/* This dictionary has an escape so that novel words and non-words can be 
+   coded. The method for coding the novel words and non-words is determined
+   by a dictionary parameter. */
+#define MG_SEED_DICTIONARY 2
+
+
+
+
+
+
+
+
+
+/*****************************************************************************
+ *
+ * With a seed dictionary there are several methods for coding the novel
+ * words and non-words the following defined values specify the different
+ * methods of coding.
+ *
+ */
+
+
+/* Code novel words and non-words character by character using huffman codes.
+   The huffman codes for the word and non-word lengths and characters are 
+   generated from the distribution of lengths and characters in the
+   dictionary. */
+#define MG_NOVEL_HUFFMAN_CHARS 0
+
+
+/* This method codes novel words using binary codes. The novel words are stored
+   in a auxillary dictionary which is built by pass two. */
+#define MG_NOVEL_BINARY 1
+
+
+/* This method codes novel words using delta codes. The novel words are stored
+   in a auxillary dictionary which is built by pass two. */
+#define MG_NOVEL_DELTA 2
+
+
+/* This method codes novel words using hybrid version of delta. The novel
+   words are stored in a auxillary dictionary which is built by pass two. */
+#define MG_NOVEL_HYBRID 3
+
+
+/* This method codes novel words using hybrid version of delta and a MTF
+   operation. The novel words are stored in a auxillary dictionary which
+   is built by pass two. */
+#define MG_NOVEL_HYBRID_MTF 4
+
+
+
+
+
+
+/* This specified an amount of extra space allocated in the
+   compression_dict_header for adding new parameters. As new
+   parameters are added this should be decreased. */
+#define TEXT_PARAMS 15
+
+
+
+typedef struct compression_dict_header
+  {
+    u_long dict_type;
+    u_long novel_method;
+    u_long params[TEXT_PARAMS];
+    u_long num_words[2];
+    u_long num_word_chars[2];
+    u_long lookback;
+  }
+compression_dict_header;
+
+
+typedef struct comp_frags_header
+  {
+    huff_data hd;
+    u_long uncompressed_size;
+    u_long huff_words_size[MAX_HUFFCODE_LEN + 1];
+  }
+comp_frags_header;
+
+
+
+typedef struct compressed_text_header
+  {
+    u_long num_of_docs;
+    u_long num_of_words;	/* number of words in collection */
+    double num_of_bytes; /* [RJM 07/97: 4G limit] */
+    double ratio;
+    u_long length_of_longest_doc;	/* (characters) */
+    u_long dummy;    /* added to make the structure the same on different architectures */
+  }
+compressed_text_header;
+
+
+typedef struct compression_stats_header
+  {
+    u_long num_docs;
+    u_long dummy;   /* added to make the structure the same on different architectures */
+    double num_bytes; /* [RJM 07/97: 4G limit] */
+  }
+compression_stats_header;
+
+
+typedef struct frags_stats_header
+  {
+    u_long num_frags;
+    u_long mem_for_frags;
+  }
+frags_stats_header;
+
+
+typedef struct aux_frags_header
+  {
+    u_long num_frags;
+    u_long mem_for_frags;
+  }
+aux_frags_header;
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass1.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass1.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass1.c	(revision 16561)
@@ -0,0 +1,405 @@
+/**************************************************************************
+ *
+ * text.pass1.c -- Text compression (Pass 1)
+ * Copyright (C) 1994  Neil Sharman, Gary Eddy and Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "huffman.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "locallib.h"
+#include "words.h"
+#include "text.h"
+#include "hash.h"
+#include "local_strings.h"
+/* for more meaningful messages - jrm21 (gsdl) */
+#ifdef HAVE_STRERROR
+#include <string.h>
+#include <errno.h>
+#endif
+
+#define POOL_SIZE 1024*1024
+#define INITIAL_HASH_SIZE 7927
+
+
+typedef struct hash_rec
+  {
+    unsigned long wcnt;		/* word frequency */
+    unsigned long occurance_num;
+    u_char *word;
+  }
+hash_rec;
+
+typedef struct dict_data
+  {
+    hash_rec *HashTable;
+    unsigned long HashSize;
+    unsigned long HashUsed;
+    unsigned long wordnum;
+    unsigned long words_read;
+    unsigned long bytes_diff;
+    huff_data hd;
+  }
+dict_data;
+
+
+
+static unsigned long LongestDoc = 0;
+static unsigned long occurance_num = 0;
+static dict_data DictData[2];
+
+static u_char *Pool;
+static int PoolLeft;
+static double inputbytes = 0; /* [RJM 07/97: 4G limit] */
+static unsigned long MaxMemInUse = 0;
+static unsigned long MemInUse = 0;
+static compression_stats_header csh =
+{0, 0.0}; /* [RJM 07/97: 4G limit] */
+
+
+static void 
+ChangeMem (int Change)
+{
+  MemInUse += Change;
+  if (MemInUse > MaxMemInUse)
+    MaxMemInUse = MemInUse;
+}
+
+
+
+
+int 
+init_text_1 (char *FileName)
+{
+  int which;
+  if (!(Pool = Xmalloc (POOL_SIZE)))
+    {
+      Message ("Unable to allocate memory for pool");
+      return (COMPERROR);
+    }
+  PoolLeft = POOL_SIZE;
+  ChangeMem (POOL_SIZE);
+
+  for (which = 1; which >= 0; which--)
+    {
+      u_char *word;
+      hash_rec *ent;
+      dict_data *dd = &DictData[which];
+
+      dd->wordnum = 0;
+      dd->words_read = 0;
+      dd->bytes_diff = 0;
+      dd->HashSize = INITIAL_HASH_SIZE;
+      dd->HashUsed = 0;
+      if (!(dd->HashTable = Xmalloc (sizeof (hash_rec) * dd->HashSize)))
+	{
+	  Message ("Unable to allocate memory for table");
+	  return (COMPERROR);
+	}
+      ChangeMem (sizeof (hash_rec) * dd->HashSize);
+      bzero ((char *) (dd->HashTable), sizeof (hash_rec) * dd->HashSize);
+
+      word = Pool;
+      *Pool++ = '\0';
+      PoolLeft--;
+      {
+	register u_char *wptr;
+	register int hsize = dd->HashSize;
+	register unsigned long hashval, step;
+
+	HASH (hashval, step, word, hsize);
+	wptr = (dd->HashTable + hashval)->word;
+	while (wptr)
+	  {
+	    hashval += step;
+	    if (hashval >= hsize)
+	      hashval -= hsize;
+	    wptr = (dd->HashTable + hashval)->word;
+	  }
+	ent = dd->HashTable + hashval;
+      }
+      ent->wcnt = 1;
+      ent->word = word;
+      dd->HashUsed = 1;
+    }
+  return (COMPALLOK);
+}
+
+
+
+
+int 
+process_text_1 (u_char * s_in, int l_in)
+{
+  int which;
+  u_char *end = s_in + l_in - 1;
+  if (l_in > LongestDoc)
+    LongestDoc = l_in;
+
+  csh.num_docs++;
+  csh.num_bytes += l_in;
+
+  which = inaword (s_in, end);
+  /*
+   ** Alternately parse off words and non-words from the input
+   ** stream beginning with a non-word. Each token is then
+   ** inserted into the set if it does not exist or has it's
+   ** frequency count incremented if it does.
+   */
+  for (; s_in <= end; which = !which)
+    {
+      u_char Word[MAXWORDLEN + 1];
+      dict_data *dd = &DictData[which];
+
+      /* First parse a word or non-word out of the string */
+      if (which)
+	PARSE_WORD (Word, s_in, end);
+      else
+	PARSE_NON_WORD (Word, s_in, end);
+
+      dd->wordnum++;
+      inputbytes += *Word;
+      dd->words_read++;
+
+      /* Search the hash table for Word */
+      {
+	register unsigned long hashval, step;
+	register int hsize = dd->HashSize;
+	HASH (hashval, step, Word, hsize);
+	for (;;)
+	  {
+	    register u_char *s1;
+	    register u_char *s2;
+	    register int len;
+	    register hash_rec *ent;
+	    ent = dd->HashTable + hashval;
+	    if (!ent->word)
+	      {
+		int len = *Word + 1;
+		if (len > PoolLeft)
+		  {
+		    if (!(Pool = Xmalloc (POOL_SIZE)))
+		      {
+			Message ("Unable to allocate memory for pool");
+			return (COMPERROR);
+		      }
+		    PoolLeft = POOL_SIZE;
+		    ChangeMem (POOL_SIZE);
+		  }
+		ent->occurance_num = occurance_num++;
+		ent->wcnt = 1;
+		ent->word = Pool;
+		memcpy (Pool, Word, len);
+		Pool += len;
+		PoolLeft -= len;
+		dd->HashUsed++;
+		dd->bytes_diff += Word[0];
+		break;
+	      }
+	    /* Compare the words */
+	    s1 = Word;
+	    s2 = ent->word;
+	    len = *s1 + 1;
+	    for (; len; len--)
+	      if (*s1++ != *s2++)
+		break;
+
+	    if (len)
+	      {
+		hashval = (hashval + step);
+		if (hashval >= hsize)
+		  hashval -= hsize;
+	      }
+	    else
+	      {
+		ent->wcnt++;
+		break;
+	      }
+	  }
+      }
+
+  
+      if (dd->HashUsed >= dd->HashSize >> 1)
+	{
+	  hash_rec *ht;
+	  unsigned long size;
+	  unsigned long i;
+	  size = prime (dd->HashSize * 2);
+	  if (!(ht = Xmalloc (sizeof (hash_rec) * size)))
+	    {
+	      Message ("Unable to allocate memory for table");
+	      return (COMPERROR);
+	    }
+	  ChangeMem (sizeof (hash_rec) * size);
+	  bzero ((char *) ht, sizeof (hash_rec) * size);
+
+	  for (i = 0; i < dd->HashSize; i++)
+	    if (dd->HashTable[i].word)
+	      {
+		register u_char *wptr;
+		register unsigned long hashval, step;
+
+		wptr = dd->HashTable[i].word;
+		HASH (hashval, step, wptr, size);
+		wptr = (ht + hashval)->word;
+		while (wptr)
+		  {
+		    hashval += step;
+		    if (hashval >= size)
+		      hashval -= size;
+		    wptr = (ht + hashval)->word;
+		  }
+		ht[hashval] = dd->HashTable[i];
+	      }
+	  Xfree (dd->HashTable);
+	  ChangeMem (-sizeof (hash_rec) * dd->HashSize);
+	  dd->HashTable = ht;
+	  dd->HashSize = size;
+
+
+	}
+    }
+  return (COMPALLOK);
+}				/* encode */
+
+
+
+static int 
+PackHashTable (dict_data * dd)
+{
+  int s, d;
+  for (s = d = 0; s < dd->HashSize; s++)
+    if (dd->HashTable[s].word)
+      dd->HashTable[d++] = dd->HashTable[s];
+  ChangeMem (-sizeof (hash_rec) * dd->HashSize);
+  ChangeMem (sizeof (hash_rec) * dd->HashUsed);
+  if (!(dd->HashTable = Xrealloc (dd->HashTable,
+				  sizeof (hash_rec) * dd->HashUsed)))
+    {
+      Message ("Out of memory");
+      return COMPERROR;
+    }
+  dd->HashSize = dd->HashUsed;
+  return COMPALLOK;
+}
+
+
+
+
+
+static int 
+ent_comp (const void *s1, const void *s2)
+{
+  return casecompare (((hash_rec *) s1)->word, ((hash_rec *) s2)->word);
+}
+
+
+
+static void 
+WriteHashTable (FILE * fp, dict_data * dd)
+{
+  frags_stats_header fsh;
+  u_long j = 0;
+  u_char *curr;
+
+  if (PackHashTable (dd) == COMPERROR)
+    return;
+
+  qsort (dd->HashTable, dd->HashUsed, sizeof (hash_rec), ent_comp);
+
+  fsh.num_frags = dd->HashSize;
+  fsh.mem_for_frags = dd->HashSize;
+  for (j = 0; j < dd->HashSize; j++)
+    fsh.mem_for_frags += dd->HashTable[j].word[0];
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(fsh.num_frags);
+  HTONUL(fsh.mem_for_frags);
+
+  fwrite (&fsh, sizeof (fsh), 1, fp);
+
+  for (j = 0; j < dd->HashSize; j++)
+    {
+      curr = dd->HashTable[j].word;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(dd->HashTable[j].wcnt);
+      HTONUL(dd->HashTable[j].occurance_num);
+
+      fwrite (&dd->HashTable[j].wcnt, sizeof (dd->HashTable[j].wcnt), 1, fp);
+      fwrite (&dd->HashTable[j].occurance_num,
+	      sizeof (dd->HashTable[j].occurance_num), 1, fp);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(dd->HashTable[j].wcnt);
+      NTOHUL(dd->HashTable[j].occurance_num);
+
+      fwrite (curr, sizeof (u_char), curr[0] + 1, fp);
+    }
+}
+
+
+int 
+done_text_1 (char *file_name)
+{
+  char *temp_str;
+  FILE *fp;
+
+  if (!(fp = create_file (file_name, TEXT_STATS_DICT_SUFFIX, "wb",
+			  MAGIC_STATS_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf(stderr,"Couldn't create file %s%s:%s\n",
+	      file_name, TEXT_STATS_DICT_SUFFIX,
+#if defined(HAVE_STRERROR) || defined(__WIN32__)
+	      strerror(errno)
+#else
+	      " "
+#endif
+	      );
+      return COMPERROR;
+    }
+
+  temp_str = msg_prefix;
+  msg_prefix = "text.pass1";
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(csh.num_docs);
+  HTOND(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  fwrite (&csh, sizeof (csh), 1, fp);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  WriteHashTable (fp, &DictData[0]);
+  WriteHashTable (fp, &DictData[1]);
+  msg_prefix = temp_str;
+  fclose(fp);
+  return COMPALLOK;
+}				/* done_encode */
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass2.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass2.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/text.pass2.c	(revision 16561)
@@ -0,0 +1,791 @@
+/**************************************************************************
+ *
+ * text.pass2.c -- Text compression (Pass 2)
+ * Copyright (C) 1994  Neil Sharman, Gary Eddy and Alistair Moffat
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_m_mem.h"
+#include "bitio_m.h"
+#include "huffman.h"
+#include "bitio_stdio.h"
+#include "huffman_stdio.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg.h"
+#include "mg_files.h"
+#include "build.h"
+#include "words.h"
+#include "text.h"
+#include "hash.h"
+#include "locallib.h"
+#include "comp_dict.h"
+
+
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.2  2002/01/22 02:41:14  jrm21
+   Try to print out an error if the aux file couldn't be written to.
+
+   Revision 1.1  1999/08/10 21:18:25  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.2  1998/12/17 09:12:54  rjmcnab
+
+   Altered mg to process utf-8 encoded Unicode. The main changes
+   are in the parsing of the input, the casefolding, and the stemming.
+
+   Revision 1.1  1998/11/17 09:35:47  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:57:10  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:42:14  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+#define POOL_SIZE 1024*256
+
+typedef struct char_pool
+  {
+    struct char_pool *next;
+    u_long left;
+    u_char *ptr;
+    u_char pool[POOL_SIZE];
+  }
+char_pool;
+
+typedef struct novel_hash_rec
+  {
+    u_long ordinal_num;
+    u_char *word;
+  }
+novel_hash_rec;
+
+
+#define INITIAL_HASH_SIZE 7927
+#define MAX_SWAPS 10000
+
+typedef struct novel_hash_table
+  {
+    novel_hash_rec *HashTable;
+    u_long HashSize, HashUsed;
+    char_pool *first_pool;
+    char_pool *pool;
+    u_long next_num, binary_start;
+    novel_hash_rec **code_to_nhr;
+  }
+novel_hash_table;
+
+
+static FILE *text, *text_idx;
+
+static u_char *comp_buffer;
+
+static u_long text_length;
+
+/* [RJM 07/97: 4G limit] */
+static double stats_in_tot_bytes = 0.0;
+static double stats_in_bytes = 0.0;
+static double stats_out_bytes = 0.0;
+
+
+static novel_hash_table nht[2];
+
+static u_long prefix_len = 0;
+
+int blk_start[2][33], blk_end[2][33];
+
+
+static char_pool *
+new_pool (char_pool * pool)
+{
+  char_pool *p = Xmalloc (sizeof (char_pool));
+  if (!p)
+    FatalError (1, "Unable to allocate memory for pool");
+  if (pool)
+    pool->next = p;
+  p->next = NULL;
+  p->left = POOL_SIZE;
+  p->ptr = p->pool;
+  return p;
+}
+
+
+
+int 
+init_text_2 (char *file_name)
+{
+  char path[512];
+  int i;
+
+  if (LoadCompressionDictionary (make_name (file_name, TEXT_DICT_SUFFIX,
+					    path)) == COMPERROR)
+    return COMPERROR;
+
+  if (!(text = create_file (file_name, TEXT_SUFFIX, "w+b",
+			    MAGIC_TEXT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return COMPERROR;
+
+  bzero ((char *) &cth, sizeof (cth));
+
+  if (fwrite (&cth, sizeof (cth), 1, text) != 1)
+    return COMPERROR;
+
+  text_length = sizeof (u_long) + sizeof (cth);
+
+  if (!(text_idx = create_file (file_name, TEXT_IDX_SUFFIX, "w+b",
+				MAGIC_TEXI, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return COMPERROR;
+
+  if (fwrite (&cth, sizeof (cth), 1, text_idx) != 1)
+    return COMPERROR;
+
+  if (!(comp_buffer = Xmalloc (sizeof (u_char) * buf_size)))
+    {
+      Message ("No memory for compression buffer");
+      return (COMPERROR);
+    }
+
+#if 0
+  MaxMemInUse += sizeof (u_char) * buf_size;
+#endif
+
+  if (cdh.novel_method != MG_NOVEL_HUFFMAN_CHARS)
+    for (i = 0; i <= 1; i++)
+      {
+	nht[i].HashSize = INITIAL_HASH_SIZE;
+	nht[i].HashTable = Xmalloc (sizeof (novel_hash_rec) * nht[i].HashSize);
+	bzero ((char *) nht[i].HashTable,
+	       sizeof (novel_hash_rec) * nht[i].HashSize);
+	nht[i].HashUsed = 0;
+	nht[i].HashSize = INITIAL_HASH_SIZE;
+	nht[i].pool = nht[i].first_pool = new_pool (NULL);
+	nht[i].next_num = 1;
+	nht[i].binary_start = 1;
+	if (cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+	  nht[i].code_to_nhr = Xmalloc (sizeof (novel_hash_rec *) *
+					((nht[i].HashSize >> 1) + 2));
+	else
+	  nht[i].code_to_nhr = NULL;
+	if (cdh.novel_method == MG_NOVEL_HYBRID ||
+	    cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+	  {
+	    int num;
+	    num = 1;
+	    blk_start[i][0] = 0;
+	    blk_end[i][0] = cdh.num_words[i] - 1;
+	    while (num < 33)
+	      {
+		blk_start[i][num] = blk_end[i][num - 1] + 1;
+		blk_end[i][num] = blk_start[i][num] +
+		  (blk_end[i][num - 1] - blk_start[i][num - 1]) * 2;
+		num++;
+	      }
+	  }
+      }
+
+  return (COMPALLOK);
+}
+
+
+
+int 
+ic (void *a, void *b)
+{
+  return *((int *) a) - *((int *) b);
+}
+
+
+
+/* #define DOCDUMP 477 */
+
+int 
+process_text_2 (u_char * s_in, int l_in)
+{
+  int which, byte_length;
+  u_char *end = s_in + l_in - 1;
+  int novels_used[2];
+  int swaps[2][MAX_SWAPS];
+
+  which = inaword (s_in, end);
+
+  ENCODE_START (comp_buffer, buf_size)
+
+    ENCODE_BIT (which);
+
+  if (cdh.novel_method == MG_NOVEL_BINARY)
+    {
+      DELTA_ENCODE_L (nht[0].binary_start, prefix_len);
+      DELTA_ENCODE_L (nht[1].binary_start, prefix_len);
+    }
+
+  novels_used[0] = novels_used[1] = 0;
+
+#ifdef DOCDUMP
+  if (cth.num_of_docs == DOCDUMP)
+    {
+      printf ("---------------------------------------------------\n");
+      printf ("which = %d\n", which);
+    }
+#endif
+
+  for (; s_in <= end; which = !which)
+    {
+      u_char Word[MAXWORDLEN + 1];
+      int res;
+
+      if (which)
+	cth.num_of_words++;
+
+      /* First parse a word or non-word out of the string */
+      if (which)
+	PARSE_WORD (Word, s_in, end);
+      else
+	PARSE_NON_WORD (Word, s_in, end);
+
+#ifdef DOCDUMP
+      if (cth.num_of_docs == DOCDUMP)
+	{
+	  printf ("%sword : \"%.*s\"", which ? "    " : "non-", Word[0], Word + 1);
+	}
+#endif
+
+      /* Search the hash table for Word */
+      if (ht[which])
+	{
+	  register unsigned long hashval, step;
+	  register int tsize = ht[which]->size;
+	  register u_char **wptr;
+	  HASH (hashval, step, Word, tsize);
+	  for (;;)
+	    {
+	      register u_char *s1;
+	      register u_char *s2;
+	      register int len;
+	      wptr = ht[which]->table[hashval];
+	      if (wptr == NULL)
+		{
+		  res = COMPERROR;
+		  break;
+		}
+
+	      /* Compare the words */
+	      s1 = Word;
+	      s2 = *wptr;
+	      len = *s1 + 1;
+	      for (; len; len--)
+		if (*s1++ != *s2++)
+		  break;
+
+	      if (len)
+		{
+		  hashval += step;
+		  if (hashval >= tsize)
+		    hashval -= tsize;
+		}
+	      else
+		{
+		  res = ht[which]->table[hashval] - ht[which]->words;
+		  break;
+		}
+	    }
+	}
+      else
+	res = COMPERROR;
+      /* Check that the word was found in the dictionary */
+      if (res == COMPERROR)
+	{
+	  if (cdh.dict_type == MG_COMPLETE_DICTIONARY)
+	    {
+	      Message ("Unknown word \"%.*s\"\n", *Word, Word + 1);
+	      return (COMPERROR);
+	    }
+	  if (cdh.dict_type == MG_PARTIAL_DICTIONARY)
+	    {
+	      u_long i;
+	      if (ht[which])
+		{
+		  res = ht[which]->hd->num_codes - 1;
+		  HUFF_ENCODE (res, ht[which]->codes, ht[which]->hd->clens);
+		}
+	      HUFF_ENCODE (Word[0], lens_codes[which], lens_huff[which].clens);
+	      for (i = 0; i < Word[0]; i++)
+		HUFF_ENCODE (Word[i + 1], char_codes[which],
+			     char_huff[which].clens);
+	    }
+	  if (cdh.dict_type == MG_SEED_DICTIONARY)
+	    {
+	      if (ht[which])
+		{
+		  res = ht[which]->hd->num_codes - 1;
+		  HUFF_ENCODE (res, ht[which]->codes, ht[which]->hd->clens);
+		}
+	      switch (cdh.novel_method)
+		{
+		case MG_NOVEL_HUFFMAN_CHARS:
+		  {
+		    u_long i;
+		    HUFF_ENCODE (Word[0], lens_codes[which],
+				 lens_huff[which].clens);
+		    for (i = 0; i < Word[0]; i++)
+		      HUFF_ENCODE (Word[i + 1], char_codes[which],
+				   char_huff[which].clens);
+		  }
+		  break;
+		case MG_NOVEL_BINARY:
+		case MG_NOVEL_DELTA:
+		case MG_NOVEL_HYBRID:
+		case MG_NOVEL_HYBRID_MTF:
+		  {
+		    register unsigned long hashval, step;
+		    register novel_hash_table *h = &nht[which];
+		    register int hsize = h->HashSize;
+		    register novel_hash_rec *ent;
+		    HASH (hashval, step, Word, hsize);
+		    for (;;)
+		      {
+			register u_char *s1, *s2;
+			register int len;
+			ent = h->HashTable + hashval;
+			if (!ent->word)
+			  {
+			    int len = *Word + 1;
+			    if (len > h->pool->left)
+			      h->pool = new_pool (h->pool);
+			    ent->word = h->pool->ptr;
+			    ent->ordinal_num = h->next_num++;
+			    if (cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+			      h->code_to_nhr[ent->ordinal_num - 1] = ent;
+			    memcpy (h->pool->ptr, Word, len);
+			    h->pool->ptr += len;
+			    h->pool->left -= len;
+			    h->HashUsed++;
+			    break;
+			  }
+			/* Compare the words */
+			s1 = Word;
+			s2 = ent->word;
+			len = *s1 + 1;
+			for (; len; len--)
+			  if (*s1++ != *s2++)
+			    break;
+
+			if (!len)
+			  break;
+
+			hashval = (hashval + step);
+			if (hashval >= hsize)
+			  hashval -= hsize;
+		      }
+
+		    switch (cdh.novel_method)
+		      {
+		      case MG_NOVEL_BINARY:
+			{
+			  BINARY_ENCODE (ent->ordinal_num, h->binary_start);
+			  if (ent->ordinal_num == h->binary_start)
+			    h->binary_start++;
+			}
+			break;
+		      case MG_NOVEL_DELTA:
+			{
+			  DELTA_ENCODE (ent->ordinal_num);
+			}
+			break;
+		      case MG_NOVEL_HYBRID:
+			{
+			  int k = 0;
+			  int j = ent->ordinal_num - 1;
+			  while (j > blk_end[which][k])
+			    k++;
+			  assert (j - blk_start[which][k] + 1 >= 1 &&
+				  j - blk_start[which][k] + 1 <=
+			       blk_end[which][k] - blk_start[which][k] + 1);
+
+			  GAMMA_ENCODE (k + 1);
+			  BINARY_ENCODE (j - blk_start[which][k] + 1,
+					 blk_end[which][k] -
+					 blk_start[which][k] + 1);
+			}
+			break;
+		      case MG_NOVEL_HYBRID_MTF:
+			{
+			  int k = 0;
+			  int j = ent->ordinal_num - 1;
+			  while (j > blk_end[which][k])
+			    k++;
+			  assert (j - blk_start[which][k] + 1 >= 1 &&
+				  j - blk_start[which][k] + 1 <=
+			       blk_end[which][k] - blk_start[which][k] + 1);
+			  GAMMA_ENCODE (k + 1);
+			  BINARY_ENCODE (j - blk_start[which][k] + 1,
+					 blk_end[which][k] -
+					 blk_start[which][k] + 1);
+
+			  if (ent->ordinal_num - 1 >= novels_used[which])
+			    {
+			      int a = novels_used[which];
+			      int b = ent->ordinal_num - 1;
+			      novel_hash_rec *temp;
+
+
+/*                            fprintf(stderr, "a = %d , b = %d\n", a, b);
+ */
+			      temp = h->code_to_nhr[a];
+			      h->code_to_nhr[a] = h->code_to_nhr[b];
+			      h->code_to_nhr[b] = temp;
+			      h->code_to_nhr[a]->ordinal_num = a + 1;
+			      h->code_to_nhr[b]->ordinal_num = b + 1;
+			      if (novels_used[which] == MAX_SWAPS)
+				FatalError (1, "Not enough mem for swapping");
+			      swaps[which][novels_used[which]] = b;
+			      novels_used[which]++;
+			    }
+			}
+			break;
+		      }
+		    if (h->HashUsed >= h->HashSize >> 1)
+		      {
+			novel_hash_rec *ht;
+			unsigned long size;
+			unsigned long i;
+			size = prime (h->HashSize * 2);
+			if (cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+			  {
+			    Xfree (h->code_to_nhr);
+			    h->code_to_nhr = Xmalloc (sizeof (novel_hash_rec *) *
+						      ((size >> 1) + 2));
+			  }
+			if (!(ht = Xmalloc (sizeof (novel_hash_rec) * size)))
+			  {
+			    Message ("Unable to allocate memory for table");
+			    return (COMPERROR);
+			  }
+			bzero ((char *) ht, sizeof (novel_hash_rec) * size);
+
+			for (i = 0; i < h->HashSize; i++)
+			  if (h->HashTable[i].word)
+			    {
+			      register u_char *wptr;
+			      register unsigned long hashval, step;
+
+			      wptr = h->HashTable[i].word;
+			      HASH (hashval, step, wptr, size);
+			      wptr = (ht + hashval)->word;
+			      while (wptr)
+				{
+				  hashval += step;
+				  if (hashval >= size)
+				    hashval -= size;
+				  wptr = (ht + hashval)->word;
+				}
+			      ht[hashval] = h->HashTable[i];
+			      if (cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+				h->code_to_nhr[ht[hashval].ordinal_num - 1] =
+				  &ht[hashval];
+			    }
+			Xfree (h->HashTable);
+			h->HashTable = ht;
+			h->HashSize = size;
+		      }
+		  }
+		  break;
+		}
+	    }
+	}
+      else
+	{
+	  HUFF_ENCODE (res, ht[which]->codes, ht[which]->hd->clens);
+#ifdef DOCDUMP
+	  if (cth.num_of_docs == DOCDUMP)
+	    {
+	      printf ("   %d %d\n", ht[which]->hd->clens[res],
+		      ht[which]->codes[res]);
+	    }
+#endif
+	}
+    }
+
+
+  /* Add a 1 bit onto the end of the buffer the remaining bits in the last
+     byte will all be zero */
+
+  ENCODE_BIT (1);
+
+  ENCODE_FLUSH;
+
+  byte_length = __pos - __base;
+  if (!__remaining)
+    {
+      Message ("The end of the buffer was probably overrun");
+      return COMPERROR;
+    }
+
+  ENCODE_DONE
+
+#ifdef DOCDUMP
+    if (cth.num_of_docs == DOCDUMP)
+    {
+      printf ("unused bits = %d\n", bits_unused);
+    }
+#endif
+
+  HTONUL(text_length);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&text_length, sizeof (text_length), 1, text_idx);
+  NTOHUL(text_length);  /* [RPAP - Jan 97: Endian Ordering] */
+  text_length += byte_length;
+
+#ifdef DOCDUMP
+  if (cth.num_of_docs == DOCDUMP)
+    {
+      int i;
+      for (i = 0; i < byte_length; i++)
+	printf ("%02x ", comp_buffer[i]);
+      printf ("\n");
+    }
+#endif
+
+  if (cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    for (which = 0; which <= 1; which++)
+      for (novels_used[which]--; novels_used[which] >= 0; novels_used[which]--)
+	{
+	  int a = novels_used[which];
+	  int b = swaps[which][novels_used[which]];
+	  novel_hash_rec *temp;
+	  temp = nht[which].code_to_nhr[a];
+	  nht[which].code_to_nhr[a] = nht[which].code_to_nhr[b];
+	  nht[which].code_to_nhr[b] = temp;
+	  nht[which].code_to_nhr[a]->ordinal_num = a + 1;
+	  nht[which].code_to_nhr[b]->ordinal_num = b + 1;
+	}
+
+
+  fwrite (comp_buffer, sizeof (*comp_buffer), byte_length, text);
+
+  if ((double) l_in / (double) byte_length > cth.ratio)
+    cth.ratio = (double) l_in / (double) byte_length;
+
+  cth.num_of_docs++;
+  if (l_in > cth.length_of_longest_doc)
+    cth.length_of_longest_doc = l_in;
+
+  cth.num_of_bytes += l_in;
+
+  if (Comp_Stats)
+    {
+      stats_in_tot_bytes += l_in;
+      stats_in_bytes += l_in;
+      stats_out_bytes += byte_length;
+      if (stats_in_bytes >= comp_stat_point)
+	{
+	  fprintf (Comp_Stats, "%10.0f %10.0f %10.0f %f\n", stats_in_tot_bytes,
+		   stats_in_bytes, stats_out_bytes,
+		   (double) stats_out_bytes / (double) stats_in_bytes);
+	  stats_in_bytes = 0.0;
+	  stats_out_bytes = 0.0;
+	}
+    }
+
+  return COMPALLOK;
+}
+
+
+
+
+
+
+int 
+write_aux_dict (char *FileName)
+{
+  int i;
+  FILE *aux;
+  if (!(aux = create_file (FileName, TEXT_DICT_AUX_SUFFIX, "wb",
+			   MAGIC_AUX_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf(stderr,"Couldn't create file %s%s:%s\n",
+              FileName, TEXT_DICT_AUX_SUFFIX,
+#if defined(HAVE_STRERROR) || defined(__WIN32__)
+              strerror(errno)
+#else
+              " "
+#endif
+              );
+      return COMPERROR;
+    }
+  for (i = 0; i <= 1; i++)
+    {
+      aux_frags_header afh;
+      char_pool *cp;
+
+      afh.num_frags = nht[i].HashUsed;
+      afh.mem_for_frags = 0;
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	afh.mem_for_frags += POOL_SIZE - cp->left;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(afh.num_frags);
+      HTONUL(afh.mem_for_frags);
+
+      fwrite (&afh, sizeof (afh), 1, aux);
+
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	fwrite (cp->pool, POOL_SIZE - cp->left, sizeof (u_char), aux);
+    }
+  fclose (aux);
+  return COMPALLOK;
+}
+
+
+void 
+estimate_compressed_aux_dict (void)
+{
+  int i;
+  u_long aux_compressed = 0, total_uncomp = 0;
+  for (i = 0; i <= 1; i++)
+    {
+      int j;
+      long chars[256], fchars[256];
+      long lens[16], flens[16];
+      char_pool *cp;
+      bzero ((char *) chars, sizeof (chars));
+      bzero ((char *) lens, sizeof (lens));
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	{
+	  u_char *buf = cp->pool;
+	  while (buf != cp->ptr)
+	    {
+	      int len = *buf++;
+	      lens[len]++;
+	      total_uncomp += len + 4;
+	      for (; len; len--)
+		chars[*buf++]++;
+	    }
+	}
+      for (j = 0; j < 256; j++)
+	if (!chars[j] && PESINAWORD (j) == i)
+	  fchars[j] = 1;
+	else
+	  fchars[j] = chars[j];
+      for (j = 0; j < 16; j++)
+	if (!lens[j])
+	  flens[j] = 1;
+	else
+	  flens[j] = lens[j];
+
+      aux_compressed += (Calculate_Huffman_Size (16, flens, lens) +
+			 Calculate_Huffman_Size (256, fchars, chars)) / 8;
+
+    }
+
+  Message ("Aux dictionary (Uncompressed) %.2f Mb ( %u bytes   %0.3f %%)",
+	   total_uncomp / 1024.0 / 1024, total_uncomp,
+	   (total_uncomp * 100.0) / bytes_processed);
+  Message ("Aux dictionary (Compressed)   %.2f Mb ( %.0f bytes   %0.3f %%)",
+	   aux_compressed / 1024.0 / 1024, aux_compressed * 1.0,
+	   (aux_compressed * 100.0) / bytes_processed);
+}
+
+
+
+
+
+
+int 
+done_text_2 (char *FileName)
+{
+  if (Comp_Stats)
+    fprintf (Comp_Stats, "%10.0f %10.0f %10.0f %f\n", stats_in_tot_bytes,
+	     stats_in_bytes, stats_out_bytes,
+	     (double) stats_out_bytes / (double) stats_in_bytes);
+
+  HTONUL(text_length);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&text_length, sizeof (text_length), 1, text_idx);
+  NTOHUL(text_length);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(cth.num_of_docs);
+  HTOND(cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+  HTONUL(cth.num_of_words);
+  HTONUL(cth.length_of_longest_doc);
+  HTOND(cth.ratio);
+
+  if (fseek (text_idx, sizeof (u_long), SEEK_SET) == -1 ||
+      fwrite (&cth, sizeof (cth), 1, text_idx) != 1)
+    return COMPERROR;
+  fclose (text_idx);
+
+  if (fseek (text, sizeof (u_long), SEEK_SET) == -1 ||
+      fwrite (&cth, sizeof (cth), 1, text) != 1)
+    return COMPERROR;
+  fclose (text);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cth.num_of_docs);
+  NTOHD(cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(cth.num_of_words);
+  NTOHUL(cth.length_of_longest_doc);
+  NTOHD(cth.ratio);
+  
+
+  Message ("Compressed Text %.2f Mb ( %u bytes   %0.3f %%)",
+	   text_length / 1024.0 / 1024.0, text_length,
+	   (text_length * 100.0) / bytes_processed);
+  Message ("Words portion of the dictionary %.2f Mb ( %.0f bytes   %0.3f %%)",
+	   Words_disk / 1024.0 / 1024, Words_disk * 1.0,
+	   (Words_disk * 100.0) / bytes_processed);
+
+  if (cdh.dict_type != MG_COMPLETE_DICTIONARY &&
+      (cdh.novel_method == MG_NOVEL_BINARY ||
+       cdh.novel_method == MG_NOVEL_DELTA ||
+       cdh.novel_method == MG_NOVEL_HYBRID ||
+       cdh.novel_method == MG_NOVEL_HYBRID_MTF))
+    {
+      if (write_aux_dict (FileName) == COMPERROR)
+	return COMPERROR;
+      estimate_compressed_aux_dict ();
+    }
+  else
+    {
+      if (cdh.dict_type != MG_COMPLETE_DICTIONARY)
+	Message ("Huffman info for chars in dictionary %.2f Mb"
+		 " ( %u bytes   %0.3f %%)",
+		 Chars_disk / 1024.0 / 1024, Chars_disk,
+		 (Chars_disk * 100.0) / bytes_processed);
+      unlink (make_name (FileName, TEXT_DICT_AUX_SUFFIX, NULL));
+    }
+
+  return (COMPALLOK);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.c	(revision 16561)
@@ -0,0 +1,1090 @@
+/**************************************************************************
+ *
+ * text_get.c -- Function for reading documents from the compressed text
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "filestats.h"
+#include "timing.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "huffman.h"
+#include "bitio_m_mem.h"
+#include "bitio_m.h"
+#include "bitio_stdio.h"
+#include "huffman_stdio.h"
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "text_get.h"
+#include "locallib.h"
+#include "words.h"
+#include "mg_errors.h"
+#include "local_strings.h"
+
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:26  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:35:48  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.3  1994/10/20  03:57:11  tes
+   * I have rewritten the boolean query optimiser and abstracted out the
+   * components of the boolean query.
+   *
+   * Revision 1.2  1994/09/20  04:42:15  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+
+
+
+/* FetchDocStart () 
+ * Reads into DocEnt the starting position of the document in the *.text file
+ * Where the first document is document number 1
+ * It returns the true weight of the document.
+ */
+
+
+
+
+static double 
+FetchDocStartLev1 (text_data * td, u_long DN,
+		   u_long * seek_pos, u_long * len)
+{
+  unsigned long data[2];
+  /* [TS:Sep/94] Fixed up the seek call to give the correct offset */
+  Fseek (td->TextIdxFile,
+	 sizeof (unsigned long) * (DN - 1) +	/* the doc offsets */
+	 sizeof (unsigned long) +	/* the magic number */
+	 sizeof (compressed_text_header),	/* the header */
+	 0);
+  Fread ((char *) &data, sizeof (data), 1, td->TextIdxFile);
+  
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(data[0]);
+  NTOHUL(data[1]);
+
+  *seek_pos = data[0];
+  *len = data[1] - data[0];
+  return (1.0);
+}
+
+#define MG_PAGE_SIZE 2048
+
+static int 
+LoadIdx (text_data * td, unsigned long DN)
+{
+  if (!td->idx_data)
+    {
+      td->idx_data = Xmalloc (sizeof (*(td->idx_data)) * MG_PAGE_SIZE);
+      if (!td->idx_data)
+	FatalError (1, "Out of memory in FDSL2");
+    }
+  if (td->current_pos == -1 || DN >= td->current_pos + MG_PAGE_SIZE - 1 ||
+      DN < td->current_pos)
+    {
+      int i, num;  /* [RPAP - Jan 97: Endian Ordering] */
+
+      long rn = (long) DN - (MG_PAGE_SIZE >> 1);
+      if (rn < 1)
+	rn = 1;
+      Fseek (td->TextIdxWgtFile, (sizeof (unsigned long) + sizeof (float)) *
+	       (rn - 1) + sizeof (unsigned long), 0);
+      num = Fread ((char *) td->idx_data, sizeof (*(td->idx_data)), MG_PAGE_SIZE, /* [RPAP - Jan 97: Endian Ordering] */
+		   td->TextIdxWgtFile);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = 0; i < num; i++)
+	{
+	  NTOHUL(td->idx_data[i].Start);
+	  NTOHF(td->idx_data[i].Weight);
+	}
+
+      td->current_pos = rn;
+    }
+  return DN - td->current_pos;
+}
+
+static double 
+FDSL2 (text_data * td, unsigned long DN, unsigned long *Pos)
+{
+  unsigned long pos = LoadIdx (td, DN);
+  *Pos = td->idx_data[pos].Start;
+  return (td->idx_data[pos].Weight);
+}
+
+
+static double 
+FetchDocStartLev2 (text_data * td, u_long DN,
+		   u_long * seek_pos, u_long * len)
+{
+  double Weight;
+  unsigned long s1, s2;
+  Weight = FDSL2 (td, DN, &s1);
+  do
+    {
+      DN++;
+      FDSL2 (td, DN, &s2);
+    }
+  while (s2 == s1);
+  *seek_pos = s1;
+  *len = s2 - s1;
+  return (Weight);
+}
+
+
+
+
+double 
+FetchDocStart (query_data * qd, u_long DN, u_long * seek_pos, u_long * len)
+{
+  qd->text_idx_lookups++;
+  if (qd->td->TextIdxWgtFile)
+    return FetchDocStartLev2 (qd->td, DN, seek_pos, len);
+  else
+    return FetchDocStartLev1 (qd->td, DN, seek_pos, len);
+}
+
+unsigned long 
+FetchInitialParagraph (text_data * td, unsigned long ParaNum)
+{
+  if (td->TextIdxWgtFile)
+    {
+      unsigned long pos;
+      unsigned long start;
+      int PN = ParaNum - 1;
+      pos = LoadIdx (td, ParaNum);
+      start = td->idx_data[pos].Start;
+      while (PN > 0)
+	{
+	  pos = LoadIdx (td, PN);
+	  if (td->idx_data[pos].Start != start)
+	    return PN + 1;
+	  PN--;
+	}
+      return PN + 1;
+    }
+  else
+    return ParaNum;
+}
+
+
+
+/* FetchCompressed () 
+ * Reads into buffer DocBuff the compressed form of document DocNum. 
+ * Where the first document is document number 1
+ */
+int 
+FetchCompressed (query_data * qd, char **DocBuff, DocEntry * DocEnt)
+{
+  if (!DocEnt->SeekPos)
+    FetchDocStart (qd, DocEnt->DocNum, &DocEnt->SeekPos, &DocEnt->Len);
+  if (!(*DocBuff = Xmalloc (DocEnt->Len)))
+    return (-1);
+
+  if (Fseek (qd->td->TextFile, DocEnt->SeekPos, 0) == -1)
+    FatalError (1, "Error when seeking into text file");
+#if 0
+  printf ("Loading compressed text %d %d\n", DocEnt->SeekPos, DocEnt->Len);
+#endif
+  if (Fread (*DocBuff, 1, DocEnt->Len, qd->td->TextFile) != DocEnt->Len)
+    FatalError (1, "Error when reading data");
+
+  return (DocEnt->Len);
+
+}
+
+
+text_data *
+LoadTextData (File * text, File * text_idx_wgt, File * text_idx)
+{
+  text_data *td;
+
+  if (!(td = Xmalloc (sizeof (text_data))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  td->TextFile = text;
+  td->TextIdxWgtFile = text_idx_wgt;
+  td->TextIdxFile = text_idx;
+  td->current_pos = -1;
+  td->idx_data = NULL;
+  Fread (&td->cth, sizeof (td->cth), 1, text);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(td->cth.num_of_docs);
+  NTOHD(td->cth.num_of_bytes); /* [RJM 07/97: 4G limit] */
+  NTOHUL(td->cth.num_of_words);
+  NTOHUL(td->cth.length_of_longest_doc);
+  NTOHD(td->cth.ratio);
+
+  return (td);
+}
+
+
+void 
+FreeTextData (text_data * td)
+{
+  if (td)
+    {
+      if (td->idx_data)
+	Xfree (td->idx_data);
+      Xfree (td);
+    }
+}
+
+
+static int 
+pts_comp (const void *A, const void *B)
+{
+  const DocEntry *const *a = A;
+  const DocEntry *const *b = B;
+  return (*a)->DocNum - (*b)->DocNum;
+}
+
+
+
+
+int 
+GetPosLens (query_data * qd, DocEntry * Docs, int num)
+{
+  DocEntry **pts;
+  int i, j;
+  if (!(pts = Xmalloc (num * sizeof (DocEntry *))))
+    {
+      mg_errno = MG_NOMEM;
+      return (-1);
+    }
+  for (i = j = 0; i < num; i++, Docs++)
+    if (!Docs->SeekPos)
+      pts[j++] = Docs;
+
+  if (j)
+    {
+      qsort (pts, j, sizeof (DocEntry *), pts_comp);
+      for (i = 0; i < j; i++)
+	FetchDocStart (qd, pts[i]->DocNum, &pts[i]->SeekPos, &pts[i]->Len);
+    }
+
+  Xfree (pts);
+  return (0);
+}
+
+
+
+
+
+int 
+LoadBuffers (query_data * qd, DocEntry * Docs, int max_mem, int num)
+{
+  DocEntry **pts;
+  int i, j;
+  int mem;
+
+  if (!num)
+    return (0);
+  if (!(pts = Xmalloc (num * sizeof (DocEntry *))))
+    {
+      mg_errno = MG_NOMEM;
+      return (-1);
+    }
+
+  mem = i = 0;
+  do
+    {
+      pts[i] = Docs;
+      mem += Docs->Len;
+      i++;
+      Docs++;
+    }
+  while (i < num && mem < max_mem);
+  if (i > 1)
+    qsort (pts, i, sizeof (DocEntry *), pts_comp);
+  for (j = 0; j < i; j++)
+    {
+      if (FetchCompressed (qd, &pts[j]->CompTextBuffer, pts[j]) == -1)
+	return (-1);
+      ChangeMemInUse (qd, pts[j]->Len);
+    }
+
+  Xfree (pts);
+
+  return (i);
+}
+
+
+
+
+
+void 
+FreeBuffers (query_data * qd, DocEntry * Docs, int num)
+{
+  int i;
+  for (i = 0; i < num; i++, Docs++)
+    if (Docs->CompTextBuffer)
+      {
+	Xfree (Docs->CompTextBuffer);
+	Docs->CompTextBuffer = NULL;
+	ChangeMemInUse (qd, -Docs->Len);
+      }
+}
+
+
+
+/****************************************************************************/
+
+static void 
+FreeAuxDict (auxiliary_dict * ad)
+{
+  if (!ad)
+    return;
+  if (ad->word_data[0])
+    Xfree (ad->word_data[0]);
+  if (ad->word_data[1])
+    Xfree (ad->word_data[1]);
+  if (ad->words[0])
+    Xfree (ad->words[0]);
+  if (ad->words[1])
+    Xfree (ad->words[1]);
+  Xfree (ad);
+}
+
+static auxiliary_dict *
+LoadAuxDict (compression_dict * cd, File * text_aux_dict)
+{
+  auxiliary_dict *ad;
+  int i;
+
+  if (!(ad = Xmalloc (sizeof (auxiliary_dict))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  bzero ((char *) ad, sizeof (*ad));
+
+  for (i = 0; i <= 1; i++)
+    {
+      int j;
+      u_char *pos;
+
+      Fread (&ad->afh[i], sizeof (aux_frags_header), 1, text_aux_dict);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(ad->afh[i].num_frags);
+      NTOHUL(ad->afh[i].mem_for_frags);
+
+      if (!(ad->word_data[i] = Xmalloc (ad->afh[i].mem_for_frags)))
+	{
+	  mg_errno = MG_NOMEM;
+	  FreeAuxDict (ad);
+	  return (NULL);
+	}
+      if (!(ad->words[i] = Xmalloc (ad->afh[i].num_frags * sizeof (u_char *))))
+	{
+	  mg_errno = MG_NOMEM;
+	  FreeAuxDict (ad);
+	  return (NULL);
+	}
+
+      Fread (ad->word_data[i], ad->afh[i].mem_for_frags, sizeof (u_char),
+	     text_aux_dict);
+
+      pos = ad->word_data[i];
+      for (j = 0; j < ad->afh[i].num_frags; j++)
+	{
+	  ad->words[i][j] = pos;
+	  pos += *pos + 1;
+	}
+      if (cd->cdh.novel_method == MG_NOVEL_HYBRID ||
+	  cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+	{
+	  int num;
+	  num = 1;
+	  ad->blk_start[i][0] = 0;
+	  ad->blk_end[i][0] = cd->cdh.num_words[i] - 1;
+	  while (num < 33)
+	    {
+	      ad->blk_start[i][num] = ad->blk_end[i][num - 1] + 1;
+	      ad->blk_end[i][num] = ad->blk_start[i][num] +
+		(ad->blk_end[i][num - 1] - ad->blk_start[i][num - 1]) * 2;
+	      num++;
+	    }
+	}
+    }
+  return (ad);
+}
+
+
+
+
+
+
+static u_char ***
+ReadInWords (File * dict, compression_dict * cd,
+	     comp_frags_header * cfh, u_char ** escape)
+{
+  int i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+  int num_set[MAX_HUFFCODE_LEN + 1];
+  u_char *next_word[MAX_HUFFCODE_LEN + 1];
+  u_char **vals;
+  u_char ***values;
+  u_char word[MAXWORDLEN + 1];
+  u_char last_word[MAX_HUFFCODE_LEN + 1][MAXWORDLEN + 1];
+
+  lookback = cd->cdh.lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; i++)
+    {
+      ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+      mem_reqd += cfh->huff_words_size[i];
+    }
+
+  if (!(vals = Xmalloc (ptrs_reqd * sizeof (*vals))))
+    return (NULL);
+
+  if (!(values = Xmalloc ((MAX_HUFFCODE_LEN + 1) * sizeof (u_char **))))
+    return (NULL);
+
+  if (!(next_word[0] = Xmalloc (mem_reqd))) return (NULL);
+
+  cd->MemForCompDict += ptrs_reqd * sizeof (*vals) +
+    (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **) +
+    mem_reqd;
+
+  values[0] = vals;
+  values[0][0] = next_word[0];
+  for (i = 1; i <= cfh->hd.maxcodelen; i++)
+    {
+      int next_start = (values[i - 1] - vals) +
+      ((cfh->hd.lencount[i - 1] + ((1 << lookback) - 1)) >> lookback);
+      values[i] = &vals[next_start];
+      next_word[i] = next_word[i - 1] + cfh->huff_words_size[i - 1];
+      values[i][0] = next_word[i];
+    }
+
+  bzero ((char *) num_set, sizeof (num_set));
+
+  for (i = 0; i < cfh->hd.num_codes; i++)
+    {
+      register int val, copy;
+      register int len = cfh->hd.clens[i];
+      val = Getc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      Fread (word + copy + 1, sizeof (u_char), val, dict);
+      *word = val + copy;
+
+      if ((num_set[len] & ((1 << lookback) - 1)) == 0)
+	{
+	  values[len][num_set[len] >> lookback] = next_word[len];
+	  memcpy (next_word[len], word, *word + 1);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    *escape = next_word[len];
+	  next_word[len] += *word + 1;
+	}
+      else
+	{
+	  copy = prefixlen (last_word[len], word);
+	  memcpy (next_word[len] + 1, word + copy + 1, *word - copy);
+	  *next_word[len] = (copy << 4) + (*word - copy);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    *escape = next_word[len];
+	  next_word[len] += (*word - copy) + 1;
+	}
+      memcpy (last_word[len], word, *word + 1);
+      num_set[len]++;
+    }
+  if (cfh->hd.clens)
+    Xfree (cfh->hd.clens);
+  cfh->hd.clens = NULL;
+  return values;
+}
+
+
+static compression_dict *
+Load_Comp_Dict (File * dict, File * aux_dict)
+{
+  int which;
+  compression_dict *cd;
+
+  if (!(cd = Xmalloc (sizeof (compression_dict))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  bzero ((char *) cd, sizeof (compression_dict));
+
+  cd->MemForCompDict = sizeof (compression_dict);
+
+  if (F_Read_cdh (dict, &cd->cdh, &cd->MemForCompDict, NULL) == -1)
+    return NULL;
+
+  for (which = 0; which < 2; which++)
+    switch (cd->cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  if (!(cd->cfh[which] = Xmalloc (sizeof (*cd->cfh[which]))))
+	    return NULL;
+	  cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	  if (F_Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL) == -1)
+	    return NULL;
+
+	  if (!(cd->values[which] = ReadInWords (dict, cd, cd->cfh[which],
+						 NULL)))
+	    return NULL;
+	  cd->escape[which] = NULL;
+
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  huff_data *hd;
+	  u_long **vals;
+	  if (cd->cdh.num_words[which])
+	    {
+	      if (!(cd->cfh[which] = Xmalloc (sizeof (*cd->cfh[which]))))
+		return NULL;
+	      cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	      if (F_Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL) == -1)
+		return NULL;
+
+	      if (!(cd->values[which] = ReadInWords (dict, cd, cd->cfh[which],
+						     &cd->escape[which])))
+		return NULL;
+	    }
+	  if (!(hd = Xmalloc (sizeof (huff_data))))
+	    return NULL;
+	  cd->MemForCompDict += sizeof (huff_data);
+	  if (F_Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL) == -1)
+	    return NULL;
+	  if (!(vals = Generate_Huffman_Vals (hd, &cd->MemForCompDict)))
+	    return NULL;
+	  if (hd->clens)
+	    Xfree (hd->clens);
+	  hd->clens = NULL;
+	  cd->chars_huff[which] = hd;
+	  cd->chars_vals[which] = vals;
+	  if (!(hd = Xmalloc (sizeof (huff_data))))
+	    return NULL;
+	  cd->MemForCompDict += sizeof (huff_data);
+	  if (F_Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL) == -1)
+	    return NULL;
+	  if (!(vals = Generate_Huffman_Vals (hd, &cd->MemForCompDict)))
+	    return NULL;
+	  cd->lens_huff[which] = hd;
+	  cd->lens_vals[which] = vals;
+	  if (hd->clens)
+	    Xfree (hd->clens);
+	  hd->clens = NULL;
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  huff_data *hd;
+	  u_long **vals;
+	  if (cd->cdh.num_words[which])
+	    {
+	      if (!(cd->cfh[which] = Xmalloc (sizeof (*cd->cfh[which]))))
+		return NULL;
+	      cd->MemForCompDict += sizeof (*cd->cfh[which]);
+	      if (F_Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL) == -1)
+		return NULL;
+
+	      if (!(cd->values[which] = ReadInWords (dict, cd, cd->cfh[which],
+						     &cd->escape[which])))
+		return NULL;
+	    }
+	  switch (cd->cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      if (!(hd = Xmalloc (sizeof (huff_data))))
+		return NULL;
+	      cd->chars_huff[which] = hd;
+	      cd->MemForCompDict += sizeof (huff_data);
+	      if (F_Read_Huffman_Data (dict, hd, &cd->MemForCompDict,
+				       NULL) == -1)
+		return NULL;
+	      if (!(vals = Generate_Huffman_Vals (hd, &cd->MemForCompDict)))
+		return NULL;
+	      cd->chars_vals[which] = vals;
+	      if (hd->clens)
+		Xfree (hd->clens);
+	      hd->clens = NULL;
+	      if (!(hd = Xmalloc (sizeof (huff_data))))
+		return NULL;
+	      cd->MemForCompDict += sizeof (huff_data);
+	      cd->lens_huff[which] = hd;
+	      if (F_Read_Huffman_Data (dict, hd, &cd->MemForCompDict
+				       ,NULL) == -1)
+		return NULL;
+	      if (!(vals = Generate_Huffman_Vals (hd, &cd->MemForCompDict)))
+		return NULL;
+	      cd->lens_vals[which] = vals;
+	      if (hd->clens)
+		Xfree (hd->clens);
+	      hd->clens = NULL;
+	      break;
+	    case MG_NOVEL_BINARY:
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    case MG_NOVEL_HYBRID_MTF:
+	      break;
+	    }
+	  break;
+	}
+      }
+
+  if (cd->cdh.novel_method == MG_NOVEL_BINARY ||
+      cd->cdh.novel_method == MG_NOVEL_DELTA ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    {
+      if (!aux_dict)
+	{
+	  mg_errno = MG_NOFILE;
+	  FreeCompDict (cd);
+	  return (NULL);
+	}
+
+      if (!(cd->ad = LoadAuxDict (cd, aux_dict)))
+	{
+	  FreeCompDict (cd);
+	  return (NULL);
+	}
+    }
+
+
+  mg_errno = MG_NOERROR;
+
+  cd->fast_loaded = 0;
+  return (cd);
+}
+
+#define WORDNO(p, base) ((((char*)(p))-((char*)(base)))/sizeof(u_char*))
+
+#define IS_FIXUP(p) ((fixup[WORDNO(p,cd)/8] & (1<<(WORDNO(p, cd) & 7))) != 0)
+
+
+static compression_dict *
+Load_Fast_Comp_Dict (File * text_fast_comp_dict)
+{
+  compression_dict *cd;
+  u_long *p, *end;
+  u_char *fixup;
+  u_long mem;
+  u_long fixup_mem;
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  Fread (&mem, sizeof (mem), 1, text_fast_comp_dict);
+  NTOHUL(mem);  /* [RPAP - Jan 97: Endian Ordering] */
+  Fread (&fixup_mem, sizeof (fixup_mem), 1, text_fast_comp_dict);
+  NTOHUL(fixup_mem);  /* [RPAP - Jan 97: Endian Ordering] */
+  if (!(cd = Xmalloc (mem)))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  end = (u_long *) (((u_char *) cd) + mem);
+  Fread (cd, sizeof (u_char), mem, text_fast_comp_dict);
+
+  if (!(fixup = Xmalloc (fixup_mem)))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  Fread (fixup, fixup_mem, sizeof (u_char), text_fast_comp_dict);
+
+  for (p = (u_long *) cd; (u_long) p < (u_long) end; p++)
+    if (IS_FIXUP (p))
+      {
+	NTOHUL(*p);  /* [RPAP - Jan 97: Endian Ordering] */
+	*p = *p + (u_long) cd;
+      }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  /* cdh */
+  NTOHUL(cd->cdh.dict_type);
+  NTOHUL(cd->cdh.novel_method);
+  for (i = 0; i < TEXT_PARAMS; i++)
+    NTOHUL(cd->cdh.params[i]);
+  NTOHUL(cd->cdh.num_words[0]);
+  NTOHUL(cd->cdh.num_words[1]);
+  NTOHUL(cd->cdh.num_word_chars[0]);
+  NTOHUL(cd->cdh.num_word_chars[1]);
+  NTOHUL(cd->cdh.lookback);
+  /* cfh */
+  for (i = 0; i <= 1; i++)
+    {
+      int j;
+
+      NTOHSI(cd->cfh[i]->hd.num_codes);
+      NTOHSI(cd->cfh[i]->hd.mincodelen);
+      NTOHSI(cd->cfh[i]->hd.maxcodelen);
+      for (j = 0; j < MAX_HUFFCODE_LEN + 1; j++)
+	{
+	  NTOHSI(cd->cfh[i]->hd.lencount[j]);
+	  NTOHUL(cd->cfh[i]->hd.min_code[j]);
+	}
+      NTOHUL(cd->cfh[i]->uncompressed_size);
+      for (j = 0; j < MAX_HUFFCODE_LEN + 1; j++)
+	NTOHUL(cd->cfh[i]->huff_words_size[j]);
+    }
+  NTOHUL(cd->MemForCompDict);
+  /* ad */
+  if (cd->cdh.novel_method == MG_NOVEL_BINARY ||
+      cd->cdh.novel_method == MG_NOVEL_DELTA ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    for (i = 0; i <= 1; i++)
+      {
+	int j;
+	
+	NTOHUL(cd->ad->afh[i].num_frags);
+	NTOHUL(cd->ad->afh[i].mem_for_frags);
+	for (j = 0; j < 33; j++)
+	  {
+	    NTOHSI(cd->ad->blk_start[i][j]);
+	    NTOHSI(cd->ad->blk_end[i][j]);
+	  }
+      }
+  NTOHSI(cd->fast_loaded);
+
+  Xfree (fixup);
+  return (cd);
+}
+
+
+
+
+compression_dict *
+LoadCompDict (File * text_comp_dict,
+	      File * text_aux_dict,
+	      File * text_fast_comp_dict)
+{
+  return text_fast_comp_dict ?
+    Load_Fast_Comp_Dict (text_fast_comp_dict) :
+    Load_Comp_Dict (text_comp_dict, text_aux_dict);
+}
+
+
+
+
+void 
+FreeCompDict (compression_dict * cd)
+{
+  int which;
+  if (cd->fast_loaded)
+    {
+      Xfree (cd);
+      return;
+    }
+  for (which = 0; which < 2; which++)
+    {
+      if (cd->cfh[which])
+	Xfree (cd->cfh[which]);
+      if (cd->chars_huff[which])
+	Xfree (cd->chars_huff[which]);
+      if (cd->lens_huff[which])
+	Xfree (cd->lens_huff[which]);
+      if (cd->values[which])
+	{
+	  Xfree (cd->values[which][0][0]); /* [RJM 07/98: Memory Leak] */
+	  Xfree (cd->values[which][0]);
+	  Xfree (cd->values[which]);
+	}
+      if (cd->chars_vals[which])
+	{
+	  Xfree (cd->chars_vals[which][0]);
+	  Xfree (cd->chars_vals[which]);
+	}
+      if (cd->lens_vals[which])
+	{
+	  Xfree (cd->lens_vals[which][0]);
+	  Xfree (cd->lens_vals[which]);
+	}
+    }
+  if (cd->ad)
+    FreeAuxDict (cd->ad);
+  Xfree (cd);
+}
+
+
+
+
+
+#define MY_HUFF_DECODE(len, code, mcodes)				\
+  do {									\
+    register unsigned long *__min_code = (mcodes);			\
+    register unsigned long *__mclen = __min_code;			\
+    register unsigned long __code = 0;					\
+    do									\
+      {									\
+        DECODE_ADD(__code);						\
+      }									\
+    while (__code < *++__mclen);					\
+    (len) = __mclen - __min_code;					\
+    (code) = __code - *__mclen;						\
+  } while(0);
+
+
+/*#define DUMPDOC */
+
+#define MAX_SWAPS 10000
+
+int 
+DecodeText (compression_dict * cd,
+	    u_char * s_in, int l_in, u_char * s_out, int *l_out)
+{
+  auxiliary_dict *ad = cd->ad;
+  int which;
+  u_long num_bits, bits;
+  u_char *ptr = s_out;
+  static int num = 0;
+  u_long binary_start[2];
+  int novels_used[2];
+  int swaps[2][MAX_SWAPS];
+  novels_used[0] = novels_used[1] = 0;
+
+  {
+    unsigned char bf = s_in[l_in - 1];
+    num_bits = 1;
+    while ((bf & 1) != 1)
+      {
+	num_bits++;
+	bf >>= 1;
+      }
+    num_bits = l_in * 8 - num_bits;
+  }
+
+  DECODE_START (s_in, l_in)
+
+    which = DECODE_BIT;
+  bits = 1;
+
+  if (cd->cdh.novel_method == MG_NOVEL_BINARY)
+    {
+      DELTA_DECODE_L (binary_start[0], bits);
+      DELTA_DECODE_L (binary_start[1], bits);
+    }
+
+
+  while (bits < num_bits)
+    {
+      register unsigned code, len;
+      register int r;
+      register u_char *t, *b = NULL;
+      u_char word[MAXWORDLEN + 1];
+
+#ifdef DUMPDOC
+      printf ("\n%d %d  ", bits, num_bits);
+#endif
+      if (cd->cfh[which])
+	{
+	  MY_HUFF_DECODE (len, code, cd->cfh[which]->hd.min_code);
+	  bits += len;
+
+	  r = code & ((1 << cd->cdh.lookback) - 1);
+	  t = cd->values[which][len][code >> cd->cdh.lookback];
+
+	  /* step through from base pointer */
+	  b = word + 1;
+	  while (r--)
+	    {
+	      register int copy = *t >> 4;
+	      memcpy (word + copy + 1, t + 1, *t & 0xf);
+	      word[0] = copy + (*t & 0xf);
+	      t += ((*t) & 0xf) + 1;
+	    }
+	}
+      else
+	t = NULL;
+      if (t == cd->escape[which])
+	{
+	  switch (cd->cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      {
+		int len, i;
+		int c;
+		HUFF_DECODE_L (len, cd->lens_huff[which]->min_code,
+			       cd->lens_vals[which], bits);
+		for (i = 0; i < len; i++)
+		  {
+		    HUFF_DECODE_L (c, cd->chars_huff[which]->min_code,
+				   cd->chars_vals[which], bits);
+		    *ptr++ = c;
+		  }
+	      }
+	      break;
+	    case MG_NOVEL_BINARY:
+	    case MG_NOVEL_DELTA:
+	    case MG_NOVEL_HYBRID:
+	    case MG_NOVEL_HYBRID_MTF:
+	      {
+		int idx = 0, len;
+		u_char *base;
+		switch (cd->cdh.novel_method)
+		  {
+		  case MG_NOVEL_BINARY:
+		    {
+		      BINARY_DECODE_L (idx, binary_start[which], bits);
+		      if (idx == binary_start[which])
+			binary_start[which]++;
+		      idx--;
+		    }
+		    break;
+		  case MG_NOVEL_DELTA:
+		    {
+		      DELTA_DECODE_L (idx, bits);
+		      idx--;
+		    }
+		    break;
+		  case MG_NOVEL_HYBRID:
+		    {
+		      int k;
+		      GAMMA_DECODE_L (k, bits);
+		      k--;
+		      BINARY_DECODE_L (idx,
+				       ad->blk_end[which][k] -
+				       ad->blk_start[which][k] + 1, bits);
+		      idx += ad->blk_start[which][k] - 1;
+		    }
+		    break;
+		  case MG_NOVEL_HYBRID_MTF:
+		    {
+		      int k;
+		      GAMMA_DECODE_L (k, bits);
+		      k--;
+		      BINARY_DECODE_L (idx,
+				       ad->blk_end[which][k] -
+				       ad->blk_start[which][k] + 1, bits);
+		      idx += ad->blk_start[which][k] - 1;
+		      if (idx >= novels_used[which])
+			{
+			  u_char *temp;
+			  temp = ad->words[which][idx];
+			  ad->words[which][idx] =
+			    ad->words[which][novels_used[which]];
+			  ad->words[which][novels_used[which]] = temp;
+			  swaps[which][novels_used[which]] = idx;
+			  idx = novels_used[which]++;
+			}
+		    }
+		    break;
+		  }
+		base = ad->words[which][idx];
+		len = *base++;
+#ifdef DUMPDOC
+		printf ("[[");
+#endif
+		for (; len; len--)
+		  {
+		    *ptr++ = *base++;
+#ifdef DUMPDOC
+		    putchar (*(base - 1));
+#endif
+		  }
+#ifdef DUMPDOC
+		printf ("]]");
+#endif
+	      }
+	      break;
+	    }
+	}
+      else
+	{
+	  /* copy over the matching prefix */
+	  r = (*t >> 4);
+	  while (r--)
+#ifndef DUMPDOC
+	    *ptr++ = *b++;
+#else
+	    {
+	      *ptr = *b++;
+	      putchar (*ptr);
+	      ptr++;
+	    }
+#endif
+
+	  /* and the stored suffix */
+	  r = ((*t) & 0xf);
+	  while (r--)
+#ifndef DUMPDOC
+	    *ptr++ = *++t;
+#else
+	    {
+	      *ptr = *++t;
+	      putchar (*ptr);
+	      ptr++;
+	    }
+#endif
+	}
+      which = !which;
+    }
+
+  DECODE_DONE
+
+    * l_out = ptr - s_out;
+  num += *l_out + 1;
+
+  if (cd->cdh.novel_method == MG_NOVEL_HYBRID_MTF)
+    for (which = 0; which <= 1; which++)
+      for (novels_used[which]--; novels_used[which] >= 0; novels_used[which]--)
+	{
+	  int a = novels_used[which];
+	  int b = swaps[which][novels_used[which]];
+	  u_char *temp;
+	  temp = ad->words[which][a];
+	  ad->words[which][a] = ad->words[which][b];
+	  ad->words[which][b] = temp;
+	}
+  return (COMPALLOK);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/text_get.h	(revision 16561)
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * text_get.h -- Function for reading documents from the compressed text
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_TEXT_GET
+#define H_TEXT_GET
+
+
+
+/* FetchDocStart () 
+ * Reads into DocEnt the starting position of the document in the *.text file
+ * Where the first document is document number 1
+ * It returns the true weight of the document.
+ */
+double FetchDocStart (query_data * qd, u_long DN, u_long * seek_pos, u_long * len);
+
+unsigned long FetchInitialParagraph (text_data * td, unsigned long ParaNum);
+
+
+/* FetchCompressed () 
+ * Reads into buffer DocBuff the compressed form of document DocNum. 
+ * Where the first document is document number 1
+ */
+int FetchCompressed (query_data * qd, char **DocBuff, DocEntry * DocEnt);
+
+text_data *LoadTextData (File * text, File * text_idx_wgt, File * text_idx);
+
+void FreeTextData (text_data * td);
+
+int GetPosLens (query_data * qd, DocEntry * Docs, int num);
+
+int LoadBuffers (query_data * qd, DocEntry * Docs, int max_mem, int num);
+
+void FreeBuffers (query_data * qd, DocEntry * Docs, int num);
+
+
+
+compression_dict *LoadCompDict (File * text_comp_dict,
+				File * text_aux_dict,
+				File * text_fast_comp_dict);
+
+void FreeCompDict (compression_dict * cd);
+
+
+int DecodeText (compression_dict * cd,
+		u_char * s_in, int l_in, u_char * s_out, int *l_out);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.h	(revision 16561)
@@ -0,0 +1,24 @@
+static char *warranty_str[] = {
+"			    NO WARRANTY\n",
+"\n",
+"  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n",
+"FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n",
+"OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n",
+"PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n",
+"OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n",
+"MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n",
+"TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n",
+"PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n",
+"REPAIR OR CORRECTION.\n",
+"\n",
+"  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n",
+"WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n",
+"REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n",
+"INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n",
+"OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n",
+"TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n",
+"YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n",
+"PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n",
+"POSSIBILITY OF SUCH DAMAGES.\n",
+"\n",
+"		     END OF TERMS AND CONDITIONS\n"};
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.src
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.src	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/warranty.src	(revision 16561)
@@ -0,0 +1,23 @@
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/weights.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/weights.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/weights.c	(revision 16561)
@@ -0,0 +1,161 @@
+/**************************************************************************
+ *
+ * weights.c -- Functions for reading the weights file in mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "filestats.h"
+#include "memlib.h"
+#include "messages.h"
+#include "timing.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg.h"
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "weights.h"
+#include "locallib.h"
+#include "mg_errors.h"
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+/*
+   $Log$
+   Revision 1.1  2003/02/20 21:18:24  mdewsnip
+   Addition of MG package for search and retrieval
+
+   Revision 1.1  1999/08/10 21:18:27  sjboddie
+   renamed mg-1.3d directory mg
+
+   Revision 1.1  1998/11/17 09:35:51  rjmcnab
+   *** empty log message ***
+
+   * Revision 1.2  1994/09/20  04:42:18  tes
+   * For version 1.1
+   *
+ */
+
+static char *RCSID = "$Id$";
+
+
+
+approx_weights_data *
+LoadDocWeights (File * weight_file,
+		unsigned long num_of_docs)
+{
+  approx_weights_data *awd;
+  int num;
+
+  if (!(awd = Xmalloc (sizeof (*awd))))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  Fseek (weight_file, sizeof (long), 0);
+  Fread (&awd->bits, sizeof (awd->bits), 1, weight_file);
+  Fread (&awd->L, sizeof (awd->L), 1, weight_file);
+  NTOHD(awd->L);  /* [RPAP - Jan 97: Endian Ordering] */
+  Fread (&awd->B, sizeof (awd->B), 1, weight_file);
+  NTOHD(awd->B);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  awd->mask = awd->bits == 32 ? 0xffffffff : (1 << awd->bits) - 1;
+
+  num = (num_of_docs * awd->bits + 31) / 32;
+  if (!(awd->DocWeights = Xmalloc (sizeof (unsigned long) * num)))
+    {
+      Xfree (awd);
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  Fread (awd->DocWeights, sizeof (unsigned long), num, weight_file);
+  {
+    /* [RPAP - Jan 97: Endian Ordering] */
+    unsigned long i;
+    for (i = 0; i < num; i++)
+      NTOHUL(awd->DocWeights[i]);
+  }
+
+  awd->MemForWeights = num * sizeof (unsigned long);
+  awd->num_of_docs = num_of_docs;
+
+  mg_errno = MG_NOERROR;
+
+  if (awd->bits <= 12)
+    {
+      int i, size = (1 << awd->bits);
+      if (!(awd->table = (float *) Xmalloc (size * sizeof (float))))
+	  return (awd);
+      awd->table[0] = awd->L;
+      for (i = 1; i < size; i++)
+	awd->table[i] = awd->table[i - 1] * awd->B;
+    }
+  else
+    awd->table = NULL;
+  return awd;
+}
+
+
+
+
+
+
+/*
+ * the first document in the collection has DocNum = 0
+ */
+float 
+GetLowerApproxDocWeight (approx_weights_data * awd, register int DocNum)
+{
+  register unsigned long c, Pos;
+  register unsigned long *dw;
+  if (awd == NULL)
+    return 1.0;
+#if 0
+  if (DocNum < 0 || DocNum >= awd->num_of_docs)
+    FatalError (1, "Something is wrong in \"GetDocWeight\" DocNum = %d\n",
+		DocNum);
+#endif
+  Pos = DocNum * awd->bits;
+  dw = &(awd->DocWeights[Pos / MAXBITS]);
+  Pos &= (MAXBITS - 1);
+  c = *dw >> Pos;
+  if (Pos + awd->bits > MAXBITS)
+    c |= *(dw + 1) << (MAXBITS - Pos);
+  c &= awd->mask;
+  if (awd->table)
+    return (awd->table[c]);
+  else
+    return (awd->L * pow (awd->B, (double) c));
+
+}
+
+void 
+FreeWeights (approx_weights_data * awd)
+{
+  Xfree (awd->DocWeights);
+  if (awd->table)
+    Xfree (awd->table);
+  Xfree (awd);
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/weights.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/weights.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/weights.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**************************************************************************
+ *
+ * weights.h -- Functions for reading the weights file in mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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 H_WEIGHTS
+#define H_WEIGHTS
+
+approx_weights_data *LoadDocWeights (File * weight_file,
+				     unsigned long num_of_docs);
+
+float GetLowerApproxDocWeight (approx_weights_data * awd, register int DocNum);
+
+void FreeWeights (approx_weights_data * awd);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/win32.mak	(revision 16561)
@@ -0,0 +1,242 @@
+###########################################################################
+#
+# win32 makefile -- mg\src\text
+# 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  The 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.
+#
+###########################################################################
+ 
+MGHOME = ..\..
+# for now put executables in indexers/bin
+INSTALLDIR = ..\..\..\bin
+
+AR = lib
+
+CC = cl
+CPPFLAGS = 
+DEFS = -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DHAVE_CONFIG_H \
+       -D__WIN32__ -D_LITTLE_ENDIAN -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"." -I"$(MGHOME)" -I"$(MGHOME)\lib"
+COMPILE = $(CC) $(CPPFLAGS) -c $(DEFS) $(INCLUDES)
+
+LDFLAGS =
+LINK = $(CC) $(LDFLAGS)
+
+LIBS = "$(MGHOME)\lib\libmglib.lib"
+
+.SUFFIXES: .c .cpp .obj
+.c.obj:
+	$(COMPILE) $<
+.cpp.obj:
+	$(COMPILE) $<
+
+o = .obj
+e = .exe
+
+HEADERS = \
+  backend.h          conditions.h       locallib.h         term_lists.h \
+  bool_optimiser.h   environment.h      mg.h               text.h       \
+  bool_parser.h      globals.h          mg_errors.h        text_get.h   \
+  bool_query.h       hash.h             mg_files.h         warranty.h   \
+  bool_tree.h        help.mg.h          mg_merge.h         weights.h    \
+  build.h            invf.h             read_line.h        words.h      \
+  commands.h         invf_get.h         stem_search.h                   \
+  comp_dict.h        lists.h            stemmer.h          query_term_list.h
+
+SOURCES = \
+  backend.c               locallib.c              mg_weights_build.c   \
+  bool_optimiser.c        mg.special.c            mgdictlist.c         \
+  bool_parser.c           mg_compression_dict.c   mgquery.c            \
+  bool_query.c            mg_errors.c             mgstat.c             \
+  bool_tester.c           mg_fast_comp_dict.c     mg_decompress_text.c \
+  bool_tree.c             mg_files.c              query.docnums.c      \
+  commands.c              mg_hilite_words.c       query.ranked.c       \
+  comp_dict.c             mg_invf_dict.c          read_line.c          \
+  environment.c           mg_invf_dump.c          stem_search.c        \
+  mg_invf_merge.c         stemmer.c               mg_stem_idx.c        \
+  mg_invf_rebuild.c       term_lists.c            mgstemidxlist.c      \
+  invf_get.c              mg_passes.c             text.pass1.c         \
+  ivf.pass1.c             mg_perf_hash_build.c    text.pass2.c         \
+  ivf.pass2.c             mg_text_estimate.c      text_get.c           \
+  lists.c                 mg_text_merge.c         weights.c            \
+  mgpass.c                query_term_list.c       words.c              \
+  mg_passes_4jni.c
+
+EXEC = \
+  mgquery$e mg_weights_build$e mgstat$e \
+  mg_invf_dump$e     mg_invf_dict$e \
+  mg_invf_rebuild$e     mgdictlist$e \
+  mg_passes$e     mg_perf_hash_build$e \
+  mg_compression_dict$e     mg_text_estimate$e \
+  mg_fast_comp_dict$e \
+  mg_hilite_words$e     mg_invf_merge$e \
+  mg_text_merge$e     bool_tester$e  mgpass$e \
+  mg_stem_idx$e  mgstemidxlist$e \
+  mg_decompress_text$e 
+
+all: $(EXEC) libmgtext.lib libmgpass.lib
+
+HILITE_OBJS = mg_hilite_words$o stemmer$o locallib$o words$o environment$o
+mg_hilite_words$e: $(HILITE_OBJS)
+	$(LINK) $(HILITE_OBJS) $(LIBS)
+
+BOOL_OBJS = bool_tester$o bool_tree$o bool_parser$o bool_optimiser$o  \
+	    term_lists$o stemmer$o stem_search$o mg_errors$o \
+	    query_term_list$o words$o environment$o
+
+bool_tester$e: $(BOOL_OBJS)
+	$(LINK) $(BOOL_OBJS) $(LIBS)
+
+QUERY_OBJS = \
+	mgquery$o locallib$o lists$o \
+	query.ranked$o query.docnums$o stem_search$o \
+	environment$o commands$o weights$o text_get$o stemmer$o \
+	read_line$o mg_errors$o backend$o invf_get$o \
+	term_lists$o bool_tree$o bool_parser$o bool_optimiser$o bool_query$o \
+	query_term_list$o words$o
+mgquery$e: $(QUERY_OBJS)
+	$(LINK) $(READLINE) $(QUERY_OBJS) $(READLINE_LIBS) $(LIBS)
+
+commands$o : help.mg.h warranty.h conditions.h
+
+PASSES_OBJS = \
+	mg_passes$o text.pass1$o comp_dict$o stemmer$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o environment$o
+mg_passes$e: $(PASSES_OBJS)
+	$(LINK) $(PASSES_OBJS) $(LIBS)
+
+PASS_OBJS = \
+	 mgpass$o words$o text.pass1$o comp_dict$o stemmer$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o environment$o
+mgpass$e: $(PASS_OBJS)
+	$(LINK) $(PASS_OBJS) $(LIBS)
+
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib
+
+BUILDER_OBJS = \
+	builder$o
+builder$e: $(BUILDER_OBJS)
+	$(LINK) $(BUILDER_OBJS) $(LIBS) $(LINK32_FLAGS)
+
+WEIGHTS_BUILD_OBJS = mg_weights_build$o mg_files$o
+mg_weights_build$e: $(WEIGHTS_BUILD_OBJS)
+	$(LINK) $(WEIGHTS_BUILD_OBJS) $(LIBS)
+
+COMP_DICT_OBJS = mg_compression_dict$o mg_files$o locallib$o
+mg_compression_dict$e: $(COMP_DICT_OBJS)
+	$(LINK) $(COMP_DICT_OBJS) $(LIBS)
+
+FAST_COMP_OBJS = mg_fast_comp_dict$o mg_files$o locallib$o
+mg_fast_comp_dict$e: $(FAST_COMP_OBJS)
+	$(LINK) $(FAST_COMP_OBJS) $(LIBS)
+
+TEXT_EST_OBJS = mg_text_estimate$o locallib$o comp_dict$o mg_files$o
+mg_text_estimate$e: $(TEXT_EST_OBJS)
+	$(LINK) $(TEXT_EST_OBJS) $(LIBS)
+
+STAT_OBJS = mgstat$o mg_files$o locallib$o
+mgstat$e: $(STAT_OBJS)
+	$(LINK) $(STAT_OBJS) $(LIBS)
+
+INVF_DUMP_OBJS = mg_invf_dump$o locallib$o mg_files$o
+mg_invf_dump$e: $(INVF_DUMP_OBJS)
+	$(LINK) $(INVF_DUMP_OBJS) $(LIBS)
+
+INVF_DICT_OBJS = mg_invf_dict$o mg_files$o locallib$o
+mg_invf_dict$e: $(INVF_DICT_OBJS)
+	$(LINK) $(INVF_DICT_OBJS) $(LIBS)
+
+INVF_REB_OBJS = mg_invf_rebuild$o locallib$o
+mg_invf_rebuild$e: $(INVF_REB_OBJS)
+	$(LINK) $(INVF_REB_OBJS) $(LIBS)
+
+DICTLIST_OBJS = mgdictlist$o locallib$o
+mgdictlist$e: $(DICTLIST_OBJS)
+	$(LINK) $(DICTLIST_OBJS) $(LIBS)
+
+PERF_HASH_OBJS = mg_perf_hash_build$o mg_files$o
+mg_perf_hash_build$e: $(PERF_HASH_OBJS)
+	$(LINK) $(PERF_HASH_OBJS) $(LIBS)
+
+STEM_IDX_OBJS = mg_stem_idx$o mg_files$o stemmer$o locallib$o \
+                mg_errors$o term_lists$o
+mg_stem_idx$e: $(STEM_IDX_OBJS)
+	$(LINK) $(STEM_IDX_OBJS) $(LIBS)
+
+STEM_IDX_LIST_OBJS = mgstemidxlist$o mg_files$o
+mgstemidxlist$e: $(STEM_IDX_LIST_OBJS)
+	$(LINK) $(STEM_IDX_LIST_OBJS) $(LIBS)
+
+DECOMPRESS_TEXT_OBJS = mg_decompress_text$o query_term_list$o mg_files$o \
+		       mg_errors$o \
+                       text_get$o locallib$o backend$o stem_search$o \
+                       term_lists$o stemmer$o weights$o invf_get$o lists$o
+mg_decompress_text$e: $(DECOMPRESS_TEXT_OBJS)
+	$(LINK) $(DECOMPRESS_TEXT_OBJS) $(LIBS)
+
+TEXT_MERGE_OBJS = mg_text_merge$o mg_files$o locallib$o
+mg_text_merge$e: $(TEXT_MERGE_OBJS)
+	$(LINK) $(TEXT_MERGE_OBJS) $(LIBS)
+
+INVF_MERGE_OBJS = mg_invf_merge$o mg_files$o locallib$o
+mg_invf_merge$e: $(INVF_MERGE_OBJS)
+	$(LINK) $(INVF_MERGE_OBJS) $(LIBS)
+
+LIB_OBJS = \
+	query_term_list$o \
+	locallib$o lists$o \
+	query.ranked$o query.docnums$o stem_search$o \
+	environment$o commands$o weights$o text_get$o stemmer$o \
+	read_line$o mg_errors$o backend$o invf_get$o \
+	term_lists$o bool_tree$o bool_parser$o bool_optimiser$o \
+        bool_query$o words$o
+
+JNI_LIB_OBJS = \
+	mg_passes_4jni$o text.pass1$o comp_dict$o stemmer$o \
+	text.pass2$o locallib$o  words$o environment$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o 
+
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+
+libmgtext.lib : $(LIB_OBJS)
+        if exist libmgtext.lib del libmgtext.lib
+	$(AR) /out:libmgtext.lib $(LIB_OBJS)
+
+libmgpass.lib : $(JNI_LIB_OBJS)
+ 	  if exist libmgpass.lib del libmgpass.lib
+	$(AR) /out:libmgpass.lib $(JNI_LIB_OBJS)
+
+install: 
+	if not exist "$(INSTALLDIR)" mkdir "$(INSTALLDIR)"
+	for %%i in ($(EXEC)) do \
+	  copy %%i "$(INSTALLDIR)"
+
+clean:
+        if exist *$o del *$o 
+	if exist *$e del *$e
+        if exist libmgtext.lib del libmgtext.lib
+        if exist libmgpass.lib del libmgpass.lib
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/words.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/words.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/words.c	(revision 16561)
@@ -0,0 +1,34 @@
+#include "words.h"
+
+
+/* Takes the place of the old INAWORD macro. It determines
+   whether a given place in a UTF-8 encoded Unicode string
+   is part of a word. */
+int inaword (const u_char *here, const u_char *end) {
+  unsigned short c;
+  if (parse_utf8_char(here, end, &c) > 0) return is_unicode_letdig (c);
+  return 0;
+}
+
+/* Return a the UTF-8 encoded Unicode string with begining
+   unicode spaces skipped. */
+u_char *skipspace(u_char *here, u_char *end)
+{
+  unsigned short c;	
+  int length;
+  while(here != end) {
+    length = parse_utf8_char(here, end, &c);
+    if (length == 0 || !is_unicode_space(c)) break;
+    here += length;
+  }
+  return here;
+}
+
+/* It determines whether a given place in a UTF-8 encoded 
+   Unicode string is a unicode space. */
+int isaspace (const u_char *here, const u_char *end)
+{
+  unsigned short c;
+  if (parse_utf8_char(here, end, &c) > 0) return is_unicode_space(c);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mg/src/text/words.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/src/text/words.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/src/text/words.h	(revision 16561)
@@ -0,0 +1,395 @@
+/**************************************************************************
+ *
+ * words.h -- Macros for parsing out words from the source text
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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$
+ *
+ **************************************************************************/
+
+
+#include "sysfuncs.h"
+#include "unitool.h"
+
+/*
+ * This has been cleaned up by Tim Shimmin. 
+ */
+
+/*
+ * ---NOTE---
+ *
+ * "WORD" refers to a word in the compressed text.
+ * "STEM" or "STEM_WORD" refers to a word for indexing on
+ *
+ */
+
+#define MAXWORDLEN	15
+	/* Maximum length in bytes of any word or non-word. Note that
+	   variations to MAXWORDLEN may have dramatic effects on the rest
+	   of the program, as the length and the prefix match are packed
+	   together into a four bit nibble, and there is not check that
+	   this is possible, i.e., leave MAXWORDLEN alone... */
+
+#define MAXSTEMLEN	255
+	/* Maximum length in bytes of any stem. Note that
+	   variations to MAXSTEMLEN may have dramatic effects on the rest
+	   of the program, , i.e., leave MAXSTEMLEN alone... */
+
+/*#define MAXNUMERIC	4*/
+
+	/* Maximum number of numeric characters permitted in a word.
+	   This avoids long sequences of numbers creating just one
+	   word occurrence for each number. At most 10,000 all numeric
+	   words will be permitted. */
+
+/* [RPAP - Jan 97: Stem Index Change] */
+#define MAXPARAMLEN     20
+        /* Maximum number of bytes to read for a parameter value for a
+	   term in a query. */
+#define WEIGHTPARAM     '/'
+#define STEMPARAM       '#'
+
+/* [RJM 07/97: Ranked Required Terms] */
+#define MUSTMATCHPARAM     '+'
+
+
+#define PESINAWORD(c)      (isalnum(c) || ((c) >= 0x80 && (c) <= 0xff))
+        /* The definition of what characters are permitted in a word.
+	   This macro is pessimistic, you cannot tell from a particular
+	   byte above 0x80 whether it is a character or not. This function
+	   is needed by various functions relating to huffman coding
+	   where frequency counts need to be primed, it should not be
+	   used in parsing the UTF-8 encoded input. */
+
+int inaword (const u_char *here, const u_char *end);
+        /* Takes the place of the old INAWORD macro. It determines
+	   whether a given place in a UTF-8 encoded Unicode string
+	   is part of a word. */
+
+int isaspace (const u_char *here, const u_char *end);
+        /* It determines whether a given place in a UTF-8 encoded 
+	   Unicode string is a unicode space. */
+ 
+u_char *skipspace(u_char *here, u_char *end);
+        /* Return a the UTF-8 encoded Unicode string with beginning 
+	   unicode spaces skipped. */
+ 
+
+/* =========================================================================
+ * Macro: PARSE_WORD
+ * Description: 
+ *      Extract a word out for compressing text
+ * Input: 
+ *      s_in = string start in buffer
+ *      end = string end in buffer
+ * Output: 
+ *      Word = extracted word with length in 1st byte
+ *      s_in = ptr to next character in buffer yet to be processed
+ * ========================================================================= */
+#define PARSE_WORD(Word, s_in, end)                                \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    register u_char *wptr = (Word)+1;                              \
+    register int length = 0;                                       \
+    register int numeric = 0;                                      \
+    unsigned short c;                                              \
+    register int maxnumeric = IntEnv (GetEnv ("maxnumeric"), 4);   \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (length+charlength <= MAXWORDLEN && charlength > 0 &&    \
+	   (is_unicode_letter(c) || (is_unicode_digit(c) &&        \
+				     ++numeric <= maxnumeric))) {  \
+      while (charlength-- > 0) {                                   \
+        *wptr++ = *(s_in)++; ++length;                             \
+      }                                                            \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+    *(Word) = length;                                              \
+  }while(0)
+
+  /*  
+#define PARSE_WORD(Word, s_in, end)                                  \
+  do {                                                               \
+	  register u_char  *wptr = (Word)+1;                         \
+	  register int    length = 0;                                \
+	  register int    c = *(s_in);                               \
+	  register int	numeric = 0;                                 \
+                                                                     \
+	  while( length < MAXWORDLEN && INAWORD(c) && (s_in)<=(end)) \
+	    {                                                        \
+	      if ((numeric += INNUMBER(c)) > MAXNUMERIC)             \
+		break;                                               \
+	      *wptr++ = c;                                           \
+	      ++length;                                              \
+	      c = *++(s_in);                                         \
+	    }                                                        \
+	  *(Word) = length;                                          \
+  }while(0)
+  */
+
+
+/* =========================================================================
+ * Macro: PARSE_NON_WORD
+ * Description: 
+ *      Extract a non-word out for storing compressed text
+ * Input: as above
+ * Output: as above
+ * ========================================================================= */
+#define PARSE_NON_WORD(Word, s_in, end)                            \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    register u_char *wptr = (Word)+1;                              \
+    register int length = 0;                                       \
+    unsigned short c;                                              \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (length+charlength <= MAXWORDLEN && charlength > 0 &&    \
+	   !is_unicode_letdig(c)) {                                \
+      while (charlength-- > 0) {                                   \
+        *wptr++ = *(s_in)++; ++length;                             \
+      }                                                            \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+    *(Word) = length;                                              \
+  }while(0)
+
+    /*
+#define PARSE_NON_WORD(Word, s_in, end)                            \
+  do {                                                             \
+	  register u_char  *wptr = (Word)+1;                       \
+	  register int    length = 0;                              \
+	  register int    c = *(s_in);                             \
+                                                                   \
+	  while( length < MAXWORDLEN && !INAWORD(c) && (s_in)<=(end) ) \
+	    {                                                      \
+	      *wptr++ = c;                                         \
+	      ++length;                                            \
+	      c = *++(s_in);                                       \
+	    }                                                      \
+	  *(Word) = length;                                        \
+  }while(0)
+    */
+
+
+/* =========================================================================
+ * Macro: PARSE_STEM_WORD 
+ * Description: 
+ *      Extracts out Word.      
+ * Input: 
+ *      s_in points to 1st letter in buffer to test
+ *      end points to last letter in buffer
+ * Output: 
+ *      s_in is modified to move to next word
+ *      Returns Word filled in with length in 1st byte.
+ * ========================================================================= */
+#define PARSE_STEM_WORD(Word, s_in, end)                           \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    register u_char *wptr = (Word)+1;                              \
+    register int length = 0;                                       \
+    register int numeric = 0;                                      \
+    unsigned short c;                                              \
+    register int maxnumeric = IntEnv (GetEnv ("maxnumeric"), 4);   \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (length+charlength <= MAXSTEMLEN && charlength > 0 &&    \
+	   (is_unicode_letter(c) || (is_unicode_digit(c) &&        \
+				     ++numeric <= maxnumeric))) {  \
+      while (charlength-- > 0) {                                   \
+        *wptr++ = *(s_in)++; ++length;                             \
+      }                                                            \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+    *(Word) = length;                                              \
+  }while(0)
+    /*
+#define PARSE_STEM_WORD(Word, s_in, end)                      \
+  do                                                          \
+    {                                                         \
+      register u_char  *wptr = (Word)+1;                      \
+      register int    length = 0;                             \
+      register int    c = *(s_in);                            \
+      register int    numeric = 0;                            \
+                                                              \
+      while ( length < MAXSTEMLEN && INAWORD(c) && (s_in)<=(end)) \
+        {                                                     \
+ 	  if ((numeric += INNUMBER(c)) > MAXNUMERIC)          \
+	    break;                                            \
+	  *wptr++ = c;                                        \
+	  ++length;                                           \
+	  c = *++(s_in);                                      \
+	}                                                     \
+      *(Word) = length;                                       \
+    }while(0)
+    */
+
+
+/* =========================================================================
+ * Macro: PARSE_NON_STEM_WORD 
+ * Description: 
+ *      Eat up non-word. Do not store non-word.
+ *      It is not needed in index only in text !
+ *      
+ * Input: as above but no Word needed
+ * Output: as above
+ * ========================================================================= */
+#define PARSE_NON_STEM_WORD(s_in, end)                             \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    unsigned short c;                                              \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (charlength > 0 && !is_unicode_letdig(c)) {              \
+      (s_in) += charlength;                                        \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+  }while(0)
+
+    /*
+#define PARSE_NON_STEM_WORD(s_in, end)           \
+  do                                             \
+    {                                            \
+      while (!INAWORD(*(s_in)) && (s_in)<=(end)) \
+	(s_in)++;                                \
+    }while(0)
+    */
+
+
+/* =========================================================================
+ * Macro: PARSE_NON_STEM_WORD_OR_SGML_TAG 
+ * Description: 
+ *      Like PARSE_NON_STEM_WORD but also eats up SGML tags
+ * Input: as above
+ * Output: as above
+ * ========================================================================= */
+#define PARSE_NON_STEM_WORD_OR_SGML_TAG(s_in, end)                 \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    unsigned short c;                                              \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (charlength > 0 && !is_unicode_letdig(c)) {              \
+      if (c == '<') {                                              \
+	while (charlength > 0 && c != '>') {                       \
+	  (s_in) += charlength;                                    \
+	  charlength = parse_utf8_char((s_in),(end),&c);           \
+	}                                                          \
+      }                                                            \
+      (s_in) += charlength;                                        \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+  }while(0)
+
+    /*
+#define PARSE_NON_STEM_WORD_OR_SGML_TAG(s_in, end) \
+  do                                               \
+    {                                              \
+      register int    c = *(s_in);                 \
+                                                   \
+      while (!INAWORD(c) && (s_in)<=(end))         \
+        {                                          \
+	  if (c == '<')                            \
+            {                                      \
+	      while (c != '>' && (s_in)<=(end))    \
+		c = *++(s_in);                     \
+            }                                      \
+	  if ((s_in)<=(end))                       \
+	    c = *++(s_in);                         \
+	}                                          \
+    }while(0)
+    */
+
+
+/* =========================================================================
+ * Macro: PARSE_OPT_TERM_PARAM     [RPAP - Jan 97: Stem Index Change]
+ * Description: 
+ *      Extracts out optional paramater for query term.
+ *      Needed only in parsing the query line !
+ *
+ *      Note: that this function has not been converted to use UTF-8
+ *            as it should still work as it is (only uses ascii
+ *            characters)
+ *      
+ * Input: as above but no Word needed
+ * Output: as above
+ * ========================================================================= */
+#define PARSE_OPT_TERM_PARAM(Param, type, s_in, end)                       \
+  do                                                                       \
+    {                                                                      \
+	  register u_char  *wptr = (Param);                                \
+	  register int    length = 0;                                      \
+	  register int    c = *(s_in);                                     \
+                                                                           \
+          if (c == WEIGHTPARAM || c == STEMPARAM)                          \
+	    {                                                              \
+	      type = c;                                                    \
+	      c = *++(s_in);                                               \
+	      while( length < MAXPARAMLEN && isdigit(c) && (s_in)<=(end))  \
+		{                                                          \
+	           *wptr++ = c;                                            \
+	           ++length;                                               \
+	           c = *++(s_in);                                          \
+	        }                                                          \
+	      *wptr = '\0';                                                \
+              for (; isdigit(c) && (s_in)<=(end); c = *++(s_in))           \
+                ;                                                          \
+            }							           \
+    }while(0)
+
+/* =========================================================================
+ * Macro: PARSE_RANKED_NON_STEM_WORD    [RJM 07/97: Ranked Required Terms]
+ * Description: 
+ *      Eat up non-word. Do not store non-word.
+ *      If come across a match requirement store it in require_match
+ *      It is not needed in index only in text !
+ *      
+ * Input: as above
+ * Output: the requirement mode for the next term. -1=must not match,
+ *      0=optional match, 1=must match
+ * ========================================================================= */
+#define PARSE_RANKED_NON_STEM_WORD(require_match, s_in, end)       \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    unsigned short c;                                              \
+    (require_match) = 0;                                           \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (charlength > 0 && !is_unicode_letdig(c)) {              \
+      if (c == MUSTMATCHPARAM) (require_match) = 1;                \
+      (s_in) += charlength;                                        \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+  }while(0)
+
+    /*
+#define PARSE_RANKED_NON_STEM_WORD(require_match, s_in, end)         \
+  do {                                            \
+    (require_match) = 0;                          \
+    while (!INAWORD(*(s_in)) && (s_in)<=(end)) {  \
+      if (*(s_in) == MUSTMATCHPARAM) {            \
+        (require_match) = 1;                      \
+      }                                           \
+      (s_in)++;                                   \
+    }                                             \
+  } while (0)
+    */
Index: /gsdl/trunk/gs2common/indexers/mg/stamp-h.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/stamp-h.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/stamp-h.in	(revision 16561)
@@ -0,0 +1,1 @@
+timestamp
Index: /gsdl/trunk/gs2common/indexers/mg/stemmerinterface.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/stemmerinterface.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/stemmerinterface.c	(revision 16561)
@@ -0,0 +1,39 @@
+#include "lib/lovinstem.h"
+#include <stdio.h>
+#include <ctype.h>
+
+int main () {
+  char word[256];
+  char c;
+  int len;
+
+  c = fgetc (stdin);
+  while (!feof (stdin)) {
+    /* output non words */
+    while (!feof (stdin) && !isalnum (c)) {
+      fputc (c, stdout);
+      c = fgetc (stdin);
+    }
+
+    /* get the word */
+    len = 0;
+    while (!feof (stdin) && isalnum (c) && len < 255) {
+      len++;
+      word[len] = c;
+      c = fgetc (stdin);
+    }
+    word[0] = len;
+
+    /* if we found a word stem and output it */
+    if (len > 0) {
+      lovinstem (word);
+      len = 0;
+      while (len < word[0]) {
+        len++;
+        fputc(word[len], stdout);
+      }
+    }
+  }
+}
+
+
Index: /gsdl/trunk/gs2common/indexers/mg/sysfuncs.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/sysfuncs.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/sysfuncs.h	(revision 16561)
@@ -0,0 +1,362 @@
+/* System dependent definitions for GNU tar.
+   Copyright (C) 1994 Free Software Foundation, Inc.
+
+   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, 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.
+ */
+
+/*
+ * [TS:Aug/95]
+ * Based on code from system.h file in GNU's tar package 
+ *
+ */
+
+#ifndef SYSFUNCS_H
+#define SYSFUNCS_H
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__
+#  include <win32cfg.h>
+# else
+#  include <config.h>
+# endif
+#endif
+
+/* Declare alloca.  AIX requires this to be the first thing in the file.  */
+
+#ifdef __GNUC__
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#else
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX
+#pragma alloca
+#else
+#ifndef alloca
+char *alloca ();
+#endif
+#endif
+#endif
+#endif
+
+#include <sys/types.h>
+
+/* ------------------------------------------------- */
+/* [TS:Aug/95]
+ * Some extra include files that mg needs
+ */
+#include <stdlib.h>
+#include <math.h>
+
+#ifndef log2
+# ifndef M_LN2
+#  define M_LN2 0.69314718055994530942
+# endif
+# define log2(x) (log(x)/M_LN2)
+#endif
+
+
+#ifndef HAVE_SETBUFFER
+# define setbuffer(f, b, s)  setvbuf(f, b, _IOFBF, s)
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#if defined(__MSDOS__)  && !defined(__WIN32__)
+# define SHORT_SUFFIX 1
+#endif
+
+
+
+#include <assert.h>
+
+/* ------------------------------------------------- */
+
+/* Declare a generic pointer type.  */
+#if __STDC__ || defined(__TURBOC__)
+#define voidstar void *
+#else
+#define voidstar char *
+#endif
+
+/* Declare ISASCII.  */
+
+#include <ctype.h>
+
+#if STDC_HEADERS
+#define ISASCII(Char) 1
+#else
+#ifdef isascii
+#define ISASCII(Char) isascii (Char)
+#else
+#if HAVE_ISASCII
+#define ISASCII(Char) isascii (Char)
+#else
+#define ISASCII(Char) 1
+#endif
+#endif
+#endif
+
+
+/* ------------------------------------------------- */
+/* [TS:Aug/95] 
+ * The following is copied from GNU Autoconf.Info (1.1)   
+ * It sets up the approriate string and memory functions.
+ */
+
+#if STDC_HEADERS || HAVE_STRING_H
+#include <string.h>
+   /* An ANSI string.h and pre-ANSI memory.h might conflict */
+#if !STDC_HEADERS && HAVE_MEMORY_H
+#include <memory.h>
+#endif /* not STDC_HEADERS and HAVE_MEMORY_H */
+#ifndef index
+/*#define index strchr 
+ *
+ * this is stuffing up our code so its commented out */
+#endif
+#ifndef rindex
+#define rindex strrchr
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n) memset((s), 0, (n))
+#endif
+#else /* not STDC_HEADERS and not HAVE_STRING_H */
+#include <strings.h>
+   /* memory.h and strings.h conflict on some systems */
+#endif
+/* ------------------------------------------------- */
+
+
+/* Declare errno.  */
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+/* Declare open parameters.  */
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif
+
+#ifndef	O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CREAT
+#define O_CREAT 0
+#endif
+#ifndef	O_NDELAY
+#define O_NDELAY 0
+#endif
+#ifndef	O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+
+/* Declare file status routines and bits.  */
+
+#include <sys/stat.h>
+
+#ifdef STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISLNK
+#undef S_ISMPB
+#undef S_ISMPC
+#undef S_ISNWK
+#undef S_ISREG
+#undef S_ISSOCK
+#endif
+
+/* On MSDOS, there are missing things from <sys/stat.h>.  */
+#if defined(__MSDOS__)
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISVTX 0
+#endif
+
+#if defined(__GNUC__) && defined(__WIN32__)
+#include <limits.h>
+#endif
+
+#ifndef S_ISREG			/* POSIX.1 stat stuff missing */
+#define mode_t unsigned short
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(Mode) (((Mode) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(Mode) (((Mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(Mode) (((Mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(Mode) (((Mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(Mode) (((Mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(Mode) (((Mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(Mode) (((Mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB)	/* V7 */
+#define S_ISMPB(Mode) (((Mode) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(Mode) (((Mode) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK)	/* HP/UX */
+#define S_ISNWK(Mode) (((Mode) & S_IFMT) == S_IFNWK)
+#endif
+
+#if !defined(S_ISCTG) && defined(S_IFCTG)	/* contiguous file */
+#define S_ISCTG(Mode) (((Mode) & S_IFMT) == S_IFCTG)
+#endif
+#if !defined(S_ISVTX)
+#define S_ISVTX 0001000
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#if !defined(_POSIX_SOURCE) && !defined(__WIN32__)
+#include <sys/param.h>
+#endif
+
+/* Include <unistd.h> before any preprocessor test of _POSIX_VERSION.  */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+/* Declare standard functions.  */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+voidstar malloc ();
+voidstar realloc ();
+char *getenv ();
+#endif
+
+#include <stdio.h>
+
+#ifndef _POSIX_VERSION
+#if defined(__WIN32__)
+#include <io.h>
+#define lseek _lseek
+#elif defined(__MSDOS__)
+#include <io.h>
+#else
+off_t lseek ();
+#endif
+#endif
+
+#include <pathmax.h>
+
+/* jrm21 2003 - this is in unistd.h now */
+#ifndef HAVE_UNISTD_H
+#include <getopt.h>
+#endif
+
+#ifdef WITH_DMALLOC
+#undef HAVE_VALLOC
+#define DMALLOC_FUNC_CHECK
+#include <dmalloc.h>
+#endif
+
+/* Prototypes for external functions.  */
+
+#ifndef __P
+#if PROTOTYPES
+#define __P(Args) Args
+#else
+#define __P(Args) ()
+#endif
+#endif
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(Text) gettext (Text)
+#else
+#define textdomain(Domain)
+#define _(Text) Text
+#endif
+
+/* Library modules.  */
+
+#if (HAVE_VPRINTF || HAVE_DOPRNT) && __STDC__
+/*void error __P ((int, int, const char *,...));*/
+void error (int, int, const char *,...);
+#else
+void error (int, int, char *, char *, char *, char *, char *, char *, char *, char *, char *);
+#endif
+
+#ifndef HAVE_STRSTR
+char *strstr __P ((const char *, const char *));
+#endif
+
+/* [RB/TS:Oct/95] commented out. Used by lib/gmalloc.c - but it defines it anyway ???
+ *#ifndef HAVE_VALLOC
+ *#define valloc(Size) malloc (Size)
+ *#endif
+ */
+
+voidstar xmalloc __P ((size_t));
+voidstar xrealloc __P ((voidstar, size_t));
+char *xstrdup __P ((const char *));
+
+/* [TS:Aug/95] 
+ * These were required by files which referred 
+ * to these as function pointers. 
+ * e.g. huffman.c - fread,  mgfelics.c - fgetc
+ */
+#ifndef HAVE_FREAD_DECL
+extern size_t   fread __P ((void *, size_t, size_t, FILE *));
+#endif
+#ifndef HAVE_FGETC_DECL
+extern int      fgetc __P ((FILE *));
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifndef u_long
+# define u_long unsigned long
+#endif
+#ifndef u_char
+# define u_char unsigned char
+#endif
+
+
+#endif /* SYSFUNCS_H */
Index: /gsdl/trunk/gs2common/indexers/mg/test/Makefile
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/Makefile	(revision 16561)
@@ -0,0 +1,10 @@
+DISTFILES = Makefile q1 q2 q3 r1 r2 r3 mgrc1 mgrc2 mgrc3 testmg \
+            alice.sum simple_sum.c alice.len
+
+all: simple_sum
+
+dist:
+	ln $(DISTFILES) $(distdir)
+
+simple_sum: simple_sum.c
+	cc -o $@ $?
Index: /gsdl/trunk/gs2common/indexers/mg/test/alice.len
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/alice.len	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/alice.len	(revision 16561)
@@ -0,0 +1,2 @@
+./mgdata/alice/alice.invf.dict.hash: 13256
+./mgdata/alice/alice.text.dict: 19321
Index: /gsdl/trunk/gs2common/indexers/mg/test/alice.sum
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/alice.sum	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/alice.sum	(revision 16561)
@@ -0,0 +1,8 @@
+./mgdata/alice/alice.invf: 2618428 19209
+./mgdata/alice/alice.invf.chunk: 189460 2134
+./mgdata/alice/alice.invf.chunk.trans: 331998 2585
+./mgdata/alice/alice.invf.dict.blocked: 1030626 33214
+./mgdata/alice/alice.invf.idx: 309022 7572
+./mgdata/alice/alice.text: 5075885 40099
+./mgdata/alice/alice.text.idx: 185535 3380
+./mgdata/alice/alice.text.stats: 2432380 49146
Index: /gsdl/trunk/gs2common/indexers/mg/test/mgrc1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/mgrc1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/mgrc1	(revision 16561)
@@ -0,0 +1,2 @@
+.set mode text
+.set query boolean
Index: /gsdl/trunk/gs2common/indexers/mg/test/mgrc2
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/mgrc2	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/mgrc2	(revision 16561)
@@ -0,0 +1,2 @@
+.set mode text
+.set query ranked
Index: /gsdl/trunk/gs2common/indexers/mg/test/mgrc3
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/mgrc3	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/mgrc3	(revision 16561)
@@ -0,0 +1,2 @@
+.set mode docnums
+.set query boolean
Index: /gsdl/trunk/gs2common/indexers/mg/test/q1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/q1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/q1	(revision 16561)
@@ -0,0 +1,1 @@
+flamingo
Index: /gsdl/trunk/gs2common/indexers/mg/test/q2
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/q2	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/q2	(revision 16561)
@@ -0,0 +1,1 @@
+flamingo
Index: /gsdl/trunk/gs2common/indexers/mg/test/q3
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/q3	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/q3	(revision 16561)
@@ -0,0 +1,2 @@
+Rabbit & !(Alice | she)
+laser
Index: /gsdl/trunk/gs2common/indexers/mg/test/r1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/r1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/r1	(revision 16561)
@@ -0,0 +1,46 @@
+> flamingo
+flamingo
+---------------------------------- 482
+  The chief difficulty Alice found at first was in managing her 
+flamingo:  she succeeded in getting its body tucked away, 
+comfortably enough, under her arm, with its legs hanging down, 
+but generally, just as she had got its neck nicely straightened 
+out, and was going to give the hedgehog a blow with its head, it 
+WOULD twist itself round and look up in her face, with such a 
+puzzled expression that she could not help bursting out laughing:  
+and when she had got its head down, and was going to begin again, 
+it was very provoking to find that the hedgehog had unrolled 
+itself, and was in the act of crawling away:  besides all this, 
+there was generally a ridge or furrow in the way wherever she 
+wanted to send the hedgehog to, and, as the doubled-up soldiers 
+were always getting up and walking off to other parts of the 
+ground, Alice soon came to the conclusion that it was a very 
+difficult game indeed. 
+---------------------------------- 487
+  Alice waited till the eyes appeared, and then nodded.  `It's no 
+use speaking to it,' she thought, `till its ears have come, or at 
+least one of them.'  In another minute the whole head appeared, 
+and then Alice put down her flamingo, and began an account of the 
+game, feeling very glad she had someone to listen to her.  The 
+Cat seemed to think that there was enough of it now in sight, and 
+no more of it appeared. 
+---------------------------------- 502
+  The hedgehog was engaged in a fight with another hedgehog, 
+which seemed to Alice an excellent opportunity for croqueting one 
+of them with the other:  the only difficulty was, that her 
+flamingo was gone across to the other side of the garden, where 
+Alice could see it trying in a helpless sort of way to fly up 
+into a tree. 
+---------------------------------- 503
+  By the time she had caught the flamingo and brought it back, 
+the fight was over, and both the hedgehogs were out of sight:  
+`but it doesn't matter much,' thought Alice, `as all the arches 
+are gone from the side of the ground.'  So she tucked it away 
+under her arm, that it might not escape again, and went back for 
+a little more conversation with her friend. 
+---------------------------------- 526
+  `I dare say you're wondering why I don't put my arm round your 
+waist,' the Duchess said after a pause:  `the reason is, that I'm 
+doubtful about the temper of your flamingo.  Shall I try the 
+experiment?' 
+5 documents retrieved.
Index: /gsdl/trunk/gs2common/indexers/mg/test/r2
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/r2	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/r2	(revision 16561)
@@ -0,0 +1,46 @@
+> flamingo
+flamingo
+---------------------------------- 526 1.322623
+  `I dare say you're wondering why I don't put my arm round your 
+waist,' the Duchess said after a pause:  `the reason is, that I'm 
+doubtful about the temper of your flamingo.  Shall I try the 
+experiment?' 
+---------------------------------- 503 1.013693
+  By the time she had caught the flamingo and brought it back, 
+the fight was over, and both the hedgehogs were out of sight:  
+`but it doesn't matter much,' thought Alice, `as all the arches 
+are gone from the side of the ground.'  So she tucked it away 
+under her arm, that it might not escape again, and went back for 
+a little more conversation with her friend. 
+---------------------------------- 502 1.009519
+  The hedgehog was engaged in a fight with another hedgehog, 
+which seemed to Alice an excellent opportunity for croqueting one 
+of them with the other:  the only difficulty was, that her 
+flamingo was gone across to the other side of the garden, where 
+Alice could see it trying in a helpless sort of way to fly up 
+into a tree. 
+---------------------------------- 487 0.882496
+  Alice waited till the eyes appeared, and then nodded.  `It's no 
+use speaking to it,' she thought, `till its ears have come, or at 
+least one of them.'  In another minute the whole head appeared, 
+and then Alice put down her flamingo, and began an account of the 
+game, feeling very glad she had someone to listen to her.  The 
+Cat seemed to think that there was enough of it now in sight, and 
+no more of it appeared. 
+---------------------------------- 482 0.529432
+  The chief difficulty Alice found at first was in managing her 
+flamingo:  she succeeded in getting its body tucked away, 
+comfortably enough, under her arm, with its legs hanging down, 
+but generally, just as she had got its neck nicely straightened 
+out, and was going to give the hedgehog a blow with its head, it 
+WOULD twist itself round and look up in her face, with such a 
+puzzled expression that she could not help bursting out laughing:  
+and when she had got its head down, and was going to begin again, 
+it was very provoking to find that the hedgehog had unrolled 
+itself, and was in the act of crawling away:  besides all this, 
+there was generally a ridge or furrow in the way wherever she 
+wanted to send the hedgehog to, and, as the doubled-up soldiers 
+were always getting up and walking off to other parts of the 
+ground, Alice soon came to the conclusion that it was a very 
+difficult game indeed. 
+5 documents retrieved.
Index: /gsdl/trunk/gs2common/indexers/mg/test/r3
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/r3	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/r3	(revision 16561)
@@ -0,0 +1,21 @@
+> Rabbit & !(Alice | she)
+Rabbit Alice she
+     14.14     
+    126.126    
+    143.143    
+    478.478    
+    701.701    
+    704.704    
+    705.705    
+    755.755    
+    774.774    
+    786.786    
+    788.788    
+    791.791    
+    793.793    
+    801.801    
+    803.803    
+15 documents retrieved.
+> laser
+laser
+No entries correspond to that query.
Index: /gsdl/trunk/gs2common/indexers/mg/test/simple_sum.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/test/simple_sum.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/test/simple_sum.c	(revision 16561)
@@ -0,0 +1,89 @@
+/**************************************************************************
+ *
+ * simple_sum.c -- byte summing program
+ * Copyright (C) 1995  Tim Shimmin
+ *
+ * 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.
+ *
+ *
+ **************************************************************************/
+
+/*
+ * If given -l option then just prints the length.
+ * Normally prints the sum and the length for the file.
+ */
+
+static int length_only = 0;
+
+#include <stdio.h>
+
+void 
+output_sum(fname, input_file, output_file)
+char *fname;
+FILE *input_file;
+FILE *output_file;
+{
+  int ch = '\0';
+  unsigned long sum = 0;
+  unsigned long num_bytes = 0;
+
+  while((ch = getc(input_file))!=EOF){
+    sum += ch;
+    num_bytes++;
+  }
+
+  if (length_only)
+    fprintf(output_file, "%s: %ld\n", fname, num_bytes);
+  else
+    fprintf(output_file, "%s: %ld %ld\n", fname, sum, num_bytes);
+}
+
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+  int i = 0;
+
+  /* use stdin */
+  if (argc == 1){
+    output_sum("-", stdin, stdout);
+    exit(0);
+  }
+
+  /* use args as file names for input */
+  for (i=1;i<argc; i++){
+    char *fname = argv[i];
+    FILE *input_file = NULL;
+    
+    if (!length_only && (argv[i][0] == '-') && (argv[i][1] =='l') ) 
+      { 
+        length_only = 1; 
+        continue;
+      }
+
+    input_file = fopen(fname, "r");
+    if (!input_file){
+      fprintf(stderr,"Could not open %s\n", fname);
+      continue;
+    }
+    else{
+      output_sum(fname, input_file, stdout);
+      fclose(input_file);
+    }
+  }/*for*/
+  exit(0);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mg/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/win32.mak	(revision 16561)
@@ -0,0 +1,76 @@
+###########################################################################
+#
+# win32 makefile -- mg
+# 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  The 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.
+#
+###########################################################################
+
+AR = lib
+CC = cl
+CPPFLAGS =
+
+MGHOME = $(MAKEDIR)
+DLL = 0
+DLLDEBUG = 0
+
+MDEFINES = /f win32.mak
+COMPILE = $(CC) -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+SOURCES = 
+
+OBJECTS = 
+
+OTHERDIRS = lib 
+
+INSTALLDIRS = src\text jni 
+
+MAKECMD = $(MAKE) $(MDEFINES)
+!IF $(DLL)
+MAKECMD = $(MAKECMD) DLL=1
+!ENDIF
+!IF $(DLLDEBUG)
+MAKECMD = $(MAKECMD) DLLDEBUG=1
+!ENDIF
+
+all: win32.mak $(OTHERDIRS) $(INSTALLDIRS)
+
+java:	FORCE
+	cd "java\org\greenstone\mg"
+	call winMake.bat
+	call winMake.bat install 
+	cd "$(MGHOME)"
+
+
+install: win32.mak $(INSTALLDIRS)
+
+$(OTHERDIRS): FORCE
+	cd "$@"
+	$(MAKECMD)
+	cd "$(MGHOME)"
+
+$(INSTALLDIRS): FORCE
+	cd "$@"
+	$(MAKECMD)
+	$(MAKECMD) install
+	cd "$(MGHOME)"
+
+FORCE:
Index: /gsdl/trunk/gs2common/indexers/mg/win32cfg.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/win32cfg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/win32cfg.h	(revision 16561)
@@ -0,0 +1,166 @@
+/* config.h.  Generated automatically by configure.  */
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+/* #undef _ALL_SOURCE */
+#endif
+
+/* Define if using alloca.c.  */
+/* #undef C_ALLOCA */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define if you have alloca, as a function or macro.  */
+#define HAVE_ALLOCA 1
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+/* #define HAVE_ALLOCA_H 1 */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define if on MINIX.  */
+/* #undef _MINIX */
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+/* #undef off_t */
+
+/* Define if the system does not provide POSIX.1 features except
+   with this defined.  */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define if you need to in order for stat and other things to work.  */
+/* #undef _POSIX_SOURCE */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+/* #define TIME_WITH_SYS_TIME 1 */
+
+/* Define if your <sys/time.h> declares struct tm.  */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define to the name of the distribution.  */
+#define PACKAGE "mg"
+
+/* Define to the version of the distribution.  */
+#define VERSION "1.2"
+
+/* Define to 1 if you have the valloc function.  */
+/* #define HAVE_VALLOC 1 */
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#define PROTOTYPES 1
+
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+/* #undef WITH_DMALLOC */
+
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+/* #undef WITH_REGEX */
+
+/* Some braindead header files do not have the requ'd function declarations */
+#define HAVE_FGETC_DECL 1
+#define HAVE_FREAD_DECL 1
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+/* #define HAVE_PR_BRKSIZE 1 */
+
+/* Define if you have the ftime function.  */
+/* #undef HAVE_FTIME */
+
+/* Define if you have the getpagesize function.  */
+/* #undef HAVE_GETPAGESIZE */
+
+/* Define if you have the getrusage function.  */
+/* #undef HAVE_GETRUSAGE */
+
+/* Define if you have the mallinfo function.  */
+/* #undef HAVE_MALLINFO */
+
+/* Define if you have the select function.  */
+#define HAVE_SELECT 1
+
+/* Define if you have the setbuffer function.  */
+/* #undef HAVE_SETBUFFER */
+
+/* Define if you have the strerror function.  */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strftime function.  */
+#define HAVE_STRFTIME 1
+
+/* Define if you have the strtol function.  */
+#define HAVE_STRTOL 1
+
+/* Define if you have the times function.  */
+/* #define HAVE_TIMES 1 */
+
+/* Define if you have the valloc function.  */
+/* #define HAVE_VALLOC 1 */
+
+/* Define if you have the <dirent.h> header file.  */
+/* #define HAVE_DIRENT_H 1 */
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <memory.h> header file.  */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <ndir.h> header file.  */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/dir.h> header file.  */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/ndir.h> header file.  */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/procfs.h> header file.  */
+/* #define HAVE_SYS_PROCFS_H 1 */
+
+/* Define if you have the <sys/time.h> header file.  */
+/* #define HAVE_SYS_TIME_H 1 */
+
+/* Define if you have the <unistd.h> header file.  */
+/* #define HAVE_UNISTD_H 1 */
+
+/* Define if you have the m library (-lm).  */
+#define HAVE_LIBM 1
Index: /gsdl/trunk/gs2common/indexers/mg/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/mg/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mg/winMake.bat	(revision 16561)
@@ -0,0 +1,61 @@
+@echo off
+
+set MAKE=nmake
+set MAKE_OPTIONS=/f
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto go
+if ""%1"" == ""install"" goto go
+if ""%1"" == ""clean"" goto go
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:go
+	if ""%2"" == ""javaonly"" goto java
+	if ""%2"" == ""nojava"" goto nojava
+
+:all
+	cd lib
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+
+	cd src\images
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd src\text
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd java\org\greenstone\mg
+	call winMake.bat %1
+	cd ..\..\..\..
+
+	cd jni
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+	goto done
+:java
+	cd java\org\greenstone\mg
+	call winMake.bat %1
+	cd ..\..\..\..
+	goto done
+
+:nojava
+	cd lib
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+
+	cd src\images
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd src\text
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+	goto done
+:done
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/COPYING
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/COPYING	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/COPYING	(revision 16561)
@@ -0,0 +1,344 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /gsdl/trunk/gs2common/indexers/mgpp/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/Makefile.in	(revision 16561)
@@ -0,0 +1,133 @@
+ # Main Makefile for MGPP
+# Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+ 
+# 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, 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.
+ 
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+@SET_MAKE@
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+srcdir = @srcdir@
+ 
+
+CC = @CC@
+CXX = @CXX@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@  
+TOUCH = echo timestamp >
+ 
+bindir = $(exec_prefix)/bin
+infodir = $(prefix)/info
+libexecdir = $(exec_prefix)/libexec
+ 
+MDEFINES = 
+#CC='$(CC)' CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' LIBS='$(LIBS)' \
+#prefix='$(prefix)' exec_prefix='$(exec_prefix)' \
+#bindir='$(bindir)' libexecdir='$(libexecdir)' \
+#infodir='$(infodir)' infodir='$(infodir)' libexecdir='$(libexecdir)'
+
+SUBDIRS = lib text 
+JAVADIRS = java/org/greenstone/mgpp 
+JNIDIRS = jni
+
+.SUFFIXES:
+
+all install uninstall execinstall execuninstall maninstall manuninstall:
+#config.h
+	for subdir in $(SUBDIRS) $(JAVADIRS) $(JNIDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) $@) || exit 1; \
+	done
+
+nojava: 
+	for subdir in $(SUBDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) all) || exit 1; \
+	done
+
+install-nojava: 
+	for subdir in $(SUBDIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) install) || exit 1; \
+	done
+
+javaonly:
+	for subdir in $(JAVADIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) all) || exit 1; \
+	done
+
+install-javaonly:
+	for subdir in $(JAVADIRS); do \
+	  echo making $@ in $$subdir; \
+	  (cd $$subdir && $(MAKE) $(MDEFINES) install) || exit 1; \
+	done
+
+
+mostlyclean: mostlyclean-recursive mostlyclean-local
+
+clean: clean-recursive clean-local
+
+distclean: distclean-recursive distclean-local
+	rm config.status
+
+maintainer-clean: maintainer-clean-recursive maintainer-clean-local
+	rm config.status
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+	for subdir in $(SUBDIRS) $(JAVADIRS) $(JNIDIRS); do \
+	  target=`echo $@ | sed 's/-recursive//'`; \
+	  echo making $$target in $$subdir; \
+	  (cd $$subdir && $(MAKE) $$target) || exit 1; \
+	done
+
+mostlyclean-local:
+
+clean-local: mostlyclean-local
+
+distclean-local: clean-local
+	rm -f Makefile config.cache config.h config.log stamp-h
+
+maintainer-clean-local: distclean-local
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+distdir = $(PACKAGE)-$(VERSION)
+
+distname = $(distdir)
+
+DISTFILES = \
+INSTALL.mg INSTALL README README.port COPYING MODIFICATIONS about_mg.html \
+Makefile.in acconfig.h aclocal.m4 configure.in install-sh mkinstalldirs \
+config.guess config.sub \
+config.h.in configure stamp-h.in
+
+#-----------------------------------------------------------------
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/acconfig.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/acconfig.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/acconfig.h	(revision 16561)
@@ -0,0 +1,41 @@
+/* Special definitions for mg, processed by autoheader.
+   Created by [TS:Aug/95] 
+   by grepping for AC_DEFINE on configure.in and using the appropriate defs 
+   of acconfig.h used by GNU tar.
+*/
+
+/* Define to the name of the distribution.  */
+#undef PACKAGE
+
+/* Define to the version of the distribution.  */
+#undef VERSION
+
+/* Define if `union wait' is the type of the first arg to wait functions.  */
+#undef HAVE_UNION_WAIT
+
+/* Define to 1 if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#undef PROTOTYPES
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+#undef WITH_DMALLOC
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+#undef WITH_REGEX
+
+/* Define to 1 if GNU readline should be used. */
+#undef WITH_GNU_READLINE
+
+/* Some braindead header files do not have the requ'd function declarations */
+#undef HAVE_FGETC_DECL
+#undef HAVE_FREAD_DECL
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+#undef HAVE_PR_BRKSIZE
+
+/* Define to 1 to override the automatic selection of "long long" */
+#undef DISABLE_LONG_LONG
Index: /gsdl/trunk/gs2common/indexers/mgpp/aclocal.m4
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/aclocal.m4	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/aclocal.m4	(revision 16561)
@@ -0,0 +1,224 @@
+# Local additions to Autoconf macros.
+# Copyright (C) 1992, 1994, 1995 Free Software Foundation, Inc.
+# François Pinard <pinard@iro.umontreal.ca>, 1992.
+
+# [TS:Aug/95] - chopped out the NLS stuff which I don't support
+
+## ------------------------------- ##
+## Check for function prototypes.  ##
+## ------------------------------- ##
+
+AC_DEFUN(fp_C_PROTOTYPES,
+[AC_REQUIRE([fp_PROG_CC_STDC])
+AC_MSG_CHECKING([for function prototypes])
+if test "$ac_cv_prog_cc_stdc" != no; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(PROTOTYPES)
+  U= ANSI2KNR=
+else
+  AC_MSG_RESULT(no)
+  U=_ ANSI2KNR=ansi2knr
+fi
+AC_SUBST(U)dnl
+AC_SUBST(ANSI2KNR)dnl
+])
+
+## ----------------------------------------- ##
+## ANSIfy the C compiler whenever possible.  ##
+## ----------------------------------------- ##
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so.  This macro tries various
+# options that select ANSI C on some system or another.  It considers the
+# compiler to be in ANSI C mode if it defines @code{__STDC__} to 1 and
+# handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{ac_cv_prog_cc_stdc} is set to @samp{no}.  If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+AC_DEFUN(fp_PROG_CC_STDC,
+[AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C)
+AC_CACHE_VAL(ac_cv_prog_cc_stdc,
+[ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX			-Aa -D_HPUX_SOURCE
+# SVR4			-Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xc
+do
+  CFLAGS="$ac_save_CFLAGS $ac_arg"
+  AC_TRY_COMPILE(
+[#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif	
+], [int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};],
+[ac_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CFLAGS="$ac_save_CFLAGS"
+])
+AC_MSG_RESULT($ac_cv_prog_cc_stdc)
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+])
+
+## --------------------------------------------------------- ##
+## Use AC_PROG_INSTALL, supplementing it with INSTALL_SCRIPT ##
+## substitution.					     ##
+## --------------------------------------------------------- ##
+
+AC_DEFUN(fp_PROG_INSTALL,
+[AC_PROG_INSTALL
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL} -m 755'
+AC_SUBST(INSTALL_SCRIPT)dnl
+])
+
+## ----------------------------------- ##
+## Check if --with-dmalloc was given.  ##
+## ----------------------------------- ##
+
+# I just checked, and GNU rx seems to work fine with a slightly
+# modified GNU m4.  So, I put out the test below in my aclocal.m4,
+# and will try to use it in my things.  The idea is to distribute
+# rx.[hc] and regex.[hc] together, for a while.  The WITH_REGEX symbol
+# (which should also be documented in acconfig.h) is used to decide
+# which of regex.h or rx.h should be included in the application.
+#
+# If `./configure --with-regex' is given, the package will use
+# the older regex.  Else, a check is made to see if rx is already
+# installed, as with newer Linux'es.  If not found, the package will
+# use the rx from the distribution.  If found, the package will use
+# the system's rx which, on Linux at least, will result in a smaller
+# executable file.
+
+AC_DEFUN(fp_WITH_DMALLOC,
+[AC_MSG_CHECKING(if malloc debugging is wanted)
+AC_ARG_WITH(dmalloc,
+[  --with-dmalloc          use dmalloc, as in
+                          ftp://ftp.letters.com/src/dmalloc/dmalloc.tar.gz],
+[if test "$withval" = yes; then
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(WITH_DMALLOC)
+  LIBS="$LIBS -ldmalloc"
+  LDFLAGS="$LDFLAGS -g"
+else
+  AC_MSG_RESULT(no)
+fi], [AC_MSG_RESULT(no)])
+])
+
+## --------------------------------- ##
+## Check if --with-regex was given.  ##
+## --------------------------------- ##
+
+AC_DEFUN(fp_WITH_REGEX,
+[AC_MSG_CHECKING(which of rx or regex is wanted)
+AC_ARG_WITH(regex,
+[  --with-regex            use older regex in lieu of GNU rx for matching],
+[if test "$withval" = yes; then
+  ac_with_regex=1
+  AC_MSG_RESULT(regex)
+  AC_DEFINE(WITH_REGEX)
+  AC_LIBOBJ([regex])	
+fi])
+if test -z "$ac_with_regex"; then
+  AC_MSG_RESULT(rx)
+  AC_CHECK_FUNC(re_rx_search, , 
+   [AC_CHECK_SIZEOF(unsigned char *, unsigned char *)  
+    if test "$ac_cv_sizeof_unsigned_char_p" = 8
+    then
+      AC_MSG_WARN(I'm forcing you to use regex because I can't 
+        find a local rx library and the one included with this 
+        distribution doesn't work on 64-bit machines like yours)
+      AC_LIBOBJ([regex])
+    else
+      AC_LIBOBJ([rx])
+    fi]
+  )
+fi
+])
+
+
+## --------------------------------------- ##
+## Check if --with-gnu-readline was given. ##
+## --------------------------------------- ##
+
+AC_DEFUN(fp_WITH_GNU_READLINE,
+[AC_MSG_CHECKING(whether GNU readline requested)
+  AC_ARG_WITH(gnu_readline,
+    [  --with-gnu-readline     compile with GNU readline support],
+    [if test "$withval" = yes; then
+      AC_MSG_RESULT(yes)
+      ac_with_gnu_readline=1
+    else
+      AC_MSG_RESULT(no)
+    fi],
+    AC_MSG_RESULT(no))
+
+  if test -n "$ac_with_gnu_readline"; then
+    AC_CHECK_HEADER(readline/readline.h, ac_mg_readline_header_found=1,
+	AC_MSG_WARN(Can't find GNU readline headers; configuring without \
+GNU readline support))
+    if test -n "$ac_mg_readline_header_found"; then
+      # first check whether we can find the readline library itself
+      AC_CHECK_LIB(readline, main, 
+        [ac_mg_readline_lib_found=1
+         AC_DEFINE(WITH_GNU_READLINE)
+         LIBS="$LIBS -lreadline"],
+        AC_MSG_WARN(Can't find GNU readline library; configuring without \
+GNU readline support))
+      if test -n "$ac_mg_readline_lib_found"; then
+        # on some systems, readline needs curses.  It is difficult to 
+        #  determine whether this is necessary on the current system,
+        #  since other undefined symbols can be turned up by an
+        #  AC_CHECK_LIB(readline, readline) test that are actually defined
+	#  by mg itself (e.g. xmalloc, xrealloc).  So, if we find libcurses,
+	#  we just bung it on and hope for the best.
+	AC_CHECK_LIB(curses, main,
+	  LIBS="$LIBS -lcurses")
+      fi
+    fi
+  fi
+])	
+
+
+
+#AC_DEFUN(fp_WITH_GNU_READLINE,
+#[AC_MSG_CHECKING(whether GNU readline requested)
+#  AC_ARG_WITH(gnu-readline,
+#    [  --with-gnu-readline   compile with GNU readline support],
+#    [if test "$withval" = yes; then
+#      AC_MSG_RESULT(yes)
+#      ac_with_gnu_readline=1
+#    else
+#      AC_MSG_RESULT(no)
+#    fi],
+#    AC_MSG_RESULT(no))
+#  if test -n "$ac_with_gnu_readline"; then
+#    AC_CHECK_LIB(readline, main, 
+#      AC_CHECK_HEADER(readline/readline.h,
+#	[AC_DEFINE(WITH_GNU_READLINE)
+## curses required on some systems (DEC OSF/1), but not on others (GNU/Linux,
+##  Solaris 2.6).  Forcing its inclusion is unsophisticated; if it causes 
+##  any actual problems on any UNIX platforms, let the maintainer know.
+#	LIBS="$LIBS -lreadline -lcurses"],
+#        AC_MSG_WARN(Can't find GNU readline headers: configuring \
+#without GNU readline support)
+#      ),
+#      AC_MSG_WARN(Can't find GNU readline library: configuring \
+#without GNU readline support)
+#    )
+#  fi 
+#])
Index: /gsdl/trunk/gs2common/indexers/mgpp/config.guess
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/config.guess	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/config.guess	(revision 16561)
@@ -0,0 +1,1298 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-07-12'
+
+# This file 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int dummy(){}" > $dummy.c ;
+	for c in cc gcc c89 ; do
+	  ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+	  if test $? = 0 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	rm -f $dummy.c $dummy.o $dummy.rel ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# Netbsd (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	# Determine the machine/vendor (is the vendor relevant).
+	case "${UNAME_MACHINE}" in
+	    amiga) machine=m68k-unknown ;;
+	    arm32) machine=arm-unknown ;;
+	    atari*) machine=m68k-atari ;;
+	    sun3*) machine=m68k-sun ;;
+	    mac68k) machine=m68k-apple ;;
+	    macppc) machine=powerpc-apple ;;
+	    hp3[0-9][05]) machine=m68k-hp ;;
+	    ibmrt|romp-ibm) machine=romp-ibm ;;
+	    *) machine=${UNAME_MACHINE}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE}" in
+	    i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	if test $UNAME_RELEASE = "V4.0"; then
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+	fi
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	cat <<EOF >$dummy.s
+	.data
+\$Lformat:
+	.byte 37,100,45,37,120,10,0	# "%d-%x\n"
+
+	.text
+	.globl main
+	.align 4
+	.ent main
+main:
+	.frame \$30,16,\$26,0
+	ldgp \$29,0(\$27)
+	.prologue 1
+	.long 0x47e03d80 # implver \$0
+	lda \$2,-1
+	.long 0x47e20c21 # amask \$2,\$1
+	lda \$16,\$Lformat
+	mov \$0,\$17
+	not \$1,\$18
+	jsr \$26,printf
+	ldgp \$29,0(\$26)
+	mov 0,\$16
+	jsr \$26,exit
+	.end main
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+	if test "$?" = 0 ; then
+		case `./$dummy` in
+			0-0)
+				UNAME_MACHINE="alpha"
+				;;
+			1-0)
+				UNAME_MACHINE="alphaev5"
+				;;
+			1-1)
+				UNAME_MACHINE="alphaev56"
+				;;
+			1-101)
+				UNAME_MACHINE="alphapca56"
+				;;
+			2-303)
+				UNAME_MACHINE="alphaev6"
+				;;
+			2-307)
+				UNAME_MACHINE="alphaev67"
+				;;
+		esac
+	fi
+	rm -f $dummy.s $dummy
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    arc64:OpenBSD:*:*)
+	echo mips64el-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    arc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hkmips:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    pmax:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy \
+	  && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		eval $set_cc_for_build
+		$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+		rm -f $dummy.c $dummy
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+              case "${HPUX_REV}" in
+                11.[0-9][0-9])
+                  if [ -x /usr/bin/getconf ]; then
+                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                        esac ;;
+                    esac
+                  fi ;;
+              esac
+              if [ "${HP_ARCH}" = "" ]; then
+              sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+	eval $set_cc_for_build
+	(CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+	if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+	rm -f $dummy.c $dummy
+	fi ;;
+	esac
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    hppa*:OpenBSD:*:*)
+	echo hppa-unknown-openbsd
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+	echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3D:*:*:*)
+	echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY-2:*:*:*)
+	echo cray2-cray-unicos
+        exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i386-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+	  big)    echo mips-unknown-linux-gnu && exit 0 ;;
+	  little) echo mipsel-unknown-linux-gnu && exit 0 ;;
+	esac
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev67 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	ld_supported_targets=`cd /; ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;		
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-pc-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-pc-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    M68*:*:R3V[567]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                           # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	echo `uname -p`-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	if test "${UNAME_MACHINE}" = "x86pc"; then
+		UNAME_MACHINE=pc
+	fi
+	echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-[KW]:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+eval $set_cc_for_build
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/mgpp/config.h.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/config.h.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/config.h.in	(revision 16561)
@@ -0,0 +1,209 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+#ifndef _MGPP_CONFIG_H
+#define _MGPP_CONFIG_H
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define if using alloca.c.  */
+#undef C_ALLOCA
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have alloca, as a function or macro.  */
+#undef HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+#undef HAVE_ALLOCA_H
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define if on MINIX.  */
+#undef _MINIX
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+#undef off_t
+
+/* Define if the system does not provide POSIX.1 features except
+   with this defined.  */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work.  */
+#undef _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void).  */
+#undef RETSIGTYPE
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your <sys/time.h> declares struct tm.  */
+#undef TM_IN_SYS_TIME
+
+/* Define to the name of the distribution.  */
+#undef PACKAGE
+
+/* Define to the version of the distribution.  */
+#undef VERSION
+
+/* Define to 1 if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#undef PROTOTYPES
+
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+#undef WITH_DMALLOC
+
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+#undef WITH_REGEX
+
+/* Define to 1 if GNU readline should be used. */
+#undef WITH_GNU_READLINE
+
+/* Some braindead header files do not have the requ'd function declarations */
+#undef HAVE_FGETC_DECL
+#undef HAVE_FREAD_DECL
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+#undef HAVE_PR_BRKSIZE
+
+/* Define to 1 to override the automatic selection of "long long" */
+#undef DISABLE_LONG_LONG
+
+/* The number of bytes in a unsigned char *.  */
+#undef SIZEOF_UNSIGNED_CHAR_P
+
+/* Define if you have the ftime function.  */
+#undef HAVE_FTIME
+
+/* Define if you have the ftruncate function.  */
+#undef HAVE_FTRUNCATE
+
+/* Define if you have the getpagesize function.  */
+#undef HAVE_GETPAGESIZE
+
+/* Define if you have the getrusage function.  */
+#undef HAVE_GETRUSAGE
+
+/* Define if you have the mallinfo function.  */
+#undef HAVE_MALLINFO
+
+/* Define if you have the select function.  */
+#undef HAVE_SELECT
+
+/* Define if you have the setbuffer function.  */
+#undef HAVE_SETBUFFER
+
+/* Define if you have the strcasecmp function.  */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the strerror function.  */
+#undef HAVE_STRERROR
+
+/* Define if you have the strftime function.  */
+#undef HAVE_STRFTIME
+
+/* Define if you have the strstr function.  */
+#undef HAVE_STRSTR
+
+/* Define if you have the strtol function.  */
+#undef HAVE_STRTOL
+
+/* Define if you have the times function.  */
+#undef HAVE_TIMES
+
+/* Define if you have the valloc function.  */
+#undef HAVE_VALLOC
+
+/* Define if you have the <crypt.h> header file.  */
+#undef HAVE_CRYPT_H
+
+/* Define if you have the <dirent.h> header file.  */
+#undef HAVE_DIRENT_H
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <limits.h> header file.  */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <memory.h> header file.  */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <ndir.h> header file.  */
+#undef HAVE_NDIR_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <sys/dir.h> header file.  */
+#undef HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file.  */
+#undef HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/procfs.h> header file.  */
+#undef HAVE_SYS_PROCFS_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the crypt library (-lcrypt).  */
+#undef HAVE_LIBCRYPT
+
+/* Define if you have the g library (-lg).  */
+#undef HAVE_LIBG
+
+/* Define if you have the gdbm library (-lgdbm).  */
+#undef HAVE_LIBGDBM
+
+/* Define if you have the m library (-lm).  */
+#undef HAVE_LIBM
+
+/* Define if you have the stdc++ library (-lstdc++).  */
+#undef HAVE_LIBSTDC__
+
+/* Define if you have gettext (eg glibc) */
+#undef ENABLE_NLS
+
+/* Define if big endian */
+#undef WORDS_BIGENDIAN
+
+#endif /* _MGPP_CONFIG_H */
Index: /gsdl/trunk/gs2common/indexers/mgpp/config.sub
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/config.sub	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/config.sub	(revision 16561)
@@ -0,0 +1,1375 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-06-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \
+	        | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \
+		| pyramid | mn10200 | mn10300 | tron | a29k \
+		| 580 | i960 | h8300 \
+		| x86 | ppcbe | mipsbe | mipsle | shbe | shle \
+		| hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+		| hppa64 \
+		| alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+		| alphaev6[78] \
+		| we32k | ns16k | clipper | i370 | sh | sh[34] \
+		| powerpc | powerpcle \
+		| 1750a | dsp16xx | pdp10 | pdp11 \
+		| mips16 | mips64 | mipsel | mips64el \
+		| mips64orion | mips64orionel | mipstx39 | mipstx39el \
+		| mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+		| mips64vr5000 | mips64vr5000el | mcore | s390 | s390x \
+		| sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
+		| v850 | c4x \
+		| thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
+		| pj | pjl | h8500 | z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	# FIXME: clean up the formatting here.
+	vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+	      | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \
+	      | arm-*  | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \
+	      | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+	      | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+	      | xmp-* | ymp-* \
+	      | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \
+	      | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+	      | hppa2.0n-* | hppa64-* \
+	      | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+	      | alphaev6[78]-* \
+	      | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+	      | clipper-* | orion-* \
+	      | sparclite-* | pdp10-* | pdp11-* | sh-* | sh[34]-* | sh[34]eb-* \
+	      | powerpc-* | powerpcle-* | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \
+	      | mips16-* | mips64-* | mipsel-* \
+	      | mips64el-* | mips64orion-* | mips64orionel-* \
+	      | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+	      | mipstx39-* | mipstx39el-* | mcore-* \
+	      | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
+	      | [cjt]90-* \
+	      | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+	      | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \
+	      | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	cray2)
+		basic_machine=cray2-cray
+		os=-unicos
+		;;
+	[cjt]90)
+		basic_machine=${basic_machine}-cray
+		os=-unicos
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mipsel*-linux*)
+		basic_machine=mipsel-unknown
+		os=-linux-gnu
+		;;
+	mips*-linux*)
+		basic_machine=mips-unknown
+		os=-linux-gnu
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	mmix*)
+		basic_machine=mmix-knuth
+		os=-mmixware
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+        pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2)
+		basic_machine=i686-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+	        ;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+	        ;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sparclite-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=t3e-cray
+		os=-unicos
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	windows32)
+		basic_machine=i386-pc
+		os=-windows32-msvcrt
+		;;
+	xmp)
+		basic_machine=xmp-cray
+		os=-unicos
+		;;
+        xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	mips)
+		if [ x$os = x-linux-gnu ]; then
+			basic_machine=mips-unknown
+		else
+			basic_machine=mips-mips
+		fi
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+        cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	c4x*)
+		basic_machine=c4x-none
+		os=-coff
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto*)
+		os=-nto-qnx
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-ns2 )
+	        os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+	        os=-mint
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	pdp10-*)
+		os=-tops20
+		;;
+        pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+        *-gould)
+		os=-sysv
+		;;
+        *-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+        *-sgi)
+		os=-irix
+		;;
+        *-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-vxsim* | -vxworks*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
Index: /gsdl/trunk/gs2common/indexers/mgpp/configure
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/configure	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/configure	(revision 16561)
@@ -0,0 +1,9058 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="text/MGQuery.cpp"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os PACKAGE VERSION COMPAT32BITFLAGS ENABLE_ACCENTFOLD CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT AWK YACC CC CFLAGS ac_ct_CC INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB CPP EGREP U ANSI2KNR UNAC_DIR ALLOCA LIBOBJS JNIINC JNISUFFIX JNIFLAGS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-accentfold          Disable Accent Folding for MGPP
+  --enable-override-longlong
+                          overide use of long long with GCC
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-unac=dir         Use local unac library and headers
+  --with-dmalloc          use dmalloc, as in
+                          ftp://ftp.letters.com/src/dmalloc/dmalloc.tar.gz
+  --with-regex            use older regex in lieu of GNU rx for matching
+  --with-gnu-readline     compile with GNU readline support
+
+Some influential environment variables:
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+          ac_config_headers="$ac_config_headers config.h"
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking target system type" >&5
+echo $ECHO_N "checking target system type... $ECHO_C" >&6
+if test "${ac_cv_target+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_target_alias=$target_alias
+test "x$ac_cv_target_alias" = "x" &&
+  ac_cv_target_alias=$ac_cv_host_alias
+ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+echo "${ECHO_T}$ac_cv_target" >&6
+target=$ac_cv_target
+target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,$program_prefix,;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$,$program_suffix,;$program_transform_name"
+# Double any \ or $.  echo might interpret backslashes.
+# By default was `s,x,x', remove it if useless.
+cat <<\_ACEOF >conftest.sed
+s/[\\$]/&&/g;s/;s,x,x,$//
+_ACEOF
+program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
+rm conftest.sed
+
+
+PACKAGE=mgpp
+VERSION=1.3
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+
+
+
+
+echo "$as_me:$LINENO: checking to see if architecture is 64-bit" >&5
+echo $ECHO_N "checking to see if architecture is 64-bit... $ECHO_C" >&6
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+
+
+# Check whether --enable-accentfold or --disable-accentfold was given.
+if test "${enable_accentfold+set}" = set; then
+  enableval="$enable_accentfold"
+  ENABLE_ACCENTFOLD=0
+else
+  ENABLE_ACCENTFOLD=1
+fi;
+cat >>confdefs.h <<\_ACEOF
+#define ENABLE_ACCENTFOLD $ENABLE_ACCENTFOLD
+_ACEOF
+
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5
+echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5
+echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$AWK" && break
+done
+
+for ac_prog in 'bison -y' byacc
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_YACC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$YACC"; then
+  ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_YACC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+  echo "$as_me:$LINENO: result: $YACC" >&5
+echo "${ECHO_T}$YACC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	    break 3
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6
+fi
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+	@echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+
+echo "$as_me:$LINENO: checking for AIX" >&5
+echo $ECHO_N "checking for AIX... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef _AIX
+  yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+cat >>confdefs.h <<\_ACEOF
+#define _ALL_SOURCE 1
+_ACEOF
+
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f conftest*
+
+
+echo "$as_me:$LINENO: checking for library containing strerror" >&5
+echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6
+if test "${ac_cv_search_strerror+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_strerror=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_strerror="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_strerror" = no; then
+  for ac_lib in cposix; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_strerror="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5
+echo "${ECHO_T}$ac_cv_search_strerror" >&6
+if test "$ac_cv_search_strerror" != no; then
+  test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS"
+
+fi
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking minix/config.h usability" >&5
+echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <minix/config.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking minix/config.h presence" >&5
+echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <minix/config.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: minix/config.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: minix/config.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_minix_config_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6
+
+fi
+if test $ac_cv_header_minix_config_h = yes; then
+  MINIX=yes
+else
+  MINIX=
+fi
+
+
+if test "$MINIX" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_SOURCE 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_1_SOURCE 2
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _MINIX 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for ${CC-cc} option to accept ANSI C" >&5
+echo $ECHO_N "checking for ${CC-cc} option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CFLAGS="$CFLAGS"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX			-Aa -D_HPUX_SOURCE
+# SVR4			-Xc
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" -Xc
+do
+  CFLAGS="$ac_save_CFLAGS $ac_arg"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if !defined(__STDC__) || __STDC__ != 1
+choke me
+#endif
+
+int
+main ()
+{
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc="$ac_arg"; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+CFLAGS="$ac_save_CFLAGS"
+
+fi
+
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+
+echo "$as_me:$LINENO: checking for function prototypes" >&5
+echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6
+if test "$ac_cv_prog_cc_stdc" != no; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define PROTOTYPES 1
+_ACEOF
+
+  U= ANSI2KNR=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  U=_ ANSI2KNR=ansi2knr
+fi
+
+echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6
+if test "${ac_cv_c_const+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this.  */
+  typedef int charset[2];
+  const charset x;
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *ccp;
+  char **p;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* AIX XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  ccp = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++ccp;
+  p = (char**) ccp;
+  ccp = (char const *const *) p;
+  { /* SCO 3.2v4 cc rejects this.  */
+    char *t;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* AIX XL C 1.02.0.0 rejects this saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; };
+    struct s *b; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+  }
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_const=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_const=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+echo "${ECHO_T}$ac_cv_c_const" >&6
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for off_t" >&5
+echo $ECHO_N "checking for off_t... $ECHO_C" >&6
+if test "${ac_cv_type_off_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((off_t *) 0)
+  return 0;
+if (sizeof (off_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_off_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_off_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5
+echo "${ECHO_T}$ac_cv_type_off_t" >&6
+if test $ac_cv_type_off_t = yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for size_t" >&5
+echo $ECHO_N "checking for size_t... $ECHO_C" >&6
+if test "${ac_cv_type_size_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((size_t *) 0)
+  return 0;
+if (sizeof (size_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_size_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_size_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+echo "${ECHO_T}$ac_cv_type_size_t" >&6
+if test $ac_cv_type_size_t = yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5
+echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6
+if test "${ac_cv_header_time+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_time=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_time=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5
+echo "${ECHO_T}$ac_cv_header_time" >&6
+if test $ac_cv_header_time = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TIME_WITH_SYS_TIME 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6
+if test "${ac_cv_struct_tm+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm *tp; tp->tm_sec;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_struct_tm=time.h
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+echo "${ECHO_T}$ac_cv_struct_tm" >&6
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+
+# Check whether --with-unac or --without-unac was given.
+if test "${with_unac+set}" = set; then
+  withval="$with_unac"
+  UNAC_DIR=$withval
+else
+  UNAC_DIR="`pwd`/../packages/unac"
+fi;
+cat >>confdefs.h <<_ACEOF
+#define UNAC_DIR "$UNAC_DIR"
+_ACEOF
+
+
+
+
+if test "$ac_cv_prog_cc_stdc" = '-Xc'; then
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+int
+main ()
+{
+ FILE *rubbish = popen("yes", "r");
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+CC="`echo $CC | sed 's/-Xc/-Xa/'`"    ac_cv_prog_cc_stdc='-Xa'
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+
+echo "$as_me:$LINENO: checking for main in -lg" >&5
+echo $ECHO_N "checking for main in -lg... $ECHO_C" >&6
+if test "${ac_cv_lib_g_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lg  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_g_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_g_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_g_main" >&5
+echo "${ECHO_T}$ac_cv_lib_g_main" >&6
+if test $ac_cv_lib_g_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBG 1
+_ACEOF
+
+  LIBS="-lg $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for main in -lm" >&5
+echo $ECHO_N "checking for main in -lm... $ECHO_C" >&6
+if test "${ac_cv_lib_m_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_m_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_m_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_m_main" >&5
+echo "${ECHO_T}$ac_cv_lib_m_main" >&6
+if test $ac_cv_lib_m_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBM 1
+_ACEOF
+
+  LIBS="-lm $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for main in -lstdc++" >&5
+echo $ECHO_N "checking for main in -lstdc++... $ECHO_C" >&6
+if test "${ac_cv_lib_stdcpp_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lstdc++  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_stdcpp_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_stdcpp_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_stdcpp_main" >&5
+echo "${ECHO_T}$ac_cv_lib_stdcpp_main" >&6
+if test $ac_cv_lib_stdcpp_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSTDC__ 1
+_ACEOF
+
+  LIBS="-lstdc++ $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for main in -lcrypt" >&5
+echo $ECHO_N "checking for main in -lcrypt... $ECHO_C" >&6
+if test "${ac_cv_lib_crypt_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_crypt_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_crypt_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_crypt_main" >&5
+echo "${ECHO_T}$ac_cv_lib_crypt_main" >&6
+if test $ac_cv_lib_crypt_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBCRYPT 1
+_ACEOF
+
+  LIBS="-lcrypt $LIBS"
+
+fi
+
+if test $ENABLE_ACCENTFOLD = 1; then
+
+echo "$as_me:$LINENO: checking for main in -liconv" >&5
+echo $ECHO_N "checking for main in -liconv... $ECHO_C" >&6
+if test "${ac_cv_lib_iconv_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-liconv  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_iconv_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_iconv_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_iconv_main" >&5
+echo "${ECHO_T}$ac_cv_lib_iconv_main" >&6
+if test $ac_cv_lib_iconv_main = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBICONV 1
+_ACEOF
+
+  LIBS="-liconv $LIBS"
+
+fi
+ac_cv_lib_iconv=ac_cv_lib_iconv_main
+
+fi
+
+
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+  echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+  for ac_lib in dir; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+  test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+else
+  echo "$as_me:$LINENO: checking for library containing opendir" >&5
+echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6
+if test "${ac_cv_search_opendir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_opendir=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_opendir" = no; then
+  for ac_lib in x; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main ()
+{
+opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_opendir="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+echo "${ECHO_T}$ac_cv_search_opendir" >&6
+if test "$ac_cv_search_opendir" != no; then
+  test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS"
+
+fi
+
+fi
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+for ac_header in fcntl.h limits.h sys/time.h unistd.h crypt.h string.h memory.h sys/procfs.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "fread" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FREAD_DECL 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "fgetc" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FGETC_DECL 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/procfs.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pr_brksize" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_PR_BRKSIZE 1
+_ACEOF
+
+fi
+rm -f conftest*
+
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments.  Useless!
+echo "$as_me:$LINENO: checking for working alloca.h" >&5
+echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6
+if test "${ac_cv_working_alloca_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <alloca.h>
+int
+main ()
+{
+char *p = (char *) alloca (2 * sizeof (int));
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_working_alloca_h=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_working_alloca_h=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5
+echo "${ECHO_T}$ac_cv_working_alloca_h" >&6
+if test $ac_cv_working_alloca_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA_H 1
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for alloca" >&5
+echo $ECHO_N "checking for alloca... $ECHO_C" >&6
+if test "${ac_cv_func_alloca_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
+# else
+#  if HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else
+#   ifdef _AIX
+ #pragma alloca
+#   else
+#    ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#    endif
+#   endif
+#  endif
+# endif
+#endif
+
+int
+main ()
+{
+char *p = (char *) alloca (1);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_alloca_works=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_alloca_works=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5
+echo "${ECHO_T}$ac_cv_func_alloca_works" >&6
+
+if test $ac_cv_func_alloca_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA 1
+_ACEOF
+
+else
+  # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble.  Some versions do not even contain alloca or
+# contain a buggy version.  If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=alloca.$ac_objext
+
+cat >>confdefs.h <<\_ACEOF
+#define C_ALLOCA 1
+_ACEOF
+
+
+echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5
+echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6
+if test "${ac_cv_os_cray+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if defined(CRAY) && ! defined(CRAY2)
+webecray
+#else
+wenotbecray
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "webecray" >/dev/null 2>&1; then
+  ac_cv_os_cray=yes
+else
+  ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5
+echo "${ECHO_T}$ac_cv_os_cray" >&6
+if test $ac_cv_os_cray = yes; then
+  for ac_func in _getb67 GETB67 getb67; do
+    as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define CRAY_STACKSEG_END $ac_func
+_ACEOF
+
+    break
+fi
+
+  done
+fi
+
+echo "$as_me:$LINENO: checking stack direction for C alloca" >&5
+echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6
+if test "${ac_cv_c_stack_direction+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_c_stack_direction=0
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+find_stack_direction ()
+{
+  static char *addr = 0;
+  auto char dummy;
+  if (addr == 0)
+    {
+      addr = &dummy;
+      return find_stack_direction ();
+    }
+  else
+    return (&dummy > addr) ? 1 : -1;
+}
+
+int
+main ()
+{
+  exit (find_stack_direction () < 0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_stack_direction=1
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_stack_direction=-1
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5
+echo "${ECHO_T}$ac_cv_c_stack_direction" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+_ACEOF
+
+
+fi
+
+if test $ac_cv_c_compiler_gnu = yes; then
+    echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+    ac_pattern="Autoconf.*'x'"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+else
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+  fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6
+if test "${ac_cv_type_signal+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+# undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int
+main ()
+{
+int i;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_signal=void
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_signal=int
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+echo "${ECHO_T}$ac_cv_type_signal" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+
+for ac_func in vprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+echo "$as_me:$LINENO: checking for _doprnt" >&5
+echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6
+if test "${ac_cv_func__doprnt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+char (*f) () = _doprnt;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != _doprnt;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func__doprnt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func__doprnt=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+echo "${ECHO_T}$ac_cv_func__doprnt" >&6
+if test $ac_cv_func__doprnt = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in ftime select strftime strtol getrusage times mallinfo setbuffer getpagesize strerror
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+for ac_func in ftruncate strstr strcasecmp
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  case $LIBOBJS in
+    "$ac_func.$ac_objext"   | \
+  *" $ac_func.$ac_objext"   | \
+    "$ac_func.$ac_objext "* | \
+  *" $ac_func.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS $ac_func.$ac_objext" ;;
+esac
+
+fi
+done
+
+
+
+
+echo "$as_me:$LINENO: checking for textdomain" >&5
+echo $ECHO_N "checking for textdomain... $ECHO_C" >&6
+if test "${ac_cv_func_textdomain+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define textdomain to an innocuous variant, in case <limits.h> declares textdomain.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define textdomain innocuous_textdomain
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char textdomain (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef textdomain
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char textdomain ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_textdomain) || defined (__stub___textdomain)
+choke me
+#else
+char (*f) () = textdomain;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != textdomain;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_textdomain=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_textdomain=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_textdomain" >&5
+echo "${ECHO_T}$ac_cv_func_textdomain" >&6
+if test $ac_cv_func_textdomain = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define ENABLE_NLS  1
+_ACEOF
+
+fi
+
+
+# we need to set an extended JNI path for Mac OS/Darwin, as jni.h is on
+# a non-standard Path
+# is there a better way to do this??
+echo "$as_me:$LINENO: checking for OS to set JNI options" >&5
+echo $ECHO_N "checking for OS to set JNI options... $ECHO_C" >&6
+# set defaults
+JNIINC=""
+JNISUFFIX="so"
+JNIFLAGS="-shared"
+
+if test "`(uname -s) 2> /dev/null`" = 'Darwin'; then
+  echo "$as_me:$LINENO: result: Darwin" >&5
+echo "${ECHO_T}Darwin" >&6
+  JNIINC="-I/System/Library/Frameworks/JavaVM.framework/Headers/ "
+  JNISUFFIX="jnilib"
+  JNIFLAGS="-dynamiclib -fno-common -single_module -framework JavaVM"
+fi
+if test "`(uname -s) 2> /dev/null`" = 'SunOS'; then
+  echo "$as_me:$LINENO: result: Solaris" >&5
+echo "${ECHO_T}Solaris" >&6
+  JNIINC="-I\$(JAVA_HOME)/include/solaris "
+fi
+if test "`(uname -s) 2> /dev/null`" = 'Linux'; then
+  echo "$as_me:$LINENO: result: Linux" >&5
+echo "${ECHO_T}Linux" >&6
+  JNIINC="-I\$(JAVA_HOME)/include/linux "
+fi
+
+
+
+
+
+# *** Custom checking (based on GNU tar configure.in) ***
+# ---------------------------------------------------------------------------
+echo "$as_me:$LINENO: checking for HP-UX needing gmalloc" >&5
+echo $ECHO_N "checking for HP-UX needing gmalloc... $ECHO_C" >&6
+if test "`(uname -s) 2> /dev/null`" = 'HP-UX'; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  case $LIBOBJS in
+    "gmalloc.$ac_objext"   | \
+  *" gmalloc.$ac_objext"   | \
+    "gmalloc.$ac_objext "* | \
+  *" gmalloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS gmalloc.$ac_objext" ;;
+esac
+
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_VALLOC 1
+_ACEOF
+
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+
+for ac_func in valloc
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+fi
+
+echo "$as_me:$LINENO: checking if malloc debugging is wanted" >&5
+echo $ECHO_N "checking if malloc debugging is wanted... $ECHO_C" >&6
+
+# Check whether --with-dmalloc or --without-dmalloc was given.
+if test "${with_dmalloc+set}" = set; then
+  withval="$with_dmalloc"
+  if test "$withval" = yes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define WITH_DMALLOC 1
+_ACEOF
+
+  LIBS="$LIBS -ldmalloc"
+  LDFLAGS="$LDFLAGS -g"
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi;
+
+echo "$as_me:$LINENO: checking which of rx or regex is wanted" >&5
+echo $ECHO_N "checking which of rx or regex is wanted... $ECHO_C" >&6
+
+# Check whether --with-regex or --without-regex was given.
+if test "${with_regex+set}" = set; then
+  withval="$with_regex"
+  if test "$withval" = yes; then
+  ac_with_regex=1
+  echo "$as_me:$LINENO: result: regex" >&5
+echo "${ECHO_T}regex" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define WITH_REGEX 1
+_ACEOF
+
+  case $LIBOBJS in
+    "regex.$ac_objext"   | \
+  *" regex.$ac_objext"   | \
+    "regex.$ac_objext "* | \
+  *" regex.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS regex.$ac_objext" ;;
+esac
+
+fi
+fi;
+if test -z "$ac_with_regex"; then
+  echo "$as_me:$LINENO: result: rx" >&5
+echo "${ECHO_T}rx" >&6
+  echo "$as_me:$LINENO: checking for re_rx_search" >&5
+echo $ECHO_N "checking for re_rx_search... $ECHO_C" >&6
+if test "${ac_cv_func_re_rx_search+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define re_rx_search to an innocuous variant, in case <limits.h> declares re_rx_search.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define re_rx_search innocuous_re_rx_search
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char re_rx_search (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef re_rx_search
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char re_rx_search ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_re_rx_search) || defined (__stub___re_rx_search)
+choke me
+#else
+char (*f) () = re_rx_search;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != re_rx_search;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_re_rx_search=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_re_rx_search=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_re_rx_search" >&5
+echo "${ECHO_T}$ac_cv_func_re_rx_search" >&6
+if test $ac_cv_func_re_rx_search = yes; then
+  :
+else
+  echo "$as_me:$LINENO: checking for unsigned char *" >&5
+echo $ECHO_N "checking for unsigned char *... $ECHO_C" >&6
+if test "${ac_cv_type_unsigned_char_p+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((unsigned char * *) 0)
+  return 0;
+if (sizeof (unsigned char *))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_unsigned_char_p=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_unsigned_char_p=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_unsigned_char_p" >&5
+echo "${ECHO_T}$ac_cv_type_unsigned_char_p" >&6
+
+echo "$as_me:$LINENO: checking size of unsigned char *" >&5
+echo $ECHO_N "checking size of unsigned char *... $ECHO_C" >&6
+if test "${ac_cv_sizeof_unsigned_char_p+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$ac_cv_type_unsigned_char_p" = yes; then
+  # The cast to unsigned long works around a bug in the HP C Compiler
+  # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+  # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+  # This bug is HP SR number 8606223364.
+  if test "$cross_compiling" = yes; then
+  # Depending upon the size, compute the lo and hi bounds.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long) (sizeof (unsigned char *))) >= 0)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long) (sizeof (unsigned char *))) <= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_hi=$ac_mid; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_lo=`expr $ac_mid + 1`
+		    if test $ac_lo -le $ac_mid; then
+		      ac_lo= ac_hi=
+		      break
+		    fi
+		    ac_mid=`expr 2 '*' $ac_mid + 1`
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long) (sizeof (unsigned char *))) < 0)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long) (sizeof (unsigned char *))) >= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_lo=$ac_mid; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_hi=`expr '(' $ac_mid ')' - 1`
+		       if test $ac_mid -le $ac_hi; then
+			 ac_lo= ac_hi=
+			 break
+		       fi
+		       ac_mid=`expr 2 '*' $ac_mid`
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_lo= ac_hi=
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo`
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long) (sizeof (unsigned char *))) <= $ac_mid)];
+test_array [0] = 0
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_hi=$ac_mid
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_lo=`expr '(' $ac_mid ')' + 1`
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in
+?*) ac_cv_sizeof_unsigned_char_p=$ac_lo;;
+'') { { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned char *), 77
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (unsigned char *), 77
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+else
+  if test "$cross_compiling" = yes; then
+  { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+long longval () { return (long) (sizeof (unsigned char *)); }
+unsigned long ulongval () { return (long) (sizeof (unsigned char *)); }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    exit (1);
+  if (((long) (sizeof (unsigned char *))) < 0)
+    {
+      long i = longval ();
+      if (i != ((long) (sizeof (unsigned char *))))
+	exit (1);
+      fprintf (f, "%ld\n", i);
+    }
+  else
+    {
+      unsigned long i = ulongval ();
+      if (i != ((long) (sizeof (unsigned char *))))
+	exit (1);
+      fprintf (f, "%lu\n", i);
+    }
+  exit (ferror (f) || fclose (f) != 0);
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_sizeof_unsigned_char_p=`cat conftest.val`
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+{ { echo "$as_me:$LINENO: error: cannot compute sizeof (unsigned char *), 77
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (unsigned char *), 77
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.val
+else
+  ac_cv_sizeof_unsigned_char_p=0
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_sizeof_unsigned_char_p" >&5
+echo "${ECHO_T}$ac_cv_sizeof_unsigned_char_p" >&6
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_CHAR_P $ac_cv_sizeof_unsigned_char_p
+_ACEOF
+
+
+    if test "$ac_cv_sizeof_unsigned_char_p" = 8
+    then
+      { echo "$as_me:$LINENO: WARNING: I'm forcing you to use regex because I can't
+        find a local rx library and the one included with this
+        distribution doesn't work on 64-bit machines like yours" >&5
+echo "$as_me: WARNING: I'm forcing you to use regex because I can't
+        find a local rx library and the one included with this
+        distribution doesn't work on 64-bit machines like yours" >&2;}
+      case $LIBOBJS in
+    "regex.$ac_objext"   | \
+  *" regex.$ac_objext"   | \
+    "regex.$ac_objext "* | \
+  *" regex.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS regex.$ac_objext" ;;
+esac
+
+    else
+      case $LIBOBJS in
+    "rx.$ac_objext"   | \
+  *" rx.$ac_objext"   | \
+    "rx.$ac_objext "* | \
+  *" rx.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS rx.$ac_objext" ;;
+esac
+
+    fi
+
+fi
+
+fi
+
+echo "$as_me:$LINENO: checking whether GNU readline requested" >&5
+echo $ECHO_N "checking whether GNU readline requested... $ECHO_C" >&6
+
+# Check whether --with-gnu_readline or --without-gnu_readline was given.
+if test "${with_gnu_readline+set}" = set; then
+  withval="$with_gnu_readline"
+  if test "$withval" = yes; then
+      echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+      ac_with_gnu_readline=1
+    else
+      echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+    fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi;
+
+  if test -n "$ac_with_gnu_readline"; then
+    if test "${ac_cv_header_readline_readline_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for readline/readline.h" >&5
+echo $ECHO_N "checking for readline/readline.h... $ECHO_C" >&6
+if test "${ac_cv_header_readline_readline_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_readline_readline_h" >&5
+echo "${ECHO_T}$ac_cv_header_readline_readline_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking readline/readline.h usability" >&5
+echo $ECHO_N "checking readline/readline.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <readline/readline.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking readline/readline.h presence" >&5
+echo $ECHO_N "checking readline/readline.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <readline/readline.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: readline/readline.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: readline/readline.h: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: readline/readline.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: readline/readline.h:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: readline/readline.h: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: readline/readline.h:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: readline/readline.h: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: readline/readline.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: readline/readline.h: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for readline/readline.h" >&5
+echo $ECHO_N "checking for readline/readline.h... $ECHO_C" >&6
+if test "${ac_cv_header_readline_readline_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_readline_readline_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_readline_readline_h" >&5
+echo "${ECHO_T}$ac_cv_header_readline_readline_h" >&6
+
+fi
+if test $ac_cv_header_readline_readline_h = yes; then
+  ac_mg_readline_header_found=1
+else
+  { echo "$as_me:$LINENO: WARNING: Can't find GNU readline headers; configuring without \
+GNU readline support" >&5
+echo "$as_me: WARNING: Can't find GNU readline headers; configuring without \
+GNU readline support" >&2;}
+fi
+
+
+    if test -n "$ac_mg_readline_header_found"; then
+      # first check whether we can find the readline library itself
+      echo "$as_me:$LINENO: checking for main in -lreadline" >&5
+echo $ECHO_N "checking for main in -lreadline... $ECHO_C" >&6
+if test "${ac_cv_lib_readline_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_readline_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_readline_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_readline_main" >&5
+echo "${ECHO_T}$ac_cv_lib_readline_main" >&6
+if test $ac_cv_lib_readline_main = yes; then
+  ac_mg_readline_lib_found=1
+         cat >>confdefs.h <<\_ACEOF
+#define WITH_GNU_READLINE 1
+_ACEOF
+
+         LIBS="$LIBS -lreadline"
+else
+  { echo "$as_me:$LINENO: WARNING: Can't find GNU readline library; configuring without \
+GNU readline support" >&5
+echo "$as_me: WARNING: Can't find GNU readline library; configuring without \
+GNU readline support" >&2;}
+fi
+
+      if test -n "$ac_mg_readline_lib_found"; then
+        # on some systems, readline needs curses.  It is difficult to
+        #  determine whether this is necessary on the current system,
+        #  since other undefined symbols can be turned up by an
+        #  AC_CHECK_LIB(readline, readline) test that are actually defined
+	#  by mg itself (e.g. xmalloc, xrealloc).  So, if we find libcurses,
+	#  we just bung it on and hope for the best.
+	echo "$as_me:$LINENO: checking for main in -lcurses" >&5
+echo $ECHO_N "checking for main in -lcurses... $ECHO_C" >&6
+if test "${ac_cv_lib_curses_main+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+main ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_curses_main=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_curses_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_curses_main" >&5
+echo "${ECHO_T}$ac_cv_lib_curses_main" >&6
+if test $ac_cv_lib_curses_main = yes; then
+  LIBS="$LIBS -lcurses"
+fi
+
+      fi
+    fi
+  fi
+
+
+# text for endianness
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+
+cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+ ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+
+# ---------------------------------------------------------------------------
+if test "$ac_cv_func_alloca" = 'no'; then
+  case $LIBOBJS in
+    "xmalloc.$ac_objext"   | \
+  *" xmalloc.$ac_objext"   | \
+    "xmalloc.$ac_objext "* | \
+  *" xmalloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS xmalloc.$ac_objext" ;;
+esac
+
+  case $LIBOBJS in
+    "error.$ac_objext"   | \
+  *" error.$ac_objext"   | \
+    "error.$ac_objext "* | \
+  *" error.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS error.$ac_objext" ;;
+esac
+
+fi
+
+
+# ---------------------------------------------------------------------------
+# see if the user wants to override use of long long
+# Check whether --enable-override-longlong or --disable-override-longlong was given.
+if test "${enable_override_longlong+set}" = set; then
+  enableval="$enable_override_longlong"
+  cat >>confdefs.h <<\_ACEOF
+#define DISABLE_LONG_LONG 1
+_ACEOF
+
+fi;
+
+
+# ---------------------------------------------------------------------------
+
+                                                  ac_config_files="$ac_config_files Makefile text/Makefile lib/Makefile jni/Makefile java/org/greenstone/mgpp/Makefile"
+          ac_config_commands="$ac_config_commands default"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "text/Makefile" ) CONFIG_FILES="$CONFIG_FILES text/Makefile" ;;
+  "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
+  "jni/Makefile" ) CONFIG_FILES="$CONFIG_FILES jni/Makefile" ;;
+  "java/org/greenstone/mgpp/Makefile" ) CONFIG_FILES="$CONFIG_FILES java/org/greenstone/mgpp/Makefile" ;;
+  "default" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
+  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@target@,$target,;t t
+s,@target_cpu@,$target_cpu,;t t
+s,@target_vendor@,$target_vendor,;t t
+s,@target_os@,$target_os,;t t
+s,@PACKAGE@,$PACKAGE,;t t
+s,@VERSION@,$VERSION,;t t
+s,@COMPAT32BITFLAGS@,$COMPAT32BITFLAGS,;t t
+s,@ENABLE_ACCENTFOLD@,$ENABLE_ACCENTFOLD,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@AWK@,$AWK,;t t
+s,@YACC@,$YACC,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@LN_S@,$LN_S,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@U@,$U,;t t
+s,@ANSI2KNR@,$ANSI2KNR,;t t
+s,@UNAC_DIR@,$UNAC_DIR,;t t
+s,@ALLOCA@,$ALLOCA,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@JNIINC@,$JNIINC,;t t
+s,@JNISUFFIX@,$JNISUFFIX,;t t
+s,@JNIFLAGS@,$JNIFLAGS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_COMMANDS section.
+#
+for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
+  ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
+  ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
+  ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
+$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_dest" : 'X\(//\)[^/]' \| \
+	 X"$ac_dest" : 'X\(//\)$' \| \
+	 X"$ac_dest" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_dest" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
+echo "$as_me: executing $ac_dest commands" >&6;}
+  case $ac_dest in
+    default ) test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h ;;
+  esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/configure.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/configure.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/configure.in	(revision 16561)
@@ -0,0 +1,192 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(text/MGQuery.cpp)
+AC_CONFIG_HEADER(config.h)
+
+dnl get $target_cpu, $target_vendor, and $target_os
+dnl (as well as host_*)
+AC_CANONICAL_SYSTEM
+AC_ARG_PROGRAM
+
+dnl
+PACKAGE=mgpp
+VERSION=1.3
+
+AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
+AC_SUBST(PACKAGE)
+AC_SUBST(VERSION)
+
+
+AC_MSG_CHECKING(to see if architecture is 64-bit)
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  AC_MSG_RESULT(yes)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  AC_MSG_RESULT(no)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+AC_SUBST(COMPAT32BITFLAGS)
+
+dnl 
+dnl disable compilation of accent folding stuff for mgpp (and one day mg)
+dnl
+AC_ARG_ENABLE(accentfold, [  --disable-accentfold          Disable Accent Folding for MGPP], ENABLE_ACCENTFOLD=0, ENABLE_ACCENTFOLD=1)
+AC_DEFINE(ENABLE_ACCENTFOLD, $ENABLE_ACCENTFOLD)
+AC_SUBST(ENABLE_ACCENTFOLD)
+
+dnl Checks for programs.
+AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_YACC
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_RANLIB
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_AIX
+AC_ISC_POSIX
+AC_MINIX
+fp_C_PROTOTYPES
+AC_C_CONST
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+dnl
+dnl look for unac package if not in standard location
+dnl
+dnl is it ok to use `pwd` like this??
+AC_ARG_WITH(unac, [  --with-unac=dir         Use local unac library and headers], UNAC_DIR=$withval, UNAC_DIR="`pwd`/../packages/unac")
+AC_DEFINE_UNQUOTED(UNAC_DIR, "$UNAC_DIR")
+AC_SUBST(UNAC_DIR)
+
+
+dnl --- This should be in the stdc checking -----------------
+if test "$ac_cv_prog_cc_stdc" = '-Xc'; then
+dnl The Pyramids running SMP_DC.OSx on MIPS r3000 chips with
+dnl /usr/opt/cc3.11/bin/cc treat -Xc as full ANSI mode - no POSIX or
+dnl XOPEN extensions (like popen).
+dnl Unfortunately -D_{XOPEN,POSIX}_SOURCE can't provide all that mg
+dnl needs. So drop back to -Xa
+AC_TRY_COMPILE(
+[#include <stdio.h>
+], [ FILE *rubbish = popen("yes", "r"); ],,
+[CC="`echo $CC | sed 's/-Xc/-Xa/'`"    ac_cv_prog_cc_stdc='-Xa'])
+fi
+dnl ----------------------
+
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lg:
+AC_CHECK_LIB(g, main)
+dnl Replace `main' with a function in -lm:
+AC_CHECK_LIB(m, main)
+dnl Replace `main' with a function in -lstdc++:
+AC_CHECK_LIB(stdc++, main)
+dnl Replace `main' with a function in -lcrypt:
+AC_CHECK_LIB(crypt, main)
+if test $ENABLE_ACCENTFOLD = 1; then
+AC_HAVE_LIBRARY(iconv)
+fi
+
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h limits.h sys/time.h unistd.h crypt.h string.h memory.h sys/procfs.h)
+AC_EGREP_HEADER(fread, stdio.h, AC_DEFINE(HAVE_FREAD_DECL))
+AC_EGREP_HEADER(fgetc, stdio.h, AC_DEFINE(HAVE_FGETC_DECL))
+AC_EGREP_HEADER(pr_brksize, sys/procfs.h, AC_DEFINE(HAVE_PR_BRKSIZE))
+
+dnl Checks for library functions.
+AC_FUNC_ALLOCA
+AC_PROG_GCC_TRADITIONAL
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(ftime select strftime strtol getrusage times mallinfo setbuffer getpagesize strerror)
+AC_REPLACE_FUNCS(ftruncate strstr strcasecmp)
+
+
+dnl see if gettext is in libc. (try the textdomain() function).
+AC_CHECK_FUNC(textdomain, AC_DEFINE(ENABLE_NLS ,1) )
+
+# we need to set an extended JNI path for Mac OS/Darwin, as jni.h is on
+# a non-standard Path
+# is there a better way to do this??
+AC_MSG_CHECKING(for OS to set JNI options)
+# set defaults
+JNIINC=""
+JNISUFFIX="so"
+JNIFLAGS="-shared"
+
+if test "`(uname -s) 2> /dev/null`" = 'Darwin'; then
+  AC_MSG_RESULT(Darwin)
+  JNIINC="-I/System/Library/Frameworks/JavaVM.framework/Headers/ "
+  JNISUFFIX="jnilib"
+  JNIFLAGS="-dynamiclib -fno-common -single_module -framework JavaVM"
+fi
+if test "`(uname -s) 2> /dev/null`" = 'SunOS'; then
+  AC_MSG_RESULT(Solaris)
+  JNIINC="-I\$(JAVA_HOME)/include/solaris "
+fi
+if test "`(uname -s) 2> /dev/null`" = 'Linux'; then
+  AC_MSG_RESULT(Linux)
+  JNIINC="-I\$(JAVA_HOME)/include/linux "
+fi
+
+AC_SUBST(JNIINC)
+AC_SUBST(JNISUFFIX)
+AC_SUBST(JNIFLAGS)
+
+# *** Custom checking (based on GNU tar configure.in) ***
+# ---------------------------------------------------------------------------
+AC_MSG_CHECKING(for HP-UX needing gmalloc)
+if test "`(uname -s) 2> /dev/null`" = 'HP-UX'; then
+  AC_MSG_RESULT(yes)
+  AC_LIBOBJ(gmalloc)
+  AC_DEFINE(HAVE_VALLOC)
+else
+  AC_MSG_RESULT(no)
+  AC_CHECK_FUNCS(valloc)
+fi
+
+fp_WITH_DMALLOC
+fp_WITH_REGEX
+fp_WITH_GNU_READLINE
+
+# text for endianness
+AC_C_BIGENDIAN
+
+# ---------------------------------------------------------------------------
+if test "$ac_cv_func_alloca" = 'no'; then
+dnl    support functions for alloca.c
+  AC_LIBOBJ(xmalloc)
+  AC_LIBOBJ(error)
+fi
+
+
+# ---------------------------------------------------------------------------
+# see if the user wants to override use of long long
+AC_ARG_ENABLE(override-longlong,
+[  --enable-override-longlong
+                          overide use of long long with GCC],
+  AC_DEFINE(DISABLE_LONG_LONG))
+
+
+# ---------------------------------------------------------------------------
+
+AC_OUTPUT([Makefile text/Makefile lib/Makefile 
+   jni/Makefile java/org/greenstone/mgpp/Makefile], 
+[test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h])
Index: /gsdl/trunk/gs2common/indexers/mgpp/install-sh
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/install-sh	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/install-sh	(revision 16561)
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPDocInfo.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPDocInfo.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPDocInfo.java	(revision 16561)
@@ -0,0 +1,20 @@
+package org.greenstone.mgpp;
+
+/** DocInfo 'struct' class for a search result from mgpp
+ * used by MGPPQueryResult
+ *
+ * @See MGPPQueryResult
+ */
+public class MGPPDocInfo {
+
+    public long num_=0;
+    public float rank_=0;
+
+    public MGPPDocInfo(long doc, float rank) {
+	num_=doc;
+	rank_=rank;
+    }
+    public String toString(){
+	return ""+num_+"("+rank_+")";
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPPassesWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPPassesWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPPassesWrapper.java	(revision 16561)
@@ -0,0 +1,146 @@
+/*
+ *    MGPassesWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mgpp;
+
+
+/** java wrapper class for access to gs3_mgpp_passes in C
+ *
+ * the native side implemented in MGPPPassesWrapperImpl.c
+ */
+
+public class MGPPPassesWrapper
+{
+    static {
+	System.loadLibrary("mgpppassjni");
+	initIDs();
+    }
+    
+    //static final public char INVF_LEVEL_1 = '1';
+    //static final public char INVF_LEVEL_2 = '2';
+    //static final public char INVF_LEVEL_3 = '3';
+    
+    static final public int TEXT_PASS_1 = 0;
+    static final public int TEXT_PASS_2 = 1;
+    static final public int INDEX_PASS_1 = 2;
+    static final public int INDEX_PASS_2 = 3;
+    static final public int SPECIAL_PASS = 4;
+
+    //static final public int NO_STEM_OR_CASE = 0;
+    //static final public int CASE_ONLY = 1;
+    //static final public int STEM_ONLY = 2;
+    //static final public int STEM_AND_CASE = 3;
+
+    //static final public String STEMMER_ENGLISH = "english";
+    //static final public String STEMMER_FRENCH = "french";
+    //static final public String STEMMER_LOVIN = "lovin";
+    //static final public String STEMMER_SIMPLE_FRENCH = "simple-french";
+
+    //static final private char END_OF_DOCUMENT = (char) 2;
+
+    public MGPPPassesWrapper() {
+	initCSide();
+    }
+
+    /** initialise the pass through the documents */
+    public native boolean init();
+    
+    /** add a pass declaration */
+    public void addPass(int pass) {
+	switch (pass) {
+	case TEXT_PASS_1:
+	    addPass('T','1');
+	    break;
+	case TEXT_PASS_2:
+	    addPass('T','2');	    
+	    break;
+	case INDEX_PASS_1:
+	    addPass('I','1');
+	    break;
+	case INDEX_PASS_2:
+	    addPass('I','2');
+	    break;
+	case SPECIAL_PASS:
+	    addPass('S','1');
+	    break;
+	}
+    }
+    /** set the base path */
+    public native void setBasePath(String basepath);
+    /** set the file name */
+    public native void setFileName(String filename);
+
+    /** set the Document tag */
+    public native void setDocumentTag(String tag);
+
+    /** add a level tag */
+    public native void addLevelTag(String tag);
+
+    /** set the index level (default word level) */
+    public native void setIndexLevel(String tag);
+    
+    /** Maximum amount of memory to use for  the index  pass-2  file
+	inversion  in  megabytes.
+    */
+    public native void setInversionMemLimit(int limit);
+
+    /** process one or more MGPP documents */
+    public boolean processDocument(String doc_text) {
+	try {
+	    processMGPPDocument(doc_text.getBytes("UTF-8"));
+	} catch (Exception e) {
+	    e.printStackTrace();
+	    return false;
+	}
+	return true;
+    }
+   
+    /** finalise the pass through the documents */
+    public native boolean finish();
+
+    /** get the exit value once finished */
+    public native int exitValue();
+
+    /** initialises field and method IDs for java side to enable access on C side */
+    private static native void initIDs();
+
+    /** initialises any C side stuff */
+    private native boolean initCSide();
+
+    private native void addPass(char pass_type, char pass_num);
+
+    /** process a MGPP document */
+    private native boolean processMGPPDocument(byte[] text);
+
+    public static void main(String []args) {
+
+	String doc = "<Document>hello there kath<Title>this is a title</Title></Document><Document>my name is helen</Document>";
+
+	MGPPPassesWrapper wrapper = new MGPPPassesWrapper();
+	wrapper.addPass(TEXT_PASS_1);
+	wrapper.addPass(TEXT_PASS_2);
+	wrapper.setDocumentTag("Document");
+	wrapper.setBasePath("/tmp/kjdon/index");
+	wrapper.setFileName("idx");
+
+	wrapper.init();
+	wrapper.processDocument(doc);
+	wrapper.finish();
+
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPQueryResult.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPQueryResult.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPQueryResult.java	(revision 16561)
@@ -0,0 +1,108 @@
+package org.greenstone.mgpp;
+
+import java.util.Vector;
+
+/** a java version of QueryResult for mgpp
+ *
+ * contains get methods for the java side, and simple
+ * set methods to be called from c++ side
+ *
+ */
+public class MGPPQueryResult {
+
+    /** the list of MGPPDocInfo */
+    protected Vector docs_=null;
+    /** the list of MGPPTermInfo */
+    protected Vector terms_=null;
+    /** the total number of docs found - not likely to be the size of docs_*/
+    protected long total_num_docs_=0;
+    /** whether a syntax error has occurred */
+    protected boolean syntax_error_ = false;
+    
+    MGPPQueryResult() {
+	docs_ = new Vector();
+	terms_ = new Vector();
+    }
+    /** clear the info from the last query - should be called before setting any new docs/terms */
+    public void clear() {
+	total_num_docs_=0;
+	docs_.clear();
+	terms_.clear();
+	syntax_error_ = false;
+    }
+    /** returns the result as a String - useful for printing out results */
+    public String toString() {
+	
+	String result = "";
+	result += "docs (ranks): ";
+	for (int i=0; i<docs_.size(); i++) {
+	    result += ((MGPPDocInfo)docs_.elementAt(i)).toString()+", ";
+	}
+	result += "\nterms: ";
+	for (int i=0; i<terms_.size(); i++) {
+	    result += ((MGPPTermInfo)terms_.elementAt(i)).toString()+", ";
+	}
+	result += "\nactual number of docs found = "+total_num_docs_;
+
+	return result;
+    }
+    /** a shorter representation - just terms and total docs - not the 
+	individual docnums and ranks */
+    public String toShortString() {
+	String result = "";
+	result += "\nterms: ";
+	for (int i=0; i<terms_.size(); i++) {
+	    result += ((MGPPTermInfo)terms_.elementAt(i)).toString()+", ";
+	}
+	result += "\nactual number of docs found = "+total_num_docs_;
+	return result;
+    }
+
+    // set methods used by c++ code
+    
+    public void setTotalDocs(long num) {
+	total_num_docs_=num;
+    }
+
+    public void addDoc(long doc, float rank) {
+	MGPPDocInfo doc_info = new MGPPDocInfo(doc, rank);
+	docs_.add(doc_info);
+    }
+
+    public void addTerm(String term, String tag, int stem, long match, long freq, String[] equivs) {
+	MGPPTermInfo ti = new MGPPTermInfo();
+	ti.term_=term;
+	ti.tag_=tag;
+	ti.stem_method_=stem;
+	ti.match_docs_=match;
+	ti.term_freq_=freq;
+	if (equivs!=null) {
+	  for (int i=0; i<equivs.length; i++) {
+	    ti.equiv_terms_.add(equivs[i]);
+	  }
+	}
+	terms_.add(ti);
+    }
+
+    // get methods for the java side
+    public Vector getDocs() {
+	return docs_;
+    }
+
+    public Vector getTerms() {
+	return terms_;
+    }
+
+    public long getTotalDocs() {
+	return total_num_docs_;
+    }
+     
+    public void setSyntaxError(boolean error) {
+	syntax_error_ = error;
+    }
+
+    public boolean hasSyntaxError() {
+	return syntax_error_;
+    }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPRetrieveWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPRetrieveWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPRetrieveWrapper.java	(revision 16561)
@@ -0,0 +1,48 @@
+/*
+ *    MGPPRetrieveWrapper.java
+ *    Copyright (C) 2007 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mgpp;
+
+
+
+/** java wrapper class for access to mgpp document retrieval mgpp in C++
+ *
+ * the native side is implemented in MGPPSearchWrapperImpl.cpp
+ * the result of getDocument is a String
+ * uses jni
+ *
+ */
+public class MGPPRetrieveWrapper {
+  
+    static {
+	System.loadLibrary("mgppretrievejni");
+    }
+    
+    public MGPPRetrieveWrapper() {
+    }
+
+    /** returns a document: number docnum at level level 
+     * text_path path should provide
+     * the absolute location of the mgpp text files eg ..../index/text/demo
+     * returns the doc in utf-8
+     */
+    public native String getDocument(String text_path,
+				     String level,
+				     long docnum );
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPSearchWrapper.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPSearchWrapper.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPSearchWrapper.java	(revision 16561)
@@ -0,0 +1,126 @@
+/*
+ *    MGPPSearchWrapper.java
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+package org.greenstone.mgpp;
+
+import java.util.Vector;
+
+/** java wrapper class for access to mgpp in C++
+ *
+ * the native side implemented in MGPPWrapperImpl.cpp
+ * uses MGPPQueryResult to hold the result of a query. the 
+ * result of a getDocument is a String
+ * uses the jni
+ *
+ *@see MGPPQueryResult
+ */
+public class MGPPSearchWrapper {
+  
+    /** the query result
+     * will be filled in by runQuery
+     */
+    protected MGPPQueryResult mgpp_query_result_=null; 
+    /** pointer to c++ MGPPWrapperData class - cached indexData and queryInfo
+     */
+    protected long mgpp_data_ptr_=0;
+
+    static {
+	System.loadLibrary("mgppsearchjni");
+	initIDs();
+    }
+    
+    public MGPPSearchWrapper() {
+	mgpp_query_result_ = new MGPPQueryResult();
+	initCppSide();
+    }
+
+    /** initialises field and method IDs for java side to enable access on
+	C++ side */
+    private static native void initIDs();
+
+    /** initialises the mgpp_data_ptr_ */
+    private native boolean initCppSide();
+
+    /** returns a document: number docnum at level level 
+     * text_path path should provide
+     * the absolute location of the mgpp text files eg ..../index/text/demo
+     * returns the doc in utf-8
+     */
+    //only this method is for search purpose
+//    public native String getDocument(String text_path,
+//				     String level,
+//				     long docnum );
+
+    /** the public method - converts between utf-8 and unicode
+     */
+    // public String getDocument(String text_path,
+    //		      String level,
+    //		      long docnum ) {
+
+    //String utf8_doc = getUtF8Document(base_dir, text_path, level, docnum);
+	
+    /** load up the data structure for index 
+     * - maintains state between requests as can be slow  
+     * index_path should provide
+     * the absolute location of the mgpp index files eg ..../index/tt/demo 
+     */
+    public native boolean loadIndexData(String index_path);
+    /** unloads the data */
+    public native boolean unloadIndexData();
+
+    // query param methods
+
+    /** if on=true, sets default stemming on  - its off by default*/
+    public native void setStem(boolean on);
+    
+    /** if on=true, sets default accentfolding on  - its off by default*/
+    public native void setAccentFold(boolean on);
+
+   /** if on=true, sets default casefolding on - its off by default */
+   public native void setCase(boolean on);
+    /** default is 50 */
+    public native void setMaxDocs(int num);
+    /** if on=true, sorts by rank, otherwise returns in build order - 
+	default is on */
+    public native void setSortByRank(boolean on);
+    /** if on=true, a query returns term freq info - default is on */
+    public native void setReturnTerms(boolean on);
+    /** sets the granularity of the  query  - default 'Document' */
+    public native void setQueryLevel(String level);
+    /** sets the granularity of the result - default 'Document' */
+    public native void setReturnLevel(String level);
+    /** sets the default boolean operator - AND(=1)/OR(=0) */
+    public native void setMatchMode(int mode);
+
+    /** sets maxnumeric */
+    public native void setMaxNumeric(int maxnumeric);
+    /** returns a string with all the current query param settings */
+    public native String getQueryParams();
+
+    /** actually carry out the query. Use the set methods to set any query
+	params 
+    * writes the result to query_result
+    */
+    public native void runQuery(String query_string); 
+
+    /** get the result out of the wrapper */
+    public MGPPQueryResult getQueryResult() {
+	return mgpp_query_result_;
+    }
+    
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPTermInfo.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPTermInfo.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/MGPPTermInfo.java	(revision 16561)
@@ -0,0 +1,46 @@
+package org.greenstone.mgpp;
+
+import java.util.Vector;
+
+/** TermInfo 'struct' class for a search result from mgpp
+ * used by MGPPQueryResult
+ * 
+ *
+ *@see MGPPQueryResult
+ */
+public class MGPPTermInfo {
+
+    /** the term itself */
+    public String term_=null;
+    /** the tag - level or metadata - for which the query was done */
+    public String tag_=null;
+    /** what stem and casefold method was used
+	0 = none
+	1 = casefold only
+	2 = stem only
+	3 = casefold and stem */
+    public int stem_method_=0;
+    /** the number of documents containing this term (where document is defined by the tag data */
+    public long match_docs_=0;
+    /** overall term freq  - word level*/
+    public long term_freq_=0;
+    /** list of stemmed and casefolded equivalent terms - if stem_method_ is non-zero 
+      - Vector of Strings */
+    public Vector equiv_terms_=null;
+
+    public MGPPTermInfo() {
+	equiv_terms_ = new Vector();
+    }
+	
+    /** output the class as a string */
+    public String toString() {
+	String result="";
+	result +="<"+tag_+">\""+term_+"\"stem("+stem_method_+")docs("+match_docs_;
+	result +=")freq("+term_freq_+")equiv terms(";
+	for (int i=0; i<equiv_terms_.size(); i++) {
+	    result += (String)equiv_terms_.elementAt(i)+", ";
+	}
+	result += ")";
+	return result;
+    }
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Makefile.in	(revision 16561)
@@ -0,0 +1,64 @@
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+libdir = @libdir@
+subdir = java/org/greenstone/mgpp
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+AR = ar
+CC = @CC@
+CXX = @CXX@
+JAVAC = $(JAVA_HOME)/bin/javac
+JAVAH = $(JAVA_HOME)/bin/javah
+JAR = $(JAVA_HOME)/bin/jar
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -DSILENT -DSHORT_SUFFIX
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@ -DSILENT -DSHORT_SUFFIX
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS =  -lstdc++ @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+
+MGPPHOME=../../../..
+JAVACLASSDIR=./classes
+JAVASRCDIR=../../..
+JAVACOPTIONS= -deprecation -g -O
+JAVACLASSPATH=$(JAVACLASSDIR):
+
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+
+
+all: setup compile
+
+setup:
+	mkdir -p $(JAVACLASSDIR)
+
+compile: setup
+	$(JAVAC) -d $(JAVACLASSDIR) -sourcepath $(JAVASRCDIR) $(JAVACOPTIONS) *.java
+#	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGPPHOME)/jni org.greenstone.mgpp.MGPPWrapper
+	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGPPHOME)/jni org.greenstone.mgpp.MGPPPassesWrapper
+	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGPPHOME)/jni org.greenstone.mgpp.MGPPSearchWrapper
+	$(JAVAH) -classpath $(JAVACLASSPATH) -d $(MGPPHOME)/jni org.greenstone.mgpp.MGPPRetrieveWrapper	
+	$(JAR) cf $(MGPPHOME)/mgpp.jar -C $(JAVACLASSDIR) org
+
+clean:
+	rm -rf $(JAVACLASSDIR) $(MGPPHOME)/mgpp.jar $(MGPPHOME)/jni/org_greenstone_mgpp_MGPPSearchWrapper.h $(MGPPHOME)/jni/org_greenstone_mgpp_MGPPRetrieveWrapper.h $(MGPPHOME)/jni/org_greenstone_mgpp_MGPPPassesWrapper.h
+
+install:
+
+distclean: clean
+	rm -f Makefile
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Queryer.java
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Queryer.java	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/Queryer.java	(revision 16561)
@@ -0,0 +1,186 @@
+package org.greenstone.mgpp;
+
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.File;
+/** Queryer - java port of c++ Queryer 
+ * - uses MGPPSearchWrapper and MGPPRetrieveWrapper to access mgpp
+ * only has get document and search - full text browse not implemented
+ *
+ * run as:
+ * java org.greenstone.mgpp.Queryer <basedir> <indexdir> <textdir>
+ *@see MGPPSearchWrapper
+ *@see MGPPRetrieveWrapper
+ */
+public class Queryer {
+  
+  /** outputs to std out a help message describing the commands used by 
+   * Queryer 
+   */
+  public void printHelp() {
+	
+    System.out.println( "commands available are:\n"+
+			"\t.q\t\tquit\n"+
+			"\t.h\t\tprint this help message\n"+
+			"\t.d\t\tprint the current query parameter settings\n" +
+			"\t.i<level>\tchange the search level to level\n"+
+			"\t.l<level>\tchange the result level to level\n"+
+			"\t.r0/.r1\t\tranking off/on\n"+
+			"\t.t0/.t1\t\tquery type some/all\n"+
+			"\t.c0/.c1\t\tcasefolding off/on\n"+
+			"\t.s0/.s1\t\tstemming off/on\n"+
+			"\t.o0/.o1\t\tshort output off/on\n"+
+			"\t.m<num>\t\tset max docs to return to num\n\n"+
+			"\t.p<docnum>\tprint document docnum\n"+
+			"\t<query string>\t do a query\n");
+  }
+
+   
+  public static void main(String[] args) {
+    if (args.length != 3) {
+      System.out.println("Usage: java org.greenstone.mgpp.Queryer <basedir> <indexdir> <textdir>");
+      return;
+    }
+	
+    Queryer self = new Queryer();
+	
+    String base_dir = args[0];
+    String text_path = args[2];
+    String index_path = args[1];
+
+    text_path = base_dir + File.separatorChar+text_path;
+    index_path = base_dir  + File.separatorChar+ index_path;
+	
+    // the jni classes to access mgpp stuff
+    MGPPSearchWrapper searcher = new MGPPSearchWrapper();
+    MGPPRetrieveWrapper retriever = new MGPPRetrieveWrapper();
+    searcher.loadIndexData(index_path);
+    
+    // the return level
+    String level = "Section";
+
+    System.out.println("Welcome to Java Queryer :-)");
+    self.printHelp();
+
+    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+    boolean shortOutput=false;
+
+    String command; // user input
+    char x; // the command letter
+    String data=null; // any auxiliary input to a command
+    while (true) {
+      System.out.print(">"); // the prompt
+      try {
+	command = br.readLine();
+	command=command.trim();
+	if (command.startsWith(".")) {
+	  // a system command
+	  x = command.charAt(1);
+	  if (command.length() > 2) {
+	    data = command.substring(2);
+	    data = data.trim();
+			
+	  }
+	  switch (x) {
+	  case 'q': // clean up and exit
+	    searcher.unloadIndexData();
+	    return;  
+	  case 'h': // print help message
+	    self.printHelp();
+	    break;
+	  case 'd': // print query param settings
+	    String info = searcher.getQueryParams();
+	    System.out.println(info);
+	    break;
+	  case 'p': // print doc
+	    int docnum = Integer.parseInt(data);
+	    String doc = retriever.getDocument(text_path, 
+					     level, docnum);
+	    System.out.println(doc);
+	    break;
+	  case 'm': //match docs
+	    int match = Integer.parseInt(data);
+	    searcher.setMaxDocs(match);
+	    break;
+	  case 's': // set stem on/off
+	    int stem = Integer.parseInt(data);
+	    if (stem==0 ){
+	      searcher.setStem(false);
+	    } else if(stem==1) {
+	      searcher.setStem(true);
+	    } else {
+	      System.err.println("Error: stem should be 0 or 1");
+	    }
+	    break;
+	  case 'c': // set case on/off
+	    int casef = Integer.parseInt(data);
+	    if (casef==0) {
+	      searcher.setCase(false);
+	    } else if (casef==1) {
+	      searcher.setCase(true);
+	    } else {
+	      System.err.println("Error: case should be 0 or 1");
+	    }
+	    break;
+	  case 'i': // set search level
+	    searcher.setQueryLevel(data);
+	    break;
+	  case 'l': // set result level
+	    searcher.setReturnLevel(data);
+	    level = data;
+	    break; 
+	  case 'r': // set rank on/off
+	    int rank = Integer.parseInt(data);
+	    if (rank==0) {
+	      searcher.setSortByRank(false);
+	    } else if (rank==1) {
+	      searcher.setSortByRank(true);
+	    } else {
+	      System.err.println("Error: rank should be 0 or 1");
+	    }
+	    break;
+	  case 't': // set query type some/all
+	    int type = Integer.parseInt(data);
+	    if (type==0 || type==1) {
+	      searcher.setMatchMode(type);
+	    } else {
+	      System.err.println("Error: type should be 0 (some) or 1 (all)");
+	    }
+	    break;
+	  case 'o': // set output short/long
+	    int output = Integer.parseInt(data);
+	    if (output==0) {
+	      shortOutput = false;
+	    } else if (output==1) {
+	      shortOutput = true;
+	    }else {
+	      System.err.println("Error: output should be 0 or 1");
+	    }
+	    break;
+	  }
+	}
+	else {
+	  // a query
+	  searcher.runQuery(command);
+	  MGPPQueryResult res = searcher.getQueryResult();
+	  if (res.hasSyntaxError()) {
+	    System.out.println("invalid syntax error\n");
+	  } else {
+	    if (shortOutput) {
+	      System.out.println(res.toShortString());
+	    } else {
+	      System.out.println(res.toString());
+	    }
+	  }
+	}
+		    
+      } catch (Exception e) {
+	System.out.println("Queryer error: "+e.getClass() + " "+e.getMessage());
+      }
+
+    }
+  }
+
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/java/org/greenstone/mgpp/winMake.bat	(revision 16561)
@@ -0,0 +1,45 @@
+@echo off
+
+set MGPPHOME=..\..\..\..
+
+rem ---- Where to put class files ----
+set JAVACLASSDIR=classes
+
+rem ---- Name and location of java programs ----
+set JAVAC="%JAVA_HOME%\bin\javac"
+set JAVAH="%JAVA_HOME%\bin\javah"
+set JAVA="%JAVA_HOME%\bin\java"
+set JAVADOC="%JAVA_HOME%\bin\javadoc"
+set JAR="%JAVA_HOME%\bin\jar"
+
+set JAVACOPTIONS= -deprecation -g -O
+
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto all
+if ""%1"" == ""install"" goto install
+if ""%1"" == ""clean"" goto clean
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:all
+	if not exist %JAVACLASSDIR% mkdir %JAVACLASSDIR%
+	echo Compiling MGPP Java classes ...
+        %JAVAC% -d %JAVACLASSDIR% %JAVACOPTIONS% *.java
+        %JAVAH% -classpath %JAVACLASSDIR% -d %MGPPHOME%\jni org.greenstone.mgpp.MGPPWrapper
+        %JAR% cf %MGPPHOME%\mgpp.jar -C %JAVACLASSDIR% org
+	goto done
+
+:install
+	goto done
+
+:clean
+	echo Cleaning up...
+	if exist %JAVACLASSDIR% rmdir /S /Q %JAVACLASSDIR%
+        if exist %MGPPHOME%\mgpp.jar del %MGPPHOME%\mgpp.jar
+        if exist %MGPPHOME%\jni\org_greenstone_mgpp_MGPPWrapper.h del %MGPPHOME%\jni\org_greenstone_mgpp_MGPPWrapper.h
+	goto done
+
+:done
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPPassesWrapperImpl.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPPassesWrapperImpl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPPassesWrapperImpl.cpp	(revision 16561)
@@ -0,0 +1,190 @@
+/*
+ *    MGPassesWrapperImpl.c
+ *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */
+
+
+#include <jni.h>
+#include <assert.h>
+#include "sysfuncs.h"
+#include "org_greenstone_mgpp_MGPPPassesWrapper.h"
+
+#include "mgpp_passes_4jni.h"
+#include "mg_files.h"
+
+/* if we need to use java objects, we should initialise their field ids here*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_initIDs(JNIEnv *j_env, jclass j_cls)
+{
+  return;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_initCSide(JNIEnv *j_env, jobject j_obj)
+{
+  clear_variables();
+  return true;  
+}  
+
+/* add a pass type T1, T2, I1, I2, S */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_addPass(JNIEnv *j_env, 
+					       jobject j_obj, 
+					       jchar j_pass_type,
+					       jchar j_pass_num)
+{
+  /* get the level as a c char */
+  const char pass_type = j_pass_type;
+  const char pass_num = j_pass_num;
+  add_pass(pass_type, pass_num);
+  
+}
+
+/* Set the filename */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_setFileName(JNIEnv *j_env, 
+						   jobject j_obj,
+						   jstring j_filename)
+{
+  /* Get the filename as a C string */
+  const char* filename = j_env->GetStringUTFChars(j_filename, NULL);
+ 
+  assert(filename != NULL);
+  set_filename(filename);
+  
+  /* Release the string */
+ j_env->ReleaseStringUTFChars(j_filename, filename);
+
+}
+
+/* Set the base path */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_setBasePath(JNIEnv *j_env, 
+						   jobject j_obj,
+						   jstring j_basepath)
+{
+  /* Get the base_path as a C string */
+  const char* basepath = j_env->GetStringUTFChars(j_basepath, NULL);
+  assert(basepath != NULL);
+ 
+  set_basepath(basepath);
+  
+  /* Release the string */
+  j_env->ReleaseStringUTFChars(j_basepath, basepath);
+
+}
+
+/* set the Document tag */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_setDocumentTag(JNIEnv *j_env, 
+						      jobject j_obj,
+						      jstring j_tag)
+{
+  const char* tag = j_env->GetStringUTFChars(j_tag, NULL);
+
+  set_document_tag(tag);
+  /* Release the string */
+  j_env->ReleaseStringUTFChars(j_tag, tag);
+
+
+}
+
+/* add a level tag */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_addLevelTag(JNIEnv *j_env, 
+						   jobject j_obj,
+						   jstring j_tag)
+{
+  const char* tag = j_env->GetStringUTFChars(j_tag, NULL);
+  add_level_tag(tag);
+
+  /* Release the string */
+  j_env->ReleaseStringUTFChars(j_tag, tag);
+
+}
+
+/* set the index level (default word level) */
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_setIndexLevel(JNIEnv *j_env, 
+						     jobject j_obj,
+						     jstring j_tag)
+{
+  const char* tag = j_env->GetStringUTFChars(j_tag, NULL);
+  set_index_level(tag);
+
+  /* Release the string */
+  j_env->ReleaseStringUTFChars(j_tag, tag);
+
+  
+}
+
+
+/** Maximum amount of memory to use for  the index  pass-2  file
+    inversion  in  megabytes.
+*/
+JNIEXPORT void JNICALL
+Java_org_greenstone_mgpp_MGPPPassesWrapper_setInversionMemLimit(JNIEnv *j_env, 
+							    jobject j_obj, 
+							    jint j_limit) {
+  int limit = j_limit;
+  set_inversion_limit(limit);
+}
+
+
+/* initialise the pass through the documents. must be called after all
+   the set methods 
+*/
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPPassesWrapper_init(JNIEnv *j_env, 
+					    jobject j_obj) {
+
+  init_driver();
+  return true;
+}
+
+
+/* process one or more documents */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPPassesWrapper_processMGPPDocument(JNIEnv *j_env, 
+						       jobject j_obj,
+						       jbyteArray j_doc_text) {
+  /* Get the text as a C string */
+  int length = j_env->GetArrayLength(j_doc_text);
+  u_char * text_buffer = (u_char *)j_env->GetByteArrayElements(j_doc_text, NULL);
+  process_document(text_buffer, length);
+  /* Release the string */
+  // why doesn't this work in c++ when it works in C?????
+  //j_env->ReleaseByteArrayElements(j_doc_text, text_buffer, 0);
+  return true;
+}
+
+/* finalise the pass through the documents */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPPassesWrapper_finish(JNIEnv *j_env, 
+					    jobject j_obj) {
+  
+  finalise_driver();
+  return true;
+}
+
+/** get the exit value once finished */
+JNIEXPORT jint JNICALL 
+Java_org_greenstone_mgpp_MGPPPassesWrapper_exitValue(JNIEnv *j_env, 
+					    jobject j_obj) {
+
+  return get_exit_value();
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPRetrieveWrapperImpl.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPRetrieveWrapperImpl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPRetrieveWrapperImpl.cpp	(revision 16561)
@@ -0,0 +1,86 @@
+/*
+ *    MGPPRetrieveWrapperImpl.c
+ *    Copyright (C) 2007 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */ 
+
+#ifdef __WIN32__
+#include <win32cfg.h>
+#endif
+
+#include <jni.h>
+#include "org_greenstone_mgpp_MGPPRetrieveWrapper.h"
+#include "TextGet.h"
+
+
+//****************************************************
+// retrieve a document
+//****************************************************
+
+/* returns a document from mgpp as a string
+Note: TextData isn't cached - just reloaded each time
+*/
+JNIEXPORT jstring JNICALL 
+Java_org_greenstone_mgpp_MGPPRetrieveWrapper_getDocument (JNIEnv *j_env, 
+ jobject j_obj, jstring j_text_name, jstring j_level, jlong j_docnum) {
+  
+#ifdef __WIN32__
+  const char* base_dir = "";
+#else
+  const char* base_dir = "/";
+#endif
+
+  const char * text_name = j_env->GetStringUTFChars(j_text_name, NULL);
+  if (text_name==NULL) {
+   return NULL;
+  }
+
+  const char * level = j_env->GetStringUTFChars( j_level, NULL);
+  if (level==NULL) {
+   j_env->ReleaseStringUTFChars(j_text_name, text_name);
+   return NULL;
+  }
+    
+  // does this work alright? j_docnum is a long (64 bit)
+  unsigned long docnum = j_docnum;
+  TextData td;
+
+  // cast to char* otherwise complains about const
+  td.LoadData((char *)base_dir, (char *)text_name);
+
+  UCArray mg_level;
+  SetCStr(mg_level, level);
+  UCArray docText;
+  docText.clear();
+  // get the actual text
+  if (!GetDocText(td, mg_level, docnum, docText)) {
+    cerr <<"MGPP JNI: couldn't retrieve doc text"<<endl;
+  }
+
+  td.UnloadData();
+  
+  char * doc = GetCStr(docText); 
+  jstring result = j_env->NewStringUTF(doc);
+  // release any gets
+  j_env->ReleaseStringUTFChars(j_text_name, text_name);
+  j_env->ReleaseStringUTFChars(j_level, level);
+
+  // free any C++ stuff
+  delete doc;
+
+  return result;
+ 
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.cpp	(revision 16561)
@@ -0,0 +1,482 @@
+/*
+ *    MGPPSearchWrapperImpl.cpp
+ *    Copyright (C) 2007 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */ 
+#ifdef __WIN32__
+#include <win32cfg.h>
+#include <strstream>
+#include <sstream>
+#else
+#ifdef __APPLE__
+#include <strstream>
+#include <sstream>
+#else
+#include <sstream>
+#endif
+#endif
+
+#include <jni.h>
+#include "org_greenstone_mgpp_MGPPSearchWrapper.h"
+#include "MGPPSearchWrapperImpl.h"
+#include "GSDLQueryParser.h"
+#include "MGQuery.h"
+  
+MGPPSearchData::MGPPSearchData() {
+  indexData = new IndexData();
+  queryInfo = new QueryInfo();
+
+  if (queryInfo==NULL) {
+    cerr<<"couldn't allocate new query info\n";
+    if (indexData!=NULL) {
+      delete indexData;
+    }
+  }
+
+  // set all the default params
+  SetCStr(queryInfo->docLevel, "Document"); // the level to search at
+  queryInfo->maxDocs = 50;
+  queryInfo->sortByRank = true;
+  queryInfo->exactWeights = false;
+  queryInfo->needRankInfo = true;
+  queryInfo->needTermFreqs = true;
+
+  UCArrayClear(level);
+  SetCStr(level, "Document"); // the level to return docs at
+  defaultStemMethod=0;
+  defaultBoolCombine=0;
+  maxNumeric = 4;
+}
+
+MGPPSearchData::~MGPPSearchData() { 
+  if (indexData !=NULL) {
+    delete indexData;
+  }
+  if (queryInfo !=NULL) {
+    delete queryInfo;
+  }
+}
+
+// ********************************************
+// initialisation stuff
+// ********************************************
+
+// cached ids for java stuff 
+jfieldID FID_mgpp_data = NULL; // MGPPSearchData
+jfieldID FID_query_result = NULL; // MGPPQueryResult
+jmethodID MID_addDoc=NULL; // MGPPQueryResult.addDoc()
+jmethodID MID_addTerm=NULL; // MGPPQueryResult.addTerm()
+jmethodID MID_setTotalDocs=NULL; // MGPPQueryResult.setTotalDocs()
+jmethodID MID_clearResult=NULL; //MGPPQueryResult.clear()
+jmethodID MID_setSyntaxError=NULL; // MGPPQueryResult.setSyntaxError()
+jclass CID_String=NULL; // class ID of String
+
+/* to access objects and methods on java side, need their field/method ids - 
+ this initialises them at the start to avoid recalculating them each time they
+ are needed 
+Note: the descriptors need to be exactly right, otherwise you get an error 
+saying "no such field" but no reference to the fact that it has the right 
+name but the wrong type.
+Note: apparently the jclass is a local ref and should only work 
+in the method that created it. It seems to work ok, but I'll make it
+ global cos the book said I should, and it may avoid future hassles.
+*/
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_initIDs (JNIEnv *j_env, jclass j_cls) {
+  
+  FID_mgpp_data = j_env->GetFieldID(j_cls, "mgpp_data_ptr_", "J"); //a long-"J"
+  if (FID_mgpp_data==NULL) {
+      cerr <<"MGPP JNI: field mgpp_data_ptr_ not found"<<endl;
+  } 
+
+  FID_query_result = j_env->GetFieldID(j_cls, "mgpp_query_result_", "Lorg/greenstone/mgpp/MGPPQueryResult;"); // an object -"L<class name>;"
+  if (FID_query_result==NULL) {
+      cerr <<"MGPP JNI: field mgpp_query_result_ not found"<<endl;
+  }
+  // the methods we want to use
+
+  // addDoc(long doc, float rank)
+  jclass JC_MGPPQueryResult = j_env->FindClass("org/greenstone/mgpp/MGPPQueryResult");
+  MID_addDoc = j_env->GetMethodID(JC_MGPPQueryResult, "addDoc", "(JF)V");
+  if (MID_addDoc==NULL) {
+      cerr <<"MGPP JNI: addDoc method not found"<<endl;
+  }
+  // addTerm(String term, String tag, int stem_method, long match_docs, 
+  // long term_freq, String[] equiv_terms)
+  MID_addTerm = j_env->GetMethodID(JC_MGPPQueryResult, "addTerm", "(Ljava/lang/String;Ljava/lang/String;IJJ[Ljava/lang/String;)V");
+  if (MID_addTerm==NULL) {
+      cerr <<"MGPP JNI: method addTerm not found"<<endl;
+  }
+  
+  // setTotalDocs(long)
+  MID_setTotalDocs = j_env->GetMethodID(JC_MGPPQueryResult, "setTotalDocs", "(J)V");
+  if (MID_setTotalDocs==NULL) {
+      cerr <<"MGPP JNI: method setTotalDocs not found"<<endl;
+  }
+  
+  MID_clearResult = j_env->GetMethodID(JC_MGPPQueryResult, "clear", "()V");
+  if (MID_clearResult==NULL) {
+      cerr <<"MGPP JNI: method clear not found"<<endl;
+  }
+  MID_setSyntaxError = j_env->GetMethodID(JC_MGPPQueryResult, "setSyntaxError", "(Z)V");
+  if (MID_clearResult==NULL) {
+      cerr <<"MGPP JNI: method setSyntaxError not found"<<endl;
+  }
+
+  // get the class for String to use in NewObjectArray in runQuery()
+  // FindClass returns a local reference - have to convert it to a global one
+  jclass local_CID_String = j_env->FindClass("java/lang/String");
+  if (local_CID_String==NULL) {
+    cerr <<"MGPP JNI: java String class not found"<<endl;
+  } else { 
+    /* create a global ref */
+    CID_String = (jclass)j_env->NewGlobalRef(local_CID_String);
+    /* The local reference is no longer useful */
+    j_env->DeleteLocalRef(local_CID_String);
+ 
+    /* Is the global reference created successfully? */
+    if (CID_String == NULL) {
+      return; /* out of memory exception thrown */
+    }
+  }
+  
+}
+
+/* the java side MGPPSearchWrapper has a pointer to a C++ object - MGPPSearchData
+   initialise this and set the pointer 
+*/
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_initCppSide (JNIEnv *j_env, jobject j_obj){
+
+  MGPPSearchData * data = new MGPPSearchData();
+  j_env->SetIntField(j_obj, FID_mgpp_data, (long)data);
+
+  return true;
+
+}
+
+//******************************************
+// do a query
+// ****************************************
+
+/* load the IndexData - cached for querying
+ */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_loadIndexData (JNIEnv *j_env, jobject j_obj,  jstring j_index_name) {
+
+  jint data_ptr = j_env->GetIntField(j_obj, FID_mgpp_data);
+  MGPPSearchData * data = (MGPPSearchData *)data_ptr;
+
+#ifdef __WIN32__
+  const char* base_dir = "";
+#else
+  const char* base_dir = "/";
+#endif
+
+  const char * index_name = j_env->GetStringUTFChars( j_index_name, NULL);
+  if (index_name==NULL) {
+    return false;
+  }
+  
+  jboolean j_result=false;
+  
+  // why doesn't this complain about const??
+  if (data->indexData->LoadData(base_dir, index_name)) {
+    j_result=true;
+  }
+  
+  // release any gets
+  j_env->ReleaseStringUTFChars(j_index_name, index_name);
+  
+  return j_result;
+}
+
+/* unload the data 
+ */
+JNIEXPORT jboolean JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_unloadIndexData (JNIEnv *j_env, jobject j_obj) {
+
+  jint data_ptr = j_env->GetIntField(j_obj, FID_mgpp_data);
+  MGPPSearchData * data = (MGPPSearchData *)data_ptr;
+
+  data->indexData->UnloadData();
+  return true;
+
+}
+
+/* do the actual query - the results are written to query_result held on the 
+   java side */
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_runQuery (JNIEnv *j_env, jobject j_obj, jstring j_query){
+  
+  jthrowable exc; // an exception - check if something funny has happened
+  const char *query = j_env->GetStringUTFChars(j_query, NULL);
+  if (query==NULL) {
+    return; // exception already thrown
+  }
+  // turn to UCArray for mgpp and then release the string
+  UCArray queryArray;
+  SetCStr(queryArray, query);
+  j_env->ReleaseStringUTFChars(j_query, query);
+  
+  // the query data
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  
+  // the result to write to
+  jobject result_ptr = j_env->GetObjectField(j_obj, FID_query_result);
+  if (result_ptr==NULL) {
+    cerr <<"couldn't access the result to write to"<<endl;
+    return;
+  }
+  
+  // clear the result
+  j_env->CallVoidMethod(result_ptr, MID_clearResult);
+  exc = j_env->ExceptionOccurred(); // this catches the exception I think - it
+  //wont be thrown any further
+  if (exc) {
+    j_env->ExceptionDescribe();
+    return;
+  }
+  // the mgpp QueryResult that we will use
+  ExtQueryResult queryResult;
+
+  QueryNode * queryTree = NULL;
+  // parse the query string into a tree structure
+  queryTree = ParseQuery(queryArray, data->defaultBoolCombine, 
+			 data->defaultStemMethod, data->maxNumeric);
+  if (queryTree == NULL) {
+    // invalid syntax
+    j_env->CallVoidMethod(result_ptr, MID_setSyntaxError, true);
+    cerr << "MGPPSearchWrapperImpl: invalid query syntax!!\n";
+    return; 
+  }
+  // print the query
+  PrintNode (cout, queryTree);
+  // finally, do the query
+  MGQuery(*(data->indexData), *(data->queryInfo), queryTree, queryResult, data->level);
+
+  delete queryTree;
+
+  // convert queryResult to the java side version
+  // use levels rather than docs of ExtQueryResult
+  // CallVoidMethod(obj, method id, args to method)
+  for (int i=0; i<queryResult.levels.size(); i++) {
+    jlong doc = queryResult.levels[i];
+    jfloat rank = queryResult.ranks[i];
+    j_env->CallVoidMethod(result_ptr, MID_addDoc, doc, rank);
+    exc = j_env->ExceptionOccurred();
+    if (exc) {
+      j_env->ExceptionDescribe();
+      return;
+    }
+    
+  }
+
+  // actual num of docs
+  jlong total = queryResult.actualNumDocs;
+  j_env->CallVoidMethod(result_ptr, MID_setTotalDocs, total);
+  exc = j_env->ExceptionOccurred();
+  if (exc) {
+    j_env->ExceptionDescribe();
+    return;
+  }
+
+  // the terms
+  for (int j=0; j<queryResult.termFreqs.size(); j++) {
+    
+    TermFreqData tf = queryResult.termFreqs[j];
+    jstring term = j_env->NewStringUTF(GetCStr(tf.term));
+    jstring tag = j_env->NewStringUTF(GetCStr(tf.tag));
+    jint stem = tf.stemMethod;
+    jlong match = tf.matchDocs;
+    jlong freq = tf.termFreq;
+    
+    jobjectArray equivs=NULL;
+    jstring empty = j_env->NewStringUTF(""); // the initial object to fill the array
+    jint num_equivs = tf.equivTerms.size();
+    equivs = j_env->NewObjectArray(num_equivs, CID_String, empty);
+    if (equivs==NULL) {
+      cerr<<"couldn't create object array"<<endl;
+      
+    } else {
+      for (int k=0; k<num_equivs;k++) {
+	jstring equiv = j_env->NewStringUTF(GetCStr(tf.equivTerms[k]));
+	j_env->SetObjectArrayElement(equivs, k, equiv);
+      }
+      
+      
+      j_env->CallVoidMethod(result_ptr, MID_addTerm, term, tag, stem, match, freq, equivs);
+      exc = j_env->ExceptionOccurred();
+      if (exc) {
+	j_env->ExceptionDescribe();
+	return;
+      }
+    }
+  }
+
+
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setStem (JNIEnv *j_env, 
+					      jobject j_obj, 
+					      jboolean j_on) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  if (j_on) {
+    data->defaultStemMethod |= 2;
+  } else {
+    data->defaultStemMethod &= 0xd;
+  }
+
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setAccentFold (JNIEnv *j_env, 
+					      jobject j_obj, 
+					      jboolean j_on) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  if (j_on) {
+    data->defaultStemMethod |= 4;
+  } else {
+    data->defaultStemMethod &= 0xb;
+  }
+}
+
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setCase (JNIEnv *j_env, 
+					      jobject j_obj, 
+					      jboolean j_on) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+
+  if (j_on) {
+    data->defaultStemMethod |= 1;
+  } else {
+    data->defaultStemMethod &= 0xe;
+  }
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxDocs (JNIEnv *j_env, 
+						 jobject j_obj, 
+						 jint j_max) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  data->queryInfo->maxDocs=j_max;
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxNumeric (JNIEnv *j_env, 
+						 jobject j_obj, 
+						 jint j_max) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  data->maxNumeric=j_max;
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setSortByRank (JNIEnv *j_env, 
+						    jobject j_obj, 
+						    jboolean j_on) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  
+  data->queryInfo->sortByRank=j_on;
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnTerms(JNIEnv *j_env, 
+						    jobject j_obj, 
+						    jboolean j_on) {
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  data->queryInfo->needTermFreqs = j_on;
+ 
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setQueryLevel(JNIEnv *j_env, 
+						   jobject j_obj, 
+						   jstring j_level){
+  
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+
+  const char * level = j_env->GetStringUTFChars(j_level, NULL);
+  if (level==NULL) {
+    return; // exception already thrown
+  }
+  
+  data->queryInfo->docLevel.clear();
+  SetCStr(data->queryInfo->docLevel, level);
+  
+  // release the java stuff
+  j_env->ReleaseStringUTFChars(j_level, level);
+ 
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnLevel(JNIEnv *j_env, 
+						    jobject j_obj, 
+						    jstring j_level){
+
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+
+  const char * level = j_env->GetStringUTFChars(j_level, NULL);
+  if (level==NULL) {
+    return; // exception already thrown
+  }
+  
+  data->level.clear();
+  SetCStr(data->level, level);
+  
+  // release the java stuff
+  j_env->ReleaseStringUTFChars(j_level, level);
+    
+
+}
+
+JNIEXPORT void JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_setMatchMode (JNIEnv *j_env,
+						   jobject j_obj,
+						   jint j_mode){
+
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  data->defaultBoolCombine=j_mode;
+
+}
+
+JNIEXPORT jstring JNICALL 
+Java_org_greenstone_mgpp_MGPPSearchWrapper_getQueryParams (JNIEnv *j_env,
+						     jobject j_obj){
+
+  MGPPSearchData * data = (MGPPSearchData *)j_env->GetIntField(j_obj, FID_mgpp_data);
+  
+  // print the data to a stringstream, then convert to char*, then to 
+  //jstring
+
+  stringstream output;
+  output << "Query params:"<<endl
+    // need to change this to use platform specific separator for niceness
+	 << "index\t\t"<<data->indexData->basePath<<"/"<<data->indexData->filename<<endl
+	 <<"search level\t"<<GetCStr(data->queryInfo->docLevel)<<endl
+	 <<"result level\t"<<GetCStr(data->level)<<endl
+	 <<"casefold\t"<<(data->defaultStemMethod&1)<<endl
+	 <<"stem\t\t"<<(data->defaultStemMethod&2)<<endl
+	 <<"order by rank\t"<<data->queryInfo->sortByRank<<endl
+	 <<"query type\t"<<(data->defaultBoolCombine==1?"all":"some")<<endl
+         <<"max docs\t"<<data->queryInfo->maxDocs<<endl<<ends;
+
+  const char *result = output.str().c_str();
+  jstring j_result = j_env->NewStringUTF(result);
+  delete (char *)result;
+  return j_result;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/MGPPSearchWrapperImpl.h	(revision 16561)
@@ -0,0 +1,35 @@
+/*
+ *    MGPPSearchWrapperImpl.h
+ *    Copyright (C) 2007 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ */ 
+
+#include "MGQuery.h"
+
+// the global stuff we need to do a query
+class MGPPSearchData {
+ public:
+  IndexData * indexData;
+  QueryInfo * queryInfo;
+  int defaultStemMethod;
+  int defaultBoolCombine;
+  int maxNumeric;
+  UCArray level; // the return level
+
+  MGPPSearchData();
+  ~MGPPSearchData();
+
+};
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/Makefile.in	(revision 16561)
@@ -0,0 +1,83 @@
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+libdir = @libdir@
+subdir = jni
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+UNAC_DIR=@UNAC_DIR@
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+AFLIBS = $(UNAC_DIR)/libunac.a 
+AFINCLUDES = -I$(UNAC_DIR)
+else
+AFLIBS = 
+AFINCLUDES =
+endif
+
+
+
+AR = ar
+CC = @CC@
+CXX = @CXX@
+CFLAGS = @CFLAGS@ -fPIC -ansi -DSILENT -DSHORT_SUFFIX
+CXXFLAGS = @CXXFLAGS@ -fPIC -DSILENT -DSHORT_SUFFIX
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@
+#NOTE: libmgpptext.a needs to come before libmgpplib.a !!!
+QUERY_LIBS = ../text/libmgpptext.a   ../lib/libmgpplib.a $(AFLIBS)  -lstdc++ @LIBS@
+PASSES_LIBS = ../text/libmgpppass.a ../lib/libmgpplib.a -lstdc++ @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+
+JNIFLAGS=@JNIFLAGS@
+JNISUFFIX=@JNISUFFIX@
+JNIINC=@JNIINC@
+
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+
+CXXCOMPILE = $(CXX) -c $(DEFS) $(INCLUDES) $(CXXFLAGS)
+
+.SUFFIXES:
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $<
+
+# can we get rid of JAVA_HOME from here??
+INCLUDES = -I.. -I../lib -I../text -I$(JAVA_HOME)/include $(JNIINC) $(AFINCLUDES)
+
+all: compile link
+
+compile:
+	$(CXXCOMPILE) MGPPSearchWrapperImpl.cpp MGPPRetrieveWrapperImpl.cpp MGPPPassesWrapperImpl.cpp
+link:
+	$(CXX) $(LDFLAGS) $(JNIFLAGS) -o libmgppretrievejni.$(JNISUFFIX) MGPPRetrieveWrapperImpl.o $(QUERY_LIBS)
+	$(CXX) $(LDFLAGS) $(JNIFLAGS) -o libmgppsearchjni.$(JNISUFFIX) MGPPSearchWrapperImpl.o $(QUERY_LIBS)
+	$(CXX) $(LDFLAGS) $(JNIFLAGS) -o libmgpppassjni.$(JNISUFFIX) MGPPPassesWrapperImpl.o $(PASSES_LIBS)
+
+clean:
+	rm -rf *.o libmgppretrievejni.$(JNISUFFIX) libmgppsearchjni.$(JNISUFFIX) libmgpppassjni.$(JNISUFFIX)
+
+distclean: clean
+	rm -f Makefile
+
+install:
+#	../mkinstalldirs $(libdir); \
+#	cp libmgppjni.$(JNISUFFIX)  libmgpppassjni.$(JNISUFFIX)  $(libdir)/;
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPPassesWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPPassesWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPPassesWrapper.h	(revision 16561)
@@ -0,0 +1,127 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mgpp_MGPPPassesWrapper */
+
+#ifndef _Included_org_greenstone_mgpp_MGPPPassesWrapper
+#define _Included_org_greenstone_mgpp_MGPPPassesWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_greenstone_mgpp_MGPPPassesWrapper_TEXT_PASS_1
+#define org_greenstone_mgpp_MGPPPassesWrapper_TEXT_PASS_1 0L
+#undef org_greenstone_mgpp_MGPPPassesWrapper_TEXT_PASS_2
+#define org_greenstone_mgpp_MGPPPassesWrapper_TEXT_PASS_2 1L
+#undef org_greenstone_mgpp_MGPPPassesWrapper_INDEX_PASS_1
+#define org_greenstone_mgpp_MGPPPassesWrapper_INDEX_PASS_1 2L
+#undef org_greenstone_mgpp_MGPPPassesWrapper_INDEX_PASS_2
+#define org_greenstone_mgpp_MGPPPassesWrapper_INDEX_PASS_2 3L
+#undef org_greenstone_mgpp_MGPPPassesWrapper_SPECIAL_PASS
+#define org_greenstone_mgpp_MGPPPassesWrapper_SPECIAL_PASS 4L
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    init
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_init
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    setBasePath
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_setBasePath
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    setFileName
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_setFileName
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    setDocumentTag
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_setDocumentTag
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    addLevelTag
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_addLevelTag
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    setIndexLevel
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_setIndexLevel
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    setInversionMemLimit
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_setInversionMemLimit
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    finish
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_finish
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    exitValue
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_exitValue
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    initCSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_initCSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    addPass
+ * Signature: (CC)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_addPass
+  (JNIEnv *, jobject, jchar, jchar);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPPassesWrapper
+ * Method:    processMGPPDocument
+ * Signature: ([B)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPPassesWrapper_processMGPPDocument
+  (JNIEnv *, jobject, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPRetrieveWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPRetrieveWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPRetrieveWrapper.h	(revision 16561)
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mgpp_MGPPRetrieveWrapper */
+
+#ifndef _Included_org_greenstone_mgpp_MGPPRetrieveWrapper
+#define _Included_org_greenstone_mgpp_MGPPRetrieveWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_greenstone_mgpp_MGPPRetrieveWrapper
+ * Method:    getDocument
+ * Signature: (Ljava/lang/String;Ljava/lang/String;J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mgpp_MGPPRetrieveWrapper_getDocument
+  (JNIEnv *, jobject, jstring, jstring, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPSearchWrapper.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPSearchWrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/org_greenstone_mgpp_MGPPSearchWrapper.h	(revision 16561)
@@ -0,0 +1,141 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_greenstone_mgpp_MGPPSearchWrapper */
+
+#ifndef _Included_org_greenstone_mgpp_MGPPSearchWrapper
+#define _Included_org_greenstone_mgpp_MGPPSearchWrapper
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_initIDs
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    initCppSide
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_initCppSide
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    loadIndexData
+ * Signature: (Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_loadIndexData
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    unloadIndexData
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_unloadIndexData
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setStem
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setStem
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setAccentFold
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setAccentFold
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setCase
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setCase
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setMaxDocs
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxDocs
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setSortByRank
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setSortByRank
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setReturnTerms
+ * Signature: (Z)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnTerms
+  (JNIEnv *, jobject, jboolean);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setQueryLevel
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setQueryLevel
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setReturnLevel
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnLevel
+  (JNIEnv *, jobject, jstring);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setMatchMode
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMatchMode
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    setMaxNumeric
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxNumeric
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    getQueryParams
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_getQueryParams
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_greenstone_mgpp_MGPPSearchWrapper
+ * Method:    runQuery
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_runQuery
+  (JNIEnv *, jobject, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/jni/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/jni/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/jni/win32.mak	(revision 16561)
@@ -0,0 +1,94 @@
+###########################################################################
+#
+# win32 makefile -- mgpp\jni
+# 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  The 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.
+#
+###########################################################################
+ 
+MGPPHOME = ..
+
+UNAC_DIR = ../../packages/unac
+
+ENABLE_ACCENTFOLD = 1
+
+!IF $(ENABLE_ACCENTFOLD)
+AFLIBS = "$(UNAC_DIR)\libunac.lib"  "$(UNAC_DIR)\iconv.lib"
+AFINCLUDES = -I"$(UNAC_DIR)"
+!ELSE
+AFLIBS = 
+AFINCLUDES = 
+!ENDIF
+
+AR = lib
+CC = cl
+CPPFLAGS = -GX
+
+DEFS = -DQUIET -DHAVE_CONFIG_H -DSHORT_SUFFIX -DHAVE_STRING_H -D__WIN32__
+INCLUDES = -I"$(MGPPHOME)" -I"$(MGPPHOME)\lib" -I"$(MGPPHOME)\text" \
+           -I"$(JAVA_HOME)\include" -I"$(JAVA_HOME)\include\win32" $(AFINCLUDES)           
+
+COMPILE = $(CC) -c $(CPPFLAGS) $(DEFS) $(INCLUDES) 
+ 
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+ 
+HEADERS = \
+    MGPPSearchWrapperImpl.h  org_greenstone_mgpp_MGPPSearchWrapper.h \
+   org_greenstone_mgpp_MGPPRetrieveWrapper.h org_greenstone_mgpp_MGPPPassesWrapperImpl.h
+
+SOURCES = \
+    MGPPSearchWrapperImpl.cpp MGPPRetrieveWrapperImpl.cpp MGPPPassesWrapperImpl.cpp
+
+RETRIEVEOBJECTS = \
+    MGPPRetrieveWrapperImpl$o  $(MGPPHOME)\text\libmgpptext.lib \
+    $(MGPPHOME)\lib\libmgpplib.lib $(AFLIBS)
+
+SEARCHOBJETCS =  \
+    MGPPSearchWrapperImpl$o $(MGPPHOME)\text\libmgpptext.lib \
+    $(MGPPHOME)\lib\libmgpplib.lib $(AFLIBS)
+
+PASSES_OBJECTS = \
+    MGPPPassesWrapperImpl$o $(MGPPHOME)\text\libmgpppass.lib \
+    $(MGPPHOME)\lib\libmgpplib.lib
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : compile link
+
+compile:
+        $(COMPILE) MGPPSearchWrapperImpl.cpp MGPPRetrieveWrapperImpl.cpp MGPPPassesWrapperImpl.cpp
+
+link:
+	$(CC) -MD -LD $(SEARCHOBJETCS) -Femgppsearchjni.dll
+	$(CC) -MD -LD $(RETRIEVEOBJECTS) -Femgppretrievejni.dll
+	$(CC) -MD -LD $(PASSES_OBJECTS) -Femgpppassjni.dll
+
+install: 
+
+clean:
+	del *$o mgppsearchjni.dll mgppsearchjni.exp mgppsearchjni.lib mgppretrievejni.dll mgppretrievejni.exp mgppretrievejni.lib mgpppassjni.dll mgpppassjni.exp mgpppassjni.lib 
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/Makefile.in	(revision 16561)
@@ -0,0 +1,156 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the general purpose library
+# Copyright (C) 1994  Neil Sharman; (C) 1995 Tim Shimmin
+#
+# 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.
+#
+#       @(#)Makefile.in	1.3 16 Mar 1994
+#
+###########################################################################
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+ 
+SHELL = /bin/sh
+srcdir = @srcdir@
+subdir = lib
+VPATH = @srcdir@
+ 
+AR = ar
+CC = @CC@
+CXX = @CXX@
+ifneq ($(NZDL), 1)
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -fPIC
+else 
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -fPIC -DSILENT
+endif
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@ -fPIC
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+libdir = @libdir@
+datadir = $(prefix)/share
+localedir = $(datadir)/locale
+ 
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES)  $(CFLAGS)
+CXXCOMPILE = $(CXX) -c $(DEFS) $(INCLUDES) $(CXXFLAGS)
+ 
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(COMPILE) -D__cplusplus $<
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+ 
+.SUFFIXES: ._c ._o
+.c._c:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $@
+._c._o:
+	@echo $(COMPILE) $<
+	@rm -f _$*.c
+	@ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+.c._o:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $*._c
+	@echo $(COMPILE) $*._c
+	@rm -f _$*.c
+	@ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+INCLUDES = -I.. -I$(srcdir)
+ 
+HEADERS = \
+    bitio_m_random.h   local_strings.h    lovinstem.h \
+    bitio_m_stdio.h    memlib.h           netorder.h \
+    bitio_gen.h        mgheap.h           messages.h \
+    bitio_m.h          huffman.h  \
+    bitio_m_mem.h      perf_hash.h \
+    bitio_m_mems.h      \
+    pathmax.h          getpagesize.h      sysfuncs.h         bitio_m_abstract.h  \
+    random.h           simplefrenchstem.h unitool.h	     longlong.h \
+    non_ansi.h
+
+
+SOURCES = \
+    mgheap.cpp         \
+    huffman.cpp       messages.cpp \
+    bitio_gen.cpp     perf_hash.cpp \
+    local_strings.cpp lovinstem.cpp \
+    bitio_m_mem.cpp   bitio_m_stdio.cpp bitio_m_mems.cpp \
+    alloca.c          xmalloc.c         strstr.cpp \
+    ftruncate.cpp     strcasecmp.cpp    random.cpp \
+    simplefrenchstem.cpp                unitool.cpp       bitio_m_random.cpp \
+    bitio_m_abstract.cpp
+# memlib.cpp removed
+
+OBJECTS =  mgheap$o   \
+    huffman$o         messages$o \
+    bitio_gen$o       perf_hash$o \
+     local_strings$o   lovinstem$o \
+    bitio_m_mem$o     bitio_m_stdio$o   bitio_m_mems$o \
+    xmalloc$o         bitio_m_random$o  bitio_m_abstract$o \
+     random$o          simplefrenchstem$o \
+    unitool$o
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+
+all : libmgpplib.a
+
+libmgpplib.a : $(OBJECTS)
+	rm -f libmgpplib.a
+	$(AR) cru libmgpplib.a $(OBJECTS)
+	$(RANLIB) libmgpplib.a
+
+install: 
+installdirs:
+uninstall:
+execinstall:
+execuninstall:
+maninstall:
+manuninstall:
+ 
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $$file $(distdir) 2> /dev/null \
+	  || cp -p $$file $(distdir); \
+	done
+
+mostlyclean:
+	rm -f *.o *_.o
+ 
+clean: mostlyclean
+	rm -f libmgpplib.a
+ 
+distclean: clean
+	rm -f Makefile
+ 
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+
+# breaks make distclean (jrm21)
+#Makefile: Makefile.in ../../../config.status
+#	cd ../../.. && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ 
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/alloca.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/alloca.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/alloca.c	(revision 16561)
@@ -0,0 +1,496 @@
+/* alloca.c -- allocate automatically reclaimed memory
+   (Mostly) portable public-domain implementation -- D A Gwyn
+
+   This implementation of the PWB library alloca function,
+   which is used to allocate space off the run-time stack so
+   that it is automatically reclaimed upon procedure exit,
+   was inspired by discussions with J. Q. Johnson of Cornell.
+   J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+   There are some preprocessor constants that can
+   be defined when compiling for your specific system, for
+   improved efficiency; however, the defaults should be okay.
+
+   The general concept of this implementation is to keep
+   track of all alloca-allocated blocks, and reclaim any
+   that are found to be deeper in the stack than the current
+   invocation.  This heuristic does not reclaim storage as
+   soon as it becomes invalid, but it will do so eventually.
+
+   As a special case, alloca(0) reclaims storage without
+   allocating any.  It is a good idea to use alloca(0) in
+   your main control loop, etc. to force garbage collection.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#ifdef emacs
+#include "blockinput.h"
+#endif
+
+/* If compiling with GCC 2, this file's not needed.  */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+   there must be some other way alloca is supposed to work.  */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+   -- this is for usg, in which emacs must undefine static
+   in order to make unexec workable
+   */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+   provide an "address metric" ADDRESS_FUNCTION macro.  */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define	NULL	0
+
+/* Different portions of Emacs need to call different versions of
+   malloc.  The Emacs executable needs alloca to call xmalloc, because
+   ordinary malloc isn't protected from input signals.  On the other
+   hand, the utilities in lib-src need alloca to call malloc; some of
+   them are very simple, and don't have an xmalloc routine.
+
+   Non-Emacs programs expect this to call use xmalloc.
+
+   Callers below should use malloc.  */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION if you know the direction of stack
+   growth for your system; otherwise it will be automatically
+   deduced at run-time.
+
+   STACK_DIRECTION > 0 => grows toward higher addresses
+   STACK_DIRECTION < 0 => grows toward lower addresses
+   STACK_DIRECTION = 0 => direction of growth unknown  */
+
+#ifndef STACK_DIRECTION
+#define	STACK_DIRECTION	0	/* Direction unknown.  */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define	STACK_DIR	STACK_DIRECTION	/* Known at compile-time.  */
+
+#else /* STACK_DIRECTION == 0; need run-time code.  */
+
+static int stack_dir;		/* 1 or -1 once known.  */
+#define	STACK_DIR	stack_dir
+
+static void
+find_stack_direction ()
+{
+  static char *addr = NULL;	/* Address of first `dummy', once known.  */
+  auto char dummy;		/* To get stack address.  */
+
+  if (addr == NULL)
+    {				/* Initial entry.  */
+      addr = ADDRESS_FUNCTION (dummy);
+
+      find_stack_direction ();	/* Recurse once.  */
+    }
+  else
+    {
+      /* Second entry.  */
+      if (ADDRESS_FUNCTION (dummy) > addr)
+	stack_dir = 1;		/* Stack grew upward.  */
+      else
+	stack_dir = -1;		/* Stack grew downward.  */
+    }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+   (a) chain together all alloca'ed blocks;
+   (b) keep track of stack depth.
+
+   It is very important that sizeof(header) agree with malloc
+   alignment chunk size.  The following default should work okay.  */
+
+#ifndef	ALIGN_SIZE
+#define	ALIGN_SIZE	sizeof(double)
+#endif
+
+typedef union hdr
+{
+  char align[ALIGN_SIZE];	/* To force sizeof(header).  */
+  struct
+    {
+      union hdr *next;		/* For chaining headers.  */
+      char *deep;		/* For stack depth measure.  */
+    } h;
+} header;
+
+static header *last_alloca_header = NULL;	/* -> last alloca header.  */
+
+/* Return a pointer to at least SIZE bytes of storage,
+   which will be automatically reclaimed upon exit from
+   the procedure that called alloca.  Originally, this space
+   was supposed to be taken from the current stack frame of the
+   caller, but that method cannot be made to work for some
+   implementations of C, for example under Gould's UTX/32.  */
+
+pointer
+alloca (size)
+     unsigned size;
+{
+  auto char probe;		/* Probes stack depth: */
+  register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+  if (STACK_DIR == 0)		/* Unknown growth direction.  */
+    find_stack_direction ();
+#endif
+
+  /* Reclaim garbage, defined as all alloca'd storage that
+     was allocated from deeper in the stack than currently. */
+
+  {
+    register header *hp;	/* Traverses linked list.  */
+
+#ifdef emacs
+    BLOCK_INPUT;
+#endif
+
+    for (hp = last_alloca_header; hp != NULL;)
+      if ((STACK_DIR > 0 && hp->h.deep > depth)
+	  || (STACK_DIR < 0 && hp->h.deep < depth))
+	{
+	  register header *np = hp->h.next;
+
+	  free ((pointer) hp);	/* Collect garbage.  */
+
+	  hp = np;		/* -> next header.  */
+	}
+      else
+	break;			/* Rest are not deeper.  */
+
+    last_alloca_header = hp;	/* -> last valid storage.  */
+
+#ifdef emacs
+    UNBLOCK_INPUT;
+#endif
+  }
+
+  if (size == 0)
+    return NULL;		/* No allocation required.  */
+
+  /* Allocate combined header + user data storage.  */
+
+  {
+    register pointer new = malloc (sizeof (header) + size);
+    /* Address of header.  */
+
+    ((header *) new)->h.next = last_alloca_header;
+    ((header *) new)->h.deep = depth;
+
+    last_alloca_header = (header *) new;
+
+    /* User storage begins just after header.  */
+
+    return (pointer) ((char *) new + sizeof (header));
+  }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+  {
+    long shgrow:32;		/* Number of times stack has grown.  */
+    long shaseg:32;		/* Size of increments to stack.  */
+    long shhwm:32;		/* High water mark of stack.  */
+    long shsize:32;		/* Current size of stack (all segments).  */
+  };
+
+/* The stack segment linkage control information occurs at
+   the high-address end of a stack segment.  (The stack
+   grows from low addresses to high addresses.)  The initial
+   part of the stack segment linkage control information is
+   0200 (octal) words.  This provides for register storage
+   for the routine which overflows the stack.  */
+
+struct stack_segment_linkage
+  {
+    long ss[0200];		/* 0200 overflow words.  */
+    long sssize:32;		/* Number of words in this segment.  */
+    long ssbase:32;		/* Offset to stack base.  */
+    long:32;
+    long sspseg:32;		/* Offset to linkage control of previous
+				   segment of stack.  */
+    long:32;
+    long sstcpt:32;		/* Pointer to task common address block.  */
+    long sscsnm;		/* Private control structure number for
+				   microtasking.  */
+    long ssusr1;		/* Reserved for user.  */
+    long ssusr2;		/* Reserved for user.  */
+    long sstpid;		/* Process ID for pid based multi-tasking.  */
+    long ssgvup;		/* Pointer to multitasking thread giveup.  */
+    long sscray[7];		/* Reserved for Cray Research.  */
+    long ssa0;
+    long ssa1;
+    long ssa2;
+    long ssa3;
+    long ssa4;
+    long ssa5;
+    long ssa6;
+    long ssa7;
+    long sss0;
+    long sss1;
+    long sss2;
+    long sss3;
+    long sss4;
+    long sss5;
+    long sss6;
+    long sss7;
+  };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+   returned by the STKSTAT library routine.  */
+struct stk_stat
+  {
+    long now;			/* Current total stack size.  */
+    long maxc;			/* Amount of contiguous space which would
+				   be required to satisfy the maximum
+				   stack demand to date.  */
+    long high_water;		/* Stack high-water mark.  */
+    long overflows;		/* Number of stack overflow ($STKOFEN) calls.  */
+    long hits;			/* Number of internal buffer hits.  */
+    long extends;		/* Number of block extensions.  */
+    long stko_mallocs;		/* Block allocations by $STKOFEN.  */
+    long underflows;		/* Number of stack underflow calls ($STKRETN).  */
+    long stko_free;		/* Number of deallocations by $STKRETN.  */
+    long stkm_free;		/* Number of deallocations by $STKMRET.  */
+    long segments;		/* Current number of stack segments.  */
+    long maxs;			/* Maximum number of stack segments so far.  */
+    long pad_size;		/* Stack pad size.  */
+    long current_address;	/* Current stack segment address.  */
+    long current_size;		/* Current stack segment size.  This
+				   number is actually corrupted by STKSTAT to
+				   include the fifteen word trailer area.  */
+    long initial_address;	/* Address of initial segment.  */
+    long initial_size;		/* Size of initial segment.  */
+  };
+
+/* The following structure describes the data structure which trails
+   any stack segment.  I think that the description in 'asdef' is
+   out of date.  I only describe the parts that I am sure about.  */
+
+struct stk_trailer
+  {
+    long this_address;		/* Address of this block.  */
+    long this_size;		/* Size of this block (does not include
+				   this trailer).  */
+    long unknown2;
+    long unknown3;
+    long link;			/* Address of trailer block of previous
+				   segment.  */
+    long unknown5;
+    long unknown6;
+    long unknown7;
+    long unknown8;
+    long unknown9;
+    long unknown10;
+    long unknown11;
+    long unknown12;
+    long unknown13;
+    long unknown14;
+  };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+   I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+  struct stk_stat status;
+  struct stk_trailer *trailer;
+  long *block, size;
+  long result = 0;
+
+  /* We want to iterate through all of the segments.  The first
+     step is to get the stack status structure.  We could do this
+     more quickly and more directly, perhaps, by referencing the
+     $LM00 common block, but I know that this works.  */
+
+  STKSTAT (&status);
+
+  /* Set up the iteration.  */
+
+  trailer = (struct stk_trailer *) (status.current_address
+				    + status.current_size
+				    - 15);
+
+  /* There must be at least one stack segment.  Therefore it is
+     a fatal error if "trailer" is null.  */
+
+  if (trailer == 0)
+    abort ();
+
+  /* Discard segments that do not contain our argument address.  */
+
+  while (trailer != 0)
+    {
+      block = (long *) trailer->this_address;
+      size = trailer->this_size;
+      if (block == 0 || size == 0)
+	abort ();
+      trailer = (struct stk_trailer *) trailer->link;
+      if ((block <= address) && (address < (block + size)))
+	break;
+    }
+
+  /* Set the result to the offset in this segment and add the sizes
+     of all predecessor segments.  */
+
+  result = address - block;
+
+  if (trailer == 0)
+    {
+      return result;
+    }
+
+  do
+    {
+      if (trailer->this_size <= 0)
+	abort ();
+      result += trailer->this_size;
+      trailer = (struct stk_trailer *) trailer->link;
+    }
+  while (trailer != 0);
+
+  /* We are done.  Note that if you present a bogus address (one
+     not in any segment), you will get a different number back, formed
+     from subtracting the address of the first block.  This is probably
+     not what you want.  */
+
+  return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+   Determine the number of the cell within the stack,
+   given the address of the cell.  The purpose of this
+   routine is to linearize, in some sense, stack addresses
+   for alloca.  */
+
+static long
+i00afunc (long address)
+{
+  long stkl = 0;
+
+  long size, pseg, this_segment, stack;
+  long result = 0;
+
+  struct stack_segment_linkage *ssptr;
+
+  /* Register B67 contains the address of the end of the
+     current stack segment.  If you (as a subprogram) store
+     your registers on the stack and find that you are past
+     the contents of B67, you have overflowed the segment.
+
+     B67 also points to the stack segment linkage control
+     area, which is what we are really interested in.  */
+
+  stkl = CRAY_STACKSEG_END ();
+  ssptr = (struct stack_segment_linkage *) stkl;
+
+  /* If one subtracts 'size' from the end of the segment,
+     one has the address of the first word of the segment.
+
+     If this is not the first segment, 'pseg' will be
+     nonzero.  */
+
+  pseg = ssptr->sspseg;
+  size = ssptr->sssize;
+
+  this_segment = stkl - size;
+
+  /* It is possible that calling this routine itself caused
+     a stack overflow.  Discard stack segments which do not
+     contain the target address.  */
+
+  while (!(this_segment <= address && address <= stkl))
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+      if (pseg == 0)
+	break;
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      this_segment = stkl - size;
+    }
+
+  result = address - this_segment;
+
+  /* If you subtract pseg from the current end of the stack,
+     you get the address of the previous stack segment's end.
+     This seems a little convoluted to me, but I'll bet you save
+     a cycle somewhere.  */
+
+  while (pseg != 0)
+    {
+#ifdef DEBUG_I00AFUNC
+      fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+      stkl = stkl - pseg;
+      ssptr = (struct stack_segment_linkage *) stkl;
+      size = ssptr->sssize;
+      pseg = ssptr->sspseg;
+      result += size;
+    }
+  return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.cpp	(revision 16561)
@@ -0,0 +1,134 @@
+/**************************************************************************
+ *
+ * bitio_gen.c -- General supoport routines for bitio
+ * Copyright (C) 1994  Neil Sharman and Alistair Moffat
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "bitio_m_abstract.h"
+
+int fprintf (FILE *, const char *,...);
+
+unsigned long 
+BIO_Unary_Length (unsigned long val)
+{
+  register unsigned long num;
+  num = bitio_buffer::unary_length(val);
+  //  UNARY_LENGTH (val, num);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Binary_Length (unsigned long val, unsigned long b)
+{
+  register unsigned long num;
+  num = bitio_buffer::binary_length (val, b);
+  return (num);
+}
+
+
+unsigned long BIO_Gamma_Length (unsigned long val) {
+  register unsigned long num;
+  num = bitio_buffer::gamma_length(val);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Delta_Length (unsigned long val)
+{
+  register unsigned long num;
+  num = bitio_buffer::delta_length(val);
+  return (num);
+}
+
+
+unsigned long 
+BIO_Elias_Length (unsigned long val, unsigned long b, double s)
+{
+  register unsigned long num;
+  num = bitio_buffer::elias_length(val, b, s);
+  return (num);
+}
+
+unsigned long 
+BIO_Bblock_Length (unsigned long val, unsigned long b)
+{
+  register unsigned long num;
+  num = bitio_buffer::bblock_length (val, b);
+  return (num);
+}
+
+
+int 
+BIO_Bblock_Init (int N, int p)
+{
+  int b;
+  b = (int) (0.5 + 0.6931471 * N / p);
+  return (b ? b : 1);
+}
+
+
+int 
+BIO_Bblock_Init_W (int N, int p)
+{
+  int logb;
+  logb = bitio_buffer::floor_log2 ((N - p) / p);
+  return (logb < 0 ? 1 : (1 << logb));
+}
+
+int 
+BIO_Bblock_Bound_b (int N, int p, int b)
+{
+  int clogb;
+  clogb = bitio_buffer::ceil_log2 (b);
+  return (p * (1 + clogb) + (N - p * ((1 << clogb) - b + 1)) / b);
+}
+
+int 
+BIO_Bblock_Bound (int N, int p)
+{
+  int b;
+  b = BIO_Bblock_Init_W (N, p);
+  return (BIO_Bblock_Bound_b (N, p, b));
+}
+
+/* adjustment is a hack to overcome inaccuracies in floating point,
+   where (int)(ln4/ln2) can equal 1, not 2. */
+int 
+BIO_Gamma_Bound (int N, int p)
+{
+  return ((int) (p * (2 * log2 (((double) N * 1.0001) / p) + 1)));
+}
+
+int 
+floorlog_2 (int b)
+{
+  int logb;
+  logb = bitio_buffer::floor_log2 (b);
+  return logb;
+}
+
+int 
+ceillog_2 (int b)
+{
+  int logb;
+  logb = bitio_buffer::ceil_log2 (b);
+  return logb;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_gen.h	(revision 16561)
@@ -0,0 +1,61 @@
+/**************************************************************************
+ *
+ * bitio_gen.h -- General supoport routines for bitio
+ * Copyright (C) 1994  Neil Sharman and Alistair Moffat
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains function definitions for doing general bitio operations.
+ *
+ *  This file defines functions for calculation the length in bits of a number
+ *  coded unary, binary, delta, gamma, elias, and  bblock coding. It defines 
+ *  routines for calculating the parameter and bounds for bblock coding. 
+ *  Routines for calculating ceil and floor lg are also defined.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_GEN
+#define H_BITIO_GEN
+
+unsigned long BIO_Unary_Length (unsigned long val);
+
+unsigned long BIO_Binary_Length (unsigned long val, unsigned long b);
+
+unsigned long BIO_Gamma_Length (unsigned long val);
+
+unsigned long BIO_Delta_Length (unsigned long val);
+
+unsigned long BIO_Elias_Length (unsigned long val, unsigned long b, double s);
+
+unsigned long BIO_Bblock_Length (unsigned long val, unsigned long b);
+
+int BIO_Bblock_Init (int N, int p);
+
+int BIO_Bblock_Init_W (int N, int p);
+
+int BIO_Bblock_Bound_b (int N, int p, int b);
+
+int BIO_Bblock_Bound (int N, int p);
+
+int BIO_Gamma_Bound (int N, int p);
+
+int floorlog_2 (int b);
+
+int ceillog_2 (int b);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.cpp	(revision 16561)
@@ -0,0 +1,9 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "bitio_m_abstract.h"
+
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_abstract.h	(revision 16561)
@@ -0,0 +1,340 @@
+#ifndef H_BITIO_ABSTRACT
+#define H_BITIO_ABSTRACT
+
+class bitio_buffer {
+public:
+  virtual int bit() = 0;
+  virtual void encodeBit(int bit) = 0;
+  virtual void seek(unsigned long topos) = 0;
+  
+  static inline void check_positive(char *type, unsigned long x) {
+    if (x <= 0) {
+      fprintf(stderr, "Error: Cannot %s encode %lu\n", type, x);
+      exit(1);
+    }
+  }
+
+  static inline int floor_log2(unsigned long x) {
+    unsigned long  _B_x = x;
+    int reply;
+    
+    reply = -1;
+    while (_B_x != 0) {
+      _B_x >>= 1;
+      ++reply;
+    }
+    return reply;
+  }
+  
+  static inline int ceil_log2(unsigned long x) {
+    register int _B_x;
+    unsigned long reply;
+    
+    _B_x = x - 1;
+    reply = 0;
+    while (_B_x != 0) {
+      _B_x >>= 1;
+      ++reply;
+    }
+    return reply;
+  }
+
+  inline unsigned long unary_decode(unsigned long *count) {
+    unsigned long x;
+    
+    x = 1;
+    while (!this->bit()) ++x;
+    if (count != NULL) {
+      *count += x;
+    }
+    return x;
+  }
+
+  inline void unary_encode(unsigned long x, unsigned long *count) {
+    register unsigned long _B_x = x;
+    check_positive("unary", _B_x);
+    if (count != NULL) {
+      *count += _B_x;
+    }
+    while(--_B_x) 
+      encodeBit(0);
+    encodeBit(1);
+  }
+
+
+  inline unsigned long binary_decode(unsigned long b, unsigned long *count) {
+    register unsigned long _B_x = 0;
+    register unsigned long _B_b = b;
+    register int _B_i, _B_logofb, _B_thresh;
+    register unsigned long reply;
+    
+    if (_B_b != 1) {					
+      _B_logofb = ceil_log2(_B_b);
+      _B_thresh = (1<<_B_logofb) - _B_b;
+      --_B_logofb;
+      
+      if (count != NULL) {
+	*count += _B_logofb;
+      }
+      
+      for (_B_i=0; _B_i < _B_logofb; ++_B_i) {
+	_B_x += _B_x + this->bit();
+      }
+
+      if ((int)_B_x >= _B_thresh) {
+	_B_x += _B_x + this->bit();
+	_B_x -= _B_thresh;
+	if (count != NULL) {
+	  *count = *count + 1;
+	}
+      }
+      reply = _B_x+1;
+      
+    } else reply = 1;
+    
+    return reply;
+  }
+
+  inline void binary_encode(unsigned long x, unsigned long b, unsigned long *count) {
+    register unsigned long _B_x = (x);
+    register unsigned long _B_b = (b);
+    register int _B_nbits, _B_logofb, _B_thresh;
+    
+    check_positive("binary", _B_x);
+    _B_logofb = ceil_log2(_B_b);
+    
+    _B_thresh = (1<<_B_logofb) - _B_b;
+    if ((int)(--_B_x) < _B_thresh) _B_nbits = _B_logofb-1;
+    else {
+      _B_nbits = _B_logofb;
+      _B_x += _B_thresh;
+    }
+    if (count != NULL) {
+      *count += _B_nbits;
+    }
+    while (--_B_nbits>=0)
+      encodeBit((_B_x>>_B_nbits) & 0x1);
+  }
+
+  inline unsigned long bblock_decode(unsigned long b, unsigned long *count) {
+    register unsigned long _B_x1, _B_xx = 0;
+    register unsigned long _B_bb = (b);
+    register int _B_xdivb;
+    
+    _B_xdivb = this->unary_decode(count);
+    --_B_xdivb;				       
+    while (_B_xdivb--) {
+      _B_xx += _B_bb;
+    }
+    _B_x1 = this->binary_decode(_B_bb, count);
+    return (_B_xx + _B_x1);
+  }
+  
+  inline void bblock_encode(unsigned long x, unsigned long b, unsigned long *count) {
+    register unsigned long _B_xx = (x);
+    register unsigned long _B_bb = (b);
+    register int _B_xdivb = 0;
+    check_positive("bblock", _B_xx);
+    --_B_xx;
+    while (_B_xx >= _B_bb) {
+      ++_B_xdivb;
+      _B_xx -= _B_bb;
+    }
+    unary_encode(_B_xdivb+1, count);
+    binary_encode(_B_xx+1, _B_bb, count);
+  }
+
+  inline unsigned long elias_decode(unsigned long b, double s, unsigned long *count) {
+    register unsigned long _B_xx;
+    register unsigned long _B_b = b;
+    register double _B_s = s;
+    register int _B_lower=1, _B_upper=1;
+    register int _B_k;
+    register double _B_pow=1.0;
+    
+    _B_k = this->unary_decode(count);
+    --_B_k;
+    while (_B_k--) {
+      _B_lower = _B_upper+1;
+      _B_pow *= _B_s;
+      _B_upper += (int) (_B_b*_B_pow);
+    }
+    _B_xx = this->binary_decode(_B_upper-_B_lower+1, count);
+    --_B_xx;
+    check_positive("gamma", _B_xx+_B_lower);
+    return _B_xx+_B_lower;
+  }
+  
+  inline void elias_encode(unsigned long x, unsigned long b, double s, unsigned long *count) {
+    register unsigned long _B_xx = x;
+    register unsigned long _B_b = b;
+    register double _B_s = s;
+    register int _B_lower=1, _B_upper=1;
+    register int _B_k=0;
+    register double _B_pow=1.0;
+    
+    check_positive("elias", _B_xx);
+    
+    while ((int)_B_xx>_B_upper) {
+      ++_B_k;
+      _B_lower = _B_upper+1;
+      _B_pow *= _B_s;
+      _B_upper += (int) (_B_b*_B_pow);
+    }
+    unary_encode(_B_k+1, count);
+    binary_encode(_B_xx-_B_lower+1, _B_upper-_B_lower+1, count);
+  }
+  
+  inline unsigned long gamma_decode(unsigned long *count) {
+    register unsigned long _B_xx = 1;
+    register int _B_nbits = 0;
+    
+    while(!this->bit()) ++_B_nbits;
+    if (count != NULL) {
+      *count += _B_nbits*2+1;
+    }
+    while (_B_nbits-- > 0)
+      _B_xx += _B_xx + this->bit();
+    return _B_xx;
+  }
+
+  inline void gamma_encode(unsigned long x, unsigned long *count) {
+    register unsigned long _B_xx = x;
+    register int _B_logofb;
+    register int _B_nbits;
+    
+    check_positive("gamma", _B_xx);
+    _B_logofb = floor_log2(_B_xx);
+    _B_nbits = _B_logofb+1;
+    
+    if (count != NULL) { 
+      *count += _B_logofb*2+1;
+    }
+    while(_B_logofb--) encodeBit(0);
+    while (--_B_nbits>=0) encodeBit((_B_xx>>_B_nbits) & 0x1);
+  }
+  
+  inline unsigned long delta_decode(unsigned long *count) {
+    register unsigned long _B_xxx;
+    register int _B_logx;
+    _B_logx = gamma_decode(count);
+    --_B_logx;
+    _B_xxx = binary_decode(1<<_B_logx, count); 
+    --_B_xxx;
+    return (_B_xxx + (1<<_B_logx));
+  }
+
+  inline void delta_encode(unsigned long x, unsigned long *count) {
+    register unsigned long _B_xxx = x;
+    register int _B_logx;
+    _B_logx = floor_log2(_B_xxx);
+    gamma_encode(_B_logx+1, count);
+    binary_encode(_B_xxx+1, 1<<_B_logx, count);
+  }
+
+  inline unsigned long huff_decode(unsigned long *mcodes, unsigned long **values,
+				   unsigned long *count = NULL) {
+    register unsigned long *__min_code = mcodes;
+    register unsigned long *__mclen    = mcodes;
+    register unsigned long **__values  = values;
+    register unsigned long __code = 0;
+    do {
+      __code += __code + bit();
+      if (count != NULL) ++(*count);
+    } while (__code < *++__mclen);
+    
+    return __values[__mclen - __min_code][__code - *__mclen];
+  }
+
+  inline void huff_encode(unsigned long x, unsigned long *codes, char *lens,
+			  unsigned long *count = NULL) {
+    register int __i;
+    register int __clen = lens[x];
+    register unsigned long __code = codes[x];
+    for (__i=__clen-1; __i>=0; --__i) {
+      encodeBit((__code >> __i) & 1);
+    }
+    if (count != NULL) {
+      *count += lens[x];
+    }
+  }
+
+  static inline unsigned long unary_length(unsigned long x) {
+    check_positive("unary", x);
+    return x;
+  }
+
+  static inline int binary_length(unsigned long x, unsigned long b) {
+    register unsigned long _B_x = (x);
+    register unsigned long _B_b = (b);
+    register int _B_nbits, _B_logofb, _B_thresh;
+    
+    check_positive("binary", _B_x);
+    _B_logofb = ceil_log2(_B_b);
+    _B_thresh = (1<<_B_logofb) - _B_b;
+    if ((int)(--_B_x) < _B_thresh) _B_nbits = _B_logofb-1;
+    else _B_nbits = _B_logofb;
+    return _B_nbits;
+  }
+
+  static inline unsigned long bblock_length(unsigned long x, unsigned long b) {
+    register unsigned long _B_bcount, count, _B_xx = (x);
+    register unsigned long _B_bb = (b);
+    register int _B_xdivb = 0;
+    
+    check_positive("bblock", _B_xx);
+    --_B_xx;
+    while (_B_xx >= _B_bb) {
+      ++_B_xdivb;
+      _B_xx -= _B_bb;
+    }
+    count = unary_length(_B_xdivb+1);
+    _B_bcount = binary_length(_B_xx+1, _B_bb);
+    count += _B_bcount;
+    return count;
+  }
+  
+  static inline unsigned long elias_length(unsigned long x, unsigned long b, 
+					   double s) {
+    register unsigned long _B_xx = x;
+    register unsigned long _B_b = b;
+    register double _B_s = s;
+    register int _B_lower=1, _B_upper=1;
+    register int _B_k=0, _B_ecount;
+    register double _B_pow=1.0;
+    register unsigned long count;
+    check_positive("gamma", _B_xx);
+    
+    while ((int)(_B_xx)>_B_upper) {
+      ++_B_k;
+      _B_lower = _B_upper+1;
+      _B_pow *= _B_s;
+      _B_upper += (int) (_B_b*_B_pow);
+    }
+    count = unary_length(_B_k+1);
+    _B_ecount = binary_length(_B_xx-_B_lower+1, _B_upper-_B_lower+1);
+    count += _B_ecount;
+    return count;
+  }
+  
+  static inline unsigned long gamma_length(unsigned long x) {
+    register unsigned long _B_xx = x;
+    register int _B_logofb;
+    check_positive("gamma", _B_xx);
+    _B_logofb = floor_log2(_B_xx);
+    return _B_logofb*2+1;
+  }
+
+  static inline unsigned long delta_length(unsigned long x) {
+    register unsigned long _B_xxx = x;
+    register int _B_logx, _B_dcount;
+    register unsigned long count;
+    _B_logx = floor_log2 (_B_xxx);
+    count = gamma_length(_B_logx+1);
+    _B_dcount = binary_length (_B_xxx+1, 1<<_B_logx);
+    count += _B_dcount;
+    return count;
+  }
+};
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.cpp	(revision 16561)
@@ -0,0 +1,128 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "bitio_m_abstract.h"
+#include "bitio_m_mem.h"
+
+mem_bitio_buffer::mem_bitio_buffer() {
+}
+
+mem_bitio_buffer::mem_bitio_buffer(unsigned char *readbuffer, int size) { 
+  pos       = readbuffer;
+  base      = readbuffer;
+  remaining = size;
+  buffer    = 0;
+  btg       = 0;
+}
+
+
+void mem_bitio_buffer::error() {
+  fprintf (stderr, "Unexpected EOF in \"%s\" on line %d\n", __FILE__, __LINE__);
+  exit(1);
+}
+
+void mem_bitio_buffer::encodeStart() {
+  btg = sizeof(buffer) * 8;
+}
+
+
+void mem_bitio_buffer::seek(unsigned long toPos) { 
+  register long _newPos;
+  do {
+    _newPos = toPos;
+    
+    pos    = base + (_newPos >> 3);
+    buffer = *pos++;
+    btg    = 8 - (_newPos & 0x07);
+  } while (0);
+}
+
+long mem_bitio_buffer::addff(long b) {
+  do {
+    if (btg == 0) {
+      if (remaining != 0) {
+	buffer = *pos++;
+	--remaining;
+      } else {
+	buffer = 0xff;
+      }
+      btg = sizeof(buffer) * 8;
+    }
+    b = b + b + ((buffer >> --btg) & 1);
+  } while(0);
+
+  return b;
+}
+
+long mem_bitio_buffer::add00(long b) {
+  do {
+    if (btg == 0) {
+      if (remaining != 0) {
+	buffer = *pos++;
+	--remaining;
+      } else {
+	buffer = 0x00;
+      }
+      btg = sizeof(buffer) * 8;
+    }
+    b = b + b + ((buffer >> --btg) & 1);
+  } while(0);
+  
+  return b;
+}
+
+int mem_bitio_buffer::bit() {
+  if (btg == 0) {
+
+    if (remaining == 0) { 
+      this->error();
+      return 0;
+
+    } else {
+      buffer = *pos++;
+      --remaining;
+      btg = sizeof(buffer) * 8;
+    }
+    
+  }
+  return ((buffer >> --btg) & 0x01);
+}
+
+void mem_bitio_buffer::encodeBit(int b) {
+  --btg;
+  if (b) buffer |= (1 << btg);
+  
+  if (btg == 0) {
+    if (remaining) {
+      *pos++ = buffer;
+      --remaining;
+    }
+    buffer = 0;
+    btg = sizeof(buffer)*8;
+  }
+}
+
+void mem_bitio_buffer::flush() {
+  if (btg != sizeof(buffer)*8) {
+    if (remaining) {
+      *pos++ = buffer;
+      --remaining;
+    }
+  }
+  btg = sizeof(buffer)*8;
+}
+
+void mem_bitio_buffer::done() {
+  // do nothing
+}
+
+int mem_bitio_buffer::encodeLength() {
+  return pos - base;
+}
+
+int mem_bitio_buffer::encodeVerify() {
+  return (remaining != 0);
+}
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mem.h	(revision 16561)
@@ -0,0 +1,104 @@
+/**************************************************************************
+ *
+ * bitio_m_mem.h -- Macros for bitio to memory
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are faster than the ones in "mems" files. But 
+ *  with these routines you cannot mix reads and writes on the array of chars,
+ *  or multiple write, at the same time and guarantee them to work, also you 
+ *  cannot seek to a point and do a write. The decode routine can detect when
+ *  you run off the end of the array and will produce an approate error 
+ *  message, and the encode routine will stop when it gets to the end of the 
+ *  character array. 
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_MEM
+#define H_BITIO_M_MEM
+
+#include "bitio_m_abstract.h"
+
+
+class mem_bitio_buffer : public bitio_buffer {
+protected:
+  unsigned char *pos;
+  unsigned char *base;
+  int            remaining;
+  unsigned char  buffer;
+  unsigned char  btg;
+  
+public:
+  mem_bitio_buffer ();
+  mem_bitio_buffer (unsigned char *buffer, int size);
+  void seek (unsigned long topos);
+  void error ();
+  long add00 (long b);
+  long addff (long b);
+  int  bit ();
+  void done ();
+
+  void encodeStart ();
+  void encodeBit (int bit);
+  void encodeDone () { flush(); }
+  int  encodeVerify ();
+  int  encodeLength ();
+  void flush ();
+};
+
+
+/*
+#define ENCODE_START(p,r)						\
+  {									\
+    mem_bitio_buffer buffer(p, r);                                     \
+    buffer.encodeStart();
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    mem_bitio_buffer buffer;                                           \
+    buffer.unpause(b);
+
+#define ENCODE_PAUSE(b)	buffer.pause(&b);}
+
+#define ENCODE_FLUSH buffer.flush()
+
+#define ENCODE_DONE buffer.flush();}
+
+
+#define DECODE_START(p,r)						\
+  {	                                                                \
+    mem_bitio_buffer buffer(p,r);
+
+#define DECODE_CONTINUE(b)						\
+  {	                                                                \
+    mem_bitio_buffer buffer;                                           \
+    buffer.unpause(b);
+
+#define DECODE_ADD_FF(b) b = buffer.addff(b)
+
+#define DECODE_ADD_00(b) b = buffer.add00(b)
+
+#define DECODE_BIT buffer.bit()
+
+#define DECODE_DONE buffer.done();}
+
+#define DECODE_PAUSE(b) buffer.pause(&b);}
+*/
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.cpp	(revision 16561)
@@ -0,0 +1,89 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "bitio_m_abstract.h"
+#include "bitio_m_mems.h"
+
+/**
+ * Create decoding "stream"/"file"
+ */
+mems_bitio_buffer::mems_bitio_buffer()
+{
+}
+
+mems_bitio_buffer::mems_bitio_buffer(unsigned char *readbuffer, long _pos)
+{ 
+  base = readbuffer;
+  pos  = _pos;
+}
+
+void mems_bitio_buffer::encodeStart()
+{
+  // Do nothing
+}
+
+
+void mems_bitio_buffer::error()
+{
+  fprintf(stderr, "Unexpected EOF in \"%s\" on line %d\n", __FILE__, __LINE__);
+  exit(1);
+}
+
+
+void mems_bitio_buffer::seek(unsigned long toPos)
+{ 
+  pos = toPos;
+}
+
+unsigned long mems_bitio_buffer::position()
+{
+  return pos;
+}
+
+long mems_bitio_buffer::addff(long b)
+{
+  do
+    {
+      b = b + b + (base[pos>>3] & (0x80 >> (pos & 0x07)) != 0);
+      ++pos;
+    }
+  while(0);
+
+  return b;
+}
+
+long mems_bitio_buffer::add00(long b)
+{
+  return addff(b);
+}
+
+int mems_bitio_buffer::bit()
+{
+  ++pos;
+  return ((base[(pos-1)>>3] & (0x80 >> ((pos-1) & 0x07))) != 0);
+}
+
+void mems_bitio_buffer::encodeBit(int bit)
+{
+  if (bit)
+    base[pos>>3] |= 0x80 >> (pos & 0x07);
+  else
+    base[pos>>3] &= 0xff7f >> (pos & 0x07);
+  ++pos;
+}
+
+void mems_bitio_buffer::flush()
+{
+  // do nothing
+}
+
+void mems_bitio_buffer::done()
+{
+  // do nothing
+}
+
+void mems_bitio_buffer::encodeDone()
+{
+  flush();
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_mems.h	(revision 16561)
@@ -0,0 +1,100 @@
+/**************************************************************************
+ *
+ * bitio_m_mems.h -- Macros for bitio to memory (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are slower than the ones in "mem" files. but 
+ *  with these routines you can mix reads and writes, or multiple writes,  on
+ *  the array of chars at the same time and guarantee them to work, also you 
+ *  can seek to a point and do a write. The decode and encode macros cannot 
+ *  detect when the end off the character is reached and just continue 
+ *  processing.
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_MEMS
+#define H_BITIO_M_MEMS
+
+#include "bitio_m_abstract.h"
+
+
+class mems_bitio_buffer : public bitio_buffer {
+protected:
+  unsigned char *base;
+  unsigned long  pos;
+  
+public:
+  mems_bitio_buffer();
+  mems_bitio_buffer(unsigned char *readbuffer, long _pos);
+  void seek(unsigned long topos);
+  void error();
+  long add00(long b);
+  long addff(long b);
+  int  bit();
+  //  int  bitOffset();
+  void done();
+
+  unsigned long position();
+
+  void encodeStart();
+  void encodeBit(int b);
+  void encodeDone();
+  void flush();
+};
+
+
+/*#define ENCODE_START(b,p)						\
+  {									\
+    mems_bitio_buffer buffer(b, p);
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    mems_bitio_buffer buffer;                                          \
+    buffer.unpause(b);
+
+#define ENCODE_PAUSE(b)	buffer.pause(&b);}
+
+#define ENCODE_FLUSH buffer.flush();
+
+
+#define ENCODE_DONE buffer.encodeDone();}
+
+
+#define DECODE_START(b,p)						\
+  {		                                                        \
+    mems_bitio_buffer buffer(b, p);
+
+#define DECODE_CONTINUE(b)						\
+  {									\
+    mems_bitio_buffer buffer;                                          \
+    buffer.unpause(b);
+
+#define DECODE_ADD_FF(b) b = buffer.addff(b);
+
+#define DECODE_ADD_00(b) b = buffer.add00(b);
+
+#define DECODE_BIT buffer.bit()
+
+#define DECODE_DONE buffer.done();}
+
+#define DECODE_PAUSE(b) buffer.pause(&b);}
+*/
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.cpp	(revision 16561)
@@ -0,0 +1,218 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "longlong.h"
+#include "sysfuncs.h"
+#include "memlib.h"
+
+#include "bitio_m_abstract.h"
+#include "bitio_m_random.h"
+
+random_bitio_buffer::random_bitio_buffer(FILE *f, unsigned long length)
+{
+  buffer = NULL;
+  attachFile (f, length);
+}
+
+random_bitio_buffer::~random_bitio_buffer() {
+  if (buffer != NULL) delete []buffer;
+}
+
+void random_bitio_buffer::attachFile (FILE *f, unsigned long length) {
+  // delete the old buffer
+  if (buffer != NULL) {
+    delete []buffer;
+    buffer = NULL;
+  }
+
+  // reset all the values
+  file   = f;
+  pos    = 0;
+  base   = 0;
+  used   = 0;
+  len    = length - 1;
+  shift  = 0;
+
+  // make sure the length is a power of 2
+  while (len != 0) {
+    ++shift;
+    len = len >> 1;
+  }
+  len = 1 << shift;
+
+  // allocate a new buffer
+  buffer = new u_char[len];
+
+  // fill up the buffer
+  if (file != NULL) {
+    fseek(file, 0, 0);
+    fread(buffer, 1, len, file);
+  }
+}
+
+random_bitio_buffer &random_bitio_buffer::operator= (const random_bitio_buffer &_rbb) {
+  // delete the old buffer
+  if (buffer != NULL) {
+    delete []buffer;
+    buffer = NULL;
+  }
+
+  file = _rbb.file;
+
+  base = _rbb.base;
+  used = _rbb.used;
+  pos = _rbb.pos;
+  len = _rbb.len;
+  shift = _rbb.shift;
+
+  // copy the buffer
+  buffer = new u_char [len];
+  //  memcpy (buffer, _rbb.buffer, len);
+memcpy (buffer, _rbb.buffer, len);
+  
+  return (*this);
+}
+
+void random_bitio_buffer::error() {
+}
+
+void random_bitio_buffer::writeRead()
+{
+  if (used)
+    {
+      fseek(file, base, 0);
+      fwrite(buffer, 1, len, file);
+    }
+  //  else
+  //    {
+      base += len;
+      fseek(file, base, 0);
+      fread(buffer, 1, len, file);
+      pos = 0;
+      used = 0;
+  //    }
+}
+
+void random_bitio_buffer::encodeBit(int bit)
+{
+  do
+    {
+      if (bit != 0)
+	{
+	  buffer[pos >> 3] |= 0x80 >> (pos & 0x07);
+	}
+      else
+	{
+	  buffer[pos >> 3] &= 0xff7f >> (pos & 0x07);
+	}
+      used = 1;
+      ++pos;
+
+      if ((pos >> 3) >= len)
+	{
+	  writeRead();
+	}
+    }
+  while (0);
+}
+
+void random_bitio_buffer::flush()
+{
+  if (used != 0)
+    {
+      fseek(file, base, 0);
+      fwrite(buffer, 1, len, file);
+      used = 0;
+    }
+}
+
+void random_bitio_buffer::encodeDone()
+{
+  flush();
+  if (buffer != NULL) {
+    delete []buffer;
+    buffer = NULL;
+  }
+}
+
+unsigned long random_bitio_buffer::tell()
+{
+  return ((base << 3) + pos);
+}
+
+void random_bitio_buffer::seek(unsigned long toPos)
+{
+  if (((toPos >> 3) >= base) && (((toPos+7) >> 3) < (base + len)))
+    {
+      pos = toPos - (base << 3);
+    }
+  else
+    {
+      flush();
+      base = toPos >> 3;
+      base = (toPos >> (shift + 3)) << shift;
+      fseek(file, base, 0);
+      fread(buffer, 1, len, file);
+      pos = toPos & 0x07;
+      pos = toPos & ((8 << shift) - 1);
+    }
+}
+
+int random_bitio_buffer::bit()
+{
+  //  fprintf(stderr, "Bit in");
+  ++pos;
+  if (((pos - 1) >> 3) >= len)
+    {
+      writeRead();
+      pos = 1;
+    }
+  return (buffer[(pos - 1) >> 3] & (0x80 >> ((pos - 1) & 0x07))) != 0;
+}
+
+long random_bitio_buffer::addff(long b)
+{
+  return b + b + bit();
+}
+
+long random_bitio_buffer::add00(long b)
+{
+  return addff(b);
+}
+
+int random_bitio_buffer::bitOffset()
+{
+  return 0;
+}
+
+void random_bitio_buffer::done()
+{
+  encodeDone();
+}
+
+
+#ifdef USE_LONG_LONG
+void random_bitio_buffer::seek_LL(mg_ullong toPos)
+{
+  if (((toPos >> 3) >= (mg_ullong) base) &&
+      (((toPos + 7) >> 3) < (mg_ullong) (base + len)))
+    {
+      pos = (long) (toPos - (mg_ullong) (base << 3));
+    }
+  else
+    {
+      flush();
+      base = (long) ((toPos >> (shift + 3)) << shift);
+
+      fseek(file, base, 0);
+      fread(buffer, 1, len, file);
+      pos = (long) (toPos & ((8 << shift) - 1));
+    }
+}
+
+mg_ullong random_bitio_buffer::tell_LL()
+{
+  return (((mg_ullong) base) << 3ull) + pos;
+}
+#endif
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_random.h	(revision 16561)
@@ -0,0 +1,127 @@
+/**************************************************************************
+ *
+ * bitio_m_random.h -- Macros for bitio to a file (random access)
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on an array 
+ *  of chars. These routines are slower than the ones in "mem" files. but 
+ *  with these routines you can mix reads and writes, or multiple writes,  on
+ *  the array of chars at the same time and guarantee them to work, also you 
+ *  can seek to a point and do a write. The decode and encode macros cannot 
+ *  detect when the end off the character is reached and just continue 
+ *  processing.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_RANDOM
+#define H_BITIO_M_RANDOM
+
+#include "longlong.h"
+#include "bitio_m_abstract.h"
+
+
+class random_bitio_buffer : public bitio_buffer { 
+public:
+  FILE *file;
+  unsigned char *buffer;
+  unsigned long  base;
+  unsigned long  used;
+  unsigned long  pos;
+  unsigned long  len;
+  unsigned long  shift;
+  void writeRead();
+
+  random_bitio_buffer(FILE *f = 0,
+		      unsigned long length = 8*1024);
+  virtual ~random_bitio_buffer();
+  
+  void attachFile (FILE *f, unsigned long length);
+
+  random_bitio_buffer &operator= (const random_bitio_buffer &_rbb);
+  
+  void encodeDone();
+  void encodeBit(int b);
+  void flush();
+  unsigned long tell();
+
+  void seek(unsigned long topos);
+  void error();
+  long add00(long b);
+  long addff(long b);
+  int  bit();
+  int  bitOffset();
+  void done();
+
+#ifdef USE_LONG_LONG
+  // extras for longlong.h
+  void seek_LL(mg_ullong pos);
+  mg_ullong tell_LL();
+#endif
+};
+
+
+/*#define ENCODE_START(f,l)						\
+  {									\
+    random_bitio_buffer buffer(f, l);
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    random_bitio_buffer buffer;                                               \
+    buffer.unpause(b);
+
+#define SEEK fprintf(stderr, "Seek to %d\n",buffer.base)
+#define READ fprintf(stderr, "Read of %d\n",buffer.len)
+#define WRITE fprintf(stderr, "Write of %d\n",buffer.used)
+
+#define WRITE_READ buffer.writeRead()
+
+#define ENCODE_PAUSE(b)	buffer.pause(&b);}
+
+#define ENCODE_FLUSH buffer.flush();
+
+#define ENCODE_DONE buffer.done();}							
+
+#define DECODE_START(f,l) \
+  {									\
+    random_bitio_buffer buffer(f, l);
+
+#define DECODE_CONTINUE(b) \
+  {									\
+    random_bitio_buffer buffer;                                               \
+    buffer.unpause(b);
+
+#define DECODE_BIT buffer.bit()
+
+#define DECODE_ADD_FF(b) b = buffer.addff(b)
+
+#define DECODE_ADD_00(b) b = buffer.add00(b)
+
+#define DECODE_DONE buffer.done();}
+
+#define DECODE_PAUSE(b)	buffer.pause(&b);}
+
+#define ENCODE_TELL buffer.tell()
+
+#define DECODE_TELL buffer.tell()
+*/
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.cpp	(revision 16561)
@@ -0,0 +1,125 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "bitio_m_stdio.h"
+
+stdio_bitio_buffer::stdio_bitio_buffer(FILE *f) {
+  attachFile (f);
+}
+
+void stdio_bitio_buffer::attachFile (FILE *f) {
+  file = f;
+  buffer = 0;
+  btg = 0;
+  bytesWritten = 0;
+}
+
+void stdio_bitio_buffer::encodeStart() {
+  btg = sizeof(buffer) * 8;
+}
+
+void stdio_bitio_buffer::error() {
+  fprintf(stderr, "Unexpected EOF in \"%s\" on line %d", __FILE__, __LINE__);
+  exit(1);
+}
+
+void stdio_bitio_buffer::seek (unsigned long byte, unsigned char bit) {
+  if (fseek (file, byte, SEEK_SET) != 0) return;
+  buffer = getc(file);
+  btg = bit;
+  bytesWritten = 0;
+}
+
+void stdio_bitio_buffer::seek(unsigned long topos) {
+  if (fseek(file, topos >> 3, SEEK_SET) != 0) return;
+  buffer = getc(file);
+  btg = 8 - (topos & 0x07);
+  bytesWritten = 0;
+}
+
+long stdio_bitio_buffer::addff(long b)
+{
+  if (!btg)
+    {
+      register int val;
+      val = getc(file);
+      if (val == EOF)
+	{
+	  buffer = 0x0ff;
+	}
+      else
+	{
+	  buffer = val;
+	}
+      btg = sizeof(buffer) * 8;
+    }
+  b = b + b + ((buffer >> --btg) & 0x01);
+
+  return b;
+}
+
+long stdio_bitio_buffer::add00(long b)
+{
+  if (!btg)
+    {
+      register int val;
+      val = getc(file);
+      if (val == EOF)
+	{
+	  buffer = 0xff;
+	}
+      else
+	{
+	  buffer = val;
+	}
+      btg = sizeof(buffer) * 8;
+    }
+  b = b + b + ((buffer >> --btg) & 0x01);
+  
+  return b;
+}
+
+void stdio_bitio_buffer::encodeBit(int b)
+{
+  --btg;
+  if (b != 0)
+    buffer |= (1 << btg);
+
+  if (btg == 0)
+    {
+      putc(buffer, file);
+      bytesWritten += sizeof (buffer);
+      buffer = 0;
+      btg = sizeof(buffer)*8;
+    }
+}
+
+int stdio_bitio_buffer::bit()
+{
+  if (btg == 0)
+    {
+      buffer = getc(file);
+      if (feof(file))
+	{
+	  error();
+	  return 0;
+	}
+      else
+	{
+	  btg = sizeof(buffer) * 8;
+	}
+    }
+  return ((buffer >> --btg) & 0x01);
+}
+
+void stdio_bitio_buffer::done() {
+  // no tidying required
+}
+
+void stdio_bitio_buffer::encodeDone() {
+  if (btg != sizeof(buffer) * 8) {
+    putc(buffer, file);
+    bytesWritten += sizeof (buffer);
+  }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/bitio_m_stdio.h	(revision 16561)
@@ -0,0 +1,101 @@
+/**************************************************************************
+ *
+ * bitio_m_stdio.h -- Macros for bitio to a file
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ *  This file contains macros for doing bitwise input and output on a FILE*.
+ *  With these routines you cannot mix reads and writes on the FILE,
+ *  or multiple writes, at the same time and guarantee them to work, also you 
+ *  cannot seek to a point and do a write. The decode routine can detect when
+ *  you run off the end of the file and will produce an approate error message.
+ *
+ *
+ **************************************************************************/
+
+#ifndef H_BITIO_M_STDIO
+#define H_BITIO_M_STDIO
+
+#include "bitio_m_abstract.h"
+
+
+class stdio_bitio_buffer : public bitio_buffer {
+protected:
+  FILE *file;
+  unsigned char buffer;
+  unsigned char btg;
+  unsigned long bytesWritten;
+  
+public:
+  stdio_bitio_buffer (FILE *f = NULL);
+
+  void attachFile (FILE *f);
+
+  unsigned char GetBtg () { return btg; }
+  unsigned long GetBytesWritten () { return bytesWritten; }
+  
+  void seek (unsigned long byte, unsigned char bit);
+  void seek (unsigned long topos);
+  void error();
+  long add00(long b);
+  long addff(long b);
+  int  bit();
+  int  bitOffset() { return btg; }
+  void done();
+
+  void encodeStart();
+  void encodeBit(int bit);
+  void encodeDone();
+};
+
+
+/*#define ENCODE_START(f)							\
+  {									\
+    stdio_bitio_buffer buffer(f);                                       \
+    buffer.encodeStart();
+
+#define ENCODE_CONTINUE(b)						\
+  {									\
+    stdio_bitio_buffer buffer;                                          \
+    buffer.unpause(b);
+
+#define ENCODE_PAUSE(b) buffer.pause(&b);}
+
+#define ENCODE_DONE buffer.encodeDone();}
+
+#define DECODE_START(f)							\
+  {		                                                        \
+    stdio_bitio_buffer buffer(f);
+
+#define DECODE_CONTINUE(b)						\
+  {									\
+    stdio_bitio_buffer buffer;                                          \
+     buffer.unpause(b);
+
+#define DECODE_ADD_FF(b) b = buffer.addff(b)
+
+#define DECODE_ADD_00(b) b = buffer.add00(b)
+
+#define DECODE_BIT buffer.bit()
+
+#define DECODE_DONE buffer.done();}
+
+#define DECODE_PAUSE(b) buffer.pause(&b);}
+*/
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/ftruncate.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/ftruncate.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/ftruncate.cpp	(revision 16561)
@@ -0,0 +1,99 @@
+/* ftruncate emulations that work on some System V's.
+   This file is in the public domain.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef F_CHSIZE
+
+int
+ftruncate (int fd, off_t length)
+{
+  return fcntl (fd, F_CHSIZE, length);
+}
+
+#else /* not F_CHSIZE */
+#ifdef F_FREESP
+
+/* By William Kucharski <kucharsk@netcom.com>.  */
+
+#include <sys/stat.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int
+ftruncate (int fd, off_t length)
+{
+  struct flock fl;
+  struct stat filebuf;
+
+  if (fstat (fd, &filebuf) < 0)
+    return -1;
+
+  if (filebuf.st_size < length)
+    {
+      /* Extend file length. */
+      if (lseek (fd, (length - 1), SEEK_SET) < 0)
+	return -1;
+
+      /* Write a "0" byte. */
+      if (write (fd, "", 1) != 1)
+	return -1;
+    }
+  else
+    {
+
+      /* Truncate length. */
+
+      fl.l_whence = 0;
+      fl.l_len = 0;
+      fl.l_start = length;
+      fl.l_type = F_WRLCK;	/* write lock on file space */
+
+      /* This relies on the *undocumented* F_FREESP argument to fcntl,
+	 which truncates the file so that it ends at the position
+	 indicated by fl.l_start.  Will minor miracles never cease?  */
+
+      if (fcntl (fd, F_FREESP, &fl) < 0)
+	return -1;
+    }
+
+  return 0;
+}
+
+#else /* not F_CHSIZE nor F_FREESP */
+#ifdef HAVE_CHSIZE
+
+int
+ftruncate (int fd, off_t length)
+{
+  return chsize (fd, length);
+}
+
+#else /* not F_CHSIZE nor F_FREESP nor HAVE_CHSIZE */
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+int
+ftruncate (int fd, off_t length)
+{
+  errno = EIO;
+  return -1;
+}
+
+#endif /* not HAVE_CHSIZE */
+#endif /* not F_FREESP */
+#endif /* not F_CHSIZE */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.c	(revision 16561)
@@ -0,0 +1,748 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+   Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+
+
+#if !__STDC__ && !defined(const) && IN_GCC
+#define const
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif /* GNU C library.  */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+   long-named option.  Because this is not POSIX.2 compliant, it is
+   being phased out.  */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+/* jrm21 - GSDL: getopt.h -> getopt_old.h */
+#include "getopt_old.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+  {
+    REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+  }
+ordering;
+
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif /* GNU C library.  */
+
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  int option_index;
+
+  optarg = 0;
+
+  /* Initialize the internal data when the first call is made.
+     Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  if (optind == 0)
+    {
+      first_nonopt = last_nonopt = optind = 1;
+
+      nextchar = NULL;
+
+      /* Determine how to handle the ordering of options and nonoptions.  */
+
+      if (optstring[0] == '-')
+	{
+	  ordering = RETURN_IN_ORDER;
+	  ++optstring;
+	}
+      else if (optstring[0] == '+')
+	{
+	  ordering = REQUIRE_ORDER;
+	  ++optstring;
+	}
+      else if (getenv ("POSIXLY_CORRECT") != NULL)
+	ordering = REQUIRE_ORDER;
+      else
+	ordering = PERMUTE;
+    }
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Now skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc
+		 && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+		 && (longopts == NULL
+		     || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+	    )
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* Special ARGV-element `--' means premature end of options.
+         Skip it like a null option,
+         then exchange with previous non-options as if it were an option,
+         then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+         and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return EOF;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+         either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+	  && (longopts == NULL
+	      || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+	)
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return EOF;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+         Start decoding its characters.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  if (longopts != NULL
+      && ((argv[optind][0] == '-'
+	   && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+	  || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+      ))
+    {
+      const struct option *p;
+      char *s = nextchar;
+      int exact = 0;
+      int ambig = 0;
+      const struct option *pfound = NULL;
+      int indfound;
+
+      while (*s && *s != '=')
+	s++;
+
+      /* Test all options for either exact match or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name;
+	   p++, option_index++)
+	if (!strncmp (p->name, nextchar, s - nextchar))
+	  {
+	    if (s - nextchar == strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, "%s: option `%s' is ambiguous\n",
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*s)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+	         allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = s + 1;
+	      else
+		{
+		  if (opterr)
+		    {
+		      if (argv[optind - 1][1] == '-')
+			/* --option */
+			fprintf (stderr,
+			    "%s: option `--%s' doesn't allow an argument\n",
+				 argv[0], pfound->name);
+		      else
+			/* +option or -option */
+			fprintf (stderr,
+			    "%s: option `%c%s' doesn't allow an argument\n",
+				 argv[0], argv[optind - 1][0], pfound->name);
+		    }
+		  nextchar += strlen (nextchar);
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr, "%s: option `%s' requires an argument\n",
+			     argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+         or the option starts with '--' or is not a valid short
+         option, then it's an error.
+         Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+	  || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, "%s: unrecognized option `--%s'\n",
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+#if 0
+	    if (c < 040 || c >= 0177)
+	      fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+		       argv[0], c);
+	    else
+	      fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+	    /* 1003.2 specifies the format of this message.  */
+	    fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+	  }
+	optopt = c;
+	return '?';
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = 0;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+#if 0
+		    fprintf (stderr, "%s: option `-%c' requires an argument\n",
+			     argv[0], c);
+#else
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr, "%s: option requires an argument -- %c\n",
+			     argv[0], c);
+#endif
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+	         increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/getopt_old.h	(revision 16561)
@@ -0,0 +1,134 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   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, 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef HAVE_DECL_GETOPT
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+  extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+  extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+  extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+  extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument          (or 0) if the option does not take an argument,
+   required_argument    (or 1) if the option requires an argument,
+   optional_argument    (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+  struct option
+    {
+#if	__STDC__
+      const char *name;
+#else
+      char *name;
+#endif
+      /* has_arg can't be an enum because some compilers complain about
+         type mismatches in all the code that assumes it is an int.  */
+      int has_arg;
+      int *flag;
+      int val;
+    };
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define	no_argument		0
+#define required_argument	1
+#define optional_argument	2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+  extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else				/* not __GNU_LIBRARY__ */
+  int getopt (int, char * const *, const char *);
+#endif				/* not __GNU_LIBRARY__ */
+  extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+			  const struct option *longopts, int *longind);
+  extern int getopt_long_only (int argc, char *const *argv,
+			       const char *shortopts,
+			       const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+  extern int _getopt_internal (int argc, char *const *argv,
+			       const char *shortopts,
+			       const struct option *longopts, int *longind,
+			       int long_only);
+#else				/* not __STDC__ */
+  extern int getopt ();
+  extern int getopt_long ();
+  extern int getopt_long_only ();
+
+  extern int _getopt_internal ();
+#endif				/* not __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif				/* _GETOPT_H */
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/getpagesize.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/getpagesize.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/getpagesize.h	(revision 16561)
@@ -0,0 +1,38 @@
+/* Emulate getpagesize on systems that lack it.  */
+
+#ifndef HAVE_GETPAGESIZE
+
+#ifdef VMS
+#define getpagesize() 512
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _SC_PAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#else
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifndef __WIN32__
+#include <sys/param.h>
+#endif
+
+#ifdef EXEC_PAGESIZE
+#define getpagesize() EXEC_PAGESIZE
+#else
+#ifdef NBPG
+#define getpagesize() NBPG * CLSIZE
+#ifndef CLSIZE
+#define CLSIZE 1
+#endif /* no CLSIZE */
+#else /* no NBPG */
+#ifdef NBPC
+#define getpagesize() NBPC
+#endif /* NBPC */
+#endif /* no NBPG */
+#endif /* no EXEC_PAGESIZE */
+#endif /* no _SC_PAGESIZE */
+
+#endif /* not HAVE_GETPAGESIZE */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/gmalloc.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/gmalloc.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/gmalloc.c	(revision 16561)
@@ -0,0 +1,1318 @@
+/* DO NOT EDIT THIS FILE -- it is automagically generated.  -*- C -*- */
+
+#define _MALLOC_INTERNAL
+
+/* The malloc headers and source files from the C library follow here.  */
+
+/* Declarations for `malloc' and friends.
+   Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef _MALLOC_H
+
+#define _MALLOC_H	1
+
+#ifdef _MALLOC_INTERNAL
+
+#ifdef	HAVE_CONFIG_H
+#include <sysfuncs.h>
+#endif
+
+#if	defined(_LIBC) || defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#else
+#ifndef memset
+#define	memset(s, zero, n)	bzero ((s), (n))
+#endif
+#ifndef memcpy
+#define	memcpy(d, s, n)		bcopy ((s), (d), (n))
+#endif
+#endif
+
+#if	defined(__GNU_LIBRARY__) || __STDC__
+#include <limits.h>
+#else
+#define	CHAR_BIT	8
+#endif
+
+#ifdef	HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#endif	/* _MALLOC_INTERNAL.  */
+
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+#if defined (__cplusplus) || __STDC__
+#undef	__P
+#define	__P(args)	args
+#undef	__ptr_t
+#define	__ptr_t		void *
+#else /* Not C++ or ANSI C.  */
+#undef	__P
+#define	__P(args)	()
+#undef	const
+#define	const
+#undef	__ptr_t
+#define	__ptr_t		char *
+#endif /* C++ or ANSI C.  */
+
+#if	__STDC__
+#include <stddef.h>
+#else
+#undef	size_t
+#define	size_t		unsigned int
+#undef	ptrdiff_t
+#define	ptrdiff_t	int
+#endif
+
+#ifndef	NULL
+#define	NULL	0
+#endif
+
+
+/* Allocate SIZE bytes of memory.  */
+extern __ptr_t malloc __P ((size_t __size));
+/* Re-allocate the previously allocated block
+   in __ptr_t, making the new block SIZE bytes long.  */
+extern __ptr_t realloc __P ((__ptr_t __ptr, size_t __size));
+/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0.  */
+extern __ptr_t calloc __P ((size_t __nmemb, size_t __size));
+/* Free a block allocated by `malloc', `realloc' or `calloc'.  */
+extern void free __P ((__ptr_t __ptr));
+
+/* Allocate SIZE bytes allocated to ALIGNMENT bytes.  */
+extern __ptr_t memalign __P ((size_t __alignment, size_t __size));
+
+/* Allocate SIZE bytes on a page boundary.  */
+extern __ptr_t valloc __P ((size_t __size));
+
+
+#ifdef _MALLOC_INTERNAL
+
+/* The allocator divides the heap into blocks of fixed size; large
+   requests receive one or more whole blocks, and small requests
+   receive a fragment of a block.  Fragment sizes are powers of two,
+   and all fragments of a block are the same size.  When all the
+   fragments in a block have been freed, the block itself is freed.  */
+#define INT_BIT		(CHAR_BIT * sizeof(int))
+#define BLOCKLOG	(INT_BIT > 16 ? 12 : 9)
+#define BLOCKSIZE	(1 << BLOCKLOG)
+#define BLOCKIFY(SIZE)	(((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
+
+/* Determine the amount of memory spanned by the initial heap table
+   (not an absolute limit).  */
+#define HEAP		(INT_BIT > 16 ? 4194304 : 65536)
+
+/* Number of contiguous free blocks allowed to build up at the end of
+   memory before they will be returned to the system.  */
+#define FINAL_FREE_BLOCKS	8
+
+/* Data structure giving per-block information.  */
+typedef union
+  {
+    /* Heap information for a busy block.  */
+    struct
+      {
+	/* Zero for a large block, or positive giving the
+	   logarithm to the base two of the fragment size.  */
+	int type;
+	union
+	  {
+	    struct
+	      {
+		size_t nfree;	/* Free fragments in a fragmented block.  */
+		size_t first;	/* First free fragment of the block.  */
+	      } frag;
+	    /* Size (in blocks) of a large cluster.  */
+	    size_t size;
+	  } info;
+      } busy;
+    /* Heap information for a free block
+       (that may be the first of a free cluster).  */
+    struct
+      {
+	size_t size;		/* Size (in blocks) of a free cluster.  */
+	size_t next;		/* Index of next free cluster.  */
+	size_t prev;		/* Index of previous free cluster.  */
+      } free;
+  } malloc_info;
+
+/* Pointer to first block of the heap.  */
+extern char *_heapbase;
+
+/* Table indexed by block number giving per-block information.  */
+extern malloc_info *_heapinfo;
+
+/* Address to block number and vice versa.  */
+#define BLOCK(A)	(((char *) (A) - _heapbase) / BLOCKSIZE + 1)
+#define ADDRESS(B)	((__ptr_t) (((B) - 1) * BLOCKSIZE + _heapbase))
+
+/* Current search index for the heap table.  */
+extern size_t _heapindex;
+
+/* Limit of valid info table indices.  */
+extern size_t _heaplimit;
+
+/* Doubly linked lists of free fragments.  */
+struct list
+  {
+    struct list *next;
+    struct list *prev;
+  };
+
+/* Free list headers for each fragment size.  */
+extern struct list _fraghead[];
+
+/* List of blocks allocated with `memalign' (or `valloc').  */
+struct alignlist
+  {
+    struct alignlist *next;
+    __ptr_t aligned;		/* The address that memaligned returned.  */
+    __ptr_t exact;		/* The address that malloc returned.  */
+  };
+extern struct alignlist *_aligned_blocks;
+
+/* Instrumentation.  */
+extern size_t _chunks_used;
+extern size_t _bytes_used;
+extern size_t _chunks_free;
+extern size_t _bytes_free;
+
+/* Internal version of `free' used in `morecore' (malloc.c). */
+extern void _free_internal __P ((__ptr_t __ptr));
+
+#endif /* _MALLOC_INTERNAL.  */
+
+/* Underlying allocation function; successive calls should
+   return contiguous pieces of memory.  */
+extern __ptr_t (*__morecore) __P ((ptrdiff_t __size));
+
+/* Default value of `__morecore'.  */
+extern __ptr_t __default_morecore __P ((ptrdiff_t __size));
+
+/* If not NULL, this function is called after each time
+   `__morecore' is called to increase the data size.  */
+extern void (*__after_morecore_hook) __P ((void));
+
+/* Nonzero if `malloc' has been called and done its initialization.  */
+extern int __malloc_initialized;
+
+/* Hooks for debugging versions.  */
+extern void (*__free_hook) __P ((__ptr_t __ptr));
+extern __ptr_t (*__malloc_hook) __P ((size_t __size));
+extern __ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
+
+/* Return values for `mprobe': these are the kinds of inconsistencies that
+   `mcheck' enables detection of.  */
+enum mcheck_status
+  {
+    MCHECK_DISABLED = -1,	/* Consistency checking is not turned on.  */
+    MCHECK_OK,			/* Block is fine.  */
+    MCHECK_FREE,		/* Block freed twice.  */
+    MCHECK_HEAD,		/* Memory before the block was clobbered.  */
+    MCHECK_TAIL			/* Memory after the block was clobbered.  */
+  };
+
+/* Activate a standard collection of debugging hooks.  This must be called
+   before `malloc' is ever called.  ABORTFUNC is called with an error code
+   (see enum above) when an inconsistency is detected.  If ABORTFUNC is
+   null, the standard function prints on stderr and then calls `abort'.  */
+extern int mcheck __P ((void (*__abortfunc) __P ((enum mcheck_status))));
+
+/* Check for aberrations in a particular malloc'd block.  You must have
+   called `mcheck' already.  These are the same checks that `mcheck' does
+   when you free or reallocate a block.  */
+extern enum mcheck_status mprobe __P ((__ptr_t __ptr));
+
+/* Activate a standard collection of tracing hooks.  */
+extern void mtrace __P ((void));
+
+/* Statistics available to the user.  */
+struct mstats
+  {
+    size_t bytes_total;		/* Total size of the heap. */
+    size_t chunks_used;		/* Chunks allocated by the user. */
+    size_t bytes_used;		/* Byte total of user-allocated chunks. */
+    size_t chunks_free;		/* Chunks in the free list. */
+    size_t bytes_free;		/* Byte total of chunks in the free list. */
+  };
+
+/* Pick up the current statistics. */
+extern struct mstats mstats __P ((void));
+
+/* Call WARNFUN with a warning message when memory usage is high.  */
+extern void memory_warnings __P ((__ptr_t __start,
+				  void (*__warnfun) __P ((const char *))));
+
+
+/* Relocating allocator.  */
+
+/* Allocate SIZE bytes, and store the address in *HANDLEPTR.  */
+extern __ptr_t r_alloc __P ((__ptr_t *__handleptr, size_t __size));
+
+/* Free the storage allocated in HANDLEPTR.  */
+extern void r_alloc_free __P ((__ptr_t *__handleptr));
+
+/* Adjust the block at HANDLEPTR to be SIZE bytes long.  */
+extern __ptr_t r_re_alloc __P ((__ptr_t *__handleptr, size_t __size));
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* malloc.h  */
+/* Allocate memory on a page boundary.
+   Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#if defined (__GNU_LIBRARY__) || defined (_LIBC)
+#include <stddef.h>
+#include <sys/cdefs.h>
+extern size_t __getpagesize __P ((void));
+#else
+#include "getpagesize.h"
+#define	 __getpagesize()	getpagesize()
+#endif
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+static size_t pagesize;
+
+__ptr_t
+valloc (size)
+     size_t size;
+{
+  if (pagesize == 0)
+    pagesize = __getpagesize ();
+
+  return memalign (pagesize, size);
+}
+/* Memory allocator `malloc'.
+   Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* How to really get more memory.  */
+__ptr_t (*__morecore) __P ((ptrdiff_t __size)) = __default_morecore;
+
+/* Debugging hook for `malloc'.  */
+__ptr_t (*__malloc_hook) __P ((size_t __size));
+
+/* Pointer to the base of the first block.  */
+char *_heapbase;
+
+/* Block information table.  Allocated with align/__free (not malloc/free).  */
+malloc_info *_heapinfo;
+
+/* Number of info entries.  */
+static size_t heapsize;
+
+/* Search index in the info table.  */
+size_t _heapindex;
+
+/* Limit of valid info table indices.  */
+size_t _heaplimit;
+
+/* Free lists for each fragment size.  */
+struct list _fraghead[BLOCKLOG];
+
+/* Instrumentation.  */
+size_t _chunks_used;
+size_t _bytes_used;
+size_t _chunks_free;
+size_t _bytes_free;
+
+/* Are you experienced?  */
+int __malloc_initialized;
+
+void (*__after_morecore_hook) __P ((void));
+
+/* Aligned allocation.  */
+static __ptr_t align __P ((size_t));
+static __ptr_t
+align (size)
+     size_t size;
+{
+  __ptr_t result;
+  unsigned long int adj;
+
+  result = (*__morecore) (size);
+  adj = (unsigned long int) ((unsigned long int) ((char *) result -
+						  (char *) NULL)) % BLOCKSIZE;
+  if (adj != 0)
+    {
+      adj = BLOCKSIZE - adj;
+      (void) (*__morecore) (adj);
+      result = (char *) result + adj;
+    }
+
+  if (__after_morecore_hook)
+    (*__after_morecore_hook) ();
+
+  return result;
+}
+
+/* Set everything up and remember that we have.  */
+static int initialize __P ((void));
+static int
+initialize ()
+{
+  heapsize = HEAP / BLOCKSIZE;
+  _heapinfo = (malloc_info *) align (heapsize * sizeof (malloc_info));
+  if (_heapinfo == NULL)
+    return 0;
+  memset (_heapinfo, 0, heapsize * sizeof (malloc_info));
+  _heapinfo[0].free.size = 0;
+  _heapinfo[0].free.next = _heapinfo[0].free.prev = 0;
+  _heapindex = 0;
+  _heapbase = (char *) _heapinfo;
+
+  /* Account for the _heapinfo block itself in the statistics.  */
+  _bytes_used = heapsize * sizeof (malloc_info);
+  _chunks_used = 1;
+
+  __malloc_initialized = 1;
+  return 1;
+}
+
+/* Get neatly aligned memory, initializing or
+   growing the heap info table as necessary. */
+static __ptr_t morecore __P ((size_t));
+static __ptr_t
+morecore (size)
+     size_t size;
+{
+  __ptr_t result;
+  malloc_info *newinfo, *oldinfo;
+  size_t newsize;
+
+  result = align (size);
+  if (result == NULL)
+    return NULL;
+
+  /* Check if we need to grow the info table.  */
+  if ((size_t) BLOCK ((char *) result + size) > heapsize)
+    {
+      newsize = heapsize;
+      while ((size_t) BLOCK ((char *) result + size) > newsize)
+	newsize *= 2;
+      newinfo = (malloc_info *) align (newsize * sizeof (malloc_info));
+      if (newinfo == NULL)
+	{
+	  (*__morecore) (-size);
+	  return NULL;
+	}
+      memcpy (newinfo, _heapinfo, heapsize * sizeof (malloc_info));
+      memset (&newinfo[heapsize], 0,
+	      (newsize - heapsize) * sizeof (malloc_info));
+      oldinfo = _heapinfo;
+      newinfo[BLOCK (oldinfo)].busy.type = 0;
+      newinfo[BLOCK (oldinfo)].busy.info.size
+	= BLOCKIFY (heapsize * sizeof (malloc_info));
+      _heapinfo = newinfo;
+      /* Account for the _heapinfo block itself in the statistics.  */
+      _bytes_used += newsize * sizeof (malloc_info);
+      ++_chunks_used;
+      _free_internal (oldinfo);
+      heapsize = newsize;
+    }
+
+  _heaplimit = BLOCK ((char *) result + size);
+  return result;
+}
+
+/* Allocate memory from the heap.  */
+__ptr_t
+malloc (size)
+     size_t size;
+{
+  __ptr_t result;
+  size_t block, blocks, lastblocks, start;
+  register size_t i;
+  struct list *next;
+
+  /* ANSI C allows `malloc (0)' to either return NULL, or to return a
+     valid address you can realloc and free (though not dereference).
+
+     It turns out that some extant code (sunrpc, at least Ultrix's version)
+     expects `malloc (0)' to return non-NULL and breaks otherwise.
+     Be compatible.  */
+
+#if	0
+  if (size == 0)
+    return NULL;
+#endif
+
+  if (__malloc_hook != NULL)
+    return (*__malloc_hook) (size);
+
+  if (!__malloc_initialized)
+    if (!initialize ())
+      return NULL;
+
+  if (size < sizeof (struct list))
+      size = sizeof (struct list);
+
+#ifdef SUNOS_LOCALTIME_BUG
+  if (size < 16)
+    size = 16;
+#endif
+
+  /* Determine the allocation policy based on the request size.  */
+  if (size <= BLOCKSIZE / 2)
+    {
+      /* Small allocation to receive a fragment of a block.
+	 Determine the logarithm to base two of the fragment size. */
+      register size_t log = 1;
+      --size;
+      while ((size /= 2) != 0)
+	++log;
+
+      /* Look in the fragment lists for a
+	 free fragment of the desired size. */
+      next = _fraghead[log].next;
+      if (next != NULL)
+	{
+	  /* There are free fragments of this size.
+	     Pop a fragment out of the fragment list and return it.
+	     Update the block's nfree and first counters. */
+	  result = (__ptr_t) next;
+	  next->prev->next = next->next;
+	  if (next->next != NULL)
+	    next->next->prev = next->prev;
+	  block = BLOCK (result);
+	  if (--_heapinfo[block].busy.info.frag.nfree != 0)
+	    _heapinfo[block].busy.info.frag.first = (unsigned long int)
+	      ((unsigned long int) ((char *) next->next - (char *) NULL)
+	       % BLOCKSIZE) >> log;
+
+	  /* Update the statistics.  */
+	  ++_chunks_used;
+	  _bytes_used += 1 << log;
+	  --_chunks_free;
+	  _bytes_free -= 1 << log;
+	}
+      else
+	{
+	  /* No free fragments of the desired size, so get a new block
+	     and break it into fragments, returning the first.  */
+	  result = malloc (BLOCKSIZE);
+	  if (result == NULL)
+	    return NULL;
+
+	  /* Link all fragments but the first into the free list.  */
+	  for (i = 1; i < (size_t) (BLOCKSIZE >> log); ++i)
+	    {
+	      next = (struct list *) ((char *) result + (i << log));
+	      next->next = _fraghead[log].next;
+	      next->prev = &_fraghead[log];
+	      next->prev->next = next;
+	      if (next->next != NULL)
+		next->next->prev = next;
+	    }
+
+	  /* Initialize the nfree and first counters for this block.  */
+	  block = BLOCK (result);
+	  _heapinfo[block].busy.type = log;
+	  _heapinfo[block].busy.info.frag.nfree = i - 1;
+	  _heapinfo[block].busy.info.frag.first = i - 1;
+
+	  _chunks_free += (BLOCKSIZE >> log) - 1;
+	  _bytes_free += BLOCKSIZE - (1 << log);
+	  _bytes_used -= BLOCKSIZE - (1 << log);
+	}
+    }
+  else
+    {
+      /* Large allocation to receive one or more blocks.
+	 Search the free list in a circle starting at the last place visited.
+	 If we loop completely around without finding a large enough
+	 space we will have to get more memory from the system.  */
+      blocks = BLOCKIFY (size);
+      start = block = _heapindex;
+      while (_heapinfo[block].free.size < blocks)
+	{
+	  block = _heapinfo[block].free.next;
+	  if (block == start)
+	    {
+	      /* Need to get more from the system.  Check to see if
+		 the new core will be contiguous with the final free
+		 block; if so we don't need to get as much.  */
+	      block = _heapinfo[0].free.prev;
+	      lastblocks = _heapinfo[block].free.size;
+	      if (_heaplimit != 0 && block + lastblocks == _heaplimit &&
+		  (*__morecore) (0) == ADDRESS (block + lastblocks) &&
+		  (morecore ((blocks - lastblocks) * BLOCKSIZE)) != NULL)
+		{
+ 		  /* Which block we are extending (the `final free
+ 		     block' referred to above) might have changed, if
+ 		     it got combined with a freed info table.  */
+ 		  block = _heapinfo[0].free.prev;
+  		  _heapinfo[block].free.size += (blocks - lastblocks);
+		  _bytes_free += (blocks - lastblocks) * BLOCKSIZE;
+		  continue;
+		}
+	      result = morecore (blocks * BLOCKSIZE);
+	      if (result == NULL)
+		return NULL;
+	      block = BLOCK (result);
+	      _heapinfo[block].busy.type = 0;
+	      _heapinfo[block].busy.info.size = blocks;
+	      ++_chunks_used;
+	      _bytes_used += blocks * BLOCKSIZE;
+	      return result;
+	    }
+	}
+
+      /* At this point we have found a suitable free list entry.
+	 Figure out how to remove what we need from the list. */
+      result = ADDRESS (block);
+      if (_heapinfo[block].free.size > blocks)
+	{
+	  /* The block we found has a bit left over,
+	     so relink the tail end back into the free list. */
+	  _heapinfo[block + blocks].free.size
+	    = _heapinfo[block].free.size - blocks;
+	  _heapinfo[block + blocks].free.next
+	    = _heapinfo[block].free.next;
+	  _heapinfo[block + blocks].free.prev
+	    = _heapinfo[block].free.prev;
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapindex = block + blocks;
+	}
+      else
+	{
+	  /* The block exactly matches our requirements,
+	     so just remove it from the list. */
+	  _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapinfo[block].free.prev;
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapindex = _heapinfo[block].free.next;
+	  --_chunks_free;
+	}
+
+      _heapinfo[block].busy.type = 0;
+      _heapinfo[block].busy.info.size = blocks;
+      ++_chunks_used;
+      _bytes_used += blocks * BLOCKSIZE;
+      _bytes_free -= blocks * BLOCKSIZE;
+    }
+
+  return result;
+}
+
+
+#ifndef _LIBC
+
+/* On some ANSI C systems, some libc functions call _malloc, _free
+   and _realloc.  Make them use the GNU functions.  */
+
+__ptr_t
+_malloc (size)
+     size_t size;
+{
+  return malloc (size);
+}
+
+void
+_free (ptr)
+     __ptr_t ptr;
+{
+  free (ptr);
+}
+
+__ptr_t
+_realloc (ptr, size)
+     __ptr_t ptr;
+     size_t size;
+{
+  return realloc (ptr, size);
+}
+
+#endif
+/* Free a block of memory allocated by `malloc'.
+   Copyright 1990, 1991, 1992 Free Software Foundation
+		  Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* Debugging hook for free.  */
+void (*__free_hook) __P ((__ptr_t __ptr));
+
+/* List of blocks allocated by memalign.  */
+struct alignlist *_aligned_blocks = NULL;
+
+/* Return memory to the heap.
+   Like `free' but don't call a __free_hook if there is one.  */
+void
+_free_internal (ptr)
+     __ptr_t ptr;
+{
+  int type;
+  size_t block, blocks;
+  register size_t i;
+  struct list *prev, *next;
+
+  block = BLOCK (ptr);
+
+  type = _heapinfo[block].busy.type;
+  switch (type)
+    {
+    case 0:
+      /* Get as many statistics as early as we can.  */
+      --_chunks_used;
+      _bytes_used -= _heapinfo[block].busy.info.size * BLOCKSIZE;
+      _bytes_free += _heapinfo[block].busy.info.size * BLOCKSIZE;
+
+      /* Find the free cluster previous to this one in the free list.
+	 Start searching at the last block referenced; this may benefit
+	 programs with locality of allocation.  */
+      i = _heapindex;
+      if (i > block)
+	while (i > block)
+	  i = _heapinfo[i].free.prev;
+      else
+	{
+	  do
+	    i = _heapinfo[i].free.next;
+	  while (i > 0 && i < block);
+	  i = _heapinfo[i].free.prev;
+	}
+
+      /* Determine how to link this block into the free list.  */
+      if (block == i + _heapinfo[i].free.size)
+	{
+	  /* Coalesce this block with its predecessor.  */
+	  _heapinfo[i].free.size += _heapinfo[block].busy.info.size;
+	  block = i;
+	}
+      else
+	{
+	  /* Really link this block back into the free list.  */
+	  _heapinfo[block].free.size = _heapinfo[block].busy.info.size;
+	  _heapinfo[block].free.next = _heapinfo[i].free.next;
+	  _heapinfo[block].free.prev = i;
+	  _heapinfo[i].free.next = block;
+	  _heapinfo[_heapinfo[block].free.next].free.prev = block;
+	  ++_chunks_free;
+	}
+
+      /* Now that the block is linked in, see if we can coalesce it
+	 with its successor (by deleting its successor from the list
+	 and adding in its size).  */
+      if (block + _heapinfo[block].free.size == _heapinfo[block].free.next)
+	{
+	  _heapinfo[block].free.size
+	    += _heapinfo[_heapinfo[block].free.next].free.size;
+	  _heapinfo[block].free.next
+	    = _heapinfo[_heapinfo[block].free.next].free.next;
+	  _heapinfo[_heapinfo[block].free.next].free.prev = block;
+	  --_chunks_free;
+	}
+
+      /* Now see if we can return stuff to the system.  */
+      blocks = _heapinfo[block].free.size;
+      if (blocks >= FINAL_FREE_BLOCKS && block + blocks == _heaplimit
+	  && (*__morecore) (0) == ADDRESS (block + blocks))
+	{
+	  register size_t bytes = blocks * BLOCKSIZE;
+	  _heaplimit -= blocks;
+	  (*__morecore) (-bytes);
+	  _heapinfo[_heapinfo[block].free.prev].free.next
+	    = _heapinfo[block].free.next;
+	  _heapinfo[_heapinfo[block].free.next].free.prev
+	    = _heapinfo[block].free.prev;
+	  block = _heapinfo[block].free.prev;
+	  --_chunks_free;
+	  _bytes_free -= bytes;
+	}
+
+      /* Set the next search to begin at this block.  */
+      _heapindex = block;
+      break;
+
+    default:
+      /* Do some of the statistics.  */
+      --_chunks_used;
+      _bytes_used -= 1 << type;
+      ++_chunks_free;
+      _bytes_free += 1 << type;
+
+      /* Get the address of the first free fragment in this block.  */
+      prev = (struct list *) ((char *) ADDRESS (block) +
+			   (_heapinfo[block].busy.info.frag.first << type));
+
+      if (_heapinfo[block].busy.info.frag.nfree == (BLOCKSIZE >> type) - 1)
+	{
+	  /* If all fragments of this block are free, remove them
+	     from the fragment list and free the whole block.  */
+	  next = prev;
+	  for (i = 1; i < (size_t) (BLOCKSIZE >> type); ++i)
+	    next = next->next;
+	  prev->prev->next = next;
+	  if (next != NULL)
+	    next->prev = prev->prev;
+	  _heapinfo[block].busy.type = 0;
+	  _heapinfo[block].busy.info.size = 1;
+
+	  /* Keep the statistics accurate.  */
+	  ++_chunks_used;
+	  _bytes_used += BLOCKSIZE;
+	  _chunks_free -= BLOCKSIZE >> type;
+	  _bytes_free -= BLOCKSIZE;
+
+	  free (ADDRESS (block));
+	}
+      else if (_heapinfo[block].busy.info.frag.nfree != 0)
+	{
+	  /* If some fragments of this block are free, link this
+	     fragment into the fragment list after the first free
+	     fragment of this block. */
+	  next = (struct list *) ptr;
+	  next->next = prev->next;
+	  next->prev = prev;
+	  prev->next = next;
+	  if (next->next != NULL)
+	    next->next->prev = next;
+	  ++_heapinfo[block].busy.info.frag.nfree;
+	}
+      else
+	{
+	  /* No fragments of this block are free, so link this
+	     fragment into the fragment list and announce that
+	     it is the first free fragment of this block. */
+	  prev = (struct list *) ptr;
+	  _heapinfo[block].busy.info.frag.nfree = 1;
+	  _heapinfo[block].busy.info.frag.first = (unsigned long int)
+	    ((unsigned long int) ((char *) ptr - (char *) NULL)
+	     % BLOCKSIZE >> type);
+	  prev->next = _fraghead[type].next;
+	  prev->prev = &_fraghead[type];
+	  prev->prev->next = prev;
+	  if (prev->next != NULL)
+	    prev->next->prev = prev;
+	}
+      break;
+    }
+}
+
+/* Return memory to the heap.  */
+void
+free (ptr)
+     __ptr_t ptr;
+{
+  register struct alignlist *l;
+
+  if (ptr == NULL)
+    return;
+
+  for (l = _aligned_blocks; l != NULL; l = l->next)
+    if (l->aligned == ptr)
+      {
+	l->aligned = NULL;	/* Mark the slot in the list as free.  */
+	ptr = l->exact;
+	break;
+      }
+
+  if (__free_hook != NULL)
+    (*__free_hook) (ptr);
+  else
+    _free_internal (ptr);
+}
+/* Copyright (C) 1991, 1993, 1994 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#ifdef _LIBC
+
+#include <ansidecl.h>
+#include <gnu-stabs.h>
+
+#undef	cfree
+
+function_alias(cfree, free, void, (ptr),
+	       DEFUN(cfree, (ptr), PTR ptr))
+
+#else
+
+void
+cfree (ptr)
+     __ptr_t ptr;
+{
+  free (ptr);
+}
+
+#endif
+/* Change the size of a block allocated by `malloc'.
+   Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+		     Written May 1989 by Mike Haertel.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#if  (defined (MEMMOVE_MISSING) || \
+      !defined(_LIBC) && !defined(STDC_HEADERS) && !defined(USG))
+
+/* Snarfed directly from Emacs src/dispnew.c:
+   XXX Should use system bcopy if it handles overlap.  */
+#ifndef emacs
+
+/* Like bcopy except never gets confused by overlap.  */
+
+static void
+safe_bcopy (from, to, size)
+     char *from, *to;
+     int size;
+{
+  if (size <= 0 || from == to)
+    return;
+
+  /* If the source and destination don't overlap, then bcopy can
+     handle it.  If they do overlap, but the destination is lower in
+     memory than the source, we'll assume bcopy can handle that.  */
+  if (to < from || from + size <= to)
+    bcopy (from, to, size);
+
+  /* Otherwise, we'll copy from the end.  */
+  else
+    {
+      register char *endf = from + size;
+      register char *endt = to + size;
+
+      /* If TO - FROM is large, then we should break the copy into
+	 nonoverlapping chunks of TO - FROM bytes each.  However, if
+	 TO - FROM is small, then the bcopy function call overhead
+	 makes this not worth it.  The crossover point could be about
+	 anywhere.  Since I don't think the obvious copy loop is too
+	 bad, I'm trying to err in its favor.  */
+      if (to - from < 64)
+	{
+	  do
+	    *--endt = *--endf;
+	  while (endf != from);
+	}
+      else
+	{
+	  for (;;)
+	    {
+	      endt -= (to - from);
+	      endf -= (to - from);
+
+	      if (endt < to)
+		break;
+
+	      bcopy (endf, endt, to - from);
+	    }
+
+	  /* If SIZE wasn't a multiple of TO - FROM, there will be a
+	     little left over.  The amount left over is
+	     (endt + (to - from)) - to, which is endt - from.  */
+	  bcopy (from, to, endt - from);
+	}
+    }
+}     
+#endif	/* Not emacs.  */
+
+#define memmove(to, from, size) safe_bcopy ((from), (to), (size))
+
+#endif
+
+
+#define min(A, B) ((A) < (B) ? (A) : (B))
+
+/* Debugging hook for realloc.  */
+__ptr_t (*__realloc_hook) __P ((__ptr_t __ptr, size_t __size));
+
+/* Resize the given region to the new size, returning a pointer
+   to the (possibly moved) region.  This is optimized for speed;
+   some benchmarks seem to indicate that greater compactness is
+   achieved by unconditionally allocating and copying to a
+   new region.  This module has incestuous knowledge of the
+   internals of both free and malloc. */
+__ptr_t
+realloc (ptr, size)
+     __ptr_t ptr;
+     size_t size;
+{
+  __ptr_t result;
+  int type;
+  size_t block, blocks, oldlimit;
+
+  if (size == 0)
+    {
+      free (ptr);
+      return malloc (0);
+    }
+  else if (ptr == NULL)
+    return malloc (size);
+
+  if (__realloc_hook != NULL)
+    return (*__realloc_hook) (ptr, size);
+
+  block = BLOCK (ptr);
+
+  type = _heapinfo[block].busy.type;
+  switch (type)
+    {
+    case 0:
+      /* Maybe reallocate a large block to a small fragment.  */
+      if (size <= BLOCKSIZE / 2)
+	{
+	  result = malloc (size);
+	  if (result != NULL)
+	    {
+	      memcpy (result, ptr, size);
+	      _free_internal (ptr);
+	      return result;
+	    }
+	}
+
+      /* The new size is a large allocation as well;
+	 see if we can hold it in place. */
+      blocks = BLOCKIFY (size);
+      if (blocks < _heapinfo[block].busy.info.size)
+	{
+	  /* The new size is smaller; return
+	     excess memory to the free list. */
+	  _heapinfo[block + blocks].busy.type = 0;
+	  _heapinfo[block + blocks].busy.info.size
+	    = _heapinfo[block].busy.info.size - blocks;
+	  _heapinfo[block].busy.info.size = blocks;
+	  /* We have just created a new chunk by splitting a chunk in two.
+	     Now we will free this chunk; increment the statistics counter
+	     so it doesn't become wrong when _free_internal decrements it.  */
+	  ++_chunks_used;
+	  _free_internal (ADDRESS (block + blocks));
+	  result = ptr;
+	}
+      else if (blocks == _heapinfo[block].busy.info.size)
+	/* No size change necessary.  */
+	result = ptr;
+      else
+	{
+	  /* Won't fit, so allocate a new region that will.
+	     Free the old region first in case there is sufficient
+	     adjacent free space to grow without moving. */
+	  blocks = _heapinfo[block].busy.info.size;
+	  /* Prevent free from actually returning memory to the system.  */
+	  oldlimit = _heaplimit;
+	  _heaplimit = 0;
+	  _free_internal (ptr);
+	  _heaplimit = oldlimit;
+	  result = malloc (size);
+	  if (result == NULL)
+	    {
+	      /* Now we're really in trouble.  We have to unfree
+		 the thing we just freed.  Unfortunately it might
+		 have been coalesced with its neighbors.  */
+	      if (_heapindex == block)
+	        (void) malloc (blocks * BLOCKSIZE);
+	      else
+		{
+		  __ptr_t previous = malloc ((block - _heapindex) * BLOCKSIZE);
+		  (void) malloc (blocks * BLOCKSIZE);
+		  _free_internal (previous);
+		}
+	      return NULL;
+	    }
+	  if (ptr != result)
+	    memmove (result, ptr, blocks * BLOCKSIZE);
+	}
+      break;
+
+    default:
+      /* Old size is a fragment; type is logarithm
+	 to base two of the fragment size.  */
+      if (size > (size_t) (1 << (type - 1)) && size <= (size_t) (1 << type))
+	/* The new size is the same kind of fragment.  */
+	result = ptr;
+      else
+	{
+	  /* The new size is different; allocate a new space,
+	     and copy the lesser of the new size and the old. */
+	  result = malloc (size);
+	  if (result == NULL)
+	    return NULL;
+	  memcpy (result, ptr, min (size, (size_t) 1 << type));
+	  free (ptr);
+	}
+      break;
+    }
+
+  return result;
+}
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.
+
+   The author may be reached (Email) at the address mike@ai.mit.edu,
+   or (US mail) as Mike Haertel c/o Free Software Foundation.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+/* Allocate an array of NMEMB elements each SIZE bytes long.
+   The entire array is initialized to zeros.  */
+__ptr_t
+calloc (nmemb, size)
+     register size_t nmemb;
+     register size_t size;
+{
+  register __ptr_t result = malloc (nmemb * size);
+
+  if (result != NULL)
+    (void) memset (result, 0, nmemb * size);
+
+  return result;
+}
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library 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, or (at your option)
+any later version.
+
+The GNU C Library 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 the GNU C Library; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define	_MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+#ifndef	__GNU_LIBRARY__
+#define	__sbrk	sbrk
+#endif
+
+#ifdef __GNU_LIBRARY__
+/* It is best not to declare this and cast its result on foreign operating
+   systems with potentially hostile include files.  */
+extern __ptr_t __sbrk __P ((int increment));
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Allocate INCREMENT more bytes of data space,
+   and return the start of data space, or NULL on errors.
+   If INCREMENT is negative, shrink data space.  */
+__ptr_t
+__default_morecore (increment)
+     ptrdiff_t increment;
+{
+  __ptr_t result = (__ptr_t) __sbrk ((int) increment);
+  if (result == (__ptr_t) -1)
+    return NULL;
+  return result;
+}
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#ifndef	_MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#endif
+
+__ptr_t
+memalign (alignment, size)
+     size_t alignment;
+     size_t size;
+{
+  __ptr_t result;
+  unsigned long int adj;
+
+  size = ((size + alignment - 1) / alignment) * alignment;
+
+  result = malloc (size);
+  if (result == NULL)
+    return NULL;
+  adj = (unsigned long int) ((unsigned long int) ((char *) result -
+						  (char *) NULL)) % alignment;
+  if (adj != 0)
+    {
+      struct alignlist *l;
+      for (l = _aligned_blocks; l != NULL; l = l->next)
+	if (l->aligned == NULL)
+	  /* This slot is free.  Use it.  */
+	  break;
+      if (l == NULL)
+	{
+	  l = (struct alignlist *) malloc (sizeof (struct alignlist));
+	  if (l == NULL)
+	    {
+	      free (result);
+	      return NULL;
+	    }
+	  l->next = _aligned_blocks;
+	  _aligned_blocks = l;
+	}
+      l->exact = result;
+      result = l->aligned = (char *) result + alignment - adj;
+    }
+
+  return result;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.cpp	(revision 16561)
@@ -0,0 +1,385 @@
+/**************************************************************************
+ *
+ * huffman.c -- Huffman coding functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "huffman.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+/* ->data on success,  
+   NULL on error 
+ */
+huff_data *
+Generate_Huffman_Data (int num, long *freqs, huff_data * data,
+		       u_long * mem)
+{
+  int HNum, i, count;
+  unsigned long *heap;
+  huff_data *hd = data;
+  if (!hd)
+    {
+      if (!(hd = new huff_data))
+	goto error0;
+    }
+  if (!(heap = new unsigned long[num * 2]))
+    goto error1;
+  if (!(hd->clens = new char[num]))
+      goto error2;
+  hd->num_codes = num;
+  if (mem)
+    {
+      if (hd != data)
+	*mem += sizeof (huff_data);
+      *mem += num * sizeof (char);
+    }
+  memcpy (&heap[num], freqs, num * sizeof (*heap));
+  memset (heap, '\0', num * sizeof (*heap));
+
+  /* Initialise the pointers to the leaves */
+  for (count = i = 0; i < num; ++i)
+    if (heap[num + i])
+      heap[count++] = num + i;
+
+  /* Reorganise the pointers so that it is a heap */
+  HNum = count;
+  for (i = HNum / 2; i > 0; --i)
+    {
+      register int curr, child;
+      curr = i;
+      child = curr * 2;
+      while (child <= HNum)
+	{
+	  if (child < HNum && heap[heap[child]] < heap[heap[child - 1]])
+	    ++child;
+	  if (heap[heap[curr - 1]] > heap[heap[child - 1]])
+	    {
+	      register u_long temp;
+	      temp = heap[child - 1];
+	      heap[child - 1] = heap[curr - 1];
+	      heap[curr - 1] = temp;
+	      curr = child;
+	      child = 2 * curr;
+	    }
+	  else
+	    break;
+	}
+    }
+
+
+  /* Form the huffman tree */
+  while (HNum > 1)
+    {
+      int pos[2];
+
+      for (i = 0; i < 2; ++i)
+	{
+	  register int curr, child;
+	  pos[i] = heap[0];
+	  heap[0] = heap[--HNum];
+	  curr = 1;
+	  child = 2;
+	  while (child <= HNum)
+	    {
+	      if (child < HNum &&
+		  heap[heap[child]] < heap[heap[child - 1]])
+		++child;
+	      if (heap[heap[curr - 1]] > heap[heap[child - 1]])
+		{
+		  register int temp;
+		  temp = heap[child - 1];
+		  heap[child - 1] = heap[curr - 1];
+		  heap[curr - 1] = temp;
+		  curr = child;
+		  child = 2 * curr;
+		}
+	      else
+		break;
+	    }
+	}
+
+      heap[HNum + 1] = heap[pos[0]] + heap[pos[1]];
+      heap[pos[0]] = heap[pos[1]] = HNum + 1;
+
+      heap[HNum] = HNum + 1;
+
+      {
+	register int parent, curr;
+	++HNum;
+	curr = HNum;
+	parent = curr >> 1;
+	while (parent && heap[heap[parent - 1]] > heap[heap[curr - 1]])
+	  {
+	    register int temp;
+	    temp = heap[parent - 1];
+	    heap[parent - 1] = heap[curr - 1];
+	    heap[curr - 1] = temp;
+	    curr = parent;
+	    parent = curr >> 1;
+	  }
+      }
+    }
+
+
+  /* Calculate the code lens */
+  heap[0] = -1UL;
+  heap[1] = 0;
+  for (i = 2; i < num * 2; ++i)
+    heap[i] = heap[heap[i]] + 1;
+
+  /* zero the lencount's array */
+  memset (hd->lencount, '\0', sizeof (hd->lencount));
+  hd->maxcodelen = 0;
+  hd->mincodelen = 100;
+
+  /* Set the code length of each leaf in the huffman tree */
+  for (i = 0; i < num; ++i)
+    {
+      register u_long codelen = heap[i + num];
+      hd->clens[i] = (char) codelen;
+      if (!codelen)
+	continue;
+      if (codelen > hd->maxcodelen)
+	hd->maxcodelen = codelen;
+      if (codelen < hd->mincodelen)
+	hd->mincodelen = codelen;
+      ++hd->lencount[codelen];
+    }
+
+
+  if (hd->maxcodelen == 0 && hd->mincodelen == 100)
+    {
+      hd->num_codes = 0;
+    }
+  else
+    {
+      /* Calculate the current codes for each different code length */
+      hd->min_code[hd->maxcodelen] = 0;
+      for (i = hd->maxcodelen - 1; i>=0; --i)
+	hd->min_code[i] = (hd->min_code[i + 1] + hd->lencount[i + 1]) >> 1;
+    }
+  delete []heap;
+  return (hd);
+
+error2:
+  delete []heap;
+error1:
+  if (!data)
+    delete hd;
+error0:
+  return (NULL);
+
+}
+
+unsigned long *
+Generate_Huffman_Codes (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *codes;
+  unsigned long mc[MAX_HUFFCODE_LEN + 1];
+
+  if (!data)
+    return (NULL);
+  if (!(codes = new unsigned long[data->num_codes]))
+    return (NULL);
+  if (mem)
+    *mem += data->num_codes * sizeof (*codes);
+  memcpy (mc, data->min_code, sizeof (mc));
+  for (i = 0; i < data->num_codes; ++i)
+    if (data->clens[i])
+      codes[i] = mc[(int) (data->clens[i])]++;
+
+  return (codes);
+}
+
+
+unsigned long **
+Generate_Huffman_Vals (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *fcode[MAX_HUFFCODE_LEN + 1];
+  unsigned long **values;
+  unsigned long *vals;
+
+  if (!data)
+    return (NULL);
+  if (!(vals = new unsigned long[data->num_codes]))
+    return (NULL);
+  if (!(values = new unsigned long *[MAX_HUFFCODE_LEN + 1]))
+    {
+      delete []vals;
+      return (NULL);
+    }
+
+  memset (values, '\0', (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *));
+
+  if (mem)
+    *mem += data->num_codes * sizeof (*vals) +
+      (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+  fcode[0] = values[0] = &vals[0];
+  for (i = 1; i <= data->maxcodelen; ++i)
+    fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]];
+
+  for (i = 0; i < data->num_codes; ++i)
+    if (data->clens[i])
+      *fcode[(int) (data->clens[i])]++ = i;
+  return (values);
+}
+
+
+
+double 
+Calculate_Huffman_Size (int num, long *freqs, long *counts)
+{
+  double size = 0;
+  huff_data hd;
+  int i;
+  if (!Generate_Huffman_Data (num, freqs, &hd, NULL))
+    return -1;
+  for (i = 0; i < num; ++i)
+    size += counts[i] * hd.clens[i];
+  delete []hd.clens;
+  return size;
+}
+
+
+
+int 
+Write_Huffman_Data (FILE * f, huff_data * hd)
+{
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONSI(hd->num_codes);
+  HTONSI(hd->mincodelen);
+  HTONSI(hd->maxcodelen);
+
+  if (fwrite (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1)
+    return -1;
+
+  if (fwrite (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1)
+    return -1;
+
+  if (fwrite (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(hd->num_codes);
+  NTOHSI(hd->mincodelen);
+  NTOHSI(hd->maxcodelen);
+
+  if (hd->num_codes)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int i;
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i)
+	HTONSI(hd->lencount[i]);
+      for (i = 0; i < hd->maxcodelen + 1; ++i)
+	HTONUL(hd->min_code[i]);
+
+      if (fwrite (hd->lencount + hd->mincodelen, sizeof (*hd->lencount),
+		  hd->maxcodelen - hd->mincodelen + 1, f) !=
+	  hd->maxcodelen - hd->mincodelen + 1)
+	return -1;
+
+      if (fwrite (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) !=
+	  hd->maxcodelen + 1)
+	return -1;
+
+      if (fwrite (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i)
+	NTOHSI(hd->lencount[i]);
+      for (i = 0; i < hd->maxcodelen + 1; ++i)
+	NTOHUL(hd->min_code[i]);
+    }
+  return 0;
+}
+
+
+
+int Read_Huffman_Data (FILE * f, huff_data * hd, u_long * mem, u_long * disk) {
+  if (fread (&hd->num_codes, sizeof (hd->num_codes), 1, f) != 1)
+    return -1;
+
+  if (fread (&hd->mincodelen, sizeof (hd->mincodelen), 1, f) != 1)
+    return -1;
+
+  if (fread (&hd->maxcodelen, sizeof (hd->maxcodelen), 1, f) != 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(hd->num_codes);
+  NTOHSI(hd->mincodelen);
+  NTOHSI(hd->maxcodelen);
+
+  if (disk)
+    (*disk) += sizeof (hd->num_codes) + sizeof (hd->mincodelen) +
+      sizeof (hd->maxcodelen);
+
+  hd->clens = NULL;
+
+  if (hd->num_codes)
+    {
+      int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+      memset (hd->lencount, '\0', sizeof (hd->lencount));
+      if (fread (hd->lencount + hd->mincodelen, sizeof (*hd->lencount),
+	      hd->maxcodelen - hd->mincodelen + 1, f) !=
+	  hd->maxcodelen - hd->mincodelen + 1)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i)
+	NTOHSI(hd->lencount[i]);
+
+      if (disk)
+	(*disk) += sizeof (*hd->lencount) * (hd->maxcodelen - hd->mincodelen + 1);
+
+      memset (hd->min_code, '\0', sizeof (hd->min_code));
+
+      if (fread (hd->min_code, sizeof (*hd->min_code), hd->maxcodelen + 1, f) !=
+	  hd->maxcodelen + 1)
+	return -1;
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (i = 0; i < hd->maxcodelen + 1; ++i)
+	NTOHUL(hd->min_code[i]);
+
+      if (disk)
+	(*disk) += sizeof (*hd->min_code) * (hd->maxcodelen + 1);
+
+      if (!(hd->clens = new char[hd->num_codes]))
+	return -1;
+
+
+      if (fread (hd->clens, sizeof (*hd->clens), hd->num_codes, f) != hd->num_codes)
+	return -1;
+
+      if (disk)
+	(*disk) += sizeof (*hd->clens) * hd->num_codes;
+    }
+
+  if (mem)
+    *mem += sizeof (*hd->clens) * hd->num_codes;
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/huffman.h	(revision 16561)
@@ -0,0 +1,59 @@
+/**************************************************************************
+ *
+ * huffman.h -- Huffman coding functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_HUFFMAN
+#define H_HUFFMAN
+
+#include <sys/types.h>
+
+#include "sysfuncs.h"
+#include "bitio_m_abstract.h"
+
+#define MAX_HUFFCODE_LEN 31
+
+struct huff_data {
+  int num_codes;
+  int mincodelen;
+  int maxcodelen;
+  int lencount[MAX_HUFFCODE_LEN + 1];
+  unsigned long min_code[MAX_HUFFCODE_LEN + 1];
+  char *clens;
+};
+
+
+huff_data *Generate_Huffman_Data (int num, long *freqs, huff_data * data,
+				  u_long * mem);
+
+unsigned long *Generate_Huffman_Codes (huff_data * data, u_long * mem);
+
+unsigned long **Generate_Huffman_Vals (huff_data * data, u_long * mem);
+
+int Write_Huffman_Data (FILE * f, huff_data * hd);
+
+int Read_Huffman_Data (FILE * f, huff_data * hd, u_long * mem, u_long * disk);
+
+/* Calculate the number of bits required to code the data with the
+   specified frequencies. Normally freqs and counts should point to
+   the same array. */
+double Calculate_Huffman_Size (int num, long *freqs, long *counts);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.cpp	(revision 16561)
@@ -0,0 +1,411 @@
+/**************************************************************************
+ *
+ * local_strings -- provides some extra string routines
+ * Copyright (C) 1994  Authors
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "local_strings.h"
+#include "memlib.h"
+
+/* =========================================================================
+ * Function: arg_atoi
+ * Description: 
+ *      Converts an ascii string to an integer
+ *      but allows a scale string immediately following.
+ *      i.e. "10Mb" or "20Kb" or "40Megabytes"
+ *      There must be no gap between the last digit and the scale.
+ *      Only the fist letter is significant.
+ *      K = Kilo
+ *      M = Mega
+ *      G = Giga
+ * Input: 
+ *      string of form: xxxxxxK or xxxxxxxM or xxxxxxxG
+ *      where x is a digit
+ * Output: 
+ *      a scaled integer
+ * ========================================================================= */
+
+#define KILO 	1024
+#define MEGA	1024*1024
+#define GIGA	1024*1024*1024
+
+int 
+arg_atoi (char *str)
+{
+  char *str_ptr = str;
+  char ch = '\0';
+  long scale = 1;
+
+  /* find any scale letter immediately after digits */
+  ch = *str_ptr;
+  while (!isalpha (ch) && (ch != '\0'))
+    ch = *str_ptr++;
+
+  /* replace scale letter by end-of-string character */
+  *str_ptr = '\0';
+
+  /* set scale appropriately */
+  switch (ch)
+    {
+    case 'K':
+      scale = KILO;
+      break;
+    case 'M':
+      scale = MEGA;
+      break;
+    case 'G':
+      scale = GIGA;
+      break;
+    default:
+      scale = 1;
+      break;
+    }
+
+  return (atoi (str) * scale);
+
+}
+
+/* ===============================================================================
+ * Function: compare
+ * Description: Compare two strings. 
+ * Input: Two binary strings, first byte of string indicates length. 
+ * Output: Like standard strcmp.
+ *      = 0 if s1 = s2
+ *      < 0 if s1 < s2
+ *      > 0 if s1 > s2
+ * =============================================================================== */
+int 
+compare (u_char * s1, u_char * s2)
+{
+  int l1 = *s1, l2 = *s2;
+  int l = (l1 < l2) ? l1 : l2;
+
+  while (l--)
+    if (*++s1 != *++s2)
+      return (*s1 - *s2);
+  return (l1 - l2);
+
+}
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static unsigned char casecharmap[] = {
+	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+/* ===============================================================================
+ * Function: casecompare   [RPAP - Jan 97: Stem Index Change]
+ * Description: Compare two strings in dictionary order
+ * Input: Two binary strings, first byte of string indicates length. 
+ * Output:
+ *      = 0 if s1 = s2
+ *      < 0 if s1 < s2
+ *      > 0 if s1 > s2
+ * =============================================================================== */
+int 
+casecompare (u_char * s1, u_char * s2)
+{
+  int l1 = *s1, l2 = *s2;
+  int l = (l1 < l2) ? l1 : l2;
+  int pos = 0;
+  register int diff = 0;
+
+  while (l--)
+    if ((diff = casecharmap[*++s1] - casecharmap[*++s2]) != 0)
+      return (diff);
+    else if ((diff = *s1 - *s2) != 0)
+      if (!pos)
+	pos = diff;
+
+  return ((l1 - l2) ? (l1 - l2) : (pos));
+
+}
+
+/* =========================================================================
+ * Function: copy_string
+ * Description: 
+ *      Copies a string with the len in 1st byte
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+u_char *
+copy_string (u_char * src_str)
+{
+  u_char *copy = new u_char(src_str[0] + 1);
+
+  if (!copy)
+    return NULL;
+
+  memcpy (copy, src_str, src_str[0] + 1);
+  return copy;
+
+}
+
+
+/* ===============================================================================
+ * Function: prefixlen
+ * Description: Returns length of common prefix string between s1 and s2.
+ * Input: two binary strings
+ * Output: prefix length
+ * =============================================================================== */
+int 
+prefixlen (u_char * s1, u_char * s2)
+{
+  int l = (*s1 < *s2) ? *s1 : *s2;
+  int i = 0;
+
+  while (i < l && *++s1 == *++s2)
+    ++i;
+  return (i);
+}
+
+
+/* ===============================================================================
+ * Function: char2str
+ * Description: 
+ *      Ensures that non-printable characters are converted to a string
+ *      which is printable i.e. it indentifies the character.
+ *      e.g. carriage-return = "\n".
+ * Input: character (may be non-printable)
+ * Output: pointer to static string
+ * =============================================================================== */
+
+char *
+char2str (u_char c)
+{
+  static char buf[10];
+  switch (c)
+    {
+    case '\n':
+      sprintf (buf, "\\n");
+      break;
+    case '\b':
+      sprintf (buf, "\\b");
+      break;
+    case '\f':
+      sprintf (buf, "\\f");
+      break;
+    case '\t':
+      sprintf (buf, "\\t");
+      break;
+    default:
+	if (c >= ' ' && c <= 126)
+	switch (c)
+	  {
+	  case '\\':
+	    sprintf (buf, "\\\\");
+	    break;
+	  case '\"':
+	    sprintf (buf, "\\\"");
+	    break;
+	  case '\'':
+	    sprintf (buf, "\\\'");
+	    break;
+	  default:
+	    buf[0] = c;
+	    buf[1] = '\0';
+	    break;
+	  }
+      else
+	sprintf (buf, "\\x%02x", c);
+      break;
+    }
+  return buf;
+}
+
+/* ===============================================================================
+ * Function: word2str
+ * Description: 
+ *      Converts a string with possible non-printable characters into a string
+ *      consisting entirely of printable characters.
+ * Input: string, length stored in first byte
+ * Output: pointer to static string
+ * =============================================================================== */
+
+char *
+word2str (u_char * s)
+{
+  static char buf[1024];
+  char *p = buf;
+  int i, len = (int) *s++;
+
+  memset (buf, '\0', sizeof(buf));
+  for (i = 0; i < len; ++i)
+    {
+      strcpy (p, char2str (s[i]));
+      p = strchr (p, '\0');
+    }
+  return buf;
+}
+
+
+/* =========================================================================
+ * Function: de_escape_string
+ * Description: 
+ *      Turn a string containing \ escapes into real characters
+ *      Note this destroys the string s in the process
+ * Input: string to de-escape 
+ * Output: the de-escaped string
+ * ========================================================================= */
+char *
+de_escape_string (char *s)
+{
+  char *org = s;
+  char *d = s;
+  while (*s)
+    {
+      if (*s == '\\')
+	{
+	  ++s;
+	  switch (*s++)
+	    {
+	    case '\0':
+	      *d++ = '\\';
+	      --s;
+	      break;
+	    case '\\':
+	      *d++ = '\\';
+	      break;
+	    case 'b':
+	      *d++ = '\b';
+	      break;
+	    case 'f':
+	      *d++ = '\f';
+	      break;
+	    case 'n':
+	      *d++ = '\n';
+	      break;
+	    case 'r':
+	      *d++ = '\r';
+	      break;
+	    case 't':
+	      *d++ = '\r';
+	      break;
+	    case '\"':
+	      *d++ = '\"';
+	      break;
+	    case '\'':
+	      *d++ = '\'';
+	      break;
+	    case 'x':
+	      {
+		int num = 0;
+		if (isxdigit (*s))
+		  {
+		    char ch = toupper (*s++);
+		    num = ch <= '9' ? ch - '0' : ch - 'A' + 10;
+		  }
+		if (isxdigit (*s))
+		  {
+		    char ch = toupper (*s++);
+		    num = (num << 4) + (ch <= '9' ? ch - '0' : ch - 'A' + 10);
+		  }
+		*d++ = num;
+		break;
+	      }
+	    case '0':
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	      {
+		int num = *(s - 1) - '0';
+		if (*s >= '0' && *s <= '7')
+		  num = (num << 3) + *s++ - '0';
+		if (*s >= '0' && *s <= '7')
+		  num = (num << 3) + *s++ - '0';
+		*d++ = num;
+		break;
+	      }
+	    }
+	}
+      else
+	*d++ = *s++;
+    }
+  *d = '\0';
+  return (org);
+}
+
+/* =========================================================================
+ * Function: str255_to_string 
+ * Description: 
+ * Input: 
+ * Output: 
+ * ========================================================================= */
+
+char *
+str255_to_string (u_char * str255, char *str)
+{
+  static char buf[256];
+  char *str_base = NULL;
+  int len = *str255++;
+  int i = 0;
+
+  /* if haven't been given a str to write to then use local buffer */
+  if (!str)
+    str = &(buf[0]);
+
+  str_base = str;
+  for (i = 0; i < len; ++i)
+    {
+      *str++ = *str255++;
+    }
+  *str = '\0';
+
+  return str_base;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/local_strings.h	(revision 16561)
@@ -0,0 +1,79 @@
+/**************************************************************************
+ *
+ * local_strings.h -- string routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef LOCAL_STRINGS_H
+#define LOCAL_STRINGS_H
+
+#include "sysfuncs.h"
+
+class mgString
+{
+  u_char *data;
+ public:
+  inline int length()
+    {
+      return data[0];
+    }
+  inline u_char *rawdata()
+    {
+      return data;
+    }
+  inline void read(u_char **base)
+    {
+      unsigned int copy, suff;
+      
+      copy  = **base;
+      *base = *base + 1;
+      suff  = **base;
+      *base = *base + 1;
+      memcpy( (data + copy + 1), *base, suff);
+      *data = copy + suff;
+      *base += suff;
+    }
+  inline void read(FILE *dict)
+    {
+      unsigned int copy, suff;
+      copy = fgetc(dict);
+      suff = fgetc(dict);
+      fread(data + copy + 1, sizeof(u_char), suff, dict);
+      *data = copy + suff;
+    }
+  inline static void readString(u_char **base, u_char *prev)
+    {
+      ((mgString *) &prev)->read(base);
+    }
+  inline static void readString(FILE *dict, u_char *prev)
+    { 
+      ((mgString *) &prev)->read(dict);
+    }
+};
+
+int arg_atoi (char *str);
+int compare (u_char * s1, u_char * s2);
+int casecompare (u_char * s1, u_char * s2);   /* [RPAP - Jan 97: Stem Index Change] */
+u_char *copy_string (u_char * src_str);
+char *str255_to_string (u_char * str255, char *str);
+int prefixlen (u_char * s1, u_char * s2);
+char *char2str (u_char c);
+char *word2str (u_char * s);
+char *de_escape_string (char *s);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/longlong.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/longlong.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/longlong.h	(revision 16561)
@@ -0,0 +1,85 @@
+/**************************************************************************
+ *
+ * longlong.h -- Use of GCC's long long integer types
+ * Copyright (C) 1999 Tim A.H. Bell
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_LONGLONG
+#define H_LONGLONG
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__
+#  include <win32cfg.h>
+# else
+#  include <config.h>
+# endif
+#endif
+
+/*
+  Use GCC's "long long" integer types for certain variables, to avoid
+  overflowing 32 bit integers.
+
+  Other files affected:
+  
+    lib:
+        bitio_random.c
+        bitio_random.h
+	
+    src/text:
+        ivf.pass1.c
+        ivf.pass2.c
+        build.h
+        mg_passes.c
+        text.h
+    
+*/
+
+/*#define TESTING_OVERFLOW*/
+
+#ifdef TESTING_OVERFLOW
+
+/* Test the overflow detection by using tiny (16-bit) types */
+typedef unsigned short int mg_ullong;
+typedef short int mg_llong;
+
+#define ULL_FS "u"
+#define LL_FS "d"
+
+#elif defined __GNUC__ && ! defined DISABLE_LONG_LONG
+
+/* We're using GCC, so it's okay to use "long long" (64-bit) types */
+#define USE_LONG_LONG
+
+typedef unsigned long long int mg_ullong;
+typedef long long int mg_llong;
+
+#define ULL_FS "llu"
+#define LL_FS "lld"
+
+#else
+
+/* Not using GCC, so fall back on plain "long" (32-bit) types */
+typedef unsigned long int mg_ullong;
+typedef long int mg_llong;
+
+#define ULL_FS "lu"
+#define LL_FS "ld"
+
+#endif /* __GNUC__ */
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.cpp	(revision 16561)
@@ -0,0 +1,1172 @@
+/**************************************************************************
+ *
+ * lovinstem.c -- Stemming code
+ * Copyright (C) 1994  Linh Huynh (linh@kbs.citri.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+
+/*****************************************************************************
+ *
+ *   Transformational Rules used in Recoding Stem Terminations
+ *
+ *   Parameter in functions:
+ *       ch : the second last character of the old ending
+ *            (after removing one of double consonants) 
+ *
+ ****************************************************************************/
+
+
+/************ Conditional rules associated with transformations **************/
+
+/*static int aio ();
+static int s ();
+static int pt ();
+static int m ();
+static int n ();*/
+
+static int 
+aio (char ch)			/* Rule number 9     */
+{
+  return ((ch != 'a') && (ch != 'i') && (ch != 'o'));
+}
+
+static int 
+s (char ch)				/* Rule Number 24    */
+{
+  return (ch != 's');
+}
+
+static int 
+pt (char ch)				/* Rule number 28    */
+{
+  return ((ch != 'p') && (ch != 't'));
+}
+
+static int 
+m (char ch)				/* Rule number 30    */
+{
+  return (ch != 'm');
+}
+
+static int 
+n (char ch)				/* Rule number 32    */
+{
+  return (ch != 'n');
+}
+
+
+/**************************************************************************
+ *
+ *  Context-sensitive rules associated with certain endings.
+ *
+ *  The notations of the rules are the same as in Lovins' paper
+ *  except that rule A is replaced by a NULL in the data structure.
+ *
+ *  Function parameters:
+ *      - stem_length: possible stem length
+ *      - end        : pointer points to the end of the possible stem.
+ *
+ *************************************************************************/
+
+
+/********* Context-sensitive rule function declarations ******************/
+
+/*static int B ();
+static int C ();
+static int D ();
+static int E ();
+static int F ();
+static int G ();
+static int H ();
+static int I ();
+static int J ();
+static int K ();
+static int L ();
+static int M ();
+static int N ();
+static int O ();
+static int P ();
+static int Q ();
+static int R ();
+static int S ();
+static int T ();
+static int U ();
+static int V ();
+static int W ();
+static int X ();
+static int Y ();
+static int Z ();
+static int AA ();
+static int BB ();
+static int CC ();
+*/
+static int 
+B (int stem_length, char *end)
+{
+  return (stem_length >= 3);
+}
+
+static int 
+C (int stem_length, char *end)
+{
+  return (stem_length >= 4);
+}
+
+static int 
+D (int stem_length, char *end)
+{
+  return (stem_length >= 5);
+}
+
+static int 
+E (int stem_length, char *end)
+
+     
+{
+  return (*end != 'e');
+}
+
+static int 
+F (int stem_length, char *end)
+     
+     
+{
+  return ((stem_length >= 3) && (*end != 'e'));
+}
+
+static int 
+G (int stem_length, char *end)
+     
+     
+{
+  return ((stem_length >= 3) && (*end == 'f'));
+}
+
+static int 
+H (int stem_length, char *end)
+     
+     
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+  return ((p1 == 't') || ((p1 == 'l') && (p2 == 'l')));
+}
+
+static int 
+I (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'o') && (p1 != 'e'));
+}
+
+static int 
+J (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'a') && (p1 != 'e'));
+}
+
+static int 
+K (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((stem_length >= 3) &&
+	  ((p1 == 'l') || (p1 == 'i') ||
+	   ((p1 == 'e') && (*(end - 2) == 'u'))));
+}
+
+static int 
+L (int stem_length, char *end)
+     
+     
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+  if (p1 == 's')
+    return (p2 == 'o');
+  else
+    return ((p1 != 'u') && (p1 != 'x'));
+}
+
+static int 
+M (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((p1 != 'a') && (p1 != 'c') &&
+	  (p1 != 'e') && (p1 != 'm'));
+}
+
+static int 
+N (int stem_length, char *end)
+     
+     
+{
+  if (stem_length >= 3)
+    {
+      if (*(end - 2) == 's')
+	return (stem_length >= 4);
+      else
+	return 1;
+    }
+  else
+    return 0;
+}
+
+static int 
+O (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((p1 == 'l') || (p1 == 'i'));
+}
+
+static int 
+P (int stem_length, char *end)
+     
+     
+{
+  return (*end != 'c');
+}
+
+static int 
+Q (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+
+  return ((stem_length >= 3) && (p1 != 'l') && (p1 != 'n'));
+}
+
+static int 
+R (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+  return ((p1 == 'n') || (p1 == 'r'));
+}
+
+static int 
+S (int stem_length, char *end)
+     
+     
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 't')
+    return (p2 != 't');
+  else
+    return ((p1 == 'r') && (p2 == 'd'));
+}
+
+static int 
+T (int stem_length, char *end)
+     
+     
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 't')
+    return (p2 != 'o');
+  else
+    return (p1 == 's');
+}
+
+static int 
+U (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+
+  return ((p1 == 'l') || (p1 == 'm') ||
+	  (p1 == 'n') || (p1 == 'r'));
+}
+
+static int 
+V (int stem_length, char *end)
+     
+     
+{
+  return (*end == 'c');
+}
+
+static int 
+W (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+
+  return ((p1 != 's') && (p1 != 'u'));
+}
+
+static int 
+X (int stem_length, char *end)
+     
+     
+{
+  char p1;
+  p1 = *end;
+
+  if (p1 == 'e')
+    return ((stem_length >= 3) && (*(end - 2) == 'u'));
+  else
+    return ((p1 == 'l') || (p1 == 'i'));
+}
+
+static int 
+Y (int stem_length, char *end)
+     
+     
+{
+  return ((*end == 'n') && (*(end - 1) == 'i'));
+}
+
+static int 
+Z (int stem_length, char *end)
+     
+     
+{
+  return (*end != 'f');
+}
+
+static int 
+AA (int stem_length, char *end)
+     
+     
+{
+  char p1, p2;
+  p1 = *end;
+  p2 = *(end - 1);
+
+  if (p1 == 'h')
+    return ((p2 == 'p') || (p2 == 't'));
+  else if (p1 == 'r')
+    return ((p2 == 'e') || (p2 == 'o'));
+  else if (p1 == 's')
+    return (p2 == 'e');
+  else
+    return ((p1 == 'd') || (p1 == 'f') ||
+	    (p1 == 'l') || (p1 == 't'));
+}
+
+static int 
+BB (int stem_length, char *end)
+     
+     
+{
+  if (stem_length >= 3)
+    {
+      if (stem_length >= 4)
+	return (strncmp (end - 3, "ryst", 4) != 0);
+      else
+	return (strncmp (end - 2, "met", 3) != 0);
+    }
+  else
+    return 0;
+}
+
+static int 
+CC (int stem_length, char *end)
+     
+     
+{
+  return (*end == 'l');
+}
+
+/**************************************************************************/
+
+#define MIN_STEM_LENGTH 2
+#define MAX_ENDING_SIZE 11
+#define PREDEFINED_SIZE (MAX_ENDING_SIZE + MIN_STEM_LENGTH)
+#define EOS     '\0'
+/* [RPAP - Jan 97: Stem Index Change] */
+#define MAX_STEM_LENGTH 255
+
+static char *remove_ending (char *word, int w_len);
+static void recode_stem (char *stem_end);
+
+/* =========================================================================
+ * Function: stem
+ * Description: 
+ *      Implemetation of the Lovins' stemming algorithm described in
+ *      J.B. Lovins, "Development of a Stemming Algorithm",
+ *      Mechanical Translation and Computational Linguistics, Vol 11,1968.
+ * Input: a word string with the length in the first byte
+ * Output: the stemmed word
+ * ========================================================================= */
+
+void
+lovinstem (u_char * word)
+{
+  char *word_start;		/* points to first letter of word */
+  char *stem_end;		/* points to last character of stem portion */
+  int old_length;		/* length of word */
+  int new_length;		/* length after stemming */
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  u_char copy[MAX_STEM_LENGTH + 2];
+  memcpy (copy, word, *word + 1);
+
+  old_length = *copy; /* [RPAP - Jan 97: Stem Index Change] */
+
+  /* The word must be at least MIN_STEM_LENGTH characters long. */
+  if (old_length <= MIN_STEM_LENGTH)
+    return;
+
+  word_start = (char *) copy + 1;	/* jump over length byte */ /* [RPAP - Jan 97: Stem Index Change] */
+  copy[old_length + 1] = '\0';	/* null terminate string */ /* [RPAP - Jan 97: Stem Index Change] */
+
+  stem_end = remove_ending (word_start, old_length);
+  recode_stem (stem_end);
+
+  /* fix up new length */
+  /* have to use strlen because have no other way of telling length */
+  new_length = strlen (word_start);
+  *copy = new_length;  /* [RPAP - Jan 97: Stem Index Change] */
+
+  /* [RPAP - Jan 97: Stem Index Change] */
+  memcpy (word, copy, *copy + 1);
+
+}				/*stem_complex */
+
+/* =========================================================================
+
+ *   Data structure for recoding and the method for recoding the stem. 
+ *
+ * ========================================================================= */
+
+typedef struct
+{
+  char *old_end;		/* old ending */
+  char *new_end;		/* new ending */
+  char old_offset;		/* length of the old ending - 1 */
+  int (*cond) (char);	        /* condition rule */
+  char end_group;		/* signal the end of the group */
+}
+Recode_Rules;
+
+/*  
+   Stem terminations are divided into groups having the same last
+   character
+ */
+
+static Recode_Rules Rules[] =
+{
+  "uad", "uas", 2, NULL, 0,
+  "vad", "vas", 2, NULL, 0,
+  "cid", "cis", 2, NULL, 0,
+  "lid", "lis", 2, NULL, 0,
+  "erid", "eris", 3, NULL, 0,
+  "pand", "pans", 3, NULL, 0,
+  "end", "ens", 2, s, 0,
+  "end", "ens", 2, m, 0,
+  "ond", "ons", 2, NULL, 0,
+  "lud", "lus", 2, NULL, 0,
+  "rud", "rus", 2, NULL, 1,
+
+  "ul", "l", 1, aio, 1,
+
+  "istr", "ister", 3, NULL, 0,
+  "metr", "meter", 3, NULL, 0,
+  "her", "hes", 2, pt, 1,
+
+  "urs", "ur", 2, NULL, 1,
+
+  "uct", "uc", 2, NULL, 0,
+  "umpt", "um", 3, NULL, 0,
+  "rpt", "rb", 2, NULL, 0,
+  "mit", "mis", 2, NULL, 0,
+  "ert", "ers", 2, NULL, 0,
+  "et", "es", 1, n, 0,
+  "yt", "ys", 1, NULL, 1,
+
+  "iev", "ief", 2, NULL, 0,
+  "olv", "olut", 2, NULL, 1,
+
+  "bex", "bic", 2, NULL, 0,
+  "dex", "dic", 2, NULL, 0,
+  "pex", "pic", 2, NULL, 0,
+  "tex", "tic", 2, NULL, 0,
+  "ax", "ac", 1, NULL, 0,
+  "ex", "ec", 1, NULL, 0,
+  "ix", "ic", 1, NULL, 0,
+  "lux", "luc", 2, NULL, 1,
+
+  "yz", "ys", 1, NULL, 1
+};
+
+typedef struct last
+  {
+    char c;			/* The last character */
+    struct last *left;		/* used in balanced   */
+    struct last *right;		/* binary tree        */
+    Recode_Rules *pt;		/* location of approriate group */
+  }
+Last_Char_Node;
+
+/*------------
+       s
+     /   \
+    l     x
+   / \   / \
+  d   r t   z
+         \
+          v
+---------------*/
+static Last_Char_Node pr[] =
+{
+  'd', NULL, NULL, Rules,
+  'l', pr, pr + 2, Rules + 11,
+  'r', NULL, NULL, Rules + 12,
+  's', pr + 1, pr + 6, Rules + 15,
+  't', NULL, pr + 5, Rules + 16,
+  'v', NULL, NULL, Rules + 23,
+  'x', pr + 4, pr + 7, Rules + 25,
+  'z', NULL, NULL, Rules + 33,
+};
+
+
+/*****************************************************************************
+ *
+ *  Recode the stem according to the transformation rules.
+ *
+ *****************************************************************************/
+
+static void
+recode_stem (char *stem_end)
+{
+  static Last_Char_Node *root = pr + 3;
+  Last_Char_Node *p_last = root;
+  Recode_Rules *rule;		/* points to the transformation list */
+  char *h,			/* points to start of possible ending */
+    ch,				/* last character of the old ending  */
+    ch2;
+
+  h = stem_end - 1;
+  ch = *stem_end;
+  if (*h == ch)			/* Check for double consonant        */
+    {
+      if (strchr ("bdglmnprst", ch) != NULL)
+	{
+	  *stem_end = EOS;
+	  stem_end = h;
+	}
+    }
+
+  do				/* Check for the last character       */
+    {
+      ch2 = p_last->c;
+      if (ch == ch2)
+	break;
+      else if (ch > ch2)
+	p_last = p_last->right;
+      else
+	p_last = p_last->left;
+    }
+  while (p_last != NULL);
+
+  if (p_last != NULL)		/* Check for the rest of suffix list */
+    {
+      rule = p_last->pt;
+      for (;;)
+	{
+	  h = stem_end - rule->old_offset;
+	  if (strcmp (h, rule->old_end) == 0)
+	    {
+	      if (!rule->cond || (*rule->cond) (*(h - 1)))
+		{
+		  (void) strcpy (h, rule->new_end);
+		  break;
+		}
+	    }
+	  if (rule->end_group)
+	    break;
+	  ++rule;
+	}
+    }
+}
+
+/* ======================================================================
+
+ *    List of endings and the method to remove the ending 
+ *
+ * ======================================================================*/
+
+/************ Data structures for list of endings  ********************/
+
+typedef struct
+{
+  char *ending;			/* old ending                */
+  int (*cond) (int length, char *end);		/* conditional rule          */
+  signed char left_offset;	/* used to find the siblings */
+  signed char right_offset;	/* in balanced binary tree   */
+}
+Ending_List;
+
+/*
+   The original list of endings is re-organised into groups having
+   the same length and the same first character.
+ */
+
+static Ending_List List[] =
+{
+  "a", NULL, 0, 0,
+
+  "ae", NULL, 0, 0,
+  "al", BB, -1, 2,
+  "ar", X, 0, 0,
+  "as", B, -1, 0,
+
+  "acy", NULL, 0, 1,
+  "age", B, 0, 0,
+  "aic", NULL, -2, 1,
+  "als", BB, 0, 0,
+  "ant", B, -2, 2,
+  "ars", O, 0, 0,
+  "ary", F, -1, 2,
+  "ata", NULL, 0, 0,
+  "ate", NULL, -1, 0,
+
+  "able", NULL, 0, 1,
+  "ably", NULL, 0, 0,
+  "ages", B, -2, 2,
+  "ally", B, 0, 0,
+  "ance", B, -1, 1,
+  "ancy", B, 0, 0,
+  "ants", B, -4, 4,
+  "aric", NULL, 0, 0,
+  "arly", K, -1, 1,
+  "ated", I, 0, 0,
+  "ates", NULL, -2, 2,
+  "atic", B, 0, 0,
+  "ator", NULL, -1, 0,
+
+  "acies", NULL, 0, 0,
+  "acity", NULL, -1, 1,
+  "aging", B, 0, 0,
+  "aical", NULL, -2, 2,
+  "alist", NULL, 0, 0,
+  "alism", B, -1, 0,
+  "ality", NULL, -3, 3,
+  "alize", NULL, 0, 1,
+  "allic", BB, 0, 0,
+  "anced", B, -2, 2,
+  "ances", B, 0, 0,
+  "antic", C, -1, 0,
+  "arial", NULL, -6, 6,
+  "aries", NULL, 0, 1,
+  "arily", NULL, 0, 0,
+  "arity", B, -2, 2,
+  "arize", NULL, 0, 0,
+  "aroid", NULL, -1, 0,
+  "ately", NULL, -3, 3,
+  "ating", I, 0, 1,
+  "ation", B, 0, 0,
+  "ative", NULL, -2, 2,
+  "ators", NULL, 0, 0,
+  "atory", NULL, -1, 1,
+  "ature", E, 0, 0,
+
+  "aceous", NULL, 0, 1,
+  "acious", B, 0, 0,
+  "action", G, -2, 2,
+  "alness", NULL, 0, 0,
+  "ancial", NULL, -1, 1,
+  "ancies", NULL, 0, 0,
+  "ancing", B, -4, 4,
+  "ariser", NULL, 0, 0,
+  "arized", NULL, -1, 1,
+  "arizer", NULL, 0, 0,
+  "atable", NULL, -2, 2,
+  "ations", B, 0, 0,
+  "atives", NULL, -1, 0,
+
+  "ability", NULL, 0, 1,
+  "aically", NULL, 0, 0,
+  "alistic", B, -2, 2,
+  "alities", NULL, 0, 0,
+  "ariness", E, -1, 0,
+  "aristic", NULL, -3, 3,
+  "arizing", NULL, 0, 1,
+  "ateness", NULL, 0, 0,
+  "atingly", NULL, -2, 2,
+  "ational", B, 0, 0,
+  "atively", NULL, -1, 1,
+  "ativism", NULL, 0, 0,
+
+  "ableness", NULL, 0, 1,
+  "arizable", NULL, 0, 0,
+
+  "allically", C, 0, 0,
+  "antaneous", NULL, -1, 1,
+  "antiality", NULL, 0, 0,
+  "arisation", NULL, -2, 2,
+  "arization", NULL, 0, 0,
+  "ationally", B, -1, 1,
+  "ativeness", NULL, 0, 0,
+
+  "antialness", NULL, 0, 0,
+  "arisations", NULL, -1, 1,
+  "arizations", NULL, 0, 0,
+
+  "alistically", B, 0, 1,
+  "arizability", NULL, 0, 0,
+
+  "e", NULL, 0, 0,
+
+  "ed", E, 0, 0,
+  "en", F, -1, 1,
+  "es", E, 0, 0,
+
+  "eal", Y, 0, 0,
+  "ear", Y, -1, 1,
+  "ely", E, 0, 0,
+  "ene", E, -2, 2,
+  "ent", C, 0, 0,
+  "ery", E, -1, 1,
+  "ese", NULL, 0, 0,
+
+  "ealy", Y, 0, 1,
+  "edly", E, 0, 0,
+  "eful", NULL, -2, 1,
+  "eity", NULL, 0, 0,
+  "ence", NULL, -2, 2,
+  "ency", NULL, 0, 0,
+  "ened", E, -1, 2,
+  "enly", E, 0, 0,
+  "eous", NULL, -1, 0,
+
+  "early", Y, 0, 1,
+  "ehood", NULL, 0, 0,
+  "eless", NULL, -2, 2,
+  "elity", NULL, 0, 0,
+  "ement", NULL, -1, 0,
+  "enced", NULL, -3, 3,
+  "ences", NULL, 0, 1,
+  "eness", E, 0, 0,
+  "ening", E, -2, 2,
+  "ental", NULL, 0, 0,
+  "ented", C, -1, 1,
+  "ently", NULL, 0, 0,
+
+  "eature", Z, 0, 0,
+  "efully", NULL, -1, 1,
+  "encies", NULL, 0, 0,
+  "encing", NULL, -2, 2,
+  "ential", NULL, 0, 0,
+  "enting", C, -1, 1,
+  "entist", NULL, 0, 1,
+  "eously", NULL, 0, 0,
+
+  "elihood", E, 0, 1,
+  "encible", NULL, 0, 0,
+  "entally", NULL, -2, 2,
+  "entials", NULL, 0, 0,
+  "entiate", NULL, -1, 1,
+  "entness", NULL, 0, 0,
+
+  "entation", NULL, 0, 0,
+  "entially", NULL, -1, 1,
+  "eousness", NULL, 0, 0,
+
+  "eableness", E, 0, 1,
+  "entations", NULL, 0, 0,
+  "entiality", NULL, -2, 2,
+  "entialize", NULL, 0, 0,
+  "entiation", NULL, -1, 0,
+
+  "entialness", NULL, 0, 0,
+
+  "ful", NULL, 0, 0,
+
+  "fully", NULL, 0, 0,
+
+  "fulness", NULL, 0, 0,
+
+  "hood", NULL, 0, 0,
+
+  "i", NULL, 0, 0,
+
+  "ia", NULL, 0, 0,
+  "ic", NULL, -1, 1,
+  "is", NULL, 0, 0,
+
+  "ial", NULL, 0, 0,
+  "ian", NULL, -1, 1,
+  "ics", NULL, 0, 1,
+  "ide", L, 0, 0,
+  "ied", NULL, -3, 3,
+  "ier", NULL, 0, 0,
+  "ies", P, -1, 0,
+  "ily", NULL, -1, 1,
+  "ine", M, 0, 0,
+  "ing", N, -5, 5,
+  "ion", Q, 0, 0,
+  "ish", C, -1, 1,
+  "ism", B, 0, 1,
+  "ist", NULL, 0, 0,
+  "ite", AA, -3, 3,
+  "ity", NULL, 0, 0,
+  "ium", NULL, -1, 0,
+  "ive", NULL, -1, 1,
+  "ize", F, 0, 0,
+
+  "ials", NULL, 0, 0,
+  "ians", NULL, -1, 0,
+  "ible", NULL, -1, 1,
+  "ibly", NULL, 0, 0,
+  "ical", NULL, -2, 2,
+  "ides", L, 0, 0,
+  "iers", NULL, -1, 1,
+  "iful", NULL, 0, 0,
+  "ines", M, -4, 4,
+  "ings", N, 0, 0,
+  "ions", B, -1, 1,
+  "ious", NULL, 0, 0,
+  "isms", B, -2, 2,
+  "ists", NULL, 0, 0,
+  "itic", H, -1, 1,
+  "ized", F, 0, 1,
+  "izer", F, 0, 0,
+
+  "ially", NULL, 0, 0,
+  "icant", NULL, -1, 1,
+  "ician", NULL, 0, 0,
+  "icide", NULL, -2, 2,
+  "icism", NULL, 0, 0,
+  "icist", NULL, -1, 0,
+  "icity", NULL, -3, 3,
+  "idine", I, 0, 1,
+  "iedly", NULL, 0, 0,
+  "ihood", NULL, -2, 2,
+  "inate", NULL, 0, 0,
+  "iness", NULL, -1, 0,
+  "ingly", B, -6, 6,
+  "inism", J, 0, 1,
+  "inity", CC, 0, 0,
+  "ional", NULL, -2, 2,
+  "ioned", NULL, 0, 0,
+  "ished", NULL, -1, 0,
+  "istic", NULL, -3, 3,
+  "ities", NULL, 0, 1,
+  "itous", NULL, 0, 0,
+  "ively", NULL, -2, 2,
+  "ivity", NULL, 0, 0,
+  "izers", F, -1, 1,
+  "izing", F, 0, 0,
+
+  "ialist", NULL, 0, 0,
+  "iality", NULL, -1, 1,
+  "ialize", NULL, 0, 0,
+  "ically", NULL, -2, 2,
+  "icance", NULL, 0, 0,
+  "icians", NULL, -1, 1,
+  "icists", NULL, 0, 0,
+  "ifully", NULL, -4, 4,
+  "ionals", NULL, 0, 0,
+  "ionate", D, -1, 1,
+  "ioning", NULL, 0, 0,
+  "ionist", NULL, -2, 2,
+  "iously", NULL, 0, 0,
+  "istics", NULL, -1, 1,
+  "izable", E, 0, 0,
+
+  "ibility", NULL, 0, 0,
+  "icalism", NULL, -1, 1,
+  "icalist", NULL, 0, 1,
+  "icality", NULL, 0, 0,
+  "icalize", NULL, -3, 3,
+  "ication", G, 0, 0,
+  "icianry", NULL, -1, 0,
+  "ination", NULL, -1, 1,
+  "ingness", NULL, 0, 0,
+  "ionally", NULL, -5, 5,
+  "isation", NULL, 0, 0,
+  "ishness", NULL, -1, 1,
+  "istical", NULL, 0, 1,
+  "iteness", NULL, 0, 0,
+  "iveness", NULL, -3, 3,
+  "ivistic", NULL, 0, 0,
+  "ivities", NULL, -1, 0,
+  "ization", F, -1, 1,
+  "izement", NULL, 0, 0,
+
+  "ibleness", NULL, 0, 0,
+  "icalness", NULL, -1, 1,
+  "ionalism", NULL, 0, 0,
+  "ionality", NULL, -2, 2,
+  "ionalize", NULL, 0, 0,
+  "iousness", NULL, -1, 1,
+  "izations", NULL, 0, 0,
+
+  "ionalness", NULL, 0, 1,
+  "istically", NULL, 0, 0,
+  "itousness", NULL, -2, 2,
+  "izability", NULL, 0, 0,
+  "izational", NULL, -1, 0,
+
+  "izationally", B, 0, 0,
+
+  "ly", B, 0, 0,
+
+  "less", NULL, 0, 1,
+  "lily", NULL, 0, 0,
+
+  "lessly", NULL, 0, 0,
+
+  "lessness", NULL, 0, 0,
+
+  "ness", NULL, 0, 0,
+
+  "nesses", NULL, 0, 0,
+
+  "o", NULL, 0, 0,
+
+  "on", S, 0, 1,
+  "or", T, 0, 0,
+
+  "oid", NULL, 0, 0,
+  "one", R, -1, 1,
+  "ous", NULL, 0, 0,
+
+  "ogen", NULL, 0, 0,
+
+  "oidal", NULL, 0, 0,
+  "oides", NULL, -1, 2,
+  "otide", NULL, 0, 0,
+  "ously", NULL, -1, 0,
+
+  "oidism", NULL, 0, 0,
+
+  "oidally", NULL, 0, 1,
+  "ousness", NULL, 0, 0,
+
+  "s", W, 0, 0,
+
+  "s'", NULL, 0, 0,
+
+  "um", U, 0, 1,
+  "us", V, 0, 0,
+
+  "ward", NULL, 0, 1,
+  "wise", NULL, 0, 0,
+
+  "y", B, 0, 0,
+
+  "yl", R, 0, 0,
+
+  "ying", B, 0, 1,
+  "yish", NULL, 0, 0,
+
+  "'s", NULL, 0, 0,
+};
+
+typedef struct node
+  {
+    char c;			/* First character  */
+    struct node *left;		/* used in balanced */
+    struct node *right;		/* binary tree      */
+    Ending_List *ptr[11];	/* the approriate location */
+  }
+First_Char_Node;
+
+static First_Char_Node First[] =
+{
+  '\'', NULL, NULL,
+  {NULL,
+   List + 293, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'a', First, NULL,
+  {List,
+   List + 2, List + 9, List + 20, List + 39, List + 58,
+   List + 70, List + 77, List + 82, List + 87, List + 89},
+
+  'e', First + 1, First + 4,
+  {List + 91,
+   List + 93, List + 98, List + 106, List + 116, List + 126,
+   List + 133, List + 138, List + 142, List + 145, NULL},
+
+  'f', NULL, NULL,
+  {NULL,
+   NULL, List + 146, NULL, List + 147, NULL,
+   List + 148, NULL, NULL, NULL, NULL},
+
+  'h', First + 3, First + 5,
+  {NULL,
+   NULL, NULL, List + 149, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'i', NULL, NULL,
+  {List + 150,
+   List + 152, List + 163, List + 181, List + 202, List + 222,
+   List + 239, List + 252, List + 258, NULL, List + 261},
+
+  'l', First + 2, First + 10,
+  {NULL,
+   List + 262, NULL, List + 263, NULL, List + 265,
+   NULL, List + 266, NULL, NULL, NULL},
+
+  'n', NULL, NULL,
+  {NULL,
+   NULL, NULL, List + 267, NULL, List + 268,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'o', First + 7, First + 9,
+  {List + 269,
+   List + 270, List + 273, List + 275, List + 277, List + 280,
+   List + 281, NULL, NULL, NULL, NULL},
+
+  's', NULL, NULL,
+  {List + 283,
+   List + 284, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'u', First + 8, First + 12,
+  {NULL,
+   List + 285, NULL, NULL, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'w', NULL, NULL,
+  {NULL,
+   NULL, NULL, List + 287, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+
+  'y', First + 11, NULL,
+  {List + 289,
+   List + 290, NULL, List + 291, NULL, NULL,
+   NULL, NULL, NULL, NULL, NULL},
+};
+
+
+/******************************************************************************
+ *
+ *  Look for a match in the suffix list.
+ *  Return the pointer to the end of the new stem if there is a match.
+ *  Otherwise, return the pointer to the end of the original word.
+ *
+ *  Method:
+ *      * Search for the first character in the suffix list.
+ *      * If there is match, search for the rest of the suffix list.
+ *         
+ *****************************************************************************/
+
+static char *
+remove_ending (char *word, int w_len)
+{
+  static First_Char_Node *root = First + 6;	/* the root of binary tree is 'l' */
+  First_Char_Node *p_first;	/* Points to the first character      */
+  /* of the possible suffix             */
+  Ending_List *p_list;		/* Points to the List of Endings      */
+  char *suffix_start,		/* Points to start of possible suffix */
+   *stem_end,			/* Points to the end of possible stem */
+    ch1, ch2;
+  int s_len,			/* Possible stem length               */
+    e_offset,			/* Offset from the end to the start of */
+  /* possible suffix                    */
+    not_done = 1, cmp;
+
+  stem_end = word + w_len - 1;
+  if (w_len > PREDEFINED_SIZE)	/* find the start of suffix   */
+    suffix_start = word + w_len - MAX_ENDING_SIZE;
+  else
+    suffix_start = word + MIN_STEM_LENGTH;
+
+  ch1 = *suffix_start;
+  do				/* Search for the first character     */
+    {
+      p_first = root;
+      do
+	{
+	  ch2 = p_first->c;
+	  if (ch1 == ch2)
+	    break;
+	  else if (ch1 > ch2)
+	    p_first = p_first->right;
+	  else
+	    p_first = p_first->left;
+	}
+      while (p_first != NULL);
+
+      if (p_first != NULL)	/* Search for the rest */
+	{
+	  e_offset = stem_end - suffix_start;
+	  if ((p_list = p_first->ptr[e_offset]) != NULL)
+	    {
+	      for (;;)		/* no need to compare the first char  */
+		{
+		  cmp = strcmp (suffix_start + 1, p_list->ending + 1);
+		  if (cmp > 0)
+		    if (p_list->right_offset)
+		      p_list += p_list->right_offset;
+		    else
+		      break;
+		  else if (cmp < 0)
+		    if (p_list->left_offset)
+		      p_list += p_list->left_offset;
+		    else
+		      break;
+		  else
+		    {
+		      s_len = suffix_start - word;
+		      if (!p_list->cond ||
+			  (*p_list->cond) (s_len, suffix_start - 1))
+			{
+			  *suffix_start = EOS;
+			  stem_end = suffix_start - 1;
+			  not_done = 0;
+			}
+		      break;
+		    }
+		}
+	    }
+	}
+      ++suffix_start;
+    }
+  while (not_done && ((ch1 = *suffix_start) != EOS));
+  return (stem_end);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/lovinstem.h	(revision 16561)
@@ -0,0 +1,28 @@
+/**************************************************************************
+ *
+ * lovinstem.h -- Lovins' stemmer header
+ * Copyright (C) 1994  Linh Huynh, 
+ *                     (glued into library by tes@kbs.citri.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef LOVINSTEM_H
+#define LOVINSTEM_H
+
+void lovinstem (unsigned char *word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.cpp	(revision 16561)
@@ -0,0 +1,41 @@
+/**************************************************************************
+ *
+ * memlib.c -- Malloc wrappers
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+/* Defined as strdup is not an ANSI function */
+char *
+my_strdup(const char *str)
+{
+  char *ret_str = new char[strlen(str)+1];
+  if (ret_str) return strcpy(ret_str, str);
+  else return (char*) 0;
+}
+
+
+Malloc_func Xmalloc = malloc;
+
+Realloc_func Xrealloc = realloc;
+
+Free_func Xfree = free;
+
+Strdup_func Xstrdup = my_strdup;
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/memlib.h	(revision 16561)
@@ -0,0 +1,55 @@
+/**************************************************************************
+ *
+ * memlib.h -- Malloc wrappers
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_MEMLIB
+#define H_MEMLIB
+
+/* changed default behaviour - we now assume std memory. - john mcp. */
+/* #ifdef STD_MEM */
+#if !defined(NOT_STD_MEM)
+#include <string.h>
+#define Xmalloc malloc
+#define Xrealloc realloc
+#define Xfree free
+
+#if defined (__WIN32__)
+# define Xstrdup _strdup
+#else
+# define Xstrdup strdup
+#endif
+
+#else
+
+
+#include "sysfuncs.h"
+
+typedef void *(*Malloc_func) (size_t);
+typedef void *(*Realloc_func) (void *, size_t);
+typedef void (*Free_func) (void *);
+typedef char *(*Strdup_func) (const char *);
+
+extern Malloc_func Xmalloc;
+extern Realloc_func Xrealloc;
+extern Free_func Xfree;
+extern Strdup_func Xstrdup;
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.cpp	(revision 16561)
@@ -0,0 +1,75 @@
+/**************************************************************************
+ *
+ * messages.c -- Message and error functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include <stdarg.h>
+#include "messages.h"
+
+char *msg_prefix = "";
+
+
+void VOLATILE 
+FatalError (int ExitCode, const char *fmt,...)
+{
+  char buf[1024];
+  char *s, *pfx;
+  va_list args;
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  s = strrchr (buf, '\n');
+  if (!s || *(s + 1) != '\0')
+    strcat (buf, "\n");
+  pfx = strrchr (msg_prefix, '/');
+  pfx = pfx ? pfx + 1 : msg_prefix;
+  fprintf (stderr, "%s%s%s", pfx, *pfx ? " : " : "", buf);
+  exit (ExitCode);
+}
+
+
+
+/*
+ * This function writes messages to stderr. Due to the fact that I can't 
+ * guarantee that the fprintf call is monatomic I have to implement a 
+ * semaphore system.
+ */
+void 
+Message (const char *fmt,...)
+{
+  char buf[1024];
+  char *s, *pfx;
+  va_list args;
+
+#ifdef SILENT
+  return; /* stop those messages!!!! */
+#endif  
+
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  s = strrchr (buf, '\n');
+  if (!s || *(s + 1) != '\0')
+    strcat (buf, "\n");
+
+  pfx = strrchr (msg_prefix, '/');
+  pfx = pfx ? pfx + 1 : msg_prefix;
+  fprintf (stderr, "%s%s%s", pfx, *pfx ? " : " : "", buf);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/messages.h	(revision 16561)
@@ -0,0 +1,49 @@
+/**************************************************************************
+ *
+ * messages.h -- Message and error functions
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_MESSAGES
+#define H_MESSAGES
+
+#ifdef __GNUC__
+#define VOLATILE volatile
+#else
+#define VOLATILE
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+extern char *msg_prefix;
+
+void VOLATILE FatalError (int ExitCode, const char *fmt,...);
+void Message (const char *fmt,...);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.cpp	(revision 16561)
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * mgheap.c -- Heap routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "mgheap.h"
+
+#define SWAP(s, a, b) 						\
+do {								\
+     register int __i;						\
+     register char *__a = (a), *__b = (b);			\
+     for (__i = (s); __i; --__i)				\
+       {							\
+         register char __t = *__a;				\
+	 *__a++ = *__b; *__b++ = __t;				\
+       }							\
+} while(0)
+
+
+
+#define COPY(s, dst, src) 					\
+do {								\
+     register int __i;						\
+     register char *__dst = (dst), *__src = (src);		\
+     for (__i = (s); __i; --__i)				\
+       *__dst++ = *__src++;					\
+} while(0)
+
+
+
+static void 
+heap_heapify (void *heap, int size, int num, int curr, heap_comp hc)
+{
+  register int child = curr * 2;
+  while (child <= num)
+    {
+      if (child < num && hc ((char *) heap + child * size,
+			     (char *) heap + child * size - size) < 0)
+	++child;
+      if (hc ((char *) heap + (curr - 1) * size, (char *) heap + (child - 1) * size) > 0)
+	{
+	  SWAP (size, (char *) heap + (curr - 1) * size,
+		(char *) heap + (child - 1) * size);
+	  curr = child;
+	  child = child * 2;
+	}
+      else
+	break;
+    }
+}
+
+static void 
+heap_pullup (void *heap, int size, int num, heap_comp hc)
+{
+  register int curr = num;
+  register int parent = curr >> 1;
+  while (parent &&
+	 hc ((char *) heap + (curr - 1) * size, (char *) heap + (parent - 1) * size) < 0)
+    {
+      SWAP (size, (char *) heap + (curr - 1) * size,
+	    (char *) heap + (parent - 1) * size);
+      curr = parent;
+      parent = curr >> 1;
+    }
+}
+
+/************************************************************************
+ *
+ * NOTE: If you choose to change the comparison function the first thing 
+ *       you do after changing the function is call heap_build.
+ *
+ */
+void 
+heap_build (void *heap, int size, int num, heap_comp hc)
+{
+  register int i;
+  for (i = num / 2; i > 0; --i)
+    heap_heapify (heap, size, num, i, hc);
+}
+
+
+
+/****************************************************
+ *
+ * NOTE : The heap must be built before heap_sort is called. 
+ *        This has the effect of reversing the order of the array.
+ *		e.g. if your comparison function is designed to pull the
+ *                   biggest thing off the heap first then the result of
+ *		     sorting with this function will be to put the bigest
+ *                   thing at the end of the array.
+ *
+ */
+void 
+heap_sort (void *heap, int size, int num, heap_comp hc)
+{
+  register int i;
+  for (i = num; i > 1; --i)
+    {
+      SWAP (size, (char *) heap, (char *) heap + (i - 1) * size);
+      heap_heapify (heap, size, i - 1, 1, hc);
+    }
+}
+
+
+void 
+heap_deletehead (void *heap, int size, int *num, heap_comp hc)
+{
+  --(*num);
+  SWAP (size, (char *) heap, (char *) heap + *num * size);
+  heap_heapify (heap, size, *num, 1, hc);
+}
+
+
+void 
+heap_changedhead (void *heap, int size, int num, heap_comp hc)
+{
+  heap_heapify (heap, size, num, 1, hc);
+}
+
+
+/***********************************************************************
+ *
+ * This assumes that the item has been added to the end of the
+ * array that is the heap. But that num has not been changed. 
+ *
+ */
+void 
+heap_additem (void *heap, int size, int *num, heap_comp hc)
+{
+  ++(*num);
+  heap_pullup (heap, size, *num, hc);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/mgheap.h	(revision 16561)
@@ -0,0 +1,82 @@
+/**************************************************************************
+ *
+ * mgheap.h -- Heap routines
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+/* Care should be taken when using these routines as NO error checking is
+ * done.
+ */
+
+
+
+/* The heap comparison routine should return 
+ * <0 : If a should be before b
+ *  0 : If a and b are equal
+ * >0 : If a should be after b
+ */
+typedef int (*heap_comp) (void *a, void *b);
+
+
+/************************************************************************
+ *
+ * NOTE: If you choose to change the comparison function the first thing 
+ *       you do after changing the function is call heap_build.
+ *
+ */
+void heap_build (void *heap, int size, int num, heap_comp hc);
+
+
+
+/************************************************************************
+ *
+ * NOTE : The heap must be built before heap_sort is called.
+ *        This has the effect of reversing the order of the array.
+ *		e.g. if your comparison function is designed to pull the
+ *                   biggest thing off the heap first then the result of
+ *		     sorting with this function will be to put the bigest
+ *                   thing at the end of the array.
+ *
+ */
+void heap_sort (void *heap, int size, int num, heap_comp hc);
+
+/***********************************************************************
+ *
+ * NOTE: After deleting the head the heap will be one item shorter.
+ *       The deleted item will be placed at the end of the heap.
+ *
+ */
+void heap_deletehead (void *heap, int size, int *num, heap_comp hc);
+
+
+/***********************************************************************
+ *
+ * If you change the "value" of the root node of the heap then you
+ * should call this to re-pritorize the heap.
+ *
+ */
+void heap_changedhead (void *heap, int size, int num, heap_comp hc);
+
+
+/***********************************************************************
+ *
+ * This assumes that the item has been added to the end of the
+ * array that is the heap. But that num has not been changed. 
+ *
+ */
+void heap_additem (void *heap, int size, int *num, heap_comp hc);
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/netorder.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/netorder.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/netorder.h	(revision 16561)
@@ -0,0 +1,177 @@
+#ifndef NETORDER_H
+#define NETORDER_H
+
+#include "sysfuncs.h"
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+#include "win32in.h"
+#else
+# include <netinet/in.h>
+#endif
+
+#ifndef WORDS_BIGENDIAN
+
+/* double */
+#define HTOND(d)                                                              \
+        do {                                                                  \
+             unsigned long tmph, tmpl;                                        \
+	     memcpy ((void *) &tmph, (const void *) &d, sizeof(double) >> 1); \
+	     memcpy ((void *) &tmpl, (const void *) ((unsigned int) &d + (sizeof(double) >> 1)), sizeof (double) >> 1);   \
+	     tmph = htonl (tmph);                                             \
+	     tmpl = htonl (tmpl);                                             \
+	     memcpy ( (void *) &d, (const void *) &tmpl, sizeof (double) >> 1);                           \
+	     memcpy ((void *) ((unsigned int) &d + (sizeof(double) >> 1)), (const void *) &tmph, sizeof (double) >> 1);   \
+	}while(0)
+#define NTOHD(d)                                                              \
+        do {                                                                  \
+             unsigned long tmph, tmpl;                                        \
+	     memcpy ((void *) &tmph, (const void *) &d, sizeof(double) >> 1); \
+	     memcpy ((void *) &tmpl, (const void *) ((int) &d + (sizeof(double) >> 1)), sizeof (double) >> 1);   \
+	     tmph = ntohl (tmph);                                             \
+	     tmpl = ntohl (tmpl);                                             \
+	     memcpy((void *) &d, (const void *) &tmpl, sizeof (double) >> 1); \
+	     memcpy ((void *) ((int) &d + (sizeof(double) >> 1)), (const void *) &tmph, sizeof (double) >> 1);   \
+        }while(0)
+#define HTOND2(hd, nd)                                                        \
+        do {                                                                  \
+             unsigned long tmph, tmpl;                                        \
+	     memcpy ((void *) &tmph, (const void *) &hd, sizeof(double) >> 1);\
+	     memcpy ((void *) &tmpl, (const void *) ((unsigned int) &hd + (sizeof(double) >> 1)), sizeof (double) >> 1);  \
+	     tmph = htonl (tmph);                                             \
+	     tmpl = htonl (tmpl);                                             \
+	     memcpy((void *) &nd, (const void *) &tmpl, sizeof (double) >> 1);\
+	     memcpy ((void *) ((unsigned int) &nd + (sizeof(double) >> 1)), (void *) &tmph, sizeof (double) >> 1);  \
+	}while(0)
+#define NTOHD2(nd, hd)                                                        \
+        do {                                                                  \
+             unsigned long tmph, tmpl;                                        \
+	     memcpy ((void *) &nd, (void *) &tmph, sizeof(double) >> 1);      \
+	     memcpy ((void *) &tmpl, (const void *) &nd + (sizeof(double) >> 1), sizeof (double) >> 1);  \
+	     tmph = ntohl (tmph);                                             \
+	     tmpl = ntohl (tmpl);                                             \
+	     memcpy((void *) &hd, (const void *) &tmpl, sizeof (double) >> 1);\
+	     memcpy ((void *) &hd + (sizeof(double) >> 1), (const void *) &tmph, sizeof (double) >> 1);  \
+        }while(0)
+
+/* float */
+#define HTONF(f)                                                              \
+        do {                                                                  \
+             unsigned long tmp;                                               \
+             memcpy ((void *) &tmp, (const void *) &(f), sizeof (float));     \
+             HTONUL (tmp);                                                    \
+	     memcpy ((void *) &(f), (const void *) &tmp, sizeof (float));     \
+	}while(0)
+#define NTOHF(f)                                                              \
+        do {                                                                  \
+             unsigned long tmp;                                               \
+             memcpy ((void *) &tmp, (const void *) &(f), sizeof (float));     \
+	     NTOHUL (tmp);                                                    \
+	     memcpy ((void *) &(f), (const void *) &tmp, sizeof (float));     \
+	}while(0)
+#define HTONF2(hf, nf)                                                        \
+        do {                                                                  \
+             unsigned long tmp;                                               \
+             memcpy ((void *) &tmp, (const void *) &(hf), sizeof (float));    \
+             HTONUL (tmp);                                                    \
+	     memcpy ((void *) &(nf), (const void *) &tmp, sizeof (float));    \
+	}while(0)
+#define NTOHF2(nf, hf)                                                        \
+        do {                                                                  \
+             unsigned long tmp;                                               \
+             memcpy ((void *) &tmp, (const void *) &(nf), sizeof (float));    \
+	     NTOHUL (tmp);                                                    \
+	     memcpy ((void *) &(hf), (const void *) &tmp, sizeof (float));    \
+	}while(0)
+
+/* pointers */
+#define HTONP(p)          ((p) = (void *) htonl ((unsigned long) p))
+#define NTOHP(p)          ((p) = (void *) ntohl ((unsigned long) p))
+#define HTONP2(hp, np)    ((np) = (void *) htonl ((unsigned long) hp))
+#define NTOHP2(np, hp)    ((hp) = (void *) ntohl ((unsigned long) np))
+
+/* unsigned long */
+#define HTONUL(l)         ((l) = htonl((l)))
+#define NTOHUL(l)         ((l) = ntohl((l)))
+#define HTONUL2(hl, nl)   ((nl) = htonl ((hl)))
+#define NTOHUL2(nl, hl)   ((hl) = ntohl ((nl)))
+
+/* signed long */
+#define HTONSL(l)         ((l) = (long) htonl ((unsigned long) (l)))
+#define NTOHSL(l)         ((l) = (long) ntohl ((unsigned long) (l)))
+#define HTONSL2(hl, nl)   ((nl) = (long) htonl ((unsigned long) (hl)))
+#define NTOHSL2(nl, hl)   ((hl) = (long) ntohl ((unsigned long) (nl)))
+
+/* unsigned int */
+#define HTONUI(i)         ((i) = (unsigned int) htonl ((unsigned long) (i)))
+#define NTOHUI(i)         ((i) = (unsigned int) ntohl ((unsigned long) (i)))
+#define HTONUI2(hi, ni)   ((ni) = (unsigned int) htonl ((unsigned long) (hi)))
+#define NTOHUI2(ni, hi)   ((hi) = (unsigned int) ntohl ((unsigned long) (ni)))
+
+/* signed int */
+#define HTONSI(i)         ((i) = (int) htonl ((unsigned long) (i)))
+#define NTOHSI(i)         ((i) = (int) ntohl ((unsigned long) (i)))
+#define HTONSI2(hi, ni)   ((ni) = (int) htonl ((unsigned long) (hi)))
+#define NTOHSI2(ni, hi)   ((hi) = (int) ntohl ((unsigned long) (ni)))
+
+/* unsigned short */
+#define HTONUS(s)         ((s) = htons((s)))
+#define NTOHUS(s)         ((s) = ntohs((s)))
+#define HTONUS2(hs, ns)   ((ns) = htons((hs)))
+#define NTOHUS2(ns, hs)   ((hs) = ntohs((ns)))
+
+#else   /* WORDS_BIGENDIAN */
+
+/* double */
+#define HTOND(d)          (d)
+#define NTOHD(d)          (d)
+#define HTOND2(hd, nd)    ((hd) = (nd))
+#define NTOHD2(nd, hd)    ((nd) = (hd))
+
+/* float */
+#define HTONF(f)          (f)
+#define NTOHF(f)          (f)
+#define HTONF2(hf, nf)    ((nf) = (hf))
+#define NTOHF2(nf, hf)    ((hf) = (nf))
+
+/* pointers */
+#define HTONP(p)          (p)
+#define NTOHP(p)          (p)
+#define HTONP2(hp, np)    ((np) = (hp))
+#define NTOHP2(np, hp)    ((hp) = (np))
+
+/* unsigned long */
+#define HTONUL(l)         (l)
+#define NTOHUL(l)         (l)
+#define HTONUL2(hl, nl)   ((nl) = (hl))
+#define NTOHUL2(nl, hl)   ((hl) = (nl))
+
+/* signed long */
+#define HTONSL(l)         (l)
+#define NTOHSL(l)         (l)
+#define HTONSL2(hl, nl)   ((nl) = (hl))
+#define NTOHSL2(nl, hl)   ((hl) = (nl))
+
+/* unsigned int */
+#define HTONUI(i)         (i)
+#define NTOHUI(i)         (i)
+#define HTONUI2(hi, ni)   ((ni) = (hi))
+#define NTOHUI2(ni, hi)   ((hi) = (ni))
+
+/* signed int */
+#define HTONSI(i)         (i)
+#define NTOHSI(i)         (i)
+#define HTONSI2(hi, ni)   ((ni) = (hi))
+#define NTOHSI2(ni, hi)   ((hi) = (ni))
+
+/* unsigned short */
+#define HTONUS(s)         (s)
+#define NTOHUS(s)         (s)
+#define HTONUS2(hs, ns)   ((ns) = (hs))
+#define NTOHUS2(ns, hs)   ((hs) = (ns))
+
+
+
+#endif
+
+#endif /* netorder.h */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/non_ansi.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/non_ansi.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/non_ansi.h	(revision 16561)
@@ -0,0 +1,24 @@
+#if !defined(_NON_ANSI_H)
+#define _NON_ANSI_H 1
+
+#include "config.h" 
+# if !defined(__STDC__)
+
+#include <stdlib.h>  /* for FILE def */
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+int fileno(FILE *);
+int ftruncate(int, long);
+int pclose(FILE *);
+FILE *popen(const char *command, const char *type);
+int getpagesize();
+int strcasecmp(const char *, const char *);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* !defined STDC, ... */
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/pathmax.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/pathmax.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/pathmax.h	(revision 16561)
@@ -0,0 +1,53 @@
+/* Define PATH_MAX somehow.  Requires sys/types.h.
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#ifndef _PATHMAX_H
+#define _PATHMAX_H
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__))
+#include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined(PATH_MAX) && defined(_PC_PATH_MAX)
+#define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN)
+#include <sys/param.h>
+#endif
+
+#if !defined(PATH_MAX) && defined(MAXPATHLEN)
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+#endif /* _PATHMAX_H */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.cpp	(revision 16561)
@@ -0,0 +1,1074 @@
+/**************************************************************************
+ *
+ * perf_hash.c -- Perfect hashing functions
+ * Copyright (C) 1992  Bohdan S. Majewski
+ *
+ * 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.
+ *
+ **************************************************************************
+ *
+ * 1994 Neil Sharman
+ *      Small modifications were made to the interface so that it fitted in
+ *      with mg.
+ *
+ **************************************************************************/
+
+#define STRUCT
+
+#include <time.h>  /* for call to time () in SEED_RANDOM() */
+#include "sysfuncs.h"
+
+#include "local_strings.h"
+#include "memlib.h"
+#include "messages.h"
+#include "perf_hash.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#define FALSE 0
+#define TRUE  1
+
+#define STATIC 0
+
+/* Random Number stuff */
+
+long irandm (long is[2]);  /* from random.c */
+
+static long seed[] = {0, 0};
+#define RANDOM() irandm(seed)
+#define SEED_RANDOM(the_seed) do{ seed[0] = the_seed; }while(0)
+
+/* Max number of keys - must be a power of 2 minus 1 */
+static int MAX_M;
+
+
+static int MAX_CH;
+
+
+/* the width of the mapping tables  */
+static int MAX_L;
+
+
+
+#define c     1.23
+static int MAX_N;
+
+
+
+
+static u_char *translate;
+
+#define MEMBER 0
+#define ERASED 1
+
+
+
+/* direction constants for the edge oriented 3-graph representation */
+#define A              0
+#define B        (MAX_M)
+#define C          (2*B)
+
+/* NODEa, NODEb, NODEc, FIRST and NEXT are used to represent a 3-graph */
+static int *NODEa, *NODEb, *NODEc;
+static int *FIRST;
+static int *NEXT;
+
+
+#define norm(e) ((e) % MAX_M)
+/* in general should be ((e) % MAX_M) but MAX_M = 2^k - 1 */
+
+
+static char *mk;		/* for marking edges/vertices as removed */
+static int *g;			/* array g defines a minimal perfect hash function */
+
+
+
+/* stack of edges, created during the independency */
+/* test, used in the assignment step               */
+static struct
+  {
+    int sp;
+    int *st;
+  }
+S;
+#define push(s, e) s.st[s.sp++] = (e)
+#define pop(s, e)  e = s.st[--s.sp]
+
+#ifndef STRUCT
+static long **tb0, **tb1, **tb2;
+#else
+struct tb_entry **tb;
+#endif
+
+
+static int 
+unique (int v)
+{				/* checks if vertex v belongs to only one edge */
+  return (FIRST[v] != 0 && NEXT[FIRST[v]] == 0) ? FIRST[v] : 0;
+}				/*unique */
+
+
+
+
+
+
+
+
+static void 
+_delete (int v, int e)
+{				/* deletes edge e from list of v */
+
+  int b;
+
+  b = FIRST[v];
+  assert (norm (b) != 0);
+  if (norm (b) == e)
+    FIRST[v] = NEXT[FIRST[v]];
+  else
+    {
+      while (norm (NEXT[b]) != e)
+	{
+	  b = NEXT[b];
+	  assert (norm (b) != 0);
+	}
+      NEXT[b] = NEXT[NEXT[b]];
+    }
+}				/*delete */
+
+
+
+
+
+
+#if 0
+/* recursively removes the edges of a 3-graph which have at least */
+/* one unique vertex. The removed edges are placed on stack S     */
+static void 
+ph_remove (int e, int v)
+{
+  int b;
+  mk[e] = ERASED;
+  push (S, e);
+
+  if (NODEa[e] != v)
+    _delete (NODEa[e], e);
+  if (NODEb[e] != v)
+    _delete (NODEb[e], e);
+  if (NODEc[e] != v)
+    _delete (NODEc[e], e);
+
+  if (NODEa[e] != v && mk[b = norm (unique (NODEa[e]))] == MEMBER)
+    ph_remove (b, NODEa[e]);
+  if (NODEb[e] != v && mk[b = norm (unique (NODEb[e]))] == MEMBER)
+    ph_remove (b, NODEb[e]);
+  if (NODEc[e] != v && mk[b = norm (unique (NODEc[e]))] == MEMBER)
+    ph_remove (b, NODEc[e]);
+}
+#else
+
+
+static int StackPos, StackMax;
+static struct Stack
+  {
+    int e, v, m;
+  }
+ *Stack = NULL;
+
+#define StackAdd(_e, _v, _m)						\
+  do {									\
+    if (StackPos == StackMax)						\
+      {									\
+        StackMax <<= 1;							\
+	if (!(Stack = (struct Stack *)Xrealloc(Stack, sizeof(*Stack) * StackMax)))	\
+	  return(-1);							\
+      }									\
+    Stack[StackPos].e = (_e);						\
+    Stack[StackPos].v = (_v);						\
+    Stack[StackPos++].m = (_m);						\
+  } while(0)
+
+
+static int 
+ph_remove (int e, int v)
+{
+  if (!Stack)
+    {
+      StackMax = 256;
+      if (!(Stack = (struct Stack *) Xmalloc (sizeof (*Stack) * StackMax)))
+	return (-1);
+      StackPos = 0;
+    }
+  Stack[StackPos].e = e;
+  Stack[StackPos].v = v;
+  Stack[StackPos].m = 0;
+  ++StackPos;
+  while (StackPos)
+    {
+      int m, b;
+      --StackPos;
+      e = Stack[StackPos].e;
+      v = Stack[StackPos].v;
+      m = Stack[StackPos].m;
+
+
+      switch (m)
+	{
+	case 0:
+	  {
+	    mk[e] = ERASED;
+	    push (S, e);
+
+	    if (NODEa[e] != v)
+	      _delete (NODEa[e], e);
+	    if (NODEb[e] != v)
+	      _delete (NODEb[e], e);
+	    if (NODEc[e] != v)
+	      _delete (NODEc[e], e);
+
+	    if (NODEa[e] != v && mk[b = norm (unique (NODEa[e]))] == MEMBER)
+	      {
+		Stack[StackPos++].m = 1;
+		StackAdd (b, NODEa[e], 0);
+		break;
+	      }
+	  }
+	case 1:
+	  {
+	    if (NODEb[e] != v && mk[b = norm (unique (NODEb[e]))] == MEMBER)
+	      {
+		Stack[StackPos++].m = 2;
+		StackAdd (b, NODEb[e], 0);
+		break;
+	      }
+	  }
+	case 2:
+	  if (NODEc[e] != v && mk[b = norm (unique (NODEc[e]))] == MEMBER)
+	    StackAdd (b, NODEc[e], 0);
+	}
+    }
+  return (0);
+}
+#endif
+
+
+
+
+
+/* returns TRUE if and only if set of edges created in the tree_builder */
+/* procedure form a 3-graph with independent edges. Edges of a 3-graph  */
+/* are independent iff repeated deletion of edges containing vertices   */
+/* of degree 1 results in a graph with no edges in it.                  */
+static int 
+acyclic (int m, int n)
+{
+
+  int v, e;
+  /* build list of edges for each node */
+  for (v = 1; v <= n; FIRST[v++] = 0);
+  for (e = 1; e <= m; ++e)
+    {
+      NEXT[A + e] = FIRST[NODEa[e]];
+      FIRST[NODEa[e]] = A + e;
+      NEXT[B + e] = FIRST[NODEb[e]];
+      FIRST[NODEb[e]] = B + e;
+      NEXT[C + e] = FIRST[NODEc[e]];
+      FIRST[NODEc[e]] = C + e;
+
+      /* mark edge e as belonging to the hypergraph */
+      mk[e] = MEMBER;
+    }
+
+
+  S.sp = 0;			/* empty the stack */
+  mk[0] = ERASED;		/* a sentinel for the test below */
+  for (v = 1; v <= n; ++v)
+    {
+      if (mk[e = norm (unique (v))] == MEMBER)
+	ph_remove (e, v);
+    }
+
+  /* check if any edges were left in the graph */
+  return S.sp == m;
+}				/*acyclic */
+
+
+
+
+
+
+
+
+/* repeatedly maps words into edges of a 3-graph until the edges */
+/* are independent.                                              */
+static int 
+tree_builder (int num, u_char ** keys, int *n)
+{
+
+  int e, k = 0;
+  int i, j;
+  u_char *w;
+
+  do
+    {
+      *n = (int) (num * c + 1);
+      /* initialize tables of random integers */
+      for (i = 0; i < MAX_CH; ++i)
+	for (j = 0; j < MAX_L; ++j)
+	  {
+#ifndef STRUCT
+	    tb0[i][j] = RANDOM () % *n;
+	    tb1[i][j] = RANDOM () % *n;
+	    tb2[i][j] = RANDOM () % *n;
+#else
+	    struct tb_entry *tbp = &tb[i][j];
+	    tbp->tb0 = RANDOM () % *n;
+	    tbp->tb1 = RANDOM () % *n;
+	    tbp->tb2 = RANDOM () % *n;
+#endif
+	  }
+
+      /* compute an edge (NODEa[e], NODEb[e], NODEc[e]) for each word */
+      for (e = 1; e <= num; ++e)
+	{
+	  /*generate an edge corresponding to the ith word */
+	  int x = 0, y = 0, z = 0, l;
+	  w = keys[e - 1];
+
+	  l = *w++;
+	  j = (l - 1) % MAX_L;
+
+	  do
+	    {
+#ifndef STRUCT
+	      x += tb0[translate[*w]][j];
+	      y += tb1[translate[*w]][j];
+	      z += tb2[translate[*w]][j];
+#else
+	      struct tb_entry *tbp = &tb[translate[*w]][j];
+	      x += tbp->tb0;
+	      y += tbp->tb1;
+	      z += tbp->tb2;
+#endif
+	      if (++j >= MAX_L)
+		j = 0;
+	      ++w;
+	    }
+	  while (--l);
+	  x = (x % *n) + 1;
+	  y = (y % *n) + 1;
+	  z = (z % *n) + 1;
+	  if (y == x && ++y > *n)
+	    y = 1;
+	  if (z == x && ++z > *n)
+	    z = 1;
+	  if (z == y)
+	    {
+	      if (++z > *n)
+		z = 1;
+	      if (z == x && ++z > *n)
+		z = 1;
+	    }
+
+	  NODEa[e] = x;
+	  NODEb[e] = y;
+	  NODEc[e] = z;
+	}
+      if (++k > 50)		/* make sure we will not wait for ever */
+	FatalError (1, "Unable to generate the perfect hash function. "
+		    "This is probably because there aren't enough words");
+    }
+  while (!acyclic (num, *n));
+  return k;
+}				/*tree_builder */
+
+
+
+
+
+
+
+
+
+/* calculates values of the g function/array so that i-th word is */
+/* placed at the (i-1)st position of a hash table.                */
+static void 
+assign (int n)
+{
+
+  int v, w = 0, e;
+
+  /* mark all the vertices of the 3-graph as unassigned */
+  for (v = 1; v <= n; mk[v++] = FALSE);
+
+  while (S.sp > 0)
+    {
+      int _xor = 0;
+      pop (S, e);
+      v = NODEa[e];
+      if (mk[v])
+	_xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      v = NODEb[e];
+      if (mk[v])
+	_xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      v = NODEc[e];
+      if (mk[v])
+	_xor ^= g[v];
+      else
+	{
+	  g[w = v] = 0;
+	  mk[v] = TRUE;
+	}
+      g[w] = (e - 1) ^ _xor;
+    }
+}				/*assign */
+
+
+
+
+
+
+
+
+
+/*check returns 0 if no error in mphf is detected, else returns error code */
+static int 
+check (int m)
+{
+
+  int e, err_code = 0;
+
+  /*for each word compute its place in hash table and check if correct */
+  for (e = 1; e <= m; ++e)
+    {
+      if ((e - 1) != (g[NODEa[e]] ^ g[NODEb[e]] ^ g[NODEc[e]]))
+	{
+	  (void) fprintf (stderr, "Error for %dth word detected.\n", e);
+	  err_code = e;
+	}
+    }
+  return err_code;
+}				/*check */
+
+
+
+
+
+
+
+#if 0
+void 
+outputh (int m, int n)
+{
+
+  int i, j, w = (int) ceil (log10 ((double) n + 1.0)) + 1;
+
+#if 0
+  char *PRG =
+  "int h(char *s)\n"
+  "{\n"
+  "  int u, v, w, i;\n"
+  "i = (strlen(s) - 1) % _HXL;\n"
+  "  u = v = w = 0;\n"
+  "  while(*s)\n"
+  "    {\n"
+  "      u += mt1[(*s) - _HFC][i];\n"
+  "      v += mt2[(*s) - _HFC][i];\n"
+  "      w += mt3[(*s++) - _HFC][i];\n"
+  "      if(++i >= _HXL) i = 0;\n"
+  "    }\n"
+  "  u %= _HFN; v %= _HFN; w %= _HFN;\n"
+  "  if(u == v && ++v >= _HFN) v = 0;\n"
+  "  if(w == u && ++w >= _HFN) w = 0;\n"
+  "  if(w == v && ++w >= _HFN) w = 0;\n"
+  "  if(w == u && ++w >= _HFN) w = 0;\n"
+  "  return (g[u] ^ g[v] ^ g[w]) % _HFM;\n"
+  "};\n\n";
+#endif
+
+  char *PRG =
+  "int h(char *s)\n"
+  "{\n"
+  "  int u, v, w, i;\n"
+  "  i = (strlen(s) - 1) % _HXL;\n"
+  "  u = v = w = 0;\n"
+  "  while(*s)\n"
+  "    {\n"
+  "      if((u += mt1[(*s) - _HFC][i]) >= _HFN) u -= _HFN;\n"
+  "      if((v += mt2[(*s) - _HFC][i]) >= _HFN) v -= _HFN;\n"
+  "      if((w += mt3[(*s++) - _HFC][i]) >= _HFN) w -= _HFN;\n"
+  "      if(++i >= _HXL) i = 0;\n"
+  "    }\n"
+  "  if(u == v && ++v >= _HFN) v = 0;\n"
+  "  if(w == v) \n"
+  "    {\n"
+  "      if(++w >= _HFN) w = 0;\n"
+  "      if(w == u && ++w >= _HFN) w = 0;\n"
+  "    }\n"
+  "  else\n"
+  "    if(w == u)\n"
+  "      {\n"
+  "	     if(++w >= _HFN) w = 0;\n"
+  "	     if(w == v && ++w >= _HFN) w = 0;\n"
+  "      }\n"
+  "  return ((g[u] ^ g[v] ^ g[w]) > _HFM)? 0 : (g[u] ^ g[v] ^ g[w]);\n"
+  "}\n\n";
+
+  printf ("#define _HFN %d\n#define _HFC %d\n", n, fc);
+  printf ("#define _HFM %d\n#define _HXL %d\n", m, MAX_L);
+  printf ("#define _HFA %d\n\nstatic int g[_HFN] = {\n", lc - fc + 1);
+  for (i = 1; i < n; ++i)
+    {
+      if (i % 8 == 0)
+	putchar ('\n');
+      printf ("%5d, ", g[i]);
+    }
+  printf ("%5d};\n\n", g[n]);
+
+  printf ("static int mt1[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; ++j)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; ++i)
+	printf ("%*d,", w, tb0[j][i]);
+      printf ("%*d},\n", w, tb0[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; ++i)
+    printf ("%*d,", w, tb0[lc][i]);
+  printf ("%*d}\n", w, tb0[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("static int mt2[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; ++j)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; ++i)
+	printf ("%*d,", w, tb1[j][i]);
+      printf ("%*d},\n", w, tb1[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; ++i)
+    printf ("%*d,", w, tb1[lc][i]);
+  printf ("%*d}\n", w, tb1[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("static int mt3[_HFA][_HXL] = {\n");
+  for (j = fc; j < lc; ++j)
+    {
+      printf ("{");
+      for (i = 0; i < MAX_L - 1; ++i)
+	printf ("%*d,", w, tb2[j][i]);
+      printf ("%*d},\n", w, tb2[j][MAX_L - 1]);
+    }
+  printf ("{");
+  for (i = 0; i < MAX_L - 1; ++i)
+    printf ("%*d,", w, tb2[lc][i]);
+  printf ("%*d}\n", w, tb2[lc][MAX_L - 1]);
+  printf ("};\n\n");
+
+  printf ("%s", PRG);
+}				/*outputh */
+#endif
+
+
+
+
+
+
+
+static void 
+make_trans_func (int num, u_char ** keys)
+{
+  int i, j;
+
+  memset (translate, '\0', sizeof (translate));
+
+  MAX_L = 1;
+  for (i = 0; i < num; ++i)
+    {
+      unsigned len = keys[i][0];
+      u_char *s = keys[i] + 1;
+      for (; len; --len, ++s)
+	translate[*s] = 1;
+      if (i)
+	{
+	  int l;
+	  l = prefixlen (keys[i - 1], keys[i]);
+	  if (l + 1 > MAX_L)
+	    MAX_L = l + 1;
+	}
+    }
+  j = 0;
+  for (i = 0; i < 256; ++i)
+    if (translate[i])
+      translate[i] = j++;
+  MAX_CH = j;
+}
+
+
+static void 
+temp_free (void)
+{
+  if (NODEa)
+    Xfree (NODEa);
+  if (NODEb)
+    Xfree (NODEb);
+  if (NODEc)
+    Xfree (NODEc);
+  if (FIRST)
+    Xfree (FIRST);
+  if (NEXT)
+    Xfree (NEXT);
+  if (S.st)
+    Xfree (S.st);
+  if (mk)
+    Xfree (mk);
+}
+
+static void 
+all_free (void)
+{
+  int i;
+  temp_free ();
+  if (translate)
+    Xfree (translate);
+  if (g)
+    Xfree (g);
+#ifndef STRUCT
+  for (i = 0; i < MAX_CH; ++i)
+    {
+      if (tb0 && tb0[i])
+	Xfree (tb0[i]);
+      if (tb1 && tb1[i])
+	Xfree (tb1[i]);
+      if (tb2 && tb2[i])
+	Xfree (tb2[i]);
+    }
+  if (tb0)
+    Xfree (tb0);
+  if (tb1)
+    Xfree (tb1);
+  if (tb2)
+    Xfree (tb2);
+#else
+  for (i = 0; i < MAX_CH; ++i)
+    if (tb && tb[i])
+      Xfree (tb[i]);
+  if (tb)
+    Xfree (tb);
+#endif
+}
+
+static int 
+allocate_memory (void)
+{
+  int i, ok = 1;
+  NODEa = (int *) Xmalloc (sizeof (int) * (MAX_M + 1));
+  NODEb = (int *)Xmalloc (sizeof (int) * (MAX_M + 1));
+  NODEc = (int *)Xmalloc (sizeof (int) * (MAX_M + 1));
+  FIRST = (int *)Xmalloc (sizeof (int) * (MAX_N + 1));
+  NEXT = (int *)Xmalloc (sizeof (int) * (MAX_M + 1) * 3);
+  S.st = (int *)Xmalloc (sizeof (int) * MAX_M);
+  mk = (char *)Xmalloc (sizeof (char) * (MAX_N + 1));
+  g = (int *)Xmalloc (sizeof (int) * (MAX_N + 1));
+#ifndef STRUCT
+  tb0 = Xmalloc (sizeof (int *) * MAX_CH);
+  tb1 = Xmalloc (sizeof (int *) * MAX_CH);
+  tb2 = Xmalloc (sizeof (int *) * MAX_CH);
+  for (i = 0; i < MAX_CH; ++i)
+    {
+      if (tb0)
+	if (!(tb0[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+      if (tb1)
+	if (!(tb1[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+      if (tb2)
+	if (!(tb2[i] = Xmalloc (sizeof (long) * MAX_L)))
+	    ok = 0;
+    }
+
+  if (!(NODEa && NODEb && NODEc && FIRST && NEXT && S.st && mk && g &&
+	tb0 && tb1 && tb2 && ok))
+    {
+      all_free ();
+      return (-1);
+    }
+#else
+  tb = (tb_entry **)Xmalloc (sizeof (struct tb_entry *) * MAX_CH);
+  for (i = 0; i < MAX_CH; ++i)
+    if (tb)
+      if (!(tb[i] = (tb_entry *)Xmalloc (sizeof (struct tb_entry) * MAX_L)))
+	  ok = 0;
+
+  if (!(NODEa && NODEb && NODEc && FIRST && NEXT && S.st && mk && g &&
+	tb && ok))
+    {
+      all_free ();
+      return (-1);
+    }
+#endif
+  return (0);
+}
+
+
+perf_hash_data *
+gen_hash_func (int num, u_char ** keys, int r)
+{
+  int n;
+  perf_hash_data *phd;
+  translate = (u_char *)Xmalloc (sizeof (u_char) * 256);
+  memset(translate, '\0', sizeof (u_char) * 256);  /* [RPAP - Feb 97: WIN32 Port] */
+  make_trans_func (num, keys);
+
+  if (r <= 0)
+    SEED_RANDOM ((long) time ((time_t *) NULL));
+  else
+    SEED_RANDOM (r);
+
+  MAX_M = num + 1;
+
+  MAX_N = MAX_M + MAX_M / 4;
+
+  if (allocate_memory () == -1)
+    return (NULL);
+
+  /* construct an "acyclic" hypergraph */
+  tree_builder (num, keys, &n);
+  /* compute function g */
+  assign (n);
+
+  if (check (num) != 0)
+    FatalError (1, "An error has been detected in the mphf.");
+  temp_free ();
+
+  if (!(phd = (perf_hash_data *)Xmalloc (sizeof (perf_hash_data))))
+    return (NULL);
+
+  memcpy ((void *) g, (const void *) &g[1], n * sizeof (int));
+
+  phd->MAX_L = MAX_L;
+  phd->MAX_N = n;
+  phd->MAX_M = num;
+  phd->MAX_CH = MAX_CH;
+  phd->translate = translate;
+  phd->g = g;
+#ifndef STRUCT
+  phd->tb0 = tb0;
+  phd->tb1 = tb1;
+  phd->tb2 = tb2;
+#else
+  phd->tb = tb;
+#endif
+  return (phd);
+}
+
+int 
+write_perf_hash_data (FILE * f, perf_hash_data * phd)
+{
+  int tot, i;
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONSI(phd->MAX_L);
+  HTONSI(phd->MAX_N);
+  HTONSI(phd->MAX_M);
+  HTONSI(phd->MAX_CH);
+
+  tot = fwrite ((char *) &phd->MAX_L, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_N, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_M, sizeof (int), 1, f) * sizeof (int);
+  tot += fwrite ((char *) &phd->MAX_CH, sizeof (int), 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(phd->MAX_L);
+  NTOHSI(phd->MAX_N);
+  NTOHSI(phd->MAX_M);
+  NTOHSI(phd->MAX_CH);
+
+  tot += fwrite ((char *) phd->translate, sizeof (u_char), 256, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; ++i)
+    HTONSI(phd->g[i]);
+
+  tot += fwrite ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; ++i)
+    NTOHSI(phd->g[i]);
+
+#ifndef STRUCT
+  for (i = 0; i < phd->MAX_CH; ++i)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int j;
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  HTONSI(phd->tb0[i][j]);
+	  HTONSI(phd->tb1[i][j]);
+	  HTONSI(phd->tb2[i][j]);
+	}
+
+      tot += fwrite ((char *) phd->tb0[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fwrite ((char *) phd->tb1[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fwrite ((char *) phd->tb2[i], sizeof (int), phd->MAX_L, f) *
+        sizeof (int);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  NTOHSI(phd->tb0[i][j]);
+	  NTOHSI(phd->tb1[i][j]);
+	  NTOHSI(phd->tb2[i][j]);
+	}
+    }
+#else
+  for (i = 0; i < phd->MAX_CH; ++i)
+    {
+      /* [RPAP - Jan 97: Endian Ordering] */
+      int j;
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  HTONSL(phd->tb[i][j].tb0);
+	  HTONSL(phd->tb[i][j].tb1);
+	  HTONSL(phd->tb[i][j].tb2);
+	}
+
+      tot += fwrite ((char *) phd->tb[i], sizeof (struct tb_entry), phd->MAX_L, f) *
+	sizeof (struct tb_entry);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  NTOHSL(phd->tb[i][j].tb0);
+	  NTOHSL(phd->tb[i][j].tb1);
+	  NTOHSL(phd->tb[i][j].tb2);
+	}
+    }
+#endif
+  return (tot);
+}
+
+
+perf_hash_data *
+read_perf_hash_data (FILE * f)
+{
+  perf_hash_data *phd;
+  int i, tot, ok = 1;
+  if (!(phd = (perf_hash_data *)Xmalloc (sizeof (perf_hash_data))))
+    return (NULL);
+  tot = fread ((char *) &phd->MAX_L, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_N, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_M, sizeof (int), 1, f) * sizeof (int);
+  tot += fread ((char *) &phd->MAX_CH, sizeof (int), 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHSI(phd->MAX_L);
+  NTOHSI(phd->MAX_N);
+  NTOHSI(phd->MAX_M);
+  NTOHSI(phd->MAX_CH);
+
+  if (tot != 4 * sizeof (int))
+      return (NULL);
+  phd->translate = (u_char *)Xmalloc (sizeof (u_char) * 256);
+  phd->g = (int *)Xmalloc (sizeof (int) * (phd->MAX_N + 1));
+#ifndef STRUCT
+  phd->tb0 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  phd->tb1 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  phd->tb2 = Xmalloc (sizeof (int *) * phd->MAX_CH);
+  for (i = 0; i < phd->MAX_CH; ++i)
+    {
+      if (phd->tb0)
+	if (!(phd->tb0[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+      if (phd->tb1)
+	if (!(phd->tb1[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+      if (phd->tb2)
+	if (!(phd->tb2[i] = Xmalloc (sizeof (long) * phd->MAX_L)))
+	    ok = 0;
+    }
+  if (!(phd->translate && phd->g && phd->tb0 && phd->tb1 && phd->tb2 && ok))
+    {
+      if (phd->translate)
+	Xfree (phd->translate);
+      if (phd->g)
+	Xfree (phd->g);
+      for (i = 0; i < MAX_CH; ++i)
+	{
+	  if (phd->tb0 && phd->tb0[i])
+	    Xfree (phd->tb0[i]);
+	  if (phd->tb1 && phd->tb1[i])
+	    Xfree (phd->tb1[i]);
+	  if (phd->tb2 && phd->tb2[i])
+	    Xfree (phd->tb2[i]);
+	}
+      if (phd->tb0)
+	Xfree (phd->tb0);
+      if (phd->tb1)
+	Xfree (phd->tb1);
+      if (phd->tb2)
+	Xfree (phd->tb2);
+      Xfree (phd);
+      return (NULL);
+    }
+  tot += fread ((char *) phd->translate, sizeof (u_char), 256, f);
+  tot += fread ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; ++i)
+    NTOHSI(phd->g[i]);
+
+  for (i = 0; i < phd->MAX_CH; ++i)
+    {
+      int j;
+
+      tot += fread ((char *) phd->tb0[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fread ((char *) phd->tb1[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+      tot += fread ((char *) phd->tb2[i], sizeof (long), phd->MAX_L, f) *
+        sizeof (int);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  NTOHSI(phd->tb0[i][j]);
+	  NTOHSI(phd->tb1[i][j]);
+	  NTOHSI(phd->tb2[i][j]);
+	}
+    }
+#else
+  phd->tb = (struct tb_entry **)Xmalloc (sizeof (struct tb_entry *) * phd->MAX_CH);
+  for (i = 0; i < phd->MAX_CH; ++i)
+    if (phd->tb)
+      if (!(phd->tb[i] = (struct tb_entry *)Xmalloc (sizeof (struct tb_entry) * phd->MAX_L)))
+	  ok = 0;
+  if (!(phd->translate && phd->g && phd->tb && ok))
+    {
+      if (phd->translate)
+	Xfree (phd->translate);
+      if (phd->g)
+	Xfree (phd->g);
+      for (i = 0; i < MAX_CH; ++i)
+	if (phd->tb && phd->tb[i])
+	  Xfree (phd->tb[i]);
+      if (phd->tb)
+	Xfree (phd->tb);
+      Xfree (phd);
+      return (NULL);
+    }
+  tot += fread ((char *) phd->translate, sizeof (u_char), 256, f);
+  tot += fread ((char *) phd->g, sizeof (int), phd->MAX_N + 1, f) * sizeof (int);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = 0; i < phd->MAX_N + 1; ++i)
+    NTOHSI(phd->g[i]);
+
+  for (i = 0; i < phd->MAX_CH; ++i)
+    {
+      int j;
+
+      tot += fread ((char *) phd->tb[i], sizeof (struct tb_entry), phd->MAX_L, f) *
+	sizeof (struct tb_entry);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      for (j = 0; j < phd->MAX_L; ++j)
+	{
+	  NTOHSL(phd->tb[i][j].tb0);
+	  NTOHSL(phd->tb[i][j].tb1);
+	  NTOHSL(phd->tb[i][j].tb2);
+	}
+    }
+#endif
+  return (phd);
+}
+
+void 
+free_perf_hash (perf_hash_data * phd)
+{
+  int i;
+  if (phd->translate)
+    Xfree (phd->translate);
+  if (phd->g)
+    Xfree (phd->g);
+  for (i = 0; i < phd->MAX_CH; ++i)
+    if (phd->tb && phd->tb[i])
+      Xfree (phd->tb[i]);
+  if (phd->tb)
+    Xfree (phd->tb);
+  Xfree (phd);
+}
+
+
+int 
+perf_hash (perf_hash_data * phd, u_char * s)
+{
+  int u, v, w, i, l;
+  l = *s++;
+  i = (l - 1) % phd->MAX_L;
+  u = v = w = 0;
+  while (l)
+    {
+#ifndef STRUCT
+      if ((u += phd->tb0[phd->translate[*s]][i]) >= phd->MAX_N)
+	u -= phd->MAX_N;
+      if ((v += phd->tb1[phd->translate[*s]][i]) >= phd->MAX_N)
+	v -= phd->MAX_N;
+      if ((w += phd->tb2[phd->translate[*s]][i]) >= phd->MAX_N)
+	w -= phd->MAX_N;
+#else
+      struct tb_entry *tbp = &phd->tb[phd->translate[*s]][i];
+      if ((u += tbp->tb0) >= phd->MAX_N)
+	u -= phd->MAX_N;
+      if ((v += tbp->tb1) >= phd->MAX_N)
+	v -= phd->MAX_N;
+      if ((w += tbp->tb2) >= phd->MAX_N)
+	w -= phd->MAX_N;
+#endif
+      if (++i >= phd->MAX_L)
+	i = 0;
+      --l;
+      ++s;
+    }
+  if (u == v && ++v >= phd->MAX_N)
+    v = 0;
+  if (w == v)
+    {
+      if (++w >= phd->MAX_N)
+	w = 0;
+      if (w == u && ++w >= phd->MAX_N)
+	w = 0;
+    }
+  else if (w == u)
+    {
+      if (++w >= phd->MAX_N)
+	w = 0;
+      if (w == v && ++w >= phd->MAX_N)
+	w = 0;
+    }
+  return ((phd->g[u] ^ phd->g[v] ^ phd->g[w]) > phd->MAX_M) ? 0 :
+    (phd->g[u] ^ phd->g[v] ^ phd->g[w]);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/perf_hash.h	(revision 16561)
@@ -0,0 +1,52 @@
+/**************************************************************************
+ *
+ * perf_hash.h -- Perfect hashing functions
+ * Copyright (C) 1994  Bohdan S. Majewski and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_PERF_HASH
+#define H_PERF_HASH
+
+struct tb_entry
+  {
+    long tb0, tb1, tb2;
+  };
+
+typedef struct
+  {
+    int MAX_L;
+    int MAX_N;
+    int MAX_M;
+    int MAX_CH;
+    u_char *translate;
+    int *g;
+    struct tb_entry **tb;
+  }
+perf_hash_data;
+
+perf_hash_data *gen_hash_func (int num, u_char ** keys, int r);
+
+int perf_hash (perf_hash_data * phd, u_char * s);
+
+int write_perf_hash_data (FILE * f, perf_hash_data * phd);
+
+void free_perf_hash (perf_hash_data * phd);
+
+perf_hash_data *read_perf_hash_data (FILE * f);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/random.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/random.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/random.cpp	(revision 16561)
@@ -0,0 +1,211 @@
+/**************************************************************************
+ *
+ * random.c -- pseudo random number generator
+ * Copyright (C) 1994  Chris Wallace (csw@bruce.cs.monash.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+/*
+ *	A random number generator called as a function by
+ *	random (iseed)	or	irandm (iseed)
+ *	The parameter should be a pointer to a 2-element long vector.
+ *	The first function returns a double uniform in 0 .. 1.
+ *	The second returns a long integer uniform in 0 .. 2**31-1
+ *	Both update iseed[] in exactly the same way.
+ *	iseed[] must be a 2-element integer vector.
+ *	The initial value of the second element may be anything.
+ *
+ *	The period of the random sequence is 2**32 * (2**32-1)
+ *	The table mt[0:127] is defined by mt[i] = 69069 ** (128-i)
+ */
+
+#define MASK ((long) 593970775)
+/*	or in hex, 23674657	*/
+
+#define SCALE ((double) 1.0 / (1024.0 * 1024.0 * 1024.0 * 2.0))
+/*	i.e. 2 to power -31	*/
+
+static long mt [128] =   {
+      902906369,
+     2030498053,
+     -473499623,
+     1640834941,
+      723406961,
+     1993558325,
+     -257162999,
+    -1627724755,
+      913952737,
+      278845029,
+     1327502073,
+    -1261253155,
+      981676113,
+    -1785280363,
+     1700077033,
+      366908557,
+    -1514479167,
+     -682799163,
+      141955545,
+     -830150595,
+      317871153,
+     1542036469,
+     -946413879,
+    -1950779155,
+      985397153,
+      626515237,
+      530871481,
+      783087261,
+    -1512358895,
+     1031357269,
+    -2007710807,
+    -1652747955,
+    -1867214463,
+      928251525,
+     1243003801,
+    -2132510467,
+     1874683889,
+     -717013323,
+      218254473,
+    -1628774995,
+    -2064896159,
+       69678053,
+      281568889,
+    -2104168611,
+     -165128239,
+     1536495125,
+      -39650967,
+      546594317,
+     -725987007,
+     1392966981,
+     1044706649,
+      687331773,
+    -2051306575,
+     1544302965,
+     -758494647,
+    -1243934099,
+      -75073759,
+      293132965,
+    -1935153095,
+      118929437,
+      807830417,
+    -1416222507,
+    -1550074071,
+      -84903219,
+     1355292929,
+     -380482555,
+    -1818444007,
+     -204797315,
+      170442609,
+    -1636797387,
+      868931593,
+     -623503571,
+     1711722209,
+      381210981,
+     -161547783,
+     -272740131,
+    -1450066095,
+     2116588437,
+     1100682473,
+      358442893,
+    -1529216831,
+     2116152005,
+     -776333095,
+     1265240893,
+     -482278607,
+     1067190005,
+      333444553,
+       86502381,
+      753481377,
+       39000101,
+     1779014585,
+      219658653,
+     -920253679,
+     2029538901,
+     1207761577,
+    -1515772851,
+     -236195711,
+      442620293,
+      423166617,
+    -1763648515,
+     -398436623,
+    -1749358155,
+     -538598519,
+     -652439379,
+      430550625,
+    -1481396507,
+     2093206905,
+    -1934691747,
+     -962631983,
+     1454463253,
+    -1877118871,
+     -291917555,
+    -1711673279,
+      201201733,
+     -474645415,
+      -96764739,
+    -1587365199,
+     1945705589,
+     1303896393,
+     1744831853,
+      381957665,
+     2135332261,
+      -55996615,
+    -1190135011,
+     1790562961,
+    -1493191723,
+      475559465,
+          69069
+		};
+
+double 
+random (long is [2])
+{
+	long it, leh, nit;
+
+	it = is [0];
+	leh = is [1];
+	if (it <= 0)	
+		it = (it + it) ^ MASK;
+	else
+		it = it + it;
+	nit = it - 1;
+/*	to ensure all-ones pattern omitted    */
+	leh = leh * mt[nit & 127] + nit;
+	is [0] = it;    is [1] = leh;
+	if (leh < 0) leh = ~leh;
+	return (SCALE * ((long) (leh | 1)));
+}
+
+
+
+long 
+irandm (long is [2])
+{
+	long it, leh, nit;
+
+	it = is [0];
+	leh = is [1];
+	if (it <= 0)	
+		it = (it + it) ^ MASK;
+	else
+		it = it + it;
+	nit = it - 1;
+/*	to ensure all-ones pattern omitted    */
+	leh = leh * mt[nit & 127] + nit;
+	is [0] = it;    is [1] = leh;
+	if (leh < 0) leh = ~leh;
+	return (leh);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/random.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/random.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/random.h	(revision 16561)
@@ -0,0 +1,42 @@
+/**************************************************************************
+ *
+ * random.h -- pseudo random number generator
+ * Copyright (C) 1994  Chris Wallace (csw@bruce.cs.monash.edu.au)
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+/*
+ *	A random number generator called as a function by
+ *	random (iseed)	or	irandm (iseed)
+ *	The parameter should be a pointer to a 2-element long vector.
+ *	The first function returns a double uniform in 0 .. 1.
+ *	The second returns a long integer uniform in 0 .. 2**31-1
+ *	Both update iseed[] in exactly the same way.
+ *	iseed[] must be a 2-element integer vector.
+ *	The initial value of the second element may be anything.
+ *
+ *	The period of the random sequence is 2**32 * (2**32-1)
+ *	The table mt[0:127] is defined by mt[i] = 69069 ** (128-i)
+ */
+
+double random (long is [2]);
+long irandm (long is [2]);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.cpp	(revision 16561)
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * simplefrenchstem.c -- a simple french stemmer
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "simplefrenchstem.h"
+#include "unitool.h"
+
+
+/* =========================================================================
+ * Function: simplefrenchstem
+ * Description: a simple french stemmer
+ * Input: a word string with the length in the first byte
+ * Output: the stemmed word
+ * ========================================================================= */
+
+void simplefrenchstem (unsigned char *word) {      
+  unsigned short out[256]; /* temp space */
+  unsigned short *wordstart; /* points to first letter of word */
+  int last;                  /* last points to the last character */
+
+  /* decode */
+  utf8_word_to_unicode (word, out, 255);
+  wordstart = out + 1;
+  last = out[0]-1;
+  
+
+  if (last > 4) {
+    if (wordstart[last]=='x') {
+      if (wordstart[last-1]=='u' && wordstart[last-2]=='a') {
+	wordstart[last-1]='l';
+      }
+      --last;
+
+    } else {
+      if (last>=0 && wordstart[last]=='s') --last;
+      if (last>=0 && wordstart[last]=='r') --last;
+      if (last>=0 && wordstart[last]=='e') --last;
+
+      /* letter with accent e + ' -- there are two possible encodings  */
+      if (last>=0 && wordstart[last]==0xe9) { 
+	--last;
+      } else if (last>=1 && wordstart[last-1]=='e' && wordstart[last]==0x301) {
+	last -= 2; 
+      }
+
+      if (last >= 1 && wordstart[last]==wordstart[last-1]) --last;
+    }  /* end else */
+
+    out[0] = (unsigned char)(last+1);
+  } /* end if (len > 4) */
+
+  /* re-code, make sure the result is not longer than the input */
+  unicode_to_utf8_word (out, word, word[0]+1);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/simplefrenchstem.h	(revision 16561)
@@ -0,0 +1,26 @@
+/**************************************************************************
+ *
+ * simplefrenchstem.h -- a simple french stemmer
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef SIMPLEFRENCHSTEM_H
+#define SIMPLEFRENCHSTEM_H
+
+void simplefrenchstem (unsigned char *word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/strcasecmp.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/strcasecmp.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/strcasecmp.cpp	(revision 16561)
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c	5.5 (Berkeley) 11/24/87";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static unsigned char charmap[] = {
+	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(char *s1, char *s2)
+{
+    register unsigned char u1, u2;
+
+    for (;;) {
+	u1 = (unsigned char) *s1++;
+	u2 = (unsigned char) *s2++;
+	if (charmap[u1] != charmap[u2]) {
+	    return charmap[u1] - charmap[u2];
+	}
+	if (u1 == '\0') {
+	    return 0;
+	}
+    }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/strstr.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/strstr.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/strstr.cpp	(revision 16561)
@@ -0,0 +1,42 @@
+/* strstr.c -- return the offset of one string within another
+   Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* Written by Mike Rendell <michael@cs.mun.ca>.  */
+
+/* Return the starting address of string S2 in S1;
+   return 0 if it is not found. */
+
+char *
+strstr (char *s1, char *s2)
+{
+  int i;
+  char *p1;
+  char *p2;
+  char *s = s1;
+
+  for (p2 = s2, i = 0; *s; p2 = s2, ++i, ++s)
+    {
+      for (p1 = s; *p1 && *p2 && *p1 == *p2; ++p1, ++p2)
+	;
+      if (!*p2)
+	break;
+    }
+  if (!*p2)
+    return s1 + i;
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.cpp	(revision 16561)
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+void *bcopy(void *s, void *d, int n)
+{ return memcpy(d, s, n);
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/sysfuncs.h	(revision 16561)
@@ -0,0 +1,388 @@
+/* System dependent definitions for GNU tar.
+   Copyright (C) 1994 Free Software Foundation, Inc.
+
+   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, 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.
+ */
+
+/*
+ * [TS:Aug/95]
+ * Based on code from system.h file in GNU's tar package 
+ *
+ */
+
+#ifndef SYSFUNCS_H
+#define SYSFUNCS_H
+
+#include <sys/stat.h>
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__
+#  include <win32cfg.h>
+# else
+#  include <config.h>
+# endif
+#endif
+
+/* Declare alloca.  AIX requires this to be the first thing in the file.  */
+
+#ifdef __GNUC__
+#ifndef alloca
+#define alloca __builtin_alloca
+#endif
+#else
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX
+#pragma alloca
+#else
+#ifndef alloca
+char *alloca ();
+#endif
+#endif
+#endif
+#endif
+
+#include <sys/types.h>
+
+/* ------------------------------------------------- */
+/* [TS:Aug/95]
+ * Some extra include files that mg needs
+ */
+#include <stdlib.h>
+#include <math.h>
+
+#ifndef log2
+# ifndef M_LN2
+#  define M_LN2 0.69314718055994530942
+# endif
+# define log2(x) (log(x)/M_LN2)
+#endif
+
+
+#ifndef HAVE_SETBUFFER
+#ifdef __CYGWIN__ 
+/* Cygwin has setbuffer in stdio.h, but can't link against it!! */
+#include <stdio.h>
+#endif
+# define setbuffer(f, b, s)  setvbuf(f, b, _IOFBF, s)
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#if defined(__MSDOS__)  && !defined(__WIN32__)
+# define SHORT_SUFFIX 1
+#endif
+
+
+
+#include <assert.h>
+
+/* ------------------------------------------------- */
+
+/* Declare a generic pointer type.  */
+#if __STDC__ || defined(__TURBOC__)
+#define voidstar void *
+#else
+#define voidstar char *
+#endif
+
+/* Declare ISASCII.  */
+
+#include <ctype.h>
+
+#if STDC_HEADERS
+#define ISASCII(Char) 1
+#else
+#ifdef isascii
+#define ISASCII(Char) isascii (Char)
+#else
+#if HAVE_ISASCII
+#define ISASCII(Char) isascii (Char)
+#else
+#define ISASCII(Char) 1
+#endif
+#endif
+#endif
+
+
+/* ------------------------------------------------- */
+/* [TS:Aug/95] 
+ * The following is copied from GNU Autoconf.Info (1.1)   
+ * It sets up the approriate string and memory functions.
+ */
+
+#if STDC_HEADERS || HAVE_STRING_H
+#include <string.h>
+   /* An ANSI string.h and pre-ANSI memory.h might conflict */
+#if !STDC_HEADERS && HAVE_MEMORY_H
+#include <memory.h>
+#endif /* not STDC_HEADERS and HAVE_MEMORY_H */
+#ifndef index
+/*#define index strchr 
+ *
+ * this is stuffing up our code so its commented out */
+#endif
+#ifndef rindex
+#define rindex strrchr
+#endif
+
+#ifdef __cplusplus /* compiling for C++ */
+#include <stdlib.h>
+#ifndef __bcopy
+inline void *bcopy(void *s, void *d, int n)
+{ return memcpy(d, s, n);
+}
+#define __bcopy 1
+#endif
+#ifndef __bcmp
+inline int bcmp(void *s1, void *s2, int n)
+{ return memcmp(s1, s2, n);
+}
+#define __bcmp 1
+#endif
+#ifndef __bzero
+inline void bzero(void *m, int n)
+{ memset(m, 0, n);
+}
+#define __bzero 1
+#endif
+#else              /* compiling for C */
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#endif
+#ifndef bcmp
+#define bcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n) memset((s), 0, (n))
+#endif
+#endif
+
+#else /* not STDC_HEADERS and not HAVE_STRING_H */
+#include <strings.h>
+   /* memory.h and strings.h conflict on some systems */
+#endif
+/* ------------------------------------------------- */
+
+
+/* Declare errno.  */
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
+/* Declare open parameters.  */
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif
+
+#ifndef	O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CREAT
+#define O_CREAT 0
+#endif
+#ifndef	O_NDELAY
+#define O_NDELAY 0
+#endif
+#ifndef	O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+
+/* Declare file status routines and bits.  */
+
+#ifdef STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISLNK
+#undef S_ISMPB
+#undef S_ISMPC
+#undef S_ISNWK
+#undef S_ISREG
+#undef S_ISSOCK
+#endif
+
+/* On MSDOS, there are missing things from <sys/stat.h>.  */
+#ifdef __MSDOS__
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISVTX 0
+#endif
+
+#ifndef S_ISREG			/* POSIX.1 stat stuff missing */
+#define mode_t unsigned short
+#endif
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(Mode) (((Mode) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(Mode) (((Mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(Mode) (((Mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(Mode) (((Mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(Mode) (((Mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(Mode) (((Mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(Mode) (((Mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB)	/* V7 */
+#define S_ISMPB(Mode) (((Mode) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(Mode) (((Mode) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK)	/* HP/UX */
+#define S_ISNWK(Mode) (((Mode) & S_IFMT) == S_IFNWK)
+#endif
+
+#if !defined(S_ISCTG) && defined(S_IFCTG)	/* contiguous file */
+#define S_ISCTG(Mode) (((Mode) & S_IFMT) == S_IFCTG)
+#endif
+#if !defined(S_ISVTX)
+#define S_ISVTX 0001000
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#if !defined(_POSIX_SOURCE) && !defined(__WIN32__)
+#include <sys/param.h>
+#endif
+
+/* Include <unistd.h> before any preprocessor test of _POSIX_VERSION.  */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+/* Declare standard functions.  */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+voidstar malloc ();
+voidstar realloc ();
+char *getenv ();
+#endif
+
+#include <stdio.h>
+
+#ifndef _POSIX_VERSION
+#ifdef __MSDOS__
+#include <io.h>
+#else
+off_t lseek ();
+#endif
+#endif
+
+#ifndef __WIN32__
+#include <pathmax.h>
+#endif
+
+/* Until getoptold function is declared in getopt.h, we need this here for
+   struct option.  */
+/*#include <getopt.h>*/
+
+#ifdef WITH_DMALLOC
+#undef HAVE_VALLOC
+#define DMALLOC_FUNC_CHECK
+#include <dmalloc.h>
+#endif
+
+/* Prototypes for external functions.  */
+
+#ifndef __P
+#if PROTOTYPES
+#define __P(Args) Args
+#else
+#define __P(Args) ()
+#endif
+#endif
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+/*** setlocale is ansi and posix...(jrm21)
+#if !HAVE_SETLOCALE
+#define setlocale(Category, Locale)
+#endif
+***/
+
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(Text) gettext (Text)
+#else
+#define textdomain(Domain)
+#define _(Text) Text
+#endif
+
+/* Library modules.  */
+
+#ifdef HAVE_VPRINTF
+void error __P ((int, int, const char *,...));
+#else
+void error ();
+#endif
+
+#ifndef HAVE_STRSTR
+char *strstr __P ((const char *, const char *));
+#endif
+
+/* [RB/TS:Oct/95] commented out. Used by lib/gmalloc.c - but it defines it anyway ???
+ *#ifndef HAVE_VALLOC
+ *#define valloc(Size) malloc (Size)
+ *#endif
+ */
+
+voidstar xmalloc __P ((size_t));
+voidstar xrealloc __P ((voidstar, size_t));
+char *xstrdup __P ((const char *));
+
+/* [TS:Aug/95] 
+ * These were required by files which referred 
+ * to these as function pointers. 
+ * e.g. huffman.c - fread,  mgfelics.c - fgetc
+ */
+#ifndef HAVE_FREAD_DECL
+extern size_t   fread __P ((void *, size_t, size_t, FILE *));
+#endif
+#ifndef HAVE_FGETC_DECL
+extern int      fgetc __P ((FILE *));
+#endif
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifndef u_long
+# define u_long unsigned long
+#endif
+#ifndef u_char
+# define u_char unsigned char
+#endif
+
+
+#endif /* SYSFUNCS_H */
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.cpp	(revision 16561)
@@ -0,0 +1,1758 @@
+#include "unitool.h"
+#include "sysfuncs.h"
+
+
+/* unicode_cat_data is a combination of the letter, digit, and
+ * space information in a form that is quick to look up.
+ * The information about each byte is stored in two bits
+ *
+ * 00 - neither letter, digit or space
+ * 01 - letter
+ * 10 - space
+ * 11 - digit
+ *
+ * You can test for letter | space by looking at the right
+ * column
+ */
+static unsigned long *unicode_cat_data=NULL;
+
+
+typedef struct unidecompose {
+  unsigned short ch;
+  unsigned short str[3];
+} unidecompose;
+
+typedef struct unirelate {
+  unsigned short ch1;
+  unsigned short ch2;
+} unirelate;
+
+typedef struct unirange {
+  unsigned short first;
+  unsigned short last;
+} unirange;
+
+
+
+/* This decomposition data only includes canonical decompositional
+ * characters.  Any unused bytes are set to 0x0. */
+
+#define NUM_DECOMPOSE_INFO 1269
+static const unidecompose decompose_info[NUM_DECOMPOSE_INFO] = {
+  {0xC0,0x41,0x300,0x0}, {0xC1,0x41,0x301,0x0}, {0xC2,0x41,0x302,0x0}, 
+  {0xC3,0x41,0x303,0x0}, {0xC4,0x41,0x308,0x0}, {0xC5,0x41,0x30A,0x0}, 
+  {0xC7,0x43,0x327,0x0}, {0xC8,0x45,0x300,0x0}, {0xC9,0x45,0x301,0x0}, 
+  {0xCA,0x45,0x302,0x0}, {0xCB,0x45,0x308,0x0}, {0xCC,0x49,0x300,0x0}, 
+  {0xCD,0x49,0x301,0x0}, {0xCE,0x49,0x302,0x0}, {0xCF,0x49,0x308,0x0}, 
+  {0xD1,0x4E,0x303,0x0}, {0xD2,0x4F,0x300,0x0}, {0xD3,0x4F,0x301,0x0}, 
+  {0xD4,0x4F,0x302,0x0}, {0xD5,0x4F,0x303,0x0}, {0xD6,0x4F,0x308,0x0}, 
+  {0xD9,0x55,0x300,0x0}, {0xDA,0x55,0x301,0x0}, {0xDB,0x55,0x302,0x0}, 
+  {0xDC,0x55,0x308,0x0}, {0xDD,0x59,0x301,0x0}, {0xE0,0x61,0x300,0x0}, 
+  {0xE1,0x61,0x301,0x0}, {0xE2,0x61,0x302,0x0}, {0xE3,0x61,0x303,0x0}, 
+  {0xE4,0x61,0x308,0x0}, {0xE5,0x61,0x30A,0x0}, {0xE7,0x63,0x327,0x0}, 
+  {0xE8,0x65,0x300,0x0}, {0xE9,0x65,0x301,0x0}, {0xEA,0x65,0x302,0x0}, 
+  {0xEB,0x65,0x308,0x0}, {0xEC,0x69,0x300,0x0}, {0xED,0x69,0x301,0x0}, 
+  {0xEE,0x69,0x302,0x0}, {0xEF,0x69,0x308,0x0}, {0xF1,0x6E,0x303,0x0}, 
+  {0xF2,0x6F,0x300,0x0}, {0xF3,0x6F,0x301,0x0}, {0xF4,0x6F,0x302,0x0}, 
+  {0xF5,0x6F,0x303,0x0}, {0xF6,0x6F,0x308,0x0}, {0xF9,0x75,0x300,0x0}, 
+  {0xFA,0x75,0x301,0x0}, {0xFB,0x75,0x302,0x0}, {0xFC,0x75,0x308,0x0}, 
+  {0xFD,0x79,0x301,0x0}, {0xFF,0x79,0x308,0x0}, {0x100,0x41,0x304,0x0}, 
+  {0x101,0x61,0x304,0x0}, {0x102,0x41,0x306,0x0}, {0x103,0x61,0x306,0x0}, 
+  {0x104,0x41,0x328,0x0}, {0x105,0x61,0x328,0x0}, {0x106,0x43,0x301,0x0}, 
+  {0x107,0x63,0x301,0x0}, {0x108,0x43,0x302,0x0}, {0x109,0x63,0x302,0x0}, 
+  {0x10A,0x43,0x307,0x0}, {0x10B,0x63,0x307,0x0}, {0x10C,0x43,0x30C,0x0}, 
+  {0x10D,0x63,0x30C,0x0}, {0x10E,0x44,0x30C,0x0}, {0x10F,0x64,0x30C,0x0}, 
+  {0x112,0x45,0x304,0x0}, {0x113,0x65,0x304,0x0}, {0x114,0x45,0x306,0x0}, 
+  {0x115,0x65,0x306,0x0}, {0x116,0x45,0x307,0x0}, {0x117,0x65,0x307,0x0}, 
+  {0x118,0x45,0x328,0x0}, {0x119,0x65,0x328,0x0}, {0x11A,0x45,0x30C,0x0}, 
+  {0x11B,0x65,0x30C,0x0}, {0x11C,0x47,0x302,0x0}, {0x11D,0x67,0x302,0x0}, 
+  {0x11E,0x47,0x306,0x0}, {0x11F,0x67,0x306,0x0}, {0x120,0x47,0x307,0x0}, 
+  {0x121,0x67,0x307,0x0}, {0x122,0x47,0x327,0x0}, {0x123,0x67,0x327,0x0}, 
+  {0x124,0x48,0x302,0x0}, {0x125,0x68,0x302,0x0}, {0x128,0x49,0x303,0x0}, 
+  {0x129,0x69,0x303,0x0}, {0x12A,0x49,0x304,0x0}, {0x12B,0x69,0x304,0x0}, 
+  {0x12C,0x49,0x306,0x0}, {0x12D,0x69,0x306,0x0}, {0x12E,0x49,0x328,0x0}, 
+  {0x12F,0x69,0x328,0x0}, {0x130,0x49,0x307,0x0}, {0x134,0x4A,0x302,0x0}, 
+  {0x135,0x6A,0x302,0x0}, {0x136,0x4B,0x327,0x0}, {0x137,0x6B,0x327,0x0}, 
+  {0x139,0x4C,0x301,0x0}, {0x13A,0x6C,0x301,0x0}, {0x13B,0x4C,0x327,0x0}, 
+  {0x13C,0x6C,0x327,0x0}, {0x13D,0x4C,0x30C,0x0}, {0x13E,0x6C,0x30C,0x0}, 
+  {0x143,0x4E,0x301,0x0}, {0x144,0x6E,0x301,0x0}, {0x145,0x4E,0x327,0x0}, 
+  {0x146,0x6E,0x327,0x0}, {0x147,0x4E,0x30C,0x0}, {0x148,0x6E,0x30C,0x0}, 
+  {0x14C,0x4F,0x304,0x0}, {0x14D,0x6F,0x304,0x0}, {0x14E,0x4F,0x306,0x0}, 
+  {0x14F,0x6F,0x306,0x0}, {0x150,0x4F,0x30B,0x0}, {0x151,0x6F,0x30B,0x0}, 
+  {0x154,0x52,0x301,0x0}, {0x155,0x72,0x301,0x0}, {0x156,0x52,0x327,0x0}, 
+  {0x157,0x72,0x327,0x0}, {0x158,0x52,0x30C,0x0}, {0x159,0x72,0x30C,0x0}, 
+  {0x15A,0x53,0x301,0x0}, {0x15B,0x73,0x301,0x0}, {0x15C,0x53,0x302,0x0}, 
+  {0x15D,0x73,0x302,0x0}, {0x15E,0x53,0x327,0x0}, {0x15F,0x73,0x327,0x0}, 
+  {0x160,0x53,0x30C,0x0}, {0x161,0x73,0x30C,0x0}, {0x162,0x54,0x327,0x0}, 
+  {0x163,0x74,0x327,0x0}, {0x164,0x54,0x30C,0x0}, {0x165,0x74,0x30C,0x0}, 
+  {0x168,0x55,0x303,0x0}, {0x169,0x75,0x303,0x0}, {0x16A,0x55,0x304,0x0}, 
+  {0x16B,0x75,0x304,0x0}, {0x16C,0x55,0x306,0x0}, {0x16D,0x75,0x306,0x0}, 
+  {0x16E,0x55,0x30A,0x0}, {0x16F,0x75,0x30A,0x0}, {0x170,0x55,0x30B,0x0}, 
+  {0x171,0x75,0x30B,0x0}, {0x172,0x55,0x328,0x0}, {0x173,0x75,0x328,0x0}, 
+  {0x174,0x57,0x302,0x0}, {0x175,0x77,0x302,0x0}, {0x176,0x59,0x302,0x0}, 
+  {0x177,0x79,0x302,0x0}, {0x178,0x59,0x308,0x0}, {0x179,0x5A,0x301,0x0}, 
+  {0x17A,0x7A,0x301,0x0}, {0x17B,0x5A,0x307,0x0}, {0x17C,0x7A,0x307,0x0}, 
+  {0x17D,0x5A,0x30C,0x0}, {0x17E,0x7A,0x30C,0x0}, {0x1A0,0x4F,0x31B,0x0}, 
+  {0x1A1,0x6F,0x31B,0x0}, {0x1AF,0x55,0x31B,0x0}, {0x1B0,0x75,0x31B,0x0}, 
+  {0x1CD,0x41,0x30C,0x0}, {0x1CE,0x61,0x30C,0x0}, {0x1CF,0x49,0x30C,0x0}, 
+  {0x1D0,0x69,0x30C,0x0}, {0x1D1,0x4F,0x30C,0x0}, {0x1D2,0x6F,0x30C,0x0}, 
+  {0x1D3,0x55,0x30C,0x0}, {0x1D4,0x75,0x30C,0x0}, {0x1D5,0xDC,0x304,0x0}, 
+  {0x1D6,0xFC,0x304,0x0}, {0x1D7,0xDC,0x301,0x0}, {0x1D8,0xFC,0x301,0x0}, 
+  {0x1D9,0xDC,0x30C,0x0}, {0x1DA,0xFC,0x30C,0x0}, {0x1DB,0xDC,0x300,0x0}, 
+  {0x1DC,0xFC,0x300,0x0}, {0x1DE,0xC4,0x304,0x0}, {0x1DF,0xE4,0x304,0x0}, 
+  {0x1E0,0x41,0x307,0x304}, {0x1E1,0x61,0x307,0x304}, {0x1E2,0xC6,0x304,0x0}, 
+  {0x1E3,0xE6,0x304,0x0}, {0x1E6,0x47,0x30C,0x0}, {0x1E7,0x67,0x30C,0x0}, 
+  {0x1E8,0x4B,0x30C,0x0}, {0x1E9,0x6B,0x30C,0x0}, {0x1EA,0x4F,0x328,0x0}, 
+  {0x1EB,0x6F,0x328,0x0}, {0x1EC,0x1EA,0x304,0x0}, {0x1ED,0x1EB,0x304,0x0}, 
+  {0x1EE,0x1B7,0x30C,0x0}, {0x1EF,0x292,0x30C,0x0}, {0x1F0,0x6A,0x30C,0x0}, 
+  {0x1F4,0x47,0x301,0x0}, {0x1F5,0x67,0x301,0x0}, {0x1FA,0xC5,0x301,0x0}, 
+  {0x1FB,0xE5,0x301,0x0}, {0x1FC,0xC6,0x301,0x0}, {0x1FD,0xE6,0x301,0x0}, 
+  {0x1FE,0xD8,0x301,0x0}, {0x1FF,0xF8,0x301,0x0}, {0x200,0x41,0x30F,0x0}, 
+  {0x201,0x61,0x30F,0x0}, {0x202,0x41,0x311,0x0}, {0x203,0x61,0x311,0x0}, 
+  {0x204,0x45,0x30F,0x0}, {0x205,0x65,0x30F,0x0}, {0x206,0x45,0x311,0x0}, 
+  {0x207,0x65,0x311,0x0}, {0x208,0x49,0x30F,0x0}, {0x209,0x69,0x30F,0x0}, 
+  {0x20A,0x49,0x311,0x0}, {0x20B,0x69,0x311,0x0}, {0x20C,0x4F,0x30F,0x0}, 
+  {0x20D,0x6F,0x30F,0x0}, {0x20E,0x4F,0x311,0x0}, {0x20F,0x6F,0x311,0x0}, 
+  {0x210,0x52,0x30F,0x0}, {0x211,0x72,0x30F,0x0}, {0x212,0x52,0x311,0x0}, 
+  {0x213,0x72,0x311,0x0}, {0x214,0x55,0x30F,0x0}, {0x215,0x75,0x30F,0x0}, 
+  {0x216,0x55,0x311,0x0}, {0x217,0x75,0x311,0x0}, {0x310,0x306,0x307,0x0}, 
+  {0x340,0x300,0x0,0x0}, {0x341,0x301,0x0,0x0}, {0x343,0x313,0x0,0x0}, 
+  {0x344,0x308,0x30D,0x0}, {0x374,0x2B9,0x0,0x0}, {0x37E,0x3B,0x0,0x0}, 
+  {0x385,0xA8,0x30D,0x0}, {0x386,0x391,0x30D,0x0}, {0x387,0xB7,0x0,0x0}, 
+  {0x388,0x395,0x30D,0x0}, {0x389,0x397,0x30D,0x0}, {0x38A,0x399,0x30D,0x0}, 
+  {0x38C,0x39F,0x30D,0x0}, {0x38E,0x3A5,0x30D,0x0}, {0x38F,0x3A9,0x30D,0x0}, 
+  {0x390,0x3B9,0x344,0x0}, {0x3AA,0x399,0x308,0x0}, {0x3AB,0x3A5,0x308,0x0}, 
+  {0x3AC,0x3B1,0x30D,0x0}, {0x3AD,0x3B5,0x30D,0x0}, {0x3AE,0x3B7,0x30D,0x0}, 
+  {0x3AF,0x3B9,0x30D,0x0}, {0x3B0,0x3C5,0x344,0x0}, {0x3CA,0x3B9,0x308,0x0}, 
+  {0x3CB,0x3C5,0x308,0x0}, {0x3CC,0x3BF,0x30D,0x0}, {0x3CD,0x3C5,0x30D,0x0}, 
+  {0x3CE,0x3C9,0x30D,0x0}, {0x3D3,0x3D2,0x30D,0x0}, {0x3D4,0x3D2,0x308,0x0}, 
+  {0x401,0x415,0x308,0x0}, {0x403,0x413,0x301,0x0}, {0x407,0x406,0x308,0x0}, 
+  {0x40C,0x41A,0x301,0x0}, {0x40E,0x423,0x306,0x0}, {0x419,0x418,0x306,0x0}, 
+  {0x439,0x438,0x306,0x0}, {0x451,0x435,0x308,0x0}, {0x453,0x433,0x301,0x0}, 
+  {0x457,0x456,0x308,0x0}, {0x45C,0x43A,0x301,0x0}, {0x45E,0x443,0x306,0x0}, 
+  {0x476,0x474,0x30F,0x0}, {0x477,0x475,0x30F,0x0}, {0x4C1,0x416,0x306,0x0}, 
+  {0x4C2,0x436,0x306,0x0}, {0x4D0,0x410,0x306,0x0}, {0x4D1,0x430,0x306,0x0}, 
+  {0x4D2,0x410,0x308,0x0}, {0x4D3,0x430,0x308,0x0}, {0x4D6,0x415,0x306,0x0}, 
+  {0x4D7,0x435,0x306,0x0}, {0x4DA,0x4D8,0x308,0x0}, {0x4DB,0x4D9,0x308,0x0}, 
+  {0x4DC,0x416,0x308,0x0}, {0x4DD,0x436,0x308,0x0}, {0x4DE,0x417,0x308,0x0}, 
+  {0x4DF,0x437,0x308,0x0}, {0x4E2,0x418,0x304,0x0}, {0x4E3,0x438,0x304,0x0}, 
+  {0x4E4,0x418,0x308,0x0}, {0x4E5,0x438,0x308,0x0}, {0x4E6,0x41E,0x308,0x0}, 
+  {0x4E7,0x43E,0x308,0x0}, {0x4EA,0x4E8,0x308,0x0}, {0x4EB,0x4E9,0x308,0x0}, 
+  {0x4EE,0x423,0x304,0x0}, {0x4EF,0x443,0x304,0x0}, {0x4F0,0x423,0x308,0x0}, 
+  {0x4F1,0x443,0x308,0x0}, {0x4F2,0x423,0x30B,0x0}, {0x4F3,0x443,0x30B,0x0}, 
+  {0x4F4,0x427,0x308,0x0}, {0x4F5,0x447,0x308,0x0}, {0x4F8,0x42B,0x308,0x0}, 
+  {0x4F9,0x44B,0x308,0x0}, {0x929,0x928,0x93C,0x0}, {0x931,0x930,0x93C,0x0}, 
+  {0x934,0x933,0x93C,0x0}, {0x958,0x915,0x93C,0x0}, {0x959,0x916,0x93C,0x0}, 
+  {0x95A,0x917,0x93C,0x0}, {0x95B,0x91C,0x93C,0x0}, {0x95C,0x921,0x93C,0x0}, 
+  {0x95D,0x922,0x93C,0x0}, {0x95E,0x92B,0x93C,0x0}, {0x95F,0x92F,0x93C,0x0}, 
+  {0x9B0,0x9AC,0x9BC,0x0}, {0x9CB,0x9C7,0x9BE,0x0}, {0x9CC,0x9C7,0x9D7,0x0}, 
+  {0x9DC,0x9A1,0x9BC,0x0}, {0x9DD,0x9A2,0x9BC,0x0}, {0x9DF,0x9AF,0x9BC,0x0}, 
+  {0xA59,0xA16,0xA3C,0x0}, {0xA5A,0xA17,0xA3C,0x0}, {0xA5B,0xA1C,0xA3C,0x0}, 
+  {0xA5C,0xA21,0xA3C,0x0}, {0xA5E,0xA2B,0xA3C,0x0}, {0xB48,0xB47,0xB56,0x0}, 
+  {0xB4B,0xB47,0xB3E,0x0}, {0xB4C,0xB47,0xB57,0x0}, {0xB5C,0xB21,0xB3C,0x0}, 
+  {0xB5D,0xB22,0xB3C,0x0}, {0xB5F,0xB2F,0xB3C,0x0}, {0xB94,0xB92,0xBD7,0x0}, 
+  {0xBCA,0xBC6,0xBBE,0x0}, {0xBCB,0xBC7,0xBBE,0x0}, {0xBCC,0xBC6,0xBD7,0x0}, 
+  {0xC48,0xC46,0xC56,0x0}, {0xCC0,0xCBF,0xCD5,0x0}, {0xCC7,0xCC6,0xCD5,0x0}, 
+  {0xCC8,0xCC6,0xCD6,0x0}, {0xCCA,0xCC6,0xCC2,0x0}, {0xCCB,0xCC6,0xCC2,0xCD5}, 
+  {0xD4A,0xD46,0xD3E,0x0}, {0xD4B,0xD47,0xD3E,0x0}, {0xD4C,0xD46,0xD57,0x0}, 
+  {0xE33,0xE4D,0xE32,0x0}, {0xEB3,0xECD,0xEB2,0x0}, {0xF43,0xF42,0xFB7,0x0}, 
+  {0xF4D,0xF4C,0xFB7,0x0}, {0xF52,0xF51,0xFB7,0x0}, {0xF57,0xF56,0xFB7,0x0}, 
+  {0xF5C,0xF5B,0xFB7,0x0}, {0xF69,0xF40,0xFB5,0x0}, {0xF73,0xF72,0xF71,0x0}, 
+  {0xF75,0xF71,0xF74,0x0}, {0xF76,0xFB2,0xF80,0x0}, {0xF77,0xF76,0xF71,0x0}, 
+  {0xF78,0xFB3,0xF80,0x0}, {0xF79,0xF78,0xF71,0x0}, {0xF81,0xF80,0xF71,0x0}, 
+  {0xF93,0xF92,0xFB7,0x0}, {0xF9D,0xF9C,0xFB7,0x0}, {0xFA2,0xFA1,0xFB7,0x0}, 
+  {0xFA7,0xFA6,0xFB7,0x0}, {0xFAC,0xFAB,0xFB7,0x0}, {0xFB9,0xF90,0xFB5,0x0}, 
+  {0x1E00,0x41,0x325,0x0}, {0x1E01,0x61,0x325,0x0}, {0x1E02,0x42,0x307,0x0}, 
+  {0x1E03,0x62,0x307,0x0}, {0x1E04,0x42,0x323,0x0}, {0x1E05,0x62,0x323,0x0}, 
+  {0x1E06,0x42,0x331,0x0}, {0x1E07,0x62,0x331,0x0}, {0x1E08,0xC7,0x301,0x0}, 
+  {0x1E09,0xE7,0x301,0x0}, {0x1E0A,0x44,0x307,0x0}, {0x1E0B,0x64,0x307,0x0}, 
+  {0x1E0C,0x44,0x323,0x0}, {0x1E0D,0x64,0x323,0x0}, {0x1E0E,0x44,0x331,0x0}, 
+  {0x1E0F,0x64,0x331,0x0}, {0x1E10,0x44,0x327,0x0}, {0x1E11,0x64,0x327,0x0}, 
+  {0x1E12,0x44,0x32D,0x0}, {0x1E13,0x64,0x32D,0x0}, {0x1E14,0x112,0x300,0x0}, 
+  {0x1E15,0x113,0x300,0x0}, {0x1E16,0x112,0x301,0x0}, {0x1E17,0x113,0x301,0x0}, 
+  {0x1E18,0x45,0x32D,0x0}, {0x1E19,0x65,0x32D,0x0}, {0x1E1A,0x45,0x330,0x0}, 
+  {0x1E1B,0x65,0x330,0x0}, {0x1E1C,0x114,0x327,0x0}, {0x1E1D,0x115,0x327,0x0}, 
+  {0x1E1E,0x46,0x307,0x0}, {0x1E1F,0x66,0x307,0x0}, {0x1E20,0x47,0x304,0x0}, 
+  {0x1E21,0x67,0x304,0x0}, {0x1E22,0x48,0x307,0x0}, {0x1E23,0x68,0x307,0x0}, 
+  {0x1E24,0x48,0x323,0x0}, {0x1E25,0x68,0x323,0x0}, {0x1E26,0x48,0x308,0x0}, 
+  {0x1E27,0x68,0x308,0x0}, {0x1E28,0x48,0x327,0x0}, {0x1E29,0x68,0x327,0x0}, 
+  {0x1E2A,0x48,0x32E,0x0}, {0x1E2B,0x68,0x32E,0x0}, {0x1E2C,0x49,0x330,0x0}, 
+  {0x1E2D,0x69,0x330,0x0}, {0x1E2E,0xCF,0x301,0x0}, {0x1E2F,0xEF,0x301,0x0}, 
+  {0x1E30,0x4B,0x301,0x0}, {0x1E31,0x6B,0x301,0x0}, {0x1E32,0x4B,0x323,0x0}, 
+  {0x1E33,0x6B,0x323,0x0}, {0x1E34,0x4B,0x331,0x0}, {0x1E35,0x6B,0x331,0x0}, 
+  {0x1E36,0x4C,0x323,0x0}, {0x1E37,0x6C,0x323,0x0}, {0x1E38,0x1E36,0x304,0x0}, 
+  {0x1E39,0x1E37,0x304,0x0}, {0x1E3A,0x4C,0x331,0x0}, {0x1E3B,0x6C,0x331,0x0}, 
+  {0x1E3C,0x4C,0x32D,0x0}, {0x1E3D,0x6C,0x32D,0x0}, {0x1E3E,0x4D,0x301,0x0}, 
+  {0x1E3F,0x6D,0x301,0x0}, {0x1E40,0x4D,0x307,0x0}, {0x1E41,0x6D,0x307,0x0}, 
+  {0x1E42,0x4D,0x323,0x0}, {0x1E43,0x6D,0x323,0x0}, {0x1E44,0x4E,0x307,0x0}, 
+  {0x1E45,0x6E,0x307,0x0}, {0x1E46,0x4E,0x323,0x0}, {0x1E47,0x6E,0x323,0x0}, 
+  {0x1E48,0x4E,0x331,0x0}, {0x1E49,0x6E,0x331,0x0}, {0x1E4A,0x4E,0x32D,0x0}, 
+  {0x1E4B,0x6E,0x32D,0x0}, {0x1E4C,0xD5,0x301,0x0}, {0x1E4D,0xF5,0x301,0x0}, 
+  {0x1E4E,0xD5,0x308,0x0}, {0x1E4F,0xF5,0x308,0x0}, {0x1E50,0x14C,0x300,0x0}, 
+  {0x1E51,0x14D,0x300,0x0}, {0x1E52,0x14C,0x301,0x0}, {0x1E53,0x14D,0x301,0x0}, 
+  {0x1E54,0x50,0x301,0x0}, {0x1E55,0x70,0x301,0x0}, {0x1E56,0x50,0x307,0x0}, 
+  {0x1E57,0x70,0x307,0x0}, {0x1E58,0x52,0x307,0x0}, {0x1E59,0x72,0x307,0x0}, 
+  {0x1E5A,0x52,0x323,0x0}, {0x1E5B,0x72,0x323,0x0}, {0x1E5C,0x1E5A,0x304,0x0}, 
+  {0x1E5D,0x1E5B,0x304,0x0}, {0x1E5E,0x52,0x331,0x0}, {0x1E5F,0x72,0x331,0x0}, 
+  {0x1E60,0x53,0x307,0x0}, {0x1E61,0x73,0x307,0x0}, {0x1E62,0x53,0x323,0x0}, 
+  {0x1E63,0x73,0x323,0x0}, {0x1E64,0x15A,0x307,0x0}, {0x1E65,0x15B,0x307,0x0}, 
+  {0x1E66,0x160,0x307,0x0}, {0x1E67,0x161,0x307,0x0}, {0x1E68,0x1E62,0x307,0x0}, 
+  {0x1E69,0x1E63,0x307,0x0}, {0x1E6A,0x54,0x307,0x0}, {0x1E6B,0x74,0x307,0x0}, 
+  {0x1E6C,0x54,0x323,0x0}, {0x1E6D,0x74,0x323,0x0}, {0x1E6E,0x54,0x331,0x0}, 
+  {0x1E6F,0x74,0x331,0x0}, {0x1E70,0x54,0x32D,0x0}, {0x1E71,0x74,0x32D,0x0}, 
+  {0x1E72,0x55,0x324,0x0}, {0x1E73,0x75,0x324,0x0}, {0x1E74,0x55,0x330,0x0}, 
+  {0x1E75,0x75,0x330,0x0}, {0x1E76,0x55,0x32D,0x0}, {0x1E77,0x75,0x32D,0x0}, 
+  {0x1E78,0x168,0x301,0x0}, {0x1E79,0x169,0x301,0x0}, {0x1E7A,0x16A,0x308,0x0}, 
+  {0x1E7B,0x16B,0x308,0x0}, {0x1E7C,0x56,0x303,0x0}, {0x1E7D,0x76,0x303,0x0}, 
+  {0x1E7E,0x56,0x323,0x0}, {0x1E7F,0x76,0x323,0x0}, {0x1E80,0x57,0x300,0x0}, 
+  {0x1E81,0x77,0x300,0x0}, {0x1E82,0x57,0x301,0x0}, {0x1E83,0x77,0x301,0x0}, 
+  {0x1E84,0x57,0x308,0x0}, {0x1E85,0x77,0x308,0x0}, {0x1E86,0x57,0x307,0x0}, 
+  {0x1E87,0x77,0x307,0x0}, {0x1E88,0x57,0x323,0x0}, {0x1E89,0x77,0x323,0x0}, 
+  {0x1E8A,0x58,0x307,0x0}, {0x1E8B,0x78,0x307,0x0}, {0x1E8C,0x58,0x308,0x0}, 
+  {0x1E8D,0x78,0x308,0x0}, {0x1E8E,0x59,0x307,0x0}, {0x1E8F,0x79,0x307,0x0}, 
+  {0x1E90,0x5A,0x302,0x0}, {0x1E91,0x7A,0x302,0x0}, {0x1E92,0x5A,0x323,0x0}, 
+  {0x1E93,0x7A,0x323,0x0}, {0x1E94,0x5A,0x331,0x0}, {0x1E95,0x7A,0x331,0x0}, 
+  {0x1E96,0x68,0x331,0x0}, {0x1E97,0x74,0x308,0x0}, {0x1E98,0x77,0x30A,0x0}, 
+  {0x1E99,0x79,0x30A,0x0}, {0x1E9B,0x17F,0x307,0x0}, {0x1EA0,0x41,0x323,0x0}, 
+  {0x1EA1,0x61,0x323,0x0}, {0x1EA2,0x41,0x309,0x0}, {0x1EA3,0x61,0x309,0x0}, 
+  {0x1EA4,0xC2,0x301,0x0}, {0x1EA5,0xE2,0x301,0x0}, {0x1EA6,0xC2,0x300,0x0}, 
+  {0x1EA7,0xE2,0x300,0x0}, {0x1EA8,0xC2,0x309,0x0}, {0x1EA9,0xE2,0x309,0x0}, 
+  {0x1EAA,0xC2,0x303,0x0}, {0x1EAB,0xE2,0x303,0x0}, {0x1EAC,0xC2,0x323,0x0}, 
+  {0x1EAD,0xE2,0x323,0x0}, {0x1EAE,0x102,0x301,0x0}, {0x1EAF,0x103,0x301,0x0}, 
+  {0x1EB0,0x102,0x300,0x0}, {0x1EB1,0x103,0x300,0x0}, {0x1EB2,0x102,0x309,0x0}, 
+  {0x1EB3,0x103,0x309,0x0}, {0x1EB4,0x102,0x303,0x0}, {0x1EB5,0x103,0x303,0x0}, 
+  {0x1EB6,0x102,0x323,0x0}, {0x1EB7,0x103,0x323,0x0}, {0x1EB8,0x45,0x323,0x0}, 
+  {0x1EB9,0x65,0x323,0x0}, {0x1EBA,0x45,0x309,0x0}, {0x1EBB,0x65,0x309,0x0}, 
+  {0x1EBC,0x45,0x303,0x0}, {0x1EBD,0x65,0x303,0x0}, {0x1EBE,0xCA,0x301,0x0}, 
+  {0x1EBF,0xEA,0x301,0x0}, {0x1EC0,0xCA,0x300,0x0}, {0x1EC1,0xEA,0x300,0x0}, 
+  {0x1EC2,0xCA,0x309,0x0}, {0x1EC3,0xEA,0x309,0x0}, {0x1EC4,0xCA,0x303,0x0}, 
+  {0x1EC5,0xEA,0x303,0x0}, {0x1EC6,0xCA,0x323,0x0}, {0x1EC7,0xEA,0x323,0x0}, 
+  {0x1EC8,0x49,0x309,0x0}, {0x1EC9,0x69,0x309,0x0}, {0x1ECA,0x49,0x323,0x0}, 
+  {0x1ECB,0x69,0x323,0x0}, {0x1ECC,0x4F,0x323,0x0}, {0x1ECD,0x6F,0x323,0x0}, 
+  {0x1ECE,0x4F,0x309,0x0}, {0x1ECF,0x6F,0x309,0x0}, {0x1ED0,0xD4,0x301,0x0}, 
+  {0x1ED1,0xF4,0x301,0x0}, {0x1ED2,0xD4,0x300,0x0}, {0x1ED3,0xF4,0x300,0x0}, 
+  {0x1ED4,0xD4,0x309,0x0}, {0x1ED5,0xF4,0x309,0x0}, {0x1ED6,0xD4,0x303,0x0}, 
+  {0x1ED7,0xF4,0x303,0x0}, {0x1ED8,0xD4,0x323,0x0}, {0x1ED9,0xF4,0x323,0x0}, 
+  {0x1EDA,0x1A0,0x301,0x0}, {0x1EDB,0x1A1,0x301,0x0}, {0x1EDC,0x1A0,0x300,0x0}, 
+  {0x1EDD,0x1A1,0x300,0x0}, {0x1EDE,0x1A0,0x309,0x0}, {0x1EDF,0x1A1,0x309,0x0}, 
+  {0x1EE0,0x1A0,0x303,0x0}, {0x1EE1,0x1A1,0x303,0x0}, {0x1EE2,0x1A0,0x323,0x0}, 
+  {0x1EE3,0x1A1,0x323,0x0}, {0x1EE4,0x55,0x323,0x0}, {0x1EE5,0x75,0x323,0x0}, 
+  {0x1EE6,0x55,0x309,0x0}, {0x1EE7,0x75,0x309,0x0}, {0x1EE8,0x1AF,0x301,0x0}, 
+  {0x1EE9,0x1B0,0x301,0x0}, {0x1EEA,0x1AF,0x300,0x0}, {0x1EEB,0x1B0,0x300,0x0}, 
+  {0x1EEC,0x1AF,0x309,0x0}, {0x1EED,0x1B0,0x309,0x0}, {0x1EEE,0x1AF,0x303,0x0}, 
+  {0x1EEF,0x1B0,0x303,0x0}, {0x1EF0,0x1AF,0x323,0x0}, {0x1EF1,0x1B0,0x323,0x0}, 
+  {0x1EF2,0x59,0x300,0x0}, {0x1EF3,0x79,0x300,0x0}, {0x1EF4,0x59,0x323,0x0}, 
+  {0x1EF5,0x79,0x323,0x0}, {0x1EF6,0x59,0x309,0x0}, {0x1EF7,0x79,0x309,0x0}, 
+  {0x1EF8,0x59,0x303,0x0}, {0x1EF9,0x79,0x303,0x0}, {0x1F00,0x3B1,0x313,0x0}, 
+  {0x1F01,0x3B1,0x314,0x0}, {0x1F02,0x1F00,0x300,0x0}, {0x1F03,0x1F01,0x300,0x0}, 
+  {0x1F04,0x1F00,0x301,0x0}, {0x1F05,0x1F01,0x301,0x0}, {0x1F06,0x1F00,0x342,0x0}, 
+  {0x1F07,0x1F01,0x342,0x0}, {0x1F08,0x391,0x313,0x0}, {0x1F09,0x391,0x314,0x0}, 
+  {0x1F0A,0x1F08,0x300,0x0}, {0x1F0B,0x1F09,0x300,0x0}, {0x1F0C,0x1F08,0x301,0x0}, 
+  {0x1F0D,0x1F09,0x301,0x0}, {0x1F0E,0x1F08,0x342,0x0}, {0x1F0F,0x1F09,0x342,0x0}, 
+  {0x1F10,0x3B5,0x313,0x0}, {0x1F11,0x3B5,0x314,0x0}, {0x1F12,0x1F10,0x300,0x0}, 
+  {0x1F13,0x1F11,0x300,0x0}, {0x1F14,0x1F10,0x301,0x0}, {0x1F15,0x1F11,0x301,0x0}, 
+  {0x1F18,0x395,0x313,0x0}, {0x1F19,0x395,0x314,0x0}, {0x1F1A,0x1F18,0x300,0x0}, 
+  {0x1F1B,0x1F19,0x300,0x0}, {0x1F1C,0x1F18,0x301,0x0}, {0x1F1D,0x1F19,0x301,0x0}, 
+  {0x1F20,0x3B7,0x313,0x0}, {0x1F21,0x3B7,0x314,0x0}, {0x1F22,0x1F20,0x300,0x0}, 
+  {0x1F23,0x1F21,0x300,0x0}, {0x1F24,0x1F20,0x301,0x0}, {0x1F25,0x1F21,0x301,0x0}, 
+  {0x1F26,0x1F20,0x342,0x0}, {0x1F27,0x1F21,0x342,0x0}, {0x1F28,0x397,0x313,0x0}, 
+  {0x1F29,0x397,0x314,0x0}, {0x1F2A,0x1F28,0x300,0x0}, {0x1F2B,0x1F29,0x300,0x0}, 
+  {0x1F2C,0x1F28,0x301,0x0}, {0x1F2D,0x1F29,0x301,0x0}, {0x1F2E,0x1F28,0x342,0x0}, 
+  {0x1F2F,0x1F29,0x342,0x0}, {0x1F30,0x3B9,0x313,0x0}, {0x1F31,0x3B9,0x314,0x0}, 
+  {0x1F32,0x1F30,0x300,0x0}, {0x1F33,0x1F31,0x300,0x0}, {0x1F34,0x1F30,0x301,0x0}, 
+  {0x1F35,0x1F31,0x301,0x0}, {0x1F36,0x1F30,0x342,0x0}, {0x1F37,0x1F31,0x342,0x0}, 
+  {0x1F38,0x399,0x313,0x0}, {0x1F39,0x399,0x314,0x0}, {0x1F3A,0x1F38,0x300,0x0}, 
+  {0x1F3B,0x1F39,0x300,0x0}, {0x1F3C,0x1F38,0x301,0x0}, {0x1F3D,0x1F39,0x301,0x0}, 
+  {0x1F3E,0x1F38,0x342,0x0}, {0x1F3F,0x1F39,0x342,0x0}, {0x1F40,0x3BF,0x313,0x0}, 
+  {0x1F41,0x3BF,0x314,0x0}, {0x1F42,0x1F40,0x300,0x0}, {0x1F43,0x1F41,0x300,0x0}, 
+  {0x1F44,0x1F40,0x301,0x0}, {0x1F45,0x1F41,0x301,0x0}, {0x1F48,0x39F,0x313,0x0}, 
+  {0x1F49,0x39F,0x314,0x0}, {0x1F4A,0x1F48,0x300,0x0}, {0x1F4B,0x1F49,0x300,0x0}, 
+  {0x1F4C,0x1F48,0x301,0x0}, {0x1F4D,0x1F49,0x301,0x0}, {0x1F50,0x3C5,0x313,0x0}, 
+  {0x1F51,0x3C5,0x314,0x0}, {0x1F52,0x1F50,0x300,0x0}, {0x1F53,0x1F51,0x300,0x0}, 
+  {0x1F54,0x1F50,0x301,0x0}, {0x1F55,0x1F51,0x301,0x0}, {0x1F56,0x1F50,0x342,0x0}, 
+  {0x1F57,0x1F51,0x342,0x0}, {0x1F59,0x3A5,0x314,0x0}, {0x1F5B,0x1F59,0x300,0x0}, 
+  {0x1F5D,0x1F59,0x301,0x0}, {0x1F5F,0x1F59,0x342,0x0}, {0x1F60,0x3C9,0x313,0x0}, 
+  {0x1F61,0x3C9,0x314,0x0}, {0x1F62,0x1F60,0x300,0x0}, {0x1F63,0x1F61,0x300,0x0}, 
+  {0x1F64,0x1F60,0x301,0x0}, {0x1F65,0x1F61,0x301,0x0}, {0x1F66,0x1F60,0x342,0x0}, 
+  {0x1F67,0x1F61,0x342,0x0}, {0x1F68,0x3A9,0x313,0x0}, {0x1F69,0x3A9,0x314,0x0}, 
+  {0x1F6A,0x1F68,0x300,0x0}, {0x1F6B,0x1F69,0x300,0x0}, {0x1F6C,0x1F68,0x301,0x0}, 
+  {0x1F6D,0x1F69,0x301,0x0}, {0x1F6E,0x1F68,0x342,0x0}, {0x1F6F,0x1F69,0x342,0x0}, 
+  {0x1F70,0x3B1,0x300,0x0}, {0x1F71,0x3B1,0x301,0x0}, {0x1F72,0x3B5,0x300,0x0}, 
+  {0x1F73,0x3B5,0x301,0x0}, {0x1F74,0x3B7,0x300,0x0}, {0x1F75,0x3B7,0x301,0x0}, 
+  {0x1F76,0x3B9,0x300,0x0}, {0x1F77,0x3B9,0x301,0x0}, {0x1F78,0x3BF,0x300,0x0}, 
+  {0x1F79,0x3BF,0x301,0x0}, {0x1F7A,0x3C5,0x300,0x0}, {0x1F7B,0x3C5,0x301,0x0}, 
+  {0x1F7C,0x3C9,0x300,0x0}, {0x1F7D,0x3C9,0x301,0x0}, {0x1F80,0x1F00,0x345,0x0}, 
+  {0x1F81,0x1F01,0x345,0x0}, {0x1F82,0x1F02,0x345,0x0}, {0x1F83,0x1F03,0x345,0x0}, 
+  {0x1F84,0x1F04,0x345,0x0}, {0x1F85,0x1F05,0x345,0x0}, {0x1F86,0x1F06,0x345,0x0}, 
+  {0x1F87,0x1F07,0x345,0x0}, {0x1F88,0x1F08,0x345,0x0}, {0x1F89,0x1F09,0x345,0x0}, 
+  {0x1F8A,0x1F0A,0x345,0x0}, {0x1F8B,0x1F0B,0x345,0x0}, {0x1F8C,0x1F0C,0x345,0x0}, 
+  {0x1F8D,0x1F0D,0x345,0x0}, {0x1F8E,0x1F0E,0x345,0x0}, {0x1F8F,0x1F0F,0x345,0x0}, 
+  {0x1F90,0x1F20,0x345,0x0}, {0x1F91,0x1F21,0x345,0x0}, {0x1F92,0x1F22,0x345,0x0}, 
+  {0x1F93,0x1F23,0x345,0x0}, {0x1F94,0x1F24,0x345,0x0}, {0x1F95,0x1F25,0x345,0x0}, 
+  {0x1F96,0x1F26,0x345,0x0}, {0x1F97,0x1F27,0x345,0x0}, {0x1F98,0x1F28,0x345,0x0}, 
+  {0x1F99,0x1F29,0x345,0x0}, {0x1F9A,0x1F2A,0x345,0x0}, {0x1F9B,0x1F2B,0x345,0x0}, 
+  {0x1F9C,0x1F2C,0x345,0x0}, {0x1F9D,0x1F2D,0x345,0x0}, {0x1F9E,0x1F2E,0x345,0x0}, 
+  {0x1F9F,0x1F2F,0x345,0x0}, {0x1FA0,0x1F60,0x345,0x0}, {0x1FA1,0x1F61,0x345,0x0}, 
+  {0x1FA2,0x1F62,0x345,0x0}, {0x1FA3,0x1F63,0x345,0x0}, {0x1FA4,0x1F64,0x345,0x0}, 
+  {0x1FA5,0x1F65,0x345,0x0}, {0x1FA6,0x1F66,0x345,0x0}, {0x1FA7,0x1F67,0x345,0x0}, 
+  {0x1FA8,0x1F68,0x345,0x0}, {0x1FA9,0x1F69,0x345,0x0}, {0x1FAA,0x1F6A,0x345,0x0}, 
+  {0x1FAB,0x1F6B,0x345,0x0}, {0x1FAC,0x1F6C,0x345,0x0}, {0x1FAD,0x1F6D,0x345,0x0}, 
+  {0x1FAE,0x1F6E,0x345,0x0}, {0x1FAF,0x1F6F,0x345,0x0}, {0x1FB0,0x3B1,0x306,0x0}, 
+  {0x1FB1,0x3B1,0x304,0x0}, {0x1FB2,0x1F70,0x345,0x0}, {0x1FB3,0x3B1,0x345,0x0}, 
+  {0x1FB4,0x1F71,0x345,0x0}, {0x1FB6,0x3B1,0x342,0x0}, {0x1FB7,0x1FB6,0x345,0x0}, 
+  {0x1FB8,0x391,0x306,0x0}, {0x1FB9,0x391,0x304,0x0}, {0x1FBA,0x391,0x300,0x0}, 
+  {0x1FBB,0x391,0x301,0x0}, {0x1FBC,0x391,0x345,0x0}, {0x1FBE,0x3B9,0x0,0x0}, 
+  {0x1FC1,0xA8,0x342,0x0}, {0x1FC2,0x1F74,0x345,0x0}, {0x1FC3,0x3B7,0x345,0x0}, 
+  {0x1FC4,0x1F75,0x345,0x0}, {0x1FC6,0x3B7,0x342,0x0}, {0x1FC7,0x1FC6,0x345,0x0}, 
+  {0x1FC8,0x395,0x300,0x0}, {0x1FC9,0x395,0x301,0x0}, {0x1FCA,0x397,0x300,0x0}, 
+  {0x1FCB,0x397,0x301,0x0}, {0x1FCC,0x397,0x345,0x0}, {0x1FCD,0x1FBF,0x300,0x0}, 
+  {0x1FCE,0x1FBF,0x301,0x0}, {0x1FCF,0x1FBF,0x342,0x0}, {0x1FD0,0x3B9,0x306,0x0}, 
+  {0x1FD1,0x3B9,0x304,0x0}, {0x1FD2,0x3CA,0x300,0x0}, {0x1FD3,0x3CA,0x301,0x0}, 
+  {0x1FD6,0x3B9,0x342,0x0}, {0x1FD7,0x3CA,0x342,0x0}, {0x1FD8,0x399,0x306,0x0}, 
+  {0x1FD9,0x399,0x304,0x0}, {0x1FDA,0x399,0x300,0x0}, {0x1FDB,0x399,0x301,0x0}, 
+  {0x1FDD,0x1FFE,0x300,0x0}, {0x1FDE,0x1FFE,0x301,0x0}, {0x1FDF,0x1FFE,0x342,0x0}, 
+  {0x1FE0,0x3C5,0x306,0x0}, {0x1FE1,0x3C5,0x304,0x0}, {0x1FE2,0x3CB,0x300,0x0}, 
+  {0x1FE3,0x3CB,0x301,0x0}, {0x1FE4,0x3C1,0x313,0x0}, {0x1FE5,0x3C1,0x314,0x0}, 
+  {0x1FE6,0x3C5,0x342,0x0}, {0x1FE7,0x3CB,0x342,0x0}, {0x1FE8,0x3A5,0x306,0x0}, 
+  {0x1FE9,0x3A5,0x304,0x0}, {0x1FEA,0x3A5,0x300,0x0}, {0x1FEB,0x3A5,0x301,0x0}, 
+  {0x1FEC,0x3A1,0x314,0x0}, {0x1FED,0xA8,0x300,0x0}, {0x1FEE,0xA8,0x301,0x0}, 
+  {0x1FEF,0x60,0x0,0x0}, {0x1FF2,0x1F7C,0x345,0x0}, {0x1FF3,0x3C9,0x345,0x0}, 
+  {0x1FF4,0x1F79,0x345,0x0}, {0x1FF6,0x3C9,0x342,0x0}, {0x1FF7,0x1FF6,0x345,0x0}, 
+  {0x1FF8,0x39F,0x300,0x0}, {0x1FF9,0x39F,0x301,0x0}, {0x1FFA,0x3A9,0x300,0x0}, 
+  {0x1FFB,0x3A9,0x301,0x0}, {0x1FFC,0x3A9,0x345,0x0}, {0x1FFD,0xB4,0x0,0x0}, 
+  {0x2000,0x2002,0x0,0x0}, {0x2001,0x2003,0x0,0x0}, {0x2126,0x3A9,0x0,0x0}, 
+  {0x212A,0x4B,0x0,0x0}, {0x212B,0xC5,0x0,0x0}, {0x2204,0x2203,0x338,0x0}, 
+  {0x2209,0x2208,0x338,0x0}, {0x220C,0x220B,0x338,0x0}, {0x2224,0x2223,0x338,0x0}, 
+  {0x2226,0x2225,0x338,0x0}, {0x2241,0x7E,0x338,0x0}, {0x2244,0x2243,0x338,0x0}, 
+  {0x2247,0x2245,0x338,0x0}, {0x2249,0x2248,0x338,0x0}, {0x2260,0x3D,0x338,0x0}, 
+  {0x2262,0x2261,0x338,0x0}, {0x226D,0x224D,0x338,0x0}, {0x226E,0x3C,0x338,0x0}, 
+  {0x226F,0x3E,0x338,0x0}, {0x2270,0x2264,0x338,0x0}, {0x2271,0x2265,0x338,0x0}, 
+  {0x2274,0x2272,0x338,0x0}, {0x2275,0x2273,0x338,0x0}, {0x2278,0x2276,0x338,0x0}, 
+  {0x2279,0x2277,0x338,0x0}, {0x2280,0x227A,0x338,0x0}, {0x2281,0x227B,0x338,0x0}, 
+  {0x2284,0x2282,0x338,0x0}, {0x2285,0x2283,0x338,0x0}, {0x2288,0x2286,0x338,0x0}, 
+  {0x2289,0x2287,0x338,0x0}, {0x22AC,0x22A2,0x338,0x0}, {0x22AD,0x22A8,0x338,0x0}, 
+  {0x22AE,0x22A9,0x338,0x0}, {0x22AF,0x22AB,0x338,0x0}, {0x22E0,0x227C,0x338,0x0}, 
+  {0x22E1,0x227D,0x338,0x0}, {0x22E2,0x2291,0x338,0x0}, {0x22E3,0x2292,0x338,0x0}, 
+  {0x22EA,0x22B2,0x338,0x0}, {0x22EB,0x22B3,0x338,0x0}, {0x22EC,0x22B4,0x338,0x0}, 
+  {0x22ED,0x22B5,0x338,0x0}, {0x2329,0x3008,0x0,0x0}, {0x232A,0x3009,0x0,0x0}, 
+  {0x304C,0x304B,0x3099,0x0}, {0x304E,0x304D,0x3099,0x0}, {0x3050,0x304F,0x3099,0x0}, 
+  {0x3052,0x3051,0x3099,0x0}, {0x3054,0x3053,0x3099,0x0}, {0x3056,0x3055,0x3099,0x0}, 
+  {0x3058,0x3057,0x3099,0x0}, {0x305A,0x3059,0x3099,0x0}, {0x305C,0x305B,0x3099,0x0}, 
+  {0x305E,0x305D,0x3099,0x0}, {0x3060,0x305F,0x3099,0x0}, {0x3062,0x3061,0x3099,0x0}, 
+  {0x3065,0x3064,0x3099,0x0}, {0x3067,0x3066,0x3099,0x0}, {0x3069,0x3068,0x3099,0x0}, 
+  {0x3070,0x306F,0x3099,0x0}, {0x3071,0x306F,0x309A,0x0}, {0x3073,0x3072,0x3099,0x0}, 
+  {0x3074,0x3072,0x309A,0x0}, {0x3076,0x3075,0x3099,0x0}, {0x3077,0x3075,0x309A,0x0}, 
+  {0x3079,0x3078,0x3099,0x0}, {0x307A,0x3078,0x309A,0x0}, {0x307C,0x307B,0x3099,0x0}, 
+  {0x307D,0x307B,0x309A,0x0}, {0x3094,0x3046,0x3099,0x0}, {0x309E,0x309D,0x3099,0x0}, 
+  {0x30AC,0x30AB,0x3099,0x0}, {0x30AE,0x30AD,0x3099,0x0}, {0x30B0,0x30AF,0x3099,0x0}, 
+  {0x30B2,0x30B1,0x3099,0x0}, {0x30B4,0x30B3,0x3099,0x0}, {0x30B6,0x30B5,0x3099,0x0}, 
+  {0x30B8,0x30B7,0x3099,0x0}, {0x30BA,0x30B9,0x3099,0x0}, {0x30BC,0x30BB,0x3099,0x0}, 
+  {0x30BE,0x30BD,0x3099,0x0}, {0x30C0,0x30BF,0x3099,0x0}, {0x30C2,0x30C1,0x3099,0x0}, 
+  {0x30C5,0x30C4,0x3099,0x0}, {0x30C7,0x30C6,0x3099,0x0}, {0x30C9,0x30C8,0x3099,0x0}, 
+  {0x30D0,0x30CF,0x3099,0x0}, {0x30D1,0x30CF,0x309A,0x0}, {0x30D3,0x30D2,0x3099,0x0}, 
+  {0x30D4,0x30D2,0x309A,0x0}, {0x30D6,0x30D5,0x3099,0x0}, {0x30D7,0x30D5,0x309A,0x0}, 
+  {0x30D9,0x30D8,0x3099,0x0}, {0x30DA,0x30D8,0x309A,0x0}, {0x30DC,0x30DB,0x3099,0x0}, 
+  {0x30DD,0x30DB,0x309A,0x0}, {0x30F4,0x30A6,0x3099,0x0}, {0x30F7,0x30EF,0x3099,0x0}, 
+  {0x30F8,0x30F0,0x3099,0x0}, {0x30F9,0x30F1,0x3099,0x0}, {0x30FA,0x30F2,0x3099,0x0}, 
+  {0x30FE,0x30FD,0x3099,0x0}, {0xF900,0x8C48,0x0,0x0}, {0xF901,0x66F4,0x0,0x0}, 
+  {0xF902,0x8ECA,0x0,0x0}, {0xF903,0x8CC8,0x0,0x0}, {0xF904,0x6ED1,0x0,0x0}, 
+  {0xF905,0x4E32,0x0,0x0}, {0xF906,0x53E5,0x0,0x0}, {0xF907,0x9F9C,0x0,0x0}, 
+  {0xF908,0x9F9C,0x0,0x0}, {0xF909,0x5951,0x0,0x0}, {0xF90A,0x91D1,0x0,0x0}, 
+  {0xF90B,0x5587,0x0,0x0}, {0xF90C,0x5948,0x0,0x0}, {0xF90D,0x61F6,0x0,0x0}, 
+  {0xF90E,0x7669,0x0,0x0}, {0xF90F,0x7F85,0x0,0x0}, {0xF910,0x863F,0x0,0x0}, 
+  {0xF911,0x87BA,0x0,0x0}, {0xF912,0x88F8,0x0,0x0}, {0xF913,0x908F,0x0,0x0}, 
+  {0xF914,0x6A02,0x0,0x0}, {0xF915,0x6D1B,0x0,0x0}, {0xF916,0x70D9,0x0,0x0}, 
+  {0xF917,0x73DE,0x0,0x0}, {0xF918,0x843D,0x0,0x0}, {0xF919,0x916A,0x0,0x0}, 
+  {0xF91A,0x99F1,0x0,0x0}, {0xF91B,0x4E82,0x0,0x0}, {0xF91C,0x5375,0x0,0x0}, 
+  {0xF91D,0x6B04,0x0,0x0}, {0xF91E,0x721B,0x0,0x0}, {0xF91F,0x862D,0x0,0x0}, 
+  {0xF920,0x9E1E,0x0,0x0}, {0xF921,0x5D50,0x0,0x0}, {0xF922,0x6FEB,0x0,0x0}, 
+  {0xF923,0x85CD,0x0,0x0}, {0xF924,0x8964,0x0,0x0}, {0xF925,0x62C9,0x0,0x0}, 
+  {0xF926,0x81D8,0x0,0x0}, {0xF927,0x881F,0x0,0x0}, {0xF928,0x5ECA,0x0,0x0}, 
+  {0xF929,0x6717,0x0,0x0}, {0xF92A,0x6D6A,0x0,0x0}, {0xF92B,0x72FC,0x0,0x0}, 
+  {0xF92C,0x90CE,0x0,0x0}, {0xF92D,0x4F86,0x0,0x0}, {0xF92E,0x51B7,0x0,0x0}, 
+  {0xF92F,0x52DE,0x0,0x0}, {0xF930,0x64C4,0x0,0x0}, {0xF931,0x6AD3,0x0,0x0}, 
+  {0xF932,0x7210,0x0,0x0}, {0xF933,0x76E7,0x0,0x0}, {0xF934,0x8001,0x0,0x0}, 
+  {0xF935,0x8606,0x0,0x0}, {0xF936,0x865C,0x0,0x0}, {0xF937,0x8DEF,0x0,0x0}, 
+  {0xF938,0x9732,0x0,0x0}, {0xF939,0x9B6F,0x0,0x0}, {0xF93A,0x9DFA,0x0,0x0}, 
+  {0xF93B,0x788C,0x0,0x0}, {0xF93C,0x797F,0x0,0x0}, {0xF93D,0x7DA0,0x0,0x0}, 
+  {0xF93E,0x83C9,0x0,0x0}, {0xF93F,0x9304,0x0,0x0}, {0xF940,0x9E7F,0x0,0x0}, 
+  {0xF941,0x8AD6,0x0,0x0}, {0xF942,0x58DF,0x0,0x0}, {0xF943,0x5F04,0x0,0x0}, 
+  {0xF944,0x7C60,0x0,0x0}, {0xF945,0x807E,0x0,0x0}, {0xF946,0x7262,0x0,0x0}, 
+  {0xF947,0x78CA,0x0,0x0}, {0xF948,0x8CC2,0x0,0x0}, {0xF949,0x96F7,0x0,0x0}, 
+  {0xF94A,0x58D8,0x0,0x0}, {0xF94B,0x5C62,0x0,0x0}, {0xF94C,0x6A13,0x0,0x0}, 
+  {0xF94D,0x6DDA,0x0,0x0}, {0xF94E,0x6F0F,0x0,0x0}, {0xF94F,0x7D2F,0x0,0x0}, 
+  {0xF950,0x7E37,0x0,0x0}, {0xF951,0x96FB,0x0,0x0}, {0xF952,0x52D2,0x0,0x0}, 
+  {0xF953,0x808B,0x0,0x0}, {0xF954,0x51DC,0x0,0x0}, {0xF955,0x51CC,0x0,0x0}, 
+  {0xF956,0x7A1C,0x0,0x0}, {0xF957,0x7DBE,0x0,0x0}, {0xF958,0x83F1,0x0,0x0}, 
+  {0xF959,0x9675,0x0,0x0}, {0xF95A,0x8B80,0x0,0x0}, {0xF95B,0x62CF,0x0,0x0}, 
+  {0xF95C,0x6A02,0x0,0x0}, {0xF95D,0x8AFE,0x0,0x0}, {0xF95E,0x4E39,0x0,0x0}, 
+  {0xF95F,0x5BE7,0x0,0x0}, {0xF960,0x6012,0x0,0x0}, {0xF961,0x7387,0x0,0x0}, 
+  {0xF962,0x7570,0x0,0x0}, {0xF963,0x5317,0x0,0x0}, {0xF964,0x78FB,0x0,0x0}, 
+  {0xF965,0x4FBF,0x0,0x0}, {0xF966,0x5FA9,0x0,0x0}, {0xF967,0x4E0D,0x0,0x0}, 
+  {0xF968,0x6CCC,0x0,0x0}, {0xF969,0x6578,0x0,0x0}, {0xF96A,0x7D22,0x0,0x0}, 
+  {0xF96B,0x53C3,0x0,0x0}, {0xF96C,0x585E,0x0,0x0}, {0xF96D,0x7701,0x0,0x0}, 
+  {0xF96E,0x8449,0x0,0x0}, {0xF96F,0x8AAA,0x0,0x0}, {0xF970,0x6BBA,0x0,0x0}, 
+  {0xF971,0x8FB0,0x0,0x0}, {0xF972,0x6C88,0x0,0x0}, {0xF973,0x62FE,0x0,0x0}, 
+  {0xF974,0x82E5,0x0,0x0}, {0xF975,0x63A0,0x0,0x0}, {0xF976,0x7565,0x0,0x0}, 
+  {0xF977,0x4EAE,0x0,0x0}, {0xF978,0x5169,0x0,0x0}, {0xF979,0x51C9,0x0,0x0}, 
+  {0xF97A,0x6881,0x0,0x0}, {0xF97B,0x7CE7,0x0,0x0}, {0xF97C,0x826F,0x0,0x0}, 
+  {0xF97D,0x8AD2,0x0,0x0}, {0xF97E,0x91CF,0x0,0x0}, {0xF97F,0x52F5,0x0,0x0}, 
+  {0xF980,0x5442,0x0,0x0}, {0xF981,0x5973,0x0,0x0}, {0xF982,0x5EEC,0x0,0x0}, 
+  {0xF983,0x65C5,0x0,0x0}, {0xF984,0x6FFE,0x0,0x0}, {0xF985,0x792A,0x0,0x0}, 
+  {0xF986,0x95AD,0x0,0x0}, {0xF987,0x9A6A,0x0,0x0}, {0xF988,0x9E97,0x0,0x0}, 
+  {0xF989,0x9ECE,0x0,0x0}, {0xF98A,0x529B,0x0,0x0}, {0xF98B,0x66C6,0x0,0x0}, 
+  {0xF98C,0x6B77,0x0,0x0}, {0xF98D,0x8F62,0x0,0x0}, {0xF98E,0x5E74,0x0,0x0}, 
+  {0xF98F,0x6190,0x0,0x0}, {0xF990,0x6200,0x0,0x0}, {0xF991,0x649A,0x0,0x0}, 
+  {0xF992,0x6F23,0x0,0x0}, {0xF993,0x7149,0x0,0x0}, {0xF994,0x7489,0x0,0x0}, 
+  {0xF995,0x79CA,0x0,0x0}, {0xF996,0x7DF4,0x0,0x0}, {0xF997,0x806F,0x0,0x0}, 
+  {0xF998,0x8F26,0x0,0x0}, {0xF999,0x84EE,0x0,0x0}, {0xF99A,0x9023,0x0,0x0}, 
+  {0xF99B,0x934A,0x0,0x0}, {0xF99C,0x5217,0x0,0x0}, {0xF99D,0x52A3,0x0,0x0}, 
+  {0xF99E,0x54BD,0x0,0x0}, {0xF99F,0x70C8,0x0,0x0}, {0xF9A0,0x88C2,0x0,0x0}, 
+  {0xF9A1,0x8AAA,0x0,0x0}, {0xF9A2,0x5EC9,0x0,0x0}, {0xF9A3,0x5FF5,0x0,0x0}, 
+  {0xF9A4,0x637B,0x0,0x0}, {0xF9A5,0x6BAE,0x0,0x0}, {0xF9A6,0x7C3E,0x0,0x0}, 
+  {0xF9A7,0x7375,0x0,0x0}, {0xF9A8,0x4EE4,0x0,0x0}, {0xF9A9,0x56F9,0x0,0x0}, 
+  {0xF9AA,0x5BE7,0x0,0x0}, {0xF9AB,0x5DBA,0x0,0x0}, {0xF9AC,0x601C,0x0,0x0}, 
+  {0xF9AD,0x73B2,0x0,0x0}, {0xF9AE,0x7469,0x0,0x0}, {0xF9AF,0x7F9A,0x0,0x0}, 
+  {0xF9B0,0x8046,0x0,0x0}, {0xF9B1,0x9234,0x0,0x0}, {0xF9B2,0x96F6,0x0,0x0}, 
+  {0xF9B3,0x9748,0x0,0x0}, {0xF9B4,0x9818,0x0,0x0}, {0xF9B5,0x4F8B,0x0,0x0}, 
+  {0xF9B6,0x79AE,0x0,0x0}, {0xF9B7,0x91B4,0x0,0x0}, {0xF9B8,0x96B8,0x0,0x0}, 
+  {0xF9B9,0x60E1,0x0,0x0}, {0xF9BA,0x4E86,0x0,0x0}, {0xF9BB,0x50DA,0x0,0x0}, 
+  {0xF9BC,0x5BEE,0x0,0x0}, {0xF9BD,0x5C3F,0x0,0x0}, {0xF9BE,0x6599,0x0,0x0}, 
+  {0xF9BF,0x6A02,0x0,0x0}, {0xF9C0,0x71CE,0x0,0x0}, {0xF9C1,0x7642,0x0,0x0}, 
+  {0xF9C2,0x84FC,0x0,0x0}, {0xF9C3,0x907C,0x0,0x0}, {0xF9C4,0x9F8D,0x0,0x0}, 
+  {0xF9C5,0x6688,0x0,0x0}, {0xF9C6,0x962E,0x0,0x0}, {0xF9C7,0x5289,0x0,0x0}, 
+  {0xF9C8,0x677B,0x0,0x0}, {0xF9C9,0x67F3,0x0,0x0}, {0xF9CA,0x6D41,0x0,0x0}, 
+  {0xF9CB,0x6E9C,0x0,0x0}, {0xF9CC,0x7409,0x0,0x0}, {0xF9CD,0x7559,0x0,0x0}, 
+  {0xF9CE,0x786B,0x0,0x0}, {0xF9CF,0x7D10,0x0,0x0}, {0xF9D0,0x985E,0x0,0x0}, 
+  {0xF9D1,0x516D,0x0,0x0}, {0xF9D2,0x622E,0x0,0x0}, {0xF9D3,0x9678,0x0,0x0}, 
+  {0xF9D4,0x502B,0x0,0x0}, {0xF9D5,0x5D19,0x0,0x0}, {0xF9D6,0x6DEA,0x0,0x0}, 
+  {0xF9D7,0x8F2A,0x0,0x0}, {0xF9D8,0x5F8B,0x0,0x0}, {0xF9D9,0x6144,0x0,0x0}, 
+  {0xF9DA,0x6817,0x0,0x0}, {0xF9DB,0x7387,0x0,0x0}, {0xF9DC,0x9686,0x0,0x0}, 
+  {0xF9DD,0x5229,0x0,0x0}, {0xF9DE,0x540F,0x0,0x0}, {0xF9DF,0x5C65,0x0,0x0}, 
+  {0xF9E0,0x6613,0x0,0x0}, {0xF9E1,0x674E,0x0,0x0}, {0xF9E2,0x68A8,0x0,0x0}, 
+  {0xF9E3,0x6CE5,0x0,0x0}, {0xF9E4,0x7406,0x0,0x0}, {0xF9E5,0x75E2,0x0,0x0}, 
+  {0xF9E6,0x7F79,0x0,0x0}, {0xF9E7,0x88CF,0x0,0x0}, {0xF9E8,0x88E1,0x0,0x0}, 
+  {0xF9E9,0x91CC,0x0,0x0}, {0xF9EA,0x96E2,0x0,0x0}, {0xF9EB,0x533F,0x0,0x0}, 
+  {0xF9EC,0x6EBA,0x0,0x0}, {0xF9ED,0x541D,0x0,0x0}, {0xF9EE,0x71D0,0x0,0x0}, 
+  {0xF9EF,0x7498,0x0,0x0}, {0xF9F0,0x85FA,0x0,0x0}, {0xF9F1,0x96A3,0x0,0x0}, 
+  {0xF9F2,0x9C57,0x0,0x0}, {0xF9F3,0x9E9F,0x0,0x0}, {0xF9F4,0x6797,0x0,0x0}, 
+  {0xF9F5,0x6DCB,0x0,0x0}, {0xF9F6,0x81E8,0x0,0x0}, {0xF9F7,0x7ACB,0x0,0x0}, 
+  {0xF9F8,0x7B20,0x0,0x0}, {0xF9F9,0x7C92,0x0,0x0}, {0xF9FA,0x72C0,0x0,0x0}, 
+  {0xF9FB,0x7099,0x0,0x0}, {0xF9FC,0x8B58,0x0,0x0}, {0xF9FD,0x4EC0,0x0,0x0}, 
+  {0xF9FE,0x8336,0x0,0x0}, {0xF9FF,0x523A,0x0,0x0}, {0xFA00,0x5207,0x0,0x0}, 
+  {0xFA01,0x5EA6,0x0,0x0}, {0xFA02,0x62D3,0x0,0x0}, {0xFA03,0x7CD6,0x0,0x0}, 
+  {0xFA04,0x5B85,0x0,0x0}, {0xFA05,0x6D1E,0x0,0x0}, {0xFA06,0x66B4,0x0,0x0}, 
+  {0xFA07,0x8F3B,0x0,0x0}, {0xFA08,0x884C,0x0,0x0}, {0xFA09,0x964D,0x0,0x0}, 
+  {0xFA0A,0x898B,0x0,0x0}, {0xFA0B,0x5ED3,0x0,0x0}, {0xFA0C,0x5140,0x0,0x0}, 
+  {0xFA0D,0x55C0,0x0,0x0}, {0xFA10,0x585A,0x0,0x0}, {0xFA12,0x6674,0x0,0x0}, 
+  {0xFA15,0x51DE,0x0,0x0}, {0xFA16,0x732A,0x0,0x0}, {0xFA17,0x76CA,0x0,0x0}, 
+  {0xFA18,0x793C,0x0,0x0}, {0xFA19,0x795E,0x0,0x0}, {0xFA1A,0x7965,0x0,0x0}, 
+  {0xFA1B,0x798F,0x0,0x0}, {0xFA1C,0x9756,0x0,0x0}, {0xFA1D,0x7CBE,0x0,0x0}, 
+  {0xFA1E,0x7FBD,0x0,0x0}, {0xFA20,0x8612,0x0,0x0}, {0xFA22,0x8AF8,0x0,0x0}, 
+  {0xFA25,0x9038,0x0,0x0}, {0xFA26,0x90FD,0x0,0x0}, {0xFA2A,0x98EF,0x0,0x0}, 
+  {0xFA2B,0x98FC,0x0,0x0}, {0xFA2C,0x9928,0x0,0x0}, {0xFA2D,0x9DB4,0x0,0x0}, 
+  {0xFB1F,0x5F2,0x5B7,0x0}, {0xFB2A,0x5E9,0x5C1,0x0}, {0xFB2B,0x5E9,0x5C2,0x0}, 
+  {0xFB2C,0x5E9,0x5BC,0x5C1}, {0xFB2D,0x5E9,0x5BC,0x5C2}, {0xFB2E,0x5D0,0x5B7,0x0}, 
+  {0xFB2F,0x5D0,0x5B8,0x0}, {0xFB30,0x5D0,0x5BC,0x0}, {0xFB31,0x5D1,0x5BC,0x0}, 
+  {0xFB32,0x5D2,0x5BC,0x0}, {0xFB33,0x5D3,0x5BC,0x0}, {0xFB34,0x5D4,0x5BC,0x0}, 
+  {0xFB35,0x5D5,0x5BC,0x0}, {0xFB36,0x5D6,0x5BC,0x0}, {0xFB38,0x5D8,0x5BC,0x0}, 
+  {0xFB39,0x5D9,0x5BC,0x0}, {0xFB3A,0x5DA,0x5BC,0x0}, {0xFB3B,0x5DB,0x5BC,0x0}, 
+  {0xFB3C,0x5DC,0x5BC,0x0}, {0xFB3E,0x5DE,0x5BC,0x0}, {0xFB40,0x5E0,0x5BC,0x0}, 
+  {0xFB41,0x5E1,0x5BC,0x0}, {0xFB43,0x5E3,0x5BC,0x0}, {0xFB44,0x5E4,0x5BC,0x0}, 
+  {0xFB46,0x5E6,0x5BC,0x0}, {0xFB47,0x5E7,0x5BC,0x0}, {0xFB48,0x5E8,0x5BC,0x0}, 
+  {0xFB49,0x5E9,0x5BC,0x0}, {0xFB4A,0x5EA,0x5BC,0x0}, {0xFB4B,0x5D5,0x5B9,0x0}, 
+  {0xFB4C,0x5D1,0x5BF,0x0}, {0xFB4D,0x5DB,0x5BF,0x0}, {0xFB4E,0x5E4,0x5BF,0x0} 
+};
+
+
+/* All upper-case translations are included here */
+
+#define NUM_TOUPPER_INFO 677
+static const unirelate toupper_info[NUM_TOUPPER_INFO] = {
+  {0x61,0x41}, {0x62,0x42}, {0x63,0x43}, {0x64,0x44}, {0x65,0x45}, 
+  {0x66,0x46}, {0x67,0x47}, {0x68,0x48}, {0x69,0x49}, {0x6A,0x4A}, 
+  {0x6B,0x4B}, {0x6C,0x4C}, {0x6D,0x4D}, {0x6E,0x4E}, {0x6F,0x4F}, 
+  {0x70,0x50}, {0x71,0x51}, {0x72,0x52}, {0x73,0x53}, {0x74,0x54}, 
+  {0x75,0x55}, {0x76,0x56}, {0x77,0x57}, {0x78,0x58}, {0x79,0x59}, 
+  {0x7A,0x5A}, {0xE0,0xC0}, {0xE1,0xC1}, {0xE2,0xC2}, {0xE3,0xC3}, 
+  {0xE4,0xC4}, {0xE5,0xC5}, {0xE6,0xC6}, {0xE7,0xC7}, {0xE8,0xC8}, 
+  {0xE9,0xC9}, {0xEA,0xCA}, {0xEB,0xCB}, {0xEC,0xCC}, {0xED,0xCD}, 
+  {0xEE,0xCE}, {0xEF,0xCF}, {0xF0,0xD0}, {0xF1,0xD1}, {0xF2,0xD2}, 
+  {0xF3,0xD3}, {0xF4,0xD4}, {0xF5,0xD5}, {0xF6,0xD6}, {0xF8,0xD8}, 
+  {0xF9,0xD9}, {0xFA,0xDA}, {0xFB,0xDB}, {0xFC,0xDC}, {0xFD,0xDD}, 
+  {0xFE,0xDE}, {0xFF,0x178}, {0x101,0x100}, {0x103,0x102}, {0x105,0x104}, 
+  {0x107,0x106}, {0x109,0x108}, {0x10B,0x10A}, {0x10D,0x10C}, {0x10F,0x10E}, 
+  {0x111,0x110}, {0x113,0x112}, {0x115,0x114}, {0x117,0x116}, {0x119,0x118}, 
+  {0x11B,0x11A}, {0x11D,0x11C}, {0x11F,0x11E}, {0x121,0x120}, {0x123,0x122}, 
+  {0x125,0x124}, {0x127,0x126}, {0x129,0x128}, {0x12B,0x12A}, {0x12D,0x12C}, 
+  {0x12F,0x12E}, {0x131,0x49}, {0x133,0x132}, {0x135,0x134}, {0x137,0x136}, 
+  {0x13A,0x139}, {0x13C,0x13B}, {0x13E,0x13D}, {0x140,0x13F}, {0x142,0x141}, 
+  {0x144,0x143}, {0x146,0x145}, {0x148,0x147}, {0x14B,0x14A}, {0x14D,0x14C}, 
+  {0x14F,0x14E}, {0x151,0x150}, {0x153,0x152}, {0x155,0x154}, {0x157,0x156}, 
+  {0x159,0x158}, {0x15B,0x15A}, {0x15D,0x15C}, {0x15F,0x15E}, {0x161,0x160}, 
+  {0x163,0x162}, {0x165,0x164}, {0x167,0x166}, {0x169,0x168}, {0x16B,0x16A}, 
+  {0x16D,0x16C}, {0x16F,0x16E}, {0x171,0x170}, {0x173,0x172}, {0x175,0x174}, 
+  {0x177,0x176}, {0x17A,0x179}, {0x17C,0x17B}, {0x17E,0x17D}, {0x17F,0x53}, 
+  {0x183,0x182}, {0x185,0x184}, {0x188,0x187}, {0x18C,0x18B}, {0x192,0x191}, 
+  {0x199,0x198}, {0x1A1,0x1A0}, {0x1A3,0x1A2}, {0x1A5,0x1A4}, {0x1A8,0x1A7}, 
+  {0x1AD,0x1AC}, {0x1B0,0x1AF}, {0x1B4,0x1B3}, {0x1B6,0x1B5}, {0x1B9,0x1B8}, 
+  {0x1BD,0x1BC}, {0x1C5,0x1C4}, {0x1C6,0x1C4}, {0x1C8,0x1C7}, {0x1C9,0x1C7}, 
+  {0x1CB,0x1CA}, {0x1CC,0x1CA}, {0x1CE,0x1CD}, {0x1D0,0x1CF}, {0x1D2,0x1D1}, 
+  {0x1D4,0x1D3}, {0x1D6,0x1D5}, {0x1D8,0x1D7}, {0x1DA,0x1D9}, {0x1DC,0x1DB}, 
+  {0x1DD,0x18E}, {0x1DF,0x1DE}, {0x1E1,0x1E0}, {0x1E3,0x1E2}, {0x1E5,0x1E4}, 
+  {0x1E7,0x1E6}, {0x1E9,0x1E8}, {0x1EB,0x1EA}, {0x1ED,0x1EC}, {0x1EF,0x1EE}, 
+  {0x1F2,0x1F1}, {0x1F3,0x1F1}, {0x1F5,0x1F4}, {0x1FB,0x1FA}, {0x1FD,0x1FC}, 
+  {0x1FF,0x1FE}, {0x201,0x200}, {0x203,0x202}, {0x205,0x204}, {0x207,0x206}, 
+  {0x209,0x208}, {0x20B,0x20A}, {0x20D,0x20C}, {0x20F,0x20E}, {0x211,0x210}, 
+  {0x213,0x212}, {0x215,0x214}, {0x217,0x216}, {0x253,0x181}, {0x254,0x186}, 
+  {0x256,0x189}, {0x257,0x18A}, {0x259,0x18F}, {0x25B,0x190}, {0x260,0x193}, 
+  {0x263,0x194}, {0x268,0x197}, {0x269,0x196}, {0x26F,0x19C}, {0x272,0x19D}, 
+  {0x275,0x19F}, {0x283,0x1A9}, {0x288,0x1AE}, {0x28A,0x1B1}, {0x28B,0x1B2}, 
+  {0x292,0x1B7}, {0x3AC,0x386}, {0x3AD,0x388}, {0x3AE,0x389}, {0x3AF,0x38A}, 
+  {0x3B1,0x391}, {0x3B2,0x392}, {0x3B3,0x393}, {0x3B4,0x394}, {0x3B5,0x395}, 
+  {0x3B6,0x396}, {0x3B7,0x397}, {0x3B8,0x398}, {0x3B9,0x399}, {0x3BA,0x39A}, 
+  {0x3BB,0x39B}, {0x3BC,0x39C}, {0x3BD,0x39D}, {0x3BE,0x39E}, {0x3BF,0x39F}, 
+  {0x3C0,0x3A0}, {0x3C1,0x3A1}, {0x3C2,0x3A3}, {0x3C3,0x3A3}, {0x3C4,0x3A4}, 
+  {0x3C5,0x3A5}, {0x3C6,0x3A6}, {0x3C7,0x3A7}, {0x3C8,0x3A8}, {0x3C9,0x3A9}, 
+  {0x3CA,0x3AA}, {0x3CB,0x3AB}, {0x3CC,0x38C}, {0x3CD,0x38E}, {0x3CE,0x38F}, 
+  {0x3D0,0x392}, {0x3D1,0x398}, {0x3D5,0x3A6}, {0x3D6,0x3A0}, {0x3E3,0x3E2}, 
+  {0x3E5,0x3E4}, {0x3E7,0x3E6}, {0x3E9,0x3E8}, {0x3EB,0x3EA}, {0x3ED,0x3EC}, 
+  {0x3EF,0x3EE}, {0x3F0,0x39A}, {0x3F1,0x3A1}, {0x3F2,0x3A3}, {0x430,0x410}, 
+  {0x431,0x411}, {0x432,0x412}, {0x433,0x413}, {0x434,0x414}, {0x435,0x415}, 
+  {0x436,0x416}, {0x437,0x417}, {0x438,0x418}, {0x439,0x419}, {0x43A,0x41A}, 
+  {0x43B,0x41B}, {0x43C,0x41C}, {0x43D,0x41D}, {0x43E,0x41E}, {0x43F,0x41F}, 
+  {0x440,0x420}, {0x441,0x421}, {0x442,0x422}, {0x443,0x423}, {0x444,0x424}, 
+  {0x445,0x425}, {0x446,0x426}, {0x447,0x427}, {0x448,0x428}, {0x449,0x429}, 
+  {0x44A,0x42A}, {0x44B,0x42B}, {0x44C,0x42C}, {0x44D,0x42D}, {0x44E,0x42E}, 
+  {0x44F,0x42F}, {0x451,0x401}, {0x452,0x402}, {0x453,0x403}, {0x454,0x404}, 
+  {0x455,0x405}, {0x456,0x406}, {0x457,0x407}, {0x458,0x408}, {0x459,0x409}, 
+  {0x45A,0x40A}, {0x45B,0x40B}, {0x45C,0x40C}, {0x45E,0x40E}, {0x45F,0x40F}, 
+  {0x461,0x460}, {0x463,0x462}, {0x465,0x464}, {0x467,0x466}, {0x469,0x468}, 
+  {0x46B,0x46A}, {0x46D,0x46C}, {0x46F,0x46E}, {0x471,0x470}, {0x473,0x472}, 
+  {0x475,0x474}, {0x477,0x476}, {0x479,0x478}, {0x47B,0x47A}, {0x47D,0x47C}, 
+  {0x47F,0x47E}, {0x481,0x480}, {0x491,0x490}, {0x493,0x492}, {0x495,0x494}, 
+  {0x497,0x496}, {0x499,0x498}, {0x49B,0x49A}, {0x49D,0x49C}, {0x49F,0x49E}, 
+  {0x4A1,0x4A0}, {0x4A3,0x4A2}, {0x4A5,0x4A4}, {0x4A7,0x4A6}, {0x4A9,0x4A8}, 
+  {0x4AB,0x4AA}, {0x4AD,0x4AC}, {0x4AF,0x4AE}, {0x4B1,0x4B0}, {0x4B3,0x4B2}, 
+  {0x4B5,0x4B4}, {0x4B7,0x4B6}, {0x4B9,0x4B8}, {0x4BB,0x4BA}, {0x4BD,0x4BC}, 
+  {0x4BF,0x4BE}, {0x4C2,0x4C1}, {0x4C4,0x4C3}, {0x4C8,0x4C7}, {0x4CC,0x4CB}, 
+  {0x4D1,0x4D0}, {0x4D3,0x4D2}, {0x4D5,0x4D4}, {0x4D7,0x4D6}, {0x4D9,0x4D8}, 
+  {0x4DB,0x4DA}, {0x4DD,0x4DC}, {0x4DF,0x4DE}, {0x4E1,0x4E0}, {0x4E3,0x4E2}, 
+  {0x4E5,0x4E4}, {0x4E7,0x4E6}, {0x4E9,0x4E8}, {0x4EB,0x4EA}, {0x4EF,0x4EE}, 
+  {0x4F1,0x4F0}, {0x4F3,0x4F2}, {0x4F5,0x4F4}, {0x4F9,0x4F8}, {0x561,0x531}, 
+  {0x562,0x532}, {0x563,0x533}, {0x564,0x534}, {0x565,0x535}, {0x566,0x536}, 
+  {0x567,0x537}, {0x568,0x538}, {0x569,0x539}, {0x56A,0x53A}, {0x56B,0x53B}, 
+  {0x56C,0x53C}, {0x56D,0x53D}, {0x56E,0x53E}, {0x56F,0x53F}, {0x570,0x540}, 
+  {0x571,0x541}, {0x572,0x542}, {0x573,0x543}, {0x574,0x544}, {0x575,0x545}, 
+  {0x576,0x546}, {0x577,0x547}, {0x578,0x548}, {0x579,0x549}, {0x57A,0x54A}, 
+  {0x57B,0x54B}, {0x57C,0x54C}, {0x57D,0x54D}, {0x57E,0x54E}, {0x57F,0x54F}, 
+  {0x580,0x550}, {0x581,0x551}, {0x582,0x552}, {0x583,0x553}, {0x584,0x554}, 
+  {0x585,0x555}, {0x586,0x556}, {0x1E01,0x1E00}, {0x1E03,0x1E02}, {0x1E05,0x1E04}, 
+  {0x1E07,0x1E06}, {0x1E09,0x1E08}, {0x1E0B,0x1E0A}, {0x1E0D,0x1E0C}, {0x1E0F,0x1E0E}, 
+  {0x1E11,0x1E10}, {0x1E13,0x1E12}, {0x1E15,0x1E14}, {0x1E17,0x1E16}, {0x1E19,0x1E18}, 
+  {0x1E1B,0x1E1A}, {0x1E1D,0x1E1C}, {0x1E1F,0x1E1E}, {0x1E21,0x1E20}, {0x1E23,0x1E22}, 
+  {0x1E25,0x1E24}, {0x1E27,0x1E26}, {0x1E29,0x1E28}, {0x1E2B,0x1E2A}, {0x1E2D,0x1E2C}, 
+  {0x1E2F,0x1E2E}, {0x1E31,0x1E30}, {0x1E33,0x1E32}, {0x1E35,0x1E34}, {0x1E37,0x1E36}, 
+  {0x1E39,0x1E38}, {0x1E3B,0x1E3A}, {0x1E3D,0x1E3C}, {0x1E3F,0x1E3E}, {0x1E41,0x1E40}, 
+  {0x1E43,0x1E42}, {0x1E45,0x1E44}, {0x1E47,0x1E46}, {0x1E49,0x1E48}, {0x1E4B,0x1E4A}, 
+  {0x1E4D,0x1E4C}, {0x1E4F,0x1E4E}, {0x1E51,0x1E50}, {0x1E53,0x1E52}, {0x1E55,0x1E54}, 
+  {0x1E57,0x1E56}, {0x1E59,0x1E58}, {0x1E5B,0x1E5A}, {0x1E5D,0x1E5C}, {0x1E5F,0x1E5E}, 
+  {0x1E61,0x1E60}, {0x1E63,0x1E62}, {0x1E65,0x1E64}, {0x1E67,0x1E66}, {0x1E69,0x1E68}, 
+  {0x1E6B,0x1E6A}, {0x1E6D,0x1E6C}, {0x1E6F,0x1E6E}, {0x1E71,0x1E70}, {0x1E73,0x1E72}, 
+  {0x1E75,0x1E74}, {0x1E77,0x1E76}, {0x1E79,0x1E78}, {0x1E7B,0x1E7A}, {0x1E7D,0x1E7C}, 
+  {0x1E7F,0x1E7E}, {0x1E81,0x1E80}, {0x1E83,0x1E82}, {0x1E85,0x1E84}, {0x1E87,0x1E86}, 
+  {0x1E89,0x1E88}, {0x1E8B,0x1E8A}, {0x1E8D,0x1E8C}, {0x1E8F,0x1E8E}, {0x1E91,0x1E90}, 
+  {0x1E93,0x1E92}, {0x1E95,0x1E94}, {0x1E9B,0x1E60}, {0x1EA1,0x1EA0}, {0x1EA3,0x1EA2}, 
+  {0x1EA5,0x1EA4}, {0x1EA7,0x1EA6}, {0x1EA9,0x1EA8}, {0x1EAB,0x1EAA}, {0x1EAD,0x1EAC}, 
+  {0x1EAF,0x1EAE}, {0x1EB1,0x1EB0}, {0x1EB3,0x1EB2}, {0x1EB5,0x1EB4}, {0x1EB7,0x1EB6}, 
+  {0x1EB9,0x1EB8}, {0x1EBB,0x1EBA}, {0x1EBD,0x1EBC}, {0x1EBF,0x1EBE}, {0x1EC1,0x1EC0}, 
+  {0x1EC3,0x1EC2}, {0x1EC5,0x1EC4}, {0x1EC7,0x1EC6}, {0x1EC9,0x1EC8}, {0x1ECB,0x1ECA}, 
+  {0x1ECD,0x1ECC}, {0x1ECF,0x1ECE}, {0x1ED1,0x1ED0}, {0x1ED3,0x1ED2}, {0x1ED5,0x1ED4}, 
+  {0x1ED7,0x1ED6}, {0x1ED9,0x1ED8}, {0x1EDB,0x1EDA}, {0x1EDD,0x1EDC}, {0x1EDF,0x1EDE}, 
+  {0x1EE1,0x1EE0}, {0x1EE3,0x1EE2}, {0x1EE5,0x1EE4}, {0x1EE7,0x1EE6}, {0x1EE9,0x1EE8}, 
+  {0x1EEB,0x1EEA}, {0x1EED,0x1EEC}, {0x1EEF,0x1EEE}, {0x1EF1,0x1EF0}, {0x1EF3,0x1EF2}, 
+  {0x1EF5,0x1EF4}, {0x1EF7,0x1EF6}, {0x1EF9,0x1EF8}, {0x1F00,0x1F08}, {0x1F01,0x1F09}, 
+  {0x1F02,0x1F0A}, {0x1F03,0x1F0B}, {0x1F04,0x1F0C}, {0x1F05,0x1F0D}, {0x1F06,0x1F0E}, 
+  {0x1F07,0x1F0F}, {0x1F10,0x1F18}, {0x1F11,0x1F19}, {0x1F12,0x1F1A}, {0x1F13,0x1F1B}, 
+  {0x1F14,0x1F1C}, {0x1F15,0x1F1D}, {0x1F20,0x1F28}, {0x1F21,0x1F29}, {0x1F22,0x1F2A}, 
+  {0x1F23,0x1F2B}, {0x1F24,0x1F2C}, {0x1F25,0x1F2D}, {0x1F26,0x1F2E}, {0x1F27,0x1F2F}, 
+  {0x1F30,0x1F38}, {0x1F31,0x1F39}, {0x1F32,0x1F3A}, {0x1F33,0x1F3B}, {0x1F34,0x1F3C}, 
+  {0x1F35,0x1F3D}, {0x1F36,0x1F3E}, {0x1F37,0x1F3F}, {0x1F40,0x1F48}, {0x1F41,0x1F49}, 
+  {0x1F42,0x1F4A}, {0x1F43,0x1F4B}, {0x1F44,0x1F4C}, {0x1F45,0x1F4D}, {0x1F51,0x1F59}, 
+  {0x1F53,0x1F5B}, {0x1F55,0x1F5D}, {0x1F57,0x1F5F}, {0x1F60,0x1F68}, {0x1F61,0x1F69}, 
+  {0x1F62,0x1F6A}, {0x1F63,0x1F6B}, {0x1F64,0x1F6C}, {0x1F65,0x1F6D}, {0x1F66,0x1F6E}, 
+  {0x1F67,0x1F6F}, {0x1F70,0x1FBA}, {0x1F71,0x1FBB}, {0x1F72,0x1FC8}, {0x1F73,0x1FC9}, 
+  {0x1F74,0x1FCA}, {0x1F75,0x1FCB}, {0x1F76,0x1FDA}, {0x1F77,0x1FDB}, {0x1F78,0x1FF8}, 
+  {0x1F79,0x1FF9}, {0x1F7A,0x1FEA}, {0x1F7B,0x1FEB}, {0x1F7C,0x1FFA}, {0x1F7D,0x1FFB}, 
+  {0x1F80,0x1F88}, {0x1F81,0x1F89}, {0x1F82,0x1F8A}, {0x1F83,0x1F8B}, {0x1F84,0x1F8C}, 
+  {0x1F85,0x1F8D}, {0x1F86,0x1F8E}, {0x1F87,0x1F8F}, {0x1F90,0x1F98}, {0x1F91,0x1F99}, 
+  {0x1F92,0x1F9A}, {0x1F93,0x1F9B}, {0x1F94,0x1F9C}, {0x1F95,0x1F9D}, {0x1F96,0x1F9E}, 
+  {0x1F97,0x1F9F}, {0x1FA0,0x1FA8}, {0x1FA1,0x1FA9}, {0x1FA2,0x1FAA}, {0x1FA3,0x1FAB}, 
+  {0x1FA4,0x1FAC}, {0x1FA5,0x1FAD}, {0x1FA6,0x1FAE}, {0x1FA7,0x1FAF}, {0x1FB0,0x1FB8}, 
+  {0x1FB1,0x1FB9}, {0x1FB3,0x1FBC}, {0x1FC3,0x1FCC}, {0x1FD0,0x1FD8}, {0x1FD1,0x1FD9}, 
+  {0x1FE0,0x1FE8}, {0x1FE1,0x1FE9}, {0x1FE5,0x1FEC}, {0x1FF3,0x1FFC}, {0x2170,0x2160}, 
+  {0x2171,0x2161}, {0x2172,0x2162}, {0x2173,0x2163}, {0x2174,0x2164}, {0x2175,0x2165}, 
+  {0x2176,0x2166}, {0x2177,0x2167}, {0x2178,0x2168}, {0x2179,0x2169}, {0x217A,0x216A}, 
+  {0x217B,0x216B}, {0x217C,0x216C}, {0x217D,0x216D}, {0x217E,0x216E}, {0x217F,0x216F}, 
+  {0x24D0,0x24B6}, {0x24D1,0x24B7}, {0x24D2,0x24B8}, {0x24D3,0x24B9}, {0x24D4,0x24BA}, 
+  {0x24D5,0x24BB}, {0x24D6,0x24BC}, {0x24D7,0x24BD}, {0x24D8,0x24BE}, {0x24D9,0x24BF}, 
+  {0x24DA,0x24C0}, {0x24DB,0x24C1}, {0x24DC,0x24C2}, {0x24DD,0x24C3}, {0x24DE,0x24C4}, 
+  {0x24DF,0x24C5}, {0x24E0,0x24C6}, {0x24E1,0x24C7}, {0x24E2,0x24C8}, {0x24E3,0x24C9}, 
+  {0x24E4,0x24CA}, {0x24E5,0x24CB}, {0x24E6,0x24CC}, {0x24E7,0x24CD}, {0x24E8,0x24CE}, 
+  {0x24E9,0x24CF}, {0xFF41,0xFF21}, {0xFF42,0xFF22}, {0xFF43,0xFF23}, {0xFF44,0xFF24}, 
+  {0xFF45,0xFF25}, {0xFF46,0xFF26}, {0xFF47,0xFF27}, {0xFF48,0xFF28}, {0xFF49,0xFF29}, 
+  {0xFF4A,0xFF2A}, {0xFF4B,0xFF2B}, {0xFF4C,0xFF2C}, {0xFF4D,0xFF2D}, {0xFF4E,0xFF2E}, 
+  {0xFF4F,0xFF2F}, {0xFF50,0xFF30}, {0xFF51,0xFF31}, {0xFF52,0xFF32}, {0xFF53,0xFF33}, 
+  {0xFF54,0xFF34}, {0xFF55,0xFF35}, {0xFF56,0xFF36}, {0xFF57,0xFF37}, {0xFF58,0xFF38}, 
+  {0xFF59,0xFF39}, {0xFF5A,0xFF3A} 
+};
+
+
+
+/* All lower-case translations are included here */
+
+#define NUM_TOLOWER_INFO 705
+static const unirelate tolower_info[NUM_TOLOWER_INFO] = {
+  {0x41,0x61}, {0x42,0x62}, {0x43,0x63}, {0x44,0x64}, {0x45,0x65}, 
+  {0x46,0x66}, {0x47,0x67}, {0x48,0x68}, {0x49,0x69}, {0x4A,0x6A}, 
+  {0x4B,0x6B}, {0x4C,0x6C}, {0x4D,0x6D}, {0x4E,0x6E}, {0x4F,0x6F}, 
+  {0x50,0x70}, {0x51,0x71}, {0x52,0x72}, {0x53,0x73}, {0x54,0x74}, 
+  {0x55,0x75}, {0x56,0x76}, {0x57,0x77}, {0x58,0x78}, {0x59,0x79}, 
+  {0x5A,0x7A}, {0xC0,0xE0}, {0xC1,0xE1}, {0xC2,0xE2}, {0xC3,0xE3}, 
+  {0xC4,0xE4}, {0xC5,0xE5}, {0xC6,0xE6}, {0xC7,0xE7}, {0xC8,0xE8}, 
+  {0xC9,0xE9}, {0xCA,0xEA}, {0xCB,0xEB}, {0xCC,0xEC}, {0xCD,0xED}, 
+  {0xCE,0xEE}, {0xCF,0xEF}, {0xD0,0xF0}, {0xD1,0xF1}, {0xD2,0xF2}, 
+  {0xD3,0xF3}, {0xD4,0xF4}, {0xD5,0xF5}, {0xD6,0xF6}, {0xD8,0xF8}, 
+  {0xD9,0xF9}, {0xDA,0xFA}, {0xDB,0xFB}, {0xDC,0xFC}, {0xDD,0xFD}, 
+  {0xDE,0xFE}, {0x100,0x101}, {0x102,0x103}, {0x104,0x105}, {0x106,0x107}, 
+  {0x108,0x109}, {0x10A,0x10B}, {0x10C,0x10D}, {0x10E,0x10F}, {0x110,0x111}, 
+  {0x112,0x113}, {0x114,0x115}, {0x116,0x117}, {0x118,0x119}, {0x11A,0x11B}, 
+  {0x11C,0x11D}, {0x11E,0x11F}, {0x120,0x121}, {0x122,0x123}, {0x124,0x125}, 
+  {0x126,0x127}, {0x128,0x129}, {0x12A,0x12B}, {0x12C,0x12D}, {0x12E,0x12F}, 
+  {0x130,0x69}, {0x132,0x133}, {0x134,0x135}, {0x136,0x137}, {0x139,0x13A}, 
+  {0x13B,0x13C}, {0x13D,0x13E}, {0x13F,0x140}, {0x141,0x142}, {0x143,0x144}, 
+  {0x145,0x146}, {0x147,0x148}, {0x14A,0x14B}, {0x14C,0x14D}, {0x14E,0x14F}, 
+  {0x150,0x151}, {0x152,0x153}, {0x154,0x155}, {0x156,0x157}, {0x158,0x159}, 
+  {0x15A,0x15B}, {0x15C,0x15D}, {0x15E,0x15F}, {0x160,0x161}, {0x162,0x163}, 
+  {0x164,0x165}, {0x166,0x167}, {0x168,0x169}, {0x16A,0x16B}, {0x16C,0x16D}, 
+  {0x16E,0x16F}, {0x170,0x171}, {0x172,0x173}, {0x174,0x175}, {0x176,0x177}, 
+  {0x178,0xFF}, {0x179,0x17A}, {0x17B,0x17C}, {0x17D,0x17E}, {0x181,0x253}, 
+  {0x182,0x183}, {0x184,0x185}, {0x186,0x254}, {0x187,0x188}, {0x189,0x256}, 
+  {0x18A,0x257}, {0x18B,0x18C}, {0x18E,0x1DD}, {0x18F,0x259}, {0x190,0x25B}, 
+  {0x191,0x192}, {0x193,0x260}, {0x194,0x263}, {0x196,0x269}, {0x197,0x268}, 
+  {0x198,0x199}, {0x19C,0x26F}, {0x19D,0x272}, {0x19F,0x275}, {0x1A0,0x1A1}, 
+  {0x1A2,0x1A3}, {0x1A4,0x1A5}, {0x1A7,0x1A8}, {0x1A9,0x283}, {0x1AC,0x1AD}, 
+  {0x1AE,0x288}, {0x1AF,0x1B0}, {0x1B1,0x28A}, {0x1B2,0x28B}, {0x1B3,0x1B4}, 
+  {0x1B5,0x1B6}, {0x1B7,0x292}, {0x1B8,0x1B9}, {0x1BC,0x1BD}, {0x1C4,0x1C6}, 
+  {0x1C5,0x1C6}, {0x1C7,0x1C9}, {0x1C8,0x1C9}, {0x1CA,0x1CC}, {0x1CB,0x1CC}, 
+  {0x1CD,0x1CE}, {0x1CF,0x1D0}, {0x1D1,0x1D2}, {0x1D3,0x1D4}, {0x1D5,0x1D6}, 
+  {0x1D7,0x1D8}, {0x1D9,0x1DA}, {0x1DB,0x1DC}, {0x1DE,0x1DF}, {0x1E0,0x1E1}, 
+  {0x1E2,0x1E3}, {0x1E4,0x1E5}, {0x1E6,0x1E7}, {0x1E8,0x1E9}, {0x1EA,0x1EB}, 
+  {0x1EC,0x1ED}, {0x1EE,0x1EF}, {0x1F1,0x1F3}, {0x1F2,0x1F3}, {0x1F4,0x1F5}, 
+  {0x1FA,0x1FB}, {0x1FC,0x1FD}, {0x1FE,0x1FF}, {0x200,0x201}, {0x202,0x203}, 
+  {0x204,0x205}, {0x206,0x207}, {0x208,0x209}, {0x20A,0x20B}, {0x20C,0x20D}, 
+  {0x20E,0x20F}, {0x210,0x211}, {0x212,0x213}, {0x214,0x215}, {0x216,0x217}, 
+  {0x386,0x3AC}, {0x388,0x3AD}, {0x389,0x3AE}, {0x38A,0x3AF}, {0x38C,0x3CC}, 
+  {0x38E,0x3CD}, {0x38F,0x3CE}, {0x391,0x3B1}, {0x392,0x3B2}, {0x393,0x3B3}, 
+  {0x394,0x3B4}, {0x395,0x3B5}, {0x396,0x3B6}, {0x397,0x3B7}, {0x398,0x3B8}, 
+  {0x399,0x3B9}, {0x39A,0x3BA}, {0x39B,0x3BB}, {0x39C,0x3BC}, {0x39D,0x3BD}, 
+  {0x39E,0x3BE}, {0x39F,0x3BF}, {0x3A0,0x3C0}, {0x3A1,0x3C1}, {0x3A3,0x3C3}, 
+  {0x3A4,0x3C4}, {0x3A5,0x3C5}, {0x3A6,0x3C6}, {0x3A7,0x3C7}, {0x3A8,0x3C8}, 
+  {0x3A9,0x3C9}, {0x3AA,0x3CA}, {0x3AB,0x3CB}, {0x3E2,0x3E3}, {0x3E4,0x3E5}, 
+  {0x3E6,0x3E7}, {0x3E8,0x3E9}, {0x3EA,0x3EB}, {0x3EC,0x3ED}, {0x3EE,0x3EF}, 
+  {0x401,0x451}, {0x402,0x452}, {0x403,0x453}, {0x404,0x454}, {0x405,0x455}, 
+  {0x406,0x456}, {0x407,0x457}, {0x408,0x458}, {0x409,0x459}, {0x40A,0x45A}, 
+  {0x40B,0x45B}, {0x40C,0x45C}, {0x40E,0x45E}, {0x40F,0x45F}, {0x410,0x430}, 
+  {0x411,0x431}, {0x412,0x432}, {0x413,0x433}, {0x414,0x434}, {0x415,0x435}, 
+  {0x416,0x436}, {0x417,0x437}, {0x418,0x438}, {0x419,0x439}, {0x41A,0x43A}, 
+  {0x41B,0x43B}, {0x41C,0x43C}, {0x41D,0x43D}, {0x41E,0x43E}, {0x41F,0x43F}, 
+  {0x420,0x440}, {0x421,0x441}, {0x422,0x442}, {0x423,0x443}, {0x424,0x444}, 
+  {0x425,0x445}, {0x426,0x446}, {0x427,0x447}, {0x428,0x448}, {0x429,0x449}, 
+  {0x42A,0x44A}, {0x42B,0x44B}, {0x42C,0x44C}, {0x42D,0x44D}, {0x42E,0x44E}, 
+  {0x42F,0x44F}, {0x460,0x461}, {0x462,0x463}, {0x464,0x465}, {0x466,0x467}, 
+  {0x468,0x469}, {0x46A,0x46B}, {0x46C,0x46D}, {0x46E,0x46F}, {0x470,0x471}, 
+  {0x472,0x473}, {0x474,0x475}, {0x476,0x477}, {0x478,0x479}, {0x47A,0x47B}, 
+  {0x47C,0x47D}, {0x47E,0x47F}, {0x480,0x481}, {0x490,0x491}, {0x492,0x493}, 
+  {0x494,0x495}, {0x496,0x497}, {0x498,0x499}, {0x49A,0x49B}, {0x49C,0x49D}, 
+  {0x49E,0x49F}, {0x4A0,0x4A1}, {0x4A2,0x4A3}, {0x4A4,0x4A5}, {0x4A6,0x4A7}, 
+  {0x4A8,0x4A9}, {0x4AA,0x4AB}, {0x4AC,0x4AD}, {0x4AE,0x4AF}, {0x4B0,0x4B1}, 
+  {0x4B2,0x4B3}, {0x4B4,0x4B5}, {0x4B6,0x4B7}, {0x4B8,0x4B9}, {0x4BA,0x4BB}, 
+  {0x4BC,0x4BD}, {0x4BE,0x4BF}, {0x4C1,0x4C2}, {0x4C3,0x4C4}, {0x4C7,0x4C8}, 
+  {0x4CB,0x4CC}, {0x4D0,0x4D1}, {0x4D2,0x4D3}, {0x4D4,0x4D5}, {0x4D6,0x4D7}, 
+  {0x4D8,0x4D9}, {0x4DA,0x4DB}, {0x4DC,0x4DD}, {0x4DE,0x4DF}, {0x4E0,0x4E1}, 
+  {0x4E2,0x4E3}, {0x4E4,0x4E5}, {0x4E6,0x4E7}, {0x4E8,0x4E9}, {0x4EA,0x4EB}, 
+  {0x4EE,0x4EF}, {0x4F0,0x4F1}, {0x4F2,0x4F3}, {0x4F4,0x4F5}, {0x4F8,0x4F9}, 
+  {0x531,0x561}, {0x532,0x562}, {0x533,0x563}, {0x534,0x564}, {0x535,0x565}, 
+  {0x536,0x566}, {0x537,0x567}, {0x538,0x568}, {0x539,0x569}, {0x53A,0x56A}, 
+  {0x53B,0x56B}, {0x53C,0x56C}, {0x53D,0x56D}, {0x53E,0x56E}, {0x53F,0x56F}, 
+  {0x540,0x570}, {0x541,0x571}, {0x542,0x572}, {0x543,0x573}, {0x544,0x574}, 
+  {0x545,0x575}, {0x546,0x576}, {0x547,0x577}, {0x548,0x578}, {0x549,0x579}, 
+  {0x54A,0x57A}, {0x54B,0x57B}, {0x54C,0x57C}, {0x54D,0x57D}, {0x54E,0x57E}, 
+  {0x54F,0x57F}, {0x550,0x580}, {0x551,0x581}, {0x552,0x582}, {0x553,0x583}, 
+  {0x554,0x584}, {0x555,0x585}, {0x556,0x586}, {0x10A0,0x10D0}, {0x10A1,0x10D1}, 
+  {0x10A2,0x10D2}, {0x10A3,0x10D3}, {0x10A4,0x10D4}, {0x10A5,0x10D5}, {0x10A6,0x10D6}, 
+  {0x10A7,0x10D7}, {0x10A8,0x10D8}, {0x10A9,0x10D9}, {0x10AA,0x10DA}, {0x10AB,0x10DB}, 
+  {0x10AC,0x10DC}, {0x10AD,0x10DD}, {0x10AE,0x10DE}, {0x10AF,0x10DF}, {0x10B0,0x10E0}, 
+  {0x10B1,0x10E1}, {0x10B2,0x10E2}, {0x10B3,0x10E3}, {0x10B4,0x10E4}, {0x10B5,0x10E5}, 
+  {0x10B6,0x10E6}, {0x10B7,0x10E7}, {0x10B8,0x10E8}, {0x10B9,0x10E9}, {0x10BA,0x10EA}, 
+  {0x10BB,0x10EB}, {0x10BC,0x10EC}, {0x10BD,0x10ED}, {0x10BE,0x10EE}, {0x10BF,0x10EF}, 
+  {0x10C0,0x10F0}, {0x10C1,0x10F1}, {0x10C2,0x10F2}, {0x10C3,0x10F3}, {0x10C4,0x10F4}, 
+  {0x10C5,0x10F5}, {0x1E00,0x1E01}, {0x1E02,0x1E03}, {0x1E04,0x1E05}, {0x1E06,0x1E07}, 
+  {0x1E08,0x1E09}, {0x1E0A,0x1E0B}, {0x1E0C,0x1E0D}, {0x1E0E,0x1E0F}, {0x1E10,0x1E11}, 
+  {0x1E12,0x1E13}, {0x1E14,0x1E15}, {0x1E16,0x1E17}, {0x1E18,0x1E19}, {0x1E1A,0x1E1B}, 
+  {0x1E1C,0x1E1D}, {0x1E1E,0x1E1F}, {0x1E20,0x1E21}, {0x1E22,0x1E23}, {0x1E24,0x1E25}, 
+  {0x1E26,0x1E27}, {0x1E28,0x1E29}, {0x1E2A,0x1E2B}, {0x1E2C,0x1E2D}, {0x1E2E,0x1E2F}, 
+  {0x1E30,0x1E31}, {0x1E32,0x1E33}, {0x1E34,0x1E35}, {0x1E36,0x1E37}, {0x1E38,0x1E39}, 
+  {0x1E3A,0x1E3B}, {0x1E3C,0x1E3D}, {0x1E3E,0x1E3F}, {0x1E40,0x1E41}, {0x1E42,0x1E43}, 
+  {0x1E44,0x1E45}, {0x1E46,0x1E47}, {0x1E48,0x1E49}, {0x1E4A,0x1E4B}, {0x1E4C,0x1E4D}, 
+  {0x1E4E,0x1E4F}, {0x1E50,0x1E51}, {0x1E52,0x1E53}, {0x1E54,0x1E55}, {0x1E56,0x1E57}, 
+  {0x1E58,0x1E59}, {0x1E5A,0x1E5B}, {0x1E5C,0x1E5D}, {0x1E5E,0x1E5F}, {0x1E60,0x1E61}, 
+  {0x1E62,0x1E63}, {0x1E64,0x1E65}, {0x1E66,0x1E67}, {0x1E68,0x1E69}, {0x1E6A,0x1E6B}, 
+  {0x1E6C,0x1E6D}, {0x1E6E,0x1E6F}, {0x1E70,0x1E71}, {0x1E72,0x1E73}, {0x1E74,0x1E75}, 
+  {0x1E76,0x1E77}, {0x1E78,0x1E79}, {0x1E7A,0x1E7B}, {0x1E7C,0x1E7D}, {0x1E7E,0x1E7F}, 
+  {0x1E80,0x1E81}, {0x1E82,0x1E83}, {0x1E84,0x1E85}, {0x1E86,0x1E87}, {0x1E88,0x1E89}, 
+  {0x1E8A,0x1E8B}, {0x1E8C,0x1E8D}, {0x1E8E,0x1E8F}, {0x1E90,0x1E91}, {0x1E92,0x1E93}, 
+  {0x1E94,0x1E95}, {0x1EA0,0x1EA1}, {0x1EA2,0x1EA3}, {0x1EA4,0x1EA5}, {0x1EA6,0x1EA7}, 
+  {0x1EA8,0x1EA9}, {0x1EAA,0x1EAB}, {0x1EAC,0x1EAD}, {0x1EAE,0x1EAF}, {0x1EB0,0x1EB1}, 
+  {0x1EB2,0x1EB3}, {0x1EB4,0x1EB5}, {0x1EB6,0x1EB7}, {0x1EB8,0x1EB9}, {0x1EBA,0x1EBB}, 
+  {0x1EBC,0x1EBD}, {0x1EBE,0x1EBF}, {0x1EC0,0x1EC1}, {0x1EC2,0x1EC3}, {0x1EC4,0x1EC5}, 
+  {0x1EC6,0x1EC7}, {0x1EC8,0x1EC9}, {0x1ECA,0x1ECB}, {0x1ECC,0x1ECD}, {0x1ECE,0x1ECF}, 
+  {0x1ED0,0x1ED1}, {0x1ED2,0x1ED3}, {0x1ED4,0x1ED5}, {0x1ED6,0x1ED7}, {0x1ED8,0x1ED9}, 
+  {0x1EDA,0x1EDB}, {0x1EDC,0x1EDD}, {0x1EDE,0x1EDF}, {0x1EE0,0x1EE1}, {0x1EE2,0x1EE3}, 
+  {0x1EE4,0x1EE5}, {0x1EE6,0x1EE7}, {0x1EE8,0x1EE9}, {0x1EEA,0x1EEB}, {0x1EEC,0x1EED}, 
+  {0x1EEE,0x1EEF}, {0x1EF0,0x1EF1}, {0x1EF2,0x1EF3}, {0x1EF4,0x1EF5}, {0x1EF6,0x1EF7}, 
+  {0x1EF8,0x1EF9}, {0x1F08,0x1F00}, {0x1F09,0x1F01}, {0x1F0A,0x1F02}, {0x1F0B,0x1F03}, 
+  {0x1F0C,0x1F04}, {0x1F0D,0x1F05}, {0x1F0E,0x1F06}, {0x1F0F,0x1F07}, {0x1F18,0x1F10}, 
+  {0x1F19,0x1F11}, {0x1F1A,0x1F12}, {0x1F1B,0x1F13}, {0x1F1C,0x1F14}, {0x1F1D,0x1F15}, 
+  {0x1F28,0x1F20}, {0x1F29,0x1F21}, {0x1F2A,0x1F22}, {0x1F2B,0x1F23}, {0x1F2C,0x1F24}, 
+  {0x1F2D,0x1F25}, {0x1F2E,0x1F26}, {0x1F2F,0x1F27}, {0x1F38,0x1F30}, {0x1F39,0x1F31}, 
+  {0x1F3A,0x1F32}, {0x1F3B,0x1F33}, {0x1F3C,0x1F34}, {0x1F3D,0x1F35}, {0x1F3E,0x1F36}, 
+  {0x1F3F,0x1F37}, {0x1F48,0x1F40}, {0x1F49,0x1F41}, {0x1F4A,0x1F42}, {0x1F4B,0x1F43}, 
+  {0x1F4C,0x1F44}, {0x1F4D,0x1F45}, {0x1F59,0x1F51}, {0x1F5B,0x1F53}, {0x1F5D,0x1F55}, 
+  {0x1F5F,0x1F57}, {0x1F68,0x1F60}, {0x1F69,0x1F61}, {0x1F6A,0x1F62}, {0x1F6B,0x1F63}, 
+  {0x1F6C,0x1F64}, {0x1F6D,0x1F65}, {0x1F6E,0x1F66}, {0x1F6F,0x1F67}, {0x1F88,0x1F80}, 
+  {0x1F89,0x1F81}, {0x1F8A,0x1F82}, {0x1F8B,0x1F83}, {0x1F8C,0x1F84}, {0x1F8D,0x1F85}, 
+  {0x1F8E,0x1F86}, {0x1F8F,0x1F87}, {0x1F98,0x1F90}, {0x1F99,0x1F91}, {0x1F9A,0x1F92}, 
+  {0x1F9B,0x1F93}, {0x1F9C,0x1F94}, {0x1F9D,0x1F95}, {0x1F9E,0x1F96}, {0x1F9F,0x1F97}, 
+  {0x1FA8,0x1FA0}, {0x1FA9,0x1FA1}, {0x1FAA,0x1FA2}, {0x1FAB,0x1FA3}, {0x1FAC,0x1FA4}, 
+  {0x1FAD,0x1FA5}, {0x1FAE,0x1FA6}, {0x1FAF,0x1FA7}, {0x1FB8,0x1FB0}, {0x1FB9,0x1FB1}, 
+  {0x1FBA,0x1F70}, {0x1FBB,0x1F71}, {0x1FBC,0x1FB3}, {0x1FC8,0x1F72}, {0x1FC9,0x1F73}, 
+  {0x1FCA,0x1F74}, {0x1FCB,0x1F75}, {0x1FCC,0x1FC3}, {0x1FD8,0x1FD0}, {0x1FD9,0x1FD1}, 
+  {0x1FDA,0x1F76}, {0x1FDB,0x1F77}, {0x1FE8,0x1FE0}, {0x1FE9,0x1FE1}, {0x1FEA,0x1F7A}, 
+  {0x1FEB,0x1F7B}, {0x1FEC,0x1FE5}, {0x1FF8,0x1F78}, {0x1FF9,0x1F79}, {0x1FFA,0x1F7C}, 
+  {0x1FFB,0x1F7D}, {0x1FFC,0x1FF3}, {0x2160,0x2170}, {0x2161,0x2171}, {0x2162,0x2172}, 
+  {0x2163,0x2173}, {0x2164,0x2174}, {0x2165,0x2175}, {0x2166,0x2176}, {0x2167,0x2177}, 
+  {0x2168,0x2178}, {0x2169,0x2179}, {0x216A,0x217A}, {0x216B,0x217B}, {0x216C,0x217C}, 
+  {0x216D,0x217D}, {0x216E,0x217E}, {0x216F,0x217F}, {0x24B6,0x24D0}, {0x24B7,0x24D1}, 
+  {0x24B8,0x24D2}, {0x24B9,0x24D3}, {0x24BA,0x24D4}, {0x24BB,0x24D5}, {0x24BC,0x24D6}, 
+  {0x24BD,0x24D7}, {0x24BE,0x24D8}, {0x24BF,0x24D9}, {0x24C0,0x24DA}, {0x24C1,0x24DB}, 
+  {0x24C2,0x24DC}, {0x24C3,0x24DD}, {0x24C4,0x24DE}, {0x24C5,0x24DF}, {0x24C6,0x24E0}, 
+  {0x24C7,0x24E1}, {0x24C8,0x24E2}, {0x24C9,0x24E3}, {0x24CA,0x24E4}, {0x24CB,0x24E5}, 
+  {0x24CC,0x24E6}, {0x24CD,0x24E7}, {0x24CE,0x24E8}, {0x24CF,0x24E9}, {0xFF21,0xFF41}, 
+  {0xFF22,0xFF42}, {0xFF23,0xFF43}, {0xFF24,0xFF44}, {0xFF25,0xFF45}, {0xFF26,0xFF46}, 
+  {0xFF27,0xFF47}, {0xFF28,0xFF48}, {0xFF29,0xFF49}, {0xFF2A,0xFF4A}, {0xFF2B,0xFF4B}, 
+  {0xFF2C,0xFF4C}, {0xFF2D,0xFF4D}, {0xFF2E,0xFF4E}, {0xFF2F,0xFF4F}, {0xFF30,0xFF50}, 
+  {0xFF31,0xFF51}, {0xFF32,0xFF52}, {0xFF33,0xFF53}, {0xFF34,0xFF54}, {0xFF35,0xFF55}, 
+  {0xFF36,0xFF56}, {0xFF37,0xFF57}, {0xFF38,0xFF58}, {0xFF39,0xFF59}, {0xFF3A,0xFF5A}
+};
+
+
+
+#define NUM_TOSIMPLIFIED_INFO 2145
+static const unirelate tosimplified_info[NUM_TOSIMPLIFIED_INFO] = {
+  {0x4E48,0x9EBD}, {0x4E82,0x4E71}, {0x4E9E,0x4E9A}, {0x4F47,0x4F2B}, {0x4F59,0x9980}, 
+  {0x4F86,0x6765}, {0x4F96,0x4ED1}, {0x4FE0,0x4FA0}, {0x5000,0x4F25}, {0x5006,0x4FE9}, 
+  {0x5009,0x4ED3}, {0x500B,0x4E2A}, {0x5011,0x4EEC}, {0x502B,0x4F26}, {0x5049,0x4F1F}, 
+  {0x5074,0x4FA7}, {0x5075,0x4FA6}, {0x5096,0x4F27}, {0x5098,0x4F1E}, {0x5099,0x5907}, 
+  {0x50AD,0x4F63}, {0x50B3,0x4F20}, {0x50B4,0x4F1B}, {0x50B5,0x503A}, {0x50B7,0x4F24}, 
+  {0x50BE,0x503E}, {0x50C2,0x507B}, {0x50C5,0x4EC5}, {0x50C9,0x4F65}, {0x50D1,0x4FA8}, 
+  {0x50D5,0x4EC6}, {0x50DE,0x4F2A}, {0x50E5,0x4FA5}, {0x50E8,0x507E}, {0x50F9,0x4EF7}, 
+  {0x5100,0x4EEA}, {0x5102,0x4FAC}, {0x5104,0x4EBF}, {0x5108,0x4FA9}, {0x5109,0x4FED}, 
+  {0x5110,0x50A7}, {0x5114,0x4FE6}, {0x5115,0x4FAA}, {0x511F,0x507F}, {0x512A,0x4F18}, 
+  {0x5132,0x50A8}, {0x5137,0x4FEA}, {0x513A,0x50A9}, {0x513B,0x50A5}, {0x513C,0x4FE8}, 
+  {0x5152,0x513F}, {0x5169,0x4E24}, {0x51C8,0x51C0}, {0x51CD,0x51BB}, {0x51F1,0x51EF}, 
+  {0x5244,0x522D}, {0x5247,0x5219}, {0x525B,0x521A}, {0x526E,0x5250}, {0x5274,0x5240}, 
+  {0x5275,0x521B}, {0x5283,0x5212}, {0x5287,0x5267}, {0x5289,0x5218}, {0x528A,0x523D}, 
+  {0x528C,0x523F}, {0x528D,0x5251}, {0x5291,0x5242}, {0x52C1,0x52B2}, {0x52D5,0x52A8}, 
+  {0x52D9,0x52A1}, {0x52DB,0x52CB}, {0x52DD,0x80DC}, {0x52DE,0x52B3}, {0x52E2,0x52BF}, 
+  {0x52F1,0x52A2}, {0x52F5,0x52B1}, {0x52F8,0x529D}, {0x532D,0x5326}, {0x532F,0x6C47}, 
+  {0x5331,0x532E}, {0x5340,0x533A}, {0x5354,0x534F}, {0x5399,0x538D}, {0x53A0,0x5395}, 
+  {0x53AD,0x538C}, {0x53B2,0x5389}, {0x53B4,0x53A3}, {0x53C3,0x53C2}, {0x53E2,0x4E1B}, 
+  {0x540E,0x5F8C}, {0x54BC,0x5459}, {0x54E1,0x5458}, {0x5504,0x5457}, {0x554F,0x95EE}, 
+  {0x5553,0x542F}, {0x555E,0x54D1}, {0x559A,0x5524}, {0x55AA,0x4E27}, {0x55AC,0x4E54}, 
+  {0x55AE,0x5355}, {0x55B2,0x54DF}, {0x55C6,0x545B}, {0x55C7,0x556C}, {0x55CE,0x5417}, 
+  {0x55DA,0x545C}, {0x55E9,0x5522}, {0x55F6,0x54D4}, {0x5606,0x53F9}, {0x560D,0x55BD}, 
+  {0x5614,0x5455}, {0x5616,0x5567}, {0x5617,0x5C1D}, {0x561C,0x551B}, {0x5629,0x54D7}, 
+  {0x562E,0x5520}, {0x562F,0x5578}, {0x5630,0x53FD}, {0x5635,0x54D3}, {0x5638,0x5452}, 
+  {0x565D,0x549D}, {0x5660,0x54D2}, {0x5665,0x54DD}, {0x5666,0x54D5}, {0x566F,0x55F3}, 
+  {0x5672,0x54D9}, {0x5674,0x55B7}, {0x5678,0x5428}, {0x5680,0x549B}, {0x5687,0x5413}, 
+  {0x568C,0x54DC}, {0x5695,0x565C}, {0x5699,0x556E}, {0x56A6,0x5456}, {0x56A8,0x5499}, 
+  {0x56B3,0x55BE}, {0x56B4,0x4E25}, {0x56B6,0x5624}, {0x56C0,0x556D}, {0x56C1,0x55EB}, 
+  {0x56C2,0x56A3}, {0x56C5,0x5181}, {0x56C8,0x5453}, {0x56D1,0x5631}, {0x5707,0x56F5}, 
+  {0x570B,0x56FD}, {0x570D,0x56F4}, {0x5712,0x56ED}, {0x5713,0x5706}, {0x5716,0x56FE}, 
+  {0x5718,0x56E2}, {0x57E1,0x57AD}, {0x57F7,0x6267}, {0x5805,0x575A}, {0x580A,0x57A9}, 
+  {0x5816,0x57B4}, {0x581D,0x57DA}, {0x582F,0x5C27}, {0x5831,0x62A5}, {0x5834,0x573A}, 
+  {0x584A,0x5757}, {0x584B,0x8314}, {0x584F,0x57B2}, {0x5852,0x57D8}, {0x5857,0x6D82}, 
+  {0x5862,0x575E}, {0x5864,0x57D9}, {0x5875,0x5C18}, {0x5879,0x5811}, {0x588A,0x57AB}, 
+  {0x589C,0x5760}, {0x58AE,0x5815}, {0x58B3,0x575F}, {0x58BB,0x5899}, {0x58BE,0x57A6}, 
+  {0x58C7,0x575B}, {0x58D3,0x538B}, {0x58D8,0x5792}, {0x58D9,0x5739}, {0x58DA,0x5786}, 
+  {0x58DE,0x574F}, {0x58DF,0x5784}, {0x58E0,0x5785}, {0x58E2,0x575C}, {0x58E9,0x575D}, 
+  {0x58EF,0x58EE}, {0x58FA,0x58F6}, {0x58FD,0x5BFF}, {0x5922,0x68A6}, {0x593E,0x5939}, 
+  {0x5950,0x5942}, {0x5969,0x5941}, {0x596A,0x593A}, {0x596C,0x5956}, {0x596E,0x594B}, 
+  {0x599D,0x5986}, {0x5A41,0x5A04}, {0x5A66,0x5987}, {0x5A6D,0x5A05}, {0x5AA7,0x5A32}, 
+  {0x5ABD,0x5988}, {0x5AD7,0x59AA}, {0x5AF5,0x59A9}, {0x5AFB,0x5A34}, {0x5B00,0x59AB}, 
+  {0x5B08,0x5A06}, {0x5B0B,0x5A75}, {0x5B0C,0x5A07}, {0x5B19,0x5AF1}, {0x5B21,0x5AD2}, 
+  {0x5B2A,0x5AD4}, {0x5B30,0x5A74}, {0x5B38,0x5A76}, {0x5B4C,0x5A08}, {0x5B6B,0x5B59}, 
+  {0x5B78,0x5B66}, {0x5B7F,0x5B6A}, {0x5BE2,0x5BDD}, {0x5BE6,0x5B9E}, {0x5BE7,0x5B81}, 
+  {0x5BE9,0x5BA1}, {0x5BEB,0x5199}, {0x5BEC,0x5BBD}, {0x5BF5,0x5BA0}, {0x5BF6,0x5B9D}, 
+  {0x5C07,0x5C06}, {0x5C08,0x4E13}, {0x5C0B,0x5BFB}, {0x5C0D,0x5BF9}, {0x5C0E,0x5BFC}, 
+  {0x5C37,0x5C34}, {0x5C62,0x5C61}, {0x5C64,0x5C42}, {0x5C68,0x5C66}, {0x5C6C,0x5C5E}, 
+  {0x5CA1,0x5188}, {0x5CF4,0x5C98}, {0x5CF6,0x5C9B}, {0x5CFD,0x5CE1}, {0x5D0D,0x5D03}, 
+  {0x5D17,0x5C97}, {0x5D22,0x5CE5}, {0x5D2C,0x5CBD}, {0x5D50,0x5C9A}, {0x5D81,0x5D5D}, 
+  {0x5D84,0x5D2D}, {0x5D87,0x5C96}, {0x5D97,0x5D02}, {0x5DA0,0x5CE4}, {0x5DA7,0x5CC4}, 
+  {0x5DB8,0x5D58}, {0x5DBA,0x5CAD}, {0x5DBC,0x5C7F}, {0x5DCB,0x5CBF}, {0x5DD2,0x5CE6}, 
+  {0x5DD4,0x5DC5}, {0x5DF0,0x5DEF}, {0x5E25,0x5E05}, {0x5E2B,0x5E08}, {0x5E33,0x5E10}, 
+  {0x5E36,0x5E26}, {0x5E40,0x5E27}, {0x5E43,0x5E0F}, {0x5E57,0x5E3C}, {0x5E58,0x5E3B}, 
+  {0x5E5F,0x5E1C}, {0x5E63,0x5E01}, {0x5E6B,0x5E2E}, {0x5E6C,0x5E31}, {0x5E79,0x5E72}, 
+  {0x5E7E,0x51E0}, {0x5EAB,0x5E93}, {0x5EDF,0x5E99}, {0x5EE0,0x5382}, {0x5EE1,0x5E91}, 
+  {0x5EE2,0x5E9F}, {0x5EE3,0x5E7F}, {0x5EEC,0x5E90}, {0x5EF3,0x5385}, {0x5F33,0x5F2A}, 
+  {0x5F35,0x5F20}, {0x5F48,0x5F39}, {0x5F4C,0x5F25}, {0x5F4E,0x5F2F}, {0x5F8C,0x540E}, 
+  {0x5F91,0x5F84}, {0x5F9E,0x4ECE}, {0x5FA0,0x5F95}, {0x5FA9,0x590D}, {0x5FB9,0x5F7B}, 
+  {0x60B5,0x6005}, {0x60B6,0x95F7}, {0x60E1,0x6076}, {0x60F1,0x607C}, {0x60F2,0x607D}, 
+  {0x60FB,0x607B}, {0x611B,0x7231}, {0x611C,0x60EC}, {0x6134,0x6006}, {0x6137,0x607A}, 
+  {0x613E,0x5FFE}, {0x614B,0x6001}, {0x6158,0x60E8}, {0x615A,0x60ED}, {0x615F,0x6078}, 
+  {0x6163,0x60EF}, {0x6164,0x60AB}, {0x616A,0x6004}, {0x616B,0x6002}, {0x616E,0x8651}, 
+  {0x6173,0x60AD}, {0x6176,0x5E86}, {0x6182,0x5FE7}, {0x618A,0x60EB}, {0x6190,0x601C}, 
+  {0x6191,0x51ED}, {0x6192,0x6126}, {0x619A,0x60EE}, {0x61A4,0x6124}, {0x61AB,0x60AF}, 
+  {0x61AE,0x6003}, {0x61B2,0x5BAA}, {0x61B6,0x5FC6}, {0x61C7,0x6073}, {0x61C9,0x5E94}, 
+  {0x61CC,0x603F}, {0x61DF,0x603C}, {0x61E3,0x61D1}, {0x61E8,0x6079}, {0x61F2,0x60E9}, 
+  {0x61F6,0x61D2}, {0x61F7,0x6000}, {0x61F8,0x60AC}, {0x61FA,0x5FCF}, {0x61FC,0x60E7}, 
+  {0x61FE,0x6151}, {0x6200,0x604B}, {0x6207,0x6206}, {0x6214,0x620B}, {0x6227,0x6217}, 
+  {0x6229,0x622C}, {0x6230,0x6218}, {0x6232,0x620F}, {0x633E,0x631F}, {0x6368,0x820D}, 
+  {0x636B,0x626A}, {0x6383,0x626B}, {0x6384,0x62A1}, {0x6399,0x6323}, {0x63C0,0x62E3}, 
+  {0x63DA,0x626C}, {0x63DB,0x6362}, {0x63EE,0x6325}, {0x640D,0x635F}, {0x6417,0x6363}, 
+  {0x6436,0x62A2}, {0x6451,0x63B4}, {0x645C,0x63BC}, {0x645F,0x6402}, {0x646F,0x631A}, 
+  {0x6473,0x62A0}, {0x6476,0x629F}, {0x647B,0x63BA}, {0x6488,0x635E}, {0x6493,0x6320}, 
+  {0x649F,0x6322}, {0x64A3,0x63B8}, {0x64A5,0x62E8}, {0x64AB,0x629A}, {0x64B2,0x6251}, 
+  {0x64B3,0x63FF}, {0x64BB,0x631E}, {0x64BE,0x631D}, {0x64BF,0x6361}, {0x64C1,0x62E5}, 
+  {0x64C4,0x63B3}, {0x64C7,0x62E9}, {0x64CA,0x51FB}, {0x64CB,0x6321}, {0x64D4,0x62C5}, 
+  {0x64DA,0x636E}, {0x64E0,0x6324}, {0x64EC,0x62DF}, {0x64EF,0x6448}, {0x64F0,0x62E7}, 
+  {0x64F1,0x6401}, {0x64F2,0x63B7}, {0x64F4,0x6269}, {0x64F7,0x64B7}, {0x64FA,0x6446}, 
+  {0x64FB,0x64DE}, {0x64FC,0x64B8}, {0x64FE,0x6270}, {0x6504,0x6445}, {0x6506,0x64B5}, 
+  {0x650F,0x62E2}, {0x6514,0x62E6}, {0x6516,0x6484}, {0x6519,0x6400}, {0x651B,0x64BA}, 
+  {0x651D,0x6444}, {0x6522,0x6512}, {0x6523,0x631B}, {0x6524,0x644A}, {0x652A,0x6405}, 
+  {0x652C,0x63FD}, {0x6557,0x8D25}, {0x6575,0x654C}, {0x6578,0x6570}, {0x6582,0x655B}, 
+  {0x6583,0x6BD9}, {0x6595,0x6593}, {0x65AC,0x65A9}, {0x65B7,0x65AD}, {0x6642,0x65F6}, 
+  {0x6649,0x664B}, {0x665D,0x663C}, {0x6688,0x6655}, {0x6689,0x6656}, {0x66A2,0x7545}, 
+  {0x66AB,0x6682}, {0x66C4,0x6654}, {0x66C7,0x6619}, {0x66C9,0x6653}, {0x66D6,0x66A7}, 
+  {0x66E0,0x65F7}, {0x66EC,0x6652}, {0x66F8,0x4E66}, {0x6703,0x4F1A}, {0x6727,0x80E7}, 
+  {0x6771,0x4E1C}, {0x6898,0x67A7}, {0x689D,0x6761}, {0x689F,0x67AD}, {0x68C4,0x5F03}, 
+  {0x68D6,0x67A8}, {0x68D7,0x67A3}, {0x68DF,0x680B}, {0x68E7,0x6808}, {0x68F2,0x6816}, 
+  {0x690F,0x6860}, {0x694A,0x6768}, {0x6953,0x67AB}, {0x6968,0x6862}, {0x696D,0x4E1A}, 
+  {0x6975,0x6781}, {0x69AA,0x6769}, {0x69AE,0x8363}, {0x69BF,0x6864}, {0x69CB,0x6784}, 
+  {0x69CD,0x67AA}, {0x69E7,0x6920}, {0x69F3,0x6868}, {0x6A01,0x6869}, {0x6A02,0x4E50}, 
+  {0x6A05,0x679E}, {0x6A13,0x697C}, {0x6A19,0x6807}, {0x6A1E,0x67A2}, {0x6A23,0x6837}, 
+  {0x6A38,0x6734}, {0x6A39,0x6811}, {0x6A3A,0x6866}, {0x6A48,0x6861}, {0x6A4B,0x6865}, 
+  {0x6A5F,0x673A}, {0x6A62,0x692D}, {0x6A89,0x67FD}, {0x6A94,0x6863}, {0x6A9C,0x6867}, 
+  {0x6AA2,0x68C0}, {0x6AA3,0x6A2F}, {0x6AB3,0x69DF}, {0x6AB8,0x67E0}, {0x6ABB,0x69DB}, 
+  {0x6AC3,0x67DC}, {0x6AD3,0x6A79}, {0x6ADA,0x6988}, {0x6ADB,0x6809}, {0x6ADD,0x691F}, 
+  {0x6ADE,0x6A7C}, {0x6ADF,0x680E}, {0x6AE7,0x69E0}, {0x6AE8,0x680C}, {0x6AEA,0x67A5}, 
+  {0x6AEC,0x6987}, {0x6AF3,0x680A}, {0x6AF8,0x6989}, {0x6AFB,0x6A31}, {0x6B04,0x680F}, 
+  {0x6B0A,0x6743}, {0x6B0F,0x6924}, {0x6B12,0x683E}, {0x6B16,0x6984}, {0x6B1E,0x68C2}, 
+  {0x6B3D,0x94A6}, {0x6B50,0x6B27}, {0x6B5F,0x6B24}, {0x6B61,0x6B22}, {0x6B72,0x5C81}, 
+  {0x6B77,0x5386}, {0x6B78,0x5F52}, {0x6B98,0x6B8B}, {0x6B9E,0x6B92}, {0x6BA4,0x6B87}, 
+  {0x6BAB,0x6B9A}, {0x6BAE,0x6B93}, {0x6BAF,0x6BA1}, {0x6BB2,0x6B7C}, {0x6BBA,0x6740}, 
+  {0x6BBB,0x58F3}, {0x6BC6,0x6BB4}, {0x6BFF,0x6BF5}, {0x6C08,0x6BE1}, {0x6C0C,0x6C07}, 
+  {0x6C23,0x6C14}, {0x6C2B,0x6C22}, {0x6C2C,0x6C29}, {0x6D79,0x6D43}, {0x6D87,0x6CFE}, 
+  {0x6DEA,0x6CA6}, {0x6DF5,0x6E0A}, {0x6DF6,0x6D9E}, {0x6DFA,0x6D45}, {0x6E19,0x6DA3}, 
+  {0x6E26,0x6DA1}, {0x6E2C,0x6D4B}, {0x6E3E,0x6D51}, {0x6E5E,0x6D48}, {0x6E6F,0x6C64}, 
+  {0x6E96,0x51C6}, {0x6E9D,0x6C9F}, {0x6EC4,0x6CA7}, {0x6EC5,0x706D}, {0x6ECC,0x6DA4}, 
+  {0x6ECE,0x8365}, {0x6EEC,0x6CAA}, {0x6EEF,0x6EDE}, {0x6EF2,0x6E17}, {0x6EF7,0x5364}, 
+  {0x6EF8,0x6D52}, {0x6EFF,0x6EE1}, {0x6F01,0x6E14}, {0x6F1A,0x6CA4}, {0x6F22,0x6C49}, 
+  {0x6F23,0x6D9F}, {0x6F2C,0x6E0D}, {0x6F32,0x6DA8}, {0x6F38,0x6E10}, {0x6F3F,0x6D46}, 
+  {0x6F41,0x988D}, {0x6F51,0x6CFC}, {0x6F54,0x6D01}, {0x6F59,0x6CA9}, {0x6F5B,0x6F5C}, 
+  {0x6F64,0x6DA6}, {0x6F6F,0x6D54}, {0x6F70,0x6E83}, {0x6F77,0x6ED7}, {0x6F7F,0x6DA0}, 
+  {0x6F80,0x6DA9}, {0x6F86,0x6D47}, {0x6F87,0x6D9D}, {0x6F97,0x6DA7}, {0x6FA0,0x6E11}, 
+  {0x6FA4,0x6CFD}, {0x6FA9,0x6CF6}, {0x6FAE,0x6D4D}, {0x6FB1,0x6DC0}, {0x6FC1,0x6D4A}, 
+  {0x6FC3,0x6D53}, {0x6FD5,0x6E7F}, {0x6FD8,0x6CDE}, {0x6FDF,0x6D4E}, {0x6FE4,0x6D9B}, 
+  {0x6FEB,0x6EE5}, {0x6FF0,0x6F4D}, {0x6FF1,0x6EE8}, {0x6FFA,0x6E85}, {0x6FFC,0x6CFA}, 
+  {0x6FFE,0x6EE4}, {0x7005,0x6EE2}, {0x7006,0x6E0E}, {0x7009,0x6CFB}, {0x700B,0x6E16}, 
+  {0x700F,0x6D4F}, {0x7015,0x6FD2}, {0x7018,0x6CF8}, {0x701D,0x6CA5}, {0x701F,0x6F47}, 
+  {0x7020,0x6F46}, {0x7027,0x6CF7}, {0x7028,0x6FD1}, {0x7032,0x6F4B}, {0x703E,0x6F9C}, 
+  {0x7043,0x6CA3}, {0x7044,0x6EE0}, {0x7051,0x6D12}, {0x7055,0x6F13}, {0x7058,0x6EE9}, 
+  {0x705D,0x704F}, {0x7063,0x6E7E}, {0x7064,0x6EE6}, {0x7067,0x6EDF}, {0x70CF,0x4E4C}, 
+  {0x70F4,0x70C3}, {0x7121,0x65E0}, {0x7149,0x70BC}, {0x7152,0x709C}, {0x7162,0x8315}, 
+  {0x7165,0x7115}, {0x7169,0x70E6}, {0x716C,0x7080}, {0x7192,0x8367}, {0x7197,0x709D}, 
+  {0x71B1,0x70ED}, {0x71BE,0x70BD}, {0x71C1,0x70E8}, {0x71C8,0x706F}, {0x71D2,0x70E7}, 
+  {0x71D9,0x70EB}, {0x71DC,0x7116}, {0x71DF,0x8425}, {0x71E6,0x707F}, {0x71ED,0x70DB}, 
+  {0x71F4,0x70E9}, {0x71FC,0x70EC}, {0x71FE,0x7118}, {0x720D,0x70C1}, {0x7210,0x7089}, 
+  {0x721B,0x70C2}, {0x722D,0x4E89}, {0x7232,0x4E3A}, {0x723A,0x7237}, {0x723E,0x5C14}, 
+  {0x7258,0x724D}, {0x727D,0x7275}, {0x7296,0x8366}, {0x72A2,0x728A}, {0x72A7,0x727A}, 
+  {0x72C0,0x72B6}, {0x72F9,0x72ED}, {0x72FD,0x72C8}, {0x7319,0x72F0}, {0x7336,0x72B9}, 
+  {0x733B,0x72F2}, {0x7341,0x72B8}, {0x7344,0x72F1}, {0x7345,0x72EE}, {0x7368,0x72EC}, 
+  {0x736A,0x72EF}, {0x736B,0x7303}, {0x7370,0x72DE}, {0x7372,0x83B7}, {0x7375,0x730E}, 
+  {0x7377,0x72B7}, {0x7378,0x517D}, {0x737A,0x736D}, {0x737B,0x732E}, {0x737C,0x7315}, 
+  {0x7380,0x7321}, {0x73FE,0x73B0}, {0x743F,0x73F2}, {0x744B,0x73AE}, {0x7463,0x7410}, 
+  {0x7469,0x83B9}, {0x746A,0x739B}, {0x7489,0x740F}, {0x74A3,0x7391}, {0x74A6,0x7477}, 
+  {0x74B0,0x73AF}, {0x74BD,0x73BA}, {0x74CA,0x743C}, {0x74CF,0x73D1}, {0x74D4,0x748E}, 
+  {0x74DA,0x74D2}, {0x750C,0x74EF}, {0x7523,0x4EA7}, {0x755D,0x4EA9}, {0x7562,0x6BD5}, 
+  {0x756B,0x753B}, {0x7576,0x5F53}, {0x7587,0x7574}, {0x758A,0x53E0}, {0x75D9,0x75C9}, 
+  {0x7602,0x75D6}, {0x760B,0x75AF}, {0x760D,0x75A1}, {0x7613,0x75EA}, {0x761E,0x7617}, 
+  {0x7621,0x75AE}, {0x7627,0x759F}, {0x763B,0x7618}, {0x7642,0x7597}, {0x7646,0x75E8}, 
+  {0x7647,0x75EB}, {0x7649,0x7605}, {0x7658,0x75A0}, {0x765F,0x762A}, {0x7662,0x75D2}, 
+  {0x7664,0x7596}, {0x7665,0x75C7}, {0x7667,0x75AC}, {0x7669,0x765E}, {0x766C,0x7663}, 
+  {0x766D,0x763F}, {0x766E,0x763E}, {0x7670,0x75C8}, {0x7671,0x762B}, {0x7672,0x766B}, 
+  {0x767C,0x53D1}, {0x769A,0x7691}, {0x76B8,0x76B2}, {0x76BA,0x76B1}, {0x76DE,0x76CF}, 
+  {0x76E1,0x5C3D}, {0x76E3,0x76D1}, {0x76E4,0x76D8}, {0x76E7,0x5362}, {0x775C,0x7741}, 
+  {0x775E,0x7750}, {0x7798,0x770D}, {0x779E,0x7792}, {0x77BC,0x7751}, {0x77DA,0x77A9}, 
+  {0x77EF,0x77EB}, {0x7864,0x7856}, {0x7868,0x7817}, {0x786F,0x781A}, {0x78A9,0x7855}, 
+  {0x78AD,0x7800}, {0x78B8,0x781C}, {0x78BA,0x786E}, {0x78BC,0x7801}, {0x78DA,0x7816}, 
+  {0x78E3,0x789C}, {0x78E7,0x789B}, {0x78EF,0x77F6}, {0x78FD,0x7857}, {0x790E,0x7840}, 
+  {0x7919,0x788D}, {0x7926,0x77FF}, {0x792A,0x783A}, {0x792B,0x783E}, {0x792C,0x77FE}, 
+  {0x7931,0x783B}, {0x798D,0x7978}, {0x798E,0x796F}, {0x79AA,0x7985}, {0x79AE,0x793C}, 
+  {0x79B0,0x7962}, {0x79B1,0x7977}, {0x7A2E,0x79CD}, {0x7A31,0x79F0}, {0x7A4C,0x7A23}, 
+  {0x7A4D,0x79EF}, {0x7A4E,0x9896}, {0x7A61,0x7A51}, {0x7A62,0x79FD}, {0x7A69,0x7A33}, 
+  {0x7AA9,0x7A9D}, {0x7AAA,0x6D3C}, {0x7AAE,0x7A77}, {0x7AB6,0x7AAD}, {0x7ABA,0x7AA5}, 
+  {0x7AC4,0x7A9C}, {0x7AC5,0x7A8D}, {0x7AC7,0x7AA6}, {0x7AC8,0x7076}, {0x7ACA,0x7A83}, 
+  {0x7AEA,0x7AD6}, {0x7AF6,0x7ADE}, {0x7B46,0x7B14}, {0x7B67,0x7B15}, {0x7B8B,0x7B3A}, 
+  {0x7B8F,0x7B5D}, {0x7BC0,0x8282}, {0x7BC4,0x8303}, {0x7BC9,0x7B51}, {0x7BCB,0x7BA7}, 
+  {0x7BE4,0x7B03}, {0x7BE9,0x7B5B}, {0x7BF3,0x7B5A}, {0x7C00,0x7BA6}, {0x7C0D,0x7BD3}, 
+  {0x7C1E,0x7BAA}, {0x7C21,0x7B80}, {0x7C23,0x7BD1}, {0x7C2B,0x7BAB}, {0x7C3D,0x7B7E}, 
+  {0x7C3E,0x5E18}, {0x7C43,0x7BEE}, {0x7C4C,0x7B79}, {0x7C5C,0x7BA8}, {0x7C5F,0x7C41}, 
+  {0x7C60,0x7B3C}, {0x7C69,0x7B3E}, {0x7C6A,0x7C16}, {0x7C6C,0x7BF1}, {0x7C6E,0x7BA9}, 
+  {0x7CDD,0x7CC1}, {0x7CDE,0x7CAA}, {0x7CE7,0x7CAE}, {0x7CF2,0x7C9D}, {0x7CF4,0x7C74}, 
+  {0x7CF6,0x7C9C}, {0x7CF9,0x7E9F}, {0x7CFE,0x7EA0}, {0x7D00,0x7EAA}, {0x7D02,0x7EA3}, 
+  {0x7D04,0x7EA6}, {0x7D05,0x7EA2}, {0x7D06,0x7EA1}, {0x7D07,0x7EA5}, {0x7D08,0x7EA8}, 
+  {0x7D09,0x7EAB}, {0x7D0B,0x7EB9}, {0x7D0D,0x7EB3}, {0x7D10,0x7EBD}, {0x7D13,0x7EBE}, 
+  {0x7D14,0x7EAF}, {0x7D15,0x7EB0}, {0x7D17,0x7EB1}, {0x7D19,0x7EB8}, {0x7D1A,0x7EA7}, 
+  {0x7D1B,0x7EB7}, {0x7D1C,0x7EAD}, {0x7D21,0x7EBA}, {0x7D30,0x7EC6}, {0x7D31,0x7EC2}, 
+  {0x7D32,0x7EC1}, {0x7D33,0x7EC5}, {0x7D39,0x7ECD}, {0x7D3A,0x7EC0}, {0x7D3C,0x7ECB}, 
+  {0x7D3F,0x7ED0}, {0x7D40,0x7ECC}, {0x7D42,0x7EC8}, {0x7D44,0x7EC4}, {0x7D46,0x7ECA}, 
+  {0x7D4E,0x7ED7}, {0x7D50,0x7ED3}, {0x7D5D,0x7ED4}, {0x7D5E,0x7EDE}, {0x7D61,0x7EDC}, 
+  {0x7D62,0x7EDA}, {0x7D66,0x7ED9}, {0x7D68,0x7ED2}, {0x7D71,0x7EDF}, {0x7D72,0x4E1D}, 
+  {0x7D73,0x7EDB}, {0x7D76,0x7EDD}, {0x7D79,0x7EE2}, {0x7D81,0x7ED1}, {0x7D83,0x7EE1}, 
+  {0x7D86,0x7EE0}, {0x7D88,0x7EE8}, {0x7D89,0x7EE3}, {0x7D8F,0x7EE5}, {0x7D93,0x7ECF}, 
+  {0x7D9C,0x7EFC}, {0x7D9E,0x7F0D}, {0x7DA2,0x7EF8}, {0x7DA3,0x7EFB}, {0x7DAB,0x7EBF}, 
+  {0x7DAC,0x7EF6}, {0x7DAD,0x7EF4}, {0x7DB0,0x7EFE}, {0x7DB1,0x7EB2}, {0x7DB2,0x7F51}, 
+  {0x7DB3,0x7EF7}, {0x7DB4,0x7F00}, {0x7DB8,0x7EB6}, {0x7DB9,0x7EFA}, {0x7DBA,0x7EEE}, 
+  {0x7DBB,0x7EFD}, {0x7DBD,0x7EF0}, {0x7DBE,0x7EEB}, {0x7DBF,0x7EF5}, {0x7DC4,0x7EF2}, 
+  {0x7DC7,0x7F01}, {0x7DCA,0x7D27}, {0x7DCB,0x7EEF}, {0x7DD1,0x7EFF}, {0x7DD2,0x7EEA}, 
+  {0x7DD4,0x7EF1}, {0x7DD7,0x7F03}, {0x7DD8,0x7F04}, {0x7DD9,0x7F02}, {0x7DDD,0x7F09}, 
+  {0x7DDE,0x7F0E}, {0x7DE0,0x7F14}, {0x7DE1,0x7F17}, {0x7DE3,0x7F18}, {0x7DE6,0x7F0C}, 
+  {0x7DE8,0x7F16}, {0x7DE9,0x7F13}, {0x7DEC,0x7F05}, {0x7DEF,0x7EAC}, {0x7DF1,0x7F11}, 
+  {0x7DF2,0x7F08}, {0x7DF4,0x7EC3}, {0x7DF6,0x7F0F}, {0x7DF9,0x7F07}, {0x7E08,0x8426}, 
+  {0x7E09,0x7F19}, {0x7E0A,0x7F22}, {0x7E0B,0x7F12}, {0x7E10,0x7EC9}, {0x7E11,0x7F23}, 
+  {0x7E1B,0x7F1A}, {0x7E1D,0x7F1C}, {0x7E1E,0x7F1F}, {0x7E1F,0x7F1B}, {0x7E23,0x53BF}, 
+  {0x7E27,0x7EE6}, {0x7E2B,0x7F1D}, {0x7E2D,0x7F21}, {0x7E2E,0x7F29}, {0x7E31,0x7EB5}, 
+  {0x7E32,0x7F27}, {0x7E35,0x7F26}, {0x7E36,0x7D77}, {0x7E37,0x7F15}, {0x7E39,0x7F25}, 
+  {0x7E3D,0x603B}, {0x7E3E,0x7EE9}, {0x7E45,0x7F2B}, {0x7E46,0x7F2A}, {0x7E52,0x7F2F}, 
+  {0x7E54,0x7EC7}, {0x7E55,0x7F2E}, {0x7E5A,0x7F2D}, {0x7E5E,0x7ED5}, {0x7E62,0x7F0B}, 
+  {0x7E69,0x7EF3}, {0x7E6A,0x7ED8}, {0x7E6D,0x8327}, {0x7E6E,0x7F30}, {0x7E6F,0x7F33}, 
+  {0x7E70,0x7F32}, {0x7E73,0x7F34}, {0x7E79,0x7ECE}, {0x7E7C,0x7EE7}, {0x7E7D,0x7F24}, 
+  {0x7E7E,0x7F31}, {0x7E88,0x7F2C}, {0x7E8A,0x7EA9}, {0x7E8C,0x7EED}, {0x7E8F,0x7F20}, 
+  {0x7E93,0x7F28}, {0x7E96,0x7EA4}, {0x7E98,0x7F35}, {0x7E9C,0x7F06}, {0x7F4C,0x7F42}, 
+  {0x7F70,0x7F5A}, {0x7F77,0x7F62}, {0x7F85,0x7F57}, {0x7F86,0x7F74}, {0x7F88,0x7F81}, 
+  {0x7FA5,0x7F9F}, {0x7FA9,0x4E49}, {0x7FD2,0x4E60}, {0x7FF9,0x7FD8}, {0x802C,0x8027}, 
+  {0x802E,0x8022}, {0x8056,0x5723}, {0x805E,0x95FB}, {0x806F,0x8054}, {0x8070,0x806A}, 
+  {0x8072,0x58F0}, {0x8073,0x8038}, {0x8075,0x8069}, {0x8076,0x8042}, {0x8077,0x804C}, 
+  {0x8079,0x804D}, {0x807D,0x542C}, {0x807E,0x804B}, {0x8085,0x8083}, {0x8105,0x80C1}, 
+  {0x811B,0x80EB}, {0x8139,0x80C0}, {0x814E,0x80BE}, {0x8156,0x80E8}, {0x8161,0x8136}, 
+  {0x8166,0x8111}, {0x816B,0x80BF}, {0x8178,0x80A0}, {0x819A,0x80A4}, {0x81A0,0x80F6}, 
+  {0x81A9,0x817B}, {0x81BD,0x80C6}, {0x81BE,0x810D}, {0x81BF,0x8113}, {0x81C9,0x8138}, 
+  {0x81CD,0x8110}, {0x81CF,0x8191}, {0x81D8,0x814A}, {0x81DA,0x80EA}, {0x81DF,0x810F}, 
+  {0x81E0,0x8114}, {0x81E8,0x4E34}, {0x81FA,0x53F0}, {0x8207,0x4E0E}, {0x8208,0x5174}, 
+  {0x8209,0x4E3E}, {0x820A,0x65E7}, {0x8259,0x8231}, {0x8264,0x8223}, {0x8266,0x8230}, 
+  {0x826B,0x823B}, {0x8271,0x8270}, {0x8277,0x8273}, {0x82BB,0x520D}, {0x82E7,0x82CE}, 
+  {0x838A,0x5E84}, {0x8396,0x830E}, {0x83A2,0x835A}, {0x83A7,0x82CB}, {0x83EF,0x534E}, 
+  {0x8407,0x82CC}, {0x840A,0x83B1}, {0x842C,0x4E07}, {0x8435,0x83B4}, {0x8449,0x53F6}, 
+  {0x8452,0x836D}, {0x8464,0x836E}, {0x8466,0x82C7}, {0x8477,0x8364}, {0x8493,0x83BC}, 
+  {0x8494,0x83B3}, {0x84BC,0x82CD}, {0x84C0,0x836A}, {0x84CB,0x76D6}, {0x84EE,0x83B2}, 
+  {0x84EF,0x82C1}, {0x84FD,0x835C}, {0x851E,0x848C}, {0x8523,0x848B}, {0x8526,0x8311}, 
+  {0x852D,0x836B}, {0x8541,0x8368}, {0x8546,0x8487}, {0x854E,0x835E}, {0x8552,0x836C}, 
+  {0x8553,0x82B8}, {0x8555,0x83B8}, {0x8558,0x835B}, {0x8562,0x8489}, {0x8569,0x8361}, 
+  {0x856A,0x829C}, {0x856D,0x8427}, {0x8577,0x84E3}, {0x8588,0x835F}, {0x858A,0x84DF}, 
+  {0x858C,0x8297}, {0x8594,0x8537}, {0x859F,0x83B6}, {0x85A6,0x8350}, {0x85A9,0x8428}, 
+  {0x85BA,0x8360}, {0x85CD,0x84DD}, {0x85CE,0x8369}, {0x85DD,0x827A}, {0x85E5,0x836F}, 
+  {0x85EA,0x85AE}, {0x85F4,0x8574}, {0x85F6,0x82C8}, {0x85F9,0x853C}, {0x85FA,0x853A}, 
+  {0x8604,0x8572}, {0x8606,0x82A6}, {0x8607,0x82CF}, {0x860B,0x82F9}, {0x861A,0x85D3}, 
+  {0x861E,0x8539}, {0x8622,0x830F}, {0x862D,0x5170}, {0x863A,0x84E0}, {0x863F,0x841D}, 
+  {0x8655,0x5904}, {0x865C,0x864F}, {0x865F,0x53F7}, {0x8667,0x4E8F}, {0x86FA,0x86F1}, 
+  {0x8706,0x86AC}, {0x8755,0x8680}, {0x8766,0x867E}, {0x8778,0x8717}, {0x8784,0x86F3}, 
+  {0x879E,0x8682}, {0x87A2,0x8424}, {0x87BB,0x877C}, {0x87C4,0x86F0}, {0x87C8,0x8748}, 
+  {0x87CE,0x87A8}, {0x87E3,0x866E}, {0x87EC,0x8749}, {0x87EF,0x86F2}, {0x87F2,0x866B}, 
+  {0x87F6,0x86CF}, {0x87FB,0x8681}, {0x8805,0x8747}, {0x8806,0x867F}, {0x8810,0x86F4}, 
+  {0x8811,0x877E}, {0x881F,0x8721}, {0x8823,0x86CE}, {0x8831,0x86CA}, {0x8836,0x8695}, 
+  {0x883B,0x86EE}, {0x8846,0x4F17}, {0x8853,0x672F}, {0x885B,0x536B}, {0x885D,0x51B2}, 
+  {0x8879,0x53EA}, {0x88CA,0x8885}, {0x88CF,0x91CC}, {0x88DC,0x8865}, {0x88DD,0x88C5}, 
+  {0x8932,0x88E4}, {0x8933,0x88E2}, {0x8938,0x891B}, {0x893B,0x4EB5}, {0x8947,0x88E5}, 
+  {0x8956,0x8884}, {0x895D,0x88E3}, {0x8960,0x88C6}, {0x8964,0x8934}, {0x896A,0x889C}, 
+  {0x896F,0x886C}, {0x8972,0x88AD}, {0x898B,0x89C1}, {0x898F,0x89C4}, {0x8993,0x89C5}, 
+  {0x8996,0x89C6}, {0x8998,0x89C7}, {0x89A1,0x89CB}, {0x89A6,0x89CE}, {0x89AA,0x4EB2}, 
+  {0x89AC,0x89CA}, {0x89AF,0x89CF}, {0x89B2,0x89D0}, {0x89B7,0x89D1}, {0x89BA,0x89C9}, 
+  {0x89BD,0x89C8}, {0x89BF,0x89CC}, {0x89C0,0x89C2}, {0x89F4,0x89DE}, {0x89F6,0x89EF}, 
+  {0x89F8,0x89E6}, {0x8A01,0x8BA0}, {0x8A02,0x8BA2}, {0x8A03,0x8BA3}, {0x8A08,0x8BA1}, 
+  {0x8A0A,0x8BAF}, {0x8A0C,0x8BA7}, {0x8A0E,0x8BA8}, {0x8A10,0x8BA6}, {0x8A13,0x8BAD}, 
+  {0x8A15,0x8BAA}, {0x8A16,0x8BAB}, {0x8A18,0x8BB0}, {0x8A1B,0x8BB9}, {0x8A1D,0x8BB6}, 
+  {0x8A1F,0x8BBC}, {0x8A23,0x8BC0}, {0x8A25,0x8BB7}, {0x8A2A,0x8BBF}, {0x8A2D,0x8BBE}, 
+  {0x8A31,0x8BB8}, {0x8A34,0x8BC9}, {0x8A36,0x8BC3}, {0x8A3A,0x8BCA}, {0x8A41,0x8BC2}, 
+  {0x8A46,0x8BCB}, {0x8A4E,0x8BB5}, {0x8A50,0x8BC8}, {0x8A52,0x8BD2}, {0x8A54,0x8BCF}, 
+  {0x8A55,0x8BC4}, {0x8A58,0x8BCE}, {0x8A5B,0x8BC5}, {0x8A5E,0x8BCD}, {0x8A61,0x8BE9}, 
+  {0x8A62,0x8BE2}, {0x8A63,0x8BE3}, {0x8A66,0x8BD5}, {0x8A69,0x8BD7}, {0x8A6B,0x8BE7}, 
+  {0x8A6C,0x8BDF}, {0x8A6D,0x8BE1}, {0x8A6E,0x8BE0}, {0x8A70,0x8BD8}, {0x8A71,0x8BDD}, 
+  {0x8A72,0x8BE5}, {0x8A73,0x8BE6}, {0x8A75,0x8BDC}, {0x8A7C,0x8BD9}, {0x8A7F,0x8BD6}, 
+  {0x8A84,0x8BD4}, {0x8A85,0x8BDB}, {0x8A86,0x8BD3}, {0x8A87,0x5938}, {0x8A8D,0x8BA4}, 
+  {0x8A91,0x8BF3}, {0x8A92,0x8BF6}, {0x8A95,0x8BDE}, {0x8A98,0x8BF1}, {0x8A9A,0x8BEE}, 
+  {0x8A9E,0x8BED}, {0x8AA0,0x8BDA}, {0x8AA1,0x8BEB}, {0x8AA3,0x8BEC}, {0x8AA4,0x8BEF}, 
+  {0x8AA5,0x8BF0}, {0x8AA6,0x8BF5}, {0x8AA8,0x8BF2}, {0x8AAC,0x8BF4}, {0x8AB0,0x8C01}, 
+  {0x8AB2,0x8BFE}, {0x8AB6,0x8C07}, {0x8AB9,0x8BFD}, {0x8ABC,0x8C0A}, {0x8ABF,0x8C03}, 
+  {0x8AC2,0x8C04}, {0x8AC4,0x8C06}, {0x8AC7,0x8C08}, {0x8AC9,0x8BFF}, {0x8ACB,0x8BF7}, 
+  {0x8ACD,0x8BE4}, {0x8ACF,0x8BF9}, {0x8AD1,0x8BFC}, {0x8AD2,0x8C05}, {0x8AD6,0x8BBA}, 
+  {0x8AD7,0x8C02}, {0x8ADB,0x8C00}, {0x8ADC,0x8C0D}, {0x8ADE,0x8C1D}, {0x8AE2,0x8BE8}, 
+  {0x8AE4,0x8C14}, {0x8AE6,0x8C1B}, {0x8AE7,0x8C10}, {0x8AEB,0x8C0F}, {0x8AED,0x8C15}, 
+  {0x8AEE,0x8C18}, {0x8AF1,0x8BB3}, {0x8AF3,0x8C19}, {0x8AF6,0x8C0C}, {0x8AF7,0x8BBD}, 
+  {0x8AF8,0x8BF8}, {0x8AFA,0x8C1A}, {0x8AFC,0x8C16}, {0x8AFE,0x8BFA}, {0x8B00,0x8C0B}, 
+  {0x8B01,0x8C12}, {0x8B02,0x8C13}, {0x8B04,0x8A8A}, {0x8B05,0x8BCC}, {0x8B0A,0x8C0E}, 
+  {0x8B0E,0x8C1C}, {0x8B10,0x8C27}, {0x8B14,0x8C11}, {0x8B16,0x8C21}, {0x8B17,0x8C24}, 
+  {0x8B19,0x8C26}, {0x8B1A,0x8C25}, {0x8B1B,0x8BB2}, {0x8B1D,0x8C22}, {0x8B21,0x8C23}, 
+  {0x8B28,0x8C1F}, {0x8B2B,0x8C2A}, {0x8B2C,0x8C2C}, {0x8B2D,0x8C2B}, {0x8B33,0x8BB4}, 
+  {0x8B39,0x8C28}, {0x8B3E,0x8C29}, {0x8B49,0x8BC1}, {0x8B4E,0x8C32}, {0x8B4F,0x8BA5}, 
+  {0x8B56,0x8C2E}, {0x8B58,0x8BC6}, {0x8B59,0x8C2F}, {0x8B5A,0x8C2D}, {0x8B5C,0x8C31}, 
+  {0x8B6B,0x8C35}, {0x8B6F,0x8BD1}, {0x8B70,0x8BAE}, {0x8B74,0x8C34}, {0x8B77,0x62A4}, 
+  {0x8B7D,0x8A89}, {0x8B80,0x8BFB}, {0x8B8A,0x53D8}, {0x8B8E,0x96E0}, {0x8B92,0x8C17}, 
+  {0x8B93,0x8BA9}, {0x8B95,0x8C30}, {0x8B96,0x8C36}, {0x8B9C,0x8C20}, {0x8B9E,0x8C33}, 
+  {0x8C48,0x5C82}, {0x8C50,0x4E30}, {0x8C9D,0x8D1D}, {0x8C9E,0x8D1E}, {0x8CA0,0x8D1F}, 
+  {0x8CA1,0x8D22}, {0x8CA2,0x8D21}, {0x8CA7,0x8D2B}, {0x8CA8,0x8D27}, {0x8CA9,0x8D29}, 
+  {0x8CAA,0x8D2A}, {0x8CAB,0x8D2F}, {0x8CAC,0x8D23}, {0x8CAF,0x8D2E}, {0x8CB0,0x8D33}, 
+  {0x8CB2,0x8D40}, {0x8CB3,0x8D30}, {0x8CB4,0x8D35}, {0x8CB6,0x8D2C}, {0x8CB7,0x4E70}, 
+  {0x8CB8,0x8D37}, {0x8CBA,0x8D36}, {0x8CBB,0x8D39}, {0x8CBC,0x8D34}, {0x8CBD,0x8D3B}, 
+  {0x8CBF,0x8D38}, {0x8CC0,0x8D3A}, {0x8CC1,0x8D32}, {0x8CC2,0x8D42}, {0x8CC3,0x8D41}, 
+  {0x8CC4,0x8D3F}, {0x8CC5,0x8D45}, {0x8CC7,0x8D44}, {0x8CC8,0x8D3E}, {0x8CCA,0x8D3C}, 
+  {0x8CD1,0x8D48}, {0x8CD2,0x8D4A}, {0x8CD3,0x5BBE}, {0x8CD5,0x8D47}, {0x8CDA,0x8D49}, 
+  {0x8CDC,0x8D50}, {0x8CDE,0x8D4F}, {0x8CE0,0x8D54}, {0x8CE1,0x8D53}, {0x8CE2,0x8D24}, 
+  {0x8CE3,0x5356}, {0x8CE4,0x8D31}, {0x8CE6,0x8D4B}, {0x8CE7,0x8D55}, {0x8CEA,0x8D28}, 
+  {0x8CEB,0x8D4D}, {0x8CEC,0x8D26}, {0x8CED,0x8D4C}, {0x8CF4,0x8D56}, {0x8CFA,0x8D5A}, 
+  {0x8CFB,0x8D59}, {0x8CFC,0x8D2D}, {0x8CFD,0x8D5B}, {0x8CFE,0x8D5C}, {0x8D04,0x8D3D}, 
+  {0x8D05,0x8D58}, {0x8D08,0x8D60}, {0x8D0A,0x8D5E}, {0x8D0B,0x8D5D}, {0x8D0D,0x8D61}, 
+  {0x8D0F,0x8D62}, {0x8D10,0x8D46}, {0x8D16,0x8D4E}, {0x8D1B,0x8D63}, {0x8D1C,0x8D43}, 
+  {0x8D95,0x8D76}, {0x8D99,0x8D75}, {0x8DA8,0x8D8B}, {0x8DB2,0x8DB1}, {0x8E10,0x8DF5}, 
+  {0x8E34,0x8E0A}, {0x8E4C,0x8DC4}, {0x8E55,0x8DF8}, {0x8E63,0x8E52}, {0x8E7A,0x8DF7}, 
+  {0x8E89,0x8DB8}, {0x8E8A,0x8E0C}, {0x8E8B,0x8DFB}, {0x8E8D,0x8DC3}, {0x8E91,0x8E2F}, 
+  {0x8E92,0x8DDE}, {0x8E93,0x8E2C}, {0x8E9A,0x8DF9}, {0x8EA1,0x8E51}, {0x8EA5,0x8E7F}, 
+  {0x8EA6,0x8E9C}, {0x8EAA,0x8E8F}, {0x8EC0,0x8EAF}, {0x8ECA,0x8F66}, {0x8ECB,0x8F67}, 
+  {0x8ECC,0x8F68}, {0x8ECD,0x519B}, {0x8ED2,0x8F69}, {0x8ED4,0x8F6B}, {0x8EDB,0x8F6D}, 
+  {0x8EDF,0x8F6F}, {0x8EE4,0x8F77}, {0x8EEB,0x8F78}, {0x8EF2,0x8F71}, {0x8EF8,0x8F74}, 
+  {0x8EF9,0x8F75}, {0x8EFA,0x8F7A}, {0x8EFB,0x8F72}, {0x8EFC,0x8F76}, {0x8EFE,0x8F7C}, 
+  {0x8F03,0x8F83}, {0x8F05,0x8F82}, {0x8F07,0x8F81}, {0x8F09,0x8F7D}, {0x8F0A,0x8F7E}, 
+  {0x8F12,0x8F84}, {0x8F14,0x8F85}, {0x8F15,0x8F7B}, {0x8F1B,0x8F86}, {0x8F1C,0x8F8E}, 
+  {0x8F1D,0x8F89}, {0x8F1E,0x8F8B}, {0x8F1F,0x8F8D}, {0x8F25,0x8F8A}, {0x8F26,0x8F87}, 
+  {0x8F29,0x8F88}, {0x8F2A,0x8F6E}, {0x8F2F,0x8F91}, {0x8F33,0x8F8F}, {0x8F38,0x8F93}, 
+  {0x8F3B,0x8F90}, {0x8F3E,0x8F97}, {0x8F3F,0x8206}, {0x8F42,0x6BC2}, {0x8F44,0x8F96}, 
+  {0x8F45,0x8F95}, {0x8F46,0x8F98}, {0x8F49,0x8F6C}, {0x8F4D,0x8F99}, {0x8F4E,0x8F7F}, 
+  {0x8F54,0x8F9A}, {0x8F5F,0x8F70}, {0x8F61,0x8F94}, {0x8F62,0x8F79}, {0x8F64,0x8F73}, 
+  {0x8FA6,0x529E}, {0x8FAD,0x8F9E}, {0x8FAE,0x8FAB}, {0x8FAF,0x8FA9}, {0x8FB2,0x519C}, 
+  {0x9015,0x8FF3}, {0x9019,0x8FD9}, {0x9023,0x8FDE}, {0x9032,0x8FDB}, {0x904B,0x8FD0}, 
+  {0x904E,0x8FC7}, {0x9054,0x8FBE}, {0x9055,0x8FDD}, {0x905C,0x900A}, {0x905E,0x9012}, 
+  {0x9060,0x8FDC}, {0x9069,0x9002}, {0x9072,0x8FDF}, {0x9077,0x8FC1}, {0x9078,0x9009}, 
+  {0x907A,0x9057}, {0x907C,0x8FBD}, {0x9081,0x8FC8}, {0x9084,0x8FD8}, {0x9087,0x8FE9}, 
+  {0x908A,0x8FB9}, {0x908F,0x903B}, {0x9090,0x9026}, {0x90DF,0x90CF}, {0x90F5,0x90AE}, 
+  {0x9106,0x90D3}, {0x9109,0x4E61}, {0x9112,0x90B9}, {0x9114,0x90AC}, {0x9116,0x90E7}, 
+  {0x9127,0x9093}, {0x912D,0x90D1}, {0x9130,0x90BB}, {0x9132,0x90F8}, {0x9134,0x90BA}, 
+  {0x9136,0x90D0}, {0x913A,0x909D}, {0x9148,0x90E6}, {0x9196,0x915D}, {0x919C,0x4E11}, 
+  {0x91AB,0x533B}, {0x91AC,0x9171}, {0x91C0,0x917F}, {0x91C1,0x8845}, {0x91C3,0x917E}, 
+  {0x91C5,0x917D}, {0x91CB,0x91CA}, {0x91D2,0x9485}, {0x91D3,0x9486}, {0x91D4,0x9487}, 
+  {0x91D5,0x948C}, {0x91D7,0x948A}, {0x91D8,0x9489}, {0x91D9,0x948B}, {0x91DD,0x9488}, 
+  {0x91E3,0x9493}, {0x91E4,0x9490}, {0x91E7,0x948F}, {0x91E9,0x9492}, {0x91F5,0x9497}, 
+  {0x91F7,0x948D}, {0x91F9,0x9495}, {0x91FA,0x948E}, {0x9200,0x94AF}, {0x9201,0x94AB}, 
+  {0x9204,0x94AD}, {0x9208,0x949A}, {0x9209,0x94A0}, {0x920D,0x949D}, {0x920E,0x94A9}, 
+  {0x9210,0x94A4}, {0x9211,0x94A3}, {0x9214,0x949E}, {0x9215,0x94AE}, {0x921E,0x94A7}, 
+  {0x9223,0x9499}, {0x9225,0x94AC}, {0x9226,0x949B}, {0x9227,0x94AA}, {0x922E,0x94CC}, 
+  {0x9230,0x94C8}, {0x9233,0x94B6}, {0x9234,0x94C3}, {0x9237,0x94B4}, {0x9238,0x94B9}, 
+  {0x9239,0x94CD}, {0x923A,0x94B0}, {0x923D,0x94B8}, {0x923E,0x94C0}, {0x923F,0x94BF}, 
+  {0x9240,0x94BE}, {0x9245,0x949C}, {0x9248,0x94CA}, {0x9249,0x94C9}, {0x924D,0x94CB}, 
+  {0x9251,0x94C2}, {0x9255,0x94B7}, {0x9257,0x94B3}, {0x925A,0x94C6}, {0x925B,0x94C5}, 
+  {0x925E,0x94BA}, {0x9262,0x94B5}, {0x9266,0x94B2}, {0x926C,0x94BC}, {0x926D,0x94BD}, 
+  {0x9278,0x94F0}, {0x927A,0x94D2}, {0x927B,0x94EC}, {0x927F,0x94EA}, {0x9280,0x94F6}, 
+  {0x9283,0x94F3}, {0x9285,0x94DC}, {0x9291,0x94E3}, {0x9293,0x94E8}, {0x9296,0x94E2}, 
+  {0x9298,0x94ED}, {0x929A,0x94EB}, {0x929C,0x8854}, {0x92A0,0x94D1}, {0x92A3,0x94F7}, 
+  {0x92A5,0x94F1}, {0x92A6,0x94DF}, {0x92A8,0x94F5}, {0x92A9,0x94E5}, {0x92AA,0x94D5}, 
+  {0x92AB,0x94EF}, {0x92AC,0x94D0}, {0x92B1,0x94DE}, {0x92B7,0x9500}, {0x92B9,0x9508}, 
+  {0x92BB,0x9511}, {0x92BC,0x9509}, {0x92C1,0x94DD}, {0x92C3,0x9512}, {0x92C5,0x950C}, 
+  {0x92C7,0x94A1}, {0x92CC,0x94E4}, {0x92CF,0x94D7}, {0x92D2,0x950B}, {0x92DD,0x950A}, 
+  {0x92DF,0x9513}, {0x92E3,0x94D8}, {0x92E4,0x9504}, {0x92E5,0x9503}, {0x92E6,0x9514}, 
+  {0x92E8,0x9507}, {0x92EA,0x94FA}, {0x92ED,0x9510}, {0x92EE,0x94D6}, {0x92EF,0x9506}, 
+  {0x92F0,0x9502}, {0x92F1,0x94FD}, {0x92F6,0x950D}, {0x92F8,0x952F}, {0x92FC,0x94A2}, 
+  {0x9301,0x951E}, {0x9306,0x9516}, {0x9307,0x952B}, {0x9308,0x9529}, {0x9310,0x9525}, 
+  {0x9312,0x9515}, {0x9315,0x951F}, {0x9318,0x9524}, {0x9319,0x9531}, {0x931A,0x94EE}, 
+  {0x931B,0x951B}, {0x931F,0x952C}, {0x9320,0x952D}, {0x9322,0x94B1}, {0x9326,0x9526}, 
+  {0x9328,0x951A}, {0x932B,0x9521}, {0x932E,0x9522}, {0x932F,0x9519}, {0x9332,0x5F55}, 
+  {0x9333,0x9530}, {0x9338,0x94FC}, {0x9340,0x951D}, {0x9341,0x9528}, {0x9343,0x952A}, 
+  {0x9346,0x9494}, {0x9347,0x9534}, {0x934B,0x9505}, {0x934D,0x9540}, {0x9354,0x9537}, 
+  {0x9358,0x94E1}, {0x935B,0x953B}, {0x9364,0x9538}, {0x9365,0x9532}, {0x9369,0x9518}, 
+  {0x936C,0x9539}, {0x9370,0x953E}, {0x9375,0x952E}, {0x9376,0x9536}, {0x937A,0x9517}, 
+  {0x937E,0x953A}, {0x9382,0x9541}, {0x9384,0x953F}, {0x9387,0x9545}, {0x938A,0x9551}, 
+  {0x9396,0x9501}, {0x9398,0x9549}, {0x93A2,0x94A8}, {0x93A3,0x84E5}, {0x93A6,0x954F}, 
+  {0x93A7,0x94E0}, {0x93A9,0x94E9}, {0x93AA,0x953C}, {0x93AC,0x9550}, {0x93AE,0x9547}, 
+  {0x93B0,0x9552}, {0x93B3,0x954D}, {0x93B5,0x9553}, {0x93B8,0x954C}, {0x93BF,0x954E}, 
+  {0x93C3,0x955E}, {0x93C7,0x955F}, {0x93C8,0x94FE}, {0x93CC,0x9546}, {0x93CD,0x9559}, 
+  {0x93D1,0x955D}, {0x93D7,0x94FF}, {0x93D8,0x9535}, {0x93DC,0x9557}, {0x93DD,0x9558}, 
+  {0x93DE,0x955B}, {0x93DF,0x94F2}, {0x93E1,0x955C}, {0x93E2,0x9556}, {0x93E4,0x9542}, 
+  {0x93E8,0x933E}, {0x93F5,0x94E7}, {0x93F7,0x9564}, {0x93F9,0x956A}, {0x9403,0x94D9}, 
+  {0x940B,0x94F4}, {0x9410,0x9563}, {0x9412,0x94F9}, {0x9413,0x9566}, {0x9414,0x9561}, 
+  {0x9418,0x949F}, {0x9419,0x956B}, {0x941D,0x9562}, {0x9420,0x9568}, {0x9426,0x950E}, 
+  {0x9427,0x950F}, {0x9428,0x9544}, {0x942E,0x9570}, {0x9432,0x956F}, {0x9433,0x956D}, 
+  {0x9435,0x94C1}, {0x9438,0x94CE}, {0x943A,0x94DB}, {0x943F,0x9571}, {0x9444,0x94F8}, 
+  {0x944A,0x956C}, {0x944C,0x9554}, {0x9452,0x9274}, {0x9454,0x9572}, {0x9460,0x94C4}, 
+  {0x9463,0x9573}, {0x9465,0x9565}, {0x946D,0x9567}, {0x9470,0x94A5}, {0x9472,0x9576}, 
+  {0x9477,0x954A}, {0x9479,0x9569}, {0x947C,0x9523}, {0x947D,0x94BB}, {0x947E,0x92AE}, 
+  {0x947F,0x51FF}, {0x9577,0x957F}, {0x9580,0x95E8}, {0x9582,0x95E9}, {0x9583,0x95EA}, 
+  {0x9586,0x95EB}, {0x9589,0x95ED}, {0x958B,0x5F00}, {0x958C,0x95F6}, {0x958E,0x95F3}, 
+  {0x958F,0x95F0}, {0x9591,0x95F2}, {0x9593,0x95F4}, {0x9594,0x95F5}, {0x9598,0x95F8}, 
+  {0x95A1,0x9602}, {0x95A3,0x9601}, {0x95A5,0x9600}, {0x95A8,0x95FA}, {0x95A9,0x95FD}, 
+  {0x95AB,0x9603}, {0x95AC,0x9606}, {0x95AD,0x95FE}, {0x95B2,0x9605}, {0x95B6,0x960A}, 
+  {0x95B9,0x9609}, {0x95BB,0x960E}, {0x95BC,0x960F}, {0x95BD,0x960D}, {0x95BE,0x9608}, 
+  {0x95BF,0x960C}, {0x95C3,0x9612}, {0x95C8,0x95F1}, {0x95CA,0x9614}, {0x95CB,0x9615}, 
+  {0x95CC,0x9611}, {0x95D0,0x9617}, {0x95D4,0x9616}, {0x95D5,0x9619}, {0x95D6,0x95EF}, 
+  {0x95DC,0x5173}, {0x95DE,0x961A}, {0x95E1,0x9610}, {0x95E5,0x95FC}, {0x9658,0x9649}, 
+  {0x965D,0x9655}, {0x9663,0x9635}, {0x9670,0x9634}, {0x9673,0x9648}, {0x9678,0x9646}, 
+  {0x967D,0x9633}, {0x9689,0x9667}, {0x968A,0x961F}, {0x968E,0x9636}, {0x9695,0x9668}, 
+  {0x969B,0x9645}, {0x96A8,0x968F}, {0x96AA,0x9669}, {0x96B1,0x9690}, {0x96B4,0x9647}, 
+  {0x96B8,0x96B6}, {0x96D6,0x867D}, {0x96D9,0x53CC}, {0x96DB,0x96CF}, {0x96DC,0x6742}, 
+  {0x96E2,0x79BB}, {0x96E3,0x96BE}, {0x96F2,0x4E91}, {0x96FB,0x7535}, {0x9727,0x96FE}, 
+  {0x973D,0x9701}, {0x9742,0x96F3}, {0x9744,0x972D}, {0x9748,0x7075}, {0x975A,0x9753}, 
+  {0x975C,0x9759}, {0x9768,0x9765}, {0x978F,0x5DE9}, {0x97BD,0x9792}, {0x97C3,0x9791}, 
+  {0x97C9,0x97AF}, {0x97CB,0x97E6}, {0x97CC,0x97E7}, {0x97D3,0x97E9}, {0x97D9,0x97EA}, 
+  {0x97DC,0x97EC}, {0x97DE,0x97EB}, {0x97FF,0x54CD}, {0x9801,0x9875}, {0x9802,0x9876}, 
+  {0x9803,0x9877}, {0x9805,0x9879}, {0x9806,0x987A}, {0x9807,0x9878}, {0x9808,0x987B}, 
+  {0x980A,0x987C}, {0x980C,0x9882}, {0x980E,0x9880}, {0x980F,0x9883}, {0x9810,0x9884}, 
+  {0x9811,0x987D}, {0x9812,0x9881}, {0x9813,0x987F}, {0x9817,0x9887}, {0x9818,0x9886}, 
+  {0x981C,0x988C}, {0x9821,0x9889}, {0x9824,0x9890}, {0x9826,0x988F}, {0x982D,0x5934}, 
+  {0x9830,0x988A}, {0x9837,0x9894}, {0x9838,0x9888}, {0x983B,0x9891}, {0x983D,0x9893}, 
+  {0x9846,0x9897}, {0x984C,0x9898}, {0x984D,0x989D}, {0x984E,0x989A}, {0x9853,0x989B}, 
+  {0x9854,0x989C}, {0x9858,0x613F}, {0x9859,0x98A1}, {0x985B,0x98A0}, {0x985E,0x7C7B}, 
+  {0x9862,0x989F}, {0x9865,0x98A2}, {0x9867,0x987E}, {0x986B,0x98A4}, {0x986C,0x98A5}, 
+  {0x986F,0x663E}, {0x9870,0x98A6}, {0x9871,0x9885}, {0x9873,0x989E}, {0x9874,0x98A7}, 
+  {0x98A8,0x98CE}, {0x98AE,0x98D1}, {0x98AF,0x98D2}, {0x98B6,0x98D3}, {0x98BC,0x98D5}, 
+  {0x98C4,0x98D8}, {0x98C6,0x98D9}, {0x98C8,0x98DA}, {0x98DB,0x98DE}, {0x98E0,0x9963}, 
+  {0x98E9,0x9968}, {0x98EA,0x996A}, {0x98EB,0x996B}, {0x98ED,0x996C}, {0x98EF,0x996D}, 
+  {0x98F2,0x996E}, {0x98F4,0x9974}, {0x98FC,0x9972}, {0x98FD,0x9971}, {0x98FE,0x9970}, 
+  {0x9903,0x997A}, {0x9905,0x997C}, {0x9909,0x9977}, {0x990A,0x517B}, {0x990C,0x9975}, 
+  {0x9911,0x997D}, {0x9912,0x9981}, {0x9913,0x997F}, {0x9918,0x4F59}, {0x991B,0x9984}, 
+  {0x991E,0x996F}, {0x9921,0x9985}, {0x9928,0x9986}, {0x9933,0x9967}, {0x9937,0x9987}, 
+  {0x993C,0x9969}, {0x993E,0x998F}, {0x993F,0x998A}, {0x9943,0x998D}, {0x9945,0x9992}, 
+  {0x9948,0x9990}, {0x9949,0x9991}, {0x994A,0x9993}, {0x994B,0x9988}, {0x994C,0x9994}, 
+  {0x9951,0x9965}, {0x9952,0x9976}, {0x9957,0x98E8}, {0x995C,0x990D}, {0x995E,0x998B}, 
+  {0x9962,0x9995}, {0x99AC,0x9A6C}, {0x99AD,0x9A6D}, {0x99AE,0x51AF}, {0x99B1,0x9A6E}, 
+  {0x99B3,0x9A70}, {0x99B4,0x9A6F}, {0x99C1,0x9A73}, {0x99D0,0x9A7B}, {0x99D1,0x9A7D}, 
+  {0x99D2,0x9A79}, {0x99D4,0x9A75}, {0x99D5,0x9A7E}, {0x99D8,0x9A80}, {0x99D9,0x9A78}, 
+  {0x99DB,0x9A76}, {0x99DD,0x9A7C}, {0x99DF,0x9A77}, {0x99E1,0x9A82}, {0x99E2,0x9A88}, 
+  {0x99ED,0x9A87}, {0x99F1,0x9A86}, {0x99FF,0x9A8F}, {0x9A01,0x9A8B}, {0x9A05,0x9A93}, 
+  {0x9A0D,0x9A92}, {0x9A0E,0x9A91}, {0x9A0F,0x9A90}, {0x9A16,0x9A9B}, {0x9A19,0x9A97}, 
+  {0x9A2B,0x9A9E}, {0x9A2D,0x9A98}, {0x9A2E,0x9A9D}, {0x9A30,0x817E}, {0x9A36,0x9A7A}, 
+  {0x9A37,0x9A9A}, {0x9A38,0x9A9F}, {0x9A3E,0x9AA1}, {0x9A40,0x84E6}, {0x9A41,0x9A9C}, 
+  {0x9A42,0x9A96}, {0x9A43,0x9AA0}, {0x9A44,0x9AA2}, {0x9A45,0x9A71}, {0x9A4A,0x9A85}, 
+  {0x9A4D,0x9A81}, {0x9A4F,0x9AA3}, {0x9A55,0x9A84}, {0x9A57,0x9A8C}, {0x9A5A,0x60CA}, 
+  {0x9A5B,0x9A7F}, {0x9A5F,0x9AA4}, {0x9A62,0x9A74}, {0x9A64,0x9AA7}, {0x9A65,0x9AA5}, 
+  {0x9A6A,0x9A8A}, {0x9AAF,0x80AE}, {0x9ACF,0x9AC5}, {0x9AD4,0x4F53}, {0x9AD5,0x9ACC}, 
+  {0x9AD6,0x9ACB}, {0x9B06,0x677E}, {0x9B22,0x9B13}, {0x9B25,0x6597}, {0x9B27,0x95F9}, 
+  {0x9B29,0x960B}, {0x9B2E,0x9604}, {0x9B31,0x90C1}, {0x9B4E,0x9B49}, {0x9B58,0x9B47}, 
+  {0x9B5A,0x9C7C}, {0x9B6F,0x9C81}, {0x9B74,0x9C82}, {0x9B77,0x9C7F}, {0x9B81,0x9C85}, 
+  {0x9B83,0x9C86}, {0x9B8E,0x9C87}, {0x9B90,0x9C90}, {0x9B91,0x9C8D}, {0x9B92,0x9C8B}, 
+  {0x9B9A,0x9C92}, {0x9B9D,0x9C9E}, {0x9B9E,0x9C95}, {0x9BAA,0x9C94}, {0x9BAB,0x9C9B}, 
+  {0x9BAD,0x9C91}, {0x9BAE,0x9C9C}, {0x9BC0,0x9CA7}, {0x9BC1,0x9CA0}, {0x9BC7,0x9CA9}, 
+  {0x9BC9,0x9CA4}, {0x9BCA,0x9CA8}, {0x9BD4,0x9CBB}, {0x9BD6,0x9CAD}, {0x9BDB,0x9CB7}, 
+  {0x9BDD,0x9CB4}, {0x9BE1,0x9CB1}, {0x9BE2,0x9CB5}, {0x9BE4,0x9CB2}, {0x9BE7,0x9CB3}, 
+  {0x9BE8,0x9CB8}, {0x9BEA,0x9CAE}, {0x9BEB,0x9CB0}, {0x9BF0,0x9CB6}, {0x9BF4,0x9CBA}, 
+  {0x9BFD,0x9CAB}, {0x9BFF,0x9CCA}, {0x9C08,0x9CBD}, {0x9C09,0x9CC7}, {0x9C0D,0x9CC5}, 
+  {0x9C10,0x9CC4}, {0x9C12,0x9CC6}, {0x9C13,0x9CC3}, {0x9C20,0x9CCB}, {0x9C23,0x9CA5}, 
+  {0x9C25,0x9CCF}, {0x9C28,0x9CCE}, {0x9C29,0x9CD0}, {0x9C2D,0x9CCD}, {0x9C31,0x9CA2}, 
+  {0x9C32,0x9CCC}, {0x9C33,0x9CD3}, {0x9C35,0x9CD8}, {0x9C37,0x9CA6}, {0x9C39,0x9CA3}, 
+  {0x9C3B,0x9CD7}, {0x9C3E,0x9CD4}, {0x9C45,0x9CD9}, {0x9C48,0x9CD5}, {0x9C49,0x9CD6}, 
+  {0x9C52,0x9CDF}, {0x9C54,0x9CDD}, {0x9C56,0x9CDC}, {0x9C57,0x9CDE}, {0x9C58,0x9C9F}, 
+  {0x9C5D,0x9CBC}, {0x9C5F,0x9C8E}, {0x9C67,0x9CE2}, {0x9C6D,0x9C9A}, {0x9C78,0x9C88}, 
+  {0x9C7A,0x9CA1}, {0x9CE5,0x9E1F}, {0x9CE9,0x9E20}, {0x9CEC,0x51EB}, {0x9CF3,0x51E4}, 
+  {0x9CF4,0x9E23}, {0x9CF6,0x9E22}, {0x9D06,0x9E29}, {0x9D07,0x9E28}, {0x9D09,0x9E26}, 
+  {0x9D15,0x9E35}, {0x9D1B,0x9E33}, {0x9D1D,0x9E32}, {0x9D1F,0x9E31}, {0x9D23,0x9E2A}, 
+  {0x9D26,0x9E2F}, {0x9D28,0x9E2D}, {0x9D2F,0x9E38}, {0x9D30,0x9E39}, {0x9D3B,0x9E3F}, 
+  {0x9D3F,0x9E3D}, {0x9D42,0x9E3A}, {0x9D51,0x9E43}, {0x9D52,0x9E46}, {0x9D53,0x9E41}, 
+  {0x9D5C,0x9E48}, {0x9D5D,0x9E45}, {0x9D60,0x9E44}, {0x9D61,0x9E49}, {0x9D6A,0x9E4C}, 
+  {0x9D6C,0x9E4F}, {0x9D6F,0x9E4E}, {0x9D72,0x9E4A}, {0x9D87,0x9E2B}, {0x9D89,0x9E51}, 
+  {0x9D93,0x9E4B}, {0x9D98,0x9E55}, {0x9D9A,0x9E57}, {0x9DA5,0x9E5B}, {0x9DA9,0x9E5C}, 
+  {0x9DAF,0x83BA}, {0x9DB4,0x9E64}, {0x9DBB,0x9E58}, {0x9DBC,0x9E63}, {0x9DC0,0x9E5A}, 
+  {0x9DC2,0x9E5E}, {0x9DC4,0x9E21}, {0x9DD3,0x9E67}, {0x9DD7,0x9E25}, {0x9DD9,0x9E37}, 
+  {0x9DDA,0x9E68}, {0x9DE5,0x9E36}, {0x9DE6,0x9E6A}, {0x9DEF,0x9E69}, {0x9DF2,0x9E6B}, 
+  {0x9DF3,0x9E47}, {0x9DF8,0x9E6C}, {0x9DF9,0x9E70}, {0x9DFA,0x9E6D}, {0x9E0C,0x9E71}, 
+  {0x9E15,0x9E2C}, {0x9E1A,0x9E66}, {0x9E1B,0x9E73}, {0x9E1D,0x9E42}, {0x9E1E,0x9E3E}, 
+  {0x9E7A,0x9E7E}, {0x9E7C,0x7877}, {0x9E7D,0x76D0}, {0x9E97,0x4E3D}, {0x9EA5,0x9EA6}, 
+  {0x9EA9,0x9EB8}, {0x9EBD,0x4E48}, {0x9ECC,0x9EC9}, {0x9EDE,0x70B9}, {0x9EE8,0x515A}, 
+  {0x9EF2,0x9EEA}, {0x9EF7,0x9EE9}, {0x9EFD,0x9EFE}, {0x9EFF,0x9F0B}, {0x9F09,0x9F0D}, 
+  {0x9F4A,0x9F50}, {0x9F4B,0x658B}, {0x9F4F,0x9F51}, {0x9F52,0x9F7F}, {0x9F54,0x9F80}, 
+  {0x9F59,0x9F85}, {0x9F5C,0x9F87}, {0x9F5F,0x9F83}, {0x9F60,0x9F86}, {0x9F61,0x9F84}, 
+  {0x9F66,0x9F88}, {0x9F6A,0x9F8A}, {0x9F6C,0x9F89}, {0x9F72,0x9F8B}, {0x9F77,0x9F8C}, 
+  {0x9F8D,0x9F99}, {0x9F90,0x5E9E}, {0x9F94,0x9F9A}, {0x9F95,0x9F9B}, {0x9F9C,0x9F9F}
+};
+
+
+
+/* the letters correspond to unicode characters which
+ * have Lu, Ll, Lt, Lm, Lo, Mn, Mc, or Me in their
+ * GENERAL CATEGORY */
+
+#define NUM_LETTER_INFO 276
+static const unirange letter_info[NUM_LETTER_INFO] = {
+  {0x41,0x5a}, {0x61,0x7a}, {0xaa,0xaa}, {0xb5,0xb5}, 
+  {0xba,0xba}, {0xc0,0xd6}, {0xd8,0xf6}, {0xf8,0x1f5}, 
+  {0x1fa,0x217}, {0x250,0x2a8}, {0x2b0,0x2b8}, {0x2bb,0x2c1}, 
+  {0x2d0,0x2d1}, {0x2e0,0x2e4}, {0x300,0x345}, {0x360,0x361}, 
+  {0x37a,0x37a}, {0x386,0x386}, {0x388,0x38a}, {0x38c,0x38c}, 
+  {0x38e,0x3a1}, {0x3a3,0x3ce}, {0x3d0,0x3d6}, {0x3da,0x3da}, 
+  {0x3dc,0x3dc}, {0x3de,0x3de}, {0x3e0,0x3e0}, {0x3e2,0x3f3}, 
+  {0x401,0x40c}, {0x40e,0x44f}, {0x451,0x45c}, {0x45e,0x481}, 
+  {0x483,0x486}, {0x490,0x4c4}, {0x4c7,0x4c8}, {0x4cb,0x4cc}, 
+  {0x4d0,0x4eb}, {0x4ee,0x4f5}, {0x4f8,0x4f9}, {0x531,0x556}, 
+  {0x559,0x559}, {0x561,0x587}, {0x591,0x5a1}, {0x5a3,0x5b9}, 
+  {0x5bb,0x5bd}, {0x5bf,0x5bf}, {0x5c1,0x5c2}, {0x5c4,0x5c4}, 
+  {0x5d0,0x5ea}, {0x5f0,0x5f2}, {0x621,0x63a}, {0x640,0x652}, 
+  {0x670,0x6b7}, {0x6ba,0x6be}, {0x6c0,0x6ce}, {0x6d0,0x6d3}, 
+  {0x6d5,0x6e8}, {0x6ea,0x6ed}, {0x901,0x903}, {0x905,0x939}, 
+  {0x93c,0x94d}, {0x951,0x954}, {0x958,0x963}, {0x981,0x983}, 
+  {0x985,0x98c}, {0x98f,0x990}, {0x993,0x9a8}, {0x9aa,0x9b0}, 
+  {0x9b2,0x9b2}, {0x9b6,0x9b9}, {0x9bc,0x9bc}, {0x9be,0x9c4}, 
+  {0x9c7,0x9c8}, {0x9cb,0x9cd}, {0x9d7,0x9d7}, {0x9dc,0x9dd}, 
+  {0x9df,0x9e3}, {0x9f0,0x9f1}, {0xa02,0xa02}, {0xa05,0xa0a}, 
+  {0xa0f,0xa10}, {0xa13,0xa28}, {0xa2a,0xa30}, {0xa32,0xa33}, 
+  {0xa35,0xa36}, {0xa38,0xa39}, {0xa3c,0xa3c}, {0xa3e,0xa42}, 
+  {0xa47,0xa48}, {0xa4b,0xa4d}, {0xa59,0xa5c}, {0xa5e,0xa5e}, 
+  {0xa70,0xa74}, {0xa81,0xa83}, {0xa85,0xa8b}, {0xa8d,0xa8d}, 
+  {0xa8f,0xa91}, {0xa93,0xaa8}, {0xaaa,0xab0}, {0xab2,0xab3}, 
+  {0xab5,0xab9}, {0xabc,0xac5}, {0xac7,0xac9}, {0xacb,0xacd}, 
+  {0xae0,0xae0}, {0xb01,0xb03}, {0xb05,0xb0c}, {0xb0f,0xb10}, 
+  {0xb13,0xb28}, {0xb2a,0xb30}, {0xb32,0xb33}, {0xb36,0xb39}, 
+  {0xb3c,0xb43}, {0xb47,0xb48}, {0xb4b,0xb4d}, {0xb56,0xb57}, 
+  {0xb5c,0xb5d}, {0xb5f,0xb61}, {0xb82,0xb83}, {0xb85,0xb8a}, 
+  {0xb8e,0xb90}, {0xb92,0xb95}, {0xb99,0xb9a}, {0xb9c,0xb9c}, 
+  {0xb9e,0xb9f}, {0xba3,0xba4}, {0xba8,0xbaa}, {0xbae,0xbb5}, 
+  {0xbb7,0xbb9}, {0xbbe,0xbc2}, {0xbc6,0xbc8}, {0xbca,0xbcd}, 
+  {0xbd7,0xbd7}, {0xc01,0xc03}, {0xc05,0xc0c}, {0xc0e,0xc10}, 
+  {0xc12,0xc28}, {0xc2a,0xc33}, {0xc35,0xc39}, {0xc3e,0xc44}, 
+  {0xc46,0xc48}, {0xc4a,0xc4d}, {0xc55,0xc56}, {0xc60,0xc61}, 
+  {0xc82,0xc83}, {0xc85,0xc8c}, {0xc8e,0xc90}, {0xc92,0xca8}, 
+  {0xcaa,0xcb3}, {0xcb5,0xcb9}, {0xcbe,0xcc4}, {0xcc6,0xcc8}, 
+  {0xcca,0xccd}, {0xcd5,0xcd6}, {0xcde,0xcde}, {0xce0,0xce1}, 
+  {0xd02,0xd03}, {0xd05,0xd0c}, {0xd0e,0xd10}, {0xd12,0xd28}, 
+  {0xd2a,0xd39}, {0xd3e,0xd43}, {0xd46,0xd48}, {0xd4a,0xd4d}, 
+  {0xd57,0xd57}, {0xd60,0xd61}, {0xe01,0xe2e}, {0xe30,0xe3a}, 
+  {0xe40,0xe4e}, {0xe81,0xe82}, {0xe84,0xe84}, {0xe87,0xe88}, 
+  {0xe8a,0xe8a}, {0xe8d,0xe8d}, {0xe94,0xe97}, {0xe99,0xe9f}, 
+  {0xea1,0xea3}, {0xea5,0xea5}, {0xea7,0xea7}, {0xeaa,0xeab}, 
+  {0xead,0xeae}, {0xeb0,0xeb9}, {0xebb,0xebd}, {0xec0,0xec4}, 
+  {0xec6,0xec6}, {0xec8,0xecd}, {0xedc,0xedd}, {0xf18,0xf19}, 
+  {0xf35,0xf35}, {0xf37,0xf37}, {0xf39,0xf39}, {0xf40,0xf47}, 
+  {0xf49,0xf69}, {0xf71,0xf84}, {0xf86,0xf87}, {0xf90,0xf95}, 
+  {0xf97,0xf97}, {0xf99,0xfad}, {0xfb1,0xfb7}, {0xfb9,0xfb9}, 
+  {0x10a0,0x10c5}, {0x10d0,0x10f6}, {0x1100,0x1159}, {0x115f,0x11a2}, 
+  {0x11a8,0x11f9}, {0x1820,0x1877}, {0x1880,0x18a9}, {0x1e00,0x1e9b}, 
+  {0x1ea0,0x1ef9}, {0x1f00,0x1f15}, 
+  {0x1f18,0x1f1d}, {0x1f20,0x1f45}, {0x1f48,0x1f4d}, {0x1f50,0x1f57}, 
+  {0x1f59,0x1f59}, {0x1f5b,0x1f5b}, {0x1f5d,0x1f5d}, {0x1f5f,0x1f7d}, 
+  {0x1f80,0x1fb4}, {0x1fb6,0x1fbc}, {0x1fbe,0x1fbe}, {0x1fc2,0x1fc4}, 
+  {0x1fc6,0x1fcc}, {0x1fd0,0x1fd3}, {0x1fd6,0x1fdb}, {0x1fe0,0x1fec}, 
+  {0x1ff2,0x1ff4}, {0x1ff6,0x1ffc}, {0x207f,0x207f}, {0x20d0,0x20e1}, 
+  {0x2102,0x2102}, {0x2107,0x2107}, {0x210a,0x2113}, {0x2115,0x2115}, 
+  {0x2118,0x211d}, {0x2124,0x2124}, {0x2126,0x2126}, {0x2128,0x2128}, 
+  {0x212a,0x2131}, {0x2133,0x2138}, {0x3005,0x3005}, {0x302a,0x302f}, 
+  {0x3031,0x3035}, {0x3041,0x3094}, {0x3099,0x309e}, {0x30a1,0x30fa}, 
+  {0x30fc,0x30fe}, {0x3105,0x312c}, {0x3131,0x318e}, {0x4e00,0x9fa5}, 
+  {0xac00,0xd7a3}, {0xf900,0xfa2d}, {0xfb00,0xfb06}, {0xfb13,0xfb17}, 
+  {0xfb1e,0xfb28}, {0xfb2a,0xfb36}, {0xfb38,0xfb3c}, {0xfb3e,0xfb3e}, 
+  {0xfb40,0xfb41}, {0xfb43,0xfb44}, {0xfb46,0xfbb1}, {0xfbd3,0xfd3d}, 
+  {0xfd50,0xfd8f}, {0xfd92,0xfdc7}, {0xfdf0,0xfdfb}, {0xfe20,0xfe23}, 
+  {0xfe70,0xfe72}, {0xfe74,0xfe74}, {0xfe76,0xfefc}, {0xff21,0xff3a}, 
+  {0xff41,0xff5a}, {0xff66,0xffbe}, {0xffc2,0xffc7}, {0xffca,0xffcf}, 
+  {0xffd2,0xffd7}, {0xffda,0xffdc}
+};
+
+
+/* the digits correspond to unicode characters which
+ * have Nd in their GENERAL CATEGORY */
+
+#define NUM_DIGIT_INFO 17
+static const unirange digit_info[NUM_DIGIT_INFO] = {
+  {0x30,0x39}, {0x660,0x669}, {0x6f0,0x6f9}, {0x966,0x96f}, 
+  {0x9e6,0x9ef}, {0xa66,0xa6f}, {0xae6,0xaef}, {0xb66,0xb6f}, 
+  {0xbe7,0xbef}, {0xc66,0xc6f}, {0xce6,0xcef}, {0xd66,0xd6f}, 
+  {0xe50,0xe59}, {0xed0,0xed9}, {0xf20,0xf29}, {0xff10,0xff19},
+  {0x1810,0x1819}
+};
+
+
+/* the spaces correspond to unicode characters which
+ * have Zs, Zl, or Zp in their GENERAL CATEGORY */
+
+#define NUM_SPACE_INFO 5
+static const unirange space_info[NUM_SPACE_INFO] = {
+  {0x20,0x20}, {0xa0,0xa0}, {0x2000,0x200b}, {0x2028,0x2029}, 
+  {0x3000,0x3000}
+};
+
+
+
+/* parse_utf8_char parses the next utf-8 character, placing its
+ * unicode equivalent in *value. The length of the utf-8 character
+ * is returned */
+int parse_utf8_char(const unsigned char *here,
+		    const unsigned char *end,
+		    unsigned short *value) {
+  unsigned short c1;
+  unsigned short c2;
+  unsigned short c3;
+  
+  *value = 0; /* will be the value for illegal characters */
+  
+  if (here <= end) {
+    c1 = (unsigned short)(*here);
+    
+    if (c1 <= 0x7f) {
+      /* found a one-byte character */
+      *value = c1;
+      return 1;
+      
+    } else if (c1 >= 0xc0 && c1 <= 0xdf && here+1 <= end &&
+	       (c2=(unsigned short)(*(here+1))) >= 0x80) {
+      /* found a two-byte character */
+      *value = ((c1 & 0x1f) << 6) + (c2 & 0x3f);
+      return 2;
+      
+    } else if (c1 >= 0xe0 && c1 <= 0xef && here+2 <= end &&
+	       (c2=(unsigned int)(*(here+1))) >= 0x80 &&
+	       (c3=(unsigned int)(*(here+2))) >= 0x80) {
+      /* found a three-byte character */
+      *value = ((c1 & 0xf) << 12) + ((c2 & 0x3f) << 6) +
+	(c3 & 0x3f);
+      return 3;
+      
+    } else {
+      /* error in encoding, return the length as 1 */
+      return 1;
+    }
+  }
+
+  /* end of string */
+  return 0;
+}
+
+
+/* output_utf8_char encodes a unicode character as a UTF-8 character.
+ * The length of the encoding is returned. If the string was not
+ * long enough to encode the character 0 is returned. */
+int output_utf8_char (unsigned short value,
+		      unsigned char *here,
+		      unsigned char *end) {
+  int len = (int)(end - here + 1); /* end is the last character */
+  if (value < 0x80) {
+    /* will be encoded in one byte */
+    if (len >= 1) {
+      *here = (unsigned char)value;
+      return 1;
+    }
+
+  } else if (value < 0x800) {
+    /* will be encoded in two bytes */
+    if (len >= 2) {
+      *here++ = 0xc0 | (unsigned char)((value >> 6) & 0x1f);
+      *here = 0x80 | (unsigned char)(value & 0x3f);
+
+      return 2;
+    }
+
+  } else {
+    /* will be encoded in three bytes */
+    if (len >= 3) {
+      *here++ = 0xe0 | (unsigned char)((value >> 12) & 0xf);
+      *here++ = 0x80 | (unsigned char)((value >> 6) & 0x3f);
+      *here = 0x80 | (unsigned char)(value & 0x3f);
+      return 3;
+    }
+  }
+
+  return 0;
+}
+
+
+static int lookup_decompose_table (unsigned short value) {
+  int first = 0;
+  int last = NUM_DECOMPOSE_INFO;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < decompose_info[here].ch) {
+      last = here;
+    } else if (value > decompose_info[here].ch) {
+      first = here+1;
+    } else {
+      return here;
+    }
+  }
+
+  return -1;
+}
+
+/* decompose_str will decompose a unicode string into its canonical
+ * equivalents. NULL is returned if the input array was not 
+ * large enough to contain the fully decomposed string (the array
+ * will be in a correct, but partially decomposed state). The input 
+ * must be null-terminated. */
+unsigned short *decompose_str (unsigned short *input,
+			       int max_output_len) {
+  int curpos = 0;
+  int curlen = unicode_strlen (input);
+  int tablepos = 0;
+  int leninc;
+  int i;
+
+  while (input[curpos] != 0) {
+    tablepos = lookup_decompose_table (input[curpos]);
+    if (tablepos >= 0) {
+      /* found a decomposition */
+      leninc = unicode_strnlen (decompose_info[tablepos].str, 3)-1;
+      if (curlen+leninc < max_output_len) {
+	/* enough space for this decomposition */
+
+	if (leninc > 1) {
+	  /* we have to shift the array along */
+	  for (i=curlen-1; i>=curpos; --i) {
+	    input[i+leninc] = input[i];
+	  }
+	  input[curlen+leninc] = 0;
+	  curlen += leninc;
+	}
+
+	/* decompose the character */
+	for (i=0; i<=leninc; ++i) {
+	  input[curpos+i] = decompose_info[tablepos].str[i];
+	}
+
+	/* note: curpos is not incremented because the decomposed */
+	/*       characters might need decomposing */
+
+      } else {
+	/* no space left in string */
+	return NULL;
+      }
+
+    } else {
+      /* the character cannot be decomposed */
+      ++curpos;
+    }
+  }
+
+  return input;
+}
+
+
+
+/* unicode_cat_data is a combination of the letter, digit, and
+ * space information in a form that is quick to look up.
+ * The information about each byte is stored in two bits
+ *
+ * 00 - neither letter, digit or space
+ * 01 - letter
+ * 10 - space
+ * 11 - digit
+ *
+ * You can test for letter | space by looking at the right
+ * column
+ */
+/*unsigned long *unicode_cat_data;*/
+
+void create_unicat_data () {
+  int i=0;
+  int min_start=0;
+  int min_end=0;
+  register unsigned long min_value=0; /* filling the whole byte */
+  
+  const unirange *letter_here = letter_info;
+  const unirange *letter_end = letter_info+NUM_LETTER_INFO;
+  const unirange *digit_here = digit_info;
+  const unirange *digit_end = digit_info+NUM_DIGIT_INFO;
+  const unirange *space_here = space_info;
+  const unirange *space_end = space_info+NUM_SPACE_INFO;
+
+  if (unicode_cat_data != NULL) return;
+  unicode_cat_data = (unsigned long *) malloc (0x10000/4);
+  if (unicode_cat_data == NULL) return;
+  
+  while (i <= 0xffff) {
+    /* decide what to fill with next */
+    min_start = i;
+    min_end = 0xffff;
+    min_value = 0; /* 00 */
+    
+    while (letter_here < letter_end && letter_here->last < i) ++letter_here;
+    if (letter_here < letter_end && letter_here->last <= min_end) {
+      min_start = letter_here->first;
+      min_end = letter_here->last;
+      min_value = 0x55555555; /* 01 */
+    }
+    
+    while (digit_here < digit_end && digit_here->last < i) ++digit_here;
+    if (digit_here < digit_end && digit_here->last <= min_end) {
+      min_start = digit_here->first;
+      min_end = digit_here->last;
+      min_value = 0xffffffff; /* 11 */
+    }
+    
+    while (space_here < space_end && space_here->last < i) ++space_here;
+    if (space_here < space_end && space_here->last <= min_end) {
+      min_start = space_here->first;
+      min_end = space_here->last;
+      min_value = 0xaaaaaaaa; /* 10 */
+    }
+
+    if (min_start < i) min_start = i;
+    else if (min_start > i) {
+      min_end = min_start-1;
+      min_start = i;
+      min_value = 0; /* 00 */
+    }
+
+    /* fill the entries from min_start to min_end with min_value */
+    while (i <= min_end) {
+      /* special version for common case */
+      if (i % 16 == 0) {
+	unicode_cat_data[i/16] = min_value; /* doesn't matter if too long */
+	i += 16;
+	if (i > min_end) {
+	  i = min_end+1;
+	  break;
+	}
+	
+      } else {
+	register unsigned long mask = 3 << ((i % 16) * 2);
+	unicode_cat_data[i/16] = (unicode_cat_data[i/16] & (~mask)) |
+				  (min_value & mask);
+	++i;
+      }
+    }
+  }
+}
+
+/* does a binary search on a range table */
+static int in_unirange (const unirange *info, int num_info,
+			unsigned short value) {
+  int first = 0;
+  int last = num_info;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < info[here].first) {
+      last = here;
+    } else if (value > info[here].last) {
+      first = here+1;
+    } else {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+
+
+/* tests to see whether 'value' is a valid Unicode letter */
+int is_unicode_letter (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(letter_info, NUM_LETTER_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x1);
+}
+
+/* tests to see whether 'value' is a valid Unicode digit */
+int is_unicode_digit (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(digit_info, NUM_DIGIT_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x3);
+}
+
+/* tests to see whether 'value' is a valid Unicode letter or 
+ * digit */
+int is_unicode_letdig (unsigned short value) {
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return (in_unirange(letter_info, NUM_LETTER_INFO, value) ||
+	    in_unirange(digit_info, NUM_DIGIT_INFO, value));
+    
+  }
+
+  return ((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x1);
+}
+
+/* tests to see whether 'value' is a valid space
+ * The test includes both "C" spaces and "Unicode" spaces, i.e. 
+ * form-feed, newline, carriage return, horizontal tab,
+ * vertical tab, and the Zs, Zl, and Zp Unicode categorizations */
+int is_unicode_space (unsigned short value) {
+  if (value < 0x80) return isspace (value);
+  if (unicode_cat_data == NULL) create_unicat_data ();
+  if (unicode_cat_data == NULL) {
+    /* use old method */
+    return in_unirange(space_info, NUM_SPACE_INFO, value);
+  }
+
+  return (((unicode_cat_data[value/16] >> ((value%16)*2)) & 0x3) == 0x2);
+}
+
+/* returns the length of the unicode string */
+int unicode_strlen (const unsigned short *str) {
+  int len = 0;
+
+  /* keep going till you find the null-terminator */
+  while (*str != 0) {
+    ++len;
+    ++str;
+  }
+
+  return len;
+}
+
+/* returns the length of the unicode string, up to a maximum */
+int unicode_strnlen (const unsigned short *str, int max_length) {
+  int len = 0;
+
+  /* keep going till you find the null-terminator */
+  while (*str != 0 && len < max_length ) {
+    ++len;
+    ++str;
+  }
+
+  return len;
+}
+
+
+/* Does a binary search on a relate table, returns -1 if it is not
+ * found. */
+static int search_unirelate (const unirelate *info, int num_info,
+			     unsigned short value) {
+  int first = 0;
+  int last = num_info;
+  int here;
+
+  while (first < last) {
+    here = (last-first)/2 + first;
+    if (value < info[here].ch1) {
+      last = here;
+    } else if (value > info[here].ch1) {
+      first = here+1;
+    } else {
+      return here;
+    }
+  }
+
+  return -1;
+}
+
+
+/* returns the upper-case equivalent of value */
+unsigned short unicode_toupper (unsigned short value) {
+  int related = search_unirelate (toupper_info, NUM_TOUPPER_INFO, value);
+  if (related >= 0) {
+    value = toupper_info[related].ch2;
+  }
+  return value;
+}
+
+/* returns the lower-case equivalent of value */
+unsigned short unicode_tolower (unsigned short value) {
+  int related = search_unirelate (tolower_info, NUM_TOLOWER_INFO, value);
+  if (related >= 0) {
+    value = tolower_info[related].ch2;
+  }
+  return value;
+}
+
+/* returns the simplified Chinese character equivalent of 
+ * another Chinese character */
+unsigned short unicode_tosimplified (unsigned short value) {
+  int related = search_unirelate (tosimplified_info, NUM_TOSIMPLIFIED_INFO, value);
+  if (related >= 0) {
+    value = tosimplified_info[related].ch2;
+  }
+  return value;
+}
+
+
+/* converts a utf-8 word (string with length stored in the first byte
+ * to a Unicode array. To handle all situations the output buffer should
+ * be 256 unsigned shorts long. The output will also have the length as
+ * the first entry. */
+unsigned short *utf8_word_to_unicode (const unsigned char *input,
+				      unsigned short *output,
+				      int max_output_length) {
+  int inlen = *input;
+  const unsigned char *inhere = input+1;
+  const unsigned char *inend = &inhere[inlen-1];
+  unsigned short c;
+  int clen;
+  
+  int outlen = 0;
+  unsigned short *outhere = output+1;
+  
+  while (inhere <= inend && inlen < max_output_length-1) {
+    /* decode the character */
+    clen = parse_utf8_char (inhere, inend, &c);
+    inhere += clen;
+
+    if (clen > 0) {
+      /* store the character */
+      *outhere = c;
+      ++outhere;
+      ++outlen;
+    }
+  }
+
+  output[0] = outlen;
+  return output;
+}
+
+
+/* converts a unicode word buffer (with the length stored in the
+ * entry) to a utf8 encoded word output (with the length stored in
+ * the first byte. Only 255 bytes (not characters) can be stored 
+ * in the output. */
+unsigned char *unicode_to_utf8_word (const unsigned short *input,
+				     unsigned char *output,
+				     int max_output_length) {
+  int inlen = *input;
+  const unsigned short *word = input + 1;
+  int in_i = 0;
+  int clen;
+
+  int outlen = 0;
+  unsigned char *outhere = output+1;
+  unsigned char *outend = output+max_output_length;
+
+  while (in_i < inlen) {
+    if ((clen=output_utf8_char(word[in_i], outhere, outend)) == 0) {
+      /* ran out of space to encode the character */
+      break;
+    }
+
+    outhere += clen;
+    outlen += clen;
+    ++in_i;
+  }
+
+  output[0] = outlen;
+
+  return output;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/unitool.h	(revision 16561)
@@ -0,0 +1,80 @@
+#ifndef UNITOOL_H
+#define UNITOOL_H
+
+/* This module is based on Unicode 2.1 */
+
+
+/* parse_utf8_char parses the next utf-8 character, placing its
+ * unicode equivalent in *value. The length of the utf-8 character
+ * is returned. end is the address of the last character. */
+int parse_utf8_char (const unsigned char *here,
+		     const unsigned char *end,
+		     unsigned short *value);
+
+/* output_utf8_char encodes a unicode character as a UTF-8 character.
+ * The length of the encoding is returned. If the string was not
+ * long enough to encode the character 0 is returned. end is the
+ * address of the last character */
+int output_utf8_char (unsigned short value,
+		      unsigned char *here,
+		      unsigned char *end);
+
+/* decompose_str will decompose a unicode string into its canonical
+ * equivalents. NULL is returned if the input array was not 
+ * large enough to contain the fully decomposed string (the array
+ * will be in a correct, but partially decomposed state). The input 
+ * must be null-terminated. */
+unsigned short *decompose_str (unsigned short *input,
+			       int max_output_len);
+
+/* tests to see whether 'value' is a valid Unicode letter */
+int is_unicode_letter (unsigned short value);
+
+/* tests to see whether 'value' is a valid Unicode digit */
+int is_unicode_digit (unsigned short value);
+
+/* tests to see whether 'value' is a valid Unicode letter or 
+ * digit */
+int is_unicode_letdig (unsigned short value);
+
+/* tests to see whether 'value' is a valid space
+ * The test includes both "C" spaces and "Unicode" spaces, i.e. 
+ * form-feed, newline, carriage return, horizontal tab,
+ * vertical tab, and the Zs, Zl, and Zp Unicode categorizations */
+int is_unicode_space (unsigned short value);
+
+/* returns the length of the unicode string */
+int unicode_strlen (const unsigned short *str);
+
+/* returns the length of the unicode string, up to a maximum */
+int unicode_strnlen (const unsigned short *str, int max_length);
+
+/* returns the upper-case equivalent of value */
+unsigned short unicode_toupper (unsigned short value);
+
+/* returns the lower-case equivalent of value */
+unsigned short unicode_tolower (unsigned short value);
+
+/* returns the simplified Chinese character equivalent of 
+ * another Chinese character */
+unsigned short unicode_tosimplified (unsigned short value);
+
+
+/* converts a utf-8 word (string with length stored in the first byte
+ * to a Unicode array. To handle all situations the output buffer should
+ * be 256 unsigned shorts long. The output will also have the length as
+ * the first entry. */
+unsigned short *utf8_word_to_unicode (const unsigned char *input,
+				      unsigned short *output,
+				      int max_output_length);
+
+/* converts a unicode word buffer (with the length stored in the
+ * entry) to a utf8 encoded word output (with the length stored in
+ * the first byte. Only 255 bytes (not characters) can be stored 
+ * in the output. */
+unsigned char *unicode_to_utf8_word (const unsigned short *input,
+				     unsigned char *output,
+				     int max_output_length);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/win32.mak	(revision 16561)
@@ -0,0 +1,96 @@
+###########################################################################
+#
+# win32 makefile -- mgpp\lib
+# 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  The 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.
+#
+###########################################################################
+
+MGPPHOME = ..
+
+AR = lib
+CC = cl
+CPPFLAGS = 
+
+DEFS = -DQUIET -DHAVE_CONFIG_H -D__WIN32__ -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"." -I"$(MGPPHOME)"
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .obj
+.c.obj:
+	$(COMPILE) $<
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+
+HEADERS = \
+    bitio_m_random.h   local_strings.h    lovinstem.h \
+    bitio_m_stdio.h    memlib.h           netorder.h \
+    bitio_gen.h        mgheap.h           messages.h \
+    bitio_m.h          huffman.h  \
+    bitio_m_mem.h      perf_hash.h \
+    bitio_m_mems.h      \
+    pathmax.h          getpagesize.h      sysfuncs.h         bitio_m_abstract.h  \
+    random.h           simplefrenchstem.h unitool.h	     longlong.h \
+    non_ansi.h         getopt_old.h       win32in.h
+
+
+SOURCES = \
+    mgheap.cpp         \
+    huffman.cpp       messages.cpp \
+    bitio_gen.cpp     perf_hash.cpp \
+    local_strings.cpp lovinstem.cpp \
+    bitio_m_mem.cpp   bitio_m_stdio.cpp bitio_m_mems.cpp \
+    alloca.c          xmalloc.c         strstr.cpp \
+    ftruncate.cpp     strcasecmp.cpp    random.cpp \
+    simplefrenchstem.cpp                unitool.cpp       bitio_m_random.cpp \
+    bitio_m_abstract.cpp                getopt_old.c      win32in.c
+# memlib.cpp removed
+
+
+OBJECTS =  mgheap$o   \
+    huffman$o         messages$o \
+    bitio_gen$o       perf_hash$o \
+     local_strings$o   lovinstem$o \
+    bitio_m_mem$o     bitio_m_stdio$o   bitio_m_mems$o \
+    xmalloc$o         bitio_m_random$o  bitio_m_abstract$o \
+     random$o          simplefrenchstem$o \
+    unitool$o         getopt_old$o      win32in$o
+
+ 
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : libmgpplib.lib
+
+libmgpplib.lib : $(OBJECTS)
+	if EXIST libmgpplib.lib del libmgpplib.lib
+	$(AR) /out:libmgpplib.lib $(OBJECTS)
+
+$(OBJECTS): "$(MGPPHOME)\win32cfg.h"
+
+install: 
+clean:
+	del *$o 
+	del libmgpplib.lib
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.c	(revision 16561)
@@ -0,0 +1,85 @@
+/* This module provides an equivalent of <netinet/in.h> on  */
+/* unix systems. */
+#include "win32in.h"
+
+unsigned long htonl(unsigned long x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) |
+		((x << 8) & 0x00FF0000) | ((x << 24) & 0xFF000000);
+#endif
+
+	return x;
+}
+
+mg_ullong ntohll(mg_ullong x)
+{
+#if defined (LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = 
+	  ((x >> 56) & 0x00000000000000FF) | ((x >> 40) & 0x000000000000FF00) |
+	  ((x >> 24) & 0x0000000000FF0000) | ((x >> 8)  & 0x00000000FF000000) |
+	  ((x << 8)  & 0x000000FF00000000) | ((x << 24) & 0x0000FF0000000000) |
+		((x << 40) & 0x00FF000000000000) | ((x << 56) & 0xFF00000000000000);
+#endif
+  return x;
+}
+
+unsigned long ntohl(unsigned long x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) |
+		((x << 8) & 0x00FF0000) | ((x << 24) & 0xFF000000);
+#endif
+	return x;
+}
+
+unsigned short htons(unsigned short x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00);
+#endif
+	return x;
+}
+
+unsigned short ntohs(unsigned short x) {
+#if defined (LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN)
+	x = ((x >> 8) & 0x00FF) | ((x << 8) & 0xFF00);
+#endif
+	return x;
+}
+
+/* just to test stuff
+#include <stdio.h>
+
+main () {
+	long x;
+	short sx;
+
+	x = 0x000000FF;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0x0000FF00;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0x00FF0000;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n",(int)x);
+
+	x = 0xFF000000;
+	printf ("before %i, ",(int)x);
+	x = htonl (x);
+	printf ("after %i\n\n",(int)x);
+
+	sx = 0x00FF;
+	printf ("before %i, ",(int)sx);
+	sx = htons (sx);
+	printf ("after %i\n",(int)sx);
+
+	sx = 0xFF00;
+	printf ("before %i, ",(int)sx);
+	sx = htons (sx);
+	printf ("after %i\n",(int)sx);
+}
+*/
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/win32in.h	(revision 16561)
@@ -0,0 +1,20 @@
+/* This module provides an equivalent of <netinet/in.h> on  */
+/* unix systems. */
+#ifdef __WIN32__
+#define _LITTLE_ENDIAN
+#endif
+
+#include "longlong.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned long htonl(unsigned long x);
+unsigned long ntohl(unsigned long x);
+mg_ullong ntohll(mg_ullong x);
+unsigned short htons(unsigned short x);
+unsigned short ntohs(unsigned short x);
+
+#ifdef __cplusplus
+}
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/lib/xmalloc.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/lib/xmalloc.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/lib/xmalloc.c	(revision 16561)
@@ -0,0 +1,111 @@
+/* xmalloc.c -- malloc with out of memory checking
+   Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__  /* [RPAP - Feb 97: WIN32 Port] */
+#  include <win32cfg.h>
+# else
+#  include <sysfuncs.h>
+# endif
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+/* This is for other GNU distributions with internationalized messages.
+   The GNU C Library itself does not yet support such messages.  */
+#if HAVE_LIBINTL_H
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+/* Exit value when the requested amount of memory is not available.
+   The caller may set it to some other value.  */
+int xmalloc_exit_failure = EXIT_FAILURE;
+
+#if defined(__WIN32__)
+void error (int, int, const char *, ...);
+#else
+#if __STDC__ && (HAVE_VPRINTF || HAVE_DOPRNT)
+void error (int, int, const char *, ...);
+#else
+void error (int, int, char *, char *, char *, char *, char *, char *, char *, char *, char *);
+#endif
+#endif
+
+static VOID *
+fixup_null_alloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = 0;
+  if (n == 0)
+    p = malloc ((size_t) 1);
+  if (p == 0)
+    error (xmalloc_exit_failure, 0, gettext ("Memory exhausted"));
+  return p;
+}
+
+/* Allocate N bytes of memory dynamically, with error checking.  */
+
+VOID *
+xmalloc (n)
+     size_t n;
+{
+  VOID *p;
+
+  p = malloc (n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+   with error checking.
+   If P is NULL, run xmalloc.  */
+
+VOID *
+xrealloc (p, n)
+     VOID *p;
+     size_t n;
+{
+  if (p == 0)
+    return xmalloc (n);
+  p = realloc (p, n);
+  if (p == 0)
+    p = fixup_null_alloc (n);
+  return p;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/mkinstalldirs
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/mkinstalldirs	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/mkinstalldirs	(revision 16561)
@@ -0,0 +1,32 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1994-03-25
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do 
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d in ${1+"$@"} ; do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp" 1>&2
+        mkdir "$pathcomp" || errstatus=$?
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
Index: /gsdl/trunk/gs2common/indexers/mgpp/stamp-h.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/stamp-h.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/stamp-h.in	(revision 16561)
@@ -0,0 +1,1 @@
+timestamp
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.cpp	(revision 16561)
@@ -0,0 +1,129 @@
+/**************************************************************************
+ *
+ * FIvfLevelInfo.cpp -- File functions for document levels
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "FIvfLevelInfo.h"
+
+
+IvfLevelInfo::IvfLevelInfo () {
+  Clear();
+}
+
+void IvfLevelInfo::Clear () {
+  levelTag.erase (levelTag.begin(), levelTag.end());
+  numEntries = 0;
+  approxWeightsDiskPtr = 0;
+  exactWeightsDiskPtr = 0;
+  workInLevel = false;
+}
+
+bool IvfLevelInfo::Read (FILE *f) {
+  workInLevel = false;
+  return (ReadUCArray (f, levelTag) &&
+	  ReadUL (f, numEntries) &&
+	  ReadUL (f, approxWeightsDiskPtr) &&
+	  ReadUL (f, exactWeightsDiskPtr));
+}
+
+bool IvfLevelInfo::Write (FILE *f) const {
+  return (WriteUCArray (f, levelTag) &&
+	  WriteUL (f, numEntries) &&
+	  WriteUL (f, approxWeightsDiskPtr) &&
+	  WriteUL (f, exactWeightsDiskPtr));
+}
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const IvfLevelInfo &l) {
+  s << "  Tag: \"" << l.levelTag << "\"\n"
+    << "  numEntries: " << l.numEntries << "\n"
+    << "  approxWeightsDiskPtr: " << l.approxWeightsDiskPtr << "\n"
+    << "  exactWeightsDiskPtr: " << l.exactWeightsDiskPtr << "\n"
+    << "  workInLevel: " << ((l.workInLevel) ? "true" : "false") << "\n\n";
+  
+  return s;
+}
+
+
+
+FIvfLevel::FIvfLevel () {
+  Clear ();
+}
+
+void FIvfLevel::Clear () {
+  docTag.erase (docTag.begin(), docTag.end());
+  indexLevel.erase (indexLevel.begin(), indexLevel.end());
+  levelInfo.erase (levelInfo.begin(), levelInfo.end());
+}
+
+bool FIvfLevel::Read (FILE *f) {
+  if (!ReadUCArray (f, docTag) ||
+      !ReadUCArray (f, indexLevel))
+    return false;
+  
+  // read in the array size
+  unsigned long arrSize = 0;
+  if (!ReadVarLenUL (f, arrSize)) return false;
+  
+  IvfLevelInfo thisLevel;
+  while (arrSize > 0) {
+    // read and insert the next level information
+    if (!thisLevel.Read (f)) return false;
+    levelInfo[thisLevel.levelTag] = thisLevel;
+    
+    --arrSize;
+  }
+
+  return true;
+}
+
+bool FIvfLevel::Write (FILE *f) const {
+  if (!WriteUCArray (f, docTag) ||
+      !WriteUCArray (f, indexLevel))
+    return false;
+  
+  // write out the array size
+  if (!WriteVarLenUL (f, levelInfo.size())) return false;
+
+  // write out each level info
+  IvfLevelInfoMap::const_iterator here = levelInfo.begin();
+  IvfLevelInfoMap::const_iterator end = levelInfo.end();
+  while (here != end) {
+    if (!(*here).second.Write (f)) return false;
+    
+    ++here;
+  }
+
+  return true;
+}
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const FIvfLevel &l) {
+  s << "docTag: \"" << l.docTag << "\"\n"
+    << "indexLevel: \"" << l.indexLevel << "\"\n";
+  
+  IvfLevelInfoMap::const_iterator here = l.levelInfo.begin ();
+  IvfLevelInfoMap::const_iterator end = l.levelInfo.end ();
+  while (here != end) {
+    s << (*here).second;
+    ++here;
+  }
+  
+  return s;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FIvfLevelInfo.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**************************************************************************
+ *
+ * FIvfLevelInfo.h -- File functions for document levels
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef FIVFLEVELINFO_H
+#define FIVFLEVELINFO_H
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+#include <stdio.h>
+#include "UCArray.h"
+
+
+class IvfLevelInfo {
+public:
+  UCArray levelTag;
+  unsigned long numEntries; // N for level
+  unsigned long approxWeightsDiskPtr;
+  unsigned long exactWeightsDiskPtr;
+  bool workInLevel;  // this is just a working variable,
+                     // it is not saved or read from the file
+  
+  IvfLevelInfo ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const IvfLevelInfo &l);
+
+
+typedef map<UCArray, IvfLevelInfo, LTUCArray> IvfLevelInfoMap;
+
+
+class FIvfLevel {
+public:
+  UCArray docTag;
+  UCArray indexLevel;    // empty for word level index
+  IvfLevelInfoMap levelInfo;
+  
+  FIvfLevel ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const FIvfLevel &l);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FText.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FText.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FText.cpp	(revision 16561)
@@ -0,0 +1,249 @@
+/**************************************************************************
+ *
+ * FText.cpp -- File structures for text compression
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "FText.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+BitAddr::BitAddr () {
+  Clear ();  
+}
+
+void BitAddr::Clear () {
+  byte = 0;
+  bit = 0;
+}
+
+bool BitAddr::Read (FILE *f) {
+  return (ReadUL (f, byte) && ReadUC (f, bit));
+}
+
+bool BitAddr::Write (FILE *f) const {
+  return (WriteUL (f, byte) && WriteUC (f, bit));
+}
+
+#define BITADDRLEN (sizeof(unsigned long) + sizeof(unsigned char))
+
+  
+TextLevelInfo::TextLevelInfo () {
+  Clear();
+}
+
+void TextLevelInfo::Clear () {
+  levelTag.erase (levelTag.begin(), levelTag.end());
+  textIdxPtr = 0;
+  numEntries = 0;
+}
+
+bool TextLevelInfo::Read (FILE *f) {
+  return (ReadUCArray (f, levelTag) &&
+	  ReadUL (f, textIdxPtr) &&
+	  ReadUL (f, numEntries));
+}
+
+bool TextLevelInfo::Write (FILE *f) const {
+  return (WriteUCArray (f, levelTag) &&
+	  WriteUL (f, textIdxPtr) &&
+	  WriteUL (f, numEntries));
+}
+
+ostream &operator<<(ostream &s, const TextLevelInfo &l) {
+  s << "  Tag: \"" << l.levelTag << "\"\n"
+    << "  textIdxPtr: " << l.textIdxPtr << "\n"
+    << "  numEntries: " << l.numEntries << "\n\n";
+  
+  return s;
+}
+
+
+FTextLevel::FTextLevel () {
+  Clear();
+}
+
+void FTextLevel::Clear () {
+  levelInfo.erase (levelInfo.begin(), levelInfo.end());
+}
+
+bool FTextLevel::Read (FILE *f) {
+  // read in the array size
+  unsigned long arrSize = 0;
+  if (!ReadVarLenUL (f, arrSize)) return false;
+
+  TextLevelInfo thisLevel;
+  while (arrSize > 0) {
+    // read and insert the next level information
+    if (!thisLevel.Read (f)) return false;
+    levelInfo[thisLevel.levelTag] = thisLevel;
+    
+    --arrSize;
+  }
+
+  return true;
+}
+
+bool FTextLevel::Write (FILE *f) const {
+  // write out the array size
+  if (!WriteVarLenUL (f, levelInfo.size())) return false;
+
+  // write out each level info
+  TextLevelInfoMap::const_iterator here = levelInfo.begin();
+  TextLevelInfoMap::const_iterator end = levelInfo.end();
+  while (here != end) {
+    if (!(*here).second.Write (f)) return false;
+    
+    ++here;
+  }
+
+  return true;
+}
+
+ostream &operator<<(ostream &s, const FTextLevel &l) {
+  TextLevelInfoMap::const_iterator here = l.levelInfo.begin ();
+  TextLevelInfoMap::const_iterator end = l.levelInfo.end ();
+  while (here != end) {
+    s << (*here).second;
+    ++here;
+  }
+  
+  return s;
+}
+
+
+TextIdx::TextIdx () {
+  Clear ();
+}
+
+void TextIdx::Clear () {
+  start.Clear();
+  end.Clear();
+  which = 0;
+}
+
+#define TEXTIDXLEN (BITADDRLEN*2 + sizeof(char))
+
+bool TextIdx::Read (FILE *f, const TextLevelInfo &levelInfo,
+		    unsigned long docNum) {
+  if (!SeekTextIdx (f, levelInfo, docNum)) return false;
+  return Read (f);
+}
+
+bool TextIdx::Read (FILE *f) {
+  return (start.Read (f) && end.Read (f) && ReadUC (f, which));
+}
+
+bool TextIdx::Write (FILE *f) const {
+  return (start.Write (f) && end.Write (f) && WriteUC (f, which));
+}
+
+ostream &operator<<(ostream &s, const TextIdx &t) {
+  s << "start byte: " << t.start.byte << "\n";
+  s << "start bit:  " << (int)t.start.bit << "\n";
+  s << "end byte:   " << t.end.byte << "\n";
+  s << "end bit:    " << (int)t.end.bit << "\n";
+  s << "which:      " << (int)t.which << "\n\n";
+  
+  return s;
+}
+
+
+bool SeekTextIdx (FILE *f, const TextLevelInfo &levelInfo,
+		  unsigned long docNum) {
+  if (docNum == 0 || docNum > levelInfo.numEntries) return false;
+  
+  unsigned long seekPos = levelInfo.textIdxPtr + (docNum-1) * TEXTIDXLEN;
+  if (fseek (f, seekPos, SEEK_SET) != 0) return false;
+  
+  return true;
+}
+
+
+bool ReadTextIdxArray (FILE *f, TextIdxArray &a, unsigned long arrSize) {
+  // clear the array
+  a.erase (a.begin(), a.end());
+
+  // read in each element
+  TextIdx idx;
+  while (arrSize > 0) {
+    if (!idx.Read(f)) return false;
+    a.push_back (idx);
+    --arrSize;
+  }
+
+  return true;
+}
+
+bool WriteTextIdxArray (FILE *f, const TextIdxArray &a) {
+  TextIdxArray::const_iterator here = a.begin();
+  TextIdxArray::const_iterator end = a.end();
+  while (here != end) {
+    if (!(*here).Write(f)) return false;
+    ++here;
+  }
+  
+  return true;
+}
+
+
+CompressTextInfo::CompressTextInfo () {
+  Clear();
+}
+
+void CompressTextInfo::Clear () {
+  ResetStart();
+  docPtrs.erase (docPtrs.begin(), docPtrs.end());
+}
+
+void CompressTextInfo::ResetStart () {
+  inDoc = false;
+  start.Clear();
+  which = 0;
+}
+  
+void CompressTextInfo::SetStart (unsigned long startPos,
+				 unsigned char startBit,
+				 unsigned char startWhich) {
+  // place an imaginary end tag if needed
+  // note: the document tag always needs a closing tag
+  if (inDoc) SetEnd (startPos, startBit);
+
+  // remember start of level
+  inDoc = true;
+  start.byte = startPos;
+  start.bit = startBit;
+  which = startWhich;
+}
+
+void CompressTextInfo::SetEnd (unsigned long endPos,
+			       unsigned char endBit) {
+  if (inDoc) {
+    // add completed entry to list of ptrs
+    TextIdx textIdx;
+    textIdx.start = start;
+    textIdx.end.byte = endPos;
+    textIdx.end.bit = endBit;
+    textIdx.which = which;
+    docPtrs.push_back (textIdx);
+    
+    // reset this entry
+    ResetStart();
+  }
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FText.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FText.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FText.h	(revision 16561)
@@ -0,0 +1,150 @@
+/**************************************************************************
+ *
+ * FText.h -- File structures for text compression
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef FTEXT_H
+#define FTEXT_H
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+#include <stdio.h>
+#include "UCArray.h"
+
+
+// holds a bit address
+class BitAddr {
+public:
+  unsigned long byte;
+  unsigned char bit;
+
+  BitAddr ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+
+class TextLevelInfo {
+public:
+  UCArray levelTag;
+  unsigned long textIdxPtr;
+  unsigned long numEntries;
+
+  TextLevelInfo ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const TextLevelInfo &l);
+
+
+typedef map<UCArray, TextLevelInfo, LTUCArray> TextLevelInfoMap;
+
+
+class FTextLevel {
+public:
+  TextLevelInfoMap levelInfo;
+  
+  FTextLevel ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const FTextLevel &l);
+
+
+// the text.idx file points into the compressed text
+class TextIdx {
+public:
+  BitAddr start;       // the first bit of the compressed text
+  BitAddr end;         // the first bit past the end of the text
+  unsigned char which; // 0 = non-word, 1 = word
+  
+  TextIdx ();
+  void Clear ();
+
+  // does a seek and reads the appropriate record
+  bool Read (FILE *f, const TextLevelInfo &levelInfo,
+	     unsigned long docNum);
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// stream output for debugging purposes
+ostream &operator<<(ostream &s, const TextIdx &t);
+
+
+// note: document numbers start at 1
+
+bool SeekTextIdx (FILE *f, const TextLevelInfo &levelInfo,
+		  unsigned long docNum);
+
+
+// used to store the pointers while compressing the text
+typedef vector<TextIdx> TextIdxArray;
+
+bool ReadTextIdxArray (FILE *f, TextIdxArray &a, unsigned long arrSize);
+bool WriteTextIdxArray (FILE *f, const TextIdxArray &a);
+
+
+// used to store information about a level while compressing the text
+class CompressTextInfo {
+public:
+  bool inDoc;
+  BitAddr start;
+  unsigned char which;
+  TextIdxArray docPtrs;
+
+  CompressTextInfo ();
+  void Clear ();
+
+  // clears inDoc, start, and which (not the docPtrs)
+  void ResetStart ();
+  
+  // set the start of a level (closing off the last opening
+  // tag if needed)
+  void SetStart (unsigned long startPos,
+		 unsigned char startBit,
+		 unsigned char startWhich);
+
+  // if in a document, it will set the end, add the document to the
+  // list of document ptrs, and then call ResetStart
+  void SetEnd (unsigned long endPos,
+	       unsigned char endBit);
+};
+
+typedef map<UCArray, CompressTextInfo, LTUCArray> CompressTextInfoMap;
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.cpp	(revision 16561)
@@ -0,0 +1,50 @@
+/**************************************************************************
+ *
+ * FileBuf.cpp -- File buffer
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "FileBuf.h"
+#include "sysfuncs.h"
+
+#ifdef __WIN32__
+# include <io.h>
+# define read _read
+#endif
+
+bool FileBuf::NextBuf () {
+  if (fd < 0) return false;
+  int numbytes = read (fd, buf, FILEBUFSIZE);
+  if (numbytes < 0) numbytes = 0;
+  
+  here = buf;
+  end = here + numbytes;
+
+  return (numbytes > 0);
+}
+
+FileBuf::FileBuf () {
+  here = end = buf;
+  fd = -1;
+}
+
+void FileBuf::SetFD (int _fd) {
+  here = end = buf;
+  fd = _fd;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FileBuf.h	(revision 16561)
@@ -0,0 +1,73 @@
+/**************************************************************************
+ *
+ * FileBuf.h -- File buffer
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef FILEBUF_H
+#define FILEBUF_H
+
+#define FILEBUFSIZE 65536
+
+// some elderly windows compilers need to include <vector> (or something
+// similar) here so that they know what a "bool" is
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#else
+#  include <vector>
+#endif
+
+
+class FileBuf {
+protected:
+  unsigned char buf[FILEBUFSIZE];
+  unsigned char *here;
+  unsigned char *end;
+
+  int fd;
+
+  // gets a new buffer if need be, returns true if
+  // more could be got. if true is returned the caller
+  // can assume that here != end
+  bool NextBuf ();
+  
+public:
+  FileBuf ();
+
+  void SetFD (int _fd);
+
+  inline bool Get (unsigned char &c) {
+    if (here != end || NextBuf()) { c = *here++; return true; }
+    return false;
+  }
+  
+  inline bool Peek (unsigned char &c) {
+    if (here != end || NextBuf()) { c = *here; return true; }
+    return false;
+  }
+
+  inline bool Next () {
+    if (here != end || NextBuf()) { ++here; return true; }
+    return false;
+  }
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.cpp	(revision 16561)
@@ -0,0 +1,105 @@
+/**************************************************************************
+ *
+ * FragLevelConvert.cpp -- converting between fragment and document numbers
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "FragLevelConvert.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+
+
+FragLevelConvert::FragLevelConvert () {
+  numFrags = 0;
+  numLevelDocs = 0;
+}
+
+bool FragLevelConvert::Read (FILE *invfFile,
+			     unsigned long levelPtr,
+			     unsigned long _numFrags,
+			     unsigned long _numLevelDocs) {
+  levelStarts.erase (levelStarts.begin(), levelStarts.end());
+  numFrags = _numFrags;
+  numLevelDocs = _numLevelDocs;
+
+  // seek to the appropriate place in the inverted file
+  fseek (invfFile, levelPtr, SEEK_SET);
+
+  stdio_bitio_buffer buffer(invfFile);
+  
+  unsigned long pTag = numLevelDocs*2;
+  unsigned long B = BIO_Bblock_Init (numFrags+pTag, pTag);
+  unsigned long fragNum = 0;
+  unsigned long i;
+  for (i=0; i<numLevelDocs; ++i) {
+    unsigned long delta = buffer.bblock_decode (B, NULL)-1;
+    fragNum += delta;
+
+    levelStarts.push_back (fragNum);
+
+    // ignore end, assume every fragment is in a level
+    delta = buffer.bblock_decode (B, NULL)-1;
+    fragNum += delta;
+  }
+
+  // fragNum would be the starting fragment of the next document
+  // if there was one
+  levelStarts.push_back (fragNum);
+  
+  if (fragNum != _numFrags) {
+    cerr << "last frag num:"<<fragNum<<" != num frags:"<<_numFrags<<endl;
+  } 
+  buffer.done();
+
+  return true;
+}
+
+bool FragLevelConvert::FragToLevel (unsigned long fragNum,
+				    unsigned long &levelDocNum) const {
+  // do binary search for something containing this fragment number
+  unsigned long low = 1, high = numLevelDocs;
+  unsigned long mid;
+
+  while ((mid = (low+high)/2) >= 1 && low <= high) {
+    if (fragNum > levelStarts[mid]) low = mid+1;
+    else if (fragNum <= levelStarts[mid-1]) high = mid-1;
+    else {
+      levelDocNum = mid;
+      return true;
+    }
+  }
+  return false;
+}
+
+
+bool FragLevelConvert::LevelToLevel (FragLevelConvert sectionLevelConvert,
+		 unsigned long levelNum, unsigned long &DocNum) {
+  
+  if (levelNum==1) {
+    DocNum=1;
+    return true;
+  }
+  unsigned long levelfragnum = sectionLevelConvert.levelStarts[levelNum-1]+1;
+
+  if (FragToLevel(levelfragnum, DocNum)) {
+    return true;
+  } 
+  return false;
+  
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/FragLevelConvert.h	(revision 16561)
@@ -0,0 +1,70 @@
+/**************************************************************************
+ *
+ * FragLevelConvert.h -- converting between fragment and document numbers
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef FRAGLEVELCONVERT_H
+#define FRAGLEVELCONVERT_H
+
+#include <stdio.h>
+#include "UCArray.h"
+
+
+/*Notes about levelStarts (kjm18) - this is my interpretation of
+whats going on, feel free to correct this if its wrong.
+
+say you have 5 docs, consisting of fragments:
+1:  1-55
+2: 56-83
+3: 84-106
+4: 107-152
+5: 153-211
+
+then levelStarts would have the values
+0  1  2   3   4   5 
+0  55 83 106 152 211
+
+*/
+class FragLevelConvert {
+protected:
+  // note fragment start is one before before real start
+  vector<unsigned long> levelStarts;
+  unsigned long numFrags;
+  unsigned long numLevelDocs;
+  
+public:
+  FragLevelConvert ();
+
+  bool Read (FILE *invfFile, unsigned long levelPtr,
+	     unsigned long _numFrags, unsigned long _numLevelDocs);
+
+  bool FragToLevel (unsigned long fragNum, unsigned long &levelDocNum) const;
+
+  // this function converts from one level number to another
+  // the FragLevelConvert thats calling the method should be loaded with
+  // the level that you want to convert to, while the sectionLevelConvert
+  // should be loaded with the level converting from.
+  // level Num has the old level, DocNum will be returned with the new
+  // coresponding level
+  bool LevelToLevel (FragLevelConvert sectionLevelConvert,
+		   unsigned long levelNum, unsigned long &DocNum);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.cpp	(revision 16561)
@@ -0,0 +1,279 @@
+/**************************************************************************
+ *
+ * GSDLQueryLex.cpp -- Lexical analyser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "GSDLQueryLex.h"
+#include "unitool.h"
+#include "words.h"
+
+inline void AddNChar (UCArray::const_iterator &here,
+		      UCArray &text,
+		      int len) {
+  if (text.capacity() < text.size() + len + 1) {
+    text.reserve(text.size() + len + 1);
+  }
+  while (len > 0) {
+    text.push_back (*here++);
+    --len;
+  }
+}
+
+static bool ParseInteger (UCArray::const_iterator &here,
+			  UCArray::const_iterator end,
+			  LexEl &el) {
+  el.Clear();
+
+  // this version of end is used in unitool
+  //  UCArray::const_iterator endMinus1 = end-1;
+  const unsigned char* endMinus1 = &*(end - 1);
+
+  int charLen;
+  unsigned short c; // one character lookahead
+  charLen = parse_utf8_char (&*here, endMinus1, &c);
+
+  // check for positive or negative
+  bool neg = false;
+  if (c == '+') {
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  } else if (c == '-') {
+    neg = true;
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  }
+
+  // read in number part
+  int numeric=0;
+  el.num = 0;
+  el.lexType = IntegerE;
+  /* stop integers at 4 digits */
+  while (c >= '0' && c <= '9'&& ++numeric<=MAXNUMERIC) {
+    el.num = el.num*10 + c - '0';
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  }
+
+  if (neg) el.num *= -1;
+
+  return (!el.text.empty());
+}
+
+static bool ParsePotentialInteger(UCArray::const_iterator &here,
+				  UCArray::const_iterator end,
+				  LexEl &el) {
+  el.Clear();
+
+  // this version of end is used in unitool
+  //UCArray::const_iterator endMinus1 = end-1;
+  const unsigned char* endMinus1 = &*(end - 1);
+
+  int charLen=0;
+  int length=0;
+  unsigned short c; // one character lookahead
+  charLen = parse_utf8_char (&*here, endMinus1, &c);
+
+  // read in number part
+  int numeric=0;
+  el.num = 0;
+  el.lexType = IntegerE;
+  /* stop integers at 4 digits */
+  while (c >= '0' && c <= '9'&& ++numeric<=MAXNUMERIC) {
+    el.num = el.num*10 + c - '0';
+    AddNChar (here, el.text, charLen);
+    length += charLen;
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  }
+  // check the next character -if it is a letter, then have a term, not an integer
+  if (!is_unicode_letter(c)) {
+    // this was just an integer
+    return (!el.text.empty());
+  }
+  // else its a term
+  el.lexType = TermE;
+  el.num = 0;
+  /* this bit taken from ParseIndexWord in words.h*/
+  while (length+charLen<=MAXSTEMLEN && charLen > 0 &&
+	 (is_unicode_letter(c) || (is_unicode_digit(c) &&
+				   ++numeric <= MAXNUMERIC))) {
+    AddNChar (here, el.text, charLen);
+    length += charLen;
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  }
+
+  return (!el.text.empty());
+}
+static bool ParseTerm (UCArray::const_iterator &here,
+		       UCArray::const_iterator end,
+		       UCArray &text) {
+  if (here == end)
+    return false;
+
+  //UCArray::const_iterator endMinus1 = end-1;
+  const unsigned char* endMinus1 = &*(end - 1);
+  const unsigned char* new_here = ParseIndexWord (&*here, endMinus1, text);
+  here += (new_here - &*here); // advance iterator by number of chars advanced
+  return !text.empty();
+}
+
+
+bool ParseLexEl (UCArray::const_iterator &here,
+		 UCArray::const_iterator end,
+		 LexEl &el) {
+  el.Clear();
+
+  // strange things can happen if here == end == 0
+  if (here == end)
+    return false;
+  
+  // this version of end is used in unitool
+  //UCArray::const_iterator endMinus1 = end-1;
+  const unsigned char* endMinus1 = &*(end - 1);
+
+  // ignore all white space
+  int charLen;
+  unsigned short c; // one character lookahead
+  charLen = parse_utf8_char (&*here, endMinus1, &c);
+  while (here != end && is_unicode_space (c)) {
+    here += charLen;
+    charLen = parse_utf8_char (&*here, endMinus1, &c);
+  }
+  if (here == end) return false;
+
+  if (c == '(') {
+    el.lexType = OpenBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == ')') {
+    el.lexType = CloseBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+
+  } else if (c =='[') {
+    el.lexType = OpenSquareBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+  
+  } else if (c ==']') {
+    el.lexType = CloseSquareBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+  
+  } else if (c == '\"') {
+    el.lexType = QuoteE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '/') {
+    el.lexType = TermWeightE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '#') {
+    el.lexType = StemMethodE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '*') {
+    el.lexType = StarE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '^') {
+    el.lexType = RangeE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '@') {
+    el.lexType = AtE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == ':') {
+    el.lexType = TagE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c=='&') {
+    el.lexType = AndOpE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '|') {
+    el.lexType = OrOpE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '!') {
+    el.lexType = NotOpE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '+' || c == '-' ) {
+    return  ParseInteger (here, end, el);
+  }
+
+  else if (c >= '0' && c <= '9') {
+    return ParsePotentialInteger (here, end, el);
+  }
+
+  // assume it is a term of some sort
+  if (!ParseTerm (here, end, el.text))  {
+    // parse term returns false if it hasn't parsed anything that is a term
+    // here should be the same as it was before 
+    el.lexType = UnknownE;
+    AddNChar (here, el.text, charLen);
+    return true;
+  }
+  //return false;
+
+  //UCArray AND; SetCStr (AND, "AND");
+  //if (el.text == AND) {
+  if (UCArrayCStrEquals(el.text, "AND")) {
+    el.lexType = AndOpE;
+    return true;
+  }
+  //UCArray OR; SetCStr (OR, "OR");
+  //if (el.text == OR) {
+  if (UCArrayCStrEquals(el.text, "OR")) {
+    el.lexType = OrOpE;
+    return true;
+  }
+  //UCArray NOT; SetCStr (NOT, "NOT");
+  //if (el.text == NOT) {
+  if (UCArrayCStrEquals(el.text, "NOT")) {
+    el.lexType = NotOpE;
+    return true;
+  }
+  UCArray NEAR; SetCStr (NEAR, "NEAR", 4);
+  if (PrefixLen(el.text, NEAR)==4) {
+    el.lexType = NearOpE;
+    return true;
+  }
+  UCArray WITHIN; SetCStr (WITHIN, "WITHIN", 6);
+  if (PrefixLen(el.text, WITHIN)==6) {
+    el.lexType = WithinOpE;
+    return true;
+  }
+  
+  el.lexType = TermE;
+  return true;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryLex.h	(revision 16561)
@@ -0,0 +1,64 @@
+/**************************************************************************
+ *
+ * GSDLQueryLex.h -- Lexical analyser for a simple query language, for GSDL
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef GSDLQUERYLEX_H
+#define GSDLQUERYLEX_H
+
+#include "UCArray.h"
+
+
+// note: white space is ignored
+enum LexType {NoTypeE, OpenBracketE, CloseBracketE, TermE, AndOpE, OrOpE,
+	      NotOpE, NearOpE, WithinOpE, QuoteE, IntegerE, TermWeightE, 
+	      StemMethodE, RangeE, AtE, TagE, OpenSquareBracketE, 
+	      CloseSquareBracketE, StarE, UnknownE};
+
+struct LexEl {
+  LexType lexType;
+  UCArray text;
+  int num; // for IntegerE
+
+  void Clear () {
+    lexType = NoTypeE;
+    UCArrayClear (text);
+    num = 0;
+  }
+  LexEl () { Clear (); }
+};
+
+
+// returns true if another lexical element could be parsed.
+bool ParseLexEl (UCArray::const_iterator &here,
+		 UCArray::const_iterator end,
+		 LexEl &el);
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.cpp	(revision 16561)
@@ -0,0 +1,534 @@
+/**************************************************************************
+ *
+ * QueryParser.cpp -- Query parser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "GSDLQueryParser.h"
+#include "GSDLQueryLex.h"
+#include "words.h"
+
+static QueryNode *ParseExpression (UCArray::const_iterator &here,
+				   UCArray::const_iterator end, 
+				   int defaultBoolCombine,
+				   int defaultStemMethod);
+
+static QueryNode *AndAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  AndQueryNode *andNode = new AndQueryNode;
+  andNode->leftNode = t1;
+  andNode->rightNode = t2;
+  return andNode;
+}
+
+static QueryNode *OrAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  OrQueryNode *orNode = new OrQueryNode;
+  orNode->leftNode = t1;
+  orNode->rightNode = t2;
+  return orNode;
+}
+
+static QueryNode *NotAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  NotQueryNode *notNode = new NotQueryNode;
+  notNode->queryNode = t1;
+  notNode->notNode = t2;
+  return notNode;
+}
+
+// expects the opening bracket to have already been parsed
+// and discarded
+static QueryNode *ParseBracketExpression (UCArray::const_iterator &here,
+					  UCArray::const_iterator end, 
+					  int defaultBoolCombine,
+					  int defaultStemMethod) {
+  // get everything in the expression
+  QueryNode *curTree = ParseExpression (here, end, defaultBoolCombine,
+					defaultStemMethod);
+  
+  // gobble up tokens until a closing bracket is found
+  // or the end of the string
+  LexEl el;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == CloseBracketE) break;
+  }
+
+  return curTree;
+}
+
+static int ParseInt (UCArray::const_iterator &here,
+		     UCArray::const_iterator end) {
+  LexEl el;
+  UCArray::const_iterator oldHere = here;
+  if (ParseLexEl (here, end, el) && el.lexType == IntegerE)
+    return el.num;
+  
+  here = oldHere; // not an integer
+  return 0;
+}
+
+// default is within 20 words
+static void SetRangeValues (TermNode &termNode,
+			    UCArray &nearby,
+			    bool reverse) {
+  UCArray NEARBY; SetCStr(NEARBY, "NEAR", 4);
+  UCArray WITHIN; SetCStr(WITHIN, "WITHIN", 6);
+  
+  if (nearby == NEARBY) { // no modifier
+    termNode.startRange = (NEAR_DEFAULT+1)*-1;
+    termNode.endRange = NEAR_DEFAULT;
+
+  } else if (nearby == WITHIN) { // no modifier
+    if (reverse) {
+      termNode.startRange = (NEAR_DEFAULT+1)*-1;
+      termNode.endRange = -1;
+    } else {
+      termNode.startRange = NEAR_DEFAULT;
+      termNode.endRange = 0;
+    }
+  }
+  else { // extract number
+    UCArray::const_iterator here;
+    bool within = false;
+    if (PrefixLen(nearby, WITHIN)==6) {
+      within=true;
+      here = nearby.begin()+6;
+    } else {
+      here = nearby.begin()+4;
+    }
+    UCArray::const_iterator end = nearby.end();
+    int size=0;
+    while (here != end) {
+      size = size*10 + (*here-'0');
+      ++here;
+    }
+    if (within) {
+      if (reverse) {
+	termNode.startRange = size;
+	termNode.endRange = 0;
+      } else {
+	termNode.startRange = -1 * (size+1);
+	termNode.endRange = -1;
+      }
+    } else {
+      termNode.startRange = -1 * (size+1);
+      termNode.endRange = size;
+    }
+  }
+}
+
+static unsigned long GetStemMethod(LexEl &el, int defaultStemMethod) {
+  // here expect el to contain some of c,s,i,u,f,a -- see mg_files.h CHAR_FLAG_STEM_* constants
+  unsigned long stem = (unsigned long)defaultStemMethod;
+      
+  UCArray::const_iterator here = el.text.begin();
+  UCArray::const_iterator end = el.text.end();
+
+  /* [JFG - Mar 06: Accent folding patch] */
+  /* Changed to use CHAR_FLAG_STEM* constants from mg_files.h */
+  while(here != end) {
+    unsigned char ch = *here; 
+    if (strchr (CHAR_FLAG_STEM_Validator, ch) == NULL)
+      return STEM_INVALID; // incorrect format
+    
+    switch(ch) {
+    case CHAR_FLAG_STEM_CaseFold:		// ignore case (fold)
+      stem |= STEM_CaseFolding;
+      break;
+    case CHAR_FLAG_STEM_NoCaseFold:		// case sensitive 
+      stem &= (~STEM_CaseFolding);
+      break;
+    case CHAR_FLAG_STEM_Stemming:		// stem words
+      stem |= STEM_Stemming;
+      break;
+    case CHAR_FLAG_STEM_NoStemming:		// do not stem words
+      stem &= (~STEM_Stemming);
+      break;
+#ifdef ENABLE_ACCENTFOLD 
+   case CHAR_FLAG_STEM_AccentFold:		// accent fold
+      stem |= STEM_AccentFolding;
+      break;
+    case CHAR_FLAG_STEM_NoAccentFold:	// do no accent folding
+      stem &= (~STEM_AccentFolding);
+      break;
+#endif
+    };
+    
+    ++here;	    
+  }
+  return stem;
+}
+  
+
+static void ParseTermModifiers (UCArray::const_iterator &here,
+				UCArray::const_iterator end,
+				TermNode &termNode,
+				int defaultStemMethod) {
+ 
+  termNode.stemMethod = defaultStemMethod;
+  bool partial_match = false;
+  LexEl el;
+  UCArray::const_iterator oldHere = here;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == TermWeightE) {
+      termNode.termWeight = ParseInt (here, end);
+      
+    } else if (el.lexType == StemMethodE) {
+      oldHere = here;
+      LexEl stem;
+      if (ParseLexEl (here, end, stem) && stem.lexType == TermE) {
+	termNode.stemMethod = GetStemMethod(stem, defaultStemMethod);
+    /* [JFG - Mar 06: Accent folding patch] */
+    /* use STEM_INVALID instead of hardcoded 4 */
+	if (termNode.stemMethod == STEM_INVALID) { // error so backtrack
+	  here = oldHere;
+	  termNode.stemMethod = (unsigned long)defaultStemMethod;
+	} 
+      } else here = oldHere; //ignore - wrong syntax
+
+    } else if (el.lexType == RangeE) {
+      termNode.startRange = ParseInt (here, end);
+      termNode.endRange = ParseInt (here, end);
+      
+    } else if (el.lexType == AtE) {
+      termNode.startRange = termNode.endRange = ParseInt (here, end);
+    } else if (el.lexType == StarE) {
+      partial_match = true;
+    } else {
+      // no term modifiers
+      here = oldHere;
+      break;
+    }
+    
+    if (partial_match) {
+      /* [JFG - Mar 06: Accent folding patch] */
+      /* use STEM_PARTIAL_MATCH flag */
+      termNode.stemMethod |= STEM_PARTIAL_MATCH; // set partial match flag
+      termNode.stemMethod &= (~STEM_Stemming); // we dont have stemming on if doing partial matching.
+      termNode.stemMethod &= (~STEM_AccentFolding); // we dont have accentfolding on if doing partial matching.
+    }
+    oldHere = here;
+  }  
+}
+
+static void ParseProxModifiers (UCArray::const_iterator &here,
+				UCArray::const_iterator end,
+				ProxMatchQueryNode *proxNode) {
+  // so far only have one - the tag stuff
+  LexEl el;
+  UCArray::const_iterator oldHere = here;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == TagE) {
+      oldHere = here; // don't backtrack past here
+      if (ParseLexEl (here, end, el) && el.lexType == TermE) {
+	proxNode->tagNodePtr = new TagNode;
+	proxNode->tagNodePtr->tagName = el.text;
+	
+      }
+      else { // error in tag
+	here = oldHere;
+      }
+    } // TagE
+    // add in other cases here
+    else {
+      // no modifiers
+      here = oldHere;
+      break;
+    }
+    oldHere = here;
+  }//while
+
+
+}
+
+// expects starting brackets to have been parsed
+// sets error to true if something has gone wrong
+static ProxMatchQueryNode *ParseSquareBrackets(UCArray::const_iterator &here,
+				UCArray::const_iterator end,
+				/*ProxMatchQueryNode *proxNode,*/
+				int defaultStemMethod,
+				bool & error) {
+
+  ProxMatchQueryNode *proxNode = new ProxMatchQueryNode;
+  LexEl el;
+  bool phrase=false;
+  bool first=true;
+  bool prox = false;
+  UCArray near_string;
+  while (ParseLexEl (here, end, el)) {
+    // cant have AND, OR, NOT in square brackets, so assume they are words
+    if (el.lexType == TermE || el.lexType == IntegerE || el.lexType == AndOpE || el.lexType == OrOpE || el.lexType == NotOpE) {
+      TermNode termNode;
+      termNode.term = el.text;
+      ParseTermModifiers (here, end, termNode, defaultStemMethod);
+      if (phrase) {
+        if (first) first=false;
+        else {
+          termNode.startRange = -2;
+          termNode.endRange = -1;
+        }
+      } else if (prox) {
+	SetRangeValues(termNode, near_string, false);
+	prox = false;
+      }
+      proxNode->terms.push_back(termNode);
+    }
+    else if (el.lexType == CloseSquareBracketE) {
+      break;
+    }
+    else if (el.lexType == QuoteE) {
+      // phrase inside square brackets
+      if (phrase) { // end of phrase
+	phrase=false;
+	first = true;
+      } else {
+	phrase=true; // start of phrase
+      }
+    } else if (el.lexType == NearOpE || el.lexType == WithinOpE) {
+      if (phrase) {
+	// cant have proximity  op in a phrase - just assume its an actual word
+	TermNode termNode;
+	termNode.term = el.text;
+	ParseTermModifiers (here, end, termNode, defaultStemMethod);
+	proxNode->terms.push_back(termNode);
+      } else {
+	// its a NEAR or within op
+	prox = true;
+	near_string = el.text;
+      }
+	  
+    } 
+    else if (el.lexType == UnknownE) {
+      // just ignore it 
+    }
+    else {
+      //error - we set the proxNode to NULL,
+      cerr <<"GSDLQueryParser: bad syntax inside []\n";
+      error = true;
+      return NULL;
+    }
+  } // while
+  return proxNode;
+}
+// expects the starting quote to have been parsed
+// and discarded
+// now phrases use the case and stem preference options
+// ie can search for a phrase ignoring case
+static void ParsePhrase (UCArray::const_iterator &here,
+			 UCArray::const_iterator end,
+			 ProxMatchQueryNode &proxNode, 
+			 int defaultStemMethod,
+			 bool &error) {
+  LexEl el;
+  bool first = true;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == TermE || el.lexType == IntegerE) {
+      TermNode termNode;
+      termNode.term = el.text;
+      //termNode.stemMethod = defaultStemMethod;
+      ParseTermModifiers (here, end, termNode, defaultStemMethod);
+      if (first) {
+	first = false;
+      }
+      else {
+	termNode.startRange = -2;
+	termNode.endRange = -1;
+      }
+      proxNode.terms.push_back (termNode);
+      
+    } else if (el.lexType == QuoteE) {
+      break;
+      
+    } else if (el.lexType == UnknownE) {
+      // just ignore it
+    } else {
+      // error 
+      error = true;
+      return;
+    }
+  }
+}
+
+static QueryNode *ParseTerm (UCArray::const_iterator &here,
+			     UCArray::const_iterator end,
+			     int defaultBoolCombine,
+			     int defaultStemMethod) {
+  LexEl el;
+
+  UCArray::const_iterator oldHere = here;
+  if (!ParseLexEl (here, end, el)) return NULL;
+
+  if (el.lexType == OpenBracketE)
+    return ParseBracketExpression (here, end, defaultBoolCombine,
+				   defaultStemMethod);
+
+  ProxMatchQueryNode *proxNode = new ProxMatchQueryNode;
+
+  if (el.lexType == TermE || el.lexType == IntegerE) {
+    TermNode termNode;
+    termNode.term = el.text;
+    ParseTermModifiers (here, end, termNode, defaultStemMethod);
+    oldHere = here;  // dont backtrack past here
+    if (ParseLexEl(here, end, el) && (el.lexType == NearOpE || el.lexType == WithinOpE )) {
+	delete proxNode;
+	oldHere = here;
+      // this is calling ParseTerm again, but only a subset of the things accepted by ParseTerm are appropriate here. add in some hacks to avoid segmentation faults - kjdon, 04/2003
+      
+      // if the next element is a '(' have a syntax error, return NULL 
+      LexEl temp_el;
+      if (ParseLexEl(here, end, temp_el) && temp_el.lexType == OpenBracketE) {
+	cerr << "GSDLQueryParser: NEAR/WITHIN cannot be followed by a '('\n";
+	return NULL;
+      }
+      here = oldHere; // else backtrack
+	
+      proxNode = (ProxMatchQueryNode *)ParseTerm(here, end, defaultBoolCombine,
+						 defaultStemMethod);
+      SetRangeValues(termNode, el.text, true);
+      proxNode->terms.push_back (termNode);
+      return proxNode;
+      
+    } else {
+      here = oldHere; // backtrack
+      proxNode->terms.push_back (termNode);
+      ParseProxModifiers(here, end, proxNode);
+      return proxNode;
+    }
+  } else if (el.lexType == QuoteE) {
+    bool error = false;
+    ParsePhrase (here, end, *proxNode, defaultStemMethod, error);
+    if (error) {
+      delete proxNode;
+      return NULL;
+    }
+    return proxNode;
+  }
+  else if (el.lexType == OpenSquareBracketE) {
+    bool error = false;
+    proxNode = ParseSquareBrackets (here, end, /*proxNode, */defaultStemMethod, error);
+    if (error) {
+      delete proxNode;
+      return NULL;
+    }
+    ParseProxModifiers (here, end, proxNode);
+    return proxNode;
+  }
+
+  // not a term
+  here = oldHere;
+  delete proxNode;
+  return NULL;
+}
+
+
+static QueryNode *ParseExpression (UCArray::const_iterator &here,
+				   UCArray::const_iterator end,
+				   int defaultBoolCombine,
+				   int defaultStemMethod) {
+  LexEl el;
+  QueryNode *curTree = NULL;
+  UCArray::const_iterator oldHere = here;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == CloseBracketE) {
+      // parsebracketexpression is waiting for the last bracket, so put it back
+      here = oldHere;
+      break;
+      
+    } else if (el.lexType == OpenSquareBracketE ||
+	       el.lexType == OpenBracketE ||
+	       el.lexType == TermE ||
+	       el.lexType == QuoteE ||
+	       el.lexType == IntegerE ) { 
+      
+      // some type of term, back track and parse it
+      here = oldHere;
+
+      //  parse the term
+      QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine, 
+				       defaultStemMethod);
+      if (newTerm == NULL) {
+	delete curTree;
+	return NULL;
+      }
+
+      // if default==1, AND, else if==0, OR
+      if (defaultBoolCombine) {
+	curTree = AndAdd (curTree, newTerm);
+      }
+      else {
+	curTree = OrAdd (curTree, newTerm); 
+      }
+      
+    } else if (el.lexType == AndOpE) {
+      QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
+				       defaultStemMethod);
+      if (newTerm == NULL) {
+	delete curTree;
+	return NULL;
+      }
+      curTree = AndAdd (curTree, newTerm);
+      
+    } else if (el.lexType == OrOpE) {
+      QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
+				       defaultStemMethod);
+      if (newTerm == NULL) {
+	delete curTree;
+	return NULL;
+      }
+      curTree = OrAdd (curTree, newTerm);
+      
+    } else if (el.lexType == NotOpE) {
+      QueryNode * newTerm = ParseTerm (here, end, defaultBoolCombine,
+				       defaultStemMethod);
+      if (newTerm == NULL) {
+	delete curTree;
+	return NULL;
+      }
+      curTree = NotAdd (curTree, newTerm); 
+      
+    } else if (el.lexType == UnknownE) {
+      // just ignore it 
+    } else {
+
+      // syntax error, return NUll
+      delete curTree;
+      return NULL;
+    }
+
+    oldHere = here;
+  }
+
+  return curTree;
+}
+
+QueryNode *ParseQuery (const UCArray &queryStr, int defaultBoolCombine,
+		       int defaultStemMethod, int maxnumeric) {
+  if (4 < maxnumeric && maxnumeric < 512) {
+    MAXNUMERIC = maxnumeric;
+  }
+  UCArray::const_iterator here = queryStr.begin();
+  UCArray::const_iterator end = queryStr.end();
+  return ParseExpression (here, end, defaultBoolCombine, defaultStemMethod);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/GSDLQueryParser.h	(revision 16561)
@@ -0,0 +1,39 @@
+/**************************************************************************
+ *
+ * QueryParser.h -- Query parser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef GSDLQUERYPARSER_H
+#define GSDLQUERYPARSER_H
+
+#include "UCArray.h"
+#include "MGQuery.h"
+
+#define NEAR_DEFAULT 20
+
+// returns NULL if the query could not be parsed
+// defaultBoolCOmbine determines if individual terms are combined using
+// AND or OR in the absence of specified operators.
+// defaultStemMethod used to set stemming and casefolding for terms where
+// its not set explicitly in the query string. THe defaultStemMethod value
+// should be set using the values from the preferences page
+QueryNode *ParseQuery (const UCArray &queryStr, int defaultBoolCombine,
+		       int defaultStemMethod, int maxnumeric);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.cpp	(revision 16561)
@@ -0,0 +1,204 @@
+/**************************************************************************
+ *
+ * IndexData.cpp -- Information needed for querying
+ * Copyright (C) 1999 Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "IndexData.h"
+#include "string.h"
+#include "mg_files.h"
+
+IndexData::IndexData () {
+  basePath[0] = '\0';
+  filename[0] = '\0';
+
+  dictFile = NULL;
+
+  /* [JFG - Mar 06: Accent folding patch] */
+  for(int i=STEM_MIN;i <= STEM_MAX;i++) 
+    stemFile[i-1] = NULL;
+
+  invfFile = NULL;
+
+  approxWeightsFile = NULL;
+  exactWeightsFile = NULL;
+
+  curLevelNum = 0;
+}
+
+IndexData::~IndexData () {
+  UnloadData ();
+}
+
+bool IndexData::LoadData (const char *_basePath, const char *_filename) {
+  if (_filename[0] == '\0') return false;
+  
+  // make sure this data has not already been loaded
+  if (strcmp (_basePath, basePath) == 0 &&
+      strcmp (_filename, filename) == 0) return true;
+  
+  // make sure there is nothing else loaded
+  UnloadData ();
+
+  strcpy (basePath, _basePath);
+  strcpy (filename, _filename);
+
+  set_basepath (basePath);
+
+  // load in the level information
+  FILE *ivfLevelFile;
+  ivfLevelFile = open_file (filename, INVF_LEVEL_SUFFIX, "rb",
+			    MAGIC_INVF_LEVELS, MG_ABORT);
+  levels.Read (ivfLevelFile);
+  fclose (ivfLevelFile);
+
+  // blocked dictionary
+  dictFile = open_file (filename, INVF_DICT_BLOCKED_SUFFIX, "rb",
+			MAGIC_STEM, MG_ABORT);
+  if (!bdh.Read (dictFile)) { UnloadData (); return false; }
+
+  fseek (dictFile, bdh.wblk_idx_start, SEEK_SET);
+  if (!ReadBlockIdx (dictFile, biWords)) { UnloadData (); return false; }
+
+  fseek (dictFile, bdh.tblk_idx_start, SEEK_SET);
+  if (!ReadBlockIdx (dictFile, biTags)) { UnloadData (); return false; }
+
+  /* [JFG - Mar 06: Accent folding patch] */
+  // read stem indexes
+  // [KJD - optional stemming patch]
+  // allow no stem indexes
+  for(int stem = STEM_MIN; stem <= STEM_MAX; stem++) { 
+    char *suffix = make_suffix (INVF_DICT_BLOCKED_SUFFIX_PAT, stem, NULL);
+    stemFile[stem-1] = open_file (filename, suffix,
+				  "rb", MAGIC_STEM_GEN(stem + '0'), MG_MESSAGE);
+    if (stemFile[stem-1]!= NULL) {
+      if (!sih[stem-1].Read (stemFile[stem-1])) { 
+	fclose (stemFile[stem-1]);
+	stemFile[stem-1] = NULL;
+	//UnloadData (); return false; 
+      }
+      
+      fseek (stemFile[stem-1], sih[stem-1].block_idx_start, SEEK_SET);
+      if (!ReadBlockIdx (stemFile[stem-1], sii[stem-1])) { 
+	fclose (stemFile[stem-1]);
+	stemFile[stem-1] = NULL;
+	//UnloadData (); return false; 
+      }
+    }
+  }
+  
+  
+  // inverted file
+  invfFile = open_file (filename, INVF_SUFFIX, "rb",
+			MAGIC_INVF, MG_ABORT);
+  ifh.Read (invfFile);
+
+  // weights
+  approxWeightsFile = open_file (filename, APPROX_WEIGHTS_SUFFIX, "rb",
+				 MAGIC_WGHT_APPROX, MG_ABORT);
+  exactWeightsFile = open_file (filename, WEIGHTS_SUFFIX, "rb",
+				MAGIC_WGHT, MG_ABORT);
+
+  return true;
+}
+
+bool IndexData::UnloadData () {
+  basePath[0] = '\0';
+  filename[0] = '\0';
+
+  if (dictFile != NULL) {
+    fclose (dictFile); dictFile = NULL;
+  }
+
+  for(int i=STEM_MIN;i <= STEM_MAX;i++) {
+    if (stemFile[i-1] != NULL) {
+      fclose (stemFile[i-1]); stemFile[i-1] = NULL;
+    }
+  }
+
+  if (invfFile != NULL) {
+    fclose (invfFile); invfFile = NULL;
+  }
+
+  if (approxWeightsFile != NULL) {
+    fclose (approxWeightsFile); approxWeightsFile = NULL;
+  }
+  if (exactWeightsFile != NULL) {
+    fclose (exactWeightsFile); exactWeightsFile = NULL;
+  }
+
+  UnloadLevel();
+  
+  return true;
+}
+
+
+bool IndexData::LoadLevel (const UCArray &level) {
+  // make sure this level is not already loaded
+  if (level == curLevel) return true;
+
+  // unload any levels currently loaded
+  UnloadLevel();
+
+  // make sure the required files are open
+  if (dictFile == NULL || invfFile == NULL ||
+      approxWeightsFile == NULL || exactWeightsFile == NULL)
+    return false;
+  
+  // read in the information from the dictionary
+  block_dict_el tagEl;
+  unsigned long tagElNum;
+  if (!SearchBlockDictEl (dictFile, biTags, bdh.entries_per_tblk,
+			  bdh.tag_dict_size, level, tagEl, tagElNum))
+    return false;
+
+  // read in the level conversion information
+  if (!levelConverter.Read (invfFile, tagEl.invf_ptr,
+			    bdh.num_frags, tagEl.frag_occur))
+    return false;
+
+  // read in the approximate weights
+  if (!weightData.Read (approxWeightsFile,
+			levels.levelInfo[level].approxWeightsDiskPtr,
+			levels.levelInfo[level].numEntries))
+    return false;
+  
+  // find the level number
+  curLevelNum = 0;
+  IvfLevelInfoMap::const_iterator levelHere, levelEnd;
+  for (levelHere=levels.levelInfo.begin(), levelEnd=levels.levelInfo.end();
+       levelHere!=levelEnd && (*levelHere).first != level; ++levelHere)
+    ++curLevelNum;
+
+  // make sure we found the level
+  if (levelHere == levelEnd) return false;
+
+  curLevel = level;
+
+  return true;
+}
+
+bool IndexData::UnloadLevel () {
+  curLevel.erase (curLevel.begin(), curLevel.end());
+  curLevelNum = 0;
+  
+  weightData.Free ();
+    
+  return true;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/IndexData.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**************************************************************************
+ *
+ * IndexData.h -- Information needed for querying
+ * Copyright (C) 1999 Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef INDEXDATA_H
+#define INDEXDATA_H
+
+#include <stdio.h>
+#include "invf.h"
+#include "FIvfLevelInfo.h"
+#include "FragLevelConvert.h"
+#include "Weights.h"
+#include "stemmer.h" 
+
+class IndexData {
+public:
+  // general information
+  char basePath[FILENAME_MAX];
+  char filename[FILENAME_MAX];
+  FIvfLevel levels;
+  
+  // blocked dictionary
+  FILE *dictFile;
+  block_dict_header bdh;
+  block_idx biWords;
+  block_idx biTags;
+
+  /* [JFG - Mar 06: Accent folding patch] */
+  // stem indexes
+  FILE *stemFile[STEM_MAX]; 
+  stem_idx_header sih[STEM_MAX];
+  block_idx sii[STEM_MAX];
+ 
+  // inverted file
+  FILE *invfFile;
+  invf_file_header ifh;
+
+  // weights
+  FILE *approxWeightsFile;
+  FILE *exactWeightsFile;
+
+  // level specific information
+  UCArray curLevel; // last level that was loaded
+  unsigned long curLevelNum;
+  FragLevelConvert levelConverter;
+  ApproxWeightsData weightData;
+  
+  IndexData ();
+  ~IndexData ();
+
+  // LoadData will only load the data if it has not been loaded before
+  bool LoadData (const char *_basePath, const char *_filename);
+  bool UnloadData ();
+
+  bool LoadLevel (const UCArray &level);
+  bool UnloadLevel ();
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.cpp	(revision 16561)
@@ -0,0 +1,760 @@
+/**************************************************************************
+ *
+ * MGQuery.cpp -- Query related functions
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "MGQuery.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include "Terms.h"
+#include "QueryResultsSort.h"
+#include <assert.h>
+
+
+void PrintIndent (ostream &s, int indent) {
+  while (indent-- > 0) s << " ";
+}
+
+
+void PrintIndentText (ostream &s, char *text, int indent) {
+  PrintIndent (s, indent);
+  s << text;
+}
+
+void PrintNode (ostream &s, QueryNode *node, int indent) {
+  if (node == NULL) {
+    PrintIndentText (s, "NULL\n", indent);
+  } else {
+    node->Print (s, indent+2);
+  }
+}
+
+  
+
+QueryNode::QueryNode () {
+}
+
+QueryNode::~QueryNode () {
+}
+
+void QueryNode::Calculate (IndexData &/*indexData*/,
+			   const QueryInfo &/*queryInfo*/,
+			   QueryResult &result) const {
+  result.Clear();
+}
+
+void QueryNode::Free () {
+}
+
+void QueryNode::Print (ostream &/*s*/, int /*indent*/) const {
+}
+
+
+
+AndQueryNode::AndQueryNode () {
+  leftNode = NULL;
+  rightNode = NULL;
+}
+
+AndQueryNode::~AndQueryNode () {
+  Free ();
+}
+
+void AndQueryNode::Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+			      QueryResult &result) const {
+  result.Clear();
+
+  // an And between nothing and something is nothing...
+  if (leftNode == NULL || rightNode == NULL) return;
+  
+  // calculate the result from the left tree and the result
+  // from the right tree
+  QueryResult rightResult;
+  leftNode->Calculate (indexData, queryInfo, result);
+  rightNode->Calculate (indexData, queryInfo, rightResult);
+
+  // merge the results, this can be done in place as the results
+  // will always shrink with an And
+
+  bool haveAccum = (queryInfo.sortByRank || queryInfo.needRankInfo);
+  if (haveAccum && (result.ranks.size() != result.docs.size() ||
+		    rightResult.ranks.size() != rightResult.docs.size())) {
+    // shouldn't ever get here
+    haveAccum = false;
+    assert (0);
+  }
+
+  // combine document numbers and corresponding ranks
+  unsigned long leftI = 0;
+  unsigned long rightI = 0;
+  unsigned long outI = 0;
+  while (leftI < result.docs.size() &&
+	 rightI < rightResult.docs.size()) {
+    if (result.docs[leftI] < rightResult.docs[rightI]) {
+      ++leftI;
+    } else if (result.docs[leftI] > rightResult.docs[rightI]) {
+      ++rightI;
+    } else {
+      // the documents are equal
+      result.docs[outI] = result.docs[leftI];
+      if (haveAccum)
+	result.ranks[outI] = result.ranks[leftI] + rightResult.ranks[rightI];
+      ++leftI;
+      ++rightI;
+      ++outI;
+    }
+  }
+
+  // erase unused document numbers and ranks
+  result.docs.erase(result.docs.begin()+outI, result.docs.end());
+  if (haveAccum)
+    result.ranks.erase(result.ranks.begin()+outI, result.ranks.end());
+    
+  // combine term frequency information
+  if (queryInfo.needTermFreqs)
+    result.termFreqs.insert (result.termFreqs.end(),
+			     rightResult.termFreqs.begin(),
+			     rightResult.termFreqs.end());
+}
+
+void AndQueryNode::Free () {
+  if (leftNode != NULL) {
+    delete leftNode;
+    leftNode = NULL;
+  }
+  if (rightNode != NULL) {
+    delete rightNode;
+    rightNode = NULL;
+  }
+}
+
+void AndQueryNode::Print (ostream &s, int indent) const {
+  PrintIndentText (s, "leftNode:\n", indent);
+  PrintNode (s, leftNode, indent+2);
+  PrintIndentText (s, "AND\n", indent);
+  PrintIndentText (s, "rightNode:\n", indent);
+  PrintNode (s, rightNode, indent+2);
+}
+
+
+OrQueryNode::OrQueryNode () {
+  leftNode = NULL;
+  rightNode = NULL;
+}
+
+OrQueryNode::~OrQueryNode () {
+  Free ();
+}
+
+void OrQueryNode::Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+			      QueryResult &result) const {
+  result.Clear();
+  
+  // calculate the result from the left tree and the result
+  // from the right tree
+  QueryResult leftResult;
+  QueryResult rightResult;
+  if (leftNode != NULL)
+    leftNode->Calculate (indexData, queryInfo, leftResult);
+  if (rightNode != NULL)
+    rightNode->Calculate (indexData, queryInfo, rightResult);
+
+  // merge the results
+  
+  bool haveAccum = (queryInfo.sortByRank || queryInfo.needRankInfo);
+  if (haveAccum && (leftResult.ranks.size() != leftResult.docs.size() ||
+		    rightResult.ranks.size() != rightResult.docs.size())) {
+    // shouldn't ever get here
+    haveAccum = false;
+    assert (0);
+  }
+
+  // combine document numbers and corresponding ranks
+  unsigned long leftSize = leftResult.docs.size();
+  unsigned long rightSize = rightResult.docs.size();
+  unsigned long leftI = 0;
+  unsigned long rightI = 0;
+  unsigned long leftDocNum = 0;
+  unsigned long rightDocNum = 0;
+  while (leftI < leftSize || rightI < rightSize) {
+    // check leftI
+    if (leftI < leftResult.docs.size())
+      leftDocNum = leftResult.docs[leftI];
+    else leftDocNum = ULONG_MAX;
+    
+    // check rightI
+    if (rightI < rightResult.docs.size())
+      rightDocNum = rightResult.docs[rightI];
+    else rightDocNum = ULONG_MAX;
+    
+    // combine
+    if (leftDocNum < rightDocNum) {
+      result.docs.push_back (leftDocNum);
+      if (haveAccum)
+	result.ranks.push_back (leftResult.ranks[leftI]);
+      ++leftI;
+      
+    } else if (leftDocNum > rightDocNum) {
+      result.docs.push_back (rightDocNum);
+      if (haveAccum)
+	result.ranks.push_back (rightResult.ranks[rightI]);
+      ++rightI;
+      
+    } else { // equal
+      result.docs.push_back (leftDocNum);
+      if (haveAccum)
+	result.ranks.push_back (leftResult.ranks[leftI] +
+				rightResult.ranks[rightI]);
+      ++leftI;
+      ++rightI;
+    }
+  }
+
+  // combine term frequency information
+  if (queryInfo.needTermFreqs) {
+    result.termFreqs.insert (result.termFreqs.end(),
+			     leftResult.termFreqs.begin(),
+			     leftResult.termFreqs.end());
+    result.termFreqs.insert (result.termFreqs.end(),
+			     rightResult.termFreqs.begin(),
+			     rightResult.termFreqs.end());
+  }
+}
+
+void OrQueryNode::Free () {
+  if (leftNode != NULL) {
+    delete leftNode;
+    leftNode = NULL;
+  }
+  if (rightNode != NULL) {
+    delete rightNode;
+    rightNode = NULL;
+  }
+}
+
+void OrQueryNode::Print (ostream &s, int indent) const {
+  PrintIndentText (s, "leftNode:\n", indent);
+  PrintNode (s, leftNode, indent+2);
+  PrintIndentText (s, "OR\n", indent);
+  PrintIndentText (s, "rightNode:\n", indent);
+  PrintNode (s, rightNode, indent+2);
+}
+
+
+
+NotQueryNode::NotQueryNode () {
+  queryNode = NULL;
+  notNode = NULL;
+}
+
+NotQueryNode::~NotQueryNode () {
+  Free ();
+}
+
+void NotQueryNode::Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+			      QueryResult &result) const {
+  result.Clear();
+
+  // check for nothing
+  if (queryNode == NULL) return;
+  if (notNode == NULL) {
+    queryNode->Calculate (indexData, queryInfo, result);
+    return;
+  }
+  
+  // calculate the result from the query tree and the result
+  // from the not tree
+  QueryResult notResult;
+  queryNode->Calculate (indexData, queryInfo, result);
+  notNode->Calculate (indexData, queryInfo, notResult);
+
+  // merge the results, this can be done in place as the results
+  // will always shrink with a Not
+
+  bool haveAccum = (queryInfo.sortByRank || queryInfo.needRankInfo);
+  if (haveAccum && (result.ranks.size() != result.docs.size() ||
+		    notResult.ranks.size() != notResult.docs.size())) {
+    // shouldn't ever get here
+    haveAccum = false;
+    assert (0);
+  }
+
+  // combine document numbers and corresponding ranks
+  unsigned long queryI = 0;
+  unsigned long notI = 0;
+  unsigned long outI = 0;
+  while (queryI < result.docs.size() &&
+	 notI < notResult.docs.size()) {
+    if (result.docs[queryI] < notResult.docs[notI]) {
+      // found a document not in the notResults
+      result.docs[outI] = result.docs[queryI];
+      if (haveAccum)
+	result.ranks[outI] = result.ranks[queryI];
+      ++queryI;
+      ++outI;
+    } else if (result.docs[queryI] > notResult.docs[notI]) {
+      ++notI;
+    } else {
+      // the documents are equal, ignore both
+      ++queryI;
+      ++notI;
+    }
+  }
+
+  // erase unused document numbers and ranks
+  result.docs.erase(result.docs.begin()+outI, result.docs.end());
+  if (haveAccum)
+    result.ranks.erase(result.ranks.begin()+outI, result.ranks.end());
+  
+  // combine term frequency information
+  if (queryInfo.needTermFreqs)
+    result.termFreqs.insert (result.termFreqs.end(),
+			     notResult.termFreqs.begin(),
+			     notResult.termFreqs.end());
+}
+
+void NotQueryNode::Free () {
+  if (queryNode != NULL) {
+    delete queryNode;
+    queryNode = NULL;
+  }
+  if (notNode != NULL) {
+    delete notNode;
+    notNode = NULL;
+  }
+}
+
+void NotQueryNode::Print (ostream &s, int indent) const {
+  PrintIndentText (s, "queryNode:\n", indent);
+  PrintNode (s, queryNode, indent+2);
+  PrintIndentText (s, "NOT\n", indent);
+  PrintIndentText (s, "notNode:\n", indent);
+  PrintNode (s, notNode, indent+2);
+}
+
+
+void TagNode::Clear () {
+  UCArrayClear (tagName);
+}
+
+void TagNode::Calculate (IndexData &indexData,
+			 FragRangeArray &fragRange) const {
+  fragRange.erase (fragRange.begin(), fragRange.end());
+  if (tagName.empty()) return;
+
+  // get information about this tag
+  block_dict_el tagEl;
+  unsigned long tagElNum;
+  if (!SearchBlockDictEl (indexData.dictFile, indexData.biTags,
+			  indexData.bdh.entries_per_tblk,
+			  indexData.bdh.tag_dict_size,
+			  tagName, tagEl, tagElNum))
+    return;
+
+  // seek to the appropriate place in the inverted file
+  fseek (indexData.invfFile, tagEl.invf_ptr, SEEK_SET);
+
+  stdio_bitio_buffer buffer(indexData.invfFile);
+  
+  unsigned long pTag = tagEl.frag_occur*2;
+  unsigned long B = BIO_Bblock_Init (indexData.bdh.num_frags+pTag, pTag);
+  unsigned long fragNum = 0;
+  unsigned long i;
+  FragRange thisFrag;
+  for (i=0; i<tagEl.frag_occur; ++i) {
+    // get start
+    unsigned long delta = buffer.bblock_decode (B, NULL)-1;
+    fragNum += delta;
+
+    thisFrag.rangeStart = fragNum;
+
+    // get end
+    delta = buffer.bblock_decode (B, NULL)-1;
+    fragNum += delta;
+
+    thisFrag.rangeEnd = fragNum;
+    fragRange.push_back (thisFrag);
+  }
+
+  buffer.done();
+}
+
+void TagNode::Free () {
+  Clear ();
+}
+
+void TagNode::Print (ostream &s, int indent) const {
+  PrintIndent (s, indent);
+  s << "TAG: \"" << tagName << "\"\n";
+}
+
+
+void TermNode::Clear () {
+  UCArrayClear (term);
+  termWeight = 1;
+  stemMethod = 0;
+  startRange = NO_TERM_RANGE_START;
+  endRange = NO_TERM_RANGE_END;
+}
+
+TermNode::TermNode () {
+  Clear ();
+}
+
+void TermNode::Calculate (IndexData &indexData,
+			  bool needFragFreqs,
+			  FragRangeArray *fragLimits,
+			  FragData &fragData,
+			  UCArrayVector &equivTerms) const {
+  fragData.Clear ();
+  equivTerms.erase(equivTerms.begin(), equivTerms.end());
+
+  // get a list of term numbers
+  vector<unsigned long> equivNums;
+  FindWordNumbers (indexData, term, stemMethod, equivNums);
+
+  // get the information for each word and merge it with
+  // previous results
+  FragData tempFragData1;
+  FragData tempFragData2;
+  UCArray equivWord;
+  vector<unsigned long>::iterator here = equivNums.begin();
+  vector<unsigned long>::iterator end = equivNums.end();
+  while (here != end) {
+    // get the information for this word
+    ReadTermFragData (indexData, needFragFreqs, *here,
+		      tempFragData1, fragLimits, equivWord);
+    equivTerms.push_back(equivWord);
+    // combine with last results
+    tempFragData2 = fragData;
+    CombineFragData (needFragFreqs, tempFragData1, tempFragData2, fragData);
+    
+    ++here;
+  }
+}
+
+void TermNode::Free () {
+  Clear ();
+}
+
+void TermNode::Print (ostream &s, int indent) const {
+  PrintIndent (s, indent);
+  s << "TERM: \"" << term << "\"\n";
+  PrintIndent (s, indent+2);
+  s << "termWeight: " << termWeight << "\n";
+  PrintIndent (s, indent+2);
+  s << "stemMethod: " << stemMethod << "\n";
+  PrintIndent (s, indent+2);
+  s << "startRange: " << startRange << "\n";
+  PrintIndent (s, indent+2);
+  s << "endRange: " << endRange << "\n";
+}
+
+
+ProxMatchQueryNode::ProxMatchQueryNode () {
+  tagNodePtr = NULL;
+}
+
+ProxMatchQueryNode::~ProxMatchQueryNode () {
+  Free ();
+}
+
+void ProxMatchQueryNode::Calculate (IndexData &indexData,
+				    const QueryInfo &queryInfo,
+				    QueryResult &result) const {
+  result.Clear ();
+
+  bool needFragFreqs = (queryInfo.sortByRank || queryInfo.needRankInfo);
+
+  // read in the tag if needed
+  FragRangeArray fragLimits;
+  FragRangeArray *fragLimitsPtr = NULL;
+  if (tagNodePtr == NULL && terms.size() > 1) {
+    // multiple terms must be compared relative to some tag
+    // otherwise phrase matches could span documents
+    TagNode tempTagNode;
+    tempTagNode.tagName = indexData.curLevel;
+    tempTagNode.Calculate (indexData, fragLimits);
+    fragLimitsPtr = &fragLimits;
+    
+  } else if (tagNodePtr != NULL) {
+    (*tagNodePtr).Calculate (indexData, fragLimits);
+    fragLimitsPtr = &fragLimits;
+  }
+
+  UCArray tagOrLevel = indexData.curLevel;
+  if (tagNodePtr != NULL) tagOrLevel = (*tagNodePtr).tagName;
+
+  // read in the first term
+  FragData termData;
+  UCArrayVector equivTerms;
+  TermNodeArray::const_iterator termHere=terms.begin(), termEnd = terms.end();
+  if (termHere != termEnd) {
+    (*termHere).Calculate (indexData, needFragFreqs, fragLimitsPtr, 
+			   termData, equivTerms);
+
+    // convert initial fragment information
+    FragsToQueryResult (indexData,
+			queryInfo,
+			termData,
+			tagOrLevel,
+			(*termHere).term,
+			(*termHere).stemMethod,
+			(*termHere).termWeight,
+			equivTerms,
+			result);
+  
+    ++termHere;
+
+    if (termHere == termEnd) return; // nothing more to do
+  }
+
+  // read and combine the rest of the terms
+  FragData comTermData;
+  while (termHere != termEnd) {
+    (*termHere).Calculate (indexData, needFragFreqs,
+			   fragLimitsPtr, comTermData, equivTerms);
+
+    AndFragsToQueryResult (indexData,
+			   queryInfo,
+			   comTermData,
+			   tagOrLevel,
+			   (*termHere).term,
+			   (*termHere).stemMethod,
+			   (*termHere).termWeight,
+			   equivTerms,
+			   result);
+    
+    AndCombineFragData (needFragFreqs, termData, comTermData,
+			(*termHere).startRange,
+			(*termHere).endRange,
+			fragLimitsPtr);
+    ++termHere;
+  }
+
+  // remove unwanted document numbers
+  RemoveUnwantedResults (indexData,
+			 queryInfo,
+			 termData,
+			 result);
+}
+
+void ProxMatchQueryNode::Free () {
+  if (tagNodePtr != NULL) {
+    delete tagNodePtr;
+    tagNodePtr = NULL;
+  }
+  terms.erase (terms.begin(), terms.end());
+}
+
+void ProxMatchQueryNode::Print (ostream &s, int indent) const {
+  PrintIndentText (s, "PROXMATCH\n", indent);
+  if (tagNodePtr != NULL) tagNodePtr->Print (s, indent+2);
+  
+  TermNodeArray::const_iterator here = terms.begin();
+  TermNodeArray::const_iterator end = terms.end();
+  while (here != end) {
+    (*here).Print (s, indent+2);
+    ++here;
+  }
+}
+
+
+void BrowseQueryNode::Clear () {
+  UCArrayClear(term);
+}
+
+void BrowseQueryNode::Calculate (IndexData &indexData, BrowseQueryResult &result) const {
+
+  unsigned long number=0;
+  FindNearestWordNumber(indexData, term, number);
+  if (number + startPosition > 0 ) {
+    number = number+startPosition;
+  }
+  else {
+    number = 1;
+  }
+
+  GetTermList (indexData, number, numTerms, result.termFreqs);
+
+}
+
+
+
+void BrowseQueryNode::Free () {
+  Clear();
+}
+
+
+void BrowseQueryNode::Print (ostream &s, int indent) const {
+  PrintIndentText(s, "BROWSEQUERYNODE\n", indent);
+  PrintIndent (s, indent+2);
+  s << "TERM:"<<term<<"\n";
+  PrintIndent (s, indent+2);
+  s << "Start position: "<< startPosition<<", Num terms: "<< numTerms<<"\n";
+
+
+}
+
+
+
+void MGQuery (IndexData &indexData,
+	      const QueryInfo &queryInfo,
+	      const QueryNode *queryTree,
+	      QueryResult &result) {
+  result.Clear ();
+
+  // make sure level is current
+  if (!indexData.LoadLevel (queryInfo.docLevel)) {
+    return;
+  }
+  // do query
+  if (queryTree == NULL) return;
+  
+  (*queryTree).Calculate (indexData, queryInfo, result);
+
+  // make weights into ranks if needed
+  unsigned long i;
+  if (queryInfo.sortByRank || queryInfo.needRankInfo) {
+    for (i=0; i<result.ranks.size(); ++i) {
+      result.ranks[i] /=
+	indexData.weightData.GetLowerApproxDocWeight (result.docs[i]);
+    }
+  }
+  
+  unsigned long resultsSize = queryInfo.maxDocs;
+  if (resultsSize == 0 || resultsSize > result.docs.size())
+    resultsSize = result.docs.size();
+
+  result.actualNumDocs = result.docs.size(); // the total number of docs 
+                                   //returned, before pruning based on maxDocs
+
+  // sort results by rank if needed
+  GTRank gtRank;
+  if (queryInfo.sortByRank) {
+    // need in ascending order for SelectAddHeap
+    MakeHeap (result.docs.begin(), result.ranks.begin(),
+	      resultsSize, gtRank);
+    SelectAddHeap (result.docs.begin(), result.ranks.begin(), resultsSize,
+		   result.docs.begin()+resultsSize,
+		   result.ranks.begin()+resultsSize,
+		   (unsigned long) result.ranks.size()-resultsSize,
+		   gtRank);
+
+    // sort into descending order
+    SortHeap (result.docs.begin(), result.ranks.begin(), resultsSize, gtRank);
+  }
+  
+  // get exact weights if needed
+  if (queryInfo.exactWeights &&
+      (queryInfo.sortByRank || queryInfo.needRankInfo)) {
+    unsigned long exactDiskPtr =
+      indexData.levels.levelInfo[indexData.curLevel].exactWeightsDiskPtr;
+    
+    for (i=0; i<resultsSize; ++i) {
+      result.ranks[i] =  result.ranks[i] *
+	indexData.weightData.GetLowerApproxDocWeight (result.docs[i]) /
+	GetExactDocWeight (indexData.exactWeightsFile, exactDiskPtr,
+			   result.docs[i]);
+    }
+
+    // re-sort top candidates based on exact weights
+    if (queryInfo.sortByRank) {
+      MakeHeap (result.docs.begin(), result.ranks.begin(),
+		resultsSize, gtRank);
+      SortHeap (result.docs.begin(), result.ranks.begin(),
+		resultsSize, gtRank);
+    }
+  }
+
+  // get rid of unwanted ranking results
+  if (result.ranks.empty()) {
+    // do nothing
+  } else if (!queryInfo.needRankInfo) {
+    result.ranks.erase(result.ranks.begin(), result.ranks.end());
+  } else {
+    result.ranks.erase(result.ranks.begin()+resultsSize, result.ranks.end());
+  }
+
+  // remove extra docs that are unwanted
+  result.docs.erase(result.docs.begin()+resultsSize, result.docs.end());
+}
+
+// new MGQuery to retrieve doc and section nums
+// this will return doc nums for the level queried at (set in queryInfo)
+// in QueryResult.docs and if a second level is specified, 
+// it will return corresponding docnums for that level in QueryResult.levels
+// If there is no level specified, or that level is invalid, the query
+// level is used
+void MGQuery (IndexData &indexData,
+	      const QueryInfo &queryInfo,
+	      const QueryNode *queryTree,
+	      ExtQueryResult &realresult, UCArray &level) {
+  realresult.Clear ();
+  QueryResult result; // temp result
+
+  // do the normal query
+  MGQuery (indexData, queryInfo, queryTree, result);
+
+  // now that have the final result stuff, convert to ExtQueryResult,
+  // add in level nums if needed
+
+  realresult.docs = result.docs;
+  realresult.ranks = result.ranks;
+  realresult.termFreqs = result.termFreqs;
+  realresult.actualNumDocs = result.actualNumDocs;
+
+  if (queryInfo.docLevel == level || level.empty()) {
+    realresult.levels = result.docs;
+    return;
+  }
+
+  // else need to convert from queryInfo.docLevel to level
+
+  // the original level info
+  FragLevelConvert sectionLevelConverter = indexData.levelConverter;
+
+  // the new level info
+  if (!indexData.LoadLevel(level)) {
+    realresult.levels = result.docs;
+    return;
+  }
+  
+  unsigned long DocNum = 0;
+  
+  for (unsigned long i=0; i<realresult.docs.size(); ++i) {
+
+    // do an if ! here????
+    indexData.levelConverter.LevelToLevel(sectionLevelConverter, realresult.docs[i], DocNum);
+    realresult.levels.push_back(DocNum);
+  }
+  
+}
+
+
+
+// new function for full text browsing, 
+void MGBrowseQuery (IndexData &indexData, UCArray &level,
+		  const BrowseQueryNode &node,
+		  BrowseQueryResult &result) {
+
+  indexData.LoadLevel(level);
+  node.Calculate(indexData, result); 
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/MGQuery.h	(revision 16561)
@@ -0,0 +1,197 @@
+/**************************************************************************
+ *
+ * MGQuery.h -- Query related functions
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef MGQUERY_H
+#define MGQUERY_H
+
+#include "IndexData.h"
+#include <limits.h>
+#include "Terms.h"
+
+// forms base for defining and calculating the boolean
+// part of a query
+class QueryNode {
+public:
+  QueryNode ();
+  virtual ~QueryNode ();
+
+  virtual void Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+			  QueryResult &result) const;
+  virtual void Free ();
+  virtual void Print (ostream &s, int indent=0) const;
+};
+
+
+
+void PrintIndent (ostream &s, int indent);
+void PrintIndentText (ostream &s, char *text, int indent);
+void PrintNode (ostream &s, QueryNode *node, int indent=0);
+
+
+
+// basic boolean nodes
+
+class AndQueryNode : public QueryNode {
+public:
+  QueryNode *leftNode;
+  QueryNode *rightNode;
+  
+  AndQueryNode ();
+  ~AndQueryNode ();
+
+  void Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+		  QueryResult &result) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+class OrQueryNode : public QueryNode {
+public:
+  QueryNode *leftNode;
+  QueryNode *rightNode;
+  
+  OrQueryNode ();
+  ~OrQueryNode ();
+
+  void Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+		  QueryResult &result) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+class NotQueryNode : public QueryNode {
+public:
+  QueryNode *queryNode;
+  QueryNode *notNode;
+  
+  NotQueryNode ();
+  ~NotQueryNode ();
+
+  void Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+		  QueryResult &result) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+
+
+class TagNode {
+public:
+  UCArray tagName;
+
+  void Clear ();
+  TagNode () { Clear (); }
+
+  void Calculate (IndexData &indexData, FragRangeArray &fragRange) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+
+#define NO_TERM_RANGE_START (LONG_MIN/2)
+#define NO_TERM_RANGE_END   (LONG_MAX/2)
+
+/* NOTE: range stuff - the range values are for the previous term relative
+to the current term. So if searching for the phrase "the cat", 'the' doesn't 
+need range limits, but 'cat' has a range of -2 to -1. ie, if have found 'cat'
+then 'the' has to be at position between -2 and -1 relative to 'cat'.
+"the cat" could also be searched for by 'cat' with no range limits, then 'the' with range 0 to 1.
+range values are relative to the gaps between words:
+   x   y   z   X   a   b   c
+    -3   -2  -1  0   1   2   3
+
+ */
+class TermNode {
+public:
+  UCArray term;
+  unsigned long termWeight;
+  unsigned long stemMethod;
+  signed long startRange; // range relative to last term
+  signed long endRange;
+  
+  void Clear ();
+  TermNode ();
+
+  // fragLimits can be NULL if not neede
+  void Calculate (IndexData &indexData,
+		  bool needFragFreqs,
+		  FragRangeArray *fragLimits,
+		  FragData &fragData,
+		  UCArrayVector &equivTerms) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+typedef vector<TermNode> TermNodeArray;
+
+
+class ProxMatchQueryNode : public QueryNode {
+public:
+  TagNode *tagNodePtr; // optional field
+  TermNodeArray terms;
+  
+  ProxMatchQueryNode ();
+  ~ProxMatchQueryNode ();
+
+  void Calculate (IndexData &indexData, const QueryInfo &queryInfo,
+		  QueryResult &result) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+};
+
+class BrowseQueryNode :public QueryNode {
+ public:
+  UCArray term;
+  signed long startPosition;
+  unsigned long numTerms;
+
+  void Clear();
+  BrowseQueryNode () { Clear(); }
+  //  ~BrowseQueryNode ();
+
+  void Calculate (IndexData &indexData, BrowseQueryResult &result) const;
+  void Free ();
+  void Print (ostream &s, int indent=0) const;
+
+
+
+
+};
+
+void MGQuery (IndexData &indexData,
+	      const QueryInfo &queryInfo,
+	      const QueryNode *queryTree,
+	      QueryResult &result);
+
+// this function for retrieving results with both section doc nums
+// and Document docnums
+void MGQuery (IndexData &indexData,
+	      const QueryInfo &queryInfo,
+	      const QueryNode *queryTree,
+	      ExtQueryResult &result, UCArray &level);
+
+
+// new function for full text browsing, 
+void MGBrowseQuery (IndexData &indexData, UCArray &level,
+		  const BrowseQueryNode &node,
+		  BrowseQueryResult &result);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Makefile.in	(revision 16561)
@@ -0,0 +1,331 @@
+###########################################################################
+#
+# Makefile.in -- Makefile for the MGPP system
+# Copyright (C) 1994  Neil Sharman;  1995 Tim Shimmin
+#
+# 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.
+#
+#       @(#)Makefile.in	1.10 22 Mar 1994
+#
+###########################################################################
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+UNAC_DIR = @UNAC_DIR@
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+AFLIBS = $(UNAC_DIR)/libunac.a 
+AFINCLUDES = -I$(UNAC_DIR)
+AFFLAGS = -DENABLE_ACCENTFOLD
+else
+AFLIBS = 
+AFINCLUDES =
+AFFLAGS = 
+endif
+
+
+SHELL = /bin/sh
+VPATH = @srcdir@
+exec_prefix = @exec_prefix@
+prefix = @prefix@
+bindir = @bindir@
+srcdir = @srcdir@
+libdir = @libdir@
+subdir = text
+top_srcdir = @top_srcdir@
+transform = @program_transform_name@
+
+AR = ar
+CC = @CC@
+CXX = @CXX@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@ -ansi -fPIC -DSILENT -DSHORT_SUFFIX $(AFFLAGS)
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@ -fPIC -DSILENT -DSHORT_SUFFIX $(AFFLAGS)
+DEFS = @DEFS@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = ../lib/libmgpplib.a $(AFLIBS) -lstdc++ @LIBS@
+TOUCH = echo timestamp >
+AWK = @AWK@
+GSDLOS = @gsdlos@
+
+datadir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+localedir = $(datadir)/locale
+mandir = $(prefix)/man/man1
+
+COMPILE = $(CC) -c $(DEFS) $(TREC_MODE) $(READLINE) $(INCLUDES) $(CFLAGS)
+CXXCOMPILE = $(CXX) -c $(DEFS) $(TREC_MODE) $(READLINE) $(INCLUDES) $(CXXFLAGS)
+LINK = $(CXX) $(LDFLAGS) -o $@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $<
+
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+
+.SUFFIXES: ._c ._o
+.c._c:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $@
+._c._o:
+	@echo $(COMPILE) $<
+	@rm -f _$*.c
+	@ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+.c._o:
+	./ansi2knr $< > $*.tmp && mv $*.tmp $*._c
+	@echo $(COMPILE) $*._c
+	@rm -f _$*.c
+	@ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+INCLUDES = -I.. -I../lib -I. $(AFINCLUDES)
+
+#############################################################################
+#
+# Uncomment (and possibly edit) the following lines if you wish
+# to use the GNU readline library for the input in mgquery
+# 
+#READLINE = -DGNU_READLINE -I/opt/gnu/include/readline -L/opt/gnu/lib
+#READLINE_LIBS = -lreadline -ltermcap
+#
+#
+#############################################################################
+
+SOURCES = \
+  FIvfLevelInfo.cpp        ivf.pass1.cpp            mgpp_invf_dump.cpp       \
+  FText.cpp                ivf.pass2.cpp            mgpp_passes.cpp          \
+  FileBuf.cpp              locallib.cpp             mgpp_perf_hash_build.cpp \
+  FragLevelConvert.cpp     mg.special.cpp           mgpp_stem_idx.cpp        \
+  IndexData.cpp            mgpp_compression_dict.cpp  mgpp_weights_build.cpp   \
+  TagInfo.cpp              mgpp_decompress_text.cpp   stemmer.cpp            \
+  TextEl.cpp               mg_errors.cpp            text.cpp               \
+  TextGet.cpp              mgpp_fast_comp_dict.cpp    text.pass1.cpp         \
+  UCArray.cpp              mg_files.cpp             text.pass2.cpp         \
+  comp_dict.cpp            mgpp_invf_dict.cpp         words.cpp              \
+  invf.cpp                 mgpp_invf_dict_dump.cpp    Weights.cpp            \
+  MGQuery.cpp              Terms.cpp                QueryTester.cpp        \
+  QueryLex.cpp             QueryParser.cpp         \
+  GSDLQueryLex.cpp	   GSDLQueryParser.cpp      Queryer.cpp \
+	mgpp_passes_4jni.cpp
+
+
+HEADERS = \
+  FIvfLevelInfo.h     TextEl.h            hash.h              stemmer.h  \
+  FText.h             TextGet.h           invf.h              text.h     \
+  FileBuf.h           UCArray.h           locallib.h          words.h    \
+  FragLevelConvert.h  WordData.h          mg.h                           \
+  IndexData.h         build.h             mg_errors.h                    \
+  TagInfo.h           comp_dict.h         mg_files.h          Weights.h  \
+  MGQuery.h           Terms.h             QueryLex.h          QueryParser.h \
+  GSDLQueryLex.h	GSDLQueryParser.h  mgpp_passes_4jni.h
+
+
+MISC          = Makefile.in 
+
+ALLFILES      =	$(SOURCES) $(HEADERS) $(MISC) $(MAN)
+
+DISTFILES     = $(ALLFILES) 
+
+EXEC          = mgpp_passes mgpp_decompress_text mgpp_perf_hash_build \
+                mgpp_invf_dump mgpp_weights_build mgpp_invf_dict mgpp_stem_idx \
+                mgpp_invf_dict_dump mgpp_compression_dict mgpp_fast_comp_dict \
+                QueryTester Queryer
+
+MAN           =  mgpp_weights_build.1  mgpp_invf_dict.1 \
+                 mgpp_passes.1 mgpp_perf_hash_build.1 \
+                 mgpp_compression_dict.1  mgpp_fast_comp_dict.1 \
+                 mgpp_stem_idx.1
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+all:	$(EXEC) libmgpptext.a libmgpppass.a
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) $(CFLAGS) -- $(SOURCES)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+
+PASSES_OBJS = \
+	mgpp_passes$o text.pass1$o comp_dict$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o invf$o FileBuf$o TextEl$o UCArray$o TagInfo$o \
+        FText$o text$o FIvfLevelInfo$o
+
+mgpp_passes: $(PASSES_OBJS)
+	$(LINK) $(PASSES_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+QUERYER_OBJS = Queryer$o mg_files$o mg_errors$o locallib$o invf$o UCArray$o \
+               FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+               IndexData$o stemmer$o Weights$o TextGet$o text$o FText$o \
+               GSDLQueryParser$o GSDLQueryLex$o words$o
+
+Queryer: $(QUERYER_OBJS)
+	$(LINK) $(QUERYER_OBJS) $(LIBS)
+
+QUERY_TESTER_OBJS = QueryTester$o mg_files$o invf$o UCArray$o \
+                    FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+                    IndexData$o stemmer$o Weights$o
+
+QueryTester: $(QUERY_TESTER_OBJS)
+	$(LINK) $(QUERY_TESTER_OBJS) $(LIBS)
+
+WEIGHTS_BUILD_OBJS = mgpp_weights_build$o mg_files$o invf$o UCArray$o \
+                     FIvfLevelInfo$o FragLevelConvert$o
+
+mgpp_weights_build: $(WEIGHTS_BUILD_OBJS)
+	$(LINK) $(WEIGHTS_BUILD_OBJS) $(LIBS)
+
+COMP_DICT_OBJS = mgpp_compression_dict$o mg_files$o locallib$o
+
+mgpp_compression_dict: $(COMP_DICT_OBJS)
+	$(LINK) $(COMP_DICT_OBJS) $(LIBS)
+
+FAST_COMP_OBJS = mgpp_fast_comp_dict$o mg_files$o locallib$o
+
+mgpp_fast_comp_dict: $(FAST_COMP_OBJS)
+	$(LINK) $(FAST_COMP_OBJS) $(LIBS)
+
+INVF_DUMP_OBJS = mgpp_invf_dump$o locallib$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dump: $(INVF_DUMP_OBJS)
+	$(LINK) $(INVF_DUMP_OBJS) $(LIBS)
+
+INVF_DICT_OBJS = mgpp_invf_dict$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dict: $(INVF_DICT_OBJS)
+	$(LINK) $(INVF_DICT_OBJS) $(LIBS)
+
+INVF_DICT_DUMP_OBJS = mgpp_invf_dict_dump$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dict_dump: $(INVF_DICT_DUMP_OBJS)
+	$(LINK) $(INVF_DICT_DUMP_OBJS) $(LIBS)
+
+PERF_HASH_OBJS = mgpp_perf_hash_build$o mg_files$o invf$o UCArray$o
+
+mgpp_perf_hash_build: $(PERF_HASH_OBJS)
+	$(LINK) $(PERF_HASH_OBJS) $(LIBS)
+
+STEM_IDX_OBJS = mgpp_stem_idx$o mg_files$o stemmer$o \
+                invf$o UCArray$o
+
+mgpp_stem_idx: $(STEM_IDX_OBJS)
+	$(LINK) $(STEM_IDX_OBJS) $(LIBS)
+
+DECOMPRESS_TEXT_OBJS = \
+                mgpp_decompress_text$o mg_files$o mg_errors$o \
+                locallib$o text$o UCArray$o TextGet$o FText$o
+
+mgpp_decompress_text: $(DECOMPRESS_TEXT_OBJS)
+	$(LINK) $(DECOMPRESS_TEXT_OBJS) $(LIBS)
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+ansi2knr: ansi2knr.o
+	$(LINK) ansi2knr.o $(LIBS)
+
+install: execinstall
+uninstall: execuninstall
+
+execinstall: $(EXEC)
+	../mkinstalldirs $(bindir); \
+	for name in $(EXEC); do \
+	  $(INSTALL_PROGRAM) $$name \
+	  $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done 
+
+execuninstall:
+	for name in $(EXEC); do \
+	 rm -f $(bindir)/`echo $$name | sed '$(transform)'`; \
+	done
+
+maninstall: $(MAN)
+	$(top_srcdir)/mkinstalldirs $(mandir); \
+	for f in $(MAN); do \
+	  $(INSTALL_DATA) $$f $(mandir)/$$f ;\
+	done
+
+manuninstall: $(MAN)
+	for f in $(MAN); do \
+	  rm -f $(mandir)/$$f ;\
+	done
+
+LIB_OBJS = mg_files$o  mg_errors$o locallib$o invf$o UCArray$o \
+           FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+           IndexData$o stemmer$o Weights$o TextGet$o text$o FText$o \
+           GSDLQueryParser$o GSDLQueryLex$o words$o
+
+libmgpptext.a: $(LIB_OBJS)
+	rm -f libmgpptext.a
+	$(AR) cru libmgpptext.a $(LIB_OBJS)
+	$(RANLIB) libmgpptext.a
+
+JNI_LIB_OBJS = \
+	mgpp_passes_4jni$o text.pass1$o comp_dict$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o invf$o FileBuf$o TextEl$o UCArray$o TagInfo$o \
+        FText$o text$o FIvfLevelInfo$o
+
+libmgpppass.a: $(JNI_LIB_OBJS)
+	rm -f libmgpppass.a
+	$(AR) cru libmgpppass.a $(JNI_LIB_OBJS)
+	$(RANLIB) libmgpppass.a
+
+mostlyclean:
+	rm -f *$o _*.c _*.o *._c *._o core core.*
+
+clean: mostlyclean
+	rm -f $(EXEC) libmgpptext.a libmgpppass.a
+
+distclean: clean
+	rm -f ansi2knr
+	rm -f Makefile
+
+maintainer-clean: distclean
+	@echo "This command is intended only for maintainers to use;"
+	@echo "rebuilding the deleted files may require special tools."
+
+dist: $(DISTFILES)
+	for file in $(DISTFILES); do \
+	  ln $(srcdir)/$$file $(distdir) 2> /dev/null \
+	  || cp -p $(srcdir)/$$file $(distdir); \
+	done
+
+movclean:
+	for file in $(SOURCES); do \
+          test = $$file; \
+          echo $("test.c"##*.c); \
+        done 
+
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exc
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.cpp	(revision 16561)
@@ -0,0 +1,175 @@
+/**************************************************************************
+ *
+ * QueryLex.cpp -- Lexical analyser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "QueryLex.h"
+#include "unitool.h"
+#include "words.h"
+
+inline void AddNChar (UCArray::const_iterator &here,
+		      UCArray &text,
+		      int len) {
+  if (text.capacity() < text.size() + len + 1) {
+    text.reserve(text.size + len + 1);
+  }
+  while (len > 0) {
+    text.push_back (*here++);
+    --len;
+  }
+}
+
+static bool ParseInteger (UCArray::const_iterator &here,
+			  UCArray::const_iterator end,
+			  LexEl &el) {
+  el.Clear();
+
+  // this version of end is used in unitool
+  UCArray::const_iterator endMinus1 = end-1;
+
+  int charLen;
+  unsigned short c; // one character lookahead
+  charLen = parse_utf8_char (here, endMinus1, &c);
+
+  // check for positive or negative
+  bool neg = false;
+  if (c == '+') {
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (here, endMinus1, &c);
+  } else if (c == '-') {
+    neg = true;
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (here, endMinus1, &c);
+  }
+
+  // read in number part
+  el.num = 0;
+  el.lexType = IntegerE;
+  while (c >= '0' && c <= '9') {
+    el.num = el.num*10 + c - '0';
+    AddNChar (here, el.text, charLen);
+    charLen = parse_utf8_char (here, endMinus1, &c);
+  }
+
+  if (neg) el.num *= -1;
+
+  return (!el.text.empty());
+}
+
+static bool ParseTerm (UCArray::const_iterator &here,
+		       UCArray::const_iterator end,
+		       UCArray &text) {
+  UCArray::const_iterator endMinus1 = end-1;
+  here = ParseIndexWord (here, endMinus1, text);
+  return !text.empty();
+}
+
+
+bool ParseLexEl (UCArray::const_iterator &here,
+		 UCArray::const_iterator end,
+		 LexEl &el) {
+  el.Clear();
+
+  // strange things can happen if here == end == 0
+  if (here == end) return false;
+  
+  // this version of end is used in unitool
+  UCArray::const_iterator endMinus1 = end-1;
+  
+  // ignore all white space
+  int charLen;
+  unsigned short c; // one character lookahead
+  charLen = parse_utf8_char (here, endMinus1, &c);
+  while (here != end && is_unicode_space (c)) {
+    here += charLen;
+    charLen = parse_utf8_char (here, endMinus1, &c);
+  }
+  if (here == end) return false;
+
+  if (c == '(') {
+    el.lexType = OpenBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == ')') {
+    el.lexType = CloseBracketE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '\"') {
+    el.lexType = QuoteE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '#') {
+    el.lexType = TermWeightE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '$') {
+    el.lexType = StemMethodE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '^') {
+    el.lexType = RangeE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '@') {
+    el.lexType = AtE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == ':') {
+    el.lexType = TagE;
+    AddNChar (here, el.text, charLen);
+    return true;
+    
+  } else if (c == '+' || c == '-' ||
+	     (c >= '0' && c <= '9')) {
+    return ParseInteger (here, end, el);
+  }
+
+  // assume it is a term of some sort
+  if (!ParseTerm (here, end, el.text)) return false;
+
+  //UCArray AND; SetCStr (AND, "AND");
+  //if (el.text == AND) {
+  if (UCArrayCStrEquals(el.text, "AND")) {  
+    el.lexType = AndOpE;
+    return true;
+  }
+  //UCArray OR; SetCStr (OR, "OR");
+  //if (el.text == OR) {
+  if (UCArrayCStrEquals(el.text, "OR")) {
+    el.lexType = OrOpE;
+    return true;
+  }
+  //UCArray NOT; SetCStr (NOT, "NOT");
+  //if (el.text == NOT) {
+  if (UCArrayCStrEquals(el.text, "NOT")) {
+    el.lexType = NotOpE;
+    return true;
+  }
+
+  el.lexType = TermE;
+  return true;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryLex.h	(revision 16561)
@@ -0,0 +1,53 @@
+/**************************************************************************
+ *
+ * QueryLex.h -- Lexical analyser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef QUERYLEX_H
+#define QUERYLEX_H
+
+#include "UCArray.h"
+
+
+// note: white space is ignored
+enum LexType {NoTypeE, OpenBracketE, CloseBracketE, TermE, AndOpE, OrOpE,
+	      NotOpE, QuoteE, IntegerE, TermWeightE, StemMethodE,
+	      RangeE, AtE, TagE};
+
+struct LexEl {
+  LexType lexType;
+  UCArray text;
+  int num; // for IntegerE
+
+  void Clear () {
+    lexType = NoTypeE;
+    UCArrayClear (text);
+    num = 0;
+  }
+  LexEl () { Clear (); }
+};
+
+
+// returns true if another lexical element could be parsed.
+bool ParseLexEl (UCArray::const_iterator &here,
+		 UCArray::const_iterator end,
+		 LexEl &el);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.cpp	(revision 16561)
@@ -0,0 +1,219 @@
+/**************************************************************************
+ *
+ * QueryParser.cpp -- Query parser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "QueryParser.h"
+#include "QueryLex.h"
+
+
+static QueryNode *ParseExpression (UCArray::const_iterator &here,
+				   UCArray::const_iterator end);
+
+static QueryNode *AndAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  AndQueryNode *andNode = new AndQueryNode;
+  andNode->leftNode = t1;
+  andNode->rightNode = t2;
+  return andNode;
+}
+
+static QueryNode *OrAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  OrQueryNode *orNode = new OrQueryNode;
+  orNode->leftNode = t1;
+  orNode->rightNode = t2;
+  return orNode;
+}
+
+static QueryNode *NotAdd (QueryNode *t1, QueryNode *t2) {
+  if (t1 == NULL) return t2;
+  if (t2 == NULL) return t1;
+
+  NotQueryNode *notNode = new NotQueryNode;
+  notNode->queryNode = t1;
+  notNode->notNode = t2;
+  return notNode;
+}
+
+// expects the opening bracket to have already been parsed
+// and discarded
+static QueryNode *ParseBracketExpression (UCArray::const_iterator &here,
+					  UCArray::const_iterator end) {
+  // get everything in the expression
+  QueryNode *curTree = ParseExpression (here, end);
+  
+  // gobble up tokens until a closing bracket is found
+  // or the end of the string
+  LexEl el;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == CloseBracketE) break;
+  }
+
+  return curTree;
+}
+
+static int ParseInt (UCArray::const_iterator &here,
+		     UCArray::const_iterator end) {
+  LexEl el;
+  UCArray::const_iterator oldHere = here;
+  if (ParseLexEl (here, end, el) && el.lexType == IntegerE)
+    return el.num;
+  
+  here = oldHere; // not an integer
+  return 0;
+}
+
+
+static void ParseTermModifiers (UCArray::const_iterator &here,
+				UCArray::const_iterator end,
+				TermNode &termNode) {
+  LexEl el;
+  UCArray::const_iterator oldHere = here;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == TermWeightE) {
+      termNode.termWeight = ParseInt (here, end);
+      
+    } else if (el.lexType == StemMethodE) {
+      termNode.stemMethod = ParseInt (here, end);
+      
+    } else if (el.lexType == RangeE) {
+      termNode.startRange = ParseInt (here, end);
+      termNode.endRange = ParseInt (here, end);
+      
+    } else if (el.lexType == AtE) {
+      termNode.startRange = termNode.endRange = ParseInt (here, end);
+      
+    } else {
+      // no term modifiers
+      here = oldHere;
+      break;
+    }
+    
+    oldHere = here;
+  }  
+}
+
+// expects the starting quote to have been parsed
+// and discarded
+static void ParsePhrase (UCArray::const_iterator &here,
+			 UCArray::const_iterator end,
+			 ProxMatchQueryNode &proxNode) {
+  LexEl el;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == TermE || el.lexType == IntegerE) {
+      TermNode termNode;
+      termNode.term = el.text;
+      ParseTermModifiers (here, end, termNode);
+      proxNode.terms.push_back (termNode);
+      
+    } else if (el.lexType == QuoteE) {
+      break;
+      
+    } else {
+      // error
+      break;
+    }
+  }
+}
+
+static QueryNode *ParseTerm (UCArray::const_iterator &here,
+			     UCArray::const_iterator end) {
+  LexEl el;
+
+  UCArray::const_iterator oldHere = here;
+  if (!ParseLexEl (here, end, el)) return NULL;
+
+  if (el.lexType == OpenBracketE)
+    return ParseBracketExpression (here, end);
+
+  ProxMatchQueryNode *proxNode = new ProxMatchQueryNode;
+
+  // check for a tag
+  if (el.lexType == TagE) {
+    oldHere = here; // don't backtrack past here
+    if (!ParseLexEl (here, end, el)) return NULL;
+    if (el.lexType == TermE) {
+      proxNode->tagNodePtr = new TagNode;
+      proxNode->tagNodePtr->tagName = el.text;
+      if (!ParseLexEl (here, end, el)) return NULL;
+    }
+  }
+  
+  if (el.lexType == TermE || el.lexType == IntegerE) {
+    TermNode termNode;
+    termNode.term = el.text;
+    ParseTermModifiers (here, end, termNode);
+    proxNode->terms.push_back (termNode);
+    return proxNode;
+    
+  } else if (el.lexType == QuoteE) {
+    ParsePhrase (here, end, *proxNode);
+    return proxNode;
+  }
+
+  // not a term
+  here = oldHere;
+  delete proxNode;
+  return NULL;
+}
+
+
+static QueryNode *ParseExpression (UCArray::const_iterator &here,
+				   UCArray::const_iterator end) {
+  LexEl el;
+  QueryNode *curTree = NULL;
+
+  UCArray::const_iterator oldHere = here;
+  while (ParseLexEl (here, end, el)) {
+    if (el.lexType == OpenBracketE ||
+	el.lexType == TermE ||
+	el.lexType == QuoteE ||
+	el.lexType == IntegerE ||
+	el.lexType == TagE) {
+      // some type of term, back track and parse it
+      here = oldHere;
+      curTree = OrAdd (curTree, ParseTerm (here, end));
+      
+    } else if (el.lexType == AndOpE) {
+      curTree = AndAdd (curTree, ParseTerm (here, end));
+      
+    } else if (el.lexType == OrOpE) {
+      curTree = OrAdd (curTree, ParseTerm (here, end));
+      
+    } else if (el.lexType == NotOpE) {
+      curTree = NotAdd (curTree, ParseTerm (here, end));
+      
+    } else break;
+    
+    oldHere = here;
+  }
+
+  return curTree;
+}
+
+QueryNode *ParseQuery (const UCArray &queryStr) {
+  UCArray::const_iterator here = queryStr.begin();
+  UCArray::const_iterator end = queryStr.end();
+  return ParseExpression (here, end);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryParser.h	(revision 16561)
@@ -0,0 +1,31 @@
+/**************************************************************************
+ *
+ * QueryParser.h -- Query parser for a simple query language
+ * Copyright (C) 2000  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef QUERYPARSER_H
+#define QUERYPARSER_H
+
+#include "UCArray.h"
+#include "MGQuery.h"
+
+// returns NULL if the query could not be parsed
+QueryNode *ParseQuery (const UCArray &queryStr);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryResultsSort.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryResultsSort.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryResultsSort.h	(revision 16561)
@@ -0,0 +1,140 @@
+/**************************************************************************
+ *
+ * QueryResultsSort.h -- Functions to sort query results and parallel data
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef QUERYRESULTSSORT_H
+#define QUERYRESULTSSORT_H
+
+#include "Terms.h"
+
+
+struct LTRank {
+  bool operator()(const float &f1, const float &f2) const
+    { return (f1 < f2); }
+};
+
+struct GTRank {
+  bool operator()(const float &f1, const float &f2) const
+    { return (f1 > f2); }
+};
+
+
+
+// assumes the rank vector is the same size as the document
+// number vector. assumes the value to be pushed is in
+// docNumHeap + heapSize - 1 and rankHeap + heapSize - 1
+// topIndex can be set
+template <class RandomAccessIterator, class Compare>
+void PushHeap (DocNumArray::iterator docNumHeap,
+	       RandomAccessIterator parallelHeap,
+	       unsigned long heapSize,
+	       const Compare &comp) {
+  unsigned long holeIndex = heapSize-1;
+  unsigned long parent = (holeIndex - 1) / 2;
+  while (holeIndex > 0 && comp (*(parallelHeap+parent),
+				*(parallelHeap+holeIndex))) {
+    swap (*(parallelHeap+holeIndex), *(parallelHeap+parent));
+    swap (*(docNumHeap+holeIndex), *(docNumHeap+parent));
+    holeIndex = parent;
+    parent = (holeIndex - 1) / 2;
+  }
+}
+
+// AdjustHeap is used to add a element to a heap after one
+// element in the heap has been replaced with a new one
+template <class RandomAccessIterator, class Compare>
+void AdjustHeap (DocNumArray::iterator docNumHeap,
+		     RandomAccessIterator parallelHeap,
+		     unsigned long holeIndex,
+		     unsigned long heapSize,
+		     const Compare &comp) {
+  unsigned long secondChild = 2 * holeIndex + 2;
+  while (secondChild < heapSize) {
+    if (comp(*(parallelHeap + secondChild),
+	     *(parallelHeap + (secondChild - 1))))
+      --secondChild;
+    swap (*(parallelHeap+holeIndex), *(parallelHeap+secondChild));
+    swap (*(docNumHeap+holeIndex), *(docNumHeap+secondChild));
+    holeIndex = secondChild;
+    secondChild = 2 * secondChild + 2;
+  }
+  if (secondChild == heapSize) {
+    swap (*(parallelHeap+holeIndex), *(parallelHeap+(secondChild-1)));
+    swap (*(docNumHeap+holeIndex), *(docNumHeap+(secondChild-1)));
+    holeIndex = secondChild - 1;
+  }
+  PushHeap (docNumHeap, parallelHeap, holeIndex+1, comp);
+}
+
+template <class RandomAccessIterator, class Compare>
+void PopHeap (DocNumArray::iterator docNumHeap,
+	      RandomAccessIterator parallelHeap,
+	      unsigned long heapSize,
+	      const Compare &comp) {
+  swap (*(parallelHeap), *(parallelHeap+heapSize-1));
+  swap (*(docNumHeap), *(docNumHeap+heapSize-1));
+  AdjustHeap (docNumHeap, parallelHeap, (unsigned long)0, heapSize-1, comp);
+}
+
+template <class RandomAccessIterator, class Compare>
+void MakeHeap (DocNumArray::iterator docNumHeap,
+	       RandomAccessIterator parallelHeap,
+	       unsigned long size,
+	       const Compare &comp) {
+  if (size < 2) return;
+  
+  unsigned long i = 0;
+  for (i=1; i<=size; ++i)
+    PushHeap (docNumHeap, parallelHeap, i, comp);
+}
+
+ 
+template <class RandomAccessIterator, class Compare>
+void SortHeap (DocNumArray::iterator docNumHeap,
+	       RandomAccessIterator parallelHeap,
+	       unsigned long size,
+	       const Compare &comp) {
+  if (size < 2) return;
+
+  unsigned long i;
+  for (i=size; i>1; --i)
+    PopHeap (docNumHeap, parallelHeap, i, comp);
+}
+
+// uses the heap to get the top "size" elements
+template <class RandomAccessIterator, class Compare>
+void SelectAddHeap (DocNumArray::iterator docNumHeap,
+		    RandomAccessIterator parallelHeap,
+		    unsigned long heapSize,
+		    DocNumArray::iterator docNumAdd,
+		    RandomAccessIterator parallelAdd,
+		    unsigned long addSize,
+		    const Compare &comp) {
+  unsigned long i;
+  for (i=0; i<addSize; ++i) {
+    if (comp(*(parallelAdd+i), *(parallelHeap))) {
+      swap (*(docNumHeap), *(docNumAdd+i));
+      swap (*(parallelHeap), *(parallelAdd+i));
+      AdjustHeap (docNumHeap, parallelHeap, (unsigned long)0, heapSize, comp);
+    }
+  }
+}
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/QueryTester.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/QueryTester.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/QueryTester.cpp	(revision 16561)
@@ -0,0 +1,447 @@
+/**************************************************************************
+ *
+ * QueryTester.cpp -- 
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "MGQuery.h"
+
+
+class SetQueryNode : public QueryNode {
+public:
+  QueryResult queryResult;
+  
+  SetQueryNode () {}
+  ~SetQueryNode () {}
+
+  void Calculate (IndexData &/*indexData*/, const QueryInfo &queryInfo,
+		  QueryResult &result) const {
+    result = queryResult;
+    if (!queryInfo.sortByRank && !queryInfo.needRankInfo)
+      result.ranks.erase (result.ranks.begin(), result.ranks.end());
+    if (!queryInfo.needTermFreqs)
+      result.termFreqs.erase (result.termFreqs.begin(),
+			      result.termFreqs.end());
+  }
+  void Free () { queryResult.Clear(); }
+};
+
+
+bool Test1 () {
+  AndQueryNode andNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  RankArray &rankSet1 = setNode1->queryResult.ranks;
+  docSet1.push_back (1);  rankSet1.push_back (0.1f);
+  docSet1.push_back (10); rankSet1.push_back (0.2f);
+  docSet1.push_back (15); rankSet1.push_back (0.2f);
+  docSet1.push_back (18); rankSet1.push_back (0.4f);
+  docSet1.push_back (19); rankSet1.push_back (0.5f);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  RankArray &rankSet2 = setNode2->queryResult.ranks;
+  docSet2.push_back (2);  rankSet2.push_back (0.1f);
+  docSet2.push_back (11); rankSet2.push_back (0.2f);
+  docSet2.push_back (12); rankSet2.push_back (0.3f);
+  docSet2.push_back (13); rankSet2.push_back (0.4f);
+  docSet2.push_back (14); rankSet2.push_back (0.5f);
+  docSet2.push_back (15); rankSet2.push_back (0.6f);
+  docSet2.push_back (16); rankSet2.push_back (0.7f);
+  docSet2.push_back (17); rankSet2.push_back (0.8f);
+  docSet2.push_back (19); rankSet2.push_back (0.9f);
+  docSet2.push_back (20); rankSet2.push_back (0.1f);
+  docSet2.push_back (21); rankSet2.push_back (0.2f);
+
+  cout << "\n" << setNode1->queryResult << "AND\n\n"
+       << setNode2->queryResult;
+  
+
+  andNode.leftNode = setNode1;
+  andNode.rightNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = true;
+  queryInfo.needRankInfo = true;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  andNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  RankArray &rcRankSet = resultCompare.ranks;
+  rcDocSet.push_back (15);  rcRankSet.push_back (0.2+0.6);
+  rcDocSet.push_back (19);  rcRankSet.push_back (1.4);
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+bool Test1b () {
+  AndQueryNode andNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  docSet1.push_back (1); 
+  docSet1.push_back (10);
+  docSet1.push_back (15);
+  docSet1.push_back (18);
+  docSet1.push_back (19);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  docSet2.push_back (2); 
+  docSet2.push_back (11);
+  docSet2.push_back (12);
+  docSet2.push_back (13);
+  docSet2.push_back (14);
+  docSet2.push_back (15);
+  docSet2.push_back (16);
+  docSet2.push_back (17);
+  docSet2.push_back (19);
+  docSet2.push_back (20);
+  docSet2.push_back (21);
+
+  cout << "\n" << setNode1->queryResult << "AND\n\n"
+       << setNode2->queryResult;
+  
+
+  andNode.leftNode = setNode1;
+  andNode.rightNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = false;
+  queryInfo.needRankInfo = false;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  andNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  rcDocSet.push_back (15); 
+  rcDocSet.push_back (19); 
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+bool Test2 () {
+  OrQueryNode orNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  RankArray &rankSet1 = setNode1->queryResult.ranks;
+  docSet1.push_back (1);  rankSet1.push_back (0.1f);
+  docSet1.push_back (10); rankSet1.push_back (0.2f);
+  docSet1.push_back (15); rankSet1.push_back (0.2f);
+  docSet1.push_back (18); rankSet1.push_back (0.4f);
+  docSet1.push_back (19); rankSet1.push_back (0.5f);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  RankArray &rankSet2 = setNode2->queryResult.ranks;
+  docSet2.push_back (2);  rankSet2.push_back (0.1f);
+  docSet2.push_back (11); rankSet2.push_back (0.2f);
+  docSet2.push_back (12); rankSet2.push_back (0.3f);
+  docSet2.push_back (13); rankSet2.push_back (0.4f);
+  docSet2.push_back (14); rankSet2.push_back (0.5f);
+  docSet2.push_back (15); rankSet2.push_back (0.6f);
+  docSet2.push_back (16); rankSet2.push_back (0.7f);
+  docSet2.push_back (17); rankSet2.push_back (0.8f);
+  docSet2.push_back (19); rankSet2.push_back (0.9f);
+  docSet2.push_back (20); rankSet2.push_back (0.1f);
+  docSet2.push_back (21); rankSet2.push_back (0.2f);
+
+  cout << "\n" << setNode1->queryResult << "OR\n\n"
+       << setNode2->queryResult;
+  
+
+  orNode.leftNode = setNode1;
+  orNode.rightNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = true;
+  queryInfo.needRankInfo = true;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  orNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  RankArray &rcRankSet = resultCompare.ranks;
+  rcDocSet.push_back (1);  rcRankSet.push_back (0.1f);
+  rcDocSet.push_back (2);  rcRankSet.push_back (0.1f);
+  rcDocSet.push_back (10); rcRankSet.push_back (0.2f);
+  rcDocSet.push_back (11); rcRankSet.push_back (0.2f);
+  rcDocSet.push_back (12); rcRankSet.push_back (0.3f);
+  rcDocSet.push_back (13); rcRankSet.push_back (0.4f);
+  rcDocSet.push_back (14); rcRankSet.push_back (0.5f);
+  rcDocSet.push_back (15); rcRankSet.push_back (0.2f+0.6f);
+  rcDocSet.push_back (16); rcRankSet.push_back (0.7f);
+  rcDocSet.push_back (17); rcRankSet.push_back (0.8f);
+  rcDocSet.push_back (18); rcRankSet.push_back (0.4f);
+  rcDocSet.push_back (19); rcRankSet.push_back (0.9f+0.5f);
+  rcDocSet.push_back (20); rcRankSet.push_back (0.1f);
+  rcDocSet.push_back (21); rcRankSet.push_back (0.2f);
+  
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+bool Test2b () {
+  OrQueryNode orNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  docSet1.push_back (1); 
+  docSet1.push_back (10);
+  docSet1.push_back (15);
+  docSet1.push_back (18);
+  docSet1.push_back (19);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  docSet2.push_back (2);
+  docSet2.push_back (11);
+  docSet2.push_back (12);
+  docSet2.push_back (13);
+  docSet2.push_back (14);
+  docSet2.push_back (15);
+  docSet2.push_back (16);
+  docSet2.push_back (17);
+  docSet2.push_back (19);
+  docSet2.push_back (20);
+  docSet2.push_back (21);
+
+  cout << "\n" << setNode1->queryResult << "OR\n\n"
+       << setNode2->queryResult;
+  
+
+  orNode.leftNode = setNode1;
+  orNode.rightNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = false;
+  queryInfo.needRankInfo = false;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  orNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  rcDocSet.push_back (1);
+  rcDocSet.push_back (2); 
+  rcDocSet.push_back (10);
+  rcDocSet.push_back (11);
+  rcDocSet.push_back (12);
+  rcDocSet.push_back (13);
+  rcDocSet.push_back (14);
+  rcDocSet.push_back (15);
+  rcDocSet.push_back (16);
+  rcDocSet.push_back (17);
+  rcDocSet.push_back (18);
+  rcDocSet.push_back (19);
+  rcDocSet.push_back (20);
+  rcDocSet.push_back (21);
+  
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+bool Test3 () {
+  NotQueryNode notNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  RankArray &rankSet1 = setNode1->queryResult.ranks;
+  docSet1.push_back (1);  rankSet1.push_back (0.1f);
+  docSet1.push_back (10); rankSet1.push_back (0.2f);
+  docSet1.push_back (15); rankSet1.push_back (0.2f);
+  docSet1.push_back (18); rankSet1.push_back (0.4f);
+  docSet1.push_back (19); rankSet1.push_back (0.5f);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  RankArray &rankSet2 = setNode2->queryResult.ranks;
+  docSet2.push_back (2);  rankSet2.push_back (0.1f);
+  docSet2.push_back (11); rankSet2.push_back (0.2f);
+  docSet2.push_back (12); rankSet2.push_back (0.3f);
+  docSet2.push_back (13); rankSet2.push_back (0.4f);
+  docSet2.push_back (14); rankSet2.push_back (0.5f);
+  docSet2.push_back (15); rankSet2.push_back (0.6f);
+  docSet2.push_back (16); rankSet2.push_back (0.7f);
+  docSet2.push_back (17); rankSet2.push_back (0.8f);
+  docSet2.push_back (19); rankSet2.push_back (0.9f);
+  docSet2.push_back (20); rankSet2.push_back (0.1f);
+  docSet2.push_back (21); rankSet2.push_back (0.2f);
+
+  cout << "\n" << setNode1->queryResult << "NOT\n\n"
+       << setNode2->queryResult;
+  
+
+  notNode.queryNode = setNode1;
+  notNode.notNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = true;
+  queryInfo.needRankInfo = true;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  notNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  RankArray &rcRankSet = resultCompare.ranks;
+  rcDocSet.push_back (1);  rcRankSet.push_back (0.1);
+  rcDocSet.push_back (10); rcRankSet.push_back (0.2);
+  rcDocSet.push_back (18); rcRankSet.push_back (0.4);
+  
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+bool Test3b () {
+  NotQueryNode notNode;
+
+  SetQueryNode *setNode1 = new SetQueryNode;
+  DocNumArray &docSet1 = setNode1->queryResult.docs;
+  docSet1.push_back (1); 
+  docSet1.push_back (10);
+  docSet1.push_back (15);
+  docSet1.push_back (18);
+  docSet1.push_back (19);
+
+  SetQueryNode *setNode2 = new SetQueryNode;
+  DocNumArray &docSet2 = setNode2->queryResult.docs;
+  docSet2.push_back (2); 
+  docSet2.push_back (11);
+  docSet2.push_back (12);
+  docSet2.push_back (13);
+  docSet2.push_back (14);
+  docSet2.push_back (15);
+  docSet2.push_back (16);
+  docSet2.push_back (17);
+  docSet2.push_back (19);
+  docSet2.push_back (20);
+  docSet2.push_back (21);
+
+  cout << "\n" << setNode1->queryResult << "NOT\n\n"
+       << setNode2->queryResult;
+  
+
+  notNode.queryNode = setNode1;
+  notNode.notNode = setNode2;
+
+  IndexData indexData; // this will not be used anywhere
+  QueryInfo queryInfo;
+  queryInfo.maxDocs = 0;
+  queryInfo.sortByRank = false;
+  queryInfo.needRankInfo = false;
+  queryInfo.needTermFreqs = true;
+  
+  QueryResult result;
+
+  notNode.Calculate(indexData, queryInfo, result);
+
+  cout << "EQUALS\n\n" << result << "\n";
+
+  QueryResult resultCompare;
+  DocNumArray &rcDocSet = resultCompare.docs;
+  rcDocSet.push_back (1); 
+  rcDocSet.push_back (10);
+  rcDocSet.push_back (18);
+  
+
+  if (!(result == resultCompare)) {
+    cout << "Test failed!!!\n";
+    cout << resultCompare;
+    return false;
+  }
+
+  return true;
+}
+
+
+int main () {
+  if (!Test1 () ||
+      !Test2 () ||
+      !Test2b () ||
+      !Test3 () ||
+      !Test3b ()) return 1;
+  
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Queryer.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Queryer.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Queryer.cpp	(revision 16561)
@@ -0,0 +1,294 @@
+/**************************************************************************
+ *
+ * Queryer.cpp -- simple interactive query program
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "MGQuery.h"
+#include "TextGet.h"
+
+#include "messages.h"
+#include "mg_files.h"
+
+#include "GSDLQueryParser.h"
+
+void printHelp() {
+
+  cout << "commands available are:\n"
+       << "\t.q\t\tquit\n"
+       << "\t.h\t\tprint the help message\n"
+       << "\t.i\t\tchange the search level (enter the new level at the prompt)\n"
+       << "\t.l\t\tchange the result level ( \"\"   \"\"  )\n"
+       << "\t.b\t\tfull text browse (enter a word or fragment at the prompt)\n"
+       << "\t.r0/.r1\t\tranking off/on\n"
+       << "\t.t0/.t1\t\tquery type some/all\n"
+       << "\t.c0/.c1\t\tcasefolding off/on\n"
+       << "\t.s0/.s1\t\tstemming off/on\n"
+#ifdef ENABLE_ACCENTFOLD
+       << "\t.a0/.a1\t\taccentfolding off/on\n"
+#endif
+       << "\t.o0/.o1\t\tshort output off/on\n"
+       << "\t.m\t\tset maxnumeric (enter the number at the prompt)\n\n"
+       << "\t.p\t\tprint a document (enter the docnum at the prompt)\n"
+       << "otherwise just enter a query\n\n";
+
+}
+
+int main (int argc, char **argv) {
+  int ch;
+  char *textfilename = "";
+  char *indexfilename = "";
+  char *basePath = "";
+  
+  opterr = 0;
+  msg_prefix = argv[0];
+  
+  // process the command line arguments
+  while ((ch = getopt (argc, argv, "f:t:d:h")) != -1) {
+    switch (ch) {
+    case 'f':		/* input file */
+      indexfilename = optarg;
+      break;
+    case 't':
+      textfilename = optarg;
+      break;
+    case 'd':
+      basePath = optarg;
+      set_basepath (basePath);
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-h] [-d directory] -f indexname -t textname\n", argv[0]);
+      exit (1);
+    }
+  }
+
+  if (textfilename[0] == '\0' || indexfilename[0] == '\0') {
+    FatalError (1, "Index and text file names must be specified with -f and -t \n");
+  }
+  
+  // init the text system
+  TextData textData;
+  if (!textData.LoadData (basePath, textfilename)) {
+    FatalError (1, "Couldn't load text information for \"%s\"", textfilename);
+  }
+  
+  // init the query system
+  IndexData indexData;
+  if (!indexData.LoadData (basePath, indexfilename)) {
+    FatalError (1, "Couldn't load index information for \"%s\"", indexfilename);
+  }
+
+  int maxnumeric = 4;
+
+  // debug output
+  cerr << "num docs: "<<indexData.bdh.num_docs       
+       << "\nnum frags: "<<indexData.bdh.num_frags
+       << "\nnum words: "<<indexData.bdh.num_words
+       << "\ntotal bytes: "<<indexData.bdh.total_bytes
+       << "\nindex string bytes: "<<indexData.bdh.index_string_bytes
+       << "\nnum levels: "<<indexData.bdh.num_levels<<endl;
+  
+  // do querying
+  QueryInfo queryInfo;
+  SetCStr (queryInfo.docLevel, "Doc", 3);
+  queryInfo.maxDocs = 50;
+  queryInfo.sortByRank = true;
+  queryInfo.exactWeights = false;
+  queryInfo.needRankInfo = true;
+  queryInfo.needTermFreqs = true;
+  
+  ExtQueryResult queryResult;
+  char query[2048];
+  UCArray queryArray;
+  QueryNode *queryTree = NULL;
+
+
+  UCArray docLevel;
+  SetCStr(docLevel, "Doc", 3);
+
+  UCArray level;
+  UCArrayClear(level);
+  //SetCStr(level, "");
+   
+  int defaultStemMethod = 0; // uncasefolded, unstemmed, unaccentfolded
+  int defaultBoolCombine = 0; // OR
+  bool shortOutput = false;
+  BrowseQueryNode browseNode;
+  browseNode.startPosition = -10;
+  browseNode.numTerms = 40;
+
+  BrowseQueryResult browseResult;
+
+  while (true) {
+    cout << "> ";
+    cin.getline(query, 2048, '\n');
+    SetCStr (queryArray, query, strlen(query));
+
+    // check for commands
+    if (queryArray.size() >= 2 && queryArray[0] == '.') {
+      if (queryArray[1] == 'q') break;  // quit
+
+      if (queryArray[1] == 'h') { // help
+	printHelp();
+      } else if (queryArray[1] == 'i') {
+	cout << "current index="<< queryInfo.docLevel << "\nchange to index:";
+	cin >> query;
+	UCArrayClear(queryInfo.docLevel);
+	SetCStr(queryInfo.docLevel, query, strlen(query));
+	cout << "index set to " << queryInfo.docLevel <<"\n";
+	cin.getline(query, 2048, '\n');
+      } else if (queryArray[1] == 'l') {
+	cout << "current level="<< level << "\nchange to level:";
+	cin >> query;
+	UCArrayClear(level);
+	SetCStr(level, query, strlen(query));
+	cout << "level set to " << level <<"\n";
+	cin.getline(query, 2048, '\n');
+      }
+
+      else if (queryArray[1] == 'm') {
+	// maxnumeric
+	int m = 0;
+	cin >> m;
+	cin.getline(query, 2048, '\n'); // eat up return
+	if (m > 4 && m < 512) {
+	  maxnumeric = m;
+	}
+      }
+      else if (queryArray[1] == 'p') {
+	// print
+	UCArray docText;
+	unsigned long docNum = 0;
+	cin >> docNum;
+	cin.getline(query, 2048, '\n'); // eat up return
+	
+	if (!GetDocText (textData, queryInfo.docLevel, docNum, docText)) {
+	  FatalError (1, "Error while trying to get document %u", docNum);
+	}
+	
+	cout << docText << "\n";
+      }
+      else if (queryArray[1] == 't') { // query type - all/some
+	if (queryArray[2] == '1') defaultBoolCombine = 1;
+	else if (queryArray[2] == '0') defaultBoolCombine = 0;
+	else {
+	  cout << "Error: please enter .t0 (some) or .t1 (all)\n";
+	}
+      }
+      else if (queryArray[1] == 'r') { // ranking - on/off
+	if (queryArray[2] == '1') queryInfo.sortByRank = true;
+	else if (queryArray[2] == '0') queryInfo.sortByRank = false;
+	else {
+	  cout << "Error: please enter .r0 (non-ranked) or .r1 (ranked)\n";
+	}
+      }
+      else if (queryArray[1] == 'c') { // casefolding - on/off
+	if (queryArray[2] == '1') defaultStemMethod |= STEM_CaseFolding;
+	else if (queryArray[2] == '0') defaultStemMethod &= (~STEM_CaseFolding);
+	else {
+	  cout << "Error: please enter .c0 (case sensitive) or .c1 (casefolded)\n";
+	}
+      }
+      else if (queryArray[1] == 's') { // stemming - on/off
+	if (queryArray[2] == '1') defaultStemMethod |= STEM_Stemming;
+	else if (queryArray[2] == '0') defaultStemMethod &= (~STEM_Stemming);
+	else {
+	  cout << "Error: please enter .s0 (unstemmed) or .s1 (stemmed)\n";
+	}
+      }      
+#ifdef ENABLE_ACCENTFOLD
+      else if (queryArray[1] == 'a') { // accentfolding - on/off
+	if (queryArray[2] == '1') defaultStemMethod |= STEM_AccentFolding;
+	else if (queryArray[2] == '0') defaultStemMethod &= (~STEM_AccentFolding);
+	else {
+	  cout << "Error: please enter .a0 (accent sensitive) or .a1 (accentfolded)\n";
+	}
+      }
+#endif
+      else if (queryArray[1] == 'o') { // output - short/long
+	if (queryArray[2] == '1') shortOutput = true;
+	else if (queryArray[2] == '0') shortOutput = false;
+	else {
+	  cout << "Error: please enter .o0 (long output) or .o1 (short output)\n";
+	}
+      }
+      else if (queryArray[1] == 'b') {
+	// full text browse
+	cout<<"enter a few letters to start browsing from:";
+	cin>>query;
+	UCArrayClear(browseNode.term);
+	SetCStr(browseNode.term, query, strlen(query));
+	cin.getline(query, 2048, '\n'); // get rest of line
+
+	// print the query
+	PrintNode (cout, &browseNode);
+
+	MGBrowseQuery(indexData, docLevel, browseNode, browseResult);
+	cout << browseResult;
+	cout << "\n";
+
+      }
+      else { // bad option
+	cout << "bad command\n\n";
+	printHelp();
+      }
+    } // if a .x query
+    else {
+      // regular query
+      queryTree = ParseQuery (queryArray, defaultBoolCombine, defaultStemMethod, maxnumeric);
+      if (queryTree == NULL) {
+	cout << "invalid syntax\n";
+      } else {
+	// print the query
+	PrintNode (cout, queryTree);
+	
+	MGQuery (indexData, queryInfo, queryTree, queryResult, level);
+	if (shortOutput) {
+	  queryResult.printShort(cout);
+	  cout << "\n";
+	} else {
+	  cout << queryResult;
+	  cout << "\n";
+	}
+	// delete the query
+	delete queryTree;
+	queryTree = NULL;
+      }
+    }
+  }
+
+
+  // clean up, everybody clean up
+  textData.UnloadData ();
+  indexData.UnloadData ();
+
+  return (0);
+}
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.cpp	(revision 16561)
@@ -0,0 +1,74 @@
+/**************************************************************************
+ *
+ * TagInfo.cpp -- 
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "TagInfo.h"
+
+
+void TagInfo::SetDocTag (const char *cStr) {
+  SetCStr (docTag, cStr, strlen(cStr));
+}
+
+void TagInfo::SetIndexLevel (const char *cStr) {
+  SetCStr (indexLevel, cStr, strlen(cStr));
+}
+void TagInfo::AddLevelTag (const char *cStr) {
+  // convert the string
+  UCArray cArr;
+  SetCStr (cArr, cStr, strlen(cStr));
+
+  // insert the tag
+  levelTags.insert (cArr);
+}
+
+
+TagInfo::TagInfo () {
+  Clear ();
+}
+
+void TagInfo::Clear () {
+  docTag.erase (docTag.begin(), docTag.end());
+  indexLevel.erase (indexLevel.begin(), indexLevel.end());
+  levelTags.erase (levelTags.begin(), levelTags.end());
+}
+
+ostream &operator<<(ostream &s, const TagInfo &t) {
+  // output docTag
+  s << "docTag: \"" << t.docTag << "\"\n";
+
+  // output indexLevel
+  s << "indexLevel: \"" << t.indexLevel << "\"\n";
+
+  // output levelTags
+  s << "levelTags: ";
+
+  UCArraySet::const_iterator here = t.levelTags.begin();
+  UCArraySet::const_iterator end = t.levelTags.end();
+  while (here != end) {
+    s << "\"" << (*here) << "\"";
+    ++here;
+    if (here != end) s << ", ";
+  }
+
+  s << "\n";
+  
+  return s;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TagInfo.h	(revision 16561)
@@ -0,0 +1,46 @@
+/**************************************************************************
+ *
+ * TagInfo.h -- 
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef TAGINFO_H
+#define TAGINFO_H
+
+#include "UCArray.h"
+
+
+class TagInfo {
+public:
+  UCArray docTag;
+  UCArray indexLevel;    // empty for word level index
+  UCArraySet levelTags;
+
+  void SetDocTag (const char *cStr);
+  void SetIndexLevel (const char *cStr);
+  void AddLevelTag (const char *cStr);
+  
+  TagInfo ();
+  void Clear ();
+};
+
+// stream operator to print TagInfo
+ostream &operator<<(ostream &s, const TagInfo &t);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.cpp	(revision 16561)
@@ -0,0 +1,791 @@
+/**************************************************************************
+ *
+ * Terms.cpp -- Query related functions
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "Terms.h"
+#include "words.h"
+#include "stemmer.h"
+#include "bitio_gen.h"
+#include "bitio_m_stdio.h"
+
+void QueryInfo::Clear () {
+  UCArrayClear (docLevel);
+  maxDocs = 0;
+  sortByRank = true;
+  exactWeights = false;
+  needRankInfo = false;
+  needTermFreqs = false;
+}
+
+
+
+void TermFreqData::Clear () {
+  UCArrayClear (tag);
+  UCArrayClear (term);
+  equivTerms.erase(equivTerms.begin(), equivTerms.end());
+  stemMethod = 0;
+  matchDocs = 0;
+  termFreq = 0;
+}
+
+ostream &operator<< (ostream &s, const TermFreqData &t) {
+  s << "<" << t.tag << ">\"" << t.term << "\"stem("
+    << t.stemMethod << ")equiv terms(";
+  
+  unsigned long i;
+  for (i=0; i<t.equivTerms.size(); ++i) {
+    s << t.equivTerms[i] << ", ";
+  }
+  s <<")docs(" << t.matchDocs << ")"
+    << "count("<<t.termFreq<<")";
+  return s;
+}
+
+bool operator== (const TermFreqData &t1, const TermFreqData &t2) {
+  return ((t1.tag == t2.tag) &&
+	  (t1.term == t2.term) &&
+	  (t1.stemMethod == t2.stemMethod) &&
+	  (t1.equivTerms == t2.equivTerms) &&
+	  (t1.matchDocs == t2.matchDocs) &&
+	  (t1.termFreq == t2.termFreq));
+}
+
+
+void QueryResult::Clear () {
+  docs.erase (docs.begin(), docs.end());
+  ranks.erase (ranks.begin(), ranks.end());
+  termFreqs.erase (termFreqs.begin(), termFreqs.end());
+  actualNumDocs = 0;
+}
+
+QueryResult::QueryResult () {
+  Clear ();
+}
+
+void QueryResult::printShort(ostream &s) {
+
+  s << "termFreqs: ";
+  for (unsigned long i=0; i<termFreqs.size(); ++i)
+    s << termFreqs[i] << ", ";
+  
+  s << "\nactual number of docs found: " << actualNumDocs;
+  s << "\n\n";
+
+}
+
+
+ostream &operator<< (ostream &s, const QueryResult &r) {
+  s << "docs: ";
+  unsigned long i;
+  for (i=0; i<r.docs.size(); ++i)
+    s << r.docs[i] << ", ";
+  
+  s << "\nranks: ";
+  for (i=0; i<r.ranks.size(); ++i)
+    s << r.ranks[i] << ", ";
+
+  s << "\ntermFreqs: ";
+  for (i=0; i<r.termFreqs.size(); ++i)
+    s << r.termFreqs[i] << ", ";
+
+  s << "\nactual number of docs found: " << r.actualNumDocs;
+  s << "\n\n";
+
+  return s;
+}
+
+
+bool operator== (const QueryResult &r1, const QueryResult &r2) {
+  return ((r1.docs == r2.docs) &&
+	  (r1.ranks == r2.ranks) &&
+	  (r1.termFreqs == r2.termFreqs) &&
+	  (r1.actualNumDocs == r2.actualNumDocs));
+}
+
+//---------------------------------------------------
+// new ExtQueryResult stuff
+void ExtQueryResult::Clear () {
+  docs.erase (docs.begin(), docs.end());
+  levels.erase (levels.begin(), levels.end());
+  ranks.erase (ranks.begin(), ranks.end());
+  termFreqs.erase (termFreqs.begin(), termFreqs.end());
+  actualNumDocs = 0;
+}
+
+ExtQueryResult::ExtQueryResult () {
+  Clear ();
+}
+
+ostream &operator<< (ostream &s, const ExtQueryResult &r) {
+  s << "docs: ";
+  unsigned long i;
+  for (i=0; i<r.docs.size(); ++i)
+    s << r.docs[i] << ", ";
+
+  s << "\nlevels: ";
+  for (i=0; i<r.levels.size(); ++i)
+    s << r.levels[i] << ", ";
+
+  
+  s << "\nranks: ";
+  for (i=0; i<r.ranks.size(); ++i)
+    s << r.ranks[i] << ", ";
+
+  s << "\ntermFreqs: ";
+  for (i=0; i<r.termFreqs.size(); ++i)
+    s << r.termFreqs[i] << ", ";
+  s << "\nactual number of docs found: " << r.actualNumDocs;
+  s << "\n\n";
+
+  return s;
+}
+
+
+bool operator== (const ExtQueryResult &r1, const ExtQueryResult &r2) {
+  return ((r1.docs == r2.docs) &&
+	  (r1.levels == r2.levels) &&
+	  (r1.ranks == r2.ranks) &&
+	  (r1.termFreqs == r2.termFreqs) &&
+	  (r1.actualNumDocs == r2.actualNumDocs));
+}
+
+//-------------------------------------------------------
+// new BrowseQueryResult stuff
+void BrowseQueryResult::Clear () {
+  termFreqs.erase (termFreqs.begin(), termFreqs.end());
+}
+
+BrowseQueryResult::BrowseQueryResult () {
+  Clear ();
+}
+
+
+
+ostream &operator<< (ostream &s, const BrowseQueryResult &r) {
+  s << "terms: ";
+  unsigned long i;
+  for (i=0; i<r.termFreqs.size(); ++i)
+    s << r.termFreqs[i] << ", ";
+    s << "\n\n";
+  return s;
+}
+
+
+bool operator== (const BrowseQueryResult &r1, const BrowseQueryResult &r2) {
+  return ((r1.termFreqs == r2.termFreqs));
+	 
+}
+
+
+
+
+//--------------------------------------
+void FragData::Clear () {
+  matchDocs = 0;
+  fragNums.erase (fragNums.begin(), fragNums.end());
+  fragFreqs.erase (fragFreqs.begin(), fragFreqs.end());
+}
+
+
+void FindWordNumbers (IndexData &indexData,
+		      const UCArray &term,
+		      unsigned long stemMethod,
+		      vector<unsigned long> &equivWords) {
+  equivWords.erase (equivWords.begin(), equivWords.end());
+
+  // if the stem method specified is not a valid one (i.e. there was no appropriate stem index, then we set it to 0)
+  // unless we have partial matching, in which case we are not doing stem indexes anyway.
+  if (!(stemMethod & STEM_PARTIAL_MATCH) && indexData.stemFile[stemMethod-1] == NULL) {
+    cerr << "Stem index for method "<<stemMethod<< " was not built, so not doing stemming\n";
+    stemMethod = 0;
+  }
+  /* [JFG - Mar 06: Accent folding patch] */
+  /* use flag PARTIAL_MATCH */  
+  if (stemMethod == 0 || (stemMethod & STEM_PARTIAL_MATCH)) {
+    // don't need to stem the word,
+    // find the word number(s) for this term
+    unsigned long wordElNum = 0;
+    unsigned long numLevels = indexData.bdh.num_levels;
+    word_block_dict_el wordDictEl;
+    wordDictEl.SetNumLevels (numLevels);
+    if (stemMethod == 0) {
+      if (SearchWordBlockDictEl (indexData.dictFile, indexData.biWords,
+				 indexData.bdh.entries_per_wblk,
+				 indexData.bdh.word_dict_size,
+				 numLevels, term, wordDictEl, wordElNum))
+	equivWords.push_back (wordElNum);
+      
+      return;
+    } else {
+      // partial matching,
+      PartialMatchSearchWordBlockDictEl (indexData.dictFile, indexData.biWords, indexData.bdh.entries_per_wblk, indexData.bdh.word_dict_size, numLevels, term, wordDictEl, equivWords, (stemMethod & STEM_CaseFolding)? true : false);
+      // TODO: Accent Folding is not handled here!!
+      return;
+    }
+  }
+		      
+  // need to stem this word and find it in the blocked stem index
+  unsigned char mgWord[MAXSTEMLEN + 1];
+  UCArray stemTerm;
+  unsigned long stemmerNum = 0;
+
+  /* [JFG - Mar 06: Accent folding patch] */
+  if(stemMethod > STEM_MAX) {
+    return;
+    //TODO: throw an error here
+  }
+
+  stemmerNum = indexData.sih[stemMethod-1].stemmer_num;
+  
+  // convert the word to an "mg word"
+  mgWord[0] = term.size();
+  memcpy ((char *)&mgWord[1], &(term[0]), term.size());
+  
+  // stem the word
+  mgpp_stemmer (stemMethod, stemmerNum, mgWord);
+  // convert the result back to a UCArray
+  stemTerm.insert (stemTerm.end(), &mgWord[1], &mgWord[1] + mgWord[0]);
+
+  // need to look up this term in the appropriate dictionary
+  stem_block_dict_el stemDictEl;
+  unsigned long stemElNum;
+  bool result = false;
+  
+  /* [JFG - Mar 06: Accent folding patch] */
+  result = SearchStemBlockDictEl (indexData.stemFile[stemMethod-1],
+			   indexData.sii[stemMethod-1],
+			   indexData.sih[stemMethod-1].entries_per_block,
+			   indexData.sih[stemMethod-1].dict_size,
+			   stemTerm,
+			   stemDictEl,
+			   stemElNum);
+  
+  if (result) {
+    equivWords = stemDictEl.equivWords;  
+  }
+}
+
+
+
+void ReadTermFragData (IndexData &indexData,
+		       bool needFragFreqs,
+		       unsigned long termNum,
+		       FragData &fragData,
+		       FragRangeArray *fragLimits,
+		       UCArray & termWord) {
+  fragData.Clear();
+
+  // look up the word in the dictionary
+  unsigned long numLevels = indexData.bdh.num_levels;
+  word_block_dict_el wordDictEl;
+  wordDictEl.SetNumLevels (numLevels);
+  if (!SearchWordBlockDictElNum (indexData.dictFile,
+				 indexData.biWords,
+				 indexData.bdh.entries_per_wblk,
+				 indexData.bdh.word_dict_size,
+				 numLevels,
+				 termNum, wordDictEl))
+    return; // nothing more to do
+
+  fragData.matchDocs = wordDictEl.levelFreqs[indexData.curLevelNum];
+  termWord = wordDictEl.el;
+  // seek to the appropriate place in the inverted file
+  fseek (indexData.invfFile, wordDictEl.invf_ptr, SEEK_SET);
+  stdio_bitio_buffer buffer (indexData.invfFile);
+    
+  unsigned long B = BIO_Bblock_Init (indexData.bdh.num_frags,
+				     wordDictEl.frag_occur);
+  unsigned long fragNum = 0;
+  unsigned long termFreq = 0;
+
+  unsigned long fragLimitI = 0;
+  unsigned long i;
+  for (i=0; i<wordDictEl.frag_occur; ++i) {
+    fragNum += buffer.bblock_decode (B, NULL);
+    if (!indexData.ifh.word_level_index) termFreq = buffer.gamma_decode (NULL);
+    else termFreq = 1;
+
+    // get the right fragment range
+    if (fragLimits != NULL) {
+      while (fragLimitI+1 < (*fragLimits).size() &&
+	     fragNum > (*fragLimits)[fragLimitI+1].rangeStart) {
+	++fragLimitI;
+      }
+    }
+
+    // add the entry if it is within the limits
+    if ((fragLimits == NULL) ||
+	(fragLimitI < (*fragLimits).size() &&
+	 fragNum > (*fragLimits)[fragLimitI].rangeStart &&
+	 fragNum <= (*fragLimits)[fragLimitI].rangeEnd)) {
+      fragData.fragNums.push_back (fragNum);
+      if (needFragFreqs)
+	fragData.fragFreqs.push_back (termFreq);
+    }
+  }
+
+  buffer.done();
+}
+
+
+void CombineFragData (bool needFragFreqs,
+		      const FragData &f1,
+		      const FragData &f2,
+		      FragData &outFragData) {
+  outFragData.Clear();
+
+  // the new number of matching documents is the maximum
+  // of the two input matching number of documents -- it
+  // is assumed that these are at the same document level
+  outFragData.matchDocs = (f1.matchDocs > f2.matchDocs) ?
+    f1.matchDocs : f2.matchDocs;
+
+  // do or
+  unsigned long f1I = 0, f1Size = f1.fragNums.size();
+  unsigned long f2I = 0, f2Size = f2.fragNums.size();
+  while (f1I < f1Size || f2I < f2Size) {
+    if (f2I < f2Size &&
+	(f1I >= f1Size ||
+	 f1.fragNums[f1I] > f2.fragNums[f2I])) {
+      // output f2I
+      outFragData.fragNums.push_back (f2.fragNums[f2I]);
+      if (needFragFreqs)
+	outFragData.fragFreqs.push_back (f2.fragFreqs[f2I]);
+      ++f2I;
+      
+    } else if (f1I < f1Size &&
+	       (f2I >= f2Size ||
+		f1.fragNums[f1I] < f2.fragNums[f2I])) {
+      // output f1I
+      outFragData.fragNums.push_back (f1.fragNums[f1I]);
+      if (needFragFreqs)
+	outFragData.fragFreqs.push_back (f1.fragFreqs[f1I]);
+      ++f1I;
+      
+    } else {
+      // must be equal combine f1I and f2I
+      outFragData.fragNums.push_back (f1.fragNums[f1I]);
+      if (needFragFreqs)
+	outFragData.fragFreqs.push_back (f1.fragFreqs[f1I]+f2.fragFreqs[f2I]);
+      ++f1I;
+      ++f2I;
+    }
+  }
+}
+
+
+void AndCombineFragData (bool needFragFreqs,
+			 FragData &fragData,
+			 const FragData &comFragData,
+			 signed long startRange,
+			 signed long endRange,
+			 const FragRangeArray *fragLimits) {
+  // sanity check on range
+  if (startRange > endRange) {
+    signed long temp = endRange;
+    endRange = startRange;
+    startRange = temp;
+  }
+
+  // get min matchdocs
+  if (comFragData.matchDocs < fragData.matchDocs)
+    fragData.matchDocs = comFragData.matchDocs;
+  
+  unsigned long fragDataI = 0;
+  unsigned long fragDataSize = fragData.fragNums.size();
+  unsigned long comFragDataI = 0;
+  unsigned long comFragDataSize = comFragData.fragNums.size();
+  unsigned long fragLimitI = 0;
+  unsigned long fragLimitSize = (fragLimits==NULL) ? 0 : (*fragLimits).size();
+  unsigned long outI = 0;
+
+  while (fragDataI < fragDataSize &&
+	 comFragDataI < comFragDataSize) {
+    signed long fragNum = (signed long)fragData.fragNums[fragDataI];
+    signed long comFragNum = (signed long)comFragData.fragNums[comFragDataI];
+    
+    // go to the right fragment limit (for the com frag)
+    if (fragLimits != NULL) {
+      while (fragLimitI+1 < fragLimitSize &&
+	     comFragNum > (signed long)(*fragLimits)[fragLimitI+1].rangeStart) {
+	++fragLimitI;
+      }
+    }
+
+    if (fragNum <= comFragNum+startRange ||
+	(fragLimits!=NULL &&
+	 fragNum<=(signed long)(*fragLimits)[fragLimitI].rangeStart)) {
+      ++fragDataI;
+      
+    } else if (fragNum > comFragNum+endRange ||
+	       (fragLimits!=NULL &&
+		fragNum>(signed long)(*fragLimits)[fragLimitI].rangeEnd)) {
+      ++comFragDataI;
+      
+    } else {
+      // equal and within tag
+      fragData.fragNums[outI] = comFragNum;
+      if (needFragFreqs) {
+	fragData.fragFreqs[outI] =
+	  (fragData.fragFreqs[fragDataI] < comFragData.fragFreqs[comFragDataI]) ?
+	  fragData.fragFreqs[fragDataI] : comFragData.fragFreqs[comFragDataI];
+      }
+      ++fragDataI;
+      ++comFragDataI;
+      ++outI;
+    }
+  }
+
+  // erase unused part of fragData
+  fragData.fragNums.erase (fragData.fragNums.begin()+outI,
+			   fragData.fragNums.end());
+  if (needFragFreqs)
+    fragData.fragFreqs.erase (fragData.fragFreqs.begin()+outI,
+			      fragData.fragFreqs.end());
+  else
+    fragData.fragFreqs.erase (fragData.fragFreqs.begin(),
+			      fragData.fragFreqs.end());
+}
+
+
+void FragsToQueryResult (IndexData &indexData,
+			 const QueryInfo &queryInfo,
+			 const FragData &termData,
+			 const UCArray &tag,
+			 const UCArray &term,
+			 unsigned long stemMethod,
+			 unsigned long termWeight,
+			 UCArrayVector &equivTerms,
+			 QueryResult &result) {
+  bool needRanks = (queryInfo.sortByRank || queryInfo.needRankInfo);
+  
+  result.Clear();
+
+  // log (N / ft)
+  unsigned long N = indexData.levels.levelInfo[indexData.curLevel].numEntries;
+  float wordLog = log((double)N / (double)termData.matchDocs);
+
+  // Wqt = fqt * log (N / ft)
+  // note: terms are allowed to have a weight of zero so
+  // they can be excluded from the ranking
+  float Wqt = termWeight * wordLog;
+
+  // Wdt = fdt * log (N / ft)
+  float Wdt;
+  
+  unsigned long termDataI = 0;
+  unsigned long termDataSize = termData.fragNums.size();
+  unsigned long levelDocNum = 0;
+  
+  unsigned long termDocFreq = 0;
+  unsigned long lastLevelDocNum = 0;
+  unsigned long overallwordfreq = 0;
+  
+  while (termDataI < termDataSize) {
+    if (indexData.levelConverter.FragToLevel (termData.fragNums[termDataI],
+					      levelDocNum)) {
+      if (levelDocNum != lastLevelDocNum) {
+	if (lastLevelDocNum > 0) {
+	  // add this doc information
+	  if (needRanks) {
+	    Wdt = termDocFreq * wordLog;
+	    result.ranks.push_back (Wqt * Wdt);
+	  }
+	  result.docs.push_back (lastLevelDocNum);
+	}
+	
+	lastLevelDocNum = levelDocNum;
+	termDocFreq = 0;
+      }
+
+      if (needRanks){
+	termDocFreq += termData.fragFreqs[termDataI];
+	overallwordfreq += termData.fragFreqs[termDataI];
+      }
+    }
+    ++termDataI;
+  }
+
+  if (lastLevelDocNum > 0) {
+    // add the last document information
+    if (needRanks) {
+      Wdt = termDocFreq * wordLog;
+      result.ranks.push_back (Wqt * Wdt);
+    }
+    result.docs.push_back (lastLevelDocNum);
+  }
+
+  // add the term frequency information
+  if (queryInfo.needTermFreqs) {
+    TermFreqData termFreqData;
+    termFreqData.tag = tag;
+    termFreqData.term = term;
+    termFreqData.stemMethod = stemMethod;
+    termFreqData.equivTerms = equivTerms;
+    termFreqData.matchDocs = termData.matchDocs;
+    termFreqData.termFreq = overallwordfreq; // will be zero if needRankInfo 
+                                              //not true
+    result.termFreqs.push_back (termFreqData);
+  }
+}
+
+void AndFragsToQueryResult (IndexData &indexData,
+			    const QueryInfo &queryInfo,
+			    const FragData &termData,
+			    const UCArray &tag,
+			    const UCArray &term,
+			    unsigned long stemMethod,
+			    unsigned long termWeight,
+			    UCArrayVector &equivTerms,
+			    QueryResult &result) {
+  bool needRanks = (queryInfo.sortByRank || queryInfo.needRankInfo);
+  
+  // log (N / ft)
+  float wordLog =
+    log((double)indexData.levels.levelInfo[indexData.curLevel].numEntries/
+	(double)termData.matchDocs);
+
+  // Wqt = fqt * log (N / ft)
+  // note: terms are allowed to have a weight of zero so
+  // they can be excluded from the ranking
+  float Wqt = termWeight * wordLog;
+
+  // Wdt = fdt * log (N / ft)
+  float Wdt;
+  
+  unsigned long termDataI = 0;
+  unsigned long termDataSize = termData.fragNums.size();
+  unsigned long levelDocNum = 0;
+  
+  unsigned long termDocFreq = 0;
+  unsigned long lastLevelDocNum = 0;
+  unsigned long overallwordfreq = 0;
+  unsigned long resultI = 0;
+  unsigned long resultSize = result.docs.size();
+  unsigned long resultOutI = 0;
+  
+  
+  while (termDataI < termDataSize) {
+    if (indexData.levelConverter.FragToLevel (termData.fragNums[termDataI],
+					      levelDocNum)) {
+      if (levelDocNum != lastLevelDocNum) {
+	if (lastLevelDocNum > 0) {
+	  // add this doc information
+	  Wdt = termDocFreq * wordLog;
+	  
+	  // find this document number
+	  while (resultI < resultSize &&
+		 result.docs[resultI] < lastLevelDocNum)
+	    ++resultI;
+	  
+	  // store the result
+	  if (resultI < resultSize && result.docs[resultI] == lastLevelDocNum) {
+	    result.docs[resultOutI] = lastLevelDocNum;
+	    if (needRanks)
+	      result.ranks[resultOutI] = result.ranks[resultI] + Wqt * Wdt;
+	    ++resultI;
+	    ++resultOutI;
+	  }
+	}
+	
+	lastLevelDocNum = levelDocNum;
+	termDocFreq = 0;
+      }
+
+      if (needRanks)
+	termDocFreq += termData.fragFreqs[termDataI];
+     overallwordfreq += termData.fragFreqs[termDataI]; 
+    }
+    
+    ++termDataI;
+  } // while
+
+  if (lastLevelDocNum > 0) {
+    // add the last document information
+    Wdt = termDocFreq * wordLog;
+
+    // find this document number
+    while (resultI < resultSize &&
+	   result.docs[resultI] < lastLevelDocNum)
+      ++resultI;
+    
+    // store the result
+    if (resultI < resultSize && result.docs[resultI] == lastLevelDocNum) {
+      result.docs[resultOutI] = lastLevelDocNum;
+      if (needRanks)
+	result.ranks[resultOutI] = result.ranks[resultI] + Wqt * Wdt;
+      ++resultI;
+      ++resultOutI;
+    }
+  }
+
+  // remove unneeded entries
+  result.docs.erase (result.docs.begin()+resultOutI, result.docs.end());
+  if (needRanks)
+    result.ranks.erase (result.ranks.begin()+resultOutI, result.ranks.end());
+  else
+    result.ranks.erase (result.ranks.begin(), result.ranks.end());
+  
+  // add the term frequency information
+  if (queryInfo.needTermFreqs) {
+    TermFreqData termFreqData;
+    termFreqData.tag = tag;
+    termFreqData.term = term;
+    termFreqData.stemMethod = stemMethod;
+    termFreqData.equivTerms = equivTerms;
+    termFreqData.matchDocs = termData.matchDocs;
+    termFreqData.termFreq = overallwordfreq;
+    result.termFreqs.push_back (termFreqData);
+  }
+}
+
+
+void RemoveUnwantedResults (IndexData &indexData,
+			    const QueryInfo &queryInfo,
+			    const FragData &termData,
+			    QueryResult &result) {
+  bool needRanks = (queryInfo.sortByRank || queryInfo.needRankInfo);
+
+  unsigned long termDataI = 0;
+  unsigned long termDataSize = termData.fragNums.size();
+  unsigned long levelDocNum = 0;
+  
+  unsigned long lastLevelDocNum = 0;
+
+  unsigned long resultI = 0;
+  unsigned long resultSize = result.docs.size();
+  unsigned long resultOutI = 0;
+  
+  while (termDataI < termDataSize) {
+    if (indexData.levelConverter.FragToLevel (termData.fragNums[termDataI],
+					      levelDocNum)) {
+      if (levelDocNum != lastLevelDocNum) {
+	if (lastLevelDocNum > 0) {
+	  // find this document number
+	  while (resultI < resultSize &&
+		 result.docs[resultI] < lastLevelDocNum)
+	    ++resultI;
+	  
+	  // store the result
+	  if (resultI < resultSize && result.docs[resultI] == lastLevelDocNum) {
+	    result.docs[resultOutI] = lastLevelDocNum;
+	    if (needRanks)
+	      result.ranks[resultOutI] = result.ranks[resultI];
+	    ++resultI;
+	    ++resultOutI;
+	  }
+	}
+	
+	lastLevelDocNum = levelDocNum;
+      }
+    }
+    
+    ++termDataI;
+  }
+
+  if (lastLevelDocNum > 0) {
+    // find this document number
+    while (resultI < resultSize &&
+	   result.docs[resultI] < lastLevelDocNum)
+      ++resultI;
+    
+    // store the result
+    if (resultI < resultSize && result.docs[resultI] == lastLevelDocNum) {
+      result.docs[resultOutI] = lastLevelDocNum;
+      if (needRanks) 
+	result.ranks[resultOutI] = result.ranks[resultI];
+      ++resultI;
+      ++resultOutI;
+    }
+  }
+
+  // remove unneeded entries
+  result.docs.erase (result.docs.begin()+resultOutI, result.docs.end());
+  if (needRanks)
+    result.ranks.erase (result.ranks.begin()+resultOutI, result.ranks.end());
+  else
+    result.ranks.erase (result.ranks.begin(), result.ranks.end());
+}
+
+
+
+//--------------------------------------------------------------
+// functions to support full text browse
+
+void FindNearestWordNumber (IndexData &indexData,
+			    const UCArray &term,
+			    unsigned long &number) {
+
+    // find the word number for this term
+    unsigned long wordElNum = 0;
+    unsigned long numLevels = indexData.bdh.num_levels;
+    word_block_dict_el wordDictEl;
+    wordDictEl.SetNumLevels (numLevels);
+    if (NearestSearchWordBlockDictEl (indexData.dictFile, indexData.biWords,
+				      indexData.bdh.entries_per_wblk,
+				      indexData.bdh.word_dict_size,
+				      numLevels, term, wordDictEl, wordElNum))
+      number = wordElNum;
+
+}
+
+void GetTermList(IndexData &indexData,
+		 unsigned long startTerm,
+		 unsigned long numTerms,
+		 TermFreqArray &terms) {
+
+  word_block_dict_el_array wordBlocks; // = new word_block_dict_el_array();
+  TermFreqData termdata;
+
+  terms.erase(terms.begin(), terms.end());
+
+  SearchWordBlockDictElNumRange (indexData.dictFile, indexData.biWords,
+				 indexData.bdh.entries_per_wblk,
+				 indexData.bdh.word_dict_size,
+				 indexData.bdh.num_levels, startTerm,
+				 numTerms, wordBlocks);
+
+  word_block_dict_el_array::iterator here = wordBlocks.begin();
+  word_block_dict_el_array::iterator end = wordBlocks.end();
+
+  while (here != end) {
+    termdata.Clear();
+    termdata.term = (*here).el;
+    termdata.termFreq = (*here).freq;
+    terms.push_back(termdata);
+    ++here;
+  }
+
+}
+
+void GetTermList(IndexData &indexData,
+		 unsigned long startTerm,
+		 unsigned long numTerms,
+		 UCArrayVector &terms) {
+
+  
+  
+  SearchWordBlockDictElNumRange (indexData.dictFile, indexData.biWords,
+				 indexData.bdh.entries_per_wblk,
+				 indexData.bdh.word_dict_size,
+				 indexData.bdh.num_levels, startTerm,
+				 numTerms, terms);
+
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Terms.h	(revision 16561)
@@ -0,0 +1,215 @@
+/**************************************************************************
+ *
+ * Terms.h -- Query related functions
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef TERMS_H
+#define TERMS_H
+
+#include "IndexData.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+
+class QueryInfo {
+public:
+  // general query information
+  UCArray docLevel;
+  unsigned long maxDocs; // 0 = all
+  bool sortByRank;
+  bool exactWeights;
+
+  // information need to return
+  bool needRankInfo;
+  bool needTermFreqs;
+
+  void Clear ();
+  QueryInfo () { Clear (); }
+};
+
+
+class TermFreqData {
+public:
+  UCArray tag;  // level tag or query tag
+  UCArray term; // unstemmed term
+  int stemMethod;
+  UCArrayVector equivTerms; // the stemmed and casefolded variants of the term
+  unsigned long matchDocs; // tf for level - num levels
+			   // containing this term
+  unsigned long termFreq;  // overall term freq - num words that
+				// are this term
+  void Clear ();
+  TermFreqData () { Clear (); }
+};
+
+ostream &operator<< (ostream &s, const TermFreqData &t);
+bool operator== (const TermFreqData &t1, const TermFreqData &t2);
+
+typedef vector<TermFreqData> TermFreqArray;
+
+
+typedef vector<float> RankArray;
+typedef vector<unsigned long> DocNumArray;
+
+class QueryResult {
+public:
+  DocNumArray docs;
+  RankArray ranks; // used for accumulators during query
+  
+  TermFreqArray termFreqs;
+
+  unsigned long actualNumDocs;
+  void Clear ();
+  QueryResult ();
+  void printShort(ostream &s);
+};
+
+ostream &operator<< (ostream &s, const QueryResult &r);
+bool operator== (const QueryResult &r1, const QueryResult &r2);
+
+typedef vector<unsigned long> FragNumArray;
+typedef vector<unsigned long> FragFreqArray;
+
+class FragData {
+public:
+  unsigned long matchDocs;    // ft for level
+  FragNumArray fragNums;
+  FragFreqArray fragFreqs;
+
+  void Clear ();
+  FragData () { Clear (); }
+};
+
+
+class FragRange {
+public:
+  unsigned long rangeStart;
+  unsigned long rangeEnd;
+
+  void Clear () { rangeStart = rangeEnd = 0; }
+  FragRange () { Clear (); }
+};
+
+typedef vector<FragRange> FragRangeArray;
+
+
+
+void FindWordNumbers (IndexData &indexData,
+		      const UCArray &term,
+		      unsigned long stemMethod,
+		      vector<unsigned long> &equivWords);
+
+void ReadTermFragData (IndexData &indexData,
+		       bool needFragFreqs,
+		       unsigned long termNum,
+		       FragData &fragData,
+		       FragRangeArray *fragLimits,
+		       UCArray &termWord);
+
+void CombineFragData (bool needFragFreqs,
+		      const FragData &f1,
+		      const FragData &f2,
+		      FragData &outFragData);
+
+// output will be in fragData (as this is an and operation)
+void AndCombineFragData (bool needFragFreqs,
+			 FragData &fragData,
+			 const FragData &comFragData,
+			 signed long startRange,
+			 signed long endRange,
+			 const FragRangeArray *fragLimits);
+
+void FragsToQueryResult (IndexData &indexData,
+			 const QueryInfo &queryInfo,
+			 const FragData &termData,
+			 const UCArray &tag,
+			 const UCArray &term,
+			 unsigned long stemMethod,
+			 unsigned long termWeight,
+			 UCArrayVector &equivTerms,
+			 QueryResult &result);
+
+void AndFragsToQueryResult (IndexData &indexData,
+			    const QueryInfo &queryInfo,
+			    const FragData &termData,
+			    const UCArray &tag,
+			    const UCArray &term,
+			    unsigned long stemMethod,
+			    unsigned long termWeight,
+			    UCArrayVector &equivTerms,
+			    QueryResult &result);
+
+void RemoveUnwantedResults (IndexData &indexData,
+			    const QueryInfo &queryInfo,
+			    const FragData &termData,
+			    QueryResult &result);
+
+//-----------------------------------------------------------------
+// new QueryResult class to handle retrieval of doc and level nums.
+// Use this class with extended version of MGQuery
+
+class ExtQueryResult : public QueryResult {
+public:
+  DocNumArray levels; // used for returning a different granularity, eg
+  // search sections but return Document numbers, or search Documents, 
+  // return Section numbers.
+ 
+  void Clear ();
+  ExtQueryResult ();
+};
+
+ostream &operator<< (ostream &s, const ExtQueryResult &r);
+bool operator== (const ExtQueryResult &r1, const ExtQueryResult &r2);
+
+//------------------------------------------------------------
+// new functions to handle full text browse
+
+class BrowseQueryResult {
+ public:
+  TermFreqArray termFreqs;
+  void Clear();
+  BrowseQueryResult ();
+
+};
+
+
+ostream &operator<< (ostream &s, const BrowseQueryResult &r);
+bool operator== (const BrowseQueryResult &r1, const BrowseQueryResult &r2);
+
+void FindNearestWordNumber (IndexData &indexData,
+			    const UCArray &term,
+			    unsigned long &number);
+
+void GetTermList(IndexData &indexData,
+		 unsigned long startTerm,
+		 unsigned long numTerms,
+		 TermFreqArray &terms);
+
+void GetTermList (IndexData &indexData,
+		 unsigned long startTerm,
+		 unsigned long numTerms,
+		 UCArrayVector &terms);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.cpp	(revision 16561)
@@ -0,0 +1,374 @@
+/**************************************************************************
+ *
+ * TextEl.cpp -- Data structures for parsed documents
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "TextEl.h"
+#include "ctype.h"
+
+#define REC_TERM   '\002'
+#define PARA_TERM  '\003'
+
+
+void TextEl::Clear () {
+  elType = TextE;
+  tagName.erase (tagName.begin(), tagName.end());
+  text.erase (text.begin(), text.end());
+}
+
+
+static bool ReadTextTag (FileBuf &buf, TextEl &el) {
+  // we must have some sort of tag
+  unsigned char c;
+  if (!buf.Peek(c)) return false;
+  
+  // get the '<'
+  if (c != '<') return false;
+  el.text.push_back (c);
+  if (!buf.Next() || !buf.Peek (c)) return false;
+  
+  // see if we have a '/'
+  if (c == '/') {
+    // we have a closing tag
+    el.text.push_back (c);
+    if (!buf.Next() || !buf.Peek (c)) return false;
+    el.elType = CloseTagE;
+  } else {
+    // we have an opening tag
+    el.elType = OpenTagE;
+  }
+
+  // get the tag name
+  while (isalnum(c)) {
+    el.text.push_back (c);
+    el.tagName.push_back (c);
+    if (!buf.Next() || !buf.Peek (c)) return false;
+  }
+  
+  // get everything up to the closing '>'
+  while (c != '>') {
+    el.text.push_back (c);
+    if (!buf.Next() || !buf.Peek (c)) return false;
+  }
+
+  // get the closing '>'
+  el.text.push_back (c);
+  buf.Next();
+
+  // if there is no tag name then make this a text element
+  if (el.tagName.empty()) el.elType = TextE;
+  
+  return true;
+}
+
+static bool ReadTextTag (u_char ** buffer, TextEl &el) {
+  // we must have some sort of tag
+  u_char * buffer_pos = *buffer;
+  if (*buffer_pos == '\0' || *buffer_pos != '<') {
+     return false;
+  }
+  el.text.push_back('<');
+  buffer_pos ++;
+  if (*buffer_pos == '\0') {
+    *buffer = buffer_pos;
+    return false;
+  }
+  // see if we have a '/'
+  if (*buffer_pos == '/') {
+    el.text.push_back('/');
+    el.elType = CloseTagE;
+    buffer_pos++;
+    if (*buffer_pos == '\0') {    
+      *buffer = buffer_pos;
+      return false;
+    }
+  } else {
+    el.elType = OpenTagE;
+  }
+  // get the tag name
+  while (isalnum(*buffer_pos)) {
+    el.text.push_back (*buffer_pos);
+    el.tagName.push_back (*buffer_pos);
+    buffer_pos++;
+    
+    if (*buffer_pos == '\0') {
+      *buffer = buffer_pos;
+      return false;
+    }
+
+  }
+  cout << "tag name = "<<el.tagName<<endl;
+  // get everything up to closing '>'
+  while (*buffer_pos != '>') {
+    el.text.push_back(*buffer_pos);
+    buffer_pos++;
+    if (*buffer_pos == '\0') {
+      *buffer = buffer_pos;
+      return false;
+    }
+    
+  }
+  // get teh closing '>'
+  el.text.push_back('>');
+  buffer_pos++;
+  *buffer = buffer_pos;
+  cout << "at end of readtexttag, buffer is "<<*buffer<<endl;
+  // if there is no tag name then make this a text element
+  if (el.tagName.empty()) el.elType = TextE;
+  
+  return true;
+}
+
+static void ToggleParaTag (TextEl &el, bool &compatInPara) {
+  SetCStr (el.tagName, "Paragraph", 9);
+  el.text.erase (el.text.begin(), el.text.end());
+  if (compatInPara) {
+    el.elType = CloseTagE;
+    el.text.push_back (PARA_TERM);
+  } else el.elType = OpenTagE;
+  compatInPara = !compatInPara;
+}
+
+static void SetRecTag (TextEl &el, TextElType elType) {
+  el.elType = elType;
+  SetCStr (el.tagName, "Document", 8);
+  el.text.erase (el.text.begin(), el.text.end());
+  if (elType == CloseTagE)
+    el.text.push_back (REC_TERM);
+}
+
+
+bool ReadTextEl (FileBuf &buf, TextEl &el,
+		 bool compatMode, bool &compatInPara) {
+  el.Clear();
+
+  unsigned char c;
+  if (!buf.Peek (c)) return false;
+
+  if (compatMode) {
+    if (c == PARA_TERM) {
+      ToggleParaTag (el, compatInPara);
+      if (!compatInPara) buf.Next();
+      return true;
+    }
+    if (c == REC_TERM) {
+      if (compatInPara) {
+	// need to close this paragraph
+	ToggleParaTag (el, compatInPara);
+	return true;
+      }
+      // can close this document
+      buf.Next();
+      SetRecTag (el, CloseTagE);
+      return true;
+    }
+    if (!compatInPara) {
+      // need to open a paragraph
+      ToggleParaTag (el, compatInPara);
+      return true;
+    }
+  }
+  
+  // see if we have some sort of tag
+  if (c == '<') return ReadTextTag (buf, el);
+
+  // read in a text element
+  el.elType = TextE;
+  while (c != '<' && !(compatMode && (c == PARA_TERM || c == REC_TERM))) {
+    el.text.push_back (c);
+    if (!buf.Next() || !buf.Peek (c)) break;
+  }
+
+  return true;
+}
+bool ReadTextEl (u_char **buffer, TextEl &el,
+		 bool compatMode, bool &compatInPara) {
+  el.Clear();
+  u_char * buffer_pos = *buffer;
+  if (*buffer_pos == '\0') return false;
+  if (compatMode) {
+    if (*buffer_pos == PARA_TERM) {
+      ToggleParaTag (el, compatInPara);
+      if (!compatInPara) buffer_pos++;
+      *buffer = buffer_pos;
+      return true;
+    }
+    if (*buffer_pos == REC_TERM) {
+      if (compatInPara) {
+	// need to close this paragraph
+	ToggleParaTag (el, compatInPara);
+	*buffer = buffer_pos;
+	return true;
+      }
+      // can close this document
+      buffer_pos++;
+      SetRecTag (el, CloseTagE);
+      *buffer = buffer_pos;
+      return true;
+    }
+    if (!compatInPara) {
+      // need to open a paragraph
+      ToggleParaTag (el, compatInPara);
+      *buffer = buffer_pos;
+      return true;
+    }
+  }
+  
+  // see if we have some sort of tag
+  if (*buffer_pos == '<') return ReadTextTag (buffer, el);
+
+  // read in a text element
+  el.elType = TextE;
+  while (*buffer_pos != '<' && !(compatMode && (*buffer_pos == PARA_TERM || *buffer_pos == REC_TERM))) {
+    el.text.push_back (*buffer_pos);
+    buffer_pos++;
+    if (*buffer_pos == '\0') break;
+  }
+  *buffer = buffer_pos;
+  cout << "text element: "<<el.text<<endl;
+  return true;
+}
+  
+static void AddTextEl (TextElArray &doc, unsigned long &docLen,
+		       const TextEl &el) {
+  doc.push_back (el);
+  docLen += el.text.size();
+
+//    cout << "elType:  " << el.elType << "\n";
+//    cout << "tagName: " << el.tagName << "\n";
+//    cout << "text:    \"" << el.text << "\"\n\n";
+}
+
+
+bool ReadDoc (FileBuf &buf, const UCArray &docTag,
+	      TextElArray &doc, unsigned long &docLen,
+	      bool compatMode) {
+  bool compatInPara = false;
+  bool foundDocEl = false;
+  TextEl el;
+  
+  doc.erase (doc.begin(), doc.end());
+  docLen = 0;
+
+  if (compatMode) {
+    // add <Document><Paragraph>
+    SetRecTag (el, OpenTagE);
+    AddTextEl (doc, docLen, el);
+    ToggleParaTag (el, compatInPara);
+    AddTextEl (doc, docLen, el);
+    
+  } else {
+    // look for an opening docTag
+    do {
+      if (!ReadTextEl (buf, el, compatMode, compatInPara)) return false;
+    } while (el.elType != OpenTagE || el.tagName != docTag);
+    
+    AddTextEl (doc, docLen, el);
+  }
+
+  
+  // get all elements until the closing docTag
+  while (ReadTextEl (buf, el, compatMode, compatInPara)) {
+    foundDocEl = true;
+    AddTextEl (doc, docLen, el);
+    if (el.elType == CloseTagE && el.tagName == docTag)
+      return true;
+  }
+
+  
+  if (compatMode) {
+    if (!foundDocEl) { // end of text
+      doc.erase (doc.begin(), doc.end());
+      return false;
+    }
+
+    // if we got here then the eof was encountered before
+    // the closing document tag
+    if (compatInPara) {
+      // need to close this paragraph
+      ToggleParaTag (el, compatInPara);
+      AddTextEl (doc, docLen, el);
+    }
+    // close this document
+    SetRecTag (el, CloseTagE);
+    AddTextEl (doc, docLen, el);
+  }
+  
+  return true;
+}
+
+// copy of readdoc to get TextElArray from u_char buffer
+bool ReadDoc(u_char ** buffer, const UCArray &docTag, 
+	     TextElArray &doc, unsigned long &docLen,
+	     bool compatMode) {
+  bool compatInPara = false;
+  bool foundDocEl = false;
+  TextEl el;
+  
+  doc.erase (doc.begin(), doc.end());
+  docLen = 0;
+
+  if (compatMode) {
+    // add <Document><Paragraph>
+    SetRecTag (el, OpenTagE);
+    AddTextEl (doc, docLen, el);
+    ToggleParaTag (el, compatInPara);
+    AddTextEl (doc, docLen, el);
+    
+  } else {
+    // look for an opening docTag
+    do {
+      if (!ReadTextEl (buffer, el, compatMode, compatInPara)) return false;
+    } while (el.elType != OpenTagE || el.tagName != docTag);
+    
+    AddTextEl (doc, docLen, el);
+  }
+
+  
+  // get all elements until the closing docTag
+  while (ReadTextEl (buffer, el, compatMode, compatInPara)) {
+    foundDocEl = true;
+    AddTextEl (doc, docLen, el);
+    if (el.elType == CloseTagE && el.tagName == docTag)
+      return true;
+  }
+
+  
+  if (compatMode) {
+    if (!foundDocEl) { // end of text
+      doc.erase (doc.begin(), doc.end());
+      return false;
+    }
+
+    // if we got here then the eof was encountered before
+    // the closing document tag
+    if (compatInPara) {
+      // need to close this paragraph
+      ToggleParaTag (el, compatInPara);
+      AddTextEl (doc, docLen, el);
+    }
+    // close this document
+    SetRecTag (el, CloseTagE);
+    AddTextEl (doc, docLen, el);
+  }
+  
+  return true;
+}
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TextEl.h	(revision 16561)
@@ -0,0 +1,69 @@
+/**************************************************************************
+ *
+ * TextEl.h -- Data structures for parsed documents
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef TEXTEL_H
+#define TEXTEL_H
+
+#include "UCArray.h"
+#include "FileBuf.h"
+#include "sysfuncs.h"
+
+enum TextElType {OpenTagE, CloseTagE, TextE};
+
+class TextEl {
+protected:
+  bool ReadTextTag (FileBuf &buf, TextEl &el);
+  bool ReadTextTag (u_char ** buffer, TextEl &el);
+public:
+  TextElType elType;
+  UCArray tagName;
+  UCArray text; // complete text, including tag
+
+  TextEl () { Clear (); }
+  void Clear ();
+};
+
+
+typedef vector<TextEl> TextElArray;
+
+
+// compatMode parses documents similar to "old mg" -- ie with
+// C-b between documents and C-c between paragraphs
+
+// returns true if a text element could be read
+bool ReadTextEl (FileBuf &buf, TextEl &el,
+		 bool compatMode, bool &compatInPara);
+bool ReadTextEl (u_char **buffer, TextEl &el,
+		 bool compatMode, bool &compatInPara);
+
+// looks for an opening docTag and reads all text elements up
+// to a corresponding closing docTag. note that this does not
+// hierarchically parse the document so you can't nest documents.
+// returns true if a document could be found
+bool ReadDoc (FileBuf &buf, const UCArray &docTag,
+	      TextElArray &doc, unsigned long &docLen,
+	      bool compatMode=false);
+
+bool ReadDoc(u_char **buffer, const UCArray &docTag, 
+	     TextElArray &doc, unsigned long &docLen,
+	     bool compatMode=false);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.cpp	(revision 16561)
@@ -0,0 +1,685 @@
+/**************************************************************************
+ *
+ * TextGet.cpp -- Decompressing the text
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+// is important to be first, so we escape the truncation warning on VC++
+#include "TextGet.h"
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "mg_files.h"
+#include "netorder.h"
+#include "mg_errors.h"
+#include "locallib.h"
+#include "words.h"
+#include "local_strings.h"
+#include "bitio_m_stdio.h"
+
+typedef enum huff_type {lengths, chars};
+
+
+static auxiliary_dict *LoadAuxDict (compression_dict &cd, FILE *text_aux_dict) {
+  auxiliary_dict *ad;
+  int i;
+
+  if (!(ad = new auxiliary_dict))
+    {
+      mg_errno = MG_NOMEM;
+      return (NULL);
+    }
+
+  memset (ad, '\0', sizeof (*ad));
+
+  for (i = 0; i <= 1; ++i)
+    {
+      int j;
+      u_char *pos;
+
+      fread (&ad->afh[i], sizeof (aux_frags_header), 1, text_aux_dict);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(ad->afh[i].num_frags);
+      NTOHUL(ad->afh[i].mem_for_frags);
+
+      if (!(ad->word_data[i] = new u_char[ad->afh[i].mem_for_frags]))
+	{
+	  mg_errno = MG_NOMEM;
+	  delete ad;
+	  return (NULL);
+	}
+      if (!(ad->words[i] = new u_char* [ad->afh[i].num_frags]))
+	{
+	  mg_errno = MG_NOMEM;
+	  delete ad;
+	  return (NULL);
+	}
+
+      fread (ad->word_data[i], ad->afh[i].mem_for_frags, sizeof (u_char),
+	     text_aux_dict);
+
+      pos = ad->word_data[i];
+      for (j = 0; j < (int)ad->afh[i].num_frags; ++j)
+	{
+	  ad->words[i][j] = pos;
+	  pos += *pos + 1;
+	}
+      if (cd.cdh.novel_method == MG_NOVEL_HYBRID)
+	{
+	  int num;
+	  num = 1;
+	  ad->blk_start[i][0] = 0;
+	  ad->blk_end[i][0] = cd.cdh.num_words[i] - 1;
+	  while (num < 33)
+	    {
+	      ad->blk_start[i][num] = ad->blk_end[i][num - 1] + 1;
+	      ad->blk_end[i][num] = ad->blk_start[i][num] +
+		(ad->blk_end[i][num - 1] - ad->blk_start[i][num - 1]) * 2;
+	      ++num;
+	    }
+	}
+    }
+  return (ad);
+}
+
+
+static u_char ***ReadInWords (FILE *dict, compression_dict &cd,
+			      comp_frags_header *cfh, u_char **escape) {
+  int i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+  int num_set[MAX_HUFFCODE_LEN + 1];
+  u_char *next_word[MAX_HUFFCODE_LEN + 1];
+  u_char **vals;
+  u_char ***values;
+  u_char word[MAXWORDLEN + 1];
+  u_char last_word[MAX_HUFFCODE_LEN + 1][MAXWORDLEN + 1];
+
+  lookback = cd.cdh.lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; ++i) {
+    ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+    mem_reqd += cfh->huff_words_size[i];
+  }
+
+  if (!(vals = new u_char* [ptrs_reqd]))
+    return (NULL);
+
+  if (!(values = new u_char** [MAX_HUFFCODE_LEN + 1]))
+    return (NULL);
+
+  if (!(next_word[0] = new u_char[mem_reqd])) 
+    return (NULL);
+
+  cd.MemForCompDict += ptrs_reqd * sizeof (*vals) +
+    (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **) +
+    mem_reqd;
+
+  values[0] = vals;
+  values[0][0] = next_word[0];
+  for (i = 1; i <= cfh->hd.maxcodelen; ++i)
+    {
+      int next_start = (values[i - 1] - vals) +
+      ((cfh->hd.lencount[i - 1] + ((1 << lookback) - 1)) >> lookback);
+      values[i] = &vals[next_start];
+      next_word[i] = next_word[i - 1] + cfh->huff_words_size[i - 1];
+      values[i][0] = next_word[i];
+    }
+
+  memset (num_set, '\0', sizeof (num_set));
+
+  for (i = 0; i < cfh->hd.num_codes; ++i)
+    {
+      register int val, copy;
+      register int len = cfh->hd.clens[i];
+      val = getc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      fread (word + copy + 1, sizeof (u_char), val, dict);
+      *word = val + copy;
+
+      if ((num_set[len] & ((1 << lookback) - 1)) == 0)
+	{
+	  values[len][num_set[len] >> lookback] = next_word[len];
+	  memcpy (next_word[len], word, *word + 1);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    *escape = next_word[len];
+	  next_word[len] += *word + 1;
+	}
+      else
+	{
+	  copy = prefixlen (last_word[len], word);
+	  memcpy (next_word[len] + 1, word + copy + 1, *word - copy);
+	  *next_word[len] = (copy << 4) + (*word - copy);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    *escape = next_word[len];
+	  next_word[len] += (*word - copy) + 1;
+	}
+      memcpy (last_word[len], word, *word + 1);
+      ++num_set[len];
+    }
+  if (cfh->hd.clens)
+    delete []cfh->hd.clens;
+  cfh->hd.clens = NULL;
+  return values;
+}
+
+static int Load_Comp_HuffData(compression_dict &cd, int which, FILE *dict, 
+			      huff_type type) {
+  huff_data * hd;
+  u_long **   vals;
+
+  if (!(hd = new huff_data))
+    return 1;
+  cd.MemForCompDict += sizeof (huff_data);
+  if (Read_Huffman_Data (dict, hd, &cd.MemForCompDict, NULL) == -1)
+    return 2;
+  if (!(vals = Generate_Huffman_Vals (hd, &cd.MemForCompDict)))
+    return 3;
+  if (hd->clens)
+    delete []hd->clens;
+  hd->clens = NULL;
+  if (type == chars)
+    {
+      cd.chars_huff[which] = hd;
+      cd.chars_vals[which] = vals;
+    }
+  else
+    {
+      cd.lens_huff[which] = hd;
+      cd.lens_vals[which] = vals;
+    }
+
+  return 0;
+}
+
+static int Load_Comp_FragsHeader(compression_dict &cd, int which, int getEscape,
+				 FILE *dict) {
+  if (!(cd.cfh[which] = new comp_frags_header))
+    return 1;
+  cd.MemForCompDict += sizeof (*cd.cfh[which]);
+  if (Read_cfh (dict, cd.cfh[which], &cd.MemForCompDict, NULL) == -1)
+    return 2;
+
+  if (!(cd.values[which] = ReadInWords (dict, cd, cd.cfh[which],
+					getEscape == 0 ? NULL : &cd.escape[which])))
+    return 3;
+  
+  return 0;
+}
+
+static bool LoadSlowCompDict (FILE *dict, FILE *aux_dict, compression_dict &cd) {
+  if (dict == NULL) return false;
+
+  int which;
+
+  memset (&cd, '\0', sizeof (compression_dict));
+
+  cd.MemForCompDict = sizeof (compression_dict);
+
+  if (Read_cdh (dict, &cd.cdh, &cd.MemForCompDict, NULL) == -1)
+    return false;
+
+  for (which = 0; which < 2; ++which)
+    switch (cd.cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  if (Load_Comp_FragsHeader(cd, which, 0, dict) != 0)
+	    return false;
+	  cd.escape[which] = NULL;
+
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (cd.cdh.num_words[which])
+	    {
+	      if (Load_Comp_FragsHeader(cd, which, 1, dict) != 0)
+		return false;
+	    }
+
+	  if (Load_Comp_HuffData(cd, which, dict, chars) != 0)
+	    return false;
+
+	  if (Load_Comp_HuffData(cd, which, dict, lengths) != 0)
+	    return false;
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (cd.cdh.num_words[which])
+	    {
+	      if (Load_Comp_FragsHeader(cd, which, 1, dict) != 0)
+		return false;
+	    }
+	  switch (cd.cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      if (Load_Comp_HuffData(cd, which, dict, chars) != 0)
+		return false;
+
+	      if (Load_Comp_HuffData(cd, which, dict, lengths) != 0)
+		return false;
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    }
+	  break;
+	}
+      }
+
+  if (cd.cdh.novel_method == MG_NOVEL_DELTA ||
+      cd.cdh.novel_method == MG_NOVEL_HYBRID)
+    {
+      if (!aux_dict)
+	{
+	  mg_errno = MG_NOFILE;
+	  cd.Clear();
+	  return false;
+	}
+
+      if (!(cd.ad = LoadAuxDict (cd, aux_dict)))
+	{
+	  cd.Clear();
+	  return false;
+	}
+    }
+
+  mg_errno = MG_NOERROR;
+
+  cd.fast_loaded = 0;
+  
+  return true;
+}
+
+
+
+#define WORDNO(p, base) ((((char*)(p))-((char*)(base)))/sizeof(u_char*))
+#define IS_FIXUP(p) ((fixup[WORDNO(p,cd)/8] & (1<<(WORDNO(p,cd) & 7))) != 0)
+
+// fast loading really needs to be totally re-writen. "Unloading" the
+// text data will currently cause a crash because memory is being
+// deleted multiple times (and probably a zillion other reasons).
+static bool LoadFastCompDict (FILE *text_fast_comp_dict, compression_dict &_cd) {
+  if (text_fast_comp_dict == NULL) return false;
+  
+  u_long *p, *end;
+  u_char *fixup;
+  u_long mem;
+  u_long fixup_mem;
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  fread (&mem, sizeof (mem), 1, text_fast_comp_dict);
+  NTOHUL(mem);  /* [RPAP - Jan 97: Endian Ordering] */
+  fread (&fixup_mem, sizeof (fixup_mem), 1, text_fast_comp_dict);
+  NTOHUL(fixup_mem);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  compression_dict *cd;
+  if (!(cd = (compression_dict *)malloc (mem))) {
+    mg_errno = MG_NOMEM;
+    return false;
+  }
+
+  end = (u_long *) (((u_char *) cd) + mem);
+  fread (cd, sizeof (u_char), mem, text_fast_comp_dict);
+
+  if (!(fixup = new u_char[fixup_mem]))
+    {
+      mg_errno = MG_NOMEM;
+      return false;
+    }
+
+  fread (fixup, fixup_mem, sizeof (u_char), text_fast_comp_dict);
+
+  for (p = (u_long *) cd; (u_long) p < (u_long) end; ++p)
+    if (IS_FIXUP (p))
+      {
+	NTOHUL(*p);  /* [RPAP - Jan 97: Endian Ordering] */
+	*p = *p + (u_long) cd;
+      }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  /* cdh */
+  NTOHUL(cd->cdh.dict_type);
+  NTOHUL(cd->cdh.novel_method);
+  for (i = 0; i < TEXT_PARAMS; ++i)
+    NTOHUL(cd->cdh.params[i]);
+  NTOHUL(cd->cdh.num_words[0]);
+  NTOHUL(cd->cdh.num_words[1]);
+  NTOHUL(cd->cdh.num_word_chars[0]);
+  NTOHUL(cd->cdh.num_word_chars[1]);
+  NTOHUL(cd->cdh.lookback);
+  /* cfh */
+  for (i = 0; i <= 1; ++i)
+    {
+      int j;
+
+      NTOHSI(cd->cfh[i]->hd.num_codes);
+      NTOHSI(cd->cfh[i]->hd.mincodelen);
+      NTOHSI(cd->cfh[i]->hd.maxcodelen);
+      for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
+	{
+	  NTOHSI(cd->cfh[i]->hd.lencount[j]);
+	  NTOHUL(cd->cfh[i]->hd.min_code[j]);
+	}
+      NTOHUL(cd->cfh[i]->uncompressed_size);
+      for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
+	NTOHUL(cd->cfh[i]->huff_words_size[j]);
+    }
+  NTOHUL(cd->MemForCompDict);
+  /* ad */
+  if (cd->cdh.novel_method == MG_NOVEL_DELTA ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID)
+    for (i = 0; i <= 1; ++i)
+      {
+	int j;
+	
+	NTOHUL(cd->ad->afh[i].num_frags);
+	NTOHUL(cd->ad->afh[i].mem_for_frags);
+	for (j = 0; j < 33; ++j)
+	  {
+	    NTOHSI(cd->ad->blk_start[i][j]);
+	    NTOHSI(cd->ad->blk_end[i][j]);
+	  }
+      }
+  NTOHSI(cd->fast_loaded);
+
+  delete []fixup;
+
+  // the whole fast comp dict is a bit of a hack so I don't
+  // feel too bad about the next line :-) -- Rodger.
+  _cd = *cd;
+  
+  return true;
+}
+
+
+static bool LoadCompDict (FILE *compDictFile,
+			  FILE *auxDictFile,
+			  FILE *fastCompDictFile,
+			  compression_dict &cd) {
+  // see if we have a fast loading compression dictionary
+  if (fastCompDictFile != NULL)
+    return LoadFastCompDict (fastCompDictFile, cd);
+  
+  // slow compression dictionary
+  return LoadSlowCompDict (compDictFile, auxDictFile, cd);
+}
+
+
+// try to open the dictionary files and load the dictionary
+static bool OpenLoadCompDict (char *textname, compression_dict &cd) {
+  FILE *compDictFile = NULL;
+  FILE *auxDictFile = NULL;
+  FILE *fastCompDictFile = NULL;
+
+  fastCompDictFile = open_file (textname, TEXT_DICT_FAST_SUFFIX,
+				"rb", MAGIC_FAST_DICT, MG_CONTINUE);
+  
+  if (fastCompDictFile == NULL) {
+    compDictFile = open_file (textname, TEXT_DICT_SUFFIX,
+			      "rb", MAGIC_DICT, MG_MESSAGE);
+    auxDictFile =  open_file (textname, TEXT_DICT_AUX_SUFFIX,
+			      "rb", MAGIC_AUX_DICT, MG_CONTINUE);
+  }
+  
+  bool res = LoadCompDict (compDictFile, auxDictFile, fastCompDictFile, cd);
+
+  if (compDictFile != NULL) fclose (compDictFile);
+  if (auxDictFile != NULL) fclose (auxDictFile);
+  if (fastCompDictFile != NULL) fclose (fastCompDictFile);
+  
+  return res;
+}
+
+static bool LoadLevels (char *textname, FTextLevel &levels) {
+  FILE *levelFile = NULL;
+
+  // open the text level file
+  levelFile = open_file (textname, TEXT_LEVEL_SUFFIX,
+			 "rb", MAGIC_TEXT_LEVELS, MG_CONTINUE);
+  if (levelFile == NULL) return false;
+
+  // seek to the appropriate place and read the level information
+  bool res = ((fseek (levelFile, sizeof (u_long), SEEK_SET) == 0) &&
+	      levels.Read (levelFile));
+
+  // close the file
+  fclose (levelFile);
+  
+  return res;
+}
+
+
+TextData::TextData () {
+  // put file pointers in known state first
+  textFile = NULL;
+  textIdxFile = NULL;
+  Clear ();
+}
+
+void TextData::Clear () {
+  cd.Clear(); 
+  textFile = NULL;
+  textIdxFile = NULL;
+  cth.Clear();
+  levels.Clear();
+}
+
+bool TextData::LoadData (char *basepath, char *textname) {
+  
+  if (textname[0] == '\0') return false;
+  
+  // set the basepath
+  set_basepath(basepath);
+  
+  // load the compression dictionary
+  if (!OpenLoadCompDict (textname, cd)) return false;
+
+  // open the compressed text and text index file
+  textFile = open_file (textname, TEXT_SUFFIX, "rb", MAGIC_TEXT, MG_CONTINUE);
+  if (textFile == NULL) return false;
+
+  textIdxFile = open_file (textname, TEXT_IDX_SUFFIX, "rb", MAGIC_TEXI, MG_CONTINUE);
+  if (textIdxFile == NULL) return false;
+
+  // read in the compressed text header
+  if ((fseek (textFile, sizeof (u_long), SEEK_SET) != 0) || !cth.Read (textFile))
+    return false;
+
+  // read in the level information
+  if (!LoadLevels (textname, levels)) return false;
+  
+  return true;
+}
+
+bool TextData::UnloadData () {
+  // close any open files
+  if (textFile != NULL) {
+    fclose (textFile);
+    textFile = NULL;
+  }
+  if (textIdxFile != NULL) {
+    fclose (textIdxFile);
+    textIdxFile = NULL;
+  }
+
+  // do general clear
+  Clear ();
+  
+  return true;  
+}
+
+
+bool GetDocIdx (TextData &td, const UCArray &docLevel,
+		unsigned long docNum, TextIdx &docIdx) {
+  // make sure the text index file was opened successfully
+  if (td.textIdxFile == NULL) return false;
+
+  // read in the index
+  TextLevelInfo &levelInfo = td.levels.levelInfo[docLevel];
+  if (!docIdx.Read (td.textIdxFile, levelInfo, docNum)) return false;
+  
+  return true;
+}
+
+
+
+
+#define MY_HUFF_DECODE(len, code, mcodes)				\
+  do {									\
+    register unsigned long *__min_code = (mcodes);			\
+    register unsigned long *__mclen = __min_code;			\
+    register unsigned long __code = 0;					\
+    do									\
+      {									\
+        __code += __code + buffer.bit();	                  	\
+      }									\
+    while (__code < *++__mclen);					\
+    (len) = __mclen - __min_code;					\
+    (code) = __code - *__mclen;						\
+  } while(0);
+
+
+bool GetDocText (TextData &td, const UCArray &docLevel,
+		 unsigned long docNum, UCArray &docText) {
+  // erase the current text
+  docText.erase (docText.begin(), docText.end());
+
+  // look up the information about this document
+  TextIdx docIdx;
+  if (!GetDocIdx (td, docLevel, docNum, docIdx)) return false;
+
+  // do seek to appropriate position
+  stdio_bitio_buffer buffer (td.textFile);
+  buffer.seek (docIdx.start.byte, docIdx.start.bit);
+  
+  // decompress the document
+  compression_dict &cd = td.cd;
+  auxiliary_dict *ad = cd.ad;
+  int which = docIdx.which;
+  unsigned long num_bits = (docIdx.end.byte*8+(8-docIdx.end.bit)) -
+    (docIdx.start.byte*8+(8-docIdx.start.bit));
+  unsigned long bits = 0;
+
+  if (docText.capacity() < docText.size() + num_bits + 1) {
+    docText.reserve(docText.size() + num_bits + 1);
+  }
+  // keep decoding bits until enough bits have been decoded
+  while (bits < num_bits) {
+    register unsigned code, len;
+    register int r;
+    register u_char *t, *b = NULL;
+    u_char word[MAXWORDLEN + 1];
+    
+    if (cd.cfh[which]) {
+      MY_HUFF_DECODE (len, code, cd.cfh[which]->hd.min_code);
+      bits += len;
+      
+      r = code & ((1 << cd.cdh.lookback) - 1);
+      t = cd.values[which][len][code >> cd.cdh.lookback];
+      
+      /* step through from base pointer */
+      b = word + 1;
+      while (r--) {
+	register int copy = *t >> 4;
+	memcpy (word + copy + 1, t + 1, *t & 0xf);
+	word[0] = copy + (*t & 0xf);
+	t += ((*t) & 0xf) + 1;
+      }
+    } else t = NULL;
+    
+    if (t == cd.escape[which]) {
+      switch (cd.cdh.novel_method) {
+      case MG_NOVEL_HUFFMAN_CHARS:
+	{
+	  int len, i;
+	  int c;
+	  len = buffer.huff_decode(cd.lens_huff[which]->min_code,
+				   cd.lens_vals[which], &bits);
+	  for (i = 0; i < len; ++i) {
+	    c = buffer.huff_decode(cd.chars_huff[which]->min_code,
+				   cd.chars_vals[which], &bits);
+	    docText.push_back (c);
+	  }
+	}
+	break;
+      case MG_NOVEL_DELTA:
+      case MG_NOVEL_HYBRID:
+	{
+	  int idx = 0, len;
+	  u_char *base;
+	  switch (cd.cdh.novel_method)
+	    {
+	    case MG_NOVEL_DELTA:
+	      {
+		idx = buffer.delta_decode (&bits);
+		--idx;
+	      }
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      {
+		int k;
+		k = buffer.gamma_decode (&bits);
+		--k;
+		idx = buffer.binary_decode(ad->blk_end[which][k] -
+					   ad->blk_start[which][k] + 1, 
+					   &bits);
+		idx += ad->blk_start[which][k] - 1;
+	      }
+	      break;
+	    }
+	  base = ad->words[which][idx];
+	  len = *base++;
+	  for (; len; --len)
+	    {
+	      docText.push_back (*base++);
+	    }
+	}
+	break;
+      }
+    }
+    else
+      {
+	/* copy over the matching prefix */
+	r = (*t >> 4);
+	while (r--) {
+	  docText.push_back (*b++);
+	}
+	
+	/* and the stored suffix */
+	r = ((*t) & 0xf);
+	while (r--) {
+	  docText.push_back (*++t);
+	}
+      }
+    which = !which;
+  }
+  
+  buffer.done();
+  
+  return true;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/TextGet.h	(revision 16561)
@@ -0,0 +1,58 @@
+/**************************************************************************
+ *
+ * TextGet.h -- Decompressing the text
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef TEXTGET_H
+#define TEXTGET_H
+
+#include "text.h"
+#include "FText.h"
+
+
+class TextData {
+public:
+  compression_dict cd;
+  FILE *textFile;
+  FILE *textIdxFile;
+  compressed_text_header cth;
+  FTextLevel levels;
+
+  TextData ();
+  void Clear ();
+
+  // loads compression dictionary, the compressed text header,
+  // and all level informaiton
+  bool LoadData (char *basepath, char *textname);
+  bool UnloadData ();
+};
+
+
+// note: document numbers start at 1
+
+// get information about where a document starts and finishes
+bool GetDocIdx (TextData &td, const UCArray &docLevel,
+		unsigned long docNum, TextIdx &docIdx);
+
+
+// decompress a document's text
+bool GetDocText (TextData &td, const UCArray &docLevel,
+		 unsigned long docNum, UCArray &docText);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.cpp	(revision 16561)
@@ -0,0 +1,440 @@
+/**************************************************************************
+ *
+ * UCArray.cpp -- vector based string class
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "UCArray.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+#include "unitool.h"
+
+void SetCStr (UCArray &text, const char *cStr) {
+  text.erase(text.begin(), text.end());
+  
+  while (*cStr != '\0') {
+    text.push_back (*cStr);
+    ++cStr;
+  }
+}
+
+void SetCStr (UCArray &text, const char *cStr, size_t nSizeHint) {
+  text.erase(text.begin(), text.end());
+
+  // reserve the needed space in advance
+  if (text.capacity() < nSizeHint + 1) {
+    text.reserve(nSizeHint + 1);
+  }
+  while (*cStr != '\0') {
+    text.push_back (*cStr);
+    ++cStr;
+  }
+}
+
+/** char * returned becomes the responsibility of the caller */
+char * GetCStr(const UCArray& text) {
+
+  char *cstr = new char[text.size()+1];
+  UCArray::const_iterator here = text.begin();
+  UCArray::const_iterator end = text.end();
+
+  int i = 0;
+  while (here != end) {
+    cstr[i] = text[i];
+    ++here; ++i;
+  }
+  cstr[i]='\0';
+  return cstr;
+}
+
+/** char * returned becomes the responsibility of the caller */
+unsigned char * MyGetCStr(const UCArray& text) {
+
+  unsigned char *cstr = new unsigned char[text.size()+1];
+  cstr[0] = text.size();
+  UCArray::const_iterator here = text.begin();
+  UCArray::const_iterator end = text.end();
+
+  int i = 1;
+  while (here != end) {
+    cstr[i] = text[i-1];
+    ++here; ++i;
+  }
+  //  cstr[i]='\0';
+  return cstr;
+}
+
+bool UCArrayCStrEquals(const UCArray &text, const unsigned char *cStr)
+{
+  if ((cStr == NULL || *cStr == '\0') && text.empty()) return true;
+  UCArray::const_iterator thisUC = text.begin();
+  UCArray::const_iterator endUC = text.end();
+  while (thisUC != endUC && *cStr != '\0') {
+    if (*thisUC != *cStr) return false;
+    ++cStr; ++thisUC;
+  }
+  if (thisUC == endUC && *cStr == '\0') return true;
+  return false;
+}
+
+ostream &operator<<(ostream &s, const UCArray &a) {
+  UCArray::const_iterator here = a.begin();
+  UCArray::const_iterator end = a.end();
+  while (here != end) {
+    s << *here;
+    ++here;
+  }
+
+  return s;
+}
+
+
+bool ReadVarLenUL (FILE *f, unsigned long &n) {
+  register unsigned long temp = 0;
+  register unsigned int bitPos = 0;
+  unsigned char b = 0;
+
+  do {
+    b = fgetc (f);
+    if (feof(f)) return false;
+    temp |= (b & 0x7f) << bitPos;
+    bitPos += 7;
+  } while (b >= 0x80 && bitPos < 32);
+
+  n = temp;
+  
+  return true;
+}
+
+bool WriteVarLenUL (FILE *f, unsigned long n) {
+  register unsigned long temp = n;
+  register unsigned char b = 0;
+  do {
+    b = static_cast<unsigned char> (temp & 0x7f);
+    if (temp >= 0x80) b |= 0x80;
+    fputc (b, f);
+    if (ferror (f) != 0) return false;
+  } while ((temp = temp >> 7) > 0);
+  
+  return true;
+}
+
+
+bool ReadUL (FILE *f, unsigned long &n) {
+  if (fread (&n, sizeof (unsigned long), 1, f) <= 0) return false;
+  NTOHUL (n);
+  return true;
+}
+
+
+bool WriteUL (FILE *f, unsigned long n) {
+  HTONUL (n);
+  return (fwrite (&n, sizeof (unsigned long), 1, f) > 0);
+}
+
+bool ReadF (FILE *f, float &n) {
+  if (fread (&n, sizeof (float), 1, f) <= 0) return false;
+  NTOHF(n);
+  return true;
+}
+
+bool WriteF (FILE *f, float n) {
+  HTONF(n);
+  return (fwrite (&n, sizeof (float), 1, f) > 0);
+}
+
+bool ReadD (FILE *f, double &n) {
+  if (fread (&n, sizeof (double), 1, f) <= 0) return false;
+  NTOHD(n);
+  return true;
+}
+
+bool WriteD (FILE *f, double n) {
+  HTOND(n);
+  return (fwrite (&n, sizeof (double), 1, f) > 0);
+}
+
+bool ReadUCArray (FILE *f, UCArray &a) {
+  // clear the array in preparation
+  a.erase (a.begin(), a.end());
+
+  // read in the array size
+  unsigned long arraySize = 0;
+  if (!ReadVarLenUL (f, arraySize)) return false;
+
+  // reserve the needed space in advance
+  if (a.capacity() < arraySize + 1) {
+    a.reserve(arraySize + 1);
+  }
+
+  // read in the array
+  unsigned char b = 0;
+  while (arraySize > 0) {
+    b = fgetc (f);
+    if (feof(f)) return false;
+    a.push_back (b);
+    
+    --arraySize;
+  }
+  
+  return true;
+}
+
+bool WriteUCArray (FILE *f, const UCArray &a) {
+  // write out the array size
+  if (!WriteVarLenUL (f, a.size())) return false;
+
+  UCArray::const_iterator here = a.begin();
+  UCArray::const_iterator end = a.end();
+  while (here != end) {
+    fputc (*here, f);
+    if (ferror (f) != 0) return false;
+    
+    ++here;
+  }
+  
+  return true;
+}
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+// static unsigned char casecharmap[] = {
+// 	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+// 	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+// 	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+// 	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+// 	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+// 	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+// 	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+// 	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+// 	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+// 	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+// 	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+// 	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+// 	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+// 	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+// 	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+// 	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+// 	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+// 	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+// 	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+// 	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+// 	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+// 	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+// 	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+// 	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+// 	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+// 	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+// 	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+// 	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+// 	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+// 	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+// 	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+// 	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+// };
+
+int DictCompare (const UCArray &a1, const UCArray &a2) {
+  unsigned short a1_out[256]; /* temp space */
+  unsigned short a2_out[256]; /* temp space */
+
+  unsigned char * a1_str = (unsigned char *)MyGetCStr(a1);
+  unsigned char * a2_str = (unsigned char *)MyGetCStr(a2);
+  
+  /* decode the words to unicode */
+  utf8_word_to_unicode (a1_str, a1_out, 255);
+  utf8_word_to_unicode (a2_str, a2_out, 255);
+
+  delete []a1_str;
+  delete []a2_str;
+
+  int l1 = a1_out[0];
+  int l2 = a2_out[0];
+
+  int len = (l1 < l2) ? l1 : l2;
+  int pos = 0;
+  int diff = 0;
+  for (int i=1; i<=len; ++i) {
+    if ((diff = unicode_tosimplified(unicode_tolower(a1_out[i])) - 
+	 unicode_tosimplified(unicode_tolower(a2_out[i]))) != 0) {
+      return diff;
+    }
+    if ((pos == 0) && (diff = a1_out[i] - a2_out[i]) != 0) {
+      pos = diff;
+    }
+  }
+  return ((l1 - l2) ? (l1 - l2) : (pos));
+  
+}
+
+// int DictCompare (const UCArray &a1, const UCArray &a2) {
+//   unsigned int l1 = a1.size();
+//   unsigned int l2 = a2.size();
+//   unsigned int l = (l1 < l2) ? l1 : l2;
+//   int pos = 0;
+//   register int diff = 0;
+
+//   UCArray::const_iterator a1Here = a1.begin();
+//   UCArray::const_iterator a2Here = a2.begin();
+  
+//   while (l--) {
+//     if ((diff = casecharmap[*a1Here] - casecharmap[*a2Here]) != 0)
+//       return diff;
+//     if (pos == 0 && (diff = *a1Here - *a2Here) != 0)
+//       pos = diff;
+
+//     ++a1Here;
+//     ++a2Here;
+//   }
+
+//   return ((l1 - l2) ? (l1 - l2) : (pos));
+// }
+
+// does the first string start with the second?
+bool StartsWith (const UCArray &a1, const UCArray &a2) {
+  unsigned int l1 = a1.size();
+  unsigned int l2 = a2.size();
+  if (l2 > l1) {
+    // if the prefix is longer than the string, it can't start with it
+    return false; 
+  }
+  unsigned int l =l2;
+  UCArray::const_iterator a1Here = a1.begin();
+  UCArray::const_iterator a2Here = a2.begin();
+  
+  while (l--) {
+    if ((*a1Here != *a2Here))
+      return false;
+    ++a1Here;
+    ++a2Here;
+  }
+  return true; // we have successfully matched the whole way
+    
+}
+
+// does the first string start with the second, ignoring case?
+bool StartsWithCasefold(const UCArray &a1, const UCArray &a2) {
+  unsigned int l1 = a1.size();
+  unsigned int l2 = a2.size();
+  if (l2 > l1) {
+    // if the prefix is longer than the string, it can't start with it
+    return false; 
+  }
+  unsigned short a1_out[256]; /* temp space */
+  unsigned short a2_out[256]; /* temp space */
+  unsigned char * a1_str = (unsigned char *)MyGetCStr(a1);
+  unsigned char * a2_str = (unsigned char *)MyGetCStr(a2);
+  
+  /* decode the words to unicode */
+  utf8_word_to_unicode (a1_str, a1_out, 255);
+  utf8_word_to_unicode (a2_str, a2_out, 255);
+
+  delete []a1_str;
+  delete []a2_str;
+
+  unsigned int len = a2_out[0];;  
+  for (int i=1; i<=len; ++i) {
+    if (unicode_tosimplified(unicode_tolower(a1_out[i])) != 
+	unicode_tosimplified(unicode_tolower(a2_out[i])) ) return false;
+    
+  }
+  return true; // we have successfully matched the whole way
+    
+}
+
+// does the first string start with the second, ignoring case?
+// bool StartsWithCasefold(const UCArray &a1, const UCArray &a2) {
+//   unsigned int l1 = a1.size();
+//   unsigned int l2 = a2.size();
+//   if (l2 > l1) {
+//     // if the prefix is longer than the string, it can't start with it
+//     return false; 
+//   }
+//   unsigned int l =l2;
+//   UCArray::const_iterator a1Here = a1.begin();
+//   UCArray::const_iterator a2Here = a2.begin();
+  
+//   while (l--) {
+//     if (casecharmap[*a1Here] != casecharmap[*a2Here]) 
+//       return false;
+//     ++a1Here;
+//     ++a2Here;
+//   }
+//   return true; // we have successfully matched the whole way
+    
+// }
+
+
+unsigned long PrefixLen (const UCArray &a1, const UCArray &a2) {
+  unsigned long l = (a1.size() < a2.size()) ? a1.size() : a2.size();
+  unsigned long i = 0;
+
+  UCArray::const_iterator a1Here = a1.begin();
+  UCArray::const_iterator a2Here = a2.begin();
+
+  while (i < l && *a1Here == *a2Here) {
+    ++i; ++a1Here; ++a2Here;
+  }
+  
+  return i;
+}
+
+bool WritePreSufStr (FILE *f, const UCArray *prev, const UCArray &a) {
+  unsigned char preLen;
+  unsigned char sufLen;
+  
+  if (prev != NULL) preLen = PrefixLen (*prev, a);
+  else preLen = 0;
+  sufLen = a.size() - preLen;
+
+  // output the prefix length, suffix length, and the suffix
+  fputc (preLen, f);
+  if (ferror(f) != 0) return false;
+  fputc (sufLen, f);
+  if (ferror(f) != 0) return false;
+  char* tmp=GetCStr(a);
+  int ret=(fwrite (tmp+preLen, sizeof (char), sufLen, f) == sufLen);
+  delete []tmp;
+  return (ret != 0);
+}
+
+// a also used for prev
+bool ReadPreSufStr (FILE *f, UCArray &a) {
+  unsigned char preLen = 0;
+  unsigned char sufLen = 0;
+
+  preLen = fgetc(f);
+  sufLen = fgetc(f);
+
+  if (a.size() > preLen) a.erase (a.begin()+preLen, a.end());
+
+  // reserve the needed space in advance
+  if (a.capacity() < a.size() + sufLen + 1) {
+    a.reserve(a.size() + sufLen + 1);
+  }
+
+  while (sufLen > 0) {
+    unsigned char c = fgetc (f);
+    a.push_back (c);
+    --sufLen;
+  }
+
+  return true;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/UCArray.h	(revision 16561)
@@ -0,0 +1,131 @@
+/**************************************************************************
+ *
+ * UCArray.h -- vector based string class
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef UCARRAY_H
+#define UCARRAY_H
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#pragma warning(disable:4786)
+#endif
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if !defined (GSDL_NAMESPACE_BROKEN) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+using namespace std;
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#  include <ospace\std\set>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#  include <set.h>
+#else
+#  include <vector>
+#  include <set>
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#include <stdio.h>
+
+
+typedef vector<unsigned char> UCArray;
+
+// functions to manipulate UCArrays
+void SetCStr (UCArray &text, const char *cStr);
+// same as SetCStr but first tries to allocate nSizeHint space (only if needed)
+void SetCStr (UCArray &text, const char *cStr, size_t nSizeHint);
+char * GetCStr(const UCArray& text);
+inline void UCArrayClear (UCArray &a) {
+  a.erase (a.begin(), a.end());
+}
+bool UCArrayCStrEquals(const UCArray &text, const unsigned char *cStr);
+inline bool UCArrayCStrEquals(const UCArray &text, const char *cStr) { return UCArrayCStrEquals(text, (const unsigned char *)cStr); }
+
+// stream operator to print UCArray
+ostream &operator<<(ostream &s, const UCArray &a);
+
+// used for reading and writing unsigned characters
+inline bool ReadUC (FILE *f, unsigned char &c) {
+  return (fread (&c, sizeof (unsigned char), 1, f) == 1);
+}
+inline bool WriteUC (FILE *f, unsigned char c) {
+  return (fwrite (&c, sizeof (unsigned char), 1, f) == 1);
+}
+
+// used for reading and writing variable length unsigned longs
+bool ReadVarLenUL (FILE *f, unsigned long &n);
+bool WriteVarLenUL (FILE *f, unsigned long n);
+
+// used for reading and writing unsigned longs
+bool ReadUL (FILE *f, unsigned long &n);
+bool WriteUL (FILE *f, unsigned long n);
+
+// used for reading and writing floats
+bool ReadF (FILE *f, float &n);
+bool WriteF (FILE *f, float n);
+
+// used for reading and writing doubles
+bool ReadD (FILE *f, double &n);
+bool WriteD (FILE *f, double n);
+
+// used for reading and writing arrays to files
+bool ReadUCArray (FILE *f, UCArray &a);
+bool WriteUCArray (FILE *f, const UCArray &a);
+
+
+// compares the two strings in dictionary order
+int DictCompare (const UCArray &a1, const UCArray &a2);
+
+// does the first string start with the second?
+bool StartsWith(const UCArray &a1, const UCArray &a2);
+// does the first string start with the second, ignoring case?
+bool StartsWithCasefold(const UCArray &a1, const UCArray &a2);
+
+struct LTUCArray {
+  bool operator()(const UCArray &a1, const UCArray &a2) const
+    { return (a1 < a2); }
+};
+
+struct DictLTUCArray {
+  bool operator() (const UCArray &a1, const UCArray &a2) const
+    { return (DictCompare (a1, a2) < 0); }
+};
+
+
+typedef set<UCArray, LTUCArray> UCArraySet;
+typedef vector<UCArray> UCArrayVector;
+
+unsigned long PrefixLen (const UCArray &a1, const UCArray &a2);
+
+// prev == NULL if no previous string
+bool WritePreSufStr (FILE *f, const UCArray *prev, const UCArray &a);
+bool ReadPreSufStr (FILE *f, UCArray &a); // a also used for prev
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.cpp	(revision 16561)
@@ -0,0 +1,126 @@
+/**************************************************************************
+ *
+ * Weights.cpp -- Dealing with document weights
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "UCArray.h"
+#include "Weights.h"
+#include "sysfuncs.h"
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+
+ApproxWeightsData::ApproxWeightsData () {
+  weightBuf = NULL;
+  table = NULL;
+  Free();
+}
+
+ApproxWeightsData::~ApproxWeightsData () {
+  Free ();
+}
+
+void ApproxWeightsData::Free () {
+  bits = 0;
+  mask = 0;
+  L = 0.0;
+  B = 0.0;
+  numLevelDocs = 0;
+  if (weightBuf != NULL) {
+    delete [] weightBuf;
+    weightBuf = NULL;
+  }
+  if (table != NULL) {
+    delete [] table;
+    table = NULL;
+  }
+}
+
+bool ApproxWeightsData::Read (FILE *approxWeightsFile, unsigned long diskPtr,
+			      unsigned long _numLevelDocs) {
+  Free ();
+  
+  // go to the appropriate place in the weights file
+  fseek (approxWeightsFile, diskPtr, SEEK_SET);
+
+  // read in approx weights parameters
+  ReadUC (approxWeightsFile, bits);
+  ReadD (approxWeightsFile, L);
+  ReadD (approxWeightsFile, B);
+
+  mask = (bits == 32) ? 0xffffffff : (1 << bits) - 1;
+  numLevelDocs = _numLevelDocs;
+
+  // allocate buffer for the weights
+  unsigned long arrSize = (bits*numLevelDocs+sizeof(unsigned long)-1) / 32;
+  weightBuf = new unsigned long[arrSize];
+
+  // read in the weights
+  unsigned long i;
+  for (i=0; i<arrSize; ++i) {
+    ReadUL (approxWeightsFile, weightBuf[i]);
+  }
+
+  // create precomputed table of values if it will be small enough
+  // (if not small enough table == NULL
+  if (bits <= 12) {
+    unsigned long i, tableSize = (1 << bits);
+    table = new float[tableSize];
+    table[0] = L;
+    for (i = 1; i < tableSize; ++i)
+      table[i] = table[i - 1] * B;
+  }
+
+  return true;
+}
+
+float ApproxWeightsData::GetLowerApproxDocWeight (unsigned long levelDocNum) {
+  // sanity check
+  if (bits == 0 || weightBuf == NULL) return 1.0;
+
+  register unsigned long c, pos;
+  register unsigned long *dw;
+
+  // get the compressed version of the weight
+  pos = levelDocNum * bits;
+  dw = &weightBuf[pos / MAXBITS];
+  pos &= (MAXBITS - 1);
+  c = *dw >> pos;
+  if (pos + bits > MAXBITS)
+    c |= *(dw + 1) << (MAXBITS - pos);
+  c &= mask;
+
+  // decompress the weight
+  if (table != NULL) return table[c];
+  return (L * pow (B, (double) c));
+}
+
+
+
+float GetExactDocWeight (FILE *exactWeightsFile, unsigned long diskPtr,
+			 unsigned long levelDocNum) {
+  // go to the appropriate place in the weights file
+  fseek (exactWeightsFile, diskPtr+sizeof(float)*levelDocNum, SEEK_SET);
+
+  // read in the exact weight
+  float weight;
+  ReadF (exactWeightsFile, weight);
+  
+  return weight;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/Weights.h	(revision 16561)
@@ -0,0 +1,56 @@
+/**************************************************************************
+ *
+ * Weights.h -- Dealing with document weights
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef WEIGHTS_H
+#define WEIGHTS_H
+
+#include <stdio.h>
+
+
+class ApproxWeightsData {
+public:
+  unsigned char bits;
+  unsigned long mask;
+  double L;
+  double B;
+  unsigned long numLevelDocs;
+  unsigned long *weightBuf;
+  float *table;   // precomputed translation table
+  
+  ApproxWeightsData ();
+  ~ApproxWeightsData ();
+
+  void Free (); // frees any allocated memory
+  
+  bool Read (FILE *approxWeightsFile, unsigned long diskPtr,
+	     unsigned long _numLevelDocs);
+
+  float GetLowerApproxDocWeight (unsigned long levelDocNum);
+};
+
+
+// diskPtr points to the start of the exact document weights
+// for this level
+float GetExactDocWeight (FILE *exactWeightsFile, unsigned long diskPtr,
+			 unsigned long levelDocNum);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/WordData.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/WordData.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/WordData.h	(revision 16561)
@@ -0,0 +1,47 @@
+/**************************************************************************
+ *
+ * WordData.h -- 
+ * Copyright (C) 1999  NZDL
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+class WordData
+{
+
+ public:
+  unsigned long docCount;
+  unsigned long wordCount;
+
+  inline void read(FILE *f)
+    {
+      fread((char *) &docCount, sizeof(docCount), 1, f);
+      fread((char *) &wordCount, sizeof(wordCount), 1, f);
+
+      NTOHUL(docCount);
+      NTOHUL(wordCount);
+    }
+
+  inline unsigned long documents()
+    {
+      return docCount;
+    }
+
+  inline int words()
+    {
+      return wordCount;
+    }
+};
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/build.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/build.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/build.h	(revision 16561)
@@ -0,0 +1,86 @@
+/**************************************************************************
+ *
+ * build.h -- Global information for the passes of mg_passes
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#ifndef H_BUILD
+#define H_BUILD
+
+#include "longlong.h"
+
+#include "TextEl.h"
+#include "TagInfo.h"
+
+
+int init_special (const TagInfo &tagInfo, char *file_name);
+int init_text_1 (const TagInfo &tagInfo, char *file_name);
+int init_text_2 (const TagInfo &tagInfo, char *file_name);
+int init_ivf_1 (const TagInfo &tagInfo, char *file_name);
+int init_ivf_2 (const TagInfo &tagInfo, char *file_name);
+/*
+ * file_name  IN      The name of the dictionary file
+ * 
+ * returns      COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot read file
+ */
+
+
+
+
+
+int process_special (const TagInfo &tagInfo, const TextElArray &doc);
+int process_text_1 (const TagInfo &tagInfo, const TextElArray &doc);
+int process_text_2 (const TagInfo &tagInfo, const TextElArray &doc);
+int process_ivf_1 (const TagInfo &tagInfo, const TextElArray &doc);
+int process_ivf_2 (const TagInfo &tagInfo, const TextElArray &doc);
+/*
+ * returns      COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot read file
+ *
+ * The calling routine is responsible for ensuring that s_out is long 
+ * enough.
+ */
+
+
+
+
+
+int done_special (const TagInfo &tagInfo, char *filename);
+int done_text_1 (const TagInfo &tagInfo, char *filename);
+int done_text_2 (const TagInfo &tagInfo, char *filename);
+int done_ivf_1 (const TagInfo &tagInfo, char *filename);
+int done_ivf_2 (const TagInfo &tagInfo, char *filename);
+/*
+ * returns    COMPALLOK for all ok,
+ *              COMPERROR for any error.  e.g. cannot write file
+ */
+
+
+
+
+extern unsigned long invf_buffer_size;
+/*
+ * The amount of memory to allocate to the invertion buffer.
+ */
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.cpp	(revision 16561)
@@ -0,0 +1,229 @@
+/**************************************************************************
+ *
+ * comp_dict.cpp -- Functions for loading the compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "huffman.h"
+#include "local_strings.h"
+#include "memlib.h"
+#include "messages.h"
+
+#include "mg.h"
+#include "hash.h"
+#include "text.h"
+#include "comp_dict.h"
+#include "locallib.h"
+#include "mg_files.h"
+
+compression_dict_header cdh;
+compressed_text_header cth;
+comp_frags_header cfh[2];
+
+dict_hash_table *ht[2];
+
+huff_data char_huff[2];
+huff_data lens_huff[2];
+u_long *char_codes[2], *lens_codes[2];
+u_long Words_disk = 0;
+u_long Chars_disk = 0;
+
+
+static dict_hash_table *
+ReadInWords (FILE * dict, comp_frags_header * cfh,
+	     int esc)
+{
+  int i;
+  u_char *allwords, *prev = NULL;
+  dict_hash_table *ht;
+  u_char **words;
+  u_long ht_size;
+
+  ht_size = prime (cfh->hd.num_codes * HASH_RATIO);
+  if (!(ht = (dict_hash_table *) Xmalloc (sizeof (dict_hash_table) +
+		                            (ht_size - 1) * sizeof (ht->table[0]))))
+    {
+      Message ("no memory for hash_table\n");
+      return NULL;
+    }
+
+  ht->size = ht_size;
+  ht->hd = &cfh->hd;
+
+  if (!(ht->codes = Generate_Huffman_Codes (&cfh->hd, NULL)))
+    {
+      Message ("no memory for huffman codes\n");
+      return NULL;
+    }
+
+  if (!(ht->words = (u_char **) Xmalloc (sizeof (u_char *) * cfh->hd.num_codes)))
+    {
+      Message ("no memory for word pointers\n");
+      return NULL;
+    }
+  words = ht->words;
+
+  memset (ht->table, '\0', ht_size * sizeof (ht->table[0]));
+
+  if (!(allwords = (u_char *) Xmalloc (sizeof (u_char) * cfh->uncompressed_size)))
+    {
+      Message ("no memory for words\n");
+      return NULL;
+    }
+
+  for (i = 0; i < cfh->hd.num_codes; ++i, ++words)
+    {
+      register int val, copy;
+      val = fgetc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      *words = allwords;
+
+      memcpy (allwords + 1, prev + 1, copy);
+      fread (allwords + copy + 1, sizeof (u_char), val, dict);
+      *allwords = val + copy;
+
+      Words_disk += val + 1;
+
+      /* insert into the hash table */
+      if (i < cfh->hd.num_codes - esc)
+	{
+	  register u_char **wptr;
+	  register unsigned long tsize = ht->size;
+	  register unsigned long hashval, step;
+
+	  HASH (hashval, step, allwords, tsize);
+
+	  wptr = ht->table[hashval];
+	  for (; wptr;)
+	    {
+	      hashval += step;
+	      if (hashval >= tsize)
+		hashval -= tsize;
+	      wptr = ht->table[hashval];
+	    }
+	  ht->table[hashval] = words;
+	}
+      prev = allwords;
+      allwords += *allwords + 1;
+    }
+  return ht;
+}
+
+inline int LoadWords(FILE *dict, int which)
+{
+  if (Read_cfh (dict, &cfh[which], NULL, &Words_disk) == -1)
+    return 1;
+  
+  if (!(ht[which] = ReadInWords (dict, &cfh[which], 0)))
+    return 1;
+
+  return 0;
+}
+
+inline int LoadHuffmanCodes(FILE *dict, int which)
+{
+  if (Read_Huffman_Data (dict, &char_huff[which], NULL,
+			 &Chars_disk) == -1)
+    return 1;
+  if (!(char_codes[which] =
+	Generate_Huffman_Codes (&char_huff[which], NULL)))
+    return 2;
+  if (Read_Huffman_Data (dict, &lens_huff[which], NULL,
+			 &Chars_disk) == -1)
+    return 3;
+  if (!(lens_codes[which] =
+	Generate_Huffman_Codes (&lens_huff[which], NULL)))
+    return 4;
+
+  return 0;
+}
+
+int 
+LoadCompressionDictionary (char *dict_file_name)
+{
+  FILE *dict;
+  int which;
+  if (!(dict = open_named_file (dict_file_name, "rb", MAGIC_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    return COMPERROR;
+
+  Words_disk = sizeof (u_long);
+
+  if (Read_cdh (dict, &cdh, NULL, &Words_disk) == -1)
+    goto error;
+
+  for (which = 0; which < 2; ++which)
+    switch (cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  if (LoadWords(dict, which) != 0)
+	    goto error;
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    {
+	      if (LoadWords(dict, which) != 0)
+		goto error;
+	    }
+	  else
+	    ht[which] = NULL;
+	  if (LoadHuffmanCodes(dict, which) != 0)
+	    goto error;
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (cdh.num_words[which])
+	    {
+	      if (LoadWords(dict, which) != 0)
+		goto error;
+	    }
+	  else
+	    ht[which] = NULL;
+	  switch (cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      if (LoadHuffmanCodes(dict, which) != 0)
+		goto error;
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    default:
+	      FatalError (1, "Bad novel method");
+	    }
+	}
+	break;
+      default:
+	FatalError (1, "Bad dictionary kind\n");
+      }
+
+  return (COMPALLOK);
+
+
+error:
+  fclose (dict);
+  return (COMPERROR);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/comp_dict.h	(revision 16561)
@@ -0,0 +1,48 @@
+/**************************************************************************
+ *
+ * comp_dict.h -- Functions for loading the compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define HASH_RATIO 2
+
+typedef struct dict_hash_table
+  {
+    u_long size;
+    huff_data *hd;
+    u_long *codes;
+    u_char **words;
+    u_char **table[1];
+  }
+dict_hash_table;
+
+extern compression_dict_header cdh;
+extern compressed_text_header cth;
+extern comp_frags_header cfh[2];
+
+extern dict_hash_table *ht[2];
+
+extern huff_data char_huff[2];
+extern huff_data lens_huff[2];
+extern u_long *char_codes[2], *lens_codes[2];
+extern u_long Words_disk;
+extern u_long Chars_disk;
+
+
+
+int LoadCompressionDictionary (char *dict_file_name);
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/hash.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/hash.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/hash.h	(revision 16561)
@@ -0,0 +1,39 @@
+/**************************************************************************
+ *
+ * hash.h -- Hash function
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_HASH
+#define H_HASH
+
+#define HASH(h, step, wd, tablesize)			\
+	do {						\
+		register u_char *p=wd;			\
+		register long len = *p++;		\
+		register unsigned long hval;		\
+		if (len>25) len = 25;			\
+		for (hval=0; len; --len)		\
+		  hval = (hval << 5) - hval + *p++;	\
+		step = hval % (tablesize - 2) + 1;	\
+		h = hval % tablesize;			\
+	} while (0)
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/invf.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/invf.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/invf.cpp	(revision 16561)
@@ -0,0 +1,914 @@
+/**************************************************************************
+ *
+ * invf.cpp -- Data structures for inverted files
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "invf.h"
+#include "UCArray.h"
+
+
+invf_dict_header::invf_dict_header () {
+  Clear();
+}
+
+invf_dict_header::~invf_dict_header () {
+}
+
+void invf_dict_header::Clear() {
+  lookback = 0;
+  word_dict_start = 0;
+  word_dict_size = 0;
+  tag_dict_start = 0;
+  tag_dict_size = 0;
+  num_docs = 0;
+  num_frags = 0;
+  num_words = 0;
+  total_bytes = 0;
+  index_string_bytes = 0;
+  num_levels = 0;
+}
+
+bool invf_dict_header::Read (FILE *f) {
+  return (ReadUL (f, lookback) &&
+	  ReadUL (f, word_dict_start) &&
+	  ReadUL (f, word_dict_size) &&
+	  ReadUL (f, tag_dict_start) &&
+	  ReadUL (f, tag_dict_size) &&
+	  ReadUL (f, num_docs) &&
+	  ReadUL (f, num_frags) &&
+	  ReadUL (f, num_words) &&
+	  ReadUL (f, total_bytes) &&
+	  ReadUL (f, index_string_bytes) &&
+	  ReadUL (f, num_levels));
+}
+
+bool invf_dict_header::Write (FILE *f) const {
+  return (WriteUL (f, lookback) &&
+	  WriteUL (f, word_dict_start) &&
+	  WriteUL (f, word_dict_size) &&
+	  WriteUL (f, tag_dict_start) &&
+	  WriteUL (f, tag_dict_size) &&
+	  WriteUL (f, num_docs) &&
+	  WriteUL (f, num_frags) &&
+	  WriteUL (f, num_words) &&
+	  WriteUL (f, total_bytes) &&
+	  WriteUL (f, index_string_bytes) &&
+	  WriteUL (f, num_levels));
+}
+
+
+
+void dict_el::Clear () {
+  el.erase (el.begin(), el.end());
+  frag_occur = 0;
+  freq = 0;
+}
+
+bool dict_el::Read (FILE *f) {
+  return (ReadPreSufStr (f, el) &&
+	  ReadUL (f, frag_occur) &&
+	  ReadUL (f, freq));
+}
+
+bool dict_el::Write (FILE *f, const UCArray *lastEl) const {
+  return (WritePreSufStr (f, lastEl, el) &&
+	  WriteUL (f, frag_occur) &&
+	  WriteUL (f, freq));
+}
+
+
+void word_dict_el::Clear () {
+  dict_el::Clear();
+  if (levelFreqs != NULL) delete [] levelFreqs;
+  levelFreqs = NULL;
+}
+
+word_dict_el::~word_dict_el () {
+  if (levelFreqs != NULL) delete [] levelFreqs;
+}
+
+void word_dict_el::SetNumLevels (unsigned long numLevels) {
+  if (levelFreqs != NULL) delete [] levelFreqs;
+  levelFreqs = new unsigned long [numLevels];
+}
+  
+bool word_dict_el::Read (FILE *f, unsigned long numLevels) {
+  if (!dict_el::Read (f)) return false;
+
+  if (levelFreqs == NULL) return false;
+  
+  unsigned long i;
+  for (i=0; i<numLevels; ++i) {
+    if (!ReadUL (f, levelFreqs[i])) return false;
+  }
+
+  return true;
+}
+
+bool word_dict_el::Write (FILE *f, const UCArray *lastEl,
+			  unsigned long numLevels) const {
+  if (!dict_el::Write (f, lastEl)) return false;
+
+  if (levelFreqs == NULL) return false;
+
+  unsigned long i;
+  for (i=0; i<numLevels; ++i) {
+    if (!WriteUL (f, levelFreqs[i])) return false;
+  }
+
+  return true;
+}
+
+
+
+block_dict_header::block_dict_header () {
+  Clear();
+}
+
+void block_dict_header::Clear () {
+  invf_dict_header::Clear();
+
+  entries_per_wblk = 0;
+  num_wblks = 0;
+  max_wblk_size = 0;
+  wblk_start = 0;
+  wblk_idx_start = 0;
+  
+  entries_per_tblk = 0;
+  num_tblks = 0;
+  max_tblk_size = 0;
+  tblk_start = 0;
+  tblk_idx_start = 0;
+}
+
+bool block_dict_header::Read (FILE *f) {
+  return (invf_dict_header::Read (f) &&
+
+	  ReadUL (f, entries_per_wblk) &&
+	  ReadUL (f, num_wblks) &&
+	  ReadUL (f, max_wblk_size) &&
+	  ReadUL (f, wblk_start) &&
+	  ReadUL (f, wblk_idx_start) &&
+  
+	  ReadUL (f, entries_per_tblk) &&
+	  ReadUL (f, num_tblks) &&
+	  ReadUL (f, max_tblk_size) &&
+	  ReadUL (f, tblk_start) &&
+	  ReadUL (f, tblk_idx_start));
+}
+
+bool block_dict_header::Write (FILE *f) const {
+  return (invf_dict_header::Write (f) &&
+
+	  WriteUL (f, entries_per_wblk) &&
+	  WriteUL (f, num_wblks) &&
+	  WriteUL (f, max_wblk_size) &&
+	  WriteUL (f, wblk_start) &&
+	  WriteUL (f, wblk_idx_start) &&
+  
+	  WriteUL (f, entries_per_tblk) &&
+	  WriteUL (f, num_tblks) &&
+	  WriteUL (f, max_tblk_size) &&
+	  WriteUL (f, tblk_start) &&
+	  WriteUL (f, tblk_idx_start));
+}
+
+
+
+
+
+void block_dict_el::Clear () {
+  el.erase (el.begin(), el.end());
+  frag_occur = 0;
+  freq = 0;
+  invf_ptr = 0;
+}
+
+bool block_dict_el::Read (FILE *f) {
+  return (ReadPreSufStr (f, el) &&
+	  ReadUL (f, frag_occur) &&
+	  ReadUL (f, freq) &&
+	  ReadUL (f, invf_ptr));
+}
+
+bool block_dict_el::Write (FILE *f, const UCArray *lastEl) const {
+  return (WritePreSufStr (f, lastEl, el) &&
+	  WriteUL (f, frag_occur) &&
+	  WriteUL (f, freq) &&
+	  WriteUL (f, invf_ptr));
+}
+
+
+
+
+
+void word_block_dict_el::Clear () {
+  block_dict_el::Clear();
+  if (levelFreqs != NULL) delete [] levelFreqs;
+  levelFreqs = NULL;
+}
+
+word_block_dict_el::~word_block_dict_el () {
+  if (levelFreqs != NULL) delete [] levelFreqs;
+}
+
+void word_block_dict_el::SetNumLevels (unsigned long numLevels) {
+  if (levelFreqs != NULL) delete [] levelFreqs;
+  levelFreqs = new unsigned long [numLevels];
+}
+
+bool word_block_dict_el::Read (FILE *f, unsigned long numLevels) {
+  if (!block_dict_el::Read (f)) return false;
+  
+  if (levelFreqs == NULL) return false;
+  
+  unsigned long i;
+  for (i=0; i<numLevels; ++i) {
+    if (!ReadUL (f, levelFreqs[i])) return false;
+  }
+
+  return true;
+}
+
+bool word_block_dict_el::Write (FILE *f, const UCArray *lastEl,
+				unsigned long numLevels) const {
+  if (!block_dict_el::Write (f, lastEl)) return false;
+  
+  if (levelFreqs == NULL) return false;
+  
+  unsigned long i;
+  for (i=0; i<numLevels; ++i) {
+    if (!WriteUL (f, levelFreqs[i])) return false;
+  }
+  
+  return true;
+}
+
+
+
+
+block_idx_info::block_idx_info () {
+  Clear ();
+}
+
+void block_idx_info::Clear () {
+  el.erase (el.begin(), el.end());
+  block_ptr = 0;
+}
+
+bool block_idx_info::Read (FILE *f) {
+  return (ReadUCArray (f, el) &&
+	  ReadUL (f, block_ptr));
+}
+
+bool block_idx_info::Write (FILE *f) const {
+  return (WriteUCArray (f, el) &&
+	  WriteUL (f, block_ptr));
+}
+
+
+bool ReadBlockIdx (FILE *f, block_idx &blockIdx) {
+  blockIdx.erase (blockIdx.begin(), blockIdx.end());
+  
+  // read in the array size
+  unsigned long arraySize = 0;
+  if (!ReadVarLenUL (f, arraySize)) return false;
+  
+  // read in the array
+  block_idx_info bi;
+  while (arraySize > 0) {
+    if (!bi.Read (f)) return false;
+    blockIdx.push_back (bi);
+    
+    --arraySize;
+  }
+  
+  return true;
+}
+
+bool WriteBlockIdx (FILE *f, const block_idx &blockIdx) {
+  // write out the array size
+  if (!WriteVarLenUL (f, blockIdx.size())) return false;
+
+  block_idx::const_iterator here = blockIdx.begin();
+  block_idx::const_iterator end = blockIdx.end();
+  while (here != end) {
+    if (!(*here).Write (f)) return false;
+    ++here;
+  }
+
+  return true;
+}
+
+
+
+
+stem_idx_header::stem_idx_header () {
+  Clear ();
+}
+
+void stem_idx_header::Clear () {
+  lookback = 0;
+  dict_size = 0;
+
+  entries_per_block = 0;
+  num_blocks = 0;
+  max_block_size = 0;
+  blocks_start = 0;
+  block_idx_start = 0;
+  
+  stemmer_num = 0;
+  stem_method = 0;
+}
+
+bool stem_idx_header::Read (FILE *f) {
+  return (ReadUL (f, lookback) &&
+	  ReadUL (f, dict_size) &&
+	  
+	  ReadUL (f, entries_per_block) &&
+	  ReadUL (f, num_blocks) &&
+	  ReadUL (f, max_block_size) &&
+	  ReadUL (f, blocks_start) &&
+	  ReadUL (f, block_idx_start) &&
+	  
+	  ReadUL (f, stemmer_num) &&
+	  ReadUL (f, stem_method));
+}
+
+bool stem_idx_header::Write (FILE *f) const {
+  return (WriteUL (f, lookback) &&
+	  WriteUL (f, dict_size) &&
+	  
+	  WriteUL (f, entries_per_block) &&
+	  WriteUL (f, num_blocks) &&
+	  WriteUL (f, max_block_size) &&
+	  WriteUL (f, blocks_start) &&
+	  WriteUL (f, block_idx_start) &&
+	  
+	  WriteUL (f, stemmer_num) &&
+	  WriteUL (f, stem_method));
+}
+
+
+
+stem_block_dict_el::stem_block_dict_el () {
+  Clear ();
+}
+
+void stem_block_dict_el::Clear () {
+  el.erase (el.begin(), el.end());
+  equivWords.erase (equivWords.begin(), equivWords.end());
+}
+
+bool stem_block_dict_el::Read (FILE *f) {
+  equivWords.erase (equivWords.begin(), equivWords.end());
+  
+  if (!ReadPreSufStr (f, el)) return false;
+  
+  // read in the array size
+  unsigned long arraySize = 0;
+  if (!ReadVarLenUL (f, arraySize)) return false;
+  
+  // read in the array
+  unsigned long wordNum;
+  while (arraySize > 0) {
+    if (!ReadUL (f, wordNum)) return false;
+    equivWords.push_back (wordNum);
+    
+    --arraySize;
+  }
+  
+  return true;
+}
+
+bool stem_block_dict_el::Write (FILE *f, const UCArray *lastEl) const {
+  if (!WritePreSufStr (f, lastEl, el)) return false;
+
+  // write out the array size
+  if (!WriteVarLenUL (f, equivWords.size())) return false;
+
+  vector<unsigned long>::const_iterator here = equivWords.begin();
+  vector<unsigned long>::const_iterator end = equivWords.end();
+  while (here != end) {
+    if (!WriteUL (f, (*here))) return false;
+    ++here;
+  }
+
+  return true;
+}
+
+
+
+
+invf_file_header::invf_file_header () {
+  Clear ();
+}
+
+void invf_file_header::Clear () {
+  no_of_words = 0;
+  no_of_tags = 0;
+  skip_mode = SKIP_MODE_NO_SKIPS;
+  word_level_index = 0;
+  
+  int i;
+  for (i=0; i<16; ++i) params[i] = 0;
+}
+  
+bool invf_file_header::Read (FILE *f) {
+  if (!ReadUL (f, no_of_words) ||
+      !ReadUL (f, no_of_tags) ||
+      !ReadUL (f, skip_mode) ||
+      !ReadUL (f, word_level_index)) return false;
+
+  int i;
+  for (i=0; i<16; ++i) {
+    if (!ReadUL (f, params[i])) return false;
+  }
+
+  return true;
+}
+
+bool invf_file_header::Write (FILE *f) const {
+  if (!WriteUL (f, no_of_words) ||
+      !WriteUL (f, no_of_tags) ||
+      !WriteUL (f, skip_mode) ||
+      !WriteUL (f, word_level_index)) return false;
+
+  int i;
+  for (i=0; i<16; ++i) {
+    if (!WriteUL (f, params[i])) return false;
+  }
+
+  return true;
+}
+
+
+
+
+
+bool SearchElNum (const block_idx &bIdx,
+		  unsigned long entriesPerBlock,
+		  unsigned long elNum,
+		  unsigned long &blockIdxNum,
+		  unsigned long &blockStartElNum) {
+  blockIdxNum = 0;
+  blockStartElNum = 0;
+
+  // make sure the element number isn't out of range
+  if (elNum > bIdx.size()*entriesPerBlock) return false;
+
+  blockIdxNum = elNum / entriesPerBlock;
+  blockStartElNum = blockIdxNum * entriesPerBlock;
+  
+  return true;
+}
+
+bool SearchEl (const block_idx &bIdx,
+	       unsigned long entriesPerBlock,
+	       const UCArray &el,
+	       unsigned long &blockIdxNum,
+	       unsigned long &blockStartElNum) {
+  blockIdxNum = 0;
+  blockStartElNum = 0;
+
+  unsigned long begin = 0;
+  unsigned long bIdxEnd = bIdx.size();
+  unsigned long end = bIdxEnd;
+  unsigned long mid;
+  while (begin < end) {
+    mid = (begin+end)/2;
+    if (DictCompare (el, bIdx[mid].el) < 0) {
+      end = mid;
+    } else if (mid+1 < bIdxEnd &&
+	       DictCompare (el, bIdx[mid+1].el) >= 0) {
+      begin = mid+1;
+    } else {
+      blockIdxNum = mid;
+      blockStartElNum = blockIdxNum * entriesPerBlock;
+      return true;
+    }
+  }
+  
+  return false;
+}
+
+
+
+// use the block dictionary functions for tag entries, and word block dict
+// functions for word entries.
+
+
+bool SearchBlockDictElNum (FILE *dictFile,
+			   const block_idx &bIdx,
+			   unsigned long entriesPerBlock,
+			   unsigned long dictSize,
+			   unsigned long elNum,
+			   block_dict_el &dictEl) {
+  UCArrayClear (dictEl.el);
+  if (elNum >= dictSize) return false;
+
+  // find the block that contains the element
+  unsigned long blockIdxNum, curElNum;
+  if (!SearchElNum (bIdx, entriesPerBlock, elNum,
+		    blockIdxNum, curElNum))
+    return false;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  do {
+    dictEl.Read (dictFile);
+  } while (curElNum++ < elNum);
+
+  return true;
+}
+
+bool SearchBlockDictEl (FILE *dictFile,
+			const block_idx &bIdx,
+			unsigned long entriesPerBlock,
+			unsigned long dictSize,
+			const UCArray &el,
+			block_dict_el &dictEl,
+			unsigned long &elNum) {
+  UCArrayClear (dictEl.el);
+
+  // find the block that contains the element
+  unsigned long blockIdxNum;
+  if (!SearchEl (bIdx, entriesPerBlock, el,
+		 blockIdxNum, elNum))
+    return false;
+
+  unsigned long blockEndElNum = elNum + entriesPerBlock;
+  if (blockEndElNum > dictSize) blockEndElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  while (elNum < blockEndElNum) {
+    dictEl.Read (dictFile);
+    int res = DictCompare (dictEl.el, el);
+    if (res == 0) return true; // found it
+    else if (res > 0) break; // not here
+    
+    ++elNum;
+  }
+
+  return false;
+}
+
+
+
+
+bool SearchWordBlockDictElNum (FILE *dictFile,
+			       const block_idx &bIdx,
+			       unsigned long entriesPerBlock,
+			       unsigned long dictSize,
+			       unsigned long numLevels,
+			       unsigned long elNum,
+			       word_block_dict_el &dictEl) {
+  UCArrayClear (dictEl.el);
+  if (elNum >= dictSize) return false;
+
+  // find the block that contains the element
+  unsigned long blockIdxNum, curElNum;
+  if (!SearchElNum (bIdx, entriesPerBlock, elNum,
+		    blockIdxNum, curElNum))
+    return false;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  do {
+    dictEl.Read (dictFile, numLevels);
+  } while (curElNum++ < elNum);
+
+  return true;
+}
+
+bool SearchWordBlockDictEl (FILE *dictFile,
+			    const block_idx &bIdx,
+			    unsigned long entriesPerBlock,
+			    unsigned long dictSize,
+			    unsigned long numLevels,
+			    const UCArray &el,
+			    word_block_dict_el &dictEl,
+			    unsigned long &elNum) {
+  UCArrayClear (dictEl.el);
+
+  // find the block that contains the element
+  unsigned long blockIdxNum;
+  if (!SearchEl (bIdx, entriesPerBlock, el,
+		 blockIdxNum, elNum))
+    return false;
+
+  unsigned long blockEndElNum = elNum + entriesPerBlock;
+  if (blockEndElNum > dictSize) blockEndElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  while (elNum < blockEndElNum) {
+    dictEl.Read (dictFile, numLevels);
+    int res = DictCompare (dictEl.el, el);
+    if (res == 0) return true; // found it
+    else if (res > 0) break; // not here
+    
+    ++elNum;
+  }
+
+  return false;
+}
+
+
+
+
+bool SearchStemBlockDictElNum (FILE *dictFile,
+			       const block_idx &bIdx,
+			       unsigned long entriesPerBlock,
+			       unsigned long dictSize,
+			       unsigned long elNum,
+			       stem_block_dict_el &dictEl) {
+  UCArrayClear (dictEl.el);
+  if (elNum >= dictSize) return false;
+
+  // find the block that contains the element
+  unsigned long blockIdxNum, curElNum;
+  if (!SearchElNum (bIdx, entriesPerBlock, elNum,
+		    blockIdxNum, curElNum))
+    return false;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  do {
+    dictEl.Read (dictFile);
+  } while (curElNum++ < elNum);
+
+  return true;
+}
+
+bool SearchStemBlockDictEl (FILE *dictFile,
+			    const block_idx &bIdx,
+			    unsigned long entriesPerBlock,
+			    unsigned long dictSize,
+			    const UCArray &el,
+			    stem_block_dict_el &dictEl,
+			    unsigned long &elNum) {
+  UCArrayClear (dictEl.el);
+
+  // find the block that contains the element
+  unsigned long blockIdxNum;
+  if (!SearchEl (bIdx, entriesPerBlock, el,
+		 blockIdxNum, elNum))
+    return false;
+
+  unsigned long blockEndElNum = elNum + entriesPerBlock;
+  if (blockEndElNum > dictSize) blockEndElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  while (elNum < blockEndElNum) {
+    dictEl.Read (dictFile);
+    int res = DictCompare (dictEl.el, el);
+    if (res == 0) return true; // found it
+    else if (res > 0) break; // not here
+    
+    ++elNum;
+  }
+
+  return false;
+}
+
+// ------------------------------------------
+// functions for partial term matching
+// ie find all words that start with xxx
+bool PartialMatchSearchWordBlockDictEl (FILE *dictFile,
+				 const block_idx &bIdx,
+				 unsigned long entriesPerBlock,
+				 unsigned long dictSize,
+				 unsigned long numLevels,
+				 const UCArray &el,
+				 word_block_dict_el &dictEl,
+				 vector<unsigned long> &elNumList,
+				 bool casefold) {
+
+  UCArrayClear (dictEl.el);
+  elNumList.erase (elNumList.begin(), elNumList.end());
+  unsigned long elNum;
+  // find the block that contains the element
+  unsigned long blockIdxNum;
+  // will this work??
+  if (!SearchEl (bIdx, entriesPerBlock, el,
+		 blockIdxNum, elNum)) {
+    return false;
+  }
+  unsigned long blockEndElNum = elNum + entriesPerBlock;
+  if (blockEndElNum > dictSize) blockEndElNum = dictSize;
+  
+  bool still_looking = true;
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  // test each element
+  while (elNum < blockEndElNum) {
+    dictEl.Read (dictFile, numLevels);
+    if (StartsWithCasefold(dictEl.el, el)) {
+      if (casefold || StartsWith(dictEl.el, el)) {
+	elNumList.push_back(elNum);
+      }
+      still_looking=false; // we have found one now, so stop the next time we don't have a match
+    } else if (!still_looking) {
+      // we have found a match previously, and now this doesn't match
+      return true;
+    } // else keep looking
+    
+    ++elNum;
+  }
+  // if we get here, we are either still searching for the first
+  // case, or we are collecting terms.
+  if (still_looking) {
+    //we haven't found a match yet, just check the next element,
+    dictEl.Read (dictFile, numLevels);
+    if (!StartsWithCasefold(dictEl.el, el)) {
+      // the first element of the next block is not a match, so there are no matches
+      return false;
+    } else {
+      if (casefold || StartsWith(dictEl.el, el)) {
+	elNumList.push_back(elNum);
+	++elNum;
+      } 
+
+    }
+  }
+  // just keep accumulating matches until there are no more
+  dictEl.Read (dictFile, numLevels);
+  while (StartsWithCasefold(dictEl.el, el)) {
+    if (casefold || StartsWith(dictEl.el, el)) {
+      elNumList.push_back(elNum);
+    }
+    ++elNum;
+    dictEl.Read (dictFile, numLevels);
+  }
+  return true;
+  
+}
+
+
+//----------------------------------------------------------------
+// functions for full text browse
+
+bool NearestSearchWordBlockDictEl (FILE *dictFile,
+				   const block_idx &bIdx,
+				   unsigned long entriesPerBlock,
+				   unsigned long dictSize,
+				   unsigned long numLevels,
+				   const UCArray &el,
+				   word_block_dict_el &dictEl,
+				   unsigned long &elNum) {
+
+  UCArrayClear (dictEl.el);
+
+  // find the block that contains the element
+  unsigned long blockIdxNum;
+  if (!SearchEl (bIdx, entriesPerBlock, el,
+		 blockIdxNum, elNum))
+    return false;
+
+  unsigned long blockEndElNum = elNum + entriesPerBlock;
+  if (blockEndElNum > dictSize) blockEndElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  while (elNum < blockEndElNum) {
+    dictEl.Read (dictFile, numLevels);
+    int res = DictCompare (el, dictEl.el); // look for the first word that is
+                                     // greater or equal to the el
+    if (res <= 0) {
+      return true; // found one
+   } 
+    
+    ++elNum;
+  }
+  // it must be the last term
+  return true;
+
+
+}
+
+
+bool SearchWordBlockDictElNumRange (FILE *dictFile,
+				    const block_idx &bIdx,
+				    unsigned long entriesPerBlock,
+				    unsigned long dictSize,
+				    unsigned long numLevels,
+				    unsigned long elNum,
+				    unsigned long numWords,
+				    UCArrayVector &terms) {
+
+  word_block_dict_el dictEl;  
+  dictEl.SetNumLevels (numLevels);
+  UCArrayClear(dictEl.el);
+
+  terms.erase(terms.begin(), terms.end());
+
+  if (elNum >= dictSize) return false;
+  
+  // find the block that contains the element
+  unsigned long blockIdxNum, curElNum;
+  if (!SearchElNum (bIdx, entriesPerBlock, elNum,
+		    blockIdxNum, curElNum))
+    return false;
+
+  unsigned long lastElNum = elNum + numWords - 1;
+  if (lastElNum > dictSize) lastElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  
+  // get the first term
+  do {
+    dictEl.Read (dictFile, numLevels);
+  } while (curElNum++ < elNum);
+
+  terms.push_back(dictEl.el);
+  while (curElNum <= lastElNum ) {
+    dictEl.Read(dictFile, numLevels);
+    terms.push_back(dictEl.el);
+    ++curElNum;
+  }
+  
+
+  return true;
+}
+
+// NOte: before each addition of dictEl to the array, the level freqs array
+// is deleted, as this was causing problems - generating a seg fault, I think if
+// the vector had to be reallocated or something.
+// setNumLevels has to be called each time before a read, now, to set up the level
+//freqs array. this is necessary.
+bool SearchWordBlockDictElNumRange (FILE *dictFile,
+				    const block_idx &bIdx,
+				    unsigned long entriesPerBlock,
+				    unsigned long dictSize,
+				    unsigned long numLevels,
+				    unsigned long elNum,
+				    unsigned long numWords,
+				    word_block_dict_el_array &terms) {
+
+  word_block_dict_el dictEl;
+  dictEl.SetNumLevels (numLevels);
+  UCArrayClear(dictEl.el);
+
+  block_dict_el elem;
+  terms.erase(terms.begin(), terms.end());
+
+  if (elNum >= dictSize) return false;
+  
+  // find the block that contains the element
+  unsigned long blockIdxNum, curElNum;
+  if (!SearchElNum (bIdx, entriesPerBlock, elNum,
+		    blockIdxNum, curElNum))
+    return false;
+
+  unsigned long lastElNum = elNum + numWords - 1;
+  if (lastElNum > dictSize) lastElNum = dictSize;
+
+  // look for the block
+  fseek (dictFile, bIdx[blockIdxNum].block_ptr, SEEK_SET);
+  // get the first term
+  do {
+    dictEl.Read (dictFile, numLevels);
+  } while (curElNum++ < elNum);
+
+  dictEl.levelFreqs = NULL;
+  terms.push_back(dictEl);
+
+  while (curElNum <= lastElNum ) { 
+    dictEl.SetNumLevels(numLevels);
+    dictEl.Read(dictFile, numLevels);
+    dictEl.levelFreqs = NULL;
+    terms.push_back(dictEl);
+    ++curElNum;
+  }
+
+  return true;
+}
+
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/invf.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/invf.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/invf.h	(revision 16561)
@@ -0,0 +1,351 @@
+/**************************************************************************
+ *
+ * invf.h -- Data structures for inverted files
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ *
+ **************************************************************************/
+
+
+#ifndef H_INVF
+#define H_INVF
+
+#include <stdio.h>
+
+#include "UCArray.h"
+
+// NOTE: This does not include the magic number
+// header info for .invf.dict file
+struct invf_dict_header {
+  unsigned long lookback;
+  unsigned long word_dict_start;
+  unsigned long word_dict_size;
+  unsigned long tag_dict_start;
+  unsigned long tag_dict_size;
+  unsigned long num_docs;
+  unsigned long num_frags;
+  unsigned long num_words;
+  unsigned long total_bytes;
+  unsigned long index_string_bytes;
+  unsigned long num_levels;
+
+  invf_dict_header ();
+  virtual ~invf_dict_header ();
+  virtual void Clear();
+
+  virtual bool Read (FILE *f);
+  virtual bool Write (FILE *f) const;
+};
+
+
+struct dict_el {
+  UCArray el; // word or tag
+  unsigned long frag_occur;
+  unsigned long freq;
+
+  virtual void Clear ();
+  dict_el () { Clear (); }
+  virtual ~dict_el () { }
+
+  // Read assumes that the last word is in el
+  bool Read (FILE *f);
+  bool Write (FILE *f, const UCArray *lastEl) const;
+};
+
+
+struct word_dict_el : public dict_el {
+  unsigned long *levelFreqs;
+
+  void Clear ();
+  word_dict_el () { levelFreqs = NULL; Clear (); }
+  ~word_dict_el ();
+
+  void SetNumLevels (unsigned long numLevels);
+
+  // SetNumLevels should be called before either
+  // reading or writing using Read and Write
+  
+  // Read assumes that the last word is in el
+  bool Read (FILE *f, unsigned long numLevels);
+  bool Write (FILE *f, const UCArray *lastEl,
+	      unsigned long numLevels) const;
+};
+
+
+// wblk = word block
+// tblk = tag block
+// this version of the blocked dictionary uses a fixed number
+// of entries per block, not a fixed block size
+// info for .invf.dict.blocked file
+// blocked dict has a heap of blocks, some for words, some for tags
+// and an index into each set of blocks. The index has pointers to
+// the first entry in each block. Can do a binary search on the index
+// to find out which block an elemnet is in
+struct block_dict_header : public invf_dict_header {
+  // note: word_dict_start and tag_dict_start are undefined
+  // for blocked dictionaries
+
+  unsigned long entries_per_wblk; // word blocks
+  unsigned long num_wblks;
+  unsigned long max_wblk_size;
+  unsigned long wblk_start;
+  unsigned long wblk_idx_start;
+  
+  unsigned long entries_per_tblk; // tag blocks
+  unsigned long num_tblks;
+  unsigned long max_tblk_size;
+  unsigned long tblk_start;
+  unsigned long tblk_idx_start;
+  
+  block_dict_header ();
+  void Clear ();
+  
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+
+struct block_dict_el {
+  UCArray el; // word or tag
+  unsigned long frag_occur; // # entries in invf file - if have a
+  // word level index, this is the same as freq, otherwise, its the number
+  // of fragments containing this word
+  unsigned long freq; // # of times this word occurs
+  unsigned long invf_ptr; // pointer into inverted file
+
+  virtual void Clear ();
+  block_dict_el () { Clear (); }
+  virtual ~block_dict_el () { }
+
+  // Read assumes that the last word is in el
+  // set lastEl = NULL when no lookback is wanted (eg
+  // for the start of a block
+  bool Read (FILE *f);
+  bool Write (FILE *f, const UCArray *lastEl) const;
+};
+
+struct word_block_dict_el : public block_dict_el {
+  unsigned long *levelFreqs; // freq of the word at each level
+
+  void Clear ();
+  word_block_dict_el () { levelFreqs = NULL; Clear (); }
+  ~word_block_dict_el ();
+
+  void SetNumLevels (unsigned long numLevels);
+
+  // SetNumLevels should be called before either
+  // reading or writing using Read and Write
+  
+  // Read assumes that the last word is in el
+  bool Read (FILE *f, unsigned long numLevels);
+  bool Write (FILE *f, const UCArray *lastEl,
+	      unsigned long numLevels) const;
+};
+
+typedef vector<word_block_dict_el> word_block_dict_el_array;
+
+struct block_idx_info {
+  UCArray el;
+  unsigned long block_ptr;
+
+  block_idx_info ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+// used for an index into the word and tag blocks
+typedef vector<block_idx_info> block_idx;
+
+bool ReadBlockIdx (FILE *f, block_idx &blockIdx);
+bool WriteBlockIdx (FILE *f, const block_idx &blockIdx);
+
+
+
+struct stem_idx_header {
+  unsigned long lookback;
+  unsigned long dict_size;
+
+  unsigned long entries_per_block;
+  unsigned long num_blocks;
+  unsigned long max_block_size;
+  unsigned long blocks_start;
+  unsigned long block_idx_start;
+  
+  unsigned long stemmer_num;
+  unsigned long stem_method;
+
+  stem_idx_header ();
+  void Clear ();
+
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+struct stem_block_dict_el {
+  UCArray el; // word or tag
+  vector<unsigned long> equivWords;
+  
+  stem_block_dict_el ();
+  void Clear ();
+
+  // Read assumes that the last word is in el
+  // set lastEl = NULL when no lookback is wanted (eg
+  // for the start of a block
+  bool Read (FILE *f);
+  bool Write (FILE *f, const UCArray *lastEl) const;
+};
+
+
+
+#define SKIP_MODE_NO_SKIPS 0
+
+// invf file - has a list of frags for each word, but the word is not 
+//  stored in the invf file - the dictionaries store the words, along
+// with num entries, and a pointer into invf file
+struct invf_file_header {
+  unsigned long no_of_words;
+  unsigned long no_of_tags;
+  unsigned long skip_mode;
+  unsigned long word_level_index; // 1 if word level index
+  unsigned long params[16];
+
+  invf_file_header ();
+  void Clear ();
+  
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+
+
+
+
+
+// the search functions returns true if a block that could
+// satisfy the request is found. these functions assume that
+// the block index is sorted by DictCompare (or DictLTUCArray)
+bool SearchElNum (const block_idx &bIdx,
+		  unsigned long entriesPerBlock,
+		  unsigned long elNum,
+		  unsigned long &blockIdxNum,
+		  unsigned long &blockStartElNum);
+bool SearchEl (const block_idx &bIdx,
+	       unsigned long entriesPerBlock,
+	       const UCArray &el,
+	       unsigned long &blockIdxNum,
+	       unsigned long &blockStartElNum);
+
+
+// The next six functions use SearchElNum and SearchEl
+// for a particular type of dictionary (Block, WordBlock, or StemBlock)
+// and then look up the entry
+bool SearchBlockDictElNum (FILE *dictFile,
+			   const block_idx &bIdx,
+			   unsigned long entriesPerBlock,
+			   unsigned long dictSize,
+			   unsigned long elNum,
+			   block_dict_el &dictEl);
+bool SearchBlockDictEl (FILE *dictFile,
+			const block_idx &bIdx,
+			unsigned long entriesPerBlock,
+			unsigned long dictSize,
+			const UCArray &el,
+			block_dict_el &dictEl,
+			unsigned long &elNum);
+
+// assumes the numLevels has been set for dictEl
+bool SearchWordBlockDictElNum (FILE *dictFile,
+			       const block_idx &bIdx,
+			       unsigned long entriesPerBlock,
+			       unsigned long dictSize,
+			       unsigned long numLevels,
+			       unsigned long elNum,
+			       word_block_dict_el &dictEl);
+bool SearchWordBlockDictEl (FILE *dictFile,
+			    const block_idx &bIdx,
+			    unsigned long entriesPerBlock,
+			    unsigned long dictSize,
+			    unsigned long numLevels,
+			    const UCArray &el,
+			    word_block_dict_el &dictEl,
+			    unsigned long &elNum);
+
+bool SearchStemBlockDictElNum (FILE *dictFile,
+			       const block_idx &bIdx,
+			       unsigned long entriesPerBlock,
+			       unsigned long dictSize,
+			       unsigned long elNum,
+			       stem_block_dict_el &dictEl);
+bool SearchStemBlockDictEl (FILE *dictFile,
+			    const block_idx &bIdx,
+			    unsigned long entriesPerBlock,
+			    unsigned long dictSize,
+			    const UCArray &el,
+			    stem_block_dict_el &dictEl,
+			    unsigned long &elNum);
+
+//-----------------------------------------------------
+// New functions for partial matching
+// returns a list of word numbers from the main block index whose
+// words start with el.
+bool PartialMatchSearchWordBlockDictEl (FILE *dictFile,
+				 const block_idx &bIdx,
+				 unsigned long entriesPerBlock,
+				 unsigned long dictSize,
+				 unsigned long numLevels,
+				 const UCArray &el,
+				 word_block_dict_el &dictEl,
+				 vector<unsigned long> &elNumList,
+				 bool casefold); 
+//----------------------------------------------------------
+
+// new functions for full text browse
+
+bool NearestSearchWordBlockDictEl (FILE *dictFile,
+			    const block_idx &bIdx,
+			    unsigned long entriesPerBlock,
+			    unsigned long dictSize,
+			    unsigned long numLevels,
+			    const UCArray &el,
+			    word_block_dict_el &dictEl,
+			    unsigned long &elNum);
+
+// returns a list of word_block_dict_el, with no levelfreqs
+bool SearchWordBlockDictElNumRange (FILE *dictFile,
+				    const block_idx &bIdx,
+				    unsigned long entriesPerBlock,
+				    unsigned long dictSize,
+				    unsigned long numLevels,
+				    unsigned long elNum,
+				    unsigned long numWords,
+				    word_block_dict_el_array &terms);
+				    
+// just returns a list of terms
+bool SearchWordBlockDictElNumRange (FILE *dictFile,
+				    const block_idx &bIdx,
+				    unsigned long entriesPerBlock,
+				    unsigned long dictSize,
+				    unsigned long numLevels,
+				    unsigned long elNum,
+				    unsigned long numWords,
+				    UCArrayVector &terms);
+				    
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass1.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass1.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass1.cpp	(revision 16561)
@@ -0,0 +1,775 @@
+/**************************************************************************
+ *
+ * ivf.pass1.cpp -- Memory efficient pass 1 inversion
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "UCArray.h"
+#include "sysfuncs.h"
+#include "mg_files.h"
+#include "invf.h"
+#include "mg.h"
+#include "build.h"
+#include "locallib.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include <stdio.h>
+#include "words.h"
+#include "messages.h"
+#include "netorder.h"
+#include "FIvfLevelInfo.h"
+
+#include "longlong.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+// a fragment corresponds to the index level (word-level is the
+// minimum index level)
+
+
+// structure to determine level information
+struct LevelWorker {
+  unsigned long lastLevelDocNum;
+  unsigned long count;
+
+  LevelWorker () {
+    lastLevelDocNum = count = 0;
+  }
+};
+
+
+// note: the word is stored in the map
+struct IvfWordInfo {
+  unsigned long wordCount;      // word frequency
+  unsigned long fragCount;      // number of fragments that contain the word
+  unsigned long lastFragNum;    // last fragment to contain the word
+  unsigned long chunkWordCount; // word frequency within this chunk
+  unsigned long chunkFragCount; // number of fragments within this chunk that
+                                //   contain the word
+
+  LevelWorker *levels;          // level info for this word
+  
+  IvfWordInfo ();
+  ~IvfWordInfo ();
+  void Clear ();                // will delete levels
+};
+
+typedef map<UCArray, IvfWordInfo, DictLTUCArray> IvfWordInfoMap;
+typedef vector<IvfWordInfoMap::iterator> IvfWordInfoItArray;
+
+// tags don't require as much information
+struct IvfTagInfo {
+  unsigned long tagCount;       // tag frequency
+  unsigned long fragCount;      // number of fragments that contain the tag
+  unsigned long lastFragNum;    // last fragment to contain the tag
+  unsigned long chunkFragCount; // number of fragments within this chunk that
+                                //   contain the tag
+
+  IvfTagInfo ();
+  void Clear ();
+};
+
+typedef map<UCArray, IvfTagInfo, DictLTUCArray> IvfTagInfoMap;
+typedef vector<IvfTagInfoMap::iterator> IvfTagInfoItArray;
+
+
+#define INIT_CHECK_FRAC 0.10
+#define CHECK_FRAC 0.75
+#define CHECK_CLOSE 0.999
+#define CHECK_DIV 1.5
+
+
+static FILE *ic;		// the invf chunk file
+static stdio_bitio_buffer icb;
+
+IvfWordInfoMap ivfWordInfo;
+IvfWordInfoItArray ivfWordInfoOccurOrder;
+IvfTagInfoMap ivfTagInfo;
+IvfTagInfoItArray ivfTagInfoOccurOrder;
+
+static unsigned long chunksWritten;
+static unsigned long maxMemNeeded;
+
+static unsigned long numDocs;
+static unsigned long numChunkDocs;
+
+static unsigned long numFrags;
+static unsigned long numChunkFrags;
+
+static unsigned long numWords;
+
+// the number of document numbers in the inverted file
+static unsigned long numChunkEntries;
+
+// next entry in the inverted file to check memory
+// requirements for the current chunk
+static unsigned long entryCheckPoint;
+
+// information about all the different levels
+static FIvfLevel ivfLevel;
+
+
+
+IvfWordInfo::IvfWordInfo () {
+  levels = NULL;
+  Clear();
+}
+
+IvfWordInfo::~IvfWordInfo () {
+  if (levels != NULL) delete [] levels;
+}
+
+void IvfWordInfo::Clear () {
+  wordCount = 0;
+  chunkWordCount = 0;
+  lastFragNum = 0;
+  fragCount = 0;
+  chunkFragCount = 0;
+
+  if (levels != NULL) {
+    delete [] levels;
+    levels = NULL;
+  }
+}
+
+
+IvfTagInfo::IvfTagInfo () {
+  Clear();
+}
+
+void IvfTagInfo::Clear () {
+  tagCount = 0;
+  lastFragNum = 0;
+  fragCount = 0;
+  chunkFragCount = 0;
+}
+
+
+
+int init_ivf_1 (const TagInfo &tagInfo, char *file_name) {
+  // set up the chunk file
+  if (!(ic = create_file (file_name, INVF_CHUNK_SUFFIX, "wb",
+			  MAGIC_CHUNK, MG_MESSAGE)))
+    return COMPERROR;
+  fwrite ("    ", sizeof (u_long), 1, ic);	// Space for the maxmem
+  icb.attachFile (ic);
+  icb.encodeStart();
+
+  // reset global variables
+  ivfWordInfo.erase (ivfWordInfo.begin(), ivfWordInfo.end());
+  ivfWordInfoOccurOrder.erase (ivfWordInfoOccurOrder.begin(), ivfWordInfoOccurOrder.end());
+  ivfTagInfo.erase (ivfTagInfo.begin(), ivfTagInfo.end());
+  ivfTagInfoOccurOrder.erase (ivfTagInfoOccurOrder.begin(), ivfTagInfoOccurOrder.end());
+  
+  chunksWritten = 0;
+  maxMemNeeded = 0;
+ 
+  numDocs = 0;
+  numChunkDocs = 0;
+
+  numFrags = 0;
+  numChunkFrags = 0;
+
+  numWords = 0;
+
+  numChunkEntries = 0;
+  entryCheckPoint = (unsigned long) ((invf_buffer_size * INIT_CHECK_FRAC) / CHECK_DIV);
+
+  // init the level information
+  ivfLevel.Clear();
+  ivfLevel.docTag = tagInfo.docTag;
+  ivfLevel.indexLevel = tagInfo.indexLevel;
+  IvfLevelInfo blankLevel;
+  UCArraySet::const_iterator levelHere = tagInfo.levelTags.begin();
+  UCArraySet::const_iterator levelEnd = tagInfo.levelTags.end();
+  while (levelHere != levelEnd) {
+    blankLevel.levelTag = *levelHere;
+    ivfLevel.levelInfo[*levelHere] = blankLevel;
+    ++levelHere;
+  }
+  
+  return COMPALLOK;
+}
+
+static void ProcessOpenTag (const TagInfo &tagInfo, const TextEl &el,
+			    bool &inFrag) {
+  bool wordLevelIndex = tagInfo.indexLevel.empty();
+
+  // check for start of next fragment
+  if (!wordLevelIndex && el.tagName == tagInfo.indexLevel) {
+    ++numFrags;
+    ++numChunkFrags;
+    inFrag = true;
+  }
+  
+  // update tag stats
+  IvfTagInfo &i = ivfTagInfo[el.tagName];
+  if (i.tagCount == 0) {
+    // new tag, add to list of iterators
+    IvfTagInfoMap::iterator iIt = ivfTagInfo.find (el.tagName);
+    ivfTagInfoOccurOrder.push_back (iIt);
+  }
+  ++i.tagCount;
+
+  // all open tags count as new tags
+  ++numChunkEntries;
+  ++i.fragCount;
+  ++i.chunkFragCount;
+  i.lastFragNum = numFrags;
+  
+  // update level information
+  IvfLevelInfoMap::iterator levelIt = ivfLevel.levelInfo.find (el.tagName);
+  if (levelIt != ivfLevel.levelInfo.end()) {
+    // is a level tag
+    ++levelIt->second.numEntries;
+    levelIt->second.workInLevel = true;
+  }
+}
+
+static void ProcessCloseTag (const TagInfo &tagInfo, const TextEl &el,
+			     bool &inFrag) {
+  bool wordLevelIndex = tagInfo.indexLevel.empty();
+
+  // check for end of fragment
+  if (!wordLevelIndex && el.tagName == tagInfo.indexLevel) {
+    inFrag = false;
+  }
+
+  // update level information
+  IvfLevelInfoMap::iterator levelIt = ivfLevel.levelInfo.find (el.tagName);
+  if (levelIt != ivfLevel.levelInfo.end()) {
+    // is a level tag
+    (*levelIt).second.workInLevel = false;
+  }
+}
+
+static void ProcessText (const TagInfo &tagInfo, const TextEl &el,
+			 bool &inFrag) {
+  bool wordLevelIndex = tagInfo.indexLevel.empty();
+
+  // make sure this text is to be indexed
+  if (!wordLevelIndex && !inFrag) return;
+
+  const unsigned char *textHere = &(el.text[0]);
+  const unsigned char *textEnd = &(el.text[el.text.size() - 1]);
+  UCArray word;
+  
+  if (!inaword_mgpp (textHere, textEnd))
+    ParseNonindexWord (textHere, textEnd);
+
+  
+  // Alternately parse off words and non-words from the input
+  // Each token is then inserted into the set if it does
+  // not exist or has it's frequency count incremented if it does.
+
+  while (textHere <= textEnd) {
+    textHere = ParseIndexWord (textHere, textEnd, word);
+    textHere = ParseNonindexWord (textHere, textEnd);
+
+    if (!word.empty()) {
+      ++numWords;
+
+      if (wordLevelIndex) {
+	++numFrags;
+	++numChunkFrags;
+      }
+      
+      // update word stats
+
+      IvfWordInfo &i = ivfWordInfo[word];
+      if (i.wordCount == 0) {
+	// new word
+	// add to list of iterators
+	IvfWordInfoMap::iterator iIt = ivfWordInfo.find (word);
+	ivfWordInfoOccurOrder.push_back (iIt);
+
+	// add level information array
+	if (ivfLevel.levelInfo.size() > 0) 
+	  i.levels = new LevelWorker [ivfLevel.levelInfo.size()];
+      }
+
+      ++i.wordCount;
+      ++i.chunkWordCount;
+      if (numFrags > i.lastFragNum) {
+	++numChunkEntries;
+	++i.fragCount;
+	++i.chunkFragCount;
+	i.lastFragNum = numFrags;
+      }
+
+      // update level information for this word
+      if (i.levels != NULL) {
+	IvfLevelInfoMap::iterator levelHere = ivfLevel.levelInfo.begin();
+	IvfLevelInfoMap::iterator levelEnd = ivfLevel.levelInfo.end();
+	LevelWorker *levelWorkerPtr = i.levels;
+	while (levelHere != levelEnd) {
+
+	  // check to make sure the level encompases this fragment
+	  if (!(*levelHere).second.workInLevel) {
+	    cerr << "Level tag <" << (*levelHere).first
+		 << "> does not encompass all fragments\n";
+	    exit (1);
+	  }
+	  
+	  if ((*levelHere).second.numEntries > (*levelWorkerPtr).lastLevelDocNum) {
+	    (*levelWorkerPtr).lastLevelDocNum = (*levelHere).second.numEntries;
+	    ++levelWorkerPtr->count;
+	  }
+	  
+	  ++levelHere;
+	  ++levelWorkerPtr;
+	}
+      }
+    }
+  }
+}
+
+
+static unsigned long MemoryRequired (bool wordLevelIndex) {
+  register unsigned long total = 0;
+
+  // add memory required for word entries
+  IvfWordInfoMap::const_iterator wordHere = ivfWordInfo.begin();
+  IvfWordInfoMap::const_iterator wordEnd = ivfWordInfo.end();
+  while (wordHere != wordEnd) {
+    register const IvfWordInfo &info = (*wordHere).second;
+    if (info.chunkFragCount > 0) {
+      total += BIO_Bblock_Bound (numChunkFrags, info.chunkFragCount);
+      if (!wordLevelIndex) {
+	total += info.chunkWordCount;
+      }
+    }
+    
+    ++wordHere;
+  }
+
+  // add memory required for tag entries
+  IvfTagInfoMap::const_iterator tagHere = ivfTagInfo.begin();
+  IvfTagInfoMap::const_iterator tagEnd = ivfTagInfo.end();
+  while (tagHere != tagEnd) {
+    register const IvfTagInfo &info = (*tagHere).second;
+    if (info.chunkFragCount > 0) {
+      // two d entries for each frag entry
+      unsigned long pTag = info.chunkFragCount*2;
+      total += BIO_Bblock_Bound (numChunkFrags+pTag, pTag);
+    }
+    
+    ++tagHere;
+  }
+  
+  total = (total + 7) >> 3;
+  return total;
+}
+
+
+/*
+static void PrintChunkInfo (unsigned long mem) {
+  cout << "Chunk Number: " << chunksWritten << "\n";
+  cout << "numChunkDocs " << numChunkDocs << "\n";
+  cout << "numChunkFrags " << numChunkFrags << "\n";
+  cout << "mem " << mem << "\n";
+  cout << "numWords " << ivfWordInfo.size() << "\n";
+  cout << "numTags " << ivfTagInfo.size() << "\n\n";
+
+  // output debug tag information in dictionary order
+  IvfTagInfoMap::iterator tagMapHere = ivfTagInfo.begin();
+  IvfTagInfoMap::iterator tagMapEnd = ivfTagInfo.end();
+  unsigned long tagNum = 0;
+  while (tagMapHere != tagMapEnd) {
+    cout << (*tagMapHere).first << " " << tagNum
+	 << " " << (*tagMapHere).second.chunkFragCount << "\n";
+    ++tagNum;
+    ++tagMapHere;
+  }
+}
+*/
+
+static void OutputChunkInfo (unsigned long mem, bool /*wordLevelIndex*/) {
+  ++chunksWritten;
+
+  // sanity check
+  if (ivfWordInfo.size() != ivfWordInfoOccurOrder.size()) {
+    Message ("ERROR: Word information size mismatch: %u vs %u\n",
+	     (unsigned int)ivfWordInfo.size(),
+	     (unsigned int)ivfWordInfoOccurOrder.size());
+    exit (1);
+  }
+  if (ivfTagInfo.size() != ivfTagInfoOccurOrder.size()) {
+    Message ("ERROR: Tag information size mismatch: %u vs %u\n",
+	     (unsigned int)ivfTagInfo.size(),
+	     (unsigned int)ivfTagInfoOccurOrder.size());
+    exit (1);
+  }
+
+  icb.gamma_encode (numChunkDocs + 1, NULL);
+  icb.gamma_encode (numChunkFrags + 1, NULL);
+  icb.gamma_encode (mem + 1, NULL);
+  icb.gamma_encode (ivfWordInfo.size() + 1, NULL);
+  icb.gamma_encode (ivfTagInfo.size() + 1, NULL);
+
+  /*  PrintChunkInfo (mem);*/
+
+  // output word information in occurance order
+  IvfWordInfoItArray::iterator wordHere = ivfWordInfoOccurOrder.begin();
+  IvfWordInfoItArray::iterator wordEnd = ivfWordInfoOccurOrder.end();
+  while (wordHere != wordEnd) {
+    register IvfWordInfo &ivfWordInfo = (*(*wordHere)).second;
+    
+    icb.gamma_encode (ivfWordInfo.chunkWordCount + 1, NULL);
+    if (ivfWordInfo.chunkWordCount >= 2) {
+      icb.gamma_encode (ivfWordInfo.chunkFragCount, NULL);
+    }
+
+    ivfWordInfo.lastFragNum = 0;
+    ivfWordInfo.chunkWordCount = 0;
+    ivfWordInfo.chunkFragCount = 0;
+    
+    ++wordHere;
+  }
+
+  // output tag information in occurance order
+  IvfTagInfoItArray::iterator tagHere = ivfTagInfoOccurOrder.begin();
+  IvfTagInfoItArray::iterator tagEnd = ivfTagInfoOccurOrder.end();
+  while (tagHere != tagEnd) {
+    register IvfTagInfo &ivfTagInfo = (*(*tagHere)).second;
+
+    icb.gamma_encode (ivfTagInfo.chunkFragCount + 1, NULL);
+
+    ivfTagInfo.lastFragNum = 0;
+    ivfTagInfo.chunkFragCount = 0;
+    
+    ++tagHere;
+  }
+
+  numChunkDocs = 0;
+  numChunkFrags = 0;
+  numChunkEntries = 0;
+}
+
+
+int process_ivf_1 (const TagInfo &tagInfo, const TextElArray &doc) {
+  bool wordLevelIndex = tagInfo.indexLevel.empty();
+  bool inFrag = false;
+  if (wordLevelIndex) inFrag = true; // unconditional
+  
+  ++numDocs;
+  ++numChunkDocs;
+
+  // process each text element in this document
+  TextElArray::const_iterator here = doc.begin();
+  TextElArray::const_iterator end = doc.end();
+  while (here != end) {
+    if ((*here).elType == OpenTagE)
+      ProcessOpenTag (tagInfo, *here, inFrag);
+    else if ((*here).elType == CloseTagE)
+      ProcessCloseTag (tagInfo, *here, inFrag);
+    else if ((*here).elType == TextE)
+      ProcessText (tagInfo, *here, inFrag);
+    
+    ++here;
+  }
+
+  // check the amount of memory needed for this chunk
+  if (numChunkEntries >= entryCheckPoint) {
+    unsigned long mem = MemoryRequired (wordLevelIndex);
+    if (mem >= invf_buffer_size * CHECK_CLOSE) {
+      if (mem > maxMemNeeded) maxMemNeeded = mem;
+      OutputChunkInfo (mem, wordLevelIndex);
+      entryCheckPoint = (unsigned long)
+	((invf_buffer_size * INIT_CHECK_FRAC) / CHECK_DIV);
+      
+    } else {
+      entryCheckPoint = (unsigned long)
+	(entryCheckPoint * ((CHECK_FRAC * (invf_buffer_size - mem)) / mem) +
+	 entryCheckPoint);
+      if (entryCheckPoint <= numChunkEntries)
+	entryCheckPoint = numChunkEntries + 1;
+    }
+  }
+  
+  return COMPALLOK;
+}
+
+
+static void CalcInvfDictSize (unsigned long &totalBytes,
+			      unsigned long &indexStringBytes) {
+  totalBytes = 0;	// The sum of the length of all words, including 
+			// the length byte
+  indexStringBytes = 0; // The amount of space required to store the 
+			// words in the diction, this takes into account 
+			// the prefixes
+  const UCArray *lastWord = NULL;
+  
+  // calculate size of word information
+  IvfWordInfoMap::iterator wordHere = ivfWordInfo.begin();
+  IvfWordInfoMap::iterator wordEnd = ivfWordInfo.end();
+  while (wordHere != wordEnd) {
+    unsigned long wordSize = (*wordHere).first.size();
+    totalBytes += wordSize + 1;
+    indexStringBytes += wordSize + 2;
+    if (lastWord != NULL)
+      indexStringBytes -= PrefixLen (*lastWord, (*wordHere).first);
+    lastWord = &((*wordHere).first);
+    
+    ++wordHere;
+  }
+
+  // calculate size of tag information
+  lastWord = NULL;
+  IvfTagInfoMap::iterator tagHere = ivfTagInfo.begin();
+  IvfTagInfoMap::iterator tagEnd = ivfTagInfo.end();
+  while (tagHere != tagEnd) {
+    unsigned long tagSize = (*tagHere).first.size();
+    totalBytes += tagSize + 1;
+    indexStringBytes += tagSize + 2;
+    if (lastWord != NULL)
+      indexStringBytes -= PrefixLen (*lastWord, (*tagHere).first);
+    lastWord = &((*tagHere).first);
+    
+    ++tagHere;
+  }
+}
+
+
+//      OutputInvfDict ():
+//                writes out the stemmed dictionary file
+//                in the following format 
+//                        lookback value (int)
+//                        totalbytes value (int)
+//                        indexstringbytes (int)
+//                        for each word 
+//                          wordlen (4 bits)
+//                          prefix match (4 bits)
+//                          word (wordlen bytes)
+//                          word frequency (int)
+//                          word count (int)
+//
+//        Accesses outside variables:     
+//
+//        Return value...:                
+static void OutputInvfDict (char *filename) {
+  // create the dictionary header
+  invf_dict_header idh;
+  idh.word_dict_size = ivfWordInfo.size();
+  idh.tag_dict_size = ivfTagInfo.size();
+  idh.num_docs = numDocs;
+  idh.num_frags = numFrags;
+  idh.num_words = numWords;
+  idh.num_levels = ivfLevel.levelInfo.size();
+  CalcInvfDictSize (idh.total_bytes, idh.index_string_bytes);
+
+  // create the inverted dictionary file
+  FILE *sp;
+  if (!(sp = create_file (filename, INVF_DICT_SUFFIX, "wb", MAGIC_STEM_BUILD,
+			  MG_MESSAGE)))
+    return;
+
+  // write out the dictionary header
+  if (!idh.Write (sp)) { fclose (sp); return; }
+
+  // remember where the word dictionary starts
+  idh.word_dict_start = ftell (sp);
+  
+  // output the word dictionary
+  const UCArray *lastWord = NULL;
+  IvfWordInfoMap::iterator wordHere = ivfWordInfo.begin();
+  IvfWordInfoMap::iterator wordEnd = ivfWordInfo.end();
+  while (wordHere != wordEnd) {
+    // get the prefix and suffix lengths
+    const UCArray &thisWord = (*wordHere).first;
+    WritePreSufStr (sp, lastWord, thisWord);
+
+    // output the number of fragments the word appeared in and the
+    // number of times the word appeared
+    WriteUL (sp, (*wordHere).second.fragCount);
+    WriteUL (sp, (*wordHere).second.wordCount);
+
+    // output frequency information for each level
+    // note that we are expecting every word to have
+    // level information
+    LevelWorker *lwHere = (*wordHere).second.levels;
+    LevelWorker *lwEnd = lwHere + idh.num_levels;
+    while (lwHere != lwEnd) {
+      WriteUL (sp, (*lwHere).count);
+      ++lwHere;
+    }
+    
+    lastWord = &thisWord;
+    ++wordHere;
+  }
+
+  // remember where the tag dictionary starts
+  idh.tag_dict_start = ftell (sp);
+  
+  // output the tag dictionary
+  const UCArray *lastTag = NULL;
+  IvfTagInfoMap::iterator tagHere = ivfTagInfo.begin();
+  IvfTagInfoMap::iterator tagEnd = ivfTagInfo.end();
+  while (tagHere != tagEnd) {
+    // get the prefix and suffix lengths
+    const UCArray &thisTag = (*tagHere).first;
+    WritePreSufStr (sp, lastTag, thisTag);
+
+    // output the number of fragments the tag appeared in and the
+    // number of times the tag appeared
+    WriteUL (sp, (*tagHere).second.fragCount);
+    WriteUL (sp, (*tagHere).second.tagCount);
+    
+    lastTag = &thisTag;
+    ++tagHere;
+  }
+
+  // write out the updated header
+  fseek (sp, sizeof (u_long), SEEK_SET);
+  if (!idh.Write (sp)) { fclose (sp); return; }
+  
+  fclose (sp);
+}
+
+static void OutputLevelFile (char *filename) {
+  // create the level file
+  FILE *f;
+  if (!(f = create_file (filename, INVF_LEVEL_SUFFIX, "wb", MAGIC_INVF_LEVELS,
+			 MG_MESSAGE)))
+    return;
+
+  // write out the information
+  ivfLevel.Write (f);
+  
+  // close the file
+  fclose (f);
+}
+
+static void OutputTransFile (char *filename) {
+  // save the word number (in lastFragNum :-/)
+  int i = 0;
+  IvfWordInfoMap::iterator wordHere = ivfWordInfo.begin();
+  IvfWordInfoMap::iterator wordEnd = ivfWordInfo.end();
+  while (wordHere != wordEnd) {
+    (*wordHere).second.lastFragNum = i;
+    ++i; ++wordHere;
+  }
+
+  // save the tag number (in lastFragNum :-/)
+  i = 0;
+  IvfTagInfoMap::iterator tagHere = ivfTagInfo.begin();
+  IvfTagInfoMap::iterator tagEnd = ivfTagInfo.end();
+  while (tagHere != tagEnd) {
+    (*tagHere).second.lastFragNum = i;
+    ++i; ++tagHere;
+  }
+  
+  // create the translation file
+  FILE *f;
+  if (!(f = create_file (filename, INVF_CHUNK_TRANS_SUFFIX, "wb",
+			 MAGIC_CHUNK_TRANS, MG_MESSAGE)))
+    return;
+
+  stdio_bitio_buffer buffer(f);
+  buffer.encodeStart();
+
+  // write out the word translation table
+  unsigned long wordDictSize = ivfWordInfoOccurOrder.size();
+  IvfWordInfoItArray::iterator wordItHere = ivfWordInfoOccurOrder.begin();
+  IvfWordInfoItArray::iterator wordItEnd = ivfWordInfoOccurOrder.end();
+  unsigned long oN = 0;
+  while (wordItHere != wordItEnd) {
+    register IvfWordInfo &ivfWordInfo = (*(*wordItHere)).second;
+    buffer.binary_encode (ivfWordInfo.lastFragNum + 1, wordDictSize + 1, NULL);
+    ++oN;
+    ++wordItHere;
+  }
+
+  // write out the tag translation table
+  unsigned long tagDictSize = ivfTagInfoOccurOrder.size();
+  IvfTagInfoItArray::iterator tagItHere = ivfTagInfoOccurOrder.begin();
+  IvfTagInfoItArray::iterator tagItEnd = ivfTagInfoOccurOrder.end();
+  while (tagItHere != tagItEnd) {
+    register IvfTagInfo &ivfTagInfo = (*(*tagItHere)).second;
+    buffer.binary_encode (ivfTagInfo.lastFragNum + 1, tagDictSize + 1, NULL);
+    ++oN;
+    ++tagItHere;
+  }
+
+  // finish encoding and close the file
+  buffer.encodeDone();
+  fclose (f);
+}
+
+#ifndef SILENT
+static void PrintStats () {
+  Message ("Inverted buffer size:          %8u bytes\n", invf_buffer_size);
+  Message ("Max memory needed for 1 chunk: %8u bytes\n", maxMemNeeded);
+
+  Message ("Number of chunks written:      %8u\n", chunksWritten);
+  Message ("Number of documents:           %8u\n", numDocs);
+  Message ("Number of fragments:           %8u\n", numFrags);
+  Message ("Number of words:               %8u\n", numWords);
+
+  Message ("Size of word dictionary:       %8u\n", ivfWordInfo.size());
+  Message ("Size of tag dictionary:        %8u\n", ivfTagInfo.size());
+}
+#endif
+
+int done_ivf_1 (const TagInfo &tagInfo, char * filename) {
+  bool wordLevelIndex = tagInfo.indexLevel.empty();
+
+  char *temp_str = msg_prefix;
+  msg_prefix = "ivf.pass1";
+
+  // output the last chunk
+  if (numChunkDocs > 0) {
+    unsigned long mem = MemoryRequired (wordLevelIndex);
+    OutputChunkInfo (mem, wordLevelIndex);
+    if (mem > maxMemNeeded) maxMemNeeded = mem;
+  }
+  
+  // write out and encoded 1 to say there are no more chunks
+  icb.gamma_encode (1, NULL);
+  icb.encodeDone ();
+
+  // write out the maximum memory required and close the file
+  fseek (ic, sizeof (long), 0);
+  WriteUL (ic, maxMemNeeded);
+  fclose (ic);
+
+  // output the inverted dictionary
+  OutputInvfDict (filename);
+
+  // write out the translation file
+  OutputTransFile (filename);
+
+  // output the level information
+  OutputLevelFile (filename);
+  
+  // output statistics
+#ifndef SILENT
+  PrintStats ();
+#endif
+  
+  msg_prefix = temp_str;
+
+  return COMPALLOK;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass2.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass2.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/ivf.pass2.cpp	(revision 16561)
@@ -0,0 +1,1111 @@
+/**************************************************************************
+ *
+ * ivf.pass2.cpp -- Memory efficient pass 2 inversion
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include <stdio.h>
+
+#if defined __WIN32__
+# include <process.h>
+# include <io.h>
+# define getpid _getpid
+# define unlink _unlink
+#else
+# include <unistd.h>
+# include "non_ansi.h"
+#endif
+
+#include "UCArray.h"
+#include "sysfuncs.h"
+#include "mg_files.h"
+#include "invf.h"
+#include "mg.h"
+#include "build.h"
+#include "locallib.h"
+#include "bitio_m_random.h"
+#include "bitio_m_stdio.h"
+#include "bitio_m_mems.h"
+#include "bitio_gen.h"
+#include <stdio.h>
+#include "words.h"
+#include "messages.h"
+#include "netorder.h"
+#include "FIvfLevelInfo.h"
+#include "perf_hash.h"
+#include "string.h"
+
+#include "longlong.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+
+#ifdef USE_LONG_LONG
+#define SEEK_X seek_LL
+#define TELL_X tell_LL
+#else
+#define SEEK_X seek
+#define TELL_X tell
+#endif
+
+#ifndef RND_BUF_SIZE
+#define RND_BUF_SIZE 8*1024
+#endif
+
+
+static unsigned long numDocs = 0;
+static unsigned long numChunkDocs = 0;
+static unsigned long numDocsInChunk = 0;
+
+static unsigned long numFrags = 0;
+static unsigned long numFragsInChunk = 0;
+static unsigned long chunkStartFragNum = 0;
+
+
+
+
+struct BitPtr {
+  unsigned long start;
+  unsigned long here;
+  unsigned long lastFragNum;
+  unsigned long lgB;
+  
+  void Clear () { start = here = lastFragNum = lgB = 0; }
+  BitPtr () { Clear(); }
+};
+
+class WordBitPtrs {
+protected:
+  unsigned long numWords;
+  unsigned long numTags;
+  unsigned long size;
+  BitPtr *wordBitPtrs;
+
+  void CheckBufOverrun (unsigned long num) {
+    if (wordBitPtrs[num].here > wordBitPtrs[num+1].start) {
+      cerr << "numDocs: " << numDocs << "\n";
+      cerr << "numChunkDocs: " << numChunkDocs << "\n";
+      cerr << "numDocsInChunk: " << numDocsInChunk << "\n";
+      cerr << "numFrags: " << numFrags << "\n";
+      cerr << "numFragsInChunk: " << numFragsInChunk << "\n";
+      cerr << "chunkStartFragNum: " << chunkStartFragNum << "\n";
+      cerr << "num: " << num << "\n";
+      cerr << "[num].start: " << wordBitPtrs[num].start << "\n";
+      cerr << "[num].here: " << wordBitPtrs[num].here << "\n";
+      cerr << "[num+1].start: " << wordBitPtrs[num+1].start << "\n";
+      FatalError (1, "Bit buffer overrun");
+    }
+  }
+  
+public:
+  void Clear ();
+  WordBitPtrs () { wordBitPtrs = NULL; Clear(); }
+  ~WordBitPtrs ();
+  void SetSize (unsigned long _numWords,
+		unsigned long _numTags);
+
+  void ResetPtrs () {
+    if (wordBitPtrs == NULL) return;
+    unsigned long i;
+    for (i=0; i<size; ++i) wordBitPtrs[i].Clear();
+  }
+  
+  BitPtr &GetWordBitPtr (unsigned long wordNum)
+    { return wordBitPtrs[wordNum]; }
+  unsigned long &GetWordStart (unsigned long wordNum)
+    { return wordBitPtrs[wordNum].start; }
+  unsigned long &GetWordHere (unsigned long wordNum)
+    { return wordBitPtrs[wordNum].here; }
+  void CheckWordBufOverrun (unsigned long wordNum)
+    { CheckBufOverrun (wordNum); }
+
+  BitPtr &GetTagBitPtr (unsigned long tagNum)
+    { return wordBitPtrs[tagNum + numWords]; }
+  unsigned long &GetTagStart (unsigned long tagNum)
+    { return wordBitPtrs[tagNum + numWords].start; }
+  unsigned long &GetTagHere (unsigned long tagNum)
+    { return wordBitPtrs[tagNum + numWords].here; }
+  void CheckTagBufOverrun (unsigned long tagNum)
+    { CheckBufOverrun (tagNum + numWords); }
+
+  BitPtr &GetEndBitPtr ()
+    { return wordBitPtrs[size-1]; }
+  unsigned long &GetEndStart ()
+    { return wordBitPtrs[size-1].start; }
+  unsigned long &GetEndHere ()
+    { return wordBitPtrs[size-1].here; }
+};
+
+
+struct IP2TagInfo {
+  bool inTag;
+  unsigned long startFrag;
+  unsigned long tagNum;
+
+  IP2TagInfo () {
+    inTag = false;
+    startFrag = 0;
+    tagNum = 0;
+  }
+};
+
+// maps tags to tag information
+typedef map<UCArray, IP2TagInfo, DictLTUCArray> TagMapDict;
+
+
+// class to handle the translation of occurrence order
+// to dictionary order for words and tags
+class OccurToDictConverter {
+protected:
+  unsigned long pos;
+  unsigned long val;
+  FILE *transFile;
+  random_bitio_buffer rbs;
+
+  unsigned long wordDictSize;
+  unsigned long tagDictSize;
+
+  void SeekStart ();
+  unsigned long TranslateNum (unsigned long num);
+  
+public:
+  OccurToDictConverter ();
+  ~OccurToDictConverter ();
+
+  void Open (char *filename, unsigned long _wordDictSize,
+	     unsigned long _tagDictSize);
+
+  // Close frees all allocated memory
+  void Close ();
+
+  unsigned long TranslateWord (unsigned long occurNum)
+    { return TranslateNum (occurNum); }
+  unsigned long TranslateTag (unsigned long occurNum)
+    { return TranslateNum (occurNum+wordDictSize); }
+};
+
+
+struct InvfStateRec {
+  mg_ullong start;
+  mg_ullong here;
+  unsigned long lastFragNum;
+  unsigned long B;
+
+  void Clear () {
+    start = here = 0;
+    lastFragNum = B = 0;
+  }
+  InvfStateRec () { Clear (); }
+};
+
+
+#define ISR_SIZE 1024
+
+class InvfStateCache {
+protected:
+  InvfStateRec recCache [ISR_SIZE];
+  unsigned long startNum;
+
+  FILE *stateFile;
+
+  void ClearCache () {
+    unsigned int i = 0;
+    for (i=0; i<ISR_SIZE; ++i) recCache[i].Clear();
+  }
+  
+public:
+  InvfStateCache ();
+  ~InvfStateCache ();
+  
+  void Open (char *filename);
+  void Close ();
+
+  // previous references to state records may be
+  // invalidated calling GetRec
+  InvfStateRec &GetRec (unsigned long num);
+};
+
+
+static invf_dict_header idh;
+static WordBitPtrs bitPtrs;
+
+static FILE *chunkFile = NULL;
+static stdio_bitio_buffer chunkBuf;
+
+static unsigned long ivfMemBufSize = 0;
+static char *ivfMemBuf = NULL;
+
+// word and tag dictionaries. a map is used for the tag dictionary
+// as it should never be very big (and the perfect hash function
+// sometimes has trouble with small values).
+static perf_hash_data *wordHashDict = NULL;
+static TagMapDict tagMapDict;
+
+// information about all the different levels
+static FIvfLevel ivfLevel;
+
+static OccurToDictConverter occurConvert;
+
+// information about the state of the inverted file
+static InvfStateCache invfState;
+
+static char collectFilename[512];
+
+
+void WordBitPtrs::Clear () {
+  numWords = 0;
+  numTags = 0;
+  size=0;
+  if (wordBitPtrs != NULL) delete [] wordBitPtrs;
+  wordBitPtrs = NULL;
+}
+
+WordBitPtrs::~WordBitPtrs () {
+  if (wordBitPtrs != NULL) delete [] wordBitPtrs;
+}
+
+void WordBitPtrs::SetSize (unsigned long _numWords,
+			   unsigned long _numTags){
+  Clear();
+  numWords = _numWords;
+  numTags = _numTags;
+  size = numWords + numTags + 1;
+  wordBitPtrs = new BitPtr [size];
+}
+
+
+void OccurToDictConverter::SeekStart () {
+  if (transFile == NULL) return;
+  rbs.SEEK_X (sizeof (unsigned long) * 8);
+  pos = 0;
+}
+
+unsigned long OccurToDictConverter::TranslateNum (unsigned long num) {
+  if (num < pos) SeekStart ();
+  while (pos <= num) {
+    if (pos < wordDictSize)
+      val = rbs.binary_decode (wordDictSize + 1, NULL) - 1;
+    else 
+      val = rbs.binary_decode (tagDictSize + 1, NULL) - 1;
+    ++pos;
+  }
+  return val;
+}
+
+OccurToDictConverter::OccurToDictConverter () {
+  pos = 0;
+  val = 0;
+  transFile = NULL;
+  wordDictSize = 0;
+  tagDictSize = 0;
+}
+
+OccurToDictConverter::~OccurToDictConverter () {
+  if (transFile != NULL) Close ();
+}
+
+void OccurToDictConverter::Open (char *filename, unsigned long _wordDictSize,
+				 unsigned long _tagDictSize) {
+  if (transFile != NULL) Close ();
+
+  wordDictSize = _wordDictSize;
+  tagDictSize = _tagDictSize;
+
+  transFile = open_file (filename, INVF_CHUNK_TRANS_SUFFIX, "rb",
+			 MAGIC_CHUNK_TRANS, MG_ABORT);
+  rbs.attachFile (transFile, RND_BUF_SIZE);
+  SeekStart ();
+  val = 0;
+}
+
+void OccurToDictConverter::Close () {
+  if (transFile == NULL) return;
+
+  rbs.done ();
+  fclose (transFile);
+  transFile = NULL;
+  pos = 0;
+  val = 0;
+
+  wordDictSize = 0;
+  tagDictSize = 0;
+}
+
+
+
+
+InvfStateCache::InvfStateCache () {
+  startNum = 0;
+  stateFile = NULL;
+}
+
+InvfStateCache::~InvfStateCache () {
+  if (stateFile != NULL) Close ();
+}
+  
+void InvfStateCache::Open (char *filename) {
+  if (stateFile != NULL) Close();
+
+  // open the state file
+  char path[512];
+  sprintf (path, FILE_NAME_FORMAT ".%ld", get_basepath (), filename,
+           ".invf.state", (long) getpid ());
+  if (!(stateFile = fopen (path, "wb+"))) {
+    Message ("Unable to create \"%s\"", path);
+    exit (1);
+  }
+  unlink (path); // file will be deleted after it is closed
+  // reset the buffer
+  startNum = 0;
+  ClearCache();
+}
+
+void InvfStateCache::Close () {
+  if (stateFile == NULL) return;
+  fclose (stateFile);
+  stateFile = NULL;
+  startNum = 0;
+}
+
+InvfStateRec &InvfStateCache::GetRec (unsigned long num) {
+  // see if cached
+  if ((num >= startNum) && (num < startNum + ISR_SIZE))
+    return recCache[num-startNum];
+
+  // not cached, write out this lot of records and read in
+  fseek (stateFile, startNum*sizeof (InvfStateRec), SEEK_SET);
+  fwrite ((char *) recCache, sizeof (InvfStateRec), ISR_SIZE, stateFile);
+
+  // read in the new set of records
+  ClearCache ();
+  startNum = num - (num % ISR_SIZE);
+  fseek (stateFile, startNum*sizeof (InvfStateRec), SEEK_SET);
+  fread ((char *) recCache, sizeof (InvfStateRec), ISR_SIZE, stateFile);
+
+  return recCache[num-startNum];
+}
+
+
+
+static void ClearCharBuf (char *buf, unsigned long size) {
+  char *end = buf + size;
+  while (buf != end) *buf++ = 0;
+}
+
+static void ReadWordDict (char *filename) {
+  // read in the perfect hash function for the word dictionary
+  FILE *wordHashFile = open_file (filename, INVF_DICT_HASH_SUFFIX, "rb",
+				  MAGIC_HASH, MG_ABORT);
+  if (!(wordHashDict = read_perf_hash_data (wordHashFile))) {
+    FatalError (1, "Unable to read in hash data for word dictionary");
+  }
+  fclose (wordHashFile);
+}
+
+static void ReadTagDict (char *filename, invf_dict_header &_idh) {
+  // open the file
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+
+  // seek to the start of the tag dictionary
+  fseek (dictFile, _idh.tag_dict_start, SEEK_SET);
+  
+  unsigned long tagNum;
+  dict_el thisEl;
+  for (tagNum = 0; tagNum < _idh.tag_dict_size; ++tagNum) {
+    thisEl.Read (dictFile);
+    tagMapDict[thisEl.el].tagNum = tagNum;
+  }
+  
+  fclose (dictFile);
+}
+
+static void ReadLevelFile (char *filename) {
+  FILE *f;
+  f = open_file (filename, INVF_LEVEL_SUFFIX, "rb",
+		 MAGIC_INVF_LEVELS, MG_ABORT);
+  ivfLevel.Read (f);
+  fclose (f);
+}
+
+void CheckIntOverflow (mg_ullong totalIBits, mg_ullong lastTotalIBits) {
+  if (totalIBits < lastTotalIBits) {
+    fprintf(stderr, "ERROR: The totalIBits counter (%d byte unsigned integer) has overflowed.\n", sizeof (mg_ullong));
+    if (sizeof (mg_ullong) < 8) {
+      fprintf(stderr, "       Try compiling with GCC to enable use of 8 bytes for this counter.\n");
+    }
+    fprintf(stderr, "       Build aborted.\n");
+    exit(1);
+  }
+}
+
+// assumes the inverted file state file has been opened
+static void InitInvfState (char *filename,
+			   invf_dict_header &_idh,
+			   InvfStateCache &_invfState,
+			   mg_ullong &totalIBits,
+			   bool wordLevelIndex) {
+  // read in the dictionary, setting inverted state information
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+
+  // seek to the start of the word dictionary
+  fseek (dictFile, _idh.word_dict_start, SEEK_SET);
+
+  // add the word entries
+  word_dict_el wordEl;
+  wordEl.SetNumLevels (_idh.num_levels);
+  unsigned long dictWordNum, p;
+  mg_ullong lastTotalIBits;
+  unsigned long N = _idh.num_frags;
+  for (dictWordNum=0; dictWordNum<_idh.word_dict_size; ++dictWordNum) {
+    // lastTotalIBits is used to detect integer overflow
+    lastTotalIBits = totalIBits;
+
+    // read the next word and associated information
+    wordEl.Read (dictFile, _idh.num_levels);
+
+    // update the state record
+    p = wordEl.frag_occur;
+    InvfStateRec &wisr = _invfState.GetRec (dictWordNum);
+    wisr.start = totalIBits;
+    wisr.here = totalIBits;
+    wisr.B = BIO_Bblock_Init (N, p);
+    
+    // add the length of the fragment numbers
+    totalIBits += BIO_Bblock_Bound_b (N, p, wisr.B);
+
+    // if needed, add the length of the fragment frequency information
+    if (!wordLevelIndex)
+      totalIBits += BIO_Gamma_Bound (wordEl.freq, wordEl.frag_occur);
+
+    // align next byte
+#ifdef USE_LONG_LONG
+    totalIBits = (totalIBits + 7ull) & 0xfffffffffffffff8ull;
+#else
+    totalIBits = (totalIBits + 7ul) & 0xfffffff8ul;
+#endif
+
+    CheckIntOverflow (totalIBits, lastTotalIBits);
+  }
+
+  
+  // seek to the start of the tag dictionary
+  fseek (dictFile, _idh.tag_dict_start, SEEK_SET);
+
+  // add the tag entries
+  dict_el tagEl;
+  unsigned long dictTagNum;
+  N = _idh.num_frags;
+  for (dictTagNum=0; dictTagNum<_idh.tag_dict_size; ++dictTagNum) {
+    // lastTotalIBits is used to detect integer overflow
+    lastTotalIBits = totalIBits;
+
+    // read the next tag and associated information
+    tagEl.Read (dictFile);
+
+    // update the state record
+    p = tagEl.frag_occur*2;
+    InvfStateRec &tisr = _invfState.GetRec (dictTagNum + _idh.word_dict_size);
+    tisr.start = totalIBits;
+    tisr.here = totalIBits;
+    tisr.B = BIO_Bblock_Init (N+p, p);
+    
+    // add the length of the fragment numbers (two numbers for each
+    // tag, one for start and one for end)
+    totalIBits += BIO_Bblock_Bound_b (N+p, p, tisr.B);
+
+    // align next byte
+#ifdef USE_LONG_LONG
+    totalIBits = (totalIBits + 7ull) & 0xfffffffffffffff8ull;
+#else
+    totalIBits = (totalIBits + 7ul) & 0xfffffff8ul;
+#endif
+
+    CheckIntOverflow (totalIBits, lastTotalIBits);
+  }
+
+  fclose (dictFile);
+}
+
+/*
+// assumes the chunk tag information has been placed in .first
+static void PrintChunkInfo (unsigned long chunkMem,
+			    unsigned long numChunkWords,
+			    unsigned long numChunkTags) {
+  static unsigned long chunksRead = 0;
+  ++chunksRead;
+  cout << "Chunk Number: " << chunksRead << "\n";
+  cout << "numChunkDocs " << numDocsInChunk << "\n";
+  cout << "numChunkFrags " << numFragsInChunk << "\n";
+  cout << "mem " << chunkMem << "\n";
+  cout << "numWords " << numChunkWords << "\n";
+  cout << "numTags " << numChunkTags << "\n\n";
+
+  TagMapDict::iterator tagMapHere = tagMapDict.begin();
+  TagMapDict::iterator tagMapEnd = tagMapDict.end();
+  while (tagMapHere != tagMapEnd) {
+    unsigned long tagMapNum = (*tagMapHere).second.tagNum;
+    cout << (*tagMapHere).first << " " << tagMapNum << " "
+	 << bitPtrs.GetTagBitPtr(tagMapNum).here << "\n";
+    ++tagMapHere;
+  }
+}
+*/
+
+void ReadChunk (invf_dict_header &_idh, bool wordLevelIndex) {
+  // reset globals
+  numChunkDocs = 0;
+  chunkStartFragNum = numFrags;
+
+  // read in information about this chunk
+  numDocsInChunk = chunkBuf.gamma_decode (NULL) - 1;
+  if (numDocsInChunk == 0)
+    FatalError (1, "The number of docs in the current chunk is 0");
+
+  numFragsInChunk = chunkBuf.gamma_decode (NULL) - 1;
+  unsigned long chunkMem = chunkBuf.gamma_decode (NULL) - 1;
+
+  if (chunkMem > ivfMemBufSize)
+    FatalError (1, "Chunk memory size is greater than maximum");
+  
+  unsigned long numChunkWords = chunkBuf.gamma_decode (NULL) - 1;
+  unsigned long numChunkTags = chunkBuf.gamma_decode (NULL) - 1;
+
+  
+  // reset stuff
+  ClearCharBuf (ivfMemBuf, ivfMemBufSize);
+  bitPtrs.ResetPtrs();  
+
+  // read in the entries in occurrence order storing the
+  // "chunkWordCount" in "start" and the "chunkFragCount"
+  // in "here"
+  unsigned long numOccur;
+  unsigned long wordNum;
+  for (numOccur=0; numOccur<numChunkWords; ++numOccur) {
+    wordNum = occurConvert.TranslateWord (numOccur);
+    BitPtr &wordPtr = bitPtrs.GetWordBitPtr (wordNum);
+    wordPtr.start = chunkBuf.gamma_decode (NULL) - 1;
+    if (wordPtr.start >= 2)
+      wordPtr.here = chunkBuf.gamma_decode (NULL);
+    else wordPtr.here = wordPtr.start;
+  }
+  unsigned long tagNum;
+  for (numOccur=0; numOccur<numChunkTags; ++numOccur) {
+    tagNum = occurConvert.TranslateTag (numOccur);
+    BitPtr &tagPtr = bitPtrs.GetTagBitPtr (tagNum);
+    // only chunkFragCount is encoded for tags
+    tagPtr.start = chunkBuf.gamma_decode (NULL) - 1;
+    tagPtr.here = tagPtr.start;
+  }
+
+  /*  PrintChunkInfo (chunkMem, numChunkWords, numChunkTags);*/
+  
+  // create the bit ptrs in dictionary order
+  unsigned long totalIBits = 0; // only dealing with memory
+  unsigned long chunkWordCount, chunkFragCount;
+  for (wordNum=0; wordNum<_idh.word_dict_size; ++wordNum) {
+    BitPtr &wordPtr = bitPtrs.GetWordBitPtr (wordNum);
+    chunkWordCount = wordPtr.start;
+    chunkFragCount = wordPtr.here;
+    wordPtr.start = totalIBits;
+    wordPtr.here = totalIBits;
+    wordPtr.lastFragNum = chunkStartFragNum;
+    wordPtr.lgB = 0;
+    if (chunkWordCount > 0) {
+      wordPtr.lgB = floorlog_2 (BIO_Bblock_Init_W (numFragsInChunk,
+						   chunkFragCount));
+      totalIBits += BIO_Bblock_Bound (numFragsInChunk, chunkFragCount);
+      // use unary encoding for memory buffer encoding of fragment freq
+      if (!wordLevelIndex) {
+	totalIBits += chunkWordCount;
+      }
+    }
+  }
+  for (tagNum=0; tagNum<_idh.tag_dict_size; ++tagNum) {
+    BitPtr &tagPtr = bitPtrs.GetTagBitPtr (tagNum);
+    chunkFragCount = tagPtr.here;
+    tagPtr.start = totalIBits;
+    tagPtr.here = totalIBits;
+    tagPtr.lastFragNum = chunkStartFragNum;
+    tagPtr.lgB = 0;
+    if (chunkFragCount > 0) {
+      unsigned long pTag = chunkFragCount*2;
+      tagPtr.lgB = floorlog_2 (BIO_Bblock_Init_W (numFragsInChunk+pTag,
+						  pTag));
+      unsigned long bLen = BIO_Bblock_Bound (numFragsInChunk+pTag,
+					     pTag);
+//        cout << tagNum + _idh.word_dict_size << " ";
+//        cout << "numFrags: " << numFragsInChunk
+//    	   << " chunkFragCount: " << chunkFragCount
+//    	   << " B: " << 1 << tagPtr.lgB
+//    	   << " blen: " << blen << "\n";
+      totalIBits += bLen;
+    }
+  }
+  bitPtrs.GetEndStart() = totalIBits;
+  bitPtrs.GetEndHere() = totalIBits;
+
+  if ((totalIBits + 7ul) >> 3ul > chunkMem) {
+    cerr << "totalIBits: " << totalIBits << "\n";
+    cerr << "bytes: " << ((totalIBits + 7ul) >> 3ul) << "\n";
+    cerr << "chunkMem: " << chunkMem << "\n";
+    FatalError (1, "Pointers exceed buffer size");
+  }
+}
+
+
+
+
+int init_ivf_2 (const TagInfo &/*tagInfo*/, char *filename) {
+  // read in compressed dictionary header
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+  idh.Read (dictFile);
+  fclose (dictFile);
+
+  // set the size of the bit ptrs
+  bitPtrs.SetSize (idh.word_dict_size, idh.tag_dict_size);
+
+  // open the chunk file and read in the maximum memory needed
+  // for the inverted memory buffer
+  chunkFile = open_file (filename, INVF_CHUNK_SUFFIX, "rb",
+			 MAGIC_CHUNK, MG_ABORT);
+  ReadUL (chunkFile, ivfMemBufSize);
+  chunkBuf.attachFile (chunkFile);
+
+  // allocate memory for the inverted buffer
+  ivfMemBuf = new char [ivfMemBufSize];
+  ClearCharBuf (ivfMemBuf, ivfMemBufSize);
+
+  // read in the word dictionary
+  ReadWordDict (filename);
+
+  // read in the tag dictionary
+  ReadTagDict (filename, idh);
+
+  // read in the level information
+  ReadLevelFile (filename);
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+  
+  // set up the translation table file
+  occurConvert.Open (filename, idh.word_dict_size, idh.tag_dict_size);
+
+  // reset some globals
+  numDocs = 0;
+  numChunkDocs = 0;
+  numDocsInChunk = 0;
+  numFrags = 0;
+  numFragsInChunk = 0;
+  chunkStartFragNum = 0;
+
+  strcpy (collectFilename, filename);
+  
+  
+  // create the inverted file
+  mg_ullong totalIBits = 0;
+  FILE *invfFile = create_file (filename, INVF_SUFFIX, "wb",
+				MAGIC_INVF, MG_ABORT);
+  totalIBits += sizeof (unsigned long) * 8; // magic number
+  totalIBits += 8 * 200;                    // 200 byte gap -- why??????
+  fclose (invfFile);
+
+  // init the inverted file state cache
+  invfState.Open (filename);
+  InitInvfState (filename, idh, invfState, totalIBits, wordLevelIndex);
+
+  return COMPALLOK;
+}
+
+static void CloseTextTag (IP2TagInfo &tInfo, const UCArray &/*tagName*/) {
+  if (!tInfo.inTag) return;
+
+    // add this tag to the inverted list
+  BitPtr &tagBitPtr = bitPtrs.GetTagBitPtr (tInfo.tagNum);
+  unsigned long endFrag = numFrags;
+  int b = 1 << tagBitPtr.lgB;
+
+  /*
+  cout << (tInfo.tagNum+idh.word_dict_size) << " \"<" << tagName << ">\" "
+       << tInfo.startFrag << " " << endFrag << "\n";
+  */
+    
+  mems_bitio_buffer buffer ((u_char *) ivfMemBuf, tagBitPtr.here);
+  buffer.bblock_encode (tInfo.startFrag - tagBitPtr.lastFragNum + 1,
+			b, NULL);
+  buffer.bblock_encode (endFrag - tInfo.startFrag + 1, b, NULL);
+  tagBitPtr.lastFragNum = endFrag;
+  tagBitPtr.here = buffer.position();
+  buffer.encodeDone();
+  
+  // check for buffer overrun
+  bitPtrs.CheckTagBufOverrun (tInfo.tagNum);
+  
+  // reset information about this tag
+  tInfo.inTag = false;
+  tInfo.startFrag = 0;
+}
+
+static void ProcessOpenTag (const TextEl &el, bool &inFrag) {
+  // close tag if open
+  IP2TagInfo &tInfo = tagMapDict[el.tagName];
+  if (tInfo.inTag) CloseTextTag (tInfo, el.tagName);
+
+  // open this tag
+  tInfo.inTag = true;
+  tInfo.startFrag = numFrags;
+
+  // check for start of next fragment
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+  if (!wordLevelIndex && el.tagName == ivfLevel.indexLevel) {
+    ++numFrags;
+    inFrag = true;
+  }
+}
+
+static void ProcessCloseTag (const TextEl &el, bool &inFrag) {
+  // check for end of fragment
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+  if (!wordLevelIndex && el.tagName == ivfLevel.indexLevel) {
+    inFrag = false;
+  }
+
+  IP2TagInfo &tInfo = tagMapDict[el.tagName];
+  CloseTextTag (tInfo, el.tagName);
+}
+
+static void ProcessText (const TextEl &el, bool &inFrag) {
+  // make sure this text is to be indexed
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+  if (!wordLevelIndex && !inFrag) return;
+
+  const unsigned char *textHere = &(el.text[0]);
+  const unsigned char *textEnd = &(el.text[el.text.size() - 1]);
+  unsigned char mgWord[MAXSTEMLEN + 1];
+  
+  if (!inaword_mgpp (textHere, textEnd))
+    ParseNonindexWord (textHere, textEnd);
+
+  
+  // Alternately parse off words and non-words from the input
+
+  while (textHere <= textEnd) {
+    textHere = ParseIndexMGWord (textHere, textEnd, mgWord);
+    textHere = ParseNonindexWord (textHere, textEnd);
+
+    if (mgWord[0] > 0) {
+      if (wordLevelIndex) ++numFrags;
+
+      unsigned long wordNum = perf_hash (wordHashDict, mgWord);
+
+      /*
+      cout << wordNum << " \"";
+      cout.write (mgWord+1, *mgWord);
+      cout << "\" " << numFrags << "\n";
+      */
+      
+      // add this word to the inverted list
+      BitPtr &wordBitPtr = bitPtrs.GetWordBitPtr (wordNum);
+      unsigned long fragNum = numFrags;
+      int b = 1 << wordBitPtr.lgB;
+  
+      mems_bitio_buffer buffer ((u_char *) ivfMemBuf, wordBitPtr.here);
+
+      // note: this assumes that fragments don't carry over between
+      // chunks (which they don't because all tags are closed at the
+      // end of each document and chunks are based on document
+      // boundaries), i.e. the first fragment number must be greater
+      // than the starting fragment number of the chunk.
+      if (fragNum > wordBitPtr.lastFragNum) {
+	buffer.bblock_encode ((fragNum - wordBitPtr.lastFragNum - 1) + 1,
+			      b, NULL);
+	if (!wordLevelIndex) buffer.encodeBit (1); // freq = 1
+	
+      } else if (!wordLevelIndex) {
+	// add one to the frequency count for this word
+	buffer.seek (buffer.position()-1);
+	buffer.encodeBit (0); // unary encoding -- last = 1
+	buffer.encodeBit (1);
+      }
+      
+      wordBitPtr.lastFragNum = fragNum;
+      wordBitPtr.here = buffer.position();
+      buffer.encodeDone();
+      
+      // check for buffer overrun
+      bitPtrs.CheckWordBufOverrun (wordNum);
+    }
+  }
+}
+
+// combine the in memory inverted buffer with the disk
+// based inverted file
+static void DiskMerge (char *filename) {
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+
+      // make sure we have something to process
+  if (numChunkDocs <= 0) return;
+    
+  // open the inverted file
+  FILE *invfFile = open_file (filename, INVF_SUFFIX, "rb+",
+			      MAGIC_INVF, MG_ABORT);
+  random_bitio_buffer invfOutBuf (invfFile);
+
+  // set up to decode the entries in memory
+  mems_bitio_buffer memInBuf ((u_char *) ivfMemBuf, 0);
+
+  // write out the word information
+  unsigned long wordNum;
+  int b;
+  unsigned long currFragNum;
+  unsigned long delta;
+  unsigned long currFreq;
+  for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+    // go to the end of the last inverted file entry
+    InvfStateRec &wordDiskState = invfState.GetRec (wordNum);
+    invfOutBuf.SEEK_X (wordDiskState.here);
+
+    // go to the start of the inverted chunk info in memory
+    BitPtr &wordBitPtr = bitPtrs.GetWordBitPtr (wordNum);
+    memInBuf.seek(wordBitPtr.start);
+
+    // decode each entry and re-write to disk
+    currFragNum = chunkStartFragNum;
+    while (memInBuf.position() < wordBitPtr.here) {
+      // decode word entry
+      b = 1 << wordBitPtr.lgB;
+      delta = memInBuf.bblock_decode (b, NULL);
+      currFragNum += delta;
+      if (!wordLevelIndex) currFreq = memInBuf.unary_decode (NULL);
+      else currFreq = 1;
+
+      // recode on disk
+      invfOutBuf.bblock_encode (currFragNum-wordDiskState.lastFragNum,
+				wordDiskState.B, NULL);
+      if (!wordLevelIndex) invfOutBuf.gamma_encode (currFreq, NULL);
+      wordDiskState.lastFragNum = currFragNum;
+    }
+
+    wordDiskState.here = invfOutBuf.TELL_X();
+  }
+
+  // write out the tag information
+  unsigned long tagNum;
+  unsigned long currTagStart;
+  unsigned long currTagEnd;
+  for (tagNum=0; tagNum<idh.tag_dict_size; ++tagNum) {
+    // go to the end of the last inverted file entry
+    InvfStateRec &tagDiskState = invfState.GetRec (tagNum+idh.word_dict_size);
+    invfOutBuf.SEEK_X (tagDiskState.here);
+
+    // go to the start of the inverted chunk info in memory
+    BitPtr &tagBitPtr = bitPtrs.GetTagBitPtr (tagNum);
+    memInBuf.seek(tagBitPtr.start);
+
+    // decode each entry and re-write to disk
+    currTagEnd = chunkStartFragNum;
+    while (memInBuf.position() < tagBitPtr.here) {
+      // decode tag entry
+      b = 1 << tagBitPtr.lgB;
+      delta = memInBuf.bblock_decode (b, NULL) - 1;
+      currTagStart = currTagEnd + delta;
+      delta = memInBuf.bblock_decode (b, NULL) - 1;
+      currTagEnd = currTagStart + delta;
+
+      // recode on disk
+      invfOutBuf.bblock_encode (currTagStart-tagDiskState.lastFragNum+1,
+				tagDiskState.B, NULL);
+      invfOutBuf.bblock_encode (currTagEnd-currTagStart+1,
+				tagDiskState.B, NULL);
+
+      tagDiskState.lastFragNum = currTagEnd;
+    }
+
+    tagDiskState.here = invfOutBuf.TELL_X();
+  }
+
+  memInBuf.done();
+
+  invfOutBuf.encodeDone();
+  fclose (invfFile);
+}
+
+
+int process_ivf_2 (const TagInfo &/*tagInfo*/, const TextElArray &doc) {
+  bool wordLevelIndex = ivfLevel.indexLevel.empty();
+  bool inFrag = false;
+  if (wordLevelIndex) inFrag = true; // unconditional
+  
+  // get next chunk information if need to. the chunk information
+  // is needed before the first document is processed
+  if (numChunkDocs >= numDocsInChunk) ReadChunk (idh, wordLevelIndex);
+
+  // process each text element
+  TextElArray::const_iterator here = doc.begin();
+  TextElArray::const_iterator end = doc.end();
+  while (here != end) {
+    // process this element
+    if ((*here).elType == OpenTagE) ProcessOpenTag (*here, inFrag);
+    else if ((*here).elType == CloseTagE) ProcessCloseTag (*here, inFrag);
+    else ProcessText (*here, inFrag);
+    
+    ++here;
+  }
+
+  // close off any unclosed tags
+  TagMapDict::iterator tdHere = tagMapDict.begin();
+  TagMapDict::iterator tdEnd = tagMapDict.end();
+  while (tdHere != tdEnd) {
+    CloseTextTag ((*tdHere).second, (*tdHere).first);
+    ++tdHere;
+  }
+  
+  // we've processed one more document
+  ++numDocs;
+  ++numChunkDocs;
+
+  // merge the memory based inverted file with the one on
+  // disk if this is the end of this chunk
+  if (numChunkDocs >= numDocsInChunk) DiskMerge (collectFilename);
+  
+  return COMPALLOK;
+}
+
+
+static void CondenseInvfFile (char *filename, unsigned long &bytesOutput) {
+  FILE *inInvfFile = open_file (filename, INVF_SUFFIX, "rb",
+				MAGIC_INVF, MG_ABORT);
+  FILE *outInvfFile = open_file (filename, INVF_SUFFIX, "rb+",
+				 MAGIC_INVF, MG_ABORT);
+
+  // skip the magic number
+  fseek (outInvfFile, sizeof (unsigned long), SEEK_SET);
+  
+  // write the inverted file header -- use defaults for most things
+  invf_file_header ifh;
+  ifh.no_of_words = idh.word_dict_size;
+  ifh.no_of_tags = idh.tag_dict_size;
+  ifh.word_level_index = (ivfLevel.indexLevel.empty()) ? 1 : 0;
+  ifh.Write (outInvfFile);
+
+  bytesOutput = ftell (outInvfFile);
+
+  // process each meaningful byte in the file
+  unsigned long numEntries = ifh.no_of_words + ifh.no_of_tags;
+  unsigned long entryNum;
+  mg_ullong lastStart = 0;
+  for (entryNum = 0; entryNum < numEntries; ++entryNum) {
+    InvfStateRec &stateRec = invfState.GetRec (entryNum);
+
+    // overrun check
+    if (stateRec.start < lastStart)
+      FatalError (1, "Inverted file Buffer overrun");
+    lastStart = stateRec.start;
+    
+    unsigned long oldEntryStart = stateRec.start >> 3;
+    unsigned long oldEntryStartOver = stateRec.start & 7; // should be 0
+    unsigned long oldEntryEnd = (stateRec.here + 7) >> 3;  // byte after end
+    unsigned long oldEntryEndOver = stateRec.here & 7;
+
+    fseek (inInvfFile, oldEntryStart, SEEK_SET);
+
+    stateRec.here -= stateRec.start;
+    stateRec.start = (mg_ullong)bytesOutput * 8 + oldEntryStartOver;
+    stateRec.here += stateRec.start;
+    while (oldEntryStart < oldEntryEnd) {
+      unsigned char c = getc (inInvfFile);
+      if (oldEntryStart == oldEntryEnd - 1) {
+	u_char ands[8] =
+	{0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
+	c &= ands[oldEntryEndOver];
+      }
+      putc (c, outInvfFile);
+      ++bytesOutput;
+      ++oldEntryStart;
+    }
+  }  
+
+  fclose (inInvfFile);
+
+#ifdef __WIN32__
+  if (_chsize (_fileno (outInvfFile), bytesOutput) != 0)
+    Message ("Could not truncate invf.");
+#else
+  ftruncate (fileno (outInvfFile), bytesOutput);
+#endif
+
+  fclose (outInvfFile);
+}
+
+static void OutputInvfIdx (char *filename, unsigned long invfNumBytes) {
+  FILE *invfIdxFile = create_file (filename, INVF_IDX_SUFFIX, "wb",
+				   MAGIC_INVI, MG_ABORT);
+
+  // process each meaningful byte in the file
+  unsigned long numEntries = idh.word_dict_size + idh.tag_dict_size;
+  unsigned long entryNum;
+  for (entryNum = 0; entryNum < numEntries; ++entryNum) {
+    InvfStateRec &stateRec = invfState.GetRec (entryNum);
+
+    // assumes that inverted entries start at beginning of each byte
+    if (!WriteUL (invfIdxFile, (stateRec.start >> 3))) break;
+  }
+
+  WriteUL (invfIdxFile, invfNumBytes);
+
+  fclose (invfIdxFile);
+}
+
+
+int done_ivf_2 (const TagInfo &/*tagInfo*/, char *filename) {
+  // close most open files
+  if (chunkFile != NULL) {
+    chunkBuf.done();
+    fclose (chunkFile);
+    chunkFile = NULL;
+  }
+  occurConvert.Close();
+  
+  // free allocated memory
+  bitPtrs.Clear();
+  if (ivfMemBuf != NULL) { delete [] ivfMemBuf; ivfMemBuf = NULL; }
+  free_perf_hash (wordHashDict);
+  wordHashDict = NULL;
+  tagMapDict.erase (tagMapDict.begin(), tagMapDict.end());
+
+  // condense the inverted file and truncate it
+  // this function also writes out the inverted header
+  unsigned long invfNumBytes = 0;
+  CondenseInvfFile (filename, invfNumBytes);
+
+  OutputInvfIdx (filename, invfNumBytes);
+
+  // close the rest of the open files
+  invfState.Close ();
+  
+  return COMPALLOK;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.cpp	(revision 16561)
@@ -0,0 +1,218 @@
+/**************************************************************************
+ *
+ * locallib.cpp -- Misc functions
+ * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+
+#include "memlib.h"
+#include "bitio_gen.h"
+#include "huffman.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "locallib.h"
+#include "text.h"
+
+int 
+vecentropy (int *A, int n)
+{
+  int total = 0, i;
+  double bits = 0.0;
+
+  for (i = 0; i < n; ++i)
+    total += A[i];
+  for (i = 0; i < n; ++i)
+    if (A[i])
+      bits += A[i] * log2 (1.0 * total / A[i]);
+  return ((int) (bits + 0.9999));
+
+}
+
+static void 
+siftdown (int *A, int i, int n)
+{
+  int ms, t;
+
+  while (2 * i + 1 <= n - 1)
+    {
+      if (2 * i + 1 == n - 1 || A[2 * i + 1] < A[2 * i + 2])
+	ms = 2 * i + 1;
+      else
+	ms = 2 * i + 2;
+      if (A[i] > A[ms])
+	{
+	  t = A[i];
+	  A[i] = A[ms];
+	  A[ms] = t;
+	}
+      i = ms;
+    }
+}
+
+static void 
+buildheap (int *A, int n)
+{
+  int i;
+  for (i = n / 2 - 1; i >= 0; --i)
+    siftdown (A, i, n);
+}
+
+int 
+huffcodebits (unsigned long *A, int n)
+{
+  int i, j, tot = 0, bits = 0, v1, v2, *B;
+
+  B = (int *) Xmalloc (n * sizeof (int));
+  j = 0;
+  for (i = 0; i < n; ++i)
+    {
+      if (A[i])
+	{
+	  tot += (B[j++] = A[i]);
+	}
+    }
+  n = j;
+
+  if (n == 0 || tot == 0)
+    return (0);
+
+  buildheap (B, n);
+  while (n > 1)
+    {
+      v1 = B[0];
+      B[0] = B[n - 1];
+      --n;
+      siftdown (B, 0, n);
+      v2 = B[0];
+      B[0] = v1 + v2;
+      siftdown (B, 0, n);
+      bits += v1 + v2;
+    }
+  Xfree (B);
+  return (bits);
+}
+
+
+int 
+modelbits (unsigned long *A, int n)
+{
+  int i, bits = 0, last, N = 0;
+
+  last = -1;
+  for (i = 0; i < n; ++i)
+    {
+      if (A[i])
+	{
+	  bits += BIO_Gamma_Length (i - last) + BIO_Gamma_Length (A[i]);
+	  last = i;
+	  ++N;
+	}
+    }
+  if (N == 0)
+    return (0);
+  bits += BIO_Gamma_Length (N);
+  return (bits);
+}
+
+
+
+
+
+/* Find the next prime number larger than p */
+int 
+prime (int p)
+{
+  int limit;
+  p += (p & 1) + 1;
+  limit = (int) sqrt ((double) p) + 1;
+  do
+    {
+      int j;
+      while (limit * limit < p)
+	++limit;
+      for (j = 3; j <= limit && p % j; j += 2)
+	;			/* NULLBODY */
+      if (j > limit)
+	break;
+      p += 2;
+    }
+  while (1);
+  return (p);
+}
+
+
+
+int Read_cdh (FILE * f, compression_dict_header * cdh, u_long * /*mem*/,
+	      u_long * disk) {
+  if (disk)
+    (*disk) += sizeof (*cdh);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  if (fread (cdh, sizeof (*cdh), 1, f) == 1)
+    {
+      int i;
+      NTOHUL(cdh->dict_type);
+      NTOHUL(cdh->novel_method);
+      for (i = 0; i < TEXT_PARAMS; ++i)
+	NTOHUL(cdh->params[i]);
+      NTOHUL(cdh->num_words[0]);
+      NTOHUL(cdh->num_words[1]);
+      NTOHUL(cdh->num_word_chars[0]);
+      NTOHUL(cdh->num_word_chars[1]);
+      NTOHUL(cdh->lookback);
+      return 0;
+    }
+  else
+    return -1;
+}
+
+
+int 
+Read_cfh (FILE * f, comp_frags_header * cfh, u_long * mem, u_long * disk)
+{
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (Read_Huffman_Data (f, &cfh->hd, mem, disk) == -1)
+    return -1;
+  if (fread (&cfh->uncompressed_size,
+	     sizeof (cfh->uncompressed_size), 1, f) != 1)
+    return -1;
+  NTOHUL(cfh->uncompressed_size);  /* [RPAP - Jan 97: Endian Ordering] */
+
+  if (disk)
+    (*disk) += sizeof (cfh->uncompressed_size);
+
+  memset (cfh->huff_words_size, '\0', sizeof (cfh->huff_words_size));
+
+  if ((int)fread (cfh->huff_words_size + cfh->hd.mincodelen,
+	     sizeof (*cfh->huff_words_size),
+	     cfh->hd.maxcodelen - cfh->hd.mincodelen + 1, f) !=
+      cfh->hd.maxcodelen - cfh->hd.mincodelen + 1)
+    return -1;
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = cfh->hd.mincodelen; i < cfh->hd.maxcodelen + 1; ++i)
+    NTOHUL(cfh->huff_words_size[i]);
+
+  if (disk)
+    (*disk) += sizeof (*cfh->huff_words_size) *
+      (cfh->hd.maxcodelen - cfh->hd.mincodelen + 1);
+  return 0;
+}
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/locallib.h	(revision 16561)
@@ -0,0 +1,45 @@
+/**************************************************************************
+ *
+ * locallib.h -- Misc functions
+ * Copyright (C) 1994  Gary Eddy, Alistair Moffat and Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef LOCALLIB_H
+#define LOCALLIB_H
+
+#include "sysfuncs.h"
+
+#include "text.h"
+
+#define NUMOF(a) (sizeof(a)/sizeof((a)[0]))
+
+int vecentropy (int *A, int n);
+
+int huffcodebits (unsigned long *A, int n);
+
+int modelbits (unsigned long *A, int n);
+
+int prime (int p);
+
+
+int Read_cdh (FILE * f, compression_dict_header * cdh, u_long * mem, u_long * disk);
+
+int Read_cfh (FILE * f, comp_frags_header * cfh, u_long * mem, u_long * disk);
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg.h	(revision 16561)
@@ -0,0 +1,37 @@
+/**************************************************************************
+ *
+ * mg.h -- General header file
+ * Copyright (C) 1994  Neil Sharman, Alistair Moffat and Lachlan Andrew
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+#ifndef H_MG
+#define H_MG
+
+
+#define COMPALLOK	(0)
+#define COMPERROR	(-1)
+
+#define MAXSTRING       3000000
+#define BUFFERSIZE      (MAXSTRING+30000)
+
+#define	MAXLN	65536
+#define WORDS           1
+#define NONWORDS        0
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg.special.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg.special.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg.special.cpp	(revision 16561)
@@ -0,0 +1,58 @@
+/**************************************************************************
+ *
+ * mg.special.cpp -- Special pass for mg_passes
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "sysfuncs.h"
+#include "mg.h"
+#include "build.h"
+
+int init_special (const TagInfo &/*tagInfo*/, char * /*FileName*/) {
+  return COMPALLOK;
+}
+
+int process_special (const TagInfo &/*tagInfo*/, const TextElArray &doc) {
+  // print out the document
+  TextElArray::const_iterator here = doc.begin();
+  TextElArray::const_iterator end = doc.end();
+  while (here != end) {
+    if ((*here).elType == OpenTagE) {
+      cout << "** OpenTag  \"" << (*here).tagName << "\"\tText \""
+	   << (*here).text << "\"\n"; 
+    } else if ((*here).elType == CloseTagE) {
+      cout << "** CloseTag \"" << (*here).tagName << "\"\tText \""
+	   << (*here).text << "\"\n";
+    } else {
+      cout << "** Text     \"" << (*here).text << "\"\n";
+    }
+    
+    ++here;
+  }
+  
+  return COMPALLOK;
+}
+
+int done_special (const TagInfo &/*tagInfo*/, char * /*FileName*/) {
+  return COMPALLOK;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.cpp	(revision 16561)
@@ -0,0 +1,72 @@
+/**************************************************************************
+ *
+ * mg_errors.cpp -- Error related stuff for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include "sysfuncs.h"
+#include "memlib.h"
+
+#include "mg_errors.h"
+
+int mg_errno = MG_NOERROR;
+
+char *mg_errorstrs[] =
+{
+  "No error",
+  "Out of memory",
+  "File \"%s\" not found",
+  "Bad magic number in \"%s\"",
+  "Error reading \"%s\"",
+  "MG_BUFTOOSMALL",
+  "Files required for level 2 and 3 inversion are missing"};
+
+
+/* [RJM 07/98: Memory Leak] -- rest of file */
+
+static char null_data[] = "";
+char *mg_error_data = null_data;
+
+
+void MgErrorData (char *s)
+{
+  /* free the current error string, unless it is the null string */
+  if ((mg_error_data != NULL) && (mg_error_data != null_data)) {
+    delete []mg_error_data;
+    mg_error_data = null_data;
+  }
+
+  /* make a copy of the string */
+  mg_error_data = Xstrdup (s);
+
+  /* if the alloc failed set the error to the null string */
+  if (!mg_error_data) mg_error_data = null_data;
+}
+
+
+void MgErrorDeinit (void)
+{
+  /* free the current error string, unless it is the null string */
+  if ((mg_error_data != NULL) && (mg_error_data != null_data)) {
+    delete []mg_error_data;
+    mg_error_data = null_data;
+  }
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg_errors.h	(revision 16561)
@@ -0,0 +1,37 @@
+/**************************************************************************
+ *
+ * mg_errors.h -- Error related stuff for mgquery
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+
+extern int mg_errno;
+extern char *mg_error_data;
+extern char *mg_errorstrs[];
+
+
+#define MG_NOERROR 0
+#define MG_NOMEM 1
+#define MG_NOFILE 2
+#define MG_BADMAGIC 3
+#define MG_READERR 4
+#define MG_BUFTOOSMALL 5
+#define MG_INVERSION 6
+
+void MgErrorData (char *s);
+void MgErrorDeinit (void);
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.cpp	(revision 16561)
@@ -0,0 +1,274 @@
+/**************************************************************************
+ *
+ * mg_files.cpp -- Routines for handling files for the auxillary programs
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+
+
+/* This must contain a valid path without a trailing slash */
+static char *basepath = NULL;
+
+/* This sets the base path for all file operations */
+void 
+set_basepath (const char *bp)
+{
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+  basepath = "";
+#else
+  char *s;
+  /* Free the memory for the base path if it has already been allocated */
+  if (basepath)
+    {
+      delete []basepath;
+      basepath = NULL;
+    }
+
+  if (!bp || *bp == '\0')
+    bp = ".";
+
+  s = strrchr (bp, '/');
+  if (s && *(s + 1) == '\0')
+    {
+      basepath = new char[strlen (bp) + 2];
+      if (!basepath)
+	return;
+      strcpy (basepath, bp);
+      strcat (basepath, ".");
+    }
+  else
+    basepath = Xstrdup (bp);
+#endif
+}
+
+
+
+/* return the currently defined basepath */
+char *
+get_basepath (void)
+{
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  return basepath;
+}
+
+
+
+/* This generates the name of a file. It places the name in the buffer
+   specified or if that is NULL it uses a static buffer. */
+char *
+make_name (const char *name, const char *suffix, char *buffer)
+{
+  static char path[512];
+  if (!buffer)
+    buffer = path;
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (buffer, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return buffer;
+}
+
+
+/* [JFG - Mar 06: Accent folding patch] */
+/* This generates a suffixe for a file name. It places the name in the
+   buffer specified or if that is NULL, it uses a static buffer.
+   Please do not specify buffers under 512 or the data to be written. */
+char *
+make_suffix (const char *suffix_format, const char suffix_arg, char *buffer)
+{
+  static char suffix[512]; 
+  if (!buffer)
+    buffer = suffix;
+  sprintf (buffer, suffix_format, suffix_arg);
+  return buffer;
+}
+ 
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+open_named_file (const char *name, const char *mode,
+		 u_long magic_num, int err_mode)
+{
+  unsigned long magic;
+  FILE *f = NULL;
+  char *err;
+  f = fopen (name, mode);
+
+  if (!f)
+    {
+      err = "Unable to open \"%s\"";
+      goto error;
+    }
+
+  if (magic_num)
+    {
+      if (fread (&magic, sizeof (magic), 1, f) != 1)
+	{
+	  err = "No magic number \"%s\"";
+	  goto error;
+	}
+
+      NTOHUL(magic);  /* [RPAP - Jan 97: Endian Ordering] */
+
+      if (!IS_MAGIC (magic))
+	{
+	  err = "No MG magic number \"%s\"";
+	  goto error;
+	}
+
+      if (magic != magic_num)
+	{
+	  err = "Wrong MG magic number \"%s\"";
+	  goto error;
+	}
+    }
+  return f;
+
+error:
+  if (f)
+    fclose (f);
+  if (err_mode == MG_ABORT)
+    FatalError (1, err, name);
+  if (err_mode == MG_MESSAGE)
+    Message (err, name);
+  return NULL;
+}
+
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+open_file (const char *name, const char *suffix, const char *mode,
+	   u_long magic_num, int err_mode)
+{
+  char path[512];
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (path, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return open_named_file (path, mode, magic_num, err_mode);
+}
+
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+create_named_file (const char *name, const char *mode,
+		   u_long magic_num, int err_mode)
+{
+  FILE *f = NULL;
+  char *err;
+  f = fopen (name, mode);
+
+  if (!f)
+    {
+      err = "Unable to open \"%s\"";
+      goto error;
+    }
+
+  if (magic_num)
+    HTONUL(magic_num);  /* [RPAP - Jan 97: Endian Ordering] */
+    if (fwrite (&magic_num, sizeof (magic_num), 1, f) != 1)
+      {
+	err = "Couldn't write magic number \"%s\"";
+	goto error;
+      }
+
+  return f;
+
+error:
+  if (f)
+    fclose (f);
+  if (err_mode == MG_ABORT)
+    FatalError (1, err, name);
+  if (err_mode == MG_MESSAGE)
+    Message (err, name);
+  return NULL;
+}
+
+
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *
+create_file (const char *name, const char *suffix, const char *mode,
+	     u_long magic_num, int err_mode)
+{
+  char path[512];
+  if (!basepath)
+    set_basepath (getenv ("MGDATA"));
+  sprintf (path, FILE_NAME_FORMAT, basepath, name, suffix);  /* [RPAP - Feb 97: WIN32 Port] */
+  return create_named_file (path, mode, magic_num, err_mode);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mg_files.h	(revision 16561)
@@ -0,0 +1,329 @@
+/**************************************************************************
+ *
+ * mg_files.h -- Routines for handling files for the auxillary programs
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef MG_FILES_H
+#define MG_FILES_H
+
+#include "sysfuncs.h"
+
+/* Magic numbers for the different types of files */
+
+#define GEN_MAGIC(a,b,c,d) ((unsigned long)(((a)<<24) + ((b)<<16) + \
+					    ((c)<<8) + (d)))
+
+#define MAGIC_XXXX		GEN_MAGIC('M','G', 0 , 0)
+#define MAGIC_STATS_DICT	GEN_MAGIC('M','G','S','D')
+#define MAGIC_AUX_DICT		GEN_MAGIC('M','G','A','D')
+#define MAGIC_FAST_DICT		GEN_MAGIC('M','G','F','D')
+#define MAGIC_DICT		GEN_MAGIC('M','G','D', 0 )
+#define MAGIC_STEM_BUILD	GEN_MAGIC('M','G','S', 0 )
+#define MAGIC_HASH		GEN_MAGIC('M','G','H', 0 )
+#define MAGIC_STEM		GEN_MAGIC('M','G','s', 0 )
+#define MAGIC_CHUNK		GEN_MAGIC('M','G','C', 0 )
+#define MAGIC_CHUNK_TRANS	GEN_MAGIC('M','G','c', 0 )
+#define MAGIC_TEXT		GEN_MAGIC('M','G','T', 0 )
+#define MAGIC_TEXI		GEN_MAGIC('M','G','t', 0 )
+#define MAGIC_TEXT_LEVELS	GEN_MAGIC('M','G','t','L')
+#define MAGIC_TEXI_WGT		GEN_MAGIC('M','G','t','W')
+#define MAGIC_INVF		GEN_MAGIC('M','G','I', 0 )
+#define MAGIC_INVF_LEVELS	GEN_MAGIC('M','G','I','L')
+#define MAGIC_INVI		GEN_MAGIC('M','G','i', 0 )
+#define MAGIC_WGHT		GEN_MAGIC('M','G','W', 0 )
+#define MAGIC_WGHT_APPROX	GEN_MAGIC('M','G','w', 0 )
+#define MAGIC_PARAGRAPH		GEN_MAGIC('M','G','P', 0 )
+#define MAGIC_STEM_GEN(x)	GEN_MAGIC('M', 'G', 's', x)
+#define IS_MAGIC(a) ((((u_long)(a)) & 0xffff0000) == MAGIC_XXXX)
+
+
+/* err_mode values for open_file and create_file */
+#define MG_ABORT 0
+#define MG_MESSAGE 1
+#define MG_CONTINUE 2
+
+
+
+
+
+/* File suffixes */
+
+
+/* The compression dictionary built by txt.pass1 */
+#ifdef SHORT_SUFFIX
+# define TEXT_STATS_DICT_SUFFIX	 ".tsd"
+#else
+# define TEXT_STATS_DICT_SUFFIX	 ".text.stats"
+#endif
+
+/* The compression dictionary built by text.pass1 and comp_dict.process */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_SUFFIX	 ".td"
+#else
+# define TEXT_DICT_SUFFIX	 ".text.dict"
+#endif
+
+/* The compression dictionary built by mg_make_fast_dict */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_FAST_SUFFIX	 ".tdf"
+#else
+# define TEXT_DICT_FAST_SUFFIX	 ".text.dict.fast"
+#endif
+
+/* The auxilary dictionary built by text.pass2 */
+#ifdef SHORT_SUFFIX
+# define TEXT_DICT_AUX_SUFFIX	 ".tda"
+#else
+# define TEXT_DICT_AUX_SUFFIX	 ".text.dict.aux"
+#endif
+
+/* The compressed text build by text.pass2 */
+#ifdef SHORT_SUFFIX
+# define TEXT_SUFFIX		 ".t"
+#else
+# define TEXT_SUFFIX		 ".text"
+#endif
+
+/* The combined compressed text index and document weight file */
+#ifdef SHORT_SUFFIX
+# define TEXT_IDX_WGT_SUFFIX	 ".tiw"
+#else
+# define TEXT_IDX_WGT_SUFFIX	 ".text.idx.wgt"
+#endif
+
+/* The compressed text index file */
+#ifdef SHORT_SUFFIX
+# define TEXT_IDX_SUFFIX	 	".ti"
+#else
+# define TEXT_IDX_SUFFIX	 	".text.idx"
+#endif
+
+/* The document levels in the text */
+#ifdef SHORT_SUFFIX
+# define TEXT_LEVEL_SUFFIX	 	".tl"
+#else
+# define TEXT_LEVEL_SUFFIX	 	".text.level"
+#endif
+
+/* The dictionary of stemmed words build by invf.pass1 and ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_SUFFIX         ".id"
+#else
+# define INVF_DICT_SUFFIX         ".invf.dict"
+#endif
+
+/* The dictionary of stemmed words build by stem.process */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_SUFFIX ".idb"
+#else
+# define INVF_DICT_BLOCKED_SUFFIX ".invf.dict.blocked"
+#endif
+
+/* The exact document weights file build by make.weights, invf.pass2,
+   or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define WEIGHTS_SUFFIX           ".w"
+#else
+# define WEIGHTS_SUFFIX           ".weight"
+#endif
+
+/* The approximate weights file built by make.weights */
+#ifdef SHORT_SUFFIX
+# define APPROX_WEIGHTS_SUFFIX    ".wa"
+#else
+# define APPROX_WEIGHTS_SUFFIX    ".weight.approx"
+#endif
+
+/* The inverted file build by invf.pass2 or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define INVF_SUFFIX              ".i"
+#else
+# define INVF_SUFFIX              ".invf"
+#endif
+
+/* The level information for the inverted text */
+#ifdef SHORT_SUFFIX
+# define INVF_LEVEL_SUFFIX              ".il"
+#else
+# define INVF_LEVEL_SUFFIX              ".invf.level"
+#endif
+
+/* The inverted file index build by invf.pass2 or ivf.pass2 */
+#ifdef SHORT_SUFFIX
+# define INVF_IDX_SUFFIX          ".ii"
+#else
+# define INVF_IDX_SUFFIX          ".invf.idx"
+#endif
+
+/* The inverted file chunk descriptor built by ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_CHUNK_SUFFIX        ".ic"
+#else
+# define INVF_CHUNK_SUFFIX        ".invf.chunk"
+#endif
+
+/* The word index translation file built by ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_CHUNK_TRANS_SUFFIX  ".ict"
+#else
+# define INVF_CHUNK_TRANS_SUFFIX  ".invf.chunk.trans"
+#endif
+
+/* The hashed stemmed dictionary built by make.perf_hash */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_HASH_SUFFIX    ".idh"
+#else
+# define INVF_DICT_HASH_SUFFIX    ".invf.dict.hash"
+#endif
+
+/* The paragraph descriptior file built by invf.pass1 or ivf.pass1 */
+#ifdef SHORT_SUFFIX
+# define INVF_PARAGRAPH_SUFFIX    ".ip"
+#else
+# define INVF_PARAGRAPH_SUFFIX    ".invf.paragraph"
+#endif
+
+/* The trace file build by mg.builder. */
+#ifdef SHORT_SUFFIX
+# define TRACE_SUFFIX             ".trc"
+#else
+# define TRACE_SUFFIX             ".trace"
+#endif
+
+/* The compression stats file build by mg.builder. */
+#ifdef SHORT_SUFFIX
+# define COMPRESSION_STATS_SUFFIX ".cs"
+#else
+# define COMPRESSION_STATS_SUFFIX ".compression.stats"
+#endif
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* [JFG - Mar 06: Accent folding patch] 
+ * Use the pattern with make_suffix */
+/* The casefolded index into the stemmed dictionary */
+#ifdef SHORT_SUFFIX
+# define INVF_DICT_BLOCKED_SUFFIX_PAT ".ib%d"
+#else
+# define INVF_DICT_BLOCKED_SUFFIX_PAT ".invf.dict.blocked.%d"
+#endif
+
+
+/* [RPAP - Feb 97: WIN32 Port] */
+#ifdef __WIN32__
+#define FILE_NAME_FORMAT "%s%s%s"
+#else
+#define FILE_NAME_FORMAT "%s/%s%s"
+#endif
+
+
+/* This sets the base path for all file operations */
+void set_basepath (const char *bp);
+
+
+/* return the currently defined basepath */
+char *get_basepath (void);
+
+
+
+
+/* This generates the name of a file. It places the name in the buffer
+   specified or if that is NULL it uses a static buffer. */
+char *make_name (const char *name, const char *suffix, char *buffer);
+
+/* [JFG - Mar 06: Accent folding patch] */
+/* This generates the suffix of a file. It places the name in the buffer
+   specified or if that is NULL it uses a static buffer. */
+char *make_suffix (const char *suffix_format, const char suffix_arg, char *buffer);
+
+
+
+
+
+/* This will open the specified file and check its magic number.
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *open_named_file (const char *name, const char *mode,
+		       u_long magic_num, int err_mode);
+
+
+
+
+/* This will open the specified file and check its magic number.
+
+   err_mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *open_file (const char *name, const char *suffix, const char *mode,
+		 u_long magic_num, int err_mode);
+
+
+
+
+
+/* This will create the specified file and set its magic number.
+
+   Mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *create_named_file (const char *name, const char *mode,
+			 u_long magic_num, int err_mode);
+
+
+
+/* This will create the specified file and set its magic number.
+
+   err_mode may take on the following values
+   MG_ABORT    : causes an error message to be generated and the
+   program aborted if there is an error.
+   MG_MESSAGE  : causes a message to be generated and a NULL value to
+   be returned if there is an error.
+   MG_CONTINUE : causes a NULL value to be returned if there is an error.
+
+   On success if returns the FILE *. On failure it will return a NULL value
+   and possibly generate an error message, or it will exit the program with
+   an error message.   */
+FILE *create_file (const char *name, const char *suffix, const char *mode,
+		   u_long magic_num, int err_mode);
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.1	(revision 16561)
@@ -0,0 +1,188 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_compression_dict 1 \*(Dt CITRI
+.SH NAME
+mgpp_compression_dict \- build a compression dictionary.
+.SH SYNOPSIS
+.B mgpp_compression_dict
+[
+.B \-h
+]
+[
+.BR \-C " |"
+.BR \-P " |"
+.B  \-S
+]
+.if n .ti +9n
+[
+.BR \-0 " |"
+.BR \-1 " |"
+.BR \-2 " |"
+.B  \-3
+]
+[
+.BR \-H " |"
+.BR \-B " |"
+.BR \-D " |"
+.BR \-Y " |"
+]
+.if n .ti +9n
+.if t .ti +.5i
+[
+.BI \-l " lookback"
+]
+[
+.BI \-k " mem"
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_compression_dict
+builds a compression dictionary based on the statistics gathered
+during the first pass over the text.  The options to the program are
+mainly concerned with limiting the amount of memory the dictionary
+will use and with how the text compressor will cope with any novel
+words found during the compression phase.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.B \-C
+Build a complete dictionary from the statistics file.  If during the
+text compression phase a novel word is found, then the compressor will
+produce an error message and stop.
+.TP
+.B \-P
+Build a partial dictionary from the statistics file.  This dictionary
+assumes that the statistics file are based on the entire text.  The
+statistics of words not includes in the dictionary are used to
+calculate the escape probability.  If novel words are being coded
+character by character, then there may not be a Huffman code for every
+possible character.  This means that the compressor may fail if a novel
+word contains a novel character.
+.TP
+.B \-S
+Build a seed dictionary from the statistics file.  This dictionary
+assumes that the statistics file is based on only a portion of the
+text to be compressed.  The probability of a novel word is based on the
+number of words that have only occurred once.  If novel words are being
+coded character by character, then the Huffman codes for characters are
+based on the frequency of characters in the dictionary.
+.TP
+.B \-0
+All words from the statistics file are included in the built
+dictionary.
+.TP
+.B \-1
+Words are included in the dictionary until the dictionary reaches the
+desired size.  Words are selected for the dictionary based on the order
+they occurred in the source text.
+.TP
+.B \-2
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.
+.TP
+.B \-3
+Words are included in the dictionary until the dictionary reaches the
+desired size.  The most frequent words are included in the dictionary
+first; where there is a tie for frequency, the shortest word is
+included first.  Words are the shuffled back and forth between the
+`keep' and `discard' lists to find the `optimal' set of words that
+should be in the dictionary.
+.TP
+.B \-H
+This specifies that novel words will be coded character by character
+using Huffman codes.
+.TP
+.B \-B
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using binary codes.  The binary code represents their occurrence
+position in the auxiliary dictionary.
+.TP
+.B \-D
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using delta codes.  The delta code represents their occurrence position
+in the auxiliary dictionary.
+.TP
+.B \-Y
+This specifies that an auxiliary dictionary will be built by the
+compressor.  Each novel word found will be placed at the end of the
+auxiliary dictionary.  Novel words will be coded in the compressed text
+using a combination of gamma and binary codes.  The code represents
+their occurrence position in the auxiliary dictionary.  This generally
+produces better compression than
+.B \-B
+or
+.BR \-D .
+.TP
+.BI \-l " lookback"
+The generated dictionary is designed to be front coded when it is
+loaded into memory.  Under normal circumstances, a front-coded
+dictionary would require scanning from the beginning in order to find
+any particular word.  However, every
+.I lookback
+words in the dictionary, the whole word is stored and a pointer to that
+word maintained.  E.g., if
+.I lookback
+is 4, then every fourth word is stored in its entirety.
+.TP
+.BI \-k " mem"
+This limits the amount of memory to use for the generated
+dictionary.  Words are selected for the dictionary based of the text
+statistics, and whether
+.BR \-0 , " \-1" , " \-2"
+or
+.B \-3
+is specified.  The memory is calculated assuming a lookback of 0,
+irrespective of what actual lookback is specified.  This means that if
+a non-zero lookback is given, the dictionary will actually occupy
+less space than specified by
+.BR \-k .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.stats
+Statistics about the source text.
+.TP
+.B *.text.dict
+Compression dictionary for the source text.
+.SH "SEE ALSO"
+.na
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_stem_idx (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_compression_dict.cpp	(revision 16561)
@@ -0,0 +1,1021 @@
+/**************************************************************************
+ *
+ * mgpp_compression_dict.cpp -- Routines for creating compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_gen.h"
+#include "bitio_m_stdio.h"
+#include "mgheap.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "invf.h"
+#include "text.h"
+#include "words.h"
+#include "mg.h"
+#include "WordData.h"
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+#define is_power_of_two(a) ((a) != 0 && (((a) & ((a)-1)) == 0))
+
+#define MAX_RECALCULATIONS 100
+
+typedef struct DictWordData : public WordData
+  {
+    float saving;
+    float char_bit_cost;
+    u_long num_trans;
+    u_char *word;
+  }
+DictWordData;
+
+static DictWordData *Words[2];
+static u_long Num[2];
+static u_long chars[2];
+
+#define KIND(p) (((p) >= Words[0] && (p) < Words[0] + Num[0]) ? 0 : 1)
+#define IsWord(p) (((p) >= Words[1] && (p) < Words[1] + Num[1]) ? 1 : 0)
+#define IsNonWord(p) (((p) >= Words[0] && (p) < Words[0] + Num[0]) ? 1 : 0)
+
+
+typedef struct DictData
+  {
+    DictWordData **wd;
+    u_long num_wds;
+    u_long chars;
+  }
+DictData;
+
+typedef DictData DictInfo[2];
+
+static DictInfo keep, discard, all;
+static compression_stats_header csh;
+
+static char *file_name = "";
+static u_long novel_method = MG_NOVEL_HUFFMAN_CHARS;
+
+
+static void ReadInWords (char *);
+static void Select_on (int k, heap_comp hc);
+static void Method3 (int k);
+static void Select_all (void);
+static u_long WriteOutWords (char *, u_long, int);
+static int DecFreqIncWL (void *a, void *b);
+static int OccuranceOrder (void *a, void *b);
+
+
+
+int main (int argc, char **argv)
+{
+  int ch;
+  char type = 'C';
+  char mode = '0';
+  int lookback = 2;
+  double k = 0;
+  u_long mem_reqd;
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "0123CPSf:d:l:hk:HDY")) != -1)
+    switch (ch)
+      {
+      case 'H':
+	novel_method = MG_NOVEL_HUFFMAN_CHARS;
+	break;
+      case 'D':
+	novel_method = MG_NOVEL_DELTA;
+	break;
+      case 'Y':
+	novel_method = MG_NOVEL_HYBRID;
+	break;
+      case 'f':		/* input file */
+	file_name = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'C':
+      case 'P':
+      case 'S':
+	type = ch;
+	break;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+	mode = ch;
+	break;
+      case 'l':
+	lookback = atoi (optarg);
+	if (!is_power_of_two (lookback))
+	  FatalError (1, "The lookback value must be a power of 2");
+	lookback = floorlog_2 (lookback);
+	break;
+      case 'k':
+	k = atof (optarg) * 1024;
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-l lookback] [-f input_file]"
+	"[-d data directory] [-h] [-0|-1|-2|-3] [-C|-P|-S] [-k mem (Kb)]\n",
+		 argv[0]);
+	exit (1);
+      }
+
+  ReadInWords (file_name);
+
+  if (type == 'C')
+    {
+      Select_all ();
+      mem_reqd = WriteOutWords (file_name, MG_COMPLETE_DICTIONARY, lookback);
+    }
+  else
+    {
+      switch (mode)
+	{
+	case '0':
+	  Select_all ();
+	  break;
+	case '1':
+#ifndef SILENT
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+#endif
+	  Select_on ((int) k, OccuranceOrder);
+	  break;
+	case '2':
+#ifndef SILENT
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+#endif
+	  Select_on ((int) k, DecFreqIncWL);
+	  break;
+	case '3':
+#ifndef SILENT
+	  Message ("Dictionary limit of %.2f Kb", k / 1024);
+#endif
+	  Method3 ((int) k);
+	  break;
+	}
+      if (type == 'P')
+	{
+	  mem_reqd = WriteOutWords (file_name, MG_PARTIAL_DICTIONARY, lookback);
+	}
+      else
+	{
+	  mem_reqd = WriteOutWords (file_name, MG_SEED_DICTIONARY, lookback);
+	}
+    }
+
+#ifndef SILENT
+  Message ("Num words           : %8u -> %8u\n", Num[1], keep[1].num_wds);
+  Message ("Num non-words       : %8u -> %8u\n", Num[0], keep[0].num_wds);
+  Message ("Chars of words      : %8u -> %8u\n", chars[1], keep[1].chars);
+  Message ("Chars of non-words  : %8u -> %8u\n", chars[0], keep[0].chars);
+  Message ("Mem usage           : %8u -> %8u\n",
+	   (Num[0] + Num[1]) * sizeof (char *) + chars[0] + chars[1],
+	     (keep[0].num_wds + keep[1].num_wds) * sizeof (char *) +
+	   keep[0].chars + keep[1].chars);
+  Message ("Actual mem required : %8u\n", mem_reqd);
+#endif
+  return (0);
+}
+
+
+
+
+static void 
+ReadInWords (char *filename)
+{
+  FILE *f;
+  unsigned long i;
+  f = open_file (filename, TEXT_STATS_DICT_SUFFIX, "rb",
+		 MAGIC_STATS_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread (&csh, sizeof (csh), 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes);
+
+  for (i = 0; i < 2; ++i)
+    {
+      frags_stats_header fsh;
+      char *buf;
+      DictWordData *wd;
+      chars[i] = 0;
+
+      fread (&fsh, sizeof (fsh), 1, f);
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(fsh.num_frags);
+      NTOHUL(fsh.mem_for_frags);
+
+      Num[i] = all[i].num_wds = fsh.num_frags;
+
+      /* The +1 on the following line is to leave room for the esc code. */
+      all[i].wd = (DictWordData **) Xmalloc (sizeof (DictWordData *) * Num[i] + 1);
+
+      buf = (char *) Xmalloc (fsh.mem_for_frags);
+      wd = Words[i] = (DictWordData *) Xmalloc (sizeof (DictWordData) * Num[i]);
+      unsigned int j;
+      for (j = 0; j < Num[i]; ++j, ++wd)
+	{
+	  int len;
+
+	  // read docCount and wordCount
+	  wd->read(f);
+
+	  // read an mgString
+	  len = fgetc (f);
+	  wd->word = (u_char *) buf;
+	  *buf++ = len;
+	  fread (buf, len, 1, f);
+	  buf += len;
+	  all[i].wd[j] = wd;
+	}
+      chars[i] = fsh.mem_for_frags - fsh.num_frags;
+    }
+  fclose (f);
+}
+
+
+static void 
+Alloc_keep_discard (void)
+{
+  keep[0].num_wds = 0;
+  keep[0].wd = (DictWordData **) Xmalloc ((Num[0] + 1) * sizeof (DictWordData *));
+  keep[1].num_wds = 0;
+  keep[1].wd = (DictWordData **) Xmalloc ((Num[1] + 1) * sizeof (DictWordData *));
+  discard[0].num_wds = 0;
+  discard[0].wd = (DictWordData **) Xmalloc ((Num[0] + 1) * sizeof (DictWordData *));
+  discard[1].num_wds = 0;
+  discard[1].wd = (DictWordData **) Xmalloc ((Num[1] + 1) * sizeof (DictWordData *));
+}
+
+
+static int 
+sort_comp (const void *a, const void *b)
+{
+  DictWordData *aa = *(DictWordData **) a;
+  DictWordData *bb = *(DictWordData **) b;
+  return casecompare (aa->word, bb->word);  /* [RPAP - Jan 97: Stem Index Change] */
+}
+
+
+
+static void 
+SortAndCount_DictData (DictData * dd)
+{
+  unsigned int i;
+  DictWordData **wd;
+  qsort (dd->wd, dd->num_wds, sizeof (DictWordData *), sort_comp);
+  dd->chars = 0;
+  wd = dd->wd;
+  for (i = 0; i < dd->num_wds; ++i, ++wd)
+    dd->chars += (*wd)->word[0];
+}
+
+
+static void 
+Select_all (void)
+{
+  unsigned int i;
+  Alloc_keep_discard ();
+  keep[0].num_wds = Num[0];
+  for (i = 0; i < Num[0]; ++i)
+    keep[0].wd[i] = Words[0] + i;
+  keep[1].num_wds = Num[1];
+  for (i = 0; i < Num[1]; ++i)
+    keep[1].wd[i] = Words[1] + i;
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+}
+
+
+
+
+static int 
+DecFreqIncWL (void *a, void *b)
+{
+  DictWordData *aa = *(DictWordData **) a;
+  DictWordData *bb = *(DictWordData **) b;
+  if (aa->docCount > bb->docCount)
+    return -1;
+  else if (aa->docCount < bb->docCount)
+    return 1;
+  else
+    return bb->word[0] - aa->word[0];
+}
+
+
+static int 
+OccuranceOrder (void *a, void *b)
+{
+  DictWordData *aa = *(DictWordData **) a;
+  DictWordData *bb = *(DictWordData **) b;
+  if (aa->words() > bb->words())
+    return 1;
+  else if (aa->words() < bb->words())
+    return -1;
+  else
+    return 0;
+}
+
+
+static void 
+Select_on (int k, heap_comp hc)
+{
+  int i, num, mem;
+  DictWordData **wd;
+
+  Alloc_keep_discard ();
+
+  num = Num[0] + Num[1];
+  wd = (DictWordData **) Xmalloc (num * sizeof (DictWordData *));
+  for (i = 0; (unsigned int)i < Num[0]; ++i)
+    wd[i] = Words[0] + i;
+  for (i = 0; (unsigned int)i < Num[1]; ++i)
+    wd[i + Num[0]] = Words[1] + i;
+
+  heap_build (wd, sizeof (*wd), num, hc);
+
+  mem = 0;
+  while (k > mem && num)
+    {
+      int idx;
+      DictWordData *word = wd[0];
+#ifdef DEBUG
+      fprintf (stderr, "%4d:%4d:%8d :%8d :%8d : \"%s\"\n",
+	       keep[0].num_wds, keep[1].num_wds,
+	       mem, word->documents(), word->word, word2str (word->word));
+#endif
+      mem += sizeof (u_char *) + word->word[0];
+      heap_deletehead (wd, sizeof (*wd), &num, hc);
+      idx = KIND (word);
+      keep[idx].wd[keep[idx].num_wds++] = word;
+    }
+
+  for (i = 0; i < num; ++i)
+    {
+      DictWordData *word = wd[i];
+      int idx = KIND (word);
+      discard[idx].wd[discard[idx].num_wds++] = word;
+    }
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+  SortAndCount_DictData (&discard[0]);
+  SortAndCount_DictData (&discard[1]);
+
+  assert (keep[0].num_wds + discard[0].num_wds == Num[0]);
+  assert (keep[1].num_wds + discard[1].num_wds == Num[1]);
+
+}
+
+
+
+static int 
+BigSaving (void *a, void *b)
+{
+  DictWordData *aa = *(DictWordData **) a;
+  DictWordData *bb = *(DictWordData **) b;
+  return (aa->saving > bb->saving) ? -1 : (aa->saving < bb->saving);
+}
+
+static int 
+SmallSaving (void *a, void *b)
+{
+  DictWordData *aa = *(DictWordData **) a;
+  DictWordData *bb = *(DictWordData **) b;
+  return (aa->saving < bb->saving) ? -1 : (aa->saving > bb->saving);
+}
+
+
+static void 
+CalcCharCounts (DictWordData ** wd, int num,
+		char *char_lens[2], char *len_lens[2],
+		u_long escape[2])
+{
+  long char_freqs[2][256];
+  long len_freqs[2][16];
+  huff_data hd;
+  int i;
+  escape[0] = 0;
+  escape[1] = 0;
+  memset (char_freqs, '\0', sizeof (char_freqs));
+  memset (len_freqs, '\0', sizeof (len_freqs));
+  for (i = 0; i < num; ++i, ++wd)
+    {
+      u_long freq = (*wd)->documents();
+      u_char *buf = (*wd)->word;
+      int len = *buf++;
+      int idx = KIND (*wd);
+      len_freqs[idx][len] += freq;
+      escape[idx] += freq;
+      for (; len; --len, ++buf)
+	char_freqs[idx][(u_long) (*buf)] += freq;
+    }
+  Generate_Huffman_Data (256, char_freqs[0], &hd, NULL);
+  char_lens[0] = hd.clens;
+  Generate_Huffman_Data (256, char_freqs[1], &hd, NULL);
+  char_lens[1] = hd.clens;
+
+  Generate_Huffman_Data (16, len_freqs[0], &hd, NULL);
+  len_lens[0] = hd.clens;
+  Generate_Huffman_Data (16, len_freqs[1], &hd, NULL);
+  len_lens[1] = hd.clens;
+}
+
+
+
+
+
+inline void CalcBitCostForWordData(DictWordData **word, int num,
+				   double freqs_trans[2], u_long escape[2],
+				   char * char_lens[2], char *len_lens[2], 
+				   double esc[2], int num_trans)
+{ 
+  int j;
+
+  for (j = 0; j < num; ++j, ++word)
+    {
+      float   cbc, wbc;
+      u_char *buf = (*word)->word;
+      int     len = *buf++;
+      u_long  freq = (*word)->documents();
+      int     idx = KIND (*word);
+
+      cbc = len_lens[idx][len];
+      for (; len; --len, ++buf)
+	cbc += char_lens[idx][(u_long) (*buf)];
+
+      (*word)->char_bit_cost = (cbc + esc[idx]) * freq;
+
+      wbc = -log2 (freq / (freqs_trans[idx] + escape[idx])) * freq;
+
+      (*word)->saving = ((*word)->char_bit_cost - wbc) /
+	(sizeof (char *) + (*word)->word[0]);
+
+      (*word)->num_trans = num_trans;
+    }
+}
+
+
+
+void 
+CalcBitCost (DictWordData ** discard_word, int discard_num,
+	     DictWordData ** keep_word, int keep_num, double freqs_trans[2],
+	     u_long escape[2], int num_trans)
+{
+  char *char_lens[2];
+  char *len_lens[2];
+  double esc[2];
+  CalcCharCounts (discard_word, discard_num, char_lens, len_lens, escape);
+  esc[0] = -log2 (escape[0] / (freqs_trans[0] + escape[0]));
+  esc[1] = -log2 (escape[1] / (freqs_trans[1] + escape[1]));
+  CalcBitCostForWordData(discard_word, discard_num, freqs_trans, escape,
+			 char_lens, len_lens, esc, num_trans);
+  CalcBitCostForWordData(keep_word, keep_num, freqs_trans, escape,
+			 char_lens, len_lens, esc, num_trans);
+  Xfree (char_lens[0]);
+  Xfree (char_lens[1]);
+  Xfree (len_lens[0]);
+  Xfree (len_lens[1]);
+}
+
+inline void m3_transferWord(DictWordData **toHeap, int *toNum, heap_comp toSaving,
+			    DictWordData **fromHeap, int *fromNum, heap_comp fromSaving,
+			    int *num_trans, double freqs_trans[], int *mem, double *total,
+			    int *count)
+{
+  /* Transfer the word at the top of the keep heap to the
+     discard heap. */
+  DictWordData *word = fromHeap[0];
+  int idx = KIND (word);
+  heap_deletehead (fromHeap, sizeof (word), fromNum, fromSaving);
+  toHeap[*toNum] = word;
+  heap_additem (toHeap, sizeof (word), toNum, toSaving);
+  freqs_trans[idx] -= word->documents();
+  *mem = (*mem) - sizeof (u_char *) + word->word[0];
+  *num_trans += 1;
+  *total += word->saving;
+  *count += 1;
+#ifdef DEBUG
+  fprintf (stderr,
+	   "KEEP -> DISCARD %8d :%8d :%8.0f :%8.0f : \"%s\"\n",
+	   *mem, word->documents(), word->char_bit_cost,
+	   word->saving, word2str (word->word));
+#endif
+}
+
+inline void m3_storeWord(DictWordData **wordHeap, int wordNum, int num_trans, 
+			 double freqs_trans[], u_long escape[], heap_comp hc)
+{
+  DictWordData *word = wordHeap[0];
+  int idx = KIND (word);
+  float wbc;
+#ifdef DEBUG1
+  fprintf (stderr, "KEEP \"%s\" %.2f ->", word2str (word->word),
+	   word->saving);
+#endif
+  wbc = -log2 (word->documents() / (freqs_trans[idx] + escape[idx])) *
+    word->documents();
+  word->saving = (word->char_bit_cost - wbc) /
+    (sizeof (char *) + word->word[0]);
+#ifdef DEBUG1
+  fprintf (stderr, " %.2f\n", word->saving);
+#endif
+  word->num_trans = num_trans;
+  heap_changedhead (wordHeap, sizeof (word), wordNum, hc);
+}
+
+
+static void 
+Method3 (int k)
+{
+  int i, keep_num, discard_num, mem, num_trans, recalc_reqd;
+  int keep_to_discard = 0;
+  int discard_to_keep = 0;
+  int recalcs = 0;
+  double freqs_trans[2], total;
+  u_long escape[2];
+  DictWordData **keep_heap, **discard_heap;
+
+  freqs_trans[0] = freqs_trans[1] = 0;
+  num_trans = 0;
+
+  discard_num = Num[0] + Num[1];
+  discard_heap = (DictWordData **) Xmalloc (discard_num * sizeof (DictWordData *));
+
+  keep_num = 0;
+  keep_heap = (DictWordData **) Xmalloc (discard_num * sizeof (DictWordData *));
+
+
+  for (i = 0; (unsigned int)i < Num[0]; ++i)
+    discard_heap[i] = Words[0] + i;
+  for (i = 0; (unsigned int)i < Num[1]; ++i)
+    discard_heap[i + Num[0]] = Words[1] + i;
+
+  heap_build (discard_heap, sizeof (*discard_heap), discard_num, DecFreqIncWL);
+
+  mem = 0;
+  while (k > mem && discard_num)
+    {
+      DictWordData *word = discard_heap[0];
+      mem += sizeof (u_char *) + word->word[0];
+      heap_deletehead (discard_heap, sizeof (word), &discard_num, DecFreqIncWL);
+      keep_heap[keep_num++] = word;
+      freqs_trans[KIND (word)] += word->documents();
+      ++num_trans;
+    }
+
+  CalcBitCost (discard_heap, discard_num, keep_heap, keep_num,
+	       freqs_trans, escape, num_trans);
+  heap_build (discard_heap, sizeof (*discard_heap), discard_num, BigSaving);
+  heap_build (keep_heap, sizeof (*keep_heap), keep_num, SmallSaving);
+
+  total = 0;
+  recalc_reqd = 0;
+  while (keep_num && discard_num)
+    {
+      if ((keep_num && keep_heap[0]->num_trans < (unsigned)num_trans) ||
+	  (discard_num && discard_heap[0]->num_trans < (unsigned)num_trans))
+	{
+	  if (keep_num && keep_heap[0]->num_trans < (unsigned)num_trans)
+	    {
+	      m3_storeWord(keep_heap, keep_num, num_trans, freqs_trans, escape, SmallSaving);
+	    }
+
+	  if (discard_num && discard_heap[0]->num_trans < (unsigned)num_trans)
+	    {
+	      m3_storeWord(discard_heap, discard_num, num_trans, freqs_trans, escape, BigSaving);
+	    }
+	}
+      else if (keep_heap[0]->saving < discard_heap[0]->saving)
+	{
+	  assert (keep_num && discard_num);
+	  if (keep_num && mem + sizeof (char *) + discard_heap[0]->word[0] > (unsigned)k)
+	    {
+	      /* Transfer the word at the top of the keep heap to the
+	         discard heap. */
+	      m3_transferWord(discard_heap, &discard_num, BigSaving,
+			      keep_heap, &keep_num, SmallSaving,
+			      &num_trans, freqs_trans, &mem, &total, &keep_to_discard);
+	    }
+	  else
+	    {
+	      /* Transfer the word at the top of the discard heap to the
+	         keep heap. */
+	      m3_transferWord(keep_heap, &keep_num, SmallSaving,
+			      discard_heap, &discard_num, BigSaving,
+			      &num_trans, freqs_trans, &mem, &total, &discard_to_keep);
+	    }
+
+	  recalc_reqd = 1;
+
+	}
+      else
+	{
+	  if (recalc_reqd == 0)
+	    break;
+#ifdef DEBUG1
+	  fprintf (stderr, "--------------\n");
+#endif
+	  if (recalcs == MAX_RECALCULATIONS)
+	    break;
+	  CalcBitCost (discard_heap, discard_num, keep_heap, keep_num,
+		       freqs_trans, escape, num_trans);
+	  heap_build (discard_heap, sizeof (*discard_heap),
+		      discard_num, BigSaving);
+	  heap_build (keep_heap, sizeof (keep_heap), keep_num, SmallSaving);
+	  recalc_reqd = 0;
+	  ++recalcs;
+	}
+    }
+
+  Alloc_keep_discard ();
+
+  for (i = 0; i < discard_num; ++i)
+    {
+      DictWordData *word = discard_heap[i];
+      int idx = KIND (word);
+      assert (IsWord (word) || IsNonWord (word));
+      discard[idx].wd[discard[idx].num_wds++] = word;
+    }
+  for (i = 0; i < keep_num; ++i)
+    {
+      DictWordData *word = keep_heap[i];
+      int idx = KIND (word);
+      assert (IsWord (word) || IsNonWord (word));
+      keep[idx].wd[keep[idx].num_wds++] = word;
+    }
+  SortAndCount_DictData (&keep[0]);
+  SortAndCount_DictData (&keep[1]);
+  SortAndCount_DictData (&discard[0]);
+  SortAndCount_DictData (&discard[1]);
+
+  assert (keep[0].num_wds + discard[0].num_wds == Num[0]);
+  assert (keep[1].num_wds + discard[1].num_wds == Num[1]);
+#ifndef SILENT
+  Message ("Keep -> Discard        : %8d", keep_to_discard);
+  Message ("Discard -> Keep        : %8d", discard_to_keep);
+  Message ("Huffman Recalculations : %8d", recalcs);
+  if (recalcs == MAX_RECALCULATIONS)
+    Message ("WARNING: The number of recalculations == %d", MAX_RECALCULATIONS);
+#endif
+}
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************
+ *****									*****
+ *****			Dictionary saving code				*****
+ *****									*****
+ ****************************************************************************/
+
+
+
+static void 
+Write_cdh (FILE * f, compression_dict_header * cdh)
+{
+  /* [RPAP - Jan 97: Endian Ordering] */
+  int i;
+  compression_dict_header tmp = *cdh;
+  HTONUL(tmp.dict_type);
+  HTONUL(tmp.novel_method);
+  for (i = 0; i < TEXT_PARAMS; ++i)
+    HTONUL(tmp.params[i]);
+  HTONUL(tmp.num_words[0]);
+  HTONUL(tmp.num_words[1]);
+  HTONUL(tmp.num_word_chars[0]);
+  HTONUL(tmp.num_word_chars[1]);
+  HTONUL(tmp.lookback);
+
+  fwrite (&tmp, sizeof (tmp), 1, f);
+}
+
+
+static void 
+Write_words (FILE * f, DictData * dd)
+{
+  unsigned int i;
+  u_char *curr, *prev = NULL;
+  for (i = 0; i < dd->num_wds; ++i)
+    {
+      int len;
+      curr = dd->wd[i]->word;
+      if (prev)
+	/* look for prefix match with prev string */
+	len = prefixlen (prev, curr);
+      else
+	len = 0;
+      fputc ((len << 4) + (curr[0] - len), f);
+      fwrite (curr + len + 1, sizeof (u_char), curr[0] - len, f);
+      prev = curr;
+    }
+
+}
+
+
+static int 
+Uncompressed_size (DictData * dd)
+{
+  unsigned int i, us;
+  for (us = i = 0; i < dd->num_wds; ++i)
+    us += dd->wd[i]->word[0];
+  return us;
+}
+
+
+static u_long 
+Write_data (FILE * f, DictData * dd, int lookback)
+{
+  u_long mem_reqd;
+  huff_data *hd;
+  int i; 
+  u_long us = dd->num_wds;
+  long *freqs;
+  u_long huff_words_size[MAX_HUFFCODE_LEN + 1];
+  u_long lencounts[MAX_HUFFCODE_LEN + 1];
+  u_char *lastword[MAX_HUFFCODE_LEN + 1];
+
+  if (!(freqs = new long [dd->num_wds]))
+    FatalError (1, "Unable to allocate memory for freqs");
+
+  for (i = 0; (unsigned)i < dd->num_wds; ++i)
+    {
+      freqs[i] = dd->wd[i]->documents();
+      us += dd->wd[i]->word[0];
+    }
+
+  if (!(hd = Generate_Huffman_Data (dd->num_wds, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  delete []freqs;
+  freqs = NULL;
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+  HTONUL(us);  /* [RPAP - Jan 97: Endian Ordering] */
+  fwrite (&us, sizeof (us), 1, f);
+  NTOHUL(us);  /* [RPAP - Jan 97: Endian Ordering] */
+
+
+/* Calculate the amount of memory that will be required to store the text for
+   each different huffman code len. Every 1<<lookback words for each different
+   codelen length will not be prefixed by previous strings. */
+
+
+  memset (&huff_words_size, '\0', sizeof (huff_words_size));
+  memset (&lencounts, '\0', sizeof (lencounts));
+
+  mem_reqd = 0;
+
+  for (i = 0; (unsigned)i < dd->num_wds; ++i)
+    {
+      int codelen = hd->clens[i];
+      u_char *word = dd->wd[i]->word;
+
+      if (!codelen)
+	FatalError (1, "The length of a code for a word was zero");
+
+      huff_words_size[codelen] += word[0] + 1;
+      mem_reqd += word[0] + (lookback != 0);
+#if 0
+      if ((lencounts[codelen] & ((1 << lookback) - 1)) == 0)
+	lastword[codelen] = word;
+      else
+	huff_words_size[codelen] -= prefixlen (lastword[codelen], word);
+#else
+      if ((lencounts[codelen] & ((1 << lookback) - 1)) != 0)
+	{
+	  int save = prefixlen (lastword[codelen], word);
+	  mem_reqd -= save;
+	  huff_words_size[codelen] -= save;
+	}
+      else
+	{
+	  mem_reqd += sizeof (u_char *);
+	}
+      lastword[codelen] = word;
+#endif
+      ++lencounts[codelen];
+    }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i)
+    HTONUL(huff_words_size[i]);
+
+  fwrite (huff_words_size + hd->mincodelen, sizeof (*huff_words_size),
+	  hd->maxcodelen - hd->mincodelen + 1, f);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  for (i = hd->mincodelen; i < hd->maxcodelen + 1; ++i)
+    NTOHUL(huff_words_size[i]);
+
+  Write_words (f, dd);
+
+  delete []hd->clens;
+  delete hd;
+
+  return mem_reqd;
+}
+
+
+
+static void 
+Write_charfreqs (FILE * f, DictData * dd, int words,
+		 int zero_freq_permitted)
+{
+  unsigned int j;
+  long freqs[256];
+  DictWordData **wd = dd->wd;
+  huff_data *hd;
+
+  memset (freqs, '\0', sizeof (freqs));
+
+  for (j = 0; j < dd->num_wds; ++j, ++wd)
+    {
+      u_char *buf = (*wd)->word;
+      int len = *buf++;
+      for (; len; --len, ++buf)
+	freqs[(u_long) (*buf)] += (*wd)->documents();
+    }
+
+  if (!zero_freq_permitted)
+    for (j = 0; j < 256; ++j)
+      if (!freqs[j] && PESINAWORD (j) == words)
+	freqs[j] = 1;
+
+  if (!(hd = Generate_Huffman_Data (256, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+  delete []hd->clens;
+  delete hd;
+}
+
+
+
+
+static void 
+Write_wordlens (FILE * f, DictData * dd, int zero_freq_permitted)
+{
+  unsigned int j;
+  long freqs[16];
+  DictWordData **wd = dd->wd;
+  huff_data *hd;
+
+  memset (freqs, '\0', sizeof (freqs));
+
+  for (j = 0; j < dd->num_wds; ++j, ++wd)
+    freqs[(*wd)->word[0]] += (*wd)->documents();
+
+  if (!zero_freq_permitted)
+    for (j = 0; j < 16; ++j)
+      if (!freqs[j])
+	freqs[j] = 1;
+
+  if (!(hd = Generate_Huffman_Data (16, freqs, NULL, NULL)))
+    FatalError (1, "Unable to allocate memory for huffman data");
+
+  if (Write_Huffman_Data (f, hd) == -1)
+    FatalError (1, "Unable to write huffman data");
+
+
+  delete []hd->clens;
+  delete hd;
+}
+
+
+
+static u_long 
+WriteOutWords (char *file_name, u_long type, int lookback)
+{
+  FILE *f;
+  int i;
+  u_long mem_reqd = 0;
+
+  compression_dict_header cdh;
+  f = create_file (file_name, TEXT_DICT_SUFFIX, "w+b",
+		   MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  memset (&cdh, '\0', sizeof(cdh));
+
+  cdh.dict_type = type;
+  cdh.novel_method = (type == MG_SEED_DICTIONARY) ? novel_method :
+    MG_NOVEL_HUFFMAN_CHARS;
+
+  cdh.num_words[1] = keep[1].num_wds;
+  cdh.num_words[0] = keep[0].num_wds;
+  cdh.num_word_chars[1] = Uncompressed_size (&keep[1]);
+  cdh.num_word_chars[0] = Uncompressed_size (&keep[0]);
+  cdh.lookback = lookback;
+
+  Write_cdh (f, &cdh);
+
+  for (i = 0; i < 2; ++i)
+    switch (type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  mem_reqd += Write_data (f, &keep[i], lookback);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (keep[i].num_wds)
+	    {
+	      int j;
+	      DictWordData esc;
+	      esc.docCount = 0;
+	      esc.word = (u_char *) "";
+	      keep[i].wd[keep[i].num_wds++] = &esc;
+	      for (j = 0; (unsigned)j < discard[i].num_wds; ++j)
+		esc.docCount += discard[i].wd[j]->documents();
+	      if (!esc.docCount)
+		++esc.docCount;
+	      mem_reqd += Write_data (f, &keep[i], lookback);
+	    }
+	  Write_charfreqs (f, &discard[i], i, 1);
+	  Write_wordlens (f, &discard[i], 1);
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (keep[i].num_wds)
+	    {
+	      int j;
+	      DictWordData esc;
+	      esc.docCount = 0;
+	      esc.word = (u_char *) "";
+	      keep[i].wd[keep[i].num_wds++] = &esc;
+	      for (j = 0; (unsigned)j < all[i].num_wds; ++j)
+		if (all[i].wd[j]->documents() == 1)
+		  ++esc.docCount;
+	      if (!esc.docCount)
+		++esc.docCount;
+	      mem_reqd += Write_data (f, &keep[i], lookback);
+	    }
+	  switch (novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      Write_charfreqs (f, &all[i], i, 0);
+	      Write_wordlens (f, &all[i], 0);
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    default:
+	      FatalError (1, "Bad novel method");
+	    }
+	}
+	break;
+      }
+  fclose (f);
+  return mem_reqd;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_decompress_text.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_decompress_text.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_decompress_text.cpp	(revision 16561)
@@ -0,0 +1,100 @@
+/**************************************************************************
+ *
+ * mgpp_decompress_text.cpp -- 
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "UCArray.h"
+#include "sysfuncs.h"
+#include "TextGet.h"
+#include "messages.h"
+#include "mg_files.h"
+
+int main (int argc, char **argv) {
+  int ch;
+  char *filename = "";
+  char *basePath = "";
+  UCArray level;
+  SetCStr (level, "Document", 8);
+
+  opterr = 0;
+  msg_prefix = argv[0];
+
+  // process the command line arguments
+  while ((ch = getopt (argc, argv, "f:d:K:h")) != -1) {
+    switch (ch) {
+    case 'f':		/* input file */
+      filename = optarg;
+      break;
+    case 'd':
+      basePath = optarg;
+      set_basepath (optarg);
+      break;
+    case 'K':
+      SetCStr (level, optarg, strlen(optarg));
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-h] [-K level] [-d directory] -f name\n",
+	       argv[0]);
+      exit (1);
+    }
+  }
+
+  // load up the text information
+  TextData td;
+  if (!td.LoadData (basePath, filename)) {
+    FatalError (1, "Couldn't load text information for \"%s\"", filename);
+  }
+  
+  // output each document in the level
+//    cout << td.levels << "\n";
+  
+  TextLevelInfo levelInfo = td.levels.levelInfo[level];
+  unsigned long docNum = 1;
+  UCArray docText;
+  while (docNum <= levelInfo.numEntries) {
+//      TextIdx docIdx;
+//      if (GetDocIdx (td, level, docNum, docIdx)) {
+//        cout << "doc: " << docNum << "\n";
+//        cout << docIdx;
+//      }
+    
+    if (!GetDocText (td, level, docNum, docText)) {
+      FatalError (1, "Error while trying to get document %u", docNum);
+    }
+
+    cout << docText << "\n";
+    
+    ++docNum;
+  }
+  
+  return 0;
+}
+
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.1	(revision 16561)
@@ -0,0 +1,69 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_fast_comp_dict 1 \*(Dt CITRI
+.SH NAME
+mgpp_fast_comp_dict \- build a fast loading compression dictionary
+.SH SYNOPSIS
+.B mgpp_fast_comp_dict
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_fast_comp_dict
+builds a fast-loading compression dictionary from the standard
+compression dictionary.  The new dictionary has the property that
+.BR mgppquery (1)
+can load it in very quickly, making the startup time for
+.BR mgppquery (1)
+much shorter.  The new dictionary is larger on disk than the original,
+but they are no different when it comes to memory usage.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.text.dict
+Compression dictionary.
+.TP
+.B *.text.dict.fast
+Fast loading compression dictionary.
+.TP
+.B *.text.dict.aux
+Auxiliary compression dictionary.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_stem_idx (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_fast_comp_dict.cpp	(revision 16561)
@@ -0,0 +1,707 @@
+/**************************************************************************
+ *
+ * mgpp_fast_comp_dict.cpp -- Program to generate a fast compression dictionary
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "huffman.h"
+#include "messages.h"
+#include "memlib.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "local_strings.h"
+#include "mg.h"
+#include "text.h"
+#include "invf.h"
+#include "mg_files.h"
+#include "locallib.h"
+#include "words.h"
+
+
+
+#define ALIGN_SIZE(p,t) (((p) + (sizeof(t)-1)) & (~(sizeof(t)-1)))
+
+#define WORDNO(p, base) ((((char*)(p))-((char*)(base)))/sizeof(u_char*))
+#define FIXUP(p) (fixup[WORDNO(p,buffer)/8] |= (1<<(WORDNO(p, buffer) & 7)))
+
+#define IS_FIXUP(p) ((fixup[WORDNO(p,buffer)/8] & (1<<(WORDNO(p, buffer) & 7))) != 0)
+
+#define FIXUP_VALS(vals) do {						\
+	int i;								\
+	for (i=0; i < MAX_HUFFCODE_LEN+1; ++i)				\
+	  FIXUP(&vals[i]);						\
+      } while(0)
+
+
+
+static u_long mem_for_comp_dict (char *filename);
+static void load_comp_dict (char *filename);
+static void save_fast_dict (char *filename);
+static void unfixup_buffer (void);
+
+
+static void *buffer;
+static void *cur;
+static u_char *fixup;
+static u_long mem, fixup_mem;
+
+int main (int argc, char **argv)
+{
+  int ch;
+  char *filename = "";
+  opterr = 0;
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "f:d:h")) != -1)
+    switch (ch)
+      {
+      case 'f':		/* input file */
+	filename = optarg;
+	break;
+      case 'd':
+	set_basepath (optarg);
+	break;
+      case 'h':
+      case '?':
+	fprintf (stderr, "usage: %s [-f input_file] [-d data directory] [-h]\n",
+		 argv[0]);
+	exit (1);
+      }
+
+  mem = mem_for_comp_dict (filename);
+
+  fixup_mem = (ALIGN_SIZE (mem, u_char *) / sizeof (u_char *) + 7) / 8;
+
+  cur = buffer = Xmalloc (mem);
+  memset (buffer, 0, mem);
+  fixup = (u_char *) Xmalloc (fixup_mem);
+  memset (fixup, 0, fixup_mem);
+
+#ifndef SILENT
+  Message ("Estimated memory for fast_dict %u", mem);
+  Message ("Estimated memory for fixups %u", fixup_mem);
+#endif
+
+  load_comp_dict (filename);
+
+#ifndef SILENT
+  Message ("Actual memory for fast_dict %u", (char *) cur - (char *) buffer);
+#endif
+
+  if ((u_long) cur > (u_long) buffer + mem)
+    FatalError (1, "The buffer was not big enough for the dictionary");
+
+  {
+    /* [RPAP - Jan 97: Endian Ordering] */
+    compression_dict *cd = (compression_dict *) buffer;
+    int i, which;
+
+    /* cfh */
+    for (which = 0; which <= 1; ++which)
+      {
+	int j;
+	
+	HTONSI(cd->cfh[which]->hd.num_codes);
+	HTONSI(cd->cfh[which]->hd.mincodelen);
+	HTONSI(cd->cfh[which]->hd.maxcodelen);
+	for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
+	  {
+	    HTONSI(cd->cfh[which]->hd.lencount[j]);
+	    HTONUL(cd->cfh[which]->hd.min_code[j]);
+	  }
+	HTONUL(cd->cfh[which]->uncompressed_size);
+	for (j = 0; j < MAX_HUFFCODE_LEN + 1; ++j)
+	  HTONUL(cd->cfh[which]->huff_words_size[j]);
+      }
+    HTONUL(cd->MemForCompDict);
+    /* ad */
+    if (cd->cdh.novel_method == MG_NOVEL_DELTA ||
+	cd->cdh.novel_method == MG_NOVEL_HYBRID)
+      for (which = 0; which <= 1; ++which)
+	{
+	  int j;
+	  
+	  HTONUL(cd->ad->afh[which].num_frags);
+	  HTONUL(cd->ad->afh[which].mem_for_frags);
+	  for (j = 0; j < 33; ++j)
+	    {
+	      HTONSI(cd->ad->blk_start[which][j]);
+	      HTONSI(cd->ad->blk_end[which][j]);
+	    }
+	}
+    /* cdh */
+    HTONUL(cd->cdh.dict_type);
+    HTONUL(cd->cdh.novel_method);
+    for (i = 0; i < TEXT_PARAMS; ++i)
+      HTONUL(cd->cdh.params[which]);
+    HTONUL(cd->cdh.num_words[0]);
+    HTONUL(cd->cdh.num_words[1]);
+    HTONUL(cd->cdh.num_word_chars[0]);
+    HTONUL(cd->cdh.num_word_chars[1]);
+    HTONUL(cd->cdh.lookback);
+    HTONSI(cd->fast_loaded);
+  }
+
+  unfixup_buffer ();
+
+  save_fast_dict (filename);
+
+  return (0);
+}
+
+
+
+static void 
+unfixup_buffer ()
+{
+  u_long *p;
+  for (p = (u_long *) buffer; (u_long) p < (u_long) cur; ++p)
+    {
+      if (IS_FIXUP (p))
+	*p = *p - (u_long) buffer;
+    }
+}
+
+
+
+
+static u_long 
+mem_for_aux_dict (compression_dict_header * /*cdh*/, char *filename)
+{
+  int i;
+  u_long mem = sizeof (auxiliary_dict);
+  FILE *aux;
+
+  aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
+		   MAGIC_AUX_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  for (i = 0; i <= 1; ++i)
+    {
+      aux_frags_header afh;
+      fread (&afh, sizeof (afh), 1, aux);
+      NTOHUL(afh.num_frags);  /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(afh.mem_for_frags);  /* [RPAP - Jan 97: Endian Ordering] */
+      mem += afh.num_frags * sizeof (u_char *);
+      mem = ALIGN_SIZE (mem + afh.mem_for_frags, u_char *);
+      fseek (aux, afh.mem_for_frags, SEEK_CUR);
+    }
+  fclose (aux);
+
+  return mem;
+}
+
+
+
+static u_long 
+mem_for_words (FILE * dict, compression_dict_header * cdh,
+	       comp_frags_header * cfh)
+{
+  u_long mem = 0;
+  long i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+
+  lookback = cdh->lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; ++i)
+    {
+      ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+      mem_reqd += cfh->huff_words_size[i];
+    }
+
+  mem += ptrs_reqd * sizeof (u_char *);
+  mem += (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **);
+  mem += mem_reqd;
+
+  for (i = 0; i < cfh->hd.num_codes; ++i)
+    {
+      register int val;
+      for (val = getc (dict) & 0xf; val; --val)
+	getc (dict);
+    }
+  return ALIGN_SIZE (mem, u_char *);
+}
+
+static u_long mem_skip_hd(FILE *dict, u_long mem)
+{ huff_data hd;
+
+  mem += sizeof (hd);
+  Read_Huffman_Data (dict, &hd, NULL, NULL);
+  if (hd.clens)
+    delete []hd.clens;
+  mem += hd.num_codes * sizeof (unsigned long);
+  mem += (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+  return mem;
+}
+
+static u_long mem_read_cfh(FILE *dict, compression_dict_header *cdh, comp_frags_header *cfh, 
+			   u_long mem)
+{
+  mem += sizeof (comp_frags_header);
+  Read_cfh (dict, cfh, NULL, NULL);
+
+  /* Don't need to count the space for the clens of the huffman data */
+
+  mem += mem_for_words (dict, cdh, cfh);
+  if (cfh->hd.clens)
+    delete []cfh->hd.clens;
+
+  return mem;
+}
+
+
+static u_long 
+mem_for_comp_dict (char *filename)
+{
+  u_long mem = sizeof (compression_dict);
+  compression_dict_header cdh;
+  comp_frags_header cfh;
+  FILE *dict;
+  int which;
+  int i;  /* [RPAP - Jan 97: Endian Ordering] */
+
+  dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
+		    MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  fread (&cdh, sizeof (cdh), 1, dict);
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(cdh.dict_type);
+  NTOHUL(cdh.novel_method);
+  for (i = 0; i < TEXT_PARAMS; ++i)
+    NTOHUL(cdh.params[i]);
+  NTOHUL(cdh.num_words[0]);
+  NTOHUL(cdh.num_words[1]);
+  NTOHUL(cdh.num_word_chars[0]);
+  NTOHUL(cdh.num_word_chars[1]);
+  NTOHUL(cdh.lookback);
+
+  for (which = 0; which < 2; ++which)
+    switch (cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  mem = mem_read_cfh(dict, &cdh, &cfh, mem);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  // huff_data hd;
+	  if (cdh.num_words[which])
+	    {
+	      mem = mem_read_cfh(dict, &cdh, &cfh, mem);
+	    }
+
+	  mem = mem_skip_hd(dict, mem);
+	  mem = mem_skip_hd(dict, mem);
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  // huff_data hd;
+	  if (cdh.num_words[which])
+	    {
+	      mem = mem_read_cfh(dict, &cdh, &cfh, mem);
+	    }
+	  switch (cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      mem = mem_skip_hd(dict, mem);
+	      mem = mem_skip_hd(dict, mem);
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    }
+	  break;
+	}
+      }
+  fclose (dict);
+
+  if (cdh.novel_method == MG_NOVEL_DELTA ||
+      cdh.novel_method == MG_NOVEL_HYBRID)
+    mem += mem_for_aux_dict (&cdh, filename);
+
+  return ALIGN_SIZE (mem, u_char *);
+}
+
+
+
+void *
+getmem (u_long size, int align)
+{
+  void *res;
+  cur = (void *) (((u_long) cur + (align - 1)) & (~(align - 1)));
+  res = cur;
+  cur = (char *) cur + size;
+  if ((u_long) cur > (u_long)buffer + mem)
+    FatalError (1, "The buffer was not big enough for the dictionary");
+  return res;
+}
+
+
+
+
+
+
+
+
+
+
+
+static auxiliary_dict *
+LoadAuxDict (compression_dict_header * cdh,
+	     char *filename)
+{
+  auxiliary_dict *ad;
+  int i;
+  FILE *aux;
+
+  aux = open_file (filename, TEXT_DICT_AUX_SUFFIX, "rb",
+		   MAGIC_AUX_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  ad = (auxiliary_dict *) getmem (sizeof (auxiliary_dict), sizeof (u_char *));
+
+  for (i = 0; i <= 1; ++i)
+    {
+      unsigned int j;
+      u_char *pos;
+
+      fread (&ad->afh[i], sizeof (aux_frags_header), 1, aux);
+
+      /* [RPAP - Jan 97: Endian Ordering] */
+      NTOHUL(ad->afh[i].num_frags);
+      NTOHUL(ad->afh[i].mem_for_frags);
+
+      ad->word_data[i] = (u_char *) getmem (ad->afh[i].mem_for_frags, 1);
+      FIXUP (&ad->word_data[i]);
+
+      ad->words[i] = (u_char **) getmem (ad->afh[i].num_frags * sizeof (u_char *),
+			     sizeof (u_char *));
+      FIXUP (&ad->words[i]);
+
+      fread (ad->word_data[i], ad->afh[i].mem_for_frags, sizeof (u_char), aux);
+
+      pos = ad->word_data[i];
+      for (j = 0; j < ad->afh[i].num_frags; ++j)
+	{
+	  ad->words[i][j] = pos;
+	  FIXUP (&ad->words[i][j]);
+	  pos += *pos + 1;
+	}
+      if (cdh->novel_method == MG_NOVEL_HYBRID)
+	{
+	  int num;
+	  num = 1;
+	  ad->blk_start[i][0] = 0;
+	  ad->blk_end[i][0] = cdh->num_words[i] - 1;
+	  while (num < 33)
+	    {
+	      ad->blk_start[i][num] = ad->blk_end[i][num - 1] + 1;
+	      ad->blk_end[i][num] = ad->blk_start[i][num] +
+		(ad->blk_end[i][num - 1] - ad->blk_start[i][num - 1]) * 2;
+	      ++num;
+	    }
+	}
+    }
+  fclose (aux);
+  return (ad);
+}
+
+
+
+
+
+static u_char ***
+ReadInWords (FILE * dict, compression_dict * cd,
+	     comp_frags_header * cfh, u_char ** escape)
+{
+  int i, lookback;
+  int ptrs_reqd = 0;
+  int mem_reqd = 0;
+  int num_set[MAX_HUFFCODE_LEN + 1];
+  u_char *next_word[MAX_HUFFCODE_LEN + 1];
+  u_char **vals;
+  u_char ***values;
+  u_char word[MAXWORDLEN + 1];
+  u_char last_word[MAX_HUFFCODE_LEN + 1][MAXWORDLEN + 1];
+
+  lookback = cd->cdh.lookback;
+
+  for (i = cfh->hd.mincodelen; i <= cfh->hd.maxcodelen; ++i)
+    {
+      ptrs_reqd += (cfh->hd.lencount[i] + ((1 << lookback) - 1)) >> lookback;
+      mem_reqd += cfh->huff_words_size[i];
+    }
+
+  vals = (u_char **) getmem (ptrs_reqd * sizeof (*vals), sizeof (u_char *));
+
+  values = (u_char ***) getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (u_char **), sizeof (u_char **));
+
+  next_word[0] = (u_char *) getmem (mem_reqd, sizeof (char));
+
+  cd->MemForCompDict += ptrs_reqd * sizeof (*vals) +
+    (MAX_HUFFCODE_LEN + 1) * sizeof (u_char **) +
+    mem_reqd;
+
+  values[0] = vals;
+  FIXUP (&values[0]);
+  values[0][0] = next_word[0];
+  FIXUP (&values[0][0]);
+  for (i = 1; i <= cfh->hd.maxcodelen; ++i)
+    {
+      int next_start = (values[i - 1] - vals) +
+      ((cfh->hd.lencount[i - 1] + ((1 << lookback) - 1)) >> lookback);
+      values[i] = &vals[next_start];
+      FIXUP (&values[i]);
+      next_word[i] = next_word[i - 1] + cfh->huff_words_size[i - 1];
+      values[i][0] = next_word[i];
+      FIXUP (&values[i][0]);
+    }
+
+  memset (num_set, '\0', sizeof (num_set));
+
+  for (i = 0; i < cfh->hd.num_codes; ++i)
+    {
+      register int val, copy;
+      register int len = cfh->hd.clens[i];
+      val = getc (dict);
+      copy = (val >> 4) & 0xf;
+      val &= 0xf;
+
+      fread (word + copy + 1, sizeof (u_char), val, dict);
+      *word = val + copy;
+
+      if ((num_set[len] & ((1 << lookback) - 1)) == 0)
+	{
+	  values[len][num_set[len] >> lookback] = next_word[len];
+	  FIXUP (&values[len][num_set[len] >> lookback]);
+	  memcpy (next_word[len], word, *word + 1);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    {
+	      *escape = next_word[len];
+	      FIXUP (escape);
+	    }
+	  next_word[len] += *word + 1;
+	}
+      else
+	{
+	  copy = prefixlen (last_word[len], word);
+	  memcpy (next_word[len] + 1, word + copy + 1, *word - copy);
+	  *next_word[len] = (copy << 4) + (*word - copy);
+	  if (escape && i == cfh->hd.num_codes - 1)
+	    {
+	      *escape = next_word[len];
+	      FIXUP (escape);
+	    }
+	  next_word[len] += (*word - copy) + 1;
+	}
+      memcpy (last_word[len], word, *word + 1);
+      ++num_set[len];
+    }
+  if (cfh->hd.clens)
+    delete []cfh->hd.clens;
+  cfh->hd.clens = NULL;
+  return values;
+}
+
+
+static unsigned long **
+Generate_Fast_Huffman_Vals (huff_data * data, u_long * mem)
+{
+  int i;
+  unsigned long *fcode[MAX_HUFFCODE_LEN + 1];
+  unsigned long **values;
+  unsigned long *vals;
+
+  if (!data)
+    return (NULL);
+  vals = (unsigned long *) getmem (data->num_codes * sizeof (*vals), sizeof (long *));
+  values = (unsigned long **) getmem ((MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *),
+		   sizeof (long *));
+
+  memset (values, '\0', (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *));
+
+  if (mem)
+    *mem += data->num_codes * sizeof (*vals) +
+      (MAX_HUFFCODE_LEN + 1) * sizeof (unsigned long *);
+
+  fcode[0] = values[0] = &vals[0];
+  FIXUP (&values[0]);
+  for (i = 1; i <= data->maxcodelen; ++i)
+    {
+      fcode[i] = values[i] = &vals[(values[i - 1] - vals) + data->lencount[i - 1]];
+      FIXUP (&values[i]);
+    }
+
+  for (i = 0; i < data->num_codes; ++i)
+    if (data->clens[i])
+      *fcode[(int) (data->clens[i])]++ = i;
+  return (values);
+}
+
+
+static void load_read_huffman(FILE *dict, compression_dict *cd, int which)
+{
+  huff_data *hd;
+  u_long   **vals;
+
+  hd = (huff_data *) getmem (sizeof (huff_data), sizeof (char *));
+  cd->MemForCompDict += sizeof (huff_data);
+  Read_Huffman_Data (dict, hd, &cd->MemForCompDict, NULL);
+  vals = Generate_Fast_Huffman_Vals (hd, &cd->MemForCompDict);
+  cd->chars_huff[which] = hd;
+  FIXUP (&cd->chars_huff[which]);
+  cd->chars_vals[which] = vals;
+  FIXUP (&cd->chars_vals[which]);
+  if (hd->clens)
+    delete []hd->clens;
+  hd->clens = NULL;
+}
+
+static void load_read_words(FILE *dict, compression_dict *cd, int which, int fixescape)
+{
+  cd->cfh[which] = (comp_frags_header *) getmem (sizeof (*cd->cfh[which]), sizeof (u_char *));
+  cd->MemForCompDict += sizeof (*cd->cfh[which]);
+  Read_cfh (dict, cd->cfh[which], &cd->MemForCompDict, NULL);
+
+  cd->values[which] = ReadInWords (dict, cd, cd->cfh[which], NULL);
+  FIXUP (&cd->cfh[which]);
+  FIXUP (&cd->values[which]);
+  if (fixescape)
+    { FIXUP(&cd->escape[which]);
+    }
+  else
+    {
+      cd->escape[which] = NULL;
+    }
+}
+
+static void 
+load_comp_dict (char *filename)
+{
+  FILE *dict;
+  int which;
+  compression_dict *cd;
+
+  cd = (compression_dict *) getmem (sizeof (compression_dict), sizeof (u_char *));
+  cd->fast_loaded = 1;
+
+  dict = open_file (filename, TEXT_DICT_SUFFIX, "rb",
+		    MAGIC_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  Read_cdh (dict, &cd->cdh, NULL, NULL);
+
+  for (which = 0; which < 2; ++which)
+    switch (cd->cdh.dict_type)
+      {
+      case MG_COMPLETE_DICTIONARY:
+	{
+	  load_read_words(dict, cd, which, 0);
+	}
+	break;
+      case MG_PARTIAL_DICTIONARY:
+	{
+	  if (cd->cdh.num_words[which])
+	    {
+	      load_read_words(dict, cd, which, 1);
+	    }
+
+	  load_read_huffman(dict, cd, which);
+	  load_read_huffman(dict, cd, which);
+	}
+	break;
+      case MG_SEED_DICTIONARY:
+	{
+	  if (cd->cdh.num_words[which])
+	    {
+	      load_read_words(dict, cd, which, 1);
+	    }
+	  switch (cd->cdh.novel_method)
+	    {
+	    case MG_NOVEL_HUFFMAN_CHARS:
+	      load_read_huffman(dict, cd, which);
+	      load_read_huffman(dict, cd, which);
+	      break;
+	    case MG_NOVEL_DELTA:
+	      break;
+	    case MG_NOVEL_HYBRID:
+	      break;
+	    }
+	  break;
+	}
+      }
+  fclose (dict);
+
+
+  if (cd->cdh.novel_method == MG_NOVEL_DELTA ||
+      cd->cdh.novel_method == MG_NOVEL_HYBRID)
+    {
+      cd->ad = LoadAuxDict (&cd->cdh, filename);
+      FIXUP (&cd->ad);
+    }
+}
+
+static void 
+save_fast_dict (char *filename)
+{
+  FILE *fdict;
+
+  fdict = create_file (filename, TEXT_DICT_FAST_SUFFIX, "wb",
+		       MAGIC_FAST_DICT, MG_ABORT);  /* [RPAP - Feb 97: WIN32 Port] */
+
+  {
+    u_long *p;
+    for (p = (u_long *) buffer; (u_long) p < (u_long) cur; ++p)
+      {
+	if (IS_FIXUP (p))
+	  HTONUL(*p);
+      }
+  }
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(mem);
+  HTONUL(fixup_mem);
+
+  fwrite (&mem, sizeof (mem), 1, fdict);
+  fwrite (&fixup_mem, sizeof (fixup_mem), 1, fdict);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(mem);
+  NTOHUL(fixup_mem);
+
+  fwrite (buffer, sizeof (u_char), mem, fdict);
+  fwrite (fixup, sizeof (u_char), fixup_mem, fdict);
+
+  fclose (fdict);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.1	(revision 16561)
@@ -0,0 +1,79 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_invf_dict 1 \*(Dt CITRI
+.SH NAME
+mgpp_invf_dict \- build an `on-disk' stemmed dictionary
+.SH SYNOPSIS
+.B mgpp_invf_dict
+[
+.B \-h
+]
+[
+.BI \-b " entries-per-block"
+]
+.if n .ti +13n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_invf_dict
+builds an `on-disk' stemmed dictionary from the
+compressed stemmed dictionary that can be used during querying.
+Each entry in the dictionary contains the disk
+pointer to the associated inverted file entry, and various other pieces
+of information about the stemmed word. The dictionary is blocked with
+each block containing the same number of entries (not the same size).
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectoryyyyyyyy\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " entries-per-block"
+The dictionary is stored in blocks on disk; this option is used to 
+specify the number of entries per block. The default is 16.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+The `on-disk' stemmed dictionary.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_stem_idx (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict.cpp	(revision 16561)
@@ -0,0 +1,237 @@
+/**************************************************************************
+ *
+ * mgpp_invf_dict.cpp -- Program to build the blocked stemmed dictionary
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "messages.h"
+#include "mg_files.h"
+#include "invf.h"
+
+static void process_files (char *filename, unsigned long entriesPerBlock) {
+  // open the dictionary
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+  invf_dict_header idh;
+  idh.Read (dictFile);
+
+  // open the inverted index file
+  FILE *invfIdxFile = open_file (filename, INVF_IDX_SUFFIX, "rb",
+				 MAGIC_INVI, MG_ABORT);
+
+  // create the blocked dictionary
+  FILE *blockDictFile = create_file (filename, INVF_DICT_BLOCKED_SUFFIX, "wb",
+				     MAGIC_STEM, MG_ABORT);
+  block_dict_header bdh;
+  bdh.lookback = idh.lookback;
+  bdh.word_dict_start = idh.word_dict_start;
+  bdh.word_dict_size = idh.word_dict_size;
+  bdh.tag_dict_start = idh.tag_dict_start;
+  bdh.tag_dict_size = idh.tag_dict_size;
+  bdh.num_docs = idh.num_docs;
+  bdh.num_frags = idh.num_frags;
+  bdh.num_words = idh.num_words;
+  bdh.total_bytes = idh.total_bytes;
+  bdh.index_string_bytes = idh.index_string_bytes;
+  bdh.num_levels = idh.num_levels;
+  bdh.Write (blockDictFile);
+
+  
+  // write out the word part of the dictionary
+
+  bdh.entries_per_wblk = entriesPerBlock;
+  bdh.max_wblk_size = 0;
+  bdh.wblk_start = ftell (blockDictFile);
+  
+  fseek (dictFile, idh.word_dict_start, SEEK_SET);
+
+  block_idx wordIdx;
+  word_block_dict_el wordBlockEl;
+  wordBlockEl.SetNumLevels (idh.num_levels);
+  
+  
+  unsigned long wordNum;
+  unsigned long wordInvfPtr;
+  UCArray lastEl;
+  word_dict_el wordEl;
+  wordEl.SetNumLevels (idh.num_levels);
+  for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+    // read in the next word and inverted file pointer
+    wordEl.Read (dictFile, idh.num_levels);
+    ReadUL (invfIdxFile, wordInvfPtr);
+
+    // remember this word (and position) if this is the start
+    // of a new block
+    if (wordNum % entriesPerBlock == 0) {
+      block_idx_info elIdx;
+      elIdx.el = wordEl.el;
+      elIdx.block_ptr = ftell (blockDictFile);
+
+      // see if this block is the longest so far
+      if (wordIdx.size() > 0) {
+	unsigned long blockLen = elIdx.block_ptr -
+	  (*(wordIdx.end()-1)).block_ptr;
+	if (blockLen > bdh.max_wblk_size) bdh.max_wblk_size = blockLen;
+      }
+      
+      wordIdx.push_back (elIdx);
+      lastEl.erase (lastEl.begin(), lastEl.end()); // output full word
+    }
+
+    // copy the information for this word
+    wordBlockEl.el = wordEl.el;
+    wordBlockEl.frag_occur = wordEl.frag_occur;
+    wordBlockEl.freq = wordEl.freq;
+    wordBlockEl.invf_ptr = wordInvfPtr;
+    unsigned long tempI;
+    for (tempI=0; tempI<idh.num_levels; ++tempI)
+      wordBlockEl.levelFreqs[tempI] = wordEl.levelFreqs[tempI];
+
+    // write out the word
+    wordBlockEl.Write (blockDictFile, &lastEl, idh.num_levels);
+
+    lastEl = wordBlockEl.el;
+  }
+
+
+  // write out the tag part of the dictionary
+
+  bdh.entries_per_tblk = entriesPerBlock;
+  bdh.max_tblk_size = 0;
+  bdh.tblk_start = ftell (blockDictFile);
+  
+  fseek (dictFile, idh.tag_dict_start, SEEK_SET);
+
+  block_idx tagIdx;
+  block_dict_el tagBlockEl;
+  
+  unsigned long tagNum;
+  unsigned long tagInvfPtr;
+  dict_el tagEl;
+  lastEl.erase (lastEl.begin(), lastEl.end());
+  for (tagNum=0; tagNum<idh.tag_dict_size; ++tagNum) {
+    // read in the next tag and inverted file pointer
+    tagEl.Read (dictFile);
+    ReadUL (invfIdxFile, tagInvfPtr);
+
+    // remember this tag (and position) if this is the start
+    // of a new block
+    if (tagNum % entriesPerBlock == 0) {
+      block_idx_info elIdx;
+      elIdx.el = tagEl.el;
+      elIdx.block_ptr = ftell (blockDictFile);
+
+      // see if this block is the longest so far
+      if (tagIdx.size() > 0) {
+	unsigned long blockLen = elIdx.block_ptr -
+	  (*(tagIdx.end()-1)).block_ptr;
+	if (blockLen > bdh.max_tblk_size) bdh.max_tblk_size = blockLen;
+      }
+      
+      tagIdx.push_back (elIdx);
+      lastEl.erase (lastEl.begin(), lastEl.end()); // output full word
+    }
+
+    // copy the information for this tag
+    tagBlockEl.el = tagEl.el;
+    tagBlockEl.frag_occur = tagEl.frag_occur;
+    tagBlockEl.freq = tagEl.freq;
+    tagBlockEl.invf_ptr = tagInvfPtr;
+
+    // write out the tag
+    tagBlockEl.Write (blockDictFile, &lastEl);
+
+    lastEl = tagBlockEl.el;
+  }
+
+
+  // write out the element indexes
+  bdh.num_wblks = wordIdx.size();
+  bdh.wblk_idx_start = ftell (blockDictFile);
+  WriteBlockIdx (blockDictFile, wordIdx);
+  
+  bdh.num_tblks = tagIdx.size();
+  bdh.tblk_idx_start = ftell (blockDictFile);
+  WriteBlockIdx (blockDictFile, tagIdx);
+
+  // write out the blocked dictionary header
+  fseek (blockDictFile, sizeof(unsigned long), SEEK_SET);
+  bdh.Write (blockDictFile);
+
+
+  // close open files
+  fclose (blockDictFile);
+  fclose (invfIdxFile);
+  fclose (dictFile);
+  
+  // print out information
+#ifndef SILENT
+  Message ("Max word block size = %d\n", bdh.max_wblk_size);
+  Message ("Max tag block size = %d\n", bdh.max_tblk_size);
+  Message ("Number of word blocks written = %d\n", bdh.num_wblks);
+  Message ("Number of tag blocks written = %d\n", bdh.num_tblks);
+#endif
+}
+
+
+int main (int argc, char **argv) {
+  unsigned long entriesPerBlock = 16;
+  char *filename = "";
+  int ch;
+  msg_prefix = argv[0];
+  opterr = 0;
+
+  while ((ch = getopt (argc, argv, "f:d:b:h")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'b':
+      entriesPerBlock = atoi (optarg);
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-f input_file] "
+	       "[-d data directory] [-b entries-per-block] "
+	       "[-h]\n", argv[0]);
+      exit (1);
+    }
+  }
+
+  process_files (filename, entriesPerBlock);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict_dump.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict_dump.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dict_dump.cpp	(revision 16561)
@@ -0,0 +1,141 @@
+/**************************************************************************
+ *
+ * mgpp_invf_dict_dump.cpp -- Program to printthe various inverted dictionaries
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "messages.h"
+#include "mg_files.h"
+#include "invf.h"
+
+static void process_files (char *filename,
+			   bool printHeader,
+			   bool printWords,
+			   bool printTags) {
+  // open the dictionary
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+  invf_dict_header idh;
+  idh.Read (dictFile);
+
+  // print the information from the inverted dictionary file header
+  if (printHeader) {
+    cout << "lookback: " << idh.lookback << "\n";
+    cout << "word_dict_start: " << idh.word_dict_start << "\n";
+    cout << "word_dict_size: " << idh.word_dict_size << "\n";
+    cout << "tag_dict_start: " << idh.tag_dict_start << "\n";
+    cout << "tag_dict_size: " << idh.tag_dict_size << "\n";
+    cout << "num_docs: " << idh.num_docs << "\n";
+    cout << "num_frags: " << idh.num_frags << "\n";
+    cout << "num_words: " << idh.num_words << "\n";
+    cout << "total_bytes: " << idh.total_bytes << "\n";
+    cout << "index_string_bytes: " << idh.index_string_bytes << "\n";
+    cout << "num_levels: " << idh.num_levels << "\n";
+
+    cout << "\n";
+  }
+
+
+  if (printWords) {
+    fseek (dictFile, idh.word_dict_start, SEEK_SET);
+    
+    unsigned long wordNum;
+    word_dict_el wordEl;
+    wordEl.SetNumLevels (idh.num_levels);
+    for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+      wordEl.Read (dictFile, idh.num_levels);
+      cout << "\"" << wordEl.el << "\"\n";// (" << wordNum << ")\n";
+    }
+    
+    cout << "\n";
+  }
+
+  // write out the tag part of the dictionary
+  if (printTags) {
+    fseek (dictFile, idh.tag_dict_start, SEEK_SET);
+    
+    unsigned long tagNum;
+    dict_el tagEl;
+    for (tagNum=0; tagNum<idh.tag_dict_size; ++tagNum) {
+      // read in the next tag and inverted file pointer
+      tagEl.Read (dictFile);
+      
+      cout << "\"" << tagEl.el << "\" (" << tagNum << ")\n";
+    }
+  }
+  
+  // close open files
+  fclose (dictFile);
+}
+
+
+int main (int argc, char **argv) {
+  char *filename = "";
+  int ch;
+  msg_prefix = argv[0];
+  opterr = 0;
+
+  bool printHeader = false;
+  bool printWords = false;
+  bool printTags = false;
+
+  while ((ch = getopt (argc, argv, "f:d:rwth")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'r':
+      printHeader = true;
+      break;
+    case 'w':
+      printWords = true;
+      break;
+    case 't':
+      printTags = true;
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-h] [-r] [-w] [-t] [-f input_file] "
+	       "[-d data directory]\n(-rwt:print header, words, tags)\n", 
+	       argv[0]);
+      exit (1);
+    }
+  }
+
+  process_files (filename, printHeader, printWords, printTags);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dump.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dump.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_invf_dump.cpp	(revision 16561)
@@ -0,0 +1,244 @@
+/**************************************************************************
+ *
+ * mgpp_invf_dump.cpp -- Program to dump uot an inverted fil
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "messages.h"
+#include "bitio_m_stdio.h"
+#include "bitio_gen.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+
+#include "mg_files.h"
+#include "locallib.h"
+#include "words.h"
+#include "invf.h"
+#include "WordData.h"
+
+static void PrintInvfWord (FILE *invfFile,
+			   invf_dict_header &idh,
+			   invf_file_header &ifh,
+			   word_dict_el &wordEl,
+			   unsigned long wordStart,
+			   bool printFrags) {
+  cout << wordEl.frag_occur << " \"" << wordEl.el << "\"\n";
+  
+  if (printFrags) {
+    // seek to the appropriate place in the inverted file
+    fseek (invfFile, wordStart, SEEK_SET);
+    
+    stdio_bitio_buffer buffer(invfFile);
+    
+    unsigned long B = BIO_Bblock_Init (idh.num_frags, wordEl.frag_occur);
+    unsigned long fragNum = 0;
+    unsigned long i;
+    for (i=0; i<wordEl.frag_occur; ++i) {
+      unsigned long delta = buffer.bblock_decode (B, NULL);
+      fragNum += delta;
+      cout << " " << fragNum;
+      
+      if (!ifh.word_level_index ) {
+	unsigned long count = buffer.gamma_decode (NULL);
+	cout << "(" << count << ")";
+      } else {
+	cout << "(1)";
+      }
+    }
+    
+    cout << "\n";
+  
+    buffer.done();
+  }
+}
+
+static void PrintInvfTag (FILE *invfFile,
+			  invf_dict_header &idh,
+			  invf_file_header &/*ifh*/,
+			  dict_el &tagEl,
+			  unsigned long tagStart,
+			  bool printFrags) {
+  cout << tagEl.frag_occur << " \"<" << tagEl.el << ">\"\n";
+  
+  if (printFrags) {
+    // seek to the appropriate place in the inverted file
+    fseek (invfFile, tagStart, SEEK_SET);
+    
+    stdio_bitio_buffer buffer(invfFile);
+    
+    unsigned long pTag = tagEl.frag_occur*2;
+    unsigned long B = BIO_Bblock_Init (idh.num_frags+pTag, pTag);
+    unsigned long fragNum = 0;
+    unsigned long i;
+    for (i=0; i<tagEl.frag_occur; ++i) {
+      unsigned long delta = buffer.bblock_decode (B, NULL)-1;
+      fragNum += delta;
+      cout << " " << fragNum;
+      cout << "-";
+      delta = buffer.bblock_decode (B, NULL)-1;
+      fragNum += delta;
+      cout << fragNum;
+    }
+    
+    cout << "\n";
+    
+    buffer.done();
+  }
+}
+
+static void PrintHeaderInfo (invf_dict_header &idh,
+			     invf_file_header &ifh) {
+  cerr << "Lookback:         " << idh.lookback << "\n";
+  cerr << "Word Dict Size:   " << idh.word_dict_size << "\n";
+  cerr << "Tag Dict Size:    " << idh.tag_dict_size << "\n";
+  cerr << "Num Documents:    " << idh.num_docs << "\n";
+  cerr << "Num Fragments:    " << idh.num_frags << "\n";
+  cerr << "Num Words:        " << idh.num_words << "\n";
+
+  cerr << "Skip Mode:        " << ifh.skip_mode << "\n";
+  cerr << "Word Level Index: " << ifh.word_level_index << "\n";
+
+  cerr << "\n";
+}
+
+
+static void process_files (char *filename,
+			   bool printHeader,
+			   bool printWords,
+			   bool printTags,
+			   bool printFrags) {
+  // open the dictionary
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			MAGIC_STEM_BUILD, MG_ABORT);
+  invf_dict_header idh;
+  idh.Read (dictFile);
+
+  // open the inverted file
+  FILE *invfFile = open_file (filename, INVF_SUFFIX, "rb",
+			MAGIC_INVF, MG_ABORT);
+  
+  invf_file_header ifh;
+  ifh.Read (invfFile);
+
+  if (ifh.skip_mode != SKIP_MODE_NO_SKIPS)
+    FatalError (1, "The invf file contains skips. Unable to dump.");
+
+  // print out header information
+  if (printHeader) {
+    PrintHeaderInfo (idh, ifh);
+  }
+
+  // open the inverted index
+  FILE *invfIdxFile = open_file (filename, INVF_IDX_SUFFIX, "rb",
+				 MAGIC_INVI, MG_ABORT);
+
+  // go to the start of the word dictionary
+  fseek (dictFile, idh.word_dict_start, SEEK_SET);
+
+  // process all the words
+  if (printWords) {
+    unsigned long wordNum;
+    unsigned long wordStart;
+    word_dict_el wordEl;
+    wordEl.SetNumLevels (idh.num_levels);
+    for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+      wordEl.Read (dictFile, idh.num_levels);
+      ReadUL (invfIdxFile, wordStart);
+      PrintInvfWord (invfFile, idh, ifh, wordEl, wordStart, printFrags);
+    }
+  }
+
+  // process all the tags
+  if (printTags) {
+    unsigned long tagNum;
+    unsigned long tagStart;
+    dict_el tagEl;
+    for (tagNum=0; tagNum<idh.tag_dict_size; ++tagNum) {
+      tagEl.Read (dictFile);
+      ReadUL (invfIdxFile, tagStart);
+      PrintInvfTag (invfFile, idh, ifh, tagEl, tagStart, printFrags);
+    }
+  }
+  // close the open files
+  fclose (invfIdxFile);
+  fclose (invfFile);
+  fclose (dictFile);
+}
+
+
+int main (int argc, char **argv) {
+  char *dir_name, *filename = "";
+  int ch;
+  msg_prefix = argv[0];
+  dir_name = getenv ("MGDATA");
+  opterr = 0;
+
+  bool printHeader = false;
+  bool printWords = false;
+  bool printTags = false;
+  bool printFrags = false;
+  
+  msg_prefix = argv[0];
+  while ((ch = getopt (argc, argv, "hrwtnf:d:")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath(optarg);
+      break;
+    case 'r':
+      printHeader = true;
+      break;
+    case 'w':
+      printWords = true;
+      break;
+    case 'n':
+      printFrags = true;
+      break;
+    case 't':
+      printTags = true;
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-h] [-r] [-w] [-t] [-n] [-f input_file]"
+	       "[-d data directory]\n(-rwnt:print header, words, tags, fragnums)\n", 
+	       argv[0]);
+      exit (1);
+    }
+  }
+  
+  process_files (filename, printHeader, printWords, printTags, printFrags);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.1	(revision 16561)
@@ -0,0 +1,323 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_passes 1 \*(Dt CITRI
+.SH NAME
+mgpp_passes \- builds mgpp databases
+.SH SYNOPSIS
+.B mgpp_passes
+[
+.BI \-J " doc-tag"
+]
+[
+.BI \-K " level-tag"
+]
+.if n .ti +10n
+[
+.BI \-L " index-level"
+]
+[
+.BI \-m " invf-mem-buffer"
+]
+.if n .ti +10n
+[
+.B \-T1
+]
+[
+.B \-T2
+]
+[
+.B \-I1
+]
+[
+.B \-I2
+]
+[
+.B \-S
+]
+[
+.B \-C
+]
+.if n .ti +10n
+[
+.B \-h
+]
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+[
+.I filename(s)
+]
+.SH DESCRIPTION
+.B mgpp_passes
+is the program that does most of the work when building mgpp
+database systems.  The input documents can come from either
+.I stdin
+or from a list of files on the command line.  In general,
+.B mgpp_passes
+must be run twice to build a database, first with the
+.B \-T1
+and
+.B \-I1
+options, and second with the
+.B \-T2
+and
+.B \-I2
+options.  Several other programs must be run in order to get an
+mgpp database.  The
+.SB EXAMPLE
+section below gives an example of how to build a complete
+mgpp database.
+.SH OPTIONS
+Options may appear in any order, but the
+.IR filename(s) ,
+if specified, must be last.
+.TP "\w'\fB\-C\fP \fIcompstatpointt\fP'u+2n"
+.BI \-J " doc-tag"
+Specifies the SGML tag that encloses each document.  Text appearing
+outside this tag is ignored.  The document tag defines the highest
+level document that can be queried and printed.  The default document
+tag is 'Document'.
+.TP
+.BI \-K " level-tag"
+Specifies the SGML tag of a sub document level.  A level tag must
+enclose all text enclosed by the document tag.  Levels can be
+queried and printed as if they were separate documents.  Multiple
+document levels can be specified (the document tag is always
+added as a document level).
+.TP
+.BI \-L " index-level"
+Specifies the SGML tag enclosing the smallest indexed element.  The
+index level should be no larger than the smallest document 
+level.  An empty string can be used to specify a word level index 
+(which is the default).
+.TP
+.BI \-m " invf-mem-buffer"
+Maximum amount of memory to use for the pass-2 file inversion in
+megabytes.  This option is only useful when used in conjunction with
+the option
+.BR \-I1 .
+The larger this value, the faster the pass-2 inversion will proceed.
+The default value is 5 MB.
+.TP
+.B \-T1
+Generate the
+.I *.text.stats
+file.
+.TP
+.B \-T2
+Generate the
+.IR *.text ,
+.IR *.text.idx ,
+.IR *.text.level ,
+and possibly the
+.I *.text.dict.aux
+files.  Using this option requires that the
+.I *.text.dict
+file be present.
+.TP
+.B \-I1
+Generate the
+.IR *.invf.dict ,
+.IR *.invf.level ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files.
+.TP
+.B \-I2
+Generate the
+.I *.invf
+and
+.I *.invf.idx
+files.  Using this option requires
+that the
+.IR *.invf.dict.hash ,
+.IR *.invf.level ,
+.IR *.invf.chunk ,
+and
+.I *.invf.chunk.trans
+files be present.  The
+.I *.invf.dict.hash
+file is generated by
+.BR mgpp_perf_hash_build (1)
+from the
+.I *.invf.dict
+file.
+.TP
+.B \-S
+This option causes a special pass to be executed.  It is up to a user
+to modify
+.I mg.special.c
+in the source code to do something with the documents it is given.
+.TP 
+.B \-C
+This activates the compatibility parsing mode.  When using this
+mode documents are separated by control-B and paragraphs are separated
+by control-C.  Internally these are converted to documents surrounded
+by 'Document' tags and paragraphs surrounded by 'Paragraph' tags.
+.TP
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is to be
+written.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection that will be
+created.
+.TP
+.I filename(s)
+This specifies the source text. If this is not specified, then the
+program expects the source text from
+.IR stdin .
+.SH EXAMPLE
+What follows is a UNIX
+.BR csh (1)
+script as an example of how to build an mgpp document collection.
+.LP
+.nf
+.DT
+.ft B
+.I #! /bin/csh
+.I
+# The first argument on the command line specifies the
+.I
+#   source of the text
+set source = ($1)
+.PP
+.I
+# The second argument is the name of the collection
+set text = ($2)
+.PP
+.I
+# Create *.text.stats, *.invf.dict, *.invf.level
+.I
+#   *.invf.chunk and *.invf.chunks.trans
+${source} | mgpp_passes -T1 -I1 -f ${text}
+.PP
+.I
+# Create *.text.dict
+mgpp_compression_dict -f ${text}
+.PP
+.I
+# Create *.invf.dict.hash
+mgpp_perf_hash_build -f ${text}
+.PP
+.I
+# Create *.text, *.text.idx, *.text.level
+.I
+#   *.invf and *.invf.idx
+${source} | mgpp_passes -T2 -I2 -f ${text}
+.PP
+.I
+# Create *.text.weight and *.weight.approx
+mgpp_weights_build -f ${text}
+.PP
+.I
+# Create *.invf.dict.blocked
+mgpp_invf_dict -f ${text}
+.PP
+.I
+# Create *.invf.dict.blocked.1
+mgpp_stem_idx -s 1 -f ${text}
+.PP
+.I
+# Create *.invf.dict.blocked.2
+mgpp_stem_idx -s 2 -f ${text}
+.PP
+.I
+# Create *.invf.dict.blocked.3
+mgpp_stem_idx -s 3 -f ${text}
+.PP
+.I
+# Create *.text.dict.fast
+mgpp_fast_comp_dict -f ${text}
+.ft R
+.fi
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 22
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.chunk
+Inverted file chunk descriptor file.  When the inverted file is
+created it is created in chunks that use no more than a set amount of
+memory.  This file describes those chunks.
+.TP
+.B *.invf.chunk.trans
+Word-occurrence-order to lexical-order translation file.  The
+.B *.invf.chunk
+file is written in word-occurrence order but is required by
+.B \-I2
+to be in lexical order.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked
+Compressed stemmed dictionary with index into the dictionary.
+.TP
+.B *.invf.dict.blocked.n
+Transformation dictionary from words stemmed with method 
+.B n
+to unstemmed words.
+.TP
+.B *.invf.dict.hash
+Data for an order-preserving perfect hash function.
+.TP
+.B *.invf.idx
+The index into the inverted file.
+.TP
+.B *.invf.level
+Information about the document levels needed for querying.
+.TP
+.B *.text
+Compressed text.
+.TP
+.B *.text.dict
+Compressed compression dictionary.
+.TP
+.B *.text.dict.fast
+A fast loading version of the compressed compression dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.text.level
+Information about the document levels needed for text decompression.
+.TP
+.B *.text.stats
+Statistics about the text.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.weight.approx
+The approximate weights file.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_stem_idx (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes.cpp	(revision 16561)
@@ -0,0 +1,274 @@
+/**************************************************************************
+ *
+ * mgpp_passes.cpp -- Driver for the various passes
+ * Copyright (C) 1994  Neil Sharman
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#pragma warning(disable:4786)
+#endif
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "sysfuncs.h"
+
+#ifdef HAVE_MALLINFO
+# include <malloc.h> 
+#endif
+
+#if defined __WIN32__
+# include <io.h>
+# include "getopt_old.h"
+# define close _close
+# define open _open
+#elif defined __CYGWIN__
+#include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "memlib.h"
+#include "messages.h"
+#include "longlong.h"
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "text.h"
+#include "stemmer.h"
+#include "FileBuf.h"
+#include "TextEl.h"
+#include "TagInfo.h"
+#include "words.h"
+#define MAX_PASSES 5
+
+#define SPECIAL 1
+#define TEXT_PASS_1 2
+#define TEXT_PASS_2 4
+#define IVF_PASS_1 8
+#define IVF_PASS_2 16
+
+#define MIN_BUF 8192
+
+
+unsigned long invf_buffer_size = 5 * 1024 * 1024;	/* 5Mb */
+
+static char Passes = 0;
+static char **files = NULL;
+static int num_files = 0;
+
+
+struct pass_data {
+  char *name;
+  int (*init) (const TagInfo &tagInfo, char *);
+  int (*process) (const TagInfo &tagInfo, const TextElArray &doc);
+  int (*done) (const TagInfo &tagInfo, char *);
+};
+
+
+static pass_data PassData[MAX_PASSES] = {
+  {"special", init_special, process_special, done_special},
+  {"text.pass1", init_text_1, process_text_1, done_text_1},
+  {"text.pass2", init_text_2, process_text_2, done_text_2},
+  {"ivf.pass1", init_ivf_1, process_ivf_1, done_ivf_1},
+  {"ivf.pass2", init_ivf_2, process_ivf_2, done_ivf_2},
+};
+
+static char *usage_str = "\nUSAGE:\n"
+"  %s [-J doc-tag] [-K level-tag] [-L index-level]\n"
+"  %*s [-m invf-memory] [-T1] [-T2] [-I1] [-I2] [-S]\n"
+"  %*s [-C] [-h] [-d directory] [-M maxnumeric] -f name\n\n";
+
+
+
+static void Usage (char *err) {
+  if (err) Message (err);
+  fprintf (stderr, usage_str, msg_prefix, strlen (msg_prefix), "",
+	   strlen (msg_prefix), "");
+  exit (1);
+}
+
+
+
+
+int OpenNextFile (int in_fd) {
+  if (in_fd > 0) close (in_fd);
+  if (num_files == 0) return (-1);
+  if ((in_fd = open (files[0], O_RDONLY)) == -1)
+    FatalError (1, "Cannot open %s", files[0]);
+  ++files;
+  --num_files;
+  return (in_fd);
+}
+
+
+static void Driver (int in_fd, char *file_name,
+		    const TagInfo &tagInfo, bool compatMode) {
+  //  cout << tagInfo;
+
+  int pass;
+
+  unsigned long numBytes = 0;
+  unsigned long numDocs = 0;
+
+  // initialise all the passes
+  for (pass = 0; pass < MAX_PASSES; ++pass) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].init (tagInfo, file_name) == COMPERROR)
+	FatalError (1, "Error during init of \"%s\"", PassData[pass].name);
+    }
+  }
+
+  
+  // set up various variables
+  FileBuf buf;
+  TextElArray doc;
+  unsigned long docLen = 0;
+
+  // read and process each file (start with an open file)
+  do {
+    
+    // read and process each document in this file
+    buf.SetFD (in_fd);
+    while (ReadDoc (buf, tagInfo.docTag, doc, docLen, compatMode)) {
+
+      // give this document to each pass
+      for (pass = 0; pass < MAX_PASSES; ++pass) {
+	if (Passes & (1 << pass)) {
+	  if (PassData[pass].process (tagInfo, doc) == COMPERROR)
+	    FatalError(1,"Error during processing of \"%s\"",PassData[pass].name);
+	}
+      }
+      
+      // another document has been processed
+      numBytes += docLen;
+      ++numDocs;
+    }
+    
+  } while ((in_fd = OpenNextFile (in_fd)) > 0);
+
+  
+  // do done for each pass
+  for (pass = 0; pass < MAX_PASSES; ++pass) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].done (tagInfo, file_name) == COMPERROR)
+	FatalError (1, "Error during done of \"%s\"", PassData[pass].name);
+    }
+  }
+}
+
+int main (int argc, char **argv) {
+  int ch, in_fd, maxnum;
+  char *filename = NULL;
+  bool compatMode = false;
+  TagInfo tagInfo;
+  tagInfo.SetDocTag ("Document");
+  
+  msg_prefix = argv[0];
+
+  opterr = 0;
+  while ((ch=getopt(argc, argv, "J:K:L:M:f:d:m:I:T:SCh"))!=-1){
+    switch (ch) {
+    case 'J':
+      tagInfo.SetDocTag (optarg);
+      break;
+    case 'K':
+      tagInfo.AddLevelTag (optarg);
+      break;
+    case 'L':
+      tagInfo.SetIndexLevel (optarg);
+      break;
+    case 'M':
+      maxnum = atoi(optarg);
+      if (maxnum > 4 && maxnum < 512) {
+	MAXNUMERIC = maxnum;
+      }
+      break;
+    case 'f':
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'm':
+      invf_buffer_size = (int) (atof (optarg) * 1024 * 1024);
+      break;
+    case 'I':
+      if (*optarg == '1')
+	Passes |= IVF_PASS_1;
+      else if (*optarg == '2')
+	Passes |= IVF_PASS_2;
+      else
+	Usage ("Invalid pass number");
+      break;
+    case 'T':
+      if (*optarg == '1')
+	Passes |= TEXT_PASS_1;
+      else if (*optarg == '2')
+	Passes |= TEXT_PASS_2;
+      else
+	Usage ("Invalid pass number");
+      break;
+    case 'S':
+      Passes |= SPECIAL;
+      break;
+    case 'C':
+      compatMode = true;
+      break;
+    case 'h':
+    case '?':
+      Usage (NULL);
+    }
+  }
+
+  if (!filename || *filename == '\0')
+    FatalError (1, "A document collection name must be specified.");
+
+  if ((Passes & (IVF_PASS_1 | IVF_PASS_2)) == (IVF_PASS_1 | IVF_PASS_2))
+    FatalError (1, "I1 and I2 cannot be done simultaneously.");
+
+  if ((Passes & (TEXT_PASS_1 | TEXT_PASS_2)) == (TEXT_PASS_1 | TEXT_PASS_2))
+    FatalError (1, "T1 and T2 cannot be done simultaneously.");
+
+  if (!Passes)
+    FatalError (1, "S, T1, T2, I1 or I2 must be specified.");
+
+  if (optind < argc) {
+    if ((in_fd = open (argv[optind], O_RDONLY)) == -1)
+      FatalError (1, "Cannot open %s", argv[optind]);
+    files = &argv[optind + 1];
+    num_files = argc - (optind + 1);
+    
+  } else in_fd = 0;  // stdin
+
+  
+  if (compatMode) tagInfo.SetDocTag ("Document");
+
+  // a document tag is also a level tag
+  tagInfo.levelTags.insert (tagInfo.docTag); 
+  
+  Driver (in_fd, filename, tagInfo, compatMode);
+
+  return (0);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.cpp	(revision 16561)
@@ -0,0 +1,316 @@
+/**************************************************************************
+ *
+ * mgpp_passes.cpp -- Driver for the various passes
+ * Copyright (C) 1994  Neil Sharman
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#pragma warning(disable:4786)
+#endif
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "sysfuncs.h"
+
+#ifdef HAVE_MALLINFO
+# include <malloc.h> 
+#endif
+
+#if defined __WIN32__
+# include <io.h>
+# include "getopt_old.h"
+# define close _close
+# define open _open
+#elif defined __CYGWIN__
+#include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "memlib.h"
+#include "messages.h"
+#include "longlong.h"
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "text.h"
+#include "stemmer.h"
+#include "FileBuf.h"
+#include "TextEl.h"
+#include "TagInfo.h"
+
+#include "mgpp_passes_4jni.h"
+
+#define MAX_PASSES 5
+
+#define SPECIAL 1
+#define TEXT_PASS_1 2
+#define TEXT_PASS_2 4
+#define IVF_PASS_1 8
+#define IVF_PASS_2 16
+
+#define MIN_BUF 8192
+
+
+unsigned long invf_buffer_size = 5 * 1024 * 1024;	/* 5Mb */
+
+static char Passes = 0;
+static char **files = NULL;
+static int num_files = 0;
+
+TagInfo tagInfo;
+char *filename = NULL;
+
+unsigned long numBytes = 0;
+unsigned long numDocs = 0;
+int mgpp_passes_exit_value = 0;
+
+struct pass_data {
+  char *name;
+  int (*init) (const TagInfo &tagInfo, char *);
+  int (*process) (const TagInfo &tagInfo, const TextElArray &doc);
+  int (*done) (const TagInfo &tagInfo, char *);
+};
+
+
+static pass_data PassData[MAX_PASSES] = {
+  {"special", init_special, process_special, done_special},
+  {"text.pass1", init_text_1, process_text_1, done_text_1},
+  {"text.pass2", init_text_2, process_text_2, done_text_2},
+  {"ivf.pass1", init_ivf_1, process_ivf_1, done_ivf_1},
+  {"ivf.pass2", init_ivf_2, process_ivf_2, done_ivf_2},
+};
+
+
+/* clear all the settings from one mgpp_passes run to the next */
+void clear_variables() {
+  tagInfo.Clear();
+  tagInfo.SetDocTag ("Document");
+  Passes = 0;
+  invf_buffer_size = 5 * 1024 * 1024;
+  numBytes = 0;
+  numDocs = 0;
+  mgpp_passes_exit_value = 0;
+}
+
+/* ################################################## */
+/* the following are methods to set all the variables that used to be
+   set by command line args */
+
+/* -S, -T1, -T2, -I1, -I2, args to mg_passes */
+void add_pass (char pass_type, char pass_num) {
+
+  switch(pass_type) {
+  case 'S':
+    Passes |= SPECIAL;
+    break;
+  case 'I':
+    if (pass_num == '1')
+      Passes |= IVF_PASS_1;
+    else if (pass_num == '2')
+      Passes |= IVF_PASS_2;
+    else
+      fprintf(stderr, "Invalid pass number %c for pass type %c\n", pass_num, pass_type);
+    break;
+  case 'T':
+    if (pass_num == '1')
+      Passes |= TEXT_PASS_1;
+    else if (pass_num == '2')
+      Passes |= TEXT_PASS_2;
+    else
+      fprintf(stderr, "Invalid pass number %c for pass type %c\n", pass_num, pass_type);
+    break;
+  }
+}
+
+/* -m arg to mgpp_passes */
+void set_inversion_limit(int limit) {
+  invf_buffer_size = limit * 1024 * 1024;
+}
+
+/* -J arg to mgpp_passes */
+void set_document_tag(const char *tag_name) {
+  tagInfo.SetDocTag (tag_name);
+
+  // a doc tag is also a level tag
+  tagInfo.AddLevelTag(tag_name);
+
+}
+
+/* -K arg to mgpp_passes */
+void add_level_tag(const char *tag_nam) {
+  tagInfo.AddLevelTag(tag_nam);
+}
+
+/* -L arg to mgpp_passes */
+void set_index_level(const char *tag_name) {
+  tagInfo.SetIndexLevel(tag_name);
+}
+
+// is this enough???
+/* -f arg to mgpp_passes */
+void set_filename(const char * filen) {
+  if (filename) {
+    Xfree (filename);
+    filename = NULL;
+  }
+  filename = Xstrdup (filen);  
+}
+ 
+
+/* ############################################### */
+/* The old driver method has been split into 3:
+init_driver, process_document (called numdocs times), 
+finalise_driver.
+The above set vars methods should all be called before init_driver.
+*/
+
+void init_driver () {
+
+  if (!filename || *filename == '\0') {
+    mgpp_passes_exit_value = 1;
+    FatalError (1, "A document collection name must be specified.");
+  }
+  if ((Passes & (IVF_PASS_1 | IVF_PASS_2)) == (IVF_PASS_1 | IVF_PASS_2)) {
+    mgpp_passes_exit_value = 1;
+    FatalError (1, "I1 and I2 cannot be done simultaneously.");
+  }
+  if ((Passes & (TEXT_PASS_1 | TEXT_PASS_2)) == (TEXT_PASS_1 | TEXT_PASS_2)) {
+    mgpp_passes_exit_value = 1;
+    FatalError (1, "T1 and T2 cannot be done simultaneously.");
+  }
+  if (!Passes) {
+    mgpp_passes_exit_value = 1;
+    FatalError (1, "S, T1, T2, I1 or I2 must be specified.");
+  }
+  
+  // initialise all the passes
+  for (int pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].init (tagInfo, filename) == COMPERROR) {
+	mgpp_passes_exit_value = 1;
+	FatalError (1, "Error during init of \"%s\"", PassData[pass].name);
+      }
+    }
+  }
+
+}
+
+void process_document(u_char *buffer, int len) {
+  
+  TextElArray doc;
+  unsigned long doc_len =  0;
+
+  while(ReadDoc(&buffer, tagInfo.docTag, doc, doc_len, false)) {
+    // give this document to each pass
+    for (int pass = 0; pass < MAX_PASSES; pass++) {
+      if (Passes & (1 << pass)) {
+	if (PassData[pass].process (tagInfo, doc) == COMPERROR) {
+	  mgpp_passes_exit_value = 1;
+	  FatalError(1,"Error during processing of \"%s\"",PassData[pass].name);
+	}
+      }
+    }
+    
+    // another document has been processed
+    numBytes += doc_len;
+    numDocs++;
+    cout << "doc_len = "<<doc_len<<endl;
+  }
+}    
+
+void finalise_driver () {
+  
+  // do done for each pass
+  for (int pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].done (tagInfo, filename) == COMPERROR) {
+	mgpp_passes_exit_value = 1;
+	FatalError (1, "Error during done of \"%s\"", PassData[pass].name);
+      }
+    }
+  }  
+}
+
+int get_exit_value() {
+  return mgpp_passes_exit_value;
+}
+  /*
+static void Driver (int in_fd, char *file_name,
+		    const TagInfo &tagInfo, bool compatMode) {
+  //  cout << tagInfo;
+
+  int pass;
+
+  unsigned long numBytes = 0;
+  unsigned long numDocs = 0;
+
+  // initialise all the passes
+  for (pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].init (tagInfo, file_name) == COMPERROR)
+	FatalError (1, "Error during init of \"%s\"", PassData[pass].name);
+    }
+  }
+
+  
+  // set up various variables
+  FileBuf buf;
+  TextElArray doc;
+  unsigned long docLen = 0;
+
+  // read and process each file (start with an open file)
+  do {
+    
+    // read and process each document in this file
+    buf.SetFD (in_fd);
+    while (ReadDoc (buf, tagInfo.docTag, doc, docLen, compatMode)) {
+
+      // give this document to each pass
+      for (pass = 0; pass < MAX_PASSES; pass++) {
+	if (Passes & (1 << pass)) {
+	  if (PassData[pass].process (tagInfo, doc) == COMPERROR)
+	    FatalError(1,"Error during processing of \"%s\"",PassData[pass].name);
+	}
+      }
+      
+      // another document has been processed
+      numBytes += docLen;
+      numDocs++;
+    }
+    
+  } while ((in_fd = OpenNextFile (in_fd)) > 0);
+
+  
+  // do done for each pass
+  for (pass = 0; pass < MAX_PASSES; pass++) {
+    if (Passes & (1 << pass)) {
+      if (PassData[pass].done (tagInfo, file_name) == COMPERROR)
+	FatalError (1, "Error during done of \"%s\"", PassData[pass].name);
+    }
+  }
+}
+
+  */
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_passes_4jni.h	(revision 16561)
@@ -0,0 +1,62 @@
+/**************************************************************************
+ *
+ * mgpp_passes_4jni.h -- modified mgpp_passes for use from jni
+ * Copyright (C) 2004 New Zealand Digital Library, http://www.nzdl.org
+ *
+ * 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 H_MGPP_PASSES_4JNI
+#define H_MGPP_PASSES_4JNI
+
+
+/* clear all the settings from one mgpp_passes run to the next */
+void clear_variables();
+
+/* -S, -T1, -T2, -I1, -I2, args to mg_passes */
+void add_pass (char pass_type, char pass_num);
+
+/* -m arg to mg_passes */
+void set_inversion_limit(int limit);
+
+/* -J arg to mgpp_passes */
+void set_document_tag(const char * tag_name);
+
+/* -K arg to mgpp_passes */
+void add_level_tag(const char * tag_name);
+
+/* -L arg to mgpp_passes */
+void set_index_level(const char * tag_name);
+
+/* -f arg to mgpp_passes */
+void set_filename(const char * filen);
+
+/* The old driver method has been split into 3:
+init_driver, process_document (called numdocs times), 
+finalise_driver.
+The above set vars methods should all be called before init_driver.
+*/
+
+void init_driver ();
+
+void process_document(u_char *buffer, int len);
+
+void finalise_driver();
+
+int get_exit_value();
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.1	(revision 16561)
@@ -0,0 +1,72 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_perf_hash_build 1 \*(Dt CITRI
+.SH NAME
+mgpp_perf_hash_build \- generate an order-preserving hash function for the stemmed dictionary
+.SH SYNOPSIS
+.B mgpp_perf_hash_build
+[
+.B \-h
+]
+[
+.BI \-r " rndseed"
+]
+[
+.BI \-d  " directory"
+]
+.if n .ti +9n
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_perf_hash_build
+generates an order-preserving hashing function from the compressed
+stemmed dictionary and writes it out to disk.
+.BR mgpp_passes (1)
+will make use of the hash function when it builds the inverted file.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-r " rndseed"
+This specifies the random seed to be used in the generation of the
+hash function; it must be an integer.  If this is not specified, the
+current time will be used as the random seed.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mg
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf.dict
+Stemmed dictionary.
+.TP
+.B *.invf.dict.hash
+Data for a order-preserving perfect hash function.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_stem_idx (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_perf_hash_build.cpp	(revision 16561)
@@ -0,0 +1,146 @@
+/**************************************************************************
+ *
+ * mgpp_perf_hash_build.cpp -- Program to build a perfect hash function
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "perf_hash.h"
+#include "netorder.h"
+
+#include "mg_files.h"
+#include "invf.h"
+#include "locallib.h"
+#include "words.h"
+#include "mg.h"
+
+#define POOL_SIZE 1024*1024
+
+static void ProcessFiles (char *filename, int r) {
+  FILE *dictFile, *hashFile;
+  unsigned long i;
+  invf_dict_header idh;
+  perf_hash_data *phd;
+  u_char *pool;
+  unsigned long pool_left;
+  u_char **starts;
+
+  // read in the dictionary
+  dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			MAGIC_STEM_BUILD, MG_ABORT);
+  if (dictFile==NULL) {
+    FatalError(1, "unable to open file");
+  }
+  idh.Read (dictFile);
+
+  //cerr << idh.lookback<<" "<<idh.word_dict_start<<endl;
+  // go to the start of the word dictionary
+  fseek (dictFile, idh.word_dict_start, SEEK_SET);
+  
+  if (!(pool = (u_char *) Xmalloc (POOL_SIZE)))
+    FatalError (1, "Out of memory");
+  pool_left = POOL_SIZE;
+
+  if (!(starts = (u_char **) Xmalloc (sizeof (u_char *) * idh.word_dict_size)))
+    FatalError (1, "Out of memory");
+  //cerr << "size= "<< idh.word_dict_size<<endl;
+  word_dict_el wordEl;
+  wordEl.SetNumLevels (idh.num_levels);
+  for (i = 0; i < idh.word_dict_size; ++i) {
+    // read the next word and associated information
+    wordEl.Read (dictFile, idh.num_levels);
+
+    // push string onto pool data
+    register unsigned long l;
+    l = wordEl.el.size() + 1;
+    if (pool_left < l) {
+      pool = (u_char *) Xmalloc (POOL_SIZE);
+      pool_left = POOL_SIZE;
+    }
+    starts[i] = pool;
+    
+    *pool++ = wordEl.el.size();
+    memcpy ((char *) pool, &(wordEl.el[0]), wordEl.el.size());
+    //cerr << pool<<"   " <<starts[i]<<endl;
+    pool += wordEl.el.size();
+    pool_left -= l;
+    
+  }
+  fclose (dictFile);
+  //cerr << pool<<"   " <<starts[i-1]<<endl;
+  //cerr<<"starts  "<<starts[113529]<<endl;
+  //cerr << starts[17][1] << " "<<starts[25][4]<<endl;
+  // create perfect hash file
+  hashFile = create_file (filename, INVF_DICT_HASH_SUFFIX, "wb",
+			  MAGIC_HASH, MG_ABORT);
+  if (!(phd = gen_hash_func (idh.word_dict_size, starts, r)))
+    FatalError (1, "Unable to generate hash function");
+  if (write_perf_hash_data (hashFile, phd) == -1)
+    FatalError (1, "Unable to write hash function");
+  fclose (hashFile);
+}
+
+
+
+int main (int argc, char **argv) {
+  int r = -1;
+  char *filename = "";
+  int ch;
+  msg_prefix = argv[0];
+  opterr = 0;
+
+  while ((ch = getopt (argc, argv, "f:d:r:h")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'r':
+      r = atoi (optarg);
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-f input_file]"
+	       "[-d data directory] [-r random seed] [-h]\n", argv[0]);
+      exit (1);
+    }
+  }
+
+  ProcessFiles (filename, r);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.1	(revision 16561)
@@ -0,0 +1,95 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_stem_idx 1 \*(Dt CITRI
+.SH NAME
+mgpp_stem_idx \- builds a stem index file
+.SH SYNOPSIS
+.B mgpp_stem_idx
+[
+.B \-h
+]
+[
+.BI \-b " entries-per-block"
+]
+.if n .ti +12n
+[
+.BI \-a " stemmer"
+]
+[
+.BI \-d " directory"
+]
+.if n .ti +12n
+.B \-s 1|2|3
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_stem_idx
+generates a stem index file for a collection.
+This program should be called three times: once for each
+.B -s
+parameter.  It uses the stemmed dictionary to create the stem index which
+contains pointers into the stemmed dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIdirectoryyyyyyyy\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " entries-per-block"
+The dictionary is stored in blocks on disk; this option is used to set
+the number of entries per block. The default is 16.
+.TP
+.BI \-a " stemmer"
+The name of the stemmer to use, the default is the Lovin stemmer.
+.TP
+.B -s 1|2|3
+The stem method to apply for the stem index.
+.br
+1 = casefolded and non-stemmed
+.br
+2 = non-casefolded and stemmed
+.br
+3 = casefolded and stemmed
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection can be found.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 22
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.invf.dict.blocked.1
+Stem index with stem index method 1.
+.TP
+.B *.invf.dict.blocked.2
+Stem index with stem index method 2.
+.TP
+.B *.invf.dict.blocked.3
+Stem index with stem index method 3.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_weights_build (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_stem_idx.cpp	(revision 16561)
@@ -0,0 +1,244 @@
+/**************************************************************************
+ *
+ * mgpp_stem_idx.cpp -- stem index builder
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "UCArray.h"
+#include "sysfuncs.h"
+#include "messages.h"
+#include "mg_files.h"
+#include "invf.h"
+#include "words.h"
+#include "stemmer.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#  include <ospace\std\vector>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#  include <vector.h>
+#else
+#  include <map>
+#  include <vector>
+#endif
+
+typedef vector<unsigned long> WordNumList;
+typedef map<UCArray, WordNumList, DictLTUCArray> StemMapDict;
+
+void CreateStemDict (char *filename,
+		     StemMapDict &stemDict,
+		     int stemMethod,
+		     int stemmerNum) {
+  stemDict.erase (stemDict.begin(), stemDict.end());
+
+  // open the dictionary
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+  invf_dict_header idh;
+  idh.Read (dictFile);
+
+  fseek (dictFile, idh.word_dict_start, SEEK_SET);
+
+  unsigned long wordNum;
+  u_char mgWord[MAXSTEMLEN + 1];
+  word_dict_el wordEl;
+  UCArray stemEl;
+  wordEl.SetNumLevels (idh.num_levels);
+  for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+    // read in the next word
+    wordEl.Read (dictFile, idh.num_levels);
+
+    // convert the word to an "mg word"
+    mgWord[0] = wordEl.el.size();
+    memcpy((char *)&mgWord[1], &(wordEl.el[0]), wordEl.el.size());
+
+    // stem the word
+    mgpp_stemmer (stemMethod, stemmerNum, mgWord);
+
+    // convert the result back to a UCArray
+    stemEl.erase (stemEl.begin(), stemEl.end());
+    stemEl.insert (stemEl.end(), &mgWord[1], &mgWord[1] + mgWord[0]);
+
+//      cout << "\"" << stemEl << "\" -> \"" << wordEl.el << "\"\n";
+    
+    // add this word number to the list of word numbers for this word
+    stemDict[stemEl].push_back (wordNum);
+  }
+
+  fclose (dictFile);
+}
+
+
+void WriteStemDict (char *filename,
+		    StemMapDict &stemDict,
+		    int stemMethod,
+		    int stemmerNum,
+		    unsigned long entriesPerBlock) {
+  
+  /* [JFG - Mar 06: Accent folding patch] */
+  // Create appropriate stem index file
+  FILE *stemDictFile = NULL;
+  if (stemMethod >= STEM_MIN && stemMethod <= STEM_MAX) {
+  	char *suffix = make_suffix (INVF_DICT_BLOCKED_SUFFIX_PAT, stemMethod, NULL);
+    stemDictFile = create_file (filename, suffix,
+				"wb", MAGIC_STEM_GEN(stemMethod + '0'), MG_ABORT);  
+  }
+  else {
+    FatalError (1, "Unknown stem method %d", stemMethod);
+  }
+
+  stem_idx_header sih;
+  sih.lookback = 0;
+  sih.dict_size = stemDict.size();
+  sih.entries_per_block = entriesPerBlock;
+  sih.max_block_size = 0;
+  
+  sih.stemmer_num = stemmerNum;
+  sih.stem_method = stemMethod;
+
+  // write out a place-holder version of the header
+  sih.Write (stemDictFile);
+
+  sih.blocks_start = ftell (stemDictFile);
+
+  block_idx stemIdx;
+  unsigned long stemNum = 0;
+  stem_block_dict_el stemEl;
+  UCArray lastEl;
+
+  StemMapDict::const_iterator here = stemDict.begin();
+  StemMapDict::const_iterator end = stemDict.end();
+  while (here != end) {
+    // remember this stem (and position) if this is the start
+    // of a new block
+    if (stemNum % entriesPerBlock == 0) {
+      block_idx_info elIdx;
+      elIdx.el = (*here).first;
+      elIdx.block_ptr = ftell (stemDictFile);
+
+      // see if this block is the longest so far
+      if (stemIdx.size() > 0) {
+	unsigned long blockLen = elIdx.block_ptr -
+	  (*(stemIdx.end()-1)).block_ptr;
+	if (blockLen > sih.max_block_size) sih.max_block_size = blockLen;
+      }
+      
+      stemIdx.push_back (elIdx);
+      lastEl.erase (lastEl.begin(), lastEl.end()); // output full word
+    }
+
+    // copy the information for this stem
+    stemEl.el = (*here).first;
+    stemEl.equivWords = (*here).second;
+
+    // write out the stem
+    stemEl.Write (stemDictFile, &lastEl);
+    
+    ++here; ++stemNum;
+  }
+
+
+  // write out the element indexes
+  sih.num_blocks = stemIdx.size();
+  sih.block_idx_start = ftell (stemDictFile);
+  WriteBlockIdx (stemDictFile, stemIdx);
+  
+  // write out the stem dictionary header
+  fseek (stemDictFile, sizeof(unsigned long), SEEK_SET);
+  sih.Write (stemDictFile);
+
+
+  // close open files
+  fclose (stemDictFile);
+  
+  // print out information
+#ifndef SILENT
+  Message ("Num word stems = %d\n", sih.dict_size);
+  Message ("Max stem block size = %d\n", sih.max_block_size);
+  Message ("Number of stem blocks written = %d\n", sih.num_blocks);
+#endif
+}
+
+
+int main (int argc, char **argv) {
+  unsigned long entriesPerBlock = 16;
+  char *filename = "";
+  int ch;
+  int stemMethod = 0; // illegal value (no translation)
+  int stemmerNum = 0; // English stemmer
+  msg_prefix = argv[0];
+  opterr = 0;
+
+  while ((ch = getopt (argc, argv, "f:d:b:s:h")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'b':
+      entriesPerBlock = atoi (optarg);
+      break;
+    case 's':
+      stemMethod = atoi (optarg);
+      break;
+    case 'a':
+      stemmerNum = mgpp_stemmernumber ((unsigned char *) optarg);
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-d directory] "
+	       "[-b entries-per-block] [-h] -s 1|2|3", argv[0]);
+#ifdef ENABLE_ACCENTFOLD
+      fprintf (stderr, "|4|5|6|7");
+#endif
+      fprintf (stderr, " [-a stemmer-method] -f name\n");
+      exit (1);
+    }
+  }
+  
+  /* [JFG - Mar 06: Accent folding patch] */
+  if (stemMethod < STEM_MIN || stemMethod > STEM_MAX) 
+    FatalError (1, "Stem method must be between %d and %d", STEM_MIN, STEM_MAX);
+#ifndef ENABLE_ACCENTFOLD
+  if (stemMethod & STEM_AccentFolding) {
+    // accent folding not enabled
+    return 2;
+  }
+#endif
+  // read in the dictionary and create the in memory dictionary
+  StemMapDict stemDict;
+  CreateStemDict (filename, stemDict, stemMethod, stemmerNum);
+  
+  // write out the dictionary as a blocked file
+  WriteStemDict (filename, stemDict, stemMethod, stemmerNum, entriesPerBlock);
+  
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.1
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.1	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.1	(revision 16561)
@@ -0,0 +1,79 @@
+.\"------------------------------------------------------------
+.\" Id - set Rv,revision, and Dt, Date using rcs-Id tag.
+.de Id
+.ds Rv \\$3
+.ds Dt \\$4
+..
+.\"------------------------------------------------------------
+.TH mgpp_weights_build 1 \*(Dt CITRI
+.SH NAME
+mgpp_weights_build \- builds the approximate weights file
+.SH SYNOPSIS
+.B mgpp_weights_build
+[
+.B \-h
+]
+[
+.BI \-b " num-bits"
+]
+.if n .ti +17n
+[
+.BI \-d " directory"
+]
+.BI \-f " name"
+.SH DESCRIPTION
+.B mgpp_weights_build
+is the program that generates the exact and approximate weights 
+files.  To do this, it uses the inverted file
+and dictionary.
+.SH OPTIONS
+Options may appear in any order.
+.TP "\w'\fB\-d\fP \fIirectory\fP'u+2n"
+.B \-h
+This displays a usage line on
+.IR stderr .
+.TP
+.BI \-b " num-bits"
+This specifies the number of bits of precision that should be used for
+the approximate weights.  If not specified, it defaults to 8.
+.TP
+.BI \-d " directory"
+This specifies the directory where the document collection is.
+.TP
+.BI \-f " name"
+This specifies the base name of the document collection.
+.SH ENVIRONMENT
+.TP "\w'\fBMGDATA\fP'u+2n"
+.SB MGDATA
+If this environment variable exists, then its value is used as the
+default directory where the mgpp
+collection files are.  If this variable does not exist, then the
+directory \*(lq\fB.\fP\*(rq is used by default.  The command line
+option
+.BI \-d " directory"
+overrides the directory in
+.BR MGDATA .
+.SH FILES
+.TP 20
+.B *.invf
+Inverted file.
+.TP
+.B *.invf.dict
+Compressed stemmed dictionary.
+.TP
+.B *.text.idx
+Index into the compressed documents.
+.TP
+.B *.weight
+The exact weights file.
+.TP
+.B *.weight.approx
+Approximate document weights.
+.SH "SEE ALSO"
+.na
+.BR mgpp_compression_dict (1),
+.BR mgpp_fast_comp_dict (1),
+.BR mgpp_invf_dict (1),
+.BR mgpp_passes (1),
+.BR mgpp_perf_hash_build (1),
+.BR mgpp_stem_idx (1)
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/mgpp_weights_build.cpp	(revision 16561)
@@ -0,0 +1,399 @@
+/**************************************************************************
+ *
+ * mgpp_weights_build.cpp -- Program to build the document weights file
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+/* getopt is in posix.2, so cygwin should have it in unistd, but doesn't */
+#if defined (__WIN32__) || defined (__CYGWIN__)
+# include "getopt_old.h"
+#else
+# include <unistd.h>
+#endif
+
+#include "UCArray.h"
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_gen.h"
+#include "bitio_m_stdio.h"
+#include "mg_files.h"
+#include "locallib.h"
+#include "invf.h"
+#include "FIvfLevelInfo.h"
+#include "FragLevelConvert.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+#define MAXBITS (sizeof(unsigned long) * 8)
+
+struct WBTagPtr {
+  unsigned long tagNum;
+  unsigned long tagPtr;
+  unsigned long fragOccur;
+  
+  WBTagPtr () {
+    tagNum = 0;
+    tagPtr = 0;
+    fragOccur = 0;
+  }
+};
+
+// maps tags to tag information
+typedef map<UCArray, WBTagPtr, DictLTUCArray> WBTagDict;
+
+typedef vector<float> Weights;
+
+
+static void ReadTagDict (const invf_dict_header &idh,
+			 FILE *dictFile,
+			 FILE *invfIdxFile,
+			 WBTagDict &tagDict) {
+  tagDict.erase (tagDict.begin(), tagDict.end());
+
+  // seek to the start of the tag information
+  fseek (dictFile, idh.tag_dict_start, SEEK_SET);
+  fseek (invfIdxFile, sizeof(unsigned long) +
+	 idh.word_dict_size*sizeof(unsigned long), SEEK_SET);
+  
+  unsigned long tagNum;
+  unsigned long tagPtr;
+  dict_el thisEl;
+  for (tagNum = 0; tagNum < idh.tag_dict_size; ++tagNum) {
+    thisEl.Read (dictFile);
+    ReadUL (invfIdxFile, tagPtr);
+    tagDict[thisEl.el].tagNum = tagNum;
+    tagDict[thisEl.el].tagPtr = tagPtr;
+    tagDict[thisEl.el].fragOccur = thisEl.frag_occur;
+  }
+}
+
+static void AddWeight (Weights &w,
+		       unsigned long levelDocNum,
+		       unsigned long termFreq,
+		       float idf) {
+  double weight = termFreq * idf;
+  w[levelDocNum-1] += weight * weight;
+}
+
+static void GenerateLevelWeights (const invf_dict_header &idh,
+				  const invf_file_header &ifh,
+				  unsigned long numLevelDocs,
+				  unsigned long levelNum,
+				  FILE *dictFile,
+				  FILE *invfFile,
+				  FILE *invfIdxFile,
+				  const FragLevelConvert &fragLevelConvert,
+				  Weights &w) {
+  // pre-allocate the right number of weights
+  w.erase (w.begin(), w.end());
+  w.insert (w.end(), (Weights::size_type)numLevelDocs, (float)0.0);
+
+  double logN = log ((double) numLevelDocs);
+
+  // reset the files
+  fseek (dictFile, idh.word_dict_start, SEEK_SET);
+  fseek (invfIdxFile, sizeof (unsigned long), SEEK_SET);
+  
+  // process each word adding its contributions to the document weights
+  unsigned long wordNum;
+  unsigned long wordStart;
+  word_dict_el wordEl;
+  wordEl.SetNumLevels (idh.num_levels);
+  for (wordNum=0; wordNum<idh.word_dict_size; ++wordNum) {
+#ifndef SILENT
+    // give a little feedback every 4096 words
+    if ((wordNum & 0xfff) == 0) fprintf (stderr, ".");
+#endif
+    
+    wordEl.Read (dictFile, idh.num_levels);
+    ReadUL (invfIdxFile, wordStart);
+
+    float idf = logN - log ((double) wordEl.levelFreqs[levelNum]);
+
+    // seek to the appropriate place in the inverted file
+    fseek (invfFile, wordStart, SEEK_SET);
+    stdio_bitio_buffer buffer (invfFile);
+    
+    unsigned long B = BIO_Bblock_Init (idh.num_frags, wordEl.frag_occur);
+    unsigned long fragNum = 0;
+    unsigned long levelDocNum = 0;
+    unsigned long lastLevelDocNum = 0;
+    unsigned long termFreq = 0;
+    unsigned long checkLevelFreq = 0;
+    
+    unsigned long count, i;
+    for (i=0; i<wordEl.frag_occur; ++i) {
+      fragNum += buffer.bblock_decode (B, NULL);
+      if (!ifh.word_level_index) count = buffer.gamma_decode (NULL);
+      else count = 1;
+
+      if (fragNum > idh.num_frags)
+	FatalError (1, "fragNum = %d, "
+		    "number of fragments = %d\n"
+		    "wordNum = %d\n"
+		    "i = %d, frag_occur = %d\n",
+		    fragNum, idh.num_frags, wordNum, i, wordEl.frag_occur);
+
+      if (!fragLevelConvert.FragToLevel (fragNum, levelDocNum))
+	FatalError (1, "could not convert fragment number %d in level %d", fragNum, levelNum);
+
+      if (levelDocNum == 0 || levelDocNum > numLevelDocs)
+	FatalError (1, "bad level document number %d in level %d", levelDocNum, levelNum);
+      
+      if (levelDocNum != lastLevelDocNum) {
+	// new level document
+	if (lastLevelDocNum > 0) {
+	  AddWeight (w, lastLevelDocNum, termFreq, idf);
+	  ++checkLevelFreq;
+	}
+	lastLevelDocNum = levelDocNum;
+	termFreq = 0;
+      }
+      termFreq += count;
+    }
+
+    if (lastLevelDocNum > 0) {
+      AddWeight (w, lastLevelDocNum, termFreq, idf);
+      ++checkLevelFreq;
+    }
+
+    if (checkLevelFreq != wordEl.levelFreqs[levelNum]) {
+      cerr << "bad level freq at level " <<levelNum<<" "<< checkLevelFreq << " != "
+	   << wordEl.levelFreqs[levelNum] << ", word \""
+	   << wordEl.el << "\" (" << wordNum << ")\n";
+      exit (1);
+    }
+    
+    buffer.done();
+  }
+
+  if (w.size() != numLevelDocs) 
+    FatalError (1, "wrong number of weights created %d != %d",
+		w.size(), numLevelDocs);
+}
+
+static void WriteExactWeights (FILE *weightsFile,
+			       unsigned long &diskPtr,
+			       const Weights &w) {
+  diskPtr = ftell(weightsFile);
+
+  Weights::const_iterator here = w.begin();
+  Weights::const_iterator end = w.end();
+  while (here != end) {
+//      cout << *here << "\n";
+    WriteF (weightsFile, sqrt (*here));
+    ++here;
+  }
+}
+
+static void WriteApproxWeights (FILE *approxWeightsFile,
+				unsigned long &diskPtr,
+				const Weights &w,
+				unsigned char bits) {
+  diskPtr = ftell(approxWeightsFile);
+
+  // calculate L, U and B
+  double L = 1e300;
+  double U = 0;
+  float wgt;
+  Weights::const_iterator here = w.begin();
+  Weights::const_iterator end = w.end();
+  while (here != end) {
+    wgt = sqrt (*here);
+    if (wgt > U) U = wgt;
+    if (wgt > 0 && wgt < L) L = wgt;
+    ++here;
+  }
+
+  double B = pow (U / L, pow (2.0, -(double) bits));
+
+#ifndef SILENT
+  fprintf (stderr, "L = %f\n", L);
+  fprintf (stderr, "U = %f\n", U);
+  fprintf (stderr, "B = %f\n", B);
+#endif
+  
+  WriteUC (approxWeightsFile, bits);
+  WriteD (approxWeightsFile, L);
+  WriteD (approxWeightsFile, B);
+  
+
+  unsigned long max = (bits == 32) ? 0xffffffff : (1 << bits) - 1;;
+  unsigned long i=0, buf=0, pos=0;
+  here = w.begin();
+  end = w.end();
+  while (here != end) {
+    unsigned long fx;
+    wgt = sqrt (*here);
+    if (wgt == 0) {
+      wgt = L;
+#ifndef SILENT
+      Message ("Warning: Document %d had a weight of 0.", i);
+#endif
+    }
+    fx = (unsigned long) floor (log (wgt / L) / log (B));
+
+    if (fx > max) fx = max;
+
+    buf |= (fx << pos);
+    pos += bits;
+
+    if (pos >= MAXBITS) {
+      WriteUL (approxWeightsFile, buf);
+      buf = fx >> (bits - (pos - MAXBITS));
+      pos = pos - MAXBITS;
+    }
+
+    ++here; ++i;
+  }
+
+  // write out the last bits
+  if (pos > 0) WriteUL (approxWeightsFile, buf);
+}
+
+int main (int argc, char **argv) {
+  unsigned char bits = 8;
+  char *filename = "";
+  int ch;
+  opterr = 0;
+  msg_prefix = argv[0];
+
+  while ((ch = getopt (argc, argv, "f:d:b:h")) != -1) {
+    switch (ch) {
+    case 'f':		// input file
+      filename = optarg;
+      break;
+    case 'd':
+      set_basepath (optarg);
+      break;
+    case 'b':
+      bits = atoi (optarg);
+      if (bits > 32) {
+	fprintf (stderr, "b may only take values 0-32\n");
+	exit (1);
+      }
+      break;
+    case 'h':
+    case '?':
+      fprintf (stderr, "usage: %s [-f input_file]"
+	       "[-d data directory] [-b bits] [-h]\n", argv[0]);
+      exit (1);
+    }
+  }
+
+  
+  // open the dictionary
+  FILE *dictFile = open_file (filename, INVF_DICT_SUFFIX, "rb",
+			      MAGIC_STEM_BUILD, MG_ABORT);
+  invf_dict_header idh;
+  idh.Read (dictFile);
+
+  // open the inverted file
+  FILE *invfFile = open_file (filename, INVF_SUFFIX, "rb",
+			MAGIC_INVF, MG_ABORT);
+  invf_file_header ifh;
+  ifh.Read (invfFile);
+  if (ifh.skip_mode != SKIP_MODE_NO_SKIPS)
+    FatalError (1, "The invf file contains skips. Unable to create weights.");
+
+  // open the inverted index file
+  FILE *invfIdxFile = open_file (filename, INVF_IDX_SUFFIX, "rb",
+				 MAGIC_INVI, MG_ABORT);
+  
+  // read the level information
+  FILE *levelFile = open_file (filename, INVF_LEVEL_SUFFIX, "rb",
+			       MAGIC_INVF_LEVELS, MG_ABORT);
+  FIvfLevel ivfLevel;
+  ivfLevel.Read (levelFile);
+  fclose (levelFile);
+
+  // read in the tag dictionary and inverted file pointers
+  WBTagDict tagDict;
+  ReadTagDict (idh, dictFile, invfIdxFile, tagDict);
+
+  
+  // create the weights file
+  FILE *weightsFile = create_file (filename, WEIGHTS_SUFFIX, "wb",
+				   MAGIC_WGHT, MG_ABORT);
+
+  // create the approx weights file
+  FILE *approxWeightsFile = create_file (filename, APPROX_WEIGHTS_SUFFIX, "wb",
+					 MAGIC_WGHT_APPROX, MG_ABORT);
+
+  
+  // create weights for each document level
+  FragLevelConvert fragLevelConvert;
+  Weights w;
+  IvfLevelInfoMap::iterator levelHere = ivfLevel.levelInfo.begin();
+  IvfLevelInfoMap::iterator levelEnd = ivfLevel.levelInfo.end();
+  unsigned long levelNum = 0;
+  while (levelHere != levelEnd) {
+    const UCArray &levelName = (*levelHere).first;
+    
+    // read the tag information about this level
+    fragLevelConvert.Read(invfFile, tagDict[levelName].tagPtr,
+			  idh.num_frags, tagDict[levelName].fragOccur);
+    
+    // create the weights for this level
+    GenerateLevelWeights (idh, ifh, tagDict[levelName].fragOccur,
+			  levelNum, dictFile, invfFile, invfIdxFile,
+			  fragLevelConvert, w);
+
+    // write out the exact weights
+    WriteExactWeights (weightsFile,
+		       (*levelHere).second.exactWeightsDiskPtr,
+		       w);
+    
+    // write out the approximate weights
+    WriteApproxWeights (approxWeightsFile,
+			(*levelHere).second.approxWeightsDiskPtr,
+			w, bits);
+    
+    ++levelHere; ++levelNum;
+  }
+  
+
+  // close input files
+  fclose (dictFile);
+  fclose (invfFile);
+  fclose (invfIdxFile);
+
+  // update the level information
+  levelFile = create_file (filename, INVF_LEVEL_SUFFIX, "wb",
+			   MAGIC_INVF_LEVELS, MG_ABORT);
+  ivfLevel.Write (levelFile);
+  fclose (levelFile);
+
+  // close output files
+  fclose (weightsFile);
+  fclose (approxWeightsFile);
+
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.cpp	(revision 16561)
@@ -0,0 +1,141 @@
+/**************************************************************************
+ *
+ * stemmer.cpp -- The stemmer/case folder
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#include "sysfuncs.h"
+#include "stemmer.h"
+#include "lovinstem.h"
+#include "simplefrenchstem.h"
+#include "unitool.h"
+
+#ifdef ENABLE_ACCENTFOLD
+/* [JFG - Mar 06: Accent folding patch] */
+#include "unac.h"
+#endif
+
+#define LOVINSTEMMER        0
+#define SIMPLEFRENCHSTEMMER 1
+
+
+/* decode the utf-8 encoded unicode, casefold and then recode 
+ * making sure the final length doesn't exceed the original
+ * length */
+static void mgpp_unicode_casefold (u_char *word) {
+  unsigned short out[256]; /* temp space */
+  int i;
+  int len;
+
+  /* decode */
+  utf8_word_to_unicode (word, out, 255);
+  len = out[0];
+
+  /* casefold and simplify-fold */
+  for (i=0; i<len; ++i) {
+    out[i+1] = unicode_tosimplified(unicode_tolower(out[i+1]));
+  }
+
+  /* re-code */
+  unicode_to_utf8_word (out, word, word[0]+1);
+}
+
+#ifdef ENABLE_ACCENTFOLD
+/* [JFG - Mar 06: Accent folding patch] */
+/* =========================================================================
+ * Function: unicode_accentfold
+ 
+ * Description: remove accents from characters
+ * Input: a word string with the length in the first byte
+ * Output: the unaccented word
+ * ========================================================================= */
+void mgpp_unicode_accentfold (unsigned char *word) {      
+  size_t unac_size = 0;
+  char *unac = NULL;
+
+
+  unac_string("utf-8", (char*)word+1, word[0], &unac, &unac_size);
+  strncpy((char*)word+1, unac, word[0]+1);
+  word[0] = unac_size;
+  
+  free(unac);
+  return;
+}
+#endif
+      
+int mgpp_stemmernumber (u_char *stemmerdescription) {
+  u_char descript[MAX_STEM_DESCRIPTION_LEN];
+  int i;
+
+  /* copy and case-fold the description */
+  for (i=0; (stemmerdescription[i] != '\0') && 
+	 (i < MAX_STEM_DESCRIPTION_LEN-1); ++i)
+    descript[i] = tolower (stemmerdescription[i]);
+  descript[i] = '\0';
+
+  /* map the description to its number */
+
+  if ((strcmp ((char *) descript,  "0") == 0) ||
+      (strcmp ((char *) descript, "english") == 0) ||
+      (strcmp ((char *) descript, "lovin") == 0))
+    return LOVINSTEMMER;
+
+  if ((strcmp ((char *) descript, "1") == 1) ||
+      (strcmp ((char *) descript, "french") == 0) ||
+      (strcmp ((char *) descript, "simplefrench") == 0))
+    return SIMPLEFRENCHSTEMMER;
+
+  return -1;
+}
+
+
+
+/*
+ * Method 0 - Do not stem or case fold.
+ * Method 1 - Case fold.
+ * Method 2 - Stem.
+ * Method 3 - Case fold and stem.
+ * Method 4 - Accent fold
+ * Method 5 - Accent fold and case fold
+ * Method 6 - Accent fold and stem
+ * Method 7 - Accent fold, stem and case fold
+
+ * The stemmer number should be obtained using
+ * the stemmernumber function above.
+ */
+void 
+mgpp_stemmer (int method, int stemmer, u_char *word) {
+  if (method & STEM_CaseFolding) {
+    mgpp_unicode_casefold (word);
+  }
+
+#ifdef ENABLE_ACCENTFOLD
+  if (method & STEM_AccentFolding) {
+    mgpp_unicode_accentfold (word);
+  }
+#endif
+
+  if (method & STEM_Stemming) {
+    switch (stemmer) {
+    case LOVINSTEMMER: lovinstem (word);
+      break;
+    case SIMPLEFRENCHSTEMMER: simplefrenchstem (word);
+      break;
+    }
+  }
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/stemmer.h	(revision 16561)
@@ -0,0 +1,90 @@
+/**************************************************************************
+ *
+ * stemmer.h -- The stemmer/case folder
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef STEMMER_H
+#define STEMMER_H
+
+/* [RPAP - Jan 97: Stem Index Change] */
+/* [JFG - Mar 06: Accent folding patch] */
+enum stemMethods {
+	STEM_None		=	0,
+	STEM_CaseFolding	=	0x1,
+	STEM_Stemming      	=	0x2,
+	STEM_AccentFolding 	=	0x4,
+};
+
+/* This is for the QueryParser */
+#define CHAR_FLAG_STEM_CaseFold    'i'	// ignore case
+#define CHAR_FLAG_STEM_NoCaseFold  'c'	// case sensitive
+#define CHAR_FLAG_STEM_Stemming    's'	// stem words
+#define CHAR_FLAG_STEM_NoStemming  'u'	// do not stem words
+#define CHAR_FLAG_STEM_AccentFold  'f'	// accent fold
+#define CHAR_FLAG_STEM_NoAccentFold  'a'	// do no accent folding
+#define CHAR_FLAG_STEM_Validator  "icsufa"	// all of the above
+
+
+#define STEM_MIN 1
+#define STEM_MAX (STEM_CaseFolding | STEM_Stemming | STEM_AccentFolding)
+#define STEM_PARTIAL_MATCH  (STEM_MAX+1)
+#define STEM_INVALID        (STEM_MAX+2)
+
+#define STEMMER_MASK 3
+#define MAX_STEM_DESCRIPTION_LEN 16
+
+/* stemmernumber will return the stemmer for
+ * a description of the stemmer. Stemmer descriptions
+ * are not case sensitive. Valid descriptions are:
+ *
+ *   'English'
+ *   'Lovin'
+ *   'French'
+ *   'SimpleFrench'
+ *
+ * More than one description might result in the same
+ * stemmer number (for example, for stemming 'English'
+ * we currently use the 'Lovin' stemmer).
+ *
+ * stemmerdescription is a normal C, null-terminated,
+ * string.
+ *
+ * stemmernumber will return -1 if it doesn't know the
+ * stemmer description.
+ */
+int mgpp_stemmernumber (unsigned char *stemmerdescription);
+
+/*
+ * Method 0 - Do not stem or case fold.
+ * Method 1 - Case fold.
+ * Method 2 - Stem.
+ * Method 3 - Case fold and stem.
+ * Method 4 - Accent fold
+ * Method 5 - Case fold and accent fold
+ * Method 6 - Stem and accent fold
+ * Method 7 - Case fold, stem and accent fold
+ * The stemmer number should be obtained using function
+ * stemmernumber above.
+ */
+#ifdef __cplusplus
+extern "C" 
+#endif
+void mgpp_stemmer (int method, int stemmer, unsigned char * word);
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/text.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/text.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/text.cpp	(revision 16561)
@@ -0,0 +1,146 @@
+/**************************************************************************
+ *
+ * text.h -- Header file for compression related stuff
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+// UCArray currently contains all the basic reading and writing functions
+#include "UCArray.h"
+#include "text.h"
+
+compressed_text_header::compressed_text_header () {
+  Clear();
+}
+
+void compressed_text_header::Clear () {
+  num_of_docs = 0;
+  num_of_words = 0;
+  num_of_bytes = 0.0;
+}
+
+
+bool compressed_text_header::Read (FILE *f) {
+  return (ReadUL (f, num_of_docs) &&
+	  ReadUL (f, num_of_words) &&
+	  ReadD (f, num_of_bytes));
+}
+
+bool compressed_text_header::Write (FILE *f) const {
+  return (WriteUL (f, num_of_docs) &&
+	  WriteUL (f, num_of_words) &&
+	  WriteD (f, num_of_bytes));
+}
+
+
+auxiliary_dict::auxiliary_dict () {
+  word_data[0] = NULL;
+  word_data[1] = NULL;
+  words[0] = NULL;
+  words[1] = NULL;
+}
+
+auxiliary_dict::~auxiliary_dict () {
+  Clear ();
+}
+
+void auxiliary_dict::Clear () {
+  if (word_data[0] != NULL) {
+    delete []word_data[0];
+    word_data[0] = NULL;
+  }
+  if (word_data[1] != NULL) {
+    delete []word_data[1];
+    word_data[1] = NULL;
+  }
+  if (words[0] != NULL) {
+    delete []words[0];
+    words[0] = NULL;
+  }
+  if (words[1] != NULL) {
+    delete []words[1];
+    words[1] = NULL;
+  }
+}
+
+
+compression_dict::compression_dict () {
+  int which;
+  for (which = 0; which < 2; ++which) {
+    cfh[which] = NULL;
+    values[which] = NULL;
+    escape[which] = NULL;
+    chars_huff[which] = NULL;
+    chars_vals[which] = NULL;
+    lens_huff[which] = NULL;
+    lens_vals[which] = NULL;
+  }
+
+  MemForCompDict = 0;
+  ad = NULL;
+  fast_loaded = 0;
+}
+
+compression_dict::~compression_dict () {
+  Clear ();
+}
+
+void compression_dict::Clear () {
+  int which;
+  for (which = 0; which < 2; ++which) {
+    if (cfh[which] != NULL) {
+      delete cfh[which];
+      cfh[which] = NULL;
+    }
+    if (values[which] != NULL) {
+      delete []values[which][0][0];
+      delete []values[which][0];
+      delete []values[which];
+      values[which] = NULL;
+    }
+    if (escape[which] != NULL) { // don't know if needed
+      //delete escape[which]; // this was causing a seg fault
+      escape[which] = NULL;
+    }
+    if (chars_huff[which] != NULL) {
+      delete chars_huff[which];
+      chars_huff[which] = NULL;
+    }
+    if (chars_vals[which] != NULL) {
+      delete []chars_vals[which][0];
+      delete []chars_vals[which];
+      chars_vals[which] = NULL;
+    }
+    if (lens_huff[which] != NULL) {
+      delete lens_huff[which];
+      lens_huff[which] = NULL;
+    }
+    if (lens_vals[which] != NULL) {
+      delete []lens_vals[which][0];
+      delete []lens_vals[which];
+      lens_vals[which] = NULL;
+    }
+  }
+
+  if (ad != NULL) {
+    delete ad;
+    ad = NULL;
+  }
+
+  MemForCompDict = 0;
+  fast_loaded = 0;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/text.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/text.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/text.h	(revision 16561)
@@ -0,0 +1,199 @@
+/**************************************************************************
+ *
+ * text.h -- Header file for compression related stuff
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef H_TEXT
+#define H_TEXT
+
+// some elderly windows compilers need to include <vector> (or something
+// similar) here so that they know what a "bool" is
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#else
+#  include <vector>
+#endif
+
+
+#include "huffman.h"
+#include <stdio.h>
+
+/*****************************************************************************
+ *
+ * There are several different methods of compressing the text of the
+ * database. The following defines list the different methods of text
+ * compression
+ *
+ */
+
+
+/* The dictionary contains all the fragments that occur in the collection
+   i.e. escapes are not possible */
+#define MG_COMPLETE_DICTIONARY 0
+
+
+/* Certain words have been deleted from the dictionary. The words deleted
+   have been used to create the frequency huffman codes of the characters. 
+   This dictionary has an escape code and may be used to compress novel
+   words. This dictionary may fail if there is a novel character. */
+#define MG_PARTIAL_DICTIONARY 1
+
+
+/* This dictionary has an escape so that novel words and non-words can be 
+   coded. The method for coding the novel words and non-words is determined
+   by a dictionary parameter. */
+#define MG_SEED_DICTIONARY 2
+
+
+
+
+
+
+
+
+
+/*****************************************************************************
+ *
+ * With a seed dictionary there are several methods for coding the novel
+ * words and non-words the following defined values specify the different
+ * methods of coding.
+ *
+ */
+
+
+/* Code novel words and non-words character by character using huffman codes.
+   The huffman codes for the word and non-word lengths and characters are 
+   generated from the distribution of lengths and characters in the
+   dictionary. */
+#define MG_NOVEL_HUFFMAN_CHARS 0
+
+
+/* This method codes novel words using delta codes. The novel words are stored
+   in a auxillary dictionary which is built by pass two. */
+#define MG_NOVEL_DELTA 2
+
+
+/* This method codes novel words using hybrid version of delta. The novel
+   words are stored in a auxillary dictionary which is built by pass two. */
+#define MG_NOVEL_HYBRID 3
+
+
+
+
+
+
+/* This specified an amount of extra space allocated in the
+   compression_dict_header for adding new parameters. As new
+   parameters are added this should be decreased. */
+#define TEXT_PARAMS 15
+
+
+
+struct compression_dict_header {
+  u_long dict_type;
+  u_long novel_method;
+  u_long params[TEXT_PARAMS];
+  u_long num_words[2];
+  u_long num_word_chars[2];
+  u_long lookback;
+};
+
+
+struct comp_frags_header {
+  huff_data hd;
+  u_long uncompressed_size;
+  u_long huff_words_size[MAX_HUFFCODE_LEN + 1];
+};
+
+// BOGUSTEXTLEN is used to replace the ratio
+// in the compressed_text_header while preparing
+// for UCArray
+#define BOGUSTEXTLEN 1000000
+
+struct compressed_text_header {
+  u_long num_of_docs;
+  u_long num_of_words; // number of words in collection
+  double num_of_bytes;
+
+  compressed_text_header ();
+  void Clear ();
+
+  // you must seek to the appropriate place before calling
+  // Read or Write
+  bool Read (FILE *f);
+  bool Write (FILE *f) const;
+};
+
+
+struct compression_stats_header {
+  u_long num_docs;
+  u_long dummy;     // alignment
+  double num_bytes; 
+};
+
+
+struct frags_stats_header {
+  u_long num_frags;
+  u_long mem_for_frags;
+};
+
+
+struct aux_frags_header {
+  u_long num_frags;
+  u_long mem_for_frags;
+};
+
+
+struct auxiliary_dict {
+  aux_frags_header afh[2];
+  u_char *word_data[2];
+  u_char **words[2];
+  int blk_start[2][33], blk_end[2][33];	/* blk_start and blk_end are required
+					   for the hybrid methods */
+
+  auxiliary_dict ();
+  ~auxiliary_dict ();
+
+  void Clear ();
+};
+
+
+struct compression_dict {
+  compression_dict_header cdh;
+  comp_frags_header *cfh[2];
+  unsigned long MemForCompDict;
+  u_char ***values[2];
+  u_char *escape[2];
+  huff_data *chars_huff[2];
+  u_long **chars_vals[2];
+  huff_data *lens_huff[2];
+  u_long **lens_vals[2];
+  auxiliary_dict *ad;
+  int fast_loaded;
+
+  compression_dict ();
+  ~compression_dict ();
+
+  void Clear ();
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass1.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass1.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass1.cpp	(revision 16561)
@@ -0,0 +1,388 @@
+/**************************************************************************
+ *
+ * text.pass1.cpp -- Text compression (Pass 1)
+ * Copyright (C) 1994  Neil Sharman, Gary Eddy and Alistair Moffat
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "huffman.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+#include "mg_files.h"
+#include "mg.h"
+#include "build.h"
+#include "locallib.h"
+#include "words.h"
+#include "text.h"
+#include "hash.h"
+#include "local_strings.h"
+#include "TextEl.h"
+
+/* for more meaningful messages - jrm21 (gsdl) */
+#ifdef HAVE_STRERROR
+#include <string.h>
+#include <errno.h>
+#endif
+
+
+#define POOL_SIZE 1024*1024
+#define INITIAL_HASH_SIZE 7927
+
+
+typedef struct hash_rec
+  {
+    unsigned long wcnt;		/* word frequency */
+    unsigned long occurance_num;
+    u_char *word;
+  }
+hash_rec;
+
+typedef struct dict_data
+  {
+    hash_rec *HashTable;
+    unsigned long HashSize;
+    unsigned long HashUsed;
+    unsigned long wordnum;
+    unsigned long words_read;
+    unsigned long bytes_diff;
+    huff_data hd;
+  }
+dict_data;
+
+
+
+static unsigned long longestDoc = 0;
+static unsigned long occurance_num = 0;
+static dict_data DictData[2];
+
+static u_char *Pool;
+static int PoolLeft;
+static double inputbytes = 0; /* [RJM 07/97: 4G limit] */
+static unsigned long MaxMemInUse = 0;
+static unsigned long MemInUse = 0;
+static compression_stats_header csh = {0, 0, 0.0}; /* [RJM 07/97: 4G limit] */
+
+
+static void ChangeMem (int Change) {
+  MemInUse += Change;
+  if (MemInUse > MaxMemInUse) MaxMemInUse = MemInUse;
+}
+
+
+int init_text_1 (const TagInfo &/*tagInfo*/, char * /*FileName*/) {
+  int which;
+  
+  if (!(Pool = (u_char *) Xmalloc (POOL_SIZE))) {
+    Message ("Unable to allocate memory for pool");
+    return (COMPERROR);
+  }
+  PoolLeft = POOL_SIZE;
+  ChangeMem (POOL_SIZE);
+  
+  for (which = 1; which >= 0; --which) {
+    u_char *word;
+    hash_rec *ent;
+    dict_data *dd = &DictData[which];
+    
+    dd->wordnum = 0;
+    dd->words_read = 0;
+    dd->bytes_diff = 0;
+    dd->HashSize = INITIAL_HASH_SIZE;
+    dd->HashUsed = 0;
+    
+    if (!(dd->HashTable = (hash_rec *) Xmalloc (sizeof (hash_rec) * dd->HashSize))) {
+      Message ("Unable to allocate memory for table");
+      return (COMPERROR);
+    }
+    ChangeMem (sizeof (hash_rec) * dd->HashSize);
+    memset (dd->HashTable,'\0' , sizeof (hash_rec) * dd->HashSize);
+
+    word = Pool;
+    *Pool++ = '\0';
+    --PoolLeft;
+    {
+      register u_char *wptr;
+      register int hsize = dd->HashSize;
+      register unsigned long hashval, step;
+      
+      HASH (hashval, step, word, hsize);
+      wptr = (dd->HashTable + hashval)->word;
+      while (wptr) {
+	hashval += step;
+	if (hashval >= (unsigned long)hsize)
+	  hashval -= hsize;
+	wptr = (dd->HashTable + hashval)->word;
+      }
+      ent = dd->HashTable + hashval;
+    }
+    ent->wcnt = 1;
+    ent->word = word;
+    dd->HashUsed = 1;
+  }
+  return (COMPALLOK);
+}
+
+
+static int process_text_element (const u_char *s_in, int l_in) {
+  const u_char *end = s_in + l_in - 1;
+  
+   /*
+   ** Alternately parse off words and non-words from the input
+   ** stream beginning with a non-word. Each token is then
+   ** inserted into the set if it does not exist or has it's
+   ** frequency count incremented if it does.
+   */
+  
+  bool which = false; // non-word
+  for (; s_in <= end; which = !which) {
+    u_char Word[MAXWORDLEN + 1];
+    dict_data *dd = &DictData[which];
+    
+    /* First parse a word or non-word out of the string */
+    if (which) PARSE_WORD (Word, s_in, end);
+    else PARSE_NON_WORD (Word, s_in, end);
+    
+    ++dd->wordnum;
+    inputbytes += *Word;
+    ++dd->words_read;
+    
+    /* Search the hash table for Word */
+    {
+      register unsigned long hashval, step;
+      register int hsize = dd->HashSize;
+      HASH (hashval, step, Word, hsize);
+      for (;;) {
+	register u_char *s1;
+	register u_char *s2;
+	register int len;
+	register hash_rec *ent;
+	ent = dd->HashTable + hashval;
+	if (!ent->word) {
+	  int len = *Word + 1;
+	  if (len > PoolLeft) {
+	    if (!(Pool = (u_char *) Xmalloc (POOL_SIZE))) {
+	      Message ("Unable to allocate memory for pool");
+	      return (COMPERROR);
+	    }
+	    PoolLeft = POOL_SIZE;
+	    ChangeMem (POOL_SIZE);
+	  }
+	  ent->occurance_num = occurance_num++;
+	  ent->wcnt = 1;
+	  ent->word = Pool;
+	  memcpy (Pool, Word, len);
+	  Pool += len;
+	  PoolLeft -= len;
+	  ++dd->HashUsed;
+	  dd->bytes_diff += Word[0];
+	  break;
+	}
+	
+	/* Compare the words */
+	s1 = Word;
+	s2 = ent->word;
+	len = *s1 + 1;
+	for (; len; --len)
+	  if (*s1++ != *s2++) break;
+	
+	if (len) {
+	  hashval = (hashval + step);
+	  if (hashval >= (unsigned long)hsize) hashval -= hsize;
+	} else {
+	  ++ent->wcnt;
+	  break;
+	}
+      }
+    }
+
+    
+    if (dd->HashUsed >= dd->HashSize >> 1) {
+      hash_rec *ht;
+      unsigned long size;
+      unsigned long i;
+      size = prime (dd->HashSize * 2);
+      if (!(ht = (hash_rec *) Xmalloc (sizeof (hash_rec) * size))) {
+	Message ("Unable to allocate memory for table");
+	return (COMPERROR);
+      }
+      ChangeMem (sizeof (hash_rec) * size);
+      memset (ht, '\0', sizeof (hash_rec) * size);
+
+      for (i = 0; i < dd->HashSize; ++i)
+	if (dd->HashTable[i].word) {
+	  register u_char *wptr;
+	  register unsigned long hashval, step;
+	  
+	  wptr = dd->HashTable[i].word;
+	  HASH (hashval, step, wptr, size);
+	  wptr = (ht + hashval)->word;
+	  while (wptr) {
+	    hashval += step;
+	    if (hashval >= size) hashval -= size;
+	    wptr = (ht + hashval)->word;
+	  }
+	  ht[hashval] = dd->HashTable[i];
+	}
+      Xfree (dd->HashTable);
+      ChangeMem (-sizeof (hash_rec) * dd->HashSize);
+      dd->HashTable = ht;
+      dd->HashSize = size;
+    }
+  }
+
+  return COMPALLOK;
+}
+
+
+int process_text_1 (const TagInfo &/*tagInfo*/, const TextElArray &doc) {
+  unsigned long textLen = 0;
+  unsigned long docLen = 0;
+  int retValue;
+
+  // process each text element in this document
+  TextElArray::const_iterator here = doc.begin();
+  TextElArray::const_iterator end = doc.end();
+  while (here != end) {
+    textLen = (*here).text.size();
+    docLen += textLen;
+
+    retValue = process_text_element (&(here->text[0]), textLen);
+    if (retValue != COMPALLOK) return retValue;
+    
+    ++here;
+  }
+
+  // get max document length
+  if (docLen > longestDoc) longestDoc = docLen;
+
+  // update header information
+  ++csh.num_docs;
+  csh.num_bytes += docLen;
+  
+  return COMPALLOK;
+}
+
+
+static int PackHashTable (dict_data * dd) {
+  int s, d;
+  for (s = d = 0; (unsigned int)s < dd->HashSize; ++s)
+    if (dd->HashTable[s].word)
+      dd->HashTable[d++] = dd->HashTable[s];
+  
+  ChangeMem (-sizeof (hash_rec) * dd->HashSize);
+  ChangeMem (sizeof (hash_rec) * dd->HashUsed);
+  
+  if (!(dd->HashTable = (hash_rec *) Xrealloc (dd->HashTable,
+			   sizeof (hash_rec) * dd->HashUsed))) {
+    Message ("Out of memory");
+    return COMPERROR;
+  }
+  dd->HashSize = dd->HashUsed;
+  return COMPALLOK;
+}
+
+
+static int ent_comp (const void *s1, const void *s2) {
+  return casecompare (((hash_rec *) s1)->word, ((hash_rec *) s2)->word);
+}
+
+
+static void WriteHashTable (FILE * fp, dict_data * dd) {
+  frags_stats_header fsh;
+  u_long j = 0;
+  u_char *curr;
+
+  if (PackHashTable (dd) == COMPERROR) return;
+  
+  qsort (dd->HashTable, dd->HashUsed, sizeof (hash_rec), ent_comp);
+  
+  fsh.num_frags = dd->HashSize;
+  fsh.mem_for_frags = dd->HashSize;
+  for (j = 0; j < dd->HashSize; ++j)
+    fsh.mem_for_frags += dd->HashTable[j].word[0];
+  
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(fsh.num_frags);
+  HTONUL(fsh.mem_for_frags);
+  
+  fwrite (&fsh, sizeof (fsh), 1, fp);
+  
+  for (j = 0; j < dd->HashSize; ++j) {
+    curr = dd->HashTable[j].word;
+    
+    /* [RPAP - Jan 97: Endian Ordering] */
+    HTONUL(dd->HashTable[j].wcnt);
+    HTONUL(dd->HashTable[j].occurance_num);
+    
+    fwrite (&dd->HashTable[j].wcnt, sizeof (dd->HashTable[j].wcnt), 1, fp);
+    fwrite (&dd->HashTable[j].occurance_num,
+	    sizeof (dd->HashTable[j].occurance_num), 1, fp);
+    
+    /* [RPAP - Jan 97: Endian Ordering] */
+    NTOHUL(dd->HashTable[j].wcnt);
+    NTOHUL(dd->HashTable[j].occurance_num);
+    
+    fwrite (curr, sizeof (u_char), curr[0] + 1, fp);
+  }
+}
+
+
+int done_text_1 (const TagInfo &/*tagInfo*/, char *file_name) {
+  char *temp_str;
+  FILE *fp;
+  
+  if (!(fp = create_file (file_name, TEXT_STATS_DICT_SUFFIX, "wb",
+			  MAGIC_STATS_DICT, MG_MESSAGE)))  /* [RPAP - Feb 97: WIN32 Port] */
+    {
+      fprintf(stderr,"Couldn't create file %s%s:%s\n",
+              file_name, TEXT_STATS_DICT_SUFFIX,
+#if defined(HAVE_STRERROR) || defined(__WIN32__)
+              strerror(errno)
+#else
+	      " "
+#endif
+              );
+      return COMPERROR;
+    }
+
+  temp_str = msg_prefix;
+  msg_prefix = "text.pass1";
+  
+  /* [RPAP - Jan 97: Endian Ordering] */
+  HTONUL(csh.num_docs);
+  HTOND(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  fwrite (&csh, sizeof (csh), 1, fp);
+
+  /* [RPAP - Jan 97: Endian Ordering] */
+  NTOHUL(csh.num_docs);
+  NTOHD(csh.num_bytes); /* [RJM 07/97: 4G limit] */
+
+  WriteHashTable (fp, &DictData[0]);
+  WriteHashTable (fp, &DictData[1]);
+  msg_prefix = temp_str;
+  fclose(fp);
+  return COMPALLOK;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass2.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass2.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/text.pass2.cpp	(revision 16561)
@@ -0,0 +1,585 @@
+/**************************************************************************
+ *
+ * text.pass2.cpp -- Text compression (Pass 2)
+ * Copyright (C) 1994  Neil Sharman, Gary Eddy and Alistair Moffat
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
+#pragma warning(disable:4786)
+#endif
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#if defined (__WIN32__)
+# include <stdio.h>
+# define unlink _unlink
+#endif
+
+#include "sysfuncs.h"
+#include "memlib.h"
+#include "messages.h"
+#include "local_strings.h"
+#include "bitio_m_stdio.h"
+#include "huffman.h"
+#include "netorder.h"  /* [RPAP - Jan 97: Endian Ordering] */
+#include "mg.h"
+#include "mg_files.h"
+#include "build.h"
+#include "words.h"
+#include "text.h"
+#include "hash.h"
+#include "locallib.h"
+#include "comp_dict.h"
+#include "FText.h"
+
+#define POOL_SIZE 1024*256
+
+
+struct char_pool {
+  struct char_pool *next;
+  u_long left;
+  u_char *ptr;
+  u_char pool[POOL_SIZE];
+};
+
+struct novel_hash_rec {
+  u_long ordinal_num;
+  u_char *word;
+};
+
+
+#define INITIAL_HASH_SIZE 7927
+
+struct novel_hash_table {
+  novel_hash_rec *HashTable;
+  u_long HashSize, HashUsed;
+  char_pool *first_pool;
+  char_pool *pool;
+  u_long next_num, binary_start;
+  novel_hash_rec **code_to_nhr;
+};
+
+
+static FILE *text;
+static stdio_bitio_buffer textOutBuf;
+static u_long headLen;
+
+static novel_hash_table nht[2];
+
+int blk_start[2][33], blk_end[2][33];
+
+// 0 = non-word, 1 = word
+unsigned char whichWordType = 0;
+
+CompressTextInfoMap textInfoMap;
+
+
+static char_pool *new_pool (char_pool * pool) {
+  char_pool *p = (char_pool *) Xmalloc (sizeof (char_pool));
+  if (!p) FatalError (1, "Unable to allocate memory for pool");
+  
+  if (pool) pool->next = p;
+  p->next = NULL;
+  p->left = POOL_SIZE;
+  p->ptr = p->pool;
+  return p;
+}
+
+
+int init_text_2 (const TagInfo &/*tagInfo*/, char *fileName) {
+  char path[512];
+
+  // load the compression dictionary
+  if (LoadCompressionDictionary (make_name (fileName, TEXT_DICT_SUFFIX,
+					    path)) == COMPERROR)
+    return COMPERROR;
+
+  // create the compressed text file and attach it to the text buffer
+  if (!(text = create_file (fileName, TEXT_SUFFIX, "w+b",
+			    MAGIC_TEXT, MG_MESSAGE)))
+    return COMPERROR;
+  textOutBuf.attachFile (text);
+  textOutBuf.encodeStart ();
+  
+  // write out the compressed text header
+  memset (&cth, '\0', sizeof (cth));
+  if (fwrite (&cth, sizeof (cth), 1, text) != 1)
+    return COMPERROR;
+
+  // the length of the compressed header is the size of the
+  // magic number plus the size of the compressed text header
+  headLen = sizeof (u_long) + sizeof (cth);
+
+  // start off with non-word
+  whichWordType = 0;
+
+  // clear the text pointers
+  textInfoMap.erase (textInfoMap.begin(), textInfoMap.end());
+  
+  // set up the hash table of novel words
+  int i;
+  if (cdh.novel_method != MG_NOVEL_HUFFMAN_CHARS)
+    for (i = 0; i <= 1; ++i)
+      {
+	nht[i].HashSize = INITIAL_HASH_SIZE;
+	nht[i].HashTable = (novel_hash_rec *) Xmalloc (sizeof (novel_hash_rec) * nht[i].HashSize);
+	memset (nht[i].HashTable, '\0',
+	       sizeof (novel_hash_rec) * nht[i].HashSize);
+	nht[i].HashUsed = 0;
+	nht[i].HashSize = INITIAL_HASH_SIZE;
+	nht[i].pool = nht[i].first_pool = new_pool (NULL);
+	nht[i].next_num = 1;
+	nht[i].binary_start = 1;
+	nht[i].code_to_nhr = NULL;
+	if (cdh.novel_method == MG_NOVEL_HYBRID)
+	  {
+	    int num;
+	    num = 1;
+	    blk_start[i][0] = 0;
+	    blk_end[i][0] = cdh.num_words[i] - 1;
+	    while (num < 33)
+	      {
+		blk_start[i][num] = blk_end[i][num - 1] + 1;
+		blk_end[i][num] = blk_start[i][num] +
+		  (blk_end[i][num - 1] - blk_start[i][num - 1]) * 2;
+		++num;
+	      }
+	  }
+      }
+
+  return (COMPALLOK);
+}
+
+
+static int compress_text (bitio_buffer &buffer, const u_char *s_in,
+			  int l_in, unsigned char &which,
+			  unsigned long &numWords) {
+  const u_char *end = s_in + l_in - 1;
+
+  for (; s_in <= end; which = !which) {
+    u_char Word[MAXWORDLEN + 1];
+    int res;
+    
+    if (which) ++numWords;
+
+    /* First parse a word or non-word out of the string */
+    if (which) PARSE_WORD (Word, s_in, end);
+    else PARSE_NON_WORD (Word, s_in, end);
+
+    /* Search the hash table for Word */
+    if (ht[which]) {
+      register unsigned long hashval, step;
+      register int tsize = ht[which]->size;
+      register u_char **wptr;
+      HASH (hashval, step, Word, tsize);
+      for (;;) {
+	register u_char *s1;
+	register u_char *s2;
+	register int len;
+	wptr = ht[which]->table[hashval];
+	if (wptr == NULL) {
+	  res = COMPERROR;
+	  break;
+	}
+
+	/* Compare the words */
+	s1 = Word;
+	s2 = *wptr;
+	len = *s1 + 1;
+	for (; len; --len)
+	  if (*s1++ != *s2++) break;
+	
+	if (len) {
+	  hashval += step;
+	  if (hashval >= (unsigned long)tsize) hashval -= tsize;
+	  
+	} else {
+	  res = ht[which]->table[hashval] - ht[which]->words;
+	  break;
+	}
+      }
+    }
+    else
+      res = COMPERROR;
+    /* Check that the word was found in the dictionary */
+    if (res == COMPERROR)
+      {
+	if (cdh.dict_type == MG_COMPLETE_DICTIONARY)
+	  {
+	    Message ("Unknown word \"%.*s\"\n", *Word, Word + 1);
+	    return (COMPERROR);
+	  }
+	if (cdh.dict_type == MG_PARTIAL_DICTIONARY)
+	  {
+	    u_long i;
+	    if (ht[which])
+	      {
+		res = ht[which]->hd->num_codes - 1;
+		buffer.huff_encode (res, ht[which]->codes, ht[which]->hd->clens, NULL);
+	      }
+	    buffer.huff_encode (Word[0], lens_codes[which], lens_huff[which].clens, NULL);
+	    for (i = 0; i < Word[0]; ++i)
+	      buffer.huff_encode (Word[i + 1], char_codes[which],
+				  char_huff[which].clens, NULL);
+	  }
+	if (cdh.dict_type == MG_SEED_DICTIONARY)
+	  {
+	    if (ht[which])
+	      {
+		res = ht[which]->hd->num_codes - 1;
+		buffer.huff_encode (res, ht[which]->codes, ht[which]->hd->clens, NULL);
+	      }
+	    switch (cdh.novel_method)
+	      {
+	      case MG_NOVEL_HUFFMAN_CHARS:
+		{
+		  u_long i;
+		  buffer.huff_encode (Word[0], lens_codes[which],
+				      lens_huff[which].clens, NULL);
+		  for (i = 0; i < Word[0]; ++i)
+		    buffer.huff_encode (Word[i + 1], char_codes[which],
+					char_huff[which].clens, NULL);
+		}
+		break;
+	      case MG_NOVEL_DELTA:
+	      case MG_NOVEL_HYBRID:
+		{
+		  register unsigned long hashval, step;
+		  register novel_hash_table *h = &nht[which];
+		  register int hsize = h->HashSize;
+		  register novel_hash_rec *ent;
+		  HASH (hashval, step, Word, hsize);
+		  for (;;)
+		    {
+		      register u_char *s1, *s2;
+		      register int len;
+		      ent = h->HashTable + hashval;
+		      if (!ent->word)
+			{
+			  int len = *Word + 1;
+			  if ((unsigned)len > h->pool->left)
+			    h->pool = new_pool (h->pool);
+			  ent->word = h->pool->ptr;
+			  ent->ordinal_num = h->next_num++;
+			  memcpy (h->pool->ptr, Word, len);
+			  h->pool->ptr += len;
+			  h->pool->left -= len;
+			  ++h->HashUsed;
+			  break;
+			}
+		      /* Compare the words */
+		      s1 = Word;
+		      s2 = ent->word;
+		      len = *s1 + 1;
+		      for (; len; --len)
+			if (*s1++ != *s2++)
+			  break;
+		      
+		      if (!len)
+			break;
+		      
+		      hashval = (hashval + step);
+		      if (hashval >= (unsigned)hsize) hashval -= hsize;
+		    }
+		  
+		  switch (cdh.novel_method) {
+		  case MG_NOVEL_DELTA:
+		    buffer.delta_encode (ent->ordinal_num, NULL);
+		    break;
+		  case MG_NOVEL_HYBRID:
+		    int k = 0;
+		    int j = ent->ordinal_num - 1;
+		    while (j > blk_end[which][k])
+		      ++k;
+		    assert (j - blk_start[which][k] + 1 >= 1 &&
+			    j - blk_start[which][k] + 1 <=
+			    blk_end[which][k] - blk_start[which][k] + 1);
+		    
+		    buffer.gamma_encode (k + 1, NULL);
+		    buffer.binary_encode (j - blk_start[which][k] + 1,
+					  blk_end[which][k] - blk_start[which][k] + 1,
+					  NULL);
+		    break;
+		  }
+		  
+		  if (h->HashUsed >= h->HashSize >> 1)
+		    {
+		      novel_hash_rec *ht;
+		      unsigned long size;
+		      unsigned long i;
+		      size = prime (h->HashSize * 2);
+		      if (!(ht = (novel_hash_rec *) Xmalloc (sizeof (novel_hash_rec) * size)))
+			{
+			  Message ("Unable to allocate memory for table");
+			  return (COMPERROR);
+			}
+		      memset (ht, '\0', sizeof (novel_hash_rec) * size);
+		      
+		      for (i = 0; i < h->HashSize; ++i)
+			if (h->HashTable[i].word)
+			  {
+			    register u_char *wptr;
+			    register unsigned long hashval, step;
+			    
+			    wptr = h->HashTable[i].word;
+			    HASH (hashval, step, wptr, size);
+			    wptr = (ht + hashval)->word;
+			    while (wptr)
+			      {
+				hashval += step;
+				if (hashval >= size)
+				  hashval -= size;
+				wptr = (ht + hashval)->word;
+			      }
+			    ht[hashval] = h->HashTable[i];
+			  }
+		      Xfree (h->HashTable);
+		      h->HashTable = ht;
+		      h->HashSize = size;
+		    }
+		}
+		break;
+	      }
+	  }
+      }
+    else
+      {
+	buffer.huff_encode (res, ht[which]->codes, ht[which]->hd->clens, NULL);
+      }
+  }
+  
+  return COMPALLOK;
+}
+
+
+int process_text_2 (const TagInfo &tagInfo, const TextElArray &doc) {
+  // we need to remember where the last text element ends
+  unsigned char endBit = textOutBuf.GetBtg (); // 8=high bit, 1=low bit
+  unsigned long endPos = textOutBuf.GetBytesWritten () + headLen;
+
+  // process each text element
+  TextElArray::const_iterator here = doc.begin();
+  TextElArray::const_iterator end = doc.end();
+  while (here != end) {
+    // remember current place in compressed text
+    unsigned char startWhich = whichWordType;
+    unsigned char startBit = textOutBuf.GetBtg (); // 8=high bit, 1=low bit
+    unsigned long startPos = textOutBuf.GetBytesWritten () + headLen;
+
+    // compress the text
+    if (compress_text (textOutBuf,
+		       &(here->text[0]),
+		       (*here).text.size(),
+		       whichWordType,
+		       cth.num_of_words) != COMPALLOK)
+      return COMPERROR;
+
+    endBit = textOutBuf.GetBtg (); // 8=high bit, 1=low bit
+    endPos = textOutBuf.GetBytesWritten () + headLen;
+    
+    // update the pointers if this is a level tag
+    if (tagInfo.levelTags.find ((*here).tagName) != tagInfo.levelTags.end()) {
+      if ((*here).elType == OpenTagE) {
+	// set the start of the document
+	textInfoMap[(*here).tagName].SetStart (startPos, startBit, startWhich);
+	
+      } else if ((*here).elType == CloseTagE) {
+	// set the end of the document
+	textInfoMap[(*here).tagName].SetEnd (endPos, endBit);
+	
+      } else {
+	// TextE, nothing needs to be done
+      }
+    }
+    
+    cth.num_of_bytes += (*here).text.size();
+    ++here;
+  }
+
+  // close off any unclosed tags
+  CompressTextInfoMap::iterator tiHere = textInfoMap.begin ();
+  CompressTextInfoMap::iterator tiEnd = textInfoMap.end ();
+  while (tiHere != tiEnd) {
+    if ((*tiHere).second.inDoc) (*tiHere).second.SetEnd (endPos, endBit);
+    ++tiHere;
+  }
+  
+  // we've processed one more document
+  ++cth.num_of_docs;
+
+  return COMPALLOK;
+}
+
+
+static int write_aux_dict (char *fileName) {
+  int i;
+  FILE *aux;
+  if (!(aux = create_file (fileName, TEXT_DICT_AUX_SUFFIX, "wb",
+			   MAGIC_AUX_DICT, MG_MESSAGE)))
+    return COMPERROR;
+  
+  for (i = 0; i <= 1; ++i)
+    {
+      aux_frags_header afh;
+      char_pool *cp;
+      
+      afh.num_frags = nht[i].HashUsed;
+      afh.mem_for_frags = 0;
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	afh.mem_for_frags += POOL_SIZE - cp->left;
+      
+      /* [RPAP - Jan 97: Endian Ordering] */
+      HTONUL(afh.num_frags);
+      HTONUL(afh.mem_for_frags);
+      
+      fwrite (&afh, sizeof (afh), 1, aux);
+      
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	fwrite (cp->pool, POOL_SIZE - cp->left, sizeof (u_char), aux);
+    }
+  fclose (aux);
+  return COMPALLOK;
+}
+
+
+static void estimate_compressed_aux_dict (void) {
+  int i;
+  u_long aux_compressed = 0, total_uncomp = 0;
+  for (i = 0; i <= 1; ++i)
+    {
+      int j;
+      long chars[256], fchars[256];
+      long lens[16], flens[16];
+      char_pool *cp;
+      memset (chars, '\0', sizeof (chars));
+      memset (lens, '\0', sizeof (lens));
+      for (cp = nht[i].first_pool; cp; cp = cp->next)
+	{
+	  u_char *buf = cp->pool;
+	  while (buf != cp->ptr)
+	    {
+	      int len = *buf++;
+	      ++lens[len];
+	      total_uncomp += len + 4;
+	      for (; len; --len)
+		++chars[*buf++];
+	    }
+	}
+      for (j = 0; j < 256; ++j)
+	if (!chars[j] && PESINAWORD (j) == i)
+	  fchars[j] = 1;
+	else
+	  fchars[j] = chars[j];
+      for (j = 0; j < 16; ++j)
+	if (!lens[j])
+	  flens[j] = 1;
+	else
+	  flens[j] = lens[j];
+      
+      aux_compressed += (long unsigned int) ((Calculate_Huffman_Size (16, flens, lens) +
+					      Calculate_Huffman_Size (256, fchars, chars)) / 8);
+      
+    }
+}
+
+
+
+static bool WriteTextIdx (char *fileName, FTextLevel &textLevels) {
+  // create the text index
+  FILE *textIdxFile;
+  if (!(textIdxFile = create_file (fileName, TEXT_IDX_SUFFIX, "w+b",
+				MAGIC_TEXI, MG_MESSAGE)))
+    return false;
+
+  // output each level
+  CompressTextInfoMap::iterator tiHere = textInfoMap.begin ();
+  CompressTextInfoMap::iterator tiEnd = textInfoMap.end ();
+  while (tiHere != tiEnd) {
+    // remember the level information
+    TextLevelInfo levelInfo;
+    levelInfo.levelTag = (*tiHere).first;
+    levelInfo.textIdxPtr = ftell (textIdxFile);
+    levelInfo.numEntries = (*tiHere).second.docPtrs.size();
+    textLevels.levelInfo[levelInfo.levelTag] = levelInfo;
+
+    // write out the index array for this level
+    if (!WriteTextIdxArray (textIdxFile, (*tiHere).second.docPtrs))
+      return false;
+    
+    ++tiHere;
+  }
+
+  // close the file
+  fclose (textIdxFile);
+
+  return true;
+}
+
+
+bool WriteTextLevels (char *fileName, const FTextLevel &textLevels) {
+  //  cout << textLevels;
+  
+  // create the text index
+  FILE *textLevelFile;
+  if (!(textLevelFile = create_file (fileName, TEXT_LEVEL_SUFFIX, "w+b",
+				     MAGIC_TEXT_LEVELS, MG_MESSAGE)))
+    return false;
+
+  // output each level
+  if (!textLevels.Write (textLevelFile)) return false;
+  
+  // close the file
+  fclose (textLevelFile);
+
+  return true;
+}
+
+
+int done_text_2 (const TagInfo &/*tagInfo*/, char *fileName) {
+  // write out any remaining bits
+  textOutBuf.encodeDone();
+
+  // write out the compressed text header (with updated statistics)
+  if (fseek (text, sizeof (u_long), SEEK_SET) == -1 || !cth.Write (text))
+    return COMPERROR;
+  fclose (text);
+
+  // write out the text index file and get the level information
+  FTextLevel textLevels;
+  if (!WriteTextIdx (fileName, textLevels)) return COMPERROR;
+  
+  // write out the text level information
+  if (!WriteTextLevels (fileName, textLevels)) return COMPERROR;
+
+  // write out the auxiliary dictionary
+  if (cdh.dict_type != MG_COMPLETE_DICTIONARY &&
+      (cdh.novel_method == MG_NOVEL_DELTA ||
+       cdh.novel_method == MG_NOVEL_HYBRID)) {
+    if (write_aux_dict (fileName) == COMPERROR) return COMPERROR;
+    estimate_compressed_aux_dict ();
+    
+  } else {
+    unlink (make_name (fileName, TEXT_DICT_AUX_SUFFIX, NULL));
+  }
+
+  return (COMPALLOK);
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/win32.mak	(revision 16561)
@@ -0,0 +1,218 @@
+###########################################################################
+#
+# win32 makefile -- mgpp\lib
+# 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  The 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.
+#
+###########################################################################
+
+MGPPHOME = ..
+
+UNAC_DIR = ../../packages/unac
+
+GSDL_VC4 = 0
+ENABLE_ACCENTFOLD = 1
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(ENABLE_ACCENTFOLD)
+AFLIBS = "$(UNAC_DIR)\libunac.lib"  "$(UNAC_DIR)\iconv.lib"
+AFINCLUDES = -I"$(UNAC_DIR)"
+AFDEFS = -DENABLE_ACCENTFOLD
+!ELSE
+AFLIBS = 
+AFINCLUDES = 
+AFDEFS = 
+!ENDIF
+
+# for now put executables in indexers/bin
+INSTALLDIR = ..\..\bin
+AR = lib
+CC = cl
+LINK = cl
+
+DEFS = -DQUIET -DHAVE_CONFIG_H -D__WIN32__ -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE \
+        -D__STDC__ -D__GNU_LIBRARY__ -D__MSDOS__ $(AFDEFS)
+INCLUDES = -I"." -I"$(MGPPHOME)" -I"$(MGPPHOME)\lib" $(AFINCLUDES)
+LIBS = $(MGPPHOME)\lib\libmgpplib.lib $(AFLIBS) 
+
+COMPILE = $(CC) -c  $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .obj
+.c.obj:
+	$(COMPILE) $<
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+HEADERS = \
+  FIvfLevelInfo.h     TextEl.h            hash.h              stemmer.h  \
+  FText.h             TextGet.h           invf.h              text.h     \
+  FileBuf.h           UCArray.h           locallib.h          words.h    \
+  FragLevelConvert.h  WordData.h          mg.h                           \
+  IndexData.h         build.h             mg_errors.h                    \
+  TagInfo.h           comp_dict.h         mg_files.h          Weights.h  \
+  MGQuery.h           Terms.h             QueryLex.h          QueryParser.h \
+  GSDLQueryLex.h	GSDLQueryParser.h
+
+
+SOURCES = \
+  FIvfLevelInfo.cpp        ivf.pass1.cpp            mgpp_invf_dump.cpp       \
+  FText.cpp                ivf.pass2.cpp            mgpp_passes.cpp          \
+  FileBuf.cpp              locallib.cpp             mgpp_perf_hash_build.cpp \
+  FragLevelConvert.cpp     mg.special.cpp           mgpp_stem_idx.cpp        \
+  IndexData.cpp            mgpp_compression_dict.cpp  mgpp_weights_build.cpp   \
+  TagInfo.cpp              mgpp_decompress_text.cpp   stemmer.cpp            \
+  TextEl.cpp               mg_errors.cpp            text.cpp               \
+  TextGet.cpp              mgpp_fast_comp_dict.cpp    text.pass1.cpp         \
+  UCArray.cpp              mg_files.cpp             text.pass2.cpp         \
+  comp_dict.cpp            mgpp_invf_dict.cpp         words.cpp              \
+  invf.cpp                 mgpp_invf_dict_dump.cpp    Weights.cpp            \
+  MGQuery.cpp              Terms.cpp                QueryTester.cpp        \
+  QueryLex.cpp             QueryParser.cpp         \
+  GSDLQueryLex.cpp	   GSDLQueryParser.cpp      Queryer.cpp
+
+
+EXEC          = mgpp_passes$e mgpp_decompress_text$e mgpp_perf_hash_build$e \
+                mgpp_invf_dump$e mgpp_weights_build$e mgpp_invf_dict$e \
+		mgpp_stem_idx$e mgpp_invf_dict_dump$e mgpp_compression_dict$e \
+		mgpp_fast_comp_dict$e QueryTester$e Queryer$e
+
+DISTFILES = Makefile.in $(HEADERS) $(SOURCES)
+ 
+all : $(EXEC) libmgpptext.lib libmgpppass.lib
+
+
+PASSES_OBJS = \
+	mgpp_passes$o text.pass1$o comp_dict$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o invf$o FileBuf$o TextEl$o UCArray$o TagInfo$o \
+        FText$o text$o FIvfLevelInfo$o
+
+mgpp_passes$e: $(PASSES_OBJS)
+	$(LINK) $(PASSES_OBJS) $(LIBS)
+
+QUERYER_OBJS = Queryer$o mg_files$o mg_errors$o locallib$o invf$o UCArray$o \
+               FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+               IndexData$o stemmer$o Weights$o TextGet$o text$o FText$o \
+               GSDLQueryParser$o GSDLQueryLex$o words$o
+
+Queryer$e: $(QUERYER_OBJS)
+	$(LINK) $(QUERYER_OBJS) $(LIBS)
+
+QUERY_TESTER_OBJS = QueryTester$o mg_files$o invf$o UCArray$o \
+                    FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+                    IndexData$o stemmer$o Weights$o
+
+QueryTester$e: $(QUERY_TESTER_OBJS)
+	$(LINK) $(QUERY_TESTER_OBJS) $(LIBS)
+
+WEIGHTS_BUILD_OBJS = mgpp_weights_build$o mg_files$o invf$o UCArray$o \
+                     FIvfLevelInfo$o FragLevelConvert$o
+
+mgpp_weights_build$e: $(WEIGHTS_BUILD_OBJS)
+	$(LINK) $(WEIGHTS_BUILD_OBJS) $(LIBS)
+
+COMP_DICT_OBJS = mgpp_compression_dict$o mg_files$o locallib$o
+
+mgpp_compression_dict$e: $(COMP_DICT_OBJS)
+	$(LINK) $(COMP_DICT_OBJS) $(LIBS)
+
+FAST_COMP_OBJS = mgpp_fast_comp_dict$o mg_files$o locallib$o
+
+mgpp_fast_comp_dict$e: $(FAST_COMP_OBJS)
+	$(LINK) $(FAST_COMP_OBJS) $(LIBS)
+
+INVF_DUMP_OBJS = mgpp_invf_dump$o locallib$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dump$e: $(INVF_DUMP_OBJS)
+	$(LINK) $(INVF_DUMP_OBJS) $(LIBS)
+
+INVF_DICT_OBJS = mgpp_invf_dict$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dict$e: $(INVF_DICT_OBJS)
+	$(LINK) $(INVF_DICT_OBJS) $(LIBS)
+
+INVF_DICT_DUMP_OBJS = mgpp_invf_dict_dump$o mg_files$o invf$o UCArray$o
+
+mgpp_invf_dict_dump$e: $(INVF_DICT_DUMP_OBJS)
+	$(LINK) $(INVF_DICT_DUMP_OBJS) $(LIBS)
+
+PERF_HASH_OBJS = mgpp_perf_hash_build$o mg_files$o invf$o UCArray$o
+
+mgpp_perf_hash_build$e: $(PERF_HASH_OBJS)
+	$(LINK) $(PERF_HASH_OBJS) $(LIBS)
+
+STEM_IDX_OBJS = mgpp_stem_idx$o mg_files$o stemmer$o \
+                invf$o UCArray$o
+
+mgpp_stem_idx$e: $(STEM_IDX_OBJS)
+	$(LINK)  $(STEM_IDX_OBJS) $(LIBS) 
+#/link /NODEFAULTLIB:libc
+
+DECOMPRESS_TEXT_OBJS = \
+                mgpp_decompress_text$o mg_files$o mg_errors$o \
+                locallib$o text$o UCArray$o TextGet$o FText$o
+
+mgpp_decompress_text$e: $(DECOMPRESS_TEXT_OBJS)
+	$(LINK) $(DECOMPRESS_TEXT_OBJS) $(LIBS)
+
+
+LIB_OBJS = mg_files$o  mg_errors$o locallib$o invf$o UCArray$o \
+           FIvfLevelInfo$o FragLevelConvert$o Terms$o MGQuery$o \
+           IndexData$o stemmer$o Weights$o TextGet$o text$o FText$o \
+           GSDLQueryParser$o GSDLQueryLex$o words$o
+
+libmgpptext.lib: $(LIB_OBJS)
+        if exist libmgpptext.lib del libmgpptext.lib
+        $(AR) /out:libmgpptext.lib $(LIB_OBJS)
+
+JNI_LIB_OBJS = \
+	mgpp_passes_4jni$o text.pass1$o comp_dict$o \
+	text.pass2$o locallib$o \
+	ivf.pass1$o ivf.pass2$o mg.special$o mg_files$o \
+	words$o invf$o FileBuf$o TextEl$o UCArray$o TagInfo$o \
+        FText$o text$o FIvfLevelInfo$o
+
+libmgpppass.lib: $(JNI_LIB_OBJS)
+	if exist libmgpppass.lib del libmgpppass.lib
+	$(AR) /out:libmgpppass.lib $(JNI_LIB_OBJS)
+
+install: 
+	if not exist "$(INSTALLDIR)" mkdir "$(INSTALLDIR)"
+	for %%i in ($(EXEC)) do \
+	  copy %%i "$(INSTALLDIR)"
+ 
+clean:
+	if exist *$o del *$o 
+	if exist *$e del *$e
+        if exist libmgpptext.lib del libmgpptext.lib 
+        if exist libmgpppass.lib del libmgpppass.lib
+
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/words.cpp
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/words.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/words.cpp	(revision 16561)
@@ -0,0 +1,123 @@
+/**************************************************************************
+ *
+ * words.cpp -- Functions for parsing out words from the source text
+ * Copyright (C) 1999  Rodger McNab
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if defined (__WIN32__) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+#endif
+
+#include "words.h"
+
+int MAXNUMERIC = 4;
+
+/* Takes the place of the old INAWORD macro. It determines
+   whether a given place in a UTF-8 encoded Unicode string
+   is part of a word. */
+int inaword_mgpp (const u_char *here, const u_char *end) {
+  unsigned short c;
+  if (parse_utf8_char(here, end, &c) > 0) return is_unicode_letdig (c);
+  return 0;
+}
+
+/* It determines whether a given place in a UTF-8 encoded Unicode string is a unicode space. */
+int isaspace_mgpp (const u_char *here, const u_char *end)
+{
+  unsigned short c;
+  if (parse_utf8_char(here, end, &c) > 0) return is_unicode_space(c);
+  return 0;
+}
+
+/* Return a the UTF-8 encoded Unicode string with begining
+   unicode spaces skippend. */
+u_char *skipspace_mgpp(u_char *here, u_char *end)
+{
+  unsigned short c;	
+  int length;
+  while(here != end) {
+    length = parse_utf8_char(here, end, &c);
+    if (length == 0 || !is_unicode_space(c)) break;
+    here += length;
+  }
+  return here;
+}
+ 
+const unsigned char *ParseIndexWord (const unsigned char *textHere,
+				     const unsigned char *textEnd,
+				     UCArray &word) {
+  word.erase (word.begin(), word.end());
+
+  register int charlength = 0;
+  register int length = 0;
+  register int numeric = 0;
+  unsigned short c;
+  
+  charlength = parse_utf8_char (textHere, textEnd, &c);
+  
+  while (length+charlength <= MAXSTEMLEN && charlength > 0 &&
+	 (is_unicode_letter(c) || (is_unicode_digit(c) &&
+				   ++numeric <= MAXNUMERIC))) {
+    while (charlength-- > 0) {
+      word.push_back (*textHere++); ++length;
+    }
+    charlength = parse_utf8_char (textHere, textEnd, &c);
+  }
+  
+  return textHere;
+}
+
+const unsigned char *ParseIndexMGWord (const unsigned char *textHere,
+				       const unsigned char *textEnd,
+				       unsigned char *mgWord) {
+  register int charlength = 0;
+  register int length = 0;
+  register int numeric = 0;
+  unsigned short c;
+  
+  charlength = parse_utf8_char (textHere, textEnd, &c);
+  
+  while (length+charlength <= MAXSTEMLEN && charlength > 0 &&
+	 (is_unicode_letter(c) || (is_unicode_digit(c) &&
+				   ++numeric <= MAXNUMERIC))) {
+    while (charlength-- > 0) {
+      mgWord[++length] = *textHere++;
+    }
+    charlength = parse_utf8_char (textHere, textEnd, &c);
+  }
+
+  mgWord[0] = length;
+  
+  return textHere;
+}
+
+const unsigned char *ParseNonindexWord (const unsigned char *textHere,
+					const unsigned char *textEnd) {
+  register int charlength = 0;
+  unsigned short c;
+  
+  charlength = parse_utf8_char(textHere, textEnd, &c);
+  
+  while (charlength > 0 && !is_unicode_letdig(c)) {
+    textHere += charlength;
+    charlength = parse_utf8_char (textHere, textEnd, &c);
+  }
+  
+  return textHere;
+}
Index: /gsdl/trunk/gs2common/indexers/mgpp/text/words.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/text/words.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/text/words.h	(revision 16561)
@@ -0,0 +1,171 @@
+/**************************************************************************
+ *
+ * words.h -- Macros for parsing out words from the source text
+ * Copyright (C) 1994  Neil Sharman
+ *
+ * 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.
+ *
+ **************************************************************************/
+#ifndef H_WORDS
+#define H_WORDS
+
+
+#include "sysfuncs.h"
+
+#include "unitool.h"
+#include "UCArray.h"
+
+
+/*
+ * This has been cleaned up by Tim Shimmin. 
+ */
+
+/*
+ * ---NOTE---
+ *
+ * "WORD" refers to a word in the compressed text.
+ * "STEM" or "STEM_WORD" refers to a word for indexing on
+ *
+ */
+
+#define MAXWORDLEN	15
+	/* Maximum length in bytes of any word or non-word. Note that
+	   variations to MAXWORDLEN may have dramatic effects on the rest
+	   of the program, as the length and the prefix match are packed
+	   together into a four bit nibble, and there is not check that
+	   this is possible, i.e., leave MAXWORDLEN alone... */
+
+#define MAXSTEMLEN	255
+	/* Maximum length in bytes of any stem. Note that
+	   variations to MAXSTEMLEN may have dramatic effects on the rest
+	   of the program, , i.e., leave MAXSTEMLEN alone... */
+
+//#define MAXNUMERIC	4
+	/* Maximum number of numeric characters permitted in a word.
+	   This avoids long sequences of numbers creating just one
+	   word occurrence for each number. At most 10,000 all numeric
+	   words will be permitted. */
+
+extern  int MAXNUMERIC;
+
+/* [RPAP - Jan 97: Stem Index Change] */
+#define MAXPARAMLEN     20
+        /* Maximum number of bytes to read for a parameter value for a
+	   term in a query. */
+#define WEIGHTPARAM     '/'
+#define STEMPARAM       '#'
+
+/* [RJM 07/97: Ranked Required Terms] */
+#define MUSTMATCHPARAM     '+'
+
+
+#define PESINAWORD(c)      (isalnum(c) || ((c) >= 0x80 && (c) <= 0xff))
+        /* The definition of what characters are permitted in a word.
+	   This macro is pessimistic, you cannot tell from a particular
+	   byte above 0x80 whether it is a character or not. This function
+	   is needed by various functions relating to huffman coding
+	   where frequency counts need to be primed, it should not be
+	   used in parsing the UTF-8 encoded input. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int inaword_mgpp (const u_char *here, const u_char *end);
+        /* Takes the place of the old INAWORD macro. It determines
+	   whether a given place in a UTF-8 encoded Unicode string
+	   is part of a word. */
+
+int isaspace_mgpp (const u_char *here, const u_char *end);
+        /* It determines whether a given place in a UTF-8 encoded Unicode string  is a unicode space. */
+
+u_char *skipspace_mgpp(u_char *here, u_char *end);
+        /* Return a the UTF-8 encoded Unicode string with begining unicode spaces skippend. */
+
+#ifdef __cplusplus
+}
+#endif
+
+const unsigned char *ParseIndexWord (const unsigned char *textHere,
+				     const unsigned char *textEnd,
+				     UCArray &word);
+const unsigned char *ParseIndexMGWord (const unsigned char *textHere,
+				       const unsigned char *textEnd,
+				       unsigned char *mgWord);
+const unsigned char *ParseNonindexWord (const unsigned char *textHere,
+					const unsigned char *textEnd);
+
+
+
+/* =========================================================================
+ * Macro: PARSE_WORD
+ * Description: 
+ *      Extract a word out for compressing text
+ * Input: 
+ *      s_in = string start in buffer
+ *      end = string end in buffer
+ * Output: 
+ *      Word = extracted word with length in 1st byte
+ *      s_in = ptr to next character in buffer yet to be processed
+ * ========================================================================= */
+#define PARSE_WORD(Word, s_in, end)                                \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    register u_char *wptr = (Word)+1;                              \
+    register int length = 0;                                       \
+    register int numeric = 0;                                      \
+    unsigned short c;                                              \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (length+charlength <= MAXWORDLEN && charlength > 0 &&    \
+	   (is_unicode_letter(c) || (is_unicode_digit(c) &&        \
+				     ++numeric <= MAXNUMERIC))) {  \
+      while (charlength-- > 0) {                                   \
+        *wptr++ = *(s_in)++; ++length;                             \
+      }                                                            \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+    *(Word) = length;                                              \
+  }while(0)
+
+
+
+/* =========================================================================
+ * Macro: PARSE_NON_WORD
+ * Description: 
+ *      Extract a non-word out for storing compressed text
+ * Input: as above
+ * Output: as above
+ * ========================================================================= */
+#define PARSE_NON_WORD(Word, s_in, end)                            \
+  do {                                                             \
+    register int charlength = 0;                                   \
+    register u_char *wptr = (Word)+1;                              \
+    register int length = 0;                                       \
+    unsigned short c;                                              \
+                                                                   \
+    charlength = parse_utf8_char((s_in),(end),&c);                 \
+                                                                   \
+    while (length+charlength <= MAXWORDLEN && charlength > 0 &&    \
+	   !is_unicode_letdig(c)) {                                \
+      while (charlength-- > 0) {                                   \
+        *wptr++ = *(s_in)++; ++length;                             \
+      }                                                            \
+      charlength = parse_utf8_char((s_in),(end),&c);               \
+    }                                                              \
+    *(Word) = length;                                              \
+  }while(0)
+
+#endif
Index: /gsdl/trunk/gs2common/indexers/mgpp/win32cfg.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/win32cfg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/win32cfg.h	(revision 16561)
@@ -0,0 +1,166 @@
+/* config.h.  Generated automatically by configure.  */
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+/* #undef _ALL_SOURCE */
+#endif
+
+/* Define if using alloca.c.  */
+/* #undef C_ALLOCA */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+   This function is required for alloca.c support on those systems.  */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define if you have alloca, as a function or macro.  */
+#define HAVE_ALLOCA 1
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix).  */
+/* #define HAVE_ALLOCA_H 1 */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define if on MINIX.  */
+/* #undef _MINIX */
+
+/* Define to `long' if <sys/types.h> doesn't define.  */
+/* #undef off_t */
+
+/* Define if the system does not provide POSIX.1 features except
+   with this defined.  */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define if you need to in order for stat and other things to work.  */
+/* #undef _POSIX_SOURCE */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+	STACK_DIRECTION > 0 => grows toward higher addresses
+	STACK_DIRECTION < 0 => grows toward lower addresses
+	STACK_DIRECTION = 0 => direction of growth unknown
+ */
+/* #undef STACK_DIRECTION */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+/* #define TIME_WITH_SYS_TIME 1 */
+
+/* Define if your <sys/time.h> declares struct tm.  */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define to the name of the distribution.  */
+#define PACKAGE "mg"
+
+/* Define to the version of the distribution.  */
+#define VERSION "1.2"
+
+/* Define to 1 if you have the valloc function.  */
+/* #define HAVE_VALLOC 1 */
+
+/* Define to 1 if ANSI function prototypes are usable.  */
+#define PROTOTYPES 1
+
+ 
+/* Define to 1 for better use of the debugging malloc library.  See
+   site ftp.antaire.com in antaire/src, file dmalloc/dmalloc.tar.gz.  */
+/* #undef WITH_DMALLOC */
+
+ 
+/* Define to 1 if GNU regex should be used instead of GNU rx.  */
+/* #undef WITH_REGEX */
+
+/* Some braindead header files do not have the requ'd function declarations */
+#define HAVE_FGETC_DECL 1
+#define HAVE_FREAD_DECL 1
+
+/* See if have this field in the prstatus_t structure */
+/* It stores the size of the process heap */
+/* #define HAVE_PR_BRKSIZE 1 */
+
+/* Define if you have the ftime function.  */
+/* #undef HAVE_FTIME */
+
+/* Define if you have the getpagesize function.  */
+/* #undef HAVE_GETPAGESIZE */
+
+/* Define if you have the getrusage function.  */
+/* #undef HAVE_GETRUSAGE */
+
+/* Define if you have the mallinfo function.  */
+/* #undef HAVE_MALLINFO */
+
+/* Define if you have the select function.  */
+#define HAVE_SELECT 1
+
+/* Define if you have the setbuffer function.  */
+/* #undef HAVE_SETBUFFER */
+
+/* Define if you have the strftime function.  */
+#define HAVE_STRFTIME 1
+
+/* Define if you have the strstr function.  */
+#define HAVE_STRSTR 1
+
+/* Define if you have the strtol function.  */
+#define HAVE_STRTOL 1
+
+/* Define if you have the times function.  */
+/* #define HAVE_TIMES 1 */
+
+/* Define if you have the valloc function.  */
+/* #define HAVE_VALLOC 1 */
+
+/* Define if you have the <dirent.h> header file.  */
+/* #define HAVE_DIRENT_H 1 */
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <memory.h> header file.  */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <ndir.h> header file.  */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/dir.h> header file.  */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/ndir.h> header file.  */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/procfs.h> header file.  */
+/* #define HAVE_SYS_PROCFS_H 1 */
+
+/* Define if you have the <sys/time.h> header file.  */
+/* #define HAVE_SYS_TIME_H 1 */
+
+/* Define if you have the <unistd.h> header file.  */
+/* #define HAVE_UNISTD_H 1 */
+
+/* Define if you have the m library (-lm).  */
+#define HAVE_LIBM 1
Index: /gsdl/trunk/gs2common/indexers/mgpp/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/mgpp/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/mgpp/winMake.bat	(revision 16561)
@@ -0,0 +1,54 @@
+@echo off
+
+set MAKE=nmake
+set MAKE_OPTIONS=/f
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto go
+if ""%1"" == ""install"" goto go
+if ""%1"" == ""clean"" goto go
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:go
+	if ""%2"" == ""javaonly"" goto java
+	if ""%2"" == ""nojava"" goto nojava
+
+:all
+	cd lib
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+
+	cd text
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+
+	cd java\org\greenstone\mgpp
+	call winMake.bat %1
+	cd ..\..\..\..
+
+	cd jni
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+	goto done
+
+:java
+	cd java\org\greenstone\mgpp
+	call winMake.bat %1
+	cd ..\..\..\..
+	goto done
+
+:nojava
+	cd lib
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+
+	cd text
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..
+	goto done
+	
+:done
+
Index: /gsdl/trunk/gs2common/indexers/packages/unac/.version
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/.version	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/.version	(revision 16561)
@@ -0,0 +1,1 @@
+1.7.0
Index: /gsdl/trunk/gs2common/indexers/packages/unac/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/Makefile.in	(revision 16561)
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 2000, 2001, 2002 Loic Dachary <loic@senga.org>
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# This Makefile produced by Katherine Don for the Greenstone Digial Library
+# software
+
+VERSION = @VERSION@
+
+CC = @CC@
+AR = ar
+RANLIB = @RANLIB@
+
+LIBICONV = @LIBICONV@
+LTLIBICONV = @LTLIBICONV@
+
+
+DEFS = @DEFS@ -DUNAC_VERSION=\"$(VERSION)\"
+INCLUDES = 
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES)  $(CFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+HEADERS = unac.h
+
+SOURCES = unac.c 
+
+OBJECTS = unac.o 
+
+
+all : libunac.a
+
+
+libunac.a: $(OBJECTS)
+	rm -f libunac.a
+	$(AR) cru libunac.a $(OBJECTS)
+	$(RANLIB) libunac.a
+
+
+install:
+
+clean:
+	rm -f *.o libunac.a
+
+distclean: clean
+	rm -f Makefile config.h config.log config.status
+
+
Index: /gsdl/trunk/gs2common/indexers/packages/unac/aclocal.m4
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/aclocal.m4	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/aclocal.m4	(revision 16561)
@@ -0,0 +1,5249 @@
+# aclocal.m4 generated automatically by aclocal 1.5
+
+# Copyright 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+dnl Copyright (C) 2000, 2001, 2002 Loic Dachary <loic@senga.org>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+dnl
+dnl Local autoconf definitions. Try to follow the guidelines of the autoconf
+dnl macro repository so that integration in the repository is easy.
+dnl To submit a macro to the repository send the macro (one macro per mail)
+dnl to Peter Simons <simons@cryp.to>.
+dnl The repository itself is at httphttp://savannah.gnu.org/projects/ac-archive/
+dnl
+
+dnl @synopsis AC_MANDATORY_HEADER(HEADER-FILE, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl
+dnl Same semantic as AC_CHECK_HEADER except that it aborts the configuration
+dnl script if the header file is not found.
+dnl
+dnl @author Loic Dachary <loic@senga.org>
+dnl
+AC_DEFUN([AC_MANDATORY_HEADER],
+[dnl Do the transliteration at runtime so arg 1 can be a shell variable.
+ac_safe=`echo "$1" | sed 'y%./+-%__p_%'`
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(ac_cv_header_$ac_safe,
+[AC_TRY_CPP([#include <$1>], eval "ac_cv_header_$ac_safe=yes",
+  eval "ac_cv_header_$ac_safe=no")])dnl
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  AC_MSG_RESULT(yes)
+  ifelse([$2], , :, [$2])
+else
+  AC_MSG_ERROR(header not found check config.log)
+ifelse([$3], , , [$3
+])dnl
+fi
+])
+
+dnl @synopsis AC_MANDATORY_HEADERS(HEADER-FILE... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl
+dnl Same semantic as AC_CHECK_HEADERS except that it aborts the configuration
+dnl script if one of the headers file is not found.
+dnl
+dnl @author Loic Dachary <loic@senga.org>
+dnl 
+AC_DEFUN([AC_MANDATORY_HEADERS],
+[for ac_hdr in $1
+do
+AC_MANDATORY_HEADER($ac_hdr,
+[changequote(, )dnl
+  ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+changequote([, ])dnl
+  AC_DEFINE_UNQUOTED($ac_tr_hdr) $2], $3)dnl
+done
+])
+
+dnl @synopsis AC_MANDATORY_LIB(LIBRARY, FUNCTION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]])
+dnl
+dnl Same semantic as AC_CHECK_LIB except that it aborts the configuration
+dnl script if the library is not found or compilation fails.
+dnl
+dnl @author Loic Dachary <loic@senga.org>
+dnl
+AC_DEFUN([AC_MANDATORY_LIB],
+[AC_MSG_CHECKING([for $2 in -l$1])
+dnl Use a cache variable name containing both the library and function name,
+dnl because the test really is for library $1 defining function $2, not
+dnl just for library $1.  Separate tests with the same $1 and different $2s
+dnl may have different results.
+ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
+AC_CACHE_VAL(ac_cv_lib_$ac_lib_var,
+[ac_save_LIBS="$LIBS"
+LIBS="-l$1 $5 $LIBS"
+AC_TRY_LINK(dnl
+ifelse(AC_LANG, [FORTRAN77], ,
+ifelse([$2], [main], , dnl Avoid conflicting decl of main.
+[/* Override any gcc2 internal prototype to avoid an error.  */
+]ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus
+extern "C"
+#endif
+])dnl
+[/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $2();
+])),
+	    [$2()],
+	    eval "ac_cv_lib_$ac_lib_var=yes",
+	    eval "ac_cv_lib_$ac_lib_var=no")
+LIBS="$ac_save_LIBS"
+])dnl
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  AC_MSG_RESULT(yes)
+  ifelse([$3], ,
+[changequote(, )dnl
+  ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+changequote([, ])dnl
+  AC_DEFINE_UNQUOTED($ac_tr_lib)
+  LIBS="-l$1 $LIBS"
+], [$3])
+else
+  AC_MSG_ERROR(library not found check config.log)
+ifelse([$4], , , [$4
+])dnl
+fi
+])
+
+dnl @synopsis AC_COMPILE_WARNINGS
+dnl
+dnl Set the maximum warning verbosity according to compiler used.
+dnl Currently supports g++ and gcc.
+dnl This macro must be put after AC_PROG_CC and AC_PROG_CXX in
+dnl configure.in
+dnl
+dnl @author Loic Dachary <loic@senga.org>
+dnl
+
+AC_DEFUN(AC_COMPILE_WARNINGS,
+[AC_MSG_CHECKING(maximum warning verbosity option)
+if test -n "$CXX"
+then
+  if test "$GXX" = "yes"
+  then
+    ac_compile_warnings_opt='-Wall -Woverloaded-virtual'
+  fi
+  CXXFLAGS="$CXXFLAGS $ac_compile_warnings_opt"
+  ac_compile_warnings_msg="$ac_compile_warnings_opt for C++"
+fi
+
+if test -n "$CC"
+then
+  if test "$GCC" = "yes"
+  then
+    ac_compile_warnings_opt='-Wall -Wmissing-declarations -Wmissing-prototypes'
+  fi
+  CFLAGS="$CFLAGS $ac_compile_warnings_opt"
+  ac_compile_warnings_msg="$ac_compile_warnings_msg $ac_compile_warnings_opt for C"
+fi
+AC_MSG_RESULT($ac_compile_warnings_msg)
+unset ac_compile_warnings_msg
+unset ac_compile_warnings_opt
+])
+
+
+# Do all the work for Automake.  This macro actually does too much --
+# some checks are only needed if your package does certain things.
+# But this isn't really a big deal.
+
+# serial 5
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# We require 2.13 because we rely on SHELL being computed by configure.
+AC_PREREQ([2.13])
+
+# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
+# -----------------------------------------------------------
+# If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED.
+# The purpose of this macro is to provide the user with a means to
+# check macros which are provided without letting her know how the
+# information is coded.
+# If this macro is not defined by Autoconf, define it here.
+ifdef([AC_PROVIDE_IFELSE],
+      [],
+      [define([AC_PROVIDE_IFELSE],
+              [ifdef([AC_PROVIDE_$1],
+                     [$2], [$3])])])
+
+
+# AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE])
+# ----------------------------------------------
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_REQUIRE([AC_PROG_INSTALL])dnl
+# test to see if srcdir already configured
+if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run \"make distclean\" there first])
+fi
+
+# Define the identity of the package.
+PACKAGE=$1
+AC_SUBST(PACKAGE)dnl
+VERSION=$2
+AC_SUBST(VERSION)dnl
+ifelse([$3],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])
+
+# Autoconf 2.50 wants to disallow AM_ names.  We explicitly allow
+# the ones we care about.
+ifdef([m4_pattern_allow],
+      [m4_pattern_allow([^AM_[A-Z]+FLAGS])])dnl
+
+# Autoconf 2.50 always computes EXEEXT.  However we need to be
+# compatible with 2.13, for now.  So we always define EXEEXT, but we
+# don't compute it.
+AC_SUBST(EXEEXT)
+# Similar for OBJEXT -- only we only use OBJEXT if the user actually
+# requests that it be used.  This is a bit dumb.
+: ${OBJEXT=o}
+AC_SUBST(OBJEXT)
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal)
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake)
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_MISSING_PROG(AMTAR, tar)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_PROVIDE_IFELSE([AC_PROG_][CC],
+                  [_AM_DEPENDENCIES(CC)],
+                  [define([AC_PROG_][CC],
+                          defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_][CXX],
+                  [_AM_DEPENDENCIES(CXX)],
+                  [define([AC_PROG_][CXX],
+                          defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+# serial 3
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+
+# serial 2
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  am_backtick='`'
+  AC_MSG_WARN([${am_backtick}missing' script is too old or missing])
+fi
+])
+
+# AM_AUX_DIR_EXPAND
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND], [
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd`
+])
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# serial 4						-*- Autoconf -*-
+
+
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+
+# _AM_DEPENDENCIES(NAME)
+# ---------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX" or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc']
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    echo '#include "conftest.h"' > conftest.c
+    echo 'int i;' > conftest.h
+    echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=conftest.c object=conftest.o \
+       depfile=conftest.Po tmpdepfile=conftest.TPo \
+       $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 &&
+       grep conftest.h conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      am_cv_$1_dependencies_compiler_type=$depmode
+      break
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+$1DEPMODE="depmode=$am_cv_$1_dependencies_compiler_type"
+AC_SUBST([$1DEPMODE])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[rm -f .deps 2>/dev/null
+mkdir .deps 2>/dev/null
+if test -d .deps; then
+  DEPDIR=.deps
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  DEPDIR=_deps
+fi
+rmdir .deps 2>/dev/null
+AC_SUBST(DEPDIR)
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking Speeds up one-time builds
+  --enable-dependency-tracking  Do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+pushdef([subst], defn([AC_SUBST]))
+subst(AMDEPBACKSLASH)
+popdef([subst])
+])
+
+# Generate code to set up dependency tracking.
+# This macro should only be invoked once -- use via AC_REQUIRE.
+# Usage:
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],[
+AC_OUTPUT_COMMANDS([
+test x"$AMDEP_TRUE" != x"" ||
+for mf in $CONFIG_FILES; do
+  case "$mf" in
+  Makefile) dirpart=.;;
+  */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;;
+  *) continue;;
+  esac
+  grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue
+  # Extract the definition of DEP_FILES from the Makefile without
+  # running `make'.
+  DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n -e '/^U = / s///p' < "$mf"`
+  test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR"
+  # We invoke sed twice because it is the simplest approach to
+  # changing $(DEPDIR) to its actual value in the expansion.
+  for file in `sed -n -e '
+    /^DEP_FILES = .*\\\\$/ {
+      s/^DEP_FILES = //
+      :loop
+	s/\\\\$//
+	p
+	n
+	/\\\\$/ b loop
+      p
+    }
+    /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`echo "$file" | sed -e 's|/[^/]*$||'`
+    $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+], [AMDEP_TRUE="$AMDEP_TRUE"
+ac_aux_dir="$ac_aux_dir"])])
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+doit:
+	@echo done
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include='#'
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote='"'
+      _am_result=BSD
+   fi
+fi
+AC_SUBST(am__include)
+AC_SUBST(am__quote)
+AC_MSG_RESULT($_am_result)
+rm -f confinc confmf
+])
+
+# serial 3
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+#
+# FIXME: Once using 2.50, use this:
+# m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_DEFUN([AM_CONDITIONAL],
+[ifelse([$1], [TRUE],
+        [errprint(__file__:__line__: [$0: invalid condition: $1
+])dnl
+m4exit(1)])dnl
+ifelse([$1], [FALSE],
+       [errprint(__file__:__line__: [$0: invalid condition: $1
+])dnl
+m4exit(1)])dnl
+AC_SUBST([$1_TRUE])
+AC_SUBST([$1_FALSE])
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi])
+
+# Like AC_CONFIG_HEADER, but automatically create stamp file.
+
+# serial 3
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  We must strip everything past the first ":",
+# and everything past the last "/".
+
+AC_PREREQ([2.12])
+
+AC_DEFUN([AM_CONFIG_HEADER],
+[ifdef([AC_FOREACH],dnl
+	 [dnl init our file count if it isn't already
+	 m4_ifndef([_AM_Config_Header_Index], m4_define([_AM_Config_Header_Index], [0]))
+	 dnl prepare to store our destination file list for use in config.status
+	 AC_FOREACH([_AM_File], [$1],
+		    [m4_pushdef([_AM_Dest], m4_patsubst(_AM_File, [:.*]))
+		    m4_define([_AM_Config_Header_Index], m4_incr(_AM_Config_Header_Index))
+		    dnl and add it to the list of files AC keeps track of, along
+		    dnl with our hook
+		    AC_CONFIG_HEADERS(_AM_File,
+dnl COMMANDS, [, INIT-CMDS]
+[# update the timestamp
+echo timestamp >"AS_ESCAPE(_AM_DIRNAME(]_AM_Dest[))/stamp-h]_AM_Config_Header_Index["
+][$2]m4_ifval([$3], [, [$3]]))dnl AC_CONFIG_HEADERS
+		    m4_popdef([_AM_Dest])])],dnl
+[AC_CONFIG_HEADER([$1])
+  AC_OUTPUT_COMMANDS(
+   ifelse(patsubst([$1], [[^ ]], []),
+	  [],
+	  [test -z "$CONFIG_HEADERS" || echo timestamp >dnl
+	   patsubst([$1], [^\([^:]*/\)?.*], [\1])stamp-h]),dnl
+[am_indx=1
+for am_file in $1; do
+  case " \$CONFIG_HEADERS " in
+  *" \$am_file "*)
+    am_dir=\`echo \$am_file |sed 's%:.*%%;s%[^/]*\$%%'\`
+    if test -n "\$am_dir"; then
+      am_tmpdir=\`echo \$am_dir |sed 's%^\(/*\).*\$%\1%'\`
+      for am_subdir in \`echo \$am_dir |sed 's%/% %'\`; do
+        am_tmpdir=\$am_tmpdir\$am_subdir/
+        if test ! -d \$am_tmpdir; then
+          mkdir \$am_tmpdir
+        fi
+      done
+    fi
+    echo timestamp > "\$am_dir"stamp-h\$am_indx
+    ;;
+  esac
+  am_indx=\`expr \$am_indx + 1\`
+done])
+])]) # AM_CONFIG_HEADER
+
+# _AM_DIRNAME(PATH)
+# -----------------
+# Like AS_DIRNAME, only do it during macro expansion
+AC_DEFUN([_AM_DIRNAME],
+       [m4_if(m4_regexp([$1], [^.*[^/]//*[^/][^/]*/*$]), -1,
+	      m4_if(m4_regexp([$1], [^//\([^/]\|$\)]), -1,
+		    m4_if(m4_regexp([$1], [^/.*]), -1,
+			  [.],
+			  m4_patsubst([$1], [^\(/\).*], [\1])),
+		    m4_patsubst([$1], [^\(//\)\([^/].*\|$\)], [\1])),
+	      m4_patsubst([$1], [^\(.*[^/]\)//*[^/][^/]*/*$], [\1]))[]dnl
+]) # _AM_DIRNAME
+
+# libtool.m4 - Configure libtool for the host system. -*-Shell-script-*-
+
+# serial 46 AC_PROG_LIBTOOL
+
+AC_DEFUN([AC_PROG_LIBTOOL],
+[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+# Prevent multiple expansion
+define([AC_PROG_LIBTOOL], [])
+])
+
+AC_DEFUN([AC_LIBTOOL_SETUP],
+[AC_PREREQ(2.13)dnl
+AC_REQUIRE([AC_ENABLE_SHARED])dnl
+AC_REQUIRE([AC_ENABLE_STATIC])dnl
+AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_LD])dnl
+AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl
+AC_REQUIRE([AC_PROG_NM])dnl
+AC_REQUIRE([LT_AC_PROG_SED])dnl
+
+AC_REQUIRE([AC_PROG_LN_S])dnl
+AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl
+AC_REQUIRE([AC_OBJEXT])dnl
+AC_REQUIRE([AC_EXEEXT])dnl
+dnl
+
+_LT_AC_PROG_ECHO_BACKSLASH
+# Only perform the check for file, if the check method requires it
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    AC_PATH_MAGIC
+  fi
+  ;;
+esac
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+AC_CHECK_TOOL(STRIP, strip, :)
+
+ifdef([AC_PROVIDE_AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no)
+ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL],
+enable_win32_dll=yes, enable_win32_dll=no)
+
+AC_ARG_ENABLE(libtool-lock,
+  [  --disable-libtool-lock  avoid locking (might break parallel builds)])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -32"
+      ;;
+    *N32*)
+      LD="${LD-ld} -n32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -64"
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_SAVE
+     AC_LANG_C
+     AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_RESTORE])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+
+ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL],
+[*-*-cygwin* | *-*-mingw* | *-*-pw32*)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+
+  # recent cygwin and mingw systems supply a stub DllMain which the user
+  # can override, but on older systems we have to supply one
+  AC_CACHE_CHECK([if libtool should supply DllMain function], lt_cv_need_dllmain,
+    [AC_TRY_LINK([],
+      [extern int __attribute__((__stdcall__)) DllMain(void*, int, void*);
+      DllMain (0, 0, 0);],
+      [lt_cv_need_dllmain=no],[lt_cv_need_dllmain=yes])])
+
+  case $host/$CC in
+  *-*-cygwin*/gcc*-mno-cygwin*|*-*-mingw*)
+    # old mingw systems require "-dll" to link a DLL, while more recent ones
+    # require "-mdll"
+    SAVE_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS -mdll"
+    AC_CACHE_CHECK([how to link DLLs], lt_cv_cc_dll_switch,
+      [AC_TRY_LINK([], [], [lt_cv_cc_dll_switch=-mdll],[lt_cv_cc_dll_switch=-dll])])
+    CFLAGS="$SAVE_CFLAGS" ;;
+  *-*-cygwin* | *-*-pw32*)
+    # cygwin systems need to pass --dll to the linker, and not link
+    # crt.o which will require a WinMain@16 definition.
+    lt_cv_cc_dll_switch="-Wl,--dll -nostartfiles" ;;
+  esac
+  ;;
+  ])
+esac
+
+_LT_AC_LTCONFIG_HACK
+
+])
+
+# AC_LIBTOOL_HEADER_ASSERT
+# ------------------------
+AC_DEFUN([AC_LIBTOOL_HEADER_ASSERT],
+[AC_CACHE_CHECK([whether $CC supports assert without backlinking],
+    [lt_cv_func_assert_works],
+    [case $host in
+    *-*-solaris*)
+      if test "$GCC" = yes && test "$with_gnu_ld" != yes; then
+        case `$CC --version 2>/dev/null` in
+        [[12]].*) lt_cv_func_assert_works=no ;;
+        *)        lt_cv_func_assert_works=yes ;;
+        esac
+      fi
+      ;;
+    esac])
+
+if test "x$lt_cv_func_assert_works" = xyes; then
+  AC_CHECK_HEADERS(assert.h)
+fi
+])# AC_LIBTOOL_HEADER_ASSERT
+
+# _LT_AC_CHECK_DLFCN
+# --------------------
+AC_DEFUN([_LT_AC_CHECK_DLFCN],
+[AC_CHECK_HEADERS(dlfcn.h)
+])# _LT_AC_CHECK_DLFCN
+
+# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
+# ---------------------------------
+AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE],
+[AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_NM])
+AC_REQUIRE([AC_OBJEXT])
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [dnl
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+lt_cv_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*) # Its linker distinguishes data from code symbols
+  lt_cv_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern char \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+  lt_cv_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+solaris* | sysv5*)
+  symcode='[[BDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $host_os in
+mingw*)
+  opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then
+  symcode='[[ABCDGISTW]]'
+fi
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Write the raw and C identifiers.
+lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ 	]]\($symcode$symcode*\)[[ 	]][[ 	]]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+  rm -f conftest*
+  cat > conftest.$ac_ext <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if egrep ' nm_test_var$' "$nlist" >/dev/null; then
+	if egrep ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_global_symbol_to_cdecl"' < "$nlist" >> conftest.$ac_ext'
+
+	  cat <<EOF >> conftest.$ac_ext
+#if defined (__STDC__) && __STDC__
+# define lt_ptr void *
+#else
+# define lt_ptr char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr address;
+}
+lt_preloaded_symbols[[]] =
+{
+EOF
+	  sed "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (lt_ptr) \&\2},/" < "$nlist" >> conftest.$ac_ext
+	  cat <<\EOF >> conftest.$ac_ext
+  {0, (lt_ptr) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  save_LIBS="$LIBS"
+	  save_CFLAGS="$CFLAGS"
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$no_builtin_flag"
+	  if AC_TRY_EVAL(ac_link) && test -s conftest; then
+	    pipe_works=yes
+	  fi
+	  LIBS="$save_LIBS"
+	  CFLAGS="$save_CFLAGS"
+	else
+	  echo "cannot find nm_test_func in $nlist" >&AC_FD_CC
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&AC_FD_CC
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AC_FD_CC
+    fi
+  else
+    echo "$progname: failed program was:" >&AC_FD_CC
+    cat conftest.$ac_ext >&5
+  fi
+  rm -f conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+global_symbol_pipe="$lt_cv_sys_global_symbol_pipe"
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  global_symbol_to_cdecl=
+  global_symbol_to_c_name_address=
+else
+  global_symbol_to_cdecl="$lt_cv_global_symbol_to_cdecl"
+  global_symbol_to_c_name_address="$lt_cv_global_symbol_to_c_name_address"
+fi
+if test -z "$global_symbol_pipe$global_symbol_to_cdec$global_symbol_to_c_name_address";
+then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
+
+# _LT_AC_LIBTOOL_SYS_PATH_SEPARATOR
+# ---------------------------------
+AC_DEFUN([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR],
+[# Find the correct PATH separator.  Usually this is `:', but
+# DJGPP uses `;' like DOS.
+if test "X${PATH_SEPARATOR+set}" != Xset; then
+  UNAME=${UNAME-`uname 2>/dev/null`}
+  case X$UNAME in
+    *-DOS) lt_cv_sys_path_separator=';' ;;
+    *)     lt_cv_sys_path_separator=':' ;;
+  esac
+  PATH_SEPARATOR=$lt_cv_sys_path_separator
+fi
+])# _LT_AC_LIBTOOL_SYS_PATH_SEPARATOR
+
+# _LT_AC_PROG_ECHO_BACKSLASH
+# --------------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH],
+[ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+			      [AC_DIVERT_PUSH(NOTICE)])
+_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+  ;;
+esac
+
+echo=${ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X[$]1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+if test -z "$ECHO"; then
+if test "X${echo_test_string+set}" != Xset; then
+# find a string as large as possible, as long as the shell can cope with it
+  for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+       echo_test_string="`eval $cmd`" &&
+       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null
+    then
+      break
+    fi
+  done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+   echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+   test "X$echo_testing_string" = "X$echo_test_string"; then
+  :
+else
+  # The Solaris, AIX, and Digital Unix default echo programs unquote
+  # backslashes.  This makes it impossible to quote backslashes using
+  #   echo "$something" | sed 's/\\/\\\\/g'
+  #
+  # So, first we look for a working echo in the user's PATH.
+
+  IFS="${IFS= 	}"; save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for dir in $PATH /usr/ucb; do
+    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      echo="$dir/echo"
+      break
+    fi
+  done
+  IFS="$save_ifs"
+
+  if test "X$echo" = Xecho; then
+    # We didn't find a better echo, so look for alternatives.
+    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      # This shell has a builtin print -r that does the trick.
+      echo='print -r'
+    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+	 test "X$CONFIG_SHELL" != X/bin/ksh; then
+      # If we have ksh, try running configure again with it.
+      ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+      export ORIGINAL_CONFIG_SHELL
+      CONFIG_SHELL=/bin/ksh
+      export CONFIG_SHELL
+      exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+    else
+      # Try using printf.
+      echo='printf %s\n'
+      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+	 echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+	 test "X$echo_testing_string" = "X$echo_test_string"; then
+	# Cool, printf works
+	:
+      elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+	export CONFIG_SHELL
+	SHELL="$CONFIG_SHELL"
+	export SHELL
+	echo="$CONFIG_SHELL [$]0 --fallback-echo"
+      elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	echo="$CONFIG_SHELL [$]0 --fallback-echo"
+      else
+	# maybe with a smaller string...
+	prev=:
+
+	for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+	  if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null
+	  then
+	    break
+	  fi
+	  prev="$cmd"
+	done
+
+	if test "$prev" != 'sed 50q "[$]0"'; then
+	  echo_test_string=`eval $prev`
+	  export echo_test_string
+	  exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+	else
+	  # Oops.  We lost completely, so just stick with echo.
+	  echo=echo
+	fi
+      fi
+    fi
+  fi
+fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+ECHO=$echo
+if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+   ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(ECHO)
+AC_DIVERT_POP
+])# _LT_AC_PROG_ECHO_BACKSLASH
+
+# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                           ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ------------------------------------------------------------------
+AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF],
+[if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}]
+EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_unknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_AC_TRY_DLOPEN_SELF
+
+# AC_LIBTOOL_DLOPEN_SELF
+# -------------------
+AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF],
+[if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+          [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+            [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"],
+	[AC_CHECK_FUNC([dlopen],
+	      [lt_cv_dlopen="dlopen"],
+	  [AC_CHECK_LIB([dl], [dlopen],
+	        [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+	    [AC_CHECK_LIB([svld], [dlopen],
+	          [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+	      [AC_CHECK_LIB([dld], [dld_link],
+	            [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"])
+	      ])
+	    ])
+	  ])
+	])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+	  lt_cv_dlopen_self, [dnl
+	  _LT_AC_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+	    lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+    	  lt_cv_dlopen_self_static, [dnl
+	  _LT_AC_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+	    lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+])# AC_LIBTOOL_DLOPEN_SELF
+
+AC_DEFUN([_LT_AC_LTCONFIG_HACK],
+[AC_REQUIRE([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])dnl
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+sed_quote_subst='s/\([[\\"\\`$\\\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([[\\"\\`\\\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Constants:
+rm="rm -f"
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except M$VC,
+# which needs '.lib').
+libext=a
+ltmain="$ac_aux_dir/ltmain.sh"
+ofile="$default_ofile"
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+need_locks="$enable_libtool_lock"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+test -z "$AS" && AS=as
+test -z "$CC" && CC=cc
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$LD" && LD=ld
+test -z "$LN_S" && LN_S="ln -s"
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+test -z "$NM" && NM=nm
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$RANLIB" && RANLIB=:
+test -z "$STRIP" && STRIP=:
+test -z "$ac_objext" && ac_objext=o
+
+if test x"$host" != x"$build"; then
+  ac_tool_prefix=${host_alias}-
+else
+  ac_tool_prefix=
+fi
+
+# Transform linux* to *-*-linux-gnu*, to support old configure scripts.
+case $host_os in
+linux-gnu*) ;;
+linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'`
+esac
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds"
+    ;;
+  *)
+    old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+# Allow CC to be a program name with arguments.
+set dummy $CC
+compiler="[$]2"
+
+AC_MSG_CHECKING([for objdir])
+rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+AC_MSG_RESULT($objdir)
+
+
+AC_ARG_WITH(pic,
+[  --with-pic              try to use only PIC/non-PIC objects [default=use both]],
+pic_mode="$withval", pic_mode=default)
+test -z "$pic_mode" && pic_mode=default
+
+# We assume here that the value for lt_cv_prog_cc_pic will not be cached
+# in isolation, and that seeing it set (from the cache) indicates that
+# the associated values are set (in the cache) correctly too.
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+AC_CACHE_VAL(lt_cv_prog_cc_pic,
+[ lt_cv_prog_cc_pic=
+  lt_cv_prog_cc_shlib=
+  lt_cv_prog_cc_wl=
+  lt_cv_prog_cc_static=
+  lt_cv_prog_cc_no_builtin=
+  lt_cv_prog_cc_can_build_shared=$can_build_shared
+
+  if test "$GCC" = yes; then
+    lt_cv_prog_cc_wl='-Wl,'
+    lt_cv_prog_cc_static='-static'
+
+    case $host_os in
+    aix*)
+      # Below there is a dirty hack to force normal static linking with -ldl
+      # The problem is because libdl dynamically linked with both libc and
+      # libC (AIX C++ library), which obviously doesn't included in libraries
+      # list by gcc. This cause undefined symbols with -static flags.
+      # This hack allows C programs to be linked with "-static -ldl", but
+      # not sure about C++ programs.
+      lt_cv_prog_cc_static="$lt_cv_prog_cc_static ${lt_cv_prog_cc_wl}-lC"
+      ;;
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_cv_prog_cc_pic='-m68020 -resident32 -malways-restore-a4'
+      ;;
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_cv_prog_cc_pic='-fno-common'
+      ;;
+    cygwin* | mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_cv_prog_cc_pic='-DDLL_EXPORT'
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	 lt_cv_prog_cc_pic=-Kconform_pic
+      fi
+      ;;
+    *)
+      lt_cv_prog_cc_pic='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for PIC flags for the system compiler.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      lt_cv_prog_cc_wl='-Wl,'
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_cv_prog_cc_static='-Bstatic'
+      else
+	lt_cv_prog_cc_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      # Is there a better lt_cv_prog_cc_static that works with the bundled CC?
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static="${lt_cv_prog_cc_wl}-a ${lt_cv_prog_cc_wl}archive"
+      lt_cv_prog_cc_pic='+Z'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static='-non_shared'
+      # PIC (with -KPIC) is the default.
+      ;;
+
+    cygwin* | mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_cv_prog_cc_pic='-DDLL_EXPORT'
+      ;;
+
+    newsos6)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      # All OSF/1 code is PIC.
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      lt_cv_prog_cc_pic='-Kpic'
+      lt_cv_prog_cc_static='-dn'
+      lt_cv_prog_cc_shlib='-belf'
+      ;;
+
+    solaris*)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Wl,'
+      ;;
+
+    sunos4*)
+      lt_cv_prog_cc_pic='-PIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Qoption ld '
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Wl,'
+      ;;
+
+    uts4*)
+      lt_cv_prog_cc_pic='-pic'
+      lt_cv_prog_cc_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_cv_prog_cc_pic='-Kconform_pic'
+	lt_cv_prog_cc_static='-Bstatic'
+      fi
+      ;;
+
+    *)
+      lt_cv_prog_cc_can_build_shared=no
+      ;;
+    esac
+  fi
+])
+if test -z "$lt_cv_prog_cc_pic"; then
+  AC_MSG_RESULT([none])
+else
+  AC_MSG_RESULT([$lt_cv_prog_cc_pic])
+
+  # Check to make sure the pic_flag actually works.
+  AC_MSG_CHECKING([if $compiler PIC flag $lt_cv_prog_cc_pic works])
+  AC_CACHE_VAL(lt_cv_prog_cc_pic_works, [dnl
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $lt_cv_prog_cc_pic -DPIC"
+    AC_TRY_COMPILE([], [], [dnl
+      case $host_os in
+      hpux9* | hpux10* | hpux11*)
+	# On HP-UX, both CC and GCC only warn that PIC is supported... then
+	# they create non-PIC objects.  So, if there were any warnings, we
+	# assume that PIC is not supported.
+	if test -s conftest.err; then
+	  lt_cv_prog_cc_pic_works=no
+	else
+	  lt_cv_prog_cc_pic_works=yes
+	fi
+	;;
+      *)
+	lt_cv_prog_cc_pic_works=yes
+	;;
+      esac
+    ], [dnl
+      lt_cv_prog_cc_pic_works=no
+    ])
+    CFLAGS="$save_CFLAGS"
+  ])
+
+  if test "X$lt_cv_prog_cc_pic_works" = Xno; then
+    lt_cv_prog_cc_pic=
+    lt_cv_prog_cc_can_build_shared=no
+  else
+    lt_cv_prog_cc_pic=" $lt_cv_prog_cc_pic"
+  fi
+
+  AC_MSG_RESULT([$lt_cv_prog_cc_pic_works])
+fi
+
+# Check for any special shared library compilation flags.
+if test -n "$lt_cv_prog_cc_shlib"; then
+  AC_MSG_WARN([\`$CC' requires \`$lt_cv_prog_cc_shlib' to build shared libraries])
+  if echo "$old_CC $old_CFLAGS " | egrep -e "[[ 	]]$lt_cv_prog_cc_shlib[[ 	]]" >/dev/null; then :
+  else
+   AC_MSG_WARN([add \`$lt_cv_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure])
+    lt_cv_prog_cc_can_build_shared=no
+  fi
+fi
+
+AC_MSG_CHECKING([if $compiler static flag $lt_cv_prog_cc_static works])
+AC_CACHE_VAL([lt_cv_prog_cc_static_works], [dnl
+  lt_cv_prog_cc_static_works=no
+  save_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $lt_cv_prog_cc_static"
+  AC_TRY_LINK([], [], [lt_cv_prog_cc_static_works=yes])
+  LDFLAGS="$save_LDFLAGS"
+])
+
+# Belt *and* braces to stop my trousers falling down:
+test "X$lt_cv_prog_cc_static_works" = Xno && lt_cv_prog_cc_static=
+AC_MSG_RESULT([$lt_cv_prog_cc_static_works])
+
+pic_flag="$lt_cv_prog_cc_pic"
+special_shlib_compile_flags="$lt_cv_prog_cc_shlib"
+wl="$lt_cv_prog_cc_wl"
+link_static_flag="$lt_cv_prog_cc_static"
+no_builtin_flag="$lt_cv_prog_cc_no_builtin"
+can_build_shared="$lt_cv_prog_cc_can_build_shared"
+
+
+# Check to see if options -o and -c are simultaneously supported by compiler
+AC_MSG_CHECKING([if $compiler supports -c -o file.$ac_objext])
+AC_CACHE_VAL([lt_cv_compiler_c_o], [
+$rm -r conftest 2>/dev/null
+mkdir conftest
+cd conftest
+echo "int some_variable = 0;" > conftest.$ac_ext
+mkdir out
+# According to Tom Tromey, Ian Lance Taylor reported there are C compilers
+# that will create temporary files in the current directory regardless of
+# the output directory.  Thus, making CWD read-only will cause this test
+# to fail, enabling locking or at least warning the user not to do parallel
+# builds.
+chmod -w .
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -o out/conftest2.$ac_objext"
+compiler_c_o=no
+if { (eval echo configure:__oline__: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
+  # The compiler can only warn and ignore the option if not recognized
+  # So say no if there are warnings
+  if test -s out/conftest.err; then
+    lt_cv_compiler_c_o=no
+  else
+    lt_cv_compiler_c_o=yes
+  fi
+else
+  # Append any errors to the config.log.
+  cat out/conftest.err 1>&AC_FD_CC
+  lt_cv_compiler_c_o=no
+fi
+CFLAGS="$save_CFLAGS"
+chmod u+w .
+$rm conftest* out/*
+rmdir out
+cd ..
+rmdir conftest
+$rm -r conftest 2>/dev/null
+])
+compiler_c_o=$lt_cv_compiler_c_o
+AC_MSG_RESULT([$compiler_c_o])
+
+if test x"$compiler_c_o" = x"yes"; then
+  # Check to see if we can write to a .lo
+  AC_MSG_CHECKING([if $compiler supports -c -o file.lo])
+  AC_CACHE_VAL([lt_cv_compiler_o_lo], [
+  lt_cv_compiler_o_lo=no
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -c -o conftest.lo"
+  save_objext="$ac_objext"
+  ac_objext=lo
+  AC_TRY_COMPILE([], [int some_variable = 0;], [dnl
+    # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+    if test -s conftest.err; then
+      lt_cv_compiler_o_lo=no
+    else
+      lt_cv_compiler_o_lo=yes
+    fi
+  ])
+  ac_objext="$save_objext"
+  CFLAGS="$save_CFLAGS"
+  ])
+  compiler_o_lo=$lt_cv_compiler_o_lo
+  AC_MSG_RESULT([$compiler_o_lo])
+else
+  compiler_o_lo=no
+fi
+
+# Check to see if we can do hard links to lock some files if needed
+hard_links="nottested"
+if test "$compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([\`$CC' does not support \`-c -o', so \`make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+if test "$GCC" = yes; then
+  # Check to see if options -fno-rtti -fno-exceptions are supported by compiler
+  AC_MSG_CHECKING([if $compiler supports -fno-rtti -fno-exceptions])
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -c conftest.$ac_ext"
+  compiler_rtti_exceptions=no
+  AC_TRY_COMPILE([], [int some_variable = 0;], [dnl
+    # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+    if test -s conftest.err; then
+      compiler_rtti_exceptions=no
+    else
+      compiler_rtti_exceptions=yes
+    fi
+  ])
+  CFLAGS="$save_CFLAGS"
+  AC_MSG_RESULT([$compiler_rtti_exceptions])
+
+  if test "$compiler_rtti_exceptions" = "yes"; then
+    no_builtin_flag=' -fno-builtin -fno-rtti -fno-exceptions'
+  else
+    no_builtin_flag=' -fno-builtin'
+  fi
+fi
+
+# See if the linker supports building shared libraries.
+AC_MSG_CHECKING([whether the linker ($LD) supports shared libraries])
+
+allow_undefined_flag=
+no_undefined_flag=
+need_lib_prefix=unknown
+need_version=unknown
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+archive_cmds=
+archive_expsym_cmds=
+old_archive_from_new_cmds=
+old_archive_from_expsyms_cmds=
+export_dynamic_flag_spec=
+whole_archive_flag_spec=
+thread_safe_flag_spec=
+hardcode_into_libs=no
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+hardcode_shlibpath_var=unsupported
+runpath_var=
+link_all_deplibs=unknown
+always_export_symbols=no
+export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | sed '\''s/.* //'\'' | sort | uniq > $export_symbols'
+# include_expsyms should be a list of space-separated symbols to be *always*
+# included in the symbol list
+include_expsyms=
+# exclude_expsyms can be an egrep regular expression of symbols to exclude
+# it will be wrapped by ` (' and `)$', so one must not match beginning or
+# end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+# as well as any symbol that contains `d'.
+exclude_expsyms="_GLOBAL_OFFSET_TABLE_"
+# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+# platforms (ab)use it in PIC code, but their linkers get confused if
+# the symbol is explicitly referenced.  Since portable code cannot
+# rely on this symbol name, it's probably fine to never include it in
+# preloaded symbol tables.
+extract_expsyms_cmds=
+
+case $host_os in
+cygwin* | mingw* | pw32*)
+  # FIXME: the MSVC++ port hasn't been tested in a loooong time
+  # When not using gcc, we currently assume that we are using
+  # Microsoft Visual C++.
+  if test "$GCC" != yes; then
+    with_gnu_ld=no
+  fi
+  ;;
+openbsd*)
+  with_gnu_ld=no
+  ;;
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+  # If archive_cmds runs LD, not CC, wlarc should be empty
+  wlarc='${wl}'
+
+  # See if GNU ld supports shared libraries.
+  case $host_os in
+  aix3* | aix4* | aix5*)
+    # On AIX, the GNU linker is very broken
+    # Note:Check GNU linker on AIX 5-IA64 when/if it becomes available.
+    ld_shlibs=no
+    cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+    ;;
+
+  amigaos*)
+    archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+
+    # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+    # that the semantics of dynamic libraries on AmigaOS, at least up
+    # to version 4, is to share data among multiple programs linked
+    # with the same dynamic library.  Since this doesn't match the
+    # behavior of shared libraries on other platforms, we can use
+    # them.
+    ld_shlibs=no
+    ;;
+
+  beos*)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      allow_undefined_flag=unsupported
+      # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+      # support --undefined.  This deserves some investigation.  FIXME
+      archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec='-L$libdir'
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+
+    extract_expsyms_cmds='test -f $output_objdir/impgen.c || \
+      sed -e "/^# \/\* impgen\.c starts here \*\//,/^# \/\* impgen.c ends here \*\// { s/^# //;s/^# *$//; p; }" -e d < $''0 > $output_objdir/impgen.c~
+      test -f $output_objdir/impgen.exe || (cd $output_objdir && \
+      if test "x$HOST_CC" != "x" ; then $HOST_CC -o impgen impgen.c ; \
+      else $CC -o impgen impgen.c ; fi)~
+      $output_objdir/impgen $dir/$soroot > $output_objdir/$soname-def'
+
+    old_archive_from_expsyms_cmds='$DLLTOOL --as=$AS --dllname $soname --def $output_objdir/$soname-def --output-lib $output_objdir/$newlib'
+
+    # cygwin and mingw dlls have different entry points and sets of symbols
+    # to exclude.
+    # FIXME: what about values for MSVC?
+    dll_entry=__cygwin_dll_entry@12
+    dll_exclude_symbols=DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12~
+    case $host_os in
+    mingw*)
+      # mingw values
+      dll_entry=_DllMainCRTStartup@12
+      dll_exclude_symbols=DllMain@12,DllMainCRTStartup@12,DllEntryPoint@12~
+      ;;
+    esac
+
+    # mingw and cygwin differ, and it's simplest to just exclude the union
+    # of the two symbol sets.
+    dll_exclude_symbols=DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12,DllMainCRTStartup@12,DllEntryPoint@12
+
+    # recent cygwin and mingw systems supply a stub DllMain which the user
+    # can override, but on older systems we have to supply one (in ltdll.c)
+    if test "x$lt_cv_need_dllmain" = "xyes"; then
+      ltdll_obj='$output_objdir/$soname-ltdll.'"$ac_objext "
+      ltdll_cmds='test -f $output_objdir/$soname-ltdll.c || sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $''0 > $output_objdir/$soname-ltdll.c~
+	test -f $output_objdir/$soname-ltdll.$ac_objext || (cd $output_objdir && $CC -c $soname-ltdll.c)~'
+    else
+      ltdll_obj=
+      ltdll_cmds=
+    fi
+
+    # Extract the symbol export list from an `--export-all' def file,
+    # then regenerate the def file from the symbol export list, so that
+    # the compiled dll only exports the symbol export list.
+    # Be careful not to strip the DATA tag left be newer dlltools.
+    export_symbols_cmds="$ltdll_cmds"'
+      $DLLTOOL --export-all --exclude-symbols '$dll_exclude_symbols' --output-def $output_objdir/$soname-def '$ltdll_obj'$libobjs $convenience~
+      sed -e "1,/EXPORTS/d" -e "s/ @ [[0-9]]*//" -e "s/ *;.*$//" < $output_objdir/$soname-def > $export_symbols'
+
+    # If the export-symbols file already is a .def file (1st line
+    # is EXPORTS), use it as is.
+    # If DATA tags from a recent dlltool are present, honour them!
+    archive_expsym_cmds='if test "x`sed 1q $export_symbols`" = xEXPORTS; then
+	cp $export_symbols $output_objdir/$soname-def;
+      else
+	echo EXPORTS > $output_objdir/$soname-def;
+	_lt_hint=1;
+	cat $export_symbols | while read symbol; do
+	 set dummy \$symbol;
+	 case \[$]# in
+	   2) echo "   \[$]2 @ \$_lt_hint ; " >> $output_objdir/$soname-def;;
+	   4) echo "   \[$]2 \[$]3 \[$]4 ; " >> $output_objdir/$soname-def; _lt_hint=`expr \$_lt_hint - 1`;;
+	   *) echo "     \[$]2 @ \$_lt_hint \[$]3 ; " >> $output_objdir/$soname-def;;
+	 esac;
+	 _lt_hint=`expr 1 + \$_lt_hint`;
+	done;
+      fi~
+      '"$ltdll_cmds"'
+      $CC -Wl,--base-file,$output_objdir/$soname-base '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols '$dll_exclude_symbols' --def $output_objdir/$soname-def --base-file $output_objdir/$soname-base --output-exp $output_objdir/$soname-exp~
+      $CC -Wl,--base-file,$output_objdir/$soname-base $output_objdir/$soname-exp '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols '$dll_exclude_symbols' --def $output_objdir/$soname-def --base-file $output_objdir/$soname-base --output-exp $output_objdir/$soname-exp --output-lib $output_objdir/$libname.dll.a~
+      $CC $output_objdir/$soname-exp '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags'
+    ;;
+
+  netbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+      wlarc=
+    else
+      archive_cmds='$CC -shared -nodefaultlibs $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared -nodefaultlibs $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    fi
+    ;;
+
+  solaris* | sysv5*)
+    if $LD -v 2>&1 | egrep 'BFD 2\.8' > /dev/null; then
+      ld_shlibs=no
+      cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+    elif $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+  sunos4*)
+    archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+    wlarc=
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+  esac
+
+  if test "$ld_shlibs" = yes; then
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    case $host_os in
+    cygwin* | mingw* | pw32*)
+      # dlltool doesn't understand --whole-archive et. al.
+      whole_archive_flag_spec=
+      ;;
+    *)
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | egrep 'no-whole-archive' > /dev/null; then
+	whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+	whole_archive_flag_spec=
+      fi
+      ;;
+    esac
+  fi
+else
+  # PORTME fill in a description of your system's linker (not GNU ld)
+  case $host_os in
+  aix3*)
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+    archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+    # Note: this linker hardcodes the directories in LIBPATH if there
+    # are no directories specified by -L.
+    hardcode_minus_L=yes
+    if test "$GCC" = yes && test -z "$link_static_flag"; then
+      # Neither direct hardcoding nor static linking is supported with a
+      # broken collect2.
+      hardcode_direct=unsupported
+    fi
+    ;;
+
+  aix4* | aix5*)
+    if test "$host_cpu" = ia64; then
+      # On IA64, the linker does run time linking by default, so we don't
+      # have to do anything special.
+      aix_use_runtimelinking=no
+      exp_sym_flag='-Bexport'
+      no_entry_flag=""
+    else
+      aix_use_runtimelinking=no
+
+      # Test if we are trying to use run time linking or normal
+      # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+      # need to do runtime linking.
+      case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*)
+	for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	done
+      esac
+
+      exp_sym_flag='-bexport'
+      no_entry_flag='-bnoentry'
+    fi
+
+    # When large executables or shared objects are built, AIX ld can
+    # have problems creating the table of contents.  If linking a library
+    # or program results in "error TOC overflow" add -mminimal-toc to
+    # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+    # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+    hardcode_direct=yes
+    archive_cmds=''
+    hardcode_libdir_separator=':'
+    if test "$GCC" = yes; then
+      case $host_os in aix4.[[012]]|aix4.[[012]].*)
+	collect2name=`${CC} -print-prog-name=collect2`
+	if test -f "$collect2name" && \
+	  strings "$collect2name" | grep resolve_lib_name >/dev/null
+	then
+	  # We have reworked collect2
+	  hardcode_direct=yes
+	else
+	  # We have old collect2
+	  hardcode_direct=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L=yes
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_libdir_separator=
+	fi
+      esac
+
+      shared_flag='-shared'
+    else
+      # not using gcc
+      if test "$host_cpu" = ia64; then
+	shared_flag='${wl}-G'
+      else
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag='${wl}-G'
+	else
+	  shared_flag='${wl}-bM:SRE'
+	fi
+      fi
+    fi
+
+    # It seems that -bexpall can do strange things, so it is better to
+    # generate a list of symbols to export.
+    always_export_symbols=yes
+    if test "$aix_use_runtimelinking" = yes; then
+      # Warning - without using the other runtime loading flags (-brtl),
+      # -berok will link without error, but may produce a broken library.
+      allow_undefined_flag='-berok'
+      hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:/usr/lib:/lib'
+      archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+    else
+      if test "$host_cpu" = ia64; then
+	hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+	allow_undefined_flag="-z nodefs"
+	archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname ${wl}-h$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+      else
+	hardcode_libdir_flag_spec='${wl}-bnolibpath ${wl}-blibpath:$libdir:/usr/lib:/lib'
+	# Warning - without using the other run time loading flags,
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='${wl}-berok'
+	# This is a bit strange, but is similar to how AIX traditionally builds
+	# it's shared libraries.
+	archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"' ~$AR -crlo $objdir/$libname$release.a $objdir/$soname'
+      fi
+    fi
+    ;;
+
+  amigaos*)
+    archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    # see comment about different semantics on the GNU ld section
+    ld_shlibs=no
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec=' '
+    allow_undefined_flag=unsupported
+    # Tell ltmain to make .lib files, not .a files.
+    libext=lib
+    # FIXME: Setting linknames here is a bad hack.
+    archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | sed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+    # The linker will automatically build a .lib file if we build a DLL.
+    old_archive_from_new_cmds='true'
+    # FIXME: Should let the user specify the lib program.
+    old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs'
+    fix_srcfile_path='`cygpath -w "$srcfile"`'
+    ;;
+
+  darwin* | rhapsody*)
+    case "$host_os" in
+    rhapsody* | darwin1.[[012]])
+      allow_undefined_flag='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      allow_undefined_flag='-flat_namespace -undefined suppress'
+      ;;
+    esac
+    # FIXME: Relying on posixy $() will cause problems for
+    #        cross-compilation, but unfortunately the echo tests do not
+    #        yet detect zsh echo's removal of \ escapes.  Also zsh mangles
+    #	     `"' quotes if we put them in here... so don't!
+    archive_cmds='$nonopt $(test .$module = .yes && echo -bundle || echo -dynamiclib) $allow_undefined_flag -o $lib $libobjs $deplibs$linker_flags -install_name $rpath/$soname $verstring'
+    # We need to add '_' to the symbols in $export_symbols first
+    #archive_expsym_cmds="$archive_cmds"' && strip -s $export_symbols'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    whole_archive_flag_spec='-all_load $convenience'
+    ;;
+
+  freebsd1*)
+    ld_shlibs=no
+    ;;
+
+  # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+  # support.  Future versions do this automatically, but an explicit c++rt0.o
+  # does not break anything, and helps significantly (at the cost of a little
+  # extra space).
+  freebsd2.2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+  freebsd2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+  freebsd*)
+    archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  hpux9* | hpux10* | hpux11*)
+    case $host_os in
+    hpux9*) archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' ;;
+    *) archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' ;;
+    esac
+    hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+    hardcode_libdir_separator=:
+    hardcode_direct=yes
+    hardcode_minus_L=yes # Not in the search PATH, but as the default
+			 # location of the library.
+    export_dynamic_flag_spec='${wl}-E'
+    ;;
+
+  irix5* | irix6* | nonstopux*)
+    if test "$GCC" = yes; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+    else
+      archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    link_all_deplibs=yes
+    ;;
+
+  netbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+    else
+      archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+    fi
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  newsos6)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    hardcode_shlibpath_var=no
+    ;;
+
+  openbsd*)
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+    else
+      case "$host_os" in
+      openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_libdir_flag_spec='-R$libdir'
+        ;;
+      *)
+        archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+        ;;
+      esac
+    fi
+    ;;
+
+  os2*)
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    allow_undefined_flag=unsupported
+    archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+    old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+    ;;
+
+  osf3*)
+    if test "$GCC" = yes; then
+      allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+      archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+    else
+      allow_undefined_flag=' -expect_unresolved \*'
+      archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    ;;
+
+  osf4* | osf5*)	# as osf3* with the addition of -msym flag
+    if test "$GCC" = yes; then
+      allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+      archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    else
+      allow_undefined_flag=' -expect_unresolved \*'
+      archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      archive_expsym_cmds='for i in `cat $export_symbols`; do printf "-exported_symbol " >> $lib.exp; echo "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+      $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+      #Both c and cxx compiler support -rpath directly
+      hardcode_libdir_flag_spec='-rpath $libdir'
+    fi
+    hardcode_libdir_separator=:
+    ;;
+
+  sco3.2v5*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_shlibpath_var=no
+    runpath_var=LD_RUN_PATH
+    hardcode_runpath_var=yes
+    export_dynamic_flag_spec='${wl}-Bexport'
+    ;;
+
+  solaris*)
+    # gcc --version < 3.0 without binutils cannot create self contained
+    # shared libraries reliably, requiring libgcc.a to resolve some of
+    # the object symbols generated in some cases.  Libraries that use
+    # assert need libgcc.a to resolve __eprintf, for example.  Linking
+    # a copy of libgcc.a into every shared library to guarantee resolving
+    # such symbols causes other problems:  According to Tim Van Holder
+    # <tim.van.holder@pandora.be>, C++ libraries end up with a separate
+    # (to the application) exception stack for one thing.
+    no_undefined_flag=' -z defs'
+    if test "$GCC" = yes; then
+      case `$CC --version 2>/dev/null` in
+      [[12]].*)
+	cat <<EOF 1>&2
+
+*** Warning: Releases of GCC earlier than version 3.0 cannot reliably
+*** create self contained shared libraries on Solaris systems, without
+*** introducing a dependency on libgcc.a.  Therefore, libtool is disabling
+*** -no-undefined support, which will at least allow you to build shared
+*** libraries.  However, you may find that when you link such libraries
+*** into an application without using GCC, you have to manually add
+*** \`gcc --print-libgcc-file-name\` to the link command.  We urge you to
+*** upgrade to a newer version of GCC.  Another option is to rebuild your
+*** current GCC to use the GNU linker from GNU binutils 2.9.1 or newer.
+
+EOF
+        no_undefined_flag=
+	;;
+      esac
+    fi
+    # $CC -shared without GNU ld will not create a library from C++
+    # object files and a static libstdc++, better avoid it by now
+    archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_shlibpath_var=no
+    case $host_os in
+    solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+    *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+      whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;;
+    esac
+    link_all_deplibs=yes
+    ;;
+
+  sunos4*)
+    if test "x$host_vendor" = xsequent; then
+      # Use $CC to link under sequent, because it throws in some extra .o
+      # files that make .init and .fini sections work.
+      archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+    else
+      archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+    fi
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4)
+    case $host_vendor in
+      sni)
+        archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_direct=yes # is this really true???
+        ;;
+      siemens)
+        ## LD is ld it makes a PLAMLIB
+        ## CC just makes a GrossModule.
+        archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+        reload_cmds='$CC -r -o $output$reload_objs'
+        hardcode_direct=no
+        ;;
+      motorola)
+        archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+        ;;
+    esac
+    runpath_var='LD_RUN_PATH'
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4.3*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_shlibpath_var=no
+    export_dynamic_flag_spec='-Bexport'
+    ;;
+
+  sysv5*)
+    no_undefined_flag=' -z text'
+    # $CC -shared without GNU ld will not create a library from C++
+    # object files and a static libstdc++, better avoid it by now
+    archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+    hardcode_libdir_flag_spec=
+    hardcode_shlibpath_var=no
+    runpath_var='LD_RUN_PATH'
+    ;;
+
+  uts4*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  dgux*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4*MP*)
+    if test -d /usr/nec; then
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ld_shlibs=yes
+    fi
+    ;;
+
+  sysv4.2uw2*)
+    archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_minus_L=no
+    hardcode_shlibpath_var=no
+    hardcode_runpath_var=yes
+    runpath_var=LD_RUN_PATH
+    ;;
+
+  sysv5uw7* | unixware7*)
+    no_undefined_flag='${wl}-z ${wl}text'
+    if test "$GCC" = yes; then
+      archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+    else
+      archive_cmds='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+    fi
+    runpath_var='LD_RUN_PATH'
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    ld_shlibs=no
+    ;;
+  esac
+fi
+AC_MSG_RESULT([$ld_shlibs])
+test "$ld_shlibs" = no && can_build_shared=no
+
+# Check hardcoding attributes.
+AC_MSG_CHECKING([how to hardcode library paths into programs])
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" || \
+   test -n "$runpath_var"; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$hardcode_shlibpath_var" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+AC_MSG_RESULT([$hardcode_action])
+
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+  AC_MSG_RESULT([no])
+fi
+
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+# PORTME Fill in your ld.so characteristics
+AC_MSG_CHECKING([dynamic linker characteristics])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}.so$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}.so$major ${libname}${release}.so$versuffix $libname.so'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+	if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	     echo ' yes '
+	     echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	  :
+	else
+	  can_build_shared=no
+	fi
+	;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can
+    # not hardcode correct soname into executable. Probably we can
+    # add versioning support to collect2, so additional links can
+    # be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}.so$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}.so'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  export_dynamic_flag_spec=-rdynamic
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  need_version=no
+  need_lib_prefix=no
+  case $GCC,$host_os in
+  yes,cygwin*)
+    library_names_spec='$libname.dll.a'
+    soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | sed -e 's/[[.]]/-/g'`${versuffix}.dll'
+    postinstall_cmds='dlpath=`bash 2>&1 -c '\''. $dir/${file}i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog .libs/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`bash 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    ;;
+  yes,mingw*)
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[[.]]/-/g'`${versuffix}.dll'
+    sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | sed -e "s/^libraries://" -e "s/;/ /g" -e "s,=/,/,g"`
+    ;;
+  yes,pw32*)
+    library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll'
+    ;;
+  *)
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[[.]]/-/g'`${versuffix}.dll $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  # FIXME: Relying on posixy $() will cause problems for
+  #        cross-compilation, but unfortunately the echo tests do not
+  #        yet detect zsh echo's removal of \ escapes.
+  library_names_spec='${libname}${release}${versuffix}.$(test .$module = .yes && echo so || echo dylib) ${libname}${release}${major}.$(test .$module = .yes && echo so || echo dylib) ${libname}.$(test .$module = .yes && echo so || echo dylib)'
+  soname_spec='${libname}${release}${major}.$(test .$module = .yes && echo so || echo dylib)'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}.so$versuffix $libname.so$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  *)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so${major} ${libname}.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  dynamic_linker="$host_os dld.sl"
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  shlibpath_var=SHLIB_PATH
+  shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+  library_names_spec='${libname}${release}.sl$versuffix ${libname}${release}.sl$major $libname.sl'
+  soname_spec='${libname}${release}.sl$major'
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)          version_type=irix ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}.so$major'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so $libname.so'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 ") libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 ") libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 ") libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so ${libname}.so'
+    soname_spec='${libname}${release}.so$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case "$host_os" in
+    openbsd2.[[89]] | openbsd2.[[89]].*)
+      shlibpath_overrides_runpath=no
+      ;;
+    *)
+      shlibpath_overrides_runpath=yes
+      ;;
+    esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+os2*)
+  libname_spec='$name'
+  need_lib_prefix=no
+  library_names_spec='$libname.dll $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_version=no
+  soname_spec='${libname}${release}.so'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}.so$major'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname.so.$versuffix $libname.so.$major $libname.so'
+    soname_spec='$libname.so.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+# Report the final consequences.
+AC_MSG_CHECKING([if libtool supports shared libraries])
+AC_MSG_RESULT([$can_build_shared])
+
+AC_MSG_CHECKING([whether to build shared libraries])
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+
+aix4*)
+  if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+    test "$enable_shared" = yes && enable_static=no
+  fi
+  ;;
+esac
+AC_MSG_RESULT([$enable_shared])
+
+AC_MSG_CHECKING([whether to build static libraries])
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+AC_MSG_RESULT([$enable_static])
+
+if test "$hardcode_action" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+AC_LIBTOOL_DLOPEN_SELF
+
+if test "$enable_shared" = yes && test "$GCC" = yes; then
+  case $archive_cmds in
+  *'~'*)
+    # FIXME: we may have to deal with multi-command sequences.
+    ;;
+  '$CC '*)
+    # Test whether the compiler implicitly links with -lc since on some
+    # systems, -lgcc has to come before -lc. If gcc already passes -lc
+    # to ld, don't add -lc before -lgcc.
+    AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+    AC_CACHE_VAL([lt_cv_archive_cmds_need_lc],
+    [$rm conftest*
+    echo 'static int dummy;' > conftest.$ac_ext
+
+    if AC_TRY_EVAL(ac_compile); then
+      soname=conftest
+      lib=conftest
+      libobjs=conftest.$ac_objext
+      deplibs=
+      wl=$lt_cv_prog_cc_wl
+      compiler_flags=-v
+      linker_flags=-v
+      verstring=
+      output_objdir=.
+      libname=conftest
+      save_allow_undefined_flag=$allow_undefined_flag
+      allow_undefined_flag=
+      if AC_TRY_EVAL(archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1)
+      then
+	lt_cv_archive_cmds_need_lc=no
+      else
+	lt_cv_archive_cmds_need_lc=yes
+      fi
+      allow_undefined_flag=$save_allow_undefined_flag
+    else
+      cat conftest.err 1>&5
+    fi])
+    AC_MSG_RESULT([$lt_cv_archive_cmds_need_lc])
+    ;;
+  esac
+fi
+need_lc=${lt_cv_archive_cmds_need_lc-yes}
+
+# The second clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  :
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  test -f Makefile && make "$ltmain"
+fi
+
+if test -f "$ltmain"; then
+  trap "$rm \"${ofile}T\"; exit 1" 1 2 15
+  $rm -f "${ofile}T"
+
+  echo creating $ofile
+
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS SED \
+    AR AR_FLAGS CC LD LN_S NM SHELL \
+    reload_flag reload_cmds wl \
+    pic_flag link_static_flag no_builtin_flag export_dynamic_flag_spec \
+    thread_safe_flag_spec whole_archive_flag_spec libname_spec \
+    library_names_spec soname_spec \
+    RANLIB old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \
+    old_postuninstall_cmds archive_cmds archive_expsym_cmds postinstall_cmds \
+    postuninstall_cmds extract_expsyms_cmds old_archive_from_expsyms_cmds \
+    old_striplib striplib file_magic_cmd export_symbols_cmds \
+    deplibs_check_method allow_undefined_flag no_undefined_flag \
+    finish_cmds finish_eval global_symbol_pipe global_symbol_to_cdecl \
+    global_symbol_to_c_name_address \
+    hardcode_libdir_flag_spec hardcode_libdir_separator  \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    compiler_c_o compiler_o_lo need_locks exclude_expsyms include_expsyms; do
+
+    case $var in
+    reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    export_symbols_cmds | archive_cmds | archive_expsym_cmds | \
+    extract_expsyms_cmds | old_archive_from_expsyms_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    finish_cmds | sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  cat <<__EOF__ > "${ofile}T"
+#! $SHELL
+
+# `$echo "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996-2000 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A sed that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="${SED} -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$need_lc
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# The default C compiler.
+CC=$lt_CC
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_wl
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_pic_flag
+pic_mode=$pic_mode
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_compiler_c_o
+
+# Can we write directly to a .lo ?
+compiler_o_lo=$lt_compiler_o_lo
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_link_static_flag
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_no_builtin_flag
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# ### END LIBTOOL CONFIG
+
+__EOF__
+
+  case $host_os in
+  aix3*)
+    cat <<\EOF >> "${ofile}T"
+
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+EOF
+    ;;
+  esac
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | os2*)
+    cat <<'EOF' >> "${ofile}T"
+      # This is a source program that is used to create dlls on Windows
+      # Don't remove nor modify the starting and closing comments
+# /* ltdll.c starts here */
+# #define WIN32_LEAN_AND_MEAN
+# #include <windows.h>
+# #undef WIN32_LEAN_AND_MEAN
+# #include <stdio.h>
+#
+# #ifndef __CYGWIN__
+# #  ifdef __CYGWIN32__
+# #    define __CYGWIN__ __CYGWIN32__
+# #  endif
+# #endif
+#
+# #ifdef __cplusplus
+# extern "C" {
+# #endif
+# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+# #ifdef __cplusplus
+# }
+# #endif
+#
+# #ifdef __CYGWIN__
+# #include <cygwin/cygwin_dll.h>
+# DECLARE_CYGWIN_DLL( DllMain );
+# #endif
+# HINSTANCE __hDllInstance_base;
+#
+# BOOL APIENTRY
+# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
+# {
+#   __hDllInstance_base = hInst;
+#   return TRUE;
+# }
+# /* ltdll.c ends here */
+	# This is a source program that is used to create import libraries
+	# on Windows for dlls which lack them. Don't remove nor modify the
+	# starting and closing comments
+# /* impgen.c starts here */
+# /*   Copyright (C) 1999-2000 Free Software Foundation, Inc.
+#
+#  This file is part of GNU libtool.
+#
+#  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#  */
+#
+# #include <stdio.h>		/* for printf() */
+# #include <unistd.h>		/* for open(), lseek(), read() */
+# #include <fcntl.h>		/* for O_RDONLY, O_BINARY */
+# #include <string.h>		/* for strdup() */
+#
+# /* O_BINARY isn't required (or even defined sometimes) under Unix */
+# #ifndef O_BINARY
+# #define O_BINARY 0
+# #endif
+#
+# static unsigned int
+# pe_get16 (fd, offset)
+#      int fd;
+#      int offset;
+# {
+#   unsigned char b[2];
+#   lseek (fd, offset, SEEK_SET);
+#   read (fd, b, 2);
+#   return b[0] + (b[1]<<8);
+# }
+#
+# static unsigned int
+# pe_get32 (fd, offset)
+#     int fd;
+#     int offset;
+# {
+#   unsigned char b[4];
+#   lseek (fd, offset, SEEK_SET);
+#   read (fd, b, 4);
+#   return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# static unsigned int
+# pe_as32 (ptr)
+#      void *ptr;
+# {
+#   unsigned char *b = ptr;
+#   return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# int
+# main (argc, argv)
+#     int argc;
+#     char *argv[];
+# {
+#     int dll;
+#     unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
+#     unsigned long export_rva, export_size, nsections, secptr, expptr;
+#     unsigned long name_rvas, nexp;
+#     unsigned char *expdata, *erva;
+#     char *filename, *dll_name;
+#
+#     filename = argv[1];
+#
+#     dll = open(filename, O_RDONLY|O_BINARY);
+#     if (dll < 1)
+# 	return 1;
+#
+#     dll_name = filename;
+#
+#     for (i=0; filename[i]; i++)
+# 	if (filename[i] == '/' || filename[i] == '\\'  || filename[i] == ':')
+# 	    dll_name = filename + i +1;
+#
+#     pe_header_offset = pe_get32 (dll, 0x3c);
+#     opthdr_ofs = pe_header_offset + 4 + 20;
+#     num_entries = pe_get32 (dll, opthdr_ofs + 92);
+#
+#     if (num_entries < 1) /* no exports */
+# 	return 1;
+#
+#     export_rva = pe_get32 (dll, opthdr_ofs + 96);
+#     export_size = pe_get32 (dll, opthdr_ofs + 100);
+#     nsections = pe_get16 (dll, pe_header_offset + 4 +2);
+#     secptr = (pe_header_offset + 4 + 20 +
+# 	      pe_get16 (dll, pe_header_offset + 4 + 16));
+#
+#     expptr = 0;
+#     for (i = 0; i < nsections; i++)
+#     {
+# 	char sname[8];
+# 	unsigned long secptr1 = secptr + 40 * i;
+# 	unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
+# 	unsigned long vsize = pe_get32 (dll, secptr1 + 16);
+# 	unsigned long fptr = pe_get32 (dll, secptr1 + 20);
+# 	lseek(dll, secptr1, SEEK_SET);
+# 	read(dll, sname, 8);
+# 	if (vaddr <= export_rva && vaddr+vsize > export_rva)
+# 	{
+# 	    expptr = fptr + (export_rva - vaddr);
+# 	    if (export_rva + export_size > vaddr + vsize)
+# 		export_size = vsize - (export_rva - vaddr);
+# 	    break;
+# 	}
+#     }
+#
+#     expdata = (unsigned char*)malloc(export_size);
+#     lseek (dll, expptr, SEEK_SET);
+#     read (dll, expdata, export_size);
+#     erva = expdata - export_rva;
+#
+#     nexp = pe_as32 (expdata+24);
+#     name_rvas = pe_as32 (expdata+32);
+#
+#     printf ("EXPORTS\n");
+#     for (i = 0; i<nexp; i++)
+#     {
+# 	unsigned long name_rva = pe_as32 (erva+name_rvas+i*4);
+# 	printf ("\t%s @ %ld ;\n", erva+name_rva, 1+ i);
+#     }
+#
+#     return 0;
+# }
+# /* impgen.c ends here */
+
+EOF
+    ;;
+  esac
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "${ofile}T" || (rm -f "${ofile}T"; exit 1)
+
+  mv -f "${ofile}T" "$ofile" || \
+    (rm -f "$ofile" && cp "${ofile}T" "$ofile" && rm -f "${ofile}T")
+  chmod +x "$ofile"
+fi
+
+])# _LT_AC_LTCONFIG_HACK
+
+# AC_LIBTOOL_DLOPEN - enable checks for dlopen support
+AC_DEFUN([AC_LIBTOOL_DLOPEN], [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])])
+
+# AC_LIBTOOL_WIN32_DLL - declare package support for building win32 dll's
+AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_BEFORE([$0], [AC_LIBTOOL_SETUP])])
+
+# AC_ENABLE_SHARED - implement the --enable-shared flag
+# Usage: AC_ENABLE_SHARED[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN([AC_ENABLE_SHARED],
+[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(shared,
+changequote(<<, >>)dnl
+<<  --enable-shared[=PKGS]  build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+  enable_shared=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_shared=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl
+])
+
+# AC_DISABLE_SHARED - set the default shared flag to --disable-shared
+AC_DEFUN([AC_DISABLE_SHARED],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_SHARED(no)])
+
+# AC_ENABLE_STATIC - implement the --enable-static flag
+# Usage: AC_ENABLE_STATIC[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN([AC_ENABLE_STATIC],
+[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(static,
+changequote(<<, >>)dnl
+<<  --enable-static[=PKGS]  build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+  enable_static=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_static=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_static=AC_ENABLE_STATIC_DEFAULT)dnl
+])
+
+# AC_DISABLE_STATIC - set the default static flag to --disable-static
+AC_DEFUN([AC_DISABLE_STATIC],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)])
+
+
+# AC_ENABLE_FAST_INSTALL - implement the --enable-fast-install flag
+# Usage: AC_ENABLE_FAST_INSTALL[(DEFAULT)]
+#   Where DEFAULT is either `yes' or `no'.  If omitted, it defaults to
+#   `yes'.
+AC_DEFUN([AC_ENABLE_FAST_INSTALL],
+[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE(fast-install,
+changequote(<<, >>)dnl
+<<  --enable-fast-install[=PKGS]  optimize for fast installation [default=>>AC_ENABLE_FAST_INSTALL_DEFAULT],
+changequote([, ])dnl
+[p=${PACKAGE-default}
+case $enableval in
+yes) enable_fast_install=yes ;;
+no) enable_fast_install=no ;;
+*)
+  enable_fast_install=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_fast_install=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac],
+enable_fast_install=AC_ENABLE_FAST_INSTALL_DEFAULT)dnl
+])
+
+# AC_DISABLE_FAST_INSTALL - set the default to --disable-fast-install
+AC_DEFUN([AC_DISABLE_FAST_INSTALL],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_FAST_INSTALL(no)])
+
+# AC_LIBTOOL_PICMODE - implement the --with-pic flag
+# Usage: AC_LIBTOOL_PICMODE[(MODE)]
+#   Where MODE is either `yes' or `no'.  If omitted, it defaults to
+#   `both'.
+AC_DEFUN([AC_LIBTOOL_PICMODE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+pic_mode=ifelse($#,1,$1,default)])
+
+
+# AC_PATH_TOOL_PREFIX - find a file program which can recognise shared library
+AC_DEFUN([AC_PATH_TOOL_PREFIX],
+[AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+  /*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  ac_save_MAGIC_CMD="$MAGIC_CMD"
+  IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS=":"
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="ifelse([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    egrep "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  MAGIC_CMD="$ac_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+])
+
+
+# AC_PATH_MAGIC - find a file program which can recognise a shared library
+AC_DEFUN([AC_PATH_MAGIC],
+[AC_REQUIRE([AC_CHECK_TOOL_PREFIX])dnl
+AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin:$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_PATH_TOOL_PREFIX(file, /usr/bin:$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])
+
+
+# AC_PROG_LD - find the path to the GNU or non-GNU linker
+AC_DEFUN([AC_PROG_LD],
+[AC_ARG_WITH(gnu-ld,
+[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR])dnl
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by GCC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | [[A-Za-z]]:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      if "$lt_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+	test "$with_gnu_ld" != no && break
+      else
+	test "$with_gnu_ld" != yes && break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_PROG_LD_GNU
+])
+
+# AC_PROG_LD_GNU -
+AC_DEFUN([AC_PROG_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  lt_cv_prog_gnu_ld=yes
+else
+  lt_cv_prog_gnu_ld=no
+fi])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])
+
+# AC_PROG_LD_RELOAD_FLAG - find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+AC_DEFUN([AC_PROG_LD_RELOAD_FLAG],
+[AC_CACHE_CHECK([for $LD option to reload object files], lt_cv_ld_reload_flag,
+[lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+test -n "$reload_flag" && reload_flag=" $reload_flag"
+])
+
+# AC_DEPLIBS_CHECK_METHOD - how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+AC_DEFUN([AC_DEPLIBS_CHECK_METHOD],
+[AC_CACHE_CHECK([how to recognise dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given egrep regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix4* | aix5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi4*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin* | mingw* | pw32*)
+  lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method='file_magic Mach-O dynamically linked shared library'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  case "$host_os" in
+  rhapsody* | darwin1.[[012]])
+    lt_cv_file_magic_test_file=`echo /System/Library/Frameworks/System.framework/Versions/*/System | head -1`
+    ;;
+  *) # Darwin 1.3 on
+    lt_cv_file_magic_test_file='/usr/lib/libSystem.dylib'
+    ;;
+  esac
+  ;;
+
+freebsd*)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20*|hpux11*)
+  lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libc.sl
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+  irix5* | nonstopux*)
+    # this will be overridden with pass_all, but let us keep it just in case
+    lt_cv_deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1"
+    ;;
+  *)
+    case $LD in
+    *-32|*"-32 ") libmagic=32-bit;;
+    *-n32|*"-n32 ") libmagic=N32;;
+    *-64|*"-64 ") libmagic=64-bit;;
+    *) libmagic=never-match;;
+    esac
+    # this will be overridden with pass_all, but let us keep it just in case
+    lt_cv_deplibs_check_method="file_magic ELF ${libmagic} MSB mips-[[1234]] dynamic lib MIPS - version 1"
+    ;;
+  esac
+  lt_cv_file_magic_test_file=`echo /lib${libsuff}/libc.so*`
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+  case $host_cpu in
+  alpha* | hppa* | i*86 | mips | mipsel | powerpc* | sparc* | ia64*)
+    lt_cv_deplibs_check_method=pass_all ;;
+  *)
+    # glibc up to 2.1.1 does not perform some relocations on ARM
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;;
+  esac
+  lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+  ;;
+
+netbsd*)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/\.]]+\.so\.[[0-9]]+\.[[0-9]]+$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/\.]]+\.so$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+openbsd*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB shared object'
+  else
+    lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  # this will be overridden with pass_all, but let us keep it just in case
+  lt_cv_deplibs_check_method='file_magic COFF format alpha shared library'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sco3.2v5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  lt_cv_file_magic_test_file=/lib/libc.so
+  ;;
+
+sysv5uw[[78]]* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+])
+
+
+# AC_PROG_NM - find the path to a BSD-compatible name lister
+AC_DEFUN([AC_PROG_NM],
+[AC_REQUIRE([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR])dnl
+AC_MSG_CHECKING([for BSD-compatible nm])
+AC_CACHE_VAL(lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
+    test -z "$ac_dir" && ac_dir=.
+    tmp_nm=$ac_dir/${ac_tool_prefix}nm
+    if test -f $tmp_nm || test -f $tmp_nm$ac_exeext ; then
+      # Check to see if the nm accepts a BSD-compat flag.
+      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+      #   nm: unknown option "B" ignored
+      # Tru64's nm complains that /dev/null is an invalid object file
+      if ($tmp_nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep '(/dev/null|Invalid file or object type)' >/dev/null; then
+	lt_cv_path_NM="$tmp_nm -B"
+	break
+      elif ($tmp_nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+	lt_cv_path_NM="$tmp_nm -p"
+	break
+      else
+	lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	continue # so that we can try to find one that supports BSD flags
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm
+fi])
+NM="$lt_cv_path_NM"
+AC_MSG_RESULT([$NM])
+])
+
+# AC_CHECK_LIBM - check for math library
+AC_DEFUN([AC_CHECK_LIBM],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32*)
+  # These system don't have libm
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, main, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, main, LIBM="-lm")
+  ;;
+esac
+])
+
+# AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for
+# the libltdl convenience library and LTDLINCL to the include flags for
+# the libltdl header and adds --enable-ltdl-convenience to the
+# configure arguments.  Note that LIBLTDL and LTDLINCL are not
+# AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called.  If DIR is not
+# provided, it is assumed to be `libltdl'.  LIBLTDL will be prefixed
+# with '${top_builddir}/' and LTDLINCL will be prefixed with
+# '${top_srcdir}/' (note the single quotes!).  If your package is not
+# flat and you're not using automake, define top_builddir and
+# top_srcdir appropriately in the Makefiles.
+AC_DEFUN([AC_LIBLTDL_CONVENIENCE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  case $enable_ltdl_convenience in
+  no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;;
+  "") enable_ltdl_convenience=yes
+      ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;;
+  esac
+  LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la
+  LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
+  # For backwards non-gettext consistent compatibility...
+  INCLTDL="$LTDLINCL"
+])
+
+# AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for
+# the libltdl installable library and LTDLINCL to the include flags for
+# the libltdl header and adds --enable-ltdl-install to the configure
+# arguments.  Note that LIBLTDL and LTDLINCL are not AC_SUBSTed, nor is
+# AC_CONFIG_SUBDIRS called.  If DIR is not provided and an installed
+# libltdl is not found, it is assumed to be `libltdl'.  LIBLTDL will
+# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed
+# with '${top_srcdir}/' (note the single quotes!).  If your package is
+# not flat and you're not using automake, define top_builddir and
+# top_srcdir appropriately in the Makefiles.
+# In the future, this macro may have to be called after AC_PROG_LIBTOOL.
+AC_DEFUN([AC_LIBLTDL_INSTALLABLE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  AC_CHECK_LIB(ltdl, main,
+  [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no],
+  [if test x"$enable_ltdl_install" = xno; then
+     AC_MSG_WARN([libltdl not installed, but installation disabled])
+   else
+     enable_ltdl_install=yes
+   fi
+  ])
+  if test x"$enable_ltdl_install" = x"yes"; then
+    ac_configure_args="$ac_configure_args --enable-ltdl-install"
+    LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la
+    LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
+  else
+    ac_configure_args="$ac_configure_args --enable-ltdl-install=no"
+    LIBLTDL="-lltdl"
+    LTDLINCL=
+  fi
+  # For backwards non-gettext consistent compatibility...
+  INCLTDL="$LTDLINCL"
+])
+
+# old names
+AC_DEFUN([AM_PROG_LIBTOOL],   [AC_PROG_LIBTOOL])
+AC_DEFUN([AM_ENABLE_SHARED],  [AC_ENABLE_SHARED($@)])
+AC_DEFUN([AM_ENABLE_STATIC],  [AC_ENABLE_STATIC($@)])
+AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+AC_DEFUN([AM_PROG_LD],        [AC_PROG_LD])
+AC_DEFUN([AM_PROG_NM],        [AC_PROG_NM])
+
+# This is just to silence aclocal about the macro not being used
+ifelse([AC_DISABLE_FAST_INSTALL])
+
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+# LT_AC_PROG_SED
+# --------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+AC_DEFUN([LT_AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_executable_p="test -f"
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+        _sed_list="$_sed_list $as_dir/$ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+
+  # Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+: ${TMPDIR=/tmp}
+{
+  tmp=`(umask 077 && mktemp -d -q "$TMPDIR/sedXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=$TMPDIR/sed$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in $TMPDIR" >&2
+   { (exit 1); exit 1; }
+}
+  _max=0
+  _count=0
+  # Add /usr/xpg4/bin/sed as it is typically found on Solaris
+  # along with /bin/sed that truncates output.
+  for _sed in $_sed_list /usr/xpg4/bin/sed; do
+    test ! -f ${_sed} && break
+    cat /dev/null > "$tmp/sed.in"
+    _count=0
+    echo ${ECHO_N-$ac_n} "0123456789${ECHO_C-$ac_c}" >"$tmp/sed.in"
+    # Check for GNU sed and select it if it is found.
+    if "${_sed}" --version 2>&1 < /dev/null | egrep '(GNU)' > /dev/null; then
+      lt_cv_path_SED=${_sed}
+      break;
+    fi
+    while true; do
+      cat "$tmp/sed.in" "$tmp/sed.in" >"$tmp/sed.tmp"
+      mv "$tmp/sed.tmp" "$tmp/sed.in"
+      cp "$tmp/sed.in" "$tmp/sed.nl"
+      echo >>"$tmp/sed.nl"
+      ${_sed} -e 's/a$//' < "$tmp/sed.nl" >"$tmp/sed.out" || break
+      cmp -s "$tmp/sed.out" "$tmp/sed.nl" || break
+      # 40000 chars as input seems more than enough
+      test $_count -gt 10 && break
+      _count=`expr $_count + 1`
+      if test $_count -gt $_max; then
+        _max=$_count
+        lt_cv_path_SED=$_sed
+      fi
+    done
+  done
+  rm -rf "$tmp"
+])
+if test "X$SED" != "X"; then
+  lt_cv_path_SED=$SED
+else
+  SED=$lt_cv_path_SED
+fi
+AC_MSG_RESULT([$SED])
+])
+
+# Add --enable-maintainer-mode option to configure.
+# From Jim Meyering
+
+# serial 1
+
+AC_DEFUN([AM_MAINTAINER_MODE],
+[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode is disabled by default
+  AC_ARG_ENABLE(maintainer-mode,
+[  --enable-maintainer-mode enable make rules and dependencies not useful
+                          (and sometimes confusing) to the casual installer],
+      USE_MAINTAINER_MODE=$enableval,
+      USE_MAINTAINER_MODE=no)
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST(MAINT)dnl
+]
+)
+
+# iconv.m4 serial AM4 (gettext-0.11.3)
+dnl Copyright (C) 2000-2002 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_ICONV_LINKFLAGS_BODY],
+[
+  dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([iconv])
+])
+
+AC_DEFUN([AM_ICONV_LINK],
+[
+  dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+  dnl those with the standalone portable GNU libiconv installed).
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+  dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed libiconv and not disabled its use
+  dnl via --without-libiconv-prefix, he wants to use it. The first
+  dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+  am_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+  AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
+    am_cv_func_iconv="no, consider installing GNU libiconv"
+    am_cv_lib_iconv=no
+    AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+      [iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);],
+      am_cv_func_iconv=yes)
+    if test "$am_cv_func_iconv" != yes; then
+      am_save_LIBS="$LIBS"
+      LIBS="$LIBS $LIBICONV"
+      AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+        [iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);],
+        am_cv_lib_iconv=yes
+        am_cv_func_iconv=yes)
+      LIBS="$am_save_LIBS"
+    fi
+  ])
+  if test "$am_cv_func_iconv" = yes; then
+    AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
+  fi
+  if test "$am_cv_lib_iconv" = yes; then
+    AC_MSG_CHECKING([how to link with libiconv])
+    AC_MSG_RESULT([$LIBICONV])
+  else
+    dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+    dnl either.
+    CPPFLAGS="$am_save_CPPFLAGS"
+    LIBICONV=
+    LTLIBICONV=
+  fi
+  AC_SUBST(LIBICONV)
+  AC_SUBST(LTLIBICONV)
+])
+
+AC_DEFUN([AM_ICONV],
+[
+  AM_ICONV_LINK
+  if test "$am_cv_func_iconv" = yes; then
+    AC_MSG_CHECKING([for iconv declaration])
+    AC_CACHE_VAL(am_cv_proto_iconv, [
+      AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+      am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+    am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+    AC_MSG_RESULT([$]{ac_t:-
+         }[$]am_cv_proto_iconv)
+    AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+      [Define as const if the declaration of iconv() needs const.])
+  fi
+])
+
+# lib-prefix.m4 serial 1 (gettext-0.11)
+dnl Copyright (C) 2001-2002 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+dnl From Bruno Haible.
+
+dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed
+dnl to access previously installed libraries. The basic assumption is that
+dnl a user will want packages to use other packages he previously installed
+dnl with the same --prefix option.
+dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate
+dnl libraries, but is otherwise very convenient.
+AC_DEFUN([AC_LIB_PREFIX],
+[
+  AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_ARG_WITH([lib-prefix],
+[  --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+  --without-lib-prefix    don't search for libraries in includedir and libdir],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+])
+  if test $use_additional = yes; then
+    dnl Potentially add $additional_includedir to $CPPFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/include,
+    dnl   2. if it's already present in $CPPFLAGS,
+    dnl   3. if it's /usr/local/include and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_includedir" != "X/usr/include"; then
+      haveit=
+      for x in $CPPFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-I$additional_includedir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_includedir" = "X/usr/local/include"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux*) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_includedir"; then
+            dnl Really add $additional_includedir to $CPPFLAGS.
+            CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+          fi
+        fi
+      fi
+    fi
+    dnl Potentially add $additional_libdir to $LDFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/lib,
+    dnl   2. if it's already present in $LDFLAGS,
+    dnl   3. if it's /usr/local/lib and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_libdir" != "X/usr/lib"; then
+      haveit=
+      for x in $LDFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-L$additional_libdir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_libdir" = "X/usr/local/lib"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux*) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_libdir"; then
+            dnl Really add $additional_libdir to $LDFLAGS.
+            LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+          fi
+        fi
+      fi
+    fi
+  fi
+])
+
+dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix,
+dnl acl_final_exec_prefix, containing the values to which $prefix and
+dnl $exec_prefix will expand at the end of the configure script.
+AC_DEFUN([AC_LIB_PREPARE_PREFIX],
+[
+  dnl Unfortunately, prefix and exec_prefix get only finally determined
+  dnl at the end of configure.
+  if test "X$prefix" = "XNONE"; then
+    acl_final_prefix="$ac_default_prefix"
+  else
+    acl_final_prefix="$prefix"
+  fi
+  if test "X$exec_prefix" = "XNONE"; then
+    acl_final_exec_prefix='${prefix}'
+  else
+    acl_final_exec_prefix="$exec_prefix"
+  fi
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+  prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the
+dnl variables prefix and exec_prefix bound to the values they will have
+dnl at the end of the configure script.
+AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX],
+[
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  $1
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+])
+
+# lib-link.m4 serial 3 (gettext-0.11.3)
+dnl Copyright (C) 2001-2002 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+dnl From Bruno Haible.
+
+dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and
+dnl augments the CPPFLAGS variable.
+AC_DEFUN([AC_LIB_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  define([Name],[translit([$1],[./-], [___])])
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+    AC_LIB_LINKFLAGS_BODY([$1], [$2])
+    ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+    ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+    ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+  ])
+  LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+  LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+  INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+  dnl results of this search when this library appears as a dependency.
+  HAVE_LIB[]NAME=yes
+  undefine([Name])
+  undefine([NAME])
+])
+
+dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode)
+dnl searches for libname and the libraries corresponding to explicit and
+dnl implicit dependencies, together with the specified include files and
+dnl the ability to compile and link the specified testcode. If found, it
+dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and
+dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and
+dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs
+dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty.
+AC_DEFUN([AC_LIB_HAVE_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  define([Name],[translit([$1],[./-], [___])])
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+  dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+  dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed lib[]Name and not disabled its use
+  dnl via --without-lib[]Name-prefix, he wants to use it.
+  ac_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+  AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIB[]NAME"
+    AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
+    LIBS="$ac_save_LIBS"
+  ])
+  if test "$ac_cv_lib[]Name" = yes; then
+    HAVE_LIB[]NAME=yes
+    AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
+    AC_MSG_CHECKING([how to link with lib[]$1])
+    AC_MSG_RESULT([$LIB[]NAME])
+  else
+    HAVE_LIB[]NAME=no
+    dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+    dnl $INC[]NAME either.
+    CPPFLAGS="$ac_save_CPPFLAGS"
+    LIB[]NAME=
+    LTLIB[]NAME=
+  fi
+  AC_SUBST([HAVE_LIB]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  undefine([Name])
+  undefine([NAME])
+])
+
+dnl Determine the platform dependent parameters needed to use rpath:
+dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator,
+dnl hardcode_direct, hardcode_minus_L,
+dnl sys_lib_search_path_spec, sys_lib_dlsearch_path_spec.
+AC_DEFUN([AC_LIB_RPATH],
+[
+  AC_REQUIRE([AC_PROG_CC])                dnl we use $CC, $GCC, $LDFLAGS
+  AC_REQUIRE([AC_LIB_PROG_LD])            dnl we use $LD, $with_gnu_ld
+  AC_REQUIRE([AC_CANONICAL_HOST])         dnl we use $host
+  AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+  AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
+    CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+    ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+    . ./conftest.sh
+    rm -f ./conftest.sh
+    acl_cv_rpath=done
+  ])
+  wl="$acl_cv_wl"
+  libext="$acl_cv_libext"
+  shlibext="$acl_cv_shlibext"
+  hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+  hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+  hardcode_direct="$acl_cv_hardcode_direct"
+  hardcode_minus_L="$acl_cv_hardcode_minus_L"
+  sys_lib_search_path_spec="$acl_cv_sys_lib_search_path_spec"
+  sys_lib_dlsearch_path_spec="$acl_cv_sys_lib_dlsearch_path_spec"
+  dnl Determine whether the user wants rpath handling at all.
+  AC_ARG_ENABLE(rpath,
+    [  --disable-rpath         do not hardcode runtime library paths],
+    :, enable_rpath=yes)
+])
+
+dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables.
+AC_DEFUN([AC_LIB_LINKFLAGS_BODY],
+[
+  define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                               [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_ARG_WITH([lib$1-prefix],
+[  --with-lib$1-prefix[=DIR]  search for lib$1 in DIR/include and DIR/lib
+  --without-lib$1-prefix     don't search for lib$1 in includedir and libdir],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+])
+  dnl Search the library and its dependencies in $additional_libdir and
+  dnl $LDFLAGS. Using breadth-first-seach.
+  LIB[]NAME=
+  LTLIB[]NAME=
+  INC[]NAME=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='$1 $2'
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+        dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+        dnl or AC_LIB_HAVE_LINKFLAGS call.
+        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
+          else
+            dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+            dnl that this library doesn't exist. So just drop it.
+            :
+          fi
+        else
+          dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+          dnl and the already constructed $LIBNAME/$LTLIBNAME.
+          found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            else
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIB[]NAME; do
+              AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  else
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+            dnl Found the library.
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+              dnl Linking with a shared library. We attempt to hardcode its
+              dnl directory into the executable's runpath, unless it's the
+              dnl standard /usr/lib.
+              if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                dnl No hardcoding is needed.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+              else
+                dnl Use an explicit option to hardcode DIR into the resulting
+                dnl binary.
+                dnl Potentially add DIR to ltrpathdirs.
+                dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                dnl The hardcoding into $LIBNAME is system dependent.
+                if test "$hardcode_direct" = yes; then
+                  dnl Using DIR/libNAME.so during linking hardcodes DIR into the
+                  dnl resulting binary.
+                  LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                    dnl Use an explicit option to hardcode DIR into the resulting
+                    dnl binary.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    dnl Potentially add DIR to rpathdirs.
+                    dnl The rpathdirs will be appended to $LIBNAME at the end.
+                    haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                    dnl Rely on "-L$found_dir".
+                    dnl But don't add it if it's already contained in the LDFLAGS
+                    dnl or the already constructed $LIBNAME
+                    haveit=
+                    for x in $LDFLAGS $LIB[]NAME; do
+                      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    else
+                      dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH
+                      dnl here, because this doesn't fit in flags passed to the
+                      dnl compiler. So give up. No hardcoding. This affects only
+                      dnl very old systems.
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                dnl Linking with a static library.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+              else
+                dnl We shouldn't come here, but anyway it's good to have a
+                dnl fallback.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+              fi
+            fi
+            dnl Assume the include files are nearby.
+            additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+              dnl Potentially add $additional_includedir to $INCNAME.
+              dnl But don't add it
+              dnl   1. if it's the standard /usr/include,
+              dnl   2. if it's /usr/local/include and we are using GCC on Linux,
+              dnl   3. if it's already present in $CPPFLAGS or the already
+              dnl      constructed $INCNAME,
+              dnl   4. if it doesn't exist as a directory.
+              if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INC[]NAME; do
+                    AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                      dnl Really add $additional_includedir to $INCNAME.
+                      INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+            dnl Look for dependencies.
+            if test -n "$found_la"; then
+              dnl Read the .la file. It defines the variables
+              dnl dlname, library_names, old_library, dependency_libs, current,
+              dnl age, revision, installed, dlopen, dlpreopen, libdir.
+              save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+              dnl We use only dependency_libs.
+              for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                    dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
+                    dnl But don't add it
+                    dnl   1. if it's the standard /usr/lib,
+                    dnl   2. if it's /usr/local/lib and we are using GCC on Linux,
+                    dnl   3. if it's already present in $LDFLAGS or the already
+                    dnl      constructed $LIBNAME,
+                    dnl   4. if it doesn't exist as a directory.
+                    if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LIBNAME.
+                            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LTLIBNAME.
+                            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                      dnl Potentially add DIR to rpathdirs.
+                      dnl The rpathdirs will be appended to $LIBNAME at the end.
+                      haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                      dnl Potentially add DIR to ltrpathdirs.
+                      dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+                      haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                    dnl Handle this in the next round.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                    dnl Handle this in the next round. Throw away the .la's
+                    dnl directory; it is already contained in a preceding -L
+                    dnl option.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                    dnl Most likely an immediate library name.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+                    LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+            dnl Didn't find the library; assume it is in the system directories
+            dnl known to the linker and runtime loader. (All the system
+            dnl directories known to the linker should also be known to the
+            dnl runtime loader, otherwise the system is severely misconfigured.)
+            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+      dnl Weird platform: only the last -rpath option counts, the user must
+      dnl pass all path elements in one option. We can arrange that for a
+      dnl single library, but not when more than one $LIBNAMEs are used.
+      alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+      dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl.
+      acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+    else
+      dnl The -rpath options are cumulative.
+      for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+    dnl When using libtool, the option that works for both libraries and
+    dnl executables is -R. The -R options are cumulative.
+    for found_dir in $ltrpathdirs; do
+      LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+    done
+  fi
+])
+
+dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR,
+dnl unless already present in VAR.
+dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes
+dnl contains two or three consecutive elements that belong together.
+AC_DEFUN([AC_LIB_APPENDTOVAR],
+[
+  for element in [$2]; do
+    haveit=
+    for x in $[$1]; do
+      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      [$1]="${[$1]}${[$1]:+ }$element"
+    fi
+  done
+])
+
+# lib-ld.m4 serial 1 (gettext-0.11)
+dnl Copyright (C) 1996-2002 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+dnl Subroutines of libtool.m4,
+dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision
+dnl with libtool.m4.
+
+dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no.
+AC_DEFUN([AC_LIB_PROG_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  acl_cv_prog_gnu_ld=yes
+else
+  acl_cv_prog_gnu_ld=no
+fi])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+
+dnl From libtool-1.4. Sets the variable LD.
+AC_DEFUN([AC_LIB_PROG_LD],
+[AC_ARG_WITH(gnu-ld,
+[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by GCC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]* | [A-Za-z]:[\\/]*)]
+      [re_direlt='/[^/][^/]*/\.\./']
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(acl_cv_path_LD,
+[if test -z "$LD"; then
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      acl_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      if "$acl_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+	test "$with_gnu_ld" != no && break
+      else
+	test "$with_gnu_ld" != yes && break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
+
Index: /gsdl/trunk/gs2common/indexers/packages/unac/config.h.in
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/config.h.in	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/config.h.in	(revision 16561)
@@ -0,0 +1,29 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the `iconv_open' function. */
+#undef HAVE_ICONV_OPEN
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
Index: /gsdl/trunk/gs2common/indexers/packages/unac/configure
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/configure	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/configure	(revision 16561)
@@ -0,0 +1,9725 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Find the correct PATH separator.  Usually this is `:', but
+# DJGPP uses `;' like DOS.
+if test "X${PATH_SEPARATOR+set}" != Xset; then
+  UNAME=${UNAME-`uname 2>/dev/null`}
+  case X$UNAME in
+    *-DOS) lt_cv_sys_path_separator=';' ;;
+    *)     lt_cv_sys_path_separator=':' ;;
+  esac
+  PATH_SEPARATOR=$lt_cv_sys_path_separator
+fi
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$ECHO" | sed 's,\\\\\$\\$0,'$0','`
+  ;;
+esac
+
+echo=${ECHO-echo}
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+
+EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+if test -z "$ECHO"; then
+if test "X${echo_test_string+set}" != Xset; then
+# find a string as large as possible, as long as the shell can cope with it
+  for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+       echo_test_string="`eval $cmd`" &&
+       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null
+    then
+      break
+    fi
+  done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+   echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+   test "X$echo_testing_string" = "X$echo_test_string"; then
+  :
+else
+  # The Solaris, AIX, and Digital Unix default echo programs unquote
+  # backslashes.  This makes it impossible to quote backslashes using
+  #   echo "$something" | sed 's/\\/\\\\/g'
+  #
+  # So, first we look for a working echo in the user's PATH.
+
+  IFS="${IFS= 	}"; save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for dir in $PATH /usr/ucb; do
+    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      echo="$dir/echo"
+      break
+    fi
+  done
+  IFS="$save_ifs"
+
+  if test "X$echo" = Xecho; then
+    # We didn't find a better echo, so look for alternatives.
+    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      # This shell has a builtin print -r that does the trick.
+      echo='print -r'
+    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+	 test "X$CONFIG_SHELL" != X/bin/ksh; then
+      # If we have ksh, try running configure again with it.
+      ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+      export ORIGINAL_CONFIG_SHELL
+      CONFIG_SHELL=/bin/ksh
+      export CONFIG_SHELL
+      exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+    else
+      # Try using printf.
+      echo='printf %s\n'
+      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+	 echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+	 test "X$echo_testing_string" = "X$echo_test_string"; then
+	# Cool, printf works
+	:
+      elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+	export CONFIG_SHELL
+	SHELL="$CONFIG_SHELL"
+	export SHELL
+	echo="$CONFIG_SHELL $0 --fallback-echo"
+      elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	echo="$CONFIG_SHELL $0 --fallback-echo"
+      else
+	# maybe with a smaller string...
+	prev=:
+
+	for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+	  if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null
+	  then
+	    break
+	  fi
+	  prev="$cmd"
+	done
+
+	if test "$prev" != 'sed 50q "$0"'; then
+	  echo_test_string=`eval $prev`
+	  export echo_test_string
+	  exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+	else
+	  # Oops.  We lost completely, so just stick with echo.
+	  echo=echo
+	fi
+      fi
+    fi
+  fi
+fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+ECHO=$echo
+if test "X$ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+   ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="config.h.in"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS VERSION build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os SET_MAKE COMPAT32BITFLAGS UNICODE_VERSION CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP LN_S ECHO RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP LIBTOOL PERL SH MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LIBICONV LTLIBICONV LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-shared=PKGS  build shared libraries default=yes
+  --enable-static=PKGS  build static libraries default=yes
+  --enable-fast-install=PKGS  optimize for fast installation default=yes
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --enable-maintainer-mode enable make rules and dependencies not useful
+                          (and sometimes confusing) to the casual installer
+  --disable-rpath         do not hardcode runtime library paths
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-gnu-ld           assume the C compiler uses GNU ld default=no
+  --with-pic              try to use only PIC/non-PIC objects default=use both
+  --with-libiconv-prefix=DIR  search for libiconv in DIR/include and DIR/lib
+  --without-libiconv-prefix     don't search for libiconv in includedir and libdir
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+VERSION=`cat .version`
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking target system type" >&5
+echo $ECHO_N "checking target system type... $ECHO_C" >&6
+if test "${ac_cv_target+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_target_alias=$target_alias
+test "x$ac_cv_target_alias" = "x" &&
+  ac_cv_target_alias=$ac_cv_host_alias
+ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+echo "${ECHO_T}$ac_cv_target" >&6
+target=$ac_cv_target
+target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+
+          ac_config_headers="$ac_config_headers config.h"
+
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+	@echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+echo "$as_me:$LINENO: checking to see if architecture is 64-bit" >&5
+echo $ECHO_N "checking to see if architecture is 64-bit... $ECHO_C" >&6
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+
+
+UNICODE_VERSION=3.2.0
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+
+echo "$as_me:$LINENO: checking for AIX" >&5
+echo $ECHO_N "checking for AIX... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef _AIX
+  yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+cat >>confdefs.h <<\_ACEOF
+#define _ALL_SOURCE 1
+_ACEOF
+
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f conftest*
+
+
+echo "$as_me:$LINENO: checking maximum warning verbosity option" >&5
+echo $ECHO_N "checking maximum warning verbosity option... $ECHO_C" >&6
+if test -n "$CXX"
+then
+  if test "$GXX" = "yes"
+  then
+    ac_compile_warnings_opt='-Wall -Woverloaded-virtual'
+  fi
+  CXXFLAGS="$CXXFLAGS $ac_compile_warnings_opt"
+  ac_compile_warnings_msg="$ac_compile_warnings_opt for C++"
+fi
+
+if test -n "$CC"
+then
+  if test "$GCC" = "yes"
+  then
+    ac_compile_warnings_opt='-Wall -Wmissing-declarations -Wmissing-prototypes'
+  fi
+  CFLAGS="$CFLAGS $ac_compile_warnings_opt"
+  ac_compile_warnings_msg="$ac_compile_warnings_msg $ac_compile_warnings_opt for C"
+fi
+echo "$as_me:$LINENO: result: $ac_compile_warnings_msg" >&5
+echo "${ECHO_T}$ac_compile_warnings_msg" >&6
+unset ac_compile_warnings_msg
+unset ac_compile_warnings_opt
+
+
+# Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+  enableval="$enable_shared"
+  p=${PACKAGE-default}
+case $enableval in
+yes) enable_shared=yes ;;
+no) enable_shared=no ;;
+*)
+  enable_shared=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_shared=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+else
+  enable_shared=yes
+fi;
+# Check whether --enable-static or --disable-static was given.
+if test "${enable_static+set}" = set; then
+  enableval="$enable_static"
+  p=${PACKAGE-default}
+case $enableval in
+yes) enable_static=yes ;;
+no) enable_static=no ;;
+*)
+  enable_static=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_static=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+else
+  enable_static=yes
+fi;
+# Check whether --enable-fast-install or --disable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then
+  enableval="$enable_fast_install"
+  p=${PACKAGE-default}
+case $enableval in
+yes) enable_fast_install=yes ;;
+no) enable_fast_install=no ;;
+*)
+  enable_fast_install=no
+  # Look at the argument we got.  We use all the common list separators.
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:,"
+  for pkg in $enableval; do
+    if test "X$pkg" = "X$p"; then
+      enable_fast_install=yes
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+else
+  enable_fast_install=yes
+fi;
+# Find the correct PATH separator.  Usually this is `:', but
+# DJGPP uses `;' like DOS.
+if test "X${PATH_SEPARATOR+set}" != Xset; then
+  UNAME=${UNAME-`uname 2>/dev/null`}
+  case X$UNAME in
+    *-DOS) lt_cv_sys_path_separator=';' ;;
+    *)     lt_cv_sys_path_separator=':' ;;
+  esac
+  PATH_SEPARATOR=$lt_cv_sys_path_separator
+fi
+
+
+# Check whether --with-gnu-ld or --without-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+  withval="$with_gnu_ld"
+  test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi;
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  echo "$as_me:$LINENO: checking for ld used by GCC" >&5
+echo $ECHO_N "checking for ld used by GCC... $ECHO_C" >&6
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | [A-Za-z]:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6
+else
+  echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$LD"; then
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      if "$lt_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+	test "$with_gnu_ld" != no && break
+      else
+	test "$with_gnu_ld" != yes && break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  lt_cv_prog_gnu_ld=yes
+else
+  lt_cv_prog_gnu_ld=no
+fi
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5
+echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6
+if test "${lt_cv_ld_reload_flag+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5
+echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6
+reload_flag=$lt_cv_ld_reload_flag
+test -n "$reload_flag" && reload_flag=" $reload_flag"
+
+echo "$as_me:$LINENO: checking for BSD-compatible nm" >&5
+echo $ECHO_N "checking for BSD-compatible nm... $ECHO_C" >&6
+if test "${lt_cv_path_NM+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
+    test -z "$ac_dir" && ac_dir=.
+    tmp_nm=$ac_dir/${ac_tool_prefix}nm
+    if test -f $tmp_nm || test -f $tmp_nm$ac_exeext ; then
+      # Check to see if the nm accepts a BSD-compat flag.
+      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+      #   nm: unknown option "B" ignored
+      # Tru64's nm complains that /dev/null is an invalid object file
+      if ($tmp_nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep '(/dev/null|Invalid file or object type)' >/dev/null; then
+	lt_cv_path_NM="$tmp_nm -B"
+	break
+      elif ($tmp_nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then
+	lt_cv_path_NM="$tmp_nm -p"
+	break
+      else
+	lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	continue # so that we can try to find one that supports BSD flags
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm
+fi
+fi
+
+NM="$lt_cv_path_NM"
+echo "$as_me:$LINENO: result: $NM" >&5
+echo "${ECHO_T}$NM" >&6
+
+echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6
+if test "${lt_cv_path_SED+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_executable_p="test -f"
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+        _sed_list="$_sed_list $as_dir/$ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+
+  # Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+: ${TMPDIR=/tmp}
+{
+  tmp=`(umask 077 && mktemp -d -q "$TMPDIR/sedXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=$TMPDIR/sed$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in $TMPDIR" >&2
+   { (exit 1); exit 1; }
+}
+  _max=0
+  _count=0
+  # Add /usr/xpg4/bin/sed as it is typically found on Solaris
+  # along with /bin/sed that truncates output.
+  for _sed in $_sed_list /usr/xpg4/bin/sed; do
+    test ! -f ${_sed} && break
+    cat /dev/null > "$tmp/sed.in"
+    _count=0
+    echo ${ECHO_N-$ac_n} "0123456789${ECHO_C-$ac_c}" >"$tmp/sed.in"
+    # Check for GNU sed and select it if it is found.
+    if "${_sed}" --version 2>&1 < /dev/null | egrep '(GNU)' > /dev/null; then
+      lt_cv_path_SED=${_sed}
+      break;
+    fi
+    while true; do
+      cat "$tmp/sed.in" "$tmp/sed.in" >"$tmp/sed.tmp"
+      mv "$tmp/sed.tmp" "$tmp/sed.in"
+      cp "$tmp/sed.in" "$tmp/sed.nl"
+      echo >>"$tmp/sed.nl"
+      ${_sed} -e 's/a$//' < "$tmp/sed.nl" >"$tmp/sed.out" || break
+      cmp -s "$tmp/sed.out" "$tmp/sed.nl" || break
+      # 40000 chars as input seems more than enough
+      test $_count -gt 10 && break
+      _count=`expr $_count + 1`
+      if test $_count -gt $_max; then
+        _max=$_count
+        lt_cv_path_SED=$_sed
+      fi
+    done
+  done
+  rm -rf "$tmp"
+
+fi
+
+if test "X$SED" != "X"; then
+  lt_cv_path_SED=$SED
+else
+  SED=$lt_cv_path_SED
+fi
+echo "$as_me:$LINENO: result: $SED" >&5
+echo "${ECHO_T}$SED" >&6
+
+echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6
+fi
+
+echo "$as_me:$LINENO: checking how to recognise dependent libraries" >&5
+echo $ECHO_N "checking how to recognise dependent libraries... $ECHO_C" >&6
+if test "${lt_cv_deplibs_check_method+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given egrep regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix4* | aix5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi4*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin* | mingw* | pw32*)
+  lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method='file_magic Mach-O dynamically linked shared library'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  case "$host_os" in
+  rhapsody* | darwin1.[012])
+    lt_cv_file_magic_test_file=`echo /System/Library/Frameworks/System.framework/Versions/*/System | head -1`
+    ;;
+  *) # Darwin 1.3 on
+    lt_cv_file_magic_test_file='/usr/lib/libSystem.dylib'
+    ;;
+  esac
+  ;;
+
+freebsd*)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20*|hpux11*)
+  lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libc.sl
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+  irix5* | nonstopux*)
+    # this will be overridden with pass_all, but let us keep it just in case
+    lt_cv_deplibs_check_method="file_magic ELF 32-bit MSB dynamic lib MIPS - version 1"
+    ;;
+  *)
+    case $LD in
+    *-32|*"-32 ") libmagic=32-bit;;
+    *-n32|*"-n32 ") libmagic=N32;;
+    *-64|*"-64 ") libmagic=64-bit;;
+    *) libmagic=never-match;;
+    esac
+    # this will be overridden with pass_all, but let us keep it just in case
+    lt_cv_deplibs_check_method="file_magic ELF ${libmagic} MSB mips-[1234] dynamic lib MIPS - version 1"
+    ;;
+  esac
+  lt_cv_file_magic_test_file=`echo /lib${libsuff}/libc.so*`
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+  case $host_cpu in
+  alpha* | hppa* | i*86 | mips | mipsel | powerpc* | sparc* | ia64*)
+    lt_cv_deplibs_check_method=pass_all ;;
+  *)
+    # glibc up to 2.1.1 does not perform some relocations on ARM
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;;
+  esac
+  lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+  ;;
+
+netbsd*)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+openbsd*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object'
+  else
+    lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  # this will be overridden with pass_all, but let us keep it just in case
+  lt_cv_deplibs_check_method='file_magic COFF format alpha shared library'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sco3.2v5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  lt_cv_file_magic_test_file=/lib/libc.so
+  ;;
+
+sysv5uw[78]* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+esac
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5
+echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+
+
+
+
+
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+echo "$as_me:$LINENO: checking command to parse $NM output" >&5
+echo $ECHO_N "checking command to parse $NM output... $ECHO_C" >&6
+if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+lt_cv_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([^ ]*\) \([^ ]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*) # Its linker distinguishes data from code symbols
+  lt_cv_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern char \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+  lt_cv_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+solaris* | sysv5*)
+  symcode='[BDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $host_os in
+mingw*)
+  opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then
+  symcode='[ABCDGISTW]'
+fi
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Write the raw and C identifiers.
+lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ 	]\($symcode$symcode*\)[ 	][ 	]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+  rm -f conftest*
+  cat > conftest.$ac_ext <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5
+  (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if egrep ' nm_test_var$' "$nlist" >/dev/null; then
+	if egrep ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_global_symbol_to_cdecl"' < "$nlist" >> conftest.$ac_ext'
+
+	  cat <<EOF >> conftest.$ac_ext
+#if defined (__STDC__) && __STDC__
+# define lt_ptr void *
+#else
+# define lt_ptr char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr address;
+}
+lt_preloaded_symbols[] =
+{
+EOF
+	  sed "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (lt_ptr) \&\2},/" < "$nlist" >> conftest.$ac_ext
+	  cat <<\EOF >> conftest.$ac_ext
+  {0, (lt_ptr) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  save_LIBS="$LIBS"
+	  save_CFLAGS="$CFLAGS"
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$no_builtin_flag"
+	  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest; then
+	    pipe_works=yes
+	  fi
+	  LIBS="$save_LIBS"
+	  CFLAGS="$save_CFLAGS"
+	else
+	  echo "cannot find nm_test_func in $nlist" >&5
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -f conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+global_symbol_pipe="$lt_cv_sys_global_symbol_pipe"
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  global_symbol_to_cdecl=
+  global_symbol_to_c_name_address=
+else
+  global_symbol_to_cdecl="$lt_cv_global_symbol_to_cdecl"
+  global_symbol_to_c_name_address="$lt_cv_global_symbol_to_c_name_address"
+fi
+if test -z "$global_symbol_pipe$global_symbol_to_cdec$global_symbol_to_c_name_address";
+then
+  echo "$as_me:$LINENO: result: failed" >&5
+echo "${ECHO_T}failed" >&6
+else
+  echo "$as_me:$LINENO: result: ok" >&5
+echo "${ECHO_T}ok" >&6
+fi
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+# Only perform the check for file, if the check method requires it
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5
+echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $MAGIC_CMD in
+  /*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  ac_save_MAGIC_CMD="$MAGIC_CMD"
+  IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="/usr/bin:$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    egrep "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  MAGIC_CMD="$ac_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    echo "$as_me:$LINENO: checking for file" >&5
+echo $ECHO_N "checking for file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $MAGIC_CMD in
+  /*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+  ?:/*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  ac_save_MAGIC_CMD="$MAGIC_CMD"
+  IFS="${IFS=   }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="/usr/bin:$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    egrep "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  MAGIC_CMD="$ac_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  STRIP=$ac_ct_STRIP
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+
+enable_dlopen=no
+enable_win32_dll=no
+
+# Check whether --enable-libtool-lock or --disable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+  enableval="$enable_libtool_lock"
+
+fi;
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line 4297 "configure"' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -32"
+      ;;
+    *N32*)
+      LD="${LD-ld} -n32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -64"
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5
+echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6
+if test "${lt_cv_cc_needs_belf+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  lt_cv_cc_needs_belf=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+lt_cv_cc_needs_belf=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5
+echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+
+
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Constants:
+rm="rm -f"
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except M$VC,
+# which needs '.lib').
+libext=a
+ltmain="$ac_aux_dir/ltmain.sh"
+ofile="$default_ofile"
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+need_locks="$enable_libtool_lock"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+test -z "$AS" && AS=as
+test -z "$CC" && CC=cc
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$LD" && LD=ld
+test -z "$LN_S" && LN_S="ln -s"
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+test -z "$NM" && NM=nm
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$RANLIB" && RANLIB=:
+test -z "$STRIP" && STRIP=:
+test -z "$ac_objext" && ac_objext=o
+
+if test x"$host" != x"$build"; then
+  ac_tool_prefix=${host_alias}-
+else
+  ac_tool_prefix=
+fi
+
+# Transform linux* to *-*-linux-gnu*, to support old configure scripts.
+case $host_os in
+linux-gnu*) ;;
+linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'`
+esac
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds"
+    ;;
+  *)
+    old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+# Allow CC to be a program name with arguments.
+set dummy $CC
+compiler="$2"
+
+echo "$as_me:$LINENO: checking for objdir" >&5
+echo $ECHO_N "checking for objdir... $ECHO_C" >&6
+rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+echo "$as_me:$LINENO: result: $objdir" >&5
+echo "${ECHO_T}$objdir" >&6
+
+
+
+# Check whether --with-pic or --without-pic was given.
+if test "${with_pic+set}" = set; then
+  withval="$with_pic"
+  pic_mode="$withval"
+else
+  pic_mode=default
+fi;
+test -z "$pic_mode" && pic_mode=default
+
+# We assume here that the value for lt_cv_prog_cc_pic will not be cached
+# in isolation, and that seeing it set (from the cache) indicates that
+# the associated values are set (in the cache) correctly too.
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+if test "${lt_cv_prog_cc_pic+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+   lt_cv_prog_cc_pic=
+  lt_cv_prog_cc_shlib=
+  lt_cv_prog_cc_wl=
+  lt_cv_prog_cc_static=
+  lt_cv_prog_cc_no_builtin=
+  lt_cv_prog_cc_can_build_shared=$can_build_shared
+
+  if test "$GCC" = yes; then
+    lt_cv_prog_cc_wl='-Wl,'
+    lt_cv_prog_cc_static='-static'
+
+    case $host_os in
+    aix*)
+      # Below there is a dirty hack to force normal static linking with -ldl
+      # The problem is because libdl dynamically linked with both libc and
+      # libC (AIX C++ library), which obviously doesn't included in libraries
+      # list by gcc. This cause undefined symbols with -static flags.
+      # This hack allows C programs to be linked with "-static -ldl", but
+      # not sure about C++ programs.
+      lt_cv_prog_cc_static="$lt_cv_prog_cc_static ${lt_cv_prog_cc_wl}-lC"
+      ;;
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_cv_prog_cc_pic='-m68020 -resident32 -malways-restore-a4'
+      ;;
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_cv_prog_cc_pic='-fno-common'
+      ;;
+    cygwin* | mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_cv_prog_cc_pic='-DDLL_EXPORT'
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	 lt_cv_prog_cc_pic=-Kconform_pic
+      fi
+      ;;
+    *)
+      lt_cv_prog_cc_pic='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for PIC flags for the system compiler.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      lt_cv_prog_cc_wl='-Wl,'
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_cv_prog_cc_static='-Bstatic'
+      else
+	lt_cv_prog_cc_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      # Is there a better lt_cv_prog_cc_static that works with the bundled CC?
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static="${lt_cv_prog_cc_wl}-a ${lt_cv_prog_cc_wl}archive"
+      lt_cv_prog_cc_pic='+Z'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static='-non_shared'
+      # PIC (with -KPIC) is the default.
+      ;;
+
+    cygwin* | mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_cv_prog_cc_pic='-DDLL_EXPORT'
+      ;;
+
+    newsos6)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      # All OSF/1 code is PIC.
+      lt_cv_prog_cc_wl='-Wl,'
+      lt_cv_prog_cc_static='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      lt_cv_prog_cc_pic='-Kpic'
+      lt_cv_prog_cc_static='-dn'
+      lt_cv_prog_cc_shlib='-belf'
+      ;;
+
+    solaris*)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Wl,'
+      ;;
+
+    sunos4*)
+      lt_cv_prog_cc_pic='-PIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Qoption ld '
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      lt_cv_prog_cc_pic='-KPIC'
+      lt_cv_prog_cc_static='-Bstatic'
+      lt_cv_prog_cc_wl='-Wl,'
+      ;;
+
+    uts4*)
+      lt_cv_prog_cc_pic='-pic'
+      lt_cv_prog_cc_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_cv_prog_cc_pic='-Kconform_pic'
+	lt_cv_prog_cc_static='-Bstatic'
+      fi
+      ;;
+
+    *)
+      lt_cv_prog_cc_can_build_shared=no
+      ;;
+    esac
+  fi
+
+fi
+
+if test -z "$lt_cv_prog_cc_pic"; then
+  echo "$as_me:$LINENO: result: none" >&5
+echo "${ECHO_T}none" >&6
+else
+  echo "$as_me:$LINENO: result: $lt_cv_prog_cc_pic" >&5
+echo "${ECHO_T}$lt_cv_prog_cc_pic" >&6
+
+  # Check to make sure the pic_flag actually works.
+  echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_cv_prog_cc_pic works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_cv_prog_cc_pic works... $ECHO_C" >&6
+  if test "${lt_cv_prog_cc_pic_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $lt_cv_prog_cc_pic -DPIC"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+        case $host_os in
+      hpux9* | hpux10* | hpux11*)
+	# On HP-UX, both CC and GCC only warn that PIC is supported... then
+	# they create non-PIC objects.  So, if there were any warnings, we
+	# assume that PIC is not supported.
+	if test -s conftest.err; then
+	  lt_cv_prog_cc_pic_works=no
+	else
+	  lt_cv_prog_cc_pic_works=yes
+	fi
+	;;
+      *)
+	lt_cv_prog_cc_pic_works=yes
+	;;
+      esac
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+      lt_cv_prog_cc_pic_works=no
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+    CFLAGS="$save_CFLAGS"
+
+fi
+
+
+  if test "X$lt_cv_prog_cc_pic_works" = Xno; then
+    lt_cv_prog_cc_pic=
+    lt_cv_prog_cc_can_build_shared=no
+  else
+    lt_cv_prog_cc_pic=" $lt_cv_prog_cc_pic"
+  fi
+
+  echo "$as_me:$LINENO: result: $lt_cv_prog_cc_pic_works" >&5
+echo "${ECHO_T}$lt_cv_prog_cc_pic_works" >&6
+fi
+
+# Check for any special shared library compilation flags.
+if test -n "$lt_cv_prog_cc_shlib"; then
+  { echo "$as_me:$LINENO: WARNING: \`$CC' requires \`$lt_cv_prog_cc_shlib' to build shared libraries" >&5
+echo "$as_me: WARNING: \`$CC' requires \`$lt_cv_prog_cc_shlib' to build shared libraries" >&2;}
+  if echo "$old_CC $old_CFLAGS " | egrep -e "[ 	]$lt_cv_prog_cc_shlib[ 	]" >/dev/null; then :
+  else
+   { echo "$as_me:$LINENO: WARNING: add \`$lt_cv_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&5
+echo "$as_me: WARNING: add \`$lt_cv_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&2;}
+    lt_cv_prog_cc_can_build_shared=no
+  fi
+fi
+
+echo "$as_me:$LINENO: checking if $compiler static flag $lt_cv_prog_cc_static works" >&5
+echo $ECHO_N "checking if $compiler static flag $lt_cv_prog_cc_static works... $ECHO_C" >&6
+if test "${lt_cv_prog_cc_static_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+    lt_cv_prog_cc_static_works=no
+  save_LDFLAGS="$LDFLAGS"
+  LDFLAGS="$LDFLAGS $lt_cv_prog_cc_static"
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  lt_cv_prog_cc_static_works=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  LDFLAGS="$save_LDFLAGS"
+
+fi
+
+
+# Belt *and* braces to stop my trousers falling down:
+test "X$lt_cv_prog_cc_static_works" = Xno && lt_cv_prog_cc_static=
+echo "$as_me:$LINENO: result: $lt_cv_prog_cc_static_works" >&5
+echo "${ECHO_T}$lt_cv_prog_cc_static_works" >&6
+
+pic_flag="$lt_cv_prog_cc_pic"
+special_shlib_compile_flags="$lt_cv_prog_cc_shlib"
+wl="$lt_cv_prog_cc_wl"
+link_static_flag="$lt_cv_prog_cc_static"
+no_builtin_flag="$lt_cv_prog_cc_no_builtin"
+can_build_shared="$lt_cv_prog_cc_can_build_shared"
+
+
+# Check to see if options -o and -c are simultaneously supported by compiler
+echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_compiler_c_o+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+$rm -r conftest 2>/dev/null
+mkdir conftest
+cd conftest
+echo "int some_variable = 0;" > conftest.$ac_ext
+mkdir out
+# According to Tom Tromey, Ian Lance Taylor reported there are C compilers
+# that will create temporary files in the current directory regardless of
+# the output directory.  Thus, making CWD read-only will cause this test
+# to fail, enabling locking or at least warning the user not to do parallel
+# builds.
+chmod -w .
+save_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -o out/conftest2.$ac_objext"
+compiler_c_o=no
+if { (eval echo configure:4855: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>out/conftest.err; } && test -s out/conftest2.$ac_objext; then
+  # The compiler can only warn and ignore the option if not recognized
+  # So say no if there are warnings
+  if test -s out/conftest.err; then
+    lt_cv_compiler_c_o=no
+  else
+    lt_cv_compiler_c_o=yes
+  fi
+else
+  # Append any errors to the config.log.
+  cat out/conftest.err 1>&5
+  lt_cv_compiler_c_o=no
+fi
+CFLAGS="$save_CFLAGS"
+chmod u+w .
+$rm conftest* out/*
+rmdir out
+cd ..
+rmdir conftest
+$rm -r conftest 2>/dev/null
+
+fi
+
+compiler_c_o=$lt_cv_compiler_c_o
+echo "$as_me:$LINENO: result: $compiler_c_o" >&5
+echo "${ECHO_T}$compiler_c_o" >&6
+
+if test x"$compiler_c_o" = x"yes"; then
+  # Check to see if we can write to a .lo
+  echo "$as_me:$LINENO: checking if $compiler supports -c -o file.lo" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.lo... $ECHO_C" >&6
+  if test "${lt_cv_compiler_o_lo+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+  lt_cv_compiler_o_lo=no
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -c -o conftest.lo"
+  save_objext="$ac_objext"
+  ac_objext=lo
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int some_variable = 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+      # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+    if test -s conftest.err; then
+      lt_cv_compiler_o_lo=no
+    else
+      lt_cv_compiler_o_lo=yes
+    fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  ac_objext="$save_objext"
+  CFLAGS="$save_CFLAGS"
+
+fi
+
+  compiler_o_lo=$lt_cv_compiler_o_lo
+  echo "$as_me:$LINENO: result: $compiler_o_lo" >&5
+echo "${ECHO_T}$compiler_o_lo" >&6
+else
+  compiler_o_lo=no
+fi
+
+# Check to see if we can do hard links to lock some files if needed
+hard_links="nottested"
+if test "$compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+  if test "$hard_links" = no; then
+    { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+if test "$GCC" = yes; then
+  # Check to see if options -fno-rtti -fno-exceptions are supported by compiler
+  echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -c conftest.$ac_ext"
+  compiler_rtti_exceptions=no
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int some_variable = 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+      # The compiler can only warn and ignore the option if not recognized
+    # So say no if there are warnings
+    if test -s conftest.err; then
+      compiler_rtti_exceptions=no
+    else
+      compiler_rtti_exceptions=yes
+    fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  CFLAGS="$save_CFLAGS"
+  echo "$as_me:$LINENO: result: $compiler_rtti_exceptions" >&5
+echo "${ECHO_T}$compiler_rtti_exceptions" >&6
+
+  if test "$compiler_rtti_exceptions" = "yes"; then
+    no_builtin_flag=' -fno-builtin -fno-rtti -fno-exceptions'
+  else
+    no_builtin_flag=' -fno-builtin'
+  fi
+fi
+
+# See if the linker supports building shared libraries.
+echo "$as_me:$LINENO: checking whether the linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+allow_undefined_flag=
+no_undefined_flag=
+need_lib_prefix=unknown
+need_version=unknown
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+archive_cmds=
+archive_expsym_cmds=
+old_archive_from_new_cmds=
+old_archive_from_expsyms_cmds=
+export_dynamic_flag_spec=
+whole_archive_flag_spec=
+thread_safe_flag_spec=
+hardcode_into_libs=no
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+hardcode_shlibpath_var=unsupported
+runpath_var=
+link_all_deplibs=unknown
+always_export_symbols=no
+export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | sed '\''s/.* //'\'' | sort | uniq > $export_symbols'
+# include_expsyms should be a list of space-separated symbols to be *always*
+# included in the symbol list
+include_expsyms=
+# exclude_expsyms can be an egrep regular expression of symbols to exclude
+# it will be wrapped by ` (' and `)$', so one must not match beginning or
+# end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+# as well as any symbol that contains `d'.
+exclude_expsyms="_GLOBAL_OFFSET_TABLE_"
+# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+# platforms (ab)use it in PIC code, but their linkers get confused if
+# the symbol is explicitly referenced.  Since portable code cannot
+# rely on this symbol name, it's probably fine to never include it in
+# preloaded symbol tables.
+extract_expsyms_cmds=
+
+case $host_os in
+cygwin* | mingw* | pw32*)
+  # FIXME: the MSVC++ port hasn't been tested in a loooong time
+  # When not using gcc, we currently assume that we are using
+  # Microsoft Visual C++.
+  if test "$GCC" != yes; then
+    with_gnu_ld=no
+  fi
+  ;;
+openbsd*)
+  with_gnu_ld=no
+  ;;
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+  # If archive_cmds runs LD, not CC, wlarc should be empty
+  wlarc='${wl}'
+
+  # See if GNU ld supports shared libraries.
+  case $host_os in
+  aix3* | aix4* | aix5*)
+    # On AIX, the GNU linker is very broken
+    # Note:Check GNU linker on AIX 5-IA64 when/if it becomes available.
+    ld_shlibs=no
+    cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+    ;;
+
+  amigaos*)
+    archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+
+    # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+    # that the semantics of dynamic libraries on AmigaOS, at least up
+    # to version 4, is to share data among multiple programs linked
+    # with the same dynamic library.  Since this doesn't match the
+    # behavior of shared libraries on other platforms, we can use
+    # them.
+    ld_shlibs=no
+    ;;
+
+  beos*)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      allow_undefined_flag=unsupported
+      # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+      # support --undefined.  This deserves some investigation.  FIXME
+      archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec='-L$libdir'
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+
+    extract_expsyms_cmds='test -f $output_objdir/impgen.c || \
+      sed -e "/^# \/\* impgen\.c starts here \*\//,/^# \/\* impgen.c ends here \*\// { s/^# //;s/^# *$//; p; }" -e d < $''0 > $output_objdir/impgen.c~
+      test -f $output_objdir/impgen.exe || (cd $output_objdir && \
+      if test "x$HOST_CC" != "x" ; then $HOST_CC -o impgen impgen.c ; \
+      else $CC -o impgen impgen.c ; fi)~
+      $output_objdir/impgen $dir/$soroot > $output_objdir/$soname-def'
+
+    old_archive_from_expsyms_cmds='$DLLTOOL --as=$AS --dllname $soname --def $output_objdir/$soname-def --output-lib $output_objdir/$newlib'
+
+    # cygwin and mingw dlls have different entry points and sets of symbols
+    # to exclude.
+    # FIXME: what about values for MSVC?
+    dll_entry=__cygwin_dll_entry@12
+    dll_exclude_symbols=DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12~
+    case $host_os in
+    mingw*)
+      # mingw values
+      dll_entry=_DllMainCRTStartup@12
+      dll_exclude_symbols=DllMain@12,DllMainCRTStartup@12,DllEntryPoint@12~
+      ;;
+    esac
+
+    # mingw and cygwin differ, and it's simplest to just exclude the union
+    # of the two symbol sets.
+    dll_exclude_symbols=DllMain@12,_cygwin_dll_entry@12,_cygwin_noncygwin_dll_entry@12,DllMainCRTStartup@12,DllEntryPoint@12
+
+    # recent cygwin and mingw systems supply a stub DllMain which the user
+    # can override, but on older systems we have to supply one (in ltdll.c)
+    if test "x$lt_cv_need_dllmain" = "xyes"; then
+      ltdll_obj='$output_objdir/$soname-ltdll.'"$ac_objext "
+      ltdll_cmds='test -f $output_objdir/$soname-ltdll.c || sed -e "/^# \/\* ltdll\.c starts here \*\//,/^# \/\* ltdll.c ends here \*\// { s/^# //; p; }" -e d < $''0 > $output_objdir/$soname-ltdll.c~
+	test -f $output_objdir/$soname-ltdll.$ac_objext || (cd $output_objdir && $CC -c $soname-ltdll.c)~'
+    else
+      ltdll_obj=
+      ltdll_cmds=
+    fi
+
+    # Extract the symbol export list from an `--export-all' def file,
+    # then regenerate the def file from the symbol export list, so that
+    # the compiled dll only exports the symbol export list.
+    # Be careful not to strip the DATA tag left be newer dlltools.
+    export_symbols_cmds="$ltdll_cmds"'
+      $DLLTOOL --export-all --exclude-symbols '$dll_exclude_symbols' --output-def $output_objdir/$soname-def '$ltdll_obj'$libobjs $convenience~
+      sed -e "1,/EXPORTS/d" -e "s/ @ [0-9]*//" -e "s/ *;.*$//" < $output_objdir/$soname-def > $export_symbols'
+
+    # If the export-symbols file already is a .def file (1st line
+    # is EXPORTS), use it as is.
+    # If DATA tags from a recent dlltool are present, honour them!
+    archive_expsym_cmds='if test "x`sed 1q $export_symbols`" = xEXPORTS; then
+	cp $export_symbols $output_objdir/$soname-def;
+      else
+	echo EXPORTS > $output_objdir/$soname-def;
+	_lt_hint=1;
+	cat $export_symbols | while read symbol; do
+	 set dummy \$symbol;
+	 case \$# in
+	   2) echo "   \$2 @ \$_lt_hint ; " >> $output_objdir/$soname-def;;
+	   4) echo "   \$2 \$3 \$4 ; " >> $output_objdir/$soname-def; _lt_hint=`expr \$_lt_hint - 1`;;
+	   *) echo "     \$2 @ \$_lt_hint \$3 ; " >> $output_objdir/$soname-def;;
+	 esac;
+	 _lt_hint=`expr 1 + \$_lt_hint`;
+	done;
+      fi~
+      '"$ltdll_cmds"'
+      $CC -Wl,--base-file,$output_objdir/$soname-base '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols '$dll_exclude_symbols' --def $output_objdir/$soname-def --base-file $output_objdir/$soname-base --output-exp $output_objdir/$soname-exp~
+      $CC -Wl,--base-file,$output_objdir/$soname-base $output_objdir/$soname-exp '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags~
+      $DLLTOOL --as=$AS --dllname $soname --exclude-symbols '$dll_exclude_symbols' --def $output_objdir/$soname-def --base-file $output_objdir/$soname-base --output-exp $output_objdir/$soname-exp --output-lib $output_objdir/$libname.dll.a~
+      $CC $output_objdir/$soname-exp '$lt_cv_cc_dll_switch' -Wl,-e,'$dll_entry' -o $output_objdir/$soname '$ltdll_obj'$libobjs $deplibs $compiler_flags'
+    ;;
+
+  netbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+      wlarc=
+    else
+      archive_cmds='$CC -shared -nodefaultlibs $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared -nodefaultlibs $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    fi
+    ;;
+
+  solaris* | sysv5*)
+    if $LD -v 2>&1 | egrep 'BFD 2\.8' > /dev/null; then
+      ld_shlibs=no
+      cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+    elif $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+  sunos4*)
+    archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+    wlarc=
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+    else
+      ld_shlibs=no
+    fi
+    ;;
+  esac
+
+  if test "$ld_shlibs" = yes; then
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    case $host_os in
+    cygwin* | mingw* | pw32*)
+      # dlltool doesn't understand --whole-archive et. al.
+      whole_archive_flag_spec=
+      ;;
+    *)
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | egrep 'no-whole-archive' > /dev/null; then
+	whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+	whole_archive_flag_spec=
+      fi
+      ;;
+    esac
+  fi
+else
+  # PORTME fill in a description of your system's linker (not GNU ld)
+  case $host_os in
+  aix3*)
+    allow_undefined_flag=unsupported
+    always_export_symbols=yes
+    archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+    # Note: this linker hardcodes the directories in LIBPATH if there
+    # are no directories specified by -L.
+    hardcode_minus_L=yes
+    if test "$GCC" = yes && test -z "$link_static_flag"; then
+      # Neither direct hardcoding nor static linking is supported with a
+      # broken collect2.
+      hardcode_direct=unsupported
+    fi
+    ;;
+
+  aix4* | aix5*)
+    if test "$host_cpu" = ia64; then
+      # On IA64, the linker does run time linking by default, so we don't
+      # have to do anything special.
+      aix_use_runtimelinking=no
+      exp_sym_flag='-Bexport'
+      no_entry_flag=""
+    else
+      aix_use_runtimelinking=no
+
+      # Test if we are trying to use run time linking or normal
+      # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+      # need to do runtime linking.
+      case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+	for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	done
+      esac
+
+      exp_sym_flag='-bexport'
+      no_entry_flag='-bnoentry'
+    fi
+
+    # When large executables or shared objects are built, AIX ld can
+    # have problems creating the table of contents.  If linking a library
+    # or program results in "error TOC overflow" add -mminimal-toc to
+    # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+    # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+    hardcode_direct=yes
+    archive_cmds=''
+    hardcode_libdir_separator=':'
+    if test "$GCC" = yes; then
+      case $host_os in aix4.[012]|aix4.[012].*)
+	collect2name=`${CC} -print-prog-name=collect2`
+	if test -f "$collect2name" && \
+	  strings "$collect2name" | grep resolve_lib_name >/dev/null
+	then
+	  # We have reworked collect2
+	  hardcode_direct=yes
+	else
+	  # We have old collect2
+	  hardcode_direct=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L=yes
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_libdir_separator=
+	fi
+      esac
+
+      shared_flag='-shared'
+    else
+      # not using gcc
+      if test "$host_cpu" = ia64; then
+	shared_flag='${wl}-G'
+      else
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag='${wl}-G'
+	else
+	  shared_flag='${wl}-bM:SRE'
+	fi
+      fi
+    fi
+
+    # It seems that -bexpall can do strange things, so it is better to
+    # generate a list of symbols to export.
+    always_export_symbols=yes
+    if test "$aix_use_runtimelinking" = yes; then
+      # Warning - without using the other runtime loading flags (-brtl),
+      # -berok will link without error, but may produce a broken library.
+      allow_undefined_flag='-berok'
+      hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:/usr/lib:/lib'
+      archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+    else
+      if test "$host_cpu" = ia64; then
+	hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+	allow_undefined_flag="-z nodefs"
+	archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname ${wl}-h$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+      else
+	hardcode_libdir_flag_spec='${wl}-bnolibpath ${wl}-blibpath:$libdir:/usr/lib:/lib'
+	# Warning - without using the other run time loading flags,
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='${wl}-berok'
+	# This is a bit strange, but is similar to how AIX traditionally builds
+	# it's shared libraries.
+	archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"' ~$AR -crlo $objdir/$libname$release.a $objdir/$soname'
+      fi
+    fi
+    ;;
+
+  amigaos*)
+    archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    # see comment about different semantics on the GNU ld section
+    ld_shlibs=no
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    # hardcode_libdir_flag_spec is actually meaningless, as there is
+    # no search path for DLLs.
+    hardcode_libdir_flag_spec=' '
+    allow_undefined_flag=unsupported
+    # Tell ltmain to make .lib files, not .a files.
+    libext=lib
+    # FIXME: Setting linknames here is a bad hack.
+    archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | sed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+    # The linker will automatically build a .lib file if we build a DLL.
+    old_archive_from_new_cmds='true'
+    # FIXME: Should let the user specify the lib program.
+    old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs'
+    fix_srcfile_path='`cygpath -w "$srcfile"`'
+    ;;
+
+  darwin* | rhapsody*)
+    case "$host_os" in
+    rhapsody* | darwin1.[012])
+      allow_undefined_flag='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      allow_undefined_flag='-flat_namespace -undefined suppress'
+      ;;
+    esac
+    # FIXME: Relying on posixy $() will cause problems for
+    #        cross-compilation, but unfortunately the echo tests do not
+    #        yet detect zsh echo's removal of \ escapes.  Also zsh mangles
+    #	     `"' quotes if we put them in here... so don't!
+    archive_cmds='$nonopt $(test .$module = .yes && echo -bundle || echo -dynamiclib) $allow_undefined_flag -o $lib $libobjs $deplibs$linker_flags -install_name $rpath/$soname $verstring'
+    # We need to add '_' to the symbols in $export_symbols first
+    #archive_expsym_cmds="$archive_cmds"' && strip -s $export_symbols'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    whole_archive_flag_spec='-all_load $convenience'
+    ;;
+
+  freebsd1*)
+    ld_shlibs=no
+    ;;
+
+  # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+  # support.  Future versions do this automatically, but an explicit c++rt0.o
+  # does not break anything, and helps significantly (at the cost of a little
+  # extra space).
+  freebsd2.2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+  freebsd2*)
+    archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+  freebsd*)
+    archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  hpux9* | hpux10* | hpux11*)
+    case $host_os in
+    hpux9*) archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' ;;
+    *) archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' ;;
+    esac
+    hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+    hardcode_libdir_separator=:
+    hardcode_direct=yes
+    hardcode_minus_L=yes # Not in the search PATH, but as the default
+			 # location of the library.
+    export_dynamic_flag_spec='${wl}-E'
+    ;;
+
+  irix5* | irix6* | nonstopux*)
+    if test "$GCC" = yes; then
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+    else
+      archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    link_all_deplibs=yes
+    ;;
+
+  netbsd*)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+    else
+      archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+    fi
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  newsos6)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    hardcode_shlibpath_var=no
+    ;;
+
+  openbsd*)
+    hardcode_direct=yes
+    hardcode_shlibpath_var=no
+    if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+    else
+      case "$host_os" in
+      openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_libdir_flag_spec='-R$libdir'
+        ;;
+      *)
+        archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+        ;;
+      esac
+    fi
+    ;;
+
+  os2*)
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_minus_L=yes
+    allow_undefined_flag=unsupported
+    archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+    old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+    ;;
+
+  osf3*)
+    if test "$GCC" = yes; then
+      allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+      archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+    else
+      allow_undefined_flag=' -expect_unresolved \*'
+      archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+    fi
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator=:
+    ;;
+
+  osf4* | osf5*)	# as osf3* with the addition of -msym flag
+    if test "$GCC" = yes; then
+      allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+      archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    else
+      allow_undefined_flag=' -expect_unresolved \*'
+      archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      archive_expsym_cmds='for i in `cat $export_symbols`; do printf "-exported_symbol " >> $lib.exp; echo "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+      $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+      #Both c and cxx compiler support -rpath directly
+      hardcode_libdir_flag_spec='-rpath $libdir'
+    fi
+    hardcode_libdir_separator=:
+    ;;
+
+  sco3.2v5*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_shlibpath_var=no
+    runpath_var=LD_RUN_PATH
+    hardcode_runpath_var=yes
+    export_dynamic_flag_spec='${wl}-Bexport'
+    ;;
+
+  solaris*)
+    # gcc --version < 3.0 without binutils cannot create self contained
+    # shared libraries reliably, requiring libgcc.a to resolve some of
+    # the object symbols generated in some cases.  Libraries that use
+    # assert need libgcc.a to resolve __eprintf, for example.  Linking
+    # a copy of libgcc.a into every shared library to guarantee resolving
+    # such symbols causes other problems:  According to Tim Van Holder
+    # <tim.van.holder@pandora.be>, C++ libraries end up with a separate
+    # (to the application) exception stack for one thing.
+    no_undefined_flag=' -z defs'
+    if test "$GCC" = yes; then
+      case `$CC --version 2>/dev/null` in
+      [12].*)
+	cat <<EOF 1>&2
+
+*** Warning: Releases of GCC earlier than version 3.0 cannot reliably
+*** create self contained shared libraries on Solaris systems, without
+*** introducing a dependency on libgcc.a.  Therefore, libtool is disabling
+*** -no-undefined support, which will at least allow you to build shared
+*** libraries.  However, you may find that when you link such libraries
+*** into an application without using GCC, you have to manually add
+*** \`gcc --print-libgcc-file-name\` to the link command.  We urge you to
+*** upgrade to a newer version of GCC.  Another option is to rebuild your
+*** current GCC to use the GNU linker from GNU binutils 2.9.1 or newer.
+
+EOF
+        no_undefined_flag=
+	;;
+      esac
+    fi
+    # $CC -shared without GNU ld will not create a library from C++
+    # object files and a static libstdc++, better avoid it by now
+    archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+    hardcode_libdir_flag_spec='-R$libdir'
+    hardcode_shlibpath_var=no
+    case $host_os in
+    solaris2.[0-5] | solaris2.[0-5].*) ;;
+    *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+      whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;;
+    esac
+    link_all_deplibs=yes
+    ;;
+
+  sunos4*)
+    if test "x$host_vendor" = xsequent; then
+      # Use $CC to link under sequent, because it throws in some extra .o
+      # files that make .init and .fini sections work.
+      archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+    else
+      archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+    fi
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_direct=yes
+    hardcode_minus_L=yes
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4)
+    case $host_vendor in
+      sni)
+        archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_direct=yes # is this really true???
+        ;;
+      siemens)
+        ## LD is ld it makes a PLAMLIB
+        ## CC just makes a GrossModule.
+        archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+        reload_cmds='$CC -r -o $output$reload_objs'
+        hardcode_direct=no
+        ;;
+      motorola)
+        archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+        hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+        ;;
+    esac
+    runpath_var='LD_RUN_PATH'
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4.3*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_shlibpath_var=no
+    export_dynamic_flag_spec='-Bexport'
+    ;;
+
+  sysv5*)
+    no_undefined_flag=' -z text'
+    # $CC -shared without GNU ld will not create a library from C++
+    # object files and a static libstdc++, better avoid it by now
+    archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+    hardcode_libdir_flag_spec=
+    hardcode_shlibpath_var=no
+    runpath_var='LD_RUN_PATH'
+    ;;
+
+  uts4*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  dgux*)
+    archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_libdir_flag_spec='-L$libdir'
+    hardcode_shlibpath_var=no
+    ;;
+
+  sysv4*MP*)
+    if test -d /usr/nec; then
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ld_shlibs=yes
+    fi
+    ;;
+
+  sysv4.2uw2*)
+    archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+    hardcode_direct=yes
+    hardcode_minus_L=no
+    hardcode_shlibpath_var=no
+    hardcode_runpath_var=yes
+    runpath_var=LD_RUN_PATH
+    ;;
+
+  sysv5uw7* | unixware7*)
+    no_undefined_flag='${wl}-z ${wl}text'
+    if test "$GCC" = yes; then
+      archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+    else
+      archive_cmds='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+    fi
+    runpath_var='LD_RUN_PATH'
+    hardcode_shlibpath_var=no
+    ;;
+
+  *)
+    ld_shlibs=no
+    ;;
+  esac
+fi
+echo "$as_me:$LINENO: result: $ld_shlibs" >&5
+echo "${ECHO_T}$ld_shlibs" >&6
+test "$ld_shlibs" = no && can_build_shared=no
+
+# Check hardcoding attributes.
+echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" || \
+   test -n "$runpath_var"; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$hardcode_shlibpath_var" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action" >&5
+echo "${ECHO_T}$hardcode_action" >&6
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+# PORTME Fill in your ld.so characteristics
+echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}.so$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}.so$major ${libname}${release}.so$versuffix $libname.so'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+	if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	     echo ' yes '
+	     echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	  :
+	else
+	  can_build_shared=no
+	fi
+	;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can
+    # not hardcode correct soname into executable. Probably we can
+    # add versioning support to collect2, so additional links can
+    # be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}.so$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}.so'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  export_dynamic_flag_spec=-rdynamic
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  need_version=no
+  need_lib_prefix=no
+  case $GCC,$host_os in
+  yes,cygwin*)
+    library_names_spec='$libname.dll.a'
+    soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll'
+    postinstall_cmds='dlpath=`bash 2>&1 -c '\''. $dir/${file}i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog .libs/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`bash 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    ;;
+  yes,mingw*)
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll'
+    sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | sed -e "s/^libraries://" -e "s/;/ /g" -e "s,=/,/,g"`
+    ;;
+  yes,pw32*)
+    library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | sed -e 's/./-/g'`${versuffix}.dll'
+    ;;
+  *)
+    library_names_spec='${libname}`echo ${release} | sed -e 's/[.]/-/g'`${versuffix}.dll $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  # FIXME: Relying on posixy $() will cause problems for
+  #        cross-compilation, but unfortunately the echo tests do not
+  #        yet detect zsh echo's removal of \ escapes.
+  library_names_spec='${libname}${release}${versuffix}.$(test .$module = .yes && echo so || echo dylib) ${libname}${release}${major}.$(test .$module = .yes && echo so || echo dylib) ${libname}.$(test .$module = .yes && echo so || echo dylib)'
+  soname_spec='${libname}${release}${major}.$(test .$module = .yes && echo so || echo dylib)'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}.so$versuffix $libname.so$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  *)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so${major} ${libname}.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  dynamic_linker="$host_os dld.sl"
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  shlibpath_var=SHLIB_PATH
+  shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+  library_names_spec='${libname}${release}.sl$versuffix ${libname}${release}.sl$major $libname.sl'
+  soname_spec='${libname}${release}.sl$major'
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)          version_type=irix ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}.so$major'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so $libname.so'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 ") libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 ") libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 ") libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux-gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major ${libname}${release}.so ${libname}.so'
+    soname_spec='${libname}${release}.so$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case "$host_os" in
+    openbsd2.[89] | openbsd2.[89].*)
+      shlibpath_overrides_runpath=no
+      ;;
+    *)
+      shlibpath_overrides_runpath=yes
+      ;;
+    esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+os2*)
+  libname_spec='$name'
+  need_lib_prefix=no
+  library_names_spec='$libname.dll $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_version=no
+  soname_spec='${libname}${release}.so'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}.so$major'
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}.so$versuffix ${libname}.so$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
+  soname_spec='${libname}${release}.so$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname.so.$versuffix $libname.so.$major $libname.so'
+    soname_spec='$libname.so.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+# Report the final consequences.
+echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $can_build_shared" >&5
+echo "${ECHO_T}$can_build_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+
+aix4*)
+  if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+    test "$enable_shared" = yes && enable_static=no
+  fi
+  ;;
+esac
+echo "$as_me:$LINENO: result: $enable_shared" >&5
+echo "${ECHO_T}$enable_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+echo "$as_me:$LINENO: result: $enable_static" >&5
+echo "${ECHO_T}$enable_static" >&6
+
+if test "$hardcode_action" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  *)
+    echo "$as_me:$LINENO: checking for shl_load" >&5
+echo $ECHO_N "checking for shl_load... $ECHO_C" >&6
+if test "${ac_cv_func_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+char (*f) () = shl_load;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shl_load;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+echo "${ECHO_T}$ac_cv_func_shl_load" >&6
+if test $ac_cv_func_shl_load = yes; then
+  lt_cv_dlopen="shl_load"
+else
+  echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+int
+main ()
+{
+shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6
+if test $ac_cv_lib_dld_shl_load = yes; then
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"
+else
+  echo "$as_me:$LINENO: checking for dlopen" >&5
+echo $ECHO_N "checking for dlopen... $ECHO_C" >&6
+if test "${ac_cv_func_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+char (*f) () = dlopen;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != dlopen;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+echo "${ECHO_T}$ac_cv_func_dlopen" >&6
+if test $ac_cv_func_dlopen = yes; then
+  lt_cv_dlopen="dlopen"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_svld_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_svld_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6
+if test $ac_cv_lib_svld_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dld_link ();
+int
+main ()
+{
+dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_dld_link=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_dld_link=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6
+if test $ac_cv_lib_dld_dld_link = yes; then
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+        test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 6773 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self" >&6
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+    lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 6871 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+if test "$enable_shared" = yes && test "$GCC" = yes; then
+  case $archive_cmds in
+  *'~'*)
+    # FIXME: we may have to deal with multi-command sequences.
+    ;;
+  '$CC '*)
+    # Test whether the compiler implicitly links with -lc since on some
+    # systems, -lgcc has to come before -lc. If gcc already passes -lc
+    # to ld, don't add -lc before -lgcc.
+    echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+    if test "${lt_cv_archive_cmds_need_lc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  $rm conftest*
+    echo 'static int dummy;' > conftest.$ac_ext
+
+    if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+      soname=conftest
+      lib=conftest
+      libobjs=conftest.$ac_objext
+      deplibs=
+      wl=$lt_cv_prog_cc_wl
+      compiler_flags=-v
+      linker_flags=-v
+      verstring=
+      output_objdir=.
+      libname=conftest
+      save_allow_undefined_flag=$allow_undefined_flag
+      allow_undefined_flag=
+      if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+      then
+	lt_cv_archive_cmds_need_lc=no
+      else
+	lt_cv_archive_cmds_need_lc=yes
+      fi
+      allow_undefined_flag=$save_allow_undefined_flag
+    else
+      cat conftest.err 1>&5
+    fi
+fi
+
+    echo "$as_me:$LINENO: result: $lt_cv_archive_cmds_need_lc" >&5
+echo "${ECHO_T}$lt_cv_archive_cmds_need_lc" >&6
+    ;;
+  esac
+fi
+need_lc=${lt_cv_archive_cmds_need_lc-yes}
+
+# The second clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  :
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  test -f Makefile && make "$ltmain"
+fi
+
+if test -f "$ltmain"; then
+  trap "$rm \"${ofile}T\"; exit 1" 1 2 15
+  $rm -f "${ofile}T"
+
+  echo creating $ofile
+
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS SED \
+    AR AR_FLAGS CC LD LN_S NM SHELL \
+    reload_flag reload_cmds wl \
+    pic_flag link_static_flag no_builtin_flag export_dynamic_flag_spec \
+    thread_safe_flag_spec whole_archive_flag_spec libname_spec \
+    library_names_spec soname_spec \
+    RANLIB old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \
+    old_postuninstall_cmds archive_cmds archive_expsym_cmds postinstall_cmds \
+    postuninstall_cmds extract_expsyms_cmds old_archive_from_expsyms_cmds \
+    old_striplib striplib file_magic_cmd export_symbols_cmds \
+    deplibs_check_method allow_undefined_flag no_undefined_flag \
+    finish_cmds finish_eval global_symbol_pipe global_symbol_to_cdecl \
+    global_symbol_to_c_name_address \
+    hardcode_libdir_flag_spec hardcode_libdir_separator  \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    compiler_c_o compiler_o_lo need_locks exclude_expsyms include_expsyms; do
+
+    case $var in
+    reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    export_symbols_cmds | archive_cmds | archive_expsym_cmds | \
+    extract_expsyms_cmds | old_archive_from_expsyms_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    finish_cmds | sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  cat <<__EOF__ > "${ofile}T"
+#! $SHELL
+
+# `$echo "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996-2000 Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A sed that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="${SED} -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$need_lc
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# The default C compiler.
+CC=$lt_CC
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_wl
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_pic_flag
+pic_mode=$pic_mode
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_compiler_c_o
+
+# Can we write directly to a .lo ?
+compiler_o_lo=$lt_compiler_o_lo
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_link_static_flag
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_no_builtin_flag
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# ### END LIBTOOL CONFIG
+
+__EOF__
+
+  case $host_os in
+  aix3*)
+    cat <<\EOF >> "${ofile}T"
+
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+EOF
+    ;;
+  esac
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | os2*)
+    cat <<'EOF' >> "${ofile}T"
+      # This is a source program that is used to create dlls on Windows
+      # Don't remove nor modify the starting and closing comments
+# /* ltdll.c starts here */
+# #define WIN32_LEAN_AND_MEAN
+# #include <windows.h>
+# #undef WIN32_LEAN_AND_MEAN
+# #include <stdio.h>
+#
+# #ifndef __CYGWIN__
+# #  ifdef __CYGWIN32__
+# #    define __CYGWIN__ __CYGWIN32__
+# #  endif
+# #endif
+#
+# #ifdef __cplusplus
+# extern "C" {
+# #endif
+# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+# #ifdef __cplusplus
+# }
+# #endif
+#
+# #ifdef __CYGWIN__
+# #include <cygwin/cygwin_dll.h>
+# DECLARE_CYGWIN_DLL( DllMain );
+# #endif
+# HINSTANCE __hDllInstance_base;
+#
+# BOOL APIENTRY
+# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
+# {
+#   __hDllInstance_base = hInst;
+#   return TRUE;
+# }
+# /* ltdll.c ends here */
+	# This is a source program that is used to create import libraries
+	# on Windows for dlls which lack them. Don't remove nor modify the
+	# starting and closing comments
+# /* impgen.c starts here */
+# /*   Copyright (C) 1999-2000 Free Software Foundation, Inc.
+#
+#  This file is part of GNU libtool.
+#
+#  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#  */
+#
+# #include <stdio.h>		/* for printf() */
+# #include <unistd.h>		/* for open(), lseek(), read() */
+# #include <fcntl.h>		/* for O_RDONLY, O_BINARY */
+# #include <string.h>		/* for strdup() */
+#
+# /* O_BINARY isn't required (or even defined sometimes) under Unix */
+# #ifndef O_BINARY
+# #define O_BINARY 0
+# #endif
+#
+# static unsigned int
+# pe_get16 (fd, offset)
+#      int fd;
+#      int offset;
+# {
+#   unsigned char b[2];
+#   lseek (fd, offset, SEEK_SET);
+#   read (fd, b, 2);
+#   return b[0] + (b[1]<<8);
+# }
+#
+# static unsigned int
+# pe_get32 (fd, offset)
+#     int fd;
+#     int offset;
+# {
+#   unsigned char b[4];
+#   lseek (fd, offset, SEEK_SET);
+#   read (fd, b, 4);
+#   return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# static unsigned int
+# pe_as32 (ptr)
+#      void *ptr;
+# {
+#   unsigned char *b = ptr;
+#   return b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
+# }
+#
+# int
+# main (argc, argv)
+#     int argc;
+#     char *argv[];
+# {
+#     int dll;
+#     unsigned long pe_header_offset, opthdr_ofs, num_entries, i;
+#     unsigned long export_rva, export_size, nsections, secptr, expptr;
+#     unsigned long name_rvas, nexp;
+#     unsigned char *expdata, *erva;
+#     char *filename, *dll_name;
+#
+#     filename = argv[1];
+#
+#     dll = open(filename, O_RDONLY|O_BINARY);
+#     if (dll < 1)
+# 	return 1;
+#
+#     dll_name = filename;
+#
+#     for (i=0; filename[i]; i++)
+# 	if (filename[i] == '/' || filename[i] == '\\'  || filename[i] == ':')
+# 	    dll_name = filename + i +1;
+#
+#     pe_header_offset = pe_get32 (dll, 0x3c);
+#     opthdr_ofs = pe_header_offset + 4 + 20;
+#     num_entries = pe_get32 (dll, opthdr_ofs + 92);
+#
+#     if (num_entries < 1) /* no exports */
+# 	return 1;
+#
+#     export_rva = pe_get32 (dll, opthdr_ofs + 96);
+#     export_size = pe_get32 (dll, opthdr_ofs + 100);
+#     nsections = pe_get16 (dll, pe_header_offset + 4 +2);
+#     secptr = (pe_header_offset + 4 + 20 +
+# 	      pe_get16 (dll, pe_header_offset + 4 + 16));
+#
+#     expptr = 0;
+#     for (i = 0; i < nsections; i++)
+#     {
+# 	char sname[8];
+# 	unsigned long secptr1 = secptr + 40 * i;
+# 	unsigned long vaddr = pe_get32 (dll, secptr1 + 12);
+# 	unsigned long vsize = pe_get32 (dll, secptr1 + 16);
+# 	unsigned long fptr = pe_get32 (dll, secptr1 + 20);
+# 	lseek(dll, secptr1, SEEK_SET);
+# 	read(dll, sname, 8);
+# 	if (vaddr <= export_rva && vaddr+vsize > export_rva)
+# 	{
+# 	    expptr = fptr + (export_rva - vaddr);
+# 	    if (export_rva + export_size > vaddr + vsize)
+# 		export_size = vsize - (export_rva - vaddr);
+# 	    break;
+# 	}
+#     }
+#
+#     expdata = (unsigned char*)malloc(export_size);
+#     lseek (dll, expptr, SEEK_SET);
+#     read (dll, expdata, export_size);
+#     erva = expdata - export_rva;
+#
+#     nexp = pe_as32 (expdata+24);
+#     name_rvas = pe_as32 (expdata+32);
+#
+#     printf ("EXPORTS\n");
+#     for (i = 0; i<nexp; i++)
+#     {
+# 	unsigned long name_rva = pe_as32 (erva+name_rvas+i*4);
+# 	printf ("\t%s @ %ld ;\n", erva+name_rva, 1+ i);
+#     }
+#
+#     return 0;
+# }
+# /* impgen.c ends here */
+
+EOF
+    ;;
+  esac
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "${ofile}T" || (rm -f "${ofile}T"; exit 1)
+
+  mv -f "${ofile}T" "$ofile" || \
+    (rm -f "$ofile" && cp "${ofile}T" "$ofile" && rm -f "${ofile}T")
+  chmod +x "$ofile"
+fi
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+# Prevent multiple expansion
+
+
+
+# Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_PERL+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $PERL in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PERL="$PERL" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="/usr/bin/perl"
+  ;;
+esac
+fi
+PERL=$ac_cv_path_PERL
+
+if test -n "$PERL"; then
+  echo "$as_me:$LINENO: result: $PERL" >&5
+echo "${ECHO_T}$PERL" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+# Extract the first word of "sh", so it can be a program name with args.
+set dummy sh; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_SH+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $SH in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_SH="$SH" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_SH="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_path_SH" && ac_cv_path_SH="/bin/sh"
+  ;;
+esac
+fi
+SH=$ac_cv_path_SH
+
+if test -n "$SH"; then
+  echo "$as_me:$LINENO: result: $SH" >&5
+echo "${ECHO_T}$SH" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+
+echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
+echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6
+    # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+  enableval="$enable_maintainer_mode"
+  USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi;
+  echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
+echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6
+
+
+if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+
+      if test "X$prefix" = "XNONE"; then
+    acl_final_prefix="$ac_default_prefix"
+  else
+    acl_final_prefix="$prefix"
+  fi
+  if test "X$exec_prefix" = "XNONE"; then
+    acl_final_exec_prefix='${prefix}'
+  else
+    acl_final_exec_prefix="$exec_prefix"
+  fi
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-gnu-ld or --without-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+  withval="$with_gnu_ld"
+  test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi;
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  echo "$as_me:$LINENO: checking for ld used by GCC" >&5
+echo $ECHO_N "checking for ld used by GCC... $ECHO_C" >&6
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | [A-Za-z]:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6
+else
+  echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6
+fi
+if test "${acl_cv_path_LD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$LD"; then
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      acl_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      if "$acl_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
+	test "$with_gnu_ld" != no && break
+      else
+	test "$with_gnu_ld" != yes && break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+  echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6
+if test "${acl_cv_prog_gnu_ld+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+  acl_cv_prog_gnu_ld=yes
+else
+  acl_cv_prog_gnu_ld=no
+fi
+fi
+echo "$as_me:$LINENO: result: $acl_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$acl_cv_prog_gnu_ld" >&6
+with_gnu_ld=$acl_cv_prog_gnu_ld
+
+
+
+                                                echo "$as_me:$LINENO: checking for shared library run path origin" >&5
+echo $ECHO_N "checking for shared library run path origin... $ECHO_C" >&6
+if test "${acl_cv_rpath+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+    CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+    ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+    . ./conftest.sh
+    rm -f ./conftest.sh
+    acl_cv_rpath=done
+
+fi
+echo "$as_me:$LINENO: result: $acl_cv_rpath" >&5
+echo "${ECHO_T}$acl_cv_rpath" >&6
+  wl="$acl_cv_wl"
+  libext="$acl_cv_libext"
+  shlibext="$acl_cv_shlibext"
+  hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+  hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+  hardcode_direct="$acl_cv_hardcode_direct"
+  hardcode_minus_L="$acl_cv_hardcode_minus_L"
+  sys_lib_search_path_spec="$acl_cv_sys_lib_search_path_spec"
+  sys_lib_dlsearch_path_spec="$acl_cv_sys_lib_dlsearch_path_spec"
+    # Check whether --enable-rpath or --disable-rpath was given.
+if test "${enable_rpath+set}" = set; then
+  enableval="$enable_rpath"
+  :
+else
+  enable_rpath=yes
+fi;
+
+
+
+
+
+
+
+    use_additional=yes
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-libiconv-prefix or --without-libiconv-prefix was given.
+if test "${with_libiconv_prefix+set}" = set; then
+  withval="$with_libiconv_prefix"
+
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+
+fi;
+      LIBICONV=
+  LTLIBICONV=
+  INCICONV=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='iconv '
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value"
+          else
+                                    :
+          fi
+        else
+                              found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            else
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIBICONV; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  else
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+                        LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                                LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+              else
+                                                                                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                                if test "$hardcode_direct" = yes; then
+                                                      LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                                                            LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+                                                            haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                                                                                haveit=
+                    for x in $LDFLAGS $LIBICONV; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                                                                                        LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+                    else
+                                                                                                                                                                                LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                                LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a"
+              else
+                                                LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name"
+              fi
+            fi
+                        additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INCICONV; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                                            INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+                        if test -n "$found_la"; then
+                                                        save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+                            for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIBICONV; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIBICONV; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                                                                  haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                                                                  haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                                        LIBICONV="${LIBICONV}${LIBICONV:+ }$dep"
+                    LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+                                                            LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+            LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name"
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+                        alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+            acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+    else
+            for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+            for found_dir in $ltrpathdirs; do
+      LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir"
+    done
+  fi
+
+
+
+
+
+
+
+          am_save_CPPFLAGS="$CPPFLAGS"
+
+  for element in $INCICONV; do
+    haveit=
+    for x in $CPPFLAGS; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+    fi
+  done
+
+
+  echo "$as_me:$LINENO: checking for iconv" >&5
+echo $ECHO_N "checking for iconv... $ECHO_C" >&6
+if test "${am_cv_func_iconv+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+    am_cv_func_iconv="no, consider installing GNU libiconv"
+    am_cv_lib_iconv=no
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  am_cv_func_iconv=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+    if test "$am_cv_func_iconv" != yes; then
+      am_save_LIBS="$LIBS"
+      LIBS="$LIBS $LIBICONV"
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  am_cv_lib_iconv=yes
+        am_cv_func_iconv=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+      LIBS="$am_save_LIBS"
+    fi
+
+fi
+echo "$as_me:$LINENO: result: $am_cv_func_iconv" >&5
+echo "${ECHO_T}$am_cv_func_iconv" >&6
+  if test "$am_cv_func_iconv" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ICONV 1
+_ACEOF
+
+  fi
+  if test "$am_cv_lib_iconv" = yes; then
+    echo "$as_me:$LINENO: checking how to link with libiconv" >&5
+echo $ECHO_N "checking how to link with libiconv... $ECHO_C" >&6
+    echo "$as_me:$LINENO: result: $LIBICONV" >&5
+echo "${ECHO_T}$LIBICONV" >&6
+  else
+            CPPFLAGS="$am_save_CPPFLAGS"
+    LIBICONV=
+    LTLIBICONV=
+  fi
+
+
+
+  if test "$am_cv_func_iconv" = yes; then
+    echo "$as_me:$LINENO: checking for iconv declaration" >&5
+echo $ECHO_N "checking for iconv declaration... $ECHO_C" >&6
+    if test "${am_cv_proto_iconv+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  am_cv_proto_iconv_arg1=""
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+am_cv_proto_iconv_arg1="const"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+      am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"
+fi
+
+    am_cv_proto_iconv=`echo "$am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+    echo "$as_me:$LINENO: result: ${ac_t:-
+         }$am_cv_proto_iconv" >&5
+echo "${ECHO_T}${ac_t:-
+         }$am_cv_proto_iconv" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define ICONV_CONST $am_cv_proto_iconv_arg1
+_ACEOF
+
+  fi
+
+
+#AC_CHECK_FUNCS(iconv_open,,AC_MSG_ERROR([
+#iconv_open not found try to install replacement from
+#http://www.gnu.org/software/libiconv/
+#ftp://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz]))
+
+
+for ac_func in vsnprintf
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+          ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@VERSION@,$VERSION,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@target@,$target,;t t
+s,@target_cpu@,$target_cpu,;t t
+s,@target_vendor@,$target_vendor,;t t
+s,@target_os@,$target_os,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@COMPAT32BITFLAGS@,$COMPAT32BITFLAGS,;t t
+s,@UNICODE_VERSION@,$UNICODE_VERSION,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@LN_S@,$LN_S,;t t
+s,@ECHO@,$ECHO,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@STRIP@,$STRIP,;t t
+s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
+s,@LIBTOOL@,$LIBTOOL,;t t
+s,@PERL@,$PERL,;t t
+s,@SH@,$SH,;t t
+s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t
+s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t
+s,@MAINT@,$MAINT,;t t
+s,@LIBICONV@,$LIBICONV,;t t
+s,@LTLIBICONV@,$LTLIBICONV,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
Index: /gsdl/trunk/gs2common/indexers/packages/unac/configure.ac
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/configure.ac	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/configure.ac	(revision 16561)
@@ -0,0 +1,77 @@
+#
+# Copyright (C) 2000, 2001, 2002 Loic Dachary <loic@senga.org>
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+AC_INIT(config.h.in)
+VERSION=`cat .version`
+AC_SUBST(VERSION)
+AC_PREREQ(2.13)
+
+
+dnl get $target_cpu, $target_vendor, and $target_os
+dnl (as well as host_*)
+AC_CANONICAL_SYSTEM
+
+AC_CONFIG_HEADER(config.h)
+
+AC_PROG_MAKE_SET
+
+AC_MSG_CHECKING(to see if architecture is 64-bit)
+arch_64bit=no
+case "$host_cpu" in
+x86_64) arch_64bit=yes ;;
+esac
+
+if test "$arch_64bit" = yes; then
+  AC_MSG_RESULT(yes)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS="-m32"
+  fi
+else
+  AC_MSG_RESULT(no)
+  if test -z "$COMPAT32BITFLAGS" ; then
+    COMPAT32BITFLAGS=
+  fi
+fi
+AC_SUBST(COMPAT32BITFLAGS)
+
+dnl
+dnl Unicode version used by unac, as published at http://www.unicode.org/Public/
+dnl
+UNICODE_VERSION=3.2.0
+AC_SUBST(UNICODE_VERSION)
+
+AC_PROG_CC
+AC_AIX
+AC_COMPILE_WARNINGS
+
+AC_PROG_LIBTOOL
+
+AC_PATH_PROG(PERL, perl, /usr/bin/perl)
+AC_PATH_PROG(SH, sh, /bin/sh)
+
+AM_MAINTAINER_MODE
+
+AM_ICONV
+
+#AC_CHECK_FUNCS(iconv_open,,AC_MSG_ERROR([
+#iconv_open not found try to install replacement from
+#http://www.gnu.org/software/libiconv/
+#ftp://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz]))
+
+AC_CHECK_FUNCS(vsnprintf)
+
+AC_OUTPUT(Makefile)
Index: /gsdl/trunk/gs2common/indexers/packages/unac/iconv.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/iconv.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/iconv.h	(revision 16561)
@@ -0,0 +1,131 @@
+/* Copyright (C) 1999-2003 Free Software Foundation, Inc.
+   This file is part of the GNU LIBICONV Library.
+
+   The GNU LIBICONV Library is free software; you can redistribute it
+   and/or modify it under the terms of the GNU Library General Public
+   License as published by the Free Software Foundation; either version 2
+   of the License, or (at your option) any later version.
+
+   The GNU LIBICONV Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU LIBICONV Library; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* When installed, this file is called "iconv.h". */
+
+#ifndef _LIBICONV_H
+#define _LIBICONV_H
+
+#define _LIBICONV_VERSION 0x0109    /* version number: (major<<8) + minor */
+extern int _libiconv_version;       /* Likewise */
+
+/* We would like to #include any system header file which could define
+   iconv_t, 1. in order to eliminate the risk that the user gets compilation
+   errors because some other system header file includes /usr/include/iconv.h
+   which defines iconv_t or declares iconv after this file, 2. when compiling
+   for LIBICONV_PLUG, we need the proper iconv_t type in order to produce
+   binary compatible code.
+   But gcc's #include_next is not portable. Thus, once libiconv's iconv.h
+   has been installed in /usr/local/include, there is no way any more to
+   include the original /usr/include/iconv.h. We simply have to get away
+   without it.
+   Ad 1. The risk that a system header file does
+   #include "iconv.h"  or  #include_next "iconv.h"
+   is small. They all do #include <iconv.h>.
+   Ad 2. The iconv_t type is a pointer type in all cases I have seen. (It
+   has to be a scalar type because (iconv_t)(-1) is a possible return value
+   from iconv_open().) */
+
+/* Define iconv_t ourselves. */
+#undef iconv_t
+#define iconv_t libiconv_t
+typedef void* iconv_t;
+
+/* Get size_t declaration. */
+#include <stddef.h>
+
+/* Get errno declaration and values. */
+#include <errno.h>
+/* Some systems, like SunOS 4, don't have EILSEQ. Some systems, like BSD/OS,
+   have EILSEQ in a different header.  On these systems, define EILSEQ
+   ourselves. */
+#ifndef EILSEQ
+#define EILSEQ @EILSEQ@
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Allocates descriptor for code conversion from encoding `fromcode' to
+   encoding `tocode'. */
+#ifndef LIBICONV_PLUG
+#define iconv_open libiconv_open
+#endif
+extern iconv_t iconv_open (const char* tocode, const char* fromcode);
+
+/* Converts, using conversion descriptor `cd', at most `*inbytesleft' bytes
+   starting at `*inbuf', writing at most `*outbytesleft' bytes starting at
+   `*outbuf'.
+   Decrements `*inbytesleft' and increments `*inbuf' by the same amount.
+   Decrements `*outbytesleft' and increments `*outbuf' by the same amount. */
+#ifndef LIBICONV_PLUG
+#define iconv libiconv
+#endif
+extern size_t iconv (iconv_t cd, const char* * inbuf, size_t *inbytesleft, char* * outbuf, size_t *outbytesleft);
+
+/* Frees resources allocated for conversion descriptor `cd'. */
+#ifndef LIBICONV_PLUG
+#define iconv_close libiconv_close
+#endif
+extern int iconv_close (iconv_t cd);
+
+
+#ifndef LIBICONV_PLUG
+
+/* Nonstandard extensions. */
+
+/* Control of attributes. */
+#define iconvctl libiconvctl
+extern int iconvctl (iconv_t cd, int request, void* argument);
+
+/* Requests for iconvctl. */
+#define ICONV_TRIVIALP            0  /* int *argument */
+#define ICONV_GET_TRANSLITERATE   1  /* int *argument */
+#define ICONV_SET_TRANSLITERATE   2  /* const int *argument */
+#define ICONV_GET_DISCARD_ILSEQ   3  /* int *argument */
+#define ICONV_SET_DISCARD_ILSEQ   4  /* const int *argument */
+
+/* Listing of locale independent encodings. */
+#define iconvlist libiconvlist
+extern void iconvlist (int (*do_one) (unsigned int namescount,
+                                      const char * const * names,
+                                      void* data),
+                       void* data);
+
+/* Support for relocatable packages.  */
+
+/* Sets the original and the current installation prefix of the package.
+   Relocation simply replaces a pathname starting with the original prefix
+   by the corresponding pathname with the current prefix instead.  Both
+   prefixes should be directory names without trailing slash (i.e. use ""
+   instead of "/").  */
+extern void libiconv_set_relocation_prefix (const char *orig_prefix,
+					    const char *curr_prefix);
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBICONV_H */
Index: /gsdl/trunk/gs2common/indexers/packages/unac/unac.c
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/unac.c	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/unac.c	(revision 16561)
@@ -0,0 +1,12843 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Loic Dachary <loic@senga.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+/*
+ * If configure.in has not defined this symbol, assume const. It
+ * does not harm much: a warning will be issued during compilation.
+ */
+#ifndef ICONV_CONST
+#define ICONV_CONST
+#endif /* ICONV_CONST */
+
+#include <stdlib.h>
+#include <string.h>
+#include <iconv.h>
+#include <errno.h>
+#ifdef HAVE_VSNPRINTF
+#include <stdio.h>
+#include <stdarg.h>
+#endif /* HAVE_VSNPRINTF */
+
+#include "unac.h"
+
+/* Generated by builder. Do not modify. Start tables */
+/*
+ * 00A0 NO-BREAK SPACE
+ * 	0020 SPACE
+ * 00A8 DIAERESIS
+ * 	0020 SPACE
+ * 00AA FEMININE ORDINAL INDICATOR
+ * 	0061 LATIN SMALL LETTER A
+ * 00AF MACRON
+ * 	0020 SPACE
+ * 00B2 SUPERSCRIPT TWO
+ * 	0032 DIGIT TWO
+ * 00B3 SUPERSCRIPT THREE
+ * 	0033 DIGIT THREE
+ * 00B4 ACUTE ACCENT
+ * 	0020 SPACE
+ * 00B5 MICRO SIGN
+ * 	03BC GREEK SMALL LETTER MU
+ * 00B8 CEDILLA
+ * 	0020 SPACE
+ * 00B9 SUPERSCRIPT ONE
+ * 	0031 DIGIT ONE
+ * 00BA MASCULINE ORDINAL INDICATOR
+ * 	006F LATIN SMALL LETTER O
+ * 00BC VULGAR FRACTION ONE QUARTER
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0034 DIGIT FOUR
+ * 00BD VULGAR FRACTION ONE HALF
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0032 DIGIT TWO
+ * 00BE VULGAR FRACTION THREE QUARTERS
+ * 	0033 DIGIT THREE
+ * 	2044 FRACTION SLASH
+ * 	0034 DIGIT FOUR
+ * 00C0 LATIN CAPITAL LETTER A WITH GRAVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C1 LATIN CAPITAL LETTER A WITH ACUTE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C3 LATIN CAPITAL LETTER A WITH TILDE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C5 LATIN CAPITAL LETTER A WITH RING ABOVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 00C6 LATIN CAPITAL LETTER AE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 	0045 LATIN CAPITAL LETTER E
+ * 00C7 LATIN CAPITAL LETTER C WITH CEDILLA
+ * 	0043 LATIN CAPITAL LETTER C
+ * 00C8 LATIN CAPITAL LETTER E WITH GRAVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 00C9 LATIN CAPITAL LETTER E WITH ACUTE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ * 	0045 LATIN CAPITAL LETTER E
+ * 00CB LATIN CAPITAL LETTER E WITH DIAERESIS
+ * 	0045 LATIN CAPITAL LETTER E
+ * 00CC LATIN CAPITAL LETTER I WITH GRAVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 00CD LATIN CAPITAL LETTER I WITH ACUTE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ * 	0049 LATIN CAPITAL LETTER I
+ * 00CF LATIN CAPITAL LETTER I WITH DIAERESIS
+ * 	0049 LATIN CAPITAL LETTER I
+ * 00D1 LATIN CAPITAL LETTER N WITH TILDE
+ * 	004E LATIN CAPITAL LETTER N
+ * 00D2 LATIN CAPITAL LETTER O WITH GRAVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D3 LATIN CAPITAL LETTER O WITH ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D5 LATIN CAPITAL LETTER O WITH TILDE
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D8 LATIN CAPITAL LETTER O WITH STROKE
+ * 	004F LATIN CAPITAL LETTER O
+ * 00D9 LATIN CAPITAL LETTER U WITH GRAVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 00DA LATIN CAPITAL LETTER U WITH ACUTE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ * 	0055 LATIN CAPITAL LETTER U
+ * 00DC LATIN CAPITAL LETTER U WITH DIAERESIS
+ * 	0055 LATIN CAPITAL LETTER U
+ * 00DD LATIN CAPITAL LETTER Y WITH ACUTE
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 00E0 LATIN SMALL LETTER A WITH GRAVE
+ * 	0061 LATIN SMALL LETTER A
+ * 00E1 LATIN SMALL LETTER A WITH ACUTE
+ * 	0061 LATIN SMALL LETTER A
+ * 00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX
+ * 	0061 LATIN SMALL LETTER A
+ * 00E3 LATIN SMALL LETTER A WITH TILDE
+ * 	0061 LATIN SMALL LETTER A
+ * 00E4 LATIN SMALL LETTER A WITH DIAERESIS
+ * 	0061 LATIN SMALL LETTER A
+ * 00E5 LATIN SMALL LETTER A WITH RING ABOVE
+ * 	0061 LATIN SMALL LETTER A
+ * 00E6 LATIN SMALL LETTER AE
+ * 	0061 LATIN SMALL LETTER A
+ * 	0065 LATIN SMALL LETTER E
+ * 00E7 LATIN SMALL LETTER C WITH CEDILLA
+ * 	0063 LATIN SMALL LETTER C
+ * 00E8 LATIN SMALL LETTER E WITH GRAVE
+ * 	0065 LATIN SMALL LETTER E
+ * 00E9 LATIN SMALL LETTER E WITH ACUTE
+ * 	0065 LATIN SMALL LETTER E
+ * 00EA LATIN SMALL LETTER E WITH CIRCUMFLEX
+ * 	0065 LATIN SMALL LETTER E
+ * 00EB LATIN SMALL LETTER E WITH DIAERESIS
+ * 	0065 LATIN SMALL LETTER E
+ * 00EC LATIN SMALL LETTER I WITH GRAVE
+ * 	0069 LATIN SMALL LETTER I
+ * 00ED LATIN SMALL LETTER I WITH ACUTE
+ * 	0069 LATIN SMALL LETTER I
+ * 00EE LATIN SMALL LETTER I WITH CIRCUMFLEX
+ * 	0069 LATIN SMALL LETTER I
+ * 00EF LATIN SMALL LETTER I WITH DIAERESIS
+ * 	0069 LATIN SMALL LETTER I
+ * 00F1 LATIN SMALL LETTER N WITH TILDE
+ * 	006E LATIN SMALL LETTER N
+ * 00F2 LATIN SMALL LETTER O WITH GRAVE
+ * 	006F LATIN SMALL LETTER O
+ * 00F3 LATIN SMALL LETTER O WITH ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX
+ * 	006F LATIN SMALL LETTER O
+ * 00F5 LATIN SMALL LETTER O WITH TILDE
+ * 	006F LATIN SMALL LETTER O
+ * 00F6 LATIN SMALL LETTER O WITH DIAERESIS
+ * 	006F LATIN SMALL LETTER O
+ * 00F8 LATIN SMALL LETTER O WITH STROKE
+ * 	006F LATIN SMALL LETTER O
+ * 00F9 LATIN SMALL LETTER U WITH GRAVE
+ * 	0075 LATIN SMALL LETTER U
+ * 00FA LATIN SMALL LETTER U WITH ACUTE
+ * 	0075 LATIN SMALL LETTER U
+ * 00FB LATIN SMALL LETTER U WITH CIRCUMFLEX
+ * 	0075 LATIN SMALL LETTER U
+ * 00FC LATIN SMALL LETTER U WITH DIAERESIS
+ * 	0075 LATIN SMALL LETTER U
+ * 00FD LATIN SMALL LETTER Y WITH ACUTE
+ * 	0079 LATIN SMALL LETTER Y
+ * 00FF LATIN SMALL LETTER Y WITH DIAERESIS
+ * 	0079 LATIN SMALL LETTER Y
+ * 0100 LATIN CAPITAL LETTER A WITH MACRON
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0101 LATIN SMALL LETTER A WITH MACRON
+ * 	0061 LATIN SMALL LETTER A
+ * 0102 LATIN CAPITAL LETTER A WITH BREVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0103 LATIN SMALL LETTER A WITH BREVE
+ * 	0061 LATIN SMALL LETTER A
+ * 0104 LATIN CAPITAL LETTER A WITH OGONEK
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0105 LATIN SMALL LETTER A WITH OGONEK
+ * 	0061 LATIN SMALL LETTER A
+ * 0106 LATIN CAPITAL LETTER C WITH ACUTE
+ * 	0043 LATIN CAPITAL LETTER C
+ * 0107 LATIN SMALL LETTER C WITH ACUTE
+ * 	0063 LATIN SMALL LETTER C
+ * 0108 LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+ * 	0043 LATIN CAPITAL LETTER C
+ * 0109 LATIN SMALL LETTER C WITH CIRCUMFLEX
+ * 	0063 LATIN SMALL LETTER C
+ * 010A LATIN CAPITAL LETTER C WITH DOT ABOVE
+ * 	0043 LATIN CAPITAL LETTER C
+ * 010B LATIN SMALL LETTER C WITH DOT ABOVE
+ * 	0063 LATIN SMALL LETTER C
+ * 010C LATIN CAPITAL LETTER C WITH CARON
+ * 	0043 LATIN CAPITAL LETTER C
+ * 010D LATIN SMALL LETTER C WITH CARON
+ * 	0063 LATIN SMALL LETTER C
+ * 010E LATIN CAPITAL LETTER D WITH CARON
+ * 	0044 LATIN CAPITAL LETTER D
+ * 010F LATIN SMALL LETTER D WITH CARON
+ * 	0064 LATIN SMALL LETTER D
+ * 0112 LATIN CAPITAL LETTER E WITH MACRON
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0113 LATIN SMALL LETTER E WITH MACRON
+ * 	0065 LATIN SMALL LETTER E
+ * 0114 LATIN CAPITAL LETTER E WITH BREVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0115 LATIN SMALL LETTER E WITH BREVE
+ * 	0065 LATIN SMALL LETTER E
+ * 0116 LATIN CAPITAL LETTER E WITH DOT ABOVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0117 LATIN SMALL LETTER E WITH DOT ABOVE
+ * 	0065 LATIN SMALL LETTER E
+ * 0118 LATIN CAPITAL LETTER E WITH OGONEK
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0119 LATIN SMALL LETTER E WITH OGONEK
+ * 	0065 LATIN SMALL LETTER E
+ * 011A LATIN CAPITAL LETTER E WITH CARON
+ * 	0045 LATIN CAPITAL LETTER E
+ * 011B LATIN SMALL LETTER E WITH CARON
+ * 	0065 LATIN SMALL LETTER E
+ * 011C LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+ * 	0047 LATIN CAPITAL LETTER G
+ * 011D LATIN SMALL LETTER G WITH CIRCUMFLEX
+ * 	0067 LATIN SMALL LETTER G
+ * 011E LATIN CAPITAL LETTER G WITH BREVE
+ * 	0047 LATIN CAPITAL LETTER G
+ * 011F LATIN SMALL LETTER G WITH BREVE
+ * 	0067 LATIN SMALL LETTER G
+ * 0120 LATIN CAPITAL LETTER G WITH DOT ABOVE
+ * 	0047 LATIN CAPITAL LETTER G
+ * 0121 LATIN SMALL LETTER G WITH DOT ABOVE
+ * 	0067 LATIN SMALL LETTER G
+ * 0122 LATIN CAPITAL LETTER G WITH CEDILLA
+ * 	0047 LATIN CAPITAL LETTER G
+ * 0123 LATIN SMALL LETTER G WITH CEDILLA
+ * 	0067 LATIN SMALL LETTER G
+ * 0124 LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+ * 	0048 LATIN CAPITAL LETTER H
+ * 0125 LATIN SMALL LETTER H WITH CIRCUMFLEX
+ * 	0068 LATIN SMALL LETTER H
+ * 0128 LATIN CAPITAL LETTER I WITH TILDE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 0129 LATIN SMALL LETTER I WITH TILDE
+ * 	0069 LATIN SMALL LETTER I
+ * 012A LATIN CAPITAL LETTER I WITH MACRON
+ * 	0049 LATIN CAPITAL LETTER I
+ * 012B LATIN SMALL LETTER I WITH MACRON
+ * 	0069 LATIN SMALL LETTER I
+ * 012C LATIN CAPITAL LETTER I WITH BREVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 012D LATIN SMALL LETTER I WITH BREVE
+ * 	0069 LATIN SMALL LETTER I
+ * 012E LATIN CAPITAL LETTER I WITH OGONEK
+ * 	0049 LATIN CAPITAL LETTER I
+ * 012F LATIN SMALL LETTER I WITH OGONEK
+ * 	0069 LATIN SMALL LETTER I
+ * 0130 LATIN CAPITAL LETTER I WITH DOT ABOVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 0132 LATIN CAPITAL LIGATURE IJ
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	004A LATIN CAPITAL LETTER J
+ * 0133 LATIN SMALL LIGATURE IJ
+ * 	0069 LATIN SMALL LETTER I
+ * 	006A LATIN SMALL LETTER J
+ * 0134 LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+ * 	004A LATIN CAPITAL LETTER J
+ * 0135 LATIN SMALL LETTER J WITH CIRCUMFLEX
+ * 	006A LATIN SMALL LETTER J
+ * 0136 LATIN CAPITAL LETTER K WITH CEDILLA
+ * 	004B LATIN CAPITAL LETTER K
+ * 0137 LATIN SMALL LETTER K WITH CEDILLA
+ * 	006B LATIN SMALL LETTER K
+ * 0139 LATIN CAPITAL LETTER L WITH ACUTE
+ * 	004C LATIN CAPITAL LETTER L
+ * 013A LATIN SMALL LETTER L WITH ACUTE
+ * 	006C LATIN SMALL LETTER L
+ * 013B LATIN CAPITAL LETTER L WITH CEDILLA
+ * 	004C LATIN CAPITAL LETTER L
+ * 013C LATIN SMALL LETTER L WITH CEDILLA
+ * 	006C LATIN SMALL LETTER L
+ * 013D LATIN CAPITAL LETTER L WITH CARON
+ * 	004C LATIN CAPITAL LETTER L
+ * 013E LATIN SMALL LETTER L WITH CARON
+ * 	006C LATIN SMALL LETTER L
+ * 013F LATIN CAPITAL LETTER L WITH MIDDLE DOT
+ * 	004C LATIN CAPITAL LETTER L
+ * 	00B7 MIDDLE DOT
+ * 0140 LATIN SMALL LETTER L WITH MIDDLE DOT
+ * 	006C LATIN SMALL LETTER L
+ * 	00B7 MIDDLE DOT
+ * 0143 LATIN CAPITAL LETTER N WITH ACUTE
+ * 	004E LATIN CAPITAL LETTER N
+ * 0144 LATIN SMALL LETTER N WITH ACUTE
+ * 	006E LATIN SMALL LETTER N
+ * 0145 LATIN CAPITAL LETTER N WITH CEDILLA
+ * 	004E LATIN CAPITAL LETTER N
+ * 0146 LATIN SMALL LETTER N WITH CEDILLA
+ * 	006E LATIN SMALL LETTER N
+ * 0147 LATIN CAPITAL LETTER N WITH CARON
+ * 	004E LATIN CAPITAL LETTER N
+ * 0148 LATIN SMALL LETTER N WITH CARON
+ * 	006E LATIN SMALL LETTER N
+ * 0149 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ * 	02BC MODIFIER LETTER APOSTROPHE
+ * 	006E LATIN SMALL LETTER N
+ * 014C LATIN CAPITAL LETTER O WITH MACRON
+ * 	004F LATIN CAPITAL LETTER O
+ * 014D LATIN SMALL LETTER O WITH MACRON
+ * 	006F LATIN SMALL LETTER O
+ * 014E LATIN CAPITAL LETTER O WITH BREVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 014F LATIN SMALL LETTER O WITH BREVE
+ * 	006F LATIN SMALL LETTER O
+ * 0150 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 0151 LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 0154 LATIN CAPITAL LETTER R WITH ACUTE
+ * 	0052 LATIN CAPITAL LETTER R
+ * 0155 LATIN SMALL LETTER R WITH ACUTE
+ * 	0072 LATIN SMALL LETTER R
+ * 0156 LATIN CAPITAL LETTER R WITH CEDILLA
+ * 	0052 LATIN CAPITAL LETTER R
+ * 0157 LATIN SMALL LETTER R WITH CEDILLA
+ * 	0072 LATIN SMALL LETTER R
+ * 0158 LATIN CAPITAL LETTER R WITH CARON
+ * 	0052 LATIN CAPITAL LETTER R
+ * 0159 LATIN SMALL LETTER R WITH CARON
+ * 	0072 LATIN SMALL LETTER R
+ * 015A LATIN CAPITAL LETTER S WITH ACUTE
+ * 	0053 LATIN CAPITAL LETTER S
+ * 015B LATIN SMALL LETTER S WITH ACUTE
+ * 	0073 LATIN SMALL LETTER S
+ * 015C LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+ * 	0053 LATIN CAPITAL LETTER S
+ * 015D LATIN SMALL LETTER S WITH CIRCUMFLEX
+ * 	0073 LATIN SMALL LETTER S
+ * 015E LATIN CAPITAL LETTER S WITH CEDILLA
+ * 	0053 LATIN CAPITAL LETTER S
+ * 015F LATIN SMALL LETTER S WITH CEDILLA
+ * 	0073 LATIN SMALL LETTER S
+ * 0160 LATIN CAPITAL LETTER S WITH CARON
+ * 	0053 LATIN CAPITAL LETTER S
+ * 0161 LATIN SMALL LETTER S WITH CARON
+ * 	0073 LATIN SMALL LETTER S
+ * 0162 LATIN CAPITAL LETTER T WITH CEDILLA
+ * 	0054 LATIN CAPITAL LETTER T
+ * 0163 LATIN SMALL LETTER T WITH CEDILLA
+ * 	0074 LATIN SMALL LETTER T
+ * 0164 LATIN CAPITAL LETTER T WITH CARON
+ * 	0054 LATIN CAPITAL LETTER T
+ * 0165 LATIN SMALL LETTER T WITH CARON
+ * 	0074 LATIN SMALL LETTER T
+ * 0168 LATIN CAPITAL LETTER U WITH TILDE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 0169 LATIN SMALL LETTER U WITH TILDE
+ * 	0075 LATIN SMALL LETTER U
+ * 016A LATIN CAPITAL LETTER U WITH MACRON
+ * 	0055 LATIN CAPITAL LETTER U
+ * 016B LATIN SMALL LETTER U WITH MACRON
+ * 	0075 LATIN SMALL LETTER U
+ * 016C LATIN CAPITAL LETTER U WITH BREVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 016D LATIN SMALL LETTER U WITH BREVE
+ * 	0075 LATIN SMALL LETTER U
+ * 016E LATIN CAPITAL LETTER U WITH RING ABOVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 016F LATIN SMALL LETTER U WITH RING ABOVE
+ * 	0075 LATIN SMALL LETTER U
+ * 0170 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 0171 LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ * 	0075 LATIN SMALL LETTER U
+ * 0172 LATIN CAPITAL LETTER U WITH OGONEK
+ * 	0055 LATIN CAPITAL LETTER U
+ * 0173 LATIN SMALL LETTER U WITH OGONEK
+ * 	0075 LATIN SMALL LETTER U
+ * 0174 LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+ * 	0057 LATIN CAPITAL LETTER W
+ * 0175 LATIN SMALL LETTER W WITH CIRCUMFLEX
+ * 	0077 LATIN SMALL LETTER W
+ * 0176 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 0177 LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ * 	0079 LATIN SMALL LETTER Y
+ * 0178 LATIN CAPITAL LETTER Y WITH DIAERESIS
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 0179 LATIN CAPITAL LETTER Z WITH ACUTE
+ * 	005A LATIN CAPITAL LETTER Z
+ * 017A LATIN SMALL LETTER Z WITH ACUTE
+ * 	007A LATIN SMALL LETTER Z
+ * 017B LATIN CAPITAL LETTER Z WITH DOT ABOVE
+ * 	005A LATIN CAPITAL LETTER Z
+ * 017C LATIN SMALL LETTER Z WITH DOT ABOVE
+ * 	007A LATIN SMALL LETTER Z
+ * 017D LATIN CAPITAL LETTER Z WITH CARON
+ * 	005A LATIN CAPITAL LETTER Z
+ * 017E LATIN SMALL LETTER Z WITH CARON
+ * 	007A LATIN SMALL LETTER Z
+ * 017F LATIN SMALL LETTER LONG S
+ * 	0073 LATIN SMALL LETTER S
+ * 01A0 LATIN CAPITAL LETTER O WITH HORN
+ * 	004F LATIN CAPITAL LETTER O
+ * 01A1 LATIN SMALL LETTER O WITH HORN
+ * 	006F LATIN SMALL LETTER O
+ * 01AF LATIN CAPITAL LETTER U WITH HORN
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01B0 LATIN SMALL LETTER U WITH HORN
+ * 	0075 LATIN SMALL LETTER U
+ * 01C4 LATIN CAPITAL LETTER DZ WITH CARON
+ * 	0044 LATIN CAPITAL LETTER D
+ * 	005A LATIN CAPITAL LETTER Z
+ * 01C5 LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+ * 	0044 LATIN CAPITAL LETTER D
+ * 	007A LATIN SMALL LETTER Z
+ * 01C6 LATIN SMALL LETTER DZ WITH CARON
+ * 	0064 LATIN SMALL LETTER D
+ * 	007A LATIN SMALL LETTER Z
+ * 01C7 LATIN CAPITAL LETTER LJ
+ * 	004C LATIN CAPITAL LETTER L
+ * 	004A LATIN CAPITAL LETTER J
+ * 01C8 LATIN CAPITAL LETTER L WITH SMALL LETTER J
+ * 	004C LATIN CAPITAL LETTER L
+ * 	006A LATIN SMALL LETTER J
+ * 01C9 LATIN SMALL LETTER LJ
+ * 	006C LATIN SMALL LETTER L
+ * 	006A LATIN SMALL LETTER J
+ * 01CA LATIN CAPITAL LETTER NJ
+ * 	004E LATIN CAPITAL LETTER N
+ * 	004A LATIN CAPITAL LETTER J
+ * 01CB LATIN CAPITAL LETTER N WITH SMALL LETTER J
+ * 	004E LATIN CAPITAL LETTER N
+ * 	006A LATIN SMALL LETTER J
+ * 01CC LATIN SMALL LETTER NJ
+ * 	006E LATIN SMALL LETTER N
+ * 	006A LATIN SMALL LETTER J
+ * 01CD LATIN CAPITAL LETTER A WITH CARON
+ * 	0041 LATIN CAPITAL LETTER A
+ * 01CE LATIN SMALL LETTER A WITH CARON
+ * 	0061 LATIN SMALL LETTER A
+ * 01CF LATIN CAPITAL LETTER I WITH CARON
+ * 	0049 LATIN CAPITAL LETTER I
+ * 01D0 LATIN SMALL LETTER I WITH CARON
+ * 	0069 LATIN SMALL LETTER I
+ * 01D1 LATIN CAPITAL LETTER O WITH CARON
+ * 	004F LATIN CAPITAL LETTER O
+ * 01D2 LATIN SMALL LETTER O WITH CARON
+ * 	006F LATIN SMALL LETTER O
+ * 01D3 LATIN CAPITAL LETTER U WITH CARON
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01D4 LATIN SMALL LETTER U WITH CARON
+ * 	0075 LATIN SMALL LETTER U
+ * 01D5 LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01D6 LATIN SMALL LETTER U WITH DIAERESIS AND MACRON
+ * 	0075 LATIN SMALL LETTER U
+ * 01D7 LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01D8 LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE
+ * 	0075 LATIN SMALL LETTER U
+ * 01D9 LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01DA LATIN SMALL LETTER U WITH DIAERESIS AND CARON
+ * 	0075 LATIN SMALL LETTER U
+ * 01DB LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 01DC LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+ * 	0075 LATIN SMALL LETTER U
+ * 01DE LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+ * 	0041 LATIN CAPITAL LETTER A
+ * 01DF LATIN SMALL LETTER A WITH DIAERESIS AND MACRON
+ * 	0061 LATIN SMALL LETTER A
+ * 01E0 LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+ * 	0041 LATIN CAPITAL LETTER A
+ * 01E1 LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON
+ * 	0061 LATIN SMALL LETTER A
+ * 01E2 LATIN CAPITAL LETTER AE WITH MACRON
+ * 	0041 LATIN CAPITAL LETTER A
+ * 	0045 LATIN CAPITAL LETTER E
+ * 01E3 LATIN SMALL LETTER AE WITH MACRON
+ * 	0061 LATIN SMALL LETTER A
+ * 	0065 LATIN SMALL LETTER E
+ * 01E6 LATIN CAPITAL LETTER G WITH CARON
+ * 	0047 LATIN CAPITAL LETTER G
+ * 01E7 LATIN SMALL LETTER G WITH CARON
+ * 	0067 LATIN SMALL LETTER G
+ * 01E8 LATIN CAPITAL LETTER K WITH CARON
+ * 	004B LATIN CAPITAL LETTER K
+ * 01E9 LATIN SMALL LETTER K WITH CARON
+ * 	006B LATIN SMALL LETTER K
+ * 01EA LATIN CAPITAL LETTER O WITH OGONEK
+ * 	004F LATIN CAPITAL LETTER O
+ * 01EB LATIN SMALL LETTER O WITH OGONEK
+ * 	006F LATIN SMALL LETTER O
+ * 01EC LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+ * 	004F LATIN CAPITAL LETTER O
+ * 01ED LATIN SMALL LETTER O WITH OGONEK AND MACRON
+ * 	006F LATIN SMALL LETTER O
+ * 01EE LATIN CAPITAL LETTER EZH WITH CARON
+ * 	01B7 LATIN CAPITAL LETTER EZH
+ * 01EF LATIN SMALL LETTER EZH WITH CARON
+ * 	0292 LATIN SMALL LETTER EZH
+ * 01F0 LATIN SMALL LETTER J WITH CARON
+ * 	006A LATIN SMALL LETTER J
+ * 01F1 LATIN CAPITAL LETTER DZ
+ * 	0044 LATIN CAPITAL LETTER D
+ * 	005A LATIN CAPITAL LETTER Z
+ * 01F2 LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+ * 	0044 LATIN CAPITAL LETTER D
+ * 	007A LATIN SMALL LETTER Z
+ * 01F3 LATIN SMALL LETTER DZ
+ * 	0064 LATIN SMALL LETTER D
+ * 	007A LATIN SMALL LETTER Z
+ * 01F4 LATIN CAPITAL LETTER G WITH ACUTE
+ * 	0047 LATIN CAPITAL LETTER G
+ * 01F5 LATIN SMALL LETTER G WITH ACUTE
+ * 	0067 LATIN SMALL LETTER G
+ * 01F8 LATIN CAPITAL LETTER N WITH GRAVE
+ * 	004E LATIN CAPITAL LETTER N
+ * 01F9 LATIN SMALL LETTER N WITH GRAVE
+ * 	006E LATIN SMALL LETTER N
+ * 01FA LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 01FB LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+ * 	0061 LATIN SMALL LETTER A
+ * 01FC LATIN CAPITAL LETTER AE WITH ACUTE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 	0045 LATIN CAPITAL LETTER E
+ * 01FD LATIN SMALL LETTER AE WITH ACUTE
+ * 	0061 LATIN SMALL LETTER A
+ * 	0065 LATIN SMALL LETTER E
+ * 01FE LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 01FF LATIN SMALL LETTER O WITH STROKE AND ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 0200 LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0201 LATIN SMALL LETTER A WITH DOUBLE GRAVE
+ * 	0061 LATIN SMALL LETTER A
+ * 0202 LATIN CAPITAL LETTER A WITH INVERTED BREVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0203 LATIN SMALL LETTER A WITH INVERTED BREVE
+ * 	0061 LATIN SMALL LETTER A
+ * 0204 LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0205 LATIN SMALL LETTER E WITH DOUBLE GRAVE
+ * 	0065 LATIN SMALL LETTER E
+ * 0206 LATIN CAPITAL LETTER E WITH INVERTED BREVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0207 LATIN SMALL LETTER E WITH INVERTED BREVE
+ * 	0065 LATIN SMALL LETTER E
+ * 0208 LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 0209 LATIN SMALL LETTER I WITH DOUBLE GRAVE
+ * 	0069 LATIN SMALL LETTER I
+ * 020A LATIN CAPITAL LETTER I WITH INVERTED BREVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 020B LATIN SMALL LETTER I WITH INVERTED BREVE
+ * 	0069 LATIN SMALL LETTER I
+ * 020C LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 020D LATIN SMALL LETTER O WITH DOUBLE GRAVE
+ * 	006F LATIN SMALL LETTER O
+ * 020E LATIN CAPITAL LETTER O WITH INVERTED BREVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 020F LATIN SMALL LETTER O WITH INVERTED BREVE
+ * 	006F LATIN SMALL LETTER O
+ * 0210 LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+ * 	0052 LATIN CAPITAL LETTER R
+ * 0211 LATIN SMALL LETTER R WITH DOUBLE GRAVE
+ * 	0072 LATIN SMALL LETTER R
+ * 0212 LATIN CAPITAL LETTER R WITH INVERTED BREVE
+ * 	0052 LATIN CAPITAL LETTER R
+ * 0213 LATIN SMALL LETTER R WITH INVERTED BREVE
+ * 	0072 LATIN SMALL LETTER R
+ * 0214 LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 0215 LATIN SMALL LETTER U WITH DOUBLE GRAVE
+ * 	0075 LATIN SMALL LETTER U
+ * 0216 LATIN CAPITAL LETTER U WITH INVERTED BREVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 0217 LATIN SMALL LETTER U WITH INVERTED BREVE
+ * 	0075 LATIN SMALL LETTER U
+ * 0218 LATIN CAPITAL LETTER S WITH COMMA BELOW
+ * 	0053 LATIN CAPITAL LETTER S
+ * 0219 LATIN SMALL LETTER S WITH COMMA BELOW
+ * 	0073 LATIN SMALL LETTER S
+ * 021A LATIN CAPITAL LETTER T WITH COMMA BELOW
+ * 	0054 LATIN CAPITAL LETTER T
+ * 021B LATIN SMALL LETTER T WITH COMMA BELOW
+ * 	0074 LATIN SMALL LETTER T
+ * 021E LATIN CAPITAL LETTER H WITH CARON
+ * 	0048 LATIN CAPITAL LETTER H
+ * 021F LATIN SMALL LETTER H WITH CARON
+ * 	0068 LATIN SMALL LETTER H
+ * 0226 LATIN CAPITAL LETTER A WITH DOT ABOVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 0227 LATIN SMALL LETTER A WITH DOT ABOVE
+ * 	0061 LATIN SMALL LETTER A
+ * 0228 LATIN CAPITAL LETTER E WITH CEDILLA
+ * 	0045 LATIN CAPITAL LETTER E
+ * 0229 LATIN SMALL LETTER E WITH CEDILLA
+ * 	0065 LATIN SMALL LETTER E
+ * 022A LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON
+ * 	004F LATIN CAPITAL LETTER O
+ * 022B LATIN SMALL LETTER O WITH DIAERESIS AND MACRON
+ * 	006F LATIN SMALL LETTER O
+ * 022C LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+ * 	004F LATIN CAPITAL LETTER O
+ * 022D LATIN SMALL LETTER O WITH TILDE AND MACRON
+ * 	006F LATIN SMALL LETTER O
+ * 022E LATIN CAPITAL LETTER O WITH DOT ABOVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 022F LATIN SMALL LETTER O WITH DOT ABOVE
+ * 	006F LATIN SMALL LETTER O
+ * 0230 LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON
+ * 	004F LATIN CAPITAL LETTER O
+ * 0231 LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON
+ * 	006F LATIN SMALL LETTER O
+ * 0232 LATIN CAPITAL LETTER Y WITH MACRON
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 0233 LATIN SMALL LETTER Y WITH MACRON
+ * 	0079 LATIN SMALL LETTER Y
+ * 02B0 MODIFIER LETTER SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 02B1 MODIFIER LETTER SMALL H WITH HOOK
+ * 	0266 LATIN SMALL LETTER H WITH HOOK
+ * 02B2 MODIFIER LETTER SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 02B3 MODIFIER LETTER SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 02B4 MODIFIER LETTER SMALL TURNED R
+ * 	0279 LATIN SMALL LETTER TURNED R
+ * 02B5 MODIFIER LETTER SMALL TURNED R WITH HOOK
+ * 	027B LATIN SMALL LETTER TURNED R WITH HOOK
+ * 02B6 MODIFIER LETTER SMALL CAPITAL INVERTED R
+ * 	0281 LATIN LETTER SMALL CAPITAL INVERTED R
+ * 02B7 MODIFIER LETTER SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 02B8 MODIFIER LETTER SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 02D8 BREVE
+ * 	0020 SPACE
+ * 02D9 DOT ABOVE
+ * 	0020 SPACE
+ * 02DA RING ABOVE
+ * 	0020 SPACE
+ * 02DB OGONEK
+ * 	0020 SPACE
+ * 02DC SMALL TILDE
+ * 	0020 SPACE
+ * 02DD DOUBLE ACUTE ACCENT
+ * 	0020 SPACE
+ * 02E0 MODIFIER LETTER SMALL GAMMA
+ * 	0263 LATIN SMALL LETTER GAMMA
+ * 02E1 MODIFIER LETTER SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 02E2 MODIFIER LETTER SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 02E3 MODIFIER LETTER SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 02E4 MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+ * 	0295 LATIN LETTER PHARYNGEAL VOICED FRICATIVE
+ * 0374 GREEK NUMERAL SIGN
+ * 	02B9 MODIFIER LETTER PRIME
+ * 037A GREEK YPOGEGRAMMENI
+ * 	0020 SPACE
+ * 037E GREEK QUESTION MARK
+ * 	003B SEMICOLON
+ * 0384 GREEK TONOS
+ * 	0020 SPACE
+ * 0385 GREEK DIALYTIKA TONOS
+ * 	0020 SPACE
+ * 0386 GREEK CAPITAL LETTER ALPHA WITH TONOS
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 0387 GREEK ANO TELEIA
+ * 	00B7 MIDDLE DOT
+ * 0388 GREEK CAPITAL LETTER EPSILON WITH TONOS
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 0389 GREEK CAPITAL LETTER ETA WITH TONOS
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 038A GREEK CAPITAL LETTER IOTA WITH TONOS
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 038C GREEK CAPITAL LETTER OMICRON WITH TONOS
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 038E GREEK CAPITAL LETTER UPSILON WITH TONOS
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 038F GREEK CAPITAL LETTER OMEGA WITH TONOS
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 0390 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 03AA GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 03AB GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 03AC GREEK SMALL LETTER ALPHA WITH TONOS
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 03AD GREEK SMALL LETTER EPSILON WITH TONOS
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 03AE GREEK SMALL LETTER ETA WITH TONOS
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 03AF GREEK SMALL LETTER IOTA WITH TONOS
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 03B0 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 03CA GREEK SMALL LETTER IOTA WITH DIALYTIKA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 03CB GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 03CC GREEK SMALL LETTER OMICRON WITH TONOS
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 03CD GREEK SMALL LETTER UPSILON WITH TONOS
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 03CE GREEK SMALL LETTER OMEGA WITH TONOS
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 03D0 GREEK BETA SYMBOL
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 03D1 GREEK THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 03D2 GREEK UPSILON WITH HOOK SYMBOL
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 03D3 GREEK UPSILON WITH ACUTE AND HOOK SYMBOL
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 03D4 GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 03D5 GREEK PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 03D6 GREEK PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 03F0 GREEK KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 03F1 GREEK RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 03F2 GREEK LUNATE SIGMA SYMBOL
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 03F4 GREEK CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 03F5 GREEK LUNATE EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 0400 CYRILLIC CAPITAL LETTER IE WITH GRAVE
+ * 	0415 CYRILLIC CAPITAL LETTER IE
+ * 0401 CYRILLIC CAPITAL LETTER IO
+ * 	0415 CYRILLIC CAPITAL LETTER IE
+ * 0403 CYRILLIC CAPITAL LETTER GJE
+ * 	0413 CYRILLIC CAPITAL LETTER GHE
+ * 0407 CYRILLIC CAPITAL LETTER YI
+ * 	0406 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ * 040C CYRILLIC CAPITAL LETTER KJE
+ * 	041A CYRILLIC CAPITAL LETTER KA
+ * 040D CYRILLIC CAPITAL LETTER I WITH GRAVE
+ * 	0418 CYRILLIC CAPITAL LETTER I
+ * 040E CYRILLIC CAPITAL LETTER SHORT U
+ * 	0423 CYRILLIC CAPITAL LETTER U
+ * 0419 CYRILLIC CAPITAL LETTER SHORT I
+ * 	0418 CYRILLIC CAPITAL LETTER I
+ * 0439 CYRILLIC SMALL LETTER SHORT I
+ * 	0438 CYRILLIC SMALL LETTER I
+ * 0450 CYRILLIC SMALL LETTER IE WITH GRAVE
+ * 	0435 CYRILLIC SMALL LETTER IE
+ * 0451 CYRILLIC SMALL LETTER IO
+ * 	0435 CYRILLIC SMALL LETTER IE
+ * 0453 CYRILLIC SMALL LETTER GJE
+ * 	0433 CYRILLIC SMALL LETTER GHE
+ * 0457 CYRILLIC SMALL LETTER YI
+ * 	0456 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ * 045C CYRILLIC SMALL LETTER KJE
+ * 	043A CYRILLIC SMALL LETTER KA
+ * 045D CYRILLIC SMALL LETTER I WITH GRAVE
+ * 	0438 CYRILLIC SMALL LETTER I
+ * 045E CYRILLIC SMALL LETTER SHORT U
+ * 	0443 CYRILLIC SMALL LETTER U
+ * 0476 CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+ * 	0474 CYRILLIC CAPITAL LETTER IZHITSA
+ * 0477 CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+ * 	0475 CYRILLIC SMALL LETTER IZHITSA
+ * 04C1 CYRILLIC CAPITAL LETTER ZHE WITH BREVE
+ * 	0416 CYRILLIC CAPITAL LETTER ZHE
+ * 04C2 CYRILLIC SMALL LETTER ZHE WITH BREVE
+ * 	0436 CYRILLIC SMALL LETTER ZHE
+ * 04D0 CYRILLIC CAPITAL LETTER A WITH BREVE
+ * 	0410 CYRILLIC CAPITAL LETTER A
+ * 04D1 CYRILLIC SMALL LETTER A WITH BREVE
+ * 	0430 CYRILLIC SMALL LETTER A
+ * 04D2 CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+ * 	0410 CYRILLIC CAPITAL LETTER A
+ * 04D3 CYRILLIC SMALL LETTER A WITH DIAERESIS
+ * 	0430 CYRILLIC SMALL LETTER A
+ * 04D6 CYRILLIC CAPITAL LETTER IE WITH BREVE
+ * 	0415 CYRILLIC CAPITAL LETTER IE
+ * 04D7 CYRILLIC SMALL LETTER IE WITH BREVE
+ * 	0435 CYRILLIC SMALL LETTER IE
+ * 04DA CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+ * 	04D8 CYRILLIC CAPITAL LETTER SCHWA
+ * 04DB CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+ * 	04D9 CYRILLIC SMALL LETTER SCHWA
+ * 04DC CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+ * 	0416 CYRILLIC CAPITAL LETTER ZHE
+ * 04DD CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+ * 	0436 CYRILLIC SMALL LETTER ZHE
+ * 04DE CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+ * 	0417 CYRILLIC CAPITAL LETTER ZE
+ * 04DF CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+ * 	0437 CYRILLIC SMALL LETTER ZE
+ * 04E2 CYRILLIC CAPITAL LETTER I WITH MACRON
+ * 	0418 CYRILLIC CAPITAL LETTER I
+ * 04E3 CYRILLIC SMALL LETTER I WITH MACRON
+ * 	0438 CYRILLIC SMALL LETTER I
+ * 04E4 CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+ * 	0418 CYRILLIC CAPITAL LETTER I
+ * 04E5 CYRILLIC SMALL LETTER I WITH DIAERESIS
+ * 	0438 CYRILLIC SMALL LETTER I
+ * 04E6 CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+ * 	041E CYRILLIC CAPITAL LETTER O
+ * 04E7 CYRILLIC SMALL LETTER O WITH DIAERESIS
+ * 	043E CYRILLIC SMALL LETTER O
+ * 04EA CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+ * 	04E8 CYRILLIC CAPITAL LETTER BARRED O
+ * 04EB CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS
+ * 	04E9 CYRILLIC SMALL LETTER BARRED O
+ * 04EC CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+ * 	042D CYRILLIC CAPITAL LETTER E
+ * 04ED CYRILLIC SMALL LETTER E WITH DIAERESIS
+ * 	044D CYRILLIC SMALL LETTER E
+ * 04EE CYRILLIC CAPITAL LETTER U WITH MACRON
+ * 	0423 CYRILLIC CAPITAL LETTER U
+ * 04EF CYRILLIC SMALL LETTER U WITH MACRON
+ * 	0443 CYRILLIC SMALL LETTER U
+ * 04F0 CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+ * 	0423 CYRILLIC CAPITAL LETTER U
+ * 04F1 CYRILLIC SMALL LETTER U WITH DIAERESIS
+ * 	0443 CYRILLIC SMALL LETTER U
+ * 04F2 CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+ * 	0423 CYRILLIC CAPITAL LETTER U
+ * 04F3 CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+ * 	0443 CYRILLIC SMALL LETTER U
+ * 04F4 CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+ * 	0427 CYRILLIC CAPITAL LETTER CHE
+ * 04F5 CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+ * 	0447 CYRILLIC SMALL LETTER CHE
+ * 04F8 CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+ * 	042B CYRILLIC CAPITAL LETTER YERU
+ * 04F9 CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+ * 	044B CYRILLIC SMALL LETTER YERU
+ * 0587 ARMENIAN SMALL LIGATURE ECH YIWN
+ * 	0565 ARMENIAN SMALL LETTER ECH
+ * 	0582 ARMENIAN SMALL LETTER YIWN
+ * 0622 ARABIC LETTER ALEF WITH MADDA ABOVE
+ * 	0627 ARABIC LETTER ALEF
+ * 0623 ARABIC LETTER ALEF WITH HAMZA ABOVE
+ * 	0627 ARABIC LETTER ALEF
+ * 0624 ARABIC LETTER WAW WITH HAMZA ABOVE
+ * 	0648 ARABIC LETTER WAW
+ * 0625 ARABIC LETTER ALEF WITH HAMZA BELOW
+ * 	0627 ARABIC LETTER ALEF
+ * 0626 ARABIC LETTER YEH WITH HAMZA ABOVE
+ * 	064A ARABIC LETTER YEH
+ * 0675 ARABIC LETTER HIGH HAMZA ALEF
+ * 	0627 ARABIC LETTER ALEF
+ * 	0674 ARABIC LETTER HIGH HAMZA
+ * 0676 ARABIC LETTER HIGH HAMZA WAW
+ * 	0648 ARABIC LETTER WAW
+ * 	0674 ARABIC LETTER HIGH HAMZA
+ * 0677 ARABIC LETTER U WITH HAMZA ABOVE
+ * 	06C7 ARABIC LETTER U
+ * 	0674 ARABIC LETTER HIGH HAMZA
+ * 0678 ARABIC LETTER HIGH HAMZA YEH
+ * 	064A ARABIC LETTER YEH
+ * 	0674 ARABIC LETTER HIGH HAMZA
+ * 06C0 ARABIC LETTER HEH WITH YEH ABOVE
+ * 	06D5 ARABIC LETTER AE
+ * 06C2 ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+ * 	06C1 ARABIC LETTER HEH GOAL
+ * 06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * 0929 DEVANAGARI LETTER NNNA
+ * 	0928 DEVANAGARI LETTER NA
+ * 0931 DEVANAGARI LETTER RRA
+ * 	0930 DEVANAGARI LETTER RA
+ * 0934 DEVANAGARI LETTER LLLA
+ * 	0933 DEVANAGARI LETTER LLA
+ * 0958 DEVANAGARI LETTER QA
+ * 	0915 DEVANAGARI LETTER KA
+ * 0959 DEVANAGARI LETTER KHHA
+ * 	0916 DEVANAGARI LETTER KHA
+ * 095A DEVANAGARI LETTER GHHA
+ * 	0917 DEVANAGARI LETTER GA
+ * 095B DEVANAGARI LETTER ZA
+ * 	091C DEVANAGARI LETTER JA
+ * 095C DEVANAGARI LETTER DDDHA
+ * 	0921 DEVANAGARI LETTER DDA
+ * 095D DEVANAGARI LETTER RHA
+ * 	0922 DEVANAGARI LETTER DDHA
+ * 095E DEVANAGARI LETTER FA
+ * 	092B DEVANAGARI LETTER PHA
+ * 095F DEVANAGARI LETTER YYA
+ * 	092F DEVANAGARI LETTER YA
+ * 09DC BENGALI LETTER RRA
+ * 	09A1 BENGALI LETTER DDA
+ * 09DD BENGALI LETTER RHA
+ * 	09A2 BENGALI LETTER DDHA
+ * 09DF BENGALI LETTER YYA
+ * 	09AF BENGALI LETTER YA
+ * 0A33 GURMUKHI LETTER LLA
+ * 	0A32 GURMUKHI LETTER LA
+ * 0A36 GURMUKHI LETTER SHA
+ * 	0A38 GURMUKHI LETTER SA
+ * 0A59 GURMUKHI LETTER KHHA
+ * 	0A16 GURMUKHI LETTER KHA
+ * 0A5A GURMUKHI LETTER GHHA
+ * 	0A17 GURMUKHI LETTER GA
+ * 0A5B GURMUKHI LETTER ZA
+ * 	0A1C GURMUKHI LETTER JA
+ * 0A5E GURMUKHI LETTER FA
+ * 	0A2B GURMUKHI LETTER PHA
+ * 0B5C ORIYA LETTER RRA
+ * 	0B21 ORIYA LETTER DDA
+ * 0B5D ORIYA LETTER RHA
+ * 	0B22 ORIYA LETTER DDHA
+ * 0B94 TAMIL LETTER AU
+ * 	0B92 TAMIL LETTER O
+ * 0E33 THAI CHARACTER SARA AM
+ * 	0E32 THAI CHARACTER SARA AA
+ * 0EB3 LAO VOWEL SIGN AM
+ * 	0EB2 LAO VOWEL SIGN AA
+ * 0EDC LAO HO NO
+ * 	0EAB LAO LETTER HO SUNG
+ * 	0E99 LAO LETTER NO
+ * 0EDD LAO HO MO
+ * 	0EAB LAO LETTER HO SUNG
+ * 	0EA1 LAO LETTER MO
+ * 0F0C TIBETAN MARK DELIMITER TSHEG BSTAR
+ * 	0F0B TIBETAN MARK INTERSYLLABIC TSHEG
+ * 0F43 TIBETAN LETTER GHA
+ * 	0F42 TIBETAN LETTER GA
+ * 0F4D TIBETAN LETTER DDHA
+ * 	0F4C TIBETAN LETTER DDA
+ * 0F52 TIBETAN LETTER DHA
+ * 	0F51 TIBETAN LETTER DA
+ * 0F57 TIBETAN LETTER BHA
+ * 	0F56 TIBETAN LETTER BA
+ * 0F5C TIBETAN LETTER DZHA
+ * 	0F5B TIBETAN LETTER DZA
+ * 0F69 TIBETAN LETTER KSSA
+ * 	0F40 TIBETAN LETTER KA
+ * 1026 MYANMAR LETTER UU
+ * 	1025 MYANMAR LETTER U
+ * 1D15E MUSICAL SYMBOL HALF NOTE
+ * 	1D157 MUSICAL SYMBOL VOID NOTEHEAD
+ * 1D15F MUSICAL SYMBOL QUARTER NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D160 MUSICAL SYMBOL EIGHTH NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D161 MUSICAL SYMBOL SIXTEENTH NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D162 MUSICAL SYMBOL THIRTY-SECOND NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D163 MUSICAL SYMBOL SIXTY-FOURTH NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D164 MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+ * 	1D158 MUSICAL SYMBOL NOTEHEAD BLACK
+ * 1D1BB MUSICAL SYMBOL MINIMA
+ * 	1D1B9 MUSICAL SYMBOL SEMIBREVIS WHITE
+ * 1D1BC MUSICAL SYMBOL MINIMA BLACK
+ * 	1D1BA MUSICAL SYMBOL SEMIBREVIS BLACK
+ * 1D1BD MUSICAL SYMBOL SEMIMINIMA WHITE
+ * 	1D1B9 MUSICAL SYMBOL SEMIBREVIS WHITE
+ * 1D1BE MUSICAL SYMBOL SEMIMINIMA BLACK
+ * 	1D1BA MUSICAL SYMBOL SEMIBREVIS BLACK
+ * 1D1BF MUSICAL SYMBOL FUSA WHITE
+ * 	1D1B9 MUSICAL SYMBOL SEMIBREVIS WHITE
+ * 1D1C0 MUSICAL SYMBOL FUSA BLACK
+ * 	1D1BA MUSICAL SYMBOL SEMIBREVIS BLACK
+ * 1D400 MATHEMATICAL BOLD CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D401 MATHEMATICAL BOLD CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D402 MATHEMATICAL BOLD CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D403 MATHEMATICAL BOLD CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D404 MATHEMATICAL BOLD CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D405 MATHEMATICAL BOLD CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D406 MATHEMATICAL BOLD CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D407 MATHEMATICAL BOLD CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D408 MATHEMATICAL BOLD CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D409 MATHEMATICAL BOLD CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D40A MATHEMATICAL BOLD CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D40B MATHEMATICAL BOLD CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D40C MATHEMATICAL BOLD CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D40D MATHEMATICAL BOLD CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D40E MATHEMATICAL BOLD CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D40F MATHEMATICAL BOLD CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D410 MATHEMATICAL BOLD CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D411 MATHEMATICAL BOLD CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D412 MATHEMATICAL BOLD CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D413 MATHEMATICAL BOLD CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D414 MATHEMATICAL BOLD CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D415 MATHEMATICAL BOLD CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D416 MATHEMATICAL BOLD CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D417 MATHEMATICAL BOLD CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D418 MATHEMATICAL BOLD CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D419 MATHEMATICAL BOLD CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D41A MATHEMATICAL BOLD SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D41B MATHEMATICAL BOLD SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D41C MATHEMATICAL BOLD SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D41D MATHEMATICAL BOLD SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D41E MATHEMATICAL BOLD SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D41F MATHEMATICAL BOLD SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D420 MATHEMATICAL BOLD SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D421 MATHEMATICAL BOLD SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D422 MATHEMATICAL BOLD SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D423 MATHEMATICAL BOLD SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D424 MATHEMATICAL BOLD SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D425 MATHEMATICAL BOLD SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D426 MATHEMATICAL BOLD SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D427 MATHEMATICAL BOLD SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D428 MATHEMATICAL BOLD SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D429 MATHEMATICAL BOLD SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D42A MATHEMATICAL BOLD SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D42B MATHEMATICAL BOLD SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D42C MATHEMATICAL BOLD SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D42D MATHEMATICAL BOLD SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D42E MATHEMATICAL BOLD SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D42F MATHEMATICAL BOLD SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D430 MATHEMATICAL BOLD SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D431 MATHEMATICAL BOLD SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D432 MATHEMATICAL BOLD SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D433 MATHEMATICAL BOLD SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D434 MATHEMATICAL ITALIC CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D435 MATHEMATICAL ITALIC CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D436 MATHEMATICAL ITALIC CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D437 MATHEMATICAL ITALIC CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D438 MATHEMATICAL ITALIC CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D439 MATHEMATICAL ITALIC CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D43A MATHEMATICAL ITALIC CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D43B MATHEMATICAL ITALIC CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D43C MATHEMATICAL ITALIC CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D43D MATHEMATICAL ITALIC CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D43E MATHEMATICAL ITALIC CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D43F MATHEMATICAL ITALIC CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D440 MATHEMATICAL ITALIC CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D441 MATHEMATICAL ITALIC CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D442 MATHEMATICAL ITALIC CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D443 MATHEMATICAL ITALIC CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D444 MATHEMATICAL ITALIC CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D445 MATHEMATICAL ITALIC CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D446 MATHEMATICAL ITALIC CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D447 MATHEMATICAL ITALIC CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D448 MATHEMATICAL ITALIC CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D449 MATHEMATICAL ITALIC CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D44A MATHEMATICAL ITALIC CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D44B MATHEMATICAL ITALIC CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D44C MATHEMATICAL ITALIC CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D44D MATHEMATICAL ITALIC CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D44E MATHEMATICAL ITALIC SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D44F MATHEMATICAL ITALIC SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D450 MATHEMATICAL ITALIC SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D451 MATHEMATICAL ITALIC SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D452 MATHEMATICAL ITALIC SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D453 MATHEMATICAL ITALIC SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D454 MATHEMATICAL ITALIC SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D456 MATHEMATICAL ITALIC SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D457 MATHEMATICAL ITALIC SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D458 MATHEMATICAL ITALIC SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D459 MATHEMATICAL ITALIC SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D45A MATHEMATICAL ITALIC SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D45B MATHEMATICAL ITALIC SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D45C MATHEMATICAL ITALIC SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D45D MATHEMATICAL ITALIC SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D45E MATHEMATICAL ITALIC SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D45F MATHEMATICAL ITALIC SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D460 MATHEMATICAL ITALIC SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D461 MATHEMATICAL ITALIC SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D462 MATHEMATICAL ITALIC SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D463 MATHEMATICAL ITALIC SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D464 MATHEMATICAL ITALIC SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D465 MATHEMATICAL ITALIC SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D466 MATHEMATICAL ITALIC SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D467 MATHEMATICAL ITALIC SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D468 MATHEMATICAL BOLD ITALIC CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D469 MATHEMATICAL BOLD ITALIC CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D46A MATHEMATICAL BOLD ITALIC CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D46B MATHEMATICAL BOLD ITALIC CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D46C MATHEMATICAL BOLD ITALIC CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D46D MATHEMATICAL BOLD ITALIC CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D46E MATHEMATICAL BOLD ITALIC CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D46F MATHEMATICAL BOLD ITALIC CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D470 MATHEMATICAL BOLD ITALIC CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D471 MATHEMATICAL BOLD ITALIC CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D472 MATHEMATICAL BOLD ITALIC CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D473 MATHEMATICAL BOLD ITALIC CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D474 MATHEMATICAL BOLD ITALIC CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D475 MATHEMATICAL BOLD ITALIC CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D476 MATHEMATICAL BOLD ITALIC CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D477 MATHEMATICAL BOLD ITALIC CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D478 MATHEMATICAL BOLD ITALIC CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D479 MATHEMATICAL BOLD ITALIC CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D47A MATHEMATICAL BOLD ITALIC CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D47B MATHEMATICAL BOLD ITALIC CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D47C MATHEMATICAL BOLD ITALIC CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D47D MATHEMATICAL BOLD ITALIC CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D47E MATHEMATICAL BOLD ITALIC CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D47F MATHEMATICAL BOLD ITALIC CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D480 MATHEMATICAL BOLD ITALIC CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D481 MATHEMATICAL BOLD ITALIC CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D482 MATHEMATICAL BOLD ITALIC SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D483 MATHEMATICAL BOLD ITALIC SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D484 MATHEMATICAL BOLD ITALIC SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D485 MATHEMATICAL BOLD ITALIC SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D486 MATHEMATICAL BOLD ITALIC SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D487 MATHEMATICAL BOLD ITALIC SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D488 MATHEMATICAL BOLD ITALIC SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D489 MATHEMATICAL BOLD ITALIC SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D48A MATHEMATICAL BOLD ITALIC SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D48B MATHEMATICAL BOLD ITALIC SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D48C MATHEMATICAL BOLD ITALIC SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D48D MATHEMATICAL BOLD ITALIC SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D48E MATHEMATICAL BOLD ITALIC SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D48F MATHEMATICAL BOLD ITALIC SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D490 MATHEMATICAL BOLD ITALIC SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D491 MATHEMATICAL BOLD ITALIC SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D492 MATHEMATICAL BOLD ITALIC SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D493 MATHEMATICAL BOLD ITALIC SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D494 MATHEMATICAL BOLD ITALIC SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D495 MATHEMATICAL BOLD ITALIC SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D496 MATHEMATICAL BOLD ITALIC SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D497 MATHEMATICAL BOLD ITALIC SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D498 MATHEMATICAL BOLD ITALIC SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D499 MATHEMATICAL BOLD ITALIC SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D49A MATHEMATICAL BOLD ITALIC SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D49B MATHEMATICAL BOLD ITALIC SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D49C MATHEMATICAL SCRIPT CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D49E MATHEMATICAL SCRIPT CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D49F MATHEMATICAL SCRIPT CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D4A2 MATHEMATICAL SCRIPT CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D4A5 MATHEMATICAL SCRIPT CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D4A6 MATHEMATICAL SCRIPT CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D4A9 MATHEMATICAL SCRIPT CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D4AA MATHEMATICAL SCRIPT CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D4AB MATHEMATICAL SCRIPT CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D4AC MATHEMATICAL SCRIPT CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D4AE MATHEMATICAL SCRIPT CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D4AF MATHEMATICAL SCRIPT CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D4B0 MATHEMATICAL SCRIPT CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D4B1 MATHEMATICAL SCRIPT CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D4B2 MATHEMATICAL SCRIPT CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D4B3 MATHEMATICAL SCRIPT CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D4B4 MATHEMATICAL SCRIPT CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D4B5 MATHEMATICAL SCRIPT CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D4B6 MATHEMATICAL SCRIPT SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D4B7 MATHEMATICAL SCRIPT SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D4B8 MATHEMATICAL SCRIPT SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D4B9 MATHEMATICAL SCRIPT SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D4BB MATHEMATICAL SCRIPT SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D4BD MATHEMATICAL SCRIPT SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D4BE MATHEMATICAL SCRIPT SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D4BF MATHEMATICAL SCRIPT SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D4C0 MATHEMATICAL SCRIPT SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D4C2 MATHEMATICAL SCRIPT SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D4C3 MATHEMATICAL SCRIPT SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D4C5 MATHEMATICAL SCRIPT SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D4C6 MATHEMATICAL SCRIPT SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D4C7 MATHEMATICAL SCRIPT SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D4C8 MATHEMATICAL SCRIPT SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D4C9 MATHEMATICAL SCRIPT SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D4CA MATHEMATICAL SCRIPT SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D4CB MATHEMATICAL SCRIPT SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D4CC MATHEMATICAL SCRIPT SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D4CD MATHEMATICAL SCRIPT SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D4CE MATHEMATICAL SCRIPT SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D4CF MATHEMATICAL SCRIPT SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D4D0 MATHEMATICAL BOLD SCRIPT CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D4D1 MATHEMATICAL BOLD SCRIPT CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D4D2 MATHEMATICAL BOLD SCRIPT CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D4D3 MATHEMATICAL BOLD SCRIPT CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D4D4 MATHEMATICAL BOLD SCRIPT CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D4D5 MATHEMATICAL BOLD SCRIPT CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D4D6 MATHEMATICAL BOLD SCRIPT CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D4D7 MATHEMATICAL BOLD SCRIPT CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D4D8 MATHEMATICAL BOLD SCRIPT CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D4D9 MATHEMATICAL BOLD SCRIPT CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D4DA MATHEMATICAL BOLD SCRIPT CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D4DB MATHEMATICAL BOLD SCRIPT CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D4DC MATHEMATICAL BOLD SCRIPT CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D4DD MATHEMATICAL BOLD SCRIPT CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D4DE MATHEMATICAL BOLD SCRIPT CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D4DF MATHEMATICAL BOLD SCRIPT CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D4E0 MATHEMATICAL BOLD SCRIPT CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D4E1 MATHEMATICAL BOLD SCRIPT CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D4E2 MATHEMATICAL BOLD SCRIPT CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D4E3 MATHEMATICAL BOLD SCRIPT CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D4E4 MATHEMATICAL BOLD SCRIPT CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D4E5 MATHEMATICAL BOLD SCRIPT CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D4E6 MATHEMATICAL BOLD SCRIPT CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D4E7 MATHEMATICAL BOLD SCRIPT CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D4E8 MATHEMATICAL BOLD SCRIPT CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D4E9 MATHEMATICAL BOLD SCRIPT CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D4EA MATHEMATICAL BOLD SCRIPT SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D4EB MATHEMATICAL BOLD SCRIPT SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D4EC MATHEMATICAL BOLD SCRIPT SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D4ED MATHEMATICAL BOLD SCRIPT SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D4EE MATHEMATICAL BOLD SCRIPT SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D4EF MATHEMATICAL BOLD SCRIPT SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D4F0 MATHEMATICAL BOLD SCRIPT SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D4F1 MATHEMATICAL BOLD SCRIPT SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D4F2 MATHEMATICAL BOLD SCRIPT SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D4F3 MATHEMATICAL BOLD SCRIPT SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D4F4 MATHEMATICAL BOLD SCRIPT SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D4F5 MATHEMATICAL BOLD SCRIPT SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D4F6 MATHEMATICAL BOLD SCRIPT SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D4F7 MATHEMATICAL BOLD SCRIPT SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D4F8 MATHEMATICAL BOLD SCRIPT SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D4F9 MATHEMATICAL BOLD SCRIPT SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D4FA MATHEMATICAL BOLD SCRIPT SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D4FB MATHEMATICAL BOLD SCRIPT SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D4FC MATHEMATICAL BOLD SCRIPT SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D4FD MATHEMATICAL BOLD SCRIPT SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D4FE MATHEMATICAL BOLD SCRIPT SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D4FF MATHEMATICAL BOLD SCRIPT SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D500 MATHEMATICAL BOLD SCRIPT SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D501 MATHEMATICAL BOLD SCRIPT SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D502 MATHEMATICAL BOLD SCRIPT SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D503 MATHEMATICAL BOLD SCRIPT SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D504 MATHEMATICAL FRAKTUR CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D505 MATHEMATICAL FRAKTUR CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D507 MATHEMATICAL FRAKTUR CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D508 MATHEMATICAL FRAKTUR CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D509 MATHEMATICAL FRAKTUR CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D50A MATHEMATICAL FRAKTUR CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D50D MATHEMATICAL FRAKTUR CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D50E MATHEMATICAL FRAKTUR CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D50F MATHEMATICAL FRAKTUR CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D510 MATHEMATICAL FRAKTUR CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D511 MATHEMATICAL FRAKTUR CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D512 MATHEMATICAL FRAKTUR CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D513 MATHEMATICAL FRAKTUR CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D514 MATHEMATICAL FRAKTUR CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D516 MATHEMATICAL FRAKTUR CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D517 MATHEMATICAL FRAKTUR CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D518 MATHEMATICAL FRAKTUR CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D519 MATHEMATICAL FRAKTUR CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D51A MATHEMATICAL FRAKTUR CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D51B MATHEMATICAL FRAKTUR CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D51C MATHEMATICAL FRAKTUR CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D51E MATHEMATICAL FRAKTUR SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D51F MATHEMATICAL FRAKTUR SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D520 MATHEMATICAL FRAKTUR SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D521 MATHEMATICAL FRAKTUR SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D522 MATHEMATICAL FRAKTUR SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D523 MATHEMATICAL FRAKTUR SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D524 MATHEMATICAL FRAKTUR SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D525 MATHEMATICAL FRAKTUR SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D526 MATHEMATICAL FRAKTUR SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D527 MATHEMATICAL FRAKTUR SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D528 MATHEMATICAL FRAKTUR SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D529 MATHEMATICAL FRAKTUR SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D52A MATHEMATICAL FRAKTUR SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D52B MATHEMATICAL FRAKTUR SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D52C MATHEMATICAL FRAKTUR SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D52D MATHEMATICAL FRAKTUR SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D52E MATHEMATICAL FRAKTUR SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D52F MATHEMATICAL FRAKTUR SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D530 MATHEMATICAL FRAKTUR SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D531 MATHEMATICAL FRAKTUR SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D532 MATHEMATICAL FRAKTUR SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D533 MATHEMATICAL FRAKTUR SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D534 MATHEMATICAL FRAKTUR SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D535 MATHEMATICAL FRAKTUR SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D536 MATHEMATICAL FRAKTUR SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D537 MATHEMATICAL FRAKTUR SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D538 MATHEMATICAL DOUBLE-STRUCK CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D539 MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D53B MATHEMATICAL DOUBLE-STRUCK CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D53C MATHEMATICAL DOUBLE-STRUCK CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D53D MATHEMATICAL DOUBLE-STRUCK CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D53E MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D540 MATHEMATICAL DOUBLE-STRUCK CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D541 MATHEMATICAL DOUBLE-STRUCK CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D542 MATHEMATICAL DOUBLE-STRUCK CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D543 MATHEMATICAL DOUBLE-STRUCK CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D544 MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D546 MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D54A MATHEMATICAL DOUBLE-STRUCK CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D54B MATHEMATICAL DOUBLE-STRUCK CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D54C MATHEMATICAL DOUBLE-STRUCK CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D54D MATHEMATICAL DOUBLE-STRUCK CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D54E MATHEMATICAL DOUBLE-STRUCK CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D54F MATHEMATICAL DOUBLE-STRUCK CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D550 MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D552 MATHEMATICAL DOUBLE-STRUCK SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D553 MATHEMATICAL DOUBLE-STRUCK SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D554 MATHEMATICAL DOUBLE-STRUCK SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D555 MATHEMATICAL DOUBLE-STRUCK SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D556 MATHEMATICAL DOUBLE-STRUCK SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D557 MATHEMATICAL DOUBLE-STRUCK SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D558 MATHEMATICAL DOUBLE-STRUCK SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D559 MATHEMATICAL DOUBLE-STRUCK SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D55A MATHEMATICAL DOUBLE-STRUCK SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D55B MATHEMATICAL DOUBLE-STRUCK SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D55C MATHEMATICAL DOUBLE-STRUCK SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D55D MATHEMATICAL DOUBLE-STRUCK SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D55E MATHEMATICAL DOUBLE-STRUCK SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D55F MATHEMATICAL DOUBLE-STRUCK SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D560 MATHEMATICAL DOUBLE-STRUCK SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D561 MATHEMATICAL DOUBLE-STRUCK SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D562 MATHEMATICAL DOUBLE-STRUCK SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D563 MATHEMATICAL DOUBLE-STRUCK SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D564 MATHEMATICAL DOUBLE-STRUCK SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D565 MATHEMATICAL DOUBLE-STRUCK SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D566 MATHEMATICAL DOUBLE-STRUCK SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D567 MATHEMATICAL DOUBLE-STRUCK SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D568 MATHEMATICAL DOUBLE-STRUCK SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D569 MATHEMATICAL DOUBLE-STRUCK SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D56A MATHEMATICAL DOUBLE-STRUCK SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D56B MATHEMATICAL DOUBLE-STRUCK SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D56C MATHEMATICAL BOLD FRAKTUR CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D56D MATHEMATICAL BOLD FRAKTUR CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D56E MATHEMATICAL BOLD FRAKTUR CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D56F MATHEMATICAL BOLD FRAKTUR CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D570 MATHEMATICAL BOLD FRAKTUR CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D571 MATHEMATICAL BOLD FRAKTUR CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D572 MATHEMATICAL BOLD FRAKTUR CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D573 MATHEMATICAL BOLD FRAKTUR CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D574 MATHEMATICAL BOLD FRAKTUR CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D575 MATHEMATICAL BOLD FRAKTUR CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D576 MATHEMATICAL BOLD FRAKTUR CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D577 MATHEMATICAL BOLD FRAKTUR CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D578 MATHEMATICAL BOLD FRAKTUR CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D579 MATHEMATICAL BOLD FRAKTUR CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D57A MATHEMATICAL BOLD FRAKTUR CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D57B MATHEMATICAL BOLD FRAKTUR CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D57C MATHEMATICAL BOLD FRAKTUR CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D57D MATHEMATICAL BOLD FRAKTUR CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D57E MATHEMATICAL BOLD FRAKTUR CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D57F MATHEMATICAL BOLD FRAKTUR CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D580 MATHEMATICAL BOLD FRAKTUR CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D581 MATHEMATICAL BOLD FRAKTUR CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D582 MATHEMATICAL BOLD FRAKTUR CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D583 MATHEMATICAL BOLD FRAKTUR CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D584 MATHEMATICAL BOLD FRAKTUR CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D585 MATHEMATICAL BOLD FRAKTUR CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D586 MATHEMATICAL BOLD FRAKTUR SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D587 MATHEMATICAL BOLD FRAKTUR SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D588 MATHEMATICAL BOLD FRAKTUR SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D589 MATHEMATICAL BOLD FRAKTUR SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D58A MATHEMATICAL BOLD FRAKTUR SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D58B MATHEMATICAL BOLD FRAKTUR SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D58C MATHEMATICAL BOLD FRAKTUR SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D58D MATHEMATICAL BOLD FRAKTUR SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D58E MATHEMATICAL BOLD FRAKTUR SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D58F MATHEMATICAL BOLD FRAKTUR SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D590 MATHEMATICAL BOLD FRAKTUR SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D591 MATHEMATICAL BOLD FRAKTUR SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D592 MATHEMATICAL BOLD FRAKTUR SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D593 MATHEMATICAL BOLD FRAKTUR SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D594 MATHEMATICAL BOLD FRAKTUR SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D595 MATHEMATICAL BOLD FRAKTUR SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D596 MATHEMATICAL BOLD FRAKTUR SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D597 MATHEMATICAL BOLD FRAKTUR SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D598 MATHEMATICAL BOLD FRAKTUR SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D599 MATHEMATICAL BOLD FRAKTUR SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D59A MATHEMATICAL BOLD FRAKTUR SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D59B MATHEMATICAL BOLD FRAKTUR SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D59C MATHEMATICAL BOLD FRAKTUR SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D59D MATHEMATICAL BOLD FRAKTUR SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D59E MATHEMATICAL BOLD FRAKTUR SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D59F MATHEMATICAL BOLD FRAKTUR SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D5A0 MATHEMATICAL SANS-SERIF CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D5A1 MATHEMATICAL SANS-SERIF CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D5A2 MATHEMATICAL SANS-SERIF CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D5A3 MATHEMATICAL SANS-SERIF CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D5A4 MATHEMATICAL SANS-SERIF CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D5A5 MATHEMATICAL SANS-SERIF CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D5A6 MATHEMATICAL SANS-SERIF CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D5A7 MATHEMATICAL SANS-SERIF CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D5A8 MATHEMATICAL SANS-SERIF CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D5A9 MATHEMATICAL SANS-SERIF CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D5AA MATHEMATICAL SANS-SERIF CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D5AB MATHEMATICAL SANS-SERIF CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D5AC MATHEMATICAL SANS-SERIF CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D5AD MATHEMATICAL SANS-SERIF CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D5AE MATHEMATICAL SANS-SERIF CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D5AF MATHEMATICAL SANS-SERIF CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D5B0 MATHEMATICAL SANS-SERIF CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D5B1 MATHEMATICAL SANS-SERIF CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D5B2 MATHEMATICAL SANS-SERIF CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D5B3 MATHEMATICAL SANS-SERIF CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D5B4 MATHEMATICAL SANS-SERIF CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D5B5 MATHEMATICAL SANS-SERIF CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D5B6 MATHEMATICAL SANS-SERIF CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D5B7 MATHEMATICAL SANS-SERIF CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D5B8 MATHEMATICAL SANS-SERIF CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D5B9 MATHEMATICAL SANS-SERIF CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D5BA MATHEMATICAL SANS-SERIF SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D5BB MATHEMATICAL SANS-SERIF SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D5BC MATHEMATICAL SANS-SERIF SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D5BD MATHEMATICAL SANS-SERIF SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D5BE MATHEMATICAL SANS-SERIF SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D5BF MATHEMATICAL SANS-SERIF SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D5C0 MATHEMATICAL SANS-SERIF SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D5C1 MATHEMATICAL SANS-SERIF SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D5C2 MATHEMATICAL SANS-SERIF SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D5C3 MATHEMATICAL SANS-SERIF SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D5C4 MATHEMATICAL SANS-SERIF SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D5C5 MATHEMATICAL SANS-SERIF SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D5C6 MATHEMATICAL SANS-SERIF SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D5C7 MATHEMATICAL SANS-SERIF SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D5C8 MATHEMATICAL SANS-SERIF SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D5C9 MATHEMATICAL SANS-SERIF SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D5CA MATHEMATICAL SANS-SERIF SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D5CB MATHEMATICAL SANS-SERIF SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D5CC MATHEMATICAL SANS-SERIF SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D5CD MATHEMATICAL SANS-SERIF SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D5CE MATHEMATICAL SANS-SERIF SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D5CF MATHEMATICAL SANS-SERIF SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D5D0 MATHEMATICAL SANS-SERIF SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D5D1 MATHEMATICAL SANS-SERIF SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D5D2 MATHEMATICAL SANS-SERIF SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D5D3 MATHEMATICAL SANS-SERIF SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D5D4 MATHEMATICAL SANS-SERIF BOLD CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D5D5 MATHEMATICAL SANS-SERIF BOLD CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D5D6 MATHEMATICAL SANS-SERIF BOLD CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D5D7 MATHEMATICAL SANS-SERIF BOLD CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D5D8 MATHEMATICAL SANS-SERIF BOLD CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D5D9 MATHEMATICAL SANS-SERIF BOLD CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D5DA MATHEMATICAL SANS-SERIF BOLD CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D5DB MATHEMATICAL SANS-SERIF BOLD CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D5DC MATHEMATICAL SANS-SERIF BOLD CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D5DD MATHEMATICAL SANS-SERIF BOLD CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D5DE MATHEMATICAL SANS-SERIF BOLD CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D5DF MATHEMATICAL SANS-SERIF BOLD CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D5E0 MATHEMATICAL SANS-SERIF BOLD CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D5E1 MATHEMATICAL SANS-SERIF BOLD CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D5E2 MATHEMATICAL SANS-SERIF BOLD CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D5E3 MATHEMATICAL SANS-SERIF BOLD CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D5E4 MATHEMATICAL SANS-SERIF BOLD CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D5E5 MATHEMATICAL SANS-SERIF BOLD CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D5E6 MATHEMATICAL SANS-SERIF BOLD CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D5E7 MATHEMATICAL SANS-SERIF BOLD CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D5E8 MATHEMATICAL SANS-SERIF BOLD CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D5E9 MATHEMATICAL SANS-SERIF BOLD CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D5EA MATHEMATICAL SANS-SERIF BOLD CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D5EB MATHEMATICAL SANS-SERIF BOLD CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D5EC MATHEMATICAL SANS-SERIF BOLD CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D5ED MATHEMATICAL SANS-SERIF BOLD CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D5EE MATHEMATICAL SANS-SERIF BOLD SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D5EF MATHEMATICAL SANS-SERIF BOLD SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D5F0 MATHEMATICAL SANS-SERIF BOLD SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D5F1 MATHEMATICAL SANS-SERIF BOLD SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D5F2 MATHEMATICAL SANS-SERIF BOLD SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D5F3 MATHEMATICAL SANS-SERIF BOLD SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D5F4 MATHEMATICAL SANS-SERIF BOLD SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D5F5 MATHEMATICAL SANS-SERIF BOLD SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D5F6 MATHEMATICAL SANS-SERIF BOLD SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D5F7 MATHEMATICAL SANS-SERIF BOLD SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D5F8 MATHEMATICAL SANS-SERIF BOLD SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D5F9 MATHEMATICAL SANS-SERIF BOLD SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D5FA MATHEMATICAL SANS-SERIF BOLD SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D5FB MATHEMATICAL SANS-SERIF BOLD SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D5FC MATHEMATICAL SANS-SERIF BOLD SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D5FD MATHEMATICAL SANS-SERIF BOLD SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D5FE MATHEMATICAL SANS-SERIF BOLD SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D5FF MATHEMATICAL SANS-SERIF BOLD SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D600 MATHEMATICAL SANS-SERIF BOLD SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D601 MATHEMATICAL SANS-SERIF BOLD SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D602 MATHEMATICAL SANS-SERIF BOLD SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D603 MATHEMATICAL SANS-SERIF BOLD SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D604 MATHEMATICAL SANS-SERIF BOLD SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D605 MATHEMATICAL SANS-SERIF BOLD SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D606 MATHEMATICAL SANS-SERIF BOLD SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D607 MATHEMATICAL SANS-SERIF BOLD SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D608 MATHEMATICAL SANS-SERIF ITALIC CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D609 MATHEMATICAL SANS-SERIF ITALIC CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D60A MATHEMATICAL SANS-SERIF ITALIC CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D60B MATHEMATICAL SANS-SERIF ITALIC CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D60C MATHEMATICAL SANS-SERIF ITALIC CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D60D MATHEMATICAL SANS-SERIF ITALIC CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D60E MATHEMATICAL SANS-SERIF ITALIC CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D60F MATHEMATICAL SANS-SERIF ITALIC CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D610 MATHEMATICAL SANS-SERIF ITALIC CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D611 MATHEMATICAL SANS-SERIF ITALIC CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D612 MATHEMATICAL SANS-SERIF ITALIC CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D613 MATHEMATICAL SANS-SERIF ITALIC CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D614 MATHEMATICAL SANS-SERIF ITALIC CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D615 MATHEMATICAL SANS-SERIF ITALIC CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D616 MATHEMATICAL SANS-SERIF ITALIC CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D617 MATHEMATICAL SANS-SERIF ITALIC CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D618 MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D619 MATHEMATICAL SANS-SERIF ITALIC CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D61A MATHEMATICAL SANS-SERIF ITALIC CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D61B MATHEMATICAL SANS-SERIF ITALIC CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D61C MATHEMATICAL SANS-SERIF ITALIC CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D61D MATHEMATICAL SANS-SERIF ITALIC CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D61E MATHEMATICAL SANS-SERIF ITALIC CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D61F MATHEMATICAL SANS-SERIF ITALIC CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D620 MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D621 MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D622 MATHEMATICAL SANS-SERIF ITALIC SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D623 MATHEMATICAL SANS-SERIF ITALIC SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D624 MATHEMATICAL SANS-SERIF ITALIC SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D625 MATHEMATICAL SANS-SERIF ITALIC SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D626 MATHEMATICAL SANS-SERIF ITALIC SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D627 MATHEMATICAL SANS-SERIF ITALIC SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D628 MATHEMATICAL SANS-SERIF ITALIC SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D629 MATHEMATICAL SANS-SERIF ITALIC SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D62A MATHEMATICAL SANS-SERIF ITALIC SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D62B MATHEMATICAL SANS-SERIF ITALIC SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D62C MATHEMATICAL SANS-SERIF ITALIC SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D62D MATHEMATICAL SANS-SERIF ITALIC SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D62E MATHEMATICAL SANS-SERIF ITALIC SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D62F MATHEMATICAL SANS-SERIF ITALIC SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D630 MATHEMATICAL SANS-SERIF ITALIC SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D631 MATHEMATICAL SANS-SERIF ITALIC SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D632 MATHEMATICAL SANS-SERIF ITALIC SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D633 MATHEMATICAL SANS-SERIF ITALIC SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D634 MATHEMATICAL SANS-SERIF ITALIC SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D635 MATHEMATICAL SANS-SERIF ITALIC SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D636 MATHEMATICAL SANS-SERIF ITALIC SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D637 MATHEMATICAL SANS-SERIF ITALIC SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D638 MATHEMATICAL SANS-SERIF ITALIC SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D639 MATHEMATICAL SANS-SERIF ITALIC SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D63A MATHEMATICAL SANS-SERIF ITALIC SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D63B MATHEMATICAL SANS-SERIF ITALIC SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D63C MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D63D MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D63E MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D63F MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D640 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D641 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D642 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D643 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D644 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D645 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D646 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D647 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D648 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D649 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D64A MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D64B MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D64C MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D64D MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D64E MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D64F MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D650 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D651 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D652 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D653 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D654 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D655 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D656 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D657 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D658 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D659 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D65A MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D65B MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D65C MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D65D MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D65E MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D65F MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D660 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D661 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D662 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D663 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D664 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D665 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D666 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D667 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D668 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D669 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D66A MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D66B MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D66C MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D66D MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D66E MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D66F MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D670 MATHEMATICAL MONOSPACE CAPITAL A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1D671 MATHEMATICAL MONOSPACE CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1D672 MATHEMATICAL MONOSPACE CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1D673 MATHEMATICAL MONOSPACE CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1D674 MATHEMATICAL MONOSPACE CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1D675 MATHEMATICAL MONOSPACE CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1D676 MATHEMATICAL MONOSPACE CAPITAL G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1D677 MATHEMATICAL MONOSPACE CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1D678 MATHEMATICAL MONOSPACE CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1D679 MATHEMATICAL MONOSPACE CAPITAL J
+ * 	004A LATIN CAPITAL LETTER J
+ * 1D67A MATHEMATICAL MONOSPACE CAPITAL K
+ * 	004B LATIN CAPITAL LETTER K
+ * 1D67B MATHEMATICAL MONOSPACE CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 1D67C MATHEMATICAL MONOSPACE CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 1D67D MATHEMATICAL MONOSPACE CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 1D67E MATHEMATICAL MONOSPACE CAPITAL O
+ * 	004F LATIN CAPITAL LETTER O
+ * 1D67F MATHEMATICAL MONOSPACE CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1D680 MATHEMATICAL MONOSPACE CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 1D681 MATHEMATICAL MONOSPACE CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1D682 MATHEMATICAL MONOSPACE CAPITAL S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1D683 MATHEMATICAL MONOSPACE CAPITAL T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1D684 MATHEMATICAL MONOSPACE CAPITAL U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1D685 MATHEMATICAL MONOSPACE CAPITAL V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1D686 MATHEMATICAL MONOSPACE CAPITAL W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1D687 MATHEMATICAL MONOSPACE CAPITAL X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1D688 MATHEMATICAL MONOSPACE CAPITAL Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1D689 MATHEMATICAL MONOSPACE CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1D68A MATHEMATICAL MONOSPACE SMALL A
+ * 	0061 LATIN SMALL LETTER A
+ * 1D68B MATHEMATICAL MONOSPACE SMALL B
+ * 	0062 LATIN SMALL LETTER B
+ * 1D68C MATHEMATICAL MONOSPACE SMALL C
+ * 	0063 LATIN SMALL LETTER C
+ * 1D68D MATHEMATICAL MONOSPACE SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 1D68E MATHEMATICAL MONOSPACE SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 1D68F MATHEMATICAL MONOSPACE SMALL F
+ * 	0066 LATIN SMALL LETTER F
+ * 1D690 MATHEMATICAL MONOSPACE SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 1D691 MATHEMATICAL MONOSPACE SMALL H
+ * 	0068 LATIN SMALL LETTER H
+ * 1D692 MATHEMATICAL MONOSPACE SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 1D693 MATHEMATICAL MONOSPACE SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 1D694 MATHEMATICAL MONOSPACE SMALL K
+ * 	006B LATIN SMALL LETTER K
+ * 1D695 MATHEMATICAL MONOSPACE SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 1D696 MATHEMATICAL MONOSPACE SMALL M
+ * 	006D LATIN SMALL LETTER M
+ * 1D697 MATHEMATICAL MONOSPACE SMALL N
+ * 	006E LATIN SMALL LETTER N
+ * 1D698 MATHEMATICAL MONOSPACE SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 1D699 MATHEMATICAL MONOSPACE SMALL P
+ * 	0070 LATIN SMALL LETTER P
+ * 1D69A MATHEMATICAL MONOSPACE SMALL Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 1D69B MATHEMATICAL MONOSPACE SMALL R
+ * 	0072 LATIN SMALL LETTER R
+ * 1D69C MATHEMATICAL MONOSPACE SMALL S
+ * 	0073 LATIN SMALL LETTER S
+ * 1D69D MATHEMATICAL MONOSPACE SMALL T
+ * 	0074 LATIN SMALL LETTER T
+ * 1D69E MATHEMATICAL MONOSPACE SMALL U
+ * 	0075 LATIN SMALL LETTER U
+ * 1D69F MATHEMATICAL MONOSPACE SMALL V
+ * 	0076 LATIN SMALL LETTER V
+ * 1D6A0 MATHEMATICAL MONOSPACE SMALL W
+ * 	0077 LATIN SMALL LETTER W
+ * 1D6A1 MATHEMATICAL MONOSPACE SMALL X
+ * 	0078 LATIN SMALL LETTER X
+ * 1D6A2 MATHEMATICAL MONOSPACE SMALL Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 1D6A3 MATHEMATICAL MONOSPACE SMALL Z
+ * 	007A LATIN SMALL LETTER Z
+ * 1D6A8 MATHEMATICAL BOLD CAPITAL ALPHA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1D6A9 MATHEMATICAL BOLD CAPITAL BETA
+ * 	0392 GREEK CAPITAL LETTER BETA
+ * 1D6AA MATHEMATICAL BOLD CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 1D6AB MATHEMATICAL BOLD CAPITAL DELTA
+ * 	0394 GREEK CAPITAL LETTER DELTA
+ * 1D6AC MATHEMATICAL BOLD CAPITAL EPSILON
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1D6AD MATHEMATICAL BOLD CAPITAL ZETA
+ * 	0396 GREEK CAPITAL LETTER ZETA
+ * 1D6AE MATHEMATICAL BOLD CAPITAL ETA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1D6AF MATHEMATICAL BOLD CAPITAL THETA
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D6B0 MATHEMATICAL BOLD CAPITAL IOTA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1D6B1 MATHEMATICAL BOLD CAPITAL KAPPA
+ * 	039A GREEK CAPITAL LETTER KAPPA
+ * 1D6B2 MATHEMATICAL BOLD CAPITAL LAMDA
+ * 	039B GREEK CAPITAL LETTER LAMDA
+ * 1D6B3 MATHEMATICAL BOLD CAPITAL MU
+ * 	039C GREEK CAPITAL LETTER MU
+ * 1D6B4 MATHEMATICAL BOLD CAPITAL NU
+ * 	039D GREEK CAPITAL LETTER NU
+ * 1D6B5 MATHEMATICAL BOLD CAPITAL XI
+ * 	039E GREEK CAPITAL LETTER XI
+ * 1D6B6 MATHEMATICAL BOLD CAPITAL OMICRON
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1D6B7 MATHEMATICAL BOLD CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 1D6B8 MATHEMATICAL BOLD CAPITAL RHO
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1D6B9 MATHEMATICAL BOLD CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D6BA MATHEMATICAL BOLD CAPITAL SIGMA
+ * 	03A3 GREEK CAPITAL LETTER SIGMA
+ * 1D6BB MATHEMATICAL BOLD CAPITAL TAU
+ * 	03A4 GREEK CAPITAL LETTER TAU
+ * 1D6BC MATHEMATICAL BOLD CAPITAL UPSILON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1D6BD MATHEMATICAL BOLD CAPITAL PHI
+ * 	03A6 GREEK CAPITAL LETTER PHI
+ * 1D6BE MATHEMATICAL BOLD CAPITAL CHI
+ * 	03A7 GREEK CAPITAL LETTER CHI
+ * 1D6BF MATHEMATICAL BOLD CAPITAL PSI
+ * 	03A8 GREEK CAPITAL LETTER PSI
+ * 1D6C0 MATHEMATICAL BOLD CAPITAL OMEGA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1D6C1 MATHEMATICAL BOLD NABLA
+ * 	2207 NABLA
+ * 1D6C2 MATHEMATICAL BOLD SMALL ALPHA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1D6C3 MATHEMATICAL BOLD SMALL BETA
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 1D6C4 MATHEMATICAL BOLD SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 1D6C5 MATHEMATICAL BOLD SMALL DELTA
+ * 	03B4 GREEK SMALL LETTER DELTA
+ * 1D6C6 MATHEMATICAL BOLD SMALL EPSILON
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D6C7 MATHEMATICAL BOLD SMALL ZETA
+ * 	03B6 GREEK SMALL LETTER ZETA
+ * 1D6C8 MATHEMATICAL BOLD SMALL ETA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1D6C9 MATHEMATICAL BOLD SMALL THETA
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D6CA MATHEMATICAL BOLD SMALL IOTA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1D6CB MATHEMATICAL BOLD SMALL KAPPA
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D6CC MATHEMATICAL BOLD SMALL LAMDA
+ * 	03BB GREEK SMALL LETTER LAMDA
+ * 1D6CD MATHEMATICAL BOLD SMALL MU
+ * 	03BC GREEK SMALL LETTER MU
+ * 1D6CE MATHEMATICAL BOLD SMALL NU
+ * 	03BD GREEK SMALL LETTER NU
+ * 1D6CF MATHEMATICAL BOLD SMALL XI
+ * 	03BE GREEK SMALL LETTER XI
+ * 1D6D0 MATHEMATICAL BOLD SMALL OMICRON
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1D6D1 MATHEMATICAL BOLD SMALL PI
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D6D2 MATHEMATICAL BOLD SMALL RHO
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D6D3 MATHEMATICAL BOLD SMALL FINAL SIGMA
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 1D6D4 MATHEMATICAL BOLD SMALL SIGMA
+ * 	03C3 GREEK SMALL LETTER SIGMA
+ * 1D6D5 MATHEMATICAL BOLD SMALL TAU
+ * 	03C4 GREEK SMALL LETTER TAU
+ * 1D6D6 MATHEMATICAL BOLD SMALL UPSILON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1D6D7 MATHEMATICAL BOLD SMALL PHI
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D6D8 MATHEMATICAL BOLD SMALL CHI
+ * 	03C7 GREEK SMALL LETTER CHI
+ * 1D6D9 MATHEMATICAL BOLD SMALL PSI
+ * 	03C8 GREEK SMALL LETTER PSI
+ * 1D6DA MATHEMATICAL BOLD SMALL OMEGA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1D6DB MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+ * 	2202 PARTIAL DIFFERENTIAL
+ * 1D6DC MATHEMATICAL BOLD EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D6DD MATHEMATICAL BOLD THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D6DE MATHEMATICAL BOLD KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D6DF MATHEMATICAL BOLD PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D6E0 MATHEMATICAL BOLD RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D6E1 MATHEMATICAL BOLD PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D6E2 MATHEMATICAL ITALIC CAPITAL ALPHA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1D6E3 MATHEMATICAL ITALIC CAPITAL BETA
+ * 	0392 GREEK CAPITAL LETTER BETA
+ * 1D6E4 MATHEMATICAL ITALIC CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 1D6E5 MATHEMATICAL ITALIC CAPITAL DELTA
+ * 	0394 GREEK CAPITAL LETTER DELTA
+ * 1D6E6 MATHEMATICAL ITALIC CAPITAL EPSILON
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1D6E7 MATHEMATICAL ITALIC CAPITAL ZETA
+ * 	0396 GREEK CAPITAL LETTER ZETA
+ * 1D6E8 MATHEMATICAL ITALIC CAPITAL ETA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1D6E9 MATHEMATICAL ITALIC CAPITAL THETA
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D6EA MATHEMATICAL ITALIC CAPITAL IOTA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1D6EB MATHEMATICAL ITALIC CAPITAL KAPPA
+ * 	039A GREEK CAPITAL LETTER KAPPA
+ * 1D6EC MATHEMATICAL ITALIC CAPITAL LAMDA
+ * 	039B GREEK CAPITAL LETTER LAMDA
+ * 1D6ED MATHEMATICAL ITALIC CAPITAL MU
+ * 	039C GREEK CAPITAL LETTER MU
+ * 1D6EE MATHEMATICAL ITALIC CAPITAL NU
+ * 	039D GREEK CAPITAL LETTER NU
+ * 1D6EF MATHEMATICAL ITALIC CAPITAL XI
+ * 	039E GREEK CAPITAL LETTER XI
+ * 1D6F0 MATHEMATICAL ITALIC CAPITAL OMICRON
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1D6F1 MATHEMATICAL ITALIC CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 1D6F2 MATHEMATICAL ITALIC CAPITAL RHO
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1D6F3 MATHEMATICAL ITALIC CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D6F4 MATHEMATICAL ITALIC CAPITAL SIGMA
+ * 	03A3 GREEK CAPITAL LETTER SIGMA
+ * 1D6F5 MATHEMATICAL ITALIC CAPITAL TAU
+ * 	03A4 GREEK CAPITAL LETTER TAU
+ * 1D6F6 MATHEMATICAL ITALIC CAPITAL UPSILON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1D6F7 MATHEMATICAL ITALIC CAPITAL PHI
+ * 	03A6 GREEK CAPITAL LETTER PHI
+ * 1D6F8 MATHEMATICAL ITALIC CAPITAL CHI
+ * 	03A7 GREEK CAPITAL LETTER CHI
+ * 1D6F9 MATHEMATICAL ITALIC CAPITAL PSI
+ * 	03A8 GREEK CAPITAL LETTER PSI
+ * 1D6FA MATHEMATICAL ITALIC CAPITAL OMEGA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1D6FB MATHEMATICAL ITALIC NABLA
+ * 	2207 NABLA
+ * 1D6FC MATHEMATICAL ITALIC SMALL ALPHA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1D6FD MATHEMATICAL ITALIC SMALL BETA
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 1D6FE MATHEMATICAL ITALIC SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 1D6FF MATHEMATICAL ITALIC SMALL DELTA
+ * 	03B4 GREEK SMALL LETTER DELTA
+ * 1D700 MATHEMATICAL ITALIC SMALL EPSILON
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D701 MATHEMATICAL ITALIC SMALL ZETA
+ * 	03B6 GREEK SMALL LETTER ZETA
+ * 1D702 MATHEMATICAL ITALIC SMALL ETA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1D703 MATHEMATICAL ITALIC SMALL THETA
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D704 MATHEMATICAL ITALIC SMALL IOTA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1D705 MATHEMATICAL ITALIC SMALL KAPPA
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D706 MATHEMATICAL ITALIC SMALL LAMDA
+ * 	03BB GREEK SMALL LETTER LAMDA
+ * 1D707 MATHEMATICAL ITALIC SMALL MU
+ * 	03BC GREEK SMALL LETTER MU
+ * 1D708 MATHEMATICAL ITALIC SMALL NU
+ * 	03BD GREEK SMALL LETTER NU
+ * 1D709 MATHEMATICAL ITALIC SMALL XI
+ * 	03BE GREEK SMALL LETTER XI
+ * 1D70A MATHEMATICAL ITALIC SMALL OMICRON
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1D70B MATHEMATICAL ITALIC SMALL PI
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D70C MATHEMATICAL ITALIC SMALL RHO
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D70D MATHEMATICAL ITALIC SMALL FINAL SIGMA
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 1D70E MATHEMATICAL ITALIC SMALL SIGMA
+ * 	03C3 GREEK SMALL LETTER SIGMA
+ * 1D70F MATHEMATICAL ITALIC SMALL TAU
+ * 	03C4 GREEK SMALL LETTER TAU
+ * 1D710 MATHEMATICAL ITALIC SMALL UPSILON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1D711 MATHEMATICAL ITALIC SMALL PHI
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D712 MATHEMATICAL ITALIC SMALL CHI
+ * 	03C7 GREEK SMALL LETTER CHI
+ * 1D713 MATHEMATICAL ITALIC SMALL PSI
+ * 	03C8 GREEK SMALL LETTER PSI
+ * 1D714 MATHEMATICAL ITALIC SMALL OMEGA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1D715 MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+ * 	2202 PARTIAL DIFFERENTIAL
+ * 1D716 MATHEMATICAL ITALIC EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D717 MATHEMATICAL ITALIC THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D718 MATHEMATICAL ITALIC KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D719 MATHEMATICAL ITALIC PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D71A MATHEMATICAL ITALIC RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D71B MATHEMATICAL ITALIC PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D71C MATHEMATICAL BOLD ITALIC CAPITAL ALPHA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1D71D MATHEMATICAL BOLD ITALIC CAPITAL BETA
+ * 	0392 GREEK CAPITAL LETTER BETA
+ * 1D71E MATHEMATICAL BOLD ITALIC CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 1D71F MATHEMATICAL BOLD ITALIC CAPITAL DELTA
+ * 	0394 GREEK CAPITAL LETTER DELTA
+ * 1D720 MATHEMATICAL BOLD ITALIC CAPITAL EPSILON
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1D721 MATHEMATICAL BOLD ITALIC CAPITAL ZETA
+ * 	0396 GREEK CAPITAL LETTER ZETA
+ * 1D722 MATHEMATICAL BOLD ITALIC CAPITAL ETA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1D723 MATHEMATICAL BOLD ITALIC CAPITAL THETA
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D724 MATHEMATICAL BOLD ITALIC CAPITAL IOTA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1D725 MATHEMATICAL BOLD ITALIC CAPITAL KAPPA
+ * 	039A GREEK CAPITAL LETTER KAPPA
+ * 1D726 MATHEMATICAL BOLD ITALIC CAPITAL LAMDA
+ * 	039B GREEK CAPITAL LETTER LAMDA
+ * 1D727 MATHEMATICAL BOLD ITALIC CAPITAL MU
+ * 	039C GREEK CAPITAL LETTER MU
+ * 1D728 MATHEMATICAL BOLD ITALIC CAPITAL NU
+ * 	039D GREEK CAPITAL LETTER NU
+ * 1D729 MATHEMATICAL BOLD ITALIC CAPITAL XI
+ * 	039E GREEK CAPITAL LETTER XI
+ * 1D72A MATHEMATICAL BOLD ITALIC CAPITAL OMICRON
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1D72B MATHEMATICAL BOLD ITALIC CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 1D72C MATHEMATICAL BOLD ITALIC CAPITAL RHO
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1D72D MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D72E MATHEMATICAL BOLD ITALIC CAPITAL SIGMA
+ * 	03A3 GREEK CAPITAL LETTER SIGMA
+ * 1D72F MATHEMATICAL BOLD ITALIC CAPITAL TAU
+ * 	03A4 GREEK CAPITAL LETTER TAU
+ * 1D730 MATHEMATICAL BOLD ITALIC CAPITAL UPSILON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1D731 MATHEMATICAL BOLD ITALIC CAPITAL PHI
+ * 	03A6 GREEK CAPITAL LETTER PHI
+ * 1D732 MATHEMATICAL BOLD ITALIC CAPITAL CHI
+ * 	03A7 GREEK CAPITAL LETTER CHI
+ * 1D733 MATHEMATICAL BOLD ITALIC CAPITAL PSI
+ * 	03A8 GREEK CAPITAL LETTER PSI
+ * 1D734 MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1D735 MATHEMATICAL BOLD ITALIC NABLA
+ * 	2207 NABLA
+ * 1D736 MATHEMATICAL BOLD ITALIC SMALL ALPHA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1D737 MATHEMATICAL BOLD ITALIC SMALL BETA
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 1D738 MATHEMATICAL BOLD ITALIC SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 1D739 MATHEMATICAL BOLD ITALIC SMALL DELTA
+ * 	03B4 GREEK SMALL LETTER DELTA
+ * 1D73A MATHEMATICAL BOLD ITALIC SMALL EPSILON
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D73B MATHEMATICAL BOLD ITALIC SMALL ZETA
+ * 	03B6 GREEK SMALL LETTER ZETA
+ * 1D73C MATHEMATICAL BOLD ITALIC SMALL ETA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1D73D MATHEMATICAL BOLD ITALIC SMALL THETA
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D73E MATHEMATICAL BOLD ITALIC SMALL IOTA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1D73F MATHEMATICAL BOLD ITALIC SMALL KAPPA
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D740 MATHEMATICAL BOLD ITALIC SMALL LAMDA
+ * 	03BB GREEK SMALL LETTER LAMDA
+ * 1D741 MATHEMATICAL BOLD ITALIC SMALL MU
+ * 	03BC GREEK SMALL LETTER MU
+ * 1D742 MATHEMATICAL BOLD ITALIC SMALL NU
+ * 	03BD GREEK SMALL LETTER NU
+ * 1D743 MATHEMATICAL BOLD ITALIC SMALL XI
+ * 	03BE GREEK SMALL LETTER XI
+ * 1D744 MATHEMATICAL BOLD ITALIC SMALL OMICRON
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1D745 MATHEMATICAL BOLD ITALIC SMALL PI
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D746 MATHEMATICAL BOLD ITALIC SMALL RHO
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D747 MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 1D748 MATHEMATICAL BOLD ITALIC SMALL SIGMA
+ * 	03C3 GREEK SMALL LETTER SIGMA
+ * 1D749 MATHEMATICAL BOLD ITALIC SMALL TAU
+ * 	03C4 GREEK SMALL LETTER TAU
+ * 1D74A MATHEMATICAL BOLD ITALIC SMALL UPSILON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1D74B MATHEMATICAL BOLD ITALIC SMALL PHI
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D74C MATHEMATICAL BOLD ITALIC SMALL CHI
+ * 	03C7 GREEK SMALL LETTER CHI
+ * 1D74D MATHEMATICAL BOLD ITALIC SMALL PSI
+ * 	03C8 GREEK SMALL LETTER PSI
+ * 1D74E MATHEMATICAL BOLD ITALIC SMALL OMEGA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1D74F MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+ * 	2202 PARTIAL DIFFERENTIAL
+ * 1D750 MATHEMATICAL BOLD ITALIC EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D751 MATHEMATICAL BOLD ITALIC THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D752 MATHEMATICAL BOLD ITALIC KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D753 MATHEMATICAL BOLD ITALIC PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D754 MATHEMATICAL BOLD ITALIC RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D755 MATHEMATICAL BOLD ITALIC PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D756 MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1D757 MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA
+ * 	0392 GREEK CAPITAL LETTER BETA
+ * 1D758 MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 1D759 MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA
+ * 	0394 GREEK CAPITAL LETTER DELTA
+ * 1D75A MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1D75B MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA
+ * 	0396 GREEK CAPITAL LETTER ZETA
+ * 1D75C MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1D75D MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D75E MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1D75F MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA
+ * 	039A GREEK CAPITAL LETTER KAPPA
+ * 1D760 MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA
+ * 	039B GREEK CAPITAL LETTER LAMDA
+ * 1D761 MATHEMATICAL SANS-SERIF BOLD CAPITAL MU
+ * 	039C GREEK CAPITAL LETTER MU
+ * 1D762 MATHEMATICAL SANS-SERIF BOLD CAPITAL NU
+ * 	039D GREEK CAPITAL LETTER NU
+ * 1D763 MATHEMATICAL SANS-SERIF BOLD CAPITAL XI
+ * 	039E GREEK CAPITAL LETTER XI
+ * 1D764 MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1D765 MATHEMATICAL SANS-SERIF BOLD CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 1D766 MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1D767 MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D768 MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA
+ * 	03A3 GREEK CAPITAL LETTER SIGMA
+ * 1D769 MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU
+ * 	03A4 GREEK CAPITAL LETTER TAU
+ * 1D76A MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1D76B MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI
+ * 	03A6 GREEK CAPITAL LETTER PHI
+ * 1D76C MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI
+ * 	03A7 GREEK CAPITAL LETTER CHI
+ * 1D76D MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI
+ * 	03A8 GREEK CAPITAL LETTER PSI
+ * 1D76E MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1D76F MATHEMATICAL SANS-SERIF BOLD NABLA
+ * 	2207 NABLA
+ * 1D770 MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1D771 MATHEMATICAL SANS-SERIF BOLD SMALL BETA
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 1D772 MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 1D773 MATHEMATICAL SANS-SERIF BOLD SMALL DELTA
+ * 	03B4 GREEK SMALL LETTER DELTA
+ * 1D774 MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D775 MATHEMATICAL SANS-SERIF BOLD SMALL ZETA
+ * 	03B6 GREEK SMALL LETTER ZETA
+ * 1D776 MATHEMATICAL SANS-SERIF BOLD SMALL ETA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1D777 MATHEMATICAL SANS-SERIF BOLD SMALL THETA
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D778 MATHEMATICAL SANS-SERIF BOLD SMALL IOTA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1D779 MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D77A MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA
+ * 	03BB GREEK SMALL LETTER LAMDA
+ * 1D77B MATHEMATICAL SANS-SERIF BOLD SMALL MU
+ * 	03BC GREEK SMALL LETTER MU
+ * 1D77C MATHEMATICAL SANS-SERIF BOLD SMALL NU
+ * 	03BD GREEK SMALL LETTER NU
+ * 1D77D MATHEMATICAL SANS-SERIF BOLD SMALL XI
+ * 	03BE GREEK SMALL LETTER XI
+ * 1D77E MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1D77F MATHEMATICAL SANS-SERIF BOLD SMALL PI
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D780 MATHEMATICAL SANS-SERIF BOLD SMALL RHO
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D781 MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 1D782 MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA
+ * 	03C3 GREEK SMALL LETTER SIGMA
+ * 1D783 MATHEMATICAL SANS-SERIF BOLD SMALL TAU
+ * 	03C4 GREEK SMALL LETTER TAU
+ * 1D784 MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1D785 MATHEMATICAL SANS-SERIF BOLD SMALL PHI
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D786 MATHEMATICAL SANS-SERIF BOLD SMALL CHI
+ * 	03C7 GREEK SMALL LETTER CHI
+ * 1D787 MATHEMATICAL SANS-SERIF BOLD SMALL PSI
+ * 	03C8 GREEK SMALL LETTER PSI
+ * 1D788 MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1D789 MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+ * 	2202 PARTIAL DIFFERENTIAL
+ * 1D78A MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D78B MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D78C MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D78D MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D78E MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D78F MATHEMATICAL SANS-SERIF BOLD PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D790 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1D791 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA
+ * 	0392 GREEK CAPITAL LETTER BETA
+ * 1D792 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 1D793 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA
+ * 	0394 GREEK CAPITAL LETTER DELTA
+ * 1D794 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1D795 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA
+ * 	0396 GREEK CAPITAL LETTER ZETA
+ * 1D796 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1D797 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D798 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1D799 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA
+ * 	039A GREEK CAPITAL LETTER KAPPA
+ * 1D79A MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA
+ * 	039B GREEK CAPITAL LETTER LAMDA
+ * 1D79B MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU
+ * 	039C GREEK CAPITAL LETTER MU
+ * 1D79C MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU
+ * 	039D GREEK CAPITAL LETTER NU
+ * 1D79D MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI
+ * 	039E GREEK CAPITAL LETTER XI
+ * 1D79E MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1D79F MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 1D7A0 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1D7A1 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL
+ * 	0398 GREEK CAPITAL LETTER THETA
+ * 1D7A2 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA
+ * 	03A3 GREEK CAPITAL LETTER SIGMA
+ * 1D7A3 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU
+ * 	03A4 GREEK CAPITAL LETTER TAU
+ * 1D7A4 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1D7A5 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI
+ * 	03A6 GREEK CAPITAL LETTER PHI
+ * 1D7A6 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI
+ * 	03A7 GREEK CAPITAL LETTER CHI
+ * 1D7A7 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI
+ * 	03A8 GREEK CAPITAL LETTER PSI
+ * 1D7A8 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1D7A9 MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+ * 	2207 NABLA
+ * 1D7AA MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1D7AB MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA
+ * 	03B2 GREEK SMALL LETTER BETA
+ * 1D7AC MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 1D7AD MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA
+ * 	03B4 GREEK SMALL LETTER DELTA
+ * 1D7AE MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D7AF MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA
+ * 	03B6 GREEK SMALL LETTER ZETA
+ * 1D7B0 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1D7B1 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D7B2 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1D7B3 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D7B4 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA
+ * 	03BB GREEK SMALL LETTER LAMDA
+ * 1D7B5 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU
+ * 	03BC GREEK SMALL LETTER MU
+ * 1D7B6 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU
+ * 	03BD GREEK SMALL LETTER NU
+ * 1D7B7 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI
+ * 	03BE GREEK SMALL LETTER XI
+ * 1D7B8 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1D7B9 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D7BA MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D7BB MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA
+ * 	03C2 GREEK SMALL LETTER FINAL SIGMA
+ * 1D7BC MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA
+ * 	03C3 GREEK SMALL LETTER SIGMA
+ * 1D7BD MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU
+ * 	03C4 GREEK SMALL LETTER TAU
+ * 1D7BE MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1D7BF MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D7C0 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI
+ * 	03C7 GREEK SMALL LETTER CHI
+ * 1D7C1 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI
+ * 	03C8 GREEK SMALL LETTER PSI
+ * 1D7C2 MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1D7C3 MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+ * 	2202 PARTIAL DIFFERENTIAL
+ * 1D7C4 MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1D7C5 MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL
+ * 	03B8 GREEK SMALL LETTER THETA
+ * 1D7C6 MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL
+ * 	03BA GREEK SMALL LETTER KAPPA
+ * 1D7C7 MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL
+ * 	03C6 GREEK SMALL LETTER PHI
+ * 1D7C8 MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1D7C9 MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
+ * 	03C0 GREEK SMALL LETTER PI
+ * 1D7CE MATHEMATICAL BOLD DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 1D7CF MATHEMATICAL BOLD DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 1D7D0 MATHEMATICAL BOLD DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 1D7D1 MATHEMATICAL BOLD DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 1D7D2 MATHEMATICAL BOLD DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 1D7D3 MATHEMATICAL BOLD DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 1D7D4 MATHEMATICAL BOLD DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 1D7D5 MATHEMATICAL BOLD DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 1D7D6 MATHEMATICAL BOLD DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 1D7D7 MATHEMATICAL BOLD DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 1D7D8 MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 1D7D9 MATHEMATICAL DOUBLE-STRUCK DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 1D7DA MATHEMATICAL DOUBLE-STRUCK DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 1D7DB MATHEMATICAL DOUBLE-STRUCK DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 1D7DC MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 1D7DD MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 1D7DE MATHEMATICAL DOUBLE-STRUCK DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 1D7DF MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 1D7E0 MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 1D7E1 MATHEMATICAL DOUBLE-STRUCK DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 1D7E2 MATHEMATICAL SANS-SERIF DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 1D7E3 MATHEMATICAL SANS-SERIF DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 1D7E4 MATHEMATICAL SANS-SERIF DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 1D7E5 MATHEMATICAL SANS-SERIF DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 1D7E6 MATHEMATICAL SANS-SERIF DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 1D7E7 MATHEMATICAL SANS-SERIF DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 1D7E8 MATHEMATICAL SANS-SERIF DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 1D7E9 MATHEMATICAL SANS-SERIF DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 1D7EA MATHEMATICAL SANS-SERIF DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 1D7EB MATHEMATICAL SANS-SERIF DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 1D7EC MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 1D7ED MATHEMATICAL SANS-SERIF BOLD DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 1D7EE MATHEMATICAL SANS-SERIF BOLD DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 1D7EF MATHEMATICAL SANS-SERIF BOLD DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 1D7F0 MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 1D7F1 MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 1D7F2 MATHEMATICAL SANS-SERIF BOLD DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 1D7F3 MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 1D7F4 MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 1D7F5 MATHEMATICAL SANS-SERIF BOLD DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 1D7F6 MATHEMATICAL MONOSPACE DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 1D7F7 MATHEMATICAL MONOSPACE DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 1D7F8 MATHEMATICAL MONOSPACE DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 1D7F9 MATHEMATICAL MONOSPACE DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 1D7FA MATHEMATICAL MONOSPACE DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 1D7FB MATHEMATICAL MONOSPACE DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 1D7FC MATHEMATICAL MONOSPACE DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 1D7FD MATHEMATICAL MONOSPACE DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 1D7FE MATHEMATICAL MONOSPACE DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 1D7FF MATHEMATICAL MONOSPACE DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 1E00 LATIN CAPITAL LETTER A WITH RING BELOW
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1E01 LATIN SMALL LETTER A WITH RING BELOW
+ * 	0061 LATIN SMALL LETTER A
+ * 1E02 LATIN CAPITAL LETTER B WITH DOT ABOVE
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1E03 LATIN SMALL LETTER B WITH DOT ABOVE
+ * 	0062 LATIN SMALL LETTER B
+ * 1E04 LATIN CAPITAL LETTER B WITH DOT BELOW
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1E05 LATIN SMALL LETTER B WITH DOT BELOW
+ * 	0062 LATIN SMALL LETTER B
+ * 1E06 LATIN CAPITAL LETTER B WITH LINE BELOW
+ * 	0042 LATIN CAPITAL LETTER B
+ * 1E07 LATIN SMALL LETTER B WITH LINE BELOW
+ * 	0062 LATIN SMALL LETTER B
+ * 1E08 LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+ * 	0043 LATIN CAPITAL LETTER C
+ * 1E09 LATIN SMALL LETTER C WITH CEDILLA AND ACUTE
+ * 	0063 LATIN SMALL LETTER C
+ * 1E0A LATIN CAPITAL LETTER D WITH DOT ABOVE
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1E0B LATIN SMALL LETTER D WITH DOT ABOVE
+ * 	0064 LATIN SMALL LETTER D
+ * 1E0C LATIN CAPITAL LETTER D WITH DOT BELOW
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1E0D LATIN SMALL LETTER D WITH DOT BELOW
+ * 	0064 LATIN SMALL LETTER D
+ * 1E0E LATIN CAPITAL LETTER D WITH LINE BELOW
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1E0F LATIN SMALL LETTER D WITH LINE BELOW
+ * 	0064 LATIN SMALL LETTER D
+ * 1E10 LATIN CAPITAL LETTER D WITH CEDILLA
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1E11 LATIN SMALL LETTER D WITH CEDILLA
+ * 	0064 LATIN SMALL LETTER D
+ * 1E12 LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+ * 	0044 LATIN CAPITAL LETTER D
+ * 1E13 LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW
+ * 	0064 LATIN SMALL LETTER D
+ * 1E14 LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1E15 LATIN SMALL LETTER E WITH MACRON AND GRAVE
+ * 	0065 LATIN SMALL LETTER E
+ * 1E16 LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1E17 LATIN SMALL LETTER E WITH MACRON AND ACUTE
+ * 	0065 LATIN SMALL LETTER E
+ * 1E18 LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1E19 LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW
+ * 	0065 LATIN SMALL LETTER E
+ * 1E1A LATIN CAPITAL LETTER E WITH TILDE BELOW
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1E1B LATIN SMALL LETTER E WITH TILDE BELOW
+ * 	0065 LATIN SMALL LETTER E
+ * 1E1C LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1E1D LATIN SMALL LETTER E WITH CEDILLA AND BREVE
+ * 	0065 LATIN SMALL LETTER E
+ * 1E1E LATIN CAPITAL LETTER F WITH DOT ABOVE
+ * 	0046 LATIN CAPITAL LETTER F
+ * 1E1F LATIN SMALL LETTER F WITH DOT ABOVE
+ * 	0066 LATIN SMALL LETTER F
+ * 1E20 LATIN CAPITAL LETTER G WITH MACRON
+ * 	0047 LATIN CAPITAL LETTER G
+ * 1E21 LATIN SMALL LETTER G WITH MACRON
+ * 	0067 LATIN SMALL LETTER G
+ * 1E22 LATIN CAPITAL LETTER H WITH DOT ABOVE
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1E23 LATIN SMALL LETTER H WITH DOT ABOVE
+ * 	0068 LATIN SMALL LETTER H
+ * 1E24 LATIN CAPITAL LETTER H WITH DOT BELOW
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1E25 LATIN SMALL LETTER H WITH DOT BELOW
+ * 	0068 LATIN SMALL LETTER H
+ * 1E26 LATIN CAPITAL LETTER H WITH DIAERESIS
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1E27 LATIN SMALL LETTER H WITH DIAERESIS
+ * 	0068 LATIN SMALL LETTER H
+ * 1E28 LATIN CAPITAL LETTER H WITH CEDILLA
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1E29 LATIN SMALL LETTER H WITH CEDILLA
+ * 	0068 LATIN SMALL LETTER H
+ * 1E2A LATIN CAPITAL LETTER H WITH BREVE BELOW
+ * 	0048 LATIN CAPITAL LETTER H
+ * 1E2B LATIN SMALL LETTER H WITH BREVE BELOW
+ * 	0068 LATIN SMALL LETTER H
+ * 1E2C LATIN CAPITAL LETTER I WITH TILDE BELOW
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1E2D LATIN SMALL LETTER I WITH TILDE BELOW
+ * 	0069 LATIN SMALL LETTER I
+ * 1E2E LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1E2F LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE
+ * 	0069 LATIN SMALL LETTER I
+ * 1E30 LATIN CAPITAL LETTER K WITH ACUTE
+ * 	004B LATIN CAPITAL LETTER K
+ * 1E31 LATIN SMALL LETTER K WITH ACUTE
+ * 	006B LATIN SMALL LETTER K
+ * 1E32 LATIN CAPITAL LETTER K WITH DOT BELOW
+ * 	004B LATIN CAPITAL LETTER K
+ * 1E33 LATIN SMALL LETTER K WITH DOT BELOW
+ * 	006B LATIN SMALL LETTER K
+ * 1E34 LATIN CAPITAL LETTER K WITH LINE BELOW
+ * 	004B LATIN CAPITAL LETTER K
+ * 1E35 LATIN SMALL LETTER K WITH LINE BELOW
+ * 	006B LATIN SMALL LETTER K
+ * 1E36 LATIN CAPITAL LETTER L WITH DOT BELOW
+ * 	004C LATIN CAPITAL LETTER L
+ * 1E37 LATIN SMALL LETTER L WITH DOT BELOW
+ * 	006C LATIN SMALL LETTER L
+ * 1E38 LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+ * 	004C LATIN CAPITAL LETTER L
+ * 1E39 LATIN SMALL LETTER L WITH DOT BELOW AND MACRON
+ * 	006C LATIN SMALL LETTER L
+ * 1E3A LATIN CAPITAL LETTER L WITH LINE BELOW
+ * 	004C LATIN CAPITAL LETTER L
+ * 1E3B LATIN SMALL LETTER L WITH LINE BELOW
+ * 	006C LATIN SMALL LETTER L
+ * 1E3C LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+ * 	004C LATIN CAPITAL LETTER L
+ * 1E3D LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW
+ * 	006C LATIN SMALL LETTER L
+ * 1E3E LATIN CAPITAL LETTER M WITH ACUTE
+ * 	004D LATIN CAPITAL LETTER M
+ * 1E3F LATIN SMALL LETTER M WITH ACUTE
+ * 	006D LATIN SMALL LETTER M
+ * 1E40 LATIN CAPITAL LETTER M WITH DOT ABOVE
+ * 	004D LATIN CAPITAL LETTER M
+ * 1E41 LATIN SMALL LETTER M WITH DOT ABOVE
+ * 	006D LATIN SMALL LETTER M
+ * 1E42 LATIN CAPITAL LETTER M WITH DOT BELOW
+ * 	004D LATIN CAPITAL LETTER M
+ * 1E43 LATIN SMALL LETTER M WITH DOT BELOW
+ * 	006D LATIN SMALL LETTER M
+ * 1E44 LATIN CAPITAL LETTER N WITH DOT ABOVE
+ * 	004E LATIN CAPITAL LETTER N
+ * 1E45 LATIN SMALL LETTER N WITH DOT ABOVE
+ * 	006E LATIN SMALL LETTER N
+ * 1E46 LATIN CAPITAL LETTER N WITH DOT BELOW
+ * 	004E LATIN CAPITAL LETTER N
+ * 1E47 LATIN SMALL LETTER N WITH DOT BELOW
+ * 	006E LATIN SMALL LETTER N
+ * 1E48 LATIN CAPITAL LETTER N WITH LINE BELOW
+ * 	004E LATIN CAPITAL LETTER N
+ * 1E49 LATIN SMALL LETTER N WITH LINE BELOW
+ * 	006E LATIN SMALL LETTER N
+ * 1E4A LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+ * 	004E LATIN CAPITAL LETTER N
+ * 1E4B LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW
+ * 	006E LATIN SMALL LETTER N
+ * 1E4C LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1E4D LATIN SMALL LETTER O WITH TILDE AND ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 1E4E LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+ * 	004F LATIN CAPITAL LETTER O
+ * 1E4F LATIN SMALL LETTER O WITH TILDE AND DIAERESIS
+ * 	006F LATIN SMALL LETTER O
+ * 1E50 LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1E51 LATIN SMALL LETTER O WITH MACRON AND GRAVE
+ * 	006F LATIN SMALL LETTER O
+ * 1E52 LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1E53 LATIN SMALL LETTER O WITH MACRON AND ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 1E54 LATIN CAPITAL LETTER P WITH ACUTE
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1E55 LATIN SMALL LETTER P WITH ACUTE
+ * 	0070 LATIN SMALL LETTER P
+ * 1E56 LATIN CAPITAL LETTER P WITH DOT ABOVE
+ * 	0050 LATIN CAPITAL LETTER P
+ * 1E57 LATIN SMALL LETTER P WITH DOT ABOVE
+ * 	0070 LATIN SMALL LETTER P
+ * 1E58 LATIN CAPITAL LETTER R WITH DOT ABOVE
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1E59 LATIN SMALL LETTER R WITH DOT ABOVE
+ * 	0072 LATIN SMALL LETTER R
+ * 1E5A LATIN CAPITAL LETTER R WITH DOT BELOW
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1E5B LATIN SMALL LETTER R WITH DOT BELOW
+ * 	0072 LATIN SMALL LETTER R
+ * 1E5C LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1E5D LATIN SMALL LETTER R WITH DOT BELOW AND MACRON
+ * 	0072 LATIN SMALL LETTER R
+ * 1E5E LATIN CAPITAL LETTER R WITH LINE BELOW
+ * 	0052 LATIN CAPITAL LETTER R
+ * 1E5F LATIN SMALL LETTER R WITH LINE BELOW
+ * 	0072 LATIN SMALL LETTER R
+ * 1E60 LATIN CAPITAL LETTER S WITH DOT ABOVE
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1E61 LATIN SMALL LETTER S WITH DOT ABOVE
+ * 	0073 LATIN SMALL LETTER S
+ * 1E62 LATIN CAPITAL LETTER S WITH DOT BELOW
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1E63 LATIN SMALL LETTER S WITH DOT BELOW
+ * 	0073 LATIN SMALL LETTER S
+ * 1E64 LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1E65 LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE
+ * 	0073 LATIN SMALL LETTER S
+ * 1E66 LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1E67 LATIN SMALL LETTER S WITH CARON AND DOT ABOVE
+ * 	0073 LATIN SMALL LETTER S
+ * 1E68 LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+ * 	0053 LATIN CAPITAL LETTER S
+ * 1E69 LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
+ * 	0073 LATIN SMALL LETTER S
+ * 1E6A LATIN CAPITAL LETTER T WITH DOT ABOVE
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1E6B LATIN SMALL LETTER T WITH DOT ABOVE
+ * 	0074 LATIN SMALL LETTER T
+ * 1E6C LATIN CAPITAL LETTER T WITH DOT BELOW
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1E6D LATIN SMALL LETTER T WITH DOT BELOW
+ * 	0074 LATIN SMALL LETTER T
+ * 1E6E LATIN CAPITAL LETTER T WITH LINE BELOW
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1E6F LATIN SMALL LETTER T WITH LINE BELOW
+ * 	0074 LATIN SMALL LETTER T
+ * 1E70 LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+ * 	0054 LATIN CAPITAL LETTER T
+ * 1E71 LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW
+ * 	0074 LATIN SMALL LETTER T
+ * 1E72 LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1E73 LATIN SMALL LETTER U WITH DIAERESIS BELOW
+ * 	0075 LATIN SMALL LETTER U
+ * 1E74 LATIN CAPITAL LETTER U WITH TILDE BELOW
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1E75 LATIN SMALL LETTER U WITH TILDE BELOW
+ * 	0075 LATIN SMALL LETTER U
+ * 1E76 LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1E77 LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW
+ * 	0075 LATIN SMALL LETTER U
+ * 1E78 LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1E79 LATIN SMALL LETTER U WITH TILDE AND ACUTE
+ * 	0075 LATIN SMALL LETTER U
+ * 1E7A LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1E7B LATIN SMALL LETTER U WITH MACRON AND DIAERESIS
+ * 	0075 LATIN SMALL LETTER U
+ * 1E7C LATIN CAPITAL LETTER V WITH TILDE
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1E7D LATIN SMALL LETTER V WITH TILDE
+ * 	0076 LATIN SMALL LETTER V
+ * 1E7E LATIN CAPITAL LETTER V WITH DOT BELOW
+ * 	0056 LATIN CAPITAL LETTER V
+ * 1E7F LATIN SMALL LETTER V WITH DOT BELOW
+ * 	0076 LATIN SMALL LETTER V
+ * 1E80 LATIN CAPITAL LETTER W WITH GRAVE
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1E81 LATIN SMALL LETTER W WITH GRAVE
+ * 	0077 LATIN SMALL LETTER W
+ * 1E82 LATIN CAPITAL LETTER W WITH ACUTE
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1E83 LATIN SMALL LETTER W WITH ACUTE
+ * 	0077 LATIN SMALL LETTER W
+ * 1E84 LATIN CAPITAL LETTER W WITH DIAERESIS
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1E85 LATIN SMALL LETTER W WITH DIAERESIS
+ * 	0077 LATIN SMALL LETTER W
+ * 1E86 LATIN CAPITAL LETTER W WITH DOT ABOVE
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1E87 LATIN SMALL LETTER W WITH DOT ABOVE
+ * 	0077 LATIN SMALL LETTER W
+ * 1E88 LATIN CAPITAL LETTER W WITH DOT BELOW
+ * 	0057 LATIN CAPITAL LETTER W
+ * 1E89 LATIN SMALL LETTER W WITH DOT BELOW
+ * 	0077 LATIN SMALL LETTER W
+ * 1E8A LATIN CAPITAL LETTER X WITH DOT ABOVE
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1E8B LATIN SMALL LETTER X WITH DOT ABOVE
+ * 	0078 LATIN SMALL LETTER X
+ * 1E8C LATIN CAPITAL LETTER X WITH DIAERESIS
+ * 	0058 LATIN CAPITAL LETTER X
+ * 1E8D LATIN SMALL LETTER X WITH DIAERESIS
+ * 	0078 LATIN SMALL LETTER X
+ * 1E8E LATIN CAPITAL LETTER Y WITH DOT ABOVE
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1E8F LATIN SMALL LETTER Y WITH DOT ABOVE
+ * 	0079 LATIN SMALL LETTER Y
+ * 1E90 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1E91 LATIN SMALL LETTER Z WITH CIRCUMFLEX
+ * 	007A LATIN SMALL LETTER Z
+ * 1E92 LATIN CAPITAL LETTER Z WITH DOT BELOW
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1E93 LATIN SMALL LETTER Z WITH DOT BELOW
+ * 	007A LATIN SMALL LETTER Z
+ * 1E94 LATIN CAPITAL LETTER Z WITH LINE BELOW
+ * 	005A LATIN CAPITAL LETTER Z
+ * 1E95 LATIN SMALL LETTER Z WITH LINE BELOW
+ * 	007A LATIN SMALL LETTER Z
+ * 1E96 LATIN SMALL LETTER H WITH LINE BELOW
+ * 	0068 LATIN SMALL LETTER H
+ * 1E97 LATIN SMALL LETTER T WITH DIAERESIS
+ * 	0074 LATIN SMALL LETTER T
+ * 1E98 LATIN SMALL LETTER W WITH RING ABOVE
+ * 	0077 LATIN SMALL LETTER W
+ * 1E99 LATIN SMALL LETTER Y WITH RING ABOVE
+ * 	0079 LATIN SMALL LETTER Y
+ * 1E9A LATIN SMALL LETTER A WITH RIGHT HALF RING
+ * 	0061 LATIN SMALL LETTER A
+ * 	02BE MODIFIER LETTER RIGHT HALF RING
+ * 1E9B LATIN SMALL LETTER LONG S WITH DOT ABOVE
+ * 	0073 LATIN SMALL LETTER S
+ * 1EA0 LATIN CAPITAL LETTER A WITH DOT BELOW
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EA1 LATIN SMALL LETTER A WITH DOT BELOW
+ * 	0061 LATIN SMALL LETTER A
+ * 1EA2 LATIN CAPITAL LETTER A WITH HOOK ABOVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EA3 LATIN SMALL LETTER A WITH HOOK ABOVE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EA4 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EA5 LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EA6 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EA7 LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EA8 LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EA9 LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EAA LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EAB LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EAC LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EAD LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+ * 	0061 LATIN SMALL LETTER A
+ * 1EAE LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EAF LATIN SMALL LETTER A WITH BREVE AND ACUTE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EB0 LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EB1 LATIN SMALL LETTER A WITH BREVE AND GRAVE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EB2 LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EB3 LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EB4 LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EB5 LATIN SMALL LETTER A WITH BREVE AND TILDE
+ * 	0061 LATIN SMALL LETTER A
+ * 1EB6 LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+ * 	0041 LATIN CAPITAL LETTER A
+ * 1EB7 LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
+ * 	0061 LATIN SMALL LETTER A
+ * 1EB8 LATIN CAPITAL LETTER E WITH DOT BELOW
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EB9 LATIN SMALL LETTER E WITH DOT BELOW
+ * 	0065 LATIN SMALL LETTER E
+ * 1EBA LATIN CAPITAL LETTER E WITH HOOK ABOVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EBB LATIN SMALL LETTER E WITH HOOK ABOVE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EBC LATIN CAPITAL LETTER E WITH TILDE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EBD LATIN SMALL LETTER E WITH TILDE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EBE LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EBF LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EC0 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EC1 LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EC2 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EC3 LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EC4 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EC5 LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
+ * 	0065 LATIN SMALL LETTER E
+ * 1EC6 LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+ * 	0045 LATIN CAPITAL LETTER E
+ * 1EC7 LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+ * 	0065 LATIN SMALL LETTER E
+ * 1EC8 LATIN CAPITAL LETTER I WITH HOOK ABOVE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1EC9 LATIN SMALL LETTER I WITH HOOK ABOVE
+ * 	0069 LATIN SMALL LETTER I
+ * 1ECA LATIN CAPITAL LETTER I WITH DOT BELOW
+ * 	0049 LATIN CAPITAL LETTER I
+ * 1ECB LATIN SMALL LETTER I WITH DOT BELOW
+ * 	0069 LATIN SMALL LETTER I
+ * 1ECC LATIN CAPITAL LETTER O WITH DOT BELOW
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ECD LATIN SMALL LETTER O WITH DOT BELOW
+ * 	006F LATIN SMALL LETTER O
+ * 1ECE LATIN CAPITAL LETTER O WITH HOOK ABOVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ECF LATIN SMALL LETTER O WITH HOOK ABOVE
+ * 	006F LATIN SMALL LETTER O
+ * 1ED0 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ED1 LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 1ED2 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ED3 LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
+ * 	006F LATIN SMALL LETTER O
+ * 1ED4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ED5 LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+ * 	006F LATIN SMALL LETTER O
+ * 1ED6 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ED7 LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
+ * 	006F LATIN SMALL LETTER O
+ * 1ED8 LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+ * 	004F LATIN CAPITAL LETTER O
+ * 1ED9 LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+ * 	006F LATIN SMALL LETTER O
+ * 1EDA LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1EDB LATIN SMALL LETTER O WITH HORN AND ACUTE
+ * 	006F LATIN SMALL LETTER O
+ * 1EDC LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1EDD LATIN SMALL LETTER O WITH HORN AND GRAVE
+ * 	006F LATIN SMALL LETTER O
+ * 1EDE LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1EDF LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
+ * 	006F LATIN SMALL LETTER O
+ * 1EE0 LATIN CAPITAL LETTER O WITH HORN AND TILDE
+ * 	004F LATIN CAPITAL LETTER O
+ * 1EE1 LATIN SMALL LETTER O WITH HORN AND TILDE
+ * 	006F LATIN SMALL LETTER O
+ * 1EE2 LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+ * 	004F LATIN CAPITAL LETTER O
+ * 1EE3 LATIN SMALL LETTER O WITH HORN AND DOT BELOW
+ * 	006F LATIN SMALL LETTER O
+ * 1EE4 LATIN CAPITAL LETTER U WITH DOT BELOW
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EE5 LATIN SMALL LETTER U WITH DOT BELOW
+ * 	0075 LATIN SMALL LETTER U
+ * 1EE6 LATIN CAPITAL LETTER U WITH HOOK ABOVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EE7 LATIN SMALL LETTER U WITH HOOK ABOVE
+ * 	0075 LATIN SMALL LETTER U
+ * 1EE8 LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EE9 LATIN SMALL LETTER U WITH HORN AND ACUTE
+ * 	0075 LATIN SMALL LETTER U
+ * 1EEA LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EEB LATIN SMALL LETTER U WITH HORN AND GRAVE
+ * 	0075 LATIN SMALL LETTER U
+ * 1EEC LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EED LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
+ * 	0075 LATIN SMALL LETTER U
+ * 1EEE LATIN CAPITAL LETTER U WITH HORN AND TILDE
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EEF LATIN SMALL LETTER U WITH HORN AND TILDE
+ * 	0075 LATIN SMALL LETTER U
+ * 1EF0 LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+ * 	0055 LATIN CAPITAL LETTER U
+ * 1EF1 LATIN SMALL LETTER U WITH HORN AND DOT BELOW
+ * 	0075 LATIN SMALL LETTER U
+ * 1EF2 LATIN CAPITAL LETTER Y WITH GRAVE
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1EF3 LATIN SMALL LETTER Y WITH GRAVE
+ * 	0079 LATIN SMALL LETTER Y
+ * 1EF4 LATIN CAPITAL LETTER Y WITH DOT BELOW
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1EF5 LATIN SMALL LETTER Y WITH DOT BELOW
+ * 	0079 LATIN SMALL LETTER Y
+ * 1EF6 LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1EF7 LATIN SMALL LETTER Y WITH HOOK ABOVE
+ * 	0079 LATIN SMALL LETTER Y
+ * 1EF8 LATIN CAPITAL LETTER Y WITH TILDE
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 1EF9 LATIN SMALL LETTER Y WITH TILDE
+ * 	0079 LATIN SMALL LETTER Y
+ * 1F00 GREEK SMALL LETTER ALPHA WITH PSILI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F01 GREEK SMALL LETTER ALPHA WITH DASIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F02 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F03 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F04 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F05 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F06 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F07 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F08 GREEK CAPITAL LETTER ALPHA WITH PSILI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F09 GREEK CAPITAL LETTER ALPHA WITH DASIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F0F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F10 GREEK SMALL LETTER EPSILON WITH PSILI
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F11 GREEK SMALL LETTER EPSILON WITH DASIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F12 GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F13 GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F14 GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F15 GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F18 GREEK CAPITAL LETTER EPSILON WITH PSILI
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F19 GREEK CAPITAL LETTER EPSILON WITH DASIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F1A GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F1B GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F1C GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F1D GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1F20 GREEK SMALL LETTER ETA WITH PSILI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F21 GREEK SMALL LETTER ETA WITH DASIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F22 GREEK SMALL LETTER ETA WITH PSILI AND VARIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F23 GREEK SMALL LETTER ETA WITH DASIA AND VARIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F24 GREEK SMALL LETTER ETA WITH PSILI AND OXIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F25 GREEK SMALL LETTER ETA WITH DASIA AND OXIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F26 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F27 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F28 GREEK CAPITAL LETTER ETA WITH PSILI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F29 GREEK CAPITAL LETTER ETA WITH DASIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F2F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F30 GREEK SMALL LETTER IOTA WITH PSILI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F31 GREEK SMALL LETTER IOTA WITH DASIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F32 GREEK SMALL LETTER IOTA WITH PSILI AND VARIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F33 GREEK SMALL LETTER IOTA WITH DASIA AND VARIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F34 GREEK SMALL LETTER IOTA WITH PSILI AND OXIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F35 GREEK SMALL LETTER IOTA WITH DASIA AND OXIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F36 GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F37 GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F38 GREEK CAPITAL LETTER IOTA WITH PSILI
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F39 GREEK CAPITAL LETTER IOTA WITH DASIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3A GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3B GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3C GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3D GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3E GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F3F GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1F40 GREEK SMALL LETTER OMICRON WITH PSILI
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F41 GREEK SMALL LETTER OMICRON WITH DASIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F42 GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F43 GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F44 GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F45 GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F48 GREEK CAPITAL LETTER OMICRON WITH PSILI
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F49 GREEK CAPITAL LETTER OMICRON WITH DASIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F4A GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F4B GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F4C GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F4D GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1F50 GREEK SMALL LETTER UPSILON WITH PSILI
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F51 GREEK SMALL LETTER UPSILON WITH DASIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F52 GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F53 GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F54 GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F55 GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F56 GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F57 GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F59 GREEK CAPITAL LETTER UPSILON WITH DASIA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1F5B GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1F5D GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1F5F GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1F60 GREEK SMALL LETTER OMEGA WITH PSILI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F61 GREEK SMALL LETTER OMEGA WITH DASIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F62 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F63 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F64 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F65 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F66 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F67 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F68 GREEK CAPITAL LETTER OMEGA WITH PSILI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F69 GREEK CAPITAL LETTER OMEGA WITH DASIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6A GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6B GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6C GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6D GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6E GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F6F GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1F70 GREEK SMALL LETTER ALPHA WITH VARIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F71 GREEK SMALL LETTER ALPHA WITH OXIA
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F72 GREEK SMALL LETTER EPSILON WITH VARIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F73 GREEK SMALL LETTER EPSILON WITH OXIA
+ * 	03B5 GREEK SMALL LETTER EPSILON
+ * 1F74 GREEK SMALL LETTER ETA WITH VARIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F75 GREEK SMALL LETTER ETA WITH OXIA
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F76 GREEK SMALL LETTER IOTA WITH VARIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F77 GREEK SMALL LETTER IOTA WITH OXIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1F78 GREEK SMALL LETTER OMICRON WITH VARIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F79 GREEK SMALL LETTER OMICRON WITH OXIA
+ * 	03BF GREEK SMALL LETTER OMICRON
+ * 1F7A GREEK SMALL LETTER UPSILON WITH VARIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F7B GREEK SMALL LETTER UPSILON WITH OXIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1F7C GREEK SMALL LETTER OMEGA WITH VARIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F7D GREEK SMALL LETTER OMEGA WITH OXIA
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1F80 GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F81 GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F82 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F83 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F84 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F85 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F86 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F87 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1F88 GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F89 GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F8F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1F90 GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F91 GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F92 GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F93 GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F94 GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F95 GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F96 GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F97 GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1F98 GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F99 GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1F9F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1FA0 GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA1 GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA2 GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA3 GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA4 GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA5 GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA6 GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA7 GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FA8 GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FA9 GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAA GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAB GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAC GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAD GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAE GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FAF GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FB0 GREEK SMALL LETTER ALPHA WITH VRACHY
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB1 GREEK SMALL LETTER ALPHA WITH MACRON
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB2 GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB3 GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB4 GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB6 GREEK SMALL LETTER ALPHA WITH PERISPOMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB7 GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B1 GREEK SMALL LETTER ALPHA
+ * 1FB8 GREEK CAPITAL LETTER ALPHA WITH VRACHY
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1FB9 GREEK CAPITAL LETTER ALPHA WITH MACRON
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1FBA GREEK CAPITAL LETTER ALPHA WITH VARIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1FBC GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+ * 	0391 GREEK CAPITAL LETTER ALPHA
+ * 1FBD GREEK KORONIS
+ * 	0020 SPACE
+ * 1FBE GREEK PROSGEGRAMMENI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FBF GREEK PSILI
+ * 	0020 SPACE
+ * 1FC0 GREEK PERISPOMENI
+ * 	0020 SPACE
+ * 1FC1 GREEK DIALYTIKA AND PERISPOMENI
+ * 	0020 SPACE
+ * 1FC2 GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1FC3 GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1FC4 GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1FC6 GREEK SMALL LETTER ETA WITH PERISPOMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1FC7 GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
+ * 	03B7 GREEK SMALL LETTER ETA
+ * 1FC8 GREEK CAPITAL LETTER EPSILON WITH VARIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA
+ * 	0395 GREEK CAPITAL LETTER EPSILON
+ * 1FCA GREEK CAPITAL LETTER ETA WITH VARIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1FCB GREEK CAPITAL LETTER ETA WITH OXIA
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1FCC GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+ * 	0397 GREEK CAPITAL LETTER ETA
+ * 1FCD GREEK PSILI AND VARIA
+ * 	0020 SPACE
+ * 1FCE GREEK PSILI AND OXIA
+ * 	0020 SPACE
+ * 1FCF GREEK PSILI AND PERISPOMENI
+ * 	0020 SPACE
+ * 1FD0 GREEK SMALL LETTER IOTA WITH VRACHY
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD1 GREEK SMALL LETTER IOTA WITH MACRON
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD2 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD6 GREEK SMALL LETTER IOTA WITH PERISPOMENI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD7 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
+ * 	03B9 GREEK SMALL LETTER IOTA
+ * 1FD8 GREEK CAPITAL LETTER IOTA WITH VRACHY
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1FD9 GREEK CAPITAL LETTER IOTA WITH MACRON
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1FDA GREEK CAPITAL LETTER IOTA WITH VARIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1FDB GREEK CAPITAL LETTER IOTA WITH OXIA
+ * 	0399 GREEK CAPITAL LETTER IOTA
+ * 1FDD GREEK DASIA AND VARIA
+ * 	0020 SPACE
+ * 1FDE GREEK DASIA AND OXIA
+ * 	0020 SPACE
+ * 1FDF GREEK DASIA AND PERISPOMENI
+ * 	0020 SPACE
+ * 1FE0 GREEK SMALL LETTER UPSILON WITH VRACHY
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE1 GREEK SMALL LETTER UPSILON WITH MACRON
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE2 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE4 GREEK SMALL LETTER RHO WITH PSILI
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1FE5 GREEK SMALL LETTER RHO WITH DASIA
+ * 	03C1 GREEK SMALL LETTER RHO
+ * 1FE6 GREEK SMALL LETTER UPSILON WITH PERISPOMENI
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE7 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
+ * 	03C5 GREEK SMALL LETTER UPSILON
+ * 1FE8 GREEK CAPITAL LETTER UPSILON WITH VRACHY
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1FE9 GREEK CAPITAL LETTER UPSILON WITH MACRON
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1FEA GREEK CAPITAL LETTER UPSILON WITH VARIA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA
+ * 	03A5 GREEK CAPITAL LETTER UPSILON
+ * 1FEC GREEK CAPITAL LETTER RHO WITH DASIA
+ * 	03A1 GREEK CAPITAL LETTER RHO
+ * 1FED GREEK DIALYTIKA AND VARIA
+ * 	0020 SPACE
+ * 1FEE GREEK DIALYTIKA AND OXIA
+ * 	0020 SPACE
+ * 1FEF GREEK VARIA
+ * 	0060 GRAVE ACCENT
+ * 1FF2 GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FF3 GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FF4 GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FF6 GREEK SMALL LETTER OMEGA WITH PERISPOMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FF7 GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
+ * 	03C9 GREEK SMALL LETTER OMEGA
+ * 1FF8 GREEK CAPITAL LETTER OMICRON WITH VARIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA
+ * 	039F GREEK CAPITAL LETTER OMICRON
+ * 1FFA GREEK CAPITAL LETTER OMEGA WITH VARIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FFC GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 1FFD GREEK OXIA
+ * 	0020 SPACE
+ * 1FFE GREEK DASIA
+ * 	0020 SPACE
+ * 2000 EN QUAD
+ * 	0020 SPACE
+ * 2001 EM QUAD
+ * 	0020 SPACE
+ * 2002 EN SPACE
+ * 	0020 SPACE
+ * 2003 EM SPACE
+ * 	0020 SPACE
+ * 2004 THREE-PER-EM SPACE
+ * 	0020 SPACE
+ * 2005 FOUR-PER-EM SPACE
+ * 	0020 SPACE
+ * 2006 SIX-PER-EM SPACE
+ * 	0020 SPACE
+ * 2007 FIGURE SPACE
+ * 	0020 SPACE
+ * 2008 PUNCTUATION SPACE
+ * 	0020 SPACE
+ * 2009 THIN SPACE
+ * 	0020 SPACE
+ * 200A HAIR SPACE
+ * 	0020 SPACE
+ * 2011 NON-BREAKING HYPHEN
+ * 	2010 HYPHEN
+ * 2017 DOUBLE LOW LINE
+ * 	0020 SPACE
+ * 2024 ONE DOT LEADER
+ * 	002E FULL STOP
+ * 2025 TWO DOT LEADER
+ * 	002E FULL STOP
+ * 	002E FULL STOP
+ * 2026 HORIZONTAL ELLIPSIS
+ * 	002E FULL STOP
+ * 	002E FULL STOP
+ * 	002E FULL STOP
+ * 202F NARROW NO-BREAK SPACE
+ * 	0020 SPACE
+ * 2033 DOUBLE PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 2034 TRIPLE PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 2036 REVERSED DOUBLE PRIME
+ * 	2035 REVERSED PRIME
+ * 	2035 REVERSED PRIME
+ * 2037 REVERSED TRIPLE PRIME
+ * 	2035 REVERSED PRIME
+ * 	2035 REVERSED PRIME
+ * 	2035 REVERSED PRIME
+ * 203C DOUBLE EXCLAMATION MARK
+ * 	0021 EXCLAMATION MARK
+ * 	0021 EXCLAMATION MARK
+ * 203E OVERLINE
+ * 	0020 SPACE
+ * 2047 DOUBLE QUESTION MARK
+ * 	003F QUESTION MARK
+ * 	003F QUESTION MARK
+ * 2048 QUESTION EXCLAMATION MARK
+ * 	003F QUESTION MARK
+ * 	0021 EXCLAMATION MARK
+ * 2049 EXCLAMATION QUESTION MARK
+ * 	0021 EXCLAMATION MARK
+ * 	003F QUESTION MARK
+ * 2057 QUADRUPLE PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 	2032 PRIME
+ * 205F MEDIUM MATHEMATICAL SPACE
+ * 	0020 SPACE
+ * 2070 SUPERSCRIPT ZERO
+ * 	0030 DIGIT ZERO
+ * 2071 SUPERSCRIPT LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 2074 SUPERSCRIPT FOUR
+ * 	0034 DIGIT FOUR
+ * 2075 SUPERSCRIPT FIVE
+ * 	0035 DIGIT FIVE
+ * 2076 SUPERSCRIPT SIX
+ * 	0036 DIGIT SIX
+ * 2077 SUPERSCRIPT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 2078 SUPERSCRIPT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 2079 SUPERSCRIPT NINE
+ * 	0039 DIGIT NINE
+ * 207A SUPERSCRIPT PLUS SIGN
+ * 	002B PLUS SIGN
+ * 207B SUPERSCRIPT MINUS
+ * 	2212 MINUS SIGN
+ * 207C SUPERSCRIPT EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * 207D SUPERSCRIPT LEFT PARENTHESIS
+ * 	0028 LEFT PARENTHESIS
+ * 207E SUPERSCRIPT RIGHT PARENTHESIS
+ * 	0029 RIGHT PARENTHESIS
+ * 207F SUPERSCRIPT LATIN SMALL LETTER N
+ * 	006E LATIN SMALL LETTER N
+ * 2080 SUBSCRIPT ZERO
+ * 	0030 DIGIT ZERO
+ * 2081 SUBSCRIPT ONE
+ * 	0031 DIGIT ONE
+ * 2082 SUBSCRIPT TWO
+ * 	0032 DIGIT TWO
+ * 2083 SUBSCRIPT THREE
+ * 	0033 DIGIT THREE
+ * 2084 SUBSCRIPT FOUR
+ * 	0034 DIGIT FOUR
+ * 2085 SUBSCRIPT FIVE
+ * 	0035 DIGIT FIVE
+ * 2086 SUBSCRIPT SIX
+ * 	0036 DIGIT SIX
+ * 2087 SUBSCRIPT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 2088 SUBSCRIPT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 2089 SUBSCRIPT NINE
+ * 	0039 DIGIT NINE
+ * 208A SUBSCRIPT PLUS SIGN
+ * 	002B PLUS SIGN
+ * 208B SUBSCRIPT MINUS
+ * 	2212 MINUS SIGN
+ * 208C SUBSCRIPT EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * 208D SUBSCRIPT LEFT PARENTHESIS
+ * 	0028 LEFT PARENTHESIS
+ * 208E SUBSCRIPT RIGHT PARENTHESIS
+ * 	0029 RIGHT PARENTHESIS
+ * 20A8 RUPEE SIGN
+ * 	0052 LATIN CAPITAL LETTER R
+ * 	0073 LATIN SMALL LETTER S
+ * 2100 ACCOUNT OF
+ * 	0061 LATIN SMALL LETTER A
+ * 	002F SOLIDUS
+ * 	0063 LATIN SMALL LETTER C
+ * 2101 ADDRESSED TO THE SUBJECT
+ * 	0061 LATIN SMALL LETTER A
+ * 	002F SOLIDUS
+ * 	0073 LATIN SMALL LETTER S
+ * 2102 DOUBLE-STRUCK CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 2103 DEGREE CELSIUS
+ * 	00B0 DEGREE SIGN
+ * 	0043 LATIN CAPITAL LETTER C
+ * 2105 CARE OF
+ * 	0063 LATIN SMALL LETTER C
+ * 	002F SOLIDUS
+ * 	006F LATIN SMALL LETTER O
+ * 2106 CADA UNA
+ * 	0063 LATIN SMALL LETTER C
+ * 	002F SOLIDUS
+ * 	0075 LATIN SMALL LETTER U
+ * 2107 EULER CONSTANT
+ * 	0190 LATIN CAPITAL LETTER OPEN E
+ * 2109 DEGREE FAHRENHEIT
+ * 	00B0 DEGREE SIGN
+ * 	0046 LATIN CAPITAL LETTER F
+ * 210A SCRIPT SMALL G
+ * 	0067 LATIN SMALL LETTER G
+ * 210B SCRIPT CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 210C BLACK-LETTER CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 210D DOUBLE-STRUCK CAPITAL H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 210E PLANCK CONSTANT
+ * 	0068 LATIN SMALL LETTER H
+ * 210F PLANCK CONSTANT OVER TWO PI
+ * 	0127 LATIN SMALL LETTER H WITH STROKE
+ * 2110 SCRIPT CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2111 BLACK-LETTER CAPITAL I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2112 SCRIPT CAPITAL L
+ * 	004C LATIN CAPITAL LETTER L
+ * 2113 SCRIPT SMALL L
+ * 	006C LATIN SMALL LETTER L
+ * 2115 DOUBLE-STRUCK CAPITAL N
+ * 	004E LATIN CAPITAL LETTER N
+ * 2116 NUMERO SIGN
+ * 	004E LATIN CAPITAL LETTER N
+ * 	006F LATIN SMALL LETTER O
+ * 2119 DOUBLE-STRUCK CAPITAL P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 211A DOUBLE-STRUCK CAPITAL Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 211B SCRIPT CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 211C BLACK-LETTER CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 211D DOUBLE-STRUCK CAPITAL R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 2120 SERVICE MARK
+ * 	0053 LATIN CAPITAL LETTER S
+ * 	004D LATIN CAPITAL LETTER M
+ * 2121 TELEPHONE SIGN
+ * 	0054 LATIN CAPITAL LETTER T
+ * 	0045 LATIN CAPITAL LETTER E
+ * 	004C LATIN CAPITAL LETTER L
+ * 2122 TRADE MARK SIGN
+ * 	0054 LATIN CAPITAL LETTER T
+ * 	004D LATIN CAPITAL LETTER M
+ * 2124 DOUBLE-STRUCK CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 2126 OHM SIGN
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 2128 BLACK-LETTER CAPITAL Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 212A KELVIN SIGN
+ * 	004B LATIN CAPITAL LETTER K
+ * 212B ANGSTROM SIGN
+ * 	0041 LATIN CAPITAL LETTER A
+ * 212C SCRIPT CAPITAL B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 212D BLACK-LETTER CAPITAL C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 212F SCRIPT SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 2130 SCRIPT CAPITAL E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 2131 SCRIPT CAPITAL F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 2133 SCRIPT CAPITAL M
+ * 	004D LATIN CAPITAL LETTER M
+ * 2134 SCRIPT SMALL O
+ * 	006F LATIN SMALL LETTER O
+ * 2135 ALEF SYMBOL
+ * 	05D0 HEBREW LETTER ALEF
+ * 2136 BET SYMBOL
+ * 	05D1 HEBREW LETTER BET
+ * 2137 GIMEL SYMBOL
+ * 	05D2 HEBREW LETTER GIMEL
+ * 2138 DALET SYMBOL
+ * 	05D3 HEBREW LETTER DALET
+ * 2139 INFORMATION SOURCE
+ * 	0069 LATIN SMALL LETTER I
+ * 213D DOUBLE-STRUCK SMALL GAMMA
+ * 	03B3 GREEK SMALL LETTER GAMMA
+ * 213E DOUBLE-STRUCK CAPITAL GAMMA
+ * 	0393 GREEK CAPITAL LETTER GAMMA
+ * 213F DOUBLE-STRUCK CAPITAL PI
+ * 	03A0 GREEK CAPITAL LETTER PI
+ * 2140 DOUBLE-STRUCK N-ARY SUMMATION
+ * 	2211 N-ARY SUMMATION
+ * 2145 DOUBLE-STRUCK ITALIC CAPITAL D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 2146 DOUBLE-STRUCK ITALIC SMALL D
+ * 	0064 LATIN SMALL LETTER D
+ * 2147 DOUBLE-STRUCK ITALIC SMALL E
+ * 	0065 LATIN SMALL LETTER E
+ * 2148 DOUBLE-STRUCK ITALIC SMALL I
+ * 	0069 LATIN SMALL LETTER I
+ * 2149 DOUBLE-STRUCK ITALIC SMALL J
+ * 	006A LATIN SMALL LETTER J
+ * 2153 VULGAR FRACTION ONE THIRD
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0033 DIGIT THREE
+ * 2154 VULGAR FRACTION TWO THIRDS
+ * 	0032 DIGIT TWO
+ * 	2044 FRACTION SLASH
+ * 	0033 DIGIT THREE
+ * 2155 VULGAR FRACTION ONE FIFTH
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0035 DIGIT FIVE
+ * 2156 VULGAR FRACTION TWO FIFTHS
+ * 	0032 DIGIT TWO
+ * 	2044 FRACTION SLASH
+ * 	0035 DIGIT FIVE
+ * 2157 VULGAR FRACTION THREE FIFTHS
+ * 	0033 DIGIT THREE
+ * 	2044 FRACTION SLASH
+ * 	0035 DIGIT FIVE
+ * 2158 VULGAR FRACTION FOUR FIFTHS
+ * 	0034 DIGIT FOUR
+ * 	2044 FRACTION SLASH
+ * 	0035 DIGIT FIVE
+ * 2159 VULGAR FRACTION ONE SIXTH
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0036 DIGIT SIX
+ * 215A VULGAR FRACTION FIVE SIXTHS
+ * 	0035 DIGIT FIVE
+ * 	2044 FRACTION SLASH
+ * 	0036 DIGIT SIX
+ * 215B VULGAR FRACTION ONE EIGHTH
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 	0038 DIGIT EIGHT
+ * 215C VULGAR FRACTION THREE EIGHTHS
+ * 	0033 DIGIT THREE
+ * 	2044 FRACTION SLASH
+ * 	0038 DIGIT EIGHT
+ * 215D VULGAR FRACTION FIVE EIGHTHS
+ * 	0035 DIGIT FIVE
+ * 	2044 FRACTION SLASH
+ * 	0038 DIGIT EIGHT
+ * 215E VULGAR FRACTION SEVEN EIGHTHS
+ * 	0037 DIGIT SEVEN
+ * 	2044 FRACTION SLASH
+ * 	0038 DIGIT EIGHT
+ * 215F FRACTION NUMERATOR ONE
+ * 	0031 DIGIT ONE
+ * 	2044 FRACTION SLASH
+ * 2160 ROMAN NUMERAL ONE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2161 ROMAN NUMERAL TWO
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2162 ROMAN NUMERAL THREE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2163 ROMAN NUMERAL FOUR
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0056 LATIN CAPITAL LETTER V
+ * 2164 ROMAN NUMERAL FIVE
+ * 	0056 LATIN CAPITAL LETTER V
+ * 2165 ROMAN NUMERAL SIX
+ * 	0056 LATIN CAPITAL LETTER V
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2166 ROMAN NUMERAL SEVEN
+ * 	0056 LATIN CAPITAL LETTER V
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2167 ROMAN NUMERAL EIGHT
+ * 	0056 LATIN CAPITAL LETTER V
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 2168 ROMAN NUMERAL NINE
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0058 LATIN CAPITAL LETTER X
+ * 2169 ROMAN NUMERAL TEN
+ * 	0058 LATIN CAPITAL LETTER X
+ * 216A ROMAN NUMERAL ELEVEN
+ * 	0058 LATIN CAPITAL LETTER X
+ * 	0049 LATIN CAPITAL LETTER I
+ * 216B ROMAN NUMERAL TWELVE
+ * 	0058 LATIN CAPITAL LETTER X
+ * 	0049 LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 216C ROMAN NUMERAL FIFTY
+ * 	004C LATIN CAPITAL LETTER L
+ * 216D ROMAN NUMERAL ONE HUNDRED
+ * 	0043 LATIN CAPITAL LETTER C
+ * 216E ROMAN NUMERAL FIVE HUNDRED
+ * 	0044 LATIN CAPITAL LETTER D
+ * 216F ROMAN NUMERAL ONE THOUSAND
+ * 	004D LATIN CAPITAL LETTER M
+ * 2170 SMALL ROMAN NUMERAL ONE
+ * 	0069 LATIN SMALL LETTER I
+ * 2171 SMALL ROMAN NUMERAL TWO
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 2172 SMALL ROMAN NUMERAL THREE
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 2173 SMALL ROMAN NUMERAL FOUR
+ * 	0069 LATIN SMALL LETTER I
+ * 	0076 LATIN SMALL LETTER V
+ * 2174 SMALL ROMAN NUMERAL FIVE
+ * 	0076 LATIN SMALL LETTER V
+ * 2175 SMALL ROMAN NUMERAL SIX
+ * 	0076 LATIN SMALL LETTER V
+ * 	0069 LATIN SMALL LETTER I
+ * 2176 SMALL ROMAN NUMERAL SEVEN
+ * 	0076 LATIN SMALL LETTER V
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 2177 SMALL ROMAN NUMERAL EIGHT
+ * 	0076 LATIN SMALL LETTER V
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 2178 SMALL ROMAN NUMERAL NINE
+ * 	0069 LATIN SMALL LETTER I
+ * 	0078 LATIN SMALL LETTER X
+ * 2179 SMALL ROMAN NUMERAL TEN
+ * 	0078 LATIN SMALL LETTER X
+ * 217A SMALL ROMAN NUMERAL ELEVEN
+ * 	0078 LATIN SMALL LETTER X
+ * 	0069 LATIN SMALL LETTER I
+ * 217B SMALL ROMAN NUMERAL TWELVE
+ * 	0078 LATIN SMALL LETTER X
+ * 	0069 LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 217C SMALL ROMAN NUMERAL FIFTY
+ * 	006C LATIN SMALL LETTER L
+ * 217D SMALL ROMAN NUMERAL ONE HUNDRED
+ * 	0063 LATIN SMALL LETTER C
+ * 217E SMALL ROMAN NUMERAL FIVE HUNDRED
+ * 	0064 LATIN SMALL LETTER D
+ * 217F SMALL ROMAN NUMERAL ONE THOUSAND
+ * 	006D LATIN SMALL LETTER M
+ * 219A LEFTWARDS ARROW WITH STROKE
+ * 	2190 LEFTWARDS ARROW
+ * 219B RIGHTWARDS ARROW WITH STROKE
+ * 	2192 RIGHTWARDS ARROW
+ * 21AE LEFT RIGHT ARROW WITH STROKE
+ * 	2194 LEFT RIGHT ARROW
+ * 21CD LEFTWARDS DOUBLE ARROW WITH STROKE
+ * 	21D0 LEFTWARDS DOUBLE ARROW
+ * 21CE LEFT RIGHT DOUBLE ARROW WITH STROKE
+ * 	21D4 LEFT RIGHT DOUBLE ARROW
+ * 21CF RIGHTWARDS DOUBLE ARROW WITH STROKE
+ * 	21D2 RIGHTWARDS DOUBLE ARROW
+ * 2204 THERE DOES NOT EXIST
+ * 	2203 THERE EXISTS
+ * 2209 NOT AN ELEMENT OF
+ * 	2208 ELEMENT OF
+ * 220C DOES NOT CONTAIN AS MEMBER
+ * 	220B CONTAINS AS MEMBER
+ * 2224 DOES NOT DIVIDE
+ * 	2223 DIVIDES
+ * 2226 NOT PARALLEL TO
+ * 	2225 PARALLEL TO
+ * 222C DOUBLE INTEGRAL
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 222D TRIPLE INTEGRAL
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 222F SURFACE INTEGRAL
+ * 	222E CONTOUR INTEGRAL
+ * 	222E CONTOUR INTEGRAL
+ * 2230 VOLUME INTEGRAL
+ * 	222E CONTOUR INTEGRAL
+ * 	222E CONTOUR INTEGRAL
+ * 	222E CONTOUR INTEGRAL
+ * 2241 NOT TILDE
+ * 	223C TILDE OPERATOR
+ * 2244 NOT ASYMPTOTICALLY EQUAL TO
+ * 	2243 ASYMPTOTICALLY EQUAL TO
+ * 2247 NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+ * 	2245 APPROXIMATELY EQUAL TO
+ * 2249 NOT ALMOST EQUAL TO
+ * 	2248 ALMOST EQUAL TO
+ * 2260 NOT EQUAL TO
+ * 	003D EQUALS SIGN
+ * 2262 NOT IDENTICAL TO
+ * 	2261 IDENTICAL TO
+ * 226D NOT EQUIVALENT TO
+ * 	224D EQUIVALENT TO
+ * 226E NOT LESS-THAN
+ * 	003C LESS-THAN SIGN
+ * 226F NOT GREATER-THAN
+ * 	003E GREATER-THAN SIGN
+ * 2270 NEITHER LESS-THAN NOR EQUAL TO
+ * 	2264 LESS-THAN OR EQUAL TO
+ * 2271 NEITHER GREATER-THAN NOR EQUAL TO
+ * 	2265 GREATER-THAN OR EQUAL TO
+ * 2274 NEITHER LESS-THAN NOR EQUIVALENT TO
+ * 	2272 LESS-THAN OR EQUIVALENT TO
+ * 2275 NEITHER GREATER-THAN NOR EQUIVALENT TO
+ * 	2273 GREATER-THAN OR EQUIVALENT TO
+ * 2278 NEITHER LESS-THAN NOR GREATER-THAN
+ * 	2276 LESS-THAN OR GREATER-THAN
+ * 2279 NEITHER GREATER-THAN NOR LESS-THAN
+ * 	2277 GREATER-THAN OR LESS-THAN
+ * 2280 DOES NOT PRECEDE
+ * 	227A PRECEDES
+ * 2281 DOES NOT SUCCEED
+ * 	227B SUCCEEDS
+ * 2284 NOT A SUBSET OF
+ * 	2282 SUBSET OF
+ * 2285 NOT A SUPERSET OF
+ * 	2283 SUPERSET OF
+ * 2288 NEITHER A SUBSET OF NOR EQUAL TO
+ * 	2286 SUBSET OF OR EQUAL TO
+ * 2289 NEITHER A SUPERSET OF NOR EQUAL TO
+ * 	2287 SUPERSET OF OR EQUAL TO
+ * 22AC DOES NOT PROVE
+ * 	22A2 RIGHT TACK
+ * 22AD NOT TRUE
+ * 	22A8 TRUE
+ * 22AE DOES NOT FORCE
+ * 	22A9 FORCES
+ * 22AF NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ * 	22AB DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ * 22E0 DOES NOT PRECEDE OR EQUAL
+ * 	227C PRECEDES OR EQUAL TO
+ * 22E1 DOES NOT SUCCEED OR EQUAL
+ * 	227D SUCCEEDS OR EQUAL TO
+ * 22E2 NOT SQUARE IMAGE OF OR EQUAL TO
+ * 	2291 SQUARE IMAGE OF OR EQUAL TO
+ * 22E3 NOT SQUARE ORIGINAL OF OR EQUAL TO
+ * 	2292 SQUARE ORIGINAL OF OR EQUAL TO
+ * 22EA NOT NORMAL SUBGROUP OF
+ * 	22B2 NORMAL SUBGROUP OF
+ * 22EB DOES NOT CONTAIN AS NORMAL SUBGROUP
+ * 	22B3 CONTAINS AS NORMAL SUBGROUP
+ * 22EC NOT NORMAL SUBGROUP OF OR EQUAL TO
+ * 	22B4 NORMAL SUBGROUP OF OR EQUAL TO
+ * 22ED DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ * 	22B5 CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ * 2329 LEFT-POINTING ANGLE BRACKET
+ * 	3008 LEFT ANGLE BRACKET
+ * 232A RIGHT-POINTING ANGLE BRACKET
+ * 	3009 RIGHT ANGLE BRACKET
+ * 2460 CIRCLED DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 2461 CIRCLED DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 2462 CIRCLED DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 2463 CIRCLED DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 2464 CIRCLED DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * 2465 CIRCLED DIGIT SIX
+ * 	0036 DIGIT SIX
+ * 2466 CIRCLED DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * 2467 CIRCLED DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * 2468 CIRCLED DIGIT NINE
+ * 	0039 DIGIT NINE
+ * 2469 CIRCLED NUMBER TEN
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 246A CIRCLED NUMBER ELEVEN
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 246B CIRCLED NUMBER TWELVE
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 246C CIRCLED NUMBER THIRTEEN
+ * 	0031 DIGIT ONE
+ * 	0033 DIGIT THREE
+ * 246D CIRCLED NUMBER FOURTEEN
+ * 	0031 DIGIT ONE
+ * 	0034 DIGIT FOUR
+ * 246E CIRCLED NUMBER FIFTEEN
+ * 	0031 DIGIT ONE
+ * 	0035 DIGIT FIVE
+ * 246F CIRCLED NUMBER SIXTEEN
+ * 	0031 DIGIT ONE
+ * 	0036 DIGIT SIX
+ * 2470 CIRCLED NUMBER SEVENTEEN
+ * 	0031 DIGIT ONE
+ * 	0037 DIGIT SEVEN
+ * 2471 CIRCLED NUMBER EIGHTEEN
+ * 	0031 DIGIT ONE
+ * 	0038 DIGIT EIGHT
+ * 2472 CIRCLED NUMBER NINETEEN
+ * 	0031 DIGIT ONE
+ * 	0039 DIGIT NINE
+ * 2473 CIRCLED NUMBER TWENTY
+ * 	0032 DIGIT TWO
+ * 	0030 DIGIT ZERO
+ * 2474 PARENTHESIZED DIGIT ONE
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0029 RIGHT PARENTHESIS
+ * 2475 PARENTHESIZED DIGIT TWO
+ * 	0028 LEFT PARENTHESIS
+ * 	0032 DIGIT TWO
+ * 	0029 RIGHT PARENTHESIS
+ * 2476 PARENTHESIZED DIGIT THREE
+ * 	0028 LEFT PARENTHESIS
+ * 	0033 DIGIT THREE
+ * 	0029 RIGHT PARENTHESIS
+ * 2477 PARENTHESIZED DIGIT FOUR
+ * 	0028 LEFT PARENTHESIS
+ * 	0034 DIGIT FOUR
+ * 	0029 RIGHT PARENTHESIS
+ * 2478 PARENTHESIZED DIGIT FIVE
+ * 	0028 LEFT PARENTHESIS
+ * 	0035 DIGIT FIVE
+ * 	0029 RIGHT PARENTHESIS
+ * 2479 PARENTHESIZED DIGIT SIX
+ * 	0028 LEFT PARENTHESIS
+ * 	0036 DIGIT SIX
+ * 	0029 RIGHT PARENTHESIS
+ * 247A PARENTHESIZED DIGIT SEVEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0037 DIGIT SEVEN
+ * 	0029 RIGHT PARENTHESIS
+ * 247B PARENTHESIZED DIGIT EIGHT
+ * 	0028 LEFT PARENTHESIS
+ * 	0038 DIGIT EIGHT
+ * 	0029 RIGHT PARENTHESIS
+ * 247C PARENTHESIZED DIGIT NINE
+ * 	0028 LEFT PARENTHESIS
+ * 	0039 DIGIT NINE
+ * 	0029 RIGHT PARENTHESIS
+ * 247D PARENTHESIZED NUMBER TEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 	0029 RIGHT PARENTHESIS
+ * 247E PARENTHESIZED NUMBER ELEVEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 	0029 RIGHT PARENTHESIS
+ * 247F PARENTHESIZED NUMBER TWELVE
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 	0029 RIGHT PARENTHESIS
+ * 2480 PARENTHESIZED NUMBER THIRTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0033 DIGIT THREE
+ * 	0029 RIGHT PARENTHESIS
+ * 2481 PARENTHESIZED NUMBER FOURTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0034 DIGIT FOUR
+ * 	0029 RIGHT PARENTHESIS
+ * 2482 PARENTHESIZED NUMBER FIFTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0035 DIGIT FIVE
+ * 	0029 RIGHT PARENTHESIS
+ * 2483 PARENTHESIZED NUMBER SIXTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0036 DIGIT SIX
+ * 	0029 RIGHT PARENTHESIS
+ * 2484 PARENTHESIZED NUMBER SEVENTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0037 DIGIT SEVEN
+ * 	0029 RIGHT PARENTHESIS
+ * 2485 PARENTHESIZED NUMBER EIGHTEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0038 DIGIT EIGHT
+ * 	0029 RIGHT PARENTHESIS
+ * 2486 PARENTHESIZED NUMBER NINETEEN
+ * 	0028 LEFT PARENTHESIS
+ * 	0031 DIGIT ONE
+ * 	0039 DIGIT NINE
+ * 	0029 RIGHT PARENTHESIS
+ * 2487 PARENTHESIZED NUMBER TWENTY
+ * 	0028 LEFT PARENTHESIS
+ * 	0032 DIGIT TWO
+ * 	0030 DIGIT ZERO
+ * 	0029 RIGHT PARENTHESIS
+ * 2488 DIGIT ONE FULL STOP
+ * 	0031 DIGIT ONE
+ * 	002E FULL STOP
+ * 2489 DIGIT TWO FULL STOP
+ * 	0032 DIGIT TWO
+ * 	002E FULL STOP
+ * 248A DIGIT THREE FULL STOP
+ * 	0033 DIGIT THREE
+ * 	002E FULL STOP
+ * 248B DIGIT FOUR FULL STOP
+ * 	0034 DIGIT FOUR
+ * 	002E FULL STOP
+ * 248C DIGIT FIVE FULL STOP
+ * 	0035 DIGIT FIVE
+ * 	002E FULL STOP
+ * 248D DIGIT SIX FULL STOP
+ * 	0036 DIGIT SIX
+ * 	002E FULL STOP
+ * 248E DIGIT SEVEN FULL STOP
+ * 	0037 DIGIT SEVEN
+ * 	002E FULL STOP
+ * 248F DIGIT EIGHT FULL STOP
+ * 	0038 DIGIT EIGHT
+ * 	002E FULL STOP
+ * 2490 DIGIT NINE FULL STOP
+ * 	0039 DIGIT NINE
+ * 	002E FULL STOP
+ * 2491 NUMBER TEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 	002E FULL STOP
+ * 2492 NUMBER ELEVEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 	002E FULL STOP
+ * 2493 NUMBER TWELVE FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 	002E FULL STOP
+ * 2494 NUMBER THIRTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0033 DIGIT THREE
+ * 	002E FULL STOP
+ * 2495 NUMBER FOURTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0034 DIGIT FOUR
+ * 	002E FULL STOP
+ * 2496 NUMBER FIFTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0035 DIGIT FIVE
+ * 	002E FULL STOP
+ * 2497 NUMBER SIXTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0036 DIGIT SIX
+ * 	002E FULL STOP
+ * 2498 NUMBER SEVENTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0037 DIGIT SEVEN
+ * 	002E FULL STOP
+ * 2499 NUMBER EIGHTEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0038 DIGIT EIGHT
+ * 	002E FULL STOP
+ * 249A NUMBER NINETEEN FULL STOP
+ * 	0031 DIGIT ONE
+ * 	0039 DIGIT NINE
+ * 	002E FULL STOP
+ * 249B NUMBER TWENTY FULL STOP
+ * 	0032 DIGIT TWO
+ * 	0030 DIGIT ZERO
+ * 	002E FULL STOP
+ * 249C PARENTHESIZED LATIN SMALL LETTER A
+ * 	0028 LEFT PARENTHESIS
+ * 	0061 LATIN SMALL LETTER A
+ * 	0029 RIGHT PARENTHESIS
+ * 249D PARENTHESIZED LATIN SMALL LETTER B
+ * 	0028 LEFT PARENTHESIS
+ * 	0062 LATIN SMALL LETTER B
+ * 	0029 RIGHT PARENTHESIS
+ * 249E PARENTHESIZED LATIN SMALL LETTER C
+ * 	0028 LEFT PARENTHESIS
+ * 	0063 LATIN SMALL LETTER C
+ * 	0029 RIGHT PARENTHESIS
+ * 249F PARENTHESIZED LATIN SMALL LETTER D
+ * 	0028 LEFT PARENTHESIS
+ * 	0064 LATIN SMALL LETTER D
+ * 	0029 RIGHT PARENTHESIS
+ * 24A0 PARENTHESIZED LATIN SMALL LETTER E
+ * 	0028 LEFT PARENTHESIS
+ * 	0065 LATIN SMALL LETTER E
+ * 	0029 RIGHT PARENTHESIS
+ * 24A1 PARENTHESIZED LATIN SMALL LETTER F
+ * 	0028 LEFT PARENTHESIS
+ * 	0066 LATIN SMALL LETTER F
+ * 	0029 RIGHT PARENTHESIS
+ * 24A2 PARENTHESIZED LATIN SMALL LETTER G
+ * 	0028 LEFT PARENTHESIS
+ * 	0067 LATIN SMALL LETTER G
+ * 	0029 RIGHT PARENTHESIS
+ * 24A3 PARENTHESIZED LATIN SMALL LETTER H
+ * 	0028 LEFT PARENTHESIS
+ * 	0068 LATIN SMALL LETTER H
+ * 	0029 RIGHT PARENTHESIS
+ * 24A4 PARENTHESIZED LATIN SMALL LETTER I
+ * 	0028 LEFT PARENTHESIS
+ * 	0069 LATIN SMALL LETTER I
+ * 	0029 RIGHT PARENTHESIS
+ * 24A5 PARENTHESIZED LATIN SMALL LETTER J
+ * 	0028 LEFT PARENTHESIS
+ * 	006A LATIN SMALL LETTER J
+ * 	0029 RIGHT PARENTHESIS
+ * 24A6 PARENTHESIZED LATIN SMALL LETTER K
+ * 	0028 LEFT PARENTHESIS
+ * 	006B LATIN SMALL LETTER K
+ * 	0029 RIGHT PARENTHESIS
+ * 24A7 PARENTHESIZED LATIN SMALL LETTER L
+ * 	0028 LEFT PARENTHESIS
+ * 	006C LATIN SMALL LETTER L
+ * 	0029 RIGHT PARENTHESIS
+ * 24A8 PARENTHESIZED LATIN SMALL LETTER M
+ * 	0028 LEFT PARENTHESIS
+ * 	006D LATIN SMALL LETTER M
+ * 	0029 RIGHT PARENTHESIS
+ * 24A9 PARENTHESIZED LATIN SMALL LETTER N
+ * 	0028 LEFT PARENTHESIS
+ * 	006E LATIN SMALL LETTER N
+ * 	0029 RIGHT PARENTHESIS
+ * 24AA PARENTHESIZED LATIN SMALL LETTER O
+ * 	0028 LEFT PARENTHESIS
+ * 	006F LATIN SMALL LETTER O
+ * 	0029 RIGHT PARENTHESIS
+ * 24AB PARENTHESIZED LATIN SMALL LETTER P
+ * 	0028 LEFT PARENTHESIS
+ * 	0070 LATIN SMALL LETTER P
+ * 	0029 RIGHT PARENTHESIS
+ * 24AC PARENTHESIZED LATIN SMALL LETTER Q
+ * 	0028 LEFT PARENTHESIS
+ * 	0071 LATIN SMALL LETTER Q
+ * 	0029 RIGHT PARENTHESIS
+ * 24AD PARENTHESIZED LATIN SMALL LETTER R
+ * 	0028 LEFT PARENTHESIS
+ * 	0072 LATIN SMALL LETTER R
+ * 	0029 RIGHT PARENTHESIS
+ * 24AE PARENTHESIZED LATIN SMALL LETTER S
+ * 	0028 LEFT PARENTHESIS
+ * 	0073 LATIN SMALL LETTER S
+ * 	0029 RIGHT PARENTHESIS
+ * 24AF PARENTHESIZED LATIN SMALL LETTER T
+ * 	0028 LEFT PARENTHESIS
+ * 	0074 LATIN SMALL LETTER T
+ * 	0029 RIGHT PARENTHESIS
+ * 24B0 PARENTHESIZED LATIN SMALL LETTER U
+ * 	0028 LEFT PARENTHESIS
+ * 	0075 LATIN SMALL LETTER U
+ * 	0029 RIGHT PARENTHESIS
+ * 24B1 PARENTHESIZED LATIN SMALL LETTER V
+ * 	0028 LEFT PARENTHESIS
+ * 	0076 LATIN SMALL LETTER V
+ * 	0029 RIGHT PARENTHESIS
+ * 24B2 PARENTHESIZED LATIN SMALL LETTER W
+ * 	0028 LEFT PARENTHESIS
+ * 	0077 LATIN SMALL LETTER W
+ * 	0029 RIGHT PARENTHESIS
+ * 24B3 PARENTHESIZED LATIN SMALL LETTER X
+ * 	0028 LEFT PARENTHESIS
+ * 	0078 LATIN SMALL LETTER X
+ * 	0029 RIGHT PARENTHESIS
+ * 24B4 PARENTHESIZED LATIN SMALL LETTER Y
+ * 	0028 LEFT PARENTHESIS
+ * 	0079 LATIN SMALL LETTER Y
+ * 	0029 RIGHT PARENTHESIS
+ * 24B5 PARENTHESIZED LATIN SMALL LETTER Z
+ * 	0028 LEFT PARENTHESIS
+ * 	007A LATIN SMALL LETTER Z
+ * 	0029 RIGHT PARENTHESIS
+ * 24B6 CIRCLED LATIN CAPITAL LETTER A
+ * 	0041 LATIN CAPITAL LETTER A
+ * 24B7 CIRCLED LATIN CAPITAL LETTER B
+ * 	0042 LATIN CAPITAL LETTER B
+ * 24B8 CIRCLED LATIN CAPITAL LETTER C
+ * 	0043 LATIN CAPITAL LETTER C
+ * 24B9 CIRCLED LATIN CAPITAL LETTER D
+ * 	0044 LATIN CAPITAL LETTER D
+ * 24BA CIRCLED LATIN CAPITAL LETTER E
+ * 	0045 LATIN CAPITAL LETTER E
+ * 24BB CIRCLED LATIN CAPITAL LETTER F
+ * 	0046 LATIN CAPITAL LETTER F
+ * 24BC CIRCLED LATIN CAPITAL LETTER G
+ * 	0047 LATIN CAPITAL LETTER G
+ * 24BD CIRCLED LATIN CAPITAL LETTER H
+ * 	0048 LATIN CAPITAL LETTER H
+ * 24BE CIRCLED LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * 24BF CIRCLED LATIN CAPITAL LETTER J
+ * 	004A LATIN CAPITAL LETTER J
+ * 24C0 CIRCLED LATIN CAPITAL LETTER K
+ * 	004B LATIN CAPITAL LETTER K
+ * 24C1 CIRCLED LATIN CAPITAL LETTER L
+ * 	004C LATIN CAPITAL LETTER L
+ * 24C2 CIRCLED LATIN CAPITAL LETTER M
+ * 	004D LATIN CAPITAL LETTER M
+ * 24C3 CIRCLED LATIN CAPITAL LETTER N
+ * 	004E LATIN CAPITAL LETTER N
+ * 24C4 CIRCLED LATIN CAPITAL LETTER O
+ * 	004F LATIN CAPITAL LETTER O
+ * 24C5 CIRCLED LATIN CAPITAL LETTER P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 24C6 CIRCLED LATIN CAPITAL LETTER Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * 24C7 CIRCLED LATIN CAPITAL LETTER R
+ * 	0052 LATIN CAPITAL LETTER R
+ * 24C8 CIRCLED LATIN CAPITAL LETTER S
+ * 	0053 LATIN CAPITAL LETTER S
+ * 24C9 CIRCLED LATIN CAPITAL LETTER T
+ * 	0054 LATIN CAPITAL LETTER T
+ * 24CA CIRCLED LATIN CAPITAL LETTER U
+ * 	0055 LATIN CAPITAL LETTER U
+ * 24CB CIRCLED LATIN CAPITAL LETTER V
+ * 	0056 LATIN CAPITAL LETTER V
+ * 24CC CIRCLED LATIN CAPITAL LETTER W
+ * 	0057 LATIN CAPITAL LETTER W
+ * 24CD CIRCLED LATIN CAPITAL LETTER X
+ * 	0058 LATIN CAPITAL LETTER X
+ * 24CE CIRCLED LATIN CAPITAL LETTER Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * 24CF CIRCLED LATIN CAPITAL LETTER Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * 24D0 CIRCLED LATIN SMALL LETTER A
+ * 	0061 LATIN SMALL LETTER A
+ * 24D1 CIRCLED LATIN SMALL LETTER B
+ * 	0062 LATIN SMALL LETTER B
+ * 24D2 CIRCLED LATIN SMALL LETTER C
+ * 	0063 LATIN SMALL LETTER C
+ * 24D3 CIRCLED LATIN SMALL LETTER D
+ * 	0064 LATIN SMALL LETTER D
+ * 24D4 CIRCLED LATIN SMALL LETTER E
+ * 	0065 LATIN SMALL LETTER E
+ * 24D5 CIRCLED LATIN SMALL LETTER F
+ * 	0066 LATIN SMALL LETTER F
+ * 24D6 CIRCLED LATIN SMALL LETTER G
+ * 	0067 LATIN SMALL LETTER G
+ * 24D7 CIRCLED LATIN SMALL LETTER H
+ * 	0068 LATIN SMALL LETTER H
+ * 24D8 CIRCLED LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * 24D9 CIRCLED LATIN SMALL LETTER J
+ * 	006A LATIN SMALL LETTER J
+ * 24DA CIRCLED LATIN SMALL LETTER K
+ * 	006B LATIN SMALL LETTER K
+ * 24DB CIRCLED LATIN SMALL LETTER L
+ * 	006C LATIN SMALL LETTER L
+ * 24DC CIRCLED LATIN SMALL LETTER M
+ * 	006D LATIN SMALL LETTER M
+ * 24DD CIRCLED LATIN SMALL LETTER N
+ * 	006E LATIN SMALL LETTER N
+ * 24DE CIRCLED LATIN SMALL LETTER O
+ * 	006F LATIN SMALL LETTER O
+ * 24DF CIRCLED LATIN SMALL LETTER P
+ * 	0070 LATIN SMALL LETTER P
+ * 24E0 CIRCLED LATIN SMALL LETTER Q
+ * 	0071 LATIN SMALL LETTER Q
+ * 24E1 CIRCLED LATIN SMALL LETTER R
+ * 	0072 LATIN SMALL LETTER R
+ * 24E2 CIRCLED LATIN SMALL LETTER S
+ * 	0073 LATIN SMALL LETTER S
+ * 24E3 CIRCLED LATIN SMALL LETTER T
+ * 	0074 LATIN SMALL LETTER T
+ * 24E4 CIRCLED LATIN SMALL LETTER U
+ * 	0075 LATIN SMALL LETTER U
+ * 24E5 CIRCLED LATIN SMALL LETTER V
+ * 	0076 LATIN SMALL LETTER V
+ * 24E6 CIRCLED LATIN SMALL LETTER W
+ * 	0077 LATIN SMALL LETTER W
+ * 24E7 CIRCLED LATIN SMALL LETTER X
+ * 	0078 LATIN SMALL LETTER X
+ * 24E8 CIRCLED LATIN SMALL LETTER Y
+ * 	0079 LATIN SMALL LETTER Y
+ * 24E9 CIRCLED LATIN SMALL LETTER Z
+ * 	007A LATIN SMALL LETTER Z
+ * 24EA CIRCLED DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * 2A0C QUADRUPLE INTEGRAL OPERATOR
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 	222B INTEGRAL
+ * 2A74 DOUBLE COLON EQUAL
+ * 	003A COLON
+ * 	003A COLON
+ * 	003D EQUALS SIGN
+ * 2A75 TWO CONSECUTIVE EQUALS SIGNS
+ * 	003D EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * 2A76 THREE CONSECUTIVE EQUALS SIGNS
+ * 	003D EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * 2ADC FORKING
+ * 	2ADD NONFORKING
+ * 2E9F CJK RADICAL MOTHER
+ * 	6BCD ??
+ * 2EF3 CJK RADICAL C-SIMPLIFIED TURTLE
+ * 	9F9F ??
+ * 2F00 KANGXI RADICAL ONE
+ * 	4E00 <CJK Ideograph, First>
+ * 2F01 KANGXI RADICAL LINE
+ * 	4E28 ??
+ * 2F02 KANGXI RADICAL DOT
+ * 	4E36 ??
+ * 2F03 KANGXI RADICAL SLASH
+ * 	4E3F ??
+ * 2F04 KANGXI RADICAL SECOND
+ * 	4E59 ??
+ * 2F05 KANGXI RADICAL HOOK
+ * 	4E85 ??
+ * 2F06 KANGXI RADICAL TWO
+ * 	4E8C ??
+ * 2F07 KANGXI RADICAL LID
+ * 	4EA0 ??
+ * 2F08 KANGXI RADICAL MAN
+ * 	4EBA ??
+ * 2F09 KANGXI RADICAL LEGS
+ * 	513F ??
+ * 2F0A KANGXI RADICAL ENTER
+ * 	5165 ??
+ * 2F0B KANGXI RADICAL EIGHT
+ * 	516B ??
+ * 2F0C KANGXI RADICAL DOWN BOX
+ * 	5182 ??
+ * 2F0D KANGXI RADICAL COVER
+ * 	5196 ??
+ * 2F0E KANGXI RADICAL ICE
+ * 	51AB ??
+ * 2F0F KANGXI RADICAL TABLE
+ * 	51E0 ??
+ * 2F10 KANGXI RADICAL OPEN BOX
+ * 	51F5 ??
+ * 2F11 KANGXI RADICAL KNIFE
+ * 	5200 ??
+ * 2F12 KANGXI RADICAL POWER
+ * 	529B ??
+ * 2F13 KANGXI RADICAL WRAP
+ * 	52F9 ??
+ * 2F14 KANGXI RADICAL SPOON
+ * 	5315 ??
+ * 2F15 KANGXI RADICAL RIGHT OPEN BOX
+ * 	531A ??
+ * 2F16 KANGXI RADICAL HIDING ENCLOSURE
+ * 	5338 ??
+ * 2F17 KANGXI RADICAL TEN
+ * 	5341 ??
+ * 2F18 KANGXI RADICAL DIVINATION
+ * 	535C ??
+ * 2F19 KANGXI RADICAL SEAL
+ * 	5369 ??
+ * 2F1A KANGXI RADICAL CLIFF
+ * 	5382 ??
+ * 2F1B KANGXI RADICAL PRIVATE
+ * 	53B6 ??
+ * 2F1C KANGXI RADICAL AGAIN
+ * 	53C8 ??
+ * 2F1D KANGXI RADICAL MOUTH
+ * 	53E3 ??
+ * 2F1E KANGXI RADICAL ENCLOSURE
+ * 	56D7 ??
+ * 2F1F KANGXI RADICAL EARTH
+ * 	571F ??
+ * 2F20 KANGXI RADICAL SCHOLAR
+ * 	58EB ??
+ * 2F21 KANGXI RADICAL GO
+ * 	5902 ??
+ * 2F22 KANGXI RADICAL GO SLOWLY
+ * 	590A ??
+ * 2F23 KANGXI RADICAL EVENING
+ * 	5915 ??
+ * 2F24 KANGXI RADICAL BIG
+ * 	5927 ??
+ * 2F25 KANGXI RADICAL WOMAN
+ * 	5973 ??
+ * 2F26 KANGXI RADICAL CHILD
+ * 	5B50 ??
+ * 2F27 KANGXI RADICAL ROOF
+ * 	5B80 ??
+ * 2F28 KANGXI RADICAL INCH
+ * 	5BF8 ??
+ * 2F29 KANGXI RADICAL SMALL
+ * 	5C0F ??
+ * 2F2A KANGXI RADICAL LAME
+ * 	5C22 ??
+ * 2F2B KANGXI RADICAL CORPSE
+ * 	5C38 ??
+ * 2F2C KANGXI RADICAL SPROUT
+ * 	5C6E ??
+ * 2F2D KANGXI RADICAL MOUNTAIN
+ * 	5C71 ??
+ * 2F2E KANGXI RADICAL RIVER
+ * 	5DDB ??
+ * 2F2F KANGXI RADICAL WORK
+ * 	5DE5 ??
+ * 2F30 KANGXI RADICAL ONESELF
+ * 	5DF1 ??
+ * 2F31 KANGXI RADICAL TURBAN
+ * 	5DFE ??
+ * 2F32 KANGXI RADICAL DRY
+ * 	5E72 ??
+ * 2F33 KANGXI RADICAL SHORT THREAD
+ * 	5E7A ??
+ * 2F34 KANGXI RADICAL DOTTED CLIFF
+ * 	5E7F ??
+ * 2F35 KANGXI RADICAL LONG STRIDE
+ * 	5EF4 ??
+ * 2F36 KANGXI RADICAL TWO HANDS
+ * 	5EFE ??
+ * 2F37 KANGXI RADICAL SHOOT
+ * 	5F0B ??
+ * 2F38 KANGXI RADICAL BOW
+ * 	5F13 ??
+ * 2F39 KANGXI RADICAL SNOUT
+ * 	5F50 ??
+ * 2F3A KANGXI RADICAL BRISTLE
+ * 	5F61 ??
+ * 2F3B KANGXI RADICAL STEP
+ * 	5F73 ??
+ * 2F3C KANGXI RADICAL HEART
+ * 	5FC3 ??
+ * 2F3D KANGXI RADICAL HALBERD
+ * 	6208 ??
+ * 2F3E KANGXI RADICAL DOOR
+ * 	6236 ??
+ * 2F3F KANGXI RADICAL HAND
+ * 	624B ??
+ * 2F40 KANGXI RADICAL BRANCH
+ * 	652F ??
+ * 2F41 KANGXI RADICAL RAP
+ * 	6534 ??
+ * 2F42 KANGXI RADICAL SCRIPT
+ * 	6587 ??
+ * 2F43 KANGXI RADICAL DIPPER
+ * 	6597 ??
+ * 2F44 KANGXI RADICAL AXE
+ * 	65A4 ??
+ * 2F45 KANGXI RADICAL SQUARE
+ * 	65B9 ??
+ * 2F46 KANGXI RADICAL NOT
+ * 	65E0 ??
+ * 2F47 KANGXI RADICAL SUN
+ * 	65E5 ??
+ * 2F48 KANGXI RADICAL SAY
+ * 	66F0 ??
+ * 2F49 KANGXI RADICAL MOON
+ * 	6708 ??
+ * 2F4A KANGXI RADICAL TREE
+ * 	6728 ??
+ * 2F4B KANGXI RADICAL LACK
+ * 	6B20 ??
+ * 2F4C KANGXI RADICAL STOP
+ * 	6B62 ??
+ * 2F4D KANGXI RADICAL DEATH
+ * 	6B79 ??
+ * 2F4E KANGXI RADICAL WEAPON
+ * 	6BB3 ??
+ * 2F4F KANGXI RADICAL DO NOT
+ * 	6BCB ??
+ * 2F50 KANGXI RADICAL COMPARE
+ * 	6BD4 ??
+ * 2F51 KANGXI RADICAL FUR
+ * 	6BDB ??
+ * 2F52 KANGXI RADICAL CLAN
+ * 	6C0F ??
+ * 2F53 KANGXI RADICAL STEAM
+ * 	6C14 ??
+ * 2F54 KANGXI RADICAL WATER
+ * 	6C34 ??
+ * 2F55 KANGXI RADICAL FIRE
+ * 	706B ??
+ * 2F56 KANGXI RADICAL CLAW
+ * 	722A ??
+ * 2F57 KANGXI RADICAL FATHER
+ * 	7236 ??
+ * 2F58 KANGXI RADICAL DOUBLE X
+ * 	723B ??
+ * 2F59 KANGXI RADICAL HALF TREE TRUNK
+ * 	723F ??
+ * 2F5A KANGXI RADICAL SLICE
+ * 	7247 ??
+ * 2F5B KANGXI RADICAL FANG
+ * 	7259 ??
+ * 2F5C KANGXI RADICAL COW
+ * 	725B ??
+ * 2F5D KANGXI RADICAL DOG
+ * 	72AC ??
+ * 2F5E KANGXI RADICAL PROFOUND
+ * 	7384 ??
+ * 2F5F KANGXI RADICAL JADE
+ * 	7389 ??
+ * 2F60 KANGXI RADICAL MELON
+ * 	74DC ??
+ * 2F61 KANGXI RADICAL TILE
+ * 	74E6 ??
+ * 2F62 KANGXI RADICAL SWEET
+ * 	7518 ??
+ * 2F63 KANGXI RADICAL LIFE
+ * 	751F ??
+ * 2F64 KANGXI RADICAL USE
+ * 	7528 ??
+ * 2F65 KANGXI RADICAL FIELD
+ * 	7530 ??
+ * 2F66 KANGXI RADICAL BOLT OF CLOTH
+ * 	758B ??
+ * 2F67 KANGXI RADICAL SICKNESS
+ * 	7592 ??
+ * 2F68 KANGXI RADICAL DOTTED TENT
+ * 	7676 ??
+ * 2F69 KANGXI RADICAL WHITE
+ * 	767D ??
+ * 2F6A KANGXI RADICAL SKIN
+ * 	76AE ??
+ * 2F6B KANGXI RADICAL DISH
+ * 	76BF ??
+ * 2F6C KANGXI RADICAL EYE
+ * 	76EE ??
+ * 2F6D KANGXI RADICAL SPEAR
+ * 	77DB ??
+ * 2F6E KANGXI RADICAL ARROW
+ * 	77E2 ??
+ * 2F6F KANGXI RADICAL STONE
+ * 	77F3 ??
+ * 2F70 KANGXI RADICAL SPIRIT
+ * 	793A ??
+ * 2F71 KANGXI RADICAL TRACK
+ * 	79B8 ??
+ * 2F72 KANGXI RADICAL GRAIN
+ * 	79BE ??
+ * 2F73 KANGXI RADICAL CAVE
+ * 	7A74 ??
+ * 2F74 KANGXI RADICAL STAND
+ * 	7ACB ??
+ * 2F75 KANGXI RADICAL BAMBOO
+ * 	7AF9 ??
+ * 2F76 KANGXI RADICAL RICE
+ * 	7C73 ??
+ * 2F77 KANGXI RADICAL SILK
+ * 	7CF8 ??
+ * 2F78 KANGXI RADICAL JAR
+ * 	7F36 ??
+ * 2F79 KANGXI RADICAL NET
+ * 	7F51 ??
+ * 2F7A KANGXI RADICAL SHEEP
+ * 	7F8A ??
+ * 2F7B KANGXI RADICAL FEATHER
+ * 	7FBD ??
+ * 2F7C KANGXI RADICAL OLD
+ * 	8001 ??
+ * 2F7D KANGXI RADICAL AND
+ * 	800C ??
+ * 2F7E KANGXI RADICAL PLOW
+ * 	8012 ??
+ * 2F7F KANGXI RADICAL EAR
+ * 	8033 ??
+ * 2F80 KANGXI RADICAL BRUSH
+ * 	807F ??
+ * 2F800 CJK COMPATIBILITY IDEOGRAPH-2F800
+ * 	4E3D ??
+ * 2F801 CJK COMPATIBILITY IDEOGRAPH-2F801
+ * 	4E38 ??
+ * 2F802 CJK COMPATIBILITY IDEOGRAPH-2F802
+ * 	4E41 ??
+ * 2F803 CJK COMPATIBILITY IDEOGRAPH-2F803
+ * 	20122 ??
+ * 2F804 CJK COMPATIBILITY IDEOGRAPH-2F804
+ * 	4F60 ??
+ * 2F805 CJK COMPATIBILITY IDEOGRAPH-2F805
+ * 	4FAE ??
+ * 2F806 CJK COMPATIBILITY IDEOGRAPH-2F806
+ * 	4FBB ??
+ * 2F807 CJK COMPATIBILITY IDEOGRAPH-2F807
+ * 	5002 ??
+ * 2F808 CJK COMPATIBILITY IDEOGRAPH-2F808
+ * 	507A ??
+ * 2F809 CJK COMPATIBILITY IDEOGRAPH-2F809
+ * 	5099 ??
+ * 2F80A CJK COMPATIBILITY IDEOGRAPH-2F80A
+ * 	50E7 ??
+ * 2F80B CJK COMPATIBILITY IDEOGRAPH-2F80B
+ * 	50CF ??
+ * 2F80C CJK COMPATIBILITY IDEOGRAPH-2F80C
+ * 	349E ??
+ * 2F80D CJK COMPATIBILITY IDEOGRAPH-2F80D
+ * 	2063A ??
+ * 2F80E CJK COMPATIBILITY IDEOGRAPH-2F80E
+ * 	514D ??
+ * 2F80F CJK COMPATIBILITY IDEOGRAPH-2F80F
+ * 	5154 ??
+ * 2F81 KANGXI RADICAL MEAT
+ * 	8089 ??
+ * 2F810 CJK COMPATIBILITY IDEOGRAPH-2F810
+ * 	5164 ??
+ * 2F811 CJK COMPATIBILITY IDEOGRAPH-2F811
+ * 	5177 ??
+ * 2F812 CJK COMPATIBILITY IDEOGRAPH-2F812
+ * 	2051C ??
+ * 2F813 CJK COMPATIBILITY IDEOGRAPH-2F813
+ * 	34B9 ??
+ * 2F814 CJK COMPATIBILITY IDEOGRAPH-2F814
+ * 	5167 ??
+ * 2F815 CJK COMPATIBILITY IDEOGRAPH-2F815
+ * 	518D ??
+ * 2F816 CJK COMPATIBILITY IDEOGRAPH-2F816
+ * 	2054B ??
+ * 2F817 CJK COMPATIBILITY IDEOGRAPH-2F817
+ * 	5197 ??
+ * 2F818 CJK COMPATIBILITY IDEOGRAPH-2F818
+ * 	51A4 ??
+ * 2F819 CJK COMPATIBILITY IDEOGRAPH-2F819
+ * 	4ECC ??
+ * 2F81A CJK COMPATIBILITY IDEOGRAPH-2F81A
+ * 	51AC ??
+ * 2F81B CJK COMPATIBILITY IDEOGRAPH-2F81B
+ * 	51B5 ??
+ * 2F81C CJK COMPATIBILITY IDEOGRAPH-2F81C
+ * 	291DF ??
+ * 2F81D CJK COMPATIBILITY IDEOGRAPH-2F81D
+ * 	51F5 ??
+ * 2F81E CJK COMPATIBILITY IDEOGRAPH-2F81E
+ * 	5203 ??
+ * 2F81F CJK COMPATIBILITY IDEOGRAPH-2F81F
+ * 	34DF ??
+ * 2F82 KANGXI RADICAL MINISTER
+ * 	81E3 ??
+ * 2F820 CJK COMPATIBILITY IDEOGRAPH-2F820
+ * 	523B ??
+ * 2F821 CJK COMPATIBILITY IDEOGRAPH-2F821
+ * 	5246 ??
+ * 2F822 CJK COMPATIBILITY IDEOGRAPH-2F822
+ * 	5272 ??
+ * 2F823 CJK COMPATIBILITY IDEOGRAPH-2F823
+ * 	5277 ??
+ * 2F824 CJK COMPATIBILITY IDEOGRAPH-2F824
+ * 	3515 ??
+ * 2F825 CJK COMPATIBILITY IDEOGRAPH-2F825
+ * 	52C7 ??
+ * 2F826 CJK COMPATIBILITY IDEOGRAPH-2F826
+ * 	52C9 ??
+ * 2F827 CJK COMPATIBILITY IDEOGRAPH-2F827
+ * 	52E4 ??
+ * 2F828 CJK COMPATIBILITY IDEOGRAPH-2F828
+ * 	52FA ??
+ * 2F829 CJK COMPATIBILITY IDEOGRAPH-2F829
+ * 	5305 ??
+ * 2F82A CJK COMPATIBILITY IDEOGRAPH-2F82A
+ * 	5306 ??
+ * 2F82B CJK COMPATIBILITY IDEOGRAPH-2F82B
+ * 	5317 ??
+ * 2F82C CJK COMPATIBILITY IDEOGRAPH-2F82C
+ * 	5349 ??
+ * 2F82D CJK COMPATIBILITY IDEOGRAPH-2F82D
+ * 	5351 ??
+ * 2F82E CJK COMPATIBILITY IDEOGRAPH-2F82E
+ * 	535A ??
+ * 2F82F CJK COMPATIBILITY IDEOGRAPH-2F82F
+ * 	5373 ??
+ * 2F83 KANGXI RADICAL SELF
+ * 	81EA ??
+ * 2F830 CJK COMPATIBILITY IDEOGRAPH-2F830
+ * 	537D ??
+ * 2F831 CJK COMPATIBILITY IDEOGRAPH-2F831
+ * 	537F ??
+ * 2F832 CJK COMPATIBILITY IDEOGRAPH-2F832
+ * 	537F ??
+ * 2F833 CJK COMPATIBILITY IDEOGRAPH-2F833
+ * 	537F ??
+ * 2F834 CJK COMPATIBILITY IDEOGRAPH-2F834
+ * 	20A2C ??
+ * 2F835 CJK COMPATIBILITY IDEOGRAPH-2F835
+ * 	7070 ??
+ * 2F836 CJK COMPATIBILITY IDEOGRAPH-2F836
+ * 	53CA ??
+ * 2F837 CJK COMPATIBILITY IDEOGRAPH-2F837
+ * 	53DF ??
+ * 2F838 CJK COMPATIBILITY IDEOGRAPH-2F838
+ * 	20B63 ??
+ * 2F839 CJK COMPATIBILITY IDEOGRAPH-2F839
+ * 	53EB ??
+ * 2F83A CJK COMPATIBILITY IDEOGRAPH-2F83A
+ * 	53F1 ??
+ * 2F83B CJK COMPATIBILITY IDEOGRAPH-2F83B
+ * 	5406 ??
+ * 2F83C CJK COMPATIBILITY IDEOGRAPH-2F83C
+ * 	549E ??
+ * 2F83D CJK COMPATIBILITY IDEOGRAPH-2F83D
+ * 	5438 ??
+ * 2F83E CJK COMPATIBILITY IDEOGRAPH-2F83E
+ * 	5448 ??
+ * 2F83F CJK COMPATIBILITY IDEOGRAPH-2F83F
+ * 	5468 ??
+ * 2F84 KANGXI RADICAL ARRIVE
+ * 	81F3 ??
+ * 2F840 CJK COMPATIBILITY IDEOGRAPH-2F840
+ * 	54A2 ??
+ * 2F841 CJK COMPATIBILITY IDEOGRAPH-2F841
+ * 	54F6 ??
+ * 2F842 CJK COMPATIBILITY IDEOGRAPH-2F842
+ * 	5510 ??
+ * 2F843 CJK COMPATIBILITY IDEOGRAPH-2F843
+ * 	5553 ??
+ * 2F844 CJK COMPATIBILITY IDEOGRAPH-2F844
+ * 	5563 ??
+ * 2F845 CJK COMPATIBILITY IDEOGRAPH-2F845
+ * 	5584 ??
+ * 2F846 CJK COMPATIBILITY IDEOGRAPH-2F846
+ * 	5584 ??
+ * 2F847 CJK COMPATIBILITY IDEOGRAPH-2F847
+ * 	5599 ??
+ * 2F848 CJK COMPATIBILITY IDEOGRAPH-2F848
+ * 	55AB ??
+ * 2F849 CJK COMPATIBILITY IDEOGRAPH-2F849
+ * 	55B3 ??
+ * 2F84A CJK COMPATIBILITY IDEOGRAPH-2F84A
+ * 	55C2 ??
+ * 2F84B CJK COMPATIBILITY IDEOGRAPH-2F84B
+ * 	5716 ??
+ * 2F84C CJK COMPATIBILITY IDEOGRAPH-2F84C
+ * 	5606 ??
+ * 2F84D CJK COMPATIBILITY IDEOGRAPH-2F84D
+ * 	5717 ??
+ * 2F84E CJK COMPATIBILITY IDEOGRAPH-2F84E
+ * 	5651 ??
+ * 2F84F CJK COMPATIBILITY IDEOGRAPH-2F84F
+ * 	5674 ??
+ * 2F85 KANGXI RADICAL MORTAR
+ * 	81FC ??
+ * 2F850 CJK COMPATIBILITY IDEOGRAPH-2F850
+ * 	5207 ??
+ * 2F851 CJK COMPATIBILITY IDEOGRAPH-2F851
+ * 	58EE ??
+ * 2F852 CJK COMPATIBILITY IDEOGRAPH-2F852
+ * 	57CE ??
+ * 2F853 CJK COMPATIBILITY IDEOGRAPH-2F853
+ * 	57F4 ??
+ * 2F854 CJK COMPATIBILITY IDEOGRAPH-2F854
+ * 	580D ??
+ * 2F855 CJK COMPATIBILITY IDEOGRAPH-2F855
+ * 	578B ??
+ * 2F856 CJK COMPATIBILITY IDEOGRAPH-2F856
+ * 	5832 ??
+ * 2F857 CJK COMPATIBILITY IDEOGRAPH-2F857
+ * 	5831 ??
+ * 2F858 CJK COMPATIBILITY IDEOGRAPH-2F858
+ * 	58AC ??
+ * 2F859 CJK COMPATIBILITY IDEOGRAPH-2F859
+ * 	214E4 ??
+ * 2F85A CJK COMPATIBILITY IDEOGRAPH-2F85A
+ * 	58F2 ??
+ * 2F85B CJK COMPATIBILITY IDEOGRAPH-2F85B
+ * 	58F7 ??
+ * 2F85C CJK COMPATIBILITY IDEOGRAPH-2F85C
+ * 	5906 ??
+ * 2F85D CJK COMPATIBILITY IDEOGRAPH-2F85D
+ * 	591A ??
+ * 2F85E CJK COMPATIBILITY IDEOGRAPH-2F85E
+ * 	5922 ??
+ * 2F85F CJK COMPATIBILITY IDEOGRAPH-2F85F
+ * 	5962 ??
+ * 2F86 KANGXI RADICAL TONGUE
+ * 	820C ??
+ * 2F860 CJK COMPATIBILITY IDEOGRAPH-2F860
+ * 	216A8 ??
+ * 2F861 CJK COMPATIBILITY IDEOGRAPH-2F861
+ * 	216EA ??
+ * 2F862 CJK COMPATIBILITY IDEOGRAPH-2F862
+ * 	59EC ??
+ * 2F863 CJK COMPATIBILITY IDEOGRAPH-2F863
+ * 	5A1B ??
+ * 2F864 CJK COMPATIBILITY IDEOGRAPH-2F864
+ * 	5A27 ??
+ * 2F865 CJK COMPATIBILITY IDEOGRAPH-2F865
+ * 	59D8 ??
+ * 2F866 CJK COMPATIBILITY IDEOGRAPH-2F866
+ * 	5A66 ??
+ * 2F867 CJK COMPATIBILITY IDEOGRAPH-2F867
+ * 	36EE ??
+ * 2F868 CJK COMPATIBILITY IDEOGRAPH-2F868
+ * 	2136A ??
+ * 2F869 CJK COMPATIBILITY IDEOGRAPH-2F869
+ * 	5B08 ??
+ * 2F86A CJK COMPATIBILITY IDEOGRAPH-2F86A
+ * 	5B3E ??
+ * 2F86B CJK COMPATIBILITY IDEOGRAPH-2F86B
+ * 	5B3E ??
+ * 2F86C CJK COMPATIBILITY IDEOGRAPH-2F86C
+ * 	219C8 ??
+ * 2F86D CJK COMPATIBILITY IDEOGRAPH-2F86D
+ * 	5BC3 ??
+ * 2F86E CJK COMPATIBILITY IDEOGRAPH-2F86E
+ * 	5BD8 ??
+ * 2F86F CJK COMPATIBILITY IDEOGRAPH-2F86F
+ * 	5BE7 ??
+ * 2F87 KANGXI RADICAL OPPOSE
+ * 	821B ??
+ * 2F870 CJK COMPATIBILITY IDEOGRAPH-2F870
+ * 	5BF3 ??
+ * 2F871 CJK COMPATIBILITY IDEOGRAPH-2F871
+ * 	21B18 ??
+ * 2F872 CJK COMPATIBILITY IDEOGRAPH-2F872
+ * 	5BFF ??
+ * 2F873 CJK COMPATIBILITY IDEOGRAPH-2F873
+ * 	5C06 ??
+ * 2F874 CJK COMPATIBILITY IDEOGRAPH-2F874
+ * 	5F33 ??
+ * 2F875 CJK COMPATIBILITY IDEOGRAPH-2F875
+ * 	5C22 ??
+ * 2F876 CJK COMPATIBILITY IDEOGRAPH-2F876
+ * 	3781 ??
+ * 2F877 CJK COMPATIBILITY IDEOGRAPH-2F877
+ * 	5C60 ??
+ * 2F878 CJK COMPATIBILITY IDEOGRAPH-2F878
+ * 	5C6E ??
+ * 2F879 CJK COMPATIBILITY IDEOGRAPH-2F879
+ * 	5CC0 ??
+ * 2F87A CJK COMPATIBILITY IDEOGRAPH-2F87A
+ * 	5C8D ??
+ * 2F87B CJK COMPATIBILITY IDEOGRAPH-2F87B
+ * 	21DE4 ??
+ * 2F87C CJK COMPATIBILITY IDEOGRAPH-2F87C
+ * 	5D43 ??
+ * 2F87D CJK COMPATIBILITY IDEOGRAPH-2F87D
+ * 	21DE6 ??
+ * 2F87E CJK COMPATIBILITY IDEOGRAPH-2F87E
+ * 	5D6E ??
+ * 2F87F CJK COMPATIBILITY IDEOGRAPH-2F87F
+ * 	5D6B ??
+ * 2F88 KANGXI RADICAL BOAT
+ * 	821F ??
+ * 2F880 CJK COMPATIBILITY IDEOGRAPH-2F880
+ * 	5D7C ??
+ * 2F881 CJK COMPATIBILITY IDEOGRAPH-2F881
+ * 	5DE1 ??
+ * 2F882 CJK COMPATIBILITY IDEOGRAPH-2F882
+ * 	5DE2 ??
+ * 2F883 CJK COMPATIBILITY IDEOGRAPH-2F883
+ * 	382F ??
+ * 2F884 CJK COMPATIBILITY IDEOGRAPH-2F884
+ * 	5DFD ??
+ * 2F885 CJK COMPATIBILITY IDEOGRAPH-2F885
+ * 	5E28 ??
+ * 2F886 CJK COMPATIBILITY IDEOGRAPH-2F886
+ * 	5E3D ??
+ * 2F887 CJK COMPATIBILITY IDEOGRAPH-2F887
+ * 	5E69 ??
+ * 2F888 CJK COMPATIBILITY IDEOGRAPH-2F888
+ * 	3862 ??
+ * 2F889 CJK COMPATIBILITY IDEOGRAPH-2F889
+ * 	22183 ??
+ * 2F88A CJK COMPATIBILITY IDEOGRAPH-2F88A
+ * 	387C ??
+ * 2F88B CJK COMPATIBILITY IDEOGRAPH-2F88B
+ * 	5EB0 ??
+ * 2F88C CJK COMPATIBILITY IDEOGRAPH-2F88C
+ * 	5EB3 ??
+ * 2F88D CJK COMPATIBILITY IDEOGRAPH-2F88D
+ * 	5EB6 ??
+ * 2F88E CJK COMPATIBILITY IDEOGRAPH-2F88E
+ * 	5ECA ??
+ * 2F88F CJK COMPATIBILITY IDEOGRAPH-2F88F
+ * 	2A392 ??
+ * 2F89 KANGXI RADICAL STOPPING
+ * 	826E ??
+ * 2F890 CJK COMPATIBILITY IDEOGRAPH-2F890
+ * 	5EFE ??
+ * 2F891 CJK COMPATIBILITY IDEOGRAPH-2F891
+ * 	22331 ??
+ * 2F892 CJK COMPATIBILITY IDEOGRAPH-2F892
+ * 	22331 ??
+ * 2F893 CJK COMPATIBILITY IDEOGRAPH-2F893
+ * 	8201 ??
+ * 2F894 CJK COMPATIBILITY IDEOGRAPH-2F894
+ * 	5F22 ??
+ * 2F895 CJK COMPATIBILITY IDEOGRAPH-2F895
+ * 	5F22 ??
+ * 2F896 CJK COMPATIBILITY IDEOGRAPH-2F896
+ * 	38C7 ??
+ * 2F897 CJK COMPATIBILITY IDEOGRAPH-2F897
+ * 	232B8 ??
+ * 2F898 CJK COMPATIBILITY IDEOGRAPH-2F898
+ * 	261DA ??
+ * 2F899 CJK COMPATIBILITY IDEOGRAPH-2F899
+ * 	5F62 ??
+ * 2F89A CJK COMPATIBILITY IDEOGRAPH-2F89A
+ * 	5F6B ??
+ * 2F89B CJK COMPATIBILITY IDEOGRAPH-2F89B
+ * 	38E3 ??
+ * 2F89C CJK COMPATIBILITY IDEOGRAPH-2F89C
+ * 	5F9A ??
+ * 2F89D CJK COMPATIBILITY IDEOGRAPH-2F89D
+ * 	5FCD ??
+ * 2F89E CJK COMPATIBILITY IDEOGRAPH-2F89E
+ * 	5FD7 ??
+ * 2F89F CJK COMPATIBILITY IDEOGRAPH-2F89F
+ * 	5FF9 ??
+ * 2F8A KANGXI RADICAL COLOR
+ * 	8272 ??
+ * 2F8A0 CJK COMPATIBILITY IDEOGRAPH-2F8A0
+ * 	6081 ??
+ * 2F8A1 CJK COMPATIBILITY IDEOGRAPH-2F8A1
+ * 	393A ??
+ * 2F8A2 CJK COMPATIBILITY IDEOGRAPH-2F8A2
+ * 	391C ??
+ * 2F8A3 CJK COMPATIBILITY IDEOGRAPH-2F8A3
+ * 	6094 ??
+ * 2F8A4 CJK COMPATIBILITY IDEOGRAPH-2F8A4
+ * 	226D4 ??
+ * 2F8A5 CJK COMPATIBILITY IDEOGRAPH-2F8A5
+ * 	60C7 ??
+ * 2F8A6 CJK COMPATIBILITY IDEOGRAPH-2F8A6
+ * 	6148 ??
+ * 2F8A7 CJK COMPATIBILITY IDEOGRAPH-2F8A7
+ * 	614C ??
+ * 2F8A8 CJK COMPATIBILITY IDEOGRAPH-2F8A8
+ * 	614E ??
+ * 2F8A9 CJK COMPATIBILITY IDEOGRAPH-2F8A9
+ * 	614C ??
+ * 2F8AA CJK COMPATIBILITY IDEOGRAPH-2F8AA
+ * 	617A ??
+ * 2F8AB CJK COMPATIBILITY IDEOGRAPH-2F8AB
+ * 	618E ??
+ * 2F8AC CJK COMPATIBILITY IDEOGRAPH-2F8AC
+ * 	61B2 ??
+ * 2F8AD CJK COMPATIBILITY IDEOGRAPH-2F8AD
+ * 	61A4 ??
+ * 2F8AE CJK COMPATIBILITY IDEOGRAPH-2F8AE
+ * 	61AF ??
+ * 2F8AF CJK COMPATIBILITY IDEOGRAPH-2F8AF
+ * 	61DE ??
+ * 2F8B KANGXI RADICAL GRASS
+ * 	8278 ??
+ * 2F8B0 CJK COMPATIBILITY IDEOGRAPH-2F8B0
+ * 	61F2 ??
+ * 2F8B1 CJK COMPATIBILITY IDEOGRAPH-2F8B1
+ * 	61F6 ??
+ * 2F8B2 CJK COMPATIBILITY IDEOGRAPH-2F8B2
+ * 	6210 ??
+ * 2F8B3 CJK COMPATIBILITY IDEOGRAPH-2F8B3
+ * 	621B ??
+ * 2F8B4 CJK COMPATIBILITY IDEOGRAPH-2F8B4
+ * 	625D ??
+ * 2F8B5 CJK COMPATIBILITY IDEOGRAPH-2F8B5
+ * 	62B1 ??
+ * 2F8B6 CJK COMPATIBILITY IDEOGRAPH-2F8B6
+ * 	62D4 ??
+ * 2F8B7 CJK COMPATIBILITY IDEOGRAPH-2F8B7
+ * 	6350 ??
+ * 2F8B8 CJK COMPATIBILITY IDEOGRAPH-2F8B8
+ * 	22B0C ??
+ * 2F8B9 CJK COMPATIBILITY IDEOGRAPH-2F8B9
+ * 	633D ??
+ * 2F8BA CJK COMPATIBILITY IDEOGRAPH-2F8BA
+ * 	62FC ??
+ * 2F8BB CJK COMPATIBILITY IDEOGRAPH-2F8BB
+ * 	6368 ??
+ * 2F8BC CJK COMPATIBILITY IDEOGRAPH-2F8BC
+ * 	6383 ??
+ * 2F8BD CJK COMPATIBILITY IDEOGRAPH-2F8BD
+ * 	63E4 ??
+ * 2F8BE CJK COMPATIBILITY IDEOGRAPH-2F8BE
+ * 	22BF1 ??
+ * 2F8BF CJK COMPATIBILITY IDEOGRAPH-2F8BF
+ * 	6422 ??
+ * 2F8C KANGXI RADICAL TIGER
+ * 	864D ??
+ * 2F8C0 CJK COMPATIBILITY IDEOGRAPH-2F8C0
+ * 	63C5 ??
+ * 2F8C1 CJK COMPATIBILITY IDEOGRAPH-2F8C1
+ * 	63A9 ??
+ * 2F8C2 CJK COMPATIBILITY IDEOGRAPH-2F8C2
+ * 	3A2E ??
+ * 2F8C3 CJK COMPATIBILITY IDEOGRAPH-2F8C3
+ * 	6469 ??
+ * 2F8C4 CJK COMPATIBILITY IDEOGRAPH-2F8C4
+ * 	647E ??
+ * 2F8C5 CJK COMPATIBILITY IDEOGRAPH-2F8C5
+ * 	649D ??
+ * 2F8C6 CJK COMPATIBILITY IDEOGRAPH-2F8C6
+ * 	6477 ??
+ * 2F8C7 CJK COMPATIBILITY IDEOGRAPH-2F8C7
+ * 	3A6C ??
+ * 2F8C8 CJK COMPATIBILITY IDEOGRAPH-2F8C8
+ * 	654F ??
+ * 2F8C9 CJK COMPATIBILITY IDEOGRAPH-2F8C9
+ * 	656C ??
+ * 2F8CA CJK COMPATIBILITY IDEOGRAPH-2F8CA
+ * 	2300A ??
+ * 2F8CB CJK COMPATIBILITY IDEOGRAPH-2F8CB
+ * 	65E3 ??
+ * 2F8CC CJK COMPATIBILITY IDEOGRAPH-2F8CC
+ * 	66F8 ??
+ * 2F8CD CJK COMPATIBILITY IDEOGRAPH-2F8CD
+ * 	6649 ??
+ * 2F8CE CJK COMPATIBILITY IDEOGRAPH-2F8CE
+ * 	3B19 ??
+ * 2F8CF CJK COMPATIBILITY IDEOGRAPH-2F8CF
+ * 	6691 ??
+ * 2F8D KANGXI RADICAL INSECT
+ * 	866B ??
+ * 2F8D0 CJK COMPATIBILITY IDEOGRAPH-2F8D0
+ * 	3B08 ??
+ * 2F8D1 CJK COMPATIBILITY IDEOGRAPH-2F8D1
+ * 	3AE4 ??
+ * 2F8D2 CJK COMPATIBILITY IDEOGRAPH-2F8D2
+ * 	5192 ??
+ * 2F8D3 CJK COMPATIBILITY IDEOGRAPH-2F8D3
+ * 	5195 ??
+ * 2F8D4 CJK COMPATIBILITY IDEOGRAPH-2F8D4
+ * 	6700 ??
+ * 2F8D5 CJK COMPATIBILITY IDEOGRAPH-2F8D5
+ * 	669C ??
+ * 2F8D6 CJK COMPATIBILITY IDEOGRAPH-2F8D6
+ * 	80AD ??
+ * 2F8D7 CJK COMPATIBILITY IDEOGRAPH-2F8D7
+ * 	43D9 ??
+ * 2F8D8 CJK COMPATIBILITY IDEOGRAPH-2F8D8
+ * 	6717 ??
+ * 2F8D9 CJK COMPATIBILITY IDEOGRAPH-2F8D9
+ * 	671B ??
+ * 2F8DA CJK COMPATIBILITY IDEOGRAPH-2F8DA
+ * 	6721 ??
+ * 2F8DB CJK COMPATIBILITY IDEOGRAPH-2F8DB
+ * 	675E ??
+ * 2F8DC CJK COMPATIBILITY IDEOGRAPH-2F8DC
+ * 	6753 ??
+ * 2F8DD CJK COMPATIBILITY IDEOGRAPH-2F8DD
+ * 	233C3 ??
+ * 2F8DE CJK COMPATIBILITY IDEOGRAPH-2F8DE
+ * 	3B49 ??
+ * 2F8DF CJK COMPATIBILITY IDEOGRAPH-2F8DF
+ * 	67FA ??
+ * 2F8E KANGXI RADICAL BLOOD
+ * 	8840 ??
+ * 2F8E0 CJK COMPATIBILITY IDEOGRAPH-2F8E0
+ * 	6785 ??
+ * 2F8E1 CJK COMPATIBILITY IDEOGRAPH-2F8E1
+ * 	6852 ??
+ * 2F8E2 CJK COMPATIBILITY IDEOGRAPH-2F8E2
+ * 	6885 ??
+ * 2F8E3 CJK COMPATIBILITY IDEOGRAPH-2F8E3
+ * 	2346D ??
+ * 2F8E4 CJK COMPATIBILITY IDEOGRAPH-2F8E4
+ * 	688E ??
+ * 2F8E5 CJK COMPATIBILITY IDEOGRAPH-2F8E5
+ * 	681F ??
+ * 2F8E6 CJK COMPATIBILITY IDEOGRAPH-2F8E6
+ * 	6914 ??
+ * 2F8E7 CJK COMPATIBILITY IDEOGRAPH-2F8E7
+ * 	3B9D ??
+ * 2F8E8 CJK COMPATIBILITY IDEOGRAPH-2F8E8
+ * 	6942 ??
+ * 2F8E9 CJK COMPATIBILITY IDEOGRAPH-2F8E9
+ * 	69A3 ??
+ * 2F8EA CJK COMPATIBILITY IDEOGRAPH-2F8EA
+ * 	69EA ??
+ * 2F8EB CJK COMPATIBILITY IDEOGRAPH-2F8EB
+ * 	6AA8 ??
+ * 2F8EC CJK COMPATIBILITY IDEOGRAPH-2F8EC
+ * 	236A3 ??
+ * 2F8ED CJK COMPATIBILITY IDEOGRAPH-2F8ED
+ * 	6ADB ??
+ * 2F8EE CJK COMPATIBILITY IDEOGRAPH-2F8EE
+ * 	3C18 ??
+ * 2F8EF CJK COMPATIBILITY IDEOGRAPH-2F8EF
+ * 	6B21 ??
+ * 2F8F KANGXI RADICAL WALK ENCLOSURE
+ * 	884C ??
+ * 2F8F0 CJK COMPATIBILITY IDEOGRAPH-2F8F0
+ * 	238A7 ??
+ * 2F8F1 CJK COMPATIBILITY IDEOGRAPH-2F8F1
+ * 	6B54 ??
+ * 2F8F2 CJK COMPATIBILITY IDEOGRAPH-2F8F2
+ * 	3C4E ??
+ * 2F8F3 CJK COMPATIBILITY IDEOGRAPH-2F8F3
+ * 	6B72 ??
+ * 2F8F4 CJK COMPATIBILITY IDEOGRAPH-2F8F4
+ * 	6B9F ??
+ * 2F8F5 CJK COMPATIBILITY IDEOGRAPH-2F8F5
+ * 	6BBA ??
+ * 2F8F6 CJK COMPATIBILITY IDEOGRAPH-2F8F6
+ * 	6BBB ??
+ * 2F8F7 CJK COMPATIBILITY IDEOGRAPH-2F8F7
+ * 	23A8D ??
+ * 2F8F8 CJK COMPATIBILITY IDEOGRAPH-2F8F8
+ * 	21D0B ??
+ * 2F8F9 CJK COMPATIBILITY IDEOGRAPH-2F8F9
+ * 	23AFA ??
+ * 2F8FA CJK COMPATIBILITY IDEOGRAPH-2F8FA
+ * 	6C4E ??
+ * 2F8FB CJK COMPATIBILITY IDEOGRAPH-2F8FB
+ * 	23CBC ??
+ * 2F8FC CJK COMPATIBILITY IDEOGRAPH-2F8FC
+ * 	6CBF ??
+ * 2F8FD CJK COMPATIBILITY IDEOGRAPH-2F8FD
+ * 	6CCD ??
+ * 2F8FE CJK COMPATIBILITY IDEOGRAPH-2F8FE
+ * 	6C67 ??
+ * 2F8FF CJK COMPATIBILITY IDEOGRAPH-2F8FF
+ * 	6D16 ??
+ * 2F90 KANGXI RADICAL CLOTHES
+ * 	8863 ??
+ * 2F900 CJK COMPATIBILITY IDEOGRAPH-2F900
+ * 	6D3E ??
+ * 2F901 CJK COMPATIBILITY IDEOGRAPH-2F901
+ * 	6D77 ??
+ * 2F902 CJK COMPATIBILITY IDEOGRAPH-2F902
+ * 	6D41 ??
+ * 2F903 CJK COMPATIBILITY IDEOGRAPH-2F903
+ * 	6D69 ??
+ * 2F904 CJK COMPATIBILITY IDEOGRAPH-2F904
+ * 	6D78 ??
+ * 2F905 CJK COMPATIBILITY IDEOGRAPH-2F905
+ * 	6D85 ??
+ * 2F906 CJK COMPATIBILITY IDEOGRAPH-2F906
+ * 	23D1E ??
+ * 2F907 CJK COMPATIBILITY IDEOGRAPH-2F907
+ * 	6D34 ??
+ * 2F908 CJK COMPATIBILITY IDEOGRAPH-2F908
+ * 	6E2F ??
+ * 2F909 CJK COMPATIBILITY IDEOGRAPH-2F909
+ * 	6E6E ??
+ * 2F90A CJK COMPATIBILITY IDEOGRAPH-2F90A
+ * 	3D33 ??
+ * 2F90B CJK COMPATIBILITY IDEOGRAPH-2F90B
+ * 	6ECB ??
+ * 2F90C CJK COMPATIBILITY IDEOGRAPH-2F90C
+ * 	6EC7 ??
+ * 2F90D CJK COMPATIBILITY IDEOGRAPH-2F90D
+ * 	23ED1 ??
+ * 2F90E CJK COMPATIBILITY IDEOGRAPH-2F90E
+ * 	6DF9 ??
+ * 2F90F CJK COMPATIBILITY IDEOGRAPH-2F90F
+ * 	6F6E ??
+ * 2F91 KANGXI RADICAL WEST
+ * 	897E ??
+ * 2F910 CJK COMPATIBILITY IDEOGRAPH-2F910
+ * 	23F5E ??
+ * 2F911 CJK COMPATIBILITY IDEOGRAPH-2F911
+ * 	23F8E ??
+ * 2F912 CJK COMPATIBILITY IDEOGRAPH-2F912
+ * 	6FC6 ??
+ * 2F913 CJK COMPATIBILITY IDEOGRAPH-2F913
+ * 	7039 ??
+ * 2F914 CJK COMPATIBILITY IDEOGRAPH-2F914
+ * 	701E ??
+ * 2F915 CJK COMPATIBILITY IDEOGRAPH-2F915
+ * 	701B ??
+ * 2F916 CJK COMPATIBILITY IDEOGRAPH-2F916
+ * 	3D96 ??
+ * 2F917 CJK COMPATIBILITY IDEOGRAPH-2F917
+ * 	704A ??
+ * 2F918 CJK COMPATIBILITY IDEOGRAPH-2F918
+ * 	707D ??
+ * 2F919 CJK COMPATIBILITY IDEOGRAPH-2F919
+ * 	7077 ??
+ * 2F91A CJK COMPATIBILITY IDEOGRAPH-2F91A
+ * 	70AD ??
+ * 2F91B CJK COMPATIBILITY IDEOGRAPH-2F91B
+ * 	20525 ??
+ * 2F91C CJK COMPATIBILITY IDEOGRAPH-2F91C
+ * 	7145 ??
+ * 2F91D CJK COMPATIBILITY IDEOGRAPH-2F91D
+ * 	24263 ??
+ * 2F91E CJK COMPATIBILITY IDEOGRAPH-2F91E
+ * 	719C ??
+ * 2F91F CJK COMPATIBILITY IDEOGRAPH-2F91F
+ * 	43AB ??
+ * 2F92 KANGXI RADICAL SEE
+ * 	898B ??
+ * 2F920 CJK COMPATIBILITY IDEOGRAPH-2F920
+ * 	7228 ??
+ * 2F921 CJK COMPATIBILITY IDEOGRAPH-2F921
+ * 	7235 ??
+ * 2F922 CJK COMPATIBILITY IDEOGRAPH-2F922
+ * 	7250 ??
+ * 2F923 CJK COMPATIBILITY IDEOGRAPH-2F923
+ * 	24608 ??
+ * 2F924 CJK COMPATIBILITY IDEOGRAPH-2F924
+ * 	7280 ??
+ * 2F925 CJK COMPATIBILITY IDEOGRAPH-2F925
+ * 	7295 ??
+ * 2F926 CJK COMPATIBILITY IDEOGRAPH-2F926
+ * 	24735 ??
+ * 2F927 CJK COMPATIBILITY IDEOGRAPH-2F927
+ * 	24814 ??
+ * 2F928 CJK COMPATIBILITY IDEOGRAPH-2F928
+ * 	737A ??
+ * 2F929 CJK COMPATIBILITY IDEOGRAPH-2F929
+ * 	738B ??
+ * 2F92A CJK COMPATIBILITY IDEOGRAPH-2F92A
+ * 	3EAC ??
+ * 2F92B CJK COMPATIBILITY IDEOGRAPH-2F92B
+ * 	73A5 ??
+ * 2F92C CJK COMPATIBILITY IDEOGRAPH-2F92C
+ * 	3EB8 ??
+ * 2F92D CJK COMPATIBILITY IDEOGRAPH-2F92D
+ * 	3EB8 ??
+ * 2F92E CJK COMPATIBILITY IDEOGRAPH-2F92E
+ * 	7447 ??
+ * 2F92F CJK COMPATIBILITY IDEOGRAPH-2F92F
+ * 	745C ??
+ * 2F93 KANGXI RADICAL HORN
+ * 	89D2 ??
+ * 2F930 CJK COMPATIBILITY IDEOGRAPH-2F930
+ * 	7471 ??
+ * 2F931 CJK COMPATIBILITY IDEOGRAPH-2F931
+ * 	7485 ??
+ * 2F932 CJK COMPATIBILITY IDEOGRAPH-2F932
+ * 	74CA ??
+ * 2F933 CJK COMPATIBILITY IDEOGRAPH-2F933
+ * 	3F1B ??
+ * 2F934 CJK COMPATIBILITY IDEOGRAPH-2F934
+ * 	7524 ??
+ * 2F935 CJK COMPATIBILITY IDEOGRAPH-2F935
+ * 	24C36 ??
+ * 2F936 CJK COMPATIBILITY IDEOGRAPH-2F936
+ * 	753E ??
+ * 2F937 CJK COMPATIBILITY IDEOGRAPH-2F937
+ * 	24C92 ??
+ * 2F938 CJK COMPATIBILITY IDEOGRAPH-2F938
+ * 	7570 ??
+ * 2F939 CJK COMPATIBILITY IDEOGRAPH-2F939
+ * 	2219F ??
+ * 2F93A CJK COMPATIBILITY IDEOGRAPH-2F93A
+ * 	7610 ??
+ * 2F93B CJK COMPATIBILITY IDEOGRAPH-2F93B
+ * 	24FA1 ??
+ * 2F93C CJK COMPATIBILITY IDEOGRAPH-2F93C
+ * 	24FB8 ??
+ * 2F93D CJK COMPATIBILITY IDEOGRAPH-2F93D
+ * 	25044 ??
+ * 2F93E CJK COMPATIBILITY IDEOGRAPH-2F93E
+ * 	3FFC ??
+ * 2F93F CJK COMPATIBILITY IDEOGRAPH-2F93F
+ * 	4008 ??
+ * 2F94 KANGXI RADICAL SPEECH
+ * 	8A00 ??
+ * 2F940 CJK COMPATIBILITY IDEOGRAPH-2F940
+ * 	76F4 ??
+ * 2F941 CJK COMPATIBILITY IDEOGRAPH-2F941
+ * 	250F3 ??
+ * 2F942 CJK COMPATIBILITY IDEOGRAPH-2F942
+ * 	250F2 ??
+ * 2F943 CJK COMPATIBILITY IDEOGRAPH-2F943
+ * 	25119 ??
+ * 2F944 CJK COMPATIBILITY IDEOGRAPH-2F944
+ * 	25133 ??
+ * 2F945 CJK COMPATIBILITY IDEOGRAPH-2F945
+ * 	771E ??
+ * 2F946 CJK COMPATIBILITY IDEOGRAPH-2F946
+ * 	771F ??
+ * 2F947 CJK COMPATIBILITY IDEOGRAPH-2F947
+ * 	771F ??
+ * 2F948 CJK COMPATIBILITY IDEOGRAPH-2F948
+ * 	774A ??
+ * 2F949 CJK COMPATIBILITY IDEOGRAPH-2F949
+ * 	4039 ??
+ * 2F94A CJK COMPATIBILITY IDEOGRAPH-2F94A
+ * 	778B ??
+ * 2F94B CJK COMPATIBILITY IDEOGRAPH-2F94B
+ * 	4046 ??
+ * 2F94C CJK COMPATIBILITY IDEOGRAPH-2F94C
+ * 	4096 ??
+ * 2F94D CJK COMPATIBILITY IDEOGRAPH-2F94D
+ * 	2541D ??
+ * 2F94E CJK COMPATIBILITY IDEOGRAPH-2F94E
+ * 	784E ??
+ * 2F94F CJK COMPATIBILITY IDEOGRAPH-2F94F
+ * 	788C ??
+ * 2F95 KANGXI RADICAL VALLEY
+ * 	8C37 ??
+ * 2F950 CJK COMPATIBILITY IDEOGRAPH-2F950
+ * 	78CC ??
+ * 2F951 CJK COMPATIBILITY IDEOGRAPH-2F951
+ * 	40E3 ??
+ * 2F952 CJK COMPATIBILITY IDEOGRAPH-2F952
+ * 	25626 ??
+ * 2F953 CJK COMPATIBILITY IDEOGRAPH-2F953
+ * 	7956 ??
+ * 2F954 CJK COMPATIBILITY IDEOGRAPH-2F954
+ * 	2569A ??
+ * 2F955 CJK COMPATIBILITY IDEOGRAPH-2F955
+ * 	256C5 ??
+ * 2F956 CJK COMPATIBILITY IDEOGRAPH-2F956
+ * 	798F ??
+ * 2F957 CJK COMPATIBILITY IDEOGRAPH-2F957
+ * 	79EB ??
+ * 2F958 CJK COMPATIBILITY IDEOGRAPH-2F958
+ * 	412F ??
+ * 2F959 CJK COMPATIBILITY IDEOGRAPH-2F959
+ * 	7A40 ??
+ * 2F95A CJK COMPATIBILITY IDEOGRAPH-2F95A
+ * 	7A4A ??
+ * 2F95B CJK COMPATIBILITY IDEOGRAPH-2F95B
+ * 	7A4F ??
+ * 2F95C CJK COMPATIBILITY IDEOGRAPH-2F95C
+ * 	2597C ??
+ * 2F95D CJK COMPATIBILITY IDEOGRAPH-2F95D
+ * 	25AA7 ??
+ * 2F95E CJK COMPATIBILITY IDEOGRAPH-2F95E
+ * 	25AA7 ??
+ * 2F95F CJK COMPATIBILITY IDEOGRAPH-2F95F
+ * 	7AAE ??
+ * 2F96 KANGXI RADICAL BEAN
+ * 	8C46 ??
+ * 2F960 CJK COMPATIBILITY IDEOGRAPH-2F960
+ * 	4202 ??
+ * 2F961 CJK COMPATIBILITY IDEOGRAPH-2F961
+ * 	25BAB ??
+ * 2F962 CJK COMPATIBILITY IDEOGRAPH-2F962
+ * 	7BC6 ??
+ * 2F963 CJK COMPATIBILITY IDEOGRAPH-2F963
+ * 	7BC9 ??
+ * 2F964 CJK COMPATIBILITY IDEOGRAPH-2F964
+ * 	4227 ??
+ * 2F965 CJK COMPATIBILITY IDEOGRAPH-2F965
+ * 	25C80 ??
+ * 2F966 CJK COMPATIBILITY IDEOGRAPH-2F966
+ * 	7CD2 ??
+ * 2F967 CJK COMPATIBILITY IDEOGRAPH-2F967
+ * 	42A0 ??
+ * 2F968 CJK COMPATIBILITY IDEOGRAPH-2F968
+ * 	7CE8 ??
+ * 2F969 CJK COMPATIBILITY IDEOGRAPH-2F969
+ * 	7CE3 ??
+ * 2F96A CJK COMPATIBILITY IDEOGRAPH-2F96A
+ * 	7D00 ??
+ * 2F96B CJK COMPATIBILITY IDEOGRAPH-2F96B
+ * 	25F86 ??
+ * 2F96C CJK COMPATIBILITY IDEOGRAPH-2F96C
+ * 	7D63 ??
+ * 2F96D CJK COMPATIBILITY IDEOGRAPH-2F96D
+ * 	4301 ??
+ * 2F96E CJK COMPATIBILITY IDEOGRAPH-2F96E
+ * 	7DC7 ??
+ * 2F96F CJK COMPATIBILITY IDEOGRAPH-2F96F
+ * 	7E02 ??
+ * 2F97 KANGXI RADICAL PIG
+ * 	8C55 ??
+ * 2F970 CJK COMPATIBILITY IDEOGRAPH-2F970
+ * 	7E45 ??
+ * 2F971 CJK COMPATIBILITY IDEOGRAPH-2F971
+ * 	4334 ??
+ * 2F972 CJK COMPATIBILITY IDEOGRAPH-2F972
+ * 	26228 ??
+ * 2F973 CJK COMPATIBILITY IDEOGRAPH-2F973
+ * 	26247 ??
+ * 2F974 CJK COMPATIBILITY IDEOGRAPH-2F974
+ * 	4359 ??
+ * 2F975 CJK COMPATIBILITY IDEOGRAPH-2F975
+ * 	262D9 ??
+ * 2F976 CJK COMPATIBILITY IDEOGRAPH-2F976
+ * 	7F7A ??
+ * 2F977 CJK COMPATIBILITY IDEOGRAPH-2F977
+ * 	2633E ??
+ * 2F978 CJK COMPATIBILITY IDEOGRAPH-2F978
+ * 	7F95 ??
+ * 2F979 CJK COMPATIBILITY IDEOGRAPH-2F979
+ * 	7FFA ??
+ * 2F97A CJK COMPATIBILITY IDEOGRAPH-2F97A
+ * 	8005 ??
+ * 2F97B CJK COMPATIBILITY IDEOGRAPH-2F97B
+ * 	264DA ??
+ * 2F97C CJK COMPATIBILITY IDEOGRAPH-2F97C
+ * 	26523 ??
+ * 2F97D CJK COMPATIBILITY IDEOGRAPH-2F97D
+ * 	8060 ??
+ * 2F97E CJK COMPATIBILITY IDEOGRAPH-2F97E
+ * 	265A8 ??
+ * 2F97F CJK COMPATIBILITY IDEOGRAPH-2F97F
+ * 	8070 ??
+ * 2F98 KANGXI RADICAL BADGER
+ * 	8C78 ??
+ * 2F980 CJK COMPATIBILITY IDEOGRAPH-2F980
+ * 	2335F ??
+ * 2F981 CJK COMPATIBILITY IDEOGRAPH-2F981
+ * 	43D5 ??
+ * 2F982 CJK COMPATIBILITY IDEOGRAPH-2F982
+ * 	80B2 ??
+ * 2F983 CJK COMPATIBILITY IDEOGRAPH-2F983
+ * 	8103 ??
+ * 2F984 CJK COMPATIBILITY IDEOGRAPH-2F984
+ * 	440B ??
+ * 2F985 CJK COMPATIBILITY IDEOGRAPH-2F985
+ * 	813E ??
+ * 2F986 CJK COMPATIBILITY IDEOGRAPH-2F986
+ * 	5AB5 ??
+ * 2F987 CJK COMPATIBILITY IDEOGRAPH-2F987
+ * 	267A7 ??
+ * 2F988 CJK COMPATIBILITY IDEOGRAPH-2F988
+ * 	267B5 ??
+ * 2F989 CJK COMPATIBILITY IDEOGRAPH-2F989
+ * 	23393 ??
+ * 2F98A CJK COMPATIBILITY IDEOGRAPH-2F98A
+ * 	2339C ??
+ * 2F98B CJK COMPATIBILITY IDEOGRAPH-2F98B
+ * 	8201 ??
+ * 2F98C CJK COMPATIBILITY IDEOGRAPH-2F98C
+ * 	8204 ??
+ * 2F98D CJK COMPATIBILITY IDEOGRAPH-2F98D
+ * 	8F9E ??
+ * 2F98E CJK COMPATIBILITY IDEOGRAPH-2F98E
+ * 	446B ??
+ * 2F98F CJK COMPATIBILITY IDEOGRAPH-2F98F
+ * 	8291 ??
+ * 2F99 KANGXI RADICAL SHELL
+ * 	8C9D ??
+ * 2F990 CJK COMPATIBILITY IDEOGRAPH-2F990
+ * 	828B ??
+ * 2F991 CJK COMPATIBILITY IDEOGRAPH-2F991
+ * 	829D ??
+ * 2F992 CJK COMPATIBILITY IDEOGRAPH-2F992
+ * 	52B3 ??
+ * 2F993 CJK COMPATIBILITY IDEOGRAPH-2F993
+ * 	82B1 ??
+ * 2F994 CJK COMPATIBILITY IDEOGRAPH-2F994
+ * 	82B3 ??
+ * 2F995 CJK COMPATIBILITY IDEOGRAPH-2F995
+ * 	82BD ??
+ * 2F996 CJK COMPATIBILITY IDEOGRAPH-2F996
+ * 	82E6 ??
+ * 2F997 CJK COMPATIBILITY IDEOGRAPH-2F997
+ * 	26B3C ??
+ * 2F998 CJK COMPATIBILITY IDEOGRAPH-2F998
+ * 	82E5 ??
+ * 2F999 CJK COMPATIBILITY IDEOGRAPH-2F999
+ * 	831D ??
+ * 2F99A CJK COMPATIBILITY IDEOGRAPH-2F99A
+ * 	8363 ??
+ * 2F99B CJK COMPATIBILITY IDEOGRAPH-2F99B
+ * 	83AD ??
+ * 2F99C CJK COMPATIBILITY IDEOGRAPH-2F99C
+ * 	8323 ??
+ * 2F99D CJK COMPATIBILITY IDEOGRAPH-2F99D
+ * 	83BD ??
+ * 2F99E CJK COMPATIBILITY IDEOGRAPH-2F99E
+ * 	83E7 ??
+ * 2F99F CJK COMPATIBILITY IDEOGRAPH-2F99F
+ * 	8457 ??
+ * 2F9A KANGXI RADICAL RED
+ * 	8D64 ??
+ * 2F9A0 CJK COMPATIBILITY IDEOGRAPH-2F9A0
+ * 	8353 ??
+ * 2F9A1 CJK COMPATIBILITY IDEOGRAPH-2F9A1
+ * 	83CA ??
+ * 2F9A2 CJK COMPATIBILITY IDEOGRAPH-2F9A2
+ * 	83CC ??
+ * 2F9A3 CJK COMPATIBILITY IDEOGRAPH-2F9A3
+ * 	83DC ??
+ * 2F9A4 CJK COMPATIBILITY IDEOGRAPH-2F9A4
+ * 	26C36 ??
+ * 2F9A5 CJK COMPATIBILITY IDEOGRAPH-2F9A5
+ * 	26D6B ??
+ * 2F9A6 CJK COMPATIBILITY IDEOGRAPH-2F9A6
+ * 	26CD5 ??
+ * 2F9A7 CJK COMPATIBILITY IDEOGRAPH-2F9A7
+ * 	452B ??
+ * 2F9A8 CJK COMPATIBILITY IDEOGRAPH-2F9A8
+ * 	84F1 ??
+ * 2F9A9 CJK COMPATIBILITY IDEOGRAPH-2F9A9
+ * 	84F3 ??
+ * 2F9AA CJK COMPATIBILITY IDEOGRAPH-2F9AA
+ * 	8516 ??
+ * 2F9AB CJK COMPATIBILITY IDEOGRAPH-2F9AB
+ * 	273CA ??
+ * 2F9AC CJK COMPATIBILITY IDEOGRAPH-2F9AC
+ * 	8564 ??
+ * 2F9AD CJK COMPATIBILITY IDEOGRAPH-2F9AD
+ * 	26F2C ??
+ * 2F9AE CJK COMPATIBILITY IDEOGRAPH-2F9AE
+ * 	455D ??
+ * 2F9AF CJK COMPATIBILITY IDEOGRAPH-2F9AF
+ * 	4561 ??
+ * 2F9B KANGXI RADICAL RUN
+ * 	8D70 ??
+ * 2F9B0 CJK COMPATIBILITY IDEOGRAPH-2F9B0
+ * 	26FB1 ??
+ * 2F9B1 CJK COMPATIBILITY IDEOGRAPH-2F9B1
+ * 	270D2 ??
+ * 2F9B2 CJK COMPATIBILITY IDEOGRAPH-2F9B2
+ * 	456B ??
+ * 2F9B3 CJK COMPATIBILITY IDEOGRAPH-2F9B3
+ * 	8650 ??
+ * 2F9B4 CJK COMPATIBILITY IDEOGRAPH-2F9B4
+ * 	865C ??
+ * 2F9B5 CJK COMPATIBILITY IDEOGRAPH-2F9B5
+ * 	8667 ??
+ * 2F9B6 CJK COMPATIBILITY IDEOGRAPH-2F9B6
+ * 	8669 ??
+ * 2F9B7 CJK COMPATIBILITY IDEOGRAPH-2F9B7
+ * 	86A9 ??
+ * 2F9B8 CJK COMPATIBILITY IDEOGRAPH-2F9B8
+ * 	8688 ??
+ * 2F9B9 CJK COMPATIBILITY IDEOGRAPH-2F9B9
+ * 	870E ??
+ * 2F9BA CJK COMPATIBILITY IDEOGRAPH-2F9BA
+ * 	86E2 ??
+ * 2F9BB CJK COMPATIBILITY IDEOGRAPH-2F9BB
+ * 	8779 ??
+ * 2F9BC CJK COMPATIBILITY IDEOGRAPH-2F9BC
+ * 	8728 ??
+ * 2F9BD CJK COMPATIBILITY IDEOGRAPH-2F9BD
+ * 	876B ??
+ * 2F9BE CJK COMPATIBILITY IDEOGRAPH-2F9BE
+ * 	8786 ??
+ * 2F9BF CJK COMPATIBILITY IDEOGRAPH-2F9BF
+ * 	4D57 ??
+ * 2F9C KANGXI RADICAL FOOT
+ * 	8DB3 ??
+ * 2F9C0 CJK COMPATIBILITY IDEOGRAPH-2F9C0
+ * 	87E1 ??
+ * 2F9C1 CJK COMPATIBILITY IDEOGRAPH-2F9C1
+ * 	8801 ??
+ * 2F9C2 CJK COMPATIBILITY IDEOGRAPH-2F9C2
+ * 	45F9 ??
+ * 2F9C3 CJK COMPATIBILITY IDEOGRAPH-2F9C3
+ * 	8860 ??
+ * 2F9C4 CJK COMPATIBILITY IDEOGRAPH-2F9C4
+ * 	8863 ??
+ * 2F9C5 CJK COMPATIBILITY IDEOGRAPH-2F9C5
+ * 	27667 ??
+ * 2F9C6 CJK COMPATIBILITY IDEOGRAPH-2F9C6
+ * 	88D7 ??
+ * 2F9C7 CJK COMPATIBILITY IDEOGRAPH-2F9C7
+ * 	88DE ??
+ * 2F9C8 CJK COMPATIBILITY IDEOGRAPH-2F9C8
+ * 	4635 ??
+ * 2F9C9 CJK COMPATIBILITY IDEOGRAPH-2F9C9
+ * 	88FA ??
+ * 2F9CA CJK COMPATIBILITY IDEOGRAPH-2F9CA
+ * 	34BB ??
+ * 2F9CB CJK COMPATIBILITY IDEOGRAPH-2F9CB
+ * 	278AE ??
+ * 2F9CC CJK COMPATIBILITY IDEOGRAPH-2F9CC
+ * 	27966 ??
+ * 2F9CD CJK COMPATIBILITY IDEOGRAPH-2F9CD
+ * 	46BE ??
+ * 2F9CE CJK COMPATIBILITY IDEOGRAPH-2F9CE
+ * 	46C7 ??
+ * 2F9CF CJK COMPATIBILITY IDEOGRAPH-2F9CF
+ * 	8AA0 ??
+ * 2F9D KANGXI RADICAL BODY
+ * 	8EAB ??
+ * 2F9D0 CJK COMPATIBILITY IDEOGRAPH-2F9D0
+ * 	8AED ??
+ * 2F9D1 CJK COMPATIBILITY IDEOGRAPH-2F9D1
+ * 	8B8A ??
+ * 2F9D2 CJK COMPATIBILITY IDEOGRAPH-2F9D2
+ * 	8C55 ??
+ * 2F9D3 CJK COMPATIBILITY IDEOGRAPH-2F9D3
+ * 	27CA8 ??
+ * 2F9D4 CJK COMPATIBILITY IDEOGRAPH-2F9D4
+ * 	8CAB ??
+ * 2F9D5 CJK COMPATIBILITY IDEOGRAPH-2F9D5
+ * 	8CC1 ??
+ * 2F9D6 CJK COMPATIBILITY IDEOGRAPH-2F9D6
+ * 	8D1B ??
+ * 2F9D7 CJK COMPATIBILITY IDEOGRAPH-2F9D7
+ * 	8D77 ??
+ * 2F9D8 CJK COMPATIBILITY IDEOGRAPH-2F9D8
+ * 	27F2F ??
+ * 2F9D9 CJK COMPATIBILITY IDEOGRAPH-2F9D9
+ * 	20804 ??
+ * 2F9DA CJK COMPATIBILITY IDEOGRAPH-2F9DA
+ * 	8DCB ??
+ * 2F9DB CJK COMPATIBILITY IDEOGRAPH-2F9DB
+ * 	8DBC ??
+ * 2F9DC CJK COMPATIBILITY IDEOGRAPH-2F9DC
+ * 	8DF0 ??
+ * 2F9DD CJK COMPATIBILITY IDEOGRAPH-2F9DD
+ * 	208DE ??
+ * 2F9DE CJK COMPATIBILITY IDEOGRAPH-2F9DE
+ * 	8ED4 ??
+ * 2F9DF CJK COMPATIBILITY IDEOGRAPH-2F9DF
+ * 	8F38 ??
+ * 2F9E KANGXI RADICAL CART
+ * 	8ECA ??
+ * 2F9E0 CJK COMPATIBILITY IDEOGRAPH-2F9E0
+ * 	285D2 ??
+ * 2F9E1 CJK COMPATIBILITY IDEOGRAPH-2F9E1
+ * 	285ED ??
+ * 2F9E2 CJK COMPATIBILITY IDEOGRAPH-2F9E2
+ * 	9094 ??
+ * 2F9E3 CJK COMPATIBILITY IDEOGRAPH-2F9E3
+ * 	90F1 ??
+ * 2F9E4 CJK COMPATIBILITY IDEOGRAPH-2F9E4
+ * 	9111 ??
+ * 2F9E5 CJK COMPATIBILITY IDEOGRAPH-2F9E5
+ * 	2872E ??
+ * 2F9E6 CJK COMPATIBILITY IDEOGRAPH-2F9E6
+ * 	911B ??
+ * 2F9E7 CJK COMPATIBILITY IDEOGRAPH-2F9E7
+ * 	9238 ??
+ * 2F9E8 CJK COMPATIBILITY IDEOGRAPH-2F9E8
+ * 	92D7 ??
+ * 2F9E9 CJK COMPATIBILITY IDEOGRAPH-2F9E9
+ * 	92D8 ??
+ * 2F9EA CJK COMPATIBILITY IDEOGRAPH-2F9EA
+ * 	927C ??
+ * 2F9EB CJK COMPATIBILITY IDEOGRAPH-2F9EB
+ * 	93F9 ??
+ * 2F9EC CJK COMPATIBILITY IDEOGRAPH-2F9EC
+ * 	9415 ??
+ * 2F9ED CJK COMPATIBILITY IDEOGRAPH-2F9ED
+ * 	28BFA ??
+ * 2F9EE CJK COMPATIBILITY IDEOGRAPH-2F9EE
+ * 	958B ??
+ * 2F9EF CJK COMPATIBILITY IDEOGRAPH-2F9EF
+ * 	4995 ??
+ * 2F9F KANGXI RADICAL BITTER
+ * 	8F9B ??
+ * 2F9F0 CJK COMPATIBILITY IDEOGRAPH-2F9F0
+ * 	95B7 ??
+ * 2F9F1 CJK COMPATIBILITY IDEOGRAPH-2F9F1
+ * 	28D77 ??
+ * 2F9F2 CJK COMPATIBILITY IDEOGRAPH-2F9F2
+ * 	49E6 ??
+ * 2F9F3 CJK COMPATIBILITY IDEOGRAPH-2F9F3
+ * 	96C3 ??
+ * 2F9F4 CJK COMPATIBILITY IDEOGRAPH-2F9F4
+ * 	5DB2 ??
+ * 2F9F5 CJK COMPATIBILITY IDEOGRAPH-2F9F5
+ * 	9723 ??
+ * 2F9F6 CJK COMPATIBILITY IDEOGRAPH-2F9F6
+ * 	29145 ??
+ * 2F9F7 CJK COMPATIBILITY IDEOGRAPH-2F9F7
+ * 	2921A ??
+ * 2F9F8 CJK COMPATIBILITY IDEOGRAPH-2F9F8
+ * 	4A6E ??
+ * 2F9F9 CJK COMPATIBILITY IDEOGRAPH-2F9F9
+ * 	4A76 ??
+ * 2F9FA CJK COMPATIBILITY IDEOGRAPH-2F9FA
+ * 	97E0 ??
+ * 2F9FB CJK COMPATIBILITY IDEOGRAPH-2F9FB
+ * 	2940A ??
+ * 2F9FC CJK COMPATIBILITY IDEOGRAPH-2F9FC
+ * 	4AB2 ??
+ * 2F9FD CJK COMPATIBILITY IDEOGRAPH-2F9FD
+ * 	29496 ??
+ * 2F9FE CJK COMPATIBILITY IDEOGRAPH-2F9FE
+ * 	980B ??
+ * 2F9FF CJK COMPATIBILITY IDEOGRAPH-2F9FF
+ * 	980B ??
+ * 2FA0 KANGXI RADICAL MORNING
+ * 	8FB0 ??
+ * 2FA00 CJK COMPATIBILITY IDEOGRAPH-2FA00
+ * 	9829 ??
+ * 2FA01 CJK COMPATIBILITY IDEOGRAPH-2FA01
+ * 	295B6 ??
+ * 2FA02 CJK COMPATIBILITY IDEOGRAPH-2FA02
+ * 	98E2 ??
+ * 2FA03 CJK COMPATIBILITY IDEOGRAPH-2FA03
+ * 	4B33 ??
+ * 2FA04 CJK COMPATIBILITY IDEOGRAPH-2FA04
+ * 	9929 ??
+ * 2FA05 CJK COMPATIBILITY IDEOGRAPH-2FA05
+ * 	99A7 ??
+ * 2FA06 CJK COMPATIBILITY IDEOGRAPH-2FA06
+ * 	99C2 ??
+ * 2FA07 CJK COMPATIBILITY IDEOGRAPH-2FA07
+ * 	99FE ??
+ * 2FA08 CJK COMPATIBILITY IDEOGRAPH-2FA08
+ * 	4BCE ??
+ * 2FA09 CJK COMPATIBILITY IDEOGRAPH-2FA09
+ * 	29B30 ??
+ * 2FA0A CJK COMPATIBILITY IDEOGRAPH-2FA0A
+ * 	9B12 ??
+ * 2FA0B CJK COMPATIBILITY IDEOGRAPH-2FA0B
+ * 	9C40 ??
+ * 2FA0C CJK COMPATIBILITY IDEOGRAPH-2FA0C
+ * 	9CFD ??
+ * 2FA0D CJK COMPATIBILITY IDEOGRAPH-2FA0D
+ * 	4CCE ??
+ * 2FA0E CJK COMPATIBILITY IDEOGRAPH-2FA0E
+ * 	4CED ??
+ * 2FA0F CJK COMPATIBILITY IDEOGRAPH-2FA0F
+ * 	9D67 ??
+ * 2FA1 KANGXI RADICAL WALK
+ * 	8FB5 ??
+ * 2FA10 CJK COMPATIBILITY IDEOGRAPH-2FA10
+ * 	2A0CE ??
+ * 2FA11 CJK COMPATIBILITY IDEOGRAPH-2FA11
+ * 	4CF8 ??
+ * 2FA12 CJK COMPATIBILITY IDEOGRAPH-2FA12
+ * 	2A105 ??
+ * 2FA13 CJK COMPATIBILITY IDEOGRAPH-2FA13
+ * 	2A20E ??
+ * 2FA14 CJK COMPATIBILITY IDEOGRAPH-2FA14
+ * 	2A291 ??
+ * 2FA15 CJK COMPATIBILITY IDEOGRAPH-2FA15
+ * 	9EBB ??
+ * 2FA16 CJK COMPATIBILITY IDEOGRAPH-2FA16
+ * 	4D56 ??
+ * 2FA17 CJK COMPATIBILITY IDEOGRAPH-2FA17
+ * 	9EF9 ??
+ * 2FA18 CJK COMPATIBILITY IDEOGRAPH-2FA18
+ * 	9EFE ??
+ * 2FA19 CJK COMPATIBILITY IDEOGRAPH-2FA19
+ * 	9F05 ??
+ * 2FA1A CJK COMPATIBILITY IDEOGRAPH-2FA1A
+ * 	9F0F ??
+ * 2FA1B CJK COMPATIBILITY IDEOGRAPH-2FA1B
+ * 	9F16 ??
+ * 2FA1C CJK COMPATIBILITY IDEOGRAPH-2FA1C
+ * 	9F3B ??
+ * 2FA1D CJK COMPATIBILITY IDEOGRAPH-2FA1D
+ * 	2A600 ??
+ * 2FA2 KANGXI RADICAL CITY
+ * 	9091 ??
+ * 2FA3 KANGXI RADICAL WINE
+ * 	9149 ??
+ * 2FA4 KANGXI RADICAL DISTINGUISH
+ * 	91C6 ??
+ * 2FA5 KANGXI RADICAL VILLAGE
+ * 	91CC ??
+ * 2FA6 KANGXI RADICAL GOLD
+ * 	91D1 ??
+ * 2FA7 KANGXI RADICAL LONG
+ * 	9577 ??
+ * 2FA8 KANGXI RADICAL GATE
+ * 	9580 ??
+ * 2FA9 KANGXI RADICAL MOUND
+ * 	961C ??
+ * 2FAA KANGXI RADICAL SLAVE
+ * 	96B6 ??
+ * 2FAB KANGXI RADICAL SHORT TAILED BIRD
+ * 	96B9 ??
+ * 2FAC KANGXI RADICAL RAIN
+ * 	96E8 ??
+ * 2FAD KANGXI RADICAL BLUE
+ * 	9751 ??
+ * 2FAE KANGXI RADICAL WRONG
+ * 	975E ??
+ * 2FAF KANGXI RADICAL FACE
+ * 	9762 ??
+ * 2FB0 KANGXI RADICAL LEATHER
+ * 	9769 ??
+ * 2FB1 KANGXI RADICAL TANNED LEATHER
+ * 	97CB ??
+ * 2FB2 KANGXI RADICAL LEEK
+ * 	97ED ??
+ * 2FB3 KANGXI RADICAL SOUND
+ * 	97F3 ??
+ * 2FB4 KANGXI RADICAL LEAF
+ * 	9801 ??
+ * 2FB5 KANGXI RADICAL WIND
+ * 	98A8 ??
+ * 2FB6 KANGXI RADICAL FLY
+ * 	98DB ??
+ * 2FB7 KANGXI RADICAL EAT
+ * 	98DF ??
+ * 2FB8 KANGXI RADICAL HEAD
+ * 	9996 ??
+ * 2FB9 KANGXI RADICAL FRAGRANT
+ * 	9999 ??
+ * 2FBA KANGXI RADICAL HORSE
+ * 	99AC ??
+ * 2FBB KANGXI RADICAL BONE
+ * 	9AA8 ??
+ * 2FBC KANGXI RADICAL TALL
+ * 	9AD8 ??
+ * 2FBD KANGXI RADICAL HAIR
+ * 	9ADF ??
+ * 2FBE KANGXI RADICAL FIGHT
+ * 	9B25 ??
+ * 2FBF KANGXI RADICAL SACRIFICIAL WINE
+ * 	9B2F ??
+ * 2FC0 KANGXI RADICAL CAULDRON
+ * 	9B32 ??
+ * 2FC1 KANGXI RADICAL GHOST
+ * 	9B3C ??
+ * 2FC2 KANGXI RADICAL FISH
+ * 	9B5A ??
+ * 2FC3 KANGXI RADICAL BIRD
+ * 	9CE5 ??
+ * 2FC4 KANGXI RADICAL SALT
+ * 	9E75 ??
+ * 2FC5 KANGXI RADICAL DEER
+ * 	9E7F ??
+ * 2FC6 KANGXI RADICAL WHEAT
+ * 	9EA5 ??
+ * 2FC7 KANGXI RADICAL HEMP
+ * 	9EBB ??
+ * 2FC8 KANGXI RADICAL YELLOW
+ * 	9EC3 ??
+ * 2FC9 KANGXI RADICAL MILLET
+ * 	9ECD ??
+ * 2FCA KANGXI RADICAL BLACK
+ * 	9ED1 ??
+ * 2FCB KANGXI RADICAL EMBROIDERY
+ * 	9EF9 ??
+ * 2FCC KANGXI RADICAL FROG
+ * 	9EFD ??
+ * 2FCD KANGXI RADICAL TRIPOD
+ * 	9F0E ??
+ * 2FCE KANGXI RADICAL DRUM
+ * 	9F13 ??
+ * 2FCF KANGXI RADICAL RAT
+ * 	9F20 ??
+ * 2FD0 KANGXI RADICAL NOSE
+ * 	9F3B ??
+ * 2FD1 KANGXI RADICAL EVEN
+ * 	9F4A ??
+ * 2FD2 KANGXI RADICAL TOOTH
+ * 	9F52 ??
+ * 2FD3 KANGXI RADICAL DRAGON
+ * 	9F8D ??
+ * 2FD4 KANGXI RADICAL TURTLE
+ * 	9F9C ??
+ * 2FD5 KANGXI RADICAL FLUTE
+ * 	9FA0 ??
+ * 3000 IDEOGRAPHIC SPACE
+ * 	0020 SPACE
+ * 3036 CIRCLED POSTAL MARK
+ * 	3012 POSTAL MARK
+ * 3038 HANGZHOU NUMERAL TEN
+ * 	5341 ??
+ * 3039 HANGZHOU NUMERAL TWENTY
+ * 	5344 ??
+ * 303A HANGZHOU NUMERAL THIRTY
+ * 	5345 ??
+ * 304C HIRAGANA LETTER GA
+ * 	304B HIRAGANA LETTER KA
+ * 304E HIRAGANA LETTER GI
+ * 	304D HIRAGANA LETTER KI
+ * 3050 HIRAGANA LETTER GU
+ * 	304F HIRAGANA LETTER KU
+ * 3052 HIRAGANA LETTER GE
+ * 	3051 HIRAGANA LETTER KE
+ * 3054 HIRAGANA LETTER GO
+ * 	3053 HIRAGANA LETTER KO
+ * 3056 HIRAGANA LETTER ZA
+ * 	3055 HIRAGANA LETTER SA
+ * 3058 HIRAGANA LETTER ZI
+ * 	3057 HIRAGANA LETTER SI
+ * 305A HIRAGANA LETTER ZU
+ * 	3059 HIRAGANA LETTER SU
+ * 305C HIRAGANA LETTER ZE
+ * 	305B HIRAGANA LETTER SE
+ * 305E HIRAGANA LETTER ZO
+ * 	305D HIRAGANA LETTER SO
+ * 3060 HIRAGANA LETTER DA
+ * 	305F HIRAGANA LETTER TA
+ * 3062 HIRAGANA LETTER DI
+ * 	3061 HIRAGANA LETTER TI
+ * 3065 HIRAGANA LETTER DU
+ * 	3064 HIRAGANA LETTER TU
+ * 3067 HIRAGANA LETTER DE
+ * 	3066 HIRAGANA LETTER TE
+ * 3069 HIRAGANA LETTER DO
+ * 	3068 HIRAGANA LETTER TO
+ * 3070 HIRAGANA LETTER BA
+ * 	306F HIRAGANA LETTER HA
+ * 3071 HIRAGANA LETTER PA
+ * 	306F HIRAGANA LETTER HA
+ * 3073 HIRAGANA LETTER BI
+ * 	3072 HIRAGANA LETTER HI
+ * 3074 HIRAGANA LETTER PI
+ * 	3072 HIRAGANA LETTER HI
+ * 3076 HIRAGANA LETTER BU
+ * 	3075 HIRAGANA LETTER HU
+ * 3077 HIRAGANA LETTER PU
+ * 	3075 HIRAGANA LETTER HU
+ * 3079 HIRAGANA LETTER BE
+ * 	3078 HIRAGANA LETTER HE
+ * 307A HIRAGANA LETTER PE
+ * 	3078 HIRAGANA LETTER HE
+ * 307C HIRAGANA LETTER BO
+ * 	307B HIRAGANA LETTER HO
+ * 307D HIRAGANA LETTER PO
+ * 	307B HIRAGANA LETTER HO
+ * 3094 HIRAGANA LETTER VU
+ * 	3046 HIRAGANA LETTER U
+ * 309B KATAKANA-HIRAGANA VOICED SOUND MARK
+ * 	0020 SPACE
+ * 309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ * 	0020 SPACE
+ * 309E HIRAGANA VOICED ITERATION MARK
+ * 	309D HIRAGANA ITERATION MARK
+ * 309F HIRAGANA DIGRAPH YORI
+ * 	3088 HIRAGANA LETTER YO
+ * 	308A HIRAGANA LETTER RI
+ * 30AC KATAKANA LETTER GA
+ * 	30AB KATAKANA LETTER KA
+ * 30AE KATAKANA LETTER GI
+ * 	30AD KATAKANA LETTER KI
+ * 30B0 KATAKANA LETTER GU
+ * 	30AF KATAKANA LETTER KU
+ * 30B2 KATAKANA LETTER GE
+ * 	30B1 KATAKANA LETTER KE
+ * 30B4 KATAKANA LETTER GO
+ * 	30B3 KATAKANA LETTER KO
+ * 30B6 KATAKANA LETTER ZA
+ * 	30B5 KATAKANA LETTER SA
+ * 30B8 KATAKANA LETTER ZI
+ * 	30B7 KATAKANA LETTER SI
+ * 30BA KATAKANA LETTER ZU
+ * 	30B9 KATAKANA LETTER SU
+ * 30BC KATAKANA LETTER ZE
+ * 	30BB KATAKANA LETTER SE
+ * 30BE KATAKANA LETTER ZO
+ * 	30BD KATAKANA LETTER SO
+ * 30C0 KATAKANA LETTER DA
+ * 	30BF KATAKANA LETTER TA
+ * 30C2 KATAKANA LETTER DI
+ * 	30C1 KATAKANA LETTER TI
+ * 30C5 KATAKANA LETTER DU
+ * 	30C4 KATAKANA LETTER TU
+ * 30C7 KATAKANA LETTER DE
+ * 	30C6 KATAKANA LETTER TE
+ * 30C9 KATAKANA LETTER DO
+ * 	30C8 KATAKANA LETTER TO
+ * 30D0 KATAKANA LETTER BA
+ * 	30CF KATAKANA LETTER HA
+ * 30D1 KATAKANA LETTER PA
+ * 	30CF KATAKANA LETTER HA
+ * 30D3 KATAKANA LETTER BI
+ * 	30D2 KATAKANA LETTER HI
+ * 30D4 KATAKANA LETTER PI
+ * 	30D2 KATAKANA LETTER HI
+ * 30D6 KATAKANA LETTER BU
+ * 	30D5 KATAKANA LETTER HU
+ * 30D7 KATAKANA LETTER PU
+ * 	30D5 KATAKANA LETTER HU
+ * 30D9 KATAKANA LETTER BE
+ * 	30D8 KATAKANA LETTER HE
+ * 30DA KATAKANA LETTER PE
+ * 	30D8 KATAKANA LETTER HE
+ * 30DC KATAKANA LETTER BO
+ * 	30DB KATAKANA LETTER HO
+ * 30DD KATAKANA LETTER PO
+ * 	30DB KATAKANA LETTER HO
+ * 30F4 KATAKANA LETTER VU
+ * 	30A6 KATAKANA LETTER U
+ * 30F7 KATAKANA LETTER VA
+ * 	30EF KATAKANA LETTER WA
+ * 30F8 KATAKANA LETTER VI
+ * 	30F0 KATAKANA LETTER WI
+ * 30F9 KATAKANA LETTER VE
+ * 	30F1 KATAKANA LETTER WE
+ * 30FA KATAKANA LETTER VO
+ * 	30F2 KATAKANA LETTER WO
+ * 30FE KATAKANA VOICED ITERATION MARK
+ * 	30FD KATAKANA ITERATION MARK
+ * 30FF KATAKANA DIGRAPH KOTO
+ * 	30B3 KATAKANA LETTER KO
+ * 	30C8 KATAKANA LETTER TO
+ * 3131 HANGUL LETTER KIYEOK
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * 3132 HANGUL LETTER SSANGKIYEOK
+ * 	1101 HANGUL CHOSEONG SSANGKIYEOK
+ * 3133 HANGUL LETTER KIYEOK-SIOS
+ * 	11AA HANGUL JONGSEONG KIYEOK-SIOS
+ * 3134 HANGUL LETTER NIEUN
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * 3135 HANGUL LETTER NIEUN-CIEUC
+ * 	11AC HANGUL JONGSEONG NIEUN-CIEUC
+ * 3136 HANGUL LETTER NIEUN-HIEUH
+ * 	11AD HANGUL JONGSEONG NIEUN-HIEUH
+ * 3137 HANGUL LETTER TIKEUT
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * 3138 HANGUL LETTER SSANGTIKEUT
+ * 	1104 HANGUL CHOSEONG SSANGTIKEUT
+ * 3139 HANGUL LETTER RIEUL
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * 313A HANGUL LETTER RIEUL-KIYEOK
+ * 	11B0 HANGUL JONGSEONG RIEUL-KIYEOK
+ * 313B HANGUL LETTER RIEUL-MIEUM
+ * 	11B1 HANGUL JONGSEONG RIEUL-MIEUM
+ * 313C HANGUL LETTER RIEUL-PIEUP
+ * 	11B2 HANGUL JONGSEONG RIEUL-PIEUP
+ * 313D HANGUL LETTER RIEUL-SIOS
+ * 	11B3 HANGUL JONGSEONG RIEUL-SIOS
+ * 313E HANGUL LETTER RIEUL-THIEUTH
+ * 	11B4 HANGUL JONGSEONG RIEUL-THIEUTH
+ * 313F HANGUL LETTER RIEUL-PHIEUPH
+ * 	11B5 HANGUL JONGSEONG RIEUL-PHIEUPH
+ * 3140 HANGUL LETTER RIEUL-HIEUH
+ * 	111A HANGUL CHOSEONG RIEUL-HIEUH
+ * 3141 HANGUL LETTER MIEUM
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * 3142 HANGUL LETTER PIEUP
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * 3143 HANGUL LETTER SSANGPIEUP
+ * 	1108 HANGUL CHOSEONG SSANGPIEUP
+ * 3144 HANGUL LETTER PIEUP-SIOS
+ * 	1121 HANGUL CHOSEONG PIEUP-SIOS
+ * 3145 HANGUL LETTER SIOS
+ * 	1109 HANGUL CHOSEONG SIOS
+ * 3146 HANGUL LETTER SSANGSIOS
+ * 	110A HANGUL CHOSEONG SSANGSIOS
+ * 3147 HANGUL LETTER IEUNG
+ * 	110B HANGUL CHOSEONG IEUNG
+ * 3148 HANGUL LETTER CIEUC
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 3149 HANGUL LETTER SSANGCIEUC
+ * 	110D HANGUL CHOSEONG SSANGCIEUC
+ * 314A HANGUL LETTER CHIEUCH
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * 314B HANGUL LETTER KHIEUKH
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * 314C HANGUL LETTER THIEUTH
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * 314D HANGUL LETTER PHIEUPH
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * 314E HANGUL LETTER HIEUH
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * 314F HANGUL LETTER A
+ * 	1161 HANGUL JUNGSEONG A
+ * 3150 HANGUL LETTER AE
+ * 	1162 HANGUL JUNGSEONG AE
+ * 3151 HANGUL LETTER YA
+ * 	1163 HANGUL JUNGSEONG YA
+ * 3152 HANGUL LETTER YAE
+ * 	1164 HANGUL JUNGSEONG YAE
+ * 3153 HANGUL LETTER EO
+ * 	1165 HANGUL JUNGSEONG EO
+ * 3154 HANGUL LETTER E
+ * 	1166 HANGUL JUNGSEONG E
+ * 3155 HANGUL LETTER YEO
+ * 	1167 HANGUL JUNGSEONG YEO
+ * 3156 HANGUL LETTER YE
+ * 	1168 HANGUL JUNGSEONG YE
+ * 3157 HANGUL LETTER O
+ * 	1169 HANGUL JUNGSEONG O
+ * 3158 HANGUL LETTER WA
+ * 	116A HANGUL JUNGSEONG WA
+ * 3159 HANGUL LETTER WAE
+ * 	116B HANGUL JUNGSEONG WAE
+ * 315A HANGUL LETTER OE
+ * 	116C HANGUL JUNGSEONG OE
+ * 315B HANGUL LETTER YO
+ * 	116D HANGUL JUNGSEONG YO
+ * 315C HANGUL LETTER U
+ * 	116E HANGUL JUNGSEONG U
+ * 315D HANGUL LETTER WEO
+ * 	116F HANGUL JUNGSEONG WEO
+ * 315E HANGUL LETTER WE
+ * 	1170 HANGUL JUNGSEONG WE
+ * 315F HANGUL LETTER WI
+ * 	1171 HANGUL JUNGSEONG WI
+ * 3160 HANGUL LETTER YU
+ * 	1172 HANGUL JUNGSEONG YU
+ * 3161 HANGUL LETTER EU
+ * 	1173 HANGUL JUNGSEONG EU
+ * 3162 HANGUL LETTER YI
+ * 	1174 HANGUL JUNGSEONG YI
+ * 3163 HANGUL LETTER I
+ * 	1175 HANGUL JUNGSEONG I
+ * 3164 HANGUL FILLER
+ * 	1160 HANGUL JUNGSEONG FILLER
+ * 3165 HANGUL LETTER SSANGNIEUN
+ * 	1114 HANGUL CHOSEONG SSANGNIEUN
+ * 3166 HANGUL LETTER NIEUN-TIKEUT
+ * 	1115 HANGUL CHOSEONG NIEUN-TIKEUT
+ * 3167 HANGUL LETTER NIEUN-SIOS
+ * 	11C7 HANGUL JONGSEONG NIEUN-SIOS
+ * 3168 HANGUL LETTER NIEUN-PANSIOS
+ * 	11C8 HANGUL JONGSEONG NIEUN-PANSIOS
+ * 3169 HANGUL LETTER RIEUL-KIYEOK-SIOS
+ * 	11CC HANGUL JONGSEONG RIEUL-KIYEOK-SIOS
+ * 316A HANGUL LETTER RIEUL-TIKEUT
+ * 	11CE HANGUL JONGSEONG RIEUL-TIKEUT
+ * 316B HANGUL LETTER RIEUL-PIEUP-SIOS
+ * 	11D3 HANGUL JONGSEONG RIEUL-PIEUP-SIOS
+ * 316C HANGUL LETTER RIEUL-PANSIOS
+ * 	11D7 HANGUL JONGSEONG RIEUL-PANSIOS
+ * 316D HANGUL LETTER RIEUL-YEORINHIEUH
+ * 	11D9 HANGUL JONGSEONG RIEUL-YEORINHIEUH
+ * 316E HANGUL LETTER MIEUM-PIEUP
+ * 	111C HANGUL CHOSEONG MIEUM-PIEUP
+ * 316F HANGUL LETTER MIEUM-SIOS
+ * 	11DD HANGUL JONGSEONG MIEUM-SIOS
+ * 3170 HANGUL LETTER MIEUM-PANSIOS
+ * 	11DF HANGUL JONGSEONG MIEUM-PANSIOS
+ * 3171 HANGUL LETTER KAPYEOUNMIEUM
+ * 	111D HANGUL CHOSEONG KAPYEOUNMIEUM
+ * 3172 HANGUL LETTER PIEUP-KIYEOK
+ * 	111E HANGUL CHOSEONG PIEUP-KIYEOK
+ * 3173 HANGUL LETTER PIEUP-TIKEUT
+ * 	1120 HANGUL CHOSEONG PIEUP-TIKEUT
+ * 3174 HANGUL LETTER PIEUP-SIOS-KIYEOK
+ * 	1122 HANGUL CHOSEONG PIEUP-SIOS-KIYEOK
+ * 3175 HANGUL LETTER PIEUP-SIOS-TIKEUT
+ * 	1123 HANGUL CHOSEONG PIEUP-SIOS-TIKEUT
+ * 3176 HANGUL LETTER PIEUP-CIEUC
+ * 	1127 HANGUL CHOSEONG PIEUP-CIEUC
+ * 3177 HANGUL LETTER PIEUP-THIEUTH
+ * 	1129 HANGUL CHOSEONG PIEUP-THIEUTH
+ * 3178 HANGUL LETTER KAPYEOUNPIEUP
+ * 	112B HANGUL CHOSEONG KAPYEOUNPIEUP
+ * 3179 HANGUL LETTER KAPYEOUNSSANGPIEUP
+ * 	112C HANGUL CHOSEONG KAPYEOUNSSANGPIEUP
+ * 317A HANGUL LETTER SIOS-KIYEOK
+ * 	112D HANGUL CHOSEONG SIOS-KIYEOK
+ * 317B HANGUL LETTER SIOS-NIEUN
+ * 	112E HANGUL CHOSEONG SIOS-NIEUN
+ * 317C HANGUL LETTER SIOS-TIKEUT
+ * 	112F HANGUL CHOSEONG SIOS-TIKEUT
+ * 317D HANGUL LETTER SIOS-PIEUP
+ * 	1132 HANGUL CHOSEONG SIOS-PIEUP
+ * 317E HANGUL LETTER SIOS-CIEUC
+ * 	1136 HANGUL CHOSEONG SIOS-CIEUC
+ * 317F HANGUL LETTER PANSIOS
+ * 	1140 HANGUL CHOSEONG PANSIOS
+ * 3180 HANGUL LETTER SSANGIEUNG
+ * 	1147 HANGUL CHOSEONG SSANGIEUNG
+ * 3181 HANGUL LETTER YESIEUNG
+ * 	114C HANGUL CHOSEONG YESIEUNG
+ * 3182 HANGUL LETTER YESIEUNG-SIOS
+ * 	11F1 HANGUL JONGSEONG YESIEUNG-SIOS
+ * 3183 HANGUL LETTER YESIEUNG-PANSIOS
+ * 	11F2 HANGUL JONGSEONG YESIEUNG-PANSIOS
+ * 3184 HANGUL LETTER KAPYEOUNPHIEUPH
+ * 	1157 HANGUL CHOSEONG KAPYEOUNPHIEUPH
+ * 3185 HANGUL LETTER SSANGHIEUH
+ * 	1158 HANGUL CHOSEONG SSANGHIEUH
+ * 3186 HANGUL LETTER YEORINHIEUH
+ * 	1159 HANGUL CHOSEONG YEORINHIEUH
+ * 3187 HANGUL LETTER YO-YA
+ * 	1184 HANGUL JUNGSEONG YO-YA
+ * 3188 HANGUL LETTER YO-YAE
+ * 	1185 HANGUL JUNGSEONG YO-YAE
+ * 3189 HANGUL LETTER YO-I
+ * 	1188 HANGUL JUNGSEONG YO-I
+ * 318A HANGUL LETTER YU-YEO
+ * 	1191 HANGUL JUNGSEONG YU-YEO
+ * 318B HANGUL LETTER YU-YE
+ * 	1192 HANGUL JUNGSEONG YU-YE
+ * 318C HANGUL LETTER YU-I
+ * 	1194 HANGUL JUNGSEONG YU-I
+ * 318D HANGUL LETTER ARAEA
+ * 	119E HANGUL JUNGSEONG ARAEA
+ * 318E HANGUL LETTER ARAEAE
+ * 	11A1 HANGUL JUNGSEONG ARAEA-I
+ * 3192 IDEOGRAPHIC ANNOTATION ONE MARK
+ * 	4E00 <CJK Ideograph, First>
+ * 3193 IDEOGRAPHIC ANNOTATION TWO MARK
+ * 	4E8C ??
+ * 3194 IDEOGRAPHIC ANNOTATION THREE MARK
+ * 	4E09 ??
+ * 3195 IDEOGRAPHIC ANNOTATION FOUR MARK
+ * 	56DB ??
+ * 3196 IDEOGRAPHIC ANNOTATION TOP MARK
+ * 	4E0A ??
+ * 3197 IDEOGRAPHIC ANNOTATION MIDDLE MARK
+ * 	4E2D ??
+ * 3198 IDEOGRAPHIC ANNOTATION BOTTOM MARK
+ * 	4E0B ??
+ * 3199 IDEOGRAPHIC ANNOTATION FIRST MARK
+ * 	7532 ??
+ * 319A IDEOGRAPHIC ANNOTATION SECOND MARK
+ * 	4E59 ??
+ * 319B IDEOGRAPHIC ANNOTATION THIRD MARK
+ * 	4E19 ??
+ * 319C IDEOGRAPHIC ANNOTATION FOURTH MARK
+ * 	4E01 ??
+ * 319D IDEOGRAPHIC ANNOTATION HEAVEN MARK
+ * 	5929 ??
+ * 319E IDEOGRAPHIC ANNOTATION EARTH MARK
+ * 	5730 ??
+ * 319F IDEOGRAPHIC ANNOTATION MAN MARK
+ * 	4EBA ??
+ * 3200 PARENTHESIZED HANGUL KIYEOK
+ * 	0028 LEFT PARENTHESIS
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * 	0029 RIGHT PARENTHESIS
+ * 3201 PARENTHESIZED HANGUL NIEUN
+ * 	0028 LEFT PARENTHESIS
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * 	0029 RIGHT PARENTHESIS
+ * 3202 PARENTHESIZED HANGUL TIKEUT
+ * 	0028 LEFT PARENTHESIS
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * 	0029 RIGHT PARENTHESIS
+ * 3203 PARENTHESIZED HANGUL RIEUL
+ * 	0028 LEFT PARENTHESIS
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * 	0029 RIGHT PARENTHESIS
+ * 3204 PARENTHESIZED HANGUL MIEUM
+ * 	0028 LEFT PARENTHESIS
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * 	0029 RIGHT PARENTHESIS
+ * 3205 PARENTHESIZED HANGUL PIEUP
+ * 	0028 LEFT PARENTHESIS
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * 	0029 RIGHT PARENTHESIS
+ * 3206 PARENTHESIZED HANGUL SIOS
+ * 	0028 LEFT PARENTHESIS
+ * 	1109 HANGUL CHOSEONG SIOS
+ * 	0029 RIGHT PARENTHESIS
+ * 3207 PARENTHESIZED HANGUL IEUNG
+ * 	0028 LEFT PARENTHESIS
+ * 	110B HANGUL CHOSEONG IEUNG
+ * 	0029 RIGHT PARENTHESIS
+ * 3208 PARENTHESIZED HANGUL CIEUC
+ * 	0028 LEFT PARENTHESIS
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 	0029 RIGHT PARENTHESIS
+ * 3209 PARENTHESIZED HANGUL CHIEUCH
+ * 	0028 LEFT PARENTHESIS
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * 	0029 RIGHT PARENTHESIS
+ * 320A PARENTHESIZED HANGUL KHIEUKH
+ * 	0028 LEFT PARENTHESIS
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * 	0029 RIGHT PARENTHESIS
+ * 320B PARENTHESIZED HANGUL THIEUTH
+ * 	0028 LEFT PARENTHESIS
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * 	0029 RIGHT PARENTHESIS
+ * 320C PARENTHESIZED HANGUL PHIEUPH
+ * 	0028 LEFT PARENTHESIS
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * 	0029 RIGHT PARENTHESIS
+ * 320D PARENTHESIZED HANGUL HIEUH
+ * 	0028 LEFT PARENTHESIS
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * 	0029 RIGHT PARENTHESIS
+ * 320E PARENTHESIZED HANGUL KIYEOK A
+ * 	0028 LEFT PARENTHESIS
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 320F PARENTHESIZED HANGUL NIEUN A
+ * 	0028 LEFT PARENTHESIS
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3210 PARENTHESIZED HANGUL TIKEUT A
+ * 	0028 LEFT PARENTHESIS
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3211 PARENTHESIZED HANGUL RIEUL A
+ * 	0028 LEFT PARENTHESIS
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3212 PARENTHESIZED HANGUL MIEUM A
+ * 	0028 LEFT PARENTHESIS
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3213 PARENTHESIZED HANGUL PIEUP A
+ * 	0028 LEFT PARENTHESIS
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3214 PARENTHESIZED HANGUL SIOS A
+ * 	0028 LEFT PARENTHESIS
+ * 	1109 HANGUL CHOSEONG SIOS
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3215 PARENTHESIZED HANGUL IEUNG A
+ * 	0028 LEFT PARENTHESIS
+ * 	110B HANGUL CHOSEONG IEUNG
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3216 PARENTHESIZED HANGUL CIEUC A
+ * 	0028 LEFT PARENTHESIS
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3217 PARENTHESIZED HANGUL CHIEUCH A
+ * 	0028 LEFT PARENTHESIS
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3218 PARENTHESIZED HANGUL KHIEUKH A
+ * 	0028 LEFT PARENTHESIS
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 3219 PARENTHESIZED HANGUL THIEUTH A
+ * 	0028 LEFT PARENTHESIS
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 321A PARENTHESIZED HANGUL PHIEUPH A
+ * 	0028 LEFT PARENTHESIS
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 321B PARENTHESIZED HANGUL HIEUH A
+ * 	0028 LEFT PARENTHESIS
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * 	1161 HANGUL JUNGSEONG A
+ * 	0029 RIGHT PARENTHESIS
+ * 321C PARENTHESIZED HANGUL CIEUC U
+ * 	0028 LEFT PARENTHESIS
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 	116E HANGUL JUNGSEONG U
+ * 	0029 RIGHT PARENTHESIS
+ * 3220 PARENTHESIZED IDEOGRAPH ONE
+ * 	0028 LEFT PARENTHESIS
+ * 	4E00 <CJK Ideograph, First>
+ * 	0029 RIGHT PARENTHESIS
+ * 3221 PARENTHESIZED IDEOGRAPH TWO
+ * 	0028 LEFT PARENTHESIS
+ * 	4E8C ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3222 PARENTHESIZED IDEOGRAPH THREE
+ * 	0028 LEFT PARENTHESIS
+ * 	4E09 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3223 PARENTHESIZED IDEOGRAPH FOUR
+ * 	0028 LEFT PARENTHESIS
+ * 	56DB ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3224 PARENTHESIZED IDEOGRAPH FIVE
+ * 	0028 LEFT PARENTHESIS
+ * 	4E94 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3225 PARENTHESIZED IDEOGRAPH SIX
+ * 	0028 LEFT PARENTHESIS
+ * 	516D ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3226 PARENTHESIZED IDEOGRAPH SEVEN
+ * 	0028 LEFT PARENTHESIS
+ * 	4E03 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3227 PARENTHESIZED IDEOGRAPH EIGHT
+ * 	0028 LEFT PARENTHESIS
+ * 	516B ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3228 PARENTHESIZED IDEOGRAPH NINE
+ * 	0028 LEFT PARENTHESIS
+ * 	4E5D ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3229 PARENTHESIZED IDEOGRAPH TEN
+ * 	0028 LEFT PARENTHESIS
+ * 	5341 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322A PARENTHESIZED IDEOGRAPH MOON
+ * 	0028 LEFT PARENTHESIS
+ * 	6708 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322B PARENTHESIZED IDEOGRAPH FIRE
+ * 	0028 LEFT PARENTHESIS
+ * 	706B ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322C PARENTHESIZED IDEOGRAPH WATER
+ * 	0028 LEFT PARENTHESIS
+ * 	6C34 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322D PARENTHESIZED IDEOGRAPH WOOD
+ * 	0028 LEFT PARENTHESIS
+ * 	6728 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322E PARENTHESIZED IDEOGRAPH METAL
+ * 	0028 LEFT PARENTHESIS
+ * 	91D1 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 322F PARENTHESIZED IDEOGRAPH EARTH
+ * 	0028 LEFT PARENTHESIS
+ * 	571F ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3230 PARENTHESIZED IDEOGRAPH SUN
+ * 	0028 LEFT PARENTHESIS
+ * 	65E5 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3231 PARENTHESIZED IDEOGRAPH STOCK
+ * 	0028 LEFT PARENTHESIS
+ * 	682A ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3232 PARENTHESIZED IDEOGRAPH HAVE
+ * 	0028 LEFT PARENTHESIS
+ * 	6709 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3233 PARENTHESIZED IDEOGRAPH SOCIETY
+ * 	0028 LEFT PARENTHESIS
+ * 	793E ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3234 PARENTHESIZED IDEOGRAPH NAME
+ * 	0028 LEFT PARENTHESIS
+ * 	540D ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3235 PARENTHESIZED IDEOGRAPH SPECIAL
+ * 	0028 LEFT PARENTHESIS
+ * 	7279 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3236 PARENTHESIZED IDEOGRAPH FINANCIAL
+ * 	0028 LEFT PARENTHESIS
+ * 	8CA1 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3237 PARENTHESIZED IDEOGRAPH CONGRATULATION
+ * 	0028 LEFT PARENTHESIS
+ * 	795D ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3238 PARENTHESIZED IDEOGRAPH LABOR
+ * 	0028 LEFT PARENTHESIS
+ * 	52B4 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3239 PARENTHESIZED IDEOGRAPH REPRESENT
+ * 	0028 LEFT PARENTHESIS
+ * 	4EE3 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323A PARENTHESIZED IDEOGRAPH CALL
+ * 	0028 LEFT PARENTHESIS
+ * 	547C ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323B PARENTHESIZED IDEOGRAPH STUDY
+ * 	0028 LEFT PARENTHESIS
+ * 	5B66 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323C PARENTHESIZED IDEOGRAPH SUPERVISE
+ * 	0028 LEFT PARENTHESIS
+ * 	76E3 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323D PARENTHESIZED IDEOGRAPH ENTERPRISE
+ * 	0028 LEFT PARENTHESIS
+ * 	4F01 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323E PARENTHESIZED IDEOGRAPH RESOURCE
+ * 	0028 LEFT PARENTHESIS
+ * 	8CC7 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 323F PARENTHESIZED IDEOGRAPH ALLIANCE
+ * 	0028 LEFT PARENTHESIS
+ * 	5354 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3240 PARENTHESIZED IDEOGRAPH FESTIVAL
+ * 	0028 LEFT PARENTHESIS
+ * 	796D ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3241 PARENTHESIZED IDEOGRAPH REST
+ * 	0028 LEFT PARENTHESIS
+ * 	4F11 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3242 PARENTHESIZED IDEOGRAPH SELF
+ * 	0028 LEFT PARENTHESIS
+ * 	81EA ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3243 PARENTHESIZED IDEOGRAPH REACH
+ * 	0028 LEFT PARENTHESIS
+ * 	81F3 ??
+ * 	0029 RIGHT PARENTHESIS
+ * 3251 CIRCLED NUMBER TWENTY ONE
+ * 	0032 DIGIT TWO
+ * 	0031 DIGIT ONE
+ * 3252 CIRCLED NUMBER TWENTY TWO
+ * 	0032 DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 3253 CIRCLED NUMBER TWENTY THREE
+ * 	0032 DIGIT TWO
+ * 	0033 DIGIT THREE
+ * 3254 CIRCLED NUMBER TWENTY FOUR
+ * 	0032 DIGIT TWO
+ * 	0034 DIGIT FOUR
+ * 3255 CIRCLED NUMBER TWENTY FIVE
+ * 	0032 DIGIT TWO
+ * 	0035 DIGIT FIVE
+ * 3256 CIRCLED NUMBER TWENTY SIX
+ * 	0032 DIGIT TWO
+ * 	0036 DIGIT SIX
+ * 3257 CIRCLED NUMBER TWENTY SEVEN
+ * 	0032 DIGIT TWO
+ * 	0037 DIGIT SEVEN
+ * 3258 CIRCLED NUMBER TWENTY EIGHT
+ * 	0032 DIGIT TWO
+ * 	0038 DIGIT EIGHT
+ * 3259 CIRCLED NUMBER TWENTY NINE
+ * 	0032 DIGIT TWO
+ * 	0039 DIGIT NINE
+ * 325A CIRCLED NUMBER THIRTY
+ * 	0033 DIGIT THREE
+ * 	0030 DIGIT ZERO
+ * 325B CIRCLED NUMBER THIRTY ONE
+ * 	0033 DIGIT THREE
+ * 	0031 DIGIT ONE
+ * 325C CIRCLED NUMBER THIRTY TWO
+ * 	0033 DIGIT THREE
+ * 	0032 DIGIT TWO
+ * 325D CIRCLED NUMBER THIRTY THREE
+ * 	0033 DIGIT THREE
+ * 	0033 DIGIT THREE
+ * 325E CIRCLED NUMBER THIRTY FOUR
+ * 	0033 DIGIT THREE
+ * 	0034 DIGIT FOUR
+ * 325F CIRCLED NUMBER THIRTY FIVE
+ * 	0033 DIGIT THREE
+ * 	0035 DIGIT FIVE
+ * 3260 CIRCLED HANGUL KIYEOK
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * 3261 CIRCLED HANGUL NIEUN
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * 3262 CIRCLED HANGUL TIKEUT
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * 3263 CIRCLED HANGUL RIEUL
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * 3264 CIRCLED HANGUL MIEUM
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * 3265 CIRCLED HANGUL PIEUP
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * 3266 CIRCLED HANGUL SIOS
+ * 	1109 HANGUL CHOSEONG SIOS
+ * 3267 CIRCLED HANGUL IEUNG
+ * 	110B HANGUL CHOSEONG IEUNG
+ * 3268 CIRCLED HANGUL CIEUC
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 3269 CIRCLED HANGUL CHIEUCH
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * 326A CIRCLED HANGUL KHIEUKH
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * 326B CIRCLED HANGUL THIEUTH
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * 326C CIRCLED HANGUL PHIEUPH
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * 326D CIRCLED HANGUL HIEUH
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * 326E CIRCLED HANGUL KIYEOK A
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * 	1161 HANGUL JUNGSEONG A
+ * 326F CIRCLED HANGUL NIEUN A
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * 	1161 HANGUL JUNGSEONG A
+ * 3270 CIRCLED HANGUL TIKEUT A
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * 	1161 HANGUL JUNGSEONG A
+ * 3271 CIRCLED HANGUL RIEUL A
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * 	1161 HANGUL JUNGSEONG A
+ * 3272 CIRCLED HANGUL MIEUM A
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * 	1161 HANGUL JUNGSEONG A
+ * 3273 CIRCLED HANGUL PIEUP A
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * 	1161 HANGUL JUNGSEONG A
+ * 3274 CIRCLED HANGUL SIOS A
+ * 	1109 HANGUL CHOSEONG SIOS
+ * 	1161 HANGUL JUNGSEONG A
+ * 3275 CIRCLED HANGUL IEUNG A
+ * 	110B HANGUL CHOSEONG IEUNG
+ * 	1161 HANGUL JUNGSEONG A
+ * 3276 CIRCLED HANGUL CIEUC A
+ * 	110C HANGUL CHOSEONG CIEUC
+ * 	1161 HANGUL JUNGSEONG A
+ * 3277 CIRCLED HANGUL CHIEUCH A
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * 	1161 HANGUL JUNGSEONG A
+ * 3278 CIRCLED HANGUL KHIEUKH A
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * 	1161 HANGUL JUNGSEONG A
+ * 3279 CIRCLED HANGUL THIEUTH A
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * 	1161 HANGUL JUNGSEONG A
+ * 327A CIRCLED HANGUL PHIEUPH A
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * 	1161 HANGUL JUNGSEONG A
+ * 327B CIRCLED HANGUL HIEUH A
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * 	1161 HANGUL JUNGSEONG A
+ * 3280 CIRCLED IDEOGRAPH ONE
+ * 	4E00 <CJK Ideograph, First>
+ * 3281 CIRCLED IDEOGRAPH TWO
+ * 	4E8C ??
+ * 3282 CIRCLED IDEOGRAPH THREE
+ * 	4E09 ??
+ * 3283 CIRCLED IDEOGRAPH FOUR
+ * 	56DB ??
+ * 3284 CIRCLED IDEOGRAPH FIVE
+ * 	4E94 ??
+ * 3285 CIRCLED IDEOGRAPH SIX
+ * 	516D ??
+ * 3286 CIRCLED IDEOGRAPH SEVEN
+ * 	4E03 ??
+ * 3287 CIRCLED IDEOGRAPH EIGHT
+ * 	516B ??
+ * 3288 CIRCLED IDEOGRAPH NINE
+ * 	4E5D ??
+ * 3289 CIRCLED IDEOGRAPH TEN
+ * 	5341 ??
+ * 328A CIRCLED IDEOGRAPH MOON
+ * 	6708 ??
+ * 328B CIRCLED IDEOGRAPH FIRE
+ * 	706B ??
+ * 328C CIRCLED IDEOGRAPH WATER
+ * 	6C34 ??
+ * 328D CIRCLED IDEOGRAPH WOOD
+ * 	6728 ??
+ * 328E CIRCLED IDEOGRAPH METAL
+ * 	91D1 ??
+ * 328F CIRCLED IDEOGRAPH EARTH
+ * 	571F ??
+ * 3290 CIRCLED IDEOGRAPH SUN
+ * 	65E5 ??
+ * 3291 CIRCLED IDEOGRAPH STOCK
+ * 	682A ??
+ * 3292 CIRCLED IDEOGRAPH HAVE
+ * 	6709 ??
+ * 3293 CIRCLED IDEOGRAPH SOCIETY
+ * 	793E ??
+ * 3294 CIRCLED IDEOGRAPH NAME
+ * 	540D ??
+ * 3295 CIRCLED IDEOGRAPH SPECIAL
+ * 	7279 ??
+ * 3296 CIRCLED IDEOGRAPH FINANCIAL
+ * 	8CA1 ??
+ * 3297 CIRCLED IDEOGRAPH CONGRATULATION
+ * 	795D ??
+ * 3298 CIRCLED IDEOGRAPH LABOR
+ * 	52B4 ??
+ * 3299 CIRCLED IDEOGRAPH SECRET
+ * 	79D8 ??
+ * 329A CIRCLED IDEOGRAPH MALE
+ * 	7537 ??
+ * 329B CIRCLED IDEOGRAPH FEMALE
+ * 	5973 ??
+ * 329C CIRCLED IDEOGRAPH SUITABLE
+ * 	9069 ??
+ * 329D CIRCLED IDEOGRAPH EXCELLENT
+ * 	512A ??
+ * 329E CIRCLED IDEOGRAPH PRINT
+ * 	5370 ??
+ * 329F CIRCLED IDEOGRAPH ATTENTION
+ * 	6CE8 ??
+ * 32A0 CIRCLED IDEOGRAPH ITEM
+ * 	9805 ??
+ * 32A1 CIRCLED IDEOGRAPH REST
+ * 	4F11 ??
+ * 32A2 CIRCLED IDEOGRAPH COPY
+ * 	5199 ??
+ * 32A3 CIRCLED IDEOGRAPH CORRECT
+ * 	6B63 ??
+ * 32A4 CIRCLED IDEOGRAPH HIGH
+ * 	4E0A ??
+ * 32A5 CIRCLED IDEOGRAPH CENTRE
+ * 	4E2D ??
+ * 32A6 CIRCLED IDEOGRAPH LOW
+ * 	4E0B ??
+ * 32A7 CIRCLED IDEOGRAPH LEFT
+ * 	5DE6 ??
+ * 32A8 CIRCLED IDEOGRAPH RIGHT
+ * 	53F3 ??
+ * 32A9 CIRCLED IDEOGRAPH MEDICINE
+ * 	533B ??
+ * 32AA CIRCLED IDEOGRAPH RELIGION
+ * 	5B97 ??
+ * 32AB CIRCLED IDEOGRAPH STUDY
+ * 	5B66 ??
+ * 32AC CIRCLED IDEOGRAPH SUPERVISE
+ * 	76E3 ??
+ * 32AD CIRCLED IDEOGRAPH ENTERPRISE
+ * 	4F01 ??
+ * 32AE CIRCLED IDEOGRAPH RESOURCE
+ * 	8CC7 ??
+ * 32AF CIRCLED IDEOGRAPH ALLIANCE
+ * 	5354 ??
+ * 32B0 CIRCLED IDEOGRAPH NIGHT
+ * 	591C ??
+ * 32B1 CIRCLED NUMBER THIRTY SIX
+ * 	0033 DIGIT THREE
+ * 	0036 DIGIT SIX
+ * 32B2 CIRCLED NUMBER THIRTY SEVEN
+ * 	0033 DIGIT THREE
+ * 	0037 DIGIT SEVEN
+ * 32B3 CIRCLED NUMBER THIRTY EIGHT
+ * 	0033 DIGIT THREE
+ * 	0038 DIGIT EIGHT
+ * 32B4 CIRCLED NUMBER THIRTY NINE
+ * 	0033 DIGIT THREE
+ * 	0039 DIGIT NINE
+ * 32B5 CIRCLED NUMBER FORTY
+ * 	0034 DIGIT FOUR
+ * 	0030 DIGIT ZERO
+ * 32B6 CIRCLED NUMBER FORTY ONE
+ * 	0034 DIGIT FOUR
+ * 	0031 DIGIT ONE
+ * 32B7 CIRCLED NUMBER FORTY TWO
+ * 	0034 DIGIT FOUR
+ * 	0032 DIGIT TWO
+ * 32B8 CIRCLED NUMBER FORTY THREE
+ * 	0034 DIGIT FOUR
+ * 	0033 DIGIT THREE
+ * 32B9 CIRCLED NUMBER FORTY FOUR
+ * 	0034 DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * 32BA CIRCLED NUMBER FORTY FIVE
+ * 	0034 DIGIT FOUR
+ * 	0035 DIGIT FIVE
+ * 32BB CIRCLED NUMBER FORTY SIX
+ * 	0034 DIGIT FOUR
+ * 	0036 DIGIT SIX
+ * 32BC CIRCLED NUMBER FORTY SEVEN
+ * 	0034 DIGIT FOUR
+ * 	0037 DIGIT SEVEN
+ * 32BD CIRCLED NUMBER FORTY EIGHT
+ * 	0034 DIGIT FOUR
+ * 	0038 DIGIT EIGHT
+ * 32BE CIRCLED NUMBER FORTY NINE
+ * 	0034 DIGIT FOUR
+ * 	0039 DIGIT NINE
+ * 32BF CIRCLED NUMBER FIFTY
+ * 	0035 DIGIT FIVE
+ * 	0030 DIGIT ZERO
+ * 32C0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY
+ * 	0031 DIGIT ONE
+ * 	6708 ??
+ * 32C1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY
+ * 	0032 DIGIT TWO
+ * 	6708 ??
+ * 32C2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH
+ * 	0033 DIGIT THREE
+ * 	6708 ??
+ * 32C3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL
+ * 	0034 DIGIT FOUR
+ * 	6708 ??
+ * 32C4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY
+ * 	0035 DIGIT FIVE
+ * 	6708 ??
+ * 32C5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE
+ * 	0036 DIGIT SIX
+ * 	6708 ??
+ * 32C6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY
+ * 	0037 DIGIT SEVEN
+ * 	6708 ??
+ * 32C7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST
+ * 	0038 DIGIT EIGHT
+ * 	6708 ??
+ * 32C8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER
+ * 	0039 DIGIT NINE
+ * 	6708 ??
+ * 32C9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 	6708 ??
+ * 32CA IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 	6708 ??
+ * 32CB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 	6708 ??
+ * 32D0 CIRCLED KATAKANA A
+ * 	30A2 KATAKANA LETTER A
+ * 32D1 CIRCLED KATAKANA I
+ * 	30A4 KATAKANA LETTER I
+ * 32D2 CIRCLED KATAKANA U
+ * 	30A6 KATAKANA LETTER U
+ * 32D3 CIRCLED KATAKANA E
+ * 	30A8 KATAKANA LETTER E
+ * 32D4 CIRCLED KATAKANA O
+ * 	30AA KATAKANA LETTER O
+ * 32D5 CIRCLED KATAKANA KA
+ * 	30AB KATAKANA LETTER KA
+ * 32D6 CIRCLED KATAKANA KI
+ * 	30AD KATAKANA LETTER KI
+ * 32D7 CIRCLED KATAKANA KU
+ * 	30AF KATAKANA LETTER KU
+ * 32D8 CIRCLED KATAKANA KE
+ * 	30B1 KATAKANA LETTER KE
+ * 32D9 CIRCLED KATAKANA KO
+ * 	30B3 KATAKANA LETTER KO
+ * 32DA CIRCLED KATAKANA SA
+ * 	30B5 KATAKANA LETTER SA
+ * 32DB CIRCLED KATAKANA SI
+ * 	30B7 KATAKANA LETTER SI
+ * 32DC CIRCLED KATAKANA SU
+ * 	30B9 KATAKANA LETTER SU
+ * 32DD CIRCLED KATAKANA SE
+ * 	30BB KATAKANA LETTER SE
+ * 32DE CIRCLED KATAKANA SO
+ * 	30BD KATAKANA LETTER SO
+ * 32DF CIRCLED KATAKANA TA
+ * 	30BF KATAKANA LETTER TA
+ * 32E0 CIRCLED KATAKANA TI
+ * 	30C1 KATAKANA LETTER TI
+ * 32E1 CIRCLED KATAKANA TU
+ * 	30C4 KATAKANA LETTER TU
+ * 32E2 CIRCLED KATAKANA TE
+ * 	30C6 KATAKANA LETTER TE
+ * 32E3 CIRCLED KATAKANA TO
+ * 	30C8 KATAKANA LETTER TO
+ * 32E4 CIRCLED KATAKANA NA
+ * 	30CA KATAKANA LETTER NA
+ * 32E5 CIRCLED KATAKANA NI
+ * 	30CB KATAKANA LETTER NI
+ * 32E6 CIRCLED KATAKANA NU
+ * 	30CC KATAKANA LETTER NU
+ * 32E7 CIRCLED KATAKANA NE
+ * 	30CD KATAKANA LETTER NE
+ * 32E8 CIRCLED KATAKANA NO
+ * 	30CE KATAKANA LETTER NO
+ * 32E9 CIRCLED KATAKANA HA
+ * 	30CF KATAKANA LETTER HA
+ * 32EA CIRCLED KATAKANA HI
+ * 	30D2 KATAKANA LETTER HI
+ * 32EB CIRCLED KATAKANA HU
+ * 	30D5 KATAKANA LETTER HU
+ * 32EC CIRCLED KATAKANA HE
+ * 	30D8 KATAKANA LETTER HE
+ * 32ED CIRCLED KATAKANA HO
+ * 	30DB KATAKANA LETTER HO
+ * 32EE CIRCLED KATAKANA MA
+ * 	30DE KATAKANA LETTER MA
+ * 32EF CIRCLED KATAKANA MI
+ * 	30DF KATAKANA LETTER MI
+ * 32F0 CIRCLED KATAKANA MU
+ * 	30E0 KATAKANA LETTER MU
+ * 32F1 CIRCLED KATAKANA ME
+ * 	30E1 KATAKANA LETTER ME
+ * 32F2 CIRCLED KATAKANA MO
+ * 	30E2 KATAKANA LETTER MO
+ * 32F3 CIRCLED KATAKANA YA
+ * 	30E4 KATAKANA LETTER YA
+ * 32F4 CIRCLED KATAKANA YU
+ * 	30E6 KATAKANA LETTER YU
+ * 32F5 CIRCLED KATAKANA YO
+ * 	30E8 KATAKANA LETTER YO
+ * 32F6 CIRCLED KATAKANA RA
+ * 	30E9 KATAKANA LETTER RA
+ * 32F7 CIRCLED KATAKANA RI
+ * 	30EA KATAKANA LETTER RI
+ * 32F8 CIRCLED KATAKANA RU
+ * 	30EB KATAKANA LETTER RU
+ * 32F9 CIRCLED KATAKANA RE
+ * 	30EC KATAKANA LETTER RE
+ * 32FA CIRCLED KATAKANA RO
+ * 	30ED KATAKANA LETTER RO
+ * 32FB CIRCLED KATAKANA WA
+ * 	30EF KATAKANA LETTER WA
+ * 32FC CIRCLED KATAKANA WI
+ * 	30F0 KATAKANA LETTER WI
+ * 32FD CIRCLED KATAKANA WE
+ * 	30F1 KATAKANA LETTER WE
+ * 32FE CIRCLED KATAKANA WO
+ * 	30F2 KATAKANA LETTER WO
+ * 3300 SQUARE APAATO
+ * 	30A2 KATAKANA LETTER A
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 	30CF KATAKANA LETTER HA
+ * 3301 SQUARE ARUHUA
+ * 	30A2 KATAKANA LETTER A
+ * 	30EB KATAKANA LETTER RU
+ * 	30D5 KATAKANA LETTER HU
+ * 	30A1 KATAKANA LETTER SMALL A
+ * 3302 SQUARE ANPEA
+ * 	30A2 KATAKANA LETTER A
+ * 	30F3 KATAKANA LETTER N
+ * 	30A2 KATAKANA LETTER A
+ * 	30D8 KATAKANA LETTER HE
+ * 3303 SQUARE AARU
+ * 	30A2 KATAKANA LETTER A
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 3304 SQUARE ININGU
+ * 	30A4 KATAKANA LETTER I
+ * 	30CB KATAKANA LETTER NI
+ * 	30F3 KATAKANA LETTER N
+ * 	30AF KATAKANA LETTER KU
+ * 3305 SQUARE INTI
+ * 	30A4 KATAKANA LETTER I
+ * 	30F3 KATAKANA LETTER N
+ * 	30C1 KATAKANA LETTER TI
+ * 3306 SQUARE UON
+ * 	30A6 KATAKANA LETTER U
+ * 	30A9 KATAKANA LETTER SMALL O
+ * 	30F3 KATAKANA LETTER N
+ * 3307 SQUARE ESUKUUDO
+ * 	30A8 KATAKANA LETTER E
+ * 	30B9 KATAKANA LETTER SU
+ * 	30AF KATAKANA LETTER KU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 3308 SQUARE EEKAA
+ * 	30A8 KATAKANA LETTER E
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30AB KATAKANA LETTER KA
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 3309 SQUARE ONSU
+ * 	30AA KATAKANA LETTER O
+ * 	30F3 KATAKANA LETTER N
+ * 	30B9 KATAKANA LETTER SU
+ * 330A SQUARE OOMU
+ * 	30AA KATAKANA LETTER O
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30E0 KATAKANA LETTER MU
+ * 330B SQUARE KAIRI
+ * 	30AB KATAKANA LETTER KA
+ * 	30A4 KATAKANA LETTER I
+ * 	30EA KATAKANA LETTER RI
+ * 330C SQUARE KARATTO
+ * 	30AB KATAKANA LETTER KA
+ * 	30E9 KATAKANA LETTER RA
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 330D SQUARE KARORII
+ * 	30AB KATAKANA LETTER KA
+ * 	30ED KATAKANA LETTER RO
+ * 	30EA KATAKANA LETTER RI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 330E SQUARE GARON
+ * 	30ED KATAKANA LETTER RO
+ * 	30F3 KATAKANA LETTER N
+ * 	30AB KATAKANA LETTER KA
+ * 330F SQUARE GANMA
+ * 	30F3 KATAKANA LETTER N
+ * 	30DE KATAKANA LETTER MA
+ * 	30AB KATAKANA LETTER KA
+ * 3310 SQUARE GIGA
+ * 	30AD KATAKANA LETTER KI
+ * 	30AB KATAKANA LETTER KA
+ * 3311 SQUARE GINII
+ * 	30CB KATAKANA LETTER NI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30AD KATAKANA LETTER KI
+ * 3312 SQUARE KYURII
+ * 	30AD KATAKANA LETTER KI
+ * 	30E5 KATAKANA LETTER SMALL YU
+ * 	30EA KATAKANA LETTER RI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 3313 SQUARE GIRUDAA
+ * 	30EB KATAKANA LETTER RU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30AD KATAKANA LETTER KI
+ * 	30BF KATAKANA LETTER TA
+ * 3314 SQUARE KIRO
+ * 	30AD KATAKANA LETTER KI
+ * 	30ED KATAKANA LETTER RO
+ * 3315 SQUARE KIROGURAMU
+ * 	30AD KATAKANA LETTER KI
+ * 	30ED KATAKANA LETTER RO
+ * 	30E9 KATAKANA LETTER RA
+ * 	30E0 KATAKANA LETTER MU
+ * 	30AF KATAKANA LETTER KU
+ * 3316 SQUARE KIROMEETORU
+ * 	30AD KATAKANA LETTER KI
+ * 	30ED KATAKANA LETTER RO
+ * 	30E1 KATAKANA LETTER ME
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 	30EB KATAKANA LETTER RU
+ * 3317 SQUARE KIROWATTO
+ * 	30AD KATAKANA LETTER KI
+ * 	30ED KATAKANA LETTER RO
+ * 	30EF KATAKANA LETTER WA
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 3318 SQUARE GURAMU
+ * 	30E9 KATAKANA LETTER RA
+ * 	30E0 KATAKANA LETTER MU
+ * 	30AF KATAKANA LETTER KU
+ * 3319 SQUARE GURAMUTON
+ * 	30E9 KATAKANA LETTER RA
+ * 	30E0 KATAKANA LETTER MU
+ * 	30C8 KATAKANA LETTER TO
+ * 	30F3 KATAKANA LETTER N
+ * 	30AF KATAKANA LETTER KU
+ * 331A SQUARE KURUZEIRO
+ * 	30AF KATAKANA LETTER KU
+ * 	30EB KATAKANA LETTER RU
+ * 	30A4 KATAKANA LETTER I
+ * 	30ED KATAKANA LETTER RO
+ * 	30BB KATAKANA LETTER SE
+ * 331B SQUARE KUROONE
+ * 	30AF KATAKANA LETTER KU
+ * 	30ED KATAKANA LETTER RO
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30CD KATAKANA LETTER NE
+ * 331C SQUARE KEESU
+ * 	30B1 KATAKANA LETTER KE
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30B9 KATAKANA LETTER SU
+ * 331D SQUARE KORUNA
+ * 	30B3 KATAKANA LETTER KO
+ * 	30EB KATAKANA LETTER RU
+ * 	30CA KATAKANA LETTER NA
+ * 331E SQUARE KOOPO
+ * 	30B3 KATAKANA LETTER KO
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30DB KATAKANA LETTER HO
+ * 331F SQUARE SAIKURU
+ * 	30B5 KATAKANA LETTER SA
+ * 	30A4 KATAKANA LETTER I
+ * 	30AF KATAKANA LETTER KU
+ * 	30EB KATAKANA LETTER RU
+ * 3320 SQUARE SANTIIMU
+ * 	30B5 KATAKANA LETTER SA
+ * 	30F3 KATAKANA LETTER N
+ * 	30C1 KATAKANA LETTER TI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30E0 KATAKANA LETTER MU
+ * 3321 SQUARE SIRINGU
+ * 	30B7 KATAKANA LETTER SI
+ * 	30EA KATAKANA LETTER RI
+ * 	30F3 KATAKANA LETTER N
+ * 	30AF KATAKANA LETTER KU
+ * 3322 SQUARE SENTI
+ * 	30BB KATAKANA LETTER SE
+ * 	30F3 KATAKANA LETTER N
+ * 	30C1 KATAKANA LETTER TI
+ * 3323 SQUARE SENTO
+ * 	30BB KATAKANA LETTER SE
+ * 	30F3 KATAKANA LETTER N
+ * 	30C8 KATAKANA LETTER TO
+ * 3324 SQUARE DAASU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30B9 KATAKANA LETTER SU
+ * 	30BF KATAKANA LETTER TA
+ * 3325 SQUARE DESI
+ * 	30B7 KATAKANA LETTER SI
+ * 	30C6 KATAKANA LETTER TE
+ * 3326 SQUARE DORU
+ * 	30EB KATAKANA LETTER RU
+ * 	30C8 KATAKANA LETTER TO
+ * 3327 SQUARE TON
+ * 	30C8 KATAKANA LETTER TO
+ * 	30F3 KATAKANA LETTER N
+ * 3328 SQUARE NANO
+ * 	30CA KATAKANA LETTER NA
+ * 	30CE KATAKANA LETTER NO
+ * 3329 SQUARE NOTTO
+ * 	30CE KATAKANA LETTER NO
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 332A SQUARE HAITU
+ * 	30CF KATAKANA LETTER HA
+ * 	30A4 KATAKANA LETTER I
+ * 	30C4 KATAKANA LETTER TU
+ * 332B SQUARE PAASENTO
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30BB KATAKANA LETTER SE
+ * 	30F3 KATAKANA LETTER N
+ * 	30C8 KATAKANA LETTER TO
+ * 	30CF KATAKANA LETTER HA
+ * 332C SQUARE PAATU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C4 KATAKANA LETTER TU
+ * 	30CF KATAKANA LETTER HA
+ * 332D SQUARE BAARERU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EC KATAKANA LETTER RE
+ * 	30EB KATAKANA LETTER RU
+ * 	30CF KATAKANA LETTER HA
+ * 332E SQUARE PIASUTORU
+ * 	30A2 KATAKANA LETTER A
+ * 	30B9 KATAKANA LETTER SU
+ * 	30C8 KATAKANA LETTER TO
+ * 	30EB KATAKANA LETTER RU
+ * 	30D2 KATAKANA LETTER HI
+ * 332F SQUARE PIKURU
+ * 	30AF KATAKANA LETTER KU
+ * 	30EB KATAKANA LETTER RU
+ * 	30D2 KATAKANA LETTER HI
+ * 3330 SQUARE PIKO
+ * 	30B3 KATAKANA LETTER KO
+ * 	30D2 KATAKANA LETTER HI
+ * 3331 SQUARE BIRU
+ * 	30EB KATAKANA LETTER RU
+ * 	30D2 KATAKANA LETTER HI
+ * 3332 SQUARE HUARADDO
+ * 	30D5 KATAKANA LETTER HU
+ * 	30A1 KATAKANA LETTER SMALL A
+ * 	30E9 KATAKANA LETTER RA
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 3333 SQUARE HUIITO
+ * 	30D5 KATAKANA LETTER HU
+ * 	30A3 KATAKANA LETTER SMALL I
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 3334 SQUARE BUSSYERU
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30B7 KATAKANA LETTER SI
+ * 	30A7 KATAKANA LETTER SMALL E
+ * 	30EB KATAKANA LETTER RU
+ * 	30D5 KATAKANA LETTER HU
+ * 3335 SQUARE HURAN
+ * 	30D5 KATAKANA LETTER HU
+ * 	30E9 KATAKANA LETTER RA
+ * 	30F3 KATAKANA LETTER N
+ * 3336 SQUARE HEKUTAARU
+ * 	30D8 KATAKANA LETTER HE
+ * 	30AF KATAKANA LETTER KU
+ * 	30BF KATAKANA LETTER TA
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 3337 SQUARE PESO
+ * 	30BD KATAKANA LETTER SO
+ * 	30D8 KATAKANA LETTER HE
+ * 3338 SQUARE PENIHI
+ * 	30CB KATAKANA LETTER NI
+ * 	30D2 KATAKANA LETTER HI
+ * 	30D8 KATAKANA LETTER HE
+ * 3339 SQUARE HERUTU
+ * 	30D8 KATAKANA LETTER HE
+ * 	30EB KATAKANA LETTER RU
+ * 	30C4 KATAKANA LETTER TU
+ * 333A SQUARE PENSU
+ * 	30F3 KATAKANA LETTER N
+ * 	30B9 KATAKANA LETTER SU
+ * 	30D8 KATAKANA LETTER HE
+ * 333B SQUARE PEEZI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30D8 KATAKANA LETTER HE
+ * 	30B7 KATAKANA LETTER SI
+ * 333C SQUARE BEETA
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30BF KATAKANA LETTER TA
+ * 	30D8 KATAKANA LETTER HE
+ * 333D SQUARE POINTO
+ * 	30A4 KATAKANA LETTER I
+ * 	30F3 KATAKANA LETTER N
+ * 	30C8 KATAKANA LETTER TO
+ * 	30DB KATAKANA LETTER HO
+ * 333E SQUARE BORUTO
+ * 	30EB KATAKANA LETTER RU
+ * 	30C8 KATAKANA LETTER TO
+ * 	30DB KATAKANA LETTER HO
+ * 333F SQUARE HON
+ * 	30DB KATAKANA LETTER HO
+ * 	30F3 KATAKANA LETTER N
+ * 3340 SQUARE PONDO
+ * 	30F3 KATAKANA LETTER N
+ * 	30DB KATAKANA LETTER HO
+ * 	30C8 KATAKANA LETTER TO
+ * 3341 SQUARE HOORU
+ * 	30DB KATAKANA LETTER HO
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 3342 SQUARE HOON
+ * 	30DB KATAKANA LETTER HO
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30F3 KATAKANA LETTER N
+ * 3343 SQUARE MAIKURO
+ * 	30DE KATAKANA LETTER MA
+ * 	30A4 KATAKANA LETTER I
+ * 	30AF KATAKANA LETTER KU
+ * 	30ED KATAKANA LETTER RO
+ * 3344 SQUARE MAIRU
+ * 	30DE KATAKANA LETTER MA
+ * 	30A4 KATAKANA LETTER I
+ * 	30EB KATAKANA LETTER RU
+ * 3345 SQUARE MAHHA
+ * 	30DE KATAKANA LETTER MA
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30CF KATAKANA LETTER HA
+ * 3346 SQUARE MARUKU
+ * 	30DE KATAKANA LETTER MA
+ * 	30EB KATAKANA LETTER RU
+ * 	30AF KATAKANA LETTER KU
+ * 3347 SQUARE MANSYON
+ * 	30DE KATAKANA LETTER MA
+ * 	30F3 KATAKANA LETTER N
+ * 	30B7 KATAKANA LETTER SI
+ * 	30E7 KATAKANA LETTER SMALL YO
+ * 	30F3 KATAKANA LETTER N
+ * 3348 SQUARE MIKURON
+ * 	30DF KATAKANA LETTER MI
+ * 	30AF KATAKANA LETTER KU
+ * 	30ED KATAKANA LETTER RO
+ * 	30F3 KATAKANA LETTER N
+ * 3349 SQUARE MIRI
+ * 	30DF KATAKANA LETTER MI
+ * 	30EA KATAKANA LETTER RI
+ * 334A SQUARE MIRIBAARU
+ * 	30DF KATAKANA LETTER MI
+ * 	30EA KATAKANA LETTER RI
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 	30CF KATAKANA LETTER HA
+ * 334B SQUARE MEGA
+ * 	30E1 KATAKANA LETTER ME
+ * 	30AB KATAKANA LETTER KA
+ * 334C SQUARE MEGATON
+ * 	30E1 KATAKANA LETTER ME
+ * 	30C8 KATAKANA LETTER TO
+ * 	30F3 KATAKANA LETTER N
+ * 	30AB KATAKANA LETTER KA
+ * 334D SQUARE MEETORU
+ * 	30E1 KATAKANA LETTER ME
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 	30EB KATAKANA LETTER RU
+ * 334E SQUARE YAADO
+ * 	30E4 KATAKANA LETTER YA
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30C8 KATAKANA LETTER TO
+ * 334F SQUARE YAARU
+ * 	30E4 KATAKANA LETTER YA
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 3350 SQUARE YUAN
+ * 	30E6 KATAKANA LETTER YU
+ * 	30A2 KATAKANA LETTER A
+ * 	30F3 KATAKANA LETTER N
+ * 3351 SQUARE RITTORU
+ * 	30EA KATAKANA LETTER RI
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 	30EB KATAKANA LETTER RU
+ * 3352 SQUARE RIRA
+ * 	30EA KATAKANA LETTER RI
+ * 	30E9 KATAKANA LETTER RA
+ * 3353 SQUARE RUPII
+ * 	30EB KATAKANA LETTER RU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30D2 KATAKANA LETTER HI
+ * 3354 SQUARE RUUBURU
+ * 	30EB KATAKANA LETTER RU
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30EB KATAKANA LETTER RU
+ * 	30D5 KATAKANA LETTER HU
+ * 3355 SQUARE REMU
+ * 	30EC KATAKANA LETTER RE
+ * 	30E0 KATAKANA LETTER MU
+ * 3356 SQUARE RENTOGEN
+ * 	30EC KATAKANA LETTER RE
+ * 	30F3 KATAKANA LETTER N
+ * 	30C8 KATAKANA LETTER TO
+ * 	30F3 KATAKANA LETTER N
+ * 	30B1 KATAKANA LETTER KE
+ * 3357 SQUARE WATTO
+ * 	30EF KATAKANA LETTER WA
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * 	30C8 KATAKANA LETTER TO
+ * 3358 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO
+ * 	0030 DIGIT ZERO
+ * 	70B9 ??
+ * 3359 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE
+ * 	0031 DIGIT ONE
+ * 	70B9 ??
+ * 335A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO
+ * 	0032 DIGIT TWO
+ * 	70B9 ??
+ * 335B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE
+ * 	0033 DIGIT THREE
+ * 	70B9 ??
+ * 335C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR
+ * 	0034 DIGIT FOUR
+ * 	70B9 ??
+ * 335D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE
+ * 	0035 DIGIT FIVE
+ * 	70B9 ??
+ * 335E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX
+ * 	0036 DIGIT SIX
+ * 	70B9 ??
+ * 335F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN
+ * 	0037 DIGIT SEVEN
+ * 	70B9 ??
+ * 3360 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT
+ * 	0038 DIGIT EIGHT
+ * 	70B9 ??
+ * 3361 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE
+ * 	0039 DIGIT NINE
+ * 	70B9 ??
+ * 3362 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 	70B9 ??
+ * 3363 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 	70B9 ??
+ * 3364 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 	70B9 ??
+ * 3365 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN
+ * 	0031 DIGIT ONE
+ * 	0033 DIGIT THREE
+ * 	70B9 ??
+ * 3366 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN
+ * 	0031 DIGIT ONE
+ * 	0034 DIGIT FOUR
+ * 	70B9 ??
+ * 3367 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN
+ * 	0031 DIGIT ONE
+ * 	0035 DIGIT FIVE
+ * 	70B9 ??
+ * 3368 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN
+ * 	0031 DIGIT ONE
+ * 	0036 DIGIT SIX
+ * 	70B9 ??
+ * 3369 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN
+ * 	0031 DIGIT ONE
+ * 	0037 DIGIT SEVEN
+ * 	70B9 ??
+ * 336A IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN
+ * 	0031 DIGIT ONE
+ * 	0038 DIGIT EIGHT
+ * 	70B9 ??
+ * 336B IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN
+ * 	0031 DIGIT ONE
+ * 	0039 DIGIT NINE
+ * 	70B9 ??
+ * 336C IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY
+ * 	0032 DIGIT TWO
+ * 	0030 DIGIT ZERO
+ * 	70B9 ??
+ * 336D IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE
+ * 	0032 DIGIT TWO
+ * 	0031 DIGIT ONE
+ * 	70B9 ??
+ * 336E IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO
+ * 	0032 DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 	70B9 ??
+ * 336F IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE
+ * 	0032 DIGIT TWO
+ * 	0033 DIGIT THREE
+ * 	70B9 ??
+ * 3370 IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR
+ * 	0032 DIGIT TWO
+ * 	0034 DIGIT FOUR
+ * 	70B9 ??
+ * 3371 SQUARE HPA
+ * 	0068 LATIN SMALL LETTER H
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0061 LATIN SMALL LETTER A
+ * 3372 SQUARE DA
+ * 	0064 LATIN SMALL LETTER D
+ * 	0061 LATIN SMALL LETTER A
+ * 3373 SQUARE AU
+ * 	0041 LATIN CAPITAL LETTER A
+ * 	0055 LATIN CAPITAL LETTER U
+ * 3374 SQUARE BAR
+ * 	0062 LATIN SMALL LETTER B
+ * 	0061 LATIN SMALL LETTER A
+ * 	0072 LATIN SMALL LETTER R
+ * 3375 SQUARE OV
+ * 	006F LATIN SMALL LETTER O
+ * 	0056 LATIN CAPITAL LETTER V
+ * 3376 SQUARE PC
+ * 	0070 LATIN SMALL LETTER P
+ * 	0063 LATIN SMALL LETTER C
+ * 337B SQUARE ERA NAME HEISEI
+ * 	5E73 ??
+ * 	6210 ??
+ * 337C SQUARE ERA NAME SYOUWA
+ * 	662D ??
+ * 	548C ??
+ * 337D SQUARE ERA NAME TAISYOU
+ * 	5927 ??
+ * 	6B63 ??
+ * 337E SQUARE ERA NAME MEIZI
+ * 	660E ??
+ * 	6CBB ??
+ * 337F SQUARE CORPORATION
+ * 	682A ??
+ * 	5F0F ??
+ * 	4F1A ??
+ * 	793E ??
+ * 3380 SQUARE PA AMPS
+ * 	0070 LATIN SMALL LETTER P
+ * 	0041 LATIN CAPITAL LETTER A
+ * 3381 SQUARE NA
+ * 	006E LATIN SMALL LETTER N
+ * 	0041 LATIN CAPITAL LETTER A
+ * 3382 SQUARE MU A
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0041 LATIN CAPITAL LETTER A
+ * 3383 SQUARE MA
+ * 	006D LATIN SMALL LETTER M
+ * 	0041 LATIN CAPITAL LETTER A
+ * 3384 SQUARE KA
+ * 	006B LATIN SMALL LETTER K
+ * 	0041 LATIN CAPITAL LETTER A
+ * 3385 SQUARE KB
+ * 	004B LATIN CAPITAL LETTER K
+ * 	0042 LATIN CAPITAL LETTER B
+ * 3386 SQUARE MB
+ * 	004D LATIN CAPITAL LETTER M
+ * 	0042 LATIN CAPITAL LETTER B
+ * 3387 SQUARE GB
+ * 	0047 LATIN CAPITAL LETTER G
+ * 	0042 LATIN CAPITAL LETTER B
+ * 3388 SQUARE CAL
+ * 	0063 LATIN SMALL LETTER C
+ * 	0061 LATIN SMALL LETTER A
+ * 	006C LATIN SMALL LETTER L
+ * 3389 SQUARE KCAL
+ * 	006B LATIN SMALL LETTER K
+ * 	0063 LATIN SMALL LETTER C
+ * 	0061 LATIN SMALL LETTER A
+ * 	006C LATIN SMALL LETTER L
+ * 338A SQUARE PF
+ * 	0070 LATIN SMALL LETTER P
+ * 	0046 LATIN CAPITAL LETTER F
+ * 338B SQUARE NF
+ * 	006E LATIN SMALL LETTER N
+ * 	0046 LATIN CAPITAL LETTER F
+ * 338C SQUARE MU F
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0046 LATIN CAPITAL LETTER F
+ * 338D SQUARE MU G
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0067 LATIN SMALL LETTER G
+ * 338E SQUARE MG
+ * 	006D LATIN SMALL LETTER M
+ * 	0067 LATIN SMALL LETTER G
+ * 338F SQUARE KG
+ * 	006B LATIN SMALL LETTER K
+ * 	0067 LATIN SMALL LETTER G
+ * 3390 SQUARE HZ
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	007A LATIN SMALL LETTER Z
+ * 3391 SQUARE KHZ
+ * 	006B LATIN SMALL LETTER K
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	007A LATIN SMALL LETTER Z
+ * 3392 SQUARE MHZ
+ * 	004D LATIN CAPITAL LETTER M
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	007A LATIN SMALL LETTER Z
+ * 3393 SQUARE GHZ
+ * 	0047 LATIN CAPITAL LETTER G
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	007A LATIN SMALL LETTER Z
+ * 3394 SQUARE THZ
+ * 	0054 LATIN CAPITAL LETTER T
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	007A LATIN SMALL LETTER Z
+ * 3395 SQUARE MU L
+ * 	03BC GREEK SMALL LETTER MU
+ * 	006C LATIN SMALL LETTER L
+ * 3396 SQUARE ML
+ * 	006D LATIN SMALL LETTER M
+ * 	006C LATIN SMALL LETTER L
+ * 3397 SQUARE DL
+ * 	0064 LATIN SMALL LETTER D
+ * 	006C LATIN SMALL LETTER L
+ * 3398 SQUARE KL
+ * 	006B LATIN SMALL LETTER K
+ * 	006C LATIN SMALL LETTER L
+ * 3399 SQUARE FM
+ * 	0066 LATIN SMALL LETTER F
+ * 	006D LATIN SMALL LETTER M
+ * 339A SQUARE NM
+ * 	006E LATIN SMALL LETTER N
+ * 	006D LATIN SMALL LETTER M
+ * 339B SQUARE MU M
+ * 	03BC GREEK SMALL LETTER MU
+ * 	006D LATIN SMALL LETTER M
+ * 339C SQUARE MM
+ * 	006D LATIN SMALL LETTER M
+ * 	006D LATIN SMALL LETTER M
+ * 339D SQUARE CM
+ * 	0063 LATIN SMALL LETTER C
+ * 	006D LATIN SMALL LETTER M
+ * 339E SQUARE KM
+ * 	006B LATIN SMALL LETTER K
+ * 	006D LATIN SMALL LETTER M
+ * 339F SQUARE MM SQUARED
+ * 	006D LATIN SMALL LETTER M
+ * 	006D LATIN SMALL LETTER M
+ * 	0032 DIGIT TWO
+ * 33A0 SQUARE CM SQUARED
+ * 	0063 LATIN SMALL LETTER C
+ * 	006D LATIN SMALL LETTER M
+ * 	0032 DIGIT TWO
+ * 33A1 SQUARE M SQUARED
+ * 	006D LATIN SMALL LETTER M
+ * 	0032 DIGIT TWO
+ * 33A2 SQUARE KM SQUARED
+ * 	006B LATIN SMALL LETTER K
+ * 	006D LATIN SMALL LETTER M
+ * 	0032 DIGIT TWO
+ * 33A3 SQUARE MM CUBED
+ * 	006D LATIN SMALL LETTER M
+ * 	006D LATIN SMALL LETTER M
+ * 	0033 DIGIT THREE
+ * 33A4 SQUARE CM CUBED
+ * 	0063 LATIN SMALL LETTER C
+ * 	006D LATIN SMALL LETTER M
+ * 	0033 DIGIT THREE
+ * 33A5 SQUARE M CUBED
+ * 	006D LATIN SMALL LETTER M
+ * 	0033 DIGIT THREE
+ * 33A6 SQUARE KM CUBED
+ * 	006B LATIN SMALL LETTER K
+ * 	006D LATIN SMALL LETTER M
+ * 	0033 DIGIT THREE
+ * 33A7 SQUARE M OVER S
+ * 	006D LATIN SMALL LETTER M
+ * 	2215 DIVISION SLASH
+ * 	0073 LATIN SMALL LETTER S
+ * 33A8 SQUARE M OVER S SQUARED
+ * 	006D LATIN SMALL LETTER M
+ * 	2215 DIVISION SLASH
+ * 	0073 LATIN SMALL LETTER S
+ * 	0032 DIGIT TWO
+ * 33A9 SQUARE PA
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0061 LATIN SMALL LETTER A
+ * 33AA SQUARE KPA
+ * 	006B LATIN SMALL LETTER K
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0061 LATIN SMALL LETTER A
+ * 33AB SQUARE MPA
+ * 	004D LATIN CAPITAL LETTER M
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0061 LATIN SMALL LETTER A
+ * 33AC SQUARE GPA
+ * 	0047 LATIN CAPITAL LETTER G
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0061 LATIN SMALL LETTER A
+ * 33AD SQUARE RAD
+ * 	0072 LATIN SMALL LETTER R
+ * 	0061 LATIN SMALL LETTER A
+ * 	0064 LATIN SMALL LETTER D
+ * 33AE SQUARE RAD OVER S
+ * 	0072 LATIN SMALL LETTER R
+ * 	0061 LATIN SMALL LETTER A
+ * 	0064 LATIN SMALL LETTER D
+ * 	2215 DIVISION SLASH
+ * 	0073 LATIN SMALL LETTER S
+ * 33AF SQUARE RAD OVER S SQUARED
+ * 	0072 LATIN SMALL LETTER R
+ * 	0061 LATIN SMALL LETTER A
+ * 	0064 LATIN SMALL LETTER D
+ * 	2215 DIVISION SLASH
+ * 	0073 LATIN SMALL LETTER S
+ * 	0032 DIGIT TWO
+ * 33B0 SQUARE PS
+ * 	0070 LATIN SMALL LETTER P
+ * 	0073 LATIN SMALL LETTER S
+ * 33B1 SQUARE NS
+ * 	006E LATIN SMALL LETTER N
+ * 	0073 LATIN SMALL LETTER S
+ * 33B2 SQUARE MU S
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0073 LATIN SMALL LETTER S
+ * 33B3 SQUARE MS
+ * 	006D LATIN SMALL LETTER M
+ * 	0073 LATIN SMALL LETTER S
+ * 33B4 SQUARE PV
+ * 	0070 LATIN SMALL LETTER P
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33B5 SQUARE NV
+ * 	006E LATIN SMALL LETTER N
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33B6 SQUARE MU V
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33B7 SQUARE MV
+ * 	006D LATIN SMALL LETTER M
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33B8 SQUARE KV
+ * 	006B LATIN SMALL LETTER K
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33B9 SQUARE MV MEGA
+ * 	004D LATIN CAPITAL LETTER M
+ * 	0056 LATIN CAPITAL LETTER V
+ * 33BA SQUARE PW
+ * 	0070 LATIN SMALL LETTER P
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33BB SQUARE NW
+ * 	006E LATIN SMALL LETTER N
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33BC SQUARE MU W
+ * 	03BC GREEK SMALL LETTER MU
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33BD SQUARE MW
+ * 	006D LATIN SMALL LETTER M
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33BE SQUARE KW
+ * 	006B LATIN SMALL LETTER K
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33BF SQUARE MW MEGA
+ * 	004D LATIN CAPITAL LETTER M
+ * 	0057 LATIN CAPITAL LETTER W
+ * 33C0 SQUARE K OHM
+ * 	006B LATIN SMALL LETTER K
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 33C1 SQUARE M OHM
+ * 	004D LATIN CAPITAL LETTER M
+ * 	03A9 GREEK CAPITAL LETTER OMEGA
+ * 33C2 SQUARE AM
+ * 	0061 LATIN SMALL LETTER A
+ * 	002E FULL STOP
+ * 	006D LATIN SMALL LETTER M
+ * 	002E FULL STOP
+ * 33C3 SQUARE BQ
+ * 	0042 LATIN CAPITAL LETTER B
+ * 	0071 LATIN SMALL LETTER Q
+ * 33C4 SQUARE CC
+ * 	0063 LATIN SMALL LETTER C
+ * 	0063 LATIN SMALL LETTER C
+ * 33C5 SQUARE CD
+ * 	0063 LATIN SMALL LETTER C
+ * 	0064 LATIN SMALL LETTER D
+ * 33C6 SQUARE C OVER KG
+ * 	0043 LATIN CAPITAL LETTER C
+ * 	2215 DIVISION SLASH
+ * 	006B LATIN SMALL LETTER K
+ * 	0067 LATIN SMALL LETTER G
+ * 33C7 SQUARE CO
+ * 	0043 LATIN CAPITAL LETTER C
+ * 	006F LATIN SMALL LETTER O
+ * 	002E FULL STOP
+ * 33C8 SQUARE DB
+ * 	0064 LATIN SMALL LETTER D
+ * 	0042 LATIN CAPITAL LETTER B
+ * 33C9 SQUARE GY
+ * 	0047 LATIN CAPITAL LETTER G
+ * 	0079 LATIN SMALL LETTER Y
+ * 33CA SQUARE HA
+ * 	0068 LATIN SMALL LETTER H
+ * 	0061 LATIN SMALL LETTER A
+ * 33CB SQUARE HP
+ * 	0048 LATIN CAPITAL LETTER H
+ * 	0050 LATIN CAPITAL LETTER P
+ * 33CC SQUARE IN
+ * 	0069 LATIN SMALL LETTER I
+ * 	006E LATIN SMALL LETTER N
+ * 33CD SQUARE KK
+ * 	004B LATIN CAPITAL LETTER K
+ * 	004B LATIN CAPITAL LETTER K
+ * 33CE SQUARE KM CAPITAL
+ * 	004B LATIN CAPITAL LETTER K
+ * 	004D LATIN CAPITAL LETTER M
+ * 33CF SQUARE KT
+ * 	006B LATIN SMALL LETTER K
+ * 	0074 LATIN SMALL LETTER T
+ * 33D0 SQUARE LM
+ * 	006C LATIN SMALL LETTER L
+ * 	006D LATIN SMALL LETTER M
+ * 33D1 SQUARE LN
+ * 	006C LATIN SMALL LETTER L
+ * 	006E LATIN SMALL LETTER N
+ * 33D2 SQUARE LOG
+ * 	006C LATIN SMALL LETTER L
+ * 	006F LATIN SMALL LETTER O
+ * 	0067 LATIN SMALL LETTER G
+ * 33D3 SQUARE LX
+ * 	006C LATIN SMALL LETTER L
+ * 	0078 LATIN SMALL LETTER X
+ * 33D4 SQUARE MB SMALL
+ * 	006D LATIN SMALL LETTER M
+ * 	0062 LATIN SMALL LETTER B
+ * 33D5 SQUARE MIL
+ * 	006D LATIN SMALL LETTER M
+ * 	0069 LATIN SMALL LETTER I
+ * 	006C LATIN SMALL LETTER L
+ * 33D6 SQUARE MOL
+ * 	006D LATIN SMALL LETTER M
+ * 	006F LATIN SMALL LETTER O
+ * 	006C LATIN SMALL LETTER L
+ * 33D7 SQUARE PH
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0048 LATIN CAPITAL LETTER H
+ * 33D8 SQUARE PM
+ * 	0070 LATIN SMALL LETTER P
+ * 	002E FULL STOP
+ * 	006D LATIN SMALL LETTER M
+ * 	002E FULL STOP
+ * 33D9 SQUARE PPM
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	004D LATIN CAPITAL LETTER M
+ * 33DA SQUARE PR
+ * 	0050 LATIN CAPITAL LETTER P
+ * 	0052 LATIN CAPITAL LETTER R
+ * 33DB SQUARE SR
+ * 	0073 LATIN SMALL LETTER S
+ * 	0072 LATIN SMALL LETTER R
+ * 33DC SQUARE SV
+ * 	0053 LATIN CAPITAL LETTER S
+ * 	0076 LATIN SMALL LETTER V
+ * 33DD SQUARE WB
+ * 	0057 LATIN CAPITAL LETTER W
+ * 	0062 LATIN SMALL LETTER B
+ * 33E0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE
+ * 	0031 DIGIT ONE
+ * 	65E5 ??
+ * 33E1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO
+ * 	0032 DIGIT TWO
+ * 	65E5 ??
+ * 33E2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE
+ * 	0033 DIGIT THREE
+ * 	65E5 ??
+ * 33E3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR
+ * 	0034 DIGIT FOUR
+ * 	65E5 ??
+ * 33E4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE
+ * 	0035 DIGIT FIVE
+ * 	65E5 ??
+ * 33E5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX
+ * 	0036 DIGIT SIX
+ * 	65E5 ??
+ * 33E6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN
+ * 	0037 DIGIT SEVEN
+ * 	65E5 ??
+ * 33E7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT
+ * 	0038 DIGIT EIGHT
+ * 	65E5 ??
+ * 33E8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE
+ * 	0039 DIGIT NINE
+ * 	65E5 ??
+ * 33E9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN
+ * 	0031 DIGIT ONE
+ * 	0030 DIGIT ZERO
+ * 	65E5 ??
+ * 33EA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN
+ * 	0031 DIGIT ONE
+ * 	0031 DIGIT ONE
+ * 	65E5 ??
+ * 33EB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE
+ * 	0031 DIGIT ONE
+ * 	0032 DIGIT TWO
+ * 	65E5 ??
+ * 33EC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN
+ * 	0031 DIGIT ONE
+ * 	0033 DIGIT THREE
+ * 	65E5 ??
+ * 33ED IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN
+ * 	0031 DIGIT ONE
+ * 	0034 DIGIT FOUR
+ * 	65E5 ??
+ * 33EE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN
+ * 	0031 DIGIT ONE
+ * 	0035 DIGIT FIVE
+ * 	65E5 ??
+ * 33EF IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN
+ * 	0031 DIGIT ONE
+ * 	0036 DIGIT SIX
+ * 	65E5 ??
+ * 33F0 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN
+ * 	0031 DIGIT ONE
+ * 	0037 DIGIT SEVEN
+ * 	65E5 ??
+ * 33F1 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN
+ * 	0031 DIGIT ONE
+ * 	0038 DIGIT EIGHT
+ * 	65E5 ??
+ * 33F2 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN
+ * 	0031 DIGIT ONE
+ * 	0039 DIGIT NINE
+ * 	65E5 ??
+ * 33F3 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY
+ * 	0032 DIGIT TWO
+ * 	0030 DIGIT ZERO
+ * 	65E5 ??
+ * 33F4 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE
+ * 	0032 DIGIT TWO
+ * 	0031 DIGIT ONE
+ * 	65E5 ??
+ * 33F5 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO
+ * 	0032 DIGIT TWO
+ * 	0032 DIGIT TWO
+ * 	65E5 ??
+ * 33F6 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE
+ * 	0032 DIGIT TWO
+ * 	0033 DIGIT THREE
+ * 	65E5 ??
+ * 33F7 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR
+ * 	0032 DIGIT TWO
+ * 	0034 DIGIT FOUR
+ * 	65E5 ??
+ * 33F8 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE
+ * 	0032 DIGIT TWO
+ * 	0035 DIGIT FIVE
+ * 	65E5 ??
+ * 33F9 IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX
+ * 	0032 DIGIT TWO
+ * 	0036 DIGIT SIX
+ * 	65E5 ??
+ * 33FA IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN
+ * 	0032 DIGIT TWO
+ * 	0037 DIGIT SEVEN
+ * 	65E5 ??
+ * 33FB IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT
+ * 	0032 DIGIT TWO
+ * 	0038 DIGIT EIGHT
+ * 	65E5 ??
+ * 33FC IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE
+ * 	0032 DIGIT TWO
+ * 	0039 DIGIT NINE
+ * 	65E5 ??
+ * 33FD IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY
+ * 	0033 DIGIT THREE
+ * 	0030 DIGIT ZERO
+ * 	65E5 ??
+ * 33FE IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE
+ * 	0033 DIGIT THREE
+ * 	0031 DIGIT ONE
+ * 	65E5 ??
+ * F900 CJK COMPATIBILITY IDEOGRAPH-F900
+ * 	8C48 ??
+ * F901 CJK COMPATIBILITY IDEOGRAPH-F901
+ * 	66F4 ??
+ * F902 CJK COMPATIBILITY IDEOGRAPH-F902
+ * 	8ECA ??
+ * F903 CJK COMPATIBILITY IDEOGRAPH-F903
+ * 	8CC8 ??
+ * F904 CJK COMPATIBILITY IDEOGRAPH-F904
+ * 	6ED1 ??
+ * F905 CJK COMPATIBILITY IDEOGRAPH-F905
+ * 	4E32 ??
+ * F906 CJK COMPATIBILITY IDEOGRAPH-F906
+ * 	53E5 ??
+ * F907 CJK COMPATIBILITY IDEOGRAPH-F907
+ * 	9F9C ??
+ * F908 CJK COMPATIBILITY IDEOGRAPH-F908
+ * 	9F9C ??
+ * F909 CJK COMPATIBILITY IDEOGRAPH-F909
+ * 	5951 ??
+ * F90A CJK COMPATIBILITY IDEOGRAPH-F90A
+ * 	91D1 ??
+ * F90B CJK COMPATIBILITY IDEOGRAPH-F90B
+ * 	5587 ??
+ * F90C CJK COMPATIBILITY IDEOGRAPH-F90C
+ * 	5948 ??
+ * F90D CJK COMPATIBILITY IDEOGRAPH-F90D
+ * 	61F6 ??
+ * F90E CJK COMPATIBILITY IDEOGRAPH-F90E
+ * 	7669 ??
+ * F90F CJK COMPATIBILITY IDEOGRAPH-F90F
+ * 	7F85 ??
+ * F910 CJK COMPATIBILITY IDEOGRAPH-F910
+ * 	863F ??
+ * F911 CJK COMPATIBILITY IDEOGRAPH-F911
+ * 	87BA ??
+ * F912 CJK COMPATIBILITY IDEOGRAPH-F912
+ * 	88F8 ??
+ * F913 CJK COMPATIBILITY IDEOGRAPH-F913
+ * 	908F ??
+ * F914 CJK COMPATIBILITY IDEOGRAPH-F914
+ * 	6A02 ??
+ * F915 CJK COMPATIBILITY IDEOGRAPH-F915
+ * 	6D1B ??
+ * F916 CJK COMPATIBILITY IDEOGRAPH-F916
+ * 	70D9 ??
+ * F917 CJK COMPATIBILITY IDEOGRAPH-F917
+ * 	73DE ??
+ * F918 CJK COMPATIBILITY IDEOGRAPH-F918
+ * 	843D ??
+ * F919 CJK COMPATIBILITY IDEOGRAPH-F919
+ * 	916A ??
+ * F91A CJK COMPATIBILITY IDEOGRAPH-F91A
+ * 	99F1 ??
+ * F91B CJK COMPATIBILITY IDEOGRAPH-F91B
+ * 	4E82 ??
+ * F91C CJK COMPATIBILITY IDEOGRAPH-F91C
+ * 	5375 ??
+ * F91D CJK COMPATIBILITY IDEOGRAPH-F91D
+ * 	6B04 ??
+ * F91E CJK COMPATIBILITY IDEOGRAPH-F91E
+ * 	721B ??
+ * F91F CJK COMPATIBILITY IDEOGRAPH-F91F
+ * 	862D ??
+ * F920 CJK COMPATIBILITY IDEOGRAPH-F920
+ * 	9E1E ??
+ * F921 CJK COMPATIBILITY IDEOGRAPH-F921
+ * 	5D50 ??
+ * F922 CJK COMPATIBILITY IDEOGRAPH-F922
+ * 	6FEB ??
+ * F923 CJK COMPATIBILITY IDEOGRAPH-F923
+ * 	85CD ??
+ * F924 CJK COMPATIBILITY IDEOGRAPH-F924
+ * 	8964 ??
+ * F925 CJK COMPATIBILITY IDEOGRAPH-F925
+ * 	62C9 ??
+ * F926 CJK COMPATIBILITY IDEOGRAPH-F926
+ * 	81D8 ??
+ * F927 CJK COMPATIBILITY IDEOGRAPH-F927
+ * 	881F ??
+ * F928 CJK COMPATIBILITY IDEOGRAPH-F928
+ * 	5ECA ??
+ * F929 CJK COMPATIBILITY IDEOGRAPH-F929
+ * 	6717 ??
+ * F92A CJK COMPATIBILITY IDEOGRAPH-F92A
+ * 	6D6A ??
+ * F92B CJK COMPATIBILITY IDEOGRAPH-F92B
+ * 	72FC ??
+ * F92C CJK COMPATIBILITY IDEOGRAPH-F92C
+ * 	90CE ??
+ * F92D CJK COMPATIBILITY IDEOGRAPH-F92D
+ * 	4F86 ??
+ * F92E CJK COMPATIBILITY IDEOGRAPH-F92E
+ * 	51B7 ??
+ * F92F CJK COMPATIBILITY IDEOGRAPH-F92F
+ * 	52DE ??
+ * F930 CJK COMPATIBILITY IDEOGRAPH-F930
+ * 	64C4 ??
+ * F931 CJK COMPATIBILITY IDEOGRAPH-F931
+ * 	6AD3 ??
+ * F932 CJK COMPATIBILITY IDEOGRAPH-F932
+ * 	7210 ??
+ * F933 CJK COMPATIBILITY IDEOGRAPH-F933
+ * 	76E7 ??
+ * F934 CJK COMPATIBILITY IDEOGRAPH-F934
+ * 	8001 ??
+ * F935 CJK COMPATIBILITY IDEOGRAPH-F935
+ * 	8606 ??
+ * F936 CJK COMPATIBILITY IDEOGRAPH-F936
+ * 	865C ??
+ * F937 CJK COMPATIBILITY IDEOGRAPH-F937
+ * 	8DEF ??
+ * F938 CJK COMPATIBILITY IDEOGRAPH-F938
+ * 	9732 ??
+ * F939 CJK COMPATIBILITY IDEOGRAPH-F939
+ * 	9B6F ??
+ * F93A CJK COMPATIBILITY IDEOGRAPH-F93A
+ * 	9DFA ??
+ * F93B CJK COMPATIBILITY IDEOGRAPH-F93B
+ * 	788C ??
+ * F93C CJK COMPATIBILITY IDEOGRAPH-F93C
+ * 	797F ??
+ * F93D CJK COMPATIBILITY IDEOGRAPH-F93D
+ * 	7DA0 ??
+ * F93E CJK COMPATIBILITY IDEOGRAPH-F93E
+ * 	83C9 ??
+ * F93F CJK COMPATIBILITY IDEOGRAPH-F93F
+ * 	9304 ??
+ * F940 CJK COMPATIBILITY IDEOGRAPH-F940
+ * 	9E7F ??
+ * F941 CJK COMPATIBILITY IDEOGRAPH-F941
+ * 	8AD6 ??
+ * F942 CJK COMPATIBILITY IDEOGRAPH-F942
+ * 	58DF ??
+ * F943 CJK COMPATIBILITY IDEOGRAPH-F943
+ * 	5F04 ??
+ * F944 CJK COMPATIBILITY IDEOGRAPH-F944
+ * 	7C60 ??
+ * F945 CJK COMPATIBILITY IDEOGRAPH-F945
+ * 	807E ??
+ * F946 CJK COMPATIBILITY IDEOGRAPH-F946
+ * 	7262 ??
+ * F947 CJK COMPATIBILITY IDEOGRAPH-F947
+ * 	78CA ??
+ * F948 CJK COMPATIBILITY IDEOGRAPH-F948
+ * 	8CC2 ??
+ * F949 CJK COMPATIBILITY IDEOGRAPH-F949
+ * 	96F7 ??
+ * F94A CJK COMPATIBILITY IDEOGRAPH-F94A
+ * 	58D8 ??
+ * F94B CJK COMPATIBILITY IDEOGRAPH-F94B
+ * 	5C62 ??
+ * F94C CJK COMPATIBILITY IDEOGRAPH-F94C
+ * 	6A13 ??
+ * F94D CJK COMPATIBILITY IDEOGRAPH-F94D
+ * 	6DDA ??
+ * F94E CJK COMPATIBILITY IDEOGRAPH-F94E
+ * 	6F0F ??
+ * F94F CJK COMPATIBILITY IDEOGRAPH-F94F
+ * 	7D2F ??
+ * F950 CJK COMPATIBILITY IDEOGRAPH-F950
+ * 	7E37 ??
+ * F951 CJK COMPATIBILITY IDEOGRAPH-F951
+ * 	964B ??
+ * F952 CJK COMPATIBILITY IDEOGRAPH-F952
+ * 	52D2 ??
+ * F953 CJK COMPATIBILITY IDEOGRAPH-F953
+ * 	808B ??
+ * F954 CJK COMPATIBILITY IDEOGRAPH-F954
+ * 	51DC ??
+ * F955 CJK COMPATIBILITY IDEOGRAPH-F955
+ * 	51CC ??
+ * F956 CJK COMPATIBILITY IDEOGRAPH-F956
+ * 	7A1C ??
+ * F957 CJK COMPATIBILITY IDEOGRAPH-F957
+ * 	7DBE ??
+ * F958 CJK COMPATIBILITY IDEOGRAPH-F958
+ * 	83F1 ??
+ * F959 CJK COMPATIBILITY IDEOGRAPH-F959
+ * 	9675 ??
+ * F95A CJK COMPATIBILITY IDEOGRAPH-F95A
+ * 	8B80 ??
+ * F95B CJK COMPATIBILITY IDEOGRAPH-F95B
+ * 	62CF ??
+ * F95C CJK COMPATIBILITY IDEOGRAPH-F95C
+ * 	6A02 ??
+ * F95D CJK COMPATIBILITY IDEOGRAPH-F95D
+ * 	8AFE ??
+ * F95E CJK COMPATIBILITY IDEOGRAPH-F95E
+ * 	4E39 ??
+ * F95F CJK COMPATIBILITY IDEOGRAPH-F95F
+ * 	5BE7 ??
+ * F960 CJK COMPATIBILITY IDEOGRAPH-F960
+ * 	6012 ??
+ * F961 CJK COMPATIBILITY IDEOGRAPH-F961
+ * 	7387 ??
+ * F962 CJK COMPATIBILITY IDEOGRAPH-F962
+ * 	7570 ??
+ * F963 CJK COMPATIBILITY IDEOGRAPH-F963
+ * 	5317 ??
+ * F964 CJK COMPATIBILITY IDEOGRAPH-F964
+ * 	78FB ??
+ * F965 CJK COMPATIBILITY IDEOGRAPH-F965
+ * 	4FBF ??
+ * F966 CJK COMPATIBILITY IDEOGRAPH-F966
+ * 	5FA9 ??
+ * F967 CJK COMPATIBILITY IDEOGRAPH-F967
+ * 	4E0D ??
+ * F968 CJK COMPATIBILITY IDEOGRAPH-F968
+ * 	6CCC ??
+ * F969 CJK COMPATIBILITY IDEOGRAPH-F969
+ * 	6578 ??
+ * F96A CJK COMPATIBILITY IDEOGRAPH-F96A
+ * 	7D22 ??
+ * F96B CJK COMPATIBILITY IDEOGRAPH-F96B
+ * 	53C3 ??
+ * F96C CJK COMPATIBILITY IDEOGRAPH-F96C
+ * 	585E ??
+ * F96D CJK COMPATIBILITY IDEOGRAPH-F96D
+ * 	7701 ??
+ * F96E CJK COMPATIBILITY IDEOGRAPH-F96E
+ * 	8449 ??
+ * F96F CJK COMPATIBILITY IDEOGRAPH-F96F
+ * 	8AAA ??
+ * F970 CJK COMPATIBILITY IDEOGRAPH-F970
+ * 	6BBA ??
+ * F971 CJK COMPATIBILITY IDEOGRAPH-F971
+ * 	8FB0 ??
+ * F972 CJK COMPATIBILITY IDEOGRAPH-F972
+ * 	6C88 ??
+ * F973 CJK COMPATIBILITY IDEOGRAPH-F973
+ * 	62FE ??
+ * F974 CJK COMPATIBILITY IDEOGRAPH-F974
+ * 	82E5 ??
+ * F975 CJK COMPATIBILITY IDEOGRAPH-F975
+ * 	63A0 ??
+ * F976 CJK COMPATIBILITY IDEOGRAPH-F976
+ * 	7565 ??
+ * F977 CJK COMPATIBILITY IDEOGRAPH-F977
+ * 	4EAE ??
+ * F978 CJK COMPATIBILITY IDEOGRAPH-F978
+ * 	5169 ??
+ * F979 CJK COMPATIBILITY IDEOGRAPH-F979
+ * 	51C9 ??
+ * F97A CJK COMPATIBILITY IDEOGRAPH-F97A
+ * 	6881 ??
+ * F97B CJK COMPATIBILITY IDEOGRAPH-F97B
+ * 	7CE7 ??
+ * F97C CJK COMPATIBILITY IDEOGRAPH-F97C
+ * 	826F ??
+ * F97D CJK COMPATIBILITY IDEOGRAPH-F97D
+ * 	8AD2 ??
+ * F97E CJK COMPATIBILITY IDEOGRAPH-F97E
+ * 	91CF ??
+ * F97F CJK COMPATIBILITY IDEOGRAPH-F97F
+ * 	52F5 ??
+ * F980 CJK COMPATIBILITY IDEOGRAPH-F980
+ * 	5442 ??
+ * F981 CJK COMPATIBILITY IDEOGRAPH-F981
+ * 	5973 ??
+ * F982 CJK COMPATIBILITY IDEOGRAPH-F982
+ * 	5EEC ??
+ * F983 CJK COMPATIBILITY IDEOGRAPH-F983
+ * 	65C5 ??
+ * F984 CJK COMPATIBILITY IDEOGRAPH-F984
+ * 	6FFE ??
+ * F985 CJK COMPATIBILITY IDEOGRAPH-F985
+ * 	792A ??
+ * F986 CJK COMPATIBILITY IDEOGRAPH-F986
+ * 	95AD ??
+ * F987 CJK COMPATIBILITY IDEOGRAPH-F987
+ * 	9A6A ??
+ * F988 CJK COMPATIBILITY IDEOGRAPH-F988
+ * 	9E97 ??
+ * F989 CJK COMPATIBILITY IDEOGRAPH-F989
+ * 	9ECE ??
+ * F98A CJK COMPATIBILITY IDEOGRAPH-F98A
+ * 	529B ??
+ * F98B CJK COMPATIBILITY IDEOGRAPH-F98B
+ * 	66C6 ??
+ * F98C CJK COMPATIBILITY IDEOGRAPH-F98C
+ * 	6B77 ??
+ * F98D CJK COMPATIBILITY IDEOGRAPH-F98D
+ * 	8F62 ??
+ * F98E CJK COMPATIBILITY IDEOGRAPH-F98E
+ * 	5E74 ??
+ * F98F CJK COMPATIBILITY IDEOGRAPH-F98F
+ * 	6190 ??
+ * F990 CJK COMPATIBILITY IDEOGRAPH-F990
+ * 	6200 ??
+ * F991 CJK COMPATIBILITY IDEOGRAPH-F991
+ * 	649A ??
+ * F992 CJK COMPATIBILITY IDEOGRAPH-F992
+ * 	6F23 ??
+ * F993 CJK COMPATIBILITY IDEOGRAPH-F993
+ * 	7149 ??
+ * F994 CJK COMPATIBILITY IDEOGRAPH-F994
+ * 	7489 ??
+ * F995 CJK COMPATIBILITY IDEOGRAPH-F995
+ * 	79CA ??
+ * F996 CJK COMPATIBILITY IDEOGRAPH-F996
+ * 	7DF4 ??
+ * F997 CJK COMPATIBILITY IDEOGRAPH-F997
+ * 	806F ??
+ * F998 CJK COMPATIBILITY IDEOGRAPH-F998
+ * 	8F26 ??
+ * F999 CJK COMPATIBILITY IDEOGRAPH-F999
+ * 	84EE ??
+ * F99A CJK COMPATIBILITY IDEOGRAPH-F99A
+ * 	9023 ??
+ * F99B CJK COMPATIBILITY IDEOGRAPH-F99B
+ * 	934A ??
+ * F99C CJK COMPATIBILITY IDEOGRAPH-F99C
+ * 	5217 ??
+ * F99D CJK COMPATIBILITY IDEOGRAPH-F99D
+ * 	52A3 ??
+ * F99E CJK COMPATIBILITY IDEOGRAPH-F99E
+ * 	54BD ??
+ * F99F CJK COMPATIBILITY IDEOGRAPH-F99F
+ * 	70C8 ??
+ * F9A0 CJK COMPATIBILITY IDEOGRAPH-F9A0
+ * 	88C2 ??
+ * F9A1 CJK COMPATIBILITY IDEOGRAPH-F9A1
+ * 	8AAA ??
+ * F9A2 CJK COMPATIBILITY IDEOGRAPH-F9A2
+ * 	5EC9 ??
+ * F9A3 CJK COMPATIBILITY IDEOGRAPH-F9A3
+ * 	5FF5 ??
+ * F9A4 CJK COMPATIBILITY IDEOGRAPH-F9A4
+ * 	637B ??
+ * F9A5 CJK COMPATIBILITY IDEOGRAPH-F9A5
+ * 	6BAE ??
+ * F9A6 CJK COMPATIBILITY IDEOGRAPH-F9A6
+ * 	7C3E ??
+ * F9A7 CJK COMPATIBILITY IDEOGRAPH-F9A7
+ * 	7375 ??
+ * F9A8 CJK COMPATIBILITY IDEOGRAPH-F9A8
+ * 	4EE4 ??
+ * F9A9 CJK COMPATIBILITY IDEOGRAPH-F9A9
+ * 	56F9 ??
+ * F9AA CJK COMPATIBILITY IDEOGRAPH-F9AA
+ * 	5BE7 ??
+ * F9AB CJK COMPATIBILITY IDEOGRAPH-F9AB
+ * 	5DBA ??
+ * F9AC CJK COMPATIBILITY IDEOGRAPH-F9AC
+ * 	601C ??
+ * F9AD CJK COMPATIBILITY IDEOGRAPH-F9AD
+ * 	73B2 ??
+ * F9AE CJK COMPATIBILITY IDEOGRAPH-F9AE
+ * 	7469 ??
+ * F9AF CJK COMPATIBILITY IDEOGRAPH-F9AF
+ * 	7F9A ??
+ * F9B0 CJK COMPATIBILITY IDEOGRAPH-F9B0
+ * 	8046 ??
+ * F9B1 CJK COMPATIBILITY IDEOGRAPH-F9B1
+ * 	9234 ??
+ * F9B2 CJK COMPATIBILITY IDEOGRAPH-F9B2
+ * 	96F6 ??
+ * F9B3 CJK COMPATIBILITY IDEOGRAPH-F9B3
+ * 	9748 ??
+ * F9B4 CJK COMPATIBILITY IDEOGRAPH-F9B4
+ * 	9818 ??
+ * F9B5 CJK COMPATIBILITY IDEOGRAPH-F9B5
+ * 	4F8B ??
+ * F9B6 CJK COMPATIBILITY IDEOGRAPH-F9B6
+ * 	79AE ??
+ * F9B7 CJK COMPATIBILITY IDEOGRAPH-F9B7
+ * 	91B4 ??
+ * F9B8 CJK COMPATIBILITY IDEOGRAPH-F9B8
+ * 	96B8 ??
+ * F9B9 CJK COMPATIBILITY IDEOGRAPH-F9B9
+ * 	60E1 ??
+ * F9BA CJK COMPATIBILITY IDEOGRAPH-F9BA
+ * 	4E86 ??
+ * F9BB CJK COMPATIBILITY IDEOGRAPH-F9BB
+ * 	50DA ??
+ * F9BC CJK COMPATIBILITY IDEOGRAPH-F9BC
+ * 	5BEE ??
+ * F9BD CJK COMPATIBILITY IDEOGRAPH-F9BD
+ * 	5C3F ??
+ * F9BE CJK COMPATIBILITY IDEOGRAPH-F9BE
+ * 	6599 ??
+ * F9BF CJK COMPATIBILITY IDEOGRAPH-F9BF
+ * 	6A02 ??
+ * F9C0 CJK COMPATIBILITY IDEOGRAPH-F9C0
+ * 	71CE ??
+ * F9C1 CJK COMPATIBILITY IDEOGRAPH-F9C1
+ * 	7642 ??
+ * F9C2 CJK COMPATIBILITY IDEOGRAPH-F9C2
+ * 	84FC ??
+ * F9C3 CJK COMPATIBILITY IDEOGRAPH-F9C3
+ * 	907C ??
+ * F9C4 CJK COMPATIBILITY IDEOGRAPH-F9C4
+ * 	9F8D ??
+ * F9C5 CJK COMPATIBILITY IDEOGRAPH-F9C5
+ * 	6688 ??
+ * F9C6 CJK COMPATIBILITY IDEOGRAPH-F9C6
+ * 	962E ??
+ * F9C7 CJK COMPATIBILITY IDEOGRAPH-F9C7
+ * 	5289 ??
+ * F9C8 CJK COMPATIBILITY IDEOGRAPH-F9C8
+ * 	677B ??
+ * F9C9 CJK COMPATIBILITY IDEOGRAPH-F9C9
+ * 	67F3 ??
+ * F9CA CJK COMPATIBILITY IDEOGRAPH-F9CA
+ * 	6D41 ??
+ * F9CB CJK COMPATIBILITY IDEOGRAPH-F9CB
+ * 	6E9C ??
+ * F9CC CJK COMPATIBILITY IDEOGRAPH-F9CC
+ * 	7409 ??
+ * F9CD CJK COMPATIBILITY IDEOGRAPH-F9CD
+ * 	7559 ??
+ * F9CE CJK COMPATIBILITY IDEOGRAPH-F9CE
+ * 	786B ??
+ * F9CF CJK COMPATIBILITY IDEOGRAPH-F9CF
+ * 	7D10 ??
+ * F9D0 CJK COMPATIBILITY IDEOGRAPH-F9D0
+ * 	985E ??
+ * F9D1 CJK COMPATIBILITY IDEOGRAPH-F9D1
+ * 	516D ??
+ * F9D2 CJK COMPATIBILITY IDEOGRAPH-F9D2
+ * 	622E ??
+ * F9D3 CJK COMPATIBILITY IDEOGRAPH-F9D3
+ * 	9678 ??
+ * F9D4 CJK COMPATIBILITY IDEOGRAPH-F9D4
+ * 	502B ??
+ * F9D5 CJK COMPATIBILITY IDEOGRAPH-F9D5
+ * 	5D19 ??
+ * F9D6 CJK COMPATIBILITY IDEOGRAPH-F9D6
+ * 	6DEA ??
+ * F9D7 CJK COMPATIBILITY IDEOGRAPH-F9D7
+ * 	8F2A ??
+ * F9D8 CJK COMPATIBILITY IDEOGRAPH-F9D8
+ * 	5F8B ??
+ * F9D9 CJK COMPATIBILITY IDEOGRAPH-F9D9
+ * 	6144 ??
+ * F9DA CJK COMPATIBILITY IDEOGRAPH-F9DA
+ * 	6817 ??
+ * F9DB CJK COMPATIBILITY IDEOGRAPH-F9DB
+ * 	7387 ??
+ * F9DC CJK COMPATIBILITY IDEOGRAPH-F9DC
+ * 	9686 ??
+ * F9DD CJK COMPATIBILITY IDEOGRAPH-F9DD
+ * 	5229 ??
+ * F9DE CJK COMPATIBILITY IDEOGRAPH-F9DE
+ * 	540F ??
+ * F9DF CJK COMPATIBILITY IDEOGRAPH-F9DF
+ * 	5C65 ??
+ * F9E0 CJK COMPATIBILITY IDEOGRAPH-F9E0
+ * 	6613 ??
+ * F9E1 CJK COMPATIBILITY IDEOGRAPH-F9E1
+ * 	674E ??
+ * F9E2 CJK COMPATIBILITY IDEOGRAPH-F9E2
+ * 	68A8 ??
+ * F9E3 CJK COMPATIBILITY IDEOGRAPH-F9E3
+ * 	6CE5 ??
+ * F9E4 CJK COMPATIBILITY IDEOGRAPH-F9E4
+ * 	7406 ??
+ * F9E5 CJK COMPATIBILITY IDEOGRAPH-F9E5
+ * 	75E2 ??
+ * F9E6 CJK COMPATIBILITY IDEOGRAPH-F9E6
+ * 	7F79 ??
+ * F9E7 CJK COMPATIBILITY IDEOGRAPH-F9E7
+ * 	88CF ??
+ * F9E8 CJK COMPATIBILITY IDEOGRAPH-F9E8
+ * 	88E1 ??
+ * F9E9 CJK COMPATIBILITY IDEOGRAPH-F9E9
+ * 	91CC ??
+ * F9EA CJK COMPATIBILITY IDEOGRAPH-F9EA
+ * 	96E2 ??
+ * F9EB CJK COMPATIBILITY IDEOGRAPH-F9EB
+ * 	533F ??
+ * F9EC CJK COMPATIBILITY IDEOGRAPH-F9EC
+ * 	6EBA ??
+ * F9ED CJK COMPATIBILITY IDEOGRAPH-F9ED
+ * 	541D ??
+ * F9EE CJK COMPATIBILITY IDEOGRAPH-F9EE
+ * 	71D0 ??
+ * F9EF CJK COMPATIBILITY IDEOGRAPH-F9EF
+ * 	7498 ??
+ * F9F0 CJK COMPATIBILITY IDEOGRAPH-F9F0
+ * 	85FA ??
+ * F9F1 CJK COMPATIBILITY IDEOGRAPH-F9F1
+ * 	96A3 ??
+ * F9F2 CJK COMPATIBILITY IDEOGRAPH-F9F2
+ * 	9C57 ??
+ * F9F3 CJK COMPATIBILITY IDEOGRAPH-F9F3
+ * 	9E9F ??
+ * F9F4 CJK COMPATIBILITY IDEOGRAPH-F9F4
+ * 	6797 ??
+ * F9F5 CJK COMPATIBILITY IDEOGRAPH-F9F5
+ * 	6DCB ??
+ * F9F6 CJK COMPATIBILITY IDEOGRAPH-F9F6
+ * 	81E8 ??
+ * F9F7 CJK COMPATIBILITY IDEOGRAPH-F9F7
+ * 	7ACB ??
+ * F9F8 CJK COMPATIBILITY IDEOGRAPH-F9F8
+ * 	7B20 ??
+ * F9F9 CJK COMPATIBILITY IDEOGRAPH-F9F9
+ * 	7C92 ??
+ * F9FA CJK COMPATIBILITY IDEOGRAPH-F9FA
+ * 	72C0 ??
+ * F9FB CJK COMPATIBILITY IDEOGRAPH-F9FB
+ * 	7099 ??
+ * F9FC CJK COMPATIBILITY IDEOGRAPH-F9FC
+ * 	8B58 ??
+ * F9FD CJK COMPATIBILITY IDEOGRAPH-F9FD
+ * 	4EC0 ??
+ * F9FE CJK COMPATIBILITY IDEOGRAPH-F9FE
+ * 	8336 ??
+ * F9FF CJK COMPATIBILITY IDEOGRAPH-F9FF
+ * 	523A ??
+ * FA00 CJK COMPATIBILITY IDEOGRAPH-FA00
+ * 	5207 ??
+ * FA01 CJK COMPATIBILITY IDEOGRAPH-FA01
+ * 	5EA6 ??
+ * FA02 CJK COMPATIBILITY IDEOGRAPH-FA02
+ * 	62D3 ??
+ * FA03 CJK COMPATIBILITY IDEOGRAPH-FA03
+ * 	7CD6 ??
+ * FA04 CJK COMPATIBILITY IDEOGRAPH-FA04
+ * 	5B85 ??
+ * FA05 CJK COMPATIBILITY IDEOGRAPH-FA05
+ * 	6D1E ??
+ * FA06 CJK COMPATIBILITY IDEOGRAPH-FA06
+ * 	66B4 ??
+ * FA07 CJK COMPATIBILITY IDEOGRAPH-FA07
+ * 	8F3B ??
+ * FA08 CJK COMPATIBILITY IDEOGRAPH-FA08
+ * 	884C ??
+ * FA09 CJK COMPATIBILITY IDEOGRAPH-FA09
+ * 	964D ??
+ * FA0A CJK COMPATIBILITY IDEOGRAPH-FA0A
+ * 	898B ??
+ * FA0B CJK COMPATIBILITY IDEOGRAPH-FA0B
+ * 	5ED3 ??
+ * FA0C CJK COMPATIBILITY IDEOGRAPH-FA0C
+ * 	5140 ??
+ * FA0D CJK COMPATIBILITY IDEOGRAPH-FA0D
+ * 	55C0 ??
+ * FA10 CJK COMPATIBILITY IDEOGRAPH-FA10
+ * 	585A ??
+ * FA12 CJK COMPATIBILITY IDEOGRAPH-FA12
+ * 	6674 ??
+ * FA15 CJK COMPATIBILITY IDEOGRAPH-FA15
+ * 	51DE ??
+ * FA16 CJK COMPATIBILITY IDEOGRAPH-FA16
+ * 	732A ??
+ * FA17 CJK COMPATIBILITY IDEOGRAPH-FA17
+ * 	76CA ??
+ * FA18 CJK COMPATIBILITY IDEOGRAPH-FA18
+ * 	793C ??
+ * FA19 CJK COMPATIBILITY IDEOGRAPH-FA19
+ * 	795E ??
+ * FA1A CJK COMPATIBILITY IDEOGRAPH-FA1A
+ * 	7965 ??
+ * FA1B CJK COMPATIBILITY IDEOGRAPH-FA1B
+ * 	798F ??
+ * FA1C CJK COMPATIBILITY IDEOGRAPH-FA1C
+ * 	9756 ??
+ * FA1D CJK COMPATIBILITY IDEOGRAPH-FA1D
+ * 	7CBE ??
+ * FA1E CJK COMPATIBILITY IDEOGRAPH-FA1E
+ * 	7FBD ??
+ * FA20 CJK COMPATIBILITY IDEOGRAPH-FA20
+ * 	8612 ??
+ * FA22 CJK COMPATIBILITY IDEOGRAPH-FA22
+ * 	8AF8 ??
+ * FA25 CJK COMPATIBILITY IDEOGRAPH-FA25
+ * 	9038 ??
+ * FA26 CJK COMPATIBILITY IDEOGRAPH-FA26
+ * 	90FD ??
+ * FA2A CJK COMPATIBILITY IDEOGRAPH-FA2A
+ * 	98EF ??
+ * FA2B CJK COMPATIBILITY IDEOGRAPH-FA2B
+ * 	98FC ??
+ * FA2C CJK COMPATIBILITY IDEOGRAPH-FA2C
+ * 	9928 ??
+ * FA2D CJK COMPATIBILITY IDEOGRAPH-FA2D
+ * 	9DB4 ??
+ * FA30 CJK COMPATIBILITY IDEOGRAPH-FA30
+ * 	4FAE ??
+ * FA31 CJK COMPATIBILITY IDEOGRAPH-FA31
+ * 	50E7 ??
+ * FA32 CJK COMPATIBILITY IDEOGRAPH-FA32
+ * 	514D ??
+ * FA33 CJK COMPATIBILITY IDEOGRAPH-FA33
+ * 	52C9 ??
+ * FA34 CJK COMPATIBILITY IDEOGRAPH-FA34
+ * 	52E4 ??
+ * FA35 CJK COMPATIBILITY IDEOGRAPH-FA35
+ * 	5351 ??
+ * FA36 CJK COMPATIBILITY IDEOGRAPH-FA36
+ * 	559D ??
+ * FA37 CJK COMPATIBILITY IDEOGRAPH-FA37
+ * 	5606 ??
+ * FA38 CJK COMPATIBILITY IDEOGRAPH-FA38
+ * 	5668 ??
+ * FA39 CJK COMPATIBILITY IDEOGRAPH-FA39
+ * 	5840 ??
+ * FA3A CJK COMPATIBILITY IDEOGRAPH-FA3A
+ * 	58A8 ??
+ * FA3B CJK COMPATIBILITY IDEOGRAPH-FA3B
+ * 	5C64 ??
+ * FA3C CJK COMPATIBILITY IDEOGRAPH-FA3C
+ * 	5C6E ??
+ * FA3D CJK COMPATIBILITY IDEOGRAPH-FA3D
+ * 	6094 ??
+ * FA3E CJK COMPATIBILITY IDEOGRAPH-FA3E
+ * 	6168 ??
+ * FA3F CJK COMPATIBILITY IDEOGRAPH-FA3F
+ * 	618E ??
+ * FA40 CJK COMPATIBILITY IDEOGRAPH-FA40
+ * 	61F2 ??
+ * FA41 CJK COMPATIBILITY IDEOGRAPH-FA41
+ * 	654F ??
+ * FA42 CJK COMPATIBILITY IDEOGRAPH-FA42
+ * 	65E2 ??
+ * FA43 CJK COMPATIBILITY IDEOGRAPH-FA43
+ * 	6691 ??
+ * FA44 CJK COMPATIBILITY IDEOGRAPH-FA44
+ * 	6885 ??
+ * FA45 CJK COMPATIBILITY IDEOGRAPH-FA45
+ * 	6D77 ??
+ * FA46 CJK COMPATIBILITY IDEOGRAPH-FA46
+ * 	6E1A ??
+ * FA47 CJK COMPATIBILITY IDEOGRAPH-FA47
+ * 	6F22 ??
+ * FA48 CJK COMPATIBILITY IDEOGRAPH-FA48
+ * 	716E ??
+ * FA49 CJK COMPATIBILITY IDEOGRAPH-FA49
+ * 	722B ??
+ * FA4A CJK COMPATIBILITY IDEOGRAPH-FA4A
+ * 	7422 ??
+ * FA4B CJK COMPATIBILITY IDEOGRAPH-FA4B
+ * 	7891 ??
+ * FA4C CJK COMPATIBILITY IDEOGRAPH-FA4C
+ * 	793E ??
+ * FA4D CJK COMPATIBILITY IDEOGRAPH-FA4D
+ * 	7949 ??
+ * FA4E CJK COMPATIBILITY IDEOGRAPH-FA4E
+ * 	7948 ??
+ * FA4F CJK COMPATIBILITY IDEOGRAPH-FA4F
+ * 	7950 ??
+ * FA50 CJK COMPATIBILITY IDEOGRAPH-FA50
+ * 	7956 ??
+ * FA51 CJK COMPATIBILITY IDEOGRAPH-FA51
+ * 	795D ??
+ * FA52 CJK COMPATIBILITY IDEOGRAPH-FA52
+ * 	798D ??
+ * FA53 CJK COMPATIBILITY IDEOGRAPH-FA53
+ * 	798E ??
+ * FA54 CJK COMPATIBILITY IDEOGRAPH-FA54
+ * 	7A40 ??
+ * FA55 CJK COMPATIBILITY IDEOGRAPH-FA55
+ * 	7A81 ??
+ * FA56 CJK COMPATIBILITY IDEOGRAPH-FA56
+ * 	7BC0 ??
+ * FA57 CJK COMPATIBILITY IDEOGRAPH-FA57
+ * 	7DF4 ??
+ * FA58 CJK COMPATIBILITY IDEOGRAPH-FA58
+ * 	7E09 ??
+ * FA59 CJK COMPATIBILITY IDEOGRAPH-FA59
+ * 	7E41 ??
+ * FA5A CJK COMPATIBILITY IDEOGRAPH-FA5A
+ * 	7F72 ??
+ * FA5B CJK COMPATIBILITY IDEOGRAPH-FA5B
+ * 	8005 ??
+ * FA5C CJK COMPATIBILITY IDEOGRAPH-FA5C
+ * 	81ED ??
+ * FA5D CJK COMPATIBILITY IDEOGRAPH-FA5D
+ * 	8279 ??
+ * FA5E CJK COMPATIBILITY IDEOGRAPH-FA5E
+ * 	8279 ??
+ * FA5F CJK COMPATIBILITY IDEOGRAPH-FA5F
+ * 	8457 ??
+ * FA60 CJK COMPATIBILITY IDEOGRAPH-FA60
+ * 	8910 ??
+ * FA61 CJK COMPATIBILITY IDEOGRAPH-FA61
+ * 	8996 ??
+ * FA62 CJK COMPATIBILITY IDEOGRAPH-FA62
+ * 	8B01 ??
+ * FA63 CJK COMPATIBILITY IDEOGRAPH-FA63
+ * 	8B39 ??
+ * FA64 CJK COMPATIBILITY IDEOGRAPH-FA64
+ * 	8CD3 ??
+ * FA65 CJK COMPATIBILITY IDEOGRAPH-FA65
+ * 	8D08 ??
+ * FA66 CJK COMPATIBILITY IDEOGRAPH-FA66
+ * 	8FB6 ??
+ * FA67 CJK COMPATIBILITY IDEOGRAPH-FA67
+ * 	9038 ??
+ * FA68 CJK COMPATIBILITY IDEOGRAPH-FA68
+ * 	96E3 ??
+ * FA69 CJK COMPATIBILITY IDEOGRAPH-FA69
+ * 	97FF ??
+ * FA6A CJK COMPATIBILITY IDEOGRAPH-FA6A
+ * 	983B ??
+ * FB00 LATIN SMALL LIGATURE FF
+ * 	0066 LATIN SMALL LETTER F
+ * 	0066 LATIN SMALL LETTER F
+ * FB01 LATIN SMALL LIGATURE FI
+ * 	0066 LATIN SMALL LETTER F
+ * 	0069 LATIN SMALL LETTER I
+ * FB02 LATIN SMALL LIGATURE FL
+ * 	0066 LATIN SMALL LETTER F
+ * 	006C LATIN SMALL LETTER L
+ * FB03 LATIN SMALL LIGATURE FFI
+ * 	0066 LATIN SMALL LETTER F
+ * 	0066 LATIN SMALL LETTER F
+ * 	0069 LATIN SMALL LETTER I
+ * FB04 LATIN SMALL LIGATURE FFL
+ * 	0066 LATIN SMALL LETTER F
+ * 	0066 LATIN SMALL LETTER F
+ * 	006C LATIN SMALL LETTER L
+ * FB05 LATIN SMALL LIGATURE LONG S T
+ * 	0074 LATIN SMALL LETTER T
+ * 	0073 LATIN SMALL LETTER S
+ * FB06 LATIN SMALL LIGATURE ST
+ * 	0073 LATIN SMALL LETTER S
+ * 	0074 LATIN SMALL LETTER T
+ * FB13 ARMENIAN SMALL LIGATURE MEN NOW
+ * 	0574 ARMENIAN SMALL LETTER MEN
+ * 	0576 ARMENIAN SMALL LETTER NOW
+ * FB14 ARMENIAN SMALL LIGATURE MEN ECH
+ * 	0574 ARMENIAN SMALL LETTER MEN
+ * 	0565 ARMENIAN SMALL LETTER ECH
+ * FB15 ARMENIAN SMALL LIGATURE MEN INI
+ * 	0574 ARMENIAN SMALL LETTER MEN
+ * 	056B ARMENIAN SMALL LETTER INI
+ * FB16 ARMENIAN SMALL LIGATURE VEW NOW
+ * 	057E ARMENIAN SMALL LETTER VEW
+ * 	0576 ARMENIAN SMALL LETTER NOW
+ * FB17 ARMENIAN SMALL LIGATURE MEN XEH
+ * 	0574 ARMENIAN SMALL LETTER MEN
+ * 	056D ARMENIAN SMALL LETTER XEH
+ * FB1D HEBREW LETTER YOD WITH HIRIQ
+ * 	05D9 HEBREW LETTER YOD
+ * FB1F HEBREW LIGATURE YIDDISH YOD YOD PATAH
+ * 	05F2 HEBREW LIGATURE YIDDISH DOUBLE YOD
+ * FB20 HEBREW LETTER ALTERNATIVE AYIN
+ * 	05E2 HEBREW LETTER AYIN
+ * FB21 HEBREW LETTER WIDE ALEF
+ * 	05D0 HEBREW LETTER ALEF
+ * FB22 HEBREW LETTER WIDE DALET
+ * 	05D3 HEBREW LETTER DALET
+ * FB23 HEBREW LETTER WIDE HE
+ * 	05D4 HEBREW LETTER HE
+ * FB24 HEBREW LETTER WIDE KAF
+ * 	05DB HEBREW LETTER KAF
+ * FB25 HEBREW LETTER WIDE LAMED
+ * 	05DC HEBREW LETTER LAMED
+ * FB26 HEBREW LETTER WIDE FINAL MEM
+ * 	05DD HEBREW LETTER FINAL MEM
+ * FB27 HEBREW LETTER WIDE RESH
+ * 	05E8 HEBREW LETTER RESH
+ * FB28 HEBREW LETTER WIDE TAV
+ * 	05EA HEBREW LETTER TAV
+ * FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN
+ * 	002B PLUS SIGN
+ * FB2A HEBREW LETTER SHIN WITH SHIN DOT
+ * 	05E9 HEBREW LETTER SHIN
+ * FB2B HEBREW LETTER SHIN WITH SIN DOT
+ * 	05E9 HEBREW LETTER SHIN
+ * FB2C HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT
+ * 	05E9 HEBREW LETTER SHIN
+ * FB2D HEBREW LETTER SHIN WITH DAGESH AND SIN DOT
+ * 	05E9 HEBREW LETTER SHIN
+ * FB2E HEBREW LETTER ALEF WITH PATAH
+ * 	05D0 HEBREW LETTER ALEF
+ * FB2F HEBREW LETTER ALEF WITH QAMATS
+ * 	05D0 HEBREW LETTER ALEF
+ * FB30 HEBREW LETTER ALEF WITH MAPIQ
+ * 	05D0 HEBREW LETTER ALEF
+ * FB31 HEBREW LETTER BET WITH DAGESH
+ * 	05D1 HEBREW LETTER BET
+ * FB32 HEBREW LETTER GIMEL WITH DAGESH
+ * 	05D2 HEBREW LETTER GIMEL
+ * FB33 HEBREW LETTER DALET WITH DAGESH
+ * 	05D3 HEBREW LETTER DALET
+ * FB34 HEBREW LETTER HE WITH MAPIQ
+ * 	05D4 HEBREW LETTER HE
+ * FB35 HEBREW LETTER VAV WITH DAGESH
+ * 	05D5 HEBREW LETTER VAV
+ * FB36 HEBREW LETTER ZAYIN WITH DAGESH
+ * 	05D6 HEBREW LETTER ZAYIN
+ * FB38 HEBREW LETTER TET WITH DAGESH
+ * 	05D8 HEBREW LETTER TET
+ * FB39 HEBREW LETTER YOD WITH DAGESH
+ * 	05D9 HEBREW LETTER YOD
+ * FB3A HEBREW LETTER FINAL KAF WITH DAGESH
+ * 	05DA HEBREW LETTER FINAL KAF
+ * FB3B HEBREW LETTER KAF WITH DAGESH
+ * 	05DB HEBREW LETTER KAF
+ * FB3C HEBREW LETTER LAMED WITH DAGESH
+ * 	05DC HEBREW LETTER LAMED
+ * FB3E HEBREW LETTER MEM WITH DAGESH
+ * 	05DE HEBREW LETTER MEM
+ * FB40 HEBREW LETTER NUN WITH DAGESH
+ * 	05E0 HEBREW LETTER NUN
+ * FB41 HEBREW LETTER SAMEKH WITH DAGESH
+ * 	05E1 HEBREW LETTER SAMEKH
+ * FB43 HEBREW LETTER FINAL PE WITH DAGESH
+ * 	05E3 HEBREW LETTER FINAL PE
+ * FB44 HEBREW LETTER PE WITH DAGESH
+ * 	05E4 HEBREW LETTER PE
+ * FB46 HEBREW LETTER TSADI WITH DAGESH
+ * 	05E6 HEBREW LETTER TSADI
+ * FB47 HEBREW LETTER QOF WITH DAGESH
+ * 	05E7 HEBREW LETTER QOF
+ * FB48 HEBREW LETTER RESH WITH DAGESH
+ * 	05E8 HEBREW LETTER RESH
+ * FB49 HEBREW LETTER SHIN WITH DAGESH
+ * 	05E9 HEBREW LETTER SHIN
+ * FB4A HEBREW LETTER TAV WITH DAGESH
+ * 	05EA HEBREW LETTER TAV
+ * FB4B HEBREW LETTER VAV WITH HOLAM
+ * 	05D5 HEBREW LETTER VAV
+ * FB4C HEBREW LETTER BET WITH RAFE
+ * 	05D1 HEBREW LETTER BET
+ * FB4D HEBREW LETTER KAF WITH RAFE
+ * 	05DB HEBREW LETTER KAF
+ * FB4E HEBREW LETTER PE WITH RAFE
+ * 	05E4 HEBREW LETTER PE
+ * FB4F HEBREW LIGATURE ALEF LAMED
+ * 	05D0 HEBREW LETTER ALEF
+ * 	05DC HEBREW LETTER LAMED
+ * FB50 ARABIC LETTER ALEF WASLA ISOLATED FORM
+ * 	0671 ARABIC LETTER ALEF WASLA
+ * FB51 ARABIC LETTER ALEF WASLA FINAL FORM
+ * 	0671 ARABIC LETTER ALEF WASLA
+ * FB52 ARABIC LETTER BEEH ISOLATED FORM
+ * 	067B ARABIC LETTER BEEH
+ * FB53 ARABIC LETTER BEEH FINAL FORM
+ * 	067B ARABIC LETTER BEEH
+ * FB54 ARABIC LETTER BEEH INITIAL FORM
+ * 	067B ARABIC LETTER BEEH
+ * FB55 ARABIC LETTER BEEH MEDIAL FORM
+ * 	067B ARABIC LETTER BEEH
+ * FB56 ARABIC LETTER PEH ISOLATED FORM
+ * 	067E ARABIC LETTER PEH
+ * FB57 ARABIC LETTER PEH FINAL FORM
+ * 	067E ARABIC LETTER PEH
+ * FB58 ARABIC LETTER PEH INITIAL FORM
+ * 	067E ARABIC LETTER PEH
+ * FB59 ARABIC LETTER PEH MEDIAL FORM
+ * 	067E ARABIC LETTER PEH
+ * FB5A ARABIC LETTER BEHEH ISOLATED FORM
+ * 	0680 ARABIC LETTER BEHEH
+ * FB5B ARABIC LETTER BEHEH FINAL FORM
+ * 	0680 ARABIC LETTER BEHEH
+ * FB5C ARABIC LETTER BEHEH INITIAL FORM
+ * 	0680 ARABIC LETTER BEHEH
+ * FB5D ARABIC LETTER BEHEH MEDIAL FORM
+ * 	0680 ARABIC LETTER BEHEH
+ * FB5E ARABIC LETTER TTEHEH ISOLATED FORM
+ * 	067A ARABIC LETTER TTEHEH
+ * FB5F ARABIC LETTER TTEHEH FINAL FORM
+ * 	067A ARABIC LETTER TTEHEH
+ * FB60 ARABIC LETTER TTEHEH INITIAL FORM
+ * 	067A ARABIC LETTER TTEHEH
+ * FB61 ARABIC LETTER TTEHEH MEDIAL FORM
+ * 	067A ARABIC LETTER TTEHEH
+ * FB62 ARABIC LETTER TEHEH ISOLATED FORM
+ * 	067F ARABIC LETTER TEHEH
+ * FB63 ARABIC LETTER TEHEH FINAL FORM
+ * 	067F ARABIC LETTER TEHEH
+ * FB64 ARABIC LETTER TEHEH INITIAL FORM
+ * 	067F ARABIC LETTER TEHEH
+ * FB65 ARABIC LETTER TEHEH MEDIAL FORM
+ * 	067F ARABIC LETTER TEHEH
+ * FB66 ARABIC LETTER TTEH ISOLATED FORM
+ * 	0679 ARABIC LETTER TTEH
+ * FB67 ARABIC LETTER TTEH FINAL FORM
+ * 	0679 ARABIC LETTER TTEH
+ * FB68 ARABIC LETTER TTEH INITIAL FORM
+ * 	0679 ARABIC LETTER TTEH
+ * FB69 ARABIC LETTER TTEH MEDIAL FORM
+ * 	0679 ARABIC LETTER TTEH
+ * FB6A ARABIC LETTER VEH ISOLATED FORM
+ * 	06A4 ARABIC LETTER VEH
+ * FB6B ARABIC LETTER VEH FINAL FORM
+ * 	06A4 ARABIC LETTER VEH
+ * FB6C ARABIC LETTER VEH INITIAL FORM
+ * 	06A4 ARABIC LETTER VEH
+ * FB6D ARABIC LETTER VEH MEDIAL FORM
+ * 	06A4 ARABIC LETTER VEH
+ * FB6E ARABIC LETTER PEHEH ISOLATED FORM
+ * 	06A6 ARABIC LETTER PEHEH
+ * FB6F ARABIC LETTER PEHEH FINAL FORM
+ * 	06A6 ARABIC LETTER PEHEH
+ * FB70 ARABIC LETTER PEHEH INITIAL FORM
+ * 	06A6 ARABIC LETTER PEHEH
+ * FB71 ARABIC LETTER PEHEH MEDIAL FORM
+ * 	06A6 ARABIC LETTER PEHEH
+ * FB72 ARABIC LETTER DYEH ISOLATED FORM
+ * 	0684 ARABIC LETTER DYEH
+ * FB73 ARABIC LETTER DYEH FINAL FORM
+ * 	0684 ARABIC LETTER DYEH
+ * FB74 ARABIC LETTER DYEH INITIAL FORM
+ * 	0684 ARABIC LETTER DYEH
+ * FB75 ARABIC LETTER DYEH MEDIAL FORM
+ * 	0684 ARABIC LETTER DYEH
+ * FB76 ARABIC LETTER NYEH ISOLATED FORM
+ * 	0683 ARABIC LETTER NYEH
+ * FB77 ARABIC LETTER NYEH FINAL FORM
+ * 	0683 ARABIC LETTER NYEH
+ * FB78 ARABIC LETTER NYEH INITIAL FORM
+ * 	0683 ARABIC LETTER NYEH
+ * FB79 ARABIC LETTER NYEH MEDIAL FORM
+ * 	0683 ARABIC LETTER NYEH
+ * FB7A ARABIC LETTER TCHEH ISOLATED FORM
+ * 	0686 ARABIC LETTER TCHEH
+ * FB7B ARABIC LETTER TCHEH FINAL FORM
+ * 	0686 ARABIC LETTER TCHEH
+ * FB7C ARABIC LETTER TCHEH INITIAL FORM
+ * 	0686 ARABIC LETTER TCHEH
+ * FB7D ARABIC LETTER TCHEH MEDIAL FORM
+ * 	0686 ARABIC LETTER TCHEH
+ * FB7E ARABIC LETTER TCHEHEH ISOLATED FORM
+ * 	0687 ARABIC LETTER TCHEHEH
+ * FB7F ARABIC LETTER TCHEHEH FINAL FORM
+ * 	0687 ARABIC LETTER TCHEHEH
+ * FB80 ARABIC LETTER TCHEHEH INITIAL FORM
+ * 	0687 ARABIC LETTER TCHEHEH
+ * FB81 ARABIC LETTER TCHEHEH MEDIAL FORM
+ * 	0687 ARABIC LETTER TCHEHEH
+ * FB82 ARABIC LETTER DDAHAL ISOLATED FORM
+ * 	068D ARABIC LETTER DDAHAL
+ * FB83 ARABIC LETTER DDAHAL FINAL FORM
+ * 	068D ARABIC LETTER DDAHAL
+ * FB84 ARABIC LETTER DAHAL ISOLATED FORM
+ * 	068C ARABIC LETTER DAHAL
+ * FB85 ARABIC LETTER DAHAL FINAL FORM
+ * 	068C ARABIC LETTER DAHAL
+ * FB86 ARABIC LETTER DUL ISOLATED FORM
+ * 	068E ARABIC LETTER DUL
+ * FB87 ARABIC LETTER DUL FINAL FORM
+ * 	068E ARABIC LETTER DUL
+ * FB88 ARABIC LETTER DDAL ISOLATED FORM
+ * 	0688 ARABIC LETTER DDAL
+ * FB89 ARABIC LETTER DDAL FINAL FORM
+ * 	0688 ARABIC LETTER DDAL
+ * FB8A ARABIC LETTER JEH ISOLATED FORM
+ * 	0698 ARABIC LETTER JEH
+ * FB8B ARABIC LETTER JEH FINAL FORM
+ * 	0698 ARABIC LETTER JEH
+ * FB8C ARABIC LETTER RREH ISOLATED FORM
+ * 	0691 ARABIC LETTER RREH
+ * FB8D ARABIC LETTER RREH FINAL FORM
+ * 	0691 ARABIC LETTER RREH
+ * FB8E ARABIC LETTER KEHEH ISOLATED FORM
+ * 	06A9 ARABIC LETTER KEHEH
+ * FB8F ARABIC LETTER KEHEH FINAL FORM
+ * 	06A9 ARABIC LETTER KEHEH
+ * FB90 ARABIC LETTER KEHEH INITIAL FORM
+ * 	06A9 ARABIC LETTER KEHEH
+ * FB91 ARABIC LETTER KEHEH MEDIAL FORM
+ * 	06A9 ARABIC LETTER KEHEH
+ * FB92 ARABIC LETTER GAF ISOLATED FORM
+ * 	06AF ARABIC LETTER GAF
+ * FB93 ARABIC LETTER GAF FINAL FORM
+ * 	06AF ARABIC LETTER GAF
+ * FB94 ARABIC LETTER GAF INITIAL FORM
+ * 	06AF ARABIC LETTER GAF
+ * FB95 ARABIC LETTER GAF MEDIAL FORM
+ * 	06AF ARABIC LETTER GAF
+ * FB96 ARABIC LETTER GUEH ISOLATED FORM
+ * 	06B3 ARABIC LETTER GUEH
+ * FB97 ARABIC LETTER GUEH FINAL FORM
+ * 	06B3 ARABIC LETTER GUEH
+ * FB98 ARABIC LETTER GUEH INITIAL FORM
+ * 	06B3 ARABIC LETTER GUEH
+ * FB99 ARABIC LETTER GUEH MEDIAL FORM
+ * 	06B3 ARABIC LETTER GUEH
+ * FB9A ARABIC LETTER NGOEH ISOLATED FORM
+ * 	06B1 ARABIC LETTER NGOEH
+ * FB9B ARABIC LETTER NGOEH FINAL FORM
+ * 	06B1 ARABIC LETTER NGOEH
+ * FB9C ARABIC LETTER NGOEH INITIAL FORM
+ * 	06B1 ARABIC LETTER NGOEH
+ * FB9D ARABIC LETTER NGOEH MEDIAL FORM
+ * 	06B1 ARABIC LETTER NGOEH
+ * FB9E ARABIC LETTER NOON GHUNNA ISOLATED FORM
+ * 	06BA ARABIC LETTER NOON GHUNNA
+ * FB9F ARABIC LETTER NOON GHUNNA FINAL FORM
+ * 	06BA ARABIC LETTER NOON GHUNNA
+ * FBA0 ARABIC LETTER RNOON ISOLATED FORM
+ * 	06BB ARABIC LETTER RNOON
+ * FBA1 ARABIC LETTER RNOON FINAL FORM
+ * 	06BB ARABIC LETTER RNOON
+ * FBA2 ARABIC LETTER RNOON INITIAL FORM
+ * 	06BB ARABIC LETTER RNOON
+ * FBA3 ARABIC LETTER RNOON MEDIAL FORM
+ * 	06BB ARABIC LETTER RNOON
+ * FBA4 ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM
+ * 	06D5 ARABIC LETTER AE
+ * FBA5 ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM
+ * 	06D5 ARABIC LETTER AE
+ * FBA6 ARABIC LETTER HEH GOAL ISOLATED FORM
+ * 	06C1 ARABIC LETTER HEH GOAL
+ * FBA7 ARABIC LETTER HEH GOAL FINAL FORM
+ * 	06C1 ARABIC LETTER HEH GOAL
+ * FBA8 ARABIC LETTER HEH GOAL INITIAL FORM
+ * 	06C1 ARABIC LETTER HEH GOAL
+ * FBA9 ARABIC LETTER HEH GOAL MEDIAL FORM
+ * 	06C1 ARABIC LETTER HEH GOAL
+ * FBAA ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM
+ * 	06BE ARABIC LETTER HEH DOACHASHMEE
+ * FBAB ARABIC LETTER HEH DOACHASHMEE FINAL FORM
+ * 	06BE ARABIC LETTER HEH DOACHASHMEE
+ * FBAC ARABIC LETTER HEH DOACHASHMEE INITIAL FORM
+ * 	06BE ARABIC LETTER HEH DOACHASHMEE
+ * FBAD ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM
+ * 	06BE ARABIC LETTER HEH DOACHASHMEE
+ * FBAE ARABIC LETTER YEH BARREE ISOLATED FORM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FBAF ARABIC LETTER YEH BARREE FINAL FORM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FBB0 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FBB1 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FBD3 ARABIC LETTER NG ISOLATED FORM
+ * 	06AD ARABIC LETTER NG
+ * FBD4 ARABIC LETTER NG FINAL FORM
+ * 	06AD ARABIC LETTER NG
+ * FBD5 ARABIC LETTER NG INITIAL FORM
+ * 	06AD ARABIC LETTER NG
+ * FBD6 ARABIC LETTER NG MEDIAL FORM
+ * 	06AD ARABIC LETTER NG
+ * FBD7 ARABIC LETTER U ISOLATED FORM
+ * 	06C7 ARABIC LETTER U
+ * FBD8 ARABIC LETTER U FINAL FORM
+ * 	06C7 ARABIC LETTER U
+ * FBD9 ARABIC LETTER OE ISOLATED FORM
+ * 	06C6 ARABIC LETTER OE
+ * FBDA ARABIC LETTER OE FINAL FORM
+ * 	06C6 ARABIC LETTER OE
+ * FBDB ARABIC LETTER YU ISOLATED FORM
+ * 	06C8 ARABIC LETTER YU
+ * FBDC ARABIC LETTER YU FINAL FORM
+ * 	06C8 ARABIC LETTER YU
+ * FBDD ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM
+ * 	06C7 ARABIC LETTER U
+ * 	0674 ARABIC LETTER HIGH HAMZA
+ * FBDE ARABIC LETTER VE ISOLATED FORM
+ * 	06CB ARABIC LETTER VE
+ * FBDF ARABIC LETTER VE FINAL FORM
+ * 	06CB ARABIC LETTER VE
+ * FBE0 ARABIC LETTER KIRGHIZ OE ISOLATED FORM
+ * 	06C5 ARABIC LETTER KIRGHIZ OE
+ * FBE1 ARABIC LETTER KIRGHIZ OE FINAL FORM
+ * 	06C5 ARABIC LETTER KIRGHIZ OE
+ * FBE2 ARABIC LETTER KIRGHIZ YU ISOLATED FORM
+ * 	06C9 ARABIC LETTER KIRGHIZ YU
+ * FBE3 ARABIC LETTER KIRGHIZ YU FINAL FORM
+ * 	06C9 ARABIC LETTER KIRGHIZ YU
+ * FBE4 ARABIC LETTER E ISOLATED FORM
+ * 	06D0 ARABIC LETTER E
+ * FBE5 ARABIC LETTER E FINAL FORM
+ * 	06D0 ARABIC LETTER E
+ * FBE6 ARABIC LETTER E INITIAL FORM
+ * 	06D0 ARABIC LETTER E
+ * FBE7 ARABIC LETTER E MEDIAL FORM
+ * 	06D0 ARABIC LETTER E
+ * FBE8 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FBE9 ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FBEA ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * 	064A ARABIC LETTER YEH
+ * FBEB ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * 	064A ARABIC LETTER YEH
+ * FBEC ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM
+ * 	06D5 ARABIC LETTER AE
+ * 	064A ARABIC LETTER YEH
+ * FBED ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM
+ * 	06D5 ARABIC LETTER AE
+ * 	064A ARABIC LETTER YEH
+ * FBEE ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM
+ * 	0648 ARABIC LETTER WAW
+ * 	064A ARABIC LETTER YEH
+ * FBEF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM
+ * 	0648 ARABIC LETTER WAW
+ * 	064A ARABIC LETTER YEH
+ * FBF0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM
+ * 	06C7 ARABIC LETTER U
+ * 	064A ARABIC LETTER YEH
+ * FBF1 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM
+ * 	06C7 ARABIC LETTER U
+ * 	064A ARABIC LETTER YEH
+ * FBF2 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM
+ * 	06C6 ARABIC LETTER OE
+ * 	064A ARABIC LETTER YEH
+ * FBF3 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM
+ * 	06C6 ARABIC LETTER OE
+ * 	064A ARABIC LETTER YEH
+ * FBF4 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM
+ * 	06C8 ARABIC LETTER YU
+ * 	064A ARABIC LETTER YEH
+ * FBF5 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM
+ * 	06C8 ARABIC LETTER YU
+ * 	064A ARABIC LETTER YEH
+ * FBF6 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM
+ * 	06D0 ARABIC LETTER E
+ * 	064A ARABIC LETTER YEH
+ * FBF7 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM
+ * 	06D0 ARABIC LETTER E
+ * 	064A ARABIC LETTER YEH
+ * FBF8 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM
+ * 	06D0 ARABIC LETTER E
+ * 	064A ARABIC LETTER YEH
+ * FBF9 ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	064A ARABIC LETTER YEH
+ * FBFA ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	064A ARABIC LETTER YEH
+ * FBFB ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	064A ARABIC LETTER YEH
+ * FBFC ARABIC LETTER FARSI YEH ISOLATED FORM
+ * 	06CC ARABIC LETTER FARSI YEH
+ * FBFD ARABIC LETTER FARSI YEH FINAL FORM
+ * 	06CC ARABIC LETTER FARSI YEH
+ * FBFE ARABIC LETTER FARSI YEH INITIAL FORM
+ * 	06CC ARABIC LETTER FARSI YEH
+ * FBFF ARABIC LETTER FARSI YEH MEDIAL FORM
+ * 	06CC ARABIC LETTER FARSI YEH
+ * FC00 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FC01 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FC02 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FC03 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	064A ARABIC LETTER YEH
+ * FC04 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	064A ARABIC LETTER YEH
+ * FC05 ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062C ARABIC LETTER JEEM
+ * FC06 ARABIC LIGATURE BEH WITH HAH ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062D ARABIC LETTER HAH
+ * FC07 ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062E ARABIC LETTER KHAH
+ * FC08 ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC09 ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC0A ARABIC LIGATURE BEH WITH YEH ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	064A ARABIC LETTER YEH
+ * FC0B ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062C ARABIC LETTER JEEM
+ * FC0C ARABIC LIGATURE TEH WITH HAH ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062D ARABIC LETTER HAH
+ * FC0D ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062E ARABIC LETTER KHAH
+ * FC0E ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC0F ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC10 ARABIC LIGATURE TEH WITH YEH ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * 	064A ARABIC LETTER YEH
+ * FC11 ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM
+ * 	062B ARABIC LETTER THEH
+ * 	062C ARABIC LETTER JEEM
+ * FC12 ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC13 ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC14 ARABIC LIGATURE THEH WITH YEH ISOLATED FORM
+ * 	062B ARABIC LETTER THEH
+ * 	064A ARABIC LETTER YEH
+ * FC15 ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FC16 ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FC17 ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FC18 ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FC19 ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	062C ARABIC LETTER JEEM
+ * FC1A ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	062D ARABIC LETTER HAH
+ * FC1B ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FC1C ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062C ARABIC LETTER JEEM
+ * FC1D ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062D ARABIC LETTER HAH
+ * FC1E ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062E ARABIC LETTER KHAH
+ * FC1F ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FC20 ARABIC LIGATURE SAD WITH HAH ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062D ARABIC LETTER HAH
+ * FC21 ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0645 ARABIC LETTER MEEM
+ * FC22 ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062C ARABIC LETTER JEEM
+ * FC23 ARABIC LIGATURE DAD WITH HAH ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062D ARABIC LETTER HAH
+ * FC24 ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062E ARABIC LETTER KHAH
+ * FC25 ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0645 ARABIC LETTER MEEM
+ * FC26 ARABIC LIGATURE TAH WITH HAH ISOLATED FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	062D ARABIC LETTER HAH
+ * FC27 ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * FC28 ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM
+ * 	0638 ARABIC LETTER ZAH
+ * 	0645 ARABIC LETTER MEEM
+ * FC29 ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	062C ARABIC LETTER JEEM
+ * FC2A ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * FC2B ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	062C ARABIC LETTER JEEM
+ * FC2C ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0645 ARABIC LETTER MEEM
+ * FC2D ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062C ARABIC LETTER JEEM
+ * FC2E ARABIC LIGATURE FEH WITH HAH ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062D ARABIC LETTER HAH
+ * FC2F ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062E ARABIC LETTER KHAH
+ * FC30 ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC31 ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC32 ARABIC LIGATURE FEH WITH YEH ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	064A ARABIC LETTER YEH
+ * FC33 ARABIC LIGATURE QAF WITH HAH ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	062D ARABIC LETTER HAH
+ * FC34 ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * FC35 ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC36 ARABIC LIGATURE QAF WITH YEH ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	064A ARABIC LETTER YEH
+ * FC37 ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0627 ARABIC LETTER ALEF
+ * FC38 ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062C ARABIC LETTER JEEM
+ * FC39 ARABIC LIGATURE KAF WITH HAH ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062D ARABIC LETTER HAH
+ * FC3A ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062E ARABIC LETTER KHAH
+ * FC3B ARABIC LIGATURE KAF WITH LAM ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0644 ARABIC LETTER LAM
+ * FC3C ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * FC3D ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC3E ARABIC LIGATURE KAF WITH YEH ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	064A ARABIC LETTER YEH
+ * FC3F ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * FC40 ARABIC LIGATURE LAM WITH HAH ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * FC41 ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062E ARABIC LETTER KHAH
+ * FC42 ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FC43 ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC44 ARABIC LIGATURE LAM WITH YEH ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	064A ARABIC LETTER YEH
+ * FC45 ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * FC46 ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FC47 ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * FC48 ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FC49 ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC4A ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FC4B ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * FC4C ARABIC LIGATURE NOON WITH HAH ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062D ARABIC LETTER HAH
+ * FC4D ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062E ARABIC LETTER KHAH
+ * FC4E ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * FC4F ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC50 ARABIC LIGATURE NOON WITH YEH ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	064A ARABIC LETTER YEH
+ * FC51 ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	062C ARABIC LETTER JEEM
+ * FC52 ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC53 ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC54 ARABIC LIGATURE HEH WITH YEH ISOLATED FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	064A ARABIC LETTER YEH
+ * FC55 ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062C ARABIC LETTER JEEM
+ * FC56 ARABIC LIGATURE YEH WITH HAH ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062D ARABIC LETTER HAH
+ * FC57 ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062E ARABIC LETTER KHAH
+ * FC58 ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC59 ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC5A ARABIC LIGATURE YEH WITH YEH ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * 	064A ARABIC LETTER YEH
+ * FC5B ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM
+ * 	0630 ARABIC LETTER THAL
+ * FC5C ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM
+ * 	0631 ARABIC LETTER REH
+ * FC5D ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC5E ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM
+ * 	0020 SPACE
+ * FC5F ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM
+ * 	0020 SPACE
+ * FC60 ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM
+ * 	0020 SPACE
+ * FC61 ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM
+ * 	0020 SPACE
+ * FC62 ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM
+ * 	0020 SPACE
+ * FC63 ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM
+ * 	0020 SPACE
+ * FC64 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM
+ * 	0631 ARABIC LETTER REH
+ * 	064A ARABIC LETTER YEH
+ * FC65 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM
+ * 	0632 ARABIC LETTER ZAIN
+ * 	064A ARABIC LETTER YEH
+ * FC66 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FC67 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	064A ARABIC LETTER YEH
+ * FC68 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	064A ARABIC LETTER YEH
+ * FC69 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	064A ARABIC LETTER YEH
+ * FC6A ARABIC LIGATURE BEH WITH REH FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0631 ARABIC LETTER REH
+ * FC6B ARABIC LIGATURE BEH WITH ZAIN FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0632 ARABIC LETTER ZAIN
+ * FC6C ARABIC LIGATURE BEH WITH MEEM FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC6D ARABIC LIGATURE BEH WITH NOON FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0646 ARABIC LETTER NOON
+ * FC6E ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC6F ARABIC LIGATURE BEH WITH YEH FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	064A ARABIC LETTER YEH
+ * FC70 ARABIC LIGATURE TEH WITH REH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0631 ARABIC LETTER REH
+ * FC71 ARABIC LIGATURE TEH WITH ZAIN FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0632 ARABIC LETTER ZAIN
+ * FC72 ARABIC LIGATURE TEH WITH MEEM FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC73 ARABIC LIGATURE TEH WITH NOON FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0646 ARABIC LETTER NOON
+ * FC74 ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC75 ARABIC LIGATURE TEH WITH YEH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	064A ARABIC LETTER YEH
+ * FC76 ARABIC LIGATURE THEH WITH REH FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0631 ARABIC LETTER REH
+ * FC77 ARABIC LIGATURE THEH WITH ZAIN FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0632 ARABIC LETTER ZAIN
+ * FC78 ARABIC LIGATURE THEH WITH MEEM FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC79 ARABIC LIGATURE THEH WITH NOON FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0646 ARABIC LETTER NOON
+ * FC7A ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC7B ARABIC LIGATURE THEH WITH YEH FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	064A ARABIC LETTER YEH
+ * FC7C ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC7D ARABIC LIGATURE FEH WITH YEH FINAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	064A ARABIC LETTER YEH
+ * FC7E ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC7F ARABIC LIGATURE QAF WITH YEH FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	064A ARABIC LETTER YEH
+ * FC80 ARABIC LIGATURE KAF WITH ALEF FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0627 ARABIC LETTER ALEF
+ * FC81 ARABIC LIGATURE KAF WITH LAM FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0644 ARABIC LETTER LAM
+ * FC82 ARABIC LIGATURE KAF WITH MEEM FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * FC83 ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC84 ARABIC LIGATURE KAF WITH YEH FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	064A ARABIC LETTER YEH
+ * FC85 ARABIC LIGATURE LAM WITH MEEM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FC86 ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC87 ARABIC LIGATURE LAM WITH YEH FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	064A ARABIC LETTER YEH
+ * FC88 ARABIC LIGATURE MEEM WITH ALEF FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0627 ARABIC LETTER ALEF
+ * FC89 ARABIC LIGATURE MEEM WITH MEEM FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FC8A ARABIC LIGATURE NOON WITH REH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0631 ARABIC LETTER REH
+ * FC8B ARABIC LIGATURE NOON WITH ZAIN FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0632 ARABIC LETTER ZAIN
+ * FC8C ARABIC LIGATURE NOON WITH MEEM FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * FC8D ARABIC LIGATURE NOON WITH NOON FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0646 ARABIC LETTER NOON
+ * FC8E ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC8F ARABIC LIGATURE NOON WITH YEH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	064A ARABIC LETTER YEH
+ * FC90 ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC91 ARABIC LIGATURE YEH WITH REH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0631 ARABIC LETTER REH
+ * FC92 ARABIC LIGATURE YEH WITH ZAIN FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0632 ARABIC LETTER ZAIN
+ * FC93 ARABIC LIGATURE YEH WITH MEEM FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * FC94 ARABIC LIGATURE YEH WITH NOON FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0646 ARABIC LETTER NOON
+ * FC95 ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FC96 ARABIC LIGATURE YEH WITH YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	064A ARABIC LETTER YEH
+ * FC97 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FC98 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FC99 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FC9A ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FC9B ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	064A ARABIC LETTER YEH
+ * FC9C ARABIC LIGATURE BEH WITH JEEM INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062C ARABIC LETTER JEEM
+ * FC9D ARABIC LIGATURE BEH WITH HAH INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062D ARABIC LETTER HAH
+ * FC9E ARABIC LIGATURE BEH WITH KHAH INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062E ARABIC LETTER KHAH
+ * FC9F ARABIC LIGATURE BEH WITH MEEM INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCA0 ARABIC LIGATURE BEH WITH HEH INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0647 ARABIC LETTER HEH
+ * FCA1 ARABIC LIGATURE TEH WITH JEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062C ARABIC LETTER JEEM
+ * FCA2 ARABIC LIGATURE TEH WITH HAH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062D ARABIC LETTER HAH
+ * FCA3 ARABIC LIGATURE TEH WITH KHAH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062E ARABIC LETTER KHAH
+ * FCA4 ARABIC LIGATURE TEH WITH MEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCA5 ARABIC LIGATURE TEH WITH HEH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0647 ARABIC LETTER HEH
+ * FCA6 ARABIC LIGATURE THEH WITH MEEM INITIAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCA7 ARABIC LIGATURE JEEM WITH HAH INITIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FCA8 ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FCA9 ARABIC LIGATURE HAH WITH JEEM INITIAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FCAA ARABIC LIGATURE HAH WITH MEEM INITIAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FCAB ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	062C ARABIC LETTER JEEM
+ * FCAC ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FCAD ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062C ARABIC LETTER JEEM
+ * FCAE ARABIC LIGATURE SEEN WITH HAH INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062D ARABIC LETTER HAH
+ * FCAF ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062E ARABIC LETTER KHAH
+ * FCB0 ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FCB1 ARABIC LIGATURE SAD WITH HAH INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062D ARABIC LETTER HAH
+ * FCB2 ARABIC LIGATURE SAD WITH KHAH INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062E ARABIC LETTER KHAH
+ * FCB3 ARABIC LIGATURE SAD WITH MEEM INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0645 ARABIC LETTER MEEM
+ * FCB4 ARABIC LIGATURE DAD WITH JEEM INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062C ARABIC LETTER JEEM
+ * FCB5 ARABIC LIGATURE DAD WITH HAH INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062D ARABIC LETTER HAH
+ * FCB6 ARABIC LIGATURE DAD WITH KHAH INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062E ARABIC LETTER KHAH
+ * FCB7 ARABIC LIGATURE DAD WITH MEEM INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0645 ARABIC LETTER MEEM
+ * FCB8 ARABIC LIGATURE TAH WITH HAH INITIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	062D ARABIC LETTER HAH
+ * FCB9 ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM
+ * 	0638 ARABIC LETTER ZAH
+ * 	0645 ARABIC LETTER MEEM
+ * FCBA ARABIC LIGATURE AIN WITH JEEM INITIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	062C ARABIC LETTER JEEM
+ * FCBB ARABIC LIGATURE AIN WITH MEEM INITIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * FCBC ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	062C ARABIC LETTER JEEM
+ * FCBD ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0645 ARABIC LETTER MEEM
+ * FCBE ARABIC LIGATURE FEH WITH JEEM INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062C ARABIC LETTER JEEM
+ * FCBF ARABIC LIGATURE FEH WITH HAH INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062D ARABIC LETTER HAH
+ * FCC0 ARABIC LIGATURE FEH WITH KHAH INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062E ARABIC LETTER KHAH
+ * FCC1 ARABIC LIGATURE FEH WITH MEEM INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCC2 ARABIC LIGATURE QAF WITH HAH INITIAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	062D ARABIC LETTER HAH
+ * FCC3 ARABIC LIGATURE QAF WITH MEEM INITIAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * FCC4 ARABIC LIGATURE KAF WITH JEEM INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062C ARABIC LETTER JEEM
+ * FCC5 ARABIC LIGATURE KAF WITH HAH INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062D ARABIC LETTER HAH
+ * FCC6 ARABIC LIGATURE KAF WITH KHAH INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	062E ARABIC LETTER KHAH
+ * FCC7 ARABIC LIGATURE KAF WITH LAM INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0644 ARABIC LETTER LAM
+ * FCC8 ARABIC LIGATURE KAF WITH MEEM INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * FCC9 ARABIC LIGATURE LAM WITH JEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * FCCA ARABIC LIGATURE LAM WITH HAH INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * FCCB ARABIC LIGATURE LAM WITH KHAH INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062E ARABIC LETTER KHAH
+ * FCCC ARABIC LIGATURE LAM WITH MEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FCCD ARABIC LIGATURE LAM WITH HEH INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0647 ARABIC LETTER HEH
+ * FCCE ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * FCCF ARABIC LIGATURE MEEM WITH HAH INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FCD0 ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * FCD1 ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FCD2 ARABIC LIGATURE NOON WITH JEEM INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * FCD3 ARABIC LIGATURE NOON WITH HAH INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062D ARABIC LETTER HAH
+ * FCD4 ARABIC LIGATURE NOON WITH KHAH INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062E ARABIC LETTER KHAH
+ * FCD5 ARABIC LIGATURE NOON WITH MEEM INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * FCD6 ARABIC LIGATURE NOON WITH HEH INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0647 ARABIC LETTER HEH
+ * FCD7 ARABIC LIGATURE HEH WITH JEEM INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	062C ARABIC LETTER JEEM
+ * FCD8 ARABIC LIGATURE HEH WITH MEEM INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCD9 ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * FCDA ARABIC LIGATURE YEH WITH JEEM INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062C ARABIC LETTER JEEM
+ * FCDB ARABIC LIGATURE YEH WITH HAH INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062D ARABIC LETTER HAH
+ * FCDC ARABIC LIGATURE YEH WITH KHAH INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062E ARABIC LETTER KHAH
+ * FCDD ARABIC LIGATURE YEH WITH MEEM INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCDE ARABIC LIGATURE YEH WITH HEH INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0647 ARABIC LETTER HEH
+ * FCDF ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FCE0 ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	064A ARABIC LETTER YEH
+ * FCE1 ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCE2 ARABIC LIGATURE BEH WITH HEH MEDIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	0647 ARABIC LETTER HEH
+ * FCE3 ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCE4 ARABIC LIGATURE TEH WITH HEH MEDIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0647 ARABIC LETTER HEH
+ * FCE5 ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCE6 ARABIC LIGATURE THEH WITH HEH MEDIAL FORM
+ * 	062B ARABIC LETTER THEH
+ * 	0647 ARABIC LETTER HEH
+ * FCE7 ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FCE8 ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0647 ARABIC LETTER HEH
+ * FCE9 ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FCEA ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0647 ARABIC LETTER HEH
+ * FCEB ARABIC LIGATURE KAF WITH LAM MEDIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0644 ARABIC LETTER LAM
+ * FCEC ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * FCED ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FCEE ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * FCEF ARABIC LIGATURE NOON WITH HEH MEDIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0647 ARABIC LETTER HEH
+ * FCF0 ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * FCF1 ARABIC LIGATURE YEH WITH HEH MEDIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0647 ARABIC LETTER HEH
+ * FCF2 ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FCF3 ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FCF4 ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FCF5 ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FCF6 ARABIC LIGATURE TAH WITH YEH ISOLATED FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	064A ARABIC LETTER YEH
+ * FCF7 ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FCF8 ARABIC LIGATURE AIN WITH YEH ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	064A ARABIC LETTER YEH
+ * FCF9 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FCFA ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	064A ARABIC LETTER YEH
+ * FCFB ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FCFC ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	064A ARABIC LETTER YEH
+ * FCFD ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FCFE ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	064A ARABIC LETTER YEH
+ * FCFF ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD00 ARABIC LIGATURE HAH WITH YEH ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FD01 ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD02 ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FD03 ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD04 ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FD05 ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD06 ARABIC LIGATURE SAD WITH YEH ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	064A ARABIC LETTER YEH
+ * FD07 ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD08 ARABIC LIGATURE DAD WITH YEH ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	064A ARABIC LETTER YEH
+ * FD09 ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062C ARABIC LETTER JEEM
+ * FD0A ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * FD0B ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062E ARABIC LETTER KHAH
+ * FD0C ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FD0D ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0631 ARABIC LETTER REH
+ * FD0E ARABIC LIGATURE SEEN WITH REH ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0631 ARABIC LETTER REH
+ * FD0F ARABIC LIGATURE SAD WITH REH ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0631 ARABIC LETTER REH
+ * FD10 ARABIC LIGATURE DAD WITH REH ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0631 ARABIC LETTER REH
+ * FD11 ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD12 ARABIC LIGATURE TAH WITH YEH FINAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	064A ARABIC LETTER YEH
+ * FD13 ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD14 ARABIC LIGATURE AIN WITH YEH FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	064A ARABIC LETTER YEH
+ * FD15 ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD16 ARABIC LIGATURE GHAIN WITH YEH FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	064A ARABIC LETTER YEH
+ * FD17 ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD18 ARABIC LIGATURE SEEN WITH YEH FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	064A ARABIC LETTER YEH
+ * FD19 ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD1A ARABIC LIGATURE SHEEN WITH YEH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	064A ARABIC LETTER YEH
+ * FD1B ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD1C ARABIC LIGATURE HAH WITH YEH FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FD1D ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD1E ARABIC LIGATURE JEEM WITH YEH FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FD1F ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD20 ARABIC LIGATURE KHAH WITH YEH FINAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FD21 ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD22 ARABIC LIGATURE SAD WITH YEH FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	064A ARABIC LETTER YEH
+ * FD23 ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD24 ARABIC LIGATURE DAD WITH YEH FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	064A ARABIC LETTER YEH
+ * FD25 ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062C ARABIC LETTER JEEM
+ * FD26 ARABIC LIGATURE SHEEN WITH HAH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * FD27 ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062E ARABIC LETTER KHAH
+ * FD28 ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FD29 ARABIC LIGATURE SHEEN WITH REH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0631 ARABIC LETTER REH
+ * FD2A ARABIC LIGATURE SEEN WITH REH FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0631 ARABIC LETTER REH
+ * FD2B ARABIC LIGATURE SAD WITH REH FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0631 ARABIC LETTER REH
+ * FD2C ARABIC LIGATURE DAD WITH REH FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	0631 ARABIC LETTER REH
+ * FD2D ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062C ARABIC LETTER JEEM
+ * FD2E ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * FD2F ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062E ARABIC LETTER KHAH
+ * FD30 ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * FD31 ARABIC LIGATURE SEEN WITH HEH INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0647 ARABIC LETTER HEH
+ * FD32 ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0647 ARABIC LETTER HEH
+ * FD33 ARABIC LIGATURE TAH WITH MEEM INITIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD34 ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062C ARABIC LETTER JEEM
+ * FD35 ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062D ARABIC LETTER HAH
+ * FD36 ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062E ARABIC LETTER KHAH
+ * FD37 ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062C ARABIC LETTER JEEM
+ * FD38 ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * FD39 ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062E ARABIC LETTER KHAH
+ * FD3A ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD3B ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM
+ * 	0638 ARABIC LETTER ZAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD3C ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FD3D ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FD50 ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD51 ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FD52 ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FD53 ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD54 ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD55 ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * FD56 ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD57 ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * FD58 ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD59 ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD5A ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FD5B ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD5C ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FD5D ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FD5E ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062C ARABIC LETTER JEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD5F ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD60 ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD61 ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * FD62 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD63 ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD64 ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062D ARABIC LETTER HAH
+ * 	062D ARABIC LETTER HAH
+ * FD65 ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062D ARABIC LETTER HAH
+ * 	062D ARABIC LETTER HAH
+ * FD66 ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD67 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD68 ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD69 ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FD6A ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * FD6B ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * FD6C ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD6D ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD6E ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD6F ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD70 ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD71 ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD72 ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD73 ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD74 ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FD75 ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD76 ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD77 ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD78 ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD79 ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD7A ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FD7B ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD7C ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD7D ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD7E ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD7F ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD80 ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD81 ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FD82 ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD83 ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * 	062C ARABIC LETTER JEEM
+ * FD84 ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * 	062C ARABIC LETTER JEEM
+ * FD85 ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD86 ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD87 ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD88 ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FD89 ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * FD8A ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD8B ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FD8C ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FD8D ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD8E ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * 	062C ARABIC LETTER JEEM
+ * FD8F ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD92 ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * 	062E ARABIC LETTER KHAH
+ * FD93 ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * FD94 ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD95 ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FD96 ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD97 ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD98 ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD99 ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD9A ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FD9B ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FD9C ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD9D ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FD9E ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FD9F ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDA0 ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062C ARABIC LETTER JEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA1 ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FDA2 ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	062E ARABIC LETTER KHAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA3 ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDA4 ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA5 ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDA6 ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA7 ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA8 ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062E ARABIC LETTER KHAH
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDA9 ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDAA ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDAB ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDAC ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDAD ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDAE ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDAF ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB0 ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB1 ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB2 ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB3 ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDB4 ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * FDB5 ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * FDB6 ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB7 ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDB8 ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FDB9 ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FDBA ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDBB ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDBC ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDBD ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * FDBE ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDBF ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDC0 ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDC1 ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * 	0645 ARABIC LETTER MEEM
+ * 	064A ARABIC LETTER YEH
+ * FDC2 ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * 	062D ARABIC LETTER HAH
+ * 	064A ARABIC LETTER YEH
+ * FDC3 ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDC4 ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	062C ARABIC LETTER JEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDC5 ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0645 ARABIC LETTER MEEM
+ * 	0645 ARABIC LETTER MEEM
+ * FDC6 ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * 	062E ARABIC LETTER KHAH
+ * 	064A ARABIC LETTER YEH
+ * FDC7 ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * 	062C ARABIC LETTER JEEM
+ * 	064A ARABIC LETTER YEH
+ * FDF0 ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0644 ARABIC LETTER LAM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FDF1 ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * 	0644 ARABIC LETTER LAM
+ * 	06D2 ARABIC LETTER YEH BARREE
+ * FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * 	0644 ARABIC LETTER LAM
+ * 	0644 ARABIC LETTER LAM
+ * 	0647 ARABIC LETTER HEH
+ * FDF3 ARABIC LIGATURE AKBAR ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * 	0643 ARABIC LETTER KAF
+ * 	0628 ARABIC LETTER BEH
+ * 	0631 ARABIC LETTER REH
+ * FDF4 ARABIC LIGATURE MOHAMMAD ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * 	062D ARABIC LETTER HAH
+ * 	0645 ARABIC LETTER MEEM
+ * 	062F ARABIC LETTER DAL
+ * FDF5 ARABIC LIGATURE SALAM ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0644 ARABIC LETTER LAM
+ * 	0639 ARABIC LETTER AIN
+ * 	0645 ARABIC LETTER MEEM
+ * FDF6 ARABIC LIGATURE RASOUL ISOLATED FORM
+ * 	0631 ARABIC LETTER REH
+ * 	0633 ARABIC LETTER SEEN
+ * 	0648 ARABIC LETTER WAW
+ * 	0644 ARABIC LETTER LAM
+ * FDF7 ARABIC LIGATURE ALAYHE ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * 	0644 ARABIC LETTER LAM
+ * 	064A ARABIC LETTER YEH
+ * 	0647 ARABIC LETTER HEH
+ * FDF8 ARABIC LIGATURE WASALLAM ISOLATED FORM
+ * 	0648 ARABIC LETTER WAW
+ * 	0633 ARABIC LETTER SEEN
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FDF9 ARABIC LIGATURE SALLA ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * 	0644 ARABIC LETTER LAM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FDFA ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM
+ * 	0635 ARABIC LETTER SAD
+ * 	0644 ARABIC LETTER LAM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * 	0020 SPACE
+ * 	0627 ARABIC LETTER ALEF
+ * 	0644 ARABIC LETTER LAM
+ * 	0644 ARABIC LETTER LAM
+ * 	0647 ARABIC LETTER HEH
+ * 	0020 SPACE
+ * 	0639 ARABIC LETTER AIN
+ * 	0644 ARABIC LETTER LAM
+ * 	064A ARABIC LETTER YEH
+ * 	0647 ARABIC LETTER HEH
+ * 	0020 SPACE
+ * 	0648 ARABIC LETTER WAW
+ * 	0633 ARABIC LETTER SEEN
+ * 	0644 ARABIC LETTER LAM
+ * 	0645 ARABIC LETTER MEEM
+ * FDFB ARABIC LIGATURE JALLAJALALOUHOU
+ * 	062C ARABIC LETTER JEEM
+ * 	0644 ARABIC LETTER LAM
+ * 	0020 SPACE
+ * 	062C ARABIC LETTER JEEM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * 	0644 ARABIC LETTER LAM
+ * 	0647 ARABIC LETTER HEH
+ * FDFC RIAL SIGN
+ * 	0631 ARABIC LETTER REH
+ * 	06CC ARABIC LETTER FARSI YEH
+ * 	0627 ARABIC LETTER ALEF
+ * 	0644 ARABIC LETTER LAM
+ * FE30 PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+ * 	002E FULL STOP
+ * 	002E FULL STOP
+ * FE31 PRESENTATION FORM FOR VERTICAL EM DASH
+ * 	2014 EM DASH
+ * FE32 PRESENTATION FORM FOR VERTICAL EN DASH
+ * 	2013 EN DASH
+ * FE33 PRESENTATION FORM FOR VERTICAL LOW LINE
+ * 	005F LOW LINE
+ * FE34 PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+ * 	005F LOW LINE
+ * FE35 PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+ * 	0028 LEFT PARENTHESIS
+ * FE36 PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+ * 	0029 RIGHT PARENTHESIS
+ * FE37 PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+ * 	007B LEFT CURLY BRACKET
+ * FE38 PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+ * 	007D RIGHT CURLY BRACKET
+ * FE39 PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+ * 	3014 LEFT TORTOISE SHELL BRACKET
+ * FE3A PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+ * 	3015 RIGHT TORTOISE SHELL BRACKET
+ * FE3B PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+ * 	3010 LEFT BLACK LENTICULAR BRACKET
+ * FE3C PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+ * 	3011 RIGHT BLACK LENTICULAR BRACKET
+ * FE3D PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+ * 	300A LEFT DOUBLE ANGLE BRACKET
+ * FE3E PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+ * 	300B RIGHT DOUBLE ANGLE BRACKET
+ * FE3F PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+ * 	3008 LEFT ANGLE BRACKET
+ * FE40 PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+ * 	3009 RIGHT ANGLE BRACKET
+ * FE41 PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+ * 	300C LEFT CORNER BRACKET
+ * FE42 PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+ * 	300D RIGHT CORNER BRACKET
+ * FE43 PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+ * 	300E LEFT WHITE CORNER BRACKET
+ * FE44 PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+ * 	300F RIGHT WHITE CORNER BRACKET
+ * FE49 DASHED OVERLINE
+ * 	0020 SPACE
+ * FE4A CENTRELINE OVERLINE
+ * 	0020 SPACE
+ * FE4B WAVY OVERLINE
+ * 	0020 SPACE
+ * FE4C DOUBLE WAVY OVERLINE
+ * 	0020 SPACE
+ * FE4D DASHED LOW LINE
+ * 	005F LOW LINE
+ * FE4E CENTRELINE LOW LINE
+ * 	005F LOW LINE
+ * FE4F WAVY LOW LINE
+ * 	005F LOW LINE
+ * FE50 SMALL COMMA
+ * 	002C COMMA
+ * FE51 SMALL IDEOGRAPHIC COMMA
+ * 	3001 IDEOGRAPHIC COMMA
+ * FE52 SMALL FULL STOP
+ * 	002E FULL STOP
+ * FE54 SMALL SEMICOLON
+ * 	003B SEMICOLON
+ * FE55 SMALL COLON
+ * 	003A COLON
+ * FE56 SMALL QUESTION MARK
+ * 	003F QUESTION MARK
+ * FE57 SMALL EXCLAMATION MARK
+ * 	0021 EXCLAMATION MARK
+ * FE58 SMALL EM DASH
+ * 	2014 EM DASH
+ * FE59 SMALL LEFT PARENTHESIS
+ * 	0028 LEFT PARENTHESIS
+ * FE5A SMALL RIGHT PARENTHESIS
+ * 	0029 RIGHT PARENTHESIS
+ * FE5B SMALL LEFT CURLY BRACKET
+ * 	007B LEFT CURLY BRACKET
+ * FE5C SMALL RIGHT CURLY BRACKET
+ * 	007D RIGHT CURLY BRACKET
+ * FE5D SMALL LEFT TORTOISE SHELL BRACKET
+ * 	3014 LEFT TORTOISE SHELL BRACKET
+ * FE5E SMALL RIGHT TORTOISE SHELL BRACKET
+ * 	3015 RIGHT TORTOISE SHELL BRACKET
+ * FE5F SMALL NUMBER SIGN
+ * 	0023 NUMBER SIGN
+ * FE60 SMALL AMPERSAND
+ * 	0026 AMPERSAND
+ * FE61 SMALL ASTERISK
+ * 	002A ASTERISK
+ * FE62 SMALL PLUS SIGN
+ * 	002B PLUS SIGN
+ * FE63 SMALL HYPHEN-MINUS
+ * 	002D HYPHEN-MINUS
+ * FE64 SMALL LESS-THAN SIGN
+ * 	003C LESS-THAN SIGN
+ * FE65 SMALL GREATER-THAN SIGN
+ * 	003E GREATER-THAN SIGN
+ * FE66 SMALL EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * FE68 SMALL REVERSE SOLIDUS
+ * 	005C REVERSE SOLIDUS
+ * FE69 SMALL DOLLAR SIGN
+ * 	0024 DOLLAR SIGN
+ * FE6A SMALL PERCENT SIGN
+ * 	0025 PERCENT SIGN
+ * FE6B SMALL COMMERCIAL AT
+ * 	0040 COMMERCIAL AT
+ * FE70 ARABIC FATHATAN ISOLATED FORM
+ * 	0020 SPACE
+ * FE71 ARABIC TATWEEL WITH FATHATAN ABOVE
+ * 	0640 ARABIC TATWEEL
+ * FE72 ARABIC DAMMATAN ISOLATED FORM
+ * 	0020 SPACE
+ * FE74 ARABIC KASRATAN ISOLATED FORM
+ * 	0020 SPACE
+ * FE76 ARABIC FATHA ISOLATED FORM
+ * 	0020 SPACE
+ * FE77 ARABIC FATHA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FE78 ARABIC DAMMA ISOLATED FORM
+ * 	0020 SPACE
+ * FE79 ARABIC DAMMA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FE7A ARABIC KASRA ISOLATED FORM
+ * 	0020 SPACE
+ * FE7B ARABIC KASRA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FE7C ARABIC SHADDA ISOLATED FORM
+ * 	0020 SPACE
+ * FE7D ARABIC SHADDA MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FE7E ARABIC SUKUN ISOLATED FORM
+ * 	0020 SPACE
+ * FE7F ARABIC SUKUN MEDIAL FORM
+ * 	0640 ARABIC TATWEEL
+ * FE80 ARABIC LETTER HAMZA ISOLATED FORM
+ * 	0621 ARABIC LETTER HAMZA
+ * FE81 ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE82 ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE83 ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE84 ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE85 ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM
+ * 	0648 ARABIC LETTER WAW
+ * FE86 ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM
+ * 	0648 ARABIC LETTER WAW
+ * FE87 ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE88 ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE89 ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * FE8A ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FE8B ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FE8C ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FE8D ARABIC LETTER ALEF ISOLATED FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE8E ARABIC LETTER ALEF FINAL FORM
+ * 	0627 ARABIC LETTER ALEF
+ * FE8F ARABIC LETTER BEH ISOLATED FORM
+ * 	0628 ARABIC LETTER BEH
+ * FE90 ARABIC LETTER BEH FINAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * FE91 ARABIC LETTER BEH INITIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * FE92 ARABIC LETTER BEH MEDIAL FORM
+ * 	0628 ARABIC LETTER BEH
+ * FE93 ARABIC LETTER TEH MARBUTA ISOLATED FORM
+ * 	0629 ARABIC LETTER TEH MARBUTA
+ * FE94 ARABIC LETTER TEH MARBUTA FINAL FORM
+ * 	0629 ARABIC LETTER TEH MARBUTA
+ * FE95 ARABIC LETTER TEH ISOLATED FORM
+ * 	062A ARABIC LETTER TEH
+ * FE96 ARABIC LETTER TEH FINAL FORM
+ * 	062A ARABIC LETTER TEH
+ * FE97 ARABIC LETTER TEH INITIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * FE98 ARABIC LETTER TEH MEDIAL FORM
+ * 	062A ARABIC LETTER TEH
+ * FE99 ARABIC LETTER THEH ISOLATED FORM
+ * 	062B ARABIC LETTER THEH
+ * FE9A ARABIC LETTER THEH FINAL FORM
+ * 	062B ARABIC LETTER THEH
+ * FE9B ARABIC LETTER THEH INITIAL FORM
+ * 	062B ARABIC LETTER THEH
+ * FE9C ARABIC LETTER THEH MEDIAL FORM
+ * 	062B ARABIC LETTER THEH
+ * FE9D ARABIC LETTER JEEM ISOLATED FORM
+ * 	062C ARABIC LETTER JEEM
+ * FE9E ARABIC LETTER JEEM FINAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * FE9F ARABIC LETTER JEEM INITIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * FEA0 ARABIC LETTER JEEM MEDIAL FORM
+ * 	062C ARABIC LETTER JEEM
+ * FEA1 ARABIC LETTER HAH ISOLATED FORM
+ * 	062D ARABIC LETTER HAH
+ * FEA2 ARABIC LETTER HAH FINAL FORM
+ * 	062D ARABIC LETTER HAH
+ * FEA3 ARABIC LETTER HAH INITIAL FORM
+ * 	062D ARABIC LETTER HAH
+ * FEA4 ARABIC LETTER HAH MEDIAL FORM
+ * 	062D ARABIC LETTER HAH
+ * FEA5 ARABIC LETTER KHAH ISOLATED FORM
+ * 	062E ARABIC LETTER KHAH
+ * FEA6 ARABIC LETTER KHAH FINAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * FEA7 ARABIC LETTER KHAH INITIAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * FEA8 ARABIC LETTER KHAH MEDIAL FORM
+ * 	062E ARABIC LETTER KHAH
+ * FEA9 ARABIC LETTER DAL ISOLATED FORM
+ * 	062F ARABIC LETTER DAL
+ * FEAA ARABIC LETTER DAL FINAL FORM
+ * 	062F ARABIC LETTER DAL
+ * FEAB ARABIC LETTER THAL ISOLATED FORM
+ * 	0630 ARABIC LETTER THAL
+ * FEAC ARABIC LETTER THAL FINAL FORM
+ * 	0630 ARABIC LETTER THAL
+ * FEAD ARABIC LETTER REH ISOLATED FORM
+ * 	0631 ARABIC LETTER REH
+ * FEAE ARABIC LETTER REH FINAL FORM
+ * 	0631 ARABIC LETTER REH
+ * FEAF ARABIC LETTER ZAIN ISOLATED FORM
+ * 	0632 ARABIC LETTER ZAIN
+ * FEB0 ARABIC LETTER ZAIN FINAL FORM
+ * 	0632 ARABIC LETTER ZAIN
+ * FEB1 ARABIC LETTER SEEN ISOLATED FORM
+ * 	0633 ARABIC LETTER SEEN
+ * FEB2 ARABIC LETTER SEEN FINAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * FEB3 ARABIC LETTER SEEN INITIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * FEB4 ARABIC LETTER SEEN MEDIAL FORM
+ * 	0633 ARABIC LETTER SEEN
+ * FEB5 ARABIC LETTER SHEEN ISOLATED FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * FEB6 ARABIC LETTER SHEEN FINAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * FEB7 ARABIC LETTER SHEEN INITIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * FEB8 ARABIC LETTER SHEEN MEDIAL FORM
+ * 	0634 ARABIC LETTER SHEEN
+ * FEB9 ARABIC LETTER SAD ISOLATED FORM
+ * 	0635 ARABIC LETTER SAD
+ * FEBA ARABIC LETTER SAD FINAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * FEBB ARABIC LETTER SAD INITIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * FEBC ARABIC LETTER SAD MEDIAL FORM
+ * 	0635 ARABIC LETTER SAD
+ * FEBD ARABIC LETTER DAD ISOLATED FORM
+ * 	0636 ARABIC LETTER DAD
+ * FEBE ARABIC LETTER DAD FINAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * FEBF ARABIC LETTER DAD INITIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * FEC0 ARABIC LETTER DAD MEDIAL FORM
+ * 	0636 ARABIC LETTER DAD
+ * FEC1 ARABIC LETTER TAH ISOLATED FORM
+ * 	0637 ARABIC LETTER TAH
+ * FEC2 ARABIC LETTER TAH FINAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * FEC3 ARABIC LETTER TAH INITIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * FEC4 ARABIC LETTER TAH MEDIAL FORM
+ * 	0637 ARABIC LETTER TAH
+ * FEC5 ARABIC LETTER ZAH ISOLATED FORM
+ * 	0638 ARABIC LETTER ZAH
+ * FEC6 ARABIC LETTER ZAH FINAL FORM
+ * 	0638 ARABIC LETTER ZAH
+ * FEC7 ARABIC LETTER ZAH INITIAL FORM
+ * 	0638 ARABIC LETTER ZAH
+ * FEC8 ARABIC LETTER ZAH MEDIAL FORM
+ * 	0638 ARABIC LETTER ZAH
+ * FEC9 ARABIC LETTER AIN ISOLATED FORM
+ * 	0639 ARABIC LETTER AIN
+ * FECA ARABIC LETTER AIN FINAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * FECB ARABIC LETTER AIN INITIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * FECC ARABIC LETTER AIN MEDIAL FORM
+ * 	0639 ARABIC LETTER AIN
+ * FECD ARABIC LETTER GHAIN ISOLATED FORM
+ * 	063A ARABIC LETTER GHAIN
+ * FECE ARABIC LETTER GHAIN FINAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * FECF ARABIC LETTER GHAIN INITIAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * FED0 ARABIC LETTER GHAIN MEDIAL FORM
+ * 	063A ARABIC LETTER GHAIN
+ * FED1 ARABIC LETTER FEH ISOLATED FORM
+ * 	0641 ARABIC LETTER FEH
+ * FED2 ARABIC LETTER FEH FINAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * FED3 ARABIC LETTER FEH INITIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * FED4 ARABIC LETTER FEH MEDIAL FORM
+ * 	0641 ARABIC LETTER FEH
+ * FED5 ARABIC LETTER QAF ISOLATED FORM
+ * 	0642 ARABIC LETTER QAF
+ * FED6 ARABIC LETTER QAF FINAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * FED7 ARABIC LETTER QAF INITIAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * FED8 ARABIC LETTER QAF MEDIAL FORM
+ * 	0642 ARABIC LETTER QAF
+ * FED9 ARABIC LETTER KAF ISOLATED FORM
+ * 	0643 ARABIC LETTER KAF
+ * FEDA ARABIC LETTER KAF FINAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * FEDB ARABIC LETTER KAF INITIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * FEDC ARABIC LETTER KAF MEDIAL FORM
+ * 	0643 ARABIC LETTER KAF
+ * FEDD ARABIC LETTER LAM ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * FEDE ARABIC LETTER LAM FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * FEDF ARABIC LETTER LAM INITIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * FEE0 ARABIC LETTER LAM MEDIAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * FEE1 ARABIC LETTER MEEM ISOLATED FORM
+ * 	0645 ARABIC LETTER MEEM
+ * FEE2 ARABIC LETTER MEEM FINAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * FEE3 ARABIC LETTER MEEM INITIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * FEE4 ARABIC LETTER MEEM MEDIAL FORM
+ * 	0645 ARABIC LETTER MEEM
+ * FEE5 ARABIC LETTER NOON ISOLATED FORM
+ * 	0646 ARABIC LETTER NOON
+ * FEE6 ARABIC LETTER NOON FINAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * FEE7 ARABIC LETTER NOON INITIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * FEE8 ARABIC LETTER NOON MEDIAL FORM
+ * 	0646 ARABIC LETTER NOON
+ * FEE9 ARABIC LETTER HEH ISOLATED FORM
+ * 	0647 ARABIC LETTER HEH
+ * FEEA ARABIC LETTER HEH FINAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * FEEB ARABIC LETTER HEH INITIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * FEEC ARABIC LETTER HEH MEDIAL FORM
+ * 	0647 ARABIC LETTER HEH
+ * FEED ARABIC LETTER WAW ISOLATED FORM
+ * 	0648 ARABIC LETTER WAW
+ * FEEE ARABIC LETTER WAW FINAL FORM
+ * 	0648 ARABIC LETTER WAW
+ * FEEF ARABIC LETTER ALEF MAKSURA ISOLATED FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FEF0 ARABIC LETTER ALEF MAKSURA FINAL FORM
+ * 	0649 ARABIC LETTER ALEF MAKSURA
+ * FEF1 ARABIC LETTER YEH ISOLATED FORM
+ * 	064A ARABIC LETTER YEH
+ * FEF2 ARABIC LETTER YEH FINAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FEF3 ARABIC LETTER YEH INITIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FEF4 ARABIC LETTER YEH MEDIAL FORM
+ * 	064A ARABIC LETTER YEH
+ * FEF5 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEF6 ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEF7 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEF8 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEF9 ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEFA ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEFB ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FEFC ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+ * 	0644 ARABIC LETTER LAM
+ * 	0627 ARABIC LETTER ALEF
+ * FF01 FULLWIDTH EXCLAMATION MARK
+ * 	0021 EXCLAMATION MARK
+ * FF02 FULLWIDTH QUOTATION MARK
+ * 	0022 QUOTATION MARK
+ * FF03 FULLWIDTH NUMBER SIGN
+ * 	0023 NUMBER SIGN
+ * FF04 FULLWIDTH DOLLAR SIGN
+ * 	0024 DOLLAR SIGN
+ * FF05 FULLWIDTH PERCENT SIGN
+ * 	0025 PERCENT SIGN
+ * FF06 FULLWIDTH AMPERSAND
+ * 	0026 AMPERSAND
+ * FF07 FULLWIDTH APOSTROPHE
+ * 	0027 APOSTROPHE
+ * FF08 FULLWIDTH LEFT PARENTHESIS
+ * 	0028 LEFT PARENTHESIS
+ * FF09 FULLWIDTH RIGHT PARENTHESIS
+ * 	0029 RIGHT PARENTHESIS
+ * FF0A FULLWIDTH ASTERISK
+ * 	002A ASTERISK
+ * FF0B FULLWIDTH PLUS SIGN
+ * 	002B PLUS SIGN
+ * FF0C FULLWIDTH COMMA
+ * 	002C COMMA
+ * FF0D FULLWIDTH HYPHEN-MINUS
+ * 	002D HYPHEN-MINUS
+ * FF0E FULLWIDTH FULL STOP
+ * 	002E FULL STOP
+ * FF0F FULLWIDTH SOLIDUS
+ * 	002F SOLIDUS
+ * FF10 FULLWIDTH DIGIT ZERO
+ * 	0030 DIGIT ZERO
+ * FF11 FULLWIDTH DIGIT ONE
+ * 	0031 DIGIT ONE
+ * FF12 FULLWIDTH DIGIT TWO
+ * 	0032 DIGIT TWO
+ * FF13 FULLWIDTH DIGIT THREE
+ * 	0033 DIGIT THREE
+ * FF14 FULLWIDTH DIGIT FOUR
+ * 	0034 DIGIT FOUR
+ * FF15 FULLWIDTH DIGIT FIVE
+ * 	0035 DIGIT FIVE
+ * FF16 FULLWIDTH DIGIT SIX
+ * 	0036 DIGIT SIX
+ * FF17 FULLWIDTH DIGIT SEVEN
+ * 	0037 DIGIT SEVEN
+ * FF18 FULLWIDTH DIGIT EIGHT
+ * 	0038 DIGIT EIGHT
+ * FF19 FULLWIDTH DIGIT NINE
+ * 	0039 DIGIT NINE
+ * FF1A FULLWIDTH COLON
+ * 	003A COLON
+ * FF1B FULLWIDTH SEMICOLON
+ * 	003B SEMICOLON
+ * FF1C FULLWIDTH LESS-THAN SIGN
+ * 	003C LESS-THAN SIGN
+ * FF1D FULLWIDTH EQUALS SIGN
+ * 	003D EQUALS SIGN
+ * FF1E FULLWIDTH GREATER-THAN SIGN
+ * 	003E GREATER-THAN SIGN
+ * FF1F FULLWIDTH QUESTION MARK
+ * 	003F QUESTION MARK
+ * FF20 FULLWIDTH COMMERCIAL AT
+ * 	0040 COMMERCIAL AT
+ * FF21 FULLWIDTH LATIN CAPITAL LETTER A
+ * 	0041 LATIN CAPITAL LETTER A
+ * FF22 FULLWIDTH LATIN CAPITAL LETTER B
+ * 	0042 LATIN CAPITAL LETTER B
+ * FF23 FULLWIDTH LATIN CAPITAL LETTER C
+ * 	0043 LATIN CAPITAL LETTER C
+ * FF24 FULLWIDTH LATIN CAPITAL LETTER D
+ * 	0044 LATIN CAPITAL LETTER D
+ * FF25 FULLWIDTH LATIN CAPITAL LETTER E
+ * 	0045 LATIN CAPITAL LETTER E
+ * FF26 FULLWIDTH LATIN CAPITAL LETTER F
+ * 	0046 LATIN CAPITAL LETTER F
+ * FF27 FULLWIDTH LATIN CAPITAL LETTER G
+ * 	0047 LATIN CAPITAL LETTER G
+ * FF28 FULLWIDTH LATIN CAPITAL LETTER H
+ * 	0048 LATIN CAPITAL LETTER H
+ * FF29 FULLWIDTH LATIN CAPITAL LETTER I
+ * 	0049 LATIN CAPITAL LETTER I
+ * FF2A FULLWIDTH LATIN CAPITAL LETTER J
+ * 	004A LATIN CAPITAL LETTER J
+ * FF2B FULLWIDTH LATIN CAPITAL LETTER K
+ * 	004B LATIN CAPITAL LETTER K
+ * FF2C FULLWIDTH LATIN CAPITAL LETTER L
+ * 	004C LATIN CAPITAL LETTER L
+ * FF2D FULLWIDTH LATIN CAPITAL LETTER M
+ * 	004D LATIN CAPITAL LETTER M
+ * FF2E FULLWIDTH LATIN CAPITAL LETTER N
+ * 	004E LATIN CAPITAL LETTER N
+ * FF2F FULLWIDTH LATIN CAPITAL LETTER O
+ * 	004F LATIN CAPITAL LETTER O
+ * FF30 FULLWIDTH LATIN CAPITAL LETTER P
+ * 	0050 LATIN CAPITAL LETTER P
+ * FF31 FULLWIDTH LATIN CAPITAL LETTER Q
+ * 	0051 LATIN CAPITAL LETTER Q
+ * FF32 FULLWIDTH LATIN CAPITAL LETTER R
+ * 	0052 LATIN CAPITAL LETTER R
+ * FF33 FULLWIDTH LATIN CAPITAL LETTER S
+ * 	0053 LATIN CAPITAL LETTER S
+ * FF34 FULLWIDTH LATIN CAPITAL LETTER T
+ * 	0054 LATIN CAPITAL LETTER T
+ * FF35 FULLWIDTH LATIN CAPITAL LETTER U
+ * 	0055 LATIN CAPITAL LETTER U
+ * FF36 FULLWIDTH LATIN CAPITAL LETTER V
+ * 	0056 LATIN CAPITAL LETTER V
+ * FF37 FULLWIDTH LATIN CAPITAL LETTER W
+ * 	0057 LATIN CAPITAL LETTER W
+ * FF38 FULLWIDTH LATIN CAPITAL LETTER X
+ * 	0058 LATIN CAPITAL LETTER X
+ * FF39 FULLWIDTH LATIN CAPITAL LETTER Y
+ * 	0059 LATIN CAPITAL LETTER Y
+ * FF3A FULLWIDTH LATIN CAPITAL LETTER Z
+ * 	005A LATIN CAPITAL LETTER Z
+ * FF3B FULLWIDTH LEFT SQUARE BRACKET
+ * 	005B LEFT SQUARE BRACKET
+ * FF3C FULLWIDTH REVERSE SOLIDUS
+ * 	005C REVERSE SOLIDUS
+ * FF3D FULLWIDTH RIGHT SQUARE BRACKET
+ * 	005D RIGHT SQUARE BRACKET
+ * FF3E FULLWIDTH CIRCUMFLEX ACCENT
+ * 	005E CIRCUMFLEX ACCENT
+ * FF3F FULLWIDTH LOW LINE
+ * 	005F LOW LINE
+ * FF40 FULLWIDTH GRAVE ACCENT
+ * 	0060 GRAVE ACCENT
+ * FF41 FULLWIDTH LATIN SMALL LETTER A
+ * 	0061 LATIN SMALL LETTER A
+ * FF42 FULLWIDTH LATIN SMALL LETTER B
+ * 	0062 LATIN SMALL LETTER B
+ * FF43 FULLWIDTH LATIN SMALL LETTER C
+ * 	0063 LATIN SMALL LETTER C
+ * FF44 FULLWIDTH LATIN SMALL LETTER D
+ * 	0064 LATIN SMALL LETTER D
+ * FF45 FULLWIDTH LATIN SMALL LETTER E
+ * 	0065 LATIN SMALL LETTER E
+ * FF46 FULLWIDTH LATIN SMALL LETTER F
+ * 	0066 LATIN SMALL LETTER F
+ * FF47 FULLWIDTH LATIN SMALL LETTER G
+ * 	0067 LATIN SMALL LETTER G
+ * FF48 FULLWIDTH LATIN SMALL LETTER H
+ * 	0068 LATIN SMALL LETTER H
+ * FF49 FULLWIDTH LATIN SMALL LETTER I
+ * 	0069 LATIN SMALL LETTER I
+ * FF4A FULLWIDTH LATIN SMALL LETTER J
+ * 	006A LATIN SMALL LETTER J
+ * FF4B FULLWIDTH LATIN SMALL LETTER K
+ * 	006B LATIN SMALL LETTER K
+ * FF4C FULLWIDTH LATIN SMALL LETTER L
+ * 	006C LATIN SMALL LETTER L
+ * FF4D FULLWIDTH LATIN SMALL LETTER M
+ * 	006D LATIN SMALL LETTER M
+ * FF4E FULLWIDTH LATIN SMALL LETTER N
+ * 	006E LATIN SMALL LETTER N
+ * FF4F FULLWIDTH LATIN SMALL LETTER O
+ * 	006F LATIN SMALL LETTER O
+ * FF50 FULLWIDTH LATIN SMALL LETTER P
+ * 	0070 LATIN SMALL LETTER P
+ * FF51 FULLWIDTH LATIN SMALL LETTER Q
+ * 	0071 LATIN SMALL LETTER Q
+ * FF52 FULLWIDTH LATIN SMALL LETTER R
+ * 	0072 LATIN SMALL LETTER R
+ * FF53 FULLWIDTH LATIN SMALL LETTER S
+ * 	0073 LATIN SMALL LETTER S
+ * FF54 FULLWIDTH LATIN SMALL LETTER T
+ * 	0074 LATIN SMALL LETTER T
+ * FF55 FULLWIDTH LATIN SMALL LETTER U
+ * 	0075 LATIN SMALL LETTER U
+ * FF56 FULLWIDTH LATIN SMALL LETTER V
+ * 	0076 LATIN SMALL LETTER V
+ * FF57 FULLWIDTH LATIN SMALL LETTER W
+ * 	0077 LATIN SMALL LETTER W
+ * FF58 FULLWIDTH LATIN SMALL LETTER X
+ * 	0078 LATIN SMALL LETTER X
+ * FF59 FULLWIDTH LATIN SMALL LETTER Y
+ * 	0079 LATIN SMALL LETTER Y
+ * FF5A FULLWIDTH LATIN SMALL LETTER Z
+ * 	007A LATIN SMALL LETTER Z
+ * FF5B FULLWIDTH LEFT CURLY BRACKET
+ * 	007B LEFT CURLY BRACKET
+ * FF5C FULLWIDTH VERTICAL LINE
+ * 	007C VERTICAL LINE
+ * FF5D FULLWIDTH RIGHT CURLY BRACKET
+ * 	007D RIGHT CURLY BRACKET
+ * FF5E FULLWIDTH TILDE
+ * 	007E TILDE
+ * FF5F FULLWIDTH LEFT WHITE PARENTHESIS
+ * 	2985 LEFT WHITE PARENTHESIS
+ * FF60 FULLWIDTH RIGHT WHITE PARENTHESIS
+ * 	2986 RIGHT WHITE PARENTHESIS
+ * FF61 HALFWIDTH IDEOGRAPHIC FULL STOP
+ * 	3002 IDEOGRAPHIC FULL STOP
+ * FF62 HALFWIDTH LEFT CORNER BRACKET
+ * 	300C LEFT CORNER BRACKET
+ * FF63 HALFWIDTH RIGHT CORNER BRACKET
+ * 	300D RIGHT CORNER BRACKET
+ * FF64 HALFWIDTH IDEOGRAPHIC COMMA
+ * 	3001 IDEOGRAPHIC COMMA
+ * FF65 HALFWIDTH KATAKANA MIDDLE DOT
+ * 	30FB KATAKANA MIDDLE DOT
+ * FF66 HALFWIDTH KATAKANA LETTER WO
+ * 	30F2 KATAKANA LETTER WO
+ * FF67 HALFWIDTH KATAKANA LETTER SMALL A
+ * 	30A1 KATAKANA LETTER SMALL A
+ * FF68 HALFWIDTH KATAKANA LETTER SMALL I
+ * 	30A3 KATAKANA LETTER SMALL I
+ * FF69 HALFWIDTH KATAKANA LETTER SMALL U
+ * 	30A5 KATAKANA LETTER SMALL U
+ * FF6A HALFWIDTH KATAKANA LETTER SMALL E
+ * 	30A7 KATAKANA LETTER SMALL E
+ * FF6B HALFWIDTH KATAKANA LETTER SMALL O
+ * 	30A9 KATAKANA LETTER SMALL O
+ * FF6C HALFWIDTH KATAKANA LETTER SMALL YA
+ * 	30E3 KATAKANA LETTER SMALL YA
+ * FF6D HALFWIDTH KATAKANA LETTER SMALL YU
+ * 	30E5 KATAKANA LETTER SMALL YU
+ * FF6E HALFWIDTH KATAKANA LETTER SMALL YO
+ * 	30E7 KATAKANA LETTER SMALL YO
+ * FF6F HALFWIDTH KATAKANA LETTER SMALL TU
+ * 	30C3 KATAKANA LETTER SMALL TU
+ * FF70 HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * 	30FC KATAKANA-HIRAGANA PROLONGED SOUND MARK
+ * FF71 HALFWIDTH KATAKANA LETTER A
+ * 	30A2 KATAKANA LETTER A
+ * FF72 HALFWIDTH KATAKANA LETTER I
+ * 	30A4 KATAKANA LETTER I
+ * FF73 HALFWIDTH KATAKANA LETTER U
+ * 	30A6 KATAKANA LETTER U
+ * FF74 HALFWIDTH KATAKANA LETTER E
+ * 	30A8 KATAKANA LETTER E
+ * FF75 HALFWIDTH KATAKANA LETTER O
+ * 	30AA KATAKANA LETTER O
+ * FF76 HALFWIDTH KATAKANA LETTER KA
+ * 	30AB KATAKANA LETTER KA
+ * FF77 HALFWIDTH KATAKANA LETTER KI
+ * 	30AD KATAKANA LETTER KI
+ * FF78 HALFWIDTH KATAKANA LETTER KU
+ * 	30AF KATAKANA LETTER KU
+ * FF79 HALFWIDTH KATAKANA LETTER KE
+ * 	30B1 KATAKANA LETTER KE
+ * FF7A HALFWIDTH KATAKANA LETTER KO
+ * 	30B3 KATAKANA LETTER KO
+ * FF7B HALFWIDTH KATAKANA LETTER SA
+ * 	30B5 KATAKANA LETTER SA
+ * FF7C HALFWIDTH KATAKANA LETTER SI
+ * 	30B7 KATAKANA LETTER SI
+ * FF7D HALFWIDTH KATAKANA LETTER SU
+ * 	30B9 KATAKANA LETTER SU
+ * FF7E HALFWIDTH KATAKANA LETTER SE
+ * 	30BB KATAKANA LETTER SE
+ * FF7F HALFWIDTH KATAKANA LETTER SO
+ * 	30BD KATAKANA LETTER SO
+ * FF80 HALFWIDTH KATAKANA LETTER TA
+ * 	30BF KATAKANA LETTER TA
+ * FF81 HALFWIDTH KATAKANA LETTER TI
+ * 	30C1 KATAKANA LETTER TI
+ * FF82 HALFWIDTH KATAKANA LETTER TU
+ * 	30C4 KATAKANA LETTER TU
+ * FF83 HALFWIDTH KATAKANA LETTER TE
+ * 	30C6 KATAKANA LETTER TE
+ * FF84 HALFWIDTH KATAKANA LETTER TO
+ * 	30C8 KATAKANA LETTER TO
+ * FF85 HALFWIDTH KATAKANA LETTER NA
+ * 	30CA KATAKANA LETTER NA
+ * FF86 HALFWIDTH KATAKANA LETTER NI
+ * 	30CB KATAKANA LETTER NI
+ * FF87 HALFWIDTH KATAKANA LETTER NU
+ * 	30CC KATAKANA LETTER NU
+ * FF88 HALFWIDTH KATAKANA LETTER NE
+ * 	30CD KATAKANA LETTER NE
+ * FF89 HALFWIDTH KATAKANA LETTER NO
+ * 	30CE KATAKANA LETTER NO
+ * FF8A HALFWIDTH KATAKANA LETTER HA
+ * 	30CF KATAKANA LETTER HA
+ * FF8B HALFWIDTH KATAKANA LETTER HI
+ * 	30D2 KATAKANA LETTER HI
+ * FF8C HALFWIDTH KATAKANA LETTER HU
+ * 	30D5 KATAKANA LETTER HU
+ * FF8D HALFWIDTH KATAKANA LETTER HE
+ * 	30D8 KATAKANA LETTER HE
+ * FF8E HALFWIDTH KATAKANA LETTER HO
+ * 	30DB KATAKANA LETTER HO
+ * FF8F HALFWIDTH KATAKANA LETTER MA
+ * 	30DE KATAKANA LETTER MA
+ * FF90 HALFWIDTH KATAKANA LETTER MI
+ * 	30DF KATAKANA LETTER MI
+ * FF91 HALFWIDTH KATAKANA LETTER MU
+ * 	30E0 KATAKANA LETTER MU
+ * FF92 HALFWIDTH KATAKANA LETTER ME
+ * 	30E1 KATAKANA LETTER ME
+ * FF93 HALFWIDTH KATAKANA LETTER MO
+ * 	30E2 KATAKANA LETTER MO
+ * FF94 HALFWIDTH KATAKANA LETTER YA
+ * 	30E4 KATAKANA LETTER YA
+ * FF95 HALFWIDTH KATAKANA LETTER YU
+ * 	30E6 KATAKANA LETTER YU
+ * FF96 HALFWIDTH KATAKANA LETTER YO
+ * 	30E8 KATAKANA LETTER YO
+ * FF97 HALFWIDTH KATAKANA LETTER RA
+ * 	30E9 KATAKANA LETTER RA
+ * FF98 HALFWIDTH KATAKANA LETTER RI
+ * 	30EA KATAKANA LETTER RI
+ * FF99 HALFWIDTH KATAKANA LETTER RU
+ * 	30EB KATAKANA LETTER RU
+ * FF9A HALFWIDTH KATAKANA LETTER RE
+ * 	30EC KATAKANA LETTER RE
+ * FF9B HALFWIDTH KATAKANA LETTER RO
+ * 	30ED KATAKANA LETTER RO
+ * FF9C HALFWIDTH KATAKANA LETTER WA
+ * 	30EF KATAKANA LETTER WA
+ * FF9D HALFWIDTH KATAKANA LETTER N
+ * 	30F3 KATAKANA LETTER N
+ * FFA0 HALFWIDTH HANGUL FILLER
+ * 	1160 HANGUL JUNGSEONG FILLER
+ * FFA1 HALFWIDTH HANGUL LETTER KIYEOK
+ * 	1100 HANGUL CHOSEONG KIYEOK
+ * FFA2 HALFWIDTH HANGUL LETTER SSANGKIYEOK
+ * 	1101 HANGUL CHOSEONG SSANGKIYEOK
+ * FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS
+ * 	11AA HANGUL JONGSEONG KIYEOK-SIOS
+ * FFA4 HALFWIDTH HANGUL LETTER NIEUN
+ * 	1102 HANGUL CHOSEONG NIEUN
+ * FFA5 HALFWIDTH HANGUL LETTER NIEUN-CIEUC
+ * 	11AC HANGUL JONGSEONG NIEUN-CIEUC
+ * FFA6 HALFWIDTH HANGUL LETTER NIEUN-HIEUH
+ * 	11AD HANGUL JONGSEONG NIEUN-HIEUH
+ * FFA7 HALFWIDTH HANGUL LETTER TIKEUT
+ * 	1103 HANGUL CHOSEONG TIKEUT
+ * FFA8 HALFWIDTH HANGUL LETTER SSANGTIKEUT
+ * 	1104 HANGUL CHOSEONG SSANGTIKEUT
+ * FFA9 HALFWIDTH HANGUL LETTER RIEUL
+ * 	1105 HANGUL CHOSEONG RIEUL
+ * FFAA HALFWIDTH HANGUL LETTER RIEUL-KIYEOK
+ * 	11B0 HANGUL JONGSEONG RIEUL-KIYEOK
+ * FFAB HALFWIDTH HANGUL LETTER RIEUL-MIEUM
+ * 	11B1 HANGUL JONGSEONG RIEUL-MIEUM
+ * FFAC HALFWIDTH HANGUL LETTER RIEUL-PIEUP
+ * 	11B2 HANGUL JONGSEONG RIEUL-PIEUP
+ * FFAD HALFWIDTH HANGUL LETTER RIEUL-SIOS
+ * 	11B3 HANGUL JONGSEONG RIEUL-SIOS
+ * FFAE HALFWIDTH HANGUL LETTER RIEUL-THIEUTH
+ * 	11B4 HANGUL JONGSEONG RIEUL-THIEUTH
+ * FFAF HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH
+ * 	11B5 HANGUL JONGSEONG RIEUL-PHIEUPH
+ * FFB0 HALFWIDTH HANGUL LETTER RIEUL-HIEUH
+ * 	111A HANGUL CHOSEONG RIEUL-HIEUH
+ * FFB1 HALFWIDTH HANGUL LETTER MIEUM
+ * 	1106 HANGUL CHOSEONG MIEUM
+ * FFB2 HALFWIDTH HANGUL LETTER PIEUP
+ * 	1107 HANGUL CHOSEONG PIEUP
+ * FFB3 HALFWIDTH HANGUL LETTER SSANGPIEUP
+ * 	1108 HANGUL CHOSEONG SSANGPIEUP
+ * FFB4 HALFWIDTH HANGUL LETTER PIEUP-SIOS
+ * 	1121 HANGUL CHOSEONG PIEUP-SIOS
+ * FFB5 HALFWIDTH HANGUL LETTER SIOS
+ * 	1109 HANGUL CHOSEONG SIOS
+ * FFB6 HALFWIDTH HANGUL LETTER SSANGSIOS
+ * 	110A HANGUL CHOSEONG SSANGSIOS
+ * FFB7 HALFWIDTH HANGUL LETTER IEUNG
+ * 	110B HANGUL CHOSEONG IEUNG
+ * FFB8 HALFWIDTH HANGUL LETTER CIEUC
+ * 	110C HANGUL CHOSEONG CIEUC
+ * FFB9 HALFWIDTH HANGUL LETTER SSANGCIEUC
+ * 	110D HANGUL CHOSEONG SSANGCIEUC
+ * FFBA HALFWIDTH HANGUL LETTER CHIEUCH
+ * 	110E HANGUL CHOSEONG CHIEUCH
+ * FFBB HALFWIDTH HANGUL LETTER KHIEUKH
+ * 	110F HANGUL CHOSEONG KHIEUKH
+ * FFBC HALFWIDTH HANGUL LETTER THIEUTH
+ * 	1110 HANGUL CHOSEONG THIEUTH
+ * FFBD HALFWIDTH HANGUL LETTER PHIEUPH
+ * 	1111 HANGUL CHOSEONG PHIEUPH
+ * FFBE HALFWIDTH HANGUL LETTER HIEUH
+ * 	1112 HANGUL CHOSEONG HIEUH
+ * FFC2 HALFWIDTH HANGUL LETTER A
+ * 	1161 HANGUL JUNGSEONG A
+ * FFC3 HALFWIDTH HANGUL LETTER AE
+ * 	1162 HANGUL JUNGSEONG AE
+ * FFC4 HALFWIDTH HANGUL LETTER YA
+ * 	1163 HANGUL JUNGSEONG YA
+ * FFC5 HALFWIDTH HANGUL LETTER YAE
+ * 	1164 HANGUL JUNGSEONG YAE
+ * FFC6 HALFWIDTH HANGUL LETTER EO
+ * 	1165 HANGUL JUNGSEONG EO
+ * FFC7 HALFWIDTH HANGUL LETTER E
+ * 	1166 HANGUL JUNGSEONG E
+ * FFCA HALFWIDTH HANGUL LETTER YEO
+ * 	1167 HANGUL JUNGSEONG YEO
+ * FFCB HALFWIDTH HANGUL LETTER YE
+ * 	1168 HANGUL JUNGSEONG YE
+ * FFCC HALFWIDTH HANGUL LETTER O
+ * 	1169 HANGUL JUNGSEONG O
+ * FFCD HALFWIDTH HANGUL LETTER WA
+ * 	116A HANGUL JUNGSEONG WA
+ * FFCE HALFWIDTH HANGUL LETTER WAE
+ * 	116B HANGUL JUNGSEONG WAE
+ * FFCF HALFWIDTH HANGUL LETTER OE
+ * 	116C HANGUL JUNGSEONG OE
+ * FFD2 HALFWIDTH HANGUL LETTER YO
+ * 	116D HANGUL JUNGSEONG YO
+ * FFD3 HALFWIDTH HANGUL LETTER U
+ * 	116E HANGUL JUNGSEONG U
+ * FFD4 HALFWIDTH HANGUL LETTER WEO
+ * 	116F HANGUL JUNGSEONG WEO
+ * FFD5 HALFWIDTH HANGUL LETTER WE
+ * 	1170 HANGUL JUNGSEONG WE
+ * FFD6 HALFWIDTH HANGUL LETTER WI
+ * 	1171 HANGUL JUNGSEONG WI
+ * FFD7 HALFWIDTH HANGUL LETTER YU
+ * 	1172 HANGUL JUNGSEONG YU
+ * FFDA HALFWIDTH HANGUL LETTER EU
+ * 	1173 HANGUL JUNGSEONG EU
+ * FFDB HALFWIDTH HANGUL LETTER YI
+ * 	1174 HANGUL JUNGSEONG YI
+ * FFDC HALFWIDTH HANGUL LETTER I
+ * 	1175 HANGUL JUNGSEONG I
+ * FFE0 FULLWIDTH CENT SIGN
+ * 	00A2 CENT SIGN
+ * FFE1 FULLWIDTH POUND SIGN
+ * 	00A3 POUND SIGN
+ * FFE2 FULLWIDTH NOT SIGN
+ * 	00AC NOT SIGN
+ * FFE3 FULLWIDTH MACRON
+ * 	0020 SPACE
+ * FFE4 FULLWIDTH BROKEN BAR
+ * 	00A6 BROKEN BAR
+ * FFE5 FULLWIDTH YEN SIGN
+ * 	00A5 YEN SIGN
+ * FFE6 FULLWIDTH WON SIGN
+ * 	20A9 WON SIGN
+ * FFE8 HALFWIDTH FORMS LIGHT VERTICAL
+ * 	2502 BOX DRAWINGS LIGHT VERTICAL
+ * FFE9 HALFWIDTH LEFTWARDS ARROW
+ * 	2190 LEFTWARDS ARROW
+ * FFEA HALFWIDTH UPWARDS ARROW
+ * 	2191 UPWARDS ARROW
+ * FFEB HALFWIDTH RIGHTWARDS ARROW
+ * 	2192 RIGHTWARDS ARROW
+ * FFEC HALFWIDTH DOWNWARDS ARROW
+ * 	2193 DOWNWARDS ARROW
+ * FFED HALFWIDTH BLACK SQUARE
+ * 	25A0 BLACK SQUARE
+ * FFEE HALFWIDTH WHITE CIRCLE
+ * 	25CB WHITE CIRCLE
+*/
+
+unsigned short unac_indexes[UNAC_INDEXES_SIZE] = {
+   0,   0,   0,   0,   0,   1,   2,   3,   4,   5,   6,   7,   0,   8,   9,
+  10,  11,  12,   0,   0,   0,  13,  14,  15,   0,   0,   0,  16,  17,  18,
+  19,  20,  21,  22,  23,  24,   0,   0,  25,  26,   0,   0,   0,   0,  27,
+   0,   0,   0,   0,  28,   0,  29,   0,   0,  30,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  31,  32,
+   0,   0,   0,  33,   0,   0,  34,  35,   0,   0,   0,   0,   0,   0,   0,
+  36,   0,  37,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,  38,   0,   0,   0,  39,  40,   0,
+  41,   0,  42,  43,   0,   0,   0,   0,   0,  44,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+  60,  61,  62,  63,  64,  65,  66,   0,   0,  67,  68,  69,  70,  71,  72,
+  73,   0,  74,  75,  76,  77,  78,  79,   0,  80,   0,  81,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  82,  83,  84,  85,  86,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,  87,   0,   0,  88,   0,   0,  89,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  90,   0,   0,
+  91,  92,  93,  94,  95,  96,  97,  98,   0,  99, 100, 101, 102, 103, 104,
+ 105, 106,   0, 107, 108, 109, 110,   0,   0,   0, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138,   0,   0,   0,   0, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162,   0, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177
+};
+
+unsigned char unac_positions[UNAC_BLOCK_COUNT][UNAC_BLOCK_SIZE + 1] = {
+/* 0 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 1 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 31, 34, 37, 38 },
+/* 2 */ { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 3 */ { 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 4 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 5 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35 },
+/* 6 */ { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 },
+/* 7 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 8 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 9 */ { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 },
+/* 10 */ { 0, 1, 2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 37, 38, 39 },
+/* 11 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 12 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 13 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 14 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 15 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 16 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 17 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 18 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 19 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 20 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 21 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 22 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 23 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 24 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 25 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 26 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 27 */ { 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 28 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 29 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 27, 29, 30, 31, 32, 33, 34, 35, 36 },
+/* 30 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 31 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 32 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 33 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 34 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 35 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 36 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 37 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 38 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 39 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 40 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 32, 33, 34 },
+/* 41 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 42 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 43 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 44 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 45 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 46 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 47 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 48 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 49 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33 },
+/* 50 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 51 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 52 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 53 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 54 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 55 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 56 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 57 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 58 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 59 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 60 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 61 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 62 */ { 0, 1, 2, 3, 4, 5, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 27, 28, 30, 33, 34, 35, 36, 37, 39, 40, 41, 42 },
+/* 63 */ { 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
+/* 64 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 65 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 66 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 67 */ { 0, 3, 6, 7, 9, 10, 13, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 },
+/* 68 */ { 0, 2, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 },
+/* 69 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 57 },
+/* 70 */ { 0, 1, 3, 6, 8, 9, 11, 14, 18, 20, 21, 23, 26, 27, 28, 29, 30, 31, 33, 36, 38, 39, 41, 44, 48, 50, 51, 53, 56, 57, 58, 59, 60 },
+/* 71 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 72 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 73 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 74 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 75 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 17, 18, 20, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38 },
+/* 76 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 77 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 78 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 79 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 80 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 81 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 82 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 62, 66, 70 },
+/* 83 */ { 0, 4, 8, 12, 16, 20, 24, 28, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 95 },
+/* 84 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 },
+/* 85 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 86 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 87 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 },
+/* 88 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23, 25, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 },
+/* 89 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 90 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 91 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 92 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 93 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 94 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 95 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 96 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 97 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 98 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 99 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 100 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 101 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 102 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 103 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33 },
+/* 104 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 105 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 106 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33 },
+/* 107 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 108 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 109 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 110 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 111 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 103, 104, 105 },
+/* 112 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96 },
+/* 113 */ { 0, 3, 6, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55 },
+/* 114 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 43, 44, 45, 46 },
+/* 115 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 116 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47 },
+/* 117 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 21, 24, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 },
+/* 118 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 119 */ { 0, 4, 8, 12, 15, 19, 22, 25, 30, 34, 37, 40, 43, 47, 51, 54, 57, 59, 62, 66, 70, 72, 77, 83, 88, 91, 96, 101, 105, 108, 111, 114, 118 },
+/* 120 */ { 0, 5, 9, 12, 15, 18, 20, 22, 24, 26, 29, 32, 37, 40, 44, 49, 52, 54, 56, 61, 65, 70, 73, 78, 80, 83, 86, 89, 92, 95, 99, 102, 104 },
+/* 121 */ { 0, 3, 6, 9, 13, 16, 19, 22, 27, 31, 33, 38, 40, 44, 48, 51, 54, 57, 61, 63, 66, 70, 72, 77, 80, 82, 84, 86, 88, 90, 92, 94, 96 },
+/* 122 */ { 0, 2, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 54, 56, 59, 61, 63, 64, 65, 66, 67, 69, 71, 73, 75, 79 },
+/* 123 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 19, 23, 25, 27, 29, 31, 33, 35, 37, 40, 43, 46, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 72 },
+/* 124 */ { 0, 3, 5, 8, 11, 14, 16, 19, 22, 26, 28, 31, 34, 37, 40, 45, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83 },
+/* 125 */ { 0, 2, 4, 8, 10, 12, 14, 18, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 44, 46, 48, 51, 54, 56, 60, 63, 65, 67, 69, 71, 72, 73 },
+/* 126 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 85 },
+/* 127 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 128 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 129 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 130 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 131 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 132 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 133 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 134 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 135 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 136 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 137 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 138 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 139 */ { 0, 2, 4, 6, 9, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46 },
+/* 140 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 141 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 142 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 143 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 144 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 145 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33 },
+/* 146 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 47, 48, 49, 50 },
+/* 147 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 },
+/* 148 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 },
+/* 149 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 55, 56, 57, 58, 59 },
+/* 150 */ { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60 },
+/* 151 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63 },
+/* 152 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 },
+/* 153 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 51, 53, 55, 57, 59, 61, 63 },
+/* 154 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 37, 38, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61 },
+/* 155 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64 },
+/* 156 */ { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 57, 58, 59, 60 },
+/* 157 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64 },
+/* 158 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96 },
+/* 159 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 49, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92 },
+/* 160 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96 },
+/* 161 */ { 0, 3, 6, 9, 12, 15, 18, 21, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48 },
+/* 162 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 22, 26, 30, 34, 38, 42, 46, 50, 53, 71, 79, 83, 84, 85, 86 },
+/* 163 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 },
+/* 164 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 165 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 166 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 167 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 168 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 169 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, 38, 39, 40 },
+/* 170 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 171 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 172 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 173 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 174 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 175 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 176 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 },
+/* 177 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }
+};
+
+unsigned short unac_data0[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data1[] = { 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0xFFFF, 0x0061, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0xFFFF, 0xFFFF, 0x0032, 0x0033, 0x0020, 0x03BC, 0xFFFF, 0xFFFF, 0x0020, 0x0031, 0x006F, 0xFFFF, 0x0031, 0x2044, 0x0034, 0x0031, 0x2044, 0x0032, 0x0033, 0x2044, 0x0034, 0xFFFF };
+unsigned short unac_data2[] = { 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0045, 0x0043, 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, 0xFFFF, 0x004E, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0xFFFF, 0x004F, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0xFFFF, 0xFFFF };
+unsigned short unac_data3[] = { 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0065, 0x0063, 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0xFFFF, 0x006E, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0xFFFF, 0x006F, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0xFFFF, 0x0079 };
+unsigned short unac_data4[] = { 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, 0xFFFF, 0xFFFF, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067 };
+unsigned short unac_data5[] = { 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0xFFFF, 0xFFFF, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0xFFFF, 0x0049, 0x004A, 0x0069, 0x006A, 0x004A, 0x006A, 0x004B, 0x006B, 0xFFFF, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x00B7 };
+unsigned short unac_data6[] = { 0x006C, 0x00B7, 0xFFFF, 0xFFFF, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, 0x006E, 0x02BC, 0x006E, 0xFFFF, 0xFFFF, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0xFFFF, 0xFFFF, 0x0052, 0x0072, 0x0052, 0x0072, 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073 };
+unsigned short unac_data7[] = { 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0xFFFF, 0xFFFF, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, 0x0059, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0073 };
+unsigned short unac_data8[] = { 0x004F, 0x006F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0055, 0x0075, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data9[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0044, 0x005A, 0x0044, 0x007A, 0x0064, 0x007A, 0x004C, 0x004A, 0x004C, 0x006A, 0x006C, 0x006A, 0x004E, 0x004A, 0x004E, 0x006A, 0x006E, 0x006A, 0x0041, 0x0061, 0x0049, 0x0069, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0xFFFF, 0x0041, 0x0061 };
+unsigned short unac_data10[] = { 0x0041, 0x0061, 0x0041, 0x0045, 0x0061, 0x0065, 0xFFFF, 0xFFFF, 0x0047, 0x0067, 0x004B, 0x006B, 0x004F, 0x006F, 0x004F, 0x006F, 0x01B7, 0x0292, 0x006A, 0x0044, 0x005A, 0x0044, 0x007A, 0x0064, 0x007A, 0x0047, 0x0067, 0xFFFF, 0xFFFF, 0x004E, 0x006E, 0x0041, 0x0061, 0x0041, 0x0045, 0x0061, 0x0065, 0x004F, 0x006F };
+unsigned short unac_data11[] = { 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F, 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, 0x0053, 0x0073, 0x0054, 0x0074, 0xFFFF, 0xFFFF, 0x0048, 0x0068 };
+unsigned short unac_data12[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0041, 0x0061, 0x0045, 0x0065, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x0059, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data13[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0068, 0x0266, 0x006A, 0x0072, 0x0279, 0x027B, 0x0281, 0x0077, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data14[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xFFFF, 0xFFFF };
+unsigned short unac_data15[] = { 0x0263, 0x006C, 0x0073, 0x0078, 0x0295, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data16[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x02B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0x003B, 0xFFFF };
+unsigned short unac_data17[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0x0391, 0x00B7, 0x0395, 0x0397, 0x0399, 0xFFFF, 0x039F, 0xFFFF, 0x03A5, 0x03A9, 0x03B9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data18[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0399, 0x03A5, 0x03B1, 0x03B5, 0x03B7, 0x03B9, 0x03C5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data19[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B9, 0x03C5, 0x03BF, 0x03C5, 0x03C9, 0xFFFF, 0x03B2, 0x03B8, 0x03A5, 0x03A5, 0x03A5, 0x03C6, 0x03C0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data20[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03BA, 0x03C1, 0x03C2, 0xFFFF, 0x0398, 0x03B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data21[] = { 0x0415, 0x0415, 0xFFFF, 0x0413, 0xFFFF, 0xFFFF, 0xFFFF, 0x0406, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x041A, 0x0418, 0x0423, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0418, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data22[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0438, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data23[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0435, 0x0435, 0xFFFF, 0x0433, 0xFFFF, 0xFFFF, 0xFFFF, 0x0456, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x043A, 0x0438, 0x0443, 0xFFFF };
+unsigned short unac_data24[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0474, 0x0475, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data25[] = { 0xFFFF, 0x0416, 0x0436, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0410, 0x0430, 0x0410, 0x0430, 0xFFFF, 0xFFFF, 0x0415, 0x0435, 0xFFFF, 0xFFFF, 0x04D8, 0x04D9, 0x0416, 0x0436, 0x0417, 0x0437 };
+unsigned short unac_data26[] = { 0xFFFF, 0xFFFF, 0x0418, 0x0438, 0x0418, 0x0438, 0x041E, 0x043E, 0xFFFF, 0xFFFF, 0x04E8, 0x04E9, 0x042D, 0x044D, 0x0423, 0x0443, 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0xFFFF, 0xFFFF, 0x042B, 0x044B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data27[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0565, 0x0582, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data28[] = { 0xFFFF, 0xFFFF, 0x0627, 0x0627, 0x0648, 0x0627, 0x064A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data29[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0627, 0x0674, 0x0648, 0x0674, 0x06C7, 0x0674, 0x064A, 0x0674, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data30[] = { 0x06D5, 0xFFFF, 0x06C1, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x06D2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data31[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0928, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0930, 0xFFFF, 0xFFFF, 0x0933, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data32[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0915, 0x0916, 0x0917, 0x091C, 0x0921, 0x0922, 0x092B, 0x092F };
+unsigned short unac_data33[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x09A1, 0x09A2, 0xFFFF, 0x09AF };
+unsigned short unac_data34[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0A32, 0xFFFF, 0xFFFF, 0x0A38, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data35[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0A16, 0x0A17, 0x0A1C, 0xFFFF, 0xFFFF, 0x0A2B, 0xFFFF };
+unsigned short unac_data36[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0B21, 0x0B22, 0xFFFF, 0xFFFF };
+unsigned short unac_data37[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0B92, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data38[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0E32, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data39[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0EB2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data40[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0EAB, 0x0E99, 0x0EAB, 0x0EA1, 0xFFFF, 0xFFFF };
+unsigned short unac_data41[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F0B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data42[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0x0F42, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F4C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F51, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F56, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F5B, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data43[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0F40, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data44[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1025, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data45[] = { 0x0041, 0x0061, 0x0042, 0x0062, 0x0042, 0x0062, 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, 0x0044, 0x0064, 0x0044, 0x0064, 0x0044, 0x0064, 0x0044, 0x0064, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0046, 0x0066 };
+unsigned short unac_data46[] = { 0x0047, 0x0067, 0x0048, 0x0068, 0x0048, 0x0068, 0x0048, 0x0068, 0x0048, 0x0068, 0x0048, 0x0068, 0x0049, 0x0069, 0x0049, 0x0069, 0x004B, 0x006B, 0x004B, 0x006B, 0x004B, 0x006B, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004D, 0x006D };
+unsigned short unac_data47[] = { 0x004D, 0x006D, 0x004D, 0x006D, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, 0x006E, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x0050, 0x0070, 0x0050, 0x0070, 0x0052, 0x0072, 0x0052, 0x0072, 0x0052, 0x0072, 0x0052, 0x0072 };
+unsigned short unac_data48[] = { 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0054, 0x0074, 0x0054, 0x0074, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0056, 0x0076, 0x0056, 0x0076 };
+unsigned short unac_data49[] = { 0x0057, 0x0077, 0x0057, 0x0077, 0x0057, 0x0077, 0x0057, 0x0077, 0x0057, 0x0077, 0x0058, 0x0078, 0x0058, 0x0078, 0x0059, 0x0079, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0068, 0x0074, 0x0077, 0x0079, 0x0061, 0x02BE, 0x0073, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data50[] = { 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065 };
+unsigned short unac_data51[] = { 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F };
+unsigned short unac_data52[] = { 0x004F, 0x006F, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0059, 0x0079, 0x0059, 0x0079, 0x0059, 0x0079, 0x0059, 0x0079, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data53[] = { 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x03B5, 0x03B5, 0x03B5, 0x03B5, 0x03B5, 0x03B5, 0xFFFF, 0xFFFF, 0x0395, 0x0395, 0x0395, 0x0395, 0x0395, 0x0395, 0xFFFF, 0xFFFF };
+unsigned short unac_data54[] = { 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0x0399, 0x0399, 0x0399, 0x0399, 0x0399, 0x0399, 0x0399, 0x0399 };
+unsigned short unac_data55[] = { 0x03BF, 0x03BF, 0x03BF, 0x03BF, 0x03BF, 0x03BF, 0xFFFF, 0xFFFF, 0x039F, 0x039F, 0x039F, 0x039F, 0x039F, 0x039F, 0xFFFF, 0xFFFF, 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0xFFFF, 0x03A5, 0xFFFF, 0x03A5, 0xFFFF, 0x03A5, 0xFFFF, 0x03A5 };
+unsigned short unac_data56[] = { 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03B1, 0x03B1, 0x03B5, 0x03B5, 0x03B7, 0x03B7, 0x03B9, 0x03B9, 0x03BF, 0x03BF, 0x03C5, 0x03C5, 0x03C9, 0x03C9, 0xFFFF, 0xFFFF };
+unsigned short unac_data57[] = { 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x03B7, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397, 0x0397 };
+unsigned short unac_data58[] = { 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03C9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03A9, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0x03B1, 0xFFFF, 0x03B1, 0x03B1, 0x0391, 0x0391, 0x0391, 0x0391, 0x0391, 0x0020, 0x03B9, 0x0020 };
+unsigned short unac_data59[] = { 0x0020, 0x0020, 0x03B7, 0x03B7, 0x03B7, 0xFFFF, 0x03B7, 0x03B7, 0x0395, 0x0395, 0x0397, 0x0397, 0x0397, 0x0020, 0x0020, 0x0020, 0x03B9, 0x03B9, 0x03B9, 0x03B9, 0xFFFF, 0xFFFF, 0x03B9, 0x03B9, 0x0399, 0x0399, 0x0399, 0x0399, 0xFFFF, 0x0020, 0x0020, 0x0020 };
+unsigned short unac_data60[] = { 0x03C5, 0x03C5, 0x03C5, 0x03C5, 0x03C1, 0x03C1, 0x03C5, 0x03C5, 0x03A5, 0x03A5, 0x03A5, 0x03A5, 0x03A1, 0x0020, 0x0020, 0x0060, 0xFFFF, 0xFFFF, 0x03C9, 0x03C9, 0x03C9, 0xFFFF, 0x03C9, 0x03C9, 0x039F, 0x039F, 0x03A9, 0x03A9, 0x03A9, 0x0020, 0x0020, 0xFFFF };
+unsigned short unac_data61[] = { 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2010, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data62[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0x2032, 0x2032, 0x2032, 0x2032, 0x2032, 0xFFFF, 0x2035, 0x2035, 0x2035, 0x2035, 0x2035, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0021, 0x0021, 0xFFFF, 0x0020, 0xFFFF };
+unsigned short unac_data63[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003F, 0x003F, 0x003F, 0x0021, 0x0021, 0x003F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2032, 0x2032, 0x2032, 0x2032, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020 };
+unsigned short unac_data64[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0030, 0x0069, 0xFFFF, 0xFFFF, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x002B, 0x2212, 0x003D, 0x0028, 0x0029, 0x006E };
+unsigned short unac_data65[] = { 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x002B, 0x2212, 0x003D, 0x0028, 0x0029, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data66[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0052, 0x0073, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data67[] = { 0x0061, 0x002F, 0x0063, 0x0061, 0x002F, 0x0073, 0x0043, 0x00B0, 0x0043, 0xFFFF, 0x0063, 0x002F, 0x006F, 0x0063, 0x002F, 0x0075, 0x0190, 0xFFFF, 0x00B0, 0x0046, 0x0067, 0x0048, 0x0048, 0x0048, 0x0068, 0x0127, 0x0049, 0x0049, 0x004C, 0x006C, 0xFFFF, 0x004E, 0x004E, 0x006F, 0xFFFF, 0xFFFF, 0x0050, 0x0051, 0x0052, 0x0052, 0x0052, 0xFFFF, 0xFFFF };
+unsigned short unac_data68[] = { 0x0053, 0x004D, 0x0054, 0x0045, 0x004C, 0x0054, 0x004D, 0xFFFF, 0x005A, 0xFFFF, 0x03A9, 0xFFFF, 0x005A, 0xFFFF, 0x004B, 0x0041, 0x0042, 0x0043, 0xFFFF, 0x0065, 0x0045, 0x0046, 0xFFFF, 0x004D, 0x006F, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x0069, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B3, 0x0393, 0x03A0 };
+unsigned short unac_data69[] = { 0x2211, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0044, 0x0064, 0x0065, 0x0069, 0x006A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0031, 0x2044, 0x0033, 0x0032, 0x2044, 0x0033, 0x0031, 0x2044, 0x0035, 0x0032, 0x2044, 0x0035, 0x0033, 0x2044, 0x0035, 0x0034, 0x2044, 0x0035, 0x0031, 0x2044, 0x0036, 0x0035, 0x2044, 0x0036, 0x0031, 0x2044, 0x0038, 0x0033, 0x2044, 0x0038, 0x0035, 0x2044, 0x0038, 0x0037, 0x2044, 0x0038, 0x0031, 0x2044 };
+unsigned short unac_data70[] = { 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0056, 0x0056, 0x0056, 0x0049, 0x0056, 0x0049, 0x0049, 0x0056, 0x0049, 0x0049, 0x0049, 0x0049, 0x0058, 0x0058, 0x0058, 0x0049, 0x0058, 0x0049, 0x0049, 0x004C, 0x0043, 0x0044, 0x004D, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, 0x0076, 0x0076, 0x0076, 0x0069, 0x0076, 0x0069, 0x0069, 0x0076, 0x0069, 0x0069, 0x0069, 0x0069, 0x0078, 0x0078, 0x0078, 0x0069, 0x0078, 0x0069, 0x0069, 0x006C, 0x0063, 0x0064, 0x006D };
+unsigned short unac_data71[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2190, 0x2192, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data72[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2194, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data73[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x21D0, 0x21D4, 0x21D2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data74[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2203, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2208, 0xFFFF, 0xFFFF, 0x220B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data75[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2223, 0xFFFF, 0x2225, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x222B, 0x222B, 0x222B, 0x222B, 0x222B, 0xFFFF, 0x222E, 0x222E, 0x222E, 0x222E, 0x222E, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data76[] = { 0xFFFF, 0x223C, 0xFFFF, 0xFFFF, 0x2243, 0xFFFF, 0xFFFF, 0x2245, 0xFFFF, 0x2248, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data77[] = { 0x003D, 0xFFFF, 0x2261, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x224D, 0x003C, 0x003E, 0x2264, 0x2265, 0xFFFF, 0xFFFF, 0x2272, 0x2273, 0xFFFF, 0xFFFF, 0x2276, 0x2277, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data78[] = { 0x227A, 0x227B, 0xFFFF, 0xFFFF, 0x2282, 0x2283, 0xFFFF, 0xFFFF, 0x2286, 0x2287, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data79[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x22A2, 0x22A8, 0x22A9, 0x22AB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data80[] = { 0x227C, 0x227D, 0x2291, 0x2292, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x22B2, 0x22B3, 0x22B4, 0x22B5, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data81[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3008, 0x3009, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data82[] = { 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0031, 0x0030, 0x0031, 0x0031, 0x0031, 0x0032, 0x0031, 0x0033, 0x0031, 0x0034, 0x0031, 0x0035, 0x0031, 0x0036, 0x0031, 0x0037, 0x0031, 0x0038, 0x0031, 0x0039, 0x0032, 0x0030, 0x0028, 0x0031, 0x0029, 0x0028, 0x0032, 0x0029, 0x0028, 0x0033, 0x0029, 0x0028, 0x0034, 0x0029, 0x0028, 0x0035, 0x0029, 0x0028, 0x0036, 0x0029, 0x0028, 0x0037, 0x0029, 0x0028, 0x0038, 0x0029, 0x0028, 0x0039, 0x0029, 0x0028, 0x0031, 0x0030, 0x0029, 0x0028, 0x0031, 0x0031, 0x0029, 0x0028, 0x0031, 0x0032, 0x0029 };
+unsigned short unac_data83[] = { 0x0028, 0x0031, 0x0033, 0x0029, 0x0028, 0x0031, 0x0034, 0x0029, 0x0028, 0x0031, 0x0035, 0x0029, 0x0028, 0x0031, 0x0036, 0x0029, 0x0028, 0x0031, 0x0037, 0x0029, 0x0028, 0x0031, 0x0038, 0x0029, 0x0028, 0x0031, 0x0039, 0x0029, 0x0028, 0x0032, 0x0030, 0x0029, 0x0031, 0x002E, 0x0032, 0x002E, 0x0033, 0x002E, 0x0034, 0x002E, 0x0035, 0x002E, 0x0036, 0x002E, 0x0037, 0x002E, 0x0038, 0x002E, 0x0039, 0x002E, 0x0031, 0x0030, 0x002E, 0x0031, 0x0031, 0x002E, 0x0031, 0x0032, 0x002E, 0x0031, 0x0033, 0x002E, 0x0031, 0x0034, 0x002E, 0x0031, 0x0035, 0x002E, 0x0031, 0x0036, 0x002E, 0x0031, 0x0037, 0x002E, 0x0031, 0x0038, 0x002E, 0x0031, 0x0039, 0x002E, 0x0032, 0x0030, 0x002E, 0x0028, 0x0061, 0x0029, 0x0028, 0x0062, 0x0029, 0x0028, 0x0063, 0x0029, 0x0028, 0x0064, 0x0029 };
+unsigned short unac_data84[] = { 0x0028, 0x0065, 0x0029, 0x0028, 0x0066, 0x0029, 0x0028, 0x0067, 0x0029, 0x0028, 0x0068, 0x0029, 0x0028, 0x0069, 0x0029, 0x0028, 0x006A, 0x0029, 0x0028, 0x006B, 0x0029, 0x0028, 0x006C, 0x0029, 0x0028, 0x006D, 0x0029, 0x0028, 0x006E, 0x0029, 0x0028, 0x006F, 0x0029, 0x0028, 0x0070, 0x0029, 0x0028, 0x0071, 0x0029, 0x0028, 0x0072, 0x0029, 0x0028, 0x0073, 0x0029, 0x0028, 0x0074, 0x0029, 0x0028, 0x0075, 0x0029, 0x0028, 0x0076, 0x0029, 0x0028, 0x0077, 0x0029, 0x0028, 0x0078, 0x0029, 0x0028, 0x0079, 0x0029, 0x0028, 0x007A, 0x0029, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A };
+unsigned short unac_data85[] = { 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070 };
+unsigned short unac_data86[] = { 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0030, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data87[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x222B, 0x222B, 0x222B, 0x222B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data88[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x003A, 0x003A, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0x003D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data89[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2ADD, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data90[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6BCD };
+unsigned short unac_data91[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x9F9F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data92[] = { 0x4E00, 0x4E28, 0x4E36, 0x4E3F, 0x4E59, 0x4E85, 0x4E8C, 0x4EA0, 0x4EBA, 0x513F, 0x5165, 0x516B, 0x5182, 0x5196, 0x51AB, 0x51E0, 0x51F5, 0x5200, 0x529B, 0x52F9, 0x5315, 0x531A, 0x5338, 0x5341, 0x535C, 0x5369, 0x5382, 0x53B6, 0x53C8, 0x53E3, 0x56D7, 0x571F };
+unsigned short unac_data93[] = { 0x58EB, 0x5902, 0x590A, 0x5915, 0x5927, 0x5973, 0x5B50, 0x5B80, 0x5BF8, 0x5C0F, 0x5C22, 0x5C38, 0x5C6E, 0x5C71, 0x5DDB, 0x5DE5, 0x5DF1, 0x5DFE, 0x5E72, 0x5E7A, 0x5E7F, 0x5EF4, 0x5EFE, 0x5F0B, 0x5F13, 0x5F50, 0x5F61, 0x5F73, 0x5FC3, 0x6208, 0x6236, 0x624B };
+unsigned short unac_data94[] = { 0x652F, 0x6534, 0x6587, 0x6597, 0x65A4, 0x65B9, 0x65E0, 0x65E5, 0x66F0, 0x6708, 0x6728, 0x6B20, 0x6B62, 0x6B79, 0x6BB3, 0x6BCB, 0x6BD4, 0x6BDB, 0x6C0F, 0x6C14, 0x6C34, 0x706B, 0x722A, 0x7236, 0x723B, 0x723F, 0x7247, 0x7259, 0x725B, 0x72AC, 0x7384, 0x7389 };
+unsigned short unac_data95[] = { 0x74DC, 0x74E6, 0x7518, 0x751F, 0x7528, 0x7530, 0x758B, 0x7592, 0x7676, 0x767D, 0x76AE, 0x76BF, 0x76EE, 0x77DB, 0x77E2, 0x77F3, 0x793A, 0x79B8, 0x79BE, 0x7A74, 0x7ACB, 0x7AF9, 0x7C73, 0x7CF8, 0x7F36, 0x7F51, 0x7F8A, 0x7FBD, 0x8001, 0x800C, 0x8012, 0x8033 };
+unsigned short unac_data96[] = { 0x807F, 0x8089, 0x81E3, 0x81EA, 0x81F3, 0x81FC, 0x820C, 0x821B, 0x821F, 0x826E, 0x8272, 0x8278, 0x864D, 0x866B, 0x8840, 0x884C, 0x8863, 0x897E, 0x898B, 0x89D2, 0x8A00, 0x8C37, 0x8C46, 0x8C55, 0x8C78, 0x8C9D, 0x8D64, 0x8D70, 0x8DB3, 0x8EAB, 0x8ECA, 0x8F9B };
+unsigned short unac_data97[] = { 0x8FB0, 0x8FB5, 0x9091, 0x9149, 0x91C6, 0x91CC, 0x91D1, 0x9577, 0x9580, 0x961C, 0x96B6, 0x96B9, 0x96E8, 0x9751, 0x975E, 0x9762, 0x9769, 0x97CB, 0x97ED, 0x97F3, 0x9801, 0x98A8, 0x98DB, 0x98DF, 0x9996, 0x9999, 0x99AC, 0x9AA8, 0x9AD8, 0x9ADF, 0x9B25, 0x9B2F };
+unsigned short unac_data98[] = { 0x9B32, 0x9B3C, 0x9B5A, 0x9CE5, 0x9E75, 0x9E7F, 0x9EA5, 0x9EBB, 0x9EC3, 0x9ECD, 0x9ED1, 0x9EF9, 0x9EFD, 0x9F0E, 0x9F13, 0x9F20, 0x9F3B, 0x9F4A, 0x9F52, 0x9F8D, 0x9F9C, 0x9FA0, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data99[] = { 0x0020, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data100[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3012, 0xFFFF, 0x5341, 0x5344, 0x5345, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data101[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x304B, 0xFFFF, 0x304D, 0xFFFF, 0x304F, 0xFFFF, 0x3051, 0xFFFF, 0x3053, 0xFFFF, 0x3055, 0xFFFF, 0x3057, 0xFFFF, 0x3059, 0xFFFF, 0x305B, 0xFFFF, 0x305D, 0xFFFF };
+unsigned short unac_data102[] = { 0x305F, 0xFFFF, 0x3061, 0xFFFF, 0xFFFF, 0x3064, 0xFFFF, 0x3066, 0xFFFF, 0x3068, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x306F, 0x306F, 0xFFFF, 0x3072, 0x3072, 0xFFFF, 0x3075, 0x3075, 0xFFFF, 0x3078, 0x3078, 0xFFFF, 0x307B, 0x307B, 0xFFFF, 0xFFFF };
+unsigned short unac_data103[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3046, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0xFFFF, 0x309D, 0x3088, 0x308A };
+unsigned short unac_data104[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x30AB, 0xFFFF, 0x30AD, 0xFFFF, 0x30AF, 0xFFFF, 0x30B1, 0xFFFF, 0x30B3, 0xFFFF, 0x30B5, 0xFFFF, 0x30B7, 0xFFFF, 0x30B9, 0xFFFF, 0x30BB, 0xFFFF, 0x30BD, 0xFFFF };
+unsigned short unac_data105[] = { 0x30BF, 0xFFFF, 0x30C1, 0xFFFF, 0xFFFF, 0x30C4, 0xFFFF, 0x30C6, 0xFFFF, 0x30C8, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x30CF, 0x30CF, 0xFFFF, 0x30D2, 0x30D2, 0xFFFF, 0x30D5, 0x30D5, 0xFFFF, 0x30D8, 0x30D8, 0xFFFF, 0x30DB, 0x30DB, 0xFFFF, 0xFFFF };
+unsigned short unac_data106[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x30A6, 0xFFFF, 0xFFFF, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0xFFFF, 0xFFFF, 0xFFFF, 0x30FD, 0x30B3, 0x30C8 };
+unsigned short unac_data107[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x1100, 0x1101, 0x11AA, 0x1102, 0x11AC, 0x11AD, 0x1103, 0x1104, 0x1105, 0x11B0, 0x11B1, 0x11B2, 0x11B3, 0x11B4, 0x11B5 };
+unsigned short unac_data108[] = { 0x111A, 0x1106, 0x1107, 0x1108, 0x1121, 0x1109, 0x110A, 0x110B, 0x110C, 0x110D, 0x110E, 0x110F, 0x1110, 0x1111, 0x1112, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, 0x116A, 0x116B, 0x116C, 0x116D, 0x116E, 0x116F, 0x1170, 0x1171 };
+unsigned short unac_data109[] = { 0x1172, 0x1173, 0x1174, 0x1175, 0x1160, 0x1114, 0x1115, 0x11C7, 0x11C8, 0x11CC, 0x11CE, 0x11D3, 0x11D7, 0x11D9, 0x111C, 0x11DD, 0x11DF, 0x111D, 0x111E, 0x1120, 0x1122, 0x1123, 0x1127, 0x1129, 0x112B, 0x112C, 0x112D, 0x112E, 0x112F, 0x1132, 0x1136, 0x1140 };
+unsigned short unac_data110[] = { 0x1147, 0x114C, 0x11F1, 0x11F2, 0x1157, 0x1158, 0x1159, 0x1184, 0x1185, 0x1188, 0x1191, 0x1192, 0x1194, 0x119E, 0x11A1, 0xFFFF, 0xFFFF, 0xFFFF, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E0A, 0x4E2D, 0x4E0B, 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x5929, 0x5730, 0x4EBA };
+unsigned short unac_data111[] = { 0x0028, 0x1100, 0x0029, 0x0028, 0x1102, 0x0029, 0x0028, 0x1103, 0x0029, 0x0028, 0x1105, 0x0029, 0x0028, 0x1106, 0x0029, 0x0028, 0x1107, 0x0029, 0x0028, 0x1109, 0x0029, 0x0028, 0x110B, 0x0029, 0x0028, 0x110C, 0x0029, 0x0028, 0x110E, 0x0029, 0x0028, 0x110F, 0x0029, 0x0028, 0x1110, 0x0029, 0x0028, 0x1111, 0x0029, 0x0028, 0x1112, 0x0029, 0x0028, 0x1100, 0x1161, 0x0029, 0x0028, 0x1102, 0x1161, 0x0029, 0x0028, 0x1103, 0x1161, 0x0029, 0x0028, 0x1105, 0x1161, 0x0029, 0x0028, 0x1106, 0x1161, 0x0029, 0x0028, 0x1107, 0x1161, 0x0029, 0x0028, 0x1109, 0x1161, 0x0029, 0x0028, 0x110B, 0x1161, 0x0029, 0x0028, 0x110C, 0x1161, 0x0029, 0x0028, 0x110E, 0x1161, 0x0029, 0x0028, 0x110F, 0x1161, 0x0029, 0x0028, 0x1110, 0x1161, 0x0029, 0x0028, 0x1111, 0x1161, 0x0029, 0x0028, 0x1112, 0x1161, 0x0029, 0x0028, 0x110C, 0x116E, 0x0029, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data112[] = { 0x0028, 0x4E00, 0x0029, 0x0028, 0x4E8C, 0x0029, 0x0028, 0x4E09, 0x0029, 0x0028, 0x56DB, 0x0029, 0x0028, 0x4E94, 0x0029, 0x0028, 0x516D, 0x0029, 0x0028, 0x4E03, 0x0029, 0x0028, 0x516B, 0x0029, 0x0028, 0x4E5D, 0x0029, 0x0028, 0x5341, 0x0029, 0x0028, 0x6708, 0x0029, 0x0028, 0x706B, 0x0029, 0x0028, 0x6C34, 0x0029, 0x0028, 0x6728, 0x0029, 0x0028, 0x91D1, 0x0029, 0x0028, 0x571F, 0x0029, 0x0028, 0x65E5, 0x0029, 0x0028, 0x682A, 0x0029, 0x0028, 0x6709, 0x0029, 0x0028, 0x793E, 0x0029, 0x0028, 0x540D, 0x0029, 0x0028, 0x7279, 0x0029, 0x0028, 0x8CA1, 0x0029, 0x0028, 0x795D, 0x0029, 0x0028, 0x52B4, 0x0029, 0x0028, 0x4EE3, 0x0029, 0x0028, 0x547C, 0x0029, 0x0028, 0x5B66, 0x0029, 0x0028, 0x76E3, 0x0029, 0x0028, 0x4F01, 0x0029, 0x0028, 0x8CC7, 0x0029, 0x0028, 0x5354, 0x0029 };
+unsigned short unac_data113[] = { 0x0028, 0x796D, 0x0029, 0x0028, 0x4F11, 0x0029, 0x0028, 0x81EA, 0x0029, 0x0028, 0x81F3, 0x0029, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0032, 0x0031, 0x0032, 0x0032, 0x0032, 0x0033, 0x0032, 0x0034, 0x0032, 0x0035, 0x0032, 0x0036, 0x0032, 0x0037, 0x0032, 0x0038, 0x0032, 0x0039, 0x0033, 0x0030, 0x0033, 0x0031, 0x0033, 0x0032, 0x0033, 0x0033, 0x0033, 0x0034, 0x0033, 0x0035 };
+unsigned short unac_data114[] = { 0x1100, 0x1102, 0x1103, 0x1105, 0x1106, 0x1107, 0x1109, 0x110B, 0x110C, 0x110E, 0x110F, 0x1110, 0x1111, 0x1112, 0x1100, 0x1161, 0x1102, 0x1161, 0x1103, 0x1161, 0x1105, 0x1161, 0x1106, 0x1161, 0x1107, 0x1161, 0x1109, 0x1161, 0x110B, 0x1161, 0x110C, 0x1161, 0x110E, 0x1161, 0x110F, 0x1161, 0x1110, 0x1161, 0x1111, 0x1161, 0x1112, 0x1161, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data115[] = { 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D, 0x5341, 0x6708, 0x706B, 0x6C34, 0x6728, 0x91D1, 0x571F, 0x65E5, 0x682A, 0x6709, 0x793E, 0x540D, 0x7279, 0x8CA1, 0x795D, 0x52B4, 0x79D8, 0x7537, 0x5973, 0x9069, 0x512A, 0x5370, 0x6CE8 };
+unsigned short unac_data116[] = { 0x9805, 0x4F11, 0x5199, 0x6B63, 0x4E0A, 0x4E2D, 0x4E0B, 0x5DE6, 0x53F3, 0x533B, 0x5B97, 0x5B66, 0x76E3, 0x4F01, 0x8CC7, 0x5354, 0x591C, 0x0033, 0x0036, 0x0033, 0x0037, 0x0033, 0x0038, 0x0033, 0x0039, 0x0034, 0x0030, 0x0034, 0x0031, 0x0034, 0x0032, 0x0034, 0x0033, 0x0034, 0x0034, 0x0034, 0x0035, 0x0034, 0x0036, 0x0034, 0x0037, 0x0034, 0x0038, 0x0034, 0x0039, 0x0035, 0x0030 };
+unsigned short unac_data117[] = { 0x0031, 0x6708, 0x0032, 0x6708, 0x0033, 0x6708, 0x0034, 0x6708, 0x0035, 0x6708, 0x0036, 0x6708, 0x0037, 0x6708, 0x0038, 0x6708, 0x0039, 0x6708, 0x0031, 0x0030, 0x6708, 0x0031, 0x0031, 0x6708, 0x0031, 0x0032, 0x6708, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF };
+unsigned short unac_data118[] = { 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0xFFFF };
+unsigned short unac_data119[] = { 0x30A2, 0x30FC, 0x30C8, 0x30CF, 0x30A2, 0x30EB, 0x30D5, 0x30A1, 0x30A2, 0x30F3, 0x30A2, 0x30D8, 0x30A2, 0x30FC, 0x30EB, 0x30A4, 0x30CB, 0x30F3, 0x30AF, 0x30A4, 0x30F3, 0x30C1, 0x30A6, 0x30A9, 0x30F3, 0x30A8, 0x30B9, 0x30AF, 0x30FC, 0x30C8, 0x30A8, 0x30FC, 0x30AB, 0x30FC, 0x30AA, 0x30F3, 0x30B9, 0x30AA, 0x30FC, 0x30E0, 0x30AB, 0x30A4, 0x30EA, 0x30AB, 0x30E9, 0x30C3, 0x30C8, 0x30AB, 0x30ED, 0x30EA, 0x30FC, 0x30ED, 0x30F3, 0x30AB, 0x30F3, 0x30DE, 0x30AB, 0x30AD, 0x30AB, 0x30CB, 0x30FC, 0x30AD, 0x30AD, 0x30E5, 0x30EA, 0x30FC, 0x30EB, 0x30FC, 0x30AD, 0x30BF, 0x30AD, 0x30ED, 0x30AD, 0x30ED, 0x30E9, 0x30E0, 0x30AF, 0x30AD, 0x30ED, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0x30AD, 0x30ED, 0x30EF, 0x30C3, 0x30C8, 0x30E9, 0x30E0, 0x30AF, 0x30E9, 0x30E0, 0x30C8, 0x30F3, 0x30AF, 0x30AF, 0x30EB, 0x30A4, 0x30ED, 0x30BB, 0x30AF, 0x30ED, 0x30FC, 0x30CD, 0x30B1, 0x30FC, 0x30B9, 0x30B3, 0x30EB, 0x30CA, 0x30B3, 0x30FC, 0x30DB, 0x30B5, 0x30A4, 0x30AF, 0x30EB };
+unsigned short unac_data120[] = { 0x30B5, 0x30F3, 0x30C1, 0x30FC, 0x30E0, 0x30B7, 0x30EA, 0x30F3, 0x30AF, 0x30BB, 0x30F3, 0x30C1, 0x30BB, 0x30F3, 0x30C8, 0x30FC, 0x30B9, 0x30BF, 0x30B7, 0x30C6, 0x30EB, 0x30C8, 0x30C8, 0x30F3, 0x30CA, 0x30CE, 0x30CE, 0x30C3, 0x30C8, 0x30CF, 0x30A4, 0x30C4, 0x30FC, 0x30BB, 0x30F3, 0x30C8, 0x30CF, 0x30FC, 0x30C4, 0x30CF, 0x30FC, 0x30EC, 0x30EB, 0x30CF, 0x30A2, 0x30B9, 0x30C8, 0x30EB, 0x30D2, 0x30AF, 0x30EB, 0x30D2, 0x30B3, 0x30D2, 0x30EB, 0x30D2, 0x30D5, 0x30A1, 0x30E9, 0x30C3, 0x30C8, 0x30D5, 0x30A3, 0x30FC, 0x30C8, 0x30C3, 0x30B7, 0x30A7, 0x30EB, 0x30D5, 0x30D5, 0x30E9, 0x30F3, 0x30D8, 0x30AF, 0x30BF, 0x30FC, 0x30EB, 0x30BD, 0x30D8, 0x30CB, 0x30D2, 0x30D8, 0x30D8, 0x30EB, 0x30C4, 0x30F3, 0x30B9, 0x30D8, 0x30FC, 0x30D8, 0x30B7, 0x30FC, 0x30BF, 0x30D8, 0x30A4, 0x30F3, 0x30C8, 0x30DB, 0x30EB, 0x30C8, 0x30DB, 0x30DB, 0x30F3 };
+unsigned short unac_data121[] = { 0x30F3, 0x30DB, 0x30C8, 0x30DB, 0x30FC, 0x30EB, 0x30DB, 0x30FC, 0x30F3, 0x30DE, 0x30A4, 0x30AF, 0x30ED, 0x30DE, 0x30A4, 0x30EB, 0x30DE, 0x30C3, 0x30CF, 0x30DE, 0x30EB, 0x30AF, 0x30DE, 0x30F3, 0x30B7, 0x30E7, 0x30F3, 0x30DF, 0x30AF, 0x30ED, 0x30F3, 0x30DF, 0x30EA, 0x30DF, 0x30EA, 0x30FC, 0x30EB, 0x30CF, 0x30E1, 0x30AB, 0x30E1, 0x30C8, 0x30F3, 0x30AB, 0x30E1, 0x30FC, 0x30C8, 0x30EB, 0x30E4, 0x30FC, 0x30C8, 0x30E4, 0x30FC, 0x30EB, 0x30E6, 0x30A2, 0x30F3, 0x30EA, 0x30C3, 0x30C8, 0x30EB, 0x30EA, 0x30E9, 0x30EB, 0x30FC, 0x30D2, 0x30EB, 0x30FC, 0x30EB, 0x30D5, 0x30EC, 0x30E0, 0x30EC, 0x30F3, 0x30C8, 0x30F3, 0x30B1, 0x30EF, 0x30C3, 0x30C8, 0x0030, 0x70B9, 0x0031, 0x70B9, 0x0032, 0x70B9, 0x0033, 0x70B9, 0x0034, 0x70B9, 0x0035, 0x70B9, 0x0036, 0x70B9, 0x0037, 0x70B9 };
+unsigned short unac_data122[] = { 0x0038, 0x70B9, 0x0039, 0x70B9, 0x0031, 0x0030, 0x70B9, 0x0031, 0x0031, 0x70B9, 0x0031, 0x0032, 0x70B9, 0x0031, 0x0033, 0x70B9, 0x0031, 0x0034, 0x70B9, 0x0031, 0x0035, 0x70B9, 0x0031, 0x0036, 0x70B9, 0x0031, 0x0037, 0x70B9, 0x0031, 0x0038, 0x70B9, 0x0031, 0x0039, 0x70B9, 0x0032, 0x0030, 0x70B9, 0x0032, 0x0031, 0x70B9, 0x0032, 0x0032, 0x70B9, 0x0032, 0x0033, 0x70B9, 0x0032, 0x0034, 0x70B9, 0x0068, 0x0050, 0x0061, 0x0064, 0x0061, 0x0041, 0x0055, 0x0062, 0x0061, 0x0072, 0x006F, 0x0056, 0x0070, 0x0063, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x5E73, 0x6210, 0x662D, 0x548C, 0x5927, 0x6B63, 0x660E, 0x6CBB, 0x682A, 0x5F0F, 0x4F1A, 0x793E };
+unsigned short unac_data123[] = { 0x0070, 0x0041, 0x006E, 0x0041, 0x03BC, 0x0041, 0x006D, 0x0041, 0x006B, 0x0041, 0x004B, 0x0042, 0x004D, 0x0042, 0x0047, 0x0042, 0x0063, 0x0061, 0x006C, 0x006B, 0x0063, 0x0061, 0x006C, 0x0070, 0x0046, 0x006E, 0x0046, 0x03BC, 0x0046, 0x03BC, 0x0067, 0x006D, 0x0067, 0x006B, 0x0067, 0x0048, 0x007A, 0x006B, 0x0048, 0x007A, 0x004D, 0x0048, 0x007A, 0x0047, 0x0048, 0x007A, 0x0054, 0x0048, 0x007A, 0x03BC, 0x006C, 0x006D, 0x006C, 0x0064, 0x006C, 0x006B, 0x006C, 0x0066, 0x006D, 0x006E, 0x006D, 0x03BC, 0x006D, 0x006D, 0x006D, 0x0063, 0x006D, 0x006B, 0x006D, 0x006D, 0x006D, 0x0032 };
+unsigned short unac_data124[] = { 0x0063, 0x006D, 0x0032, 0x006D, 0x0032, 0x006B, 0x006D, 0x0032, 0x006D, 0x006D, 0x0033, 0x0063, 0x006D, 0x0033, 0x006D, 0x0033, 0x006B, 0x006D, 0x0033, 0x006D, 0x2215, 0x0073, 0x006D, 0x2215, 0x0073, 0x0032, 0x0050, 0x0061, 0x006B, 0x0050, 0x0061, 0x004D, 0x0050, 0x0061, 0x0047, 0x0050, 0x0061, 0x0072, 0x0061, 0x0064, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x0072, 0x0061, 0x0064, 0x2215, 0x0073, 0x0032, 0x0070, 0x0073, 0x006E, 0x0073, 0x03BC, 0x0073, 0x006D, 0x0073, 0x0070, 0x0056, 0x006E, 0x0056, 0x03BC, 0x0056, 0x006D, 0x0056, 0x006B, 0x0056, 0x004D, 0x0056, 0x0070, 0x0057, 0x006E, 0x0057, 0x03BC, 0x0057, 0x006D, 0x0057, 0x006B, 0x0057, 0x004D, 0x0057 };
+unsigned short unac_data125[] = { 0x006B, 0x03A9, 0x004D, 0x03A9, 0x0061, 0x002E, 0x006D, 0x002E, 0x0042, 0x0071, 0x0063, 0x0063, 0x0063, 0x0064, 0x0043, 0x2215, 0x006B, 0x0067, 0x0043, 0x006F, 0x002E, 0x0064, 0x0042, 0x0047, 0x0079, 0x0068, 0x0061, 0x0048, 0x0050, 0x0069, 0x006E, 0x004B, 0x004B, 0x004B, 0x004D, 0x006B, 0x0074, 0x006C, 0x006D, 0x006C, 0x006E, 0x006C, 0x006F, 0x0067, 0x006C, 0x0078, 0x006D, 0x0062, 0x006D, 0x0069, 0x006C, 0x006D, 0x006F, 0x006C, 0x0050, 0x0048, 0x0070, 0x002E, 0x006D, 0x002E, 0x0050, 0x0050, 0x004D, 0x0050, 0x0052, 0x0073, 0x0072, 0x0053, 0x0076, 0x0057, 0x0062, 0xFFFF, 0xFFFF };
+unsigned short unac_data126[] = { 0x0031, 0x65E5, 0x0032, 0x65E5, 0x0033, 0x65E5, 0x0034, 0x65E5, 0x0035, 0x65E5, 0x0036, 0x65E5, 0x0037, 0x65E5, 0x0038, 0x65E5, 0x0039, 0x65E5, 0x0031, 0x0030, 0x65E5, 0x0031, 0x0031, 0x65E5, 0x0031, 0x0032, 0x65E5, 0x0031, 0x0033, 0x65E5, 0x0031, 0x0034, 0x65E5, 0x0031, 0x0035, 0x65E5, 0x0031, 0x0036, 0x65E5, 0x0031, 0x0037, 0x65E5, 0x0031, 0x0038, 0x65E5, 0x0031, 0x0039, 0x65E5, 0x0032, 0x0030, 0x65E5, 0x0032, 0x0031, 0x65E5, 0x0032, 0x0032, 0x65E5, 0x0032, 0x0033, 0x65E5, 0x0032, 0x0034, 0x65E5, 0x0032, 0x0035, 0x65E5, 0x0032, 0x0036, 0x65E5, 0x0032, 0x0037, 0x65E5, 0x0032, 0x0038, 0x65E5, 0x0032, 0x0039, 0x65E5, 0x0033, 0x0030, 0x65E5, 0x0033, 0x0031, 0x65E5, 0xFFFF };
+unsigned short unac_data127[] = { 0x8C48, 0x66F4, 0x8ECA, 0x8CC8, 0x6ED1, 0x4E32, 0x53E5, 0x9F9C, 0x9F9C, 0x5951, 0x91D1, 0x5587, 0x5948, 0x61F6, 0x7669, 0x7F85, 0x863F, 0x87BA, 0x88F8, 0x908F, 0x6A02, 0x6D1B, 0x70D9, 0x73DE, 0x843D, 0x916A, 0x99F1, 0x4E82, 0x5375, 0x6B04, 0x721B, 0x862D };
+unsigned short unac_data128[] = { 0x9E1E, 0x5D50, 0x6FEB, 0x85CD, 0x8964, 0x62C9, 0x81D8, 0x881F, 0x5ECA, 0x6717, 0x6D6A, 0x72FC, 0x90CE, 0x4F86, 0x51B7, 0x52DE, 0x64C4, 0x6AD3, 0x7210, 0x76E7, 0x8001, 0x8606, 0x865C, 0x8DEF, 0x9732, 0x9B6F, 0x9DFA, 0x788C, 0x797F, 0x7DA0, 0x83C9, 0x9304 };
+unsigned short unac_data129[] = { 0x9E7F, 0x8AD6, 0x58DF, 0x5F04, 0x7C60, 0x807E, 0x7262, 0x78CA, 0x8CC2, 0x96F7, 0x58D8, 0x5C62, 0x6A13, 0x6DDA, 0x6F0F, 0x7D2F, 0x7E37, 0x964B, 0x52D2, 0x808B, 0x51DC, 0x51CC, 0x7A1C, 0x7DBE, 0x83F1, 0x9675, 0x8B80, 0x62CF, 0x6A02, 0x8AFE, 0x4E39, 0x5BE7 };
+unsigned short unac_data130[] = { 0x6012, 0x7387, 0x7570, 0x5317, 0x78FB, 0x4FBF, 0x5FA9, 0x4E0D, 0x6CCC, 0x6578, 0x7D22, 0x53C3, 0x585E, 0x7701, 0x8449, 0x8AAA, 0x6BBA, 0x8FB0, 0x6C88, 0x62FE, 0x82E5, 0x63A0, 0x7565, 0x4EAE, 0x5169, 0x51C9, 0x6881, 0x7CE7, 0x826F, 0x8AD2, 0x91CF, 0x52F5 };
+unsigned short unac_data131[] = { 0x5442, 0x5973, 0x5EEC, 0x65C5, 0x6FFE, 0x792A, 0x95AD, 0x9A6A, 0x9E97, 0x9ECE, 0x529B, 0x66C6, 0x6B77, 0x8F62, 0x5E74, 0x6190, 0x6200, 0x649A, 0x6F23, 0x7149, 0x7489, 0x79CA, 0x7DF4, 0x806F, 0x8F26, 0x84EE, 0x9023, 0x934A, 0x5217, 0x52A3, 0x54BD, 0x70C8 };
+unsigned short unac_data132[] = { 0x88C2, 0x8AAA, 0x5EC9, 0x5FF5, 0x637B, 0x6BAE, 0x7C3E, 0x7375, 0x4EE4, 0x56F9, 0x5BE7, 0x5DBA, 0x601C, 0x73B2, 0x7469, 0x7F9A, 0x8046, 0x9234, 0x96F6, 0x9748, 0x9818, 0x4F8B, 0x79AE, 0x91B4, 0x96B8, 0x60E1, 0x4E86, 0x50DA, 0x5BEE, 0x5C3F, 0x6599, 0x6A02 };
+unsigned short unac_data133[] = { 0x71CE, 0x7642, 0x84FC, 0x907C, 0x9F8D, 0x6688, 0x962E, 0x5289, 0x677B, 0x67F3, 0x6D41, 0x6E9C, 0x7409, 0x7559, 0x786B, 0x7D10, 0x985E, 0x516D, 0x622E, 0x9678, 0x502B, 0x5D19, 0x6DEA, 0x8F2A, 0x5F8B, 0x6144, 0x6817, 0x7387, 0x9686, 0x5229, 0x540F, 0x5C65 };
+unsigned short unac_data134[] = { 0x6613, 0x674E, 0x68A8, 0x6CE5, 0x7406, 0x75E2, 0x7F79, 0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x533F, 0x6EBA, 0x541D, 0x71D0, 0x7498, 0x85FA, 0x96A3, 0x9C57, 0x9E9F, 0x6797, 0x6DCB, 0x81E8, 0x7ACB, 0x7B20, 0x7C92, 0x72C0, 0x7099, 0x8B58, 0x4EC0, 0x8336, 0x523A };
+unsigned short unac_data135[] = { 0x5207, 0x5EA6, 0x62D3, 0x7CD6, 0x5B85, 0x6D1E, 0x66B4, 0x8F3B, 0x884C, 0x964D, 0x898B, 0x5ED3, 0x5140, 0x55C0, 0xFFFF, 0xFFFF, 0x585A, 0xFFFF, 0x6674, 0xFFFF, 0xFFFF, 0x51DE, 0x732A, 0x76CA, 0x793C, 0x795E, 0x7965, 0x798F, 0x9756, 0x7CBE, 0x7FBD, 0xFFFF };
+unsigned short unac_data136[] = { 0x8612, 0xFFFF, 0x8AF8, 0xFFFF, 0xFFFF, 0x9038, 0x90FD, 0xFFFF, 0xFFFF, 0xFFFF, 0x98EF, 0x98FC, 0x9928, 0x9DB4, 0xFFFF, 0xFFFF, 0x4FAE, 0x50E7, 0x514D, 0x52C9, 0x52E4, 0x5351, 0x559D, 0x5606, 0x5668, 0x5840, 0x58A8, 0x5C64, 0x5C6E, 0x6094, 0x6168, 0x618E };
+unsigned short unac_data137[] = { 0x61F2, 0x654F, 0x65E2, 0x6691, 0x6885, 0x6D77, 0x6E1A, 0x6F22, 0x716E, 0x722B, 0x7422, 0x7891, 0x793E, 0x7949, 0x7948, 0x7950, 0x7956, 0x795D, 0x798D, 0x798E, 0x7A40, 0x7A81, 0x7BC0, 0x7DF4, 0x7E09, 0x7E41, 0x7F72, 0x8005, 0x81ED, 0x8279, 0x8279, 0x8457 };
+unsigned short unac_data138[] = { 0x8910, 0x8996, 0x8B01, 0x8B39, 0x8CD3, 0x8D08, 0x8FB6, 0x9038, 0x96E3, 0x97FF, 0x983B, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data139[] = { 0x0066, 0x0066, 0x0066, 0x0069, 0x0066, 0x006C, 0x0066, 0x0066, 0x0069, 0x0066, 0x0066, 0x006C, 0x0074, 0x0073, 0x0073, 0x0074, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0574, 0x0576, 0x0574, 0x0565, 0x0574, 0x056B, 0x057E, 0x0576, 0x0574, 0x056D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x05D9, 0xFFFF, 0x05F2 };
+unsigned short unac_data140[] = { 0x05E2, 0x05D0, 0x05D3, 0x05D4, 0x05DB, 0x05DC, 0x05DD, 0x05E8, 0x05EA, 0x002B, 0x05E9, 0x05E9, 0x05E9, 0x05E9, 0x05D0, 0x05D0, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0xFFFF, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0xFFFF, 0x05DE, 0xFFFF };
+unsigned short unac_data141[] = { 0x05E0, 0x05E1, 0xFFFF, 0x05E3, 0x05E4, 0xFFFF, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05D5, 0x05D1, 0x05DB, 0x05E4, 0x05D0, 0x05DC, 0x0671, 0x0671, 0x067B, 0x067B, 0x067B, 0x067B, 0x067E, 0x067E, 0x067E, 0x067E, 0x0680, 0x0680, 0x0680, 0x0680, 0x067A, 0x067A };
+unsigned short unac_data142[] = { 0x067A, 0x067A, 0x067F, 0x067F, 0x067F, 0x067F, 0x0679, 0x0679, 0x0679, 0x0679, 0x06A4, 0x06A4, 0x06A4, 0x06A4, 0x06A6, 0x06A6, 0x06A6, 0x06A6, 0x0684, 0x0684, 0x0684, 0x0684, 0x0683, 0x0683, 0x0683, 0x0683, 0x0686, 0x0686, 0x0686, 0x0686, 0x0687, 0x0687 };
+unsigned short unac_data143[] = { 0x0687, 0x0687, 0x068D, 0x068D, 0x068C, 0x068C, 0x068E, 0x068E, 0x0688, 0x0688, 0x0698, 0x0698, 0x0691, 0x0691, 0x06A9, 0x06A9, 0x06A9, 0x06A9, 0x06AF, 0x06AF, 0x06AF, 0x06AF, 0x06B3, 0x06B3, 0x06B3, 0x06B3, 0x06B1, 0x06B1, 0x06B1, 0x06B1, 0x06BA, 0x06BA };
+unsigned short unac_data144[] = { 0x06BB, 0x06BB, 0x06BB, 0x06BB, 0x06D5, 0x06D5, 0x06C1, 0x06C1, 0x06C1, 0x06C1, 0x06BE, 0x06BE, 0x06BE, 0x06BE, 0x06D2, 0x06D2, 0x06D2, 0x06D2, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data145[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x06AD, 0x06AD, 0x06AD, 0x06AD, 0x06C7, 0x06C7, 0x06C6, 0x06C6, 0x06C8, 0x06C8, 0x06C7, 0x0674, 0x06CB, 0x06CB };
+unsigned short unac_data146[] = { 0x06C5, 0x06C5, 0x06C9, 0x06C9, 0x06D0, 0x06D0, 0x06D0, 0x06D0, 0x0649, 0x0649, 0x0627, 0x064A, 0x0627, 0x064A, 0x06D5, 0x064A, 0x06D5, 0x064A, 0x0648, 0x064A, 0x0648, 0x064A, 0x06C7, 0x064A, 0x06C7, 0x064A, 0x06C6, 0x064A, 0x06C6, 0x064A, 0x06C8, 0x064A, 0x06C8, 0x064A, 0x06D0, 0x064A, 0x06D0, 0x064A, 0x06D0, 0x064A, 0x0649, 0x064A, 0x0649, 0x064A, 0x0649, 0x064A, 0x06CC, 0x06CC, 0x06CC, 0x06CC };
+unsigned short unac_data147[] = { 0x062C, 0x064A, 0x062D, 0x064A, 0x0645, 0x064A, 0x0649, 0x064A, 0x064A, 0x064A, 0x0628, 0x062C, 0x0628, 0x062D, 0x0628, 0x062E, 0x0628, 0x0645, 0x0628, 0x0649, 0x0628, 0x064A, 0x062A, 0x062C, 0x062A, 0x062D, 0x062A, 0x062E, 0x062A, 0x0645, 0x062A, 0x0649, 0x062A, 0x064A, 0x062B, 0x062C, 0x062B, 0x0645, 0x062B, 0x0649, 0x062B, 0x064A, 0x062C, 0x062D, 0x062C, 0x0645, 0x062D, 0x062C, 0x062D, 0x0645, 0x062E, 0x062C, 0x062E, 0x062D, 0x062E, 0x0645, 0x0633, 0x062C, 0x0633, 0x062D, 0x0633, 0x062E, 0x0633, 0x0645 };
+unsigned short unac_data148[] = { 0x0635, 0x062D, 0x0635, 0x0645, 0x0636, 0x062C, 0x0636, 0x062D, 0x0636, 0x062E, 0x0636, 0x0645, 0x0637, 0x062D, 0x0637, 0x0645, 0x0638, 0x0645, 0x0639, 0x062C, 0x0639, 0x0645, 0x063A, 0x062C, 0x063A, 0x0645, 0x0641, 0x062C, 0x0641, 0x062D, 0x0641, 0x062E, 0x0641, 0x0645, 0x0641, 0x0649, 0x0641, 0x064A, 0x0642, 0x062D, 0x0642, 0x0645, 0x0642, 0x0649, 0x0642, 0x064A, 0x0643, 0x0627, 0x0643, 0x062C, 0x0643, 0x062D, 0x0643, 0x062E, 0x0643, 0x0644, 0x0643, 0x0645, 0x0643, 0x0649, 0x0643, 0x064A, 0x0644, 0x062C };
+unsigned short unac_data149[] = { 0x0644, 0x062D, 0x0644, 0x062E, 0x0644, 0x0645, 0x0644, 0x0649, 0x0644, 0x064A, 0x0645, 0x062C, 0x0645, 0x062D, 0x0645, 0x062E, 0x0645, 0x0645, 0x0645, 0x0649, 0x0645, 0x064A, 0x0646, 0x062C, 0x0646, 0x062D, 0x0646, 0x062E, 0x0646, 0x0645, 0x0646, 0x0649, 0x0646, 0x064A, 0x0647, 0x062C, 0x0647, 0x0645, 0x0647, 0x0649, 0x0647, 0x064A, 0x064A, 0x062C, 0x064A, 0x062D, 0x064A, 0x062E, 0x064A, 0x0645, 0x064A, 0x0649, 0x064A, 0x064A, 0x0630, 0x0631, 0x0649, 0x0020, 0x0020 };
+unsigned short unac_data150[] = { 0x0020, 0x0020, 0x0020, 0x0020, 0x0631, 0x064A, 0x0632, 0x064A, 0x0645, 0x064A, 0x0646, 0x064A, 0x0649, 0x064A, 0x064A, 0x064A, 0x0628, 0x0631, 0x0628, 0x0632, 0x0628, 0x0645, 0x0628, 0x0646, 0x0628, 0x0649, 0x0628, 0x064A, 0x062A, 0x0631, 0x062A, 0x0632, 0x062A, 0x0645, 0x062A, 0x0646, 0x062A, 0x0649, 0x062A, 0x064A, 0x062B, 0x0631, 0x062B, 0x0632, 0x062B, 0x0645, 0x062B, 0x0646, 0x062B, 0x0649, 0x062B, 0x064A, 0x0641, 0x0649, 0x0641, 0x064A, 0x0642, 0x0649, 0x0642, 0x064A };
+unsigned short unac_data151[] = { 0x0643, 0x0627, 0x0643, 0x0644, 0x0643, 0x0645, 0x0643, 0x0649, 0x0643, 0x064A, 0x0644, 0x0645, 0x0644, 0x0649, 0x0644, 0x064A, 0x0645, 0x0627, 0x0645, 0x0645, 0x0646, 0x0631, 0x0646, 0x0632, 0x0646, 0x0645, 0x0646, 0x0646, 0x0646, 0x0649, 0x0646, 0x064A, 0x0649, 0x064A, 0x0631, 0x064A, 0x0632, 0x064A, 0x0645, 0x064A, 0x0646, 0x064A, 0x0649, 0x064A, 0x064A, 0x062C, 0x064A, 0x062D, 0x064A, 0x062E, 0x064A, 0x0645, 0x064A, 0x0647, 0x064A, 0x0628, 0x062C, 0x0628, 0x062D, 0x0628, 0x062E, 0x0628, 0x0645 };
+unsigned short unac_data152[] = { 0x0628, 0x0647, 0x062A, 0x062C, 0x062A, 0x062D, 0x062A, 0x062E, 0x062A, 0x0645, 0x062A, 0x0647, 0x062B, 0x0645, 0x062C, 0x062D, 0x062C, 0x0645, 0x062D, 0x062C, 0x062D, 0x0645, 0x062E, 0x062C, 0x062E, 0x0645, 0x0633, 0x062C, 0x0633, 0x062D, 0x0633, 0x062E, 0x0633, 0x0645, 0x0635, 0x062D, 0x0635, 0x062E, 0x0635, 0x0645, 0x0636, 0x062C, 0x0636, 0x062D, 0x0636, 0x062E, 0x0636, 0x0645, 0x0637, 0x062D, 0x0638, 0x0645, 0x0639, 0x062C, 0x0639, 0x0645, 0x063A, 0x062C, 0x063A, 0x0645, 0x0641, 0x062C, 0x0641, 0x062D };
+unsigned short unac_data153[] = { 0x0641, 0x062E, 0x0641, 0x0645, 0x0642, 0x062D, 0x0642, 0x0645, 0x0643, 0x062C, 0x0643, 0x062D, 0x0643, 0x062E, 0x0643, 0x0644, 0x0643, 0x0645, 0x0644, 0x062C, 0x0644, 0x062D, 0x0644, 0x062E, 0x0644, 0x0645, 0x0644, 0x0647, 0x0645, 0x062C, 0x0645, 0x062D, 0x0645, 0x062E, 0x0645, 0x0645, 0x0646, 0x062C, 0x0646, 0x062D, 0x0646, 0x062E, 0x0646, 0x0645, 0x0646, 0x0647, 0x0647, 0x062C, 0x0647, 0x0645, 0x0647, 0x064A, 0x062C, 0x064A, 0x062D, 0x064A, 0x062E, 0x064A, 0x0645, 0x064A, 0x0647, 0x0645, 0x064A };
+unsigned short unac_data154[] = { 0x0647, 0x064A, 0x0628, 0x0645, 0x0628, 0x0647, 0x062A, 0x0645, 0x062A, 0x0647, 0x062B, 0x0645, 0x062B, 0x0647, 0x0633, 0x0645, 0x0633, 0x0647, 0x0634, 0x0645, 0x0634, 0x0647, 0x0643, 0x0644, 0x0643, 0x0645, 0x0644, 0x0645, 0x0646, 0x0645, 0x0646, 0x0647, 0x064A, 0x0645, 0x064A, 0x0647, 0x0640, 0x0640, 0x0640, 0x0637, 0x0649, 0x0637, 0x064A, 0x0639, 0x0649, 0x0639, 0x064A, 0x063A, 0x0649, 0x063A, 0x064A, 0x0633, 0x0649, 0x0633, 0x064A, 0x0634, 0x0649, 0x0634, 0x064A, 0x062D, 0x0649 };
+unsigned short unac_data155[] = { 0x062D, 0x064A, 0x062C, 0x0649, 0x062C, 0x064A, 0x062E, 0x0649, 0x062E, 0x064A, 0x0635, 0x0649, 0x0635, 0x064A, 0x0636, 0x0649, 0x0636, 0x064A, 0x0634, 0x062C, 0x0634, 0x062D, 0x0634, 0x062E, 0x0634, 0x0645, 0x0634, 0x0631, 0x0633, 0x0631, 0x0635, 0x0631, 0x0636, 0x0631, 0x0637, 0x0649, 0x0637, 0x064A, 0x0639, 0x0649, 0x0639, 0x064A, 0x063A, 0x0649, 0x063A, 0x064A, 0x0633, 0x0649, 0x0633, 0x064A, 0x0634, 0x0649, 0x0634, 0x064A, 0x062D, 0x0649, 0x062D, 0x064A, 0x062C, 0x0649, 0x062C, 0x064A, 0x062E, 0x0649 };
+unsigned short unac_data156[] = { 0x062E, 0x064A, 0x0635, 0x0649, 0x0635, 0x064A, 0x0636, 0x0649, 0x0636, 0x064A, 0x0634, 0x062C, 0x0634, 0x062D, 0x0634, 0x062E, 0x0634, 0x0645, 0x0634, 0x0631, 0x0633, 0x0631, 0x0635, 0x0631, 0x0636, 0x0631, 0x0634, 0x062C, 0x0634, 0x062D, 0x0634, 0x062E, 0x0634, 0x0645, 0x0633, 0x0647, 0x0634, 0x0647, 0x0637, 0x0645, 0x0633, 0x062C, 0x0633, 0x062D, 0x0633, 0x062E, 0x0634, 0x062C, 0x0634, 0x062D, 0x0634, 0x062E, 0x0637, 0x0645, 0x0638, 0x0645, 0x0627, 0x0627, 0xFFFF, 0xFFFF };
+unsigned short unac_data157[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x062A, 0x062C, 0x0645, 0x062A, 0x062D, 0x062C, 0x062A, 0x062D, 0x062C, 0x062A, 0x062D, 0x0645, 0x062A, 0x062E, 0x0645, 0x062A, 0x0645, 0x062C, 0x062A, 0x0645, 0x062D, 0x062A, 0x0645, 0x062E, 0x062C, 0x0645, 0x062D, 0x062C, 0x0645, 0x062D, 0x062D, 0x0645, 0x064A, 0x062D, 0x0645, 0x0649, 0x0633, 0x062D, 0x062C, 0x0633, 0x062C, 0x062D, 0x0633, 0x062C, 0x0649, 0x0633, 0x0645, 0x062D };
+unsigned short unac_data158[] = { 0x0633, 0x0645, 0x062D, 0x0633, 0x0645, 0x062C, 0x0633, 0x0645, 0x0645, 0x0633, 0x0645, 0x0645, 0x0635, 0x062D, 0x062D, 0x0635, 0x062D, 0x062D, 0x0635, 0x0645, 0x0645, 0x0634, 0x062D, 0x0645, 0x0634, 0x062D, 0x0645, 0x0634, 0x062C, 0x064A, 0x0634, 0x0645, 0x062E, 0x0634, 0x0645, 0x062E, 0x0634, 0x0645, 0x0645, 0x0634, 0x0645, 0x0645, 0x0636, 0x062D, 0x0649, 0x0636, 0x062E, 0x0645, 0x0636, 0x062E, 0x0645, 0x0637, 0x0645, 0x062D, 0x0637, 0x0645, 0x062D, 0x0637, 0x0645, 0x0645, 0x0637, 0x0645, 0x064A, 0x0639, 0x062C, 0x0645, 0x0639, 0x0645, 0x0645, 0x0639, 0x0645, 0x0645, 0x0639, 0x0645, 0x0649, 0x063A, 0x0645, 0x0645, 0x063A, 0x0645, 0x064A, 0x063A, 0x0645, 0x0649, 0x0641, 0x062E, 0x0645, 0x0641, 0x062E, 0x0645, 0x0642, 0x0645, 0x062D, 0x0642, 0x0645, 0x0645 };
+unsigned short unac_data159[] = { 0x0644, 0x062D, 0x0645, 0x0644, 0x062D, 0x064A, 0x0644, 0x062D, 0x0649, 0x0644, 0x062C, 0x062C, 0x0644, 0x062C, 0x062C, 0x0644, 0x062E, 0x0645, 0x0644, 0x062E, 0x0645, 0x0644, 0x0645, 0x062D, 0x0644, 0x0645, 0x062D, 0x0645, 0x062D, 0x062C, 0x0645, 0x062D, 0x0645, 0x0645, 0x062D, 0x064A, 0x0645, 0x062C, 0x062D, 0x0645, 0x062C, 0x0645, 0x0645, 0x062E, 0x062C, 0x0645, 0x062E, 0x0645, 0xFFFF, 0xFFFF, 0x0645, 0x062C, 0x062E, 0x0647, 0x0645, 0x062C, 0x0647, 0x0645, 0x0645, 0x0646, 0x062D, 0x0645, 0x0646, 0x062D, 0x0649, 0x0646, 0x062C, 0x0645, 0x0646, 0x062C, 0x0645, 0x0646, 0x062C, 0x0649, 0x0646, 0x0645, 0x064A, 0x0646, 0x0645, 0x0649, 0x064A, 0x0645, 0x0645, 0x064A, 0x0645, 0x0645, 0x0628, 0x062E, 0x064A, 0x062A, 0x062C, 0x064A };
+unsigned short unac_data160[] = { 0x062A, 0x062C, 0x0649, 0x062A, 0x062E, 0x064A, 0x062A, 0x062E, 0x0649, 0x062A, 0x0645, 0x064A, 0x062A, 0x0645, 0x0649, 0x062C, 0x0645, 0x064A, 0x062C, 0x062D, 0x0649, 0x062C, 0x0645, 0x0649, 0x0633, 0x062E, 0x0649, 0x0635, 0x062D, 0x064A, 0x0634, 0x062D, 0x064A, 0x0636, 0x062D, 0x064A, 0x0644, 0x062C, 0x064A, 0x0644, 0x0645, 0x064A, 0x064A, 0x062D, 0x064A, 0x064A, 0x062C, 0x064A, 0x064A, 0x0645, 0x064A, 0x0645, 0x0645, 0x064A, 0x0642, 0x0645, 0x064A, 0x0646, 0x062D, 0x064A, 0x0642, 0x0645, 0x062D, 0x0644, 0x062D, 0x0645, 0x0639, 0x0645, 0x064A, 0x0643, 0x0645, 0x064A, 0x0646, 0x062C, 0x062D, 0x0645, 0x062E, 0x064A, 0x0644, 0x062C, 0x0645, 0x0643, 0x0645, 0x0645, 0x0644, 0x062C, 0x0645, 0x0646, 0x062C, 0x062D, 0x062C, 0x062D, 0x064A, 0x062D, 0x062C, 0x064A };
+unsigned short unac_data161[] = { 0x0645, 0x062C, 0x064A, 0x0641, 0x0645, 0x064A, 0x0628, 0x062D, 0x064A, 0x0643, 0x0645, 0x0645, 0x0639, 0x062C, 0x0645, 0x0635, 0x0645, 0x0645, 0x0633, 0x062E, 0x064A, 0x0646, 0x062C, 0x064A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data162[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0635, 0x0644, 0x06D2, 0x0642, 0x0644, 0x06D2, 0x0627, 0x0644, 0x0644, 0x0647, 0x0627, 0x0643, 0x0628, 0x0631, 0x0645, 0x062D, 0x0645, 0x062F, 0x0635, 0x0644, 0x0639, 0x0645, 0x0631, 0x0633, 0x0648, 0x0644, 0x0639, 0x0644, 0x064A, 0x0647, 0x0648, 0x0633, 0x0644, 0x0645, 0x0635, 0x0644, 0x0649, 0x0635, 0x0644, 0x0649, 0x0020, 0x0627, 0x0644, 0x0644, 0x0647, 0x0020, 0x0639, 0x0644, 0x064A, 0x0647, 0x0020, 0x0648, 0x0633, 0x0644, 0x0645, 0x062C, 0x0644, 0x0020, 0x062C, 0x0644, 0x0627, 0x0644, 0x0647, 0x0631, 0x06CC, 0x0627, 0x0644, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data163[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x002E, 0x002E, 0x2014, 0x2013, 0x005F, 0x005F, 0x0028, 0x0029, 0x007B, 0x007D, 0x3014, 0x3015, 0x3010, 0x3011, 0x300A, 0x300B, 0x3008 };
+unsigned short unac_data164[] = { 0x3009, 0x300C, 0x300D, 0x300E, 0x300F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0020, 0x0020, 0x0020, 0x005F, 0x005F, 0x005F, 0x002C, 0x3001, 0x002E, 0xFFFF, 0x003B, 0x003A, 0x003F, 0x0021, 0x2014, 0x0028, 0x0029, 0x007B, 0x007D, 0x3014, 0x3015, 0x0023 };
+unsigned short unac_data165[] = { 0x0026, 0x002A, 0x002B, 0x002D, 0x003C, 0x003E, 0x003D, 0xFFFF, 0x005C, 0x0024, 0x0025, 0x0040, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0020, 0x0640, 0x0020, 0xFFFF, 0x0020, 0xFFFF, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640, 0x0020, 0x0640 };
+unsigned short unac_data166[] = { 0x0621, 0x0627, 0x0627, 0x0627, 0x0627, 0x0648, 0x0648, 0x0627, 0x0627, 0x064A, 0x064A, 0x064A, 0x064A, 0x0627, 0x0627, 0x0628, 0x0628, 0x0628, 0x0628, 0x0629, 0x0629, 0x062A, 0x062A, 0x062A, 0x062A, 0x062B, 0x062B, 0x062B, 0x062B, 0x062C, 0x062C, 0x062C };
+unsigned short unac_data167[] = { 0x062C, 0x062D, 0x062D, 0x062D, 0x062D, 0x062E, 0x062E, 0x062E, 0x062E, 0x062F, 0x062F, 0x0630, 0x0630, 0x0631, 0x0631, 0x0632, 0x0632, 0x0633, 0x0633, 0x0633, 0x0633, 0x0634, 0x0634, 0x0634, 0x0634, 0x0635, 0x0635, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636 };
+unsigned short unac_data168[] = { 0x0636, 0x0637, 0x0637, 0x0637, 0x0637, 0x0638, 0x0638, 0x0638, 0x0638, 0x0639, 0x0639, 0x0639, 0x0639, 0x063A, 0x063A, 0x063A, 0x063A, 0x0641, 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, 0x0642, 0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644 };
+unsigned short unac_data169[] = { 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646, 0x0646, 0x0647, 0x0647, 0x0647, 0x0647, 0x0648, 0x0648, 0x0649, 0x0649, 0x064A, 0x064A, 0x064A, 0x064A, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0x0644, 0x0627, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data170[] = { 0xFFFF, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F };
+unsigned short unac_data171[] = { 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F };
+unsigned short unac_data172[] = { 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x2985 };
+unsigned short unac_data173[] = { 0x2986, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD };
+unsigned short unac_data174[] = { 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0xFFFF, 0xFFFF };
+unsigned short unac_data175[] = { 0x1160, 0x1100, 0x1101, 0x11AA, 0x1102, 0x11AC, 0x11AD, 0x1103, 0x1104, 0x1105, 0x11B0, 0x11B1, 0x11B2, 0x11B3, 0x11B4, 0x11B5, 0x111A, 0x1106, 0x1107, 0x1108, 0x1121, 0x1109, 0x110A, 0x110B, 0x110C, 0x110D, 0x110E, 0x110F, 0x1110, 0x1111, 0x1112, 0xFFFF };
+unsigned short unac_data176[] = { 0xFFFF, 0xFFFF, 0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0xFFFF, 0xFFFF, 0x1167, 0x1168, 0x1169, 0x116A, 0x116B, 0x116C, 0xFFFF, 0xFFFF, 0x116D, 0x116E, 0x116F, 0x1170, 0x1171, 0x1172, 0xFFFF, 0xFFFF, 0x1173, 0x1174, 0x1175, 0xFFFF, 0xFFFF, 0xFFFF };
+unsigned short unac_data177[] = { 0x00A2, 0x00A3, 0x00AC, 0x0020, 0x00A6, 0x00A5, 0x20A9, 0xFFFF, 0x2502, 0x2190, 0x2191, 0x2192, 0x2193, 0x25A0, 0x25CB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
+
+unsigned short* unac_data_table[UNAC_BLOCK_COUNT] = {
+unac_data0,
+unac_data1,
+unac_data2,
+unac_data3,
+unac_data4,
+unac_data5,
+unac_data6,
+unac_data7,
+unac_data8,
+unac_data9,
+unac_data10,
+unac_data11,
+unac_data12,
+unac_data13,
+unac_data14,
+unac_data15,
+unac_data16,
+unac_data17,
+unac_data18,
+unac_data19,
+unac_data20,
+unac_data21,
+unac_data22,
+unac_data23,
+unac_data24,
+unac_data25,
+unac_data26,
+unac_data27,
+unac_data28,
+unac_data29,
+unac_data30,
+unac_data31,
+unac_data32,
+unac_data33,
+unac_data34,
+unac_data35,
+unac_data36,
+unac_data37,
+unac_data38,
+unac_data39,
+unac_data40,
+unac_data41,
+unac_data42,
+unac_data43,
+unac_data44,
+unac_data45,
+unac_data46,
+unac_data47,
+unac_data48,
+unac_data49,
+unac_data50,
+unac_data51,
+unac_data52,
+unac_data53,
+unac_data54,
+unac_data55,
+unac_data56,
+unac_data57,
+unac_data58,
+unac_data59,
+unac_data60,
+unac_data61,
+unac_data62,
+unac_data63,
+unac_data64,
+unac_data65,
+unac_data66,
+unac_data67,
+unac_data68,
+unac_data69,
+unac_data70,
+unac_data71,
+unac_data72,
+unac_data73,
+unac_data74,
+unac_data75,
+unac_data76,
+unac_data77,
+unac_data78,
+unac_data79,
+unac_data80,
+unac_data81,
+unac_data82,
+unac_data83,
+unac_data84,
+unac_data85,
+unac_data86,
+unac_data87,
+unac_data88,
+unac_data89,
+unac_data90,
+unac_data91,
+unac_data92,
+unac_data93,
+unac_data94,
+unac_data95,
+unac_data96,
+unac_data97,
+unac_data98,
+unac_data99,
+unac_data100,
+unac_data101,
+unac_data102,
+unac_data103,
+unac_data104,
+unac_data105,
+unac_data106,
+unac_data107,
+unac_data108,
+unac_data109,
+unac_data110,
+unac_data111,
+unac_data112,
+unac_data113,
+unac_data114,
+unac_data115,
+unac_data116,
+unac_data117,
+unac_data118,
+unac_data119,
+unac_data120,
+unac_data121,
+unac_data122,
+unac_data123,
+unac_data124,
+unac_data125,
+unac_data126,
+unac_data127,
+unac_data128,
+unac_data129,
+unac_data130,
+unac_data131,
+unac_data132,
+unac_data133,
+unac_data134,
+unac_data135,
+unac_data136,
+unac_data137,
+unac_data138,
+unac_data139,
+unac_data140,
+unac_data141,
+unac_data142,
+unac_data143,
+unac_data144,
+unac_data145,
+unac_data146,
+unac_data147,
+unac_data148,
+unac_data149,
+unac_data150,
+unac_data151,
+unac_data152,
+unac_data153,
+unac_data154,
+unac_data155,
+unac_data156,
+unac_data157,
+unac_data158,
+unac_data159,
+unac_data160,
+unac_data161,
+unac_data162,
+unac_data163,
+unac_data164,
+unac_data165,
+unac_data166,
+unac_data167,
+unac_data168,
+unac_data169,
+unac_data170,
+unac_data171,
+unac_data172,
+unac_data173,
+unac_data174,
+unac_data175,
+unac_data176,
+unac_data177
+};
+/* Generated by builder. Do not modify. End tables */
+
+/*
+ * Debug level. See unac.h for a detailed discussion of the
+ * values.
+ */
+static int debug_level = UNAC_DEBUG_NONE;
+
+#ifdef UNAC_DEBUG_AVAILABLE
+
+/*
+ * Default debug function, printing on stderr.
+ */
+static void debug_doprint_default(const char* message, void* data)
+{
+  fprintf(stderr, "%s", message);
+}
+
+/*
+ * Default doprint handler is debug_doprint.
+ */
+static unac_debug_print_t debug_doprint = debug_doprint_default;
+/*
+ * Default app data is null.
+ */
+static void* debug_appdata = (void*)0;
+
+/*
+ * Generate a debug message (arguments ala printf) and
+ * send it to the doprint handler.
+ */
+#define DEBUG debug_print("%s:%d: ", __FILE__, __LINE__), debug_print
+#define DEBUG_APPEND debug_print
+static void debug_print(const char* message, ...)
+{
+#define UNAC_MAXIMUM_MESSAGE_SIZE 512
+  /*
+   * UNAC_MAXIMUM_MESSAGE_SIZE is supposedly enough but I
+   * do trust some vsnprintf implementations to be bugous.
+   */
+  char unac_message_buffer[UNAC_MAXIMUM_MESSAGE_SIZE+1] = { '\0' };
+  va_list args;
+  va_start(args, message);
+  if(vsnprintf(unac_message_buffer, UNAC_MAXIMUM_MESSAGE_SIZE, message, args) < 0) {
+    char tmp[UNAC_MAXIMUM_MESSAGE_SIZE];
+    sprintf(tmp, "[message larger than %d, truncated]", UNAC_MAXIMUM_MESSAGE_SIZE);
+    debug_doprint(tmp, debug_appdata);
+  }
+  va_end(args);
+  unac_message_buffer[UNAC_MAXIMUM_MESSAGE_SIZE] = '\0';
+
+  debug_doprint(unac_message_buffer, debug_appdata);
+}
+
+void unac_debug_callback(int level, unac_debug_print_t function, void* data)
+{
+  debug_level = level;
+  if(function)
+    debug_doprint = function;
+  debug_appdata = data;
+}
+
+#else /* UNAC_DEBUG_AVAILABLE */
+#define DEBUG 
+#define DEBUG_APPEND
+#endif /* UNAC_DEBUG_AVAILABLE */
+
+
+/*
+ * If UTF-16BE exists, use it. If not, use UTF-16 and hope it is
+ * encoded in big endian. This fallback is a iconv related
+ * compatibility hack introduced in some GNU/Linux distributions that
+ * did not know UTF-16BE.
+ */
+static const char* utf16be()
+{
+  iconv_t cd;
+  static char* name = 0;
+
+  if(name == 0) {
+    if((cd = iconv_open("UTF-16BE", "UTF-16BE")) == (iconv_t)-1) {
+      if(debug_level == UNAC_DEBUG_LOW) DEBUG("could not find UTF-16BE (see iconv -l), using UTF-16. If UTF-16 happens to be encoded in little endian, be prepared for an horrible mess.");
+      name = "UTF-16";
+    } else {
+      iconv_close(cd);
+      name = "UTF-16BE";
+    }
+  }
+
+  return name;
+}
+
+int unac_string_utf16(const char* in, size_t in_length,
+		      char** outp, size_t* out_lengthp)
+{
+  char* out;
+  int out_size;
+  int out_length;
+  int i;
+
+  out_size = in_length > 0 ? in_length : 1024;
+  if(*outp) {
+    out = *outp;
+    /* +1 for null */
+    out = realloc(out, out_size + 1);
+  } else {
+    /* +1 for null */
+    out = malloc(out_size + 1);
+    if(out == 0) return -1;
+  }
+  out_length = 0;
+
+  for(i = 0; i < in_length; i += 2) {
+    unsigned short c;
+    unsigned short* p;
+    int l;
+    int k;
+    c = (in[i] << 8) | (in[i + 1] & 0xff);
+    /*
+     * Lookup the tables for decomposition information
+     */
+    unac_char_utf16(c, p, l);
+    /*
+     * Explain what's done in great detail
+     */
+    if(debug_level == UNAC_DEBUG_HIGH) {
+      unsigned short index = unac_indexes[(c) >> UNAC_BLOCK_SHIFT];
+      unsigned char position = (c) & UNAC_BLOCK_MASK;
+      DEBUG("unac_data%d[%d] & unac_positions[%d][%d]: ", index, unac_positions[index][position], index, position+1);
+      DEBUG_APPEND("0x%04x => ", (c));
+      if(l == 0) {
+	DEBUG_APPEND("untouched\n");
+      } else {
+	int i;
+	for(i = 0; i < l; i++)
+	  DEBUG_APPEND("0x%04x ", p[i]);
+	DEBUG_APPEND("\n");
+      }
+    }
+    /*
+     * Make sure there is enough space to hold the decomposition
+     */
+    if(out_length + ((l + 1) * 2) > out_size) {
+      out_size += ((l + 1) * 2) + 1024;
+      out = realloc(out, out_size);
+      if(out == 0) {
+	if(debug_level == UNAC_DEBUG_LOW)
+	  DEBUG("realloc %d bytes failed\n", out_size);
+	return -1;
+      }
+    }
+    if(l > 0) {
+      /*
+       * If there is a decomposition, insert it in the output 
+       * string.
+       */
+      for(k = 0; k < l; k++) {
+	out[out_length++] = (p[k] >> 8) & 0xff;
+	out[out_length++] = (p[k] & 0xff);
+      }
+    } else {
+      /*
+       * If there is no decomposition leave it unchanged
+       */
+      out[out_length++] = in[i];
+      out[out_length++] = in[i + 1];
+    }
+  }
+
+  *outp = out;
+  *out_lengthp = out_length;
+  (*outp)[*out_lengthp] = '\0';
+
+  return 0;
+}
+
+#define MAXOUT 1024
+
+static int convert(const char* from, const char* to,
+		   const char* in, size_t in_length,
+		   char** outp, size_t* out_lengthp);
+
+/*
+ * Convert buffer <in> containing string encoded in charset <from> into
+ * a string in charset <to> and return it in buffer <outp>. The <outp>
+ * points to a malloced string large enough to hold the conversion result.
+ * It is the responsibility of the caller to free this array.
+ * The out string is always null terminated.
+ */
+static int convert(const char* from, const char* to,
+		   const char* in, size_t in_length,
+		   char** outp, size_t* out_lengthp)
+{
+  iconv_t cd;
+  char* out;
+  size_t out_remain;
+  size_t out_size;
+  char* out_base;
+  int from_utf16 = !strcmp(utf16be(), from);
+  const char space[] = { 0x00, 0x20 };
+
+  out_size = in_length > 0 ? (in_length * 2) : 1024;
+  if(*outp) {
+    out = *outp;
+    /* +1 for null */
+    out = realloc(out, out_size + 1);
+  } else {
+    /* +1 for null */
+    out = malloc(out_size + 1);
+    if(out == 0) return -1;
+  }
+  out_remain = out_size;
+  out_base = out;
+
+  if((cd = iconv_open(to, from)) == (iconv_t)-1) {
+    return -1;
+  }
+  do {
+    if(iconv(cd, (ICONV_CONST char **) &in, &in_length, &out, &out_remain) == (size_t)-1) {
+      switch(errno) {
+      case EILSEQ:
+	/*
+	 * If an illegal sequence is found in the context of unac_string
+	 * it means the unaccented version of a character contains
+	 * a sequence that cannot be mapped back to the original charset.
+	 * For instance, the 1/4 character in ISO-8859-1 is decomposed
+	 * in three characters including the FRACTION SLASH (2044) which
+	 * have no equivalent in the ISO-8859-1 map. One can argue that
+	 * the conversions tables should map it to the regular / character
+	 * or that a <compat> entry should be associated with it. 
+	 *
+	 * To cope with this situation, convert silently transform all
+	 * illegal sequences (EILSEQ) into a SPACE character 0x0020.
+	 *
+	 * In the general conversion case this behaviour is not desirable.
+	 * However, it is not the responsibility of this program to cope
+	 * with inconsistencies of the Unicode description and a bug report
+	 * should be submited to Unicode so that they can fix the problem.
+	 * 
+	 */
+	if(from_utf16) {
+	  const char* tmp = space;
+	  size_t tmp_length = 2;
+	  if(iconv(cd, (ICONV_CONST char **) &tmp, &tmp_length, &out, &out_remain) == (size_t)-1) {
+	    if(errno == E2BIG)
+	      /* fall thru to the E2BIG case below */;
+	    else
+	      return -1;
+	  } else {
+	    /* The offending character was replaced by a SPACE, skip it. */
+	    in += 2;
+	    in_length -= 2;
+	    /* And continue conversion. */
+	    break;
+	  }
+	} else {
+	  return -1;
+	}
+      case E2BIG:
+	{
+	  /*
+	 * The output does not fit in the current out buffer, enlarge it.
+	 */
+	  int length = out - out_base;
+	  out_size *= 2;
+	  /* +1 for null */
+	  out_base = realloc(out_base, out_size + 1);
+	  if(out_base == 0) return -1;
+	  out = out_base + length;
+	  out_remain = out_size - length;
+	}
+	break;
+      default:
+	return -1;
+	break;
+      }
+    }
+  } while(in_length > 0);
+  iconv_close(cd);
+
+  *outp = out_base;
+  *out_lengthp = out - out_base;
+  (*outp)[*out_lengthp] = '\0';
+
+  return 0;
+}
+
+int unac_string(const char* charset,
+		const char* in, size_t in_length,
+		char** outp, size_t* out_lengthp)
+{
+  /*
+   * When converting an empty string, skip everything but alloc the
+   * buffer if NULL pointer.
+   */
+  if(in_length <= 0) {
+    if(!*outp)
+      *outp = malloc(32);
+    (*outp)[0] = '\0';
+    *out_lengthp = 0;
+  } else {
+    char* utf16 = 0;
+    int utf16_length = 0;
+    char* utf16_unaccented = 0;
+    int utf16_unaccented_length = 0;
+  
+    if(convert(charset, utf16be(), in, in_length, &utf16, &utf16_length) < 0) {
+      return -1;
+    }
+
+    unac_string_utf16(utf16, utf16_length, &utf16_unaccented, &utf16_unaccented_length);
+    free(utf16);
+
+    if(convert(utf16be(), charset, utf16_unaccented, utf16_unaccented_length, outp, out_lengthp) < 0) {
+      return -1;
+    }
+    free(utf16_unaccented);
+  }
+
+  return 0;
+}
+
+
+const char* unac_version(void)
+{
+  return UNAC_VERSION;
+}
+
Index: /gsdl/trunk/gs2common/indexers/packages/unac/unac.h
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/unac.h	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/unac.h	(revision 16561)
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Loic Dachary <loic@senga.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Provides functions to strip accents from a string in all the
+ * charset supported by iconv(3).
+ *
+ * See the unac(3) manual page for more information.
+ *
+ */
+
+#ifndef _unac_h
+#define _unac_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Generated by builder. Do not modify. Start defines */
+#define UNAC_BLOCK_SHIFT 5
+#define UNAC_BLOCK_MASK ((1 << UNAC_BLOCK_SHIFT) - 1)
+#define UNAC_BLOCK_SIZE (1 << UNAC_BLOCK_SHIFT)
+#define UNAC_BLOCK_COUNT 178
+#define UNAC_INDEXES_SIZE (0x10000 >> UNAC_BLOCK_SHIFT)
+/* Generated by builder. Do not modify. End defines */
+
+/*
+ * Return the unaccented equivalent of the UTF-16 character <c>
+ * in the pointer <p>. The length of the unsigned short array pointed
+ * by <p> is returned in the <l> argument.
+ * The C++ prototype of this macro would be:
+ *
+ * void unac_char(const unsigned short c, unsigned short*& p, int& l)
+ *
+ * See unac(3) in IMPLEMENTATION NOTES for more information about the
+ * tables (unac_data_table, unac_positions) layout.
+ */ 
+#define unac_char_utf16(c,p,l) \
+  { \
+    unsigned short index = unac_indexes[(c) >> UNAC_BLOCK_SHIFT]; \
+    unsigned char position = (c) & UNAC_BLOCK_MASK; \
+    (p) = &(unac_data_table[index][unac_positions[index][position]]); \
+    (l) = unac_positions[index][position + 1] - unac_positions[index][position]; \
+    if((l) == 1 && *(p) == 0xFFFF) { \
+      (p) = 0; \
+      (l) = 0; \
+    } \
+  }
+
+/*
+ * Return the unaccented equivalent of the UTF-16 string <in> of
+ * length <in_length> in the pointer <out>. The length of the UTF-16
+ * string returned in <out> is stored in <out_length>. If the pointer
+ * *out is null, a new string is allocated using malloc(3). If the
+ * pointer *out is not null, the available length must also be given
+ * in the *out_length argument.  The pointer passed to *out must have
+ * been allocated by malloc(3) and may be reallocated by realloc(3) if
+ * needs be. It is the responsibility of the caller to free the
+ * pointer returned in *out.  The return value is 0 on success and -1
+ * on error, in which case the errno variable is set to the
+ * corresponding error code.
+ */
+int unac_string_utf16(const char* in, size_t in_length,
+		char** out, size_t* out_length);
+
+/*
+ * The semantic of this function is stricly equal to the function
+ * unac_string_utf16. The <charset> argument applies to the content of the
+ * input string. It is converted to UTF-16 using iconv(3) before calling
+ * the unac_string function and the result is converted from UTF-16 to
+ * the specified <charset> before returning it in the <out> pointer.
+ * For efficiency purpose it is recommended that the caller uses
+ * unac_string and iconv(3) to save buffer allocations overhead. 
+ * The return value is 0 on success and -1 on error, in which case
+ * the errno variable is set to the corresponding error code.
+ */
+int unac_string(const char* charset,
+		const char* in, size_t in_length,
+		char** out, size_t* out_length);
+
+/*
+ * Return unac version number.
+ */
+const char* unac_version(void);
+
+#define UNAC_DEBUG_NONE	0x00
+#define UNAC_DEBUG_LOW	0x01
+#define UNAC_DEBUG_HIGH 0x02
+
+#ifdef HAVE_VSNPRINTF
+#define UNAC_DEBUG_AVAILABLE 1
+/*
+ * Set the unac debug level. <l> is one of:
+ * UNAC_DEBUG_NONE for no debug messages at all
+ * UNAC_DEBUG_LOW for minimal information
+ * UNAC_DEBUG_HIGH for extremely verbose information,
+ *                 only usable when translating a few short strings.
+ *
+ * unac_debug with anything but UNAC_DEBUG_NONE is not
+ * thread safe.
+ */
+#define unac_debug(l) unac_debug_callback((l), 0, (void*)0);
+
+/*
+ * Set the debug level and define a printing function callback.
+ * The <level> debug level is the same as in unac_debug. The
+ * <function> is in charge of dealing with the debug messages,
+ * presumably to print them to the user. The <data> is an opaque
+ * pointer that is passed along to <function>, should it
+ * need to manage a persistent context.
+ *
+ * The prototype of <function> allows two arguments. The first
+ * is the debug message (const char*), the second is the opaque
+ * pointer given as <data> argument to unac_debug_callback.
+ *
+ * If <function> is NULL, messages are printed on the standard
+ * error output using fprintf(stderr...).
+ * 
+ * unac_debug_callback with anything but UNAC_DEBUG_NONE is not
+ * thread safe.
+ *
+ */
+typedef void (*unac_debug_print_t)(const char* message, void* data);
+void unac_debug_callback(int level, unac_debug_print_t function, void* data);
+#endif /* HAVE_VSNPRINTF */
+
+/* Generated by builder. Do not modify. Start declarations */
+extern unsigned short unac_indexes[UNAC_INDEXES_SIZE];
+extern unsigned char unac_positions[UNAC_BLOCK_COUNT][UNAC_BLOCK_SIZE + 1];
+extern unsigned short* unac_data_table[UNAC_BLOCK_COUNT];
+extern unsigned short unac_data0[];
+extern unsigned short unac_data1[];
+extern unsigned short unac_data2[];
+extern unsigned short unac_data3[];
+extern unsigned short unac_data4[];
+extern unsigned short unac_data5[];
+extern unsigned short unac_data6[];
+extern unsigned short unac_data7[];
+extern unsigned short unac_data8[];
+extern unsigned short unac_data9[];
+extern unsigned short unac_data10[];
+extern unsigned short unac_data11[];
+extern unsigned short unac_data12[];
+extern unsigned short unac_data13[];
+extern unsigned short unac_data14[];
+extern unsigned short unac_data15[];
+extern unsigned short unac_data16[];
+extern unsigned short unac_data17[];
+extern unsigned short unac_data18[];
+extern unsigned short unac_data19[];
+extern unsigned short unac_data20[];
+extern unsigned short unac_data21[];
+extern unsigned short unac_data22[];
+extern unsigned short unac_data23[];
+extern unsigned short unac_data24[];
+extern unsigned short unac_data25[];
+extern unsigned short unac_data26[];
+extern unsigned short unac_data27[];
+extern unsigned short unac_data28[];
+extern unsigned short unac_data29[];
+extern unsigned short unac_data30[];
+extern unsigned short unac_data31[];
+extern unsigned short unac_data32[];
+extern unsigned short unac_data33[];
+extern unsigned short unac_data34[];
+extern unsigned short unac_data35[];
+extern unsigned short unac_data36[];
+extern unsigned short unac_data37[];
+extern unsigned short unac_data38[];
+extern unsigned short unac_data39[];
+extern unsigned short unac_data40[];
+extern unsigned short unac_data41[];
+extern unsigned short unac_data42[];
+extern unsigned short unac_data43[];
+extern unsigned short unac_data44[];
+extern unsigned short unac_data45[];
+extern unsigned short unac_data46[];
+extern unsigned short unac_data47[];
+extern unsigned short unac_data48[];
+extern unsigned short unac_data49[];
+extern unsigned short unac_data50[];
+extern unsigned short unac_data51[];
+extern unsigned short unac_data52[];
+extern unsigned short unac_data53[];
+extern unsigned short unac_data54[];
+extern unsigned short unac_data55[];
+extern unsigned short unac_data56[];
+extern unsigned short unac_data57[];
+extern unsigned short unac_data58[];
+extern unsigned short unac_data59[];
+extern unsigned short unac_data60[];
+extern unsigned short unac_data61[];
+extern unsigned short unac_data62[];
+extern unsigned short unac_data63[];
+extern unsigned short unac_data64[];
+extern unsigned short unac_data65[];
+extern unsigned short unac_data66[];
+extern unsigned short unac_data67[];
+extern unsigned short unac_data68[];
+extern unsigned short unac_data69[];
+extern unsigned short unac_data70[];
+extern unsigned short unac_data71[];
+extern unsigned short unac_data72[];
+extern unsigned short unac_data73[];
+extern unsigned short unac_data74[];
+extern unsigned short unac_data75[];
+extern unsigned short unac_data76[];
+extern unsigned short unac_data77[];
+extern unsigned short unac_data78[];
+extern unsigned short unac_data79[];
+extern unsigned short unac_data80[];
+extern unsigned short unac_data81[];
+extern unsigned short unac_data82[];
+extern unsigned short unac_data83[];
+extern unsigned short unac_data84[];
+extern unsigned short unac_data85[];
+extern unsigned short unac_data86[];
+extern unsigned short unac_data87[];
+extern unsigned short unac_data88[];
+extern unsigned short unac_data89[];
+extern unsigned short unac_data90[];
+extern unsigned short unac_data91[];
+extern unsigned short unac_data92[];
+extern unsigned short unac_data93[];
+extern unsigned short unac_data94[];
+extern unsigned short unac_data95[];
+extern unsigned short unac_data96[];
+extern unsigned short unac_data97[];
+extern unsigned short unac_data98[];
+extern unsigned short unac_data99[];
+extern unsigned short unac_data100[];
+extern unsigned short unac_data101[];
+extern unsigned short unac_data102[];
+extern unsigned short unac_data103[];
+extern unsigned short unac_data104[];
+extern unsigned short unac_data105[];
+extern unsigned short unac_data106[];
+extern unsigned short unac_data107[];
+extern unsigned short unac_data108[];
+extern unsigned short unac_data109[];
+extern unsigned short unac_data110[];
+extern unsigned short unac_data111[];
+extern unsigned short unac_data112[];
+extern unsigned short unac_data113[];
+extern unsigned short unac_data114[];
+extern unsigned short unac_data115[];
+extern unsigned short unac_data116[];
+extern unsigned short unac_data117[];
+extern unsigned short unac_data118[];
+extern unsigned short unac_data119[];
+extern unsigned short unac_data120[];
+extern unsigned short unac_data121[];
+extern unsigned short unac_data122[];
+extern unsigned short unac_data123[];
+extern unsigned short unac_data124[];
+extern unsigned short unac_data125[];
+extern unsigned short unac_data126[];
+extern unsigned short unac_data127[];
+extern unsigned short unac_data128[];
+extern unsigned short unac_data129[];
+extern unsigned short unac_data130[];
+extern unsigned short unac_data131[];
+extern unsigned short unac_data132[];
+extern unsigned short unac_data133[];
+extern unsigned short unac_data134[];
+extern unsigned short unac_data135[];
+extern unsigned short unac_data136[];
+extern unsigned short unac_data137[];
+extern unsigned short unac_data138[];
+extern unsigned short unac_data139[];
+extern unsigned short unac_data140[];
+extern unsigned short unac_data141[];
+extern unsigned short unac_data142[];
+extern unsigned short unac_data143[];
+extern unsigned short unac_data144[];
+extern unsigned short unac_data145[];
+extern unsigned short unac_data146[];
+extern unsigned short unac_data147[];
+extern unsigned short unac_data148[];
+extern unsigned short unac_data149[];
+extern unsigned short unac_data150[];
+extern unsigned short unac_data151[];
+extern unsigned short unac_data152[];
+extern unsigned short unac_data153[];
+extern unsigned short unac_data154[];
+extern unsigned short unac_data155[];
+extern unsigned short unac_data156[];
+extern unsigned short unac_data157[];
+extern unsigned short unac_data158[];
+extern unsigned short unac_data159[];
+extern unsigned short unac_data160[];
+extern unsigned short unac_data161[];
+extern unsigned short unac_data162[];
+extern unsigned short unac_data163[];
+extern unsigned short unac_data164[];
+extern unsigned short unac_data165[];
+extern unsigned short unac_data166[];
+extern unsigned short unac_data167[];
+extern unsigned short unac_data168[];
+extern unsigned short unac_data169[];
+extern unsigned short unac_data170[];
+extern unsigned short unac_data171[];
+extern unsigned short unac_data172[];
+extern unsigned short unac_data173[];
+extern unsigned short unac_data174[];
+extern unsigned short unac_data175[];
+extern unsigned short unac_data176[];
+extern unsigned short unac_data177[];
+/* Generated by builder. Do not modify. End declarations */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unac_h */
Index: /gsdl/trunk/gs2common/indexers/packages/unac/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/indexers/packages/unac/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/packages/unac/win32.mak	(revision 16561)
@@ -0,0 +1,59 @@
+# UNAC
+
+CC = cl
+AR = lib
+
+DLL = 0
+DLLDEBUG = 0
+GSDL_VC4 = 0
+
+INCLUDES = -I. -DUNAC_VERSION=\"1.7.0\"
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD 
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+LIBS = $(ICONVLIB)
+
+HEADERS = unac.h
+
+SOURCES = unac.c 
+
+OBJECTS = unac$o 
+
+DEFS = 
+
+all: libunac.lib
+
+libunac.lib: $(OBJECTS)
+	if EXIST libunac.lib del libunac.lib
+	$(AR) -nologo /out:libunac.lib $(OBJECTS)
+
+install: all
+
+clean:
+	del *.obj libunac.lib
+
+
Index: /gsdl/trunk/gs2common/indexers/winMake.bat
===================================================================
--- /gsdl/trunk/gs2common/indexers/winMake.bat	(revision 16561)
+++ /gsdl/trunk/gs2common/indexers/winMake.bat	(revision 16561)
@@ -0,0 +1,90 @@
+@echo off
+
+set MAKE=nmake
+set MAKE_OPTIONS=/f
+
+if ""%1"" == """" goto all
+if ""%1"" == ""all"" goto go
+if ""%1"" == ""install"" goto go
+if ""%1"" == ""clean"" goto clean
+
+:unknown
+	echo Error: Unrecognized argument %1.
+	goto done
+
+:clean
+	del bin\*
+
+:go
+	if ""%2"" == ""javaonly"" goto java
+	if ""%2"" == ""nojava"" goto nojava
+	if ""%2"" == ""gs2"" goto gs2
+
+:all
+	mkdir bin
+	cd packages\unac
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd mg
+	call winMake.bat %1 
+	cd ..
+
+	cd mgpp
+	call winMake.bat %1 
+	cd ..
+	
+	cd lucene-gs
+	call winMake.bat %1
+	cd ..
+	goto done
+
+:java
+	cd mg
+	call winMake.bat %1 javaonly
+	cd ..
+
+	cd mgpp
+	call winMake.bat %1 javaonly
+	cd ..
+	
+	cd lucene-gs
+	call winMake.bat %1 
+	cd ..
+	goto done
+
+:nojava
+	cd packages\unac
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd mg
+	call winMake.bat %1 nojava
+	cd ..
+
+	cd mgpp
+	call winMake.bat %1 nojava
+	cd ..
+	goto done
+
+:gs2
+	
+	if NOT EXIST "bin" mkdir bin
+	cd packages\unac
+	%MAKE% %MAKE_OPTIONS% win32.mak %1
+	cd ..\..
+
+	cd mg
+	call winMake.bat %1 nojava
+	cd ..
+
+	cd mgpp
+	call winMake.bat %1 nojava
+	cd ..
+	
+	cd lucene-gs
+	call winMake.bat %1
+	cd ..
+	goto done
+
+:done
Index: /gsdl/trunk/gs2common/packages/expat/Makefile
===================================================================
--- /gsdl/trunk/gs2common/packages/expat/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2common/packages/expat/Makefile	(revision 16561)
@@ -0,0 +1,12 @@
+
+all:
+	(cd expat-1.95.8 && $(MAKE) all installlib && cp lib/expat_external.h ../include)
+
+clean:
+	(cd expat-1.95.8 && $(MAKE) clean)
+
+distclean:
+	(cd expat-1.95.8 && $(MAKE) distclean)
+
+install:
+	@echo Expat installed
Index: /gsdl/trunk/gs2common/packages/sqlite/README.txt
===================================================================
--- /gsdl/trunk/gs2common/packages/sqlite/README.txt	(revision 16561)
+++ /gsdl/trunk/gs2common/packages/sqlite/README.txt	(revision 16561)
@@ -0,0 +1,10 @@
+This package contains the source code for the sqlite3 executable and library in
+amalgamated form, as downloaded from http://www.sqlite.org. The only modifications
+have been to add a win32.mak file for compiling on Windows.
+
+SQLite is in the public domain and is thus free for use for any purpose,
+commercial or private.
+
+30 May 2008
+Michael Dewsnip
+DL Consulting Ltd. (http://www.dlconsulting.com)
Index: /gsdl/trunk/gs2common/packages/windows/crypt/README.TXT
===================================================================
--- /gsdl/trunk/gs2common/packages/windows/crypt/README.TXT	(revision 16561)
+++ /gsdl/trunk/gs2common/packages/windows/crypt/README.TXT	(revision 16561)
@@ -0,0 +1,7 @@
+This directory contains a win32 port of the crypt library (see the enclosed
+README files for details). To compile with Microsoft Visual C++ unzip
+crypt.zip and run "nmake /f win32.mak" in the top level directory.
+
+Link crypt.lib with your project when compiling Greenstone.
+
+Stefan Boddie (sjboddie@cs.waikato.ac.nz)
Index: /gsdl/trunk/gs2common/packages/windows/gdbm/README.TXT
===================================================================
--- /gsdl/trunk/gs2common/packages/windows/gdbm/README.TXT	(revision 16561)
+++ /gsdl/trunk/gs2common/packages/windows/gdbm/README.TXT	(revision 16561)
@@ -0,0 +1,7 @@
+This directory contains a win32 port of gdbm. To compile with Microsoft
+Visual C++ unzip gdbm.zip and run "nmake /f win32.mak" in the top level
+directory.
+
+Link gdbm.lib with your project when compiling Greenstone.
+
+Stefan Boddie (sjboddie@cs.waikato.ac.nz)
Index: /gsdl/trunk/gs2common/packages/xlhtml/README.txt
===================================================================
--- /gsdl/trunk/gs2common/packages/xlhtml/README.txt	(revision 16561)
+++ /gsdl/trunk/gs2common/packages/xlhtml/README.txt	(revision 16561)
@@ -0,0 +1,19 @@
+This package contains two programs:
+* ppthtml, which converts Microsoft PowerPoint 95 and 97 files
+into html or xml, and
+* xlhtml, which converts Microsoft Excel 95 and 97 files.
+
+It has been slightly modified for greenstone use as we only want the
+ppt to html program and the xls to html program, and none of the other
+programs (summary/diagnostics, man pages, etc).
+
+This version (0.4.9.0) was the latest development snapshot at the
+time of writing.
+
+See http://www.xlhtml.org/ for newer versions or more info.
+
+xlhtml is released under the GNU GPL.
+
+20 Feb 2002
+John McPherson
+NZDL.
Index: /gsdl/trunk/gs2common/src/corba/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/corba/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/Makefile.in	(revision 16561)
@@ -0,0 +1,156 @@
+###########################################################################
+#
+# Makefile -- src/corba
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+COLSERVR_DIR = $(GSDL_DIR)/src/colservr
+PROTOCOL_DIR = $(GSDL_DIR)/src/protocol
+RECPT_DIR = $(GSDL_DIR)/src/recpt
+
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+ACCENTFOLD_LIBS = $(INDEXERS_DIR)/packages/unac/libunac.a 
+else
+ACCENTFOLD_LIBS = 
+endif
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_LIBS = $(PACKAGES_DIR)/sqlite/lib/libsqlite3.a -ldl -lpthread
+else
+SQLITE_LIBS = 
+endif
+
+MICO_DIR = @MICO_DIR@
+MICO_VER = @MICO_VER@
+ifeq ($(MICO_DIR), default)
+MICO_INCLUDES = -I$(PACKAGES_DIR)/mico/mico/include
+MICO_PATH = $(PACKAGES_DIR)/mico/bin/
+else
+ifeq ($(MICO_DIR), yes)
+MICO_INCLUDES =
+else
+MICO_INCLUDES = -I$(MICO_DIR)
+endif
+MICO_PATH =
+endif
+
+USE_CORBA = @USE_CORBA@
+ifeq ($(USE_CORBA), 1)
+AR = ar
+CC = @CC@
+CFLAGS = @CFLAGS@
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DGSDLSERVER
+INCLUDES = -I$(GSDL_DIR)/src/lib -I$(COLSERVR_DIR) -I$(PROTOCOL_DIR) -I$(RECPT_DIR) -I$(INDEXERS_DIR)/mgpp/text \
+	$(MICO_INCLUDES)
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@ @GDBM_LIBPATH@ -lgdbm -L$(PACKAGES_DIR)/expat/lib -lexpat $(ACCENTFOLD_LIBS) \
+        $(SQLITE_LIBS) -L$(PACKAGES_DIR)/mico/lib -lmico$(MICO_VER)
+RANLIB = @RANLIB@
+
+
+COMPILE = $(MICO_PATH)mico-c++ -x c++ $(CFLAGS) $(DEFS) $(INCLUDES) -c
+LINK = $(MICO_PATH)mico-ld $(LDFLAGS) -o $@
+
+
+.SUFFIXES: .mpp .o
+.mpp.o:
+	$(COMPILE) $<
+
+
+SOURCES = \
+	corbaconv_text_t.mpp \
+	corbaiface.mpp \
+	corbaiface_skel.mpp \
+	corbaproto.mpp \
+	corbatext_t.mpp
+
+OBJECTS = \
+	corbaconv_text_t.o \
+	corbaiface.o \
+	corbaiface_skel.o \
+	corbaproto.o \
+	corbatext_t.o
+
+CORBASERVER_OBJECTS = \
+	corbaServer.o
+
+CORBARECPT_OBJECTS = \
+	corbaclient.o
+
+GSDL_LIBS = \
+	$(RECPT_DIR)/gsdlrecpt.a \
+	$(PROTOCOL_DIR)/gsdlprotocol.a \
+	$(COLSERVR_DIR)/gsdlcolservr.a \
+	$(INDEXERS_DIR)/mg/src/text/libmgtext.a \
+	$(INDEXERS_DIR)/mg/lib/libmglib.a \
+	$(INDEXERS_DIR)/mgpp/text/libmgpptext.a \
+	$(INDEXERS_DIR)/mgpp/lib/libmgpplib.a \
+	$(GSDL_DIR)/src/lib/gsdllib.a
+
+CORBASERVER_EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(CORBASERVER_OBJECTS) \
+	$(GSDL_LIBS)
+
+CORBARECPT_EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(CORBARECPT_OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLES = corbaserver corbarecptldd
+
+
+# Default target: make both corbaserver and corbarecptldd
+all: $(EXECUTABLES)
+
+corbaserver: $(CORBASERVER_EXECUTABLE_OBJECTS)
+	$(LINK) $(CORBASERVER_EXECUTABLE_OBJECTS) $(LIBS)
+
+corbarecptldd: $(CORBARECPT_EXECUTABLE_OBJECTS)
+	$(LINK) $(CORBARECPT_EXECUTABLE_OBJECTS) $(LIBS)
+
+install:
+
+clean:
+	rm -f $(OBJECTS) $(CORBASERVER_OBJECTS) $(CORBARECPT_OBJECTS) $(EXECUTABLES)
+
+distclean:
+	rm -f $(OBJECTS) $(CORBASERVER_OBJECTS) $(CORBARECPT_OBJECTS) $(EXECUTABLES) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
+
+else
+all:
+install:
+clean:
+distclean:
+depend:
+endif
Index: /gsdl/trunk/gs2common/src/corba/corbaServer.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaServer.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaServer.mpp	(revision 16561)
@@ -0,0 +1,491 @@
+// Standard headers
+#include <iostream.h>
+#include <fstream.h>
+
+// protocol headers
+#include "corbaiface.h"
+using namespace gsdlInterface;
+
+#include "corbaconv_text_t.h"
+
+// library headers
+#include "fileutil.h"
+//#include "gsdlhome.h"
+
+// greenstone headers
+#include "comtypes.h"
+#include "recptproto.h"
+#include "nullproto.h"
+
+// local headers
+#include "corbatext_t.h"
+
+void corbatext_setToCorbaArray(text_tset set, corbatext_tset *cset);
+void corbatext_corbaArrayToSet(corbatext_tset cset, text_tset *set);
+void corbatext_mapToCorbaMap(text_tmap map, corbatext_tmap *tm);
+void corbaresponse_resultDocInfoToCorba(ResultDocInfo_tarray docinfo, corbaResultDocInfo_array &corbainfo);
+
+void corbaresponse_termInfoToCorba(TermInfo_tarray terminfo, corbaTermInfo_array &corbainfo) {
+  TermInfo_tarray::iterator here = terminfo.begin();
+  TermInfo_tarray::iterator end  = terminfo.end();
+  unsigned int i;
+	
+  i = 0;
+
+  while (here != end) 
+    {
+      corbainfo.length(i+1);
+      
+      corbainfo[i].frequency = here->freq;
+      corbaconv_text_t::getCorbatext(here->term,corbainfo[i].term);
+      corbatext_arrayToCorbaArray(here->matchTerms, &corbainfo[i].matchTerms);
+
+      i ++;
+      here ++;
+    }
+  cout << "Terminfo " << i << endl;
+}
+
+void corbatext_optionValueArrayToC(corbaOptionValue_array corba, OptionValue_tarray &array)
+{
+  unsigned int i = 0;
+  OptionValue_t option;
+
+  for (i = 0; i < corba.length(); i ++)
+    {
+      corbaconv_text_t::setCorbatext(option.name,corba[i].name);
+      corbaconv_text_t::setCorbatext(option.value,corba[i].value);
+
+      // cout << corbatext_string(corba[i].name) << "?" << option
+
+      array.push_back(option);
+    }
+}
+
+class corbaServer : virtual public corbaiface_skel
+{
+private:
+  text_tarray collections;
+  recptproto *protocol;
+  text_t      gsdlhome;
+
+  void openLogfile(text_t extension, ofstream &out)
+  {
+    char *cfilename;
+    text_t filename;
+
+    filename = filename_cat(gsdlhome, extension);
+    cfilename = filename.getcstr();
+
+    out.open(cfilename);
+    delete cfilename;
+  }
+
+public:
+  corbaServer(recptproto *protocol, text_t home)
+  { 
+    text_t dirname;
+    char * cdirname;
+
+    cout << "CorbaServer: gsdlhome = " << home << endl;
+
+    this->gsdlhome = home;
+    cout << gsdlhome.getcstr() << endl;
+    dirname = filename_cat(gsdlhome, "/collect");
+    cdirname = dirname.getcstr();
+    if (!read_dir (cdirname, collections)) {
+      cout << "Unable to read the gsdl directory; terminating" << endl;
+      exit (1);
+    }
+    delete cdirname;
+
+    cout << "Constructing set list server" << endl;
+    this->protocol = protocol;
+
+  }
+  
+  CORBA::Boolean initialise(corbaComError &err) 
+  {
+      ofstream      logout;
+      int           reply;
+      comerror_t    error = noError;
+
+      this->openLogfile("/etc/corbaout.txt", logout);
+      reply = protocol->init(error, logout);
+      logout.close();
+      return (reply != 0 ? 1 : 0);
+    }
+
+  void configure(const corbatext_t &corbaKey, const corbatext_tarray &corbaCfgline,corbaComError &err)
+    {
+      text_t        key;
+      text_tarray   cfgline;
+      comerror_t    error = noError;
+
+      corbaconv_text_t::setCorbatext(key,corbaKey);
+      corbatext_corbaArrayToArray(corbaCfgline, &cfgline);
+
+
+      // ****
+      cout << "Recieved " << key << " = ";
+      for (int unsigned i=0; i<cfgline.size(); i++)
+	{
+	  cout << cfgline[i] << " ";
+	}
+      cout << endl;
+
+      // DB // ****
+      if (key=="gsdlhome")
+	{  
+	  text_tarray cfgline;
+	  cfgline.push_back (gsdlhome);
+	  cout << "Changing gsdlhome to " << gsdlhome << endl;
+	  protocol->configure(key, cfgline, error);
+	}
+      else if (key=="httpdomain")
+	{
+	  // Only let gsdlhome through !!!! // ****
+	  cout << "Supressing httpdomain" << endl;
+	}
+      else if (key=="httpprefix")
+	{
+	  cout << "Supressing httpprefix" << endl;
+	}
+      else
+	{ 
+	  protocol->configure(key, cfgline,error);
+	}
+    }
+
+  void hasCollection(const corbatext_t &corbaCollect, CORBA::Boolean &has,
+                     corbaComError &error) {
+    // do nowt!
+    corbaconv_text_t *cct;
+    bool    _has;
+    comerror_t err = noError;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    cct = new corbaconv_text_t(corbaCollect);
+
+    cout << "Collection: " << cct->getcstr() << endl;
+
+    protocol->has_collection(*cct, _has, err, logout); // 'cct' typecast to text_t
+    if (_has)
+      { 
+	has = 1;
+      }
+    else
+      {
+	has = 0;
+      }
+
+    // cout << "  " << has << endl;
+
+    delete cct;
+
+    error = corbaNoError;
+    logout.close();
+  }
+
+  void ping(const struct corbatext_t &corbaCollect, CORBA::Boolean &wasSuccess, corbaComError &corbaError)
+  { 
+    text_t collection;
+    bool   success;
+    comerror_t error = noError;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    corbaconv_text_t::setCorbatext(collection,corbaCollect);
+    protocol->ping(collection, success, error, logout);
+
+    corbaError = (corbaComError) error;
+    wasSuccess = (CORBA::Boolean) success;
+    logout.close();
+  }
+
+  void filter(const struct corbatext_t &corbaCollect,
+	      const struct corbaFilterRequest &corbaRequest,
+	      struct corbaFilterResponse &corbaResponse,
+	      corbaComError &corbaError)
+  {
+    // variables for action
+    FilterRequest_t request;
+    FilterResponse_t response;
+    comerror_t       error = noError;
+    text_t           collection;
+    ofstream         logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    // copy collection id
+    corbaconv_text_t::setCorbatext(collection,corbaCollect);
+
+    // copy request values
+    corbaconv_text_t::setCorbatext(request.filterName,corbaRequest.filter);
+    corbatext_optionValueArrayToC(corbaRequest.filterOptions, request.filterOptions);
+    corbatext_corbaArrayToArray(corbaRequest.docSet, &request.docSet);
+    request.filterResultOptions = corbaRequest.filterResultOptions;
+    corbaconv_text_t::setCorbatext(request.requestParams,corbaRequest.requestParams);
+    corbaconv_text_t::setCorbatext(request.refParams,corbaRequest.refParams);
+    corbatext_corbaArrayToSet(corbaRequest.fields, &request.fields);
+    request.getParents = corbaRequest.getParents;
+
+    protocol->filter(collection, request, response, error, logout);
+
+    // copy response values
+    corbaResponse.numDocs = response.numDocs;
+    corbaResponse.isApprox = (::gsdlInterface::corbaIsApprox) response.isApprox;
+    corbaresponse_termInfoToCorba(response.termInfo, corbaResponse.termInfo);
+    corbaresponse_resultDocInfoToCorba(response.docInfo, corbaResponse.docInfo);
+
+    // tidy up variables
+    logout.close();
+    corbaError = (corbaComError) error;
+    cout << "Filter prepared" << corbaResponse.docInfo.length() << "," << corbaResponse.numDocs << endl;
+  }
+
+  void getCollectInfo(const struct corbatext_t &corbaCollect,
+                      corbaColInfoResponse &corbaResponse,
+                      corbaComError &corbaError)
+  {
+    ColInfoResponse_t response;
+    comerror_t       error = noError;
+    text_t           collection;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    corbaconv_text_t::setCorbatext(collection,corbaCollect);
+    protocol->get_collectinfo(collection, response, error, logout);
+
+    cout << "IsPublic  = " << ((response.isPublic)?"True":"False") << endl;
+    cout << "IsBeta    = " << ((response.isBeta)  ?"True":"False") << endl;
+    cout << "BuildDate = " << response.buildDate << endl;
+    cout << "NumDocs   = " << response.numDocs << endl;
+    cout << "NumBytes  = " << response.numBytes << endl;
+    cout << "NumWords  = " << response.numWords << endl;
+    cout << "UseBook   = " << ((response.useBook)  ?"True":"False") << endl;
+    
+    corbaResponse.useBook   = response.useBook;
+    corbaResponse.isPublic  = response.isPublic;
+    corbaResponse.isBeta    = response.isBeta;
+    corbaResponse.buildDate = response.buildDate;
+    corbatext_arrayToCorbaArray(response.languages, &corbaResponse.languages);
+    corbatext_arrayToCorbaArray(response.ccsCols, &corbaResponse.ccsCols);
+    corbaResponse.numDocs   = response.numDocs;
+    corbaResponse.numBytes  = response.numBytes;
+    corbaResponse.numWords  = response.numWords;
+
+    corbatext_mapToCorbaMap(response.collectionmeta, &corbaResponse.collectionMeta);
+    corbatext_mapToCorbaMap(response.format, &corbaResponse.format);
+    corbatext_mapToCorbaMap(response.building, &corbaResponse.building);
+    corbaconv_text_t::getCorbatext(response.httpdomain,corbaResponse.httpdomain);
+
+    corbaconv_text_t::getCorbatext(response.httpprefix,corbaResponse.httpprefix);
+    corbaconv_text_t::getCorbatext(response.receptionist,corbaResponse.receptionist);
+
+    /* text_tmap::iterator f_here = response.format.begin();
+    text_tmap::iterator f_end = response.format.end();
+    while (f_here!=f_end)
+    {
+      cout << "**** format: " << f_here->first << " = " << f_here->second << endl;
+      f_here++;
+    } 
+    */ // ****
+
+    logout.close();
+    corbaError = (corbaComError) error;
+  }
+
+  void getFilterOptions(const struct corbatext_t &corbaCollect,
+			const struct corbatext_t &corbaFilterName,
+			struct corbaFilterOptionsResponse &corbaResponse,
+			enum corbaComError &corbaError)
+  {   
+    text_t collection;
+    InfoFilterOptionsRequest_t request;
+    InfoFilterOptionsResponse_t response;
+    comerror_t error = noError;
+    unsigned int i;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    corbaconv_text_t::setCorbatext(collection,corbaCollect);
+    corbaconv_text_t::setCorbatext(request.filterName,corbaFilterName);
+    protocol->get_filteroptions(collection, request, response, error, logout);
+
+    // decode filters back into corba format
+    FilterOption_tmap::iterator here = response.filterOptions.begin();
+    FilterOption_tmap::iterator end = response.filterOptions.end();
+    i = 0;
+    while (here != end)
+      {
+	// set array for name/options to be one bigger
+	corbaResponse.names.length(i+1);
+	corbaResponse.options.length(i+1);
+
+	// get left-hand side of map
+	corbaconv_text_t::getCorbatext((*here).first,corbaResponse.names[i]);
+	
+	// get right-hand side of map
+	corbaconv_text_t::getCorbatext((*here).second.name,corbaResponse.options[i].name);
+	corbaResponse.options[i].type = 
+	  (::gsdlInterface::corbaFilterType)(*here).second.type;
+	corbaResponse.options[i].repeatable = 
+	  (::gsdlInterface::corbaFilterRepeatable)(*here).second.repeatable;
+	corbaconv_text_t::getCorbatext((*here).second.defaultValue,corbaResponse.options[i].defaultValue);
+	corbatext_arrayToCorbaArray((*here).second.validValues, &corbaResponse.options[i].validValues);
+
+	// take next iterators
+	i++;
+	here ++;
+      }
+
+    logout.close();
+    corbaError = (corbaComError) error;
+  }
+
+  void getFilterInfo(const struct corbatext_t &corbaCollect, 
+		     corbatext_tarray &filterNames, 
+		     enum corbaComError &corbaError)
+  { text_tarray ta;
+    comerror_t error = noError;
+    text_t collection;    
+    InfoFiltersResponse_t response;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    corbaconv_text_t::setCorbatext(collection,corbaCollect);
+    cout << collection.getcstr() << endl;
+			    
+    protocol->get_filterinfo(collection, response, error, logout);
+    corbatext_setToCorbaArray(response.filterNames, &filterNames);
+
+    corbaError = (corbaComError) error;
+    logout.close();
+  }
+  
+  void getDocument(const struct corbatext_t &corbaCollection, struct corbaDocRequest &corbaRequest, 
+		   struct corbaDocResponse &corbaResponse,  enum corbaComError &corbaError)
+  { DocumentRequest_t request;
+    DocumentResponse_t response;
+    comerror_t         error = noError;
+    text_t             collection;
+    ofstream logout;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    // set up request
+    corbaconv_text_t::setCorbatext(collection,corbaCollection);
+    corbaconv_text_t::setCorbatext(request.OID,corbaRequest.OID);
+    corbaconv_text_t::setCorbatext(request.docType,corbaRequest.docType);
+    corbaconv_text_t::setCorbatext(request.docFormat,corbaRequest.docFormat);
+
+    // do actual get document
+    protocol->get_document(collection, request, response, error, logout);
+    corbaconv_text_t::getCorbatext(response.doc,corbaResponse.doc);
+
+    // decode response
+    corbaError = (corbaComError) error;
+
+    logout.close();
+  }
+
+  void collectionList(corbatext_tarray &corbalist, corbaComError &error)
+  {
+    /* stringSeq *reply;
+    unsigned int i;
+    char *istring; */
+
+    text_tarray collist;
+    comerror_t  err;
+    ofstream logout;
+
+    cout << "Received collection list request" << endl;
+
+    this->openLogfile("/etc/corbaout.txt", logout);
+
+    protocol->get_collection_list(collist, err, logout);
+
+    /*    reply = new stringSeq(collections.size());
+    
+    (*reply).length(collections.size());
+    for (i = 0; i < collections.size(); i ++)
+      {
+	istring = collections[i].getcstr();
+	(*reply)[i] = CORBA::string_dup(istring);
+	delete istring;
+      }
+    
+    return reply;
+    */
+
+    corbatext_arrayToCorbaArray(collist, &corbalist);
+    cout << "Replying " << collist.size() << ": " << endl;
+
+    for (int unsigned i = 0; i < collist.size(); i ++)
+      {
+	cout << collist[i];
+	if (i<collist.size()-1) cout << ", ";
+      }
+    cout << endl;
+
+    logout.close();
+    // CORBA::string_dup(_listString);
+  }
+};
+
+int main (int argc, char *argv[])
+{
+  nullproto protocol;
+  collectset *cservers;
+  text_t     gsdlhome;
+
+  cservers = new collectset(gsdlhome);
+  protocol.set_collectset(cservers);
+
+  cout << "Started Corba ..." << endl;
+
+  // ORB initialization
+  CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );
+  CORBA::BOA_var boa = orb->BOA_init( argc, argv, "mico-local-boa" );
+
+  cout << "Initialised ORB and BOA" << endl;
+
+  corbaServer* server = new corbaServer(&protocol, gsdlhome);
+
+  cout << "Initialised server" << endl;
+
+  CORBA::String_var ref = orb->object_to_string( server );
+
+  cout << "Mapped Corba object to string" << endl;
+
+  // write id to "naming service"
+  ofstream out ("/tmp/localcorba.objid");
+	if (out.is_open()) {
+		out << ref << endl;
+		if (out.fail() || out.bad()) {
+			cout << "Failed " << endl;
+		}
+		out.close ();
+	}
+	else {
+		cout << "Unable to write string to file /tmp/localcorba.objid" << endl;
+		cout << "Please check the file permissions/ownership" << endl;
+		return 0;
+	}
+  cout << "Corba string is " << ref << endl;
+
+  // indicate readiness and run
+  boa->impl_is_ready( CORBA::ImplementationDef::_nil() );
+  orb->run ();
+  CORBA::release( server );
+  return 0;
+}
+
+
Index: /gsdl/trunk/gs2common/src/corba/corbaclient.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaclient.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaclient.mpp	(revision 16561)
@@ -0,0 +1,160 @@
+/**********************************************************************
+ *
+ * corbaclient.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$
+ *
+ *********************************************************************/
+
+#include "receptionist.h"
+#include "cgiwrapper.h"
+#include "corbaproto.h"
+#include "collectserver.h"
+#include "filter.h"
+#include "browsefilter.h"
+#include "queryfilter.h"
+#include "mgsearch.h"
+#include "fileutil.h"
+#include <assert.h>
+
+#include "action.h"
+#include "statusaction.h"
+#include "pageaction.h"
+#include "pingaction.h"
+#include "queryaction.h"
+#include "documentaction.h"
+#include "authenaction.h"
+#include "usersaction.h"
+#include "authenaction.h"
+#include "extlinkaction.h"
+#include "tipaction.h"
+#include "collectoraction.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"
+#include "gsdlsitecfg.h"
+
+int main (int argc, char *argv[]) {
+  receptionist recpt;
+  corbaproto*  cproto_array[argc];
+  text_t       gsdlhome;
+  text_t       httpdomain;
+  text_t       httpprefix;
+  
+  cproto_array[0] = NULL; // unused
+
+  if (argc == 1)
+  { cout << "Content-type: text/html" << endl << endl;
+    cout << "Usage: " << argv[0] << "site1.objid [site2.objid ....]" << endl;
+    return 0;
+  }
+
+  // Strictly speaking httpdomain and httpprefix are not needed.
+  // Need to modifiy site_cfg_read to eliminate this dependency. // ****
+  site_cfg_read(gsdlhome, httpdomain, httpprefix);
+
+  for (int i=1; i<argc; i ++)
+    {
+      // add the protocol to the receptionist
+      cproto_array[i] = new corbaproto(argv[i]);
+      recpt.add_protocol(cproto_array[i]);
+    }
+
+//  cout << "GRB 2" << endl;
+
+  // the list of actions. Note: these actions will become invalid
+  // at the end of this function.
+  tipaction atipaction;
+  recpt.add_action(&atipaction);
+
+  statusaction astatusaction;
+  astatusaction.set_receptionist (&recpt);
+  recpt.add_action (&astatusaction);
+
+  pageaction apageaction;
+  apageaction.set_receptionist (&recpt);
+  recpt.add_action (&apageaction);
+
+  pingaction apingaction;
+  recpt.add_action (&apingaction);
+
+  queryaction aqueryaction;
+  aqueryaction.set_receptionist (&recpt);
+  recpt.add_action (&aqueryaction);
+
+  documentaction adocumentaction;
+  adocumentaction.set_receptionist (&recpt);
+  recpt.add_action (&adocumentaction);
+
+  usersaction ausersaction;
+  recpt.add_action (&ausersaction);
+
+  extlinkaction anextlinkaction;
+  recpt.add_action (&anextlinkaction);
+
+  authenaction aauthenaction;
+  aauthenaction.set_receptionist(&recpt);
+  recpt.add_action (&aauthenaction);
+
+  collectoraction acollectoraction;
+  acollectoraction.set_receptionist (&recpt);
+  recpt.add_action(&acollectoraction);
+
+//  cout << "GRB 3" << endl;
+
+  // 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, "");
+
+  for (int i=1; i< argc; i++)
+    {
+      delete cproto_array[i];
+    }
+
+  return 0;
+}
+
Index: /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.h
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.h	(revision 16561)
@@ -0,0 +1,68 @@
+/**********************************************************************
+ *
+ * corbaconv_text_t.mpp -- conversion support text_t <-> corba_text_t
+ * 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 CORBACONV_TEXT_T_H
+#define CORBACONV_TEXT_T_H
+
+#include "text_t.h"
+
+// Inherit from text_t
+// Provide support to convert from corba_text_t to text_t and vice versa
+
+class corbaconv_text_t : public text_t {
+  
+public:
+  // constructors
+  corbaconv_text_t (corbatext_t t);
+
+  void setCorbatext(corbatext_t ct);
+  void getCorbatext(corbatext_t &ct) const;
+  void fillUsString(usString *us) const;
+
+  static void setCorbatext(text_t& t, corbatext_t ct);
+  static void getCorbatext(const text_t& t, corbatext_t &ct);
+  static void fillUsString(const text_t& t, usString *us);
+};
+
+
+inline void corbaconv_text_t::setCorbatext(corbatext_t ct) 
+{ 
+  setCorbatext(*this,ct); 
+}
+
+inline void corbaconv_text_t::getCorbatext(corbatext_t& ct) const
+{ 
+  getCorbatext(*this,ct); 
+}
+
+inline void corbaconv_text_t::fillUsString(usString *us) const
+{
+  fillUsString(*this,us);
+}
+
+#endif
Index: /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaconv_text_t.mpp	(revision 16561)
@@ -0,0 +1,109 @@
+/**********************************************************************
+ *
+ * corbaconv_text_t.mpp -- conversion support text_t <-> corba_text_t
+ * 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$
+ *
+ *********************************************************************/
+
+#if defined(GSDL_USE_STL_H) || defined(__APPLE__)
+#include <vector.h>
+#else
+#include <vector>
+#endif
+
+// protocol header
+#include "corbaiface.h"
+using namespace gsdlInterface;
+
+#include "corbatext_t.h"
+
+#include "corbaconv_text_t.h"
+
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\algorithm>
+#elif defined(GSDL_USE_STL_H)
+#  if defined(GSDL_USE_ALGO_H)
+#    include <algo.h>
+#  else
+#    include <algorithm.h>
+#  endif
+#else
+#  include <algorithm>
+#endif
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__
+#  include "win32cfg.h"
+# else
+#  include "mico/config.h"
+# endif
+#endif
+
+
+// #include "unitool.h" // ****
+
+////////////////////////////////////
+// corbaconv_text_t methods
+////////////////////////////////////
+
+corbaconv_text_t::corbaconv_text_t (corbatext_t t)
+{ 
+  setCorbatext(t);  
+}
+
+void corbaconv_text_t::getCorbatext(const text_t& t, corbatext_t &ct)
+{
+  ct.encoding = 0;
+  fillUsString(t,&ct.text);
+}
+
+void corbaconv_text_t::setCorbatext(text_t& t, corbatext_t ct)
+{
+  t.setencoding(0);
+  t.clear();
+  corbatext_usvector(ct, t.text_as_usvector());
+}
+
+void corbaconv_text_t::fillUsString(const text_t& t, usString *us)
+{
+  unsigned int i;
+  usvector::const_iterator here = t.text_as_usvector().begin();
+  usvector::const_iterator end = t.text_as_usvector().end();
+
+
+  if (end == here)
+    { 	
+      return;
+    }
+  (*us).length(end - here);
+  i = 0;
+
+  while (here != end)
+    {
+      (*us)[i] = *here;
+      i ++;
+      here ++;
+    }
+  
+}
Index: /gsdl/trunk/gs2common/src/corba/corbaiface.h
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaiface.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaiface.h	(revision 16561)
@@ -0,0 +1,614 @@
+/*
+ *  MICO --- a free CORBA implementation
+ *  Copyright (C) 1997-98 Kay Roemer & Arno Puder
+ *
+ *  This file was automatically generated. DO NOT EDIT!
+ */
+
+#if !defined(__CORBAIFACE_H__) || defined(MICO_NO_TOPLEVEL_MODULES)
+#define __CORBAIFACE_H__
+
+#ifndef MICO_NO_TOPLEVEL_MODULES
+#include <CORBA.h>
+#include <mico/throw.h>
+#endif
+
+#ifndef MICO_NO_TOPLEVEL_MODULES
+MICO_NAMESPACE_DECL gsdlInterface {
+#endif
+
+#if !defined(MICO_NO_TOPLEVEL_MODULES) || defined(MICO_MODULE_gsdlInterface)
+
+
+typedef SequenceTmpl<CORBA::UShort,MICO_TID_DEF> usString;
+#ifdef _WINDOWS
+static usString _dummy_usString;
+#endif
+typedef TSeqVar<SequenceTmpl<CORBA::UShort,MICO_TID_DEF> > usString_var;
+typedef TSeqOut<SequenceTmpl<CORBA::UShort,MICO_TID_DEF> > usString_out;
+
+typedef StringSequenceTmpl<CORBA::String_var> stringSeq;
+#ifdef _WINDOWS
+static stringSeq _dummy_stringSeq;
+#endif
+typedef TSeqVar<StringSequenceTmpl<CORBA::String_var> > stringSeq_var;
+typedef TSeqOut<StringSequenceTmpl<CORBA::String_var> > stringSeq_out;
+
+struct corbatext_t;
+typedef TVarVar<corbatext_t> corbatext_t_var;
+typedef TVarOut<corbatext_t> corbatext_t_out;
+
+struct corbatext_t {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbatext_t_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbatext_t();
+  ~corbatext_t();
+  corbatext_t( const corbatext_t& s );
+  corbatext_t& operator=( const corbatext_t& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  usString text;
+  CORBA::UShort encoding;
+  CORBA::ULong length;
+};
+
+typedef SequenceTmpl<corbatext_t,MICO_TID_DEF> corbatext_tarray;
+#ifdef _WINDOWS
+static corbatext_tarray _dummy_corbatext_tarray;
+#endif
+typedef TSeqVar<SequenceTmpl<corbatext_t,MICO_TID_DEF> > corbatext_tarray_var;
+typedef TSeqOut<SequenceTmpl<corbatext_t,MICO_TID_DEF> > corbatext_tarray_out;
+
+typedef corbatext_tarray corbatext_tset;
+#ifdef _WINDOWS
+static corbatext_tset _dummy_corbatext_tset;
+#endif
+typedef TSeqVar<corbatext_tarray > corbatext_tset_var;
+typedef TSeqOut<corbatext_tarray > corbatext_tset_out;
+
+struct corbatext_tmap;
+typedef TVarVar<corbatext_tmap> corbatext_tmap_var;
+typedef TVarOut<corbatext_tmap> corbatext_tmap_out;
+
+struct corbatext_tmap {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbatext_tmap_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbatext_tmap();
+  ~corbatext_tmap();
+  corbatext_tmap( const corbatext_tmap& s );
+  corbatext_tmap& operator=( const corbatext_tmap& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_tarray names;
+  corbatext_tarray values;
+};
+
+enum corbaComError {
+  corbaNoError = 0,
+  corbaAuthenticationFailure,
+  corbaProtocolError,
+  corbaConfigurationError,
+  corbaSystemProblem
+};
+
+typedef corbaComError& corbaComError_out;
+
+struct corbaShortColInfo;
+typedef TVarVar<corbaShortColInfo> corbaShortColInfo_var;
+typedef TVarOut<corbaShortColInfo> corbaShortColInfo_out;
+
+struct corbaShortColInfo {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaShortColInfo_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaShortColInfo();
+  ~corbaShortColInfo();
+  corbaShortColInfo( const corbaShortColInfo& s );
+  corbaShortColInfo& operator=( const corbaShortColInfo& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t name;
+  corbatext_t host;
+  CORBA::Long response;
+};
+
+struct corbaColInfoResponse;
+typedef TVarVar<corbaColInfoResponse> corbaColInfoResponse_var;
+typedef TVarOut<corbaColInfoResponse> corbaColInfoResponse_out;
+
+struct corbaColInfoResponse {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaColInfoResponse_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaColInfoResponse();
+  ~corbaColInfoResponse();
+  corbaColInfoResponse( const corbaColInfoResponse& s );
+  corbaColInfoResponse& operator=( const corbaColInfoResponse& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbaShortColInfo shoftInfo;
+  CORBA::Boolean useBook;
+  CORBA::Boolean isPublic;
+  CORBA::Boolean isBeta;
+  CORBA::ULong buildDate;
+  corbatext_tarray ccsCols;
+  corbatext_tarray languages;
+  CORBA::ULong numDocs;
+  CORBA::ULong numWords;
+  CORBA::ULong numBytes;
+  corbatext_tmap collectionMeta;
+  corbatext_tmap format;
+  corbatext_tmap building;
+  corbatext_t httpdomain;
+  corbatext_t httpprefix;
+  corbatext_t receptionist;
+};
+
+struct corbaDocRequest;
+typedef TVarVar<corbaDocRequest> corbaDocRequest_var;
+typedef TVarOut<corbaDocRequest> corbaDocRequest_out;
+
+struct corbaDocRequest {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaDocRequest_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaDocRequest();
+  ~corbaDocRequest();
+  corbaDocRequest( const corbaDocRequest& s );
+  corbaDocRequest& operator=( const corbaDocRequest& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t OID;
+  corbatext_t docType;
+  corbatext_t docFormat;
+};
+
+struct corbaDocResponse;
+typedef TVarVar<corbaDocResponse> corbaDocResponse_var;
+typedef TVarOut<corbaDocResponse> corbaDocResponse_out;
+
+struct corbaDocResponse {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaDocResponse_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaDocResponse();
+  ~corbaDocResponse();
+  corbaDocResponse( const corbaDocResponse& s );
+  corbaDocResponse& operator=( const corbaDocResponse& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t doc;
+};
+
+enum corbaFilterType {
+  booleanType = 0,
+  integerType,
+  enumeratedType,
+  stringType
+};
+
+typedef corbaFilterType& corbaFilterType_out;
+
+enum corbaFilterRepeatable {
+  onePerQuery = 0,
+  onePerTerm,
+  nPerTerm
+};
+
+typedef corbaFilterRepeatable& corbaFilterRepeatable_out;
+
+struct corbaFilterOption;
+typedef TVarVar<corbaFilterOption> corbaFilterOption_var;
+typedef TVarOut<corbaFilterOption> corbaFilterOption_out;
+
+struct corbaFilterOption {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaFilterOption_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaFilterOption();
+  ~corbaFilterOption();
+  corbaFilterOption( const corbaFilterOption& s );
+  corbaFilterOption& operator=( const corbaFilterOption& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t name;
+  corbaFilterType type;
+  corbaFilterRepeatable repeatable;
+  corbatext_t defaultValue;
+  corbatext_tarray validValues;
+};
+
+typedef SequenceTmpl<corbaFilterOption,MICO_TID_DEF> corbaFilterOptionList;
+#ifdef _WINDOWS
+static corbaFilterOptionList _dummy_corbaFilterOptionList;
+#endif
+typedef TSeqVar<SequenceTmpl<corbaFilterOption,MICO_TID_DEF> > corbaFilterOptionList_var;
+typedef TSeqOut<SequenceTmpl<corbaFilterOption,MICO_TID_DEF> > corbaFilterOptionList_out;
+
+struct corbaFilterOptionsResponse;
+typedef TVarVar<corbaFilterOptionsResponse> corbaFilterOptionsResponse_var;
+typedef TVarOut<corbaFilterOptionsResponse> corbaFilterOptionsResponse_out;
+
+struct corbaFilterOptionsResponse {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaFilterOptionsResponse_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaFilterOptionsResponse();
+  ~corbaFilterOptionsResponse();
+  corbaFilterOptionsResponse( const corbaFilterOptionsResponse& s );
+  corbaFilterOptionsResponse& operator=( const corbaFilterOptionsResponse& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbaFilterOptionList options;
+  corbatext_tarray names;
+};
+
+struct corbaOptionValue;
+typedef TVarVar<corbaOptionValue> corbaOptionValue_var;
+typedef TVarOut<corbaOptionValue> corbaOptionValue_out;
+
+struct corbaOptionValue {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaOptionValue_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaOptionValue();
+  ~corbaOptionValue();
+  corbaOptionValue( const corbaOptionValue& s );
+  corbaOptionValue& operator=( const corbaOptionValue& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t name;
+  corbatext_t value;
+};
+
+typedef SequenceTmpl<corbaOptionValue,MICO_TID_DEF> corbaOptionValue_array;
+#ifdef _WINDOWS
+static corbaOptionValue_array _dummy_corbaOptionValue_array;
+#endif
+typedef TSeqVar<SequenceTmpl<corbaOptionValue,MICO_TID_DEF> > corbaOptionValue_array_var;
+typedef TSeqOut<SequenceTmpl<corbaOptionValue,MICO_TID_DEF> > corbaOptionValue_array_out;
+
+struct corbaFilterRequest;
+typedef TVarVar<corbaFilterRequest> corbaFilterRequest_var;
+typedef TVarOut<corbaFilterRequest> corbaFilterRequest_out;
+
+struct corbaFilterRequest {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaFilterRequest_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaFilterRequest();
+  ~corbaFilterRequest();
+  corbaFilterRequest( const corbaFilterRequest& s );
+  corbaFilterRequest& operator=( const corbaFilterRequest& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t filter;
+  corbaOptionValue_array filterOptions;
+  corbatext_tarray docSet;
+  CORBA::Long filterResultOptions;
+  corbatext_t requestParams;
+  corbatext_t refParams;
+  corbatext_tset fields;
+  CORBA::Boolean getParents;
+};
+
+struct corbaTermInfo;
+typedef TVarVar<corbaTermInfo> corbaTermInfo_var;
+typedef TVarOut<corbaTermInfo> corbaTermInfo_out;
+
+struct corbaTermInfo {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaTermInfo_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaTermInfo();
+  ~corbaTermInfo();
+  corbaTermInfo( const corbaTermInfo& s );
+  corbaTermInfo& operator=( const corbaTermInfo& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t term;
+  CORBA::Long frequency;
+  corbatext_tarray matchTerms;
+};
+
+typedef SequenceTmpl<corbaTermInfo,MICO_TID_DEF> corbaTermInfo_array;
+#ifdef _WINDOWS
+static corbaTermInfo_array _dummy_corbaTermInfo_array;
+#endif
+typedef TSeqVar<SequenceTmpl<corbaTermInfo,MICO_TID_DEF> > corbaTermInfo_array_var;
+typedef TSeqOut<SequenceTmpl<corbaTermInfo,MICO_TID_DEF> > corbaTermInfo_array_out;
+
+struct corbaMetadataInfo;
+typedef TVarVar<corbaMetadataInfo> corbaMetadataInfo_var;
+typedef TVarOut<corbaMetadataInfo> corbaMetadataInfo_out;
+
+struct corbaMetadataInfo {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaMetadataInfo_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaMetadataInfo();
+  ~corbaMetadataInfo();
+  corbaMetadataInfo( const corbaMetadataInfo& s );
+  corbaMetadataInfo& operator=( const corbaMetadataInfo& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t params;
+  CORBA::Boolean isRef;
+  corbatext_tarray values;
+  CORBA::Long id;
+  CORBA::Long parentid;
+};
+
+typedef SequenceTmpl<corbaMetadataInfo,MICO_TID_DEF> corbaMetadataInfo_array;
+#ifdef _WINDOWS
+static corbaMetadataInfo_array _dummy_corbaMetadataInfo_array;
+#endif
+typedef TSeqVar<SequenceTmpl<corbaMetadataInfo,MICO_TID_DEF> > corbaMetadataInfo_array_var;
+typedef TSeqOut<SequenceTmpl<corbaMetadataInfo,MICO_TID_DEF> > corbaMetadataInfo_array_out;
+
+struct corbaMetadataInfo_map;
+typedef TVarVar<corbaMetadataInfo_map> corbaMetadataInfo_map_var;
+typedef TVarOut<corbaMetadataInfo_map> corbaMetadataInfo_map_out;
+
+struct corbaMetadataInfo_map {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaMetadataInfo_map_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaMetadataInfo_map();
+  ~corbaMetadataInfo_map();
+  corbaMetadataInfo_map( const corbaMetadataInfo_map& s );
+  corbaMetadataInfo_map& operator=( const corbaMetadataInfo_map& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_tarray names;
+  corbaMetadataInfo_array values;
+};
+
+typedef SequenceTmpl<CORBA::Long,MICO_TID_DEF> corbaDocFreq_array;
+#ifdef _WINDOWS
+static corbaDocFreq_array _dummy_corbaDocFreq_array;
+#endif
+typedef TSeqVar<SequenceTmpl<CORBA::Long,MICO_TID_DEF> > corbaDocFreq_array_var;
+typedef TSeqOut<SequenceTmpl<CORBA::Long,MICO_TID_DEF> > corbaDocFreq_array_out;
+
+struct corbaResultDocInfo;
+typedef TVarVar<corbaResultDocInfo> corbaResultDocInfo_var;
+typedef TVarOut<corbaResultDocInfo> corbaResultDocInfo_out;
+
+struct corbaResultDocInfo {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaResultDocInfo_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaResultDocInfo();
+  ~corbaResultDocInfo();
+  corbaResultDocInfo( const corbaResultDocInfo& s );
+  corbaResultDocInfo& operator=( const corbaResultDocInfo& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  corbatext_t OID;
+  CORBA::Long resultNum;
+  CORBA::Long ranking;
+  CORBA::Long termsMatched;
+  CORBA::Long phraseMatched;
+  corbaDocFreq_array docFreq;
+  corbaMetadataInfo_map metadata;
+  corbatext_t classifierMetadataType;
+  CORBA::Long classifierMetadataOffset;
+  corbaMetadataInfo_array metaparents;
+};
+
+typedef SequenceTmpl<corbaResultDocInfo,MICO_TID_DEF> corbaResultDocInfo_array;
+#ifdef _WINDOWS
+static corbaResultDocInfo_array _dummy_corbaResultDocInfo_array;
+#endif
+typedef TSeqVar<SequenceTmpl<corbaResultDocInfo,MICO_TID_DEF> > corbaResultDocInfo_array_var;
+typedef TSeqOut<SequenceTmpl<corbaResultDocInfo,MICO_TID_DEF> > corbaResultDocInfo_array_out;
+
+enum corbaIsApprox {
+  resultExact = 0,
+  resultApprox,
+  resultMany
+};
+
+typedef corbaIsApprox& corbaIsApprox_out;
+
+struct corbaFilterResponse;
+typedef TVarVar<corbaFilterResponse> corbaFilterResponse_var;
+typedef TVarOut<corbaFilterResponse> corbaFilterResponse_out;
+
+struct corbaFilterResponse {
+  #ifdef HAVE_TYPEDEF_OVERLOAD
+  typedef corbaFilterResponse_var _var_type;
+  #endif
+  #ifdef HAVE_EXPLICIT_STRUCT_OPS
+  corbaFilterResponse();
+  ~corbaFilterResponse();
+  corbaFilterResponse( const corbaFilterResponse& s );
+  corbaFilterResponse& operator=( const corbaFilterResponse& s );
+  #endif //HAVE_EXPLICIT_STRUCT_OPS
+
+  CORBA::Long numDocs;
+  corbaIsApprox isApprox;
+  corbaTermInfo_array termInfo;
+  corbaResultDocInfo_array docInfo;
+};
+
+class corbaiface;
+typedef corbaiface *corbaiface_ptr;
+typedef corbaiface_ptr corbaifaceRef;
+typedef ObjVar<corbaiface> corbaiface_var;
+typedef ObjOut<corbaiface> corbaiface_out;
+
+
+// Common definitions for interface corbaiface
+class corbaiface : 
+  virtual public CORBA::Object
+{
+  public:
+    virtual ~corbaiface();
+
+    #ifdef HAVE_TYPEDEF_OVERLOAD
+    typedef corbaiface_ptr _ptr_type;
+    typedef corbaiface_var _var_type;
+    #endif
+
+    static corbaiface_ptr _narrow( CORBA::Object_ptr obj );
+    static corbaiface_ptr _narrow( CORBA::AbstractBase_ptr obj );
+    static corbaiface_ptr _duplicate( corbaiface_ptr _obj )
+    {
+      CORBA::Object::_duplicate (_obj);
+      return _obj;
+    }
+
+    static corbaiface_ptr _nil()
+    {
+      return 0;
+    }
+
+    virtual void *_narrow_helper( const char *repoid );
+    static vector<CORBA::Narrow_proto> *_narrow_helpers;
+    static bool _narrow_helper2( CORBA::Object_ptr obj );
+
+    virtual CORBA::Boolean initialise( corbaComError& error ) = 0;
+    virtual void configure( const corbatext_t& key, const corbatext_tarray& cfgline, corbaComError& error ) = 0;
+    virtual void collectionList( corbatext_tarray& collist, corbaComError& error ) = 0;
+    virtual void hasCollection( const corbatext_t& corbaCollection, CORBA::Boolean& has, corbaComError& error ) = 0;
+    virtual void ping( const corbatext_t& collection, CORBA::Boolean& wasSuccess, corbaComError& error ) = 0;
+    virtual void getDocument( const corbatext_t& collection, corbaDocRequest& request, corbaDocResponse& response, corbaComError& error ) = 0;
+    virtual void getCollectInfo( const corbatext_t& collection, corbaColInfoResponse& response, corbaComError& error ) = 0;
+    virtual void getFilterInfo( const corbatext_t& collection, corbatext_tarray& filterNames, corbaComError& error ) = 0;
+    virtual void getFilterOptions( const corbatext_t& collection, const corbatext_t& option, corbaFilterOptionsResponse& response, corbaComError& error ) = 0;
+    virtual void filter( const corbatext_t& collection, const corbaFilterRequest& request, corbaFilterResponse& response, corbaComError& error ) = 0;
+
+  protected:
+    corbaiface() {};
+  private:
+    corbaiface( const corbaiface& );
+    void operator=( const corbaiface& );
+};
+
+// Stub for interface corbaiface
+class corbaiface_stub:
+  virtual public corbaiface
+{
+  public:
+    virtual ~corbaiface_stub();
+    CORBA::Boolean initialise( corbaComError& error );
+    void configure( const corbatext_t& key, const corbatext_tarray& cfgline, corbaComError& error );
+    void collectionList( corbatext_tarray& collist, corbaComError& error );
+    void hasCollection( const corbatext_t& corbaCollection, CORBA::Boolean& has, corbaComError& error );
+    void ping( const corbatext_t& collection, CORBA::Boolean& wasSuccess, corbaComError& error );
+    void getDocument( const corbatext_t& collection, corbaDocRequest& request, corbaDocResponse& response, corbaComError& error );
+    void getCollectInfo( const corbatext_t& collection, corbaColInfoResponse& response, corbaComError& error );
+    void getFilterInfo( const corbatext_t& collection, corbatext_tarray& filterNames, corbaComError& error );
+    void getFilterOptions( const corbatext_t& collection, const corbatext_t& option, corbaFilterOptionsResponse& response, corbaComError& error );
+    void filter( const corbatext_t& collection, const corbaFilterRequest& request, corbaFilterResponse& response, corbaComError& error );
+
+  private:
+    void operator=( const corbaiface_stub& );
+};
+
+class corbaiface_skel :
+  virtual public StaticMethodDispatcher,
+  virtual public corbaiface
+{
+  public:
+    corbaiface_skel( const CORBA::BOA::ReferenceData & = CORBA::BOA::ReferenceData() );
+    virtual ~corbaiface_skel();
+    corbaiface_skel( CORBA::Object_ptr obj );
+    virtual bool dispatch( CORBA::StaticServerRequest_ptr _req, CORBA::Environment &_env );
+    corbaiface_ptr _this();
+
+};
+
+#endif // !defined(MICO_NO_TOPLEVEL_MODULES) || defined(MICO_MODULE_gsdlInterface)
+
+#ifndef MICO_NO_TOPLEVEL_MODULES
+
+} MICO_NAMESPACE_END_DECL
+#endif
+
+
+
+#ifndef MICO_CONF_NO_POA
+
+#endif // MICO_CONF_NO_POA
+
+#if !defined(MICO_NO_TOPLEVEL_MODULES) || defined(MICO_MODULE__GLOBAL)
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbatext_t;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbatext_tmap;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaComError;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaShortColInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaColInfoResponse;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaDocRequest;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaDocResponse;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterType;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterRepeatable;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterOption;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterOptionsResponse;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaOptionValue;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterRequest;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaTermInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaMetadataInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaMetadataInfo_map;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaResultDocInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaIsApprox;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterResponse;
+
+extern CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaiface;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbatext_t;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaFilterOption;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaOptionValue;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaTermInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaMetadataInfo;
+
+extern CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaResultDocInfo;
+
+#endif // !defined(MICO_NO_TOPLEVEL_MODULES) || defined(MICO_MODULE__GLOBAL)
+
+
+#if !defined(MICO_NO_TOPLEVEL_MODULES) && !defined(MICO_IN_GENERATED_CODE)
+#include <mico/template_impl.h>
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/src/corba/corbaiface.idl
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaiface.idl	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaiface.idl	(revision 16561)
@@ -0,0 +1,231 @@
+// basic elements for text
+
+module gsdlInterface {
+
+typedef sequence<unsigned short> usString;
+
+typedef sequence<string> stringSeq;
+
+struct corbatext_t
+{ 
+  usString text;
+  unsigned short encoding;
+  unsigned long  length;
+};
+
+typedef sequence<corbatext_t> corbatext_tarray;
+
+typedef corbatext_tarray corbatext_tset;
+
+struct corbatext_tmap
+{
+  corbatext_tarray names;
+  corbatext_tarray values;
+};
+
+// standard error values
+
+enum corbaComError
+{ corbaNoError, 
+  corbaAuthenticationFailure, 
+  corbaProtocolError, 
+  corbaConfigurationError, 
+  corbaSystemProblem
+};
+
+//
+// getColInfo response
+//
+
+struct corbaShortColInfo
+{
+  corbatext_t name;
+  corbatext_t host;
+  long        response;
+};
+
+struct corbaColInfoResponse
+{
+  boolean       useBook;
+  corbaShortColInfo shoftInfo;
+  boolean           isPublic;
+  boolean           isBeta;
+  unsigned long     buildDate;
+  corbatext_tarray  ccsCols;        // Added 05/04/2000
+  corbatext_tarray  languages;
+  unsigned long     numDocs;
+  unsigned long     numWords;
+  unsigned long     numBytes;
+  corbatext_tmap    collectionMeta;
+  corbatext_tmap    format;
+  corbatext_tmap    building;
+  corbatext_t       httpdomain ;    // Added 27/07/2000
+  corbatext_t       httpprefix;     // Added 27/07/2000
+  corbatext_t       receptionist;
+};
+
+//
+// getDocument request
+//
+
+struct corbaDocRequest
+{ 
+  corbatext_t OID;
+  corbatext_t docType;
+  corbatext_t docFormat;
+};
+
+//
+// getDocument response
+//
+
+struct corbaDocResponse
+{ 
+  corbatext_t doc;
+};
+
+enum corbaFilterType
+{
+  booleanType,
+  integerType,
+  enumeratedType,
+  stringType
+};
+
+enum corbaFilterRepeatable
+{
+  onePerQuery,
+  onePerTerm,
+  nPerTerm
+};
+
+struct corbaFilterOption
+{
+  corbatext_t           name;
+  corbaFilterType       type;
+  corbaFilterRepeatable repeatable;
+  corbatext_t           defaultValue;
+  corbatext_tarray      validValues;
+};
+
+typedef sequence<corbaFilterOption> corbaFilterOptionList;
+
+struct corbaFilterOptionsResponse
+{
+  corbaFilterOptionList options;
+  corbatext_tarray      names;
+};
+
+//
+// structures for filter call
+//
+
+struct corbaOptionValue
+{
+  corbatext_t name;
+  corbatext_t value;
+};
+
+typedef sequence<corbaOptionValue> corbaOptionValue_array;
+
+struct corbaFilterRequest
+{ 
+  corbatext_t            filter;
+  corbaOptionValue_array filterOptions;
+  corbatext_tarray       docSet;
+  long                   filterResultOptions;
+  corbatext_t            requestParams;
+  corbatext_t            refParams;
+  corbatext_tset         fields;
+  boolean                getParents;
+};
+
+struct corbaTermInfo
+{
+  corbatext_t      term;
+  long             frequency;
+  corbatext_tarray matchTerms;
+};
+
+typedef sequence<corbaTermInfo> corbaTermInfo_array;
+
+struct corbaMetadataInfo
+{
+  corbatext_t      params;
+  boolean          isRef;
+  corbatext_tarray values;
+  long             id;
+  long             parentid; // dodge for avoiding pointers for the moment
+};
+
+typedef sequence<corbaMetadataInfo> corbaMetadataInfo_array;
+
+struct corbaMetadataInfo_map
+{ 
+  corbatext_tarray        names;
+  corbaMetadataInfo_array values;
+};
+
+typedef sequence<long> corbaDocFreq_array;
+
+struct corbaResultDocInfo
+{
+  corbatext_t             OID;
+  long                    resultNum;
+  long                    ranking;
+  long                    termsMatched;
+  long                    phraseMatched;
+  corbaDocFreq_array      docFreq;
+  corbaMetadataInfo_map   metadata;
+  corbatext_t             classifierMetadataType;    // Added 05/04/2000
+  long                    classifierMetadataOffset;  // Added 05/04/2000
+  corbaMetadataInfo_array metaparents;
+};
+
+typedef sequence<corbaResultDocInfo> corbaResultDocInfo_array;
+
+enum corbaIsApprox
+{
+  resultExact,
+  resultApprox,
+  resultMany
+};
+
+struct corbaFilterResponse
+{
+  long                     numDocs;
+  corbaIsApprox            isApprox;
+  corbaTermInfo_array      termInfo;
+  corbaResultDocInfo_array docInfo;
+};
+
+interface corbaiface
+{
+  boolean   initialise(inout corbaComError error);
+  void      configure(in corbatext_t key, in corbatext_tarray cfgline,
+		      inout corbaComError error);
+  void      collectionList(inout corbatext_tarray collist,
+			   inout corbaComError error);
+  void      hasCollection(in corbatext_t corbaCollection, inout boolean has,
+		       inout corbaComError error);
+  void      ping(in corbatext_t collection, inout boolean wasSuccess, inout corbaComError error);
+  void      getDocument(in corbatext_t collection,
+			inout corbaDocRequest request, inout corbaDocResponse response,
+			inout corbaComError error);
+  void      getCollectInfo(in corbatext_t collection,
+			   inout corbaColInfoResponse response,
+			   inout corbaComError error);
+  void      getFilterInfo(in corbatext_t collection, 
+                          inout corbatext_tarray filterNames, 
+			  inout corbaComError error);
+  void      getFilterOptions(in corbatext_t collection,
+                             in corbatext_t option,
+                             inout corbaFilterOptionsResponse response,
+			     inout corbaComError error);
+  void      filter(in corbatext_t collection,
+		   in corbaFilterRequest request,
+                   inout corbaFilterResponse response,
+                   inout corbaComError error);
+};
+
+};
Index: /gsdl/trunk/gs2common/src/corba/corbaiface.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaiface.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaiface.mpp	(revision 16561)
@@ -0,0 +1,2000 @@
+/*
+ *  MICO --- a free CORBA implementation
+ *  Copyright (C) 1997-98 Kay Roemer & Arno Puder
+ *
+ *  This file was automatically generated. DO NOT EDIT!
+ */
+
+#include "corbaiface.h"
+
+//--------------------------------------------------------
+//  Implementation of stubs
+//--------------------------------------------------------
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbatext_t::corbatext_t()
+{
+}
+
+gsdlInterface::corbatext_t::corbatext_t( const corbatext_t& _s )
+{
+  text = ((corbatext_t&)_s).text;
+  encoding = ((corbatext_t&)_s).encoding;
+  length = ((corbatext_t&)_s).length;
+}
+
+gsdlInterface::corbatext_t::~corbatext_t()
+{
+}
+
+gsdlInterface::corbatext_t&
+gsdlInterface::corbatext_t::operator=( const corbatext_t& _s )
+{
+  text = ((corbatext_t&)_s).text;
+  encoding = ((corbatext_t&)_s).encoding;
+  length = ((corbatext_t&)_s).length;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbatext_t : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbatext_t _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbatext_t::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbatext_t::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbatext_t::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbatext_t::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    CORBA::_stcseq_ushort->demarshal( dc, &((_MICO_T*)v)->text ) &&
+    CORBA::_stc_ushort->demarshal( dc, &((_MICO_T*)v)->encoding ) &&
+    CORBA::_stc_ulong->demarshal( dc, &((_MICO_T*)v)->length ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbatext_t::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  CORBA::_stcseq_ushort->marshal( ec, &((_MICO_T*)v)->text );
+  CORBA::_stc_ushort->marshal( ec, &((_MICO_T*)v)->encoding );
+  CORBA::_stc_ulong->marshal( ec, &((_MICO_T*)v)->length );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbatext_t;
+
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbatext_tmap::corbatext_tmap()
+{
+}
+
+gsdlInterface::corbatext_tmap::corbatext_tmap( const corbatext_tmap& _s )
+{
+  names = ((corbatext_tmap&)_s).names;
+  values = ((corbatext_tmap&)_s).values;
+}
+
+gsdlInterface::corbatext_tmap::~corbatext_tmap()
+{
+}
+
+gsdlInterface::corbatext_tmap&
+gsdlInterface::corbatext_tmap::operator=( const corbatext_tmap& _s )
+{
+  names = ((corbatext_tmap&)_s).names;
+  values = ((corbatext_tmap&)_s).values;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbatext_tmap : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbatext_tmap _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbatext_tmap::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbatext_tmap::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbatext_tmap::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbatext_tmap::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->names ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->values ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbatext_tmap::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->names );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->values );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbatext_tmap;
+
+
+class _Marshaller_gsdlInterface_corbaComError : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaComError _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaComError::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaComError::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaComError::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaComError::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong ul;
+  if( !dc.enumeration( ul ) )
+    return FALSE;
+  *(_MICO_T*) v = (_MICO_T) ul;
+  return TRUE;
+}
+
+void _Marshaller_gsdlInterface_corbaComError::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.enumeration( (CORBA::ULong) *(_MICO_T *) v );
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaComError;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaShortColInfo::corbaShortColInfo()
+{
+}
+
+gsdlInterface::corbaShortColInfo::corbaShortColInfo( const corbaShortColInfo& _s )
+{
+  name = ((corbaShortColInfo&)_s).name;
+  host = ((corbaShortColInfo&)_s).host;
+  response = ((corbaShortColInfo&)_s).response;
+}
+
+gsdlInterface::corbaShortColInfo::~corbaShortColInfo()
+{
+}
+
+gsdlInterface::corbaShortColInfo&
+gsdlInterface::corbaShortColInfo::operator=( const corbaShortColInfo& _s )
+{
+  name = ((corbaShortColInfo&)_s).name;
+  host = ((corbaShortColInfo&)_s).host;
+  response = ((corbaShortColInfo&)_s).response;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaShortColInfo : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaShortColInfo _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaShortColInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaShortColInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaShortColInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaShortColInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->name ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->host ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->response ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaShortColInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->name );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->host );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->response );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaShortColInfo;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaColInfoResponse::corbaColInfoResponse()
+{
+}
+
+gsdlInterface::corbaColInfoResponse::corbaColInfoResponse( const corbaColInfoResponse& _s )
+{
+  shoftInfo = ((corbaColInfoResponse&)_s).shoftInfo;
+  useBook = ((corbaColInfoResponse&)_s).useBook;
+  isPublic = ((corbaColInfoResponse&)_s).isPublic;
+  isBeta = ((corbaColInfoResponse&)_s).isBeta;
+  buildDate = ((corbaColInfoResponse&)_s).buildDate;
+  ccsCols = ((corbaColInfoResponse&)_s).ccsCols;
+  languages = ((corbaColInfoResponse&)_s).languages;
+  numDocs = ((corbaColInfoResponse&)_s).numDocs;
+  numWords = ((corbaColInfoResponse&)_s).numWords;
+  numBytes = ((corbaColInfoResponse&)_s).numBytes;
+  collectionMeta = ((corbaColInfoResponse&)_s).collectionMeta;
+  format = ((corbaColInfoResponse&)_s).format;
+  building = ((corbaColInfoResponse&)_s).building;
+  httpdomain = ((corbaColInfoResponse&)_s).httpdomain;
+  httpprefix = ((corbaColInfoResponse&)_s).httpprefix;
+  receptionist = ((corbaColInfoResponse&)_s).receptionist;
+}
+
+gsdlInterface::corbaColInfoResponse::~corbaColInfoResponse()
+{
+}
+
+gsdlInterface::corbaColInfoResponse&
+gsdlInterface::corbaColInfoResponse::operator=( const corbaColInfoResponse& _s )
+{
+  shoftInfo = ((corbaColInfoResponse&)_s).shoftInfo;
+  useBook = ((corbaColInfoResponse&)_s).useBook;
+  isPublic = ((corbaColInfoResponse&)_s).isPublic;
+  isBeta = ((corbaColInfoResponse&)_s).isBeta;
+  buildDate = ((corbaColInfoResponse&)_s).buildDate;
+  ccsCols = ((corbaColInfoResponse&)_s).ccsCols;
+  languages = ((corbaColInfoResponse&)_s).languages;
+  numDocs = ((corbaColInfoResponse&)_s).numDocs;
+  numWords = ((corbaColInfoResponse&)_s).numWords;
+  numBytes = ((corbaColInfoResponse&)_s).numBytes;
+  collectionMeta = ((corbaColInfoResponse&)_s).collectionMeta;
+  format = ((corbaColInfoResponse&)_s).format;
+  building = ((corbaColInfoResponse&)_s).building;
+  httpdomain = ((corbaColInfoResponse&)_s).httpdomain;
+  httpprefix = ((corbaColInfoResponse&)_s).httpprefix;
+  receptionist = ((corbaColInfoResponse&)_s).receptionist;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaColInfoResponse : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaColInfoResponse _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaColInfoResponse::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaColInfoResponse::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaColInfoResponse::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaColInfoResponse::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbaShortColInfo->demarshal( dc, &((_MICO_T*)v)->shoftInfo ) &&
+    CORBA::_stc_boolean->demarshal( dc, &((_MICO_T*)v)->useBook ) &&
+    CORBA::_stc_boolean->demarshal( dc, &((_MICO_T*)v)->isPublic ) &&
+    CORBA::_stc_boolean->demarshal( dc, &((_MICO_T*)v)->isBeta ) &&
+    CORBA::_stc_ulong->demarshal( dc, &((_MICO_T*)v)->buildDate ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->ccsCols ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->languages ) &&
+    CORBA::_stc_ulong->demarshal( dc, &((_MICO_T*)v)->numDocs ) &&
+    CORBA::_stc_ulong->demarshal( dc, &((_MICO_T*)v)->numWords ) &&
+    CORBA::_stc_ulong->demarshal( dc, &((_MICO_T*)v)->numBytes ) &&
+    _marshaller_gsdlInterface_corbatext_tmap->demarshal( dc, &((_MICO_T*)v)->collectionMeta ) &&
+    _marshaller_gsdlInterface_corbatext_tmap->demarshal( dc, &((_MICO_T*)v)->format ) &&
+    _marshaller_gsdlInterface_corbatext_tmap->demarshal( dc, &((_MICO_T*)v)->building ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->httpdomain ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->httpprefix ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->receptionist ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaColInfoResponse::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbaShortColInfo->marshal( ec, &((_MICO_T*)v)->shoftInfo );
+  CORBA::_stc_boolean->marshal( ec, &((_MICO_T*)v)->useBook );
+  CORBA::_stc_boolean->marshal( ec, &((_MICO_T*)v)->isPublic );
+  CORBA::_stc_boolean->marshal( ec, &((_MICO_T*)v)->isBeta );
+  CORBA::_stc_ulong->marshal( ec, &((_MICO_T*)v)->buildDate );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->ccsCols );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->languages );
+  CORBA::_stc_ulong->marshal( ec, &((_MICO_T*)v)->numDocs );
+  CORBA::_stc_ulong->marshal( ec, &((_MICO_T*)v)->numWords );
+  CORBA::_stc_ulong->marshal( ec, &((_MICO_T*)v)->numBytes );
+  _marshaller_gsdlInterface_corbatext_tmap->marshal( ec, &((_MICO_T*)v)->collectionMeta );
+  _marshaller_gsdlInterface_corbatext_tmap->marshal( ec, &((_MICO_T*)v)->format );
+  _marshaller_gsdlInterface_corbatext_tmap->marshal( ec, &((_MICO_T*)v)->building );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->httpdomain );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->httpprefix );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->receptionist );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaColInfoResponse;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaDocRequest::corbaDocRequest()
+{
+}
+
+gsdlInterface::corbaDocRequest::corbaDocRequest( const corbaDocRequest& _s )
+{
+  OID = ((corbaDocRequest&)_s).OID;
+  docType = ((corbaDocRequest&)_s).docType;
+  docFormat = ((corbaDocRequest&)_s).docFormat;
+}
+
+gsdlInterface::corbaDocRequest::~corbaDocRequest()
+{
+}
+
+gsdlInterface::corbaDocRequest&
+gsdlInterface::corbaDocRequest::operator=( const corbaDocRequest& _s )
+{
+  OID = ((corbaDocRequest&)_s).OID;
+  docType = ((corbaDocRequest&)_s).docType;
+  docFormat = ((corbaDocRequest&)_s).docFormat;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaDocRequest : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaDocRequest _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaDocRequest::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaDocRequest::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaDocRequest::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaDocRequest::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->OID ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->docType ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->docFormat ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaDocRequest::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->OID );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->docType );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->docFormat );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaDocRequest;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaDocResponse::corbaDocResponse()
+{
+}
+
+gsdlInterface::corbaDocResponse::corbaDocResponse( const corbaDocResponse& _s )
+{
+  doc = ((corbaDocResponse&)_s).doc;
+}
+
+gsdlInterface::corbaDocResponse::~corbaDocResponse()
+{
+}
+
+gsdlInterface::corbaDocResponse&
+gsdlInterface::corbaDocResponse::operator=( const corbaDocResponse& _s )
+{
+  doc = ((corbaDocResponse&)_s).doc;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaDocResponse : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaDocResponse _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaDocResponse::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaDocResponse::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaDocResponse::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaDocResponse::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->doc ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaDocResponse::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->doc );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaDocResponse;
+
+
+class _Marshaller_gsdlInterface_corbaFilterType : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterType _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterType::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterType::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterType::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterType::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong ul;
+  if( !dc.enumeration( ul ) )
+    return FALSE;
+  *(_MICO_T*) v = (_MICO_T) ul;
+  return TRUE;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterType::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.enumeration( (CORBA::ULong) *(_MICO_T *) v );
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterType;
+
+
+class _Marshaller_gsdlInterface_corbaFilterRepeatable : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterRepeatable _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterRepeatable::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRepeatable::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRepeatable::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterRepeatable::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong ul;
+  if( !dc.enumeration( ul ) )
+    return FALSE;
+  *(_MICO_T*) v = (_MICO_T) ul;
+  return TRUE;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRepeatable::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.enumeration( (CORBA::ULong) *(_MICO_T *) v );
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterRepeatable;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaFilterOption::corbaFilterOption()
+{
+}
+
+gsdlInterface::corbaFilterOption::corbaFilterOption( const corbaFilterOption& _s )
+{
+  name = ((corbaFilterOption&)_s).name;
+  type = ((corbaFilterOption&)_s).type;
+  repeatable = ((corbaFilterOption&)_s).repeatable;
+  defaultValue = ((corbaFilterOption&)_s).defaultValue;
+  validValues = ((corbaFilterOption&)_s).validValues;
+}
+
+gsdlInterface::corbaFilterOption::~corbaFilterOption()
+{
+}
+
+gsdlInterface::corbaFilterOption&
+gsdlInterface::corbaFilterOption::operator=( const corbaFilterOption& _s )
+{
+  name = ((corbaFilterOption&)_s).name;
+  type = ((corbaFilterOption&)_s).type;
+  repeatable = ((corbaFilterOption&)_s).repeatable;
+  defaultValue = ((corbaFilterOption&)_s).defaultValue;
+  validValues = ((corbaFilterOption&)_s).validValues;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaFilterOption : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterOption _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterOption::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOption::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOption::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterOption::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->name ) &&
+    _marshaller_gsdlInterface_corbaFilterType->demarshal( dc, &((_MICO_T*)v)->type ) &&
+    _marshaller_gsdlInterface_corbaFilterRepeatable->demarshal( dc, &((_MICO_T*)v)->repeatable ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->defaultValue ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->validValues ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOption::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->name );
+  _marshaller_gsdlInterface_corbaFilterType->marshal( ec, &((_MICO_T*)v)->type );
+  _marshaller_gsdlInterface_corbaFilterRepeatable->marshal( ec, &((_MICO_T*)v)->repeatable );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->defaultValue );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->validValues );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterOption;
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaFilterOptionsResponse::corbaFilterOptionsResponse()
+{
+}
+
+gsdlInterface::corbaFilterOptionsResponse::corbaFilterOptionsResponse( const corbaFilterOptionsResponse& _s )
+{
+  options = ((corbaFilterOptionsResponse&)_s).options;
+  names = ((corbaFilterOptionsResponse&)_s).names;
+}
+
+gsdlInterface::corbaFilterOptionsResponse::~corbaFilterOptionsResponse()
+{
+}
+
+gsdlInterface::corbaFilterOptionsResponse&
+gsdlInterface::corbaFilterOptionsResponse::operator=( const corbaFilterOptionsResponse& _s )
+{
+  options = ((corbaFilterOptionsResponse&)_s).options;
+  names = ((corbaFilterOptionsResponse&)_s).names;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaFilterOptionsResponse : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterOptionsResponse _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterOptionsResponse::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOptionsResponse::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOptionsResponse::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterOptionsResponse::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller__seq_gsdlInterface_corbaFilterOption->demarshal( dc, &((_MICO_T*)v)->options ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->names ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaFilterOptionsResponse::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller__seq_gsdlInterface_corbaFilterOption->marshal( ec, &((_MICO_T*)v)->options );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->names );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterOptionsResponse;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaOptionValue::corbaOptionValue()
+{
+}
+
+gsdlInterface::corbaOptionValue::corbaOptionValue( const corbaOptionValue& _s )
+{
+  name = ((corbaOptionValue&)_s).name;
+  value = ((corbaOptionValue&)_s).value;
+}
+
+gsdlInterface::corbaOptionValue::~corbaOptionValue()
+{
+}
+
+gsdlInterface::corbaOptionValue&
+gsdlInterface::corbaOptionValue::operator=( const corbaOptionValue& _s )
+{
+  name = ((corbaOptionValue&)_s).name;
+  value = ((corbaOptionValue&)_s).value;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaOptionValue : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaOptionValue _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaOptionValue::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaOptionValue::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaOptionValue::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaOptionValue::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->name ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->value ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaOptionValue::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->name );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->value );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaOptionValue;
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaFilterRequest::corbaFilterRequest()
+{
+}
+
+gsdlInterface::corbaFilterRequest::corbaFilterRequest( const corbaFilterRequest& _s )
+{
+  filter = ((corbaFilterRequest&)_s).filter;
+  filterOptions = ((corbaFilterRequest&)_s).filterOptions;
+  docSet = ((corbaFilterRequest&)_s).docSet;
+  filterResultOptions = ((corbaFilterRequest&)_s).filterResultOptions;
+  requestParams = ((corbaFilterRequest&)_s).requestParams;
+  refParams = ((corbaFilterRequest&)_s).refParams;
+  fields = ((corbaFilterRequest&)_s).fields;
+  getParents = ((corbaFilterRequest&)_s).getParents;
+}
+
+gsdlInterface::corbaFilterRequest::~corbaFilterRequest()
+{
+}
+
+gsdlInterface::corbaFilterRequest&
+gsdlInterface::corbaFilterRequest::operator=( const corbaFilterRequest& _s )
+{
+  filter = ((corbaFilterRequest&)_s).filter;
+  filterOptions = ((corbaFilterRequest&)_s).filterOptions;
+  docSet = ((corbaFilterRequest&)_s).docSet;
+  filterResultOptions = ((corbaFilterRequest&)_s).filterResultOptions;
+  requestParams = ((corbaFilterRequest&)_s).requestParams;
+  refParams = ((corbaFilterRequest&)_s).refParams;
+  fields = ((corbaFilterRequest&)_s).fields;
+  getParents = ((corbaFilterRequest&)_s).getParents;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaFilterRequest : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterRequest _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterRequest::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRequest::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRequest::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterRequest::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->filter ) &&
+    _marshaller__seq_gsdlInterface_corbaOptionValue->demarshal( dc, &((_MICO_T*)v)->filterOptions ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->docSet ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->filterResultOptions ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->requestParams ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->refParams ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->fields ) &&
+    CORBA::_stc_boolean->demarshal( dc, &((_MICO_T*)v)->getParents ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaFilterRequest::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->filter );
+  _marshaller__seq_gsdlInterface_corbaOptionValue->marshal( ec, &((_MICO_T*)v)->filterOptions );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->docSet );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->filterResultOptions );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->requestParams );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->refParams );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->fields );
+  CORBA::_stc_boolean->marshal( ec, &((_MICO_T*)v)->getParents );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterRequest;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaTermInfo::corbaTermInfo()
+{
+}
+
+gsdlInterface::corbaTermInfo::corbaTermInfo( const corbaTermInfo& _s )
+{
+  term = ((corbaTermInfo&)_s).term;
+  frequency = ((corbaTermInfo&)_s).frequency;
+  matchTerms = ((corbaTermInfo&)_s).matchTerms;
+}
+
+gsdlInterface::corbaTermInfo::~corbaTermInfo()
+{
+}
+
+gsdlInterface::corbaTermInfo&
+gsdlInterface::corbaTermInfo::operator=( const corbaTermInfo& _s )
+{
+  term = ((corbaTermInfo&)_s).term;
+  frequency = ((corbaTermInfo&)_s).frequency;
+  matchTerms = ((corbaTermInfo&)_s).matchTerms;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaTermInfo : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaTermInfo _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaTermInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaTermInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaTermInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaTermInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->term ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->frequency ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->matchTerms ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaTermInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->term );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->frequency );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->matchTerms );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaTermInfo;
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaMetadataInfo::corbaMetadataInfo()
+{
+}
+
+gsdlInterface::corbaMetadataInfo::corbaMetadataInfo( const corbaMetadataInfo& _s )
+{
+  params = ((corbaMetadataInfo&)_s).params;
+  isRef = ((corbaMetadataInfo&)_s).isRef;
+  values = ((corbaMetadataInfo&)_s).values;
+  id = ((corbaMetadataInfo&)_s).id;
+  parentid = ((corbaMetadataInfo&)_s).parentid;
+}
+
+gsdlInterface::corbaMetadataInfo::~corbaMetadataInfo()
+{
+}
+
+gsdlInterface::corbaMetadataInfo&
+gsdlInterface::corbaMetadataInfo::operator=( const corbaMetadataInfo& _s )
+{
+  params = ((corbaMetadataInfo&)_s).params;
+  isRef = ((corbaMetadataInfo&)_s).isRef;
+  values = ((corbaMetadataInfo&)_s).values;
+  id = ((corbaMetadataInfo&)_s).id;
+  parentid = ((corbaMetadataInfo&)_s).parentid;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaMetadataInfo : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaMetadataInfo _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaMetadataInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaMetadataInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->params ) &&
+    CORBA::_stc_boolean->demarshal( dc, &((_MICO_T*)v)->isRef ) &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->values ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->id ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->parentid ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->params );
+  CORBA::_stc_boolean->marshal( ec, &((_MICO_T*)v)->isRef );
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->values );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->id );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->parentid );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaMetadataInfo;
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaMetadataInfo_map::corbaMetadataInfo_map()
+{
+}
+
+gsdlInterface::corbaMetadataInfo_map::corbaMetadataInfo_map( const corbaMetadataInfo_map& _s )
+{
+  names = ((corbaMetadataInfo_map&)_s).names;
+  values = ((corbaMetadataInfo_map&)_s).values;
+}
+
+gsdlInterface::corbaMetadataInfo_map::~corbaMetadataInfo_map()
+{
+}
+
+gsdlInterface::corbaMetadataInfo_map&
+gsdlInterface::corbaMetadataInfo_map::operator=( const corbaMetadataInfo_map& _s )
+{
+  names = ((corbaMetadataInfo_map&)_s).names;
+  values = ((corbaMetadataInfo_map&)_s).values;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaMetadataInfo_map : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaMetadataInfo_map _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaMetadataInfo_map::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo_map::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo_map::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaMetadataInfo_map::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller__seq_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->names ) &&
+    _marshaller__seq_gsdlInterface_corbaMetadataInfo->demarshal( dc, &((_MICO_T*)v)->values ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaMetadataInfo_map::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller__seq_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->names );
+  _marshaller__seq_gsdlInterface_corbaMetadataInfo->marshal( ec, &((_MICO_T*)v)->values );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaMetadataInfo_map;
+
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaResultDocInfo::corbaResultDocInfo()
+{
+}
+
+gsdlInterface::corbaResultDocInfo::corbaResultDocInfo( const corbaResultDocInfo& _s )
+{
+  OID = ((corbaResultDocInfo&)_s).OID;
+  resultNum = ((corbaResultDocInfo&)_s).resultNum;
+  ranking = ((corbaResultDocInfo&)_s).ranking;
+  termsMatched = ((corbaResultDocInfo&)_s).termsMatched;
+  phraseMatched = ((corbaResultDocInfo&)_s).phraseMatched;
+  docFreq = ((corbaResultDocInfo&)_s).docFreq;
+  metadata = ((corbaResultDocInfo&)_s).metadata;
+  classifierMetadataType = ((corbaResultDocInfo&)_s).classifierMetadataType;
+  classifierMetadataOffset = ((corbaResultDocInfo&)_s).classifierMetadataOffset;
+  metaparents = ((corbaResultDocInfo&)_s).metaparents;
+}
+
+gsdlInterface::corbaResultDocInfo::~corbaResultDocInfo()
+{
+}
+
+gsdlInterface::corbaResultDocInfo&
+gsdlInterface::corbaResultDocInfo::operator=( const corbaResultDocInfo& _s )
+{
+  OID = ((corbaResultDocInfo&)_s).OID;
+  resultNum = ((corbaResultDocInfo&)_s).resultNum;
+  ranking = ((corbaResultDocInfo&)_s).ranking;
+  termsMatched = ((corbaResultDocInfo&)_s).termsMatched;
+  phraseMatched = ((corbaResultDocInfo&)_s).phraseMatched;
+  docFreq = ((corbaResultDocInfo&)_s).docFreq;
+  metadata = ((corbaResultDocInfo&)_s).metadata;
+  classifierMetadataType = ((corbaResultDocInfo&)_s).classifierMetadataType;
+  classifierMetadataOffset = ((corbaResultDocInfo&)_s).classifierMetadataOffset;
+  metaparents = ((corbaResultDocInfo&)_s).metaparents;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaResultDocInfo : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaResultDocInfo _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaResultDocInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaResultDocInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaResultDocInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaResultDocInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->OID ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->resultNum ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->ranking ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->termsMatched ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->phraseMatched ) &&
+    CORBA::_stcseq_long->demarshal( dc, &((_MICO_T*)v)->docFreq ) &&
+    _marshaller_gsdlInterface_corbaMetadataInfo_map->demarshal( dc, &((_MICO_T*)v)->metadata ) &&
+    _marshaller_gsdlInterface_corbatext_t->demarshal( dc, &((_MICO_T*)v)->classifierMetadataType ) &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->classifierMetadataOffset ) &&
+    _marshaller__seq_gsdlInterface_corbaMetadataInfo->demarshal( dc, &((_MICO_T*)v)->metaparents ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaResultDocInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->OID );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->resultNum );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->ranking );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->termsMatched );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->phraseMatched );
+  CORBA::_stcseq_long->marshal( ec, &((_MICO_T*)v)->docFreq );
+  _marshaller_gsdlInterface_corbaMetadataInfo_map->marshal( ec, &((_MICO_T*)v)->metadata );
+  _marshaller_gsdlInterface_corbatext_t->marshal( ec, &((_MICO_T*)v)->classifierMetadataType );
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->classifierMetadataOffset );
+  _marshaller__seq_gsdlInterface_corbaMetadataInfo->marshal( ec, &((_MICO_T*)v)->metaparents );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaResultDocInfo;
+
+
+
+class _Marshaller_gsdlInterface_corbaIsApprox : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaIsApprox _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaIsApprox::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaIsApprox::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaIsApprox::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaIsApprox::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong ul;
+  if( !dc.enumeration( ul ) )
+    return FALSE;
+  *(_MICO_T*) v = (_MICO_T) ul;
+  return TRUE;
+}
+
+void _Marshaller_gsdlInterface_corbaIsApprox::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.enumeration( (CORBA::ULong) *(_MICO_T *) v );
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaIsApprox;
+
+#ifdef HAVE_EXPLICIT_STRUCT_OPS
+gsdlInterface::corbaFilterResponse::corbaFilterResponse()
+{
+}
+
+gsdlInterface::corbaFilterResponse::corbaFilterResponse( const corbaFilterResponse& _s )
+{
+  numDocs = ((corbaFilterResponse&)_s).numDocs;
+  isApprox = ((corbaFilterResponse&)_s).isApprox;
+  termInfo = ((corbaFilterResponse&)_s).termInfo;
+  docInfo = ((corbaFilterResponse&)_s).docInfo;
+}
+
+gsdlInterface::corbaFilterResponse::~corbaFilterResponse()
+{
+}
+
+gsdlInterface::corbaFilterResponse&
+gsdlInterface::corbaFilterResponse::operator=( const corbaFilterResponse& _s )
+{
+  numDocs = ((corbaFilterResponse&)_s).numDocs;
+  isApprox = ((corbaFilterResponse&)_s).isApprox;
+  termInfo = ((corbaFilterResponse&)_s).termInfo;
+  docInfo = ((corbaFilterResponse&)_s).docInfo;
+  return *this;
+}
+#endif
+
+class _Marshaller_gsdlInterface_corbaFilterResponse : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaFilterResponse _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaFilterResponse::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterResponse::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller_gsdlInterface_corbaFilterResponse::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaFilterResponse::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  return
+    dc.struct_begin() &&
+    CORBA::_stc_long->demarshal( dc, &((_MICO_T*)v)->numDocs ) &&
+    _marshaller_gsdlInterface_corbaIsApprox->demarshal( dc, &((_MICO_T*)v)->isApprox ) &&
+    _marshaller__seq_gsdlInterface_corbaTermInfo->demarshal( dc, &((_MICO_T*)v)->termInfo ) &&
+    _marshaller__seq_gsdlInterface_corbaResultDocInfo->demarshal( dc, &((_MICO_T*)v)->docInfo ) &&
+    dc.struct_end();
+}
+
+void _Marshaller_gsdlInterface_corbaFilterResponse::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  ec.struct_begin();
+  CORBA::_stc_long->marshal( ec, &((_MICO_T*)v)->numDocs );
+  _marshaller_gsdlInterface_corbaIsApprox->marshal( ec, &((_MICO_T*)v)->isApprox );
+  _marshaller__seq_gsdlInterface_corbaTermInfo->marshal( ec, &((_MICO_T*)v)->termInfo );
+  _marshaller__seq_gsdlInterface_corbaResultDocInfo->marshal( ec, &((_MICO_T*)v)->docInfo );
+  ec.struct_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaFilterResponse;
+
+
+// Stub interface corbaiface
+gsdlInterface::corbaiface::~corbaiface()
+{
+}
+
+void *gsdlInterface::corbaiface::_narrow_helper( const char *_rid )
+{
+  if( strcmp( _rid, "IDL:gsdlInterface/corbaiface:1.0" ) == 0 )
+    return (void *)this;
+  return NULL;
+}
+
+bool gsdlInterface::corbaiface::_narrow_helper2( CORBA::Object_ptr _obj )
+{
+  if( strcmp( _obj->_repoid(), "IDL:gsdlInterface/corbaiface:1.0" ) == 0) {
+    return true;
+  }
+  for( vector<CORBA::Narrow_proto>::size_type _i = 0;
+       _narrow_helpers && _i < _narrow_helpers->size(); _i++ ) {
+    bool _res = (*(*_narrow_helpers)[ _i ])( _obj );
+    if( _res )
+      return true;
+  }
+  return false;
+}
+
+gsdlInterface::corbaiface_ptr gsdlInterface::corbaiface::_narrow( CORBA::Object_ptr _obj )
+{
+  gsdlInterface::corbaiface_ptr _o;
+  if( !CORBA::is_nil( _obj ) ) {
+    void *_p;
+    if( (_p = _obj->_narrow_helper( "IDL:gsdlInterface/corbaiface:1.0" )))
+      return _duplicate( (gsdlInterface::corbaiface_ptr) _p );
+    if( _narrow_helper2( _obj ) ||
+       ( _obj->_is_a_remote( "IDL:gsdlInterface/corbaiface:1.0" ) ) ) {
+      _o = new gsdlInterface::corbaiface_stub;
+      _o->MICO_SCOPE(CORBA,Object::operator=)( *_obj );
+      return _o;
+    }
+  }
+  return _nil();
+}
+
+gsdlInterface::corbaiface_ptr
+gsdlInterface::corbaiface::_narrow( CORBA::AbstractBase_ptr _obj )
+{
+  return _narrow (_obj->_to_object());
+}
+
+gsdlInterface::corbaiface_stub::~corbaiface_stub()
+{
+}
+
+CORBA::Boolean gsdlInterface::corbaiface_stub::initialise( gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::Boolean _res;
+  CORBA::StaticAny __res( CORBA::_stc_boolean, &_res );
+
+  CORBA::StaticRequest __req( this, "initialise" );
+  __req.add_inout_arg( &_error );
+  __req.set_result( &__res );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+  return _res;
+}
+
+
+void gsdlInterface::corbaiface_stub::configure( const gsdlInterface::corbatext_t& key, const gsdlInterface::corbatext_tarray& cfgline, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _key( _marshaller_gsdlInterface_corbatext_t, &key );
+  CORBA::StaticAny _cfgline( _marshaller__seq_gsdlInterface_corbatext_t, &cfgline );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "configure" );
+  __req.add_in_arg( &_key );
+  __req.add_in_arg( &_cfgline );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::collectionList( gsdlInterface::corbatext_tarray& collist, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collist( _marshaller__seq_gsdlInterface_corbatext_t, &collist );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "collectionList" );
+  __req.add_inout_arg( &_collist );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::hasCollection( const gsdlInterface::corbatext_t& corbaCollection, CORBA::Boolean& has, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _corbaCollection( _marshaller_gsdlInterface_corbatext_t, &corbaCollection );
+  CORBA::StaticAny _has( CORBA::_stc_boolean, &has );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "hasCollection" );
+  __req.add_in_arg( &_corbaCollection );
+  __req.add_inout_arg( &_has );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::ping( const gsdlInterface::corbatext_t& collection, CORBA::Boolean& wasSuccess, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _wasSuccess( CORBA::_stc_boolean, &wasSuccess );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "ping" );
+  __req.add_in_arg( &_collection );
+  __req.add_inout_arg( &_wasSuccess );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::getDocument( const gsdlInterface::corbatext_t& collection, gsdlInterface::corbaDocRequest& request, gsdlInterface::corbaDocResponse& response, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _request( _marshaller_gsdlInterface_corbaDocRequest, &request );
+  CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaDocResponse, &response );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "getDocument" );
+  __req.add_in_arg( &_collection );
+  __req.add_inout_arg( &_request );
+  __req.add_inout_arg( &_response );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::getCollectInfo( const gsdlInterface::corbatext_t& collection, gsdlInterface::corbaColInfoResponse& response, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaColInfoResponse, &response );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "getCollectInfo" );
+  __req.add_in_arg( &_collection );
+  __req.add_inout_arg( &_response );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::getFilterInfo( const gsdlInterface::corbatext_t& collection, gsdlInterface::corbatext_tarray& filterNames, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _filterNames( _marshaller__seq_gsdlInterface_corbatext_t, &filterNames );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "getFilterInfo" );
+  __req.add_in_arg( &_collection );
+  __req.add_inout_arg( &_filterNames );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::getFilterOptions( const gsdlInterface::corbatext_t& collection, const gsdlInterface::corbatext_t& option, gsdlInterface::corbaFilterOptionsResponse& response, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _option( _marshaller_gsdlInterface_corbatext_t, &option );
+  CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaFilterOptionsResponse, &response );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "getFilterOptions" );
+  __req.add_in_arg( &_collection );
+  __req.add_in_arg( &_option );
+  __req.add_inout_arg( &_response );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+void gsdlInterface::corbaiface_stub::filter( const gsdlInterface::corbatext_t& collection, const gsdlInterface::corbaFilterRequest& request, gsdlInterface::corbaFilterResponse& response, gsdlInterface::corbaComError& error )
+{
+  CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+  CORBA::StaticAny _request( _marshaller_gsdlInterface_corbaFilterRequest, &request );
+  CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaFilterResponse, &response );
+  CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+  CORBA::StaticRequest __req( this, "filter" );
+  __req.add_in_arg( &_collection );
+  __req.add_in_arg( &_request );
+  __req.add_inout_arg( &_response );
+  __req.add_inout_arg( &_error );
+
+  __req.invoke();
+
+  mico_sii_throw( &__req, 
+    0);
+}
+
+
+#ifdef HAVE_NAMESPACE
+namespace gsdlInterface { vector<CORBA::Narrow_proto> * corbaiface::_narrow_helpers; }
+#else
+vector<CORBA::Narrow_proto> * gsdlInterface::corbaiface::_narrow_helpers;
+#endif
+
+class _Marshaller_gsdlInterface_corbaiface : public CORBA::StaticTypeInfo {
+    typedef gsdlInterface::corbaiface_ptr _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller_gsdlInterface_corbaiface::create() const
+{
+  return (StaticValueType) new _MICO_T( 0 );
+}
+
+void _Marshaller_gsdlInterface_corbaiface::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = ::gsdlInterface::corbaiface::_duplicate( *(_MICO_T*) s );
+}
+
+void _Marshaller_gsdlInterface_corbaiface::free( StaticValueType v ) const
+{
+  CORBA::release( *(_MICO_T *) v );
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller_gsdlInterface_corbaiface::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::Object_ptr obj;
+  if (!CORBA::_stc_Object->demarshal(dc, &obj))
+    return FALSE;
+  *(_MICO_T *) v = ::gsdlInterface::corbaiface::_narrow( obj );
+  CORBA::Boolean ret = CORBA::is_nil (obj) || !CORBA::is_nil (*(_MICO_T *)v);
+  CORBA::release (obj);
+  return ret;
+}
+
+void _Marshaller_gsdlInterface_corbaiface::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::Object_ptr obj = *(_MICO_T *) v;
+  CORBA::_stc_Object->marshal( ec, &obj );
+}
+
+CORBA::StaticTypeInfo *_marshaller_gsdlInterface_corbaiface;
+
+class _Marshaller__seq_gsdlInterface_corbatext_t : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbatext_t,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbatext_t::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbatext_t::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbatext_t::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbatext_t::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbatext_t->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbatext_t::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbatext_t->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbatext_t;
+
+class _Marshaller__seq_gsdlInterface_corbaFilterOption : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbaFilterOption,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbaFilterOption::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaFilterOption::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaFilterOption::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbaFilterOption::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbaFilterOption->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbaFilterOption::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbaFilterOption->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaFilterOption;
+
+class _Marshaller__seq_gsdlInterface_corbaOptionValue : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbaOptionValue,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbaOptionValue::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaOptionValue::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaOptionValue::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbaOptionValue::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbaOptionValue->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbaOptionValue::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbaOptionValue->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaOptionValue;
+
+class _Marshaller__seq_gsdlInterface_corbaTermInfo : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbaTermInfo,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbaTermInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaTermInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaTermInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbaTermInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbaTermInfo->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbaTermInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbaTermInfo->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaTermInfo;
+
+class _Marshaller__seq_gsdlInterface_corbaMetadataInfo : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbaMetadataInfo,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbaMetadataInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaMetadataInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaMetadataInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbaMetadataInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbaMetadataInfo->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbaMetadataInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbaMetadataInfo->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaMetadataInfo;
+
+class _Marshaller__seq_gsdlInterface_corbaResultDocInfo : public CORBA::StaticTypeInfo {
+    typedef SequenceTmpl<gsdlInterface::corbaResultDocInfo,MICO_TID_DEF> _MICO_T;
+  public:
+    StaticValueType create () const;
+    void assign (StaticValueType dst, const StaticValueType src) const;
+    void free (StaticValueType) const;
+    CORBA::Boolean demarshal (CORBA::DataDecoder&, StaticValueType) const;
+    void marshal (CORBA::DataEncoder &, StaticValueType) const;
+};
+
+
+CORBA::StaticValueType _Marshaller__seq_gsdlInterface_corbaResultDocInfo::create() const
+{
+  return (StaticValueType) new _MICO_T;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaResultDocInfo::assign( StaticValueType d, const StaticValueType s ) const
+{
+  *(_MICO_T*) d = *(_MICO_T*) s;
+}
+
+void _Marshaller__seq_gsdlInterface_corbaResultDocInfo::free( StaticValueType v ) const
+{
+  delete (_MICO_T*) v;
+}
+
+CORBA::Boolean _Marshaller__seq_gsdlInterface_corbaResultDocInfo::demarshal( CORBA::DataDecoder &dc, StaticValueType v ) const
+{
+  CORBA::ULong len;
+  if( !dc.seq_begin( len ) )
+    return FALSE;
+  ((_MICO_T *) v)->length( len );
+  for( CORBA::ULong i = 0; i < len; i++ ) {
+    if( !_marshaller_gsdlInterface_corbaResultDocInfo->demarshal( dc, &(*(_MICO_T*)v)[i] ) )
+      return FALSE;
+  }
+  return dc.seq_end();
+}
+
+void _Marshaller__seq_gsdlInterface_corbaResultDocInfo::marshal( CORBA::DataEncoder &ec, StaticValueType v ) const
+{
+  CORBA::ULong len = ((_MICO_T *) v)->length();
+  ec.seq_begin( len );
+  for( CORBA::ULong i = 0; i < len; i++ )
+    _marshaller_gsdlInterface_corbaResultDocInfo->marshal( ec, &(*(_MICO_T*)v)[i] );
+  ec.seq_end();
+}
+
+CORBA::StaticTypeInfo *_marshaller__seq_gsdlInterface_corbaResultDocInfo;
+
+struct __tc_init_CORBAIFACE {
+  __tc_init_CORBAIFACE()
+  {
+    _marshaller_gsdlInterface_corbatext_t = new _Marshaller_gsdlInterface_corbatext_t;
+    _marshaller_gsdlInterface_corbatext_tmap = new _Marshaller_gsdlInterface_corbatext_tmap;
+    _marshaller_gsdlInterface_corbaComError = new _Marshaller_gsdlInterface_corbaComError;
+    _marshaller_gsdlInterface_corbaShortColInfo = new _Marshaller_gsdlInterface_corbaShortColInfo;
+    _marshaller_gsdlInterface_corbaColInfoResponse = new _Marshaller_gsdlInterface_corbaColInfoResponse;
+    _marshaller_gsdlInterface_corbaDocRequest = new _Marshaller_gsdlInterface_corbaDocRequest;
+    _marshaller_gsdlInterface_corbaDocResponse = new _Marshaller_gsdlInterface_corbaDocResponse;
+    _marshaller_gsdlInterface_corbaFilterType = new _Marshaller_gsdlInterface_corbaFilterType;
+    _marshaller_gsdlInterface_corbaFilterRepeatable = new _Marshaller_gsdlInterface_corbaFilterRepeatable;
+    _marshaller_gsdlInterface_corbaFilterOption = new _Marshaller_gsdlInterface_corbaFilterOption;
+    _marshaller_gsdlInterface_corbaFilterOptionsResponse = new _Marshaller_gsdlInterface_corbaFilterOptionsResponse;
+    _marshaller_gsdlInterface_corbaOptionValue = new _Marshaller_gsdlInterface_corbaOptionValue;
+    _marshaller_gsdlInterface_corbaFilterRequest = new _Marshaller_gsdlInterface_corbaFilterRequest;
+    _marshaller_gsdlInterface_corbaTermInfo = new _Marshaller_gsdlInterface_corbaTermInfo;
+    _marshaller_gsdlInterface_corbaMetadataInfo = new _Marshaller_gsdlInterface_corbaMetadataInfo;
+    _marshaller_gsdlInterface_corbaMetadataInfo_map = new _Marshaller_gsdlInterface_corbaMetadataInfo_map;
+    _marshaller_gsdlInterface_corbaResultDocInfo = new _Marshaller_gsdlInterface_corbaResultDocInfo;
+    _marshaller_gsdlInterface_corbaIsApprox = new _Marshaller_gsdlInterface_corbaIsApprox;
+    _marshaller_gsdlInterface_corbaFilterResponse = new _Marshaller_gsdlInterface_corbaFilterResponse;
+    _marshaller_gsdlInterface_corbaiface = new _Marshaller_gsdlInterface_corbaiface;
+    _marshaller__seq_gsdlInterface_corbatext_t = new _Marshaller__seq_gsdlInterface_corbatext_t;
+    _marshaller__seq_gsdlInterface_corbaFilterOption = new _Marshaller__seq_gsdlInterface_corbaFilterOption;
+    _marshaller__seq_gsdlInterface_corbaOptionValue = new _Marshaller__seq_gsdlInterface_corbaOptionValue;
+    _marshaller__seq_gsdlInterface_corbaTermInfo = new _Marshaller__seq_gsdlInterface_corbaTermInfo;
+    _marshaller__seq_gsdlInterface_corbaMetadataInfo = new _Marshaller__seq_gsdlInterface_corbaMetadataInfo;
+    _marshaller__seq_gsdlInterface_corbaResultDocInfo = new _Marshaller__seq_gsdlInterface_corbaResultDocInfo;
+  }
+};
+
+static __tc_init_CORBAIFACE __init_CORBAIFACE;
+
Index: /gsdl/trunk/gs2common/src/corba/corbaiface_skel.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaiface_skel.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaiface_skel.mpp	(revision 16561)
@@ -0,0 +1,274 @@
+/*
+ *  MICO --- a free CORBA implementation
+ *  Copyright (C) 1997-98 Kay Roemer & Arno Puder
+ *
+ *  This file was automatically generated. DO NOT EDIT!
+ */
+#include "corbaiface.h"
+
+//--------------------------------------------------------
+//  Implementation of skeletons
+//--------------------------------------------------------
+
+gsdlInterface::corbaiface_skel::corbaiface_skel( const CORBA::BOA::ReferenceData &_id )
+{
+  CORBA::ImplementationDef_var _impl =
+    _find_impl( "IDL:gsdlInterface/corbaiface:1.0", "corbaiface" );
+  assert( !CORBA::is_nil( _impl ) );
+  _create_ref( _id,
+    0,
+    _impl,
+    "IDL:gsdlInterface/corbaiface:1.0" );
+  register_dispatcher( new StaticInterfaceDispatcherWrapper<corbaiface_skel>( this ) );
+}
+
+gsdlInterface::corbaiface_skel::corbaiface_skel( CORBA::Object_ptr _obj )
+{
+  CORBA::ImplementationDef_var _impl =
+    _find_impl( "IDL:gsdlInterface/corbaiface:1.0", "corbaiface" );
+  assert( !CORBA::is_nil( _impl ) );
+  _restore_ref( _obj,
+    CORBA::BOA::ReferenceData(),
+    0,
+    _impl );
+  register_dispatcher( new StaticInterfaceDispatcherWrapper<corbaiface_skel>( this ) );
+}
+
+gsdlInterface::corbaiface_skel::~corbaiface_skel()
+{
+}
+
+bool gsdlInterface::corbaiface_skel::dispatch( CORBA::StaticServerRequest_ptr _req, CORBA::Environment & /*_env*/ )
+{
+  #ifdef HAVE_EXCEPTIONS
+  try {
+  #endif
+    switch (mico_string_hash (_req->op_name(), 17)) {
+    case 0:
+      if( strcmp( _req->op_name(), "collectionList" ) == 0 ) {
+        corbatext_tarray collist;
+        CORBA::StaticAny _collist( _marshaller__seq_gsdlInterface_corbatext_t, &collist );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_inout_arg( &_collist );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        collectionList( collist, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 3:
+      if( strcmp( _req->op_name(), "ping" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        CORBA::Boolean wasSuccess;
+        CORBA::StaticAny _wasSuccess( CORBA::_stc_boolean, &wasSuccess );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_inout_arg( &_wasSuccess );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        ping( collection, wasSuccess, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 7:
+      if( strcmp( _req->op_name(), "filter" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        corbaFilterRequest request;
+        CORBA::StaticAny _request( _marshaller_gsdlInterface_corbaFilterRequest, &request );
+        corbaFilterResponse response;
+        CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaFilterResponse, &response );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_in_arg( &_request );
+        _req->add_inout_arg( &_response );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        filter( collection, request, response, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 9:
+      if( strcmp( _req->op_name(), "getFilterOptions" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        corbatext_t option;
+        CORBA::StaticAny _option( _marshaller_gsdlInterface_corbatext_t, &option );
+        corbaFilterOptionsResponse response;
+        CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaFilterOptionsResponse, &response );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_in_arg( &_option );
+        _req->add_inout_arg( &_response );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        getFilterOptions( collection, option, response, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 10:
+      if( strcmp( _req->op_name(), "getCollectInfo" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        corbaColInfoResponse response;
+        CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaColInfoResponse, &response );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_inout_arg( &_response );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        getCollectInfo( collection, response, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 11:
+      if( strcmp( _req->op_name(), "initialise" ) == 0 ) {
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        CORBA::Boolean _res;
+        CORBA::StaticAny __res( CORBA::_stc_boolean, &_res );
+        _req->add_inout_arg( &_error );
+        _req->set_result( &__res );
+
+        if( !_req->read_args() )
+          return true;
+
+        _res = initialise( error );
+        _req->write_results();
+        return true;
+      }
+      if( strcmp( _req->op_name(), "getFilterInfo" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        corbatext_tarray filterNames;
+        CORBA::StaticAny _filterNames( _marshaller__seq_gsdlInterface_corbatext_t, &filterNames );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_inout_arg( &_filterNames );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        getFilterInfo( collection, filterNames, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 12:
+      if( strcmp( _req->op_name(), "hasCollection" ) == 0 ) {
+        corbatext_t corbaCollection;
+        CORBA::StaticAny _corbaCollection( _marshaller_gsdlInterface_corbatext_t, &corbaCollection );
+        CORBA::Boolean has;
+        CORBA::StaticAny _has( CORBA::_stc_boolean, &has );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_corbaCollection );
+        _req->add_inout_arg( &_has );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        hasCollection( corbaCollection, has, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    case 13:
+      if( strcmp( _req->op_name(), "configure" ) == 0 ) {
+        corbatext_t key;
+        CORBA::StaticAny _key( _marshaller_gsdlInterface_corbatext_t, &key );
+        corbatext_tarray cfgline;
+        CORBA::StaticAny _cfgline( _marshaller__seq_gsdlInterface_corbatext_t, &cfgline );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_key );
+        _req->add_in_arg( &_cfgline );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        configure( key, cfgline, error );
+        _req->write_results();
+        return true;
+      }
+      if( strcmp( _req->op_name(), "getDocument" ) == 0 ) {
+        corbatext_t collection;
+        CORBA::StaticAny _collection( _marshaller_gsdlInterface_corbatext_t, &collection );
+        corbaDocRequest request;
+        CORBA::StaticAny _request( _marshaller_gsdlInterface_corbaDocRequest, &request );
+        corbaDocResponse response;
+        CORBA::StaticAny _response( _marshaller_gsdlInterface_corbaDocResponse, &response );
+        corbaComError error;
+        CORBA::StaticAny _error( _marshaller_gsdlInterface_corbaComError, &error );
+
+        _req->add_in_arg( &_collection );
+        _req->add_inout_arg( &_request );
+        _req->add_inout_arg( &_response );
+        _req->add_inout_arg( &_error );
+
+        if( !_req->read_args() )
+          return true;
+
+        getDocument( collection, request, response, error );
+        _req->write_results();
+        return true;
+      }
+      break;
+    }
+  #ifdef HAVE_EXCEPTIONS
+  } catch( CORBA::SystemException_catch &_ex ) {
+    _req->set_exception( _ex->_clone() );
+    _req->write_results();
+    return true;
+  } catch( ... ) {
+    assert( 0 );
+    return true;
+  }
+  #endif
+  return false;
+}
+
+gsdlInterface::corbaiface_ptr gsdlInterface::corbaiface_skel::_this()
+{
+  return gsdlInterface::corbaiface::_duplicate( this );
+}
+
Index: /gsdl/trunk/gs2common/src/corba/corbaproto.h
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaproto.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaproto.h	(revision 16561)
@@ -0,0 +1,101 @@
+/**********************************************************************
+ *
+ * corbaproto.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.
+ *
+ *********************************************************************/
+
+
+#ifndef CORBAPROTO_H
+#define CORBAPROTO_H
+
+#include "gsdlconf.h"
+#include "collectserver.h"
+#include "recptproto.h"
+#include "corbaiface.h"
+using namespace gsdlInterface;
+
+class corbaproto : public recptproto {
+ private:
+  corbaiface_var getCorbaClient();
+ protected:
+  collectservermapclass cservers;
+  corbaiface_var        client;
+  CORBA::ORB_var        orb;
+  CORBA::BOA_var        boa;
+  CORBA::Object_var     obj;
+  text_t gsdlhome;
+  text_t site_name;
+
+ public:
+  virtual ~corbaproto() {}
+  corbaproto(char* site_name);
+
+  // add_collectserver should be called for each collection server
+  // before any configuration is done
+  void add_collectserver (collectserver *thecserver) 
+    {cservers.addcollectserver(thecserver);}
+
+  // this configure will configure each of the collection servers
+  void configure (const text_t &key, const text_tarray &cfgline, comerror_t &err);
+
+  // this init will init each of the collection servers
+  bool init (comerror_t &err,ostream &logout);
+
+  // used to check for icons and retrieve them from the collection
+  // site if they are missing
+  void  cache_missing_icons(text_tmap& format_map,
+			    text_t& httpdomain, 
+			    text_t& httpprefix);
+
+  text_t get_site_name (comerror_t &err);
+
+  text_t get_protocol_name (comerror_t &err);
+  void get_collection_list (text_tarray &collist, comerror_t &err, 
+			    ostream &logout);
+  void has_collection (const text_t &collection, bool &hascollection, 
+		       comerror_t &err, ostream &logout);
+  void ping (const text_t &collection, bool &wassuccess, 
+	     comerror_t &err, ostream &logout);
+  void get_collectinfo (const text_t &collection, 
+			ColInfoResponse_t &collectinfo,
+			comerror_t &err, ostream &logout);
+  void get_filterinfo (const text_t &collection,
+		       InfoFiltersResponse_t &response,
+		       comerror_t &err, ostream &logout);
+  void get_filteroptions (const text_t &collection,
+			  const InfoFilterOptionsRequest_t &request,
+			  InfoFilterOptionsResponse_t &response, 
+			  comerror_t &err, ostream &logout);
+  void filter (const text_t &collection,
+	       FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+
+  void get_document (const text_t &collection,
+		     const DocumentRequest_t &request,
+		     DocumentResponse_t &response,
+		     comerror_t &err, ostream &logout);
+
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/corba/corbaproto.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbaproto.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbaproto.mpp	(revision 16561)
@@ -0,0 +1,750 @@
+/**********************************************************************
+ *
+ * corbaproto.cpp -- 
+ * Copyright (C) 2000 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$
+ *
+ *********************************************************************/
+
+#include <iostream.h>
+#include <fstream.h>
+
+#include "fileutil.h"
+
+// protocol headers for CORBA
+#include "corbaiface.h"
+using namespace gsdlInterface;
+
+// greenstone library headers
+#include "corbaconv_text_t.h"
+
+// greenstone local headers
+#include "corbatext_t.h"
+
+#include "corbaproto.h"
+#include "colservrconfig.h"
+
+// default headers
+#include <assert.h>
+
+void corbatext_optionValueArrayToCorba(OptionValue_tarray array, corbaOptionValue_array &corba)
+{
+  unsigned int i = 0;
+  OptionValue_tarray::iterator here = array.begin();
+  OptionValue_tarray::iterator end = array.end();
+  
+  while (here != end)
+    {
+      corba.length(i+1);
+      
+      corbaconv_text_t::getCorbatext(here->name,corba[i].name);
+      corbaconv_text_t::getCorbatext(here->value,corba[i].value);
+
+      i ++;
+      here ++;
+    }
+  //  cout << "Options " << i << endl;
+}
+
+void corbaresponse_resultMetadataToCorba(MetadataInfo_t &meta, corbaMetadataInfo &corbaMeta)
+{
+  corbaconv_text_t::getCorbatext(meta.params,corbaMeta.params);
+  corbatext_arrayToCorbaArray(meta.values, &corbaMeta.values);
+  corbaMeta.isRef    = meta.isRef;
+}
+
+void corbaresponse_resultMetadataToC(const corbaMetadataInfo &corbaMeta, MetadataInfo_t &meta)
+{
+  corbaconv_text_t::setCorbatext(meta.params,corbaMeta.params);
+  corbatext_corbaArrayToArray(corbaMeta.values, &meta.values);
+  meta.isRef    = corbaMeta.isRef;
+}
+
+int corbaresponse_resultMetaParentToCorba(MetadataInfo_t *parent, corbaMetadataInfo_array &corbaparent)
+{
+  int reply = -1, pid = -1;
+
+  // extend for new parent
+  reply = corbaparent.length();
+  corbaparent.length(reply+1);
+
+  // copy content into new array element
+  corbaresponse_resultMetadataToCorba(*parent, corbaparent[reply]);
+
+  // deal with our own parent
+  if (parent->parent != NULL)
+    {
+      // get offset of new parent and extend parent vector
+
+      pid  = corbaresponse_resultMetaParentToCorba(parent->parent, corbaparent);
+    }
+  corbaparent[reply].parentid = pid;
+
+  // return the parent id from here
+  return reply;
+}
+
+MetadataInfo_t *corbaresponse_resultMetaParentToC(corbaMetadataInfo_array &corbaparent, int pid)
+{
+  MetadataInfo_t *reply;
+
+  reply = new MetadataInfo_t;
+
+  corbaresponse_resultMetadataToC(corbaparent[pid], *reply);
+
+  // deal with our own parent
+  if (corbaparent[pid].parentid >= 0)
+    {
+      // get offset of new parent and extend parent vector
+
+      reply->parent = corbaresponse_resultMetaParentToC(corbaparent, corbaparent[pid].parentid);
+    }
+  else
+    {
+      reply->parent = NULL;
+    }
+
+  // return the parent id from here
+  return reply;
+}
+
+void corbaresponse_resultDocInfoToCorba(ResultDocInfo_tarray docinfo, corbaResultDocInfo_array &corbainfo)
+{
+  ResultDocInfo_tarray::iterator here = docinfo.begin();
+  ResultDocInfo_tarray::iterator end  = docinfo.end();
+  unsigned int i;
+
+  i = 0;
+  while (here != end)
+    {
+      corbainfo.length(i+1);
+
+      // copy the easy bits
+      corbaconv_text_t::getCorbatext(here->OID,corbainfo[i].OID);
+
+      corbainfo[i].resultNum     = here->result_num;
+      corbainfo[i].ranking       = here->ranking;
+      corbainfo[i].termsMatched  = here->num_terms_matched;
+      corbainfo[i].phraseMatched = here->num_phrase_match;
+
+      // copy the docFreq array
+      vector<int>::iterator fhere = here->docFreq.begin();
+      vector<int>::iterator fend  = here->docFreq.end();
+      unsigned int          fi = 0;
+
+      while (fhere != fend)
+	{
+	  corbainfo[i].docFreq.length(fi+1);
+
+	  corbainfo[i].docFreq[fi] = *fhere;
+
+	  fhere ++;
+	  fi ++;
+	}
+
+      // copy the MetadataInfo map
+      MetadataInfo_tmap::iterator mhere = here->metadata.begin();
+      MetadataInfo_tmap::iterator mend  = here->metadata.end();
+      unsigned int mi = 0;
+
+      while (mhere != mend)
+	{
+	  corbainfo[i].metadata.names.length(mi+1);
+	  corbainfo[i].metadata.values.length(mi+1);
+	  
+	  // copy name
+	  corbaconv_text_t::getCorbatext((*mhere).first,corbainfo[i].metadata.names[mi]);
+
+	  // copy metadata
+	  corbaresponse_resultMetadataToCorba((*mhere).second, corbainfo[i].metadata.values[mi]);
+	  if (mhere->second.parent != NULL)
+	    {
+	      int pid;
+
+	      pid = corbaresponse_resultMetaParentToCorba(mhere->second.parent, 
+							      corbainfo[i].metaparents);
+	      corbainfo[i].metadata.values[mi].parentid = pid;
+	    }
+	  else
+	    {
+	      corbainfo[i].metadata.values[mi].parentid = -1;
+	    }
+
+	  cout << "Parent " << mi << " " << (int) &mhere->second << " " << (int) mhere->second.parent << endl;
+
+	  mhere ++;
+	  mi ++;
+	}
+
+      // Copy the classifier variables
+      corbaconv_text_t::getCorbatext(here->classifier_metadata_type, corbainfo[i].classifierMetadataType);
+      corbainfo[i].classifierMetadataOffset = here->classifier_metadata_offset;
+
+      // Get next Docinfo item
+      i++;
+      here ++;
+    }
+}
+
+void corbaresponse_resultDocInfoToC(corbaResultDocInfo_array corbainfo, ResultDocInfo_tarray &docinfo)
+{
+  unsigned int i;
+  ResultDocInfo_t doc;
+
+  for (i = 0; i < corbainfo.length(); i++)
+    {
+      corbaconv_text_t::setCorbatext(doc.OID,corbainfo[i].OID);
+      doc.result_num        = corbainfo[i].resultNum;
+      doc.ranking           = corbainfo[i].ranking;
+      doc.num_terms_matched = corbainfo[i].termsMatched;
+      doc.num_phrase_match  = corbainfo[i].phraseMatched;
+
+      // copy the doc freq array
+      unsigned int fi;
+      for (fi = 0; fi < corbainfo[i].docFreq.length(); fi ++)
+	{
+	  doc.docFreq.push_back(corbainfo[i].docFreq[fi]);
+	}
+
+      // copy the MetadataInfomap
+      unsigned int   mi;
+      text_t         mname;
+      MetadataInfo_t mdata;
+      
+      doc.metadata.clear();
+      for (mi = 0; mi < corbainfo[i].metadata.names.length(); mi ++)
+	{
+	  // get name
+	  corbaconv_text_t::setCorbatext(mname,corbainfo[i].metadata.names[mi]);
+
+	  // get metadata
+	  corbaresponse_resultMetadataToC(corbainfo[i].metadata.values[mi], mdata);
+
+	  if (corbainfo[i].metadata.values[mi].parentid >= 0)
+	    {
+	      mdata.parent = 
+		corbaresponse_resultMetaParentToC(corbainfo[i].metaparents, 
+						  corbainfo[i].metadata.values[mi].parentid);	      
+	    }
+	 
+	  doc.metadata.insert(make_pair(mname, mdata));
+	}
+
+      // GRB: sort out parent data
+      for (mi = 0; mi < corbainfo[i].metadata.names.length(); mi ++)
+	{
+	  if (corbainfo[i].metadata.values[mi].parentid != 0)
+	    { 
+	      int pi;
+
+	      for (pi = 0; pi < corbainfo[i].metadata.names.length(); pi ++)
+		{
+		  if (pi != mi &&
+		      corbainfo[i].metadata.values[mi].parentid ==
+		      corbainfo[i].metadata.values[pi].id)
+		    {
+		      doc.metadata[mi].parent = &doc.metadata[pi];
+		    }
+		}
+	    }
+	}
+      //      doc.metadata
+
+      // convert metadata classifer information
+      corbaconv_text_t::setCorbatext(doc.classifier_metadata_type,corbainfo[i].classifierMetadataType);
+      doc.classifier_metadata_offset = corbainfo[i].classifierMetadataOffset;
+
+      docinfo.push_back(doc); 
+    }
+}
+
+
+// Added 31/1/2001, Michael Dewsnip
+void corbaresponse_resultTermInfoToC(corbaTermInfo_array corbainfo, TermInfo_tarray &terminfo)
+{
+  TermInfo_t ti;
+
+  for (unsigned int i = 0; i < corbainfo.length(); i++) {
+    corbaconv_text_t::setCorbatext(ti.term, corbainfo[i].term);
+    ti.freq = corbainfo[i].frequency;
+
+    for (unsigned int j = 0; j < corbainfo[i].matchTerms.length(); j++) {
+      text_t mt;
+      corbaconv_text_t::setCorbatext(mt, corbainfo[i].matchTerms[j]);
+
+      ti.matchTerms.push_back(mt);
+    }
+
+    terminfo.push_back(ti);
+  }
+}
+
+
+corbaproto::corbaproto(char* _site_name)
+{ char *dummyv[1] = {""};
+  int   dummyc = 0;
+  // CORBA variables
+  /* static CORBA::ORB_var orb = CORBA::ORB_init( dummyc , dummyv, "mico-local-orb" );
+     static CORBA::BOA_var boa = orb->BOA_init( dummyc , dummyv, "mico-local-boa" ); */
+
+  CORBA::ORB_var orb = CORBA::ORB_init( dummyc , dummyv, "mico-local-orb" );
+  CORBA::BOA_var boa = orb->BOA_init( dummyc , dummyv, "mico-local-boa" );
+
+  char objid_filename[256];
+  sprintf(objid_filename,"/tmp/%s.objid",_site_name);
+
+  // Get ref from "naming service"
+  ifstream in (objid_filename);
+  char ref[1000];
+  in >> ref;
+  in.close ();
+
+  // startup corba
+  // static CORBA::Object_var obj = orb->string_to_object (ref);
+  CORBA::Object_var obj = orb->string_to_object (ref);
+
+  client = corbaiface::_narrow( obj ); 
+  // client = NULL; // ****
+
+  gsdlhome.clear();
+  site_name = _site_name;
+}
+
+corbaiface_var corbaproto::getCorbaClient()
+{
+  // This function was written to help corba when ORB and BOA were
+  // not working in "persistent" mode.
+  // Now that persistent mode is working it is not longer needed/used.
+
+  char *dummyv[1] = {""};
+  int   dummyc = 0;
+  // CORBA variables
+//  static CORBA::ORB_var orb = CORBA::ORB_init( dummyc , dummyv, "mico-local-orb" );
+//  static CORBA::BOA_var boa = orb->BOA_init( dummyc , dummyv, "mico-local-boa" );
+
+  // DB // Try this out!! // ****
+  CORBA::ORB_var orb = CORBA::ORB_init( dummyc , dummyv, "mico-local-orb" );
+  CORBA::BOA_var boa = orb->BOA_init( dummyc , dummyv, "mico-local-boa" );
+
+  /*  char* site_name_chars = site_name.getcstr();
+  char objid_filename[256];
+  sprintf(objid_filename,"/tmp/%s.objid",site_name_chars);
+  // delete site_name_chars; */
+
+  text_t objid_filename = "/tmp/" + site_name + ".objid";
+
+  // Get ref from "naming service"
+  ifstream in (objid_filename.getcstr());
+  char ref[1000];
+  in >> ref;
+  in.close ();
+
+  // startup corba
+  // DB // Try this out!! // ****
+  // static CORBA::Object_var obj = orb->string_to_object (ref);
+  CORBA::Object_var obj = orb->string_to_object (ref);
+
+  return corbaiface::_narrow( obj );
+}
+
+
+// this configure will configure each of the collection servers
+void corbaproto::configure (const text_t &key, const text_tarray &cfgline,
+			    comerror_t &err) {
+  // the naming of the collection should not be done here,
+  // it should be done just after the collection server has been 
+  // created
+
+  // cout << "Corbaproto::Configure" << endl;
+  corbatext_t      corbaKey;
+  corbatext_tarray corbaCfgline;
+  gsdlInterface::corbaComError error = corbaNoError;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(key,corbaKey);
+  corbatext_arrayToCorbaArray(cfgline, &corbaCfgline);
+
+  // execute the corba transaction
+  lclient->configure(corbaKey, corbaCfgline, error);    
+
+  if (key=="gsdlhome")
+    {
+      // store gsdlhome in corba prototype so it can be used later to
+      // check if icons specified in format statements exist on the
+      // recptionist's side
+      gsdlhome = cfgline[0];
+    }
+  err = (comerror_t) error;
+}
+
+// this init will configure and init each of the collection servers
+bool corbaproto::init (comerror_t &err, ostream &logout) {
+//  cout << "Corbaproto::Init" << endl;
+  gsdlInterface::corbaComError error = corbaNoError;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // execute the corba transaction
+  return lclient->initialise(error);
+}
+
+void  corbaproto::cache_missing_icons(text_tmap& format_map,
+				      text_t& httpdomain, 
+				      text_t& httpprefix)
+{
+  // consider making this a member function in the object?
+
+  text_tmap::iterator format_here = format_map.begin();
+  text_tmap::iterator format_end = format_map.end();
+  while (format_here!=format_end)
+    {
+      text_t format_line = format_here->second;
+
+      // cout << "*** format line = " << format_line << endl; // ****
+
+      text_t::iterator fl_begin = format_line.begin();
+      text_t::iterator fl_end = format_line.end();
+
+      text_t::iterator httpimg_find = findword(fl_begin,fl_end,"_httpimg_");
+      text_t::iterator http_find = httpimg_find;
+
+      while (httpimg_find!=fl_end)
+	{
+	  http_find = httpimg_find+5;
+	  httpimg_find += 9;
+
+	  // look for white space " or >
+	  text_t::iterator filename_end = httpimg_find;
+	  while (filename_end != fl_end)
+	    {
+	      if (*filename_end == ' ') break;
+	      if (*filename_end == '\t') break;
+	      if (*filename_end == '\"') break;
+	      if (*filename_end == '>') break;
+	      filename_end++;
+	    }
+
+	  text_t img_filename = substr(httpimg_find,filename_end);
+
+	  text_t site_img_filename = filename_cat(gsdlhome,"images",img_filename);
+
+	  // look to see if img filename exists in images directory
+	  if (!file_exists(site_img_filename))
+	    {
+	      text_t cache_img_dir
+		= filename_cat(gsdlhome,"images",".cache");
+
+	      text_t remote_img_url = "http://" + httpdomain + httpprefix
+		+ "/images" + img_filename;
+
+	      text_t wget_cmd = "wget -q -N --directory-prefix="+cache_img_dir
+		+ " " + remote_img_url;
+
+	      char* wget_cmd_chars = wget_cmd.getcstr();
+
+	      system(wget_cmd_chars);
+	      delete wget_cmd_chars;
+
+	      format_map[format_here->first] 
+		= substr(fl_begin,http_find) + "cache" + substr(http_find,fl_end);
+
+	    }
+
+	  httpimg_find = findword(httpimg_find,fl_end,"_httpimg_");
+	}
+      format_here++;
+    }
+}
+
+text_t corbaproto::get_site_name (comerror_t &err) {
+  return site_name;
+}
+
+text_t corbaproto::get_protocol_name (comerror_t &err) {
+  return "corbaproto";
+}
+
+
+void corbaproto::get_collection_list (text_tarray &collist, comerror_t &err, 
+				     ostream &/*logout*/) {
+
+  // cout << "Corbaproto::Collection list" << endl;
+
+  corbatext_tarray corba_collist;
+  text_tarray      tcollist;
+  gsdlInterface::corbaComError error = corbaNoError;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // execute the corba transaction
+  lclient->collectionList(corba_collist,error);
+
+  // convert the response back to normal form
+  corbatext_corbaArrayToArray(corba_collist, &tcollist);
+  collist = tcollist;
+
+  err = (comerror_t) error;
+}
+
+void corbaproto::has_collection (const text_t &collection, bool &hascollection, 
+				comerror_t &err, ostream &/*logout*/) {
+  // cout << "Corbaproto::Has collection" << endl;
+
+  corbatext_t     corbaCollection;
+  corbaComError corbaError = corbaNoError;
+  CORBA::Boolean corbaHas;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollection);
+  
+  // execute the corba transaction
+  lclient->hasCollection(corbaCollection, corbaHas, corbaError);
+
+  // convert the response back to normal form
+  hascollection = (corbaHas != 0);
+  err = noError;
+}
+
+void corbaproto::ping (const text_t &collection, bool &wassuccess, 
+		      comerror_t &err, ostream &/*logout*/) {
+  // cout << "Corbaproto::Ping" << endl;
+
+  corbatext_t corbaCollect;
+  corbaComError corbaError = corbaNoError;
+  CORBA::Boolean success;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollect);
+
+  // execute the corba transaction
+  lclient->ping(corbaCollect, success, corbaError);
+  
+  // convert the response back to normal form
+  wassuccess = (success != 0);
+  err = (comerror_t) corbaError;  
+  cout << "Corbaproto::Ping" << err << endl;
+}
+
+
+void corbaproto::get_collectinfo (const text_t &collection, 
+				 ColInfoResponse_t &collectinfo,
+				 comerror_t &err, ostream &logout) {
+  //  cout << "Corbaproto::Collectinfo" << endl;
+
+  corbatext_t          corbaCollection;
+  corbaComError        corbaError = corbaNoError;
+  corbaColInfoResponse corbaCollectInfo;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollection);
+
+  // execute the corba transaction
+  lclient->getCollectInfo(corbaCollection, corbaCollectInfo, corbaError);
+
+  // convert the response back to normal form
+  collectinfo.useBook = corbaCollectInfo.useBook;
+  collectinfo.isPublic = corbaCollectInfo.isPublic;
+  collectinfo.isBeta   = corbaCollectInfo.isBeta;
+  collectinfo.buildDate = corbaCollectInfo.buildDate;
+  corbatext_corbaArrayToArray(corbaCollectInfo.ccsCols, &collectinfo.ccsCols);
+  corbatext_corbaArrayToArray(corbaCollectInfo.languages, &collectinfo.languages);
+  collectinfo.numDocs   = corbaCollectInfo.numDocs;
+  collectinfo.numWords  = corbaCollectInfo.numWords;
+  collectinfo.numBytes  = corbaCollectInfo.numBytes;
+  corbatext_corbaMapToMap(corbaCollectInfo.collectionMeta, collectinfo.collectionmeta);
+  corbatext_corbaMapToMap(corbaCollectInfo.format, collectinfo.format);
+  corbatext_corbaMapToMap(corbaCollectInfo.building, collectinfo.building);
+  corbaconv_text_t::setCorbatext(collectinfo.httpdomain,corbaCollectInfo.httpdomain);
+  corbaconv_text_t::setCorbatext(collectinfo.httpprefix,corbaCollectInfo.httpprefix);
+  corbaconv_text_t::setCorbatext(collectinfo.receptionist,corbaCollectInfo.receptionist);
+
+  cache_missing_icons(collectinfo.format,collectinfo.httpdomain,
+		      collectinfo.httpprefix);
+
+  err = (comerror_t) corbaError;  
+}
+
+
+void corbaproto::get_filterinfo (const text_t &collection,
+				InfoFiltersResponse_t &response,
+				comerror_t &err, ostream &logout) {
+  //  cout << "Corbaproto::Filterinfo" << endl;
+
+  corbatext_t corbaCollection;
+  corbatext_tset corbaResponse;
+  corbaComError corbaError = corbaNoError;
+  
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollection);
+
+  // execute the corba transaction
+  lclient->getFilterInfo(corbaCollection, corbaResponse, corbaError);
+
+  // convert the response back to normal form
+  corbatext_corbaArrayToSet(corbaResponse, &response.filterNames);
+
+  err = (comerror_t) corbaError;
+}
+
+void corbaproto::get_filteroptions (const text_t &collection,
+				   const InfoFilterOptionsRequest_t &request,
+				   InfoFilterOptionsResponse_t &response, 
+				   comerror_t &err, ostream &logout) {
+  //  cout << "Corbaproto::Filteroptions" << endl;
+
+  corbatext_t corbaCollection;
+  corbatext_t filterName;
+  corbaFilterOptionsResponse corbaResponse;
+  corbaComError corbaError = corbaNoError;
+  unsigned int i;
+  corbaconv_text_t *cc_name;
+  FilterOption_t *option;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollection);
+  corbaconv_text_t::getCorbatext(request.filterName,filterName);
+
+  // execute the corba transaction
+  lclient->getFilterOptions(corbaCollection, filterName, corbaResponse, corbaError);
+
+  // convert the response back to normal form
+  for (i = 0; i < corbaResponse.names.length(); i ++)
+    {
+      cc_name = new corbaconv_text_t(corbaResponse.names[i]);
+
+      option = new FilterOption_t;
+      corbaconv_text_t::setCorbatext(option->name,corbaResponse.options[i].name);
+      option->type = (FilterOption_t::type_t) corbaResponse.options[i].type;
+      option->repeatable = (FilterOption_t::repeatable_t) corbaResponse.options[i].repeatable;
+      corbaconv_text_t::setCorbatext(option->defaultValue,corbaResponse.options[i].defaultValue);
+      corbatext_corbaArrayToArray(corbaResponse.options[i].validValues, &option->validValues);
+
+      response.filterOptions.insert(make_pair(*cc_name, *option)); // typecast to text_t
+    }
+
+  err =  (comerror_t) corbaError;
+}
+
+
+void corbaproto::filter (const text_t &collection,
+			  FilterRequest_t &request,
+			  FilterResponse_t &response,
+			  comerror_t &err, ostream &logout) {
+  //  cout << "Corbaproto::Filter" << endl;
+
+  corbaFilterRequest corbaRequest;
+  corbaFilterResponse corbaResponse;
+  corbatext_t     corbaCollection;
+  corbaComError corbaError = corbaNoError;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollection);
+  corbaconv_text_t::getCorbatext(request.filterName,corbaRequest.filter);
+  corbatext_optionValueArrayToCorba(request.filterOptions, corbaRequest.filterOptions);
+  corbatext_arrayToCorbaArray(request.docSet, &corbaRequest.docSet);
+  corbaRequest.filterResultOptions = request.filterResultOptions;
+  corbaconv_text_t::getCorbatext(request.requestParams,corbaRequest.requestParams);
+  corbaconv_text_t::getCorbatext(request.refParams,corbaRequest.refParams);
+  corbaRequest.getParents = request.getParents;
+  corbatext_setToCorbaArray(request.fields, &corbaRequest.fields);
+
+  corbaResponse.numDocs = response.numDocs;
+  corbaresponse_resultDocInfoToCorba(response.docInfo, corbaResponse.docInfo);
+
+  // execute the corba transaction
+  lclient->filter (corbaCollection, corbaRequest, corbaResponse, corbaError);
+
+  // convert the response back to normal form
+  response.numDocs = corbaResponse.numDocs;
+  response.isApprox = (enum isapprox) corbaResponse.isApprox;
+  corbaresponse_resultDocInfoToC(corbaResponse.docInfo, response.docInfo);
+
+  // Added 31/1/2001, Michael Dewsnip
+  corbaresponse_resultTermInfoToC(corbaResponse.termInfo, response.termInfo);
+
+  err = (comerror_t) corbaError;
+  //  cout << "Corbaproto::Filter" << endl;
+}
+
+
+void corbaproto::get_document (const text_t &collection,
+			       const DocumentRequest_t &request,
+			       DocumentResponse_t &response,
+			       comerror_t &err, ostream &logout) {
+  //  cout << "Corbaproto::Get Document" << endl;
+
+  corbatext_t      corbaCollect;
+  corbaDocRequest  corbaRequest;
+  corbaDocResponse corbaResponse;
+  corbaComError    corbaError = corbaNoError;
+
+  // get the corba client reference
+  // corbaiface_var lclient = this->getCorbaClient(); // ****
+  corbaiface_var lclient = client;
+
+  // convert all the requisite structures into their CORBA form
+  corbaconv_text_t::getCorbatext(collection,corbaCollect);
+  corbaconv_text_t::getCorbatext(request.OID,corbaRequest.OID);
+  corbaconv_text_t::getCorbatext(request.docType,corbaRequest.docType);
+  corbaconv_text_t::getCorbatext(request.docFormat,corbaRequest.docFormat);
+
+  // execute the corba transaction
+  lclient->getDocument(corbaCollect, corbaRequest, corbaResponse, corbaError);
+
+  // convert the response back to normal form
+  corbaconv_text_t::setCorbatext(response.doc,corbaResponse.doc);
+
+  err = (comerror_t) corbaError;
+}
+
Index: /gsdl/trunk/gs2common/src/corba/corbatext_t.h
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbatext_t.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbatext_t.h	(revision 16561)
@@ -0,0 +1,13 @@
+void corbatext_fillChar(corbatext_t *t, char *text);
+void corbatext_usvector(corbatext_t t, vector<unsigned short>& us);
+char *corbatext_string(corbatext_t t);
+#ifdef TEXT_T_H
+void corbatext_arrayToCorbaArray(text_tarray t, corbatext_tarray *ca);
+void corbatext_corbaArrayToArray(corbatext_tarray ca, text_tarray *ta);
+void corbatext_mapToCorbaMap(text_tmap tm, corbatext_tmap *cm);
+void corbatext_corbaMapToMap(corbatext_tmap cm, text_tmap &tm);
+void corbatext_setToCorbaArray(text_tset set, corbatext_tset *cset);
+void corbatext_corbaArrayToSet(corbatext_tset cset, text_tset *set);
+corbatext_t_var corbatext_corbatext(text_t t);
+int corbatext_corbatext(text_t t, corbatext_t_var ct);
+#endif
Index: /gsdl/trunk/gs2common/src/corba/corbatext_t.mpp
===================================================================
--- /gsdl/trunk/gs2common/src/corba/corbatext_t.mpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/corba/corbatext_t.mpp	(revision 16561)
@@ -0,0 +1,179 @@
+// standard headers
+#if defined(GSDL_USE_STL_H)
+#include <iostream.h>
+#include <fstream.h>
+#include <vector.h>
+#else
+#include <iostream>
+#include <fstream>
+#include <vector>
+#endif
+
+// protocol headers
+#include "corbaiface.h"
+using namespace gsdlInterface;
+
+// greenstone headers
+#include "corbaconv_text_t.h"
+
+//local headers
+#include "corbatext_t.h"
+
+void corbatext_fillChar(corbatext_t *t, char *text)
+{
+  unsigned int i;
+
+  t->text.length(0);
+  if (text == NULL)
+      {
+	  return;
+      }
+  for (i = 0; i < strlen(text); i ++)
+      {
+	  t->text[i] = text[i];
+      }  
+  t->encoding = 0;
+}
+
+void corbatext_usvector(corbatext_t t, usvector& us)
+{
+  unsigned int i;
+
+  for (i = 0; i < t.text.length(); i ++)
+    {
+      us.push_back(t.text[i]);
+    }
+}
+
+char *corbatext_string(corbatext_t t)
+{ 
+  char *lcopy;
+  unsigned int i;
+    lcopy = (char *) malloc(t.text.length() + 1);
+    for (i = 0; i < t.text.length(); i ++)
+    {
+      lcopy[i] = (char) t.text[i];
+    }
+    lcopy[i] = '\0';
+    return lcopy;
+}
+
+corbatext_t_var corbatext_corbatext(text_t t)
+{ corbatext_t_var ct;
+// usString_var    us;
+
+  // cout << "Making text\n";
+  ct = new corbatext_t;
+  //  ct->text.length(20);
+  // us = new usString(20);
+  //  cout << ct->text.length() << endl;
+  //  cout << ct->text.maximum() << endl;
+  corbaconv_text_t::fillUsString(t,&ct->text);
+  //t.fillUsString(us);
+  // delete us;
+  // cout << "Encoding text\n";
+  ct->encoding = 0;
+  // cout << "Encoding text\n";
+  return ct;
+}
+
+int corbatext_corbatext(text_t t, corbatext_t_var ct)
+{
+  cout << "A" << endl;
+  corbaconv_text_t::fillUsString(t,&ct->text);
+  cout << "B" << endl;
+  ct->encoding = 0;
+  cout << "C" << endl;
+}
+
+void corbatext_corbaArrayToArray(corbatext_tarray ca, text_tarray *ta)
+{
+  unsigned int i;
+  corbaconv_text_t *cct;
+
+  ta->clear();
+  ta->reserve(ca.length());
+  for (i = 0; i < ca.length(); i ++)
+    {
+      cct = new corbaconv_text_t(ca[i]);
+      ta->push_back(*cct); // cast to text_t
+    }  
+}
+
+void corbatext_arrayToCorbaArray(text_tarray ta, corbatext_tarray *_ca)
+{
+  text_tarray::iterator here = ta.begin();
+  text_tarray::iterator end = ta.end();
+  corbatext_tarray ca(end - here);
+  unsigned int i;
+
+  i = 0;
+  (*_ca).length(end - here);
+
+  while (here != end)
+    {
+      corbatext_t_var ptr;
+      //      cout << "Array: " << (*_ca).maximum() << " " << (*_ca).length() << endl;
+      ptr = corbatext_corbatext(*here);
+      (*_ca)[i] = ptr;
+      here ++;
+      i++;      
+    }
+}
+
+void corbatext_corbaArrayToSet(corbatext_tarray t, text_tset *ts)
+{ unsigned int i;
+ 
+  for (i = 0; i < t.length(); i ++)
+    {
+      ts->insert((corbaconv_text_t) t[i]); // further typecast to text_t
+    } 
+
+  return;
+}
+
+void corbatext_setToCorbaArray(text_tset ts, corbatext_tarray *t)
+{
+  corbatext_t str;
+  text_tset::iterator here = ts.begin();
+  text_tset::iterator end = ts.end();
+  unsigned int i;
+
+  i = 0;
+  while (here != end)
+    {
+      (*t).length(i+1);
+      (*t)[i] = *corbatext_corbatext(*here);
+      here ++;
+      i ++;
+    }
+}
+
+void corbatext_mapToCorbaMap(text_tmap map, corbatext_tmap *cmap)
+{
+  text_tmap::iterator here = map.begin();
+  text_tmap::iterator end  = map.end();
+  unsigned int i = 0;
+
+  while (here != end)
+    {
+      (*cmap).names.length(i+1);
+      (*cmap).values.length(i+1);
+      cmap->names[i] = *corbatext_corbatext((*here).first);
+      cmap->values[i] = *corbatext_corbatext((*here).second);
+      here ++;
+      i ++;
+    }
+}
+
+void corbatext_corbaMapToMap(corbatext_tmap cmap, text_tmap &map)
+{
+  unsigned int i;
+
+  for (i = 0; i < cmap.names.length(); i ++)
+    {
+      map.insert(make_pair(*(new corbaconv_text_t(cmap.names[i])), 
+                           *(new corbaconv_text_t(cmap.values[i])))); // further typecast to text_t
+    }
+}
+
Index: /gsdl/trunk/gs2common/src/gdbmedit/db2txt/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/db2txt/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/db2txt/Makefile.in	(revision 16561)
@@ -0,0 +1,79 @@
+###########################################################################
+#
+# Makefile -- src/gdbmedit/db2txt
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = @GDBM_INCLUDE@ -I$(GSDL_DIR)/src/lib
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @GDBM_LIBPATH@ -lgdbm @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	db2txt.cpp
+
+OBJECTS = \
+	db2txt.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = db2txt
+
+
+# Default target: make the db2txt executable
+all: $(EXECUTABLE)
+
+db2txt: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/gdbmedit/db2txt/db2txt.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/db2txt/db2txt.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/db2txt/db2txt.cpp	(revision 16561)
@@ -0,0 +1,107 @@
+/**********************************************************************
+ *
+ * db2txt.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.
+ *
+ *********************************************************************/
+
+#ifdef __WIN32__
+#include "autoconf.h"
+#include "systems.h"
+#include "gdbmconst.h"
+#include "gdbm.h"
+
+#else
+#include <gdbm.h>
+#endif
+
+#include "gsdlconf.h"
+#include <stdlib.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+void print_usage (char *program_name) {
+  cerr << "usage: " << program_name << " database-name\n\n";
+}
+
+
+int main (int argc, char *argv[]) {
+  int block_size = 0, i;
+  GDBM_FILE dbf;
+  datum key, value, nextkey;
+      
+  // sanity check
+  if (argc != 2) {
+    print_usage (argv[0]);
+    exit (0);
+  }
+  
+  // open the database
+#ifdef __WIN32__
+  dbf = gdbm_open (argv[1], block_size, GDBM_READER, 00664, NULL, 0);
+#else
+  dbf = gdbm_open  (argv[1], block_size, GDBM_READER, 00664, NULL);
+#endif
+  if (dbf == NULL) {
+    cerr << "couldn't open " << argv[1] << "\n";
+    exit (0);
+  }
+
+  key = gdbm_firstkey (dbf);
+  while (key.dptr != NULL) {
+    cout << "[";
+    for (i = 0; i < key.dsize; ++i)
+      cout << key.dptr[i];
+    cout << "]\n";
+    value = gdbm_fetch (dbf, key);
+    for (i = 0; i < value.dsize; ++i)
+      cout << value.dptr[i];
+    cout << "\n----------------------------------------------------------------------\n";
+    free(value.dptr);
+
+    /* get next key */
+    nextkey = gdbm_nextkey (dbf, key);
+
+    /* free old key's dptr, otherwise causes memory leak */
+    free(key.dptr); 
+    
+    /* can now safely copy content of nextkey into key */
+    key = nextkey;
+  }
+  
+  gdbm_close (dbf);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/src/gdbmedit/db2txt/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/db2txt/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/db2txt/win32.mak	(revision 16561)
@@ -0,0 +1,84 @@
+###########################################################################
+#
+# win32 makefile -- db2txt
+# 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  The 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.
+#
+###########################################################################
+
+GSDLHOME = ..\..
+GSDL_VC4 = 0
+STLPATH = "$(GSDLHOME)\packages\windows\stlport\stlport"
+
+AR = lib
+CC = cl
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+LDFLAGS =
+
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"$(GSDLHOME)\src\db2txt" -I"$(GSDLHOME)\src\lib" -I"$(GSDLHOME)\packages\mg\lib" \
+	   -I"$(GSDLHOME)\packages\windows\gdbm\gdbm"
+!IF $(GSDL_VC4)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I$(STLPATH)
+!ENDIF
+
+LIBS = "$(GSDLHOME)\indexers\mg\lib\unitool.obj" "$(GSDLHOME)\src\lib\gsdllib.lib" \
+       "$(GSDLHOME)\packages\windows\gdbm\gdbm\gdbm.lib"
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+HEADERS = 
+
+SOURCES = db2txt.cpp
+
+OBJECTS = db2txt$o
+
+EXEC = db2txt$e
+
+all : $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *$o
+	del $(EXEC)
+
+db2txt$e: $(OBJECTS) $(LIBS)
+	  $(LINK) $(OBJECTS) $(LIBS)
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/Makefile.in	(revision 16561)
@@ -0,0 +1,79 @@
+###########################################################################
+#
+# Makefile -- src/gdbmedit/gdbmget
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = @GDBM_INCLUDE@ -I$(GSDL_DIR)/src/lib
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @GDBM_LIBPATH@ -lgdbm @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	gdbmget.cpp
+
+OBJECTS = \
+	gdbmget.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = gdbmget
+
+
+# Default target: make the gdbmget executable
+all: $(EXECUTABLE)
+
+gdbmget: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/gdbmget.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/gdbmget.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/gdbmget.cpp	(revision 16561)
@@ -0,0 +1,109 @@
+/**********************************************************************
+ *
+ * gdbmget -- retrieve a single value from the GDBM database
+ * 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.
+ *
+ *********************************************************************/
+
+#ifdef __WIN32__
+#include "autoconf.h"
+#include "systems.h"
+#include "gdbmconst.h"
+#include "gdbm.h"
+
+#else
+#include <gdbm.h>
+#endif
+
+#include "gsdlconf.h"
+#include <stdlib.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+void 
+print_usage (char *program_name) 
+{
+  cerr << "usage: " << program_name << " <database_path> <key>\n\n";
+}
+
+
+int 
+main (int argc, char *argv[]) 
+{
+  int block_size = 0;
+  GDBM_FILE dbf;
+  datum key, value;
+  
+  // sanity check
+  if (argc != 3) 
+    {
+      print_usage (argv[0]);
+      exit (0);
+    }
+  
+  // open the database
+#ifdef __WIN32__
+  dbf = gdbm_open (argv[1], block_size, GDBM_READER, 00664, NULL, 0);
+#else
+  dbf = gdbm_open  (argv[1], block_size, GDBM_READER, 00664, NULL);
+#endif
+  if (dbf == NULL) 
+    {
+      cerr << "couldn't create " << argv[1] << "\n";
+      exit (0);
+    }
+  
+  key.dsize = strlen(argv[2]);
+  key.dptr = argv[2];
+  value = gdbm_fetch (dbf, key);
+  if (value.dsize > 0) 
+    {
+      for (int i = 0; i < value.dsize; i++)
+        {
+          cout << value.dptr[i];
+        }
+      printf("\n");
+      // ?? free(value.dptri);
+    } 
+  else 
+    {
+      printf("\n");
+    }
+  
+  gdbm_close (dbf);
+  return 0;
+}
+
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmget/win32.mak	(revision 16561)
@@ -0,0 +1,77 @@
+###########################################################################
+#
+# win32 makefile -- db2txt
+# 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  The 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.
+#
+###########################################################################
+
+GSDLHOME = ..\..\..\..
+USE_STLPORT = 0
+STLPATH = "$(GSDLHOME)\packages\windows\stlport\stlport"
+
+AR = lib
+CC = cl
+CPPFLAGS = 
+LDFLAGS =
+
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H
+INCLUDES = -I"$(GSDLHOME)\lib" \
+	   -I"$(GSDLHOME)\packages\windows\gdbm\gdbm"
+!IF $(USE_STLPORT)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I$(STLPATH)
+!ENDIF
+
+LIBS = "$(GSDLHOME)\packages\windows\gdbm\gdbm\gdbm.lib"
+
+COMPILE = $(CC) -c -GX $(DEFS) $(INCLUDES)
+LINK = $(CC) $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+HEADERS = 
+
+SOURCES = gdbmget.cpp
+
+OBJECTS = gdbmget$o
+
+EXEC = gdbmget$e
+
+all : $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *$o
+	del $(EXEC)
+
+gdbmget$e: $(OBJECTS) $(LIBS)
+	  $(LINK) $(OBJECTS) $(LIBS)
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/Makefile.in	(revision 16561)
@@ -0,0 +1,79 @@
+###########################################################################
+#
+# Makefile -- src/gdbmedit/gdbmset
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = @GDBM_INCLUDE@ -I$(GSDL_DIR)/src/lib
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @GDBM_LIBPATH@ -lgdbm @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	gdbmset.cpp
+
+OBJECTS = \
+	gdbmset.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = gdbmset
+
+
+# Default target: make the gdbmset executable
+all: $(EXECUTABLE)
+
+gdbmset: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/gdbmset.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/gdbmset.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/gdbmset.cpp	(revision 16561)
@@ -0,0 +1,111 @@
+/**********************************************************************
+ *
+ * gdbmset -- add or replace a single value from the GDBM database
+ * 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.
+ *
+ *********************************************************************/
+
+#ifdef __WIN32__
+#include "autoconf.h"
+#include "systems.h"
+#include "gdbmconst.h"
+#include "gdbm.h"
+
+#else
+#include <gdbm.h>
+#endif
+
+#include "gsdlconf.h"
+#include <stdlib.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#include <iostream.h>
+#else
+#include <iostream>
+#endif
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+void 
+print_usage (char *program_name) 
+{
+  cerr << "usage: " << program_name << " <database_path> <key> [<value>]\n";
+  cerr << "\t- if no value is given then the lexicon indicated by the key is removed\n\n";
+}
+
+
+int 
+main (int argc, char *argv[]) 
+{
+  int block_size = 0;
+  GDBM_FILE dbf;
+  datum key, content;
+  
+  // sanity check
+  if (argc < 3) 
+    {
+      print_usage (argv[0]);
+      exit (0);
+    }
+  
+  // open the database
+#ifdef __WIN32__
+  dbf = gdbm_open (argv[1], block_size, GDBM_WRITER, 00664, NULL, 0);
+#else
+  dbf = gdbm_open  (argv[1], block_size, GDBM_WRITER, 00664, NULL);
+#endif
+  if (dbf == NULL) 
+    {
+      cerr << "couldn't open database connection to " << argv[1] << "\n";
+      exit (0);
+    }
+  
+  key.dsize = strlen(argv[2]);
+  key.dptr = argv[2];
+  if(argc == 4)
+    {
+      //cerr << "Setting [" << argv[2] << "] to '" << argv[3] << "' (length " << strlen(argv[3]) << ")\n";
+      content.dsize = strlen(argv[3]);
+      content.dptr = argv[3];
+      if(gdbm_store(dbf, key, content, GDBM_REPLACE) != 0)
+        {
+          cerr << "failed to set [" << argv[2] << "] to '" << argv[3] << "'\n";
+        }
+    }
+  else
+    {
+      if(gdbm_delete(dbf, key) != 0)
+        {
+          cerr << "failed to remove [" << argv[2] << "]\n";
+        }
+    }
+  gdbm_close (dbf);
+  return 0;
+}
Index: /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/gdbmset/win32.mak	(revision 16561)
@@ -0,0 +1,77 @@
+###########################################################################
+#
+# win32 makefile -- db2txt
+# 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  The 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.
+#
+###########################################################################
+
+GSDLHOME = ..\..\..\..
+USE_STLPORT = 0
+STLPATH = "$(GSDLHOME)\packages\windows\stlport\stlport"
+
+AR = lib
+CC = cl
+CPPFLAGS = 
+LDFLAGS =
+
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H
+INCLUDES = -I"$(GSDLHOME)\lib" \
+	   -I"$(GSDLHOME)\packages\windows\gdbm\gdbm"
+!IF $(USE_STLPORT)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I$(STLPATH)
+!ENDIF
+
+LIBS = "$(GSDLHOME)\packages\windows\gdbm\gdbm\gdbm.lib"
+
+COMPILE = $(CC) -c -GX $(DEFS) $(INCLUDES)
+LINK = $(CC) $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+
+HEADERS = 
+
+SOURCES = gdbmset.cpp
+
+OBJECTS = gdbmset$o
+
+EXEC = gdbmset$e
+
+all : $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *$o
+	del $(EXEC)
+
+gdbmset$e: $(OBJECTS) $(LIBS)
+	  $(LINK) $(OBJECTS) $(LIBS)
Index: /gsdl/trunk/gs2common/src/gdbmedit/txt2db/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/txt2db/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/txt2db/Makefile.in	(revision 16561)
@@ -0,0 +1,85 @@
+###########################################################################
+#
+# Makefile -- src/gdbmedit/txt2db
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = @GDBM_INCLUDE@ -I$(GSDL_DIR)/src/lib
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @GDBM_LIBPATH@ -lgdbm @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	txt2db.cpp
+
+OBJECTS = \
+	txt2db.o
+
+GSDL_LIBS = \
+	$(GSDL_DIR)/src/lib/gsdllib.a \
+	$(INDEXERS_DIR)/mg/lib/libmglib.a
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = txt2db
+
+
+# Default target: make the txt2db executable
+all: $(EXECUTABLE)
+
+txt2db: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/gdbmedit/txt2db/txt2db.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/txt2db/txt2db.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/txt2db/txt2db.cpp	(revision 16561)
@@ -0,0 +1,171 @@
+/**********************************************************************
+ *
+ * txt2db.cpp -- 
+ * 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  The 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.
+ *
+ **********************************************************************/
+
+
+#ifdef __WIN32__
+#include "autoconf.h"
+#include "systems.h"
+#include "gdbmconst.h"
+#include "gdbm.h"
+
+#else
+#include <gdbm.h>
+#endif
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include <stdlib.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+
+void print_usage (char *program_name) {
+  cerr << "usage: " << program_name << " [options] database-name\n\n";
+  cerr << "options:\n";
+  cerr << " -append        append to existing database\n\n";
+}
+
+
+int main (int argc, char *argv[]) {
+  int block_size = 0;
+  GDBM_FILE dbf;
+  char c;
+  text_t key;
+  text_t value;
+  text_t tmp;
+  int num_dashes = 0;
+
+  // sanity check
+  if (argc != 2 && argc != 3) {
+    print_usage (argv[0]);
+    exit (0);
+  }
+  
+  char *dbname;
+  int append = 0;
+  if (argc == 3) {
+    if (strcmp (argv[1], "-append") == 0) {
+      append = 1;
+      dbname = argv[2];
+    } else {
+      cerr << argv[1] << " is not a valid option.\n\n";
+      print_usage (argv[0]);
+      exit (0);
+    }
+  } else dbname = argv[1];
+
+  
+  // open the database
+  // note that GDBM_FAST is obsolete on newer versions of gdbm
+  int read_write = GDBM_NEWDB | GDBM_FAST;
+  if (append) read_write = GDBM_WRCREAT | GDBM_FAST;
+  
+#ifdef __WIN32__
+  dbf = gdbm_open (dbname, block_size, read_write, 00664, NULL, 1);
+#else
+  dbf = gdbm_open  (dbname, block_size, read_write, 00664, NULL);
+#endif
+  if (dbf == NULL) {
+    cerr << "couldn't create " << dbname << "\n";
+    exit (0);
+  }
+  
+  cin.get(c);
+  while (!cin.eof()) {
+    num_dashes = 0;
+    key = "";
+    value = "";
+    
+    // look for [key]\n
+    while (!cin.eof() && c != '[') cin.get(c);
+    if (!cin.eof()) cin.get(c); // skip [
+    while (!cin.eof() && c != ']') {
+      key.push_back ((unsigned char)c);
+      cin.get(c);
+    }
+    if (!cin.eof()) cin.get(c); // skip ]
+    while (!cin.eof() && (c == '\n' || c == 'r')) cin.get(c);
+    
+    // look for 70 dashes
+    tmp = "";
+    while (!cin.eof() && (num_dashes < 70)) {
+      if (c == '\n' || c == '\r') {
+	tmp.push_back ((unsigned char)c);
+	num_dashes = 0;
+	
+      } else if (c == '-') {
+	tmp.push_back ((unsigned char)c);
+	++num_dashes;
+	
+      } else {
+	value += tmp;
+	value.push_back ((unsigned char)c);
+	tmp = "";
+	num_dashes = 0;
+      }
+      cin.get(c);
+    }
+    
+    // if the key is not an empty string store this key-value pair
+    if (!key.empty()) {
+      // convert key to a datum datatype
+      datum key_data;
+      key_data.dptr = key.getcstr();
+      if (key_data.dptr == NULL) {
+	cerr << "NULL key_data.dptr\n";
+	exit (0);
+      }
+      key_data.dsize = strlen(key_data.dptr);
+      
+      // convert value to a datum datatype
+      datum value_data;
+      value_data.dptr = value.getcstr();
+      if (value_data.dptr == NULL) {
+	cerr << "NULL value_data.dptr\n";
+	exit (0);
+      }
+      value_data.dsize = strlen(value_data.dptr);
+      
+      // store the value
+      if (gdbm_store (dbf, key_data, value_data, GDBM_REPLACE) < 0) {
+	cerr << "gdbm_store returned an error\n";
+	exit (0);
+      }
+      
+      free(key_data.dptr);
+      free(value_data.dptr);
+    }
+  }
+  
+  gdbm_close (dbf);
+  
+  return 0;
+}
Index: /gsdl/trunk/gs2common/src/gdbmedit/txt2db/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/src/gdbmedit/txt2db/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/src/gdbmedit/txt2db/win32.mak	(revision 16561)
@@ -0,0 +1,84 @@
+###########################################################################
+#
+# win32 makefile -- txt2db
+# 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  The 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.
+#
+###########################################################################
+
+GSDLHOME = ..\..
+GSDL_VC4 = 0
+STLPATH = "$(GSDLHOME)\packages\windows\stlport\stlport"
+
+AR = lib
+CC = cl
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+LDFLAGS =
+
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"$(GSDLHOME)\src\txt2db" -I"$(GSDLHOME)\src\lib" -I"$(GSDLHOME)\indexers\mg\lib" \
+	   -I"$(GSDLHOME)\packages\windows\gdbm\gdbm"
+!IF $(GSDL_VC4)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I$(STLPATH)
+!ENDIF
+
+LIBS = "$(GSDLHOME)\indexers\mg\lib\unitool.obj" "$(GSDLHOME)\src\lib\gsdllib.lib" \
+       "$(GSDLHOME)\packages\windows\gdbm\gdbm\gdbm.lib"
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS)
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $<
+
+ANSI2KNR = 
+o = .obj
+e = .exe
+ 
+HEADERS = 
+
+SOURCES = txt2db.cpp
+
+OBJECTS = txt2db$o
+
+EXEC = txt2db$e
+ 
+all : $(EXEC)
+
+install: $(EXEC)
+	if NOT EXIST "$(GSDLHOME)\bin\windows" mkdir "$(GSDLHOME)\bin\windows"
+	copy $(EXEC) "$(GSDLHOME)\bin\windows"
+
+clean:
+	del *.obj
+	del $(EXEC)
+
+txt2db$e: $(OBJECTS) $(LIBS)
+	  $(LINK) $(OBJECTS) $(LIBS)
Index: /gsdl/trunk/gs2common/src/getpw/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/getpw/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/getpw/Makefile.in	(revision 16561)
@@ -0,0 +1,78 @@
+###########################################################################
+#
+# Makefile -- src/getpw
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the
+# University of Waikato, New Zealand.
+#
+# Copyright (C) 2000-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+GSDLOS = @gsdlos@
+INCLUDES = -I$(GSDL_DIR) -I$(GSDL_DIR)/src/lib
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	getpw.cpp
+
+OBJECTS = \
+	getpw.o
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS)
+
+EXECUTABLE = getpw
+
+
+# Default target: make the getpw executable
+all: $(EXECUTABLE)
+
+getpw: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -d $(GSDL_DIR)/bin/$(GSDLOS)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/getpw/getpw.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/getpw/getpw.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/getpw/getpw.cpp	(revision 16561)
@@ -0,0 +1,93 @@
+/**********************************************************************
+ *
+ * getpw.cpp -- 
+ * A component of the Greenstone digital library software
+ * from the New Zealand Digital Library Project at the 
+ * University of Waikato, New Zealand.
+ *
+ * Copyright (C) 2000  The 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.
+ *
+ **********************************************************************/
+
+#define _XOPEN_SOURCE 1
+/* following is for solaris */
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include "gsdlconf.h"
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+
+// include crypt
+#if defined(__WIN32__)
+#include "crypt.h"
+#else
+
+#include "config.h"
+#if defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+/* we are probably in trouble if we have neither crypt.h or unistd.h,
+   but for now we'll do nothing about it... */
+
+#endif  /* not WIN32 */
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#include <string.h>
+#include <pwd.h>
+
+int main (int argc, char *argv[]) {
+
+  char c[10];
+  while (1) {
+    char *a = getpass("Enter password:");
+    int len = strlen (a);
+    if (len < 3 || len > 8) {
+      cerr << "Password must be between 3 and 8 characters long. Try again\n";
+      continue;
+    }
+    strcpy (c, a);
+    char *b = getpass("Re-enter password:");  
+    if ((strcmp (c, b)) == 0) break;
+    else cerr << "Passwords didn't match. Try again.\n";
+  }
+
+  char *salt = "Tp";
+  char *pw = crypt (c, salt);
+
+  cout << pw << "\n";
+  
+  return 0;
+}
Index: /gsdl/trunk/gs2common/src/lib/Makefile.in
===================================================================
--- /gsdl/trunk/gs2common/src/lib/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/Makefile.in	(revision 16561)
@@ -0,0 +1,119 @@
+###########################################################################
+#
+# Makefile -- lib
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_INCLUDES = -I$(PACKAGES_DIR)/sqlite/include
+SQLITE_OBJECTS = sqlitedbclass.o
+SQLITE_SOURCES = sqlitedbclass.cpp
+else
+SQLITE_INCLUDES = 
+SQLITE_OBJECTS = 
+SQLITE_SOURCES = 
+endif
+
+
+AR = ar
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@
+INCLUDES = @GDBM_INCLUDE@ -I$(GSDL_DIR) -I$(INDEXERS_DIR)/mg/lib $(SQLITE_INCLUDES)
+RANLIB = @RANLIB@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	cfgread.cpp \
+	cnfgable.cpp \
+	cnfgator.cpp \
+	dbclass.cpp \
+	display.cpp \
+	fileutil.cpp \
+	gdbmclass.cpp \
+	gsdlsitecfg.cpp \
+	gsdltimes.cpp \
+	gsdltools.cpp \
+	gsdlunicode.cpp \
+	infodbclass.cpp \
+	md5.cpp \
+	OIDtools.cpp \
+	phrases.cpp \
+	text_t.cpp \
+	$(SQLITE_SOURCES)
+
+OBJECTS = \
+	cfgread.o \
+	cnfgable.o \
+	cnfgator.o \
+	dbclass.o \
+	display.o \
+	fileutil.o \
+	gdbmclass.o \
+	gsdlsitecfg.o \
+	gsdltimes.o \
+	gsdltools.o \
+	gsdlunicode.o \
+	infodbclass.o \
+	md5.o \
+	OIDtools.o \
+	phrases.o \
+	text_t.o \
+	$(SQLITE_OBJECTS)
+
+LIBRARY = gsdllib.a
+
+
+# Default target: make gsdllib.a
+all: $(LIBRARY)
+
+gsdllib.a: $(OBJECTS)
+	rm -f $(LIBRARY)
+	$(AR) cru $(LIBRARY) $(OBJECTS)
+	$(RANLIB) $(LIBRARY)
+
+install:
+
+clean:
+	rm -f $(OBJECTS) $(LIBRARY)
+
+distclean:
+	rm -f $(OBJECTS) $(LIBRARY) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2common/src/lib/OIDtools.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/OIDtools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/OIDtools.cpp	(revision 16561)
@@ -0,0 +1,164 @@
+/**********************************************************************
+ *
+ * OIDtools.cpp -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#include "OIDtools.h"
+
+
+// returns (in top) the top level of OID (i.e. everything
+// up until the first dot)
+void get_top (const text_t &OID, text_t &top)
+{
+  top.clear();
+  if (OID.empty()) return;
+
+  text_t::const_iterator begin = OID.begin();
+  text_t::const_iterator end = OID.end();
+
+  top.appendrange (begin, findchar(begin, end, '.'));
+}	
+
+
+// checks if OID is top level (i.e. contains no dots)
+bool is_top (const text_t &OID)
+{
+  if (OID.empty()) return true;
+
+  text_t::const_iterator here = OID.begin();
+  text_t::const_iterator end = OID.end();
+  here = findchar (here, end, '.');
+
+  if (here == end) return true;
+  return false;
+}
+
+
+// get_parents_array loads the parents array with all the parents of the
+// document or classification specified by OID (not including OID itself)
+void get_parents_array (const text_t &OID, text_tarray &parents)
+{
+  text_t::const_iterator here = OID.begin ();
+  text_t::const_iterator end = OID.end ();
+  text_t thisparent;
+
+  while (here != end) {
+    if (*here == '.') parents.push_back(thisparent);
+    thisparent.push_back(*here);
+    ++here;
+  }
+}
+
+
+// get_parent returns the parent of the document or classification
+// specified by OID
+text_t get_parent (const text_t& OID)
+{
+  if (OID.empty() || is_top (OID)) return g_EmptyText;
+
+  text_t::const_iterator begin = OID.begin();
+  text_t::const_iterator here = (OID.end() - 1);
+
+  while (here >= begin) {
+    if (*here == '.')
+      break;
+    if (here == begin)
+      break;
+    --here;
+  }
+
+  if (here != begin) {
+    text_t parentOID;
+    parentOID.appendrange(begin, here);
+    return parentOID;
+  }
+
+  return g_EmptyText;
+}
+
+
+// takes an OID like ".2 and replaces the " with parent
+void translate_parent (text_t &OID, const text_t &parent)
+{
+  text_t::const_iterator here = OID.begin();
+  text_t::const_iterator end = OID.end();
+  text_t temp;
+
+  while (here != end) {
+    if (*here == '"') temp += parent;
+    else temp.push_back (*here);
+    ++here;
+  }
+  OID = temp;
+}
+
+
+// shrink_parent does the opposite to translate_parent
+void shrink_parent (text_t &OID)
+{
+  text_tarray tmp;
+  splitchar (OID.begin(), OID.end(), '.', tmp);
+  OID = "\"." + tmp.back();
+}
+
+
+// checks if OID uses ".fc", ".lc", ".pr", "rt", ".ns",
+// or ".ps" syntax (first child, last child, parent, root,
+// next sibling, previous sibling)
+bool needs_translating (const text_t &OID)
+{
+  if (OID.size() < 4) return false;
+
+  text_t tail = substr (OID.end()-3, OID.end());
+  if (tail == ".fc" || tail == ".lc" || tail == ".pr" || 
+      tail == ".rt" || tail == ".ns" || tail == ".ps") return true;
+
+  return false;
+}
+
+
+// strips the ".fc", ".lc", ".pr", ".ns",
+// or ".ps" suffix from the end of OID
+void strip_suffix (text_t &OID)
+{
+  text_t tail = substr (OID.end()-3, OID.end());
+  while (tail == ".fc" || tail == ".lc" || tail == ".pr" || 
+	 tail == ".rt" || tail == ".ns" || tail == ".ps") {
+    OID.erase (OID.end()-3, OID.end());
+    tail = substr (OID.end()-3, OID.end());
+  }
+}
+
+
+// is_child_of returns true if OID2 is a child of OID1
+bool is_child_of(const text_t &OID1, const text_t &OID2)
+{
+  text_t parent = get_parent(OID2);
+
+  while (!parent.empty()) {
+    if (parent == OID1) return true;
+    parent = get_parent(parent);
+  }
+
+  return false;
+}
Index: /gsdl/trunk/gs2common/src/lib/OIDtools.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/OIDtools.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/OIDtools.h	(revision 16561)
@@ -0,0 +1,68 @@
+/**********************************************************************
+ *
+ * OIDtools.h -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#ifndef OIDTOOLS_H
+#define OIDTOOLS_H
+
+#include "text_t.h"
+
+
+void get_oid (const text_t &OID, text_t &to, size_t level);
+
+// returns (in top) the top level of OID (i.e. everything
+// up until the first dot)
+void get_top (const text_t &OID, text_t &top);
+
+// checks if OID is top level (i.e. contains no dots)
+bool is_top (const text_t &OID);
+
+// get_parents_array loads the parents array with all the parents of the
+// document or classification specified by OID (not including OID itself)
+void get_parents_array (const text_t &OID, text_tarray &parents);
+
+// get_parent returns the parent of the document or classification
+// specified by OID
+text_t get_parent (const text_t& OID);
+
+// takes an OID like ".2.3 and replaces the " with parent
+void translate_parent (text_t &OID, const text_t &parent);
+
+// shrink_parent does the opposite to translate_parent
+void shrink_parent (text_t &OID);
+
+// checks if OID uses ".fc", ".lc", ".pr", ".ns",
+// or ".ps" syntax (first child, last child, parent,
+// next sibling, previous sibling)
+bool needs_translating (const text_t &OID);
+
+// strips the ".fc", ".lc", ".pr", ".ns",
+// or ".ps" suffix from the end of OID
+void strip_suffix (text_t &OID);
+
+// is_child_of returns true if OID2 is a child of OID1
+bool is_child_of(const text_t &OID1, const text_t &OID2);
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/cfgread.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cfgread.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cfgread.cpp	(revision 16561)
@@ -0,0 +1,220 @@
+/**********************************************************************
+ *
+ * cfgread.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.
+ *
+ *********************************************************************/
+
+#include "cfgread.h"
+#include <cctype> // for isspace()
+
+int write_ini_line (ofstream &fileout, const text_t &key, const text_t &value) {
+  if (key.empty() || value.empty()) return -1;
+  outconvertclass text_t2ascii;
+  fileout << text_t2ascii << key << "=" << value << "\n";
+  return 0;
+}
+
+int read_ini_line (ifstream &filein, text_t &key, text_t &value) {
+  if (filein.eof()) return -1;
+
+  key.clear();
+  value.clear();
+  char c;
+  filein.get(c);
+
+  int foundeq = 0;
+  while (!filein.eof() && c != '\n') {
+    if (!foundeq && c == '=') {foundeq = 1; filein.get(c);}
+
+    if (foundeq) value.push_back(c);
+    else key.push_back(c);
+    filein.get(c);
+  }
+  if (key.empty()) return 0; // blank line maybe?
+  return 0;
+} 
+
+// write out line of values to cfgfile
+// does nothing fancy - make sure no values contain carriage returns
+int write_cfg_line (ofstream &fileout, const text_tarray &values) {
+  outconvertclass text_t2ascii;
+
+  text_tarray::const_iterator here = values.begin();
+  text_tarray::const_iterator end = values.end();
+
+  bool first = true;
+  while (here != end) {
+    if (first) fileout << text_t2ascii << *here;
+    else fileout << text_t2ascii << "  \"" << *here << "\"";
+    first = false;
+    ++here;
+  }
+  fileout << "\n";
+  return 0;
+}
+
+// same, but use a file descriptor this time
+int write_cfg_line (int fileout, const text_tarray &values) {
+  outconvertclass text_t2ascii;
+
+  text_tarray::const_iterator here = values.begin();
+  text_tarray::const_iterator end = values.end();
+
+  if (here != end) {
+    char *s=here->getcstr();
+    write(fileout,s ,here->size());
+    delete []s;
+
+    ++here;
+  }
+  while (here != end) {
+    write(fileout, " \"", 2);
+    char *s=here->getcstr();
+    write(fileout,s ,here->size());
+    delete []s;
+    write(fileout, "\"", 1);
+
+    ++here;
+  }
+  write(fileout,"\n",1);
+  return 0;
+}
+
+
+// returns 0 on success, -1 on failure
+int read_cfg_line (ifstream &filein, text_tarray &values) {
+  // we split up the line into tokens, pushing each token into the
+  // values array. Quoted phrases are a single token. A "\" at the end
+  // of a line continues onto the next line.
+
+  values.erase(values.begin(), values.end());
+
+  if (!filein.good()) return -1;
+
+  text_t curvalue;
+  char c1;
+  filein.get(c1);
+
+  // skip white space
+  while (!filein.eof() && isspace((unsigned char) c1)) { filein.get(c1); }
+
+    // ignore comments
+  while (c1 == '#') {
+    while (!filein.eof() && c1!='\n' && c1!='\r') { filein.get(c1); }
+    // skip white space...
+    while (!filein.eof() && isspace((unsigned char) c1)) { filein.get(c1); }
+  }
+
+  // deal with all the records on this line (possibly multi-line)
+
+  while (!filein.eof()) {
+    if (c1=='\n' || c1=='\r') { // shouldn't happen?
+      break;
+    }
+
+    // get the next token
+    curvalue.clear();
+      
+    bool inquote=false;
+    char quotemark='"';
+    char preceding='\0'; // 1-char state to allow \" and \'
+    // see if this is a quoted phrase
+    if (c1=='\'' || c1=='\"') { // starts with a quote
+      inquote=true;
+      quotemark = c1;
+      preceding = c1; // just to initialise
+      filein.get(c1);
+    }
+
+    // get token or a whole phrase
+    while (!filein.eof()) {
+      if (isspace((unsigned char) c1)) {
+	if (! inquote) {
+	  // end of token, not inside quote marks
+	  break;
+	} else {
+	  // inside quote marks.
+	  /* Turn eol into space, in case other parsing bits expect eol to
+	     also mean end of parsing... */
+	  c1=' ';
+	}
+      }
+      if (c1 == quotemark && inquote && preceding != '\\') {
+	// end of quoted phrase found
+	inquote=false;
+	filein.get(c1);
+	continue;
+      }
+
+      // add current char to token/phrase
+      // see if current byte is part of a multibyte char (utf-8 only!)
+      unsigned short int c; // text_t uses 16bit unicode
+      unsigned char uc1=(unsigned)c1;
+      if (uc1 < 0x80) {
+	c=uc1;
+      } else if (uc1 >= 0xc0 && uc1 <= 0xdf) {
+	// 2-byte utf-8
+	unsigned char c2;
+	// two byte character
+	filein.get((char&)c2); // get takes a signed char
+	c = ((uc1 & 0x1f) << 6) + (c2 & 0x3f);
+      } else if (uc1 >= 0xe0 && uc1 <= 0xef) {
+	// 3-byte character
+	unsigned char c2, c3;
+	filein.get((char&)c2);
+	filein.get((char&)c3);
+	c = ((uc1 & 0xf) << 12) + ((c2 & 0x3f) << 6)
+	  + (c3 & 0x3f);
+      } else {c=uc1;} // we don't do group2/plane0 (4,5,6-byte utf-8)
+
+      curvalue.push_back(c); // 16bit unicode
+      if (inquote)
+	preceding = c1;
+
+      filein.get(c1);
+    }
+    // we now have a token or a phrase
+    
+    // see if we've reached the end of the line
+    if (c1 == '\n' || c1 == '\r') {
+      if (curvalue != "\\") { // the line DOESN'T continue. End of line.
+	values.push_back(curvalue);
+	break; // end of token/phrase
+      } else {
+	// swallow up the EOL chars
+	while (!filein.eof() && (c1=='\r' || c1=='\n')) filein.get(c1);
+	// the current token "\\" will be cleared below
+      }
+    } else { // no new line seen
+      values.push_back(curvalue);
+    }
+
+    curvalue.clear();
+
+    // remove whitespace (but not newline/CR chars) before next token
+    while (!filein.eof() && (c1==' ' || c1=='\t')) filein.get(c1);
+
+  } // while(1)
+  
+
+  return 0;
+}
Index: /gsdl/trunk/gs2common/src/lib/cfgread.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cfgread.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cfgread.h	(revision 16561)
@@ -0,0 +1,48 @@
+/**********************************************************************
+ *
+ * cfgread.h -- reads in configuration files
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef CFGREAD_H
+#define CFGREAD_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <fstream.h>
+#else
+#  include <fstream>
+#endif
+
+// return 0 on success, -1 on failure
+int write_ini_line (ofstream &fileout, const text_t &key, const text_t &value);
+int read_ini_line (ifstream &filein, text_t &key, text_t &value);
+int write_cfg_line (ofstream &fileout, const text_tarray &values);
+int write_cfg_line (int filedesc, const text_tarray &values);
+int read_cfg_line (ifstream &filein, text_tarray &values);
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/cnfgable.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cnfgable.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cnfgable.cpp	(revision 16561)
@@ -0,0 +1,37 @@
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+#include "cnfgable.h"
+#include "cfgread.h"
+
+bool configurable::read_configfile (const text_t &filename) {
+  // read in the build configuration file
+  text_t key;
+  text_tarray cfgline;
+  char *cstr = filename.getcstr();
+  ifstream confin (cstr);
+  delete []cstr;
+
+  if (confin) {
+    while (read_cfg_line(confin, cfgline) >= 0) {
+      if (cfgline.size () >= 2) {
+	key = cfgline[0];
+	cfgline.erase(cfgline.begin());
+
+	// configure the collection server
+	this->configure (key, cfgline);
+      }
+    }
+    confin.close ();
+    return true;
+  }
+  return false;
+}
Index: /gsdl/trunk/gs2common/src/lib/cnfgable.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cnfgable.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cnfgable.h	(revision 16561)
@@ -0,0 +1,16 @@
+#ifndef CONFIGURABLE_H
+#define CONFIGURABLE_H
+
+#include "text_t.h"
+
+class configurable {
+ public:
+  virtual void configure (const text_t &key, const text_tarray &cfgline) = 0;
+  virtual ~configurable () {}
+
+  bool read_configfile (const text_t &filename);
+ protected:
+  configurable() {}
+};
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/cnfgator.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cnfgator.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cnfgator.cpp	(revision 16561)
@@ -0,0 +1,52 @@
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+#include "cnfgator.h"
+
+configurator::configurator(configurable *configureobject) {
+  configureobjs.push_back(configureobject);
+}
+
+void configurator::add_configurable(configurable *configureobject) {
+  configureobjs.push_back(configureobject);
+}
+
+bool configurator::configure(const text_t &filename) {
+  // read in the build configuration file
+  text_t key;
+  text_tarray cfgline;
+  char *cstr = filename.getcstr();
+  ifstream confin (cstr);
+  delete []cstr;
+
+  if (confin) {
+    while (read_cfg_line(confin, cfgline) >= 0) {
+      if (cfgline.size () >= 2) {
+	key = cfgline[0];
+	cfgline.erase(cfgline.begin());
+
+	// configure the configurables
+	configurable *configureObject;
+	vector<configurable *>::iterator configureIter = configureobjs.begin();
+
+	while (configureIter != configureobjs.end()) {
+	  configureObject = *configureIter;
+	  ++configureIter;
+    
+	  configureObject->configure (key, cfgline);
+	}
+      }
+    }
+    confin.close ();
+    return true;
+  }
+  return false;
+}
Index: /gsdl/trunk/gs2common/src/lib/cnfgator.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/cnfgator.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/cnfgator.h	(revision 16561)
@@ -0,0 +1,18 @@
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/vector>
+#else
+#  include <vector>
+#endif
+
+#include "cfgread.h"
+#include "cnfgable.h"
+
+class configurator
+{ 
+ private:
+  vector<configurable *> configureobjs;
+ public:
+  configurator(configurable *configureobject);
+  bool configure(const text_t &filename);
+  void add_configurable(configurable *configureobject);
+};
Index: /gsdl/trunk/gs2common/src/lib/dbclass.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/dbclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/dbclass.cpp	(revision 16561)
@@ -0,0 +1,307 @@
+/**********************************************************************
+ *
+ * dbclass.cpp -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#include "dbclass.h"
+#include "OIDtools.h"
+#include "unitool.h"
+
+
+dbclass::~dbclass() {}
+
+
+// May be overwritten by subclasses; returns true if exists
+bool dbclass::exists (const text_t& key)
+{
+  text_t data;
+  return getkeydata (key, data);
+}
+
+
+// May be overwritten by subclasses; returns true on success
+bool dbclass::getinfo (const text_t& key, infodbclass &info)
+{
+  text_t data;
+  if (!getkeydata(key, data)) return false;
+
+  // Use getinfoline() to parse the data value into the infodbclass object
+  text_t::iterator data_iterator = data.begin();
+  text_t ikey, ivalue;
+  info.clear();
+  while (getinfoline(data_iterator, data.end(), ikey, ivalue))
+  {
+    info.addinfo(ikey, ivalue);
+  }
+
+  return true;
+}
+
+
+// returns true on success
+bool dbclass::getinfoline (text_t::iterator &here, text_t::iterator end, 
+			   text_t &key, text_t &value)
+{
+  key.clear();
+  value.clear();
+
+  // ignore white space
+  while (here != end && is_unicode_space (*here)) ++here;
+
+  // get the '<'
+  if (here == end || *here != '<') return false;
+  ++here;
+  
+  // get the key
+  while (here != end && *here != '>') {
+    key.push_back(*here);
+    ++here;
+  }
+  
+  // get the '>'
+  if (here == end || *here != '>') return false;
+  ++here;
+  
+  // get the value
+  while (here != end && *here != '\n') {
+    if (*here == '\\') { 
+      // found escape character
+      ++here;
+      if (here != end) {
+	if (*here == 'n') value.push_back ('\n');
+	else if (*here == 'r') value.push_back ('\r');
+	else value.push_back(*here);
+      }
+
+    } else {
+      // a normal character
+      value.push_back(*here);
+    }
+
+    ++here;
+  }
+
+  return true;
+}
+
+
+// returns true on success
+bool dbclass::setinfo (const text_t &key, const infodbclass &info)
+{
+  text_t subkey;
+  text_t data;
+
+  // get all the keys and values
+  infodbclass::const_iterator info_here = info.begin();
+  infodbclass::const_iterator info_end = info.end();
+  while (info_here != info_end) {
+    // add the key
+    subkey.clear();
+    subkey.push_back('<');
+    text_t::const_iterator subkey_here = (*info_here).first.begin();
+    text_t::const_iterator subkey_end = (*info_here).first.end();
+    while (subkey_here != subkey_end) {
+      if (*subkey_here == '>') {
+	subkey.push_back('\\'); subkey.push_back('>');
+      } else if (*subkey_here == '\n') {
+	subkey.push_back('\\'); subkey.push_back('n');
+      } else if (*subkey_here == '\r') {
+	subkey.push_back('\\'); subkey.push_back('r');
+      } else if (*subkey_here == '\\') {
+	subkey.push_back('\\'); subkey.push_back('\\');
+      } else {
+	subkey.push_back (*subkey_here);
+      }
+      ++subkey_here;
+    }
+    subkey.push_back('>');
+
+    // add the values
+    text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
+    text_tarray::const_iterator subvalue_end = (*info_here).second.end();
+    while (subvalue_here != subvalue_end) {
+      data += subkey;
+      
+      text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
+      text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
+      while (thissubvalue_here != thissubvalue_end) {
+	if (*thissubvalue_here == '>') {
+	  data.push_back('\\'); data.push_back('>');
+	} else if (*thissubvalue_here == '\n') {
+	  data.push_back('\\'); data.push_back('n');
+	} else if (*thissubvalue_here == '\r') {
+	  data.push_back('\\'); data.push_back('r');
+	} else if (*thissubvalue_here == '\\') {
+	  data.push_back('\\'); data.push_back('\\');
+	} else {
+	  data.push_back (*thissubvalue_here);
+	}
+	
+	++thissubvalue_here;
+      }
+      
+      data.push_back('\n');
+      ++subvalue_here;
+    }
+
+    ++info_here;
+  }
+
+  return setkeydata(key, data);
+}
+
+
+// replaces the .fc, .lc, .pr, .rt, .ns and .ps syntax (first child, 
+// last child, parent, root, next sibling, previous sibling) 
+// it expects child, parent, etc. to exist if syntax has been used
+// so you should test before using
+text_t dbclass::translate_OID (const text_t &inOID, infodbclass &info)
+{
+  if (inOID.size() < 4) return inOID;
+  if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID;
+
+  text_t OID = inOID;
+  text_tarray tailarray;
+  text_t tail = substr (OID.end()-3, OID.end());
+  if (tail == ".rt") {
+    get_top (inOID, OID);
+    return OID;
+  }
+  while (tail == ".fc" || tail == ".lc" || tail == ".pr" || 
+	 tail == ".ns" || tail == ".ps") {
+    tailarray.push_back(tail);
+    OID.erase (OID.end()-3, OID.end());
+    tail = substr (OID.end()-3, OID.end());
+    if (tail == ".rt") {
+      get_top (inOID, OID);
+      return OID;
+    }
+  }
+
+  if (tailarray.empty()) return inOID;
+  text_tarray::const_iterator begin = tailarray.begin();
+  text_tarray::const_iterator here = tailarray.end() - 1;
+
+  while (here >= begin) {
+
+    if (*here == ".fc")
+      get_first_child (OID, info);
+    else if (*here == ".lc")
+      get_last_child (OID, info);
+    else if (*here == ".pr")
+      OID = get_parent (OID);
+    else if (*here == ".ns")
+      get_next_sibling (OID, info);
+    else if (*here == ".ps")
+      get_previous_sibling (OID, info);
+    
+    if (here == begin)
+      break;
+    --here;
+  }
+
+  return OID;
+}
+
+
+void dbclass::get_first_child (text_t &OID, infodbclass &info)
+{
+  text_t firstchild;
+  if (getinfo (OID, info)) {
+    text_t &contains = info["contains"];
+    if (!contains.empty()) {
+      text_t parent = OID;
+      getdelimitstr (contains.begin(), contains.end(), ';', firstchild);
+      if (firstchild.empty()) OID = contains;
+      else OID = firstchild;
+      if (*(OID.begin()) == '"') translate_parent (OID, parent);
+    }
+  }
+}
+
+
+void dbclass::get_last_child (text_t &OID, infodbclass &info)
+{
+  text_tarray children;
+  if (getinfo (OID, info)) {
+    text_t &contains = info["contains"];
+    if (!contains.empty()) {
+      text_t parent = OID;
+      splitchar (contains.begin(), contains.end(), ';', children);
+      OID = children.back();
+      if (*(OID.begin()) == '"') translate_parent (OID, parent);
+    }
+  }
+}
+
+  
+void dbclass::get_next_sibling (text_t &OID, infodbclass &info)
+{
+  text_tarray siblings;
+  text_t parent = get_parent (OID);
+	     
+  if (getinfo (parent, info)) {
+    text_t &contains = info["contains"];
+    if (!contains.empty()) {
+      splitchar (contains.begin(), contains.end(), ';', siblings);
+      text_tarray::const_iterator here = siblings.begin();
+      text_tarray::const_iterator end = siblings.end();
+      text_t shrunk_OID = OID;
+      shrink_parent (shrunk_OID);
+      while (here != end) {
+	if (*here == shrunk_OID && (here+1 != end)) {
+	  OID = *(here+1);
+	  if (*(OID.begin()) == '"') translate_parent (OID, parent);
+	  break;
+	}
+	++here;
+      }
+    }
+  }
+}
+
+
+void dbclass::get_previous_sibling (text_t &OID, infodbclass &info)
+{
+  text_tarray siblings;
+  text_t parent = get_parent (OID);
+
+  if (getinfo (parent, info)) {
+    text_t &contains = info["contains"];
+    if (!contains.empty()) {
+      splitchar (contains.begin(), contains.end(), ';', siblings);
+      text_tarray::const_iterator here = siblings.begin();
+      text_tarray::const_iterator end = siblings.end();
+      text_t shrunk_OID = OID;
+      shrink_parent (shrunk_OID);
+      while (here != end) {
+	if (*here == shrunk_OID && (here != siblings.begin())) {
+	  OID = *(here-1);
+	  if (*(OID.begin()) == '"') translate_parent (OID, parent);
+	  break;
+	}
+	++here;
+      }
+    }
+  }
+}
Index: /gsdl/trunk/gs2common/src/lib/dbclass.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/dbclass.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/dbclass.h	(revision 16561)
@@ -0,0 +1,113 @@
+/**********************************************************************
+ *
+ * dbclass.h -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#ifndef DBCLASS_H
+#define DBCLASS_H
+
+
+#include "text_t.h"
+#include "infodbclass.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+#define DB_READER         1
+#define DB_WRITER         2
+#define DB_WRITER_CREATE  3
+
+
+class dbclass
+{
+public:
+  dbclass() { logout = &cerr; }
+  virtual ~dbclass();
+
+  void setlogout (ostream *logout_arg) { logout = logout_arg; }
+
+
+  // -----------------------------------------------------------------------------------------------
+  // These functions MUST be implemented by subclasses, as they are database-specific
+
+  // returns true if opened
+  virtual bool opendatabase (const text_t &filename, int mode, int num_retrys, bool need_filelock) = 0;
+
+  virtual void closedatabase () = 0;
+
+  virtual void deletekey (const text_t &key) = 0;
+
+  // returns file extension string
+  virtual text_t getfileextension () = 0;
+
+  // returns true on success
+  virtual bool getkeydata (const text_t& key, text_t &data) = 0;
+
+  // returns array of keys
+  virtual text_tarray getkeys () = 0;
+
+  // returns true on success
+  virtual bool setkeydata (const text_t &key, const text_t &data) = 0;
+
+
+  // -----------------------------------------------------------------------------------------------
+  // These functions may be overwritten by subclasses, but a default implementation is provided
+
+  // returns true if exists
+  virtual bool exists (const text_t& key);
+
+  // returns true on success
+  virtual bool getinfo (const text_t& key, infodbclass &info);
+
+  // returns true on success
+  virtual bool setinfo (const text_t &key, const infodbclass &info);
+
+
+  // replaces the .c, .p, .n, .l syntax (child, parent, next, previous) 
+  // it expects child, parent, etc. to exist if syntax has been used
+  // so you should test before using
+  text_t translate_OID(const text_t &OID, infodbclass &info);
+
+protected:
+  ostream *logout;
+
+  // returns true on success
+  bool getinfoline (text_t::iterator &here, text_t::iterator end, text_t &key, text_t &value);
+
+  void get_first_child (text_t &OID, infodbclass &info);
+  void get_last_child (text_t &OID, infodbclass &info);
+  void get_next_sibling (text_t &OID, infodbclass &info);
+  void get_previous_sibling (text_t &OID, infodbclass &info);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/display.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/display.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/display.cpp	(revision 16561)
@@ -0,0 +1,1834 @@
+/**********************************************************************
+ *
+ * display.cpp -- Context sensitive macro language
+ * 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.
+ *
+ *********************************************************************/
+
+#include "display.h"
+#include "gsdlunicode.h"
+#include "unitool.h"
+#include <assert.h>
+
+// include to get NULL
+#include <stdlib.h>
+
+text_t displayclass::defaultpackage = "Global";
+
+/////////////////////////////////////
+// misc classes
+/////////////////////////////////////
+
+
+// precedencetype defines a 'precedence value' for each parameter
+typedef map<text_t, double, lttext_t> precedencetype;
+
+
+
+inline bool operator==(const mvalue &x, const mvalue &y) {
+  return (x.filename==y.filename && x.value==y.value);
+}
+
+inline bool operator<(const mvalue &x, const mvalue &y) {
+  return (x.filename<y.filename ||
+	  (x.filename==y.filename && x.filename<y.filename));
+}
+
+
+
+/////////////////////////////////////
+// stuff for parammacros_t
+/////////////////////////////////////
+  
+typedef map<text_t, mvalue, lttext_t> defparammap;
+typedef map<text_t, defparammap, lttext_t> defmacromap;
+typedef map<text_t, defmacromap, lttext_t> defpackagemap;
+
+// all methods in parammacros_t assume the parameters
+// and packages in a standard form and not empty
+class parammacros_t
+{
+protected:
+  defpackagemap macros;
+
+public:
+  typedef defpackagemap::const_iterator const_package_iterator;
+  typedef defpackagemap::size_type package_size_type;
+
+  // setmacro returns 0 if there was no error,
+  //            -1 if it redefined a macro
+  //            -2 if it hid a Global macro
+  //            -3 if it redefined a macro and hid a Global macro
+  //            -4 if either a package, macroname, or params were not supplied
+  int setmacro(const text_t &package,
+	  const text_t &macroname, 
+	  const text_t &params, 
+	  const text_t &filename, 
+	  const text_t &macrovalue);
+
+  void delmacro(const text_t &package,
+	      const text_t &macroname, 
+	      const text_t &params);
+
+  void clear () {macros.erase(macros.begin(), macros.end());}
+
+  // top level stuff
+  const_package_iterator package_begin () const {return macros.begin();}
+  const_package_iterator package_end () const {return macros.end();}
+  bool package_empty () const {return macros.empty();}
+  package_size_type package_size() const {return macros.size();}
+
+  // methods to find things
+  defmacromap *package_find (const text_t &packagename);
+  defparammap *macro_find (const text_t &packagename, 
+			   const text_t &macroname);
+  mvalue *parameter_find (const text_t &packagename, 
+			  const text_t &macroname,
+			  const text_t &parametername);
+};
+
+
+// setmacro returns 0 if there was no error,
+//            -1 if it redefined a macro
+//            -2 if it hid a Global macro
+//            -3 if it redefined a macro and hid a Global macro
+//            -4 if either a package, macroname, or params were not supplied
+int parammacros_t::setmacro(const text_t &package,
+			 const text_t &macroname, 
+			 const text_t &params, 
+			 const text_t &filename, 
+			 const text_t &macrovalue)
+{
+  paramhashtype paramhash;
+
+  defmacromap *macromapptr;
+  defparammap *parammapptr;
+  mvalue *mvalueptr;
+
+  int warning = 0;
+
+  // check to see if we would be hiding a Global macro with the 
+  // same name. Note: it doesn't matter what parameters are used
+  // a Global macro will still be hid by a non-Global one with
+  // the same name.
+  if ((package != displayclass::defaultpackage) && 
+      (macro_find (displayclass::defaultpackage, macroname) != NULL))
+    {
+      warning -= 2; // found the macroname
+    }
+
+  // define this macro
+  macromapptr = &(macros[package]); // -- package
+  parammapptr = &((*macromapptr)[macroname]); // -- macro name
+  // -- parameters
+  if ((*parammapptr).find(params) != (*parammapptr).end()) {
+    warning -= 1; // found the parameters
+  }
+  mvalueptr = &((*parammapptr)[params]);
+
+  // -- value
+  (*mvalueptr).filename = filename;
+  (*mvalueptr).value = macrovalue;
+
+  return warning;
+}
+
+void parammacros_t::delmacro(const text_t &package,
+			     const text_t &macroname, 
+			     const text_t &params)
+{
+  // make sure everything was supplied
+  if (package.empty() || macroname.empty() || params.empty()) return;
+
+  // find the package and macroname
+  defparammap *parammapptr = macro_find(package, macroname);
+  if (parammapptr == NULL) return;
+
+  // find the parameters
+  defparammap::iterator parammapit = parammapptr->find(params);
+  if (parammapit == parammapptr->end()) return;
+
+  // finally delete this element
+  parammapptr->erase(parammapit);
+}
+
+defmacromap *parammacros_t::package_find (const text_t &packagename)
+{
+  if (packagename.empty()) return NULL;
+
+  defpackagemap::iterator it = macros.find(packagename);
+  if (it == macros.end()) return NULL;
+
+  return &((*it).second);
+}
+
+defparammap *parammacros_t::macro_find (const text_t &packagename, 
+					const text_t &macroname)
+{
+  defmacromap *macromapptr = package_find(packagename);
+  if (macromapptr == NULL || macroname.empty()) return NULL;
+  
+  defmacromap::iterator it = (*macromapptr).find(macroname);
+  if (it == (*macromapptr).end()) return NULL;
+
+  return &((*it).second);
+}
+
+mvalue *parammacros_t::parameter_find (const text_t &packagename, 
+					 const text_t &macroname,
+					 const text_t &parametername)
+{
+  defparammap *parammapptr = macro_find(packagename, macroname);
+  if (parammapptr == NULL || parametername.empty()) return NULL;
+
+  defparammap::iterator it = (*parammapptr).find(parametername);
+  if (it == (*parammapptr).end()) return NULL;
+
+  return &((*it).second);
+}
+
+
+
+/////////////////////////////////////
+// stuff for currentmacros_t
+/////////////////////////////////////
+
+typedef map<text_t, mvalue, lttext_t> curmacromap;
+typedef map<text_t, curmacromap, lttext_t> curpackagemap;
+
+// all methods in currentmacros_t assume the parameters
+// and packages in a standard form and not empty
+class currentmacros_t 
+{
+protected:
+  curpackagemap macros;
+
+public:
+  typedef curpackagemap::const_iterator const_package_iterator;
+  typedef curpackagemap::size_type package_size_type;
+
+  // setmacro returns 0 if there was no error,
+  //            -1 if it redefined a macro
+  //            -4 if either a package, or macroname were not supplied
+  int setmacro(const text_t &package,
+	  const text_t &macroname, 
+	  const text_t &filename, 
+	  const text_t &macrovalue);
+
+  void delmacro(const text_t &package,
+		const text_t &macroname);
+
+  void clear () {macros.erase(macros.begin(), macros.end());}
+
+  // top level stuff
+  const_package_iterator package_begin () const {return macros.begin();}
+  const_package_iterator package_end () const {return macros.end();}
+  bool package_empty () const {return macros.empty();}
+  package_size_type package_size() const {return macros.size();}
+
+  // methods to find things
+  curmacromap *package_find (const text_t &packagename);
+  mvalue *macro_find (const text_t &packagename, 
+		      const text_t &macroname);
+};
+
+
+// setmacro returns 0 if there was no error,
+//            -1 if it redefined a macro
+//            -4 if either a package, or macroname were not supplied
+int currentmacros_t::setmacro(const text_t &package,
+			 const text_t &macroname, 
+			 const text_t &filename, 
+			 const text_t &macrovalue)
+{
+  int warning = 0;
+
+  // make sure everything was supplied
+  if (package.empty() || macroname.empty()) return -4;
+
+  // define this macro
+  curmacromap *macromapptr = &(macros[package]);
+  if ((*macromapptr).find(macroname) != (*macromapptr).end()) 
+    {
+      warning -= 1; // found the macroname
+    }
+  mvalue *mvalueptr = &((*macromapptr)[macroname]);
+
+  // -- value
+  (*mvalueptr).filename = filename;
+  (*mvalueptr).value = macrovalue;
+
+  return warning;
+}
+
+void currentmacros_t::delmacro(const text_t &package,
+			     const text_t &macroname)
+{
+  // make sure everything was supplied
+  if (package.empty() || macroname.empty()) return;
+
+  // find the package
+  curmacromap *macromapptr = package_find(package);
+  if (macromapptr == NULL) return;
+
+  // find the macroname
+  curmacromap::iterator macromapit = macromapptr->find(macroname);
+  if (macromapit == macromapptr->end()) return;
+
+  // finally delete this element
+  macromapptr->erase(macromapit);
+}
+
+curmacromap *currentmacros_t::package_find (const text_t &packagename)
+{
+  if (packagename.empty()) return NULL;
+
+  curpackagemap::iterator it = macros.find(packagename);
+  if (it == macros.end()) return NULL;
+
+  return &((*it).second);
+}
+
+mvalue *currentmacros_t::macro_find (const text_t &packagename, 
+				     const text_t &macroname)
+{
+  curmacromap *macromapptr = package_find(packagename);
+  if (macromapptr == NULL || macroname.empty()) return NULL;
+
+  curmacromap::iterator it = (*macromapptr).find(macroname);
+  if (it == (*macromapptr).end()) return NULL;
+
+  return &((*it).second);
+}
+
+
+
+
+/////////////////////////////////////
+// support functions
+/////////////////////////////////////
+
+// this calculation of precedence will make one occurance of a high
+// precedence item override many occurances of lower precedence items
+void calcprecedence (const text_t &precedstr, precedencetype &precedhash) 
+{
+  text_t param;
+  double multiple = 5.0;
+  double nextprec = multiple;
+
+  text_t::const_iterator here, end;
+
+  // set precedhash to an empty hash
+  precedhash.erase(precedhash.begin(), precedhash.end());
+
+  // iterate through paramstring ignoring space and extracting
+  // out key value pairs.
+  here = precedstr.begin();
+  end = precedstr.end();
+  while (here != end) 
+    {
+      // get the next parameter
+      param.clear(); // set param to an empty string
+      
+      while (here != end) 
+	{
+	  if (*here == ',') 
+	    {
+	      // found the end of the parameter
+	      ++here;
+	      break;
+	    } 
+	  else if (*here == ' ') 
+	    {
+	      // do nothing (ignore spaces)
+	    } 
+	  else 
+	    {
+	      // character in parameter
+	      param.push_back (*here); // copy this character
+	    }
+	  
+	  ++here;
+	}
+
+      // if a parameter was found insert its value
+      if (!param.empty()) 
+	{
+	  precedhash[param] = nextprec;
+	  nextprec *= multiple;
+	}
+    }
+}
+
+
+// decides how specific any given parameter is to the page parameters. Returns 
+// negative if any options are different or else the sum of the precedence for
+// the options which match. 
+double getspecificness(const paramhashtype &pageparams, 
+		       const paramhashtype &theseparams, 
+		       const precedencetype &precedhash) 
+{
+  double specificness = 0.0;
+  paramhashtype::const_iterator here, pagevalueit;
+  precedencetype::const_iterator precedit;
+  text_t thesekey, thesevalue, constignore("ignore");
+  
+  here = theseparams.begin();
+  while (here != theseparams.end()) 
+    {
+      thesekey = (*here).first;
+      thesevalue = (*here).second;
+      
+      if (thesekey == constignore) 
+	{
+	  // do nothing, this is a placeholder
+	} 
+      else 
+	{
+	  pagevalueit = pageparams.find(thesekey);
+	  if ((pagevalueit != pageparams.end()) && 
+	      ((*pagevalueit).second == thesevalue)) 
+	    {
+	      // this parameter fits, add its precedence value
+	      precedit = precedhash.find(thesekey);
+	      if (precedit == precedhash.end()) 
+		specificness += 1.0; // no precedence defined
+	      else 
+		  specificness += (*precedit).second;
+	    }
+	  else 
+	    {
+	      return -1.0; // did not match
+	    }
+	}
+
+      ++here;
+    }
+  
+  return specificness;
+}
+
+
+void splitparams (const text_t &paramstring, paramhashtype &paramhash)
+{
+  text_t key;
+  text_t value;
+
+  text_t::const_iterator here, end;
+
+  // set paramhash to an empty hash
+  paramhash.erase(paramhash.begin(), paramhash.end());
+
+  // iterate through paramstring ignoring space and extracting
+  // out key value pairs.
+  here = paramstring.begin();
+  end  = paramstring.end();
+  while (here != end) 
+    {
+      // reset key and value
+      key.clear();
+      value.clear();
+
+      // get key
+      while (here != end) 
+	{
+	  if (*here == ',') 
+	    {
+	      // have a key with no value
+	      break;
+	    } 
+	  else if (*here == '=') 
+	    {
+	      // found the end of the key
+	      ++here; 
+	      break;
+	    } 
+	  else if (*here == ' ') 
+	    {
+	      // do nothing (ignore spaces)
+	    } 
+	  else 
+	    {
+	      // character in key
+	      key.push_back (*here); // copy this character
+	    }
+	  ++here;
+	}
+
+      // get value
+      while (here != end) 
+	{
+	  if (*here == '=') 
+	    {
+	      // multiple values for one key, ignore previous
+	      // values
+	      value.clear();
+	    } 
+	  else if (*here == ',') 
+	    {
+	      // found the end of the value
+	      ++here;
+	      break;
+	    } 
+	  else if (*here == ' ') 
+	    {
+	      // do nothing (ignore spaces)
+	    } 
+	  else 
+	    {
+	      // character in value
+	      value.push_back (*here); // copy this character
+	    }
+	  ++here;
+	}
+    
+      // if a key was found insert the key/value pair in 
+      // the map 
+      if (!key.empty()) 
+	{
+	  paramhash[key] = value;
+	}
+    }
+}
+
+
+void joinparams (const paramhashtype &paramhash, text_t &paramstring)
+{
+  //set paramstring to an empty string
+  paramstring.clear();
+
+  //iterate through the paramhash
+  paramhashtype::const_iterator thepair;
+  int firstparam = 1;
+
+  for (thepair=paramhash.begin();thepair!=paramhash.end(); ++thepair)
+    {
+      if (!firstparam) paramstring.push_back(',');
+      firstparam = 0;
+      
+      // insert pairs of "key=value"
+      text_t thevalue;
+      paramstring.append((*thepair).first);
+      paramstring.push_back('=');
+      paramstring.append((*thepair).second);
+    }
+}
+
+
+
+/////////////////////////////////////
+// parsing support functions
+/////////////////////////////////////
+
+inline int my_isalpha (unsigned short c) 
+{
+  return ((c >= 'A' && c <= 'Z') ||
+	  (c >= 'a' && c <= 'z'));
+} 
+
+inline int my_isalphanum (unsigned short c) 
+{
+  return ((c >= 'A' && c <= 'Z') ||
+	  (c >= 'a' && c <= 'z') || (c >='0' && c <= '9'));
+} 
+ 
+
+
+inline unsigned short my_ttnextchar (text_t::const_iterator &here, 
+				     text_t::const_iterator &end) 
+{
+  if (here != end) 
+    {
+      ++here;
+      if (here != end) return (*here);
+    }
+  return '\0';
+}
+
+
+
+/////////////////////////////////////
+// methods for display
+/////////////////////////////////////
+
+// public methods for display
+
+displayclass::displayclass ()
+{
+  defaultmacros    = new parammacros_t;
+  collectionmacros = new parammacros_t;
+  defaultfiles     = new fileinfomap;
+  
+  orderparamlist = new paramspeclist;
+  currentmacros = new currentmacros_t;
+
+  outc = NULL;
+
+  logout = NULL;
+
+  /* this is a bit of a hack, but if the displayclass has never seen this
+     parameter before when it does openpage() then macros with this set will
+     always be ignored. And we set collection-specific macros after doing
+     openpage() - jrm */
+  allparams.insert("l=en");
+}
+
+
+displayclass::~displayclass ()
+{
+  delete defaultmacros;
+  delete collectionmacros;
+  delete defaultfiles;
+  
+  delete orderparamlist;
+  delete currentmacros;
+}
+  
+// setdefaultmacro adds an entry to the list of default macros 
+// returns 0 if there was no error,
+//        -1 if it redefined a macro
+//        -2 if it hid a Global macro
+//        -3 if it redefined a macro and hid a Global macro
+//        -4 if no macroname was supplied
+int displayclass::setdefaultmacro (const text_t &package, const text_t &macroname, 
+				   const text_t &mparams, const text_t &macrovalue) 
+{
+  return setdefaultmacro (package, macroname, mparams, "memory", macrovalue);
+}
+
+
+
+// as we are using one character lookahead the
+// value of line might be off by one.
+// the input file must be in the utf-8 or unicode format
+// initially for each file isunicode should be set to 0 and 
+// bigendian should be set to 1
+// 0 will be returned when the end of the file has been found
+unsigned short my_uni_get (/*unistream*/ifstream &fin, int &line, 
+			   int &isunicode, int &bigendian) {
+  unsigned short c = 0;
+  
+  if (isunicode) {
+    // unicode text
+    // get the next two characters
+    unsigned char c1 = 0, c2 = 0;
+    
+    if (!fin.eof()) c1=fin.get();
+    if (!fin.eof()) c2=fin.get();
+    else c1 = 0;
+
+    // if they indicate the order get the next character
+    // otherwise just get these characters
+    if (c1 == 0xff && c2 == 0xfe) {
+      bigendian = 0;
+      c = my_uni_get (fin, line, isunicode, bigendian);
+    } else if (c1 == 0xfe && c2 == 0xff) {
+      bigendian = 1;
+      c = my_uni_get (fin, line, isunicode, bigendian);
+    } else c = (bigendian) ? (c1*256+c2) : (c2*256+c1);
+    
+  } else {
+    // utf-8 text
+    // how many characters we get depends on what we find
+    unsigned char c1 = 0, c2 = 0, c3 = 0;
+    while (!fin.eof()) {
+      c1=fin.get();
+      if (c1 == 0xfe || c1 == 0xff) {
+	// switch to unicode
+	isunicode = 1;
+	if (!fin.eof()) c2=fin.get();
+
+	if (c1 == 0xff && c2 == 0xfe) bigendian = 0;
+	else bigendian = 1;
+
+	c = my_uni_get (fin, line, isunicode, bigendian);
+	break;
+
+      } else if (c1 <= 0x7f) {
+	// one byte character
+	c = c1;
+	break;
+
+      } else if (c1 >= 0xc0 && c1 <= 0xdf) {
+	// two byte character
+	if (!fin.eof()) c2=fin.get();
+	c = ((c1 & 0x1f) << 6) + (c2 & 0x3f);
+	break;
+
+      } else if (c1 >= 0xe0 && c1 <= 0xef) {
+	// three byte character
+	if (!fin.eof()) c2=fin.get();
+	if (!fin.eof()) c3=fin.get();
+	c = ((c1 & 0xf) << 12) + ((c2 & 0x3f) << 6) + (c3 & 0x3f);
+	break;
+      }
+
+      // if we get here there was an error in the file, we should
+      // be able to recover from it however, maybe the file is in
+      // another encoding
+    }
+  }
+
+  if (c == '\n') ++line;
+  return c;
+}
+
+
+
+// loads a macro file into the specified macrotable datastructure
+// returns 0 if didn't need to load the file (it was already loaded)
+//         1 if was (re)loaded
+//        -1 an error occurred while trying to load the file
+int displayclass::loadparammacros (parammacros_t* macrotable, const text_t &thisfilename) {
+  // convert the filename to a C string
+  char *filenamestr = thisfilename.getcstr();
+  outconvertclass text_t2ascii;
+
+  // see if we need to open this file -- this isn't implemented yet
+  
+  // open the file
+  //  unistream fin (filenamestr);
+  ifstream fin (filenamestr);
+
+  if (fin.fail()) return -1; // read failed
+
+  text_t package = defaultpackage;
+  int line = 1;
+  int isunicode = 0, bigendian = 1;
+
+  // pre-fetch the next character
+  unsigned short c = my_uni_get(fin, line, isunicode, bigendian);
+
+  text_t macropackage, macroname, macroparameters, macrovalue;
+  int err; // for keeping track of whether an error occurred somewhere
+
+  while (!fin.eof()) {
+    // expect: white space, comment, "package", or macroname
+    if (is_unicode_space(c)) {
+      // found some white-space
+      c = my_uni_get(fin, line, isunicode, bigendian);
+
+    } else if (c == '#') {
+      // found the start of a comment
+      // skip all characters up to the end of the line
+      c = my_uni_get(fin, line, isunicode, bigendian); // skip the '#'
+      while (!fin.eof ()) {
+	if (c == '\n') break;
+	c = my_uni_get(fin, line, isunicode, bigendian);
+      }
+
+    } else if (c == 'p') {
+      // found the start of 'package' (hopefully)
+      // get everything up to the next space
+      text_t tmp;
+      while (!fin.eof() && my_isalpha(c)) {
+	tmp.push_back(c);
+	c = my_uni_get(fin, line, isunicode, bigendian);
+      }
+      // see if we have a package name
+      if (tmp == "package") {
+	// skip all white space
+	while (!fin.eof() && is_unicode_space(c)) 
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	
+	// get the package name
+	tmp.clear(); // init tmp
+	while (!fin.eof() && my_isalphanum(c)) {
+	  tmp.push_back(c);
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	}
+	package = tmp;
+	if (package.empty()) package = defaultpackage;
+
+      } else {
+	// error
+	if (logout != NULL) {
+	  (*logout) << text_t2ascii << text_t("Expected 'package' on line ") + line + text_t(" of ") + thisfilename + "\n";
+	}
+      }
+      
+    } else if (c == '_') {
+      // found the start of a macro (hopefully)
+      c = my_uni_get(fin, line, isunicode, bigendian); // skip the _
+
+      // init variables
+      err = 0;
+      macropackage = package;
+      macroname.clear(); // init macroname
+      macroparameters.clear(); // init macroname
+      macrovalue.clear(); // init macroname
+      
+      // get the macro name
+      while ((!fin.eof()) && (!is_unicode_space(c)) && 
+	     (c != '\\') && (c != '_') &&(c != ':') && 
+	     (macroname.size() < 80)) {
+	macroname.push_back(c);
+	c = my_uni_get(fin, line, isunicode, bigendian);
+      }
+	  
+      if (c == ':') {
+	// we actually had the macro package
+	c = my_uni_get(fin, line, isunicode, bigendian); // skip :
+	macropackage = macroname;
+	macroname.clear ();
+	
+	// get the macro name (honest!)
+	while ((!fin.eof()) && (!is_unicode_space(c)) && 
+	       (c != '\\') && (c != '_') &&(c != ':') && 
+	       (macroname.size() < 80)) {
+	  macroname.push_back(c);
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	}
+      }
+      
+      if (!err && c == '_') {
+	c = my_uni_get(fin, line, isunicode, bigendian); // skip the _
+	
+	// skip all white space
+	while (!fin.eof() && is_unicode_space(c)) c = my_uni_get(fin, line, isunicode, bigendian);
+      } else if (!err) err = 1;
+
+      // get the macro parameters (optional)
+      if (!err && c == '[') {
+	c = my_uni_get(fin, line, isunicode, bigendian); // skip the [
+	while ((!fin.eof()) && (c != '\n') && (c != '\\') && (c != ']')) {
+	  macroparameters.push_back(c);
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	}
+	
+	if (c == ']') {
+	  c = my_uni_get(fin, line, isunicode, bigendian); // skip the ]
+	  
+	  // skip all white space
+	  while (!fin.eof() && is_unicode_space(c)) c = my_uni_get(fin, line, isunicode, bigendian);
+	} 
+	else if (!err) err = 2;
+      }
+
+      // get the macro value
+      if (!err && c == '{') {
+	c = my_uni_get(fin, line, isunicode, bigendian); // skip the {
+	while ((!fin.eof()) && (c != '}')) {
+	  if (c == '\\') {
+	    macrovalue.push_back(c); // keep the '\'
+	    c = my_uni_get(fin, line, isunicode, bigendian); // store the *next* value regardless
+	    if (!fin.eof()) macrovalue.push_back(c);
+	    c = my_uni_get(fin, line, isunicode, bigendian);
+	  }
+	  macrovalue.push_back(c);
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	}
+	
+	if (c == '}') {
+	  c = my_uni_get(fin, line, isunicode, bigendian); // skip the }
+	  
+	  // define the macro
+	  err = setparammacro (macrotable, macropackage, macroname, macroparameters,
+			       thisfilename, macrovalue);
+	  if ((err == -1 || err == -3) && logout != NULL) {
+	    (*logout) << text_t2ascii << ("Warning: redefinition of _" + package + ":" + macroname + "_[" + macroparameters + "] on line " + line +  " of " + thisfilename + "\n");
+	    
+	  } else if (err == -2 && logout != NULL) {
+	    // I don't think we really want this warning since it's a very
+	    // common occurance - Stefan.
+
+	    //	    (*logout) << text_t2ascii << "Warning: _" << 
+	    //	      package << ":" << macroname << "_[" << macroparameters <<
+	    //	      "] on line ";
+	    //	    (*logout) << line;
+	    //	    (*logout) << text_t2ascii << " of " << 
+	    //	      thisfilename << " hides a Global macro with the same name\n";
+
+	  } else if (err == -4 && logout != NULL) {
+	    (*logout) << text_t2ascii << text_t("Error: macro name expected on line ") +  line + text_t(" of ") + thisfilename + "\n";
+	  }		  
+	  
+	  err = 0; // for the test below
+	}
+	else if (!err) err = 3;
+      }
+      else if (!err) err = 4;
+      
+      if (err) {
+	// found an error, skip to the end of the line
+	if (logout != NULL) {
+	  text_t message = "Error: ";
+	  if (err == 1) message += "'_'";
+	  else if (err == 2) message += "']'";
+	  else if (err == 3) message += "'}'";
+	  else if (err == 4) message += "'{'";
+	  message += " expected on line ";
+	  message += line;
+	  message += " of " + thisfilename + "\n";
+	  (*logout) << text_t2ascii << message;
+	  
+// 	  (*logout) << text_t2ascii << "Error: ";
+// 	  if (err == 1) (*logout) << text_t2ascii << "'_'";
+// 	  else if (err == 2) (*logout) << text_t2ascii << "']'";
+// 	  else if (err == 3) (*logout) << text_t2ascii << "'}'";
+// 	  else if (err == 4) (*logout) << text_t2ascii << "'{'";
+// 	  (*logout) << text_t2ascii << " expected on line ";
+// 	  (*logout) << line ;
+// 	  (*logout) << text_t2ascii << " of " << thisfilename << "\n";
+	}
+	while (!fin.eof ()) {
+	  if (c == '\n') break;
+	  c = my_uni_get(fin, line, isunicode, bigendian);
+	}
+      }
+      
+    } else {
+      // found an error, skip to the end of the line
+      if (logout != NULL) {
+	(*logout) << text_t2ascii << text_t("Error: Unexpected input on line ") + line + text_t(" of ") + thisfilename + "\n";
+      }
+      while (!fin.eof ()) {
+	if (c == '\n') break;
+	c = my_uni_get(fin, line, isunicode, bigendian);
+      }
+      
+    }
+  }
+  
+  fin.close ();
+  
+  // free up memory
+  delete []filenamestr;
+  return 0;
+}
+
+
+// loads a default macro file (if it isn't already loaded)
+// returns 0 if didn't need to load the file (it was already loaded)
+//         1 if was (re)loaded
+//        -1 an error occurred while trying to load the file
+int displayclass::loaddefaultmacros (const text_t &thisfilename) 
+{
+  return loadparammacros(defaultmacros,thisfilename);
+}
+
+// loads a collection specific macro file 
+// returns 0 if didn't need to load the file (it was already loaded)
+//         1 if was (re)loaded
+//        -1 an error occurred while trying to load the file
+int displayclass::loadcollectionmacros (const text_t &thisfilename) 
+{
+  return loadparammacros(collectionmacros,thisfilename);
+}
+
+
+void displayclass::unloaddefaultmacros () {
+  defaultmacros->clear();
+}
+
+void displayclass::unloadcollectionmacros () {
+  collectionmacros->clear();
+}
+
+// prepares to create a page.
+void displayclass::openpage (const text_t &thispageparams, 
+			const text_t &thisprecedence)
+{
+  // init variables
+  currentmacros->clear();
+
+  // reload any default macro files which have changed.
+  if (checkdefaultmacrofiles() < 0) 
+    {
+      // print error message
+    }
+
+  setpageparams (thispageparams, thisprecedence);
+}
+
+
+// changes the parameters for the current page.
+void displayclass::setpageparams (text_t thispageparams, 
+			     text_t thisprecedence)
+{
+  paramhashtype thissplitparams, splittheseparams;
+  precedencetype precedhash;
+  text_tset::iterator text_tsetit;
+  paramspec tmpps;
+
+  precedence = thisprecedence;
+  calcprecedence(thisprecedence, precedhash);
+
+  splitparams(thispageparams, thissplitparams);
+  joinparams(thissplitparams, thispageparams);
+  params = thispageparams;
+
+  // get a list of parameters with their specificness
+  orderparamlist->erase(orderparamlist->begin(), orderparamlist->end());
+  for (text_tsetit=allparams.begin();
+       text_tsetit!=allparams.end();
+       ++text_tsetit) 
+    {
+      tmpps.param = (*text_tsetit);
+      splitparams(tmpps.param, splittheseparams);
+      tmpps.spec = getspecificness(thissplitparams, splittheseparams, precedhash);
+      if (tmpps.spec >= 0) 
+	{
+	  orderparamlist->push_back (tmpps);
+	}
+    }
+
+  // sort the list
+  sort(orderparamlist->begin(), orderparamlist->end());
+
+#if 0 // debugging
+  outconvertclass text_t2ascii;
+
+  paramspeclist::iterator pshere = orderparamlist->begin();
+  paramspeclist::iterator psend = orderparamlist->end();
+  while (pshere != psend) 
+    {
+      cerr << text_t2ascii << "param=" << (*pshere).param;
+      cerr << " spec=" << (int)((*pshere).spec) << "\n";
+      ++pshere;
+    }
+#endif
+}
+
+
+// overrides (or sets) a macro for the current page.
+// returns 0 if there was no error,
+//        -1 if it redefined a macro
+//        -4 if no macroname was supplied
+int displayclass::setmacro (const text_t &macroname, 
+			    const text_t &package, 
+			    const text_t &macrovalue)
+{
+  // make sure a macroname was supplied
+  if (macroname.empty()) return -4;
+
+  // make package "Global" if it doesn't point to anything yet
+  if (package.empty()) 
+    return currentmacros->setmacro(defaultpackage, macroname, "memory", macrovalue);
+  
+  // set the macro
+  return currentmacros->setmacro(package, macroname, "memory", macrovalue);
+}
+
+int displayclass::setmacroif (const text_t &macro_to_set_and_test,
+			      const text_t &macro_to_set_if_macro_not_set, 
+			      const text_t &macroname, 	
+			      const text_t &package) {
+  
+  if (macroname.empty()) return -4;
+  if (macro_to_set_and_test.empty()) return -40;
+  
+  text_t temp;
+  expandstring(defaultpackage, macro_to_set_and_test, temp);
+  
+  if (package.empty()) {
+    if (temp == macro_to_set_and_test) return currentmacros->setmacro(defaultpackage, macroname, "memory", macro_to_set_if_macro_not_set);
+    return currentmacros->setmacro(defaultpackage, macroname, "memory", macro_to_set_and_test);
+  } else {
+    if (temp == macro_to_set_and_test) return currentmacros->setmacro(package, macroname, "memory", macro_to_set_if_macro_not_set);
+    return currentmacros->setmacro(package, macroname, "memory", macro_to_set_and_test);
+    
+  }
+}
+
+void displayclass::expandstring (const text_t &package, const text_t &inputtext,
+				 text_t &outputtext, int recursiondepth)
+{
+  text_t macroname, macropackage, macroargs;
+  text_t::const_iterator tthere = inputtext.begin();
+  text_t::const_iterator ttend = inputtext.end();
+  unsigned short c = '\0';
+  int openbrackets=0;
+
+  if (package.empty()) expandstring(defaultpackage, inputtext, outputtext, recursiondepth);
+
+  outputtext.clear();
+  // use one-character lookahead
+  if (tthere != ttend) c = (*tthere);
+  while (tthere != ttend) 
+    {
+      if (c == '\\')
+	{
+	  // found an escape character
+	  c = my_ttnextchar (tthere, ttend); // skip the escape character
+	  if (tthere != ttend) 
+	    {
+	      if (c == 'n') outputtext.push_back('\n');
+	      else if (c == 't') outputtext.push_back('\t');
+	      else outputtext.push_back(c);
+	    }
+	  c = my_ttnextchar (tthere, ttend);
+	  
+	} 
+      else if (c == '_') 
+	{
+	  // might have found the start of a macro
+      
+	  // embedded macros (macros within the names
+	  // of other macros) are no longer supported -- Rodger.
+      
+	  // save the current state (in case we need to back out)
+	  text_t::const_iterator savedtthere = tthere;
+	  unsigned short savedc = c;
+	  
+	  macroname.clear();
+	  macropackage = package;
+	  macroargs.clear();
+      
+	  c = my_ttnextchar (tthere, ttend); // skip the '_'
+	  
+	  // get the macroname
+	  while (tthere != ttend && (!is_unicode_space(c)) && 
+		 (c != '\\') && (c != '_') &&(c != ':') && 
+		 (macroname.size() < 80)) 
+	    {
+	      macroname.push_back((unsigned char)c);
+	      c = my_ttnextchar (tthere, ttend);
+	    }
+      
+	  if (c == ':') 
+	    {
+	      // we actually had the macro package
+	      c = my_ttnextchar (tthere, ttend);
+	      macropackage = macroname;
+	      macroname.clear ();
+	
+	      // get the macro name (honest!)
+	      while ((tthere != ttend) && (!is_unicode_space(c)) && 
+		     (c != '\\') && (c != '_') &&(c != ':') && 
+		     (macroname.size() < 80)) 
+		{
+		  macroname.push_back((unsigned char)c);
+		  c = my_ttnextchar (tthere, ttend);
+		}
+	    }
+      
+	  if ((tthere != ttend) && (c == '_') &&
+	      (macroname.size() > 0)) 
+	    {
+	      // found a macro!!!! (maybe)
+	      c = my_ttnextchar (tthere, ttend); // skip the '_'
+	
+	      // get the parameters (if there are any)
+	      if ((tthere != ttend) && (c == '(')) 
+		{
+		  c = my_ttnextchar (tthere, ttend); // skip '('
+	  	  ++openbrackets;
+		  // have to be careful of quotes
+		  unsigned short quote = '\0';
+		  while (tthere != ttend) 
+		    {
+		      if (c == '\\') 
+			{
+			  // have an escape character, save escape 
+			  // character and next character as well
+			  macroargs.push_back (c);
+			  c = my_ttnextchar (tthere, ttend); 
+	      
+			} 
+		      else if (quote == '\0') 
+			{
+			  // not in a quote at the moment
+			  if (c == '(')
+			    {
+			      ++openbrackets;
+			    }
+			  else if (c == ')') 
+			    {
+			      --openbrackets;
+			      if (openbrackets==0) {
+				// found end of the arguments
+				c = my_ttnextchar (tthere, ttend); // skip ')'
+				break;
+			      } else {
+				// nested brackets
+			      }
+			    } 
+			  else if (c == '\'' || c == '\"') 
+			    {
+			      // found a quote
+			      quote = c;
+			    }
+	      
+			} 
+		      else 
+			{
+			  // we are in a quote
+			  if (c == quote) quote = '\0';
+			}
+	    
+		      // save this character
+		      if (tthere != ttend) macroargs.push_back (c);
+		      c = my_ttnextchar (tthere, ttend);
+		    } // while (tthere != ttend)
+		}
+	
+	      //
+	      // we now have the macropackage, macroname, and macroargs
+	      //
+	
+	      // try to expand out this macro
+	      text_t expandedmacro;
+	      if (macro (macroname, macropackage.empty()?defaultpackage:macropackage, macroargs, expandedmacro, recursiondepth)) 
+		{
+		  // append the expanded macro
+		  outputtext += expandedmacro;
+		} 
+	      else
+		{
+		  // wasn't a macro
+		  tthere = savedtthere;
+		  c = savedc;
+		  outputtext.push_back(c); // the '_'
+		  c = my_ttnextchar (tthere, ttend);
+		}
+	
+
+	    }
+	  else
+	    {
+	      // wasn't a macro
+	      tthere = savedtthere;
+	      c = savedc;
+	      outputtext.push_back(c); // the '_'
+	      c = my_ttnextchar (tthere, ttend);
+	    }
+
+	}
+      else 
+	{
+	  // nothing of interest
+	  outputtext.push_back(c); // the '_'
+	  c = my_ttnextchar (tthere, ttend);
+	}
+    } // end of while (tthere != ttend)
+}
+
+ostream *displayclass::setlogout (ostream *thelogout) {
+
+  ostream *oldlogout = logout;
+  logout = thelogout;
+  return oldlogout;
+}
+
+// protected methods for display
+
+// reloads any default macro files which have changed
+// returns 0 no errors occurred
+//        -1 an error occurred while trying to load one of the files
+int displayclass::checkdefaultmacrofiles ()
+{
+  // this isn't implemented yet
+  return 0;
+}
+
+// isdefaultmacro sees if there is an entry in the list of
+// default macros with the given package and macro name
+// returns 0 if no macros in the package or in the global package
+//           were found
+//         1 if no macros in the package were found but a macro
+//           in the global package was found
+//         2 if a macro in the given package was found
+int displayclass::isdefaultmacro (const text_t &package, const text_t &macroname) {
+  // make sure a macroname was supplied
+  if (macroname.empty()) return 0;
+
+  if (!package.empty()) {
+    // check the given package
+    if (defaultmacros->macro_find(package, macroname) != NULL) return 2;
+  }
+    
+  // check the Global package
+  if (defaultmacros->macro_find(defaultpackage, macroname) != NULL) return 1;
+  // not found
+  return 0;
+}
+
+// havemacro sees if there is an entry in the list of macros or
+// default macros with the given package and macro name
+// returns 0 if no macros in the package or in the global package
+//           were found
+//         1 if no macros in the package were found but a macro
+//           in the global package was found
+//         2 if a macro in the given package was found
+//         4 if no macros in the package were found but a macro
+//           in the global package was found in default macros
+//         8 if a macro in the given package was found in default
+int displayclass::havemacro(const text_t &package, const text_t &macroname)
+{
+  // make sure a macroname was supplied
+  if (macroname.empty()) return 0;
+  
+  // make package "Global" if it doesn't point to anything yet
+  if (package.empty()) {
+    // check the Global package in current macros list
+    if (currentmacros->macro_find(defaultpackage, macroname) != NULL) return 1;
+    
+    // check the Global package in default macros list
+    if (defaultmacros->macro_find(defaultpackage, macroname) != NULL) return 4;
+  } else {
+    // check the given package in current macros list
+    if (currentmacros->macro_find(package, macroname) != NULL) return 2;
+    
+    // check the Global package in current macros list
+    if (currentmacros->macro_find(defaultpackage, macroname) != NULL) return 1;
+    
+    // check the given package in default macros list
+    if (defaultmacros->macro_find(package, macroname) != NULL) return 8;
+    
+    // check the Global package in default macros list
+    if (defaultmacros->macro_find(defaultpackage, macroname) != NULL) return 4;
+  }
+
+  // nothing was found
+  return 0;
+}
+  
+  
+// setparammacro adds an entry to the list of default macros 
+// returns 0 if there was no error,
+//        -1 if it redefined a macro
+//        -2 if it hid a Global macro
+//        -3 if it redefined a macro and hid a Global macro
+//        -4 if no macroname was supplied
+int displayclass::setparammacro (parammacros_t* macrotable, 
+				 const text_t &package, 
+				 const text_t &macroname, 
+				 text_t macroparams, 
+				 const text_t &filename,
+				 const text_t &macrovalue) 
+{
+  // make sure a macroname was supplied
+  if (macroname.empty()) return -4;
+
+  // put the parameters in a standard form
+  paramhashtype paramhash;
+  if (macroparams.empty()) macroparams = "ignore=yes";
+  splitparams (macroparams, paramhash);
+  joinparams (paramhash, macroparams);
+
+  // remember these parameters
+  allparams.insert (macroparams);
+
+  // remember this filename (this part isn't finished yet -- Rodger).
+
+  // set the macro
+  if (package.empty()) {
+    return macrotable->setmacro(defaultpackage, macroname, macroparams, filename, macrovalue);
+  } else {
+    return macrotable->setmacro(package, macroname, macroparams, filename, macrovalue);
+  }
+}
+
+// setdefaultmacro adds an entry to the list of default macros 
+// returns 0 if there was no error,
+//        -1 if it redefined a macro
+//        -2 if it hid a Global macro
+//        -3 if it redefined a macro and hid a Global macro
+//        -4 if no macroname was supplied
+int displayclass::setdefaultmacro (const text_t &package, 
+				   const text_t &macroname, 
+				   text_t macroparams, 
+				   const text_t &filename,
+				   const text_t &macrovalue) 
+{
+  return setparammacro(defaultmacros,package,macroname,
+		       macroparams,filename,macrovalue);
+}
+
+// public function
+int displayclass::setcollectionmacro(const text_t &package, 
+				     const text_t &macroname, 
+				     text_t mparams, 
+				     const text_t &macrovalue)
+{
+  return setcollectionmacro(package, macroname, mparams, "memory", macrovalue);
+}
+
+// setcollectionmacro adds an entry to the list of collection specific macros 
+// returns 0 if there was no error,
+//        -1 if it redefined a macro
+//        -2 if it hid a Global macro
+//        -3 if it redefined a macro and hid a Global macro
+//        -4 if no macroname was supplied
+int displayclass::setcollectionmacro (const text_t &package, 
+				      const text_t &macroname,
+				      text_t mparams, 
+				      const text_t &filename,
+				      const text_t &macrovalue) 
+{
+  return setparammacro(collectionmacros,package,macroname,mparams,
+		       filename,macrovalue);
+}
+
+
+// evaluates a boolean expression
+// returns false if expr equals "" or "0". 
+// otherwise returns true *unless* expr is 
+// format "XXXX" eq/ne "dddd" in which case
+// the two quoted strings are compared
+bool displayclass::boolexpr (const text_t &package, const text_t &expr, int recursiondepth) {
+  if (expr.empty()) return false;
+
+  text_t expexpr;
+  if (package.empty()) {
+    expandstring (defaultpackage, expr, expexpr, recursiondepth);
+  } else {
+    expandstring (package, expr, expexpr, recursiondepth);
+  }
+  if (expexpr.empty() || expexpr == "0") return false;
+  // allow \" as start of exp as well as ", cos this is what the GLI writes out
+  if (expr[0] != '\"' && !(expr[0] == '\\' && expr[1] == '"')) {
+    return true;
+  }
+  // don't use expexpr while separating quoted parts of
+  // expression just in case expanded out macros contain
+  // quotes
+  text_t::const_iterator here = expr.begin();
+  text_t::const_iterator end = expr.end();
+
+  int quotecount = 0;
+  text_t string1, expstring1;
+  text_t string2, expstring2;
+  text_t op;
+  text_t combineop;
+  bool result = false; // an empty string is false
+  bool result2 = false;
+
+  while (here != end) {
+    // get a comparison
+    quotecount = 0;
+    string1.clear();
+    string2.clear();
+    op.clear();
+    while (here != end) {
+      if (*here == '\\' && *(here+1) =='"') {
+	++quotecount; // found an escaped quote
+	++here;
+      }
+      else if (*here == '"') ++quotecount;
+      else if (quotecount == 1) string1.push_back(*here);
+      else if ((quotecount == 2) && !is_unicode_space (*here))
+	op.push_back(*here);
+      else if (quotecount == 3) string2.push_back(*here);
+      else if (quotecount >= 4) break;
+      ++here;
+    }
+    
+    expandstring (package, string1, expstring1, recursiondepth);
+    expandstring (package, string2, expstring2, recursiondepth);
+
+    // get next result
+    result2 = true; // any badly formatted string will return true
+    if (op == "eq") result2 = (expstring1 == expstring2);
+    else if (op == "ne") result2 = (expstring1 != expstring2);
+    else if (op == "gt") result2 = (expstring1 > expstring2);
+    else if (op == "ge") result2 = (expstring1 >= expstring2);
+    else if (op == "lt") result2 = (expstring1 < expstring2);
+    else if (op == "le") result2 = (expstring1 <= expstring2);
+    else if (op == "==") result2 = (expstring1.getint() == expstring2.getint());
+    else if (op == "!=") result2 = (expstring1.getint() != expstring2.getint());
+    else if (op == ">") result2 = (expstring1.getint() > expstring2.getint());
+    else if (op == ">=") result2 = (expstring1.getint() >= expstring2.getint());
+    else if (op == "<") result2 = (expstring1.getint() < expstring2.getint());
+    else if (op == "<=") result2 = (expstring1.getint() <= expstring2.getint());
+    else if (op == "sw") result2 = (starts_with(expstring1,expstring2));
+    else if (op == "ew") result2 = (ends_with(expstring1,expstring2));
+
+    // combine the results
+    if (combineop == "&&") result = (result && result2);
+    else if (combineop == "||") result = (result || result2);
+    else result = result2;
+
+    // get next combination operator
+    combineop.clear();
+    while (here != end && *here != '"') {
+      if (!is_unicode_space(*here)) combineop.push_back(*here);
+      ++here;
+    }
+  }
+
+  return result;
+}
+
+
+mvalue* displayclass::macro_find (parammacros_t* macrotable, 
+				  const text_t &packagename,
+				  const text_t &macroname)
+{
+  mvalue *macrovalue = NULL;
+
+  // next look in the named parammacros for the named package
+  if (macrotable->macro_find (packagename, macroname) != NULL)
+    {
+      paramspeclist::iterator paramhere, paramend;
+
+      paramhere = orderparamlist->begin(); paramend = orderparamlist->end();
+      while ((macrovalue == NULL) && (paramhere != paramend))
+	{
+	  macrovalue = macrotable->parameter_find(packagename, macroname,
+						  (*paramhere).param);
+	  ++paramhere;
+	}
+    }
+
+  return macrovalue;
+}
+
+
+// (recursively) expand out a macro
+// returns true  if the macro was found
+//         false if the macro was not found
+// macropackage should never be empty
+bool displayclass::macro (const text_t &macroname, 
+			  const text_t &macropackage, 
+			  const text_t &macroparam, 
+			  text_t &outputtext,
+			  int recursiondepth)
+{
+  assert(!macropackage.empty()); //???
+  //outconvertclass text_t2ascii;
+  text_tlist splitmacroparam;
+  text_t::const_iterator hereit, endit;
+  text_t aparam;
+  unsigned short c = '\0', quote = '\0';
+
+  //  cerr << "r: " << recursiondepth << "\n";
+  // check for deep recursion
+  if (recursiondepth >= MAXRECURSIONDEPTH)
+    {
+      if (logout != NULL)
+	(*logout) << "Warning: deep recursion, limiting macro expansion\n";
+      return false;
+    }
+
+  // check the macropackage
+  //if (macropackage.empty()) macropackage = "Global";
+
+  // get the parameters (but don't expand them)
+  if (macroparam.size() > 0) {
+    // set up for loop
+    hereit = macroparam.begin();
+    endit = macroparam.end();
+    if (hereit != endit) c = (*hereit);
+    int openbrackets=0; // keep track of bracket level eg _If_(1, _macro_(arg))
+			// this allows commas inside brackets (as arguments)
+    while (hereit != endit) {
+      // get the next parameter
+      aparam.clear();
+      quote = '\0'; // not-quoted unless proven quoted
+      
+      // ignore initial whitespace
+      while ((hereit!=endit)&&is_unicode_space(c)) c=my_ttnextchar(hereit,endit);
+      
+      // look for the end of the parameter
+      while (hereit != endit) {
+	if (c == '\\') {
+	  // found escape character, also get next character
+	  aparam.push_back(c);
+	  c = my_ttnextchar (hereit, endit);
+	  if (hereit != endit) aparam.push_back(c);
+	  
+	} else if (quote=='\0' && (c=='\'' /*|| c=='"'*/)) {
+	  // found a quoted section
+	  quote = c;
+	  if (openbrackets>0) aparam.push_back(c);
+
+	} else if (quote!='\0' && c==quote) {
+	  // found the end of a quote
+	  quote = '\0';
+	  if (openbrackets<0) aparam.push_back(c);
+
+	} else if (quote=='\0' && (c==',' || c==')') && openbrackets==0) {
+	  // found the end of a parameter
+	  c = my_ttnextchar (hereit, endit);
+	  break;
+
+	} else if (c=='(') {
+	  aparam.push_back(c);
+	  ++openbrackets;
+	} else if (c==')') {
+	  --openbrackets;
+	  aparam.push_back(c);
+	} else {
+	  // ordinary character
+	  aparam.push_back(c);
+	}
+
+	c = my_ttnextchar (hereit, endit);
+      }
+
+
+      // add this parameter to the list
+      splitmacroparam.push_back(aparam);
+    }
+  }
+
+  if (macroname == "If") {
+    // get the condition, then clause and else clause
+    text_tlist::iterator paramcond = splitmacroparam.begin();
+    text_tlist::iterator paramend = splitmacroparam.end();
+    text_tlist::iterator paramthen = paramend;
+    text_tlist::iterator paramelse = paramend;
+    
+    if (paramcond != paramend) {
+      paramthen = paramcond;
+      ++paramthen;
+    }
+    if (paramthen != paramend) {
+      paramelse = paramthen;
+      ++paramelse;
+    }
+    
+    // will always output something
+    outputtext.clear();
+
+
+    // expand out the first parameter
+    if (paramcond != paramend) {
+      /// text_t tmpoutput;
+      /// expandstring (macropackage, *paramcond, tmpoutput, recursiondepth+1);
+      /// lc (tmpoutput);
+      // test the expanded string
+      if (boolexpr (macropackage, *paramcond, recursiondepth+1)) {
+	//(tmpoutput.size()) && (tmpoutput != "false") && (tmpoutput != "0")) {
+	// true
+	if (paramthen != paramend)
+	  expandstring (macropackage, *paramthen, outputtext, recursiondepth+1);
+      } else {
+	// false
+	if (paramelse != paramend)
+	  expandstring (macropackage, *paramelse, outputtext, recursiondepth+1);
+      }
+    }
+    
+    return true;
+  }
+
+  // try and find this macro
+
+  // this list might be replaced by something a little more
+  // sophisticated in the future.
+  text_tlist packagelist;
+  packagelist.push_back (macropackage);
+  packagelist.push_back ("Global");
+
+  text_tlist::iterator packagehere = packagelist.begin();
+  text_tlist::iterator packageend = packagelist.end();
+  mvalue *macrovalue = NULL;
+  while (packagehere != packageend)
+    {
+      // first look in the currentmacros
+      macrovalue = currentmacros->macro_find(*packagehere, macroname);
+      if (macrovalue != NULL)
+	break;
+
+      macrovalue = currentmacros->macro_find("Style", macroname);
+      if (macrovalue != NULL)
+	break;
+
+      // look in the collection specific macros
+      macrovalue = macro_find(collectionmacros,*packagehere,macroname);
+      if (macrovalue != NULL)
+	break;
+
+      // and again in the collection specific package "Style"
+      macrovalue = macro_find(collectionmacros,(char*)"Style",macroname);
+      if (macrovalue != NULL)
+	break;
+
+      // look in the default macros
+      macrovalue = macro_find(defaultmacros,*packagehere,macroname);
+      if (macrovalue != NULL)
+	break;
+
+      // and again in the package "Style"
+      macrovalue = macro_find(defaultmacros,(char*)"Style",macroname);
+      if (macrovalue != NULL)
+	break;
+      ++packagehere;
+    }
+
+  if (macrovalue != NULL)
+    {
+      // we found a macro
+
+      // replace all the option macros (_1_, _2_, ...) in the value of this macro
+      // and decide if we need to expand the value of this macro
+      bool needsexpansion = false;
+      text_t tempmacrovalue;
+      tempmacrovalue.clear();
+      hereit = macrovalue->value.begin();
+      endit = macrovalue->value.end();
+      if (hereit != endit) c = (*hereit);
+      while (hereit != endit)
+	{
+	  if (c == '\\')
+	    {
+	      // found an escape character
+	      needsexpansion = true;
+	      tempmacrovalue.push_back(c);
+	      c = my_ttnextchar (hereit, endit);
+	      if (hereit != endit) tempmacrovalue.push_back(c);
+	      c = my_ttnextchar (hereit, endit);
+	    }
+	  else if (c == '_')
+	    {
+	      text_t::const_iterator savedhereit = hereit;
+
+	      // attempt to get a number
+	      int argnum = 0;
+	      unsigned short digc = my_ttnextchar (hereit, endit);
+	      while (digc >= '0' && digc <= '9') {
+		argnum = argnum*10 + digc - '0';
+		digc = my_ttnextchar (hereit, endit);
+	      }
+	      if (digc == '_' && argnum > 0) {
+		// found an option macro, append the appropriate text
+		text_tlist::iterator ttlisthere = splitmacroparam.begin();
+		text_tlist::iterator ttlistend = splitmacroparam.end();
+		while ((argnum > 1) && (ttlisthere != ttlistend)) {
+		  --argnum;
+		  ++ttlisthere;
+		}
+		if (ttlisthere != ttlistend) {
+		  tempmacrovalue.append(*ttlisthere);
+		  if (findchar((*ttlisthere).begin(), (*ttlisthere).end(), '_') != (*ttlisthere).end())
+		    needsexpansion = true;
+		}
+
+		c = my_ttnextchar (hereit, endit);
+
+	      } else {
+		// wasn't a option macro
+		needsexpansion = true;
+		tempmacrovalue.push_back(c);
+		hereit = savedhereit;
+		c = my_ttnextchar (hereit, endit);
+	      }
+	    }
+	  else
+	    {
+	      tempmacrovalue.push_back(c);
+	      c = my_ttnextchar (hereit, endit);
+	    }
+	}
+      
+      // recursively replace this macro (if we need to)
+      if (needsexpansion) {
+	expandstring (*packagehere, tempmacrovalue, outputtext, recursiondepth+1);
+      } else {
+	outputtext += tempmacrovalue;
+      }
+
+      return true;
+    }
+
+  // GRB: deal with remaining code properly as it faults
+  return false;
+  outconvertclass text_t2ascii;
+  if (logout != NULL) {
+    (*logout) << text_t2ascii << "Warning: _" << 
+      macropackage << ":" << macroname << "_ not found\n";
+  }
+  
+  return false; // didn't find the macro
+}
+
+
+void displayclass::printdefaultmacros ()
+{
+  defpackagemap::const_iterator packagemapit;
+  defmacromap::const_iterator macromapit;
+  defparammap::const_iterator parammapit;
+  const mvalue *mvalueptr;
+  outconvertclass text_t2ascii;
+
+  text_t packagename, macroname, macroparam;
+  
+  for (packagemapit = defaultmacros->package_begin();
+       packagemapit != defaultmacros->package_end();
+       ++packagemapit) 
+    {
+      packagename = (*packagemapit).first;
+      //     cerr << "***package   : \"" << packagename << "\"\n";
+    
+      for (macromapit = (*packagemapit).second.begin();
+	   macromapit != (*packagemapit).second.end();
+	   ++macromapit) 
+	{
+	  macroname = (*macromapit).first;
+	  //       cerr << "***macroname : \"" << macroname << "\"\n";
+
+	  for (parammapit = (*macromapit).second.begin();
+	       parammapit != (*macromapit).second.end();
+	       ++parammapit) 
+	    {
+	      macroparam = (*parammapit).first;
+	      mvalueptr = &((*parammapit).second);
+	      cerr << text_t2ascii << "package   : \"" << packagename << "\"\n";
+	      cerr << text_t2ascii << "macroname : \"" << macroname << "\"\n";
+	      cerr << text_t2ascii << "parameters: [" << macroparam << "]\n";
+	      cerr << text_t2ascii << "filename  : \"" << mvalueptr->filename << "\"\n";
+	      cerr << text_t2ascii << "value     : {" << mvalueptr->value << "}\n\n";
+	    }
+	}
+    }
+}
+
+void displayclass::printallparams ()
+{
+  text_tset::iterator text_tsetit;
+  outconvertclass text_t2ascii;
+
+  cerr << text_t2ascii << "** allparams\n";
+  for (text_tsetit=allparams.begin();
+       text_tsetit!=allparams.end();
+       ++text_tsetit) {
+    cerr << text_t2ascii << (*text_tsetit) << "\n";
+  }
+  cerr << text_t2ascii << "\n";
+}
+
+
+
+/////////////////////////////////////
+// stuff to do tricky output
+/////////////////////////////////////
+
+displayclass &operator<< (outconvertclass &theoutc, displayclass &display)
+{
+  display.setconvertclass (&theoutc);
+  return display;
+}
+
+displayclass &operator<< (displayclass &display, const text_t &t)
+{
+  outconvertclass *theoutc = display.getconvertclass();
+
+  if (theoutc == NULL) return display;
+  
+  text_t output;
+  display.expandstring (t, output);
+  (*theoutc) << output;
+
+  return display;
+}
Index: /gsdl/trunk/gs2common/src/lib/display.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/display.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/display.h	(revision 16561)
@@ -0,0 +1,341 @@
+/**********************************************************************
+ *
+ * display.h -- Context sensitive macro language
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#  include <ospace\std\list>
+#  include <ospace\std\set>
+#  include <ospace\std\map>
+#  include <ospace\std\algorithm>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#  include <list.h>
+#  include <set.h>
+#  include <map.h>
+#  if defined(GSDL_USE_ALGO_H)
+#    include <algo.h>
+#  else
+#    include <algorithm.h>
+#  endif
+#else
+#  include <vector>
+#  include <list>
+#  include <set>
+#  include <map>
+#  include <algorithm>
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+
+#define unistream ifstream
+#else
+#  include <iostream>
+#  include <fstream>
+
+typedef std::basic_ifstream<unsigned char> unistream;
+
+#endif
+
+
+// use the standard namespace
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#elif !defined(GSDL_NAMESPACE_BROKEN)
+using namespace std;
+#endif
+
+
+
+// MAXRECURSIONDEPTH is a cutoff to catch
+// cyclic macros (a includes b and b includes a)
+#define MAXRECURSIONDEPTH 30
+
+// class prototypes
+class parammacros_t;
+class currentmacros_t;
+
+// a few supporting types
+// fileelement isn't finished yet, I have to find out
+// more about getting information about files
+struct fileinfoelement 
+{
+  int otherinfo;
+};
+
+// macro value structure
+struct mvalue
+{
+  text_t filename;
+  text_t value;
+};
+
+inline bool operator==(const fileinfoelement &x, const fileinfoelement &y) {
+  return (x.otherinfo==y.otherinfo);
+}
+
+inline bool operator<(const fileinfoelement &x, const fileinfoelement &y) {
+  return (x.otherinfo<y.otherinfo);
+}
+
+typedef map<text_t, fileinfoelement, lttext_t> fileinfomap;
+
+
+typedef map<text_t, text_t, lttext_t> paramhashtype;
+void splitparams (const text_t &paramstring, paramhashtype &paramhash);
+
+
+struct paramspec 
+{
+  double spec;
+  text_t param;
+};
+
+inline bool operator==(const paramspec& x, const paramspec& y) 
+{
+  return ((x.spec == y.spec) && (x.param == y.param));
+}
+
+inline bool operator!=(const paramspec& x, const paramspec& y) 
+{
+  return ((x.spec != y.spec) || (x.param != y.param));
+}
+
+// note: paramspec is sorted by reverse spec and then param
+
+inline bool operator<(const paramspec& x, const paramspec& y) 
+{
+  return ((x.spec > y.spec) ||
+	  ((x.spec == y.spec) && (x.param < y.param)));
+}
+
+inline bool operator>(const paramspec& x, const paramspec& y) 
+{
+  return ((x.spec < y.spec) ||
+	  ((x.spec == y.spec) && (x.param > y.param)));
+}
+
+typedef vector<paramspec> paramspeclist;
+
+
+
+// NOTE: when macros are used within text they should have
+// underscores '_' on both sides, however, in calls to
+// setdefaultmacro and setmacro they should not have the
+// underscores. So you might have
+//
+// setmacro ("aname", "query", " some text ");
+// expandstring ("_aname_", expandedtext);
+//
+// because the input to expandstring is a block of text, not
+// a macroname
+
+class displayclass 
+{
+public:
+  static text_t defaultpackage;
+
+  displayclass ();
+  ~displayclass ();
+  
+  // isdefaultmacro sees if there is an entry in the list of
+  // default macros with the given package and macro name
+  // returns 0 if no macros in the package or in the global package
+  //           were found
+  //         1 if no macros in the package were found but a macro
+  //           in the global package was found
+  //         2 if a macro in the given package was found
+  int isdefaultmacro (const text_t &package, const text_t &macroname);
+
+  // setdefaultmacro adds an entry to the list of default macros 
+  // returns 0 if there was no error,
+  //        -1 if it redefined a macro
+  //        -2 if it hid a Global macro
+  //        -3 if it redefined a macro and hid a Global macro
+  //        -4 if no macroname was supplied
+  int setdefaultmacro (const text_t &package, 
+		       const text_t &macroname, 
+		       const text_t &params, 
+		       const text_t &macrovalue);
+  
+  // loads a default macro file (if it isn't already loaded)
+  // returns 0 if didn't need to load the file (it was already loaded)
+  //         1 if was (re)loaded
+  //        -1 an error occurred while trying to load the file
+  int loaddefaultmacros (const text_t &thisfilename);
+
+
+  // overrides (or sets) a macro for the current page.
+  // returns 0 if there was no error,
+  //        -1 if it redefined a macro
+  //        -4 if no macroname was supplied
+  int setcollectionmacro (const text_t &package, 
+			  const text_t &macroname, 
+			  text_t params, 
+			  const text_t &macrovalue);
+
+  // loads a collection specific macro file 
+  // returns 0 if didn't need to load the file (it was already loaded)
+  //         1 if was (re)loaded
+  //        -1 an error occurred while trying to load the file
+  int loadcollectionmacros (const text_t &thisfilename);
+
+  // unloads all default macros
+  void unloaddefaultmacros ();
+
+  // unloads all collection specific macros
+  void unloadcollectionmacros ();
+  
+  // prepares to create a page: deletes all macros set with
+  // 'setmacro', checks all the default macro files to see
+  // if any need reloading, and sets the page parameters.
+  void openpage (const text_t &thispageparams, 
+		 const text_t &thisprecedence);
+  
+  // changes the parameters for the current page.
+  void setpageparams (text_t thispageparams, 
+		      text_t thisprecedence);
+  
+  // overrides (or sets) a macro for the current page.
+  // returns 0 if there was no error,
+  //        -1 if it redefined a macro
+  //        -4 if no macroname was supplied
+  int setmacro (const text_t &macroname, 
+		const text_t &package, 
+		const text_t &macrovalue);
+  
+// havemacro sees if there is an entry in the list of macros or
+// default macros with the given package and macro name
+// returns 0 if no macros in the package or in the global package
+//           were found
+//         1 if no macros in the package were found but a macro
+//           in the global package was found
+//         2 if a macro in the given package was found
+//         4 if no macros in the package were found but a macro
+//           in the global package was found in default macros
+//         8 if a macro in the given package was found in default
+  int havemacro(const text_t &package, const text_t &macroname);
+
+  int setmacroif (const text_t &macro_to_set_and_test, 
+		  const text_t &macro_to_set_if_macro_not_set,
+		  const text_t &macroname, 
+		  const text_t& package);
+  
+  inline void expandstring (const text_t &inputtext, text_t &outputtext) { expandstring(defaultpackage, inputtext, outputtext); }
+
+  void expandstring (const text_t &package, const text_t &inputtext,
+		     text_t &outputtext, int recursiondepth = 0);
+
+  // these functions are not ment to be used directly, they are used to permit 
+  // concise stream output like:
+  // cout << text_t2ascii << display << "_amacro_" << "_anothermacro_";
+  void setconvertclass (outconvertclass *theoutc) {outc = theoutc;}
+  outconvertclass *getconvertclass () {return outc;}
+
+  // say where any error logging goes, this can be NULL
+  // if no error logging is desired - returns previous value
+  ostream *setlogout (ostream *thelogout);
+
+  // debug stuff
+  void printdefaultmacros ();
+  void printallparams ();
+
+protected:
+  // special variables to permit trickly output
+  outconvertclass *outc;
+
+  // general variables
+  text_tset        allparams;  // possible parameter combinations
+  parammacros_t *defaultmacros;
+  parammacros_t *collectionmacros;
+  fileinfomap     *defaultfiles;
+  
+  // variables to do with a page expansion
+  text_t params;
+  text_t precedence;
+  paramspeclist *orderparamlist; // ordered by specificness
+  currentmacros_t *currentmacros; // only macros set by setmacro
+
+  // logging variable
+  ostream *logout;
+
+  // reloads any default macro files which have changed
+  // returns 0 no errors occurred
+  //        -1 an error occurred while trying to load one of the files
+  int checkdefaultmacrofiles ();
+
+  int loadparammacros (parammacros_t* macrotable, const text_t &thisfilename);
+
+  int setparammacro (parammacros_t* macrotable,
+		     const text_t &package, 
+		     const text_t &macroname, 
+		     text_t params, const text_t &filename,
+		     const text_t &macrovalue); 
+
+  int setdefaultmacro (const text_t &package, 
+		       const text_t &macroname, 
+		       text_t params, const text_t &filename,
+		       const text_t &macrovalue); 
+
+  int setcollectionmacro(const text_t &package, 
+			 const text_t &macroname, 
+			 text_t params, 
+			 const text_t &filename,
+			 const text_t &macrovalue); 
+  
+  // evaluates a boolean expression
+  bool boolexpr (const text_t &package, const text_t &expr, 
+		 int recursiondepth);
+
+  mvalue* macro_find (parammacros_t* macrotable, 
+		      const text_t &packagename,
+		      const text_t &macroname);
+
+  // (recursively) expand out a macro
+  // returns true  if the macro was found
+  //         false if the macro was not found
+  bool macro (const text_t &macroname, 
+	      const text_t &macropackage, 
+	      const text_t &macroparam, text_t &outputtext,
+	      int recursiondepth);
+};
+
+
+displayclass &operator<< (outconvertclass &theoutc, displayclass &display);
+displayclass &operator<< (displayclass &display, const text_t &t);
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/fileutil.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/fileutil.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/fileutil.cpp	(revision 16561)
@@ -0,0 +1,446 @@
+/**********************************************************************
+ *
+ * fileutil.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.
+ *
+ *********************************************************************/
+
+#include "fileutil.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+// returns the proper concatenation of the two paths
+text_t filename_cat (text_t path1, text_t path2) {
+  text_t::iterator here;
+  text_t::iterator begin;
+  text_t::iterator end;
+
+  // make sure there is just one slash, of the correct type,
+  // at the end of path1 (unless path1 is an empty string).
+  if (!path1.empty()) {
+    // remove all trailing slashes
+    here = path1.end();
+    --here;
+    begin = path1.begin();
+    while (here != begin && (*here == '/' || *here == '\\')) {
+      --here;
+    }
+    ++here;
+    path1.erase(here,path1.end());
+    
+    // add one final slash
+#ifdef __WIN32__
+    path1 += "\\";
+#else
+    path1 += "/";
+#endif 
+  }
+
+  // remove all slashes from the start of path2
+  here = path2.begin();
+  end = path2.end();
+  while (here != end && (*here == '/' || *here == '\\')) {
+    ++here;
+  }
+  path2.erase (path2.begin(), here);
+  
+  text_t fullpath = path1 + path2;
+  
+  // make sure all the right slashes are used
+  here = fullpath.begin();
+  end = fullpath.end();
+  while (here != end) {
+#ifdef __WIN32__
+    if (*here == '/') *here = '\\';
+#else
+    if (*here == '\\') *here = '/';
+#endif
+    ++here ;
+  }
+  return fullpath;
+}
+
+text_t filename_cat (text_t path1, text_t path2, text_t path3) {
+  return filename_cat(filename_cat(path1,path2),path3);
+}
+
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4) {
+  return filename_cat(filename_cat(filename_cat(path1,path2),path3),path4);
+}
+
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4, 
+		     text_t path5) {
+  return filename_cat(filename_cat(filename_cat(filename_cat(path1,path2),path3),
+				   path4),path5);
+}
+
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4, 
+		     text_t path5, text_t path6) {
+  return filename_cat(filename_cat(path1,path2,path3,path4,path5),path6);
+}
+
+// returns true if filename can be opened
+bool file_exists (const text_t &filename) {
+  char *cstr = filename.getcstr();
+#ifdef GSDL_USE_IOS_H
+  ifstream filestream (cstr, ios::in | ios::nocreate);
+#else
+  ifstream filestream (cstr, ios::in);
+#endif
+  delete []cstr;
+  
+  if (filestream) {
+    // file exists
+    filestream.close ();
+    return true;
+  }
+  
+  // file does not exist
+  return false;
+}
+
+// returns true if filename can be opened
+bool file_writable (const text_t &filename) {
+  char *cstr = filename.getcstr();
+#ifdef GSDL_USE_IOS_H
+  ifstream filestream (cstr, ios::out | ios::nocreate);
+#else
+  ifstream filestream (cstr, ios::out);
+#endif
+  delete []cstr;
+  
+  if (filestream) {
+    // file exists
+    filestream.close ();
+    return true;
+  }
+  
+  // file does not exist
+  return false;
+}
+
+#if defined(__WIN32__) && !defined(__GNUC__)
+
+#include <windows.h>
+
+bool directory_exists (const text_t &dirname) {
+  
+  WIN32_FIND_DATA FileData;  
+  HANDLE hSearch; 
+
+  text_t dir = dirname + "\\*";
+  char *dirpath = dir.getcstr();
+  
+  hSearch = FindFirstFile(dirpath, &FileData);
+  if (hSearch == INVALID_HANDLE_VALUE) {
+    delete []dirpath;
+    return false;
+  }
+
+  FindClose (hSearch);
+  delete []dirpath;
+  return true;
+}
+
+bool read_dir (const text_t &dirname, text_tset &filelist) {
+  
+  WIN32_FIND_DATA FileData;  
+  HANDLE hSearch; 
+  
+  text_t dir = dirname + "\\*";
+  char *dirpath = dir.getcstr();
+  
+  hSearch = FindFirstFile(dirpath, &FileData); 
+  if (hSearch == INVALID_HANDLE_VALUE) {
+    delete []dirpath;
+    return false;
+  }
+  
+  text_t filename = FileData.cFileName;
+  if (filename != "." && filename != ".." && filename != "CVS" && filename != ".svn")
+    filelist.insert (filename); 
+  
+  while (FindNextFile(hSearch, &FileData)) {
+    filename = FileData.cFileName;
+    if (filename == "." || filename == ".." || filename == "CVS" || filename == ".svn")
+      continue;
+    filelist.insert (filename); 
+  }
+  
+  FindClose(hSearch);
+  delete []dirpath;
+  
+  return true;
+}
+
+bool read_dir (const text_t &dirname, text_tarray &filelist) {
+  
+  WIN32_FIND_DATA FileData;  
+  HANDLE hSearch; 
+  
+  text_t dir = dirname + "\\*";
+  char *dirpath = dir.getcstr();
+  
+  hSearch = FindFirstFile(dirpath, &FileData); 
+  if (hSearch == INVALID_HANDLE_VALUE) {
+    delete []dirpath;
+    return false;
+  }
+  
+  text_t filename = FileData.cFileName;
+  if (filename != "." && filename != ".." && filename != "CVS" && filename != ".svn")
+    filelist.push_back (filename); 
+  
+  while (FindNextFile(hSearch, &FileData)) {
+    filename = FileData.cFileName;
+    if (filename == "." || filename == ".." || filename == "CVS" || filename == ".svn")
+      continue;
+    filelist.push_back (filename); 
+  }
+  
+  FindClose(hSearch);
+  delete []dirpath;
+
+  return true;
+}
+
+#else
+
+#include <dirent.h>
+
+bool directory_exists (const text_t &dirname) {
+  
+  char *tmp = dirname.getcstr();
+  DIR *dirin = opendir (tmp);
+  delete []tmp;
+  
+  if (dirin == NULL) return false;
+  closedir (dirin);
+  return true;
+}
+
+bool read_dir (const text_t &dirname, text_tset &filelist) {
+  
+  char *tmp = dirname.getcstr();
+  DIR *dirin = opendir (tmp);
+  delete []tmp;
+  
+  if (dirin == NULL) return false;
+  
+  dirent *dirp; 
+  
+  text_t filename;
+  while ((dirp = readdir (dirin)) != NULL) {
+    filename = dirp->d_name;
+    if (filename == "." || filename == ".." || filename == "CVS" || filename == ".svn")
+      continue;
+    filelist.insert (filename);
+  }
+  closedir (dirin);
+  return true;
+}
+
+bool read_dir (const text_t &dirname, text_tarray &filelist) {
+  
+  char *tmp = dirname.getcstr();
+  DIR *dirin = opendir (tmp);
+  delete []tmp;
+  
+  if (dirin == NULL) return false;
+  
+  dirent *dirp; 
+  
+  text_t filename;
+  while ((dirp = readdir (dirin)) != NULL) {
+    filename = dirp->d_name;
+    if (filename == "." || filename == ".." || filename == "CVS" || filename == ".svn")
+      continue;
+    filelist.push_back (filename);
+  }
+  closedir (dirin);
+  return true;
+}
+
+#endif
+
+// returns true if things look like they happened ok
+bool file_copy (const text_t &fromfile, const text_t &tofile) {
+  
+  char *fromfilec = fromfile.getcstr();
+  char *tofilec = tofile.getcstr();
+  bool fail = false;
+
+#ifdef __WIN32__
+  if (CopyFile (fromfilec, tofilec, FALSE) == 0) fail = true;
+
+#else
+
+  ifstream from (fromfilec);
+  if (!from) {
+    fail = true;
+  } else {
+    ofstream to (tofilec);
+    if (!to) {
+      fail = true;
+      from.close();
+    } else {
+      from >> to.rdbuf();
+      from.close();
+      to.close();
+    }
+  }
+
+#endif
+
+  delete []fromfilec;
+  delete []tofilec;
+  if (fail) return false;
+  return true;
+}
+
+// This is a fairly quick and nasty attempt at doing a "tail" on a file
+// (i.e. returning the last numlines lines of filename).  It has one
+// important limitation in that it expects lines to be linelength
+// characters long on average.  This of course makes it possible that it
+// won't return numlines lines if there are some lines that are longer than
+// linelength.  It also makes it fairly inefficient as it always reads in
+// numlines*linelength characters when it most often doesn't need them all.
+// For current needs it's fine though.
+// -- Pass a linelength of 0 to use the default linelength (256 chars).
+// -- The maximum value of linelength is MAXLINELENGTH
+text_t file_tail (const text_t &filename, int numlines, int linelength) {
+
+#define MAXLINELENGTH 2048
+
+  if (numlines < 1) numlines = 1;
+  if (linelength < 1) linelength = 256;
+  if (linelength > MAXLINELENGTH) linelength = MAXLINELENGTH;
+
+  streampos numchars = linelength*numlines;
+
+  text_tarray lines;
+  text_t ret;
+
+  char *filenamec = filename.getcstr();
+  char linec[MAXLINELENGTH];
+  ifstream file_in (filenamec);
+  delete []filenamec;
+  if (file_in) {
+    file_in.seekg (0, ios::end);
+    streampos file_length = file_in.tellg();
+    if (file_length < numchars) numchars = file_length;
+    file_in.seekg (-numchars, ios::end);
+
+    while (!file_in.eof()) {
+      file_in.getline (linec, linelength);
+      ret.setcstr(linec);
+      text_t::const_iterator here = ret.begin();
+      text_t::const_iterator end = ret.end();
+      // make sure line has content
+      while (here != end) {
+	if (*here != '\n' && *here != ' ') {
+	  lines.push_back (ret);
+	  break;
+	}
+	++here;
+      }
+    }
+    file_in.close();
+  }
+
+  ret.clear();
+  int numlinesgot = lines.size();
+  int sindex = 0;
+  if (numlinesgot > numlines) sindex = numlinesgot - numlines;
+  for (int i = sindex; i < numlinesgot; ++i) {
+    ret += lines[i] + "\n";
+  }
+
+  return ret;
+}
+
+#ifdef __WIN32__
+
+#include <direct.h>
+// returns true if directory was created successfully
+bool mk_dir (const text_t &dirname) {
+  char *dirnamec = dirname.getcstr();
+  int rv = _mkdir (dirnamec);
+  delete []dirnamec;
+  if (rv == 0) return true;
+  return false;
+}
+
+#else
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+// returns true if directory was created successfully
+bool mk_dir (const text_t &dirname) {
+  mode_t mode = 0777;
+  char *dirnamec = dirname.getcstr();
+  int rv = mkdir (dirnamec, mode);
+  delete []dirnamec;
+  if (rv == 0) return true;
+  return false;
+}
+
+#endif
+
+// read in file from filename and load into content
+bool read_file (const text_t &filename, text_t &content) {
+
+  content.clear();
+
+  char *filenamec = filename.getcstr();
+#ifdef GSDL_USE_IOS_H
+  ifstream file_in (filenamec, ios::in | ios::nocreate);
+#else
+  ifstream file_in (filenamec, ios::in);
+#endif
+  delete []filenamec;
+
+  if (file_in) {
+    char c;
+    file_in.get(c);
+    while (!file_in.eof ()) {
+      // Casting c to an unsigned char is vital when reading non-ASCII documents
+      content.push_back((unsigned char) c);
+      file_in.get(c);
+    }
+    file_in.close();
+  } else {
+    return false;
+  }
+  return true;
+}
Index: /gsdl/trunk/gs2common/src/lib/fileutil.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/fileutil.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/fileutil.h	(revision 16561)
@@ -0,0 +1,65 @@
+/**********************************************************************
+ *
+ * fileutil.h -- a few useful file utilities
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef FILEUTIL_H
+#define FILEUTIL_H
+
+#include "text_t.h"
+
+
+// returns the proper concatenation of the two paths
+text_t filename_cat (text_t path1, text_t path2);
+text_t filename_cat (text_t path1, text_t path2, text_t path3);
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4);
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4, 
+		     text_t path5);
+text_t filename_cat (text_t path1, text_t path2, text_t path3, text_t path4, 
+		     text_t path5, text_t path6);
+
+// returns true if filename can be opened
+bool file_exists (const text_t &filename);
+
+// returns true if filename can be written to
+bool file_writable (const text_t &filename);
+
+// returns true if directory exists
+bool directory_exists (const text_t &dirname);
+
+// note that read_dir appends to filelist, it doesn't clear
+// it first
+bool read_dir (const text_t &dirname, text_tarray &filelist);
+bool read_dir (const text_t &dirname, text_tset &filelist);
+
+bool file_copy (const text_t &fromfile, const text_t &tofile);
+
+text_t file_tail (const text_t &filename, int numlines, int linelength);
+
+// returns true if directory created successfully
+bool mk_dir (const text_t &dirname);
+
+bool read_file (const text_t &filename, text_t &content);
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/gdbmclass.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gdbmclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gdbmclass.cpp	(revision 16561)
@@ -0,0 +1,250 @@
+/**********************************************************************
+ *
+ * gdbmclass.cpp -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#include "gdbmclass.h"
+#include "gsdltools.h"
+#include "gsdlunicode.h"
+
+
+gdbmclass::~gdbmclass()
+{
+  closedatabase();
+}
+
+
+// returns true if opened
+bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys, 
+#ifdef __WIN32__
+			      bool need_filelock
+#else
+                              bool
+#endif
+			      )
+{
+  text_t data_location;
+  int block_size = 512;
+  
+  if (gdbmfile != NULL) {
+    if (openfile == filename) return true;
+    else closedatabase ();
+  }
+
+  openfile = filename;
+
+  // Map the DB mode values into GDBM mode values
+  int gdbm_mode = GDBM_READER;
+  if (mode == DB_WRITER)
+  {
+    gdbm_mode = GDBM_WRITER;
+  }
+  else if (mode == DB_WRITER_CREATE)
+  {
+    gdbm_mode = GDBM_WRCREAT;
+  }
+
+  char *namebuffer = filename.getcstr();
+  do {
+#ifdef __WIN32__
+    gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL, (need_filelock) ? 1 : 0);
+#else
+    gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL);
+#endif
+    --num_retrys;
+  } while (num_retrys>0 && gdbmfile==NULL &&
+	   (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER));
+  delete []namebuffer;
+  
+  if (gdbmfile == NULL && logout != NULL) {
+    outconvertclass text_t2ascii;
+    (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
+  }
+
+  return (gdbmfile != NULL);
+}
+
+
+void gdbmclass::closedatabase ()
+{
+  if (gdbmfile == NULL) return;
+  
+  gdbm_close (gdbmfile);
+  gdbmfile = NULL;
+  openfile.clear();
+}
+
+
+void gdbmclass::deletekey (const text_t &key)
+{
+  if (gdbmfile == NULL) return;
+
+  // get a utf-8 encoded c string of the unicode key
+  datum key_data;
+  key_data.dptr = (to_utf8(key)).getcstr();
+  if (key_data.dptr == NULL) return;
+  key_data.dsize = strlen (key_data.dptr);
+
+  // delete the key
+  gdbm_delete (gdbmfile, key_data);
+
+  // free up the key memory
+  delete []key_data.dptr;
+}
+
+
+// returns file extension string
+text_t gdbmclass::getfileextension ()
+{
+  if (littleEndian()) return ".ldb";
+  return ".bdb";
+}
+
+
+// returns true on success
+bool gdbmclass::getkeydata (const text_t& key, text_t &data)
+{
+  datum key_data;
+  datum return_data;
+
+  if (gdbmfile == NULL) return false;
+  
+  // get a utf-8 encoded c string of the unicode key
+  key_data.dptr = (to_utf8(key)).getcstr();
+  if (key_data.dptr == NULL) {
+    if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
+    return false;
+  }
+  key_data.dsize = strlen (key_data.dptr);
+  
+  // fetch the result
+  return_data = gdbm_fetch (gdbmfile, key_data);
+  delete []key_data.dptr;
+  
+  if (return_data.dptr == NULL) return false;
+
+  data.setcarr (return_data.dptr, return_data.dsize);
+  free (return_data.dptr);
+  data = to_uni(data);  // convert to unicode
+
+  return true;
+}
+
+
+// returns array of keys
+text_tarray gdbmclass::getkeys ()
+{
+  text_tarray keys;
+
+  text_t key = getfirstkey();
+  while (!key.empty())
+  {
+    keys.push_back(key);
+    key = getnextkey(key);
+  }
+
+  return keys;
+}
+
+
+// returns true on success
+bool gdbmclass::setkeydata (const text_t &key, const text_t &data)
+{
+  if (gdbmfile == NULL) return false;
+  
+  // store the value
+  datum key_data;
+  datum data_data;
+
+  // get a utf-8 encoded c string of the unicode key
+  key_data.dptr = (to_utf8(key)).getcstr();
+  if (key_data.dptr == NULL) {
+    if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
+    return false;
+  }
+  key_data.dsize = strlen (key_data.dptr);
+
+  data_data.dptr = (to_utf8(data)).getcstr();
+  if (data_data.dptr == NULL) {
+    if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
+    delete []key_data.dptr;
+    return false;
+  }
+  data_data.dsize = strlen (data_data.dptr);
+
+  int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
+  delete []key_data.dptr;
+  delete []data_data.dptr;
+
+  return (ret == 0);
+}
+
+
+// ----------------------------------------------------------------------------------------
+//   GDBM-ONLY FUNCTIONS
+// ----------------------------------------------------------------------------------------
+
+// getfirstkey and getnextkey are used for traversing the database
+// no insertions or deletions should be carried out while traversing
+// the database. when there are no keys left to visit in the database
+// an empty string is returned.
+text_t gdbmclass::getfirstkey ()
+{
+  if (gdbmfile == NULL) return g_EmptyText;
+
+  // get the first key
+  datum firstkey_data = gdbm_firstkey (gdbmfile);
+  if (firstkey_data.dptr == NULL) return g_EmptyText;
+
+  // convert it to text_t
+  text_t firstkey;
+  firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize);
+  free (firstkey_data.dptr);
+  return to_uni(firstkey);  // convert to unicode
+}
+
+
+text_t gdbmclass::getnextkey (const text_t &key)
+{
+  if (gdbmfile == NULL || key.empty()) return g_EmptyText;
+
+  // get a utf-8 encoded c string of the unicode key
+  datum key_data;
+  key_data.dptr = (to_utf8(key)).getcstr();
+  if (key_data.dptr == NULL) return g_EmptyText;
+  key_data.dsize = strlen (key_data.dptr);
+  
+  // get the next key
+  datum nextkey_data = gdbm_nextkey (gdbmfile, key_data);
+  if (nextkey_data.dptr == NULL) {
+    delete []key_data.dptr;
+    return g_EmptyText;
+  }
+
+  // convert it to text_t
+  text_t nextkey;
+  nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize);
+  free (nextkey_data.dptr);
+  delete []key_data.dptr;
+  return to_uni(nextkey);  // convert to unicode
+}
Index: /gsdl/trunk/gs2common/src/lib/gdbmclass.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gdbmclass.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gdbmclass.h	(revision 16561)
@@ -0,0 +1,89 @@
+/**********************************************************************
+ *
+ * gdbmclass.h -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#ifndef GDBMCLASS_H
+#define GDBMCLASS_H
+
+
+#include "gsdlconf.h"
+#include "dbclass.h"
+
+#ifdef __WIN32__
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+#include "autoconf.h"
+#include "systems.h"
+#include "gdbmconst.h"
+#include "gdbm.h"
+#ifdef __cplusplus
+  }
+#endif
+
+#else
+#include <gdbm.h>
+#endif
+
+
+class gdbmclass : public dbclass
+{
+public:
+  gdbmclass() { gdbmfile = NULL; };
+  ~gdbmclass();
+
+  // returns true if opened
+  bool opendatabase (const text_t &filename, int mode, int num_retrys, bool need_filelock);
+
+  void closedatabase ();
+
+  void deletekey (const text_t &key);
+
+  // returns file extension string
+  text_t getfileextension ();
+
+  // returns true on success
+  bool getkeydata (const text_t& key, text_t &data);
+
+  // returns array of keys
+  text_tarray getkeys ();
+
+  // returns true on success
+  bool setkeydata (const text_t &key, const text_t &data);
+
+protected:
+  text_t openfile;
+  GDBM_FILE gdbmfile;
+
+  // getfirstkey and getnextkey are used for traversing the database
+  // no insertions or deletions should be carried out while traversing
+  // the database. when there are no keys left to visit in the database
+  // an empty string is returned.
+  text_t getfirstkey ();
+  text_t getnextkey (const text_t &key);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/gsdlconf.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdlconf.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdlconf.h	(revision 16561)
@@ -0,0 +1,161 @@
+/**********************************************************************
+ *
+ * gsdlconf.h -- system specific includes
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef GSDLCONF_H
+#define GSDLCONF_H
+
+#define GSDL_VERSION "svn-trunk"
+
+#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_) || defined(WIN32)
+#ifndef __WIN32__
+#define __WIN32__ 1
+#endif
+#endif
+
+#if defined(__WIN32__) && !defined(__CYGWIN__)
+#include "../../win32cfg.h"
+#else
+#include "../../config.h"
+#endif
+
+// Note: all macros used in the gsdl software (except the _H macros) should
+// now start with GSDL_
+
+// GSDL_NEED_STRINGS_H should be defined if some string functions
+// needed (like strcasecmp) are in strings.h
+#if defined(__IBMCPP__)
+#  define GSDL_NEED_STRINGS_H
+#endif
+
+// GSDL_USE_IOS_H should be defined if the .h extension needs to
+// be used for the io stream headers
+#if (defined(__GNUG__)  && __GNUG__ + 0 < 3) || defined(__IBMCPP__)
+#  define GSDL_USE_IOS_H 1
+#endif
+
+// GSDL_USE_STL_H should be defined if the .h extension needs to
+// be used for the stl header files
+#if defined(__IBMCPP__)
+#  define GSDL_USE_STL_H 1
+#endif
+
+// GSDL_USE_ALGO_H should be defined if algo.h should be used 
+// instead of algorithm.h
+#if defined(__IBMCPP__)
+#  define GSDL_USE_ALGO_H 1
+#endif
+
+// GSDL_NAMESPACE_BROKEN should be defined if namespaces should
+// not be used
+#if (defined(__GNUG__)  && __GNUG__ + 0 < 3) || defined(__IBMCPP__)
+#  define GSDL_NAMESPACE_BROKEN 1
+#endif
+
+// GSDL_USE_OBJECTSPACE should now be used instead of USE_OBJECTSPACE
+#if defined(USE_OBJECTSPACE)
+#  define GSDL_USE_OBJECTSPACE
+#endif
+
+// GSDL_NEED_DESTROY_USHORT should be defined if the compiler needs
+// a definition for void destroy(short unsigned int *)
+#if defined(__IBMCPP__)
+# define GSDL_NEED_DESTROY_USHORT
+#endif
+
+// mktemp
+
+#if !defined(__WIN32__) || defined(__GNUC__)
+#include <unistd.h>
+#define GSDL_MKTEMP(str) mktemp(str)
+#else
+#include <io.h>
+#define GSDL_MKTEMP(str) _mktemp(str)
+#endif
+
+
+
+// file locking
+
+#if defined(__WIN32__)
+
+#if !defined(LK_UNLOCK) && defined(LK_UNLCK)
+#define LK_UNLOCK LK_UNLCK
+#endif
+
+#include <io.h>
+#include <sys/locking.h>
+
+#define GSDL_UNLOCK_FILE(fd) _locking(fd, LK_UNLCK, 200)
+#define GSDL_LOCK_FILE(fd) lock_val = _locking(fd, LK_NBLCK, 200)
+
+#else     /* not WIN32 */
+
+#if GSDL_USE_FLOCK
+#include <sys/lock.h>
+
+#ifndef LOCK_SH
+#define LOCK_SH	1
+#endif
+
+#ifndef LOCK_EX
+#define LOCK_EX	2
+#endif
+
+#ifndef LOCK_NB
+#define LOCK_NB 4
+#endif
+
+#ifndef LOCK_UN
+#define LOCK_UN 8
+#endif
+
+#define GSDL_UNLOCK_FILE(fd) flock (fd, LOCK_UN)
+#define GSDL_LOCK_FILE(fd) lock_val = flock (fd, LOCK_EX + LOCK_NB)
+
+#else  /* ! GSDL_USE_FLOCK */
+// use fcntl
+#include <fcntl.h>
+#define GSDL_UNLOCK_FILE(fd) \
+	{					\
+	  struct flock flock;			\
+	  flock.l_type = F_UNLCK;		\
+	  flock.l_whence = SEEK_SET;		\
+	  flock.l_start = flock.l_len = 0L;	\
+	  fcntl (fd, F_SETLK, &flock);	\
+	}
+#define GSDL_LOCK_FILE(fd) \
+	{					\
+	  struct flock flock;			\
+	  flock.l_type = F_WRLCK;		\
+	  flock.l_whence = SEEK_SET;			\
+	  flock.l_start = flock.l_len = 0L;	\
+	  lock_val = fcntl (fd, F_SETLK, &flock);	\
+	}
+
+#endif
+#endif
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.cpp	(revision 16561)
@@ -0,0 +1,227 @@
+/**********************************************************************
+ *
+ * gsdlsitecfg.cpp -- 
+ * Copyright (C) 2008  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.
+ *
+ *********************************************************************/
+
+#include "fileutil.h"
+#include "gsdlsitecfg.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+class __site_configuration : public configurable {
+private:
+  text_t *gsdlhome;
+  text_t *collecthome;
+  text_t *httpdomain;
+  text_t *httpprefix;
+  text_t *collection;
+  text_tset *actions;
+  text_tset *browsers;
+  int    *maxrequests;
+public:
+  __site_configuration (text_t *_gsdlhome, text_t* _collecthome, int *_maxrequests) {
+    gsdlhome = _gsdlhome;
+    collecthome = _collecthome;
+    httpdomain = NULL;
+    httpprefix = NULL;
+    collection = NULL;
+    actions = NULL;
+    browsers = NULL;
+    maxrequests = _maxrequests;
+  }
+
+  __site_configuration (text_t *_gsdlhome, text_t* _collecthome, 
+			text_t *_httpdomain, text_t *_httpprefix) {
+    gsdlhome    = _gsdlhome;
+    collecthome  = _collecthome;
+    httpdomain = _httpdomain;
+    httpprefix = _httpprefix;
+    collection = NULL;
+    actions = NULL;
+    browsers = NULL;
+    maxrequests = NULL;
+  }
+
+  __site_configuration (text_t *_gsdlhome, text_t* _collecthome, 
+			text_t *_httpdomain,
+			text_t *_httpprefix, text_t *_collection) {
+    gsdlhome    = _gsdlhome;
+    collecthome  = _collecthome;
+    httpdomain = _httpdomain;
+    httpprefix = _httpprefix;
+    collection = _collection;
+    actions = NULL;
+    browsers = NULL;
+    maxrequests = NULL;
+  }
+
+  __site_configuration (text_t *_httpprefix, text_tset *_actions, text_tset *_browsers) {
+    gsdlhome    = NULL;
+    collecthome  = NULL;
+    httpdomain = NULL;
+    httpprefix = _httpprefix;
+    collection = NULL;
+    actions = _actions;
+    browsers = _browsers;
+    maxrequests = NULL;
+  }
+
+  inline void configure (const text_t &key, const text_tarray &cfgline) {
+    if (gsdlhome != NULL && key == "gsdlhome") {
+      *gsdlhome = cfgline[0];
+
+      if ((collecthome != NULL) && (collecthome->empty())) {
+	// defaults to <gsdlhome>/collect
+	*collecthome = filename_cat(*gsdlhome,"collect");
+      }
+    }
+
+    if (collecthome != NULL && key == "collecthome") {
+      if (!collecthome->empty()) {
+	// if it has been previously set to default, free and then reassign
+	collecthome->clear();
+      }
+      *collecthome = cfgline[0];
+    }
+
+    if (httpprefix != NULL && key == "httpprefix") {
+      *httpprefix = cfgline[0];
+    }
+
+    if (httpdomain != NULL && key == "httpdomain") {
+      *httpdomain = cfgline[0];
+    }
+
+    if (collection != NULL && key == "collection") {
+      *collection = cfgline[0];
+    } 
+
+    if (actions != NULL && key == "actions") {
+      actions->clear();
+      text_tarray::const_iterator thisAction = cfgline.begin();
+      text_tarray::const_iterator endAction = cfgline.end();
+      while (thisAction != endAction) {
+	actions->insert(*thisAction);
+	++thisAction;
+      }
+    } 
+
+    if (browsers != NULL && key == "browsers") {
+      browsers->clear();
+      text_tarray::const_iterator thisBrowser = cfgline.begin();
+      text_tarray::const_iterator endBrowser = cfgline.end();
+      while (thisBrowser != endBrowser) {
+	browsers->insert(*thisBrowser);
+	++thisBrowser;
+      }
+    }
+
+    if (maxrequests != NULL && key == "maxrequests") {
+      *maxrequests = cfgline[0].getint();
+      if ((*maxrequests) < 1) {
+	*maxrequests = 1;
+      }
+    }
+  }
+};
+
+
+// reads site configuration file returning true on success
+// also sets gsdlhome and maxrequests 
+// gsdlsite.cfg should be in same directory as library executable
+bool site_cfg_read (configurator gsdlconfigurator, text_t &gsdlhome, 
+		    text_t& collecthome, int &maxrequests)
+{
+  __site_configuration sitecfg(&gsdlhome, &collecthome, &maxrequests);
+  gsdlconfigurator.add_configurable(&sitecfg);
+
+  // blank the gsdlhome and collecthome text
+  gsdlhome.clear();
+  collecthome.clear();
+
+  if (gsdlconfigurator.configure("gsdlsite.cfg"))
+  {
+    return true;
+  }
+
+  return false;
+}
+
+
+// this version grabs gsdlhome, collecthome, httpdomain and httpprefix, 
+// returns false if it can't find all of them
+bool site_cfg_read (text_t &gsdlhome, text_t& collecthome, text_t &httpdomain, 
+		    text_t &httpprefix)
+{
+  // get gsdlhome etc
+  __site_configuration sitecfg(&gsdlhome, &collecthome, &httpdomain, &httpprefix);
+  configurator gsdlconfigurator(&sitecfg);
+
+  gsdlhome.clear();
+  collecthome.clear();
+  httpdomain.clear();
+  httpprefix.clear();
+
+  if (gsdlconfigurator.configure("gsdlsite.cfg"))
+  {
+    return true;
+  }
+
+  return false;
+}
+
+
+// this version grabs gsdlhome, collecthome, httpdomain, httpprefix, collection, 
+// returns false if it can't find gsdlhome, httpdomain and httpprefix
+bool site_cfg_read (text_t &gsdlhome, text_t& collecthome, text_t &httpdomain, 
+		    text_t &httpprefix, text_t &collection)
+{
+  // get gsdlhome etc
+  __site_configuration sitecfg(&gsdlhome, &collecthome, &httpdomain, &httpprefix, &collection);
+  configurator gsdlconfigurator(&sitecfg);
+	
+  gsdlhome.clear();
+  collecthome.clear();
+  httpdomain.clear();
+  httpprefix.clear();
+  collection.clear();
+	
+  if (gsdlconfigurator.configure("gsdlsite.cfg") &&
+      !gsdlhome.empty() && !collecthome.empty() 
+      && !httpdomain.empty() && !httpprefix.empty())
+  {
+    return true;
+  }	
+
+  return false;
+}
Index: /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdlsitecfg.h	(revision 16561)
@@ -0,0 +1,42 @@
+/**********************************************************************
+ *
+ * gsdlsitecfg.h -- 
+ * Copyright (C) 2008  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.
+ *
+ *********************************************************************/
+
+
+#ifndef GSDLSITECFG_H
+#define GSDLSITECFG_H
+
+
+#include "cnfgator.h"
+#include "text_t.h"
+
+
+// reads gsdlsite.cfg file returning true on success
+bool site_cfg_read (configurator gsdlconfigurator, text_t &gsdlhome, 
+		    text_t& collecthome, int &maxrequests);
+bool site_cfg_read (text_t &gsdlhome, text_t& collecthome, text_t &httpdomain, text_t &httpprefix);
+bool site_cfg_read (text_t &gsdlhome, text_t& collecthome, text_t &httpdomain, text_t &httpprefix, text_t &collection);
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/gsdltimes.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdltimes.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdltimes.cpp	(revision 16561)
@@ -0,0 +1,163 @@
+/**********************************************************************
+ *
+ * gsdltimes.cpp -- functions to deal with time
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdltimes.h"
+
+// returns a string of the form "YYYY/MM/DD hh:mm:ss" (expressed in
+// Coordinated Universal Time (UTC))
+// returns "" if an error occurs
+// !! Actually, we now return a string in localtime, as text2time returns
+//    a time in localtime (mktime assumes local timezone). - jrm21 Sep 2002
+text_t time2text (time_t time) {
+  // tm *tm_ptr = gmtime(&time);
+  tm *tm_ptr = localtime(&time);
+
+  if (tm_ptr == NULL) return g_EmptyText;
+
+  // year
+  text_t timestr(tm_ptr->tm_year+1900);
+  timestr.push_back('/');
+
+  // month (note the +1)
+  if ((tm_ptr->tm_mon+1) < 10) timestr.push_back('0');
+  timestr += (tm_ptr->tm_mon+1);
+  timestr.push_back('/');
+
+  // day
+  if (tm_ptr->tm_mday < 10) timestr.push_back('0');
+  timestr += tm_ptr->tm_mday;
+  timestr.push_back(' ');
+
+  // hour
+  if (tm_ptr->tm_hour < 10) timestr.push_back('0');
+  timestr += tm_ptr->tm_hour;
+  timestr.push_back(':');
+
+  // minutes
+  if (tm_ptr->tm_min < 10) timestr.push_back('0');
+  timestr += tm_ptr->tm_min;
+  timestr.push_back(':');
+
+  // seconds
+  if (tm_ptr->tm_sec < 10) timestr.push_back('0');
+  timestr += tm_ptr->tm_sec;
+
+  return timestr;
+}
+
+// takes a string like that returned by time2text and returns corresponding time_t
+// returns -1 if an error occurs
+time_t text2time (const text_t &timestr) {
+  tm timetm;
+  text_t tempstr;
+  
+  text_t::const_iterator time_here = timestr.begin();
+  text_t::const_iterator time_end = timestr.end();
+
+  // year
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, '/', tempstr);
+  timetm.tm_year = tempstr.getint() - 1900;
+  if (timetm.tm_year < 0) return (time_t) -1;
+
+  // month (note the -1)
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, '/', tempstr);
+  timetm.tm_mon = tempstr.getint() - 1;
+  if (timetm.tm_mon < 0 || timetm.tm_mon > 11)
+    return (time_t) -1;
+  
+  // day
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, ' ', tempstr);
+  timetm.tm_mday = tempstr.getint();
+  if (timetm.tm_mday < 1 || timetm.tm_mday > 31)
+    return (time_t) -1;
+  
+  // hour
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, ':', tempstr);
+  timetm.tm_hour = tempstr.getint();
+  if (timetm.tm_hour < 0 || timetm.tm_hour > 23)
+    return (time_t) -1;
+  
+  // min
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, ':', tempstr);
+  timetm.tm_min = tempstr.getint();
+  if (timetm.tm_min < 0 || timetm.tm_min > 59)
+    return (time_t) -1;
+  
+  // sec
+  if (time_here == time_end) return (time_t) -1;
+  time_here = getdelimitstr (time_here, time_end, ':', tempstr);
+  timetm.tm_sec = tempstr.getint();
+  if (timetm.tm_sec < 0 || timetm.tm_sec > 59)
+    return (time_t) -1;
+
+  timetm.tm_isdst=0;
+
+  /* This is for darwin - mktime seems to use this, so don't leave it
+     uninitialised!! Maybe the same on freebsd/netbsd?
+  */
+#if defined(__MACH__) && defined(__APPLE__)
+  timetm.tm_gmtoff=0;
+#endif
+
+  // mktime assumes timetm is localtime
+  return mktime (&timetm);
+}
+
+// returns current date and time formatted like "Thu Dec 07 23:43:38 GMT 2000"
+// if ltime is true return value will be expressed in local time, otherwise
+// it'll be Coordinated Universal Time (UTC) - no promises are made that the
+// resulting string will be formatted exactly as expected, especially on windows
+// returns "" if an error occurs
+text_t get_date (bool ltime) {
+
+  tm *tm_ptr = NULL;
+  time_t t = time(NULL);
+  if (ltime) tm_ptr = localtime (&t);
+  else tm_ptr = gmtime (&t);
+  if (tm_ptr == NULL) return g_EmptyText;
+  char *timestr = new char[128];
+  text_t ret;
+
+  // we do this in such an ugly way because windows is kind of flakey when
+  // it comes to timezones
+  if (ltime) {
+    strftime (timestr, 128, "%a %b %d %H:%M:%S %z %Y", tm_ptr);
+    ret.setcstr (timestr);
+  } else {
+    strftime (timestr, 128, "%a %b %d %H:%M:%S", tm_ptr);
+    ret.setcstr (timestr);
+    ret += " GMT ";
+    strftime (timestr, 128, "%Y", tm_ptr);
+    ret += timestr;
+  }
+
+  delete []timestr;
+  return ret;
+}
Index: /gsdl/trunk/gs2common/src/lib/gsdltimes.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdltimes.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdltimes.h	(revision 16561)
@@ -0,0 +1,46 @@
+/**********************************************************************
+ *
+ * gsdltimes.h -- functions to deal with time
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef GSDLTIMES_H
+#define GSDLTIMES_H
+
+#include <time.h>
+#include "text_t.h"
+
+// dates are stored in the format YYYY/MM/DD hh:mm:ss
+
+// returns "" if an error occurs
+text_t time2text (time_t time);
+
+// returns -1 if an error occurs
+time_t text2time (const text_t &timestr);
+
+
+// returns formatted date in local or UTC time
+// returns "" if an error occurs
+text_t get_date (bool ltime);
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/gsdltools.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdltools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdltools.cpp	(revision 16561)
@@ -0,0 +1,171 @@
+/**********************************************************************
+ *
+ * gsdltools.cpp -- 
+ * 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  The 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.
+ *
+ *********************************************************************/
+
+#include "gsdltools.h"
+
+#if defined(__WIN32__)
+#include <windows.h>
+#include <process.h>
+#endif
+
+bool littleEndian() {
+  char s[2] = {'\xFE', '\xEF'};
+
+  if (sizeof(unsigned short) == 2)
+    return *(unsigned short*)s == 0xEFFE;
+  else if (sizeof(unsigned int) == 2)
+    return *(unsigned int*)s == 0xEFFE;
+}
+
+text_t dm_safe (const text_t &instring) {
+
+  text_t outstring;
+  text_t::const_iterator here = instring.begin();
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (*here == '_' || *here == '\\') outstring.push_back('\\');
+    outstring.push_back(*here);
+    ++here;
+  }
+  return outstring;
+}
+
+void dm_js_safe(const text_t &instring, text_t &outstring)
+{
+  text_t::const_iterator here = instring.begin();
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (*here == '_') outstring.push_back('\\');
+    else if (*here == '\\' || *here == '\'') {
+      outstring.push_back('\\');
+      outstring.push_back('\\');
+    }
+    outstring.push_back(*here);
+    ++here;
+  }
+}
+
+// gsdl_system creates a new process for the cmd command (which
+// may contain arguments).
+// cmd should contain the full path of the program to run.
+// The child process inherits the environment of the calling
+// process.
+// If sync is true a synchronous call will be made, otherwise
+// an asyncronous call.
+// If sync is true the return value will be the exit code of
+// the child process or -1 if the child process wasn't started.
+// If sync is false the return value will be 0 if the process
+// was started ok or -1 if it failed.
+int gsdl_system (const text_t &cmd, bool sync, ostream &logout) {
+  if (cmd.empty()) return -1;
+  char *cmd_c = cmd.getcstr();
+
+#if defined (__WIN32__)
+  // the windows version - this is implemented this way
+  // to prevent windows popping up all over the place when
+  // we call a console application (like perl)
+  STARTUPINFO ps = {sizeof(STARTUPINFO), NULL, NULL, NULL,
+                    0, 0, 0, 0, 0, 0,
+                    0, STARTF_USESHOWWINDOW,
+                    SW_HIDE, 0, NULL,
+		    NULL, NULL, NULL};
+  PROCESS_INFORMATION pi;
+  BOOL res = CreateProcess(NULL,
+                           cmd_c,
+                           NULL,
+                           NULL,
+                           FALSE,
+#if defined (GSDL_LOCAL_LIBRARY)
+			   NULL,
+#else
+			   DETACHED_PROCESS,
+#endif
+                           NULL,
+                           NULL,
+                           &ps,
+                           &pi);
+  if (!res) {
+    logout << "gsdl_system failed to start " << cmd_c 
+	   << " process, error code " << GetLastError();
+    delete []cmd_c;
+    return -1;
+  } 
+
+  DWORD ret = 0;
+  if (sync) { // synchronous system call
+    // wait until child process exits.
+    WaitForSingleObject(pi.hProcess, INFINITE);
+    // set ret to exit code of child process
+    GetExitCodeProcess(pi.hProcess, &ret);
+  }
+
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+
+#else
+  // the unix version
+  int ret = 0;
+  if (sync) { // synchronous system call
+    // make sure the command interpreter is found
+    if (system (NULL) == 0) {
+      logout << "gsdl_system failed to start " << cmd_c
+	     << " process, command interpreter not found\n";
+      delete []cmd_c;
+      return -1;
+    }
+    ret = system (cmd_c);
+
+  } else { // asynchronous system call
+    int pid = fork();
+    if (pid == -1) {
+      delete []cmd_c;
+      return -1;
+    }
+    if (pid == 0) {
+      // child process
+      char *argv[4];
+      argv[0] = "sh";
+      argv[1] = "-c";
+      argv[2] = cmd_c;
+      argv[3] = 0;
+      execv("/bin/sh", argv);
+    }
+  }
+#endif
+
+  delete []cmd_c;
+  return ret;
+}
+
+// attempts to work out if perl is functional
+bool perl_ok (ostream &logout) {
+#if defined(__WIN32__)
+  text_t cmd = "perl -e \"exit 0\"";
+#else 
+  text_t cmd = "perl -e 'exit 0'";
+#endif
+  int i = gsdl_system (cmd, true, logout);
+  return (i == 0);
+}
Index: /gsdl/trunk/gs2common/src/lib/gsdltools.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdltools.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdltools.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * gsdltools.h -- 
+ * 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  The 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.
+ *
+ *********************************************************************/
+
+
+#ifndef GSDLTOOLS_H
+#define GSDLTOOLS_H
+
+// don't actually use this but some windows
+// versions of STL complain about use of 
+// bool keyword without it
+#include "text_t.h"
+
+// returns true if machine is little endian
+bool littleEndian();
+
+// escapes '\' and '_' characters with '\'
+// note that single '\' characters occurring 
+// naturally within text (or filenames!!) will
+// be removed by the dm macro language
+text_t dm_safe (const text_t &instring);
+
+// escapes "\", "'" and "_" characters with '\'
+// note that single '\' characters occurring 
+// naturally within text (or filenames!!) will
+// be removed by the dm macro language
+//WARNING: instring will be appended to outstring
+// so is your responsability to clean the outstring
+// before call this function;
+void dm_js_safe(const text_t &instring, text_t &outstring);
+inline text_t dm_js_safe (const text_t &instring) {text_t outstring; dm_js_safe(instring, outstring); return outstring;}
+
+// gsdl_system creates a new process for the cmd command (which
+// may contain arguments).
+// cmd should contain the full path of the program to run.
+// The child process inherits the environment of the calling
+// process.
+// If sync is true a synchronous call will be made, otherwise
+// an asyncronous call.
+// If sync is true the return value will be the exit code of
+// the child process or -1 if the child process wasn't started.
+// If sync is false the return value will be 0 if the process
+// was started ok or -1 if it failed.
+int gsdl_system (const text_t &cmd, bool sync, ostream &logout);
+
+// attempts to work out if perl is functional
+bool perl_ok (ostream &logout);
+
+#endif
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2common/src/lib/gsdlunicode.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdlunicode.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdlunicode.cpp	(revision 16561)
@@ -0,0 +1,671 @@
+/**********************************************************************
+ *
+ * gsdlunicode.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.
+ *
+ *********************************************************************/
+
+#include "gsdlunicode.h"
+
+
+// unitool is currently in mg, if mg is not being used it should
+// be moved into GSDLHOME/lib 
+#include "unitool.h"
+
+#include "fileutil.h"
+
+#include <stdio.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+// converts a unicode encode text_t string to a utf-8
+// encoded text_t string
+text_t to_utf8 (text_t::const_iterator here, text_t::const_iterator end) {
+  text_t out;
+
+  unsigned char thischar[MAXUTF8CHARLEN];
+  int i, charlen;
+
+  while (here != end) {
+    charlen = output_utf8_char (*here, thischar, &thischar[MAXUTF8CHARLEN-1]);
+    for (i=0; i<charlen; ++i) out.push_back(thischar[i]);
+    ++here;
+  }
+
+  return out;
+}
+
+// converts a utf-8 encoded text_t string to a unicode
+// encoded text_t string
+text_t to_uni (const text_t &in) {
+  text_t out;
+  unsigned char *in_cstr = (unsigned char *)in.getcstr();
+  unsigned char *here = in_cstr;
+  unsigned char *end = in_cstr;
+
+  unsigned short unichar;
+  int charlen = 0;
+
+  // get the last valid character in the string
+  while (*end != '\0') ++end;
+  --end;
+
+  while ((charlen = parse_utf8_char (here, end, &unichar)) > 0) {
+    out.push_back(unichar);
+    here += charlen;
+  }
+
+  delete []in_cstr;
+
+  return out;
+}
+
+
+// this works for all unicode values < 65536...
+void utf16outconvertclass::convert (char *out, size_t maxlen, size_t &len, status_t &status) {
+  // we should already have text_t* input set...
+  if (input == NULL || out == NULL)
+  {
+    status = finished;
+    return;
+  }
+  unsigned char *output = (unsigned char *)out;
+  text_t::iterator textend = input->end();
+  len = 0;
+  if (maxlen % 2) --maxlen; // we need an even number of output bytes...
+  while ((len < maxlen) && (texthere != textend)) {
+    unsigned short int uni_char=(unsigned short int) *texthere;
+    // big endian utf-16...
+    if (uni_char < 256) {
+      out[len]=0;   
+      out[len+1]=uni_char;
+    } else {
+      out[len]=uni_char >> 8;
+      out[len+1]=uni_char & 255;
+    }
+    len+=2;
+    ++texthere;
+  }
+  if (texthere==textend)
+    status=finished;
+  else
+    status=unfinished;
+}
+
+
+utf8inconvertclass::utf8inconvertclass () {
+  utf8buflen = 0;
+}
+
+void utf8inconvertclass::reset () {
+  start = NULL;
+  len = 0;
+  utf8buflen=0;
+}
+
+void utf8inconvertclass::convert (text_t &output, status_t &status) {
+  output.clear();
+  output.reserve (len/3);
+  
+  if (start == NULL || len == 0) {
+    if (utf8buflen == 0) status = finished;
+    else status = stopped;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *here = (unsigned char *)start;
+  unsigned char *end = here+len-1;
+  unsigned short c;
+  size_t realcharlen;
+
+  size_t charlen = getutf8charlen ();
+  while (len > 0) {
+    if (charlen == 0) {
+      // start parsing a new character
+      utf8buflen = 0;
+
+      // fast common case
+      while (len > 3) {
+  	realcharlen = parse_utf8_char (here, end, &c);
+  	output.push_back (c);
+  	here += realcharlen;
+  	len -= realcharlen;
+      }
+
+      utf8buf[utf8buflen++] = *here;
+      ++here;
+      --len;
+      charlen = getutf8charlen ();
+
+    } else if (utf8buflen < charlen) {
+      // assumes charlen is always less than MAXUTF8CHARLEN
+      utf8buf[utf8buflen++] = *here;
+      ++here;
+      --len;
+    }
+
+    if (utf8buflen == charlen) {
+      // got a complete character
+      realcharlen = parse_utf8_char (utf8buf, &utf8buf[utf8buflen-1], &c);
+      output.push_back (c);
+      
+      // move any unparsed characters. If an error occurred some of
+      // the characters might be unused.
+      int i;
+      int diff = utf8buflen - realcharlen;
+      for (i=0; i < diff; ++i) utf8buf[i] = utf8buf[i+diff];
+      utf8buflen = diff;
+      charlen = getutf8charlen ();
+    }
+  }
+
+  start = (char *)here; // save current position
+
+  if (utf8buflen == 0) status = finished;
+  else status = stopped;
+}
+
+
+// returns the length that the current contents of the 
+// utf8buf should be
+size_t utf8inconvertclass::getutf8charlen () {
+  if (utf8buflen == 0) return 0;
+
+  // one byte character
+  if (utf8buf[0] < 0x80) return 1;
+
+  // error, is not the start of a utf-8 character
+  if (utf8buf[0] < 0xc0) return 1;
+
+  // two bute character
+  if (utf8buf[0] < 0xe0) return 2;
+
+  // three byte character
+  if (utf8buf[0] < 0xf0) return 3;
+
+  // error, character too long for unicode
+  return 1;
+}
+
+
+void utf8outconvertclass::reset () {
+  input = NULL;
+  outs = NULL;
+  utf8buflen = 0;
+  utf8bufhere = 0;
+}
+
+// note that convert does not null-terminate the
+// output array of characters
+void utf8outconvertclass::convert (char *output, size_t maxlen, 
+				   size_t &len, status_t &status) {
+  if (input == NULL || output == NULL) {
+    if (utf8buflen == 0) status = finished;
+    else status = unfinished;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *uoutput = (unsigned char *)output;
+  text_t::iterator textend = input->end();
+  len = 0;
+  while (len < maxlen) {
+    // empty the contents of the internal buffer
+    if (utf8buflen > 0) {
+      while (len < maxlen && utf8bufhere < utf8buflen) {
+	*uoutput = utf8buf[utf8bufhere];
+	++uoutput;
+	++len;
+	++utf8bufhere;
+      }
+
+      if (utf8bufhere == utf8buflen) {
+	utf8bufhere = 0;
+	utf8buflen = 0;
+      }
+    }
+
+    // fill up the buffer with the next character
+    if (utf8buflen == 0) {
+      if (texthere == textend) break; // finished!
+      if (!rzws || (*texthere != 0x200b))
+	utf8buflen = output_utf8_char (*texthere, utf8buf, 
+				       &utf8buf[MAXUTF8CHARLEN-1]);
+      ++texthere;
+      utf8bufhere = 0;
+    }
+  }
+  
+  if (texthere == textend && utf8buflen == 0) status = finished;
+  else status = unfinished;
+}
+
+
+
+
+
+
+mapdata_t::mapdata_t () {
+
+  // reset all the map ptrs to be NULL
+  for (int i=0; i<256; ++i) {
+    ptrs[i] = (unsigned short *)NULL;
+  }
+
+  // say nothing has been loaded
+  loaded = false;
+}
+
+
+mapconvert::mapconvert () {
+  absentc = 0;
+}
+
+// setmapfile will cause loadmapfile to be called when conversion is
+// needed
+bool mapconvert::setmapfile (const text_t &themapfile, unsigned short theabsentc) {
+  // check to see if the mapfile has been already loaded
+  if (mapdata.loaded && mapfile == themapfile && absentc == theabsentc) return true;
+
+  unloadmapfile ();
+  mapfile = themapfile;
+  absentc = theabsentc;
+  
+  return true;
+}
+
+
+
+// loadmapfile should be called before any conversion is done
+bool mapconvert::loadmapfile (const text_t &themapfile,
+			      unsigned short theabsentc) {
+  FILE *mapfilein = (FILE *)NULL;
+
+  // check to see if the mapfile has been already loaded
+  if (mapdata.loaded && mapfile == themapfile && absentc == theabsentc) return true;
+
+  unloadmapfile ();
+  mapfile = themapfile;
+  absentc = theabsentc;
+
+  // open the map file
+  char *cfilename = mapfile.getcstr();
+  if (cfilename == (char *)NULL) return false;
+  mapfilein = fopen(cfilename, "rb");
+  delete []cfilename; cfilename = NULL;
+
+  if (mapfilein == (FILE *)NULL) return false;
+
+  unsigned char c, n1, n2;
+  unsigned short *arrptr;
+  int i;
+  c = fgetc (mapfilein);
+  while (!feof (mapfilein)) {
+    if (mapdata.ptrs[c] == (unsigned short *)NULL) {
+      // allocate a new array
+      arrptr = new unsigned short[256];
+      mapdata.ptrs[c] = arrptr;
+    } else arrptr = mapdata.ptrs[c];
+
+    // clear the array
+    for (i=0; i<256; ++i) arrptr[i] = 0;
+    
+    // read in this block
+    n1 = fgetc (mapfilein);
+    n2 = fgetc (mapfilein);
+    i=0;
+    while (!feof (mapfilein)) {
+      arrptr[i] = ((unsigned short)n1 << 8) | (unsigned short)n2;
+
+      ++i;
+      if (i >= 256) break;
+      n1 = fgetc (mapfilein);
+      n2 = fgetc (mapfilein);
+    }
+
+    c = fgetc (mapfilein);
+  }
+
+  mapdata.loaded = true;
+
+  return true;
+}
+
+void mapconvert::unloadmapfile () {
+  if (!mapdata.loaded) return;
+
+  for (int i=0; i<256; ++i) {
+    if (mapdata.ptrs[i] != (unsigned short *)NULL) {
+      delete [] mapdata.ptrs[i];
+      mapdata.ptrs[i] = (unsigned short *)NULL;
+    }
+  }
+
+  mapdata.loaded = false;
+}
+
+
+unsigned short mapconvert::convert (unsigned short c) {
+  if (!mapdata.loaded) {
+    if (!mapfile.empty() && loadmapfile (mapfile, absentc)) {
+      // do nothing, successfully loaded database
+    } else return absentc;
+  }
+
+  if (c == 0) return 0; // 0 always maps to 0...
+
+  unsigned short n1 = c >> 8;
+  unsigned short n2 = c & 0xff;
+
+  unsigned short *arrptr = mapdata.ptrs[n1];
+  if (arrptr == (unsigned short *)NULL) return absentc;
+
+  if (arrptr[n2] == 0) return absentc;
+  return arrptr[n2];
+}
+
+text_t mapconvert::convert (const text_t &instr) {
+  if (!mapdata.loaded) return absentc;
+
+  text_t outstr;
+  text_t::const_iterator here = instr.begin();
+  text_t::const_iterator end = instr.end();
+
+  while (here != end) {
+    outstr.push_back(this->convert(*here));
+    ++here;
+  }
+  
+  return outstr;
+}
+
+
+
+
+mapinconvertclass::mapinconvertclass () {
+  m_multibyte = 0;
+  mapbuflen = 0;
+}
+
+void mapinconvertclass::reset () {
+  start = NULL;
+  len = 0;
+  mapbuflen=0;
+}
+
+void mapinconvertclass::convert (text_t &output, status_t &status) {
+  output.clear();
+
+  if (start == NULL || len == 0) {
+    if (mapbuflen == 0) status = finished;
+    else status = stopped;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *here = (unsigned char *)start;
+
+  size_t charlen = getmapcharlen ();
+  while (len > 0) {
+    if (charlen == 0) {
+      // start parsing a new character
+      mapbuflen = 0;
+      mapbuf[mapbuflen++] = *here;
+      ++here;
+      --len;
+      charlen = getmapcharlen ();
+
+    } else if (mapbuflen < charlen) {
+      // assumes charlen is always less than MAXMAPCHARLEN
+      mapbuf[mapbuflen++] = *here;
+      ++here;
+      --len;
+    }
+
+    if (mapbuflen == charlen) {
+      // got a complete character
+      if (charlen == 1) {
+	if (mapbuf[0] < 0x80) {
+	  // ascii character
+	  output.push_back (mapbuf[0]);
+	} else {
+	  output.push_back (converter.convert((unsigned short)mapbuf[0]));
+	}
+
+      } else {
+	// two byte character
+	output.push_back (converter.convert(((unsigned short)mapbuf[0] << 8) |
+					    (unsigned short)mapbuf[1]));
+      }
+
+      mapbuflen = 0;
+      charlen = 0;
+    }
+  }
+
+  start = (char *)here; // save current position
+
+  if (mapbuflen == 0) status = finished;
+  else status = stopped;
+}
+
+
+
+mapoutconvertclass::mapoutconvertclass () {
+  m_multibyte = 0;
+  mapbuflen=0; 
+  mapbufhere=0;
+}
+
+void mapoutconvertclass::reset () {
+  input = NULL;
+  outs = NULL;
+  mapbuflen = 0;
+  mapbufhere = 0;
+}
+
+// note that convert does not null-terminate the
+// output array of characters
+void mapoutconvertclass::convert (char *output, size_t maxlen, 
+				 size_t &len, status_t &status) {
+  unsigned short outc;
+
+  if (input == NULL || output == NULL) {
+    if (mapbuflen == 0) status = finished;
+    else status = unfinished;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *uoutput = (unsigned char *)output;
+  text_t::iterator textend = input->end();
+  len = 0;
+  while (len < maxlen) {
+    // empty the contents of the internal buffer
+    if (mapbuflen > 0) {
+      while (len < maxlen && mapbufhere < mapbuflen) {
+	*uoutput = mapbuf[mapbufhere];
+	++uoutput;
+	++len;
+	++mapbufhere;
+      }
+
+      if (mapbufhere == mapbuflen) {
+	mapbufhere = 0;
+	mapbuflen = 0;
+      }
+    }
+
+    // fill up the buffer with the next character
+    if (mapbuflen == 0) {
+      if (texthere == textend) break; // finished!
+      if (!rzws || (*texthere != 0x200b)) {
+	if (*texthere < 0x80) {
+	  mapbuf[0] = (unsigned char)*texthere;
+	  mapbuflen = 1;
+	} else {
+	  outc = converter.convert (*texthere);
+	  if (m_multibyte) {
+	    mapbuf[0] = (unsigned char)(outc >> 8);
+	    mapbuf[1] = (unsigned char)(outc & 0xff);
+	    mapbuflen = 2;
+	  } else {
+	    mapbuf[0] = outc;
+	    mapbuflen = 1;
+	  }
+	}
+      }
+
+      ++texthere;
+      mapbufhere = 0;
+    }
+  }
+  
+  if (texthere == textend && mapbuflen == 0) status = finished;
+  else status = unfinished;
+}
+
+
+bool simplemapconvert::loadmapfile (bool in) {
+  if (loaded) return true;
+  if (mapfile.empty()) return false;
+
+  char *cfilename = mapfile.getcstr();
+#ifdef GSDL_USE_IOS_H
+  ifstream mapfilein (cfilename, ios::in | ios::nocreate);
+#else
+  ifstream mapfilein (cfilename, ios::in);
+#endif
+  delete []cfilename;
+  if (!mapfilein) return false;
+
+  char cline[2048];
+  text_t line;
+
+  while (!mapfilein.eof()) {
+    mapfilein.getline (cline, 2048);
+    line.clear();
+    line.appendcstr (cline);
+    if (line.empty()) continue;
+    // remove comments
+    text_t::iterator end = line.end();
+    text_t::iterator here = findchar (line.begin(), end, '#');
+    if (here != end) {
+      line.erase (here, end);
+      if (line.empty()) continue;
+    }
+    
+    text_tarray parts;
+    splitchar (line.begin(), line.end(), '\t', parts);
+    
+    // do some simple sanity checks
+    if (parts.size() < 2) continue;
+    text_t::iterator begin1 = parts[0].begin();
+    text_t::iterator begin2 = parts[1].begin();
+    if (*begin1 != '0' || *(begin1+1) != 'x') continue;
+    if (*begin2 != '0' || *(begin2+1) != 'x') continue;
+    char *from = parts[0].getcstr();
+    char *to = parts[1].getcstr();
+    unsigned int f = 0, t = 0;
+    sscanf (from, "%i", &f);
+    sscanf (to, "%i", &t);
+    delete []from;
+    delete []to;
+    
+    if (in) mapping[(unsigned short)f] = (unsigned short)t;
+    else mapping[(unsigned short)t] = (unsigned short)f;
+  }
+
+  loaded = true;
+  return true;
+}
+
+unsigned short simplemapconvert::convert (unsigned short c, bool in) {
+
+  if (!loaded) 
+    if (!loadmapfile(in)) return absentc;
+  
+  return mapping[c];
+}
+
+
+void simplemapinconvertclass::convert (text_t &output, status_t &status) {
+  output.clear();
+  
+  if (start == NULL || len == 0) {
+    status = finished;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *here = (unsigned char *)start;
+  while (len > 0) {
+
+    if (*here < 0x80)
+      output.push_back (*here); // append this character
+    else 
+      output.push_back (converter.convert(*here, true));
+
+    ++here;
+    --len;
+  }
+
+  start = (char *)here; // save current position
+  status = finished;
+}
+
+
+void simplemapoutconvertclass::convert (char *output, size_t maxlen, 
+					size_t &len, status_t &status) {
+
+  if (input == NULL || output == NULL) {
+    status = finished;
+    return;
+  }
+
+  // don't want any funny sign conversions happening
+  unsigned char *uoutput = (unsigned char *)output;
+  text_t::iterator textend = input->end();
+  len = 0;
+  while ((len < maxlen) && (texthere != textend)) {
+
+    if (*texthere < 0x80) *uoutput = (unsigned char)(*texthere);
+    else *uoutput = converter.convert (*texthere, false);
+
+    ++uoutput;
+    ++len;
+    ++texthere;
+  }
+  
+  if (texthere == textend) status = finished;
+  else status = unfinished;
+}
Index: /gsdl/trunk/gs2common/src/lib/gsdlunicode.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/gsdlunicode.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/gsdlunicode.h	(revision 16561)
@@ -0,0 +1,277 @@
+/**********************************************************************
+ *
+ * gsdlunicode.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.
+ *
+ *********************************************************************/
+
+
+#ifndef GSDLUNICODE_H
+#define GSDLUNICODE_H
+
+#include "text_t.h"
+
+
+// converts a unicode encode text_t string to a utf-8
+// encoded text_t string
+text_t to_utf8 (text_t::const_iterator here, text_t::const_iterator end);
+inline text_t to_utf8 (const text_t &in) {return to_utf8 (in.begin(), in.end());}
+
+// converts a utf-8 encoded text_t string to a unicode
+// encoded text_t string
+text_t to_uni (const text_t &in);
+
+#define MAXUTF8CHARLEN 3
+
+// convert from a utf-8 char stream to the text_t class
+class utf8inconvertclass : public inconvertclass {
+public:
+  utf8inconvertclass();
+  void reset ();
+  void convert (text_t &output, status_t &status);
+
+protected:
+  // buffer to hold unconverted characters in a stream
+  unsigned char utf8buf[MAXUTF8CHARLEN];
+  size_t utf8buflen;
+
+  // returns the length that the current contents of the 
+  // utf8buf should be
+  size_t getutf8charlen ();
+};
+
+
+// This class provides the option of removing zero width
+// spaces (U+200B) during the output. By default this
+// option is turned off. The functionality is actually
+// implemented by the sub-classes, this class just provides
+// the framework for these classes. 
+//
+// Note: by convention reset() should not reset the rzws flag.
+class rzwsoutconvertclass : public outconvertclass {
+public:
+  rzwsoutconvertclass () {rzws = 0;};
+  void set_rzws (int new_rzws) {rzws = new_rzws;};
+
+protected:
+  int rzws;
+};
+
+// utf16 is almost the same as unicode, except for unicode values > 65535.
+class utf16outconvertclass : public rzwsoutconvertclass {
+public:
+  utf16outconvertclass () {};
+  void convert (char *out, size_t maxlen, size_t &len, status_t &status);
+};
+
+
+// Convert from a text_t class to a utf-8 char stream
+class utf8outconvertclass : public rzwsoutconvertclass {
+public:
+  utf8outconvertclass () {utf8buflen=0; utf8bufhere=0;};
+  void reset ();
+  // note that convert does not null-terminate the
+  // output array of characters
+  void convert (char *output, size_t maxlen, 
+		size_t &len, status_t &status);
+
+protected:
+  unsigned char utf8buf[MAXUTF8CHARLEN];
+  size_t utf8buflen;
+  size_t utf8bufhere;
+};
+
+
+// mapdata_t is used by mapconvert to hold the map file data
+class mapdata_t {
+public:
+  mapdata_t();
+  bool loaded;
+  unsigned short *ptrs[256];
+};
+
+// mapconvert is used in situations where conversion is best
+// done using a map file. The mapfile should reside in 
+// gsdlhome/unicode.
+class mapconvert {
+public:
+  mapconvert ();
+  ~mapconvert () {unloadmapfile();};
+
+  // setmapfile will cause loadmapfile to be called when conversion is
+  // needed
+  bool setmapfile (const text_t &themapfile, unsigned short theabsentc);
+
+  // loadmapfile should be called before any conversion is done
+  bool loadmapfile (const text_t &themapfile, unsigned short theabsentc);
+  void unloadmapfile ();
+
+  unsigned short convert (unsigned short c);
+
+  // note that this version of convert has different semantics to
+  // the convertclass version.
+  text_t convert (const text_t &instr);
+
+protected:
+  text_t mapfile;
+  unsigned short absentc;
+  mapdata_t mapdata;
+};
+
+
+
+#define MAXMAPCHARLEN 2
+
+// convert from a gb char stream to the unicode text_t class
+class mapinconvertclass : public inconvertclass {
+public:
+  mapinconvertclass();
+  virtual ~mapinconvertclass() {};
+
+  // setmapfile will cause loadmapfile to be called when conversion is needed
+  bool setmapfile (const text_t &themapfile, unsigned short theabsentc) {
+    return converter.setmapfile (themapfile, theabsentc);
+  };
+
+  // loadmapfile should be called before any conversion takes
+  // place
+  bool loadmapfile (const text_t &themapfile, unsigned short theabsentc) {
+    return converter.loadmapfile (themapfile, theabsentc);
+  };
+
+  void set_multibyte (int new_multibyte) {m_multibyte = new_multibyte;};
+
+  void reset ();
+  void convert (text_t &output, status_t &status);
+
+protected:
+  // buffer to hold unconverted characters in a stream
+  unsigned char mapbuf[MAXMAPCHARLEN];
+  size_t mapbuflen;
+  int m_multibyte;
+
+  // note: multiple instances of mapinconvert class are expensive
+  // as each will have its own copy of the map file data. This
+  // could be reduced by making map2unimap static, but then it
+  // wouldn't be thread safe.
+  mapconvert converter;
+
+  // returns the length that the current contents of the 
+  // mapbuf should be
+  inline size_t getmapcharlen () {
+    if (mapbuflen == 0) return 0;
+    if (mapbuf[0] < 0x80) return 1;
+    if (!m_multibyte) return 1;
+    return 2;
+  }
+};
+
+
+// Convert from a text_t class to a map char stream
+class mapoutconvertclass : public rzwsoutconvertclass {
+public:
+  mapoutconvertclass ();
+  virtual ~mapoutconvertclass() {};
+
+  // setmapfile will cause loadmapfile to be called when conversion is needed
+  bool setmapfile (const text_t &themapfile, unsigned short theabsentc) {
+    return converter.setmapfile (themapfile, theabsentc);
+  };
+
+  // loadmapfile should be called before any conversion takes
+  // place
+  bool loadmapfile (const text_t &themapfile, unsigned short theabsentc) {
+    return converter.loadmapfile (themapfile, theabsentc);
+  };
+
+  void set_multibyte (int new_multibyte) {m_multibyte = new_multibyte;};
+
+  void reset ();
+  void convert (char *output, size_t maxlen, 
+		size_t &len, status_t &status);
+
+protected:
+  unsigned char mapbuf[MAXMAPCHARLEN];
+  size_t mapbuflen;
+  size_t mapbufhere;
+  int m_multibyte;
+
+  mapconvert converter;
+};
+
+
+// Simple input and output converter classes for use with 8 bit encodings
+// using simple textual map files. Map files should contain (at least) two
+// tab-separated fields. The first field is the mapped value and the second
+// field is the unicode value.
+
+struct ltus_t 
+{
+  bool operator()(const unsigned short &t1, const unsigned short &t2) const
+  { return t1 < t2; }
+};
+
+
+class simplemapconvert {
+public:
+  simplemapconvert () {absentc=0; loaded=false;}
+  unsigned short convert (unsigned short c, bool in);
+  void setmapfile (const text_t &themapfile) {mapfile = themapfile;}
+
+protected:
+  bool loadmapfile (bool in);
+
+  map <unsigned short, unsigned short, ltus_t> mapping;
+  bool loaded;
+  text_t mapfile;
+  unsigned short absentc;
+};
+
+
+class simplemapinconvertclass : public inconvertclass {
+public:
+  virtual ~simplemapinconvertclass () {}
+
+  void convert (text_t &output, status_t &status);
+
+  void setmapfile (const text_t &themapfile) {converter.setmapfile(themapfile);}
+  
+protected:
+  simplemapconvert converter;
+};
+
+class simplemapoutconvertclass : public rzwsoutconvertclass {
+public:
+  virtual ~simplemapoutconvertclass () {}
+
+  void convert (char *output, size_t maxlen, 
+		size_t &len, status_t &status);
+
+  void setmapfile (const text_t &themapfile) {converter.setmapfile(themapfile);}
+  
+protected:
+  simplemapconvert converter;
+};
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/infodbclass.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/infodbclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/infodbclass.cpp	(revision 16561)
@@ -0,0 +1,147 @@
+/**********************************************************************
+ *
+ * infodbclass.cpp -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#include "infodbclass.h"
+
+
+// constructors
+infodbclass::infodbclass () {
+}
+
+
+void infodbclass::setinfo (const text_t &key, const text_t &value)
+{
+  text_tarray &tarr = info[key];
+  tarr.erase(tarr.begin(), tarr.end());
+  tarr.push_back(value);
+}
+
+
+void infodbclass::setintinfo (const text_t &key, int value)
+{
+  setinfo (key, value);
+}
+
+
+void infodbclass::setcinfo (const text_t &key, unsigned short c)
+{
+  text_t t;
+  t.push_back (c);
+  setinfo (key, t);
+}
+
+
+text_t *infodbclass::getinfo (const text_t &key)
+{
+  iterator here = info.find (key);
+  if (here == info.end()) return NULL;
+
+  if ((*here).second.empty()) return NULL;
+  
+  return &((*here).second[0]);
+}
+
+
+text_t *infodbclass::getinfo (const text_t &key, const text_t& slang)
+{
+  text_tarray *t = getmultinfo(key, slang);
+  if (t == NULL || t->empty()) return NULL;
+  return &((*t)[0]);
+}
+
+
+int infodbclass::getintinfo (const text_t &key)
+{
+  text_t *t = getinfo (key);
+  if (t == NULL) return 0;
+  return t->getint();
+}
+
+
+int infodbclass::getintinfo (const text_t &key, const text_t& slang)
+{
+  text_tarray *t = getmultinfo(key, slang);
+  if (t == NULL || t->empty()) return 0;
+  return (*t)[0].getint();
+}
+
+
+text_t &infodbclass::operator[] (const text_t &key)
+{
+  text_tarray &tarr = info[key];
+  if (tarr.empty()) {
+    text_t e;
+    tarr.push_back(e);
+  }
+  return tarr[0];
+}
+
+
+void infodbclass::addinfo (const text_t &key, const text_t &value)
+{
+  text_tarray &tarr = info[key];
+  tarr.push_back (value);
+}
+
+
+void infodbclass::addintinfo (const text_t &key, int value)
+{
+  addinfo (key, value);
+}
+
+
+void infodbclass::addcinfo (const text_t &key, unsigned short c)
+{
+  text_t t;
+  t.push_back(c);
+  addinfo (key, t);
+}
+
+
+text_tarray *infodbclass::getmultinfo (const text_t &key)
+{
+  iterator here = info.find (key);
+  if (here == info.end()) return NULL;
+
+  return &((*here).second);
+}
+
+
+text_tarray *infodbclass::getmultinfo (const text_t &key, const text_t& slang)
+{
+  iterator here = info.end();
+  if (slang.empty()) {
+    here = info.find (key);
+  }
+  else {
+    here = info.find (key+":"+slang);
+    if (here == info.end()) {
+      here = info.find (key);
+    }
+  }
+  if (here == info.end()) return NULL;
+
+  return &((*here).second);
+}
Index: /gsdl/trunk/gs2common/src/lib/infodbclass.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/infodbclass.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/infodbclass.h	(revision 16561)
@@ -0,0 +1,110 @@
+/**********************************************************************
+ *
+ * infodbclass.h -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#ifndef INFODBCLASS_H
+#define INFODBCLASS_H
+
+
+#include "text_t.h"
+
+
+typedef map<text_t, text_tarray, lttext_t> text_tarraymap;
+
+
+// infodbclass is used to store information about a object
+class infodbclass {
+protected:
+  text_tarraymap info;
+  text_t lang;
+
+public:
+  // type support for text_tarraymap
+  typedef text_tarraymap::iterator iterator;
+  typedef text_tarraymap::const_iterator const_iterator;
+  typedef text_tarraymap::reference reference;
+  typedef text_tarraymap::const_reference const_reference;
+  typedef text_tarraymap::size_type size_type;
+  typedef text_tarraymap::difference_type difference_type;
+  typedef text_tarraymap::const_reverse_iterator const_reverse_iterator;
+  typedef text_tarraymap::reverse_iterator reverse_iterator;
+  
+  // constructors
+  infodbclass ();
+
+  // basic container support
+  iterator begin () {return info.begin();}
+  const_iterator begin () const {return info.begin();}
+  iterator end () {return info.end();}
+  const_iterator end () const {return info.end();}
+
+  void erase(iterator pos) {info.erase(pos);}
+  void erase(iterator first, iterator last) {info.erase(first, last);}
+  infodbclass &operator=(const infodbclass &x) {info=x.info;return *this;}
+
+  bool empty () const {return info.empty();}
+  size_type size() const {return info.size();}
+
+
+  // added functionality
+  void clear () {info.erase(info.begin(),info.end());}
+
+  // set default language
+  void set_language(const text_t& str_lang) { lang = str_lang; }
+  const text_t &get_language() const { return lang; }
+
+  // the following functions deal with keys that can only
+  // have one value for compatibility
+  
+  // getinfo returns NULL if there isn't an entry with
+  // 'key' already defined, getintinfo returns 0 if there wasn't an
+  // entry with 'key' defined and operator[] returns "" if
+  // 'key' wasn't already defined (and sets 'key'="").
+  void setinfo (const text_t &key, const text_t &value);
+  void setintinfo (const text_t &key, int value);
+  void setcinfo (const text_t &key, unsigned short c);
+  text_t *getinfo (const text_t &key);
+  // get info overriding language set on this object
+  text_t *getinfo (const text_t &key, const text_t& lang);
+  int getintinfo (const text_t &key);
+  // get info overriding language set on this object
+  int getintinfo (const text_t &key, const text_t& lang);
+  text_t &operator[] (const text_t &key);
+
+
+  // the next set of functions allow you to set and access keys
+  // that can have more than one value
+
+  // getmultinfo returns NULL if there isn't an entry with
+  // 'key' already defined
+  void addinfo (const text_t &key, const text_t &value);
+  void addintinfo (const text_t &key, int value);
+  void addcinfo (const text_t &key, unsigned short c);
+  text_tarray *getmultinfo (const text_t &key);
+  // get info overriding language set on this object
+  text_tarray *getmultinfo (const text_t &key, const text_t& lang);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/md5.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/md5.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/md5.cpp	(revision 16561)
@@ -0,0 +1,283 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.	This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* Brutally hacked by John Walker back from ANSI C to K&R (no
+   prototypes) to maintain the tradition that Netfone will compile
+   with Sun's original "cc". */
+
+/* Hacked about for use with Greenstone by Stefan Boddie, DL Consulting Ltd
+   (http://www.dlconsulting.co.nz)
+*/
+
+#include <fstream>
+#include <stdio.h>
+#include <memory.h>		 /* for memcpy() */
+#include "md5.h"
+
+#ifdef _LITTLE_ENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned int longs) {
+  uint32 t;
+  do {
+    t = (uint32) ((unsigned int) buf[3] << 8 | buf[2]) << 16 |
+      ((unsigned int) buf[1] << 8 | buf[0]);
+    *(uint32 *) buf = t;
+    buf += 4;
+  } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5Context *ctx) {
+  ctx->buf[0] = 0x67452301;
+  ctx->buf[1] = 0xefcdab89;
+  ctx->buf[2] = 0x98badcfe;
+  ctx->buf[3] = 0x10325476;
+  
+  ctx->bits[0] = 0;
+  ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5Context *ctx, unsigned char *buf, unsigned int len) {
+  uint32 t;
+  
+  /* Update bitcount */
+  
+  t = ctx->bits[0];
+  if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+    ctx->bits[1]++; 	/* Carry from low to high */
+  ctx->bits[1] += len >> 29;
+  
+  t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+  
+  /* Handle any leading odd-sized chunks */
+  
+    if (t) {
+      unsigned char *p = (unsigned char *) ctx->in + t;
+      
+      t = 64 - t;
+      if (len < t) {
+	memcpy(p, buf, len);
+	return;
+      }
+      memcpy(p, buf, t);
+      byteReverse(ctx->in, 16);
+      MD5Transform(ctx->buf, (uint32 *) ctx->in);
+      buf += t;
+      len -= t;
+    }
+    /* Process data in 64-byte chunks */
+    
+    while (len >= 64) {
+      memcpy(ctx->in, buf, 64);
+      byteReverse(ctx->in, 16);
+      MD5Transform(ctx->buf, (uint32 *) ctx->in);
+      buf += 64;
+      len -= 64;
+    }
+    
+    /* Handle any remaining bytes of data. */
+    
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], MD5Context *ctx) {
+  unsigned int count;
+  unsigned char *p;
+  
+  /* Compute number of bytes mod 64 */
+  count = (ctx->bits[0] >> 3) & 0x3F;
+  
+  /* Set the first char of padding to 0x80.  This is safe since there is
+     always at least one byte free */
+  p = ctx->in + count;
+  *p++ = 0x80;
+  
+  /* Bytes of padding needed to make 64 bytes */
+  count = 64 - 1 - count;
+  
+  /* Pad out to 56 mod 64 */
+  if (count < 8) {
+    /* Two lots of padding:  Pad the first block to 64 bytes */
+    memset(p, 0, count);
+    byteReverse(ctx->in, 16);
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    
+    /* Now fill the next block with 56 bytes */
+    memset(ctx->in, 0, 56);
+  } else {
+    /* Pad block to 56 bytes */
+    memset(p, 0, count - 8);
+  }
+  byteReverse(ctx->in, 14);
+  
+  /* Append length in bits and transform */
+  ((uint32 *) ctx->in)[14] = ctx->bits[0];
+  ((uint32 *) ctx->in)[15] = ctx->bits[1];
+  
+  MD5Transform(ctx->buf, (uint32 *) ctx->in);
+  byteReverse((unsigned char *) ctx->buf, 4);
+  memcpy(digest, ctx->buf, 16);
+  memset(ctx, 0, sizeof(ctx));        /* In case it's sensitive */
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], uint32 in[16]) {
+  register uint32 a, b, c, d;
+  
+  a = buf[0];
+  b = buf[1];
+  c = buf[2];
+  d = buf[3];
+  
+  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+  
+  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+  
+  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+  
+  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+    
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+text_t md5data(const text_t &data) {
+
+  unsigned char buffer[16384];
+  int buflen = sizeof(buffer);
+  int i = 0;
+  MD5Context md5c;
+
+  MD5Init(&md5c);
+
+  text_t::const_iterator here = data.begin();
+  text_t::const_iterator end = data.end();
+  while (here != end) {
+    i = 0;
+    while ((i < buflen) && (here != end)) {
+      buffer[i] = (unsigned char)(*here);
+      here++; i++;
+    }
+    MD5Update(&md5c, buffer, i);
+  }
+
+  unsigned char signature[16];
+  text_t rv;
+  MD5Final(signature, &md5c);
+  char *b = new char[8];
+  for (int j = 0; j < sizeof signature; j++) {
+    sprintf(b, "%02x", signature[j]);
+    rv += b;
+  }
+  delete[] b;
+  return rv;
+}
Index: /gsdl/trunk/gs2common/src/lib/md5.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/md5.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/md5.h	(revision 16561)
@@ -0,0 +1,25 @@
+#ifndef MD5_H
+#define MD5_H
+
+#include "text_t.h"
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+  uint32 buf[4];
+  uint32 bits[2];
+  unsigned char in[64];
+};
+
+void MD5Init(MD5Context *ctx);
+void MD5Update(MD5Context *ctx, unsigned char *buf, unsigned int len);
+void MD5Final(unsigned char digest[16], MD5Context *ctx);
+void MD5Transform(uint32 buf[4], uint32 in[16]);
+
+text_t md5data(const text_t &data);
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/phrases.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/phrases.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/phrases.cpp	(revision 16561)
@@ -0,0 +1,66 @@
+/**********************************************************************
+ *
+ * phrases.cpp -- routines to do with phrases - want to share between colservr
+ * and recpt
+ * 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.
+ *
+ *
+ *********************************************************************/
+
+#include "phrases.h"
+#include "unitool.h" // for is_unicode_letdig
+
+
+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;
+      bool foundbreak = false;
+     while (here != end) {
+	if (*here == '"') {
+	  if (foundquote) {
+	    if (!tmptext.empty() && foundbreak) {
+	      phrases.push_back(tmptext);
+	    }
+	    tmptext.clear();
+	      
+	    foundquote = false;
+	    foundbreak = false;
+	  } else foundquote = true;
+	} else {
+	  if (foundquote) {
+	    if (!is_unicode_letdig(*here)) {
+	      foundbreak = true;
+	    }
+	    tmptext.push_back (*here);
+	  }
+	}
+	++here;
+     }
+    }
+  }
+}
Index: /gsdl/trunk/gs2common/src/lib/phrases.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/phrases.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/phrases.h	(revision 16561)
@@ -0,0 +1,36 @@
+/**********************************************************************
+ *
+ * phrases.h -- routines to do with phrases - want to share between colservr
+ * and recpt
+ * 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.
+ *
+ *
+ *********************************************************************/
+
+#ifndef PHRASES_H
+#define PHRASES_H
+
+#include "text_t.h"
+
+void get_phrases (const text_t &querystring, text_tarray &phrases); 
+
+#endif /* PHRASES_H */
+
Index: /gsdl/trunk/gs2common/src/lib/sqldbclass.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/sqldbclass.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/sqldbclass.h	(revision 16561)
@@ -0,0 +1,56 @@
+/**********************************************************************
+ *
+ * sqldbclass.h -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef SQLDBCLASS_H
+#define SQLDBCLASS_H
+
+
+#include "dbclass.h"
+#include "text_t.h"
+
+
+class sqldbclass : public dbclass
+{
+public:
+  sqldbclass() {}
+  virtual ~sqldbclass() {}
+
+
+  // -----------------------------------------------------------------------------------------------
+  // These functions MUST be implemented by subclasses, as they are database-specific
+
+  // returns array of document OIDs
+  virtual text_tarray get_documents_with_metadata_value (const text_tarray &metadata_element_names,
+							 const text_t &metadata_value,
+							 const text_t &sort_by_metadata_element_name) = 0;
+
+  // returns array of values
+  virtual text_tarray get_metadata_values (const text_tarray &metadata_element_names,
+					   const text_t &metadata_value_filter,
+					   const text_t &metadata_value_grouping_expression) = 0;
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/sqlitedbclass.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/sqlitedbclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/sqlitedbclass.cpp	(revision 16561)
@@ -0,0 +1,388 @@
+/**********************************************************************
+ *
+ * sqlitedbclass.cpp -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "sqlitedbclass.h"
+#include "gsdlunicode.h"
+#include "unitool.h"
+
+#ifdef __WIN32__
+// for Sleep
+#  include <windows.h>
+#else
+// for usleep
+#  include <unistd.h>
+#endif
+
+
+#define SQLITE_MAX_RETRIES 8
+
+
+sqlitedbclass::~sqlitedbclass()
+{
+  closedatabase();
+}
+
+
+// returns true if opened
+bool sqlitedbclass::opendatabase (const text_t &filename, int mode, int num_retrys, 
+#ifdef __WIN32__
+				  bool need_filelock
+#else
+				  bool
+#endif
+			      )
+{
+  // Check if we've already got the database open
+  if (sqlitefile != NULL)
+  {
+    if (openfile == filename) return true;
+    else closedatabase();
+  }
+
+  char *filename_cstr = filename.getcstr();
+  sqlite3_open(filename_cstr, &sqlitefile);
+  delete[] filename_cstr;
+
+  if (sqlitefile == NULL)
+  {
+    (*logout) << "ERROR: sqlitedbclass::opendatabase() failed on: " << filename << "\n";
+    return false;
+  }
+
+  if ((mode == DB_WRITER || mode == DB_WRITER_CREATE) && !sqltableexists("data"))
+  {
+    sqlexec("CREATE TABLE data (key TEXT, value TEXT, PRIMARY KEY(key))");
+  }
+
+  return true;
+}
+
+
+void sqlitedbclass::closedatabase ()
+{
+  if (sqlitefile == NULL) return;
+
+  sqlite3_close(sqlitefile);
+  sqlitefile = NULL;
+  openfile.clear();
+}
+
+
+void sqlitedbclass::deletekey (const text_t &key)
+{
+  text_t sql_cmd = "DELETE FROM data WHERE key='" + sqlite_safe(key) + "'";
+  sqlexec(sql_cmd);
+}
+
+
+// returns array of document OIDs
+text_tarray sqlitedbclass::get_documents_with_metadata_value (const text_tarray &metadata_element_names,
+							      const text_t &metadata_value,
+							      const text_t &sort_by_metadata_element_name)
+{
+  text_tarray document_OIDs;
+
+  // Check at least one metadata element and a metadata value has been specified
+  if (metadata_element_names.empty() || metadata_value == "")
+  {
+    return document_OIDs;
+  }
+
+  // Get the entries in the "document_metadata" table where the element and value matches those specified
+  text_t sql_cmd = "SELECT DISTINCT docOID FROM document_metadata WHERE element IN ('" + sqlite_safe(metadata_element_names[0]) + "'";
+  for (int i = 1; i < metadata_element_names.size(); i++)
+  {
+    sql_cmd += ",'" + sqlite_safe(metadata_element_names[i]) + "'";
+  }
+  sql_cmd += ") AND value='" + sqlite_safe(metadata_value) + "'";
+
+  // If we're sorting the documents by a certain metadata element, extend the SQL command to do this
+  if (sort_by_metadata_element_name != "")
+  {
+    sql_cmd = "SELECT docOID FROM (" + sql_cmd + ") LEFT JOIN (SELECT docOID,value from document_metadata WHERE element='" + sqlite_safe(sort_by_metadata_element_name) + "') USING (docOID) ORDER by value";
+  }
+
+  // Perform the SQL request
+  vector<text_tmap> sql_results;
+  if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
+  {
+    return document_OIDs;
+  }
+
+  // Iterate through the documents and add them to the array to be returned
+  vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
+  while (sql_results_iterator != sql_results.end())
+  {
+    text_tmap sql_result = (*sql_results_iterator);
+    document_OIDs.push_back(sql_result["docOID"]);
+    sql_results_iterator++;
+  }
+
+  return document_OIDs;
+}
+
+
+// returns file extension string
+text_t sqlitedbclass::getfileextension ()
+{
+  return ".db";
+}
+
+
+// returns true on success
+bool sqlitedbclass::getkeydata (const text_t& key, text_t &data)
+{
+  text_t sql_cmd = "SELECT value FROM data WHERE key='" + sqlite_safe(key) + "'";
+  vector<text_tmap> sql_results;
+  if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
+  {
+    return false;
+  }
+
+  text_tmap sql_result = sql_results[0];
+  data = sql_result["value"];
+  return true;
+}
+
+
+// returns array of keys
+text_tarray sqlitedbclass::getkeys ()
+{
+  text_tarray keys;
+
+  // Get all the entries in the "key" column of the table
+  text_t sql_cmd = "SELECT key FROM data";
+  vector<text_tmap> sql_results;
+  if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
+  {
+    return keys;
+  }
+
+  // Iterate through the keys and add them to the array to be returned
+  vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
+  while (sql_results_iterator != sql_results.end())
+  {
+    text_tmap sql_result = (*sql_results_iterator);
+    keys.push_back(sql_result["key"]);
+    sql_results_iterator++;
+  }
+
+  return keys;
+}
+
+
+// returns array of values
+text_tarray sqlitedbclass::get_metadata_values (const text_tarray &metadata_element_names,
+						const text_t &metadata_value_filter,
+						const text_t &metadata_value_grouping_expression)
+{
+  text_tarray metadata_values;
+
+  // Check at least one metadata element has been specified
+  if (metadata_element_names.empty())
+  {
+    return metadata_values;
+  }
+
+  // Get the raw "value" field unless a grouping expression was provided (in this case an edited value is returned)
+  text_t value_select_expression = "value";
+  if (metadata_value_grouping_expression != "")
+  {
+    value_select_expression = metadata_value_grouping_expression;
+  }
+
+  // Get the entries in the "document_metadata" table where the element matches that specified
+  text_t sql_cmd = "SELECT DISTINCT docOID," + value_select_expression + " FROM document_metadata WHERE element IN ('" + sqlite_safe(metadata_element_names[0]) + "'";
+  for (int i = 1; i < metadata_element_names.size(); i++)
+  {
+    sql_cmd += ",'" + sqlite_safe(metadata_element_names[i]) + "'";
+  }
+  sql_cmd += ")";
+
+  // Add value filter, if one has been defined
+  if (metadata_value_filter != "")
+  {
+    sql_cmd += " AND value GLOB '" + sqlite_safe(metadata_value_filter) + "'";
+  }
+
+  // Perform the SQL request
+  vector<text_tmap> sql_results;
+  if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
+  {
+    return metadata_values;
+  }
+
+  // Iterate through the values and add them to the array to be returned
+  vector<text_tmap>::iterator sql_results_iterator = sql_results.begin();
+  while (sql_results_iterator != sql_results.end())
+  {
+    text_tmap sql_result = (*sql_results_iterator);
+    metadata_values.push_back(sql_result[value_select_expression]);
+    sql_results_iterator++;
+  }
+
+  return metadata_values;
+}
+
+
+// returns true on success
+bool sqlitedbclass::setkeydata (const text_t &key, const text_t &data)
+{
+  // We need to do either an INSERT or UPDATE depending on whether the key already exists
+  if (!exists(key))
+  {
+    text_t sql_cmd = "INSERT INTO data (key, value) VALUES ('" + sqlite_safe(key) + "', '" + sqlite_safe(data) + "')";
+    return sqlexec(sql_cmd);
+  }
+  else
+  {
+    text_t sql_cmd = "UPDATE data SET value='" + sqlite_safe(data) + "' WHERE key='" + sqlite_safe(key) + "'";
+    return sqlexec(sql_cmd);
+  }
+}
+
+
+// ----------------------------------------------------------------------------------------
+//   SQLITE-ONLY FUNCTIONS
+// ----------------------------------------------------------------------------------------
+
+// sleep for the given number of milliseconds
+void sleep(int m)
+{
+#ifdef __WIN32__
+  Sleep(m);
+#else
+  usleep(m);
+#endif
+}
+
+
+text_t sqlitedbclass::sqlite_safe (const text_t &value_arg)
+{
+  text_t value = value_arg;
+  value.replace("'", "''");
+  return value;
+}
+
+
+// sqlexec simply executes the given sql statement - it doesn't obtain a
+// result set - returns true if the sql statement was executed successfully
+bool sqlitedbclass::sqlexec(const text_t &sql_cmd)
+{
+  if (sqlitefile == NULL) return false;
+
+  char *sql_cmd_cstr = sql_cmd.getcstr();
+
+  int rv = 0;
+  int tries = 0;
+  while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, NULL, NULL, NULL)) == SQLITE_BUSY)
+  {
+    sleep(1000);
+    tries++;
+    if (tries > SQLITE_MAX_RETRIES)
+    {
+      outconvertclass text_t2ascii;
+      (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
+      break;
+    }
+  }
+
+  delete[] sql_cmd_cstr;
+
+  if (rv == SQLITE_OK) return true;
+
+  // sqlite3_exec failed - return false
+  outconvertclass text_t2ascii;
+  (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
+  return false;
+}
+
+
+// callback functions for sqlgetarray
+static int sqlgetarray_callback(void *res, int numcols, char **vals, char **columnnames)
+{
+  vector<text_tmap> *result = (vector<text_tmap>*) res;
+  text_tmap row;
+
+  for (int i = 0; i < numcols; i++)
+  {
+    row[columnnames[i]] = "";
+    // vals[i] will be NULL if set to a NULL db value
+    if (vals[i])
+    {
+      row[columnnames[i]] = to_uni(vals[i]);
+    }
+  }
+
+  result->push_back(row);
+  return 0;
+}
+
+
+// sqlgetarray executes sql and returns the result set in sql_results
+bool sqlitedbclass::sqlgetarray(const text_t &sql_cmd, vector<text_tmap> &sql_results)
+{
+  if (sqlitefile == NULL) return false;
+
+  char *sql_cmd_cstr = sql_cmd.getcstr();
+  sql_results.erase(sql_results.begin(), sql_results.end());
+
+  int rv = 0;
+  int tries = 0;
+  while ((rv = sqlite3_exec(sqlitefile, sql_cmd_cstr, sqlgetarray_callback, &sql_results, NULL)) == SQLITE_BUSY)
+  {
+    sleep(1000);
+    tries++;
+    if (tries > SQLITE_MAX_RETRIES)
+    {
+      outconvertclass text_t2ascii;
+      (*logout) << text_t2ascii << "max_retries exceeded for sql query: " << sql_cmd << "\n";
+      break;
+    }
+  }
+
+  delete[] sql_cmd_cstr;
+
+  if (rv == SQLITE_OK) return true;
+
+  // sqlite3_exec failed - return empty result set
+  outconvertclass text_t2ascii;
+  (*logout) << text_t2ascii << "Error executing sql statement: " << sql_cmd << "\n";
+  return false;
+}
+
+
+// returns true if exists
+bool sqlitedbclass::sqltableexists(const text_t &table_name)
+{
+  text_t sql_cmd = "SELECT * FROM sqlite_master WHERE tbl_name='" + sqlite_safe(table_name) + "'";
+  vector<text_tmap> sql_results;
+  if (!sqlgetarray(sql_cmd, sql_results) || sql_results.size() == 0)
+  {
+    return false;
+  }
+
+  return true;
+}
Index: /gsdl/trunk/gs2common/src/lib/sqlitedbclass.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/sqlitedbclass.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/sqlitedbclass.h	(revision 16561)
@@ -0,0 +1,81 @@
+/**********************************************************************
+ *
+ * sqlitedbclass.h -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef SQLITEDBCLASS_H
+#define SQLITEDBCLASS_H
+
+
+#include "sqldbclass.h"
+#include "sqlite3.h"
+
+
+class sqlitedbclass : public sqldbclass
+{
+public:
+  sqlitedbclass() { sqlitefile = NULL; }
+  ~sqlitedbclass();
+
+  // returns true if opened
+  bool opendatabase (const text_t &filename, int mode, int num_retrys, bool need_filelock);
+
+  void closedatabase ();
+
+  void deletekey (const text_t &key);
+
+  // returns array of document OIDs
+  text_tarray get_documents_with_metadata_value (const text_tarray &metadata_element_names,
+						 const text_t &metadata_value,
+						 const text_t &sort_by_metadata_element_name);
+
+  // returns file extension string
+  text_t getfileextension ();
+
+  // returns true on success
+  bool getkeydata (const text_t& key, text_t &data);
+
+  // returns array of keys
+  text_tarray getkeys ();
+
+  // returns array of values
+  text_tarray get_metadata_values (const text_tarray &metadata_element_names,
+				   const text_t &metadata_value_filter,
+				   const text_t &metadata_value_grouping_expression);
+
+  // returns true on success
+  bool setkeydata (const text_t &key, const text_t &data);
+
+protected:
+  text_t openfile;
+  sqlite3* sqlitefile;
+
+  text_t sqlite_safe (const text_t &value_arg);
+
+  bool sqlexec (const text_t &sql_cmd);
+  bool sqlgetarray (const text_t &sql_cmd, vector<text_tmap> &sql_results);
+  bool sqltableexists (const text_t &table_name);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/text_t.cpp
===================================================================
--- /gsdl/trunk/gs2common/src/lib/text_t.cpp	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/text_t.cpp	(revision 16561)
@@ -0,0 +1,970 @@
+/**********************************************************************
+ *
+ * text_t.cpp -- a simple 16-bit character string class
+ * 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$
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\algorithm>
+#elif defined(GSDL_USE_STL_H)
+#  if defined(GSDL_USE_ALGO_H)
+#    include <algo.h>
+#  else
+#    include <algorithm.h>
+#  endif
+#else
+#  include <algorithm>
+#endif
+
+#ifdef HAVE_CONFIG_H
+# ifdef __WIN32__
+#  include "win32cfg.h"
+# else
+#  include "config.h"
+# endif
+#endif
+
+
+#include "unitool.h"
+
+const text_t g_EmptyText("");
+
+////////////////////////////////////
+// text_t methods
+////////////////////////////////////
+
+// new stream converter ...
+ostream& operator<< (ostream &o, const text_t &text)
+{
+  text_t::const_iterator ithere = text.begin();
+  text_t::const_iterator itend = text.end();
+
+  while (ithere != itend)
+    {
+      if (*ithere < 256)
+	{
+	  o << (unsigned char)(*ithere);
+	}
+      else 
+	{
+	// put a space or a question mark depending on what
+	// the character is. Question marks tell the user that
+	// they are missing some information.
+	if (is_unicode_space (*ithere)) 
+	  o << ' ';
+	else 
+	  o << '?';
+      }
+      ++ithere;
+    }
+
+  return o;
+}
+
+text_t::text_t () 
+{
+  setencoding(0);
+  clear ();
+}
+
+text_t::text_t (int i) 
+{
+  setencoding(0);
+  clear ();
+  appendint (i);
+}
+
+text_t::text_t (const char *s) 
+{ 
+  setencoding(0);
+  clear ();
+  appendcstr (s);
+}
+
+text_t::text_t (const char *s, size_type nLength)
+{
+  setencoding(0);
+  clear ();
+  appendcarr(s, nLength);
+}
+
+
+void text_t::append (const text_t &t) 
+{
+  text.insert(text.end(), t.begin(), t.end());
+}
+
+void text_t::appendrange (iterator first, iterator last) 
+{
+  text.insert(text.end(), first, last);
+}
+
+void text_t::appendrange (const_iterator first, const_iterator last) 
+{
+  text.insert(text.end(), first, last);
+}
+
+void text_t::appendint (int i)
+{
+  // deal with zeros and negatives
+  if (i == 0) 
+    {
+      text.push_back('0');
+      return;
+    }
+  else if (i < 0)
+    {
+      text.push_back('-');
+      i *= -1;
+    }
+
+  // get a buffer for the conversion
+  int maxbuflen = sizeof(int)*3;
+  char *buf = new char[maxbuflen];
+  int len = 0;
+  
+  // get the number in reverse
+  while (i > 0)
+    {
+      buf[len++] = '0'+ (i%10);
+      i = i/10;
+    }
+
+  // reverse the number
+  while (len > 0)
+    {
+      text.push_back(buf[--len]);
+    }
+
+  delete []buf;
+}
+
+int text_t::getint () const
+{
+  int i = 0;
+  int mult = 1; // become -1 for negative numbers
+
+  const_iterator here = text.begin();
+  const_iterator end = text.end();
+  
+  // do plus and minus signs
+  if (here != end)
+    {
+      if (*here == '-')
+	{
+	  mult = -1;
+	  ++here;
+	}
+      else if (*here == '+')
+	{
+	  mult = 1;
+	  ++here;
+	}
+    }
+
+  // deal with the number
+  while ((here != end) && (*here >= '0') && (*here <= '9'))
+    {
+      i = 10*i + (*here - '0');
+      ++here;
+    }
+
+  i *= mult;
+  return i;
+}
+
+unsigned long text_t::getulong () const
+{
+  unsigned long i = 0;
+
+  const_iterator here = text.begin();
+  const_iterator end = text.end();
+
+  while ((here != end) && (*here >= '0') && (*here <= '9'))
+    {
+      i = 10*i + (*here - '0');
+      ++here;
+    }
+
+  return i;
+}
+
+void text_t::appendcarr (const char *s, size_type len)
+{
+  unsigned char *us = (unsigned char *)s;
+  if (text.capacity() < (text.size() + len + 2)) {
+    text.reserve(text.size() + len + 2);
+  }
+
+  while (len > 0) 
+    {
+      text.push_back (*us); // append this character
+      ++us;
+      --len;
+    }
+}
+
+void text_t::appendcstr (const char *s) 
+{
+  size_t len = strlen(s);
+  if (text.capacity() < (text.size() + len + 2)) {
+    text.reserve(text.size() + len + 2);
+  }
+  
+  unsigned char *us = (unsigned char *)s;
+  while (*us != '\0') 
+    {
+      text.push_back (*us); // append this character
+      ++us;
+    }
+}
+
+
+// strings returned from getcarr and getcstr become the callers 
+// responsibility and should be deallocated with "delete []"
+
+char *text_t::getcarr(size_type &len) const
+{
+  unsigned char *cstr = new unsigned char[size()];
+  len = 0;
+
+  const_iterator ithere = begin();
+  const_iterator itend = end();
+  while (ithere != itend)
+    {
+      if (*ithere < 256) cstr[len] = (unsigned char)(*ithere);
+      else {
+	// put a space or a question mark depending on what
+	// the character is. Question marks tell the user that
+	// they are missing some information.
+	if (is_unicode_space (*ithere)) cstr[len] = ' ';
+	else cstr[len] = '?';
+      }
+      ++len;
+      ++ithere;
+    }
+
+  return (char *)cstr;
+}
+
+char *text_t::getcstr() const
+{
+  unsigned char *cstr = new unsigned char[size() + 1];
+  const_iterator ithere = begin();
+  const_iterator itend = end();
+  int len = 0;
+
+  while (ithere != itend)
+    {
+      if (*ithere < 256) cstr[len] = (unsigned char)(*ithere);
+      else {
+	// put a space or a question mark depending on what
+	// the character is. Question marks tell the user that
+	// they are missing some information.
+	if (is_unicode_space (*ithere)) cstr[len] = ' ';
+	else cstr[len] = '?';
+      }
+      ++len;
+      ++ithere;
+    }
+
+  cstr[len] = '\0';
+
+  return (char *)cstr;
+}
+
+
+int text_t::replace(text_t toreplace, text_t replacement)
+{
+  // Get the beginning and end of the current text
+  text_t::iterator text_begin = text.begin(), text_end = text.end();
+  int count = 0;
+  text_t new_text, temp_text;
+
+  // Loop through and grab the text off the end
+  while (text_begin < text_end)
+  {
+    // Find where the next toreplace is
+    text_t::iterator next_toreplace = findword(text_begin, text_end, toreplace);
+
+    // We've found a match
+    if (next_toreplace != text_end)
+    {
+      new_text.append(substr(text_begin, next_toreplace));
+      new_text.append(replacement);
+      count++;
+      text_begin = next_toreplace + toreplace.size();
+    }
+    // We haven't found a match
+    else
+    {
+      new_text.append(substr(text_begin, text_end));
+      text_begin = text_end;
+    }
+  }
+
+  text.clear();
+  text = new_text.text_as_usvector();
+  return count;
+}
+
+
+// general functions which work on text_ts
+
+// find a character within a range
+text_t::const_iterator findchar (text_t::const_iterator first, text_t::const_iterator last, 
+				 unsigned short c)
+{
+  while (first != last)
+    {
+      if (*first == c) break;
+      ++first;
+    }
+  return first;
+}
+
+text_t::iterator findchar (text_t::iterator first, text_t::iterator last, 
+			   unsigned short c)
+{
+  while (first != last)
+    {
+      if (*first == c) break;
+      ++first;
+    }
+  return first;
+}
+
+text_t::iterator findlastchar (text_t::iterator first, text_t::iterator last, 
+			   unsigned short c)
+{
+  text_t::iterator current = last;
+  while (current != first) {
+    if (*current == c) break;
+    --current;
+  }
+  if (current == first) {
+    if (*current == c) return current;
+    return last;
+  }
+
+  return current;
+}
+
+text_t::const_iterator findword (text_t::const_iterator first, 
+				 text_t::const_iterator last, 
+				 const text_t& word)
+{
+  text_t::const_iterator word_begin = word.begin();
+  text_t::const_iterator word_end = word.end();
+
+  while (first != last)
+    {
+      text_t::const_iterator char_match = first;
+      text_t::const_iterator word_here = word_begin;
+      while (word_here!=word_end)
+	{
+	  if (*char_match != *word_here)
+	    {
+	      break;
+	    }
+	  ++char_match;
+	  ++word_here;
+	}
+      if (word_here==word_end)
+	{
+	  return first;
+	}
+      ++first;
+    }
+  return last; // get to here only if there is no match
+}
+
+text_t::iterator findword (text_t::iterator first, 
+			   text_t::iterator last, 
+			   const text_t& word)
+{
+  text_t::const_iterator word_begin = word.begin();
+  text_t::const_iterator word_end = word.end();
+
+  while (first != last)
+    {
+      text_t::iterator char_match = first;
+      text_t::const_iterator word_here = word_begin;
+      while (word_here!=word_end)
+	{
+	  if (*char_match != *word_here)
+	    {
+	      break;
+	    }
+	  ++char_match;
+	  ++word_here;
+	}
+      if (word_here==word_end)
+	{
+	  return first;
+	}
+      ++first;
+    }
+  return last; // get to here only if there is no match
+}
+
+// get a string up to the next delimiter (which is skipped)
+text_t::const_iterator getdelimitstr (text_t::const_iterator first, 
+				      text_t::const_iterator last,
+				      unsigned short c, text_t &outstr)
+{
+  text_t::const_iterator here = first;
+  here = findchar (first, last, c);
+  outstr.clear();
+  outstr.appendrange (first, here);
+  if (here != last) ++here; // skip c
+  return here;
+}
+
+text_t::iterator getdelimitstr (text_t::iterator first, text_t::iterator last,
+				unsigned short c, text_t &outstr)
+{
+  text_t::iterator here = first;
+  here = findchar (first, last, c);
+  outstr.clear();
+  outstr.appendrange (first, here);
+  if (here != last) ++here; // skip c
+  return here;
+}
+
+text_t::const_iterator getdelimitstr (text_t::const_iterator first, text_t::const_iterator last,
+				      text_t w, text_t &outstr)
+{
+  text_t::const_iterator here = first;
+  here = findword (first, last, w);
+  outstr.clear();
+  outstr.appendrange (first, here);
+  if (here != last) here += w.size(); // skip w
+  return here;
+}
+
+// split a string with a character
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tset &outlist)
+{
+  outlist.erase(outlist.begin(), outlist.end());
+
+  text_t t;
+
+  while (first != last)
+    {
+      first = getdelimitstr (first, last, c, t);
+      outlist.insert (t);
+    }
+}
+
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tlist &outlist)
+{
+  outlist.erase(outlist.begin(), outlist.end());
+
+  text_t t;
+
+  while (first != last)
+    {
+      first = getdelimitstr (first, last, c, t);
+      outlist.push_back (t);
+    }
+}
+
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tarray &outlist)
+{
+  outlist.erase(outlist.begin(), outlist.end());
+
+  text_t t;
+
+  while (first != last)
+    {
+      first = getdelimitstr (first, last, c, t);
+      outlist.push_back (t);
+    }
+}
+
+void splitword (text_t::const_iterator first, text_t::const_iterator last,
+		text_t w, text_tlist &outlist)
+{
+  outlist.erase(outlist.begin(), outlist.end());
+
+  text_t t;
+
+  while (first != last)
+    {
+      first = getdelimitstr (first, last, w, t);
+      outlist.push_back (t);
+    }
+}
+
+// join a string using a character
+void joinchar (const text_tset &inlist, unsigned short c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tset::const_iterator here = inlist.begin ();
+  text_tset::const_iterator end = inlist.end ();
+
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext.push_back (c);
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+void joinchar (const text_tlist &inlist, unsigned short c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tlist::const_iterator here = inlist.begin ();
+  text_tlist::const_iterator end = inlist.end ();
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext.push_back (c);
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+void joinchar (const text_tarray &inlist, unsigned short c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tarray::const_iterator here = inlist.begin ();
+  text_tarray::const_iterator end = inlist.end ();
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext.push_back (c);
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+void joinchar (const text_tlist &inlist, const text_t &c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tlist::const_iterator here = inlist.begin ();
+  text_tlist::const_iterator end = inlist.end ();
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext += c;
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+void joinchar (const text_tset &inlist, const text_t &c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tset::const_iterator here = inlist.begin ();
+  text_tset::const_iterator end = inlist.end ();
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext += c;
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+void joinchar (const text_tarray &inlist, const text_t &c, text_t &outtext)
+{
+  outtext.clear ();
+
+  text_tarray::const_iterator here = inlist.begin ();
+  text_tarray::const_iterator end = inlist.end ();
+  if (here != end) {
+    outtext += *here; ++here;
+    while (here != end) {
+      outtext += c;
+      outtext += *here;
+      ++here;
+    }
+  }
+}
+
+// count the occurances of a character within a range
+int countchar (text_t::const_iterator first, text_t::const_iterator last,
+	       unsigned short c)
+{
+  int count = 0;
+  while (first != last) {
+    if (*first == c) ++count;
+    ++first;
+  }
+  return count;
+}
+
+// return a substring of string from first up to but not including last
+text_t substr (text_t::const_iterator first, text_t::const_iterator last) {
+
+  text_t substr; substr.reserve(last - first + 2);
+  while (first != last) {
+    substr.push_back(*first);
+    ++first;
+  }
+  return substr;
+}
+
+
+// convert to lowercase
+void lc (text_t::iterator first, text_t::iterator last) {
+  while (first != last) {
+    *first = unicode_tolower(*first);
+    ++first;
+  }
+}
+
+// convert to uppercase
+void uc (text_t::iterator first, text_t::iterator last) {
+  while (first != last) {
+    *first = unicode_toupper(*first);
+    ++first;
+  }
+}
+
+
+// checks to see if it is a number (i.e. contains only 0-9)
+bool is_number (const text_t &text) {
+
+  text_t::const_iterator here = text.begin();
+  text_t::const_iterator end = text.end();
+
+  while (here != end) {
+    if ((*here!='0') && (*here!='1') && (*here!='2') &&
+	(*here!='3') && (*here!='4') && (*here!='5') &&
+	(*here!='6') && (*here!='7') && (*here!='8') &&
+	(*here!='9')) return false;
+    ++here;
+  }
+  return true;
+}
+
+
+// checks to see if the text has any letters or digits
+bool has_unicode_letdig (const text_t &text) {
+  if (text.empty()) return false;
+  
+  text_t::const_iterator here = text.begin();
+  text_t::const_iterator end = text.end();
+  while (here != end) {
+    if (is_unicode_letdig (*here)) return true;
+    ++here;
+  }
+
+  return false;
+}
+
+// checks to see if a text_t starts with the specified prefix
+bool starts_with(const text_t& text, const text_t& prefix) {
+  if (prefix.empty()) return true;
+  if (text.empty() || text.size()<prefix.size()) return false;
+  text_t substring = substr(text.begin(), text.begin()+prefix.size());
+  return substring == prefix;
+}
+// checks to see if a text_t ends with the specified suffix
+bool ends_with(const text_t& text, const text_t& suffix) {
+  if (suffix.empty()) return true;
+  if (text.empty() || text.size() < suffix.size()) return false;
+  text_t substring = substr(text.end()-suffix.size(),text.end());
+  return substring == suffix;
+
+}
+
+
+////////////////////////////////////
+// convertclass methods
+////////////////////////////////////
+
+// conversion classes used for getting information in to and out of
+// the text_t class.
+
+convertclass::convertclass () 
+{
+  // nothing to do
+}
+
+void convertclass::reset ()
+{
+  // nothing to do
+}
+
+
+////////////////////////////////////
+// inconvertclass methods
+////////////////////////////////////
+
+// convert from a char stream to the text_t class
+// the default version assumes the input is a ascii
+// character array
+
+inconvertclass::inconvertclass () 
+{
+  start = NULL;
+  len = 0;
+}
+
+
+void inconvertclass::reset ()
+{
+  start = NULL;
+  len = 0;
+}
+
+void inconvertclass::setinput (char *thestart, size_t thelen)
+{
+  start = thestart;
+  len = thelen;
+}
+
+void inconvertclass::convert (text_t &output, status_t &status)
+{
+  output.clear();
+
+  if (start == NULL || len == 0)
+    {
+      status = finished;
+      return;
+    }
+
+  if (output.capacity() < len + 2)
+    output.reserve(len + 2);
+  
+  // don't want any funny sign conversions happening
+  unsigned char *here = (unsigned char *)start;
+  while (len > 0) 
+    {
+      output.push_back (*here); // append this character
+      ++here;
+      --len;
+    }
+
+  start = (char *)here; // save current position
+  status = finished;
+}
+
+// will treat the text_t as a 8-bit string and convert
+// it to a 16-bit string using the about convert method.
+text_t inconvertclass::convert (const text_t &t) {
+  text_t out;
+  text_t tmpout;
+  status_t status;
+  text_t::const_iterator here = t.begin();
+  text_t::const_iterator end = t.end();
+  unsigned char cbuf[256];
+  size_t cbuflen = 0;
+ 
+  out.clear();
+  if (out.capacity() < t.size() + 2)
+    out.reserve(t.size() + 2);
+  while (here != end) {
+    while (here != end && cbuflen < 256) {
+      cbuf[cbuflen++] = (unsigned char)(*here & 0xff);
+      ++here;
+    }
+
+    if (cbuflen > 0) {
+      setinput ((char *)cbuf, cbuflen);
+      status = unfinished;
+      while (status == unfinished) {
+	convert (tmpout, status);
+	out += tmpout;
+      }
+      cbuflen = 0;
+    }
+  }
+
+  out.setencoding (0); // unicode
+
+  return out;
+}
+
+// an instance of the default inconvertclass to do simple
+// conversions. Note that any functions that use this are
+// not reentrant. If a function needs to be reentrant it
+// should declare its own instance.
+inconvertclass ascii2text_t;
+
+
+////////////////////////////////////
+// outconvertclass methods
+////////////////////////////////////
+
+// Convert from a text_t class to a char stream
+// This default version assumes the output is a ascii
+// character array. If you set the output stream you
+// can use this class to output to a stream using the
+// << operator. The << operator can also be conveniently
+// used to set the output stream by doing something like
+//
+// cout << text_t2ascii << text_tstr << anothertext_tstr;
+//
+outconvertclass::outconvertclass ()
+{
+  input = NULL;
+  outs = NULL;
+}
+
+void outconvertclass::reset ()
+{
+  input = NULL;
+  outs = NULL;
+}
+
+void outconvertclass::setinput (text_t *theinput)
+{
+  input = theinput;
+  if (input != NULL) texthere = input->begin();
+}
+
+void outconvertclass::setdata(text_t *theinput, text_t::iterator thetexthere)
+{
+  input = theinput;     
+  texthere = thetexthere;
+}
+ 
+void outconvertclass::convert (char *output, size_t maxlen, 
+		      size_t &len, status_t &status)
+{
+  if (input == NULL || output == NULL)
+    {
+      status = finished;
+      return;
+    }
+
+  // don't want any funny sign conversions happening
+  unsigned char *uoutput = (unsigned char *)output;
+  text_t::iterator textend = input->end();
+  len = 0;
+  while ((len < maxlen) && (texthere != textend)) 
+    {
+      if (*texthere < 256) *uoutput = (unsigned char)(*texthere);
+      else {
+	// put a space or a question mark depending on what
+	// the character is. Question marks tell the user that
+	// they are missing some information.
+	if (is_unicode_space (*texthere)) *uoutput = ' ';
+	else *uoutput = '?';
+      }
+      ++uoutput;
+      ++len;
+      ++texthere;
+    }
+  
+  if (texthere == textend) status = finished;
+  else status = unfinished;
+}
+
+// will convert the 16-bit string to a 8-bit stream
+// and place the result in a text_t. This method uses
+// the above convert function.
+text_t outconvertclass::convert (const text_t &t) {
+  text_t out;
+  unsigned char cbuf[256];
+  size_t cbuflen = 0;
+  status_t status = unfinished;
+  
+  out.clear(); 
+  if (out.capacity() < t.size() + 2)
+    out.reserve(t.size() + 2);
+  setinput ((text_t *)&t); // discard constant
+  while (status == unfinished) {
+    convert ((char *)cbuf, 256, cbuflen, status);
+    out.appendcarr ((char *)cbuf, cbuflen);
+  }
+
+  out.setencoding (1); // other encoding
+  
+  return out;
+}
+
+
+void outconvertclass::setostream (ostream *theouts)
+{
+  outs = theouts;
+}
+
+ostream *outconvertclass::getostream ()
+{
+  return outs;
+}
+
+
+
+
+// an instance of the default outconvertclass to do simple
+// conversions
+outconvertclass text_t2ascii;
+
+
+
+// stream operators for the output class
+
+outconvertclass &operator<< (ostream &theouts, outconvertclass &outconverter)
+{
+  outconverter.setostream(&theouts);
+  return outconverter;
+}
+
+
+#define STREAMBUFSIZE 256
+outconvertclass &operator<< (outconvertclass &outconverter, const text_t &t)
+{
+  ostream *outstream = outconverter.getostream();
+
+  if (outstream == NULL) return outconverter;
+
+  char outbuf[STREAMBUFSIZE];
+  size_t len;
+  outconvertclass::status_t status = outconvertclass::unfinished;
+
+  // assume that there is no data needing converting
+  // left in the converter
+  outconverter.setinput ((text_t *)(&t)); // note the const -> nonconst conversion
+
+  while (status == outconvertclass::unfinished)
+    {
+      outconverter.convert (outbuf, STREAMBUFSIZE, len, status);
+      if (len > 0) outstream->write(outbuf, len);
+    }
+
+  return outconverter;
+}
Index: /gsdl/trunk/gs2common/src/lib/text_t.h
===================================================================
--- /gsdl/trunk/gs2common/src/lib/text_t.h	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/text_t.h	(revision 16561)
@@ -0,0 +1,385 @@
+/**********************************************************************
+ *
+ * text_t.h -- a simple 16-bit character string class
+ * 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 TEXT_T_H
+#define TEXT_T_H
+
+#include "gsdlconf.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#  include <ospace\std\list>
+#  include <ospace\std\set>
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#  include <list.h>
+#  include <set.h>
+#  include <map.h>
+#else
+#  include <vector>
+#  include <list>
+#  include <set>
+#  include <map>
+#endif
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+// class prototypes
+class text_t;
+class inconvertclass;
+class outconvertclass;
+
+
+// for those stupid compilers which need it
+#if defined(GSDL_NEED_DESTROY_USHORT)
+inline void destroy(unsigned short *) {};
+inline void destroy(int *) {};
+#endif
+
+typedef vector<unsigned short> usvector;
+extern const text_t g_EmptyText;
+
+// The class text_t can handle long strings which may contain
+// null characters. It uses unsigned shorts to represent up to
+// 64K character values.
+class text_t {
+  public:
+  //type support for ucvector
+  typedef usvector::iterator iterator;
+  typedef usvector::const_iterator const_iterator;
+  typedef usvector::reference reference;
+  typedef usvector::const_reference const_reference;
+  typedef usvector::size_type size_type;
+  typedef usvector::difference_type difference_type;
+  typedef usvector::const_reverse_iterator const_reverse_iterator;
+  typedef usvector::reverse_iterator reverse_iterator;
+  
+protected:
+  usvector text;
+  unsigned short encoding; // 0 = unicode, 1 = other
+  
+public:
+  // constructors
+  text_t ();
+  text_t (int i);
+  text_t (const char *s); // assumed to be a normal c string
+  text_t (const char *s, size_type nLength); // support for arrays of chars
+  void setencoding (unsigned short theencoding) {encoding=theencoding;};
+  unsigned short getencoding () {return encoding;};
+
+  usvector& text_as_usvector() { return text ; };
+  const usvector& text_as_usvector() const { return text ; };
+
+  // basic container support
+  iterator begin () {return text.begin();}
+  const_iterator begin () const {return text.begin();}
+  iterator end () {return text.end();}
+  const_iterator end () const {return text.end();}
+
+  void erase(iterator pos) {text.erase(pos);}
+  void erase(iterator first, iterator last) {text.erase(first, last);}
+  void push_back(unsigned short c) {text.push_back(c);}
+  void pop_back() {text.pop_back();}
+  text_t &operator=(const text_t &x) {text=x.text; encoding=x.encoding; return *this;}
+  reference operator[](size_type n) {return text[n];};
+  const_reference operator[](size_type n) const {return text[n];};
+
+  void reserve (size_type n) {text.reserve(n);}
+  size_type capacity() const { return text.capacity(); }
+  
+  bool empty () const {return text.empty();}
+  size_type size() const {return text.size();}
+  friend inline bool operator!=(const text_t& x, const text_t& y)
+	{return (x.text != y.text);}
+  friend inline bool operator==(const text_t& x, const text_t& y) 
+	{return (x.text == y.text);}
+  friend inline bool operator<(const text_t& x, const text_t& y) 
+	{return (x.text < y.text);}
+  friend inline bool operator>(const text_t& x, const text_t& y) 
+	{return (x.text > y.text);}
+  friend inline bool operator>=(const text_t& x, const text_t& y) 
+	{return (x.text >= y.text);}
+  friend inline bool operator<=(const text_t& x, const text_t& y) 
+	{return (x.text <= y.text);}
+  
+  // added functionality
+  void clear () {text.erase(text.begin(),text.end());}
+  void append (const text_t &t);
+  void appendrange (iterator first, iterator last);
+  void appendrange (const_iterator first, const_iterator last);
+  text_t &operator+= (const text_t &t) {append(t);return *this;}
+
+  // support for integers
+  void appendint (int i);
+  void setint (int i) {clear();appendint(i);}
+  text_t &operator=(int i) {setint (i);return *this;}
+  text_t &operator+= (int i) {appendint(i);return *this;}
+  int getint () const;
+
+  // same as getint but returns an unsigned long
+  unsigned long getulong () const;
+
+  // support for arrays of chars
+  void appendcarr (const char *s, size_type len);
+  void setcarr (char *s, size_type len) {clear();appendcarr(s,len);}
+
+  // support for const null-terminated C strings
+  void appendcstr (const char *s);
+  void setcstr (const char *s) {clear();appendcstr(s);}
+  text_t &operator= (const char *s) {setcstr(s);return *this;} // c string
+  text_t &operator+= (const char *s) {appendcstr(s);return *this;} // c string
+
+  // strings returned from getcarr and getcstr become the callers 
+  // responsibility and should be deallocated with "delete []"
+  char *getcarr(size_type &len) const;
+  char *getcstr() const;
+
+  int replace(text_t toreplace, text_t replacement);
+};
+
+// new stream converter ...
+ostream& operator<< (ostream &o, const text_t &text);
+
+inline text_t operator+(const text_t &t1, const text_t &t2)
+{
+  text_t tnew = t1;
+  tnew.append(t2);
+  return tnew;
+}
+
+inline text_t operator+(const text_t &t1, int i1)
+{
+  text_t tnew = t1;
+  tnew.appendint(i1);
+  return tnew;
+}
+
+inline text_t operator+(const text_t &t1, char *s1)
+{
+  text_t tnew = t1;
+  tnew.appendcstr(s1);
+  return tnew;
+}
+
+
+
+
+struct eqtext_t 
+{
+  bool operator()(const text_t &t1, const text_t &t2) const
+  { return t1 == t2; }
+};
+
+struct lttext_t 
+{
+  bool operator()(const text_t &t1, const text_t &t2) const
+  { return t1 < t2; }
+};
+
+
+// frequently used derived types
+typedef set<text_t,lttext_t> text_tset;
+typedef list<text_t> text_tlist;  // more efficient for insertions/deletions
+typedef vector<text_t> text_tarray; // more space efficient than text_tlist
+typedef map<text_t, text_t, lttext_t> text_tmap;
+
+
+// general functions which work on text_ts
+
+// find a character within a range
+text_t::const_iterator findchar (text_t::const_iterator first, text_t::const_iterator last, 
+				 unsigned short c);
+text_t::iterator findchar (text_t::iterator first, text_t::iterator last, 
+			   unsigned short c);
+text_t::iterator findlastchar (text_t::iterator first, text_t::iterator last, 
+			       unsigned short c);
+text_t::iterator findword (text_t::iterator first, text_t::iterator last,
+			   const text_t &word);
+text_t::const_iterator findword (text_t::const_iterator first, text_t::const_iterator last, 
+				 const text_t& word);
+
+// get a string up to the next delimiter (which is skipped)
+text_t::const_iterator getdelimitstr (text_t::const_iterator first, 
+				      text_t::const_iterator last,
+				      unsigned short c, text_t &outstr);
+text_t::iterator getdelimitstr (text_t::iterator first, text_t::iterator last,
+				unsigned short c, text_t &outstr);
+
+text_t::const_iterator getdelimitstr (text_t::const_iterator first, text_t::const_iterator last,
+				      text_t w, text_t &outstr);
+
+// split a string with a character
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tset &outlist);
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tlist &outlist);
+void splitchar (text_t::const_iterator first, text_t::const_iterator last,
+		unsigned short c, text_tarray &outlist);
+
+void splitword (text_t::const_iterator first, text_t::const_iterator last,
+		text_t w, text_tlist &outlist);
+
+// join a string using a character
+void joinchar (const text_tset &inlist, unsigned short c, text_t &outtext);
+void joinchar (const text_tlist &inlist, unsigned short c, text_t &outtext);
+void joinchar (const text_tarray &inlist, unsigned short c, text_t &outtext);
+void joinchar (const text_tset &inlist, const text_t &c, text_t &outtext);
+void joinchar (const text_tlist &inlist, const text_t &c, text_t &outtext);
+void joinchar (const text_tarray &inlist, const text_t &c, text_t &outtext);
+
+// count the occurances of a character within a range
+int countchar (text_t::const_iterator first, text_t::const_iterator last,
+	       unsigned short c);
+
+// return a substring of string from first up to but not including last
+text_t substr (text_t::const_iterator first, text_t::const_iterator last);
+
+// convert to lowercase
+void lc (text_t::iterator first, text_t::iterator last);
+inline void lc (text_t &t) {lc (t.begin(), t.end());}
+
+// convert to uppercase
+void uc (text_t::iterator first, text_t::iterator last);
+inline void uc (text_t &t) {uc (t.begin(), t.end());}
+
+// checks to see if it is a number (i.e. contains only 0-9)
+bool is_number (const text_t &text);
+
+// checks to see if the text has any letters or digits
+bool has_unicode_letdig (const text_t &text);
+
+// checks to see if a text_t starts with the specified prefix
+bool starts_with(const text_t& text, const text_t& prefix);
+// checks to see if a text_t ends with the specified suffix
+bool ends_with(const text_t& text, const text_t& suffix);
+
+// conversion classes used for getting information in to and out of
+// the text_t class.
+
+class convertclass 
+{
+public:
+  enum status_t {finished, stopped, unfinished};
+  
+  convertclass ();
+  virtual void reset ();
+};
+
+
+
+// convert from a char stream to the text_t class
+// the default version assumes the input is a ascii
+// character array
+class inconvertclass : public convertclass
+{
+public:
+  inconvertclass ();
+  void reset ();
+  void setinput (char *thestart, size_t thelen);
+
+  // output will be cleared before the conversion
+  virtual void convert (text_t &output, status_t &status);
+
+  // will treat the text_t as a 8-bit string and convert
+  // it to a 16-bit string using the about convert method.
+  text_t convert (const text_t &t);
+
+protected:
+  char *start;
+  size_t len;
+};
+
+// to get something which will do the conversion
+// to ascii declare a (non global!) instance like
+// this
+// inconvertclass ascii2text_t;
+
+#if defined(GSDL_USE_IOS_H)
+#include <iostream.h> // darwin doesn't have ostream.h...
+#else
+#include <ostream>
+#endif
+
+// Convert from a text_t class to a char stream
+// This default version assumes the output is a ascii
+// character array. If you set the output stream you
+// can use this class to output to a stream using the
+// << operator. The << operator can also be conveniently
+// used to set the output stream by doing something like
+//
+// cout << text_t2ascii << textstr << anothertextstr;
+//
+// this class assumes that the input text doesn't change
+// while the conversion takes place
+class outconvertclass : public convertclass
+{
+public:
+  outconvertclass ();
+  virtual void reset ();
+  virtual void setinput (text_t *theinput);
+  virtual void setdata(text_t *input, text_t::iterator texthere);
+  // note that convert does not null-terminate the
+  // output array of characters
+  virtual void convert (char *output, size_t maxlen, 
+			size_t &len, status_t &status);
+
+  // will convert the 16-bit string to a 8-bit stream
+  // and place the result in a text_t. This method uses
+  // the above convert function.
+  text_t convert (const text_t &t);
+
+  virtual void setostream (ostream *theouts);
+  ostream *getostream ();
+
+protected:
+  text_t *input;
+  text_t::iterator texthere; // only valid if input is valid
+
+  ostream *outs;
+};
+
+// to get something which will do the conversion
+// to text_t declare a (non global!) instance like
+// this
+// outconvertclass text_t2ascii;
+
+
+// stream operators for the output class
+outconvertclass &operator<< (ostream &theouts, outconvertclass &outconverter);
+outconvertclass &operator<< (outconvertclass &outconverter, const text_t &t);
+
+
+#endif
Index: /gsdl/trunk/gs2common/src/lib/win32.mak
===================================================================
--- /gsdl/trunk/gs2common/src/lib/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2common/src/lib/win32.mak	(revision 16561)
@@ -0,0 +1,145 @@
+###########################################################################
+#
+# win32 makefile -- lib
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+
+
+GSDL_VC4 = 0
+LOCAL_LIBRARY = 0
+DLL = 0
+DLLDEBUG = 0
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_INCLUDES = -I"$(PACKAGES_DIR)\sqlite\include"
+SQLITE_OBJECTS = sqlitedbclass.obj
+SQLITE_SOURCES = sqlitedbclass.cpp
+!ELSE
+SQLITE_INCLUDES = 
+SQLITE_OBJECTS = 
+SQLITE_SOURCES = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"$(GSDL_DIR)" -I"$(INDEXERS_DIR)\mg\lib" -I"$(PACKAGES_DIR)\windows\gdbm\gdbm" $(SQLITE_INCLUDES)
+
+!IF $(GSDL_VC4)
+DEFS = $(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+!IF $(LOCAL_LIBRARY)
+DEFS = $(DEFS) -DGSDL_LOCAL_LIBRARY
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+SOURCES = \
+	cfgread.cpp \
+	cnfgable.cpp \
+	cnfgator.cpp \
+	dbclass.cpp \
+	display.cpp \
+	fileutil.cpp \
+	gdbmclass.cpp \
+	gsdlsitecfg.cpp \
+	gsdltimes.cpp \
+	gsdltools.cpp \
+	gsdlunicode.cpp \
+	infodbclass.cpp \
+	md5.cpp \
+	OIDtools.cpp \
+	phrases.cpp \
+	text_t.cpp \
+	$(SQLITE_SOURCES)
+
+OBJECTS = \
+	cfgread.obj \
+	cnfgable.obj \
+	cnfgator.obj \
+	dbclass.obj \
+	display.obj \
+	fileutil.obj \
+	gdbmclass.obj \
+	gsdlsitecfg.obj \
+	gsdltimes.obj \
+	gsdltools.obj \
+	gsdlunicode.obj \
+	infodbclass.obj \
+	md5.obj \
+	OIDtools.obj \
+	phrases.obj \
+	text_t.obj \
+	$(SQLITE_OBJECTS)
+
+LIBRARY = gsdllib.lib	
+
+
+# Default target: make gsdllib.lib
+all: $(LIBRARY)
+
+gsdllib.lib : $(OBJECTS)
+	if EXIST $(LIBRARY) del $(LIBRARY)
+	$(AR) -nologo /out:$(LIBRARY) $(OBJECTS)
+
+install:
+
+clean:
+	del *.obj 
+	del $(LIBRARY)
+
+
+# Always recompile gsdltools in case GSDL_LOCAL_LIBRARY has changed
+gsdltools.obj: FORCE
+
+FORCE:
Index: /gsdl/trunk/gs2common/yaz/GSDL_USAGE.html
===================================================================
--- /gsdl/trunk/gs2common/yaz/GSDL_USAGE.html	(revision 16561)
+++ /gsdl/trunk/gs2common/yaz/GSDL_USAGE.html	(revision 16561)
@@ -0,0 +1,124 @@
+<html>
+<head>
+<title>Using the Z39.50 Client in Greenstone</title>
+</head>
+
+<body>
+<a name=top>
+<h1>Using the Z39.50 Client in Greenstone</h1>
+</a>
+<b>Table of Contents</b><br>
+<a href="#whatis">About: What is Z39.50?</a><br>
+<a href="#config">Configuring Greenstone - Adding Servers</a><br>
+<a href="#todo">Currently known problems, and TODO list</a><br>
+<a href="#contact">Contacting Us</a><br>
+
+
+
+<a name=whatis>
+<h2>What is Z39.50?</h2>
+</a>
+Z39.50 is an international client/server protocol for searching bibliographic
+data. It can use the Internet Protocol (TCP/IP), which makes the databases
+on a server available from almost anywhere around the globe. It is widely
+used, for example, in on-line library catalogues. It allows a user to search
+one or more databases and retrieve the results of the query.
+
+<p>
+Our implementation uses the YAZ z39.50 toolkit, written by
+<a href="http://www.indexdata.dk">Index Data</a>.
+
+
+
+<a name=config>
+<h2>Adding Servers</h2>
+</a>
+The file z3950.cfg in the etc/packages directory contains entries for
+each server. By default, no servers are set up, although the config file
+comes with two (commented out) example Z39.50 servers, both for servers
+of the United States' <b>Library of Congress</b>.
+<p>
+Each entry consists of:
+<ul>
+<li>A unique "short name" for internal use by Greenstone.</li>
+<li>The internet name or address of the server, and optionally the
+port that the server is running on if not the default port 210.</li>
+<li>The name of the database to search on that server.</li>
+<li>A string that provides a meaningful name for the "collection".</li>
+<li>An optional "About" string, providing some information about the database
+and/or server.</li>
+<li>Optional icon fields, which are displayed instead of the text on the
+front page.</li>
+</ul>
+The entries need only be separated by whitespace, but for the purposes of
+clarity the sample entry uses newlines and tabs.
+<p>
+The sample Library of Congress entry looks like this:
+<pre>
+LOC
+        z3950.loc.gov:7090
+        Voyager
+        "Library of Congress z39.50 server"
+        About  en  "This is the z39.50 interface to the US LoC catalogue
+           system. It contains approximately 12 million bibliographic records.
+           For server capabilities, look at
+	   &lt;a href=\"http://lcweb.loc.gov/z3950/lcserver.html\"&gt;their web pages&lt;/a&gt;."
+</pre>
+There is a list at the Library Of Congress website containing some 
+<a href="http://lcweb.loc.gov/z3950/agency/resources/testport.html">servers 
+publicly available for testing</a>.
+
+
+<a name=todo>
+<h2>Known Problems/Issues list</h2>
+</a>
+<ul>
+<li>Because of the open nature of the standard, our client may not work with
+some servers. Please notify the author (contact details below) of any
+such problems.</li>
+<li>Because of the large number of Marc fields, only the most frequently
+used fields have been given explicit names in the results. Furthermore,
+these are currently hard-coded to correspond to the USMARC field names.</li>
+<li><b>The z39.50 client will not work when fast-cgi is used!</b>. Currently,
+the use of the fast-cgi package (off by default) will disable the
+use of the z39.50 client code for Greenstone.</li>
+<li>There are spurious &lt;B&gt; tags in the title of the window when
+viewing an individual record.</li>
+<li>Greenstone will claim there are no collections if there are no
+sub-directories in the "collect" directory, even if there are configured
+z39.50 servers, although it will work if there are unbuilt collections.</li>
+</ul>
+
+<b>History</b><br>
+Jul 2001 - finally tracked down and fixed an annoying bug that caused
+greenstone to segfault when retrieving documents for some queries.<br>
+Jun 2001 - decided to drop href for field 856, as some examples have 
+explanatory text in the field as well as the URI.<br>
+Oct 2000 - cache results from server, so cut down the number of connections for
+a single query/document. Also added href for MARC field 856.<br>
+Aug 2000 - Various bug fixes and minor modifications.<br>
+Fri Aug 4 2000 - The z39.50 code is now "stable" in the main source code (in CVS).<br>
+Tue 1 Aug 2000 - selecting a field from the drop-down box now actually works. 
+This results in both quicker replies and more accurate results.<br>
+Mon 24 Jul 2000 - queries are now "AND"ed - for example, the query "computer 
+science" will now only return records that contain both words.<br>
+May-June 2000 - Code written.<br>
+
+<a name=contact>
+<h2>Contact Details</h2>
+</a>
+For general comments about Greenstone, or suggestions for improvements, send
+email to
+<a href="mailto:greenstone@cs.waikato.ac.nz?subject=z39.50 client in Greenstone">greenstone@cs.waikato.ac.nz</a><br>
+<br>
+For bug reports or questions about the z39.50 code itself, send email to
+the author, John McPherson -
+<a href="mailto:jrm21@cs.waikato.ac.nz?subject=z39.50 client in Greenstone"
+>jrm21@cs.waikato.ac.nz</a>.
+
+<p>
+<a href="#top">Back to top</a>
+<p>
+<address>Last modified: July 12, 2001 by John McPherson</address>
+</body>
+</html>
Index: /gsdl/trunk/gs2common/yaz/README.gsdl
===================================================================
--- /gsdl/trunk/gs2common/yaz/README.gsdl	(revision 16561)
+++ /gsdl/trunk/gs2common/yaz/README.gsdl	(revision 16561)
@@ -0,0 +1,24 @@
+The YAZ toolkit is used by the GSDL receptionist
+when acting as a z39.50 client.
+
+June 2005, kjdon:
+Changed to use yaz version 2.1.4. This is also used by the new z3950server.
+The original yaz download has been added to cvs - this is unzipped and untared by the configure script, then only the src and client directories are compiled.
+Greenstone uses the libyaz.a library. yaz_zclient.o is modified from the sample client.o, and now lives in the colservr directory.
+
+Two files are linked against: 
+ client/yaz_client.o, which was modified from Index Data's sample 
+	client code, and
+
+ lib/libyaz.a, which contains all the z39.50 and MARC formatting code.
+	There is probably a lot of stuff in this library that does
+	not need to be there for our purposes, but for now it is,
+	merely wasting some disk space and some compile-time CPU.
+
+John McPherson
+3 August 2000
+
+yaz version 1.6
+
+September 2000:
+removed "ill" "server" "ztest" and "retrieval" subdirs.
Index: /gsdl/trunk/gs2common/yaz/config.status
===================================================================
--- /gsdl/trunk/gs2common/yaz/config.status	(revision 16561)
+++ /gsdl/trunk/gs2common/yaz/config.status	(revision 16561)
@@ -0,0 +1,159 @@
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host ickenham.mdx.ac.uk:
+#
+# ./configure  --cache-file=/home/George/corbanormal/gsdl/config.cache
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: ./config.status [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running ${CONFIG_SHELL-/bin/sh} ./configure  --cache-file=/home/George/corbanormal/gsdl/config.cache --no-create --no-recursion"
+    exec ${CONFIG_SHELL-/bin/sh} ./configure  --cache-file=/home/George/corbanormal/gsdl/config.cache --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "./config.status generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "$ac_cs_usage"; exit 0 ;;
+  *) echo "$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=.
+
+trap 'rm -fr Makefile util/Makefile odr/Makefile z39.50/Makefile asn/Makefile zutil/Makefile comstack/Makefile ccl/Makefile tab/Makefile include/Makefile include/yaz/Makefile lib/Makefile client/Makefile doc/Makefile conftest*; exit 1' 1 2 15
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\&%]/\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
+/^[ 	]*VPATH[ 	]*=[^:]*$/d
+
+s%@SHELL@%/bin/sh%g
+s%@CFLAGS@%-g -O2%g
+s%@CPPFLAGS@%%g
+s%@CXXFLAGS@%%g
+s%@FFLAGS@%%g
+s%@DEFS@% -DHAVE_READLINE_READLINE_H=1 -DHAVE_READLINE_HISTORY_H=1 -DSTDC_HEADERS=1 %g
+s%@LDFLAGS@%%g
+s%@LIBS@% -lreadline -lhistory%g
+s%@exec_prefix@%${prefix}%g
+s%@prefix@%/usr/local%g
+s%@program_transform_name@%s,x,x,%g
+s%@bindir@%${exec_prefix}/bin%g
+s%@sbindir@%${exec_prefix}/sbin%g
+s%@libexecdir@%${exec_prefix}/libexec%g
+s%@datadir@%${prefix}/share%g
+s%@sysconfdir@%${prefix}/etc%g
+s%@sharedstatedir@%${prefix}/com%g
+s%@localstatedir@%${prefix}/var%g
+s%@libdir@%${exec_prefix}/lib%g
+s%@includedir@%${prefix}/include%g
+s%@oldincludedir@%/usr/include%g
+s%@infodir@%${prefix}/info%g
+s%@mandir@%${prefix}/man%g
+s%@CC@%gcc%g
+s%@CPP@%gcc -E%g
+s%@RANLIB@%ranlib%g
+s%@have_sgml2txt@%no%g
+s%@have_sgml2latex@%no%g
+s%@have_sgml2html@%no%g
+s%@ASNMODULE@%z39.50%g
+s%@ILLMODULE@%ill%g
+s%@ILLLIB@%../ill/libill.a%g
+s%@SUBDIRS_VAR@%util odr z39.50 zutil comstack ccl tab include lib client %g
+
+CEOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+
+CONFIG_FILES=${CONFIG_FILES-"Makefile util/Makefile odr/Makefile z39.50/Makefile asn/Makefile zutil/Makefile comstack/Makefile ccl/Makefile tab/Makefile include/Makefile include/yaz/Makefile lib/Makefile client/Makefile doc/Makefile"}
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+
+
+exit 0
Index: /gsdl/trunk/gs2runtime/packages/d2m/D2Mcgi.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/D2Mcgi.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/D2Mcgi.c	(revision 16561)
@@ -0,0 +1,326 @@
+/* ------------------------------------------------------------------- */
+/* D2Mcgi.c  : CGI program for DC -> MARC conversion                   */
+/* Author    : Ole Husby                                               */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------
+D2MCgi() will take metadata input from a parameter, or fetch an
+HTML file using HTTP.
+
+DC metadata will be converted into MARC, and presented in one of
+several formats.
+
+The program will look for the following QUERY_STRING:
+
+url=xxx&fmt=xxx&prs=xxx&txt=xxx&par=xxx 
+
+where: 
+
+  url
+      xxx = the URL for the file containing the metadata
+  fmt
+      The MARC format. xxx =
+      DANMARC | FINMARC | ISMARC | NORMARC (default) | SWEMARC | USMARC.
+  prs
+      The presentation format. xxx =
+      plain (default) | html | 2709 | binary.
+
+  txt
+      If present, this field will be parsed for metadata, and the only 
+      use of the URL (if entered) will be used to produce the 856 field 
+      of the MARC record. 
+
+  par
+      If xxx = on, the program will additionally display the tags as
+      they are recognized. Will only work together with the html format.
+   ------------------------------------------------------------------- */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "d2m.h"
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* main() program                                                      */
+/* ------------------------------------------------------------------- */
+
+int main ()
+{
+  int rc, i, source, textarea, f2709, tracing, mformat, marcfile;
+  int linel, llen, slen;
+  int *meta;
+
+  FILE *df, *of;
+  char contype[64], dfilename[64], tfilename[64], line[50000];
+  char *pq, *p, *q, pre[16], *l, *u;
+  char marcformat[256], url[1000], method[32];
+  char *buffer[50000];
+  char syntax[64];
+
+  char recstatus = 'n';
+  char rectype   = 'm';
+  char biblevel  = ' ';
+
+/* Initializing  */
+
+  of = stdout;
+  *url = 0;
+  marcfile = 0;
+
+  strcpy(marcformat, D2M_DEFAULT_FORMAT);
+  strcpy(syntax, D2M_DEFAULT_SYNTAX);
+  sprintf(dfilename, "%s.%d", D2M_TEMPFILE, getpid());
+
+  textarea = tracing = f2709 = 0;
+  *line = 0;
+
+
+/* Decide parameters for line format formating   */
+
+  linel = D2M_LINELEN;
+  if (!linel)
+    linel = 80;
+
+  if (linel > 200)
+    linel = 200;
+
+  llen = linel - 9;
+  slen = linel - 30;
+  if (slen < 10)
+    slen = 10;
+
+
+/* Check request method (POST or GET)  */
+
+  strcpy(method, getenv("REQUEST_METHOD"));
+
+  if (strcmp(method, "GET") == 0)
+  {
+    strcpy(line, getenv("QUERY_STRING"));
+    pq = (char *) strdup(line);
+  }
+  
+  else if (strcmp(method, "POST") == 0)
+    pq = poq();
+
+  else
+    pq = (char *) NULL;           /* Error exit is missing here ! */
+
+
+/* Parse parameters  */
+  
+  if (pq)
+  {
+    strcpy(line, pq);
+    free(pq);
+
+    l = (char *) line;
+
+    u = strtok(l, "&");
+    while (u)
+    {
+      unescape(u);
+      if (strncasecmp(u, "url=", 4) == 0)
+        strcpy(url, u + 4); 
+      else if (strncasecmp(u, "fmt=", 4) == 0)
+        strcpy(marcformat, u + 4); 
+      else if (strncasecmp(u, "prs=", 4) == 0)
+        strcpy(syntax, u + 4); 
+      else if (strncasecmp(u, "par=", 4) == 0)
+      {
+        if (strcmp(u + 4, "on") == 0) 
+          tracing = 1;
+      }
+      else if (strncasecmp(u, "txt=", 4) == 0)
+      {
+        if (strlen(u + 4))
+        {
+          if ( df = fopen(dfilename, "w"))
+          {
+            fprintf(df, "%s", u + 4);
+            textarea = 1;
+            fclose(df);
+          }
+        }
+      }
+      u = strtok(NULL, "&");
+    }
+  }
+
+
+
+/* Check the syntax */
+
+  if (strcmp(syntax, "2709") == 0)
+  {
+    strcpy(contype, "text/plain");
+    tracing = 0;
+    f2709 = 1;
+  }
+
+  else if (strcmp(syntax, "binary") == 0)
+  {
+    strcpy(contype, "application/marc");
+    tracing = 0;
+    f2709 = 1;
+  }
+
+  else if (strcmp(syntax, "html") == 0)
+  {
+    strcpy(contype, "text/html");
+  }
+
+  else if (strcmp(syntax, "plain") == 0)
+  {
+    strcpy(contype, "text/plain");
+    tracing = 0;
+  }
+
+
+/* Write HTTP header and HTML <HEAD>: */
+
+  printf("Content-type: %s\n\n", contype);
+  if (strcmp(syntax, "html") == 0)
+  {
+    printf("<html><head><title>\n");
+    printf("%s\n", HTML_TITLE);
+    printf("</title></head>\n\n<body bgcolor=\"#ffffbb\">\n");
+  }
+
+
+
+/* Check if metadata in input (source = TEXTAREA) or in URL   */
+
+  if (!textarea && !*url)
+  {
+    printf("1: ");
+    printf("Error: Insufficient input\n");
+    exit(0);
+  }
+
+  else if (textarea)
+    source = TEXTAREA;
+
+  else
+    source = URL;
+
+  if (!textarea && !*url)
+  {
+    printf("2: ");
+    printf("Error: Insufficient input\n");
+    exit(0);
+  }
+
+
+/* Check the marcformat   */
+
+  if (strncasecmp(marcformat, "DAN", 3) == 0)
+    mformat = DANMARC;
+  else if (strncasecmp(marcformat, "FIN", 3) == 0)
+    mformat = FINMARC;
+  else if (strncasecmp(marcformat, "IS", 2) == 0)
+    mformat = ISMARC;
+  else if (strncasecmp(marcformat, "US", 2) == 0)
+    mformat = USMARC;
+  else if (strncasecmp(marcformat, "NOR", 3) == 0)
+    mformat = NORMARC;
+  else if (strncasecmp(marcformat, "SWE", 3) == 0)
+    mformat = SWEMARC;
+  else
+  {
+    printf("Error: This MARC format is not supported!\n");
+    exit(0);
+  }
+
+
+/* Fetch URL if necessary    */
+
+  if (source == URL)
+  {
+    if (strcmp(url, "test") == 0)
+      strcpy(url, D2M_DEFAULT_URL);
+    rc = wfetch(&url, &dfilename);
+    if (!rc)
+    {
+      printf("Error %d: Unable to fetch file %s\n", rc, url);
+      exit(0);
+    }
+    if (rc == 2)
+      marcfile = 1;
+  }
+
+  if (*url && !textarea && ( strcmp(syntax, "html") == 0 ) )
+  {
+    printf("<h2><a href=\"%s\">%s</a></h2>\n", url, url);
+    printf("<h3>%s record:</h3><hr>\n", marcformat);
+  }
+
+   if (strcmp(syntax, "html") == 0)
+     printf("<pre>\n");
+
+   if (tracing)
+     strcpy(tfilename, D2M_TRACEFILE);
+   else
+     *tfilename = 0;
+
+/* Convert to MARC                                                    */
+/* NOTE: M2Mconv() will even convert from MARC 2709 to line format    */
+
+   if (marcfile)
+     rc = M2Mconv((char *) dfilename, (char *) buffer,
+                 (char *) tfilename, mformat);
+   else            
+     rc = D2Mconv((char *) dfilename, (char *) buffer,
+                 (char *) tfilename, (char *) url, mformat);
+
+   remove(dfilename);
+
+
+/* Produce output                                                      */
+
+   if (!rc)
+     printf("Error: Unable to parse data\n");
+
+   else
+   {
+     if (f2709)
+       printf("%s", 
+         (char *) MARC2709(buffer, 0, 6, recstatus, rectype, biblevel));
+     else
+  
+     {
+       l = strtok((char *) buffer, "\n");
+  
+       while (l)
+       {
+         write_field(l, of, linel, llen, slen);
+         l = strtok(NULL, "\n");
+       }
+     }
+   }
+
+   if (*url && !textarea && ( strcmp(syntax, "html") == 0 ) )
+     printf("<hr>\n");
+
+   if (tracing)
+   {
+     printf("</pre>\n");
+     printf("<h3>Parsed DC metadata:</h3>\n");
+     printf("<pre>\n");
+     write_trace(tfilename, of);
+   }
+
+   if (strcmp(syntax, "html") == 0)
+   {
+     printf("</pre>\n");
+     printf("</body></html>\n");
+   }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/D2Mconv.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/D2Mconv.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/D2Mconv.c	(revision 16561)
@@ -0,0 +1,265 @@
+/* ------------------------------------------------------------------- */
+/* D2Mconv   : Converts metadata present in a file to MARC             */
+/*             Writes MARC record in "line format"                     */
+/*                                                                     */
+/* Syntax    : Arguments:                                              */
+/*                                                                     */
+/*       char *dfile : input file                                      */
+/*                     DC metadata must be present in HTML 2.0         */
+/*                     or HTML 4.0 syntax                              */
+/*       char *buffer: output record in line format                    */
+/*                     Must be allocated in calling program!           */
+/*       char *tfile : tracefile                                       */
+/*                     No trace if NULL                                */
+/*       char *url   : URL for use in case no URL in DC.Identifier     */
+/*                                                                     */
+/*       char *format: MARC format (see d2m.h)                         */
+/*                                                                     */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include "d2m.h"
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Checks a new line                                                   */
+/* ------------------------------------------------------------------- */
+/*
+   This is where starting and ending of a meta tag is parsed.
+   The whole metatag (resulting from one ore more lines) is put
+   into the buffer.
+
+   Note: This function is called recursively!
+*/
+
+int mline(char *l, char *buf, int *meta, 
+          struct marcrec *mrec, FILE *tf, int format)
+{
+  char *p, *q, rc;
+  int diff;
+
+  struct metatag *mtag;
+ 
+  if (!l || !*l) return 1;
+
+  q = l;
+
+
+/* Looks for the end of an already started metatag buffer              */
+/* The end is normally '">'                                            */
+
+  if (*meta)
+  {
+     p = strstr(l, "\">");
+
+     if (p) 
+     {
+        if ( (char *) p != (char *) l)
+        {
+           diff = (int)(p - l);
+           if (*buf)
+             strcat(buf, " ");
+           strncat(buf, l, diff);
+        }
+
+/* Parsing the buffer for metadata                                  */
+
+        mtag = dc_parse(buf);
+
+        if (mtag)
+        {
+          if (tf)
+          {
+            fprintf(tf, "Name  : %s\n", mtag->name   );
+            fprintf(tf, "Type  : %s\n", mtag->type   );
+            fprintf(tf, "Scheme: %s\n", mtag->scheme );
+            fprintf(tf, "Value : %s\n", mtag->value  );
+            fprintf(tf, "\n");
+          }
+
+/*        Convert to MARC                                           */
+
+          MARCmake(mtag, mrec, format);
+        }
+        free(mtag);
+
+        *buf = 0;
+        *meta = FALSE;
+        q = p + 2;
+     }
+     else
+     {
+        strcat(buf, l);
+        q = NULL;
+     }
+  }
+
+
+  if (!q) return 1;
+
+  p = (char *) cstr(q, "<meta ");
+
+  if (p)
+  {
+    *meta = TRUE;
+    mline(p + 6, buf, meta, mrec, tf, format);
+  }
+
+  return 1;
+}
+
+
+
+int D2Mconv(char *dfile, char *buffer, char *tfile, char *url, int format)
+{
+  struct marcrec *mrec;
+  struct metatag *mtag;
+  char line[50000], today[32];
+  char *l, *p;
+  time_t d;
+  struct tm *time_struct;
+
+  FILE *df, *tf;
+  int *meta;
+
+  putenv("TZ=NFT-1DFT");
+  d = time(NULL);
+  time_struct = localtime(&d);
+  strftime(today, 16, "%y%m%d", time_struct);
+
+  mrec = malloc(sizeof(*mrec));
+
+  mrec->marcline = malloc(100000);
+  mrec->partitle = malloc(10000);
+  mrec->subtitle = malloc(10000);
+  mrec->year     = malloc(1000);
+  mrec->url      = malloc(1000);
+  mrec->fmat     = malloc(1000);
+  mrec->s008     = malloc(41);
+
+  mrec->ncreators = 0;
+  mrec->ntitles   = 0;
+  *mrec->marcline = 0;
+  *mrec->subtitle = 0;
+  *mrec->partitle = 0;
+  *mrec->year     = 0;
+  *mrec->url      = 0;
+  *mrec->fmat     = 0;
+  strcpy(mrec->s008, "                                        ");
+
+  meta = malloc(sizeof(int));
+
+  tf = (FILE *) NULL;
+
+
+/* Open inputfile */
+
+  df = fopen(dfile, "r");
+  if (!df)
+    return 0;                            /* Error: Unable to read data */
+
+
+
+/* Open tracefile */
+
+   if (*tfile)
+   {
+     tf = fopen(tfile, "w");
+     if (!tf)
+     {
+       fclose(df);
+       return 0;                         /* Error: Unable to create trace */
+     }
+   }
+
+   *meta = FALSE;
+
+
+/* Reads the file */ 
+
+
+   while (l = fgets(line, 9999, df))
+   {
+
+/*   Remove trailing LF and / or CR     */
+
+     while ( (l[strlen(l) - 1] == '\n') || (l[strlen(l) - 1] == '\r') )
+       l[strlen(l) -1] = '\0';
+
+
+/*   printf("... main  %s\n",l);                             */ 
+
+/*   Stops reading file if "</head" or "<body" is encountered  */
+
+     p = cstr(l, (char *) "</head");
+     if (p) break;
+
+     p = cstr(l, (char *) "<body");
+     if (p) break;
+
+     mline(l, (char *) buffer, (int *) meta, mrec, tf, format);
+   }
+
+
+
+/* Check if remaining data in buffer */
+  
+   mtag = dc_parse((char *) buffer);
+
+   if (mtag) 
+   {
+     if (tf)
+     {
+       fprintf(tf, "Name  : %s\n", mtag->name   );
+       fprintf(tf, "Type  : %s\n", mtag->type   );
+       fprintf(tf, "Scheme: %s\n", mtag->scheme );
+       fprintf(tf, "Value : %s\n", mtag->value  );
+       fprintf(tf, "\n");
+     }
+     MARCmake(mtag, mrec, format);
+   }
+   free(mtag);
+
+
+/* Close files */ 
+
+   fclose(df);
+
+   if (tf)
+     fclose(tf);
+
+/* Put URL into mrec (if not present)  */
+
+   if (!*mrec->url)
+     strcpy(mrec->url, url);
+
+/* Put date into 008 */
+
+   put008(mrec->s008, today, F008_DATE_ENTERED);
+   put008(mrec->s008, "s", F008_TYPE_OF_DATE);
+
+   switch (format)
+   {
+     case ISMARC  :
+     {
+       put008(mrec->s008, "k", F008_FORM_OF_PUBLICATION);
+       break;
+     }
+   }
+
+/* Tidy up the MARC buffer                                               */
+
+   MARCtidy(mrec, buffer, format);
+
+   return 1;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/D2Mmain.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/D2Mmain.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/D2Mmain.c	(revision 16561)
@@ -0,0 +1,246 @@
+/* ------------------------------------------------------------------- */
+/* D2Mmain   : standalone program for DC -> MARC conversion            */
+/*                                                                     */
+/* Syntax    : d2main [File] [Options]                                 */
+/*                                                                     */
+/* File      : This may be a local filename, or a URL (which is        */
+/*             recognized by the "http://" prefix). When a URL,        */
+/*             the remote file will be fetched using the builtin       */
+/*             HTTP "client".                                          */
+/*                                                                     */
+/* Options   : -l                       : Format = line format         */
+/*                                        (Default is ISO 2709)        */
+/* Options   : -f [filename]            : Outputfile                   */
+/*                                        (Default is stdout)          */
+/* Options   : -m [marcformat]          : Marcformat (upper case)      */
+/*                                        (Default is NORMARC)         */
+/* Options   : -t [filename]            : Tracefile                    */
+/*                                        (Default is no trace)        */
+/*                                                                     */
+/* Return    : 0 : OK                                                  */
+/*             1 : Syntax error (no parms != 2)                        */
+/*             2 : Illegal MARC format                                 */
+/*             3 : Cannot open output file                             */
+/*             8 : Error in MARC conversion                            */
+/*             9 : URL not found                                       */
+/*                                                                     */
+/* Author    : Ole Husby                                               */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "d2m.h"
+
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* main() program                                                      */
+/* ------------------------------------------------------------------- */
+
+int main (int argc, char **argv)
+{
+  int rc, i, f2709, tracing, argtype, mformat, marcfile;
+  int linel, llen, slen;
+
+  FILE *df, *of;
+  char dfilename[64], tfilename[64], ofilename[64], line[50000];
+  char *pq, *p, *q, pre[16], *l, *u;
+  char marcformat[64], url[1000];
+  char buffer[50000];
+
+  char recstatus = 'n';
+  char rectype   = 'm';
+  char biblevel  = ' ';
+
+  enum options {OPT_NONE, OPT_FILE, OPT_TRAC, OPT_MARC};
+
+
+/* Initialize */
+
+  of = stdout;
+  *url = *ofilename = 0;
+  strcpy(marcformat, "NORMARC");
+
+  tracing = FALSE;
+  *tfilename = 0;
+  f2709 = TRUE;
+  marcfile = 0;
+
+  *line = 0;
+
+  linel = D2M_LINELEN;
+  if (!linel)
+    linel = 80;
+
+  if (linel > 200)
+    linel = 200;
+
+  llen = linel - 9;
+  slen = linel - 30;
+  if (slen < 10)
+    slen = 10;
+
+
+/* Read command line arguments */
+
+  if (argc < 2)
+  { 
+    printf(" Syntax    : d2main [File] [Options]\n\n");
+    printf(" File      : This may be a local filename, or a URL (which is\n");
+    printf("             recognized by the \"http://\" prefix). When a URL,\n");
+    printf("             the remote file will be fetched using the builtin\n");
+    printf("             HTTP \"client\".\n\n");
+    printf(" Options   : -l                       : Format = line format\n");
+    printf("                                        (Default is ISO 2709)\n");
+    printf(" Options   : -f [filename]            : Outputfile\n");
+    printf("                                        (Default is stdout)\n");
+    printf(" Options   : -m [marcformat]          : Marcformat (upper case)\n");
+    printf("                                        (Default is NORMARC)\n");
+    printf(" Options   : -t [filename]            : Tracefile\n");
+    printf("                                        (Default is no trace)\n\n");
+    printf(" Return    : 0 : OK\n");
+    printf("             1 : Syntax error\n");
+    printf("             2 : Illegal MARC format\n");
+    printf("             3 : Cannot open output file\n");
+    printf("             8 : Error in MARC conversion\n");
+    printf("             9 : URL not found\n");
+    exit(1);
+  }
+
+  strcpy(dfilename, argv[1]);
+
+
+/* Get options                                                         */
+
+  if (argc > 2)
+  {
+    argtype = OPT_NONE;
+
+    for (i = 1; i < argc; i++)
+    {
+      if (argtype == OPT_FILE)
+      {
+        strcpy(ofilename, argv[i]);
+        argtype = OPT_NONE;
+      }
+      else if (argtype == OPT_TRAC)
+      {
+        strcpy(tfilename, argv[i]);
+        tracing = TRUE;
+        argtype = OPT_NONE;
+      }
+      else if (argtype == OPT_MARC)
+      {
+        strcpy(marcformat, argv[i]);
+        argtype = OPT_NONE;
+      }
+      else if (strcmp(argv[i], "-l") == 0)
+      {
+        f2709 = FALSE;
+        argtype = OPT_NONE;
+      }
+      else if (strcmp(argv[i], "-m") == 0)
+         argtype = OPT_MARC;
+      else if (strcmp(argv[i], "-f") == 0)
+         argtype = OPT_FILE;
+      else if (strcmp(argv[i], "-t") == 0)
+         argtype = OPT_TRAC;
+      else
+        argtype = OPT_NONE;
+    }
+  }
+
+/* Verify MARC format */
+
+  if (strncasecmp(marcformat, "US", 2) == 0)
+    mformat = USMARC;
+  else if (strncasecmp(marcformat, "DAN", 3) == 0)
+    mformat = DANMARC;
+  else if (strncasecmp(marcformat, "FIN", 3) == 0)
+    mformat = FINMARC;
+  else if (strncasecmp(marcformat, "IS", 2) == 0)
+    mformat = ISMARC;
+  else if (strncasecmp(marcformat, "NOR", 3) == 0)
+    mformat = NORMARC;
+  else if (strncasecmp(marcformat, "SWE", 3) == 0)
+    mformat = SWEMARC;
+  else 
+    exit(2);
+
+
+/* Fetch URL if remote file  */
+
+  if (strncmp(dfilename, "http://", 7) == 0)
+  {
+    strcpy(url, dfilename);
+    sprintf(dfilename, "%s.%d", D2M_TEMPFILE, getpid());
+    rc = wfetch(&url, &dfilename);
+    if (!rc)
+    {
+      remove(dfilename);
+      exit(9);
+    }
+    if (rc == 2)
+      marcfile = 1;
+  }
+
+
+
+
+/* Convert into buffer (line format)  */
+
+  if (marcfile)
+    rc = M2Mconv((char *) dfilename, (char *) buffer,
+                (char *) tfilename, mformat);
+  else
+    rc = D2Mconv((char *) dfilename, (char *) buffer,
+                (char *) tfilename, (char *) url, mformat);
+
+  if (!rc)
+  {
+    fclose(df);
+    exit(8);
+  }
+
+  if (*url)
+    remove(dfilename);
+
+
+/* Prepare for output      */
+
+   if (*ofilename)
+   {
+     of = fopen(ofilename, "w");
+     if (!of)
+       exit(3);
+   }
+   else
+     of = stdout;
+
+
+/* Write output, if necessary converted to ISO 27809   */
+
+   if (f2709)
+     fprintf(of, "%s", 
+       (char *) MARC2709(buffer, 0, 6, recstatus, rectype, biblevel));
+
+   else
+   {
+     l = strtok((char *) buffer, "\n");
+
+     while (l)
+     {
+       write_field(l, of, linel, llen, slen);
+       l = strtok(NULL, "\n");
+     }
+   }
+
+   if (*ofilename)
+     fclose(of);
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/M2Mconv.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/M2Mconv.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/M2Mconv.c	(revision 16561)
@@ -0,0 +1,84 @@
+/* ------------------------------------------------------------------- */
+/* M2Mconv   : Convert an ISO 2709 record into MARC format             */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+
+
+
+int M2Mconv(char *dfile, char *buffer, char *tfile, int format)
+{
+  char mtype[16], line[50000];
+  char *l, *p, *bf;
+  int lines;
+  FILE *df, *tf;
+
+  tf = (FILE *) NULL;
+  strcpy(mtype, "bibsys");
+
+
+/* Open inputfile */
+
+  df = fopen(dfile, "r");
+  if (!df)
+    return 0;                            /* Error: Unable to read data */
+
+
+
+/* Open tracefile */
+
+   if (*tfile)
+   {
+     tf = fopen(tfile, "w");
+     if (!tf)
+     {
+       fclose(df);
+       return 0;                         /* Error: Unable to create trace */
+     }
+   }
+
+
+/* Reads the file */ 
+
+   lines = 0;
+
+   while (l = fgets(line, 49999, df))
+   {
+/*   Remove trailing lf or cr     */
+
+     while ( (l[strlen(l) - 1] == '\n') || (l[strlen(l) - 1] == '\r') )
+       l[strlen(l) -1] = '\0';
+
+     lines++;
+     if (lines > 1)
+       break;                            /* Error: Not a 2709 file       */
+     p = l;
+   }
+
+
+/* Close files */ 
+
+  fclose(df);
+
+  if (tf)
+    fclose(tf);
+
+  if ( lines > 1 )
+    return 0;
+
+
+/* Convert to line format */
+
+  bf = (char *) marc2line(p, mtype);
+
+  strcpy(buffer, bf);
+
+  return 1;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/MARC2709.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/MARC2709.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/MARC2709.c	(revision 16561)
@@ -0,0 +1,495 @@
+/* ------------------------------------------------------------------- */
+/* MARC2709  : Converts a "line format MARC record" into ISO 2709      */
+/*                                                                     */
+/*             Input string requirements:                              */
+/*             1. New MARC tag is indicated by MARC tag in pos 1-3     */
+/*                (may be preceded by *). Indicator follows in next 2  */
+/*                bytes.                                               */
+/*             2. Continuation line is indicated by blank in col 1     */
+/*             3. End-of-record (^) may or may not be present          */
+/*                                                                     */
+/* author    : Ole Husby                                               */
+/* updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define NORMARC                                12
+
+#define SUBFIELD_CODE                          "\x1f"
+#define FIELD_TERMINATOR                       "\x1e"
+#define RECORD_TERMINATOR                      "\x1d"
+
+#define INDICATOR_COUNT                        '2'
+#define SUBFIELD_CODE_COUNT                    '2'
+#define LENGTH_OF_LENGTH_OF_FIELD              '4'
+#define LENGTH_OF_STARTING_CHARACTER_POSITION  '5'
+#define UNUSED                                 "   "
+#define TAG001                                 "001"
+#define TAG008                                 "008"
+#define SKIP                                     41
+
+static char out_record[10240],
+	      leader[24+1],
+	      directory[10240],
+	      field_001[32], 
+	      field_008[40+2],
+	      data_field[10240];
+
+
+static char logline[128];
+static char rstr[6], dollar, dollarstring[2];
+static int skip;
+
+static char direlem[300][12+1];
+static int dircntr;
+
+union LDR   {
+   char ldr_string[24+1];
+   struct {
+      char record_length[5],
+      record_status,
+      record_type,
+      bibliographic_level,
+      unused1[2],
+      indicator_count,
+      subfield_code_count,
+      base_adress[5],
+      unused2[3],
+      entry1,
+      entry2,
+      entry3,
+      entry4;
+   } ltab;
+ } ldr;
+
+static int startet = 0;
+
+
+
+/* ------------------------------------------------------------------- */
+/* Compare function for qsort                                          */
+/* ------------------------------------------------------------------- */
+
+int cmp(const void *a, const void *b)
+{
+   return strcmp((char*)a, (char*)b);
+}
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Convert integer to string of fixed length. Right justified padded   */
+/* with zeros. Returns NULL if error                                   */
+/* ------------------------------------------------------------------- */
+
+char *itoal(int n, int len)
+{
+   char *r;
+   r = (char *) rstr;
+
+   switch (len)
+   {
+      case  4 : sprintf(r, "%04d", n); break;
+      case  5 : sprintf(r, "%05d", n); break;
+      default : return NULL;                       
+   }
+
+   r[len] = '\0';
+
+   return r;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* One 001 subfield is put into field_001                              */
+/* ------------------------------------------------------------------- */
+
+static int make_001(char *data, char subfield)
+{
+   int len;
+
+   startet = 0;
+   switch (subfield)
+   {
+      case ' ' : break;
+      case 'a' : break;
+      default  : return 1;
+   }
+
+   while (data[0] == ' ') data++;
+   strcpy(field_001, data);
+
+   len = strlen(data) + 1;
+   skip = SKIP + len;
+
+   sprintf(directory, "00100%2d000000080041000%2d", len, len);
+   return 1;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* One subfield string is appended to data_field                       */
+/* ------------------------------------------------------------------- */
+
+static int out_subfield(char *data, char *ftag, char subfield, char nextsubfield)
+{
+   int len, subint;
+   char subreplace, *p, *up;
+
+   while (data[strlen(data) -1] == ' ') 
+          data[strlen(data) -1] = '\0';            /* strip trailing blanks */
+
+   if (subfield == '3')                            /* always discard $3     */
+      return 1;
+
+   if (strcmp(ftag, TAG001) == 0)                  /* 001 data treatment     */
+   {
+      make_001(data, subfield);
+      return 1;
+   }
+   
+   if (strcmp(ftag, TAG008) == 0)                  /* 008 data treatment     */
+   {
+      strncpy(field_008, data, strlen(data)); 
+      return 1;
+   }
+   
+   if (subfield != ' ')                             /* handle subfield code  */
+   {
+      strcat(data_field, SUBFIELD_CODE);
+      len = strlen(data_field);
+      data_field[len] = subfield;
+      data_field[len + 1] = '\0';
+   }
+
+   while(data[0] == ' ') data++;                   /* strip leading blanks   */
+   if (!data) return 0;
+
+   strcat(data_field, data);                       /* append data            */
+   
+   return 1;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* One MARC field is split into subfields and given to output          */
+/* ------------------------------------------------------------------- */
+
+static int out_field(char *data, char *ftag, char *find)
+{
+   char lnumber[5], snumber[6];
+   int fStart, fLen, moresubs;
+   char subfield[5120], *sub, subcode, *nextsub, nextsubcode;
+   char *v, subfield_v[32];
+
+   if (!startet) return 1;
+
+   if ((strncmp(ftag, "00", 2) == 0) &&
+       (strcmp(ftag, TAG001) != 0) &&
+       (strcmp(ftag, TAG008) != 0))
+      return 1;
+
+   fStart = strlen(data_field);
+   
+   if (strncmp(data, "  $", 3) == 0) data +=2;   /* possible if 008         */
+
+   if (data[0] != dollar)                        /* Dollar sign missing     */
+   {
+      if (strncmp(ftag,"00",2) != 0) return 0;   /* only allowed if tag=00x */
+	 
+      if (strcmp(ftag, TAG001) == 0)             /* 001: skip leading blanks */
+         while (data[0] == ' ') 
+            data++;  
+
+      subcode = ' ';
+      nextsubcode = ' ';
+
+      if (!out_subfield(data, ftag, subcode, nextsubcode)) 
+	 return 0;
+      return 1;
+   }
+
+   else
+   {
+      data++;
+
+      if (!data) return 0;
+
+      if (strncmp(ftag,"00",2) != 0)             /* append indicators       */
+         strcat(data_field, find);
+      
+      sub = strtok(data, dollarstring);          /* first subfield          */
+      if (!sub)
+         return 0;
+
+/*    printf("--- %s\n", sub); */
+
+      subcode = sub[0];
+      sub++;
+
+      moresubs = 1;
+
+      while (moresubs)                           /* loop while more subflds */
+      {  
+         nextsub = strtok(NULL, dollarstring);   /* next subfld             */
+         if (nextsub) 
+         {
+/*          printf("--- %s\n", nextsub); */
+	    nextsubcode = nextsub[0];
+	    nextsub++;
+         }
+         else
+            nextsubcode = ' ';
+
+         if (sub)
+         {
+	    if (!out_subfield(sub, ftag, subcode, nextsubcode)) 
+	       return 0;
+/*          printf("Denne |%c| Neste |%c|\n", subcode, nextsubcode);  */
+         }
+
+         if (nextsub)
+         {
+            subcode = nextsubcode;
+            sub = nextsub;
+         }
+         else
+            moresubs = 0;
+      }
+   }
+
+   if (strcmp(ftag,TAG001) == 0 || strcmp(ftag,TAG008) == 0)              
+      return 1;                                  /* directory ready         */
+
+   strcat(data_field, FIELD_TERMINATOR);         /* terminate data field   */
+   fLen = strlen(data_field) - fStart;           /* length of field        */
+
+   strcpy(lnumber,itoal(fLen,4));                /* convert to 4digit str  */
+   if (!lnumber) return 0;
+   strcpy(snumber,itoal(fStart + skip,5));       /* convert start          */
+   if (!snumber) return 0;
+
+   strcpy(direlem[dircntr], ftag);               /* start direlem with tag */
+   strcat(direlem[dircntr], lnumber);            /* append length to dir   */
+   strcat(direlem[dircntr], snumber);            /* append start to dir    */
+   dircntr++;
+
+   return 1;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Main program                                                        */
+/* ------------------------------------------------------------------- */
+
+char *MARC2709(char *record, int last, int first, char recstatus, 
+  char rectype, char biblevel)          
+{
+   static char buffer[10240], field_data[5120], tag[4], ind[3];
+   char        alen[3], *cp, *buf = buffer, *cq, logline[128];
+   int                  prelen, ii; 
+   int                  lastchar, Last, First, x, more;
+   int			SOF, digits, newField;
+
+   char test[6];
+
+   strcpy(buf, record);
+
+   Last = First = 0;                        /* initialize             */
+   if (last > 0)                            /* Last : linelength if   */
+   {                                        /*        fixed           */
+      Last = last;                          /* First: starting col    */
+      First = first;                        /*        for cont. line  */
+   }
+  
+   prelen = 5;
+   dollar = '$';
+   strcpy(dollarstring, "$");
+
+   data_field[0] = '\0';
+   directory[0]  = '\0';
+   strcpy(field_001, "         ");
+   strcpy(field_008, "                                        ");
+   out_record[0] = '\0';
+   dircntr       = 0;
+   startet       = 0;
+
+   /* initialize directory  with 9 bytes for 001 */
+
+   sprintf(directory, "001001000000008004100010", alen, alen);
+   skip = SKIP + 10;
+
+   more = 1; 
+   while (more)                                       /* loop until EOR  */ 
+   {
+      x = 0;                                          /* x counts bytes  */
+
+      for (cp = buf; *cp && *cp != '\n'; cp++) x++;   /* read until LF   */
+
+      if (!*cp)                                       /* EOR             */
+        more = 0; 
+      else
+        cp++;
+
+      SOF = 0;
+
+      if (buf[0] == '^') break;                       /* "EOR-line"      */
+      if (buf[0] == '*')                              /* skip leading *  */
+      {
+	 buf++;
+	 x--;
+         SOF = 1;
+      }
+
+      if (x == 0) continue;                           /* empty line     */
+
+      digits = 1;                                     /* test if first 3     */ 
+      for (ii = 0; ii < 3; ii++)                      /* bytes are digits    */
+         if (!isdigit(buf[ii]))
+         {
+            digits = 0;
+            break;
+         }
+         
+      newField = 0;     
+
+      if (SOF)
+      {
+	if (!digits) continue;                      /* illegal line        */
+        else newField = 1;
+      } 
+      else
+	 if (digits) newField = 1;
+
+
+      if (newField)                                  /* start of new field   */
+      {
+	 if (!out_field(field_data, tag, ind))       /* output prev. field   */
+	    return NULL;
+
+	 startet = 1;
+	 field_data[0] = '\0';                       /* reset field buffer   */
+	 strncpy(tag, buf, 3);                       /* keep MARC tag        */
+	 buf += 3;
+	 x -=3;
+     
+         if (strncmp(tag, "00", 2) != 0) 
+         {
+            strncpy(ind, buf, 2);                 /* keep indicator       */
+            buf += 2;                             /* if not control field */
+            x -= 2;
+         }
+
+	 strncat(field_data, buf, x);             /* keep data               */
+        
+	   					/* strip trailing blanks   */
+	 while (field_data[strlen(field_data) -1] == ' ')
+	 {
+	    field_data[strlen(field_data) -1] = '\0';
+	    x--;
+	 }
+
+	 if (Last && x == (Last - prelen))           /* full line            */
+            lastchar = 1;
+         else 
+            lastchar = 0;
+      }
+
+      else                                     /* continuation line     */
+      {
+	if (Last)
+	{
+	   cq  = buf + First - 1;              /* skip fixed nmbr chars */
+	   x -= (First - 1); 
+	   if (!lastchar && cq[0] != ' '       /* and insert one blank  */
+	       && cq[0] != dollar)
+	      strcat(field_data, " ");         /* if needed             */
+	}
+	else
+	{
+	   for (cq = buf; *cq == ' '; cq++) x--;  /* skip leading blanks   */
+	   strcat(field_data, " ");               /* insert one blank      */
+	}
+
+	strncat(field_data, cq, x);            /* append data           */
+                                               /* strip trail. blanks   */
+        while (field_data[strlen(field_data) -1] == ' ')
+        {
+	   field_data[strlen(field_data) -1] = '\0';
+	   x--;
+	}
+
+	if (Last && x == (Last - 5))           /* full line            */
+	   lastchar = 1;
+        else lastchar = 0;
+        
+      }
+
+      while (field_data[strlen(field_data) -1] == ' ')
+         field_data[strlen(field_data) - 1] = '\0'; /* rm trailing blanks */
+      buf = cp;
+   }
+
+   if (!out_field(field_data, tag, ind))       /* output last field     */
+      return NULL;
+
+                                               /* terminate field_001   */
+   strcat(field_001, FIELD_TERMINATOR); 
+
+   field_008[40] = '\0';                       /* terminate field_008   */
+   strcat(field_008, FIELD_TERMINATOR); 
+
+					       /* sort directory elem.  */
+   qsort ((void *) direlem, dircntr, sizeof(direlem[0]), cmp); 
+   for (ii = 0; ii < dircntr; ii++)
+      strcat(directory, direlem[ii]);          /* append to directory   */
+
+   strcat(directory, FIELD_TERMINATOR);        /* terminate dictionary  */ 
+   
+   strcat(data_field, RECORD_TERMINATOR);      /* terminate data        */
+
+   x = 24 + strlen(directory);                 /* construct leader      */
+   
+   strcpy(test, itoal(x,5));
+
+   strncpy(ldr.ltab.base_adress, test, 5);
+
+   x += (strlen(data_field)
+       + strlen(field_001)
+       + strlen(field_008));
+
+   strcpy(test, itoal(x,5));
+   
+   strncpy(ldr.ltab.record_length, test, 5);
+   
+   ldr.ltab.record_status = recstatus;
+   ldr.ltab.record_type = rectype;
+   ldr.ltab.bibliographic_level = biblevel;
+   ldr.ltab.indicator_count = INDICATOR_COUNT;
+   ldr.ltab.subfield_code_count = SUBFIELD_CODE_COUNT;
+   strncpy(ldr.ltab.unused1,UNUSED,2);
+   strncpy(ldr.ltab.unused2,UNUSED,3);
+   ldr.ltab.entry1 = LENGTH_OF_LENGTH_OF_FIELD;
+   ldr.ltab.entry2 = LENGTH_OF_STARTING_CHARACTER_POSITION;
+   ldr.ltab.entry3 = '0';     
+   ldr.ltab.entry4 = '0';     
+
+   ldr.ldr_string[24] = '\0';
+
+   strcpy(out_record, ldr.ldr_string);          /* put together record  */
+   strcat(out_record, directory);
+   strcat(out_record, field_001);
+   strcat(out_record, field_008);
+   strcat(out_record, data_field);
+ 
+   return out_record; 
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/MARCmake.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/MARCmake.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/MARCmake.c	(revision 16561)
@@ -0,0 +1,33 @@
+/* ------------------------------------------------------------------- */
+/* MARCmake  : Convert a metatag struct to MARC and add it to the      */
+/*             marcrecord struct.                                      */
+/*                                                                     */
+/*             This is where the format-dependent conversion           */
+/*             program is selected.                                    */
+/*                                                                     */
+/* Author    : Ole Husby                                               */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include "d2m.h" 
+
+
+
+int MARCmake(struct metatag *mt, struct marcrec *mr, int format)
+{
+  char pre[64], creator_tag[4];
+
+  if (format == DANMARC)
+    return danMARC(mt, mr);
+  else if (format == FINMARC)
+    return finMARC(mt, mr);
+  else if (format == ISMARC)
+    return isMARC(mt, mr);
+  else if (format == USMARC)
+    return usMARC(mt, mr);
+  else if (format == SWEMARC)
+    return sweMARC(mt, mr);
+  else
+    return norMARC(mt, mr);
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/MARCtidy.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/MARCtidy.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/MARCtidy.c	(revision 16561)
@@ -0,0 +1,147 @@
+/* ------------------------------------------------------------------- */
+/* MARCtidy  : Tidying up of MARC record, building the line format     */
+/*             record.                                                 */
+/*                                                                     */
+/* Syntax    : MARCtidy(marcrec *mrec, char *buf, int format)          */
+/*                                                                     */
+/*             The preliminary MARC data are converted from the        */
+/*             mrec struct to the buf line format buffer.              */
+/*                                                                     */
+/*             The main data is in mrec->marcline, while some          */
+/*             additional data (year, URL ...) are kept in separat     */
+/*             variables to be added in.                               */
+/*                                                                     */
+/* NOTE      : The behaviour of this program i sto some extent         */
+/*             depending on the format argument!                       */
+/*                                                                     */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "d2m.h"
+
+
+
+void  nline(char *p, char *l, char *buf)
+{
+   strcat(buf, p);
+   if (strncmp(p, "00", 2) != 0)
+     strcat(buf, " ");
+   strcat(buf, l);
+   strcat(buf, "\n");
+}
+
+
+
+void MARCtidy(struct marcrec *mrec, char *buf, int format)
+{
+
+   char *p, *u, *l;
+   char pre[64], line[10000];
+
+   p = (char *) &pre;
+   l = (char *) &line;
+
+   *buf = 0;
+
+   strcpy(p, "008");
+   strcpy(l, mrec->s008);
+   nline(p, l, buf);
+
+   u = strtok(mrec->marcline, "\n");
+   while (u && *u)
+   {
+      if (strlen(u) > 7)
+      {
+        *p = 0;
+        strncat(p, u, 7);
+        strcpy(l, u + 8);
+        nline(p, l, buf);
+
+
+/*      Add 245 subfields after $a */
+
+        if ( (strncmp(p, "245", 3) == 0) && (strncmp(p + 5, "$a", 2) == 0) )
+        {
+          if ( ( format == NORMARC ) && ( *mrec->subtitle) )
+          {
+            strcpy(p, "     $b");
+            strcpy(l, mrec->subtitle);
+            nline(p, l, buf);
+          }
+          if ( *mrec->partitle )
+          {
+            if ( format == FINMARC || format == DANMARC ) 
+            {
+              strcpy(p, "    ");
+              strcpy(l, mrec->partitle);
+              nline(p, l, buf);
+            }
+            if ( format == ISMARC ) 
+            {
+              strcpy(l, mrec->partitle);
+              nline(p, l, buf);
+            }
+          }
+        } 
+
+/*      Add year as 260 $c after $b                                    */
+
+        if ( (strncmp(p, "260", 3) == 0) && (strncmp(p + 5, "$b", 2) == 0) )
+        {
+          if (*mrec->year)
+          {
+            strcpy(p, "     $c");
+            strcpy(l, mrec->year);
+            nline(p, l, buf);
+            *mrec->year = 0;
+          }
+        } 
+      }
+      u = strtok(NULL, "\n");
+   }
+
+/* Add year as 260 $c if not aleady done                               */
+
+   if (*mrec->year)
+   {
+     strcpy(p, "260  $c");
+     strcpy(l, mrec->year);
+     nline(p, l, buf);
+   }
+
+/* Add parallel title in spearate field  */
+
+   if ( *mrec->partitle && ( format == ISMARC ) ) 
+   {
+     strcpy(p, "24630$c");
+     strcpy(l, mrec->partitle);
+     nline(p, l, buf);
+   }
+
+
+/* Add URL as 856 $u                                                 */
+
+   if (*mrec->url)
+   {
+     strcpy(p, "856  $u");
+     strcpy(l, mrec->url);
+     nline(p, l, buf);
+
+/* Add format as $q                                                    */
+
+     if (*mrec->fmat)
+     {
+       strcpy(p, "     $q");
+       strcpy(l, mrec->fmat);
+       nline(p, l, buf);
+     }
+   }
+
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/Makefile.in	(revision 16561)
@@ -0,0 +1,45 @@
+# ///////////////// d2m : Dublin Core to MARC converter /////////////////////
+#
+#
+# Modified for Greenstone kjdon May 2005.
+# Used by z3950server
+
+CC = @CC@
+AR = ar
+RANLIB = @RANLIB@
+
+o = .@U@o
+
+# This may have to uncomment this on solaris ???
+# LIBS = -lsocket -lnsl
+
+LIB_OBJS = MARC2709.o MARCtidy.o usMARC.o util.o
+
+all: libd2m.a
+
+libd2m.a: $(LIB_OBJS)
+	rm -f libd2m.a
+	$(AR) cru libd2m.a $(LIB_OBJS)
+	$(RANLIB) libd2m.a
+
+
+MARCtidy.o: MARCtidy.c d2m.h
+	$(CC) -c MARCtidy.c
+
+MARC2709.o: MARC2709.c d2m.h
+	$(CC) -c MARC2709.c
+
+util.o: util.c d2m.h
+	$(CC) -c util.c 
+
+usMARC.o: usMARC.c d2m.h
+	$(CC) -c usMARC.c
+
+install:
+
+clean:
+	rm -f *.o libd2m.a
+
+distclean: clean
+	rm -f Makefile config.h
+
Index: /gsdl/trunk/gs2runtime/packages/d2m/Makefile.orig
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/Makefile.orig	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/Makefile.orig	(revision 16561)
@@ -0,0 +1,111 @@
+# ///////////////// d2m : Dublin Core to MARC converter /////////////////////
+#
+# The basic conversion (reading a file and producing a MARC record)
+# is performed by D2Mconv.c
+#
+# On top of D2Mconv.c is built 2 applications:
+#
+# d2m   : A CGI version
+#         (See documentation in main program D2Mcgi.c)
+# d2main: A command line (standalone version)
+#         (See documentation in main program D2Mmain.c)
+#
+# Both applications are capable of reading from a local file or a
+# remote file using builtin HTTP client services. 
+#
+# (You can compile a version of d2main without HTTP ability by
+#   make d2main-nosock 
+# )
+#
+# Both applications has the added ability to read a 2709 MARC record
+# instead of DC metadata, using M2Mconv.c to perform the conversion.
+# (This is not a MARC converter with the ability to convert between
+# MARC formats, just between "presentation syntaxes".
+#
+# 1998-10-15 Ole Husby
+#
+
+CC = cc 
+
+# This may have to uncomment this on solaris:
+# LIBS = -lsocket -lnsl
+
+OBJ = D2Mconv.o M2Mconv.o marc2line.o util.o MARC2709.o MARCmake.o MARCtidy.o dc_parse.o
+OBJ_MARC = danMARC.o finMARC.o isMARC.o norMARC.o sweMARC.o usMARC.o
+OBJ_SOCK = wfetch.o htget.o
+OBJ_NOSOCK = wfetch-l.o
+
+all: d2m d2main
+
+d2m: D2Mcgi.o $(OBJ) $(OBJ_MARC) $(OBJ_SOCK) 
+	@echo "------------------- Compiling d2m -------------------------------" 
+	$(CC) D2Mcgi.o $(OBJ) $(OBJ_MARC) $(OBJ_SOCK) -o d2m $(LIBS) 
+
+d2main: D2Mmain.o $(OBJ) $(OBJ_MARC) $(OBJ_SOCK) 
+	@echo "------------------- Compiling d2main ----------------------------" 
+	$(CC) D2Mmain.o $(OBJ) $(OBJ_MARC) $(OBJ_SOCK) -o d2main $(LIBS) 
+
+d2main-nosock: D2Mmain.o $(OBJ) $(OBJ_MARC) $(OBJ_NOSOCK) 
+	@echo "------------------- Compiling d2main (no sockets) ---------------" 
+	$(CC) D2Mmain.o $(OBJ) $(OBJ_MARC) $(OBJ_NOSOCK) -o d2main $(LIBS) 
+
+
+D2Mcgi.o: D2Mcgi.c d2m.h
+	$(CC) -c D2Mcgi.c
+
+D2Mmain.o: D2Mmain.c d2m.h
+	$(CC) -c D2Mmain.c
+
+D2Mconv.o: D2Mconv.c d2m.h
+	$(CC) -c D2Mconv.c
+
+M2Mconv.o: M2Mconv.c
+	$(CC) -c M2Mconv.c
+
+marc2line.o: marc2line.c 
+	$(CC) -c marc2line.c
+
+MARCmake.o: MARCmake.c d2m.h
+	$(CC) -c MARCmake.c
+
+MARCtidy.o: MARCtidy.c d2m.h
+	$(CC) -c MARCtidy.c
+
+MARC2709.o: MARC2709.c d2m.h
+	$(CC) -c MARC2709.c
+
+dc_parse.o: dc_parse.c d2m.h
+	$(CC) -c dc_parse.c
+
+util.o: util.c d2m.h
+	$(CC) -c util.c 
+
+danMARC.o: danMARC.c d2m.h
+	$(CC) -c danMARC.c
+
+finMARC.o: finMARC.c d2m.h
+	$(CC) -c finMARC.c
+
+isMARC.o: isMARC.c d2m.h
+	$(CC) -c isMARC.c
+
+norMARC.o: norMARC.c d2m.h
+	$(CC) -c norMARC.c
+
+sweMARC.o: sweMARC.c d2m.h
+	$(CC) -c sweMARC.c
+
+usMARC.o: usMARC.c d2m.h
+	$(CC) -c usMARC.c
+
+wfetch.o: wfetch.c
+	$(CC) -c wfetch.c
+
+wfetch-l.o: wfetch-l.c
+	$(CC) -c wfetch-l.c
+
+htget.o: htget.c 
+	$(CC) -c htget.c 
+
+clean:
+	rm -f *.o d2m d2main
Index: /gsdl/trunk/gs2runtime/packages/d2m/README.html
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/README.html	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/README.html	(revision 16561)
@@ -0,0 +1,159 @@
+<html><head><title>
+d2m v0.2
+</title>
+</head>
+
+<body bgcolor="#ffffcc">
+
+<font face="Comic Sans MS">
+<h1 align=center>d2m : A Dublin Core to MARC converter</h1>
+
+<div align=center>
+
+version 0.2<br>
+1998-10-14<br>
+Ole Husby<br>
+BIBSYS<br>
+ole.husby@bibsys.no
+</div>
+</font>
+
+
+<h2>1. Introduction</h2>
+
+This software is part of the results from the Nordic Metadata project.
+(See <a href="http://linnea.helsinki.fi/meta/">
+http://linnea.helsinki.fi/meta/</a>).
+
+<p>
+ 
+This version is a "pre-release". It is not really ready for distribution.
+All the customary disclaimers apply!
+The programs have been compiled on Solaris, AIX and Linux.
+
+
+
+<h2>2. Contents</h2>
+
+The distribution includes the following files:
+
+<table>
+<tr><td><b>README.html </b></td><td>This file</td></tr>
+<tr><td><b>Makefile    </b></td><td>Also includes some documentation</td></tr>
+<tr><td><b>d2m.h       </b></td><td>Common include file</td></tr>
+<tr><td><b>D2Mcgi.c    </b></td><td>Main program for the CGI version</td></tr>
+<tr><td><b>D2Mmain.c   </b></td><td>Main program for the standalone version</td></tr>
+<tr><td><b>D2Mconv.c   </b></td><td>Inputs file with DC metadata, outputs line MARC record</td></tr>
+<tr><td><b>M2Mconv.c   </b></td><td>Inputs file with ISO 2709 MARC record, outputs line MARC record</td></tr>
+<tr><td><b>MARC2709.c  </b></td><td>Converts from line MARC format to ISO 2709 MARC format</td></tr>
+<tr><td><b>MARCmake.c  </b></td><td>Builds a MARC struct from a DC struct. Selects one of the following :</td></tr>
+<tr><td><b>danMARC.c   </b></td><td>&nbsp;</td></tr>
+<tr><td><b>finMARC.c   </b></td><td>&nbsp;</td></tr>
+<tr><td><b>isMARC.c    </b></td><td>&nbsp;</td></tr>
+<tr><td><b>norMARC.c   </b></td><td>&nbsp;</td></tr>
+<tr><td><b>sweMARC.c   </b></td><td>&nbsp;</td></tr>
+<tr><td><b>usMARC.c    </b></td><td>&nbsp;</td></tr>
+<tr><td><b>MARCtidy.c  </b></td><td>Finishes the line MARC record from the MARC struct</td></tr>
+<tr><td><b>marc2line.c </b></td><td>Converts from ISO 2709 MARC format to line MARC format</td></tr>
+<tr><td><b>htget.c     </b></td><td>Fetches a remote file using HTTP</td></tr>
+<tr><td><b>dc_parse.c  </b></td><td>Parses an HTML <META> tag</td></tr>
+<tr><td><b>util.c      </b></td><td>Some utilities</td></tr>
+<tr><td><b>wfetch.c    </b></td><td>Small program on top of htget()</td></tr>
+<tr><td><b>wfetch-l.c  </b></td><td>Dummy version of wfetch.c, does nothing except return 0</td></tr>
+<tr><td><b>test.data   </b></td><td>A simple test file with a <meta> tag.</td></tr>
+</table>
+
+<h2>3. Compilation</h2>
+
+Unpack the d2m-v02.tar file. It will produce a directory <b>d2m-v02/</b>
+with the contents listed above.
+
+<p>
+
+Check the Makefile.
+
+<p>
+
+Type
+
+<pre>
+
+  <b>make all</b>
+
+</pre>
+
+This should produce the programs <b>d2m</b> and <b>d2main</b> . 
+
+<p>
+
+If you do not need access to remote files, or have problems compiling
+socket libraries, try to compile
+  
+<pre>
+
+  <b>make d2main-nosock</b>
+
+</pre>
+
+
+<h2>4. Use of d2main</h2>
+
+<b>d2main</b> should be ready to execute from the command line. No configuration
+or environment variables are needed.
+
+<p>
+
+If you do not enter any arguments, a short instruction will be printed.
+
+<p>
+
+The first test could be 
+
+<pre>
+
+  <b>d2main test.data -l</b>
+
+</pre>
+
+The next test could be (if d2main is not compiled using Makefile-l)
+
+<pre>
+
+  <b>d2main http://linnea.helsinki.fi/meta/ -l -m FINMARC</b>
+
+</pre>
+
+<h2>5. Use of d2m</h2>
+
+<b>d2m</b> should be used as a CGI program.
+
+<p>
+  
+A simple users guide is in D2Mcgi.c
+
+<p>
+
+See an example of use in <a href="http://www.bibsys.no/meta/d2m">
+http://www.bibsys.no/meta/d2m</a>
+
+
+
+<h2>6. Tailoring</h2>
+
+This version is not table-driven. The different MARC formats are
+treated by customized functions (norMARC.c, isMARC.c etc.). The
+selection of conversion program is performed by MARKmake.c .
+
+<p>
+
+The program MARCtidy.c is also to some degree depending of the
+MARC format. In addition there is a small sequence in D2Mconv.c
+with a special treatment for ISMARC. (It should really not be
+done there ...). Apart from this, all programs are independent on
+MARC format.
+
+<p>
+
+The supported formats are introduced in d2m.h .
+
+</body></html>
Index: /gsdl/trunk/gs2runtime/packages/d2m/README_GS
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/README_GS	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/README_GS	(revision 16561)
@@ -0,0 +1,14 @@
+d2m : Dublin Core to MARC converter
+Software distribution: Prerelease version
+version 0.2
+1998-10-14
+Ole Husby
+BIBSYS
+ole.husby@bibsys.no
+
+http://www.bibsys.no/meta/d2m/distr/
+
+The original Makefile that comes with d2m has been saved as Makefile.orig, and a new Makefile.in and some configure scripts have been generated for use with Greenstone.
+
+We no longer compile up the two executables, instead just compile the source into a library: libd2m.a.
+
Index: /gsdl/trunk/gs2runtime/packages/d2m/config.h.in
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/config.h.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/config.h.in	(revision 16561)
@@ -0,0 +1,107 @@
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the `alarm' function. */
+#undef HAVE_ALARM
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+   to 0 otherwise. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
Index: /gsdl/trunk/gs2runtime/packages/d2m/configure
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/configure	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/configure	(revision 16561)
@@ -0,0 +1,4903 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for FULL-PACKAGE-NAME VERSION.
+#
+# Report bugs to <BUG-REPORT-ADDRESS>.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='FULL-PACKAGE-NAME'
+PACKAGE_TARNAME='full-package-name'
+PACKAGE_VERSION='VERSION'
+PACKAGE_STRING='FULL-PACKAGE-NAME VERSION'
+PACKAGE_BUGREPORT='BUG-REPORT-ADDRESS'
+
+ac_unique_file="danMARC.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT RANLIB ac_ct_RANLIB CPP EGREP LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures FULL-PACKAGE-NAME VERSION to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of FULL-PACKAGE-NAME VERSION:";;
+   esac
+  cat <<\_ACEOF
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <BUG-REPORT-ADDRESS>.
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+FULL-PACKAGE-NAME configure VERSION
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by FULL-PACKAGE-NAME $as_me VERSION, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+          ac_config_headers="$ac_config_headers config.h"
+
+
+# Checks for programs.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+# Checks for libraries.
+
+# Checks for header files.
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+
+
+for ac_header in fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to BUG-REPORT-ADDRESS ##
+## --------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6
+if test "${ac_cv_c_const+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this.  */
+  typedef int charset[2];
+  const charset x;
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *ccp;
+  char **p;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* AIX XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  ccp = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++ccp;
+  p = (char**) ccp;
+  ccp = (char const *const *) p;
+  { /* SCO 3.2v4 cc rejects this.  */
+    char *t;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* AIX XL C 1.02.0.0 rejects this saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; };
+    struct s *b; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+  }
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_const=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_const=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+echo "${ECHO_T}$ac_cv_c_const" >&6
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking for size_t" >&5
+echo $ECHO_N "checking for size_t... $ECHO_C" >&6
+if test "${ac_cv_type_size_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((size_t *) 0)
+  return 0;
+if (sizeof (size_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_size_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_size_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+echo "${ECHO_T}$ac_cv_type_size_t" >&6
+if test $ac_cv_type_size_t = yes; then
+  :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned
+_ACEOF
+
+fi
+
+echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6
+if test "${ac_cv_struct_tm+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm *tp; tp->tm_sec;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_struct_tm=time.h
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+echo "${ECHO_T}$ac_cv_struct_tm" >&6
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+# Checks for library functions.
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to BUG-REPORT-ADDRESS ##
+## --------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5
+echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6
+if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_malloc_0_nonnull=no
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#if STDC_HEADERS || HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+int
+main ()
+{
+exit (malloc (0) ? 0 : 1);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_malloc_0_nonnull=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_malloc_0_nonnull=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5
+echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6
+if test $ac_cv_func_malloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 1
+_ACEOF
+
+else
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 0
+_ACEOF
+
+   case $LIBOBJS in
+    "malloc.$ac_objext"   | \
+  *" malloc.$ac_objext"   | \
+    "malloc.$ac_objext "* | \
+  *" malloc.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define malloc rpl_malloc
+_ACEOF
+
+fi
+
+
+
+echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6
+if test "${ac_cv_type_signal+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+# undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int
+main ()
+{
+int i;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_signal=void
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_signal=int
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+echo "${ECHO_T}$ac_cv_type_signal" >&6
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+
+for ac_func in strftime
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+  # strftime is in -lintl on SCO UNIX.
+echo "$as_me:$LINENO: checking for strftime in -lintl" >&5
+echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6
+if test "${ac_cv_lib_intl_strftime+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lintl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char strftime ();
+int
+main ()
+{
+strftime ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_intl_strftime=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_intl_strftime=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5
+echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6
+if test $ac_cv_lib_intl_strftime = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_STRFTIME 1
+_ACEOF
+
+LIBS="-lintl $LIBS"
+fi
+
+fi
+done
+
+
+
+
+
+
+
+
+
+for ac_func in alarm gethostbyname putenv socket strcasecmp strdup strncasecmp strstr
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+          ac_config_files="$ac_config_files Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by FULL-PACKAGE-NAME $as_me VERSION, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+FULL-PACKAGE-NAME config.status VERSION
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
Index: /gsdl/trunk/gs2runtime/packages/d2m/configure.in
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/configure.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/configure.in	(revision 16561)
@@ -0,0 +1,30 @@
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
+AC_CONFIG_SRCDIR([danMARC.c])
+AC_CONFIG_HEADER([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_TYPE_SIGNAL
+AC_FUNC_STRFTIME
+AC_CHECK_FUNCS([alarm gethostbyname putenv socket strcasecmp strdup strncasecmp strstr])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
Index: /gsdl/trunk/gs2runtime/packages/d2m/d2m.h
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/d2m.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/d2m.h	(revision 16561)
@@ -0,0 +1,76 @@
+#ifndef D2MH
+#define D2MH
+
+#include <stdio.h>
+
+#define  TRUE    1
+#define  FALSE   0
+
+#define  HTML_TITLE           "d2m: Dublin Core to MARC conversion"
+
+#define  D2M_LINELEN          80
+#define  D2M_TEMPFILE         "/tmp/d2m.data"
+#define  D2M_TRACEFILE        "/tmp/d2m.trace"
+#define  D2M_DEFAULT_SYNTAX   "plain"
+#define  D2M_DEFAULT_FORMAT   "NORMARC"        
+#define  D2M_DEFAULT_URL      "http://www.bibsys.no/meta/test.html"
+
+struct metatag {
+   char    *name;
+   char    *type;
+   char    *scheme;
+   char    *value;
+               };
+
+struct marcrec {
+   int     ntitles;
+   int     ncreators;
+   char    *marcline;
+   char    *partitle;
+   char    *subtitle;
+   char    *year;
+   char    *url;
+   char    *fmat;
+   char    *s008;
+               };
+
+enum mformats {
+   USMARC,
+   DANMARC,
+   FINMARC,
+   ISMARC,
+   NORMARC,
+   SWEMARC    };
+
+enum SOURCES { TEXTAREA, URL };
+
+enum fields_in_008 {
+   F008_DATE_ENTERED,
+   F008_TYPE_OF_DATE,
+   F008_DATE1,
+   F008_LANGUAGE,
+   F008_FORM_OF_PUBLICATION };
+              
+enum levels {
+  LNAME,
+  LTYPE,
+  LSCHEME,
+  LOCC,
+  LTAG,
+  LIND,
+  LSUBF,
+  LRULE
+            };
+
+extern void write_field(char *i, FILE *of, int l, int n, int s);
+extern int write_trace(char *n, FILE *of);
+extern struct metatag *dc_parse(char *b); 
+extern char *cstr(char *h, char *n);
+extern void unescape(char *n);
+extern char *poq();
+extern int  put008(char *n, char *s, int p);
+extern int  find_year(char *n);
+extern int  split_name(char *n);
+extern int  split_subj(char *n);
+
+#endif
Index: /gsdl/trunk/gs2runtime/packages/d2m/d2m4gs.h
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/d2m4gs.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/d2m4gs.h	(revision 16561)
@@ -0,0 +1,22 @@
+#ifndef D2M4GSH
+#define D2M4GSH
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "d2m.h"
+
+void MARCtidy(struct marcrec *mrec, char *buf, int format);
+void usMARC(struct metatag *mt, struct marcrec *mr);
+char *MARC2709(char *record, int last, int first, char recstatus, 
+	       char rectype, char biblevel);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /gsdl/trunk/gs2runtime/packages/d2m/danMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/danMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/danMARC.c	(revision 16561)
@@ -0,0 +1,239 @@
+/* ------------------------------------------------------------------- */
+/* danMARC    : Convert a metatag struct to DANMARC2                   */
+/*                                                                     */
+/* Specs      : http://www.bibsys.no/meta/d2m/dancross.html            */
+/*                                                                     */
+/* Author     : Ole Husby                                              */
+/* Last update: 1998-09-30                                             */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+
+/* Splits subject heading on ":". First occurence go into $a, second into $v */ 
+
+int split_dk5(char *str)
+{
+  char *p, *q, subtag[5];
+
+  if (!strstr(str, ":"))
+    return 1;
+
+  p = (char *) malloc(strlen(str) + 50);
+  *p = 0;
+  *subtag = 0;
+
+  q = strtok(str, ":");
+  if (q)
+  {
+    while ( ( q[0] == ' ' ) && ( strlen(q) ) )
+      q++;
+
+    while( ( q[strlen(q) - 1] == ' ' ) && ( strlen(q) ) )
+      q[strlen(q) - 1] = '\0';
+
+    if (*q)
+    {
+      strcat(p, subtag);
+      strcat(p, q);
+      strcpy(subtag, " $v ");
+    }
+  }
+
+  strcpy(str, p);
+  free(p);
+
+  return 1;
+}
+
+
+
+
+int danMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], type_tag[4];
+  int i;
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+      {
+        strcat(mr->partitle, "$a ");
+        strcat(mr->partitle, mt->value);
+
+      }
+      else
+      {
+        mr->ntitles++;
+        strcpy(pre, "245  $a");
+      }
+    }
+  }
+
+  else if (strcasecmp(mt->name, "creator") == 0)
+  {
+    if ( strcasecmp(mt->type, "personal") == 0) 
+    {
+      strcpy(pre, "700  $a");
+      split_name(mt->value);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+
+  else if (strcasecmp(mt->name, "publisher") == 0)
+    strcpy(pre, "260  $b");
+
+  else if (strcasecmp(mt->name, "description") == 0)
+    strcpy(pre, "504  $a");
+
+  else if (strncasecmp(mt->name, "contributor", 11) == 0)   
+  {
+    if ( strcasecmp(mt->type, "personal") == 0 )
+    {
+      strcpy(pre, "700  $a");
+      split_name(mt->value);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "559  $a");
+    else if (strcasecmp(mt->type , "spatial") == 0)
+    {
+      strcpy(pre, "633  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->type,  "temporal") == 0)
+    {
+      strcpy(pre, "334  $b");
+      for (i = 0; i < strlen(mt->value); i++)
+      {
+        if (!isdigit(mt->value[i]))
+        {
+          strcpy(pre, "334  $a");
+        }
+      } 
+    }
+  }
+
+  else if (strcasecmp(mt->name, "source") == 0)
+    strcpy(pre, "523  $a");
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "856  $3 rights $u");
+    else
+      strcpy(pre, "518  $a");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $g urn:");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "021  $a");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "022  $a");
+    else if (strcasecmp(mt->scheme, "isrc") == 0)
+      strcpy(pre, "024  $a");
+    else if (strcasecmp(mt->scheme, "ismn") == 0)
+      strcpy(pre, "028  $a");
+    else if (strcasecmp(mt->scheme, "isrn") == 0)
+      strcpy(pre, "027  $a");
+  }
+
+  else if (strcasecmp(mt->name, "type") == 0)
+    strcpy(pre, "505  $a");
+
+  else if (strcasecmp(mt->name, "relation") == 0)
+    strcpy(pre, "559  $a");
+
+  else if (strcasecmp(mt->name, "language") == 0)  /* Only if repeated ! */
+  {
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+    {
+      strcpy(pre, "041  $a");
+      put008(mr->s008, mt->value, F008_LANGUAGE);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+  {
+    if ( (!*mt->scheme)
+      || (strcasecmp(mt->scheme, "imt") == 0)
+      || (strcasecmp(mt->scheme, "mime") == 0) )
+      strcpy(mr->fmat, mt->value); 
+  }
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "631  $a");
+    else if (strcasecmp(mt->scheme, "udc") == 0)
+      strcpy(pre, "080  $a");
+    else if (strcasecmp(mt->scheme, "lccs") == 0)
+      strcpy(pre, "050  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+    else if (strcasecmp(mt->scheme, "nlm") == 0)
+      strcpy(pre, "060  $a");
+    else if (strcasecmp(mt->scheme, "agr") == 0)
+      strcpy(pre, "662  $a");
+    else if (strcasecmp(mt->scheme, "eudised") == 0)
+      strcpy(pre, "661  $b");
+    else if (strcasecmp(mt->scheme, "dbc") == 0)
+      strcpy(pre, "666  $f");
+    else if (strcasecmp(mt->scheme, "mesh") == 0)
+      strcpy(pre, "690  $a");
+    else if (strcasecmp(mt->scheme, "dk5") == 0)
+    {
+      strcpy(pre, "652  $m");
+      split_dk5(mt->value);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if ( (!*mt->type) || ( strcasecmp(mt->type, "current") == 0) )
+    {
+      if ( (strncasecmp(mt->scheme, "ans", 3) == 0) 
+         || (strncasecmp(mt->scheme, "iso", 3) == 0) )
+      {
+        if (find_year(mt->value))
+        {
+          strcpy(mr->year, mt->value);
+          put008(mr->s008, mt->value, F008_DATE1);
+        }
+      }
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/dc_parse.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/dc_parse.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/dc_parse.c	(revision 16561)
@@ -0,0 +1,216 @@
+/* ------------------------------------------------------------------- */
+/* dc_parse   : parse the meta tag, build a metatag struct             */
+/* Author     : Ole Husby                                              */
+/* Updated    : 1998-09-30                                             */
+/*                                                                     */
+/* Syntax     : #include "d2m.h"                                       */
+/*              dc_parse(char * string)                                */
+/*                                                                     */
+/*              Returns pointer to struct metatag                      */
+/*                                                                     */
+/*              Memory for the struct is allocated in the program!     */
+/*                                                                     */
+/*              String includes a meta tag, after the initial          */
+/*              "<meta " and the ending ">" are stripped away.         */
+/*                                                                     */
+/*              The program accepts the tag if name starts with "dc."  */
+/*              (case insensitive), else NULL is returned.             */
+/*                                                                     */
+/*              The parsing looks for the HTML attributes "name",      */
+/*              "scheme", and "content" only. Other attributes are     */
+/*              ignored.                                               */
+/*                                                                     */
+/*              The DC "type" will be recognized if suffixed           */
+/*              "name" by "dot notation", or contained within          */
+/*              parentheses within "content" (old notation).           */
+/*              (type = subelement)                                    */
+/*                                                                     */
+/*              The DC "scheme" will be recognized if                  */
+/*              present in "scheme" attribute (HTML 4.0), or if        */
+/*              contained within aprentheses in "content" (HTML 2.0)   */
+/*                                                                     */
+/*              Examples:                                              */
+/*                                                                     */
+/*              meta name="DC.Creator.Personalname"                    */
+/*                content="Smith, John"                                */
+/*                                                                     */
+/*              meta name="dc.Creator"                                 */
+/*                content="(type=Personalname) Smith, John">           */
+/*                                                                     */
+/*              meta name="DC.Subject" scheme="LCSH"                   */
+/*                content="Cataloguing"                                */
+/*                                                                     */
+/*              meta name="DC.Subject"                                 */
+/*                content="(scheme=LCSH) Cataloguing"                  */
+/*                                                                     */
+/*              The values of the DC attributes are NOT validated!     */
+/*                                                                     */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include "d2m.h"
+
+
+
+
+struct metatag *dc_parse(char *buf)
+{
+  int more;
+  char *p, *q;
+  struct metatag *mtag;
+  int strict = TRUE;       /* Indicates that DC. prefix is mandatory */
+  char attrname[16];
+
+/* Allocates and initializes metatag struct */
+
+  mtag = malloc(sizeof(*mtag));
+
+  mtag->name   = malloc(256);
+  mtag->type   = malloc(256);
+  mtag->scheme = malloc(256);
+  mtag->value  = malloc(10000);
+
+  *mtag->name   = 0;
+  *mtag->type   = 0;
+  *mtag->scheme = 0;
+  *mtag->value  = 0;
+
+
+
+
+/* Parse attribute name/value pairs: */ 
+ 
+  if (!buf || !*buf) return NULL;
+
+  p = strtok(buf, "\"");
+
+
+
+  while (p)
+  {
+/*  Look for attribute name  */
+
+    *attrname = 0;
+
+    while (*p && ( p[strlen(p) - 1] == ' ' || p[strlen(p) - 1] == '=')) 
+      p[strlen(p) - 1] = '\0'; 
+
+    if (*p && strlen(p) < 10)
+      strcpy(attrname, p);
+
+/*  Look for attribute value */
+
+    p = strtok(NULL, "\"");
+
+    while(*p && p[0] == ' ')
+      p++;
+
+    if (!p || !*p)
+      break;
+
+    if (strncasecmp(attrname, "name", 4) == 0)
+      strcpy(mtag->name, p);
+    else if (strncasecmp(attrname, "scheme", 6) == 0)
+      strcpy(mtag->scheme, p);
+    else if (strncasecmp(attrname, "content", 7) == 0)
+      strcpy(mtag->value, p);
+    p = strtok(NULL, "\"");
+
+    if (p)
+    {
+      while (*p && p[0] == ' ')
+        p++;
+      if (!*p) p = NULL;      
+    }
+  }
+
+
+
+/* Check the name attribute value */
+
+  p = mtag->name;
+  
+  if (!p || !*p)
+    return NULL;
+
+/* Check for leading "dc." and suffixed type */
+
+  if (strncasecmp(p, "dc.", 3) == 0)
+  {
+    p += 3;
+    q = strstr(p, ".");
+    if (q)
+    {
+      strcpy(mtag->type, q + 1);
+      *q = 0;
+    }
+    mtag->name = p;
+  }
+  else
+  {
+    if (strict)
+      return NULL;
+  }
+
+
+/* Check the content attribute value */
+/* Extracting type and scheme        */
+ 
+
+  p = mtag->value;
+ 
+  more = 1;
+     
+  while (more)
+  {
+    if (strncasecmp(p, "(type=", 6) == 0)
+    {
+       p += 6;
+       q = strtok(p, ")");
+       if (!q)
+         return NULL;
+       else
+       {
+          if (!*mtag->type)
+            strcpy(mtag->type, q);
+          q = strtok(NULL, "\0");
+          p = q;
+          while (p && *p && p[0] == ' ') p++;
+
+       }
+    }
+
+    else if (strncasecmp(p, "(scheme=", 8) == 0)
+    {
+       p += 8;
+       q = strtok(p, ")");
+       if (!q)
+         return NULL;
+       else
+       {
+          if (!*mtag->scheme)
+            strcpy(mtag->scheme, q);
+          q = strtok(NULL, "\0");
+          p = q;
+          while (p && *p && p[0] == ' ') p++;
+       }
+    }
+
+    else
+    {
+      mtag->value = p;
+      more = 0; 
+    }
+
+  }
+
+/* Finished */
+
+  if (!*mtag->value)
+    mtag = (struct metatag *) NULL;
+
+  return mtag;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/finMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/finMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/finMARC.c	(revision 16561)
@@ -0,0 +1,275 @@
+/* ------------------------------------------------------------------- */
+/* finMARC    : Convert a metatag struct to FINMARC                    */
+/*                                                                     */
+/* Author     : Ole Husby                                              */
+/* Last update: 1998-09-30                                             */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+/* Decides tag for format                                                    */
+
+int parse_format(char *str, char *format)
+{
+   if (strcasecmp(str, "text/html") == 0)
+     strcpy(format, "HTML"); 
+
+   return 1;
+}
+
+
+/* Decides tag for type                                                      */
+
+int parse_type(char *str, char *tag)
+{
+   strcpy(tag, "500");
+
+   if ( (strcasecmp(str, "diss."             ) == 0) 
+     || (strcasecmp(str, "dissertation"      ) == 0) 
+     || (strcasecmp(str, "phd thesis"        ) == 0) 
+     || (strcasecmp(str, "väitöskirja"       ) == 0) 
+     || (strcasecmp(str, "väitösk."          ) == 0) 
+     || (strcasecmp(str, "avhandling"        ) == 0) ) 
+     strcpy(tag, "502");
+
+   else
+   if ( (strcasecmp(str, "masters thesis"    ) == 0) 
+     || (strcasecmp(str, "honours thesis"    ) == 0) 
+     || (strcasecmp(str, "opinnäyte"         ) == 0) 
+     || (strcasecmp(str, "opinn."            ) == 0) ) 
+     strcpy(tag, "509");
+
+   return 1;
+}
+
+
+int finMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], type_tag[4];
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+      {
+        strcat(mr->partitle, "$r ");
+        strcat(mr->partitle, mt->value);
+
+      }
+      else
+      {
+        mr->ntitles++;
+        strcpy(pre, "245  $a");
+      }
+    }
+  }
+
+  else if (strcasecmp(mt->name, "creator") == 0)
+  {
+    if (!*mt->type)
+    {
+      strcpy(pre, "720  $a");
+      split_name(mt->value);
+    }
+    else if ( (strcasecmp(mt->type, "name") == 0) 
+            || (strcasecmp(mt->type, "personal") == 0) )
+    {
+      strcpy(pre, "100  $a");
+      split_name(mt->value);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "110  $a");
+  }
+
+  else if (strcasecmp(mt->name, "publisher") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "260  $b");
+    else if (strcasecmp(mt->type, "name") == 0)
+      strcpy(pre, "260  $b");
+  }
+
+  else if (strcasecmp(mt->name, "description") == 0)
+    strcpy(pre, "519  $a");
+
+  else if (strncasecmp(mt->name, "contributor", 11) == 0)     /* Note both pl and sgl allowed */
+  {
+    if (!*mt->type)
+    {
+      strcpy(pre, "720  $a");
+      split_name(mt->value);
+    }
+    else if ( (strcasecmp(mt->type, "name") == 0) 
+            || (strcasecmp(mt->type, "personal") == 0) )
+    {
+      strcpy(pre, "700  $a");
+      split_name(mt->value);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "500  $a");
+    else if (strcasecmp(mt->type , "spatial") == 0)
+    {
+      strcpy(pre, "655  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->type,  "temporal") == 0)
+    {
+      strcpy(pre, "652  $a");
+      split_subj(mt->value);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "source") == 0)
+    strcpy(pre, "501  $a");
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "856  $u");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $g");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "021  $a");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "022  $a");
+    else if (strcasecmp(mt->scheme, "nbn") == 0)
+      strcpy(pre, "015  $a");
+    else if (strcasecmp(mt->scheme, "isrc") == 0)
+      strcpy(pre, "015  $a");
+    else if (strcasecmp(mt->scheme, "ismn") == 0)
+      strcpy(pre, "024  $a");
+    else if (strcasecmp(mt->scheme, "isrn") == 0)
+      strcpy(pre, "027  $a");
+  }
+
+  else if (strcasecmp(mt->name, "type") == 0)
+  {
+    parse_type(mt->value, (char *) type_tag);
+    sprintf(pre, "%s  $a", type_tag);
+  }
+
+  else if (strcasecmp(mt->name, "language") == 0)
+  {
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+    {
+      strcpy(pre, "041  $a");
+      if (strlen(mt->value) == 3)
+        put008(mr->s008, mt->value, F008_LANGUAGE);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+  {
+    if ( (!*mt->scheme)
+      || (strcasecmp(mt->scheme, "imt") == 0)
+      || (strcasecmp(mt->scheme, "mime") == 0) )
+      parse_format(mt->value, (char *) mr->fmat);
+  }
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "692  $a");
+    else if (strcasecmp(mt->scheme, "udc") == 0)
+      strcpy(pre, "080  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+    else if (strcasecmp(mt->scheme, "mesh") == 0)
+      strcpy(pre, "690  $a");
+    else if (strcasecmp(mt->scheme, "ykl") == 0)
+      strcpy(pre, "098  $a");
+    else if (strcasecmp(mt->scheme, "ysa") == 0)
+    {
+      strcpy(pre, "652  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fimu") == 0)
+    {
+      strcpy(pre, "653  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fige") == 0)
+    {
+      strcpy(pre, "655  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "file") == 0)
+    {
+      strcpy(pre, "665  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fiso") == 0)
+    {
+      strcpy(pre, "670  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fire") == 0)
+    {
+      strcpy(pre, "680  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fifo") == 0)
+    {
+      strcpy(pre, "685  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fiag") == 0)
+    {
+      strcpy(pre, "686  $a");
+      split_subj(mt->value);
+    }
+    else if (strcasecmp(mt->scheme, "fiep") == 0)
+    {
+      strcpy(pre, "660  $a");
+      split_subj(mt->value);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if ( (!*mt->type) || ( strcasecmp(mt->type, "current") == 0) )
+    {
+      if ( (strncasecmp(mt->scheme, "ans", 3) == 0) 
+         || (strncasecmp(mt->scheme, "iso", 3) == 0) )
+      {
+        if (find_year(mt->value))
+        {
+          strcpy(mr->year, mt->value);
+          put008(mr->s008, mt->value, F008_DATE1);
+        }
+      }
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/htget.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/htget.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/htget.c	(revision 16561)
@@ -0,0 +1,545 @@
+/* ------------------------------------------------------------------- */
+/* htget     : Fetch a file using HTTP protocol                        */
+/*                                                                     */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-09-30                                              */
+/*                                                                     */
+/* ------------------------------------------------------------------- */
+/*                                                                     */
+/* htget(url, type, timeout_seconds, outfile, content_type, location)  */
+/*                                                                     */
+/*   Returns: HTTP statuscode, with additional private:                */
+/*                 0 : OK ( = 200)                                     */
+/*               900 : Error, possible timeout                         */
+/*               901 : Syntax error in url                             */
+/*               902 : Unknown host                                    */
+/*               903 : No response from server (no connection)         */
+/*               904 : File is not text/html                           */
+/*               905 : Statusline > 255 bytes                          */
+/*               906 : Statusline < 4 bytes                            */
+/*               907 : Statusline not starting with "HTTP"             */
+/*               908 : Statuscode not numeric                          */
+/*               909 : Size of header > BUFSIZE                        */
+/*               910 : Unable to open output file                      */
+/*               999 : Unspecified TCP/IP error                        */
+/*                                                                     */
+/*   Writes to outfile, depending on type, if statuscode = 0 | 200 :   */
+/*                                                                     */
+/*   type = 0  : Nothing                                               */
+/*   type = 1  : HTTP header                                           */
+/*   type = 2  : HTTP header + entitybody                              */
+/*   type = 3  : HTTP entitybody                                       */
+/*   type = 4  : HTTP entitybody if text/html                          */
+/*   type = 5  : HTTP <HEAD> part of entitybody if text/html           */
+/*   type = 6  : HTTP <HEAD> part of entitybody if text/html           */
+/*               HTTP entitybody if application/marc                   */
+/*                                                                     */
+/* ------------------------------------------------------------------- */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <signal.h>
+
+#define FALSE               0
+#define TRUE                1
+#define BUFSIZE             10000
+#define TYPE_NONE           0     
+#define TYPE_HTTPHEAD       1     
+#define TYPE_HTTPALL        2     
+#define TYPE_HTTPBODY       3     
+#define TYPE_HTMLALL        4     
+#define TYPE_HTMLHEAD       5     
+#define TYPE_MARC           6     
+
+#define TRACE               0
+
+#define AGENT               "BIBSYS_htget v1.1"
+
+char conType[128];
+int htmlonly, marcrecord;
+
+
+
+void thandler(int i)
+{
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* geteoHEAD: Look for </HEAD> or <BODY                                */
+/*                                                                     */
+/* returns: 0 if not found                                             */
+/*          1 if found. Terminates buf immediately after </HEAD> or    */
+/*            immediately before <BODY                                 */
+/* ------------------------------------------------------------------- */
+
+int geteoHEAD(char *buf)
+{
+  char *p;
+  int i;
+
+  p = (char *) cstr(buf, "<BODY");
+  if (p)
+  {
+    i = p - buf;
+
+    buf[i] = '\n';
+    buf[i+1] = '\0';
+    return 1;
+  }
+
+  p = (char *) cstr(buf, "</HEAD>");
+  if (p)
+  {
+    i = p - buf + 7;
+
+    buf[i] = '\n';
+    buf[i+1] = '\0';
+    return 1;
+  }
+
+  return 0;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* writeRequest: send request to server                                */
+/*                                                                     */
+/* returns: Number of bytes written                                    */
+/* ------------------------------------------------------------------- */
+
+int writeRequest(char *req, int server)
+{
+   if (TRACE)
+      printf("*** send(): %s\n", req);
+
+   return write(server, req, strlen(req));
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* getBody : Read the Entity-body into the file filename               */
+/*                                                                     */
+/* Open the given file for writing, read data from the                 */
+/* socket until a terminating '\0' is found, write to                  */
+/* the file. Returns 0 if ok, positive if an error results             */
+/* in errno being set, or -1 if other error.                           */
+
+/* Single read()'s blocking for more than TIMEOUT_SECONDS will         */
+/* be interrupted. The read() then returns a negative value, and       */
+/* errno will be set appropriately (EINTR).                            */
+/*                                                                     */
+/* Returns   0 if ok                                                   */
+/*         900 if read error                                           */
+/*                                                                     */
+/* ------------------------------------------------------------------- */
+
+int getBody(int server, int timeout, int fd, char *filename)
+{
+   int i, ef;
+   unsigned char *bf, buf[BUFSIZE + 1];
+   int  bytecount;
+   int  found_end;
+   
+   if (TRACE)
+     printf("*** Read entitybody\n");
+
+/* Loop until the endmark is found                                     */
+
+   found_end = FALSE;
+
+
+   while (!found_end) 
+   {
+      alarm (timeout);
+      bytecount = read (server, buf, BUFSIZE);
+      alarm (0);
+
+      if (bytecount < 0) 
+        return 900;                                 /* error in read() */
+
+      else if (bytecount == 0)
+        break;                                 /* server closed socket */
+
+
+      else
+      {
+       if (buf[bytecount-1] == '\0') 
+         {
+            bytecount--;              /* do not write the '\0' to file */
+            found_end = TRUE;                    /* terminate the loop */
+	 }
+
+/*       Write to file                                                 */
+
+         if (bytecount > 0) 
+         {
+            bf = (unsigned char *) buf;
+            bf[bytecount] = '\0';
+            if ( htmlonly )
+            {
+              ef = geteoHEAD(bf);
+              bytecount = strlen(bf);
+            }
+            else
+              ef = 0;
+            write (fd, bf, bytecount);
+            if (ef)
+              break;
+         }
+      }
+   }
+
+   return 0; 
+}
+      
+
+
+/* ------------------------------------------------------------------- */
+/* getHeader : Access file and read HTTP header                        */
+/*                                                                     */
+/* Returns   0 if ok                                                   */
+/*         904 if Content-type not "text/html"                         */
+/*         905 if Statusline > 255 bytes                               */
+/*         906 if Statusline < 4 bytes                                 */
+/*         907 if Statusline not starting with "HTTP"                  */
+/*         908 if Statuscode not numeric                               */
+/*         909 if size of header > BUFSIZE                             */
+/*         HTTP statuscode if <> 200                                   */
+/*                                                                     */
+/* ------------------------------------------------------------------- */
+
+int getHeader(int server, int timeout, int type, int fd, char *reason, char *loc)
+{
+   char buf[BUFSIZE+1], dummy[8];
+   unsigned char *p,  *cp = buf, *d = dummy;
+   int i, rc, statuscode, plf, rlen = 0;
+
+   reason = (char *) NULL;
+   *conType = 0;
+
+   if (TRACE)
+     printf("*** Read statusline\n");
+
+
+/* Read HTTP statusline (until LF or 0, max 256 byte)                 */
+
+   for ( cp = buf, *buf = 0; 1; cp++ )
+   {
+      alarm (timeout);
+      rc = read ( server, cp, 1 );
+      alarm (0);
+
+      if ( rc < 0 )
+	 return 900;
+
+      if ( *cp == '\r')
+      {
+        cp--;
+        continue;
+      }
+
+      rlen++;
+
+      if (TRACE)
+        printf("%c", *cp);
+
+      if ( *cp == 0 || rlen > 255)
+	 return 905;
+      if ( *cp == '\n')
+      {
+         *cp = 0;
+	 break;
+      }
+   }
+
+/*  Write statusline                                                 */
+
+   if ( ( type == TYPE_HTTPHEAD ) || ( type == TYPE_HTTPALL ) )
+   {
+     write(fd, buf, strlen(buf));
+     write(fd, "\n\n", 2);
+   }
+
+   if (TRACE)
+      printf("*** read() : (%d) %s\n", rlen, buf);
+
+
+/* Parse statusline                                                   */
+
+   if (rlen < 4)
+     return 906;
+
+   if (strncmp(buf, "HTTP", 4) != 0)
+     return 907;
+
+   p = strtok(buf, " ");
+   p = strtok(NULL, " ");
+
+   if (!*p)
+     statuscode = 200;
+   else
+   {
+     for (i = 0; i < strlen(p); i++)
+       if (!isdigit(p[i]))
+         return 908;
+   }
+   statuscode = atoi(p);
+
+   p = strtok(NULL, "\0");
+   if (p)
+     reason = p;
+
+   if (statuscode == 200)
+     statuscode = 0;
+
+   if (!type)
+     return statuscode;
+
+
+/* Read HTTP response header (until 0 or empty line, max BUFSIZE bytes  */
+
+   rlen = 0;
+   plf = FALSE;
+
+   if (TRACE)
+     printf("*** Read responseheader\n");
+
+   for ( cp = buf, *buf = 0; 1; cp++ )
+   {
+      alarm (timeout);
+      rc = read ( server, cp, 1 ); 
+      alarm (0);
+
+      if ( rc < 0 )
+	 return 900;
+
+      if ( *cp == '\r')
+      {
+        cp--;
+        continue;
+      }
+
+      rlen++;
+
+      if ( *cp == 0 || rlen > BUFSIZE)
+	 return 909;
+      else if ( *cp == '\n')
+      {
+         if (plf)
+         {
+           *cp = 0;
+	   break;
+         }
+         else
+           plf = TRUE;
+      }
+      else
+        plf = FALSE;
+   }
+
+/*  Write rest of HTTP header                                         */
+
+   if ( ( type == TYPE_HTTPHEAD ) || ( type == TYPE_HTTPALL ) )
+   {
+     write(fd, buf, strlen(buf));
+     write(fd, "\n", 1);
+   }
+
+   if (TRACE)
+      printf("*** read() : (%d) %s\n", rlen, buf);
+
+
+/* Parse header for Content-Type and Loaction                         */
+
+   rc = 904;
+
+   p = strtok(buf, "\n");
+   while (p)
+   {
+     if (strncasecmp(p, "Content-Type:", 13) == 0)
+     {
+       p += 13;
+       while (p[0] == ' ')
+         p++;
+       strcpy(conType, p);
+
+       if (strncasecmp(p, "text/html", 7) == 0)
+       {
+         if ( ( type == TYPE_HTMLHEAD) || (type == TYPE_MARC ) )
+           htmlonly = 1;
+         rc = 0;
+       }
+
+       else if (strncasecmp(p, "application/marc", 16) == 0)
+       {
+         if ( type == TYPE_MARC )
+         {
+           marcrecord = 1; 
+           rc = 0;
+         }
+       }
+     }
+
+     else if (strncasecmp(p, "Location:", 9) == 0)
+     {
+       p += 9;
+       while (p[0] == ' ')
+         p++;
+       strcpy(loc, p);
+     }
+     p = strtok(NULL, "\n");
+   }
+
+
+/* All OK. Socket is positioned at start of HTTP Entity-Body          */
+
+   if (rc)
+     return rc;
+   else
+     return statuscode;
+}
+
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* htget  : Fetch a URL                                                */
+/* ------------------------------------------------------------------- */
+
+int htget(char *iurl, int type, int timeout, char *outfile, char *h_contype, char *h_location)
+{
+   int i, rc, fd, soc, port;
+   struct sockaddr_in addr; 
+   struct hostent *hp, *gethostbyname();
+   char	uurl[1024], *url = uurl, hostname[256], cport[64], req[1024];
+   char *h, *p, *q, *r;
+   char tfile[256], blank[2];
+
+   *blank = *h_contype = *h_location = 0; 
+   htmlonly = marcrecord = 0;
+
+   strcpy(tfile, "/tmp/geturl.tmp");
+   if (!*outfile)
+     outfile = (char *) tfile;
+
+   if (!*iurl || ( strlen(iurl) > 1023 ) )
+     return 901;
+
+   strcpy(url, iurl);
+
+/* Parse and validate URL                                             */
+
+   if (strncmp(url, "http://", 7) != 0)
+     return 901;
+
+   url += 7;
+
+   q = strtok(url, "/");
+   if (!q)
+     return 901;
+
+   r = strtok(NULL, "\0");
+   if (!r)
+     r = (char *) blank;
+
+   h = strtok(q, ":");
+   if (!*h)
+     return 901;
+
+   strcpy(hostname, h);
+
+   p = strtok(NULL, "\0");
+
+   if (!p || !*p)
+     port = 80;
+   else
+   {
+     for (i = 0; i < strlen(p); i++)
+       if (!isdigit(p[i]))
+         return 901;
+     port = atoi(p);
+   }
+   sprintf(req, "GET /%s HTTP/1.0\r\nUser_Agent: %s\n\n", r, AGENT);
+  
+
+/* Establish handler for the alarm-signal                              */
+
+   signal (SIGALRM, thandler);
+
+
+/* Get IP address                                                      */
+ 
+   hp = gethostbyname(hostname);
+   if (!hp)
+     return 902;
+
+/* Get socket and connect                                              */
+ 
+   soc = socket(AF_INET, SOCK_STREAM, 0);
+ 
+   addr.sin_family = AF_INET;
+   
+   memcpy( &addr.sin_addr.s_addr, hp->h_addr, (size_t) hp->h_length );
+
+   addr.sin_port = htons(port);
+   if (connect(soc, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0)
+      return 903;
+
+
+/* Open the destination file                                           */
+ 
+   if (type)
+   {
+     fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC , 0666);
+     if (fd < 0)  
+     {
+        close(soc);
+        return 910;
+     }
+   }
+
+
+
+
+/* Write HTTP-request                                               */
+ 
+   if (!writeRequest(req, soc))
+   {
+     close(soc);
+     if (type)
+       close(fd);
+     return 999;
+   }
+
+
+/* Read header part of response                                     */
+
+   rc = getHeader(soc, timeout, type, fd, r, h_location);
+   
+   if ( ( rc == 904 ) && (type < 4 ) )
+     rc = 0;
+
+   if ( ( rc == 0 ) && ( type > 1 ) )
+     rc = getBody(soc, timeout, fd, outfile);
+ 
+   close(soc);
+   if (type)
+     close(fd);
+   strcpy(h_contype, conType);
+
+   return rc;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/isMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/isMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/isMARC.c	(revision 16561)
@@ -0,0 +1,333 @@
+/* ------------------------------------------------------------------- */
+/* isMARC     : Convert a metatag struct to ISMARC                     */
+/*              (i.e. UKMARC as used in the National and University    */
+/*              Library of Island).                                    */
+/*                                                                     */
+/* Specs      : http://www.bibsys.no/meta/d2m/iscross.txt              */
+/*                                                                     */
+/* Author     : Ole Husby                                              */
+/* Last update: 1998-09-30                                             */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Splits Icelandig name without a comma                               */
+/* ------------------------------------------------------------------- */
+
+int split_ice2(char *str)
+{
+  char *q, *temp, *suba, *subi, *subb;
+
+  if (!strstr(str, " "))
+    return 1;
+
+  suba = (char *) malloc(strlen(str) + 50);
+  subi = (char *) malloc(strlen(str) + 50);
+  subb = (char *) malloc(strlen(str) + 50);
+  temp = (char *) malloc(strlen(str) + 50);
+
+  *suba = *subi = *subb = *temp = 0;
+
+  q = strtok(str, " ");
+  while (q)
+  {
+    while ( ( q[0] == ' ' ) && ( strlen(q) ) )
+      q++;
+
+    while( ( q[strlen(q) - 1] == ' ' ) && ( strlen(q) ) )
+      q[strlen(q) - 1] = '\0';
+
+    if (*q)
+    {
+      if (!*suba)
+        strcpy(suba, q);
+      else
+      {
+        if (*temp)
+        {
+          if (*subi)
+            strcat(subi, " ");
+          strcat(subi, temp);
+        }
+        strcpy(temp, q);
+      }
+    }
+    q = strtok(NULL, " ");
+  }
+
+  *str = 0;
+
+  if (*temp)
+  {
+    if (*subi)
+      strcpy(subb, temp);
+    else
+      strcpy(subi, temp);
+  }
+
+  if (*suba)
+  {
+    strcpy(str, suba);
+    if (*subi)
+    {
+      strcat(str, " $i ");
+      strcat(str, subi); 
+    }
+    if (*subb)
+    {
+      strcat(str, " $b ");
+      strcat(str, subb); 
+    }
+  }
+
+  free(suba);
+  free(subi);
+  free(subb);
+  free(temp);
+
+  return 40;
+}
+
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Splits Icelandig name                                               */
+/* ------------------------------------------------------------------- */
+
+int split_icename(char *str, int i2)
+{
+  char *p, *q, subtag[5];
+
+  if (!strstr(str, ","))
+  {
+    if (i2)
+      return split_ice2(str);
+    else
+      return 1;
+  }
+
+  p = (char *) malloc(strlen(str) + 50);
+  *p = 0;
+  *subtag = 0;
+
+  q = strtok(str, ",");
+  if (q)
+  {
+    strcpy(p, q);
+    q = strtok(NULL, "\0");
+    if (q)
+    {
+      while ( ( q[0] == ' ' ) && ( strlen(q) ) )
+        q++;
+      strcat(p, " $h ");
+      strcat(p, q);
+    }
+    strcpy(str, p);
+  }
+
+  free(p);
+
+  return 10;
+}
+
+
+int isMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], type_tag[4], creator_tag[4], title_tag[6];
+  int i, ind, ice2;
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+        strcpy(title_tag, "24630");
+      else
+        strcpy(title_tag, "24510");
+
+      mr->ntitles++;
+
+      sprintf(pre, "%s$a", title_tag);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "creator") == 0)
+  {
+    if ( strcasecmp(mt->type, "personal") == 0) 
+    {
+      if (!mr->ncreators)
+        strcpy(creator_tag, "100");
+      else 
+        strcpy(creator_tag, "700");
+
+      ice2 = 1; 
+      ind = split_icename(mt->value, ice2);
+      sprintf(pre, "%s%2d$a", creator_tag, ind);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+    {
+      if (!mr->ncreators)
+        strcpy(creator_tag, "110");
+      else 
+        strcpy(creator_tag, "710");
+      sprintf(pre, "%s20$a", creator_tag);
+    }
+    else
+    {
+      ice2 = 0;
+      strcpy(pre, "720  $a");
+      ind = split_icename(mt->value, ice2);
+    }
+    mr->ncreators++;
+  }
+
+  else if (strncasecmp(mt->name, "contributor", 11) == 0)
+  {
+    if ( strcasecmp(mt->type, "personal") == 0) 
+    {
+      ice2 = 1; 
+      ind = split_icename(mt->value, ice2);
+      sprintf(pre, "700%2d$a", ind);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+    {
+      strcpy(pre, "71020$a");
+    }
+    else
+    {
+      ice2 = 0;
+      strcpy(pre, "720  $a");
+      ind = split_icename(mt->value, ice2);
+    }
+    mr->ncreators++;
+  }
+
+
+  else if (strcasecmp(mt->name, "publisher") == 0)
+    strcpy(pre, "260  $b");
+
+  else if (strcasecmp(mt->name, "description") == 0)
+    strcpy(pre, "513  $a");
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (strcasecmp(mt->type , "spatial") == 0)
+    {
+      strcpy(pre, "651  $a");
+      split_subj(mt->value);
+    }
+    else
+      strcpy(pre, "500  $a");
+  }
+
+  else if (strcasecmp(mt->name, "source") == 0)
+    strcpy(pre, "787  $a");
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "856  $3 rights $u");
+    else
+      strcpy(pre, "500  $a");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "8567 $u urn:");
+    else if (strcasecmp(mt->scheme, "nbn") == 0)
+      strcpy(pre, "015  $a");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "021  $a");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "022  $a");
+    else if (strcasecmp(mt->scheme, "isrc") == 0)
+      strcpy(pre, "024  $a");
+    else if (strcasecmp(mt->scheme, "ismn") == 0)
+      strcpy(pre, "025  $a");
+    else if (strcasecmp(mt->scheme, "isrn") == 0)
+      strcpy(pre, "027  $a");
+  }
+
+  else if (strcasecmp(mt->name, "type") == 0)
+    strcpy(pre, "655  $a");
+
+  else if (strcasecmp(mt->name, "relation") == 0)
+    strcpy(pre, "787  $a");
+
+  else if (strcasecmp(mt->name, "language") == 0)
+  {
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+    {
+      strcpy(pre, "04109$a");
+      if (strlen(mt->value) == 3) 
+        put008(mr->s008, mt->value, F008_LANGUAGE);
+    }
+    else if (strcasecmp(mt->scheme, "usmarc") == 0)
+      strcpy(pre, "04109$a");
+    else
+      strcpy(pre, "546  $a");
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+    strcpy(mr->fmat, mt->value); 
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "680  $a");
+    else if (strcasecmp(mt->scheme, "udc") == 0)
+      strcpy(pre, "080  $a");
+    else if (strcasecmp(mt->scheme, "lcc") == 0)
+      strcpy(pre, "050  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+    else if (strcasecmp(mt->scheme, "nlm") == 0)
+      strcpy(pre, "060  $a");
+    else if (strcasecmp(mt->scheme, "landslyk") == 0)
+      strcpy(pre, "659  $a");
+    else if (strcasecmp(mt->scheme, "kerfefn") == 0)
+      strcpy(pre, "659  $a");
+    else if (strcasecmp(mt->scheme, "dbc") == 0)
+      strcpy(pre, "65002$a");
+    else if (strcasecmp(mt->scheme, "mesh") == 0)
+      strcpy(pre, "690  $a");
+    else
+      strcpy(pre, "65004$a");
+  }
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if (find_year(mt->value))
+    {
+      strcpy(mr->year, mt->value);
+      put008(mr->s008, mt->value, F008_DATE1);
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/marc2line.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/marc2line.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/marc2line.c	(revision 16561)
@@ -0,0 +1,243 @@
+/* ------------------------------------------------------------------- */
+/* marc2line : Reads an ISO 2709 MARC record, converts to MARC         */
+/*             "Line format"                                           */
+/*                                                                     */
+/* author    : Ole Husby, BIBSYS                                       */
+/* updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define DIRL                                   3600
+
+#define LINEL                                  72 
+#define SUBFIELD_CODE                          '\x1f'
+#define SUBFIELD_STR                           "\x1f" 
+#define FIELD_TERMINATOR                       '\x1e'
+#define RECORD_TERMINATOR                      '\x1d'
+
+#define BIBSYS_TYPE                            0
+#define NORMARC_TYPE                           1
+
+union LDR   {
+   char ldr_string[24+1];
+   struct {
+      char record_length[5],
+      record_status,
+      record_type,
+      bibliographic_level,
+      unused1[2],
+      indicator_count,
+      subfield_code_count,
+      base_adress[5],
+      unused2[3],
+      entry1,
+      entry2,
+      entry3,
+      entry4;
+   } ltab;
+} ldr; 
+
+static char out_record[10240];
+static int out_type;
+
+
+
+/* ------------------------------------------------------------------- */
+/* One subfield string is split into output lines                      */
+/* ------------------------------------------------------------------- */
+
+static void out_subfield(data, prefix, slen)
+char *data, *prefix;
+int slen;
+
+{
+   char lineprefix[7], subprefix[4], *spre;
+   int i, more, len, max;
+
+   if (out_type == BIBSYS_TYPE)
+      strcpy(lineprefix, prefix);
+   else
+   {
+      if (prefix[0] == ' ') strcpy(lineprefix, " ");
+      else                  strcpy(lineprefix, "*");
+      strcat(lineprefix, prefix);
+   }
+
+   max = LINEL;
+
+   if (slen)
+   {
+      spre = (char *) subprefix;
+      strcpy(spre, "$");                       /* Construct subfield    */
+      strncat(spre, data, 1);                  /* prefix                */
+      if (out_type == BIBSYS_TYPE) 
+	 strcat(spre, " ");
+      data++;
+      max = LINEL - 2; 
+      if (out_type == BIBSYS_TYPE) 
+	 max--;
+   }
+   else spre = NULL;  
+
+   more = 1;
+
+   while (more)
+   {
+      if (slen)
+         while (data[0] == ' ') data++;    /* skip leading blanks    */
+
+      strcat(out_record, lineprefix);      /* Start with prefix      */
+      strcpy(lineprefix, "     ");
+      if (out_type == NORMARC_TYPE) 
+	 strcat(lineprefix, " ");
+
+      len = strlen(data);
+      if (len > max)                       /* Must fit into one line */
+      {
+	 len = max;
+	 for (i = max; i > max - 15; i--)
+	 {
+	    if (data[i] == ' ')
+	    {
+	       len = i;
+	       break;
+	    }
+	 }
+      }
+      else more = 0;
+
+      if (spre)
+      {
+	 strcat(out_record, spre);         /* subfield pref needed  */
+	 spre = NULL;                      /* only first line       */
+	 max = LINEL;
+      }
+
+      strncat(out_record, data, len);      /* append data           */
+      if (more) data += len;
+      strcat(out_record, "\n");
+   }
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* One MARC field is split into subfields and given to output          */
+/* ------------------------------------------------------------------- */
+
+static int out_field(data, ftag, find)
+char *data, *ftag, *find;
+
+{
+   char prefix[5];
+   char subfield[5120], *sub = subfield;
+   int subcode = 0;
+
+   strcpy(prefix, ftag);
+
+   if (!find)                                 /* output entire field   */ 
+   {
+      out_subfield(data, prefix, subcode);
+      return 1;
+   }
+
+   strcat(prefix, find);
+
+   if (data[0] != SUBFIELD_CODE) return 0;    /* requires subf code in */
+   data++;                                    /*   first byte          */
+
+   sub = strtok(data, SUBFIELD_STR);          /* find first subfield   */
+   if (!sub) return 0;
+
+   subcode = 1;
+   out_subfield(sub, prefix, subcode);        /* output subfield       */
+   strcpy(prefix, "     ");                   /* reset prefix          */
+
+   while (sub = strtok(NULL, SUBFIELD_STR))   /* find and output rest  */
+      out_subfield(sub, prefix, subcode);     /* of the subfields      */
+
+   return 1;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Main program                                                        */
+/* ------------------------------------------------------------------- */
+
+char *marc2line(record, type)          
+char *record, *type;
+{
+   char buffer[10240], tag[4], indicators[3];
+   char *cp, *fld, *buf = buffer, *cq;
+   char directory[DIRL+1], *dir = directory, *ind;
+   char sBase[6], fLen[5], fStart[6];
+   int           dlen, base, x = 0, more = 1, len, start;
+
+   if (!record || !*record)
+     return (char *) NULL;
+
+   strcpy(buf, record);
+   *out_record = 0;                                 /* reset out area     */
+
+   if (strcmp(type, "bibsys") == 0) out_type = BIBSYS_TYPE;
+   else out_type = NORMARC_TYPE;
+
+   strncpy(ldr.ldr_string, buf, 24);                 /* get leader         */
+   ldr.ldr_string[24] = '\0';
+   buf += 24;
+  
+   strncpy(sBase, ldr.ltab.base_adress, 5);
+   sBase[5] = '\0';
+   base = atoi(sBase);
+
+   dlen = base - 24;                                 /* get directory      */
+   strncpy(dir, buf, dlen);
+   dir[dlen] = '\0';
+   buf += dlen;
+
+   cp = dir;
+
+   while (more)                                      /* loop until EOdir   */ 
+   {
+      if (*cp == FIELD_TERMINATOR)  break;
+      if (strlen(cp) < 13) return (char *) NULL;
+
+      strncpy(tag, cp, 3);                           /* get tag            */
+      tag[3] = '\0';
+      cp +=3;
+
+      strncpy(fLen, cp, 4);                         /* get length          */
+      fLen[4] = '\0';
+      len = atoi(fLen) -1;                          /* discard field term. */
+      cp += 4;
+
+      strncpy(fStart, cp, 5);                       /* get start of field  */
+      fStart[5] = '\0';
+      start = atoi(fStart);
+      cp += 5;
+
+      fld = buf + start;                            /* get field           */ 
+      fld[len] = '\0';
+      
+      if (strncmp(tag, "00", 2) != 0)               /* get indicators      */
+      {
+	 indicators[0] = '\0';
+	 strncat(indicators, fld, 2);
+	 ind = (char *) indicators;
+	 fld += 2;
+      }
+      else ind = NULL;
+
+      if (!out_field(fld, tag, ind))                /* output field         */
+	 return (char *) NULL;
+
+   }
+
+   return (char *) out_record;
+
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/norMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/norMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/norMARC.c	(revision 16561)
@@ -0,0 +1,194 @@
+/* ------------------------------------------------------------------- */
+/* norMARC   : Convert a metatag struct to NORMARC                     */
+/*                                                                     */
+/* 1998-09-30: Ole Husby                                               */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+
+int norMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], creator_tag[4], title_tag[4];
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+        strcpy(title_tag, "246");
+      else
+        strcpy(title_tag, "245");
+
+      mr->ntitles++;
+
+      sprintf(pre, "%s  $a", title_tag);
+    }
+    else if (strcasecmp(mt->type, "subtitle") == 0)
+    {
+      if (mr->ntitles < 2)
+        strcpy(mr->subtitle, mt->value);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "creator") == 0)
+  {
+    if ( (!*mt->type) || (strcasecmp(mt->type, "name") == 0) 
+      || (strcasecmp(mt->type, "personalname") == 0) )
+    {
+      if (mr->ncreators)
+        strcpy(creator_tag, "700");
+      else
+        strcpy(creator_tag, "100");
+
+      mr->ncreators++;
+
+      sprintf(pre, "%s  $a", creator_tag);
+    }
+
+    else if (strcasecmp(mt->type, "corporatename") == 0) 
+    {
+      if (mr->ncreators)
+        strcpy(creator_tag, "710");
+      else
+        strcpy(creator_tag, "110");
+
+      mr->ncreators++;
+
+      sprintf(pre, "%s  $a", creator_tag);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "publisher") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "260  $b");
+    else if (strcasecmp(mt->type, "name") == 0)
+      strcpy(pre, "260  $b");
+  }
+
+  else if (strcasecmp(mt->name, "contributor") == 0)
+  {
+    if ( (!*mt->type)|| (strcasecmp(mt->type, "name") == 0)
+      || (strcasecmp(mt->type, "personalname") == 0) )
+      strcpy(pre, "700  $a");
+    else if (strcasecmp(mt->type, "corporatename") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "500  $a");
+    else if (strcasecmp(mt->scheme, "freetext") == 0)
+      strcpy(pre, "500  $a");
+  }
+
+  else if (strcasecmp(mt->name, "source") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "500  $a Kilde:");
+    else if (strcasecmp(mt->scheme, "freetext") == 0)
+      strcpy(pre, "500  $a Kilde:");
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "500  $a Kilde: URL:");
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "500  $a Kilde: URN:");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "500  $a Kilde: ISBN:");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "500  $a Kilde: ISSN:");
+  }
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "500  $a Copyright:");
+    else if (strcasecmp(mt->scheme, "freetext") == 0)
+      strcpy(pre, "500  $a Copyright:");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $u urn:");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "020  $a");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "022  $a");
+  }
+
+  else if (strcasecmp(mt->name, "language") == 0)
+  {
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+    {
+      if (strlen(mt->value) == 3)
+        put008(mr->s008, mt->value, F008_LANGUAGE);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->fmat, mt->value);
+    else if (strcasecmp(mt->scheme, "imt") == 0)
+      strcpy(mr->fmat, mt->value);
+    else if (strcasecmp(mt->scheme, "mime") == 0)
+      strcpy(mr->fmat, mt->value);
+  }
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "691  $a");
+    else if (strcasecmp(mt->scheme, "internal") == 0)
+      strcpy(pre, "691  $a");
+    else if (strcasecmp(mt->scheme, "udc") == 0)
+      strcpy(pre, "080  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+    else if (strcasecmp(mt->scheme, "mesh") == 0)
+      strcpy(pre, "660  $a");
+    else if (strcasecmp(mt->scheme, "humord") == 0)
+      strcpy(pre, "698  $a");
+    else if (strcasecmp(mt->scheme, "ntub") == 0)
+      strcpy(pre, "699  $a");
+  }
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if ( (strncasecmp(mt->scheme, "ans", 3) == 0) ||
+         (strncasecmp(mt->scheme, "iso", 3) == 0) )
+    {
+      if ( (!*mt->type) || ( strcasecmp(mt->type, "current") == 0) )
+      {
+        if (find_year(mt->value))
+        {
+          strcpy(mr->year, mt->value);
+          put008(mr->s008, mt->value, F008_DATE1);
+        }
+      }
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/strcasecmp.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/strcasecmp.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/strcasecmp.c	(revision 16561)
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strcasecmp.c	5.5 (Berkeley) 11/24/87";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison.  The mappings are
+ * based upon ascii character sequences.
+ */
+static unsigned char charmap[] = {
+	'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
+	'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
+	'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
+	'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
+	'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
+	'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
+	'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
+	'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
+	'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
+	'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
+	'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
+	'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
+	'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
+	'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
+	'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
+	'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
+	'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
+	'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
+	'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
+	'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
+	'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+	'\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
+	'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+	'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
+	'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
+	'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
+};
+
+int
+strcasecmp(s1, s2)
+    char *s1, *s2;
+{
+    register unsigned char u1, u2;
+
+    for (;;) {
+	u1 = (unsigned char) *s1++;
+	u2 = (unsigned char) *s2++;
+	if (charmap[u1] != charmap[u2]) {
+	    return charmap[u1] - charmap[u2];
+	}
+	if (u1 == '\0') {
+	    return 0;
+	}
+    }
+}
+int
+strncasecmp(s1, s2, n)
+    char *s1, *s2;
+    int n;
+{
+    register unsigned char u1, u2;
+    int i;
+    for (i=0;i<n;i++) {
+	u1 = (unsigned char) *s1++;
+	u2 = (unsigned char) *s2++;
+	if (charmap[u1] != charmap[u2]) {
+	    return charmap[u1] - charmap[u2];
+	}
+	if (u1 == '\0') {
+	    return 0;
+	}
+    }
+    return 0;
+}
+
+
+
+
Index: /gsdl/trunk/gs2runtime/packages/d2m/sweMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/sweMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/sweMARC.c	(revision 16561)
@@ -0,0 +1,259 @@
+/* ------------------------------------------------------------------- */
+/* sweMARC    : Convert a metatag struct to LIBRISMARC                 */
+/*                                                                     */
+/* Author     : Ole Husby                                              */
+/* Last update: 1998-09-30                                             */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+
+
+/* ---------------------------------------------------------------------- */
+/* Decides on first indicator for personal name                           */
+/*                                                                        */
+/* f : no comma                                                           */
+/* e : comma, with only one word preceeding                               */
+/* s : comma, with more than one word preceeding (delimiter blank or "-") */
+/* ---------------------------------------------------------------------- */
+
+int personal_indicator(char *pname)
+{
+  char *comma, *blank, *hyphen;
+
+  comma = strstr(pname, ",");
+  if (!comma)
+    return (int) 'f';
+
+  blank = strstr(pname, " ");
+  hyphen = strstr(pname, "-");
+
+  if ( (blank && blank < comma) || (hyphen && hyphen < comma) )
+    return (int) 's';
+  else
+    return (int) 'e';
+}
+
+
+
+/* ---------------------------------------------------------------------- */
+/* Decides tag for type                                                   */
+/* ---------------------------------------------------------------------- */
+
+int parse_swetype(char *str, char *tag)
+{
+   if ( (strcasecmp(str, "diss."             ) == 0) 
+     || (strcasecmp(str, "dissertation"      ) == 0) 
+     || (strcasecmp(str, "thesis"            ) == 0) 
+     || (strcasecmp(str, "domkap.avh."       ) == 0) 
+     || (strcasecmp(str, "hab.-schr"         ) == 0) 
+     || (strcasecmp(str, "prästmösteavh."    ) == 0)
+     || (strcasecmp(str, "avhandling"        ) == 0) ) 
+     strcpy(tag, "502");
+   else
+     strcpy(tag, "500");
+
+   return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* sweMARC()                                                              */
+/* ---------------------------------------------------------------------- */
+
+int sweMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], creator_tag[4], type_tag[4];
+  int ind1;
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+        strcpy(pre, "740  $a");
+      else
+      {
+        mr->ntitles++;
+        strcpy(pre, "245  $a");
+      }
+    }
+  }
+
+
+  else if (strcasecmp(mt->name, "creator") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "720  $a");
+    else if (strcasecmp(mt->type, "personal") == 0) 
+    {
+      ind1 = personal_indicator(mt->value);
+      if (mr->ncreators)
+        strcpy(creator_tag, "700");
+      else
+      {
+        strcpy(creator_tag, "100");
+        mr-> ncreators++;
+      }
+      sprintf(pre, "%s%c $a", creator_tag, ind1);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+    {
+      if (mr->ncreators)
+        strcpy(creator_tag, "710");
+      else
+      {
+        strcpy(creator_tag, "110");
+        mr-> ncreators++;
+      }
+      sprintf(pre, "%sc $a", creator_tag);
+    }
+  }
+
+
+  else if (strncasecmp(mt->name, "contributor", 11) == 0)     /* Note both pl and sgl allowed */
+  {
+    if (!*mt->type)
+      strcpy(pre, "720  $a");
+    else if (strcasecmp(mt->type, "personal") == 0) 
+    {
+      ind1 = personal_indicator(mt->value);
+      sprintf(pre, "700%cm$a", ind1);
+    }
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+
+  else if (strcasecmp(mt->name, "publisher") == 0)          /* Note both should go to same tag! */
+  {
+    if (!*mt->type)
+      strcpy(pre, "260  $b");
+    else if (strcasecmp(mt->type, "email") == 0)
+      strcpy(pre, "260  $d");
+  }
+
+
+
+  else if (strcasecmp(mt->name, "description") == 0)
+    strcpy(pre, "520  $a");
+
+
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "500  $a");
+    else if (strcasecmp(mt->type , "spatial") == 0)
+      strcpy(pre, "651  $a");
+    else if (strcasecmp(mt->type,  "temporal") == 0)
+      strcpy(pre, "650  $a");
+  }
+
+  else if (strcasecmp(mt->name, "source") == 0)
+    strcpy(pre, "786  $a");
+
+
+  else if (strcasecmp(mt->name, "relation") == 0)
+    strcpy(pre, "787  $a");
+
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "500  $a");
+    if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "856  $u");
+    if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $g");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $g");
+    else if (strcasecmp(mt->scheme, "ismn") == 0)
+      strcpy(pre, "538  $a ISMN $b");
+    else if (strcasecmp(mt->scheme, "sici") == 0)
+      strcpy(pre, "538  $a SICI $b");
+    else 
+      sprintf(pre, "538  $a %s $b", mt->scheme);
+  }
+
+  else if (strcasecmp(mt->name, "type") == 0)
+  {
+    parse_swetype(mt->value, (char *) type_tag);
+    sprintf(pre, "%s  $a", type_tag);
+  }
+
+  else if (strcasecmp(mt->name, "language") == 0)
+  {
+    strcpy(pre, "506  $a");
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+      put008(mr->s008, mt->value, F008_LANGUAGE);
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+  {
+    if ( (!*mt->scheme)
+      || (strcasecmp(mt->scheme, "imt") == 0)
+      || (strcasecmp(mt->scheme, "mime") == 0) )
+      strcpy(pre, "856  $q");
+  }
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "697  $a");
+    else if (strcasecmp(mt->scheme, "udc") == 0)
+      strcpy(pre, "080  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+    else if (strcasecmp(mt->scheme, "lcc") == 0)
+      strcpy(pre, "087  $a");
+    else if (strcasecmp(mt->scheme, "sab") == 0)
+      strcpy(pre, "081  $a");
+    else if (strcasecmp(mt->scheme, "mesh") == 0)
+      strcpy(pre, "660  $a");
+    else
+      strcpy(pre, "650  $a");
+  }
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if ( (!*mt->type) || ( strcasecmp(mt->type, "current") == 0) )
+    {
+      if ( (strncasecmp(mt->scheme, "ans", 3) == 0) 
+         || (strncasecmp(mt->scheme, "iso", 3) == 0) )
+      {
+        if (find_year(mt->value))
+        {
+          strcpy(mr->year, mt->value);
+          put008(mr->s008, mt->value, F008_DATE1);
+        }
+      }
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/test.data
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/test.data	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/test.data	(revision 16561)
@@ -0,0 +1,1 @@
+<meta name="DC.title" content="Test file for d2m">
Index: /gsdl/trunk/gs2runtime/packages/d2m/usMARC.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/usMARC.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/usMARC.c	(revision 16561)
@@ -0,0 +1,165 @@
+/* ------------------------------------------------------------------- */
+/* usMARC   : Convert a metatag struct to USMARC                       */
+/*                                                                     */
+/* 1998-09-30: Ole Husby                                               */
+/* ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include "d2m.h" 
+
+
+
+void usMARC(struct metatag *mt, struct marcrec *mr)
+{
+  char pre[64], title_tag[4];
+
+  *pre = 0;
+
+  if (strcasecmp(mt->name, "title") == 0)
+  {
+    if ( (!*mt->type)
+         | (strcasecmp(mt->type, "main") == 0)
+         | (strcasecmp(mt->type, "long") == 0) )
+    {
+      if (mr->ntitles)
+        strcpy(title_tag, "2460");
+      else
+        strcpy(title_tag, "245 ");
+
+      mr->ntitles++;
+
+      sprintf(pre, "%s $a", title_tag);
+    }
+  }
+
+  else if (strcasecmp(mt->name, "creator") == 0) 
+  {
+    if (!*mt->type)
+      strcpy(pre, "720  $a");
+    else if ( (strcasecmp(mt->type, "name") == 0)
+            || (strcasecmp(mt->type, "personal") == 0) )
+      strcpy(pre, "700  $a");
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+
+  else if (strcasecmp(mt->name, "publisher") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "260  $b");
+    else if (strcasecmp(mt->type, "name") == 0)
+      strcpy(pre, "260  $b");
+  }
+
+  else if (strncasecmp(mt->name, "contributor", 11) == 0)     /* Note both pl and sgl allowed */
+  {
+    if (!*mt->type)
+      strcpy(pre, "720  $a");
+    else if ( (strcasecmp(mt->type, "name") == 0)
+            || (strcasecmp(mt->type, "personal") == 0) )
+      strcpy(pre, "700  $a");
+    else if (strcasecmp(mt->type, "corporate") == 0)
+      strcpy(pre, "710  $a");
+  }
+
+
+  else if (strcasecmp(mt->name, "coverage") == 0)
+  {
+    if (!*mt->type)
+      strcpy(pre, "500  $a");
+    else if (strcasecmp(mt->type , "spatial") == 0)
+      strcpy(pre, "255  $c");
+    else if (strcasecmp(mt->type,  "temporal") == 0)
+      strcpy(pre, "513  $b");
+  }
+
+
+  else if (strcasecmp(mt->name, "source") == 0)
+    strcpy(pre, "7860 $n");
+
+  else if (strcasecmp(mt->name, "rights") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "540  $a");
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(pre, "856  $3 rights $a");
+  }
+
+  else if (strcasecmp(mt->name, "identifier") == 0)
+
+/* Not supported: Indicator for urn! (See crosswalk)            */
+
+  {
+    if (!*mt->scheme)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "url") == 0)
+      strcpy(mr->url, mt->value);
+    else if (strcasecmp(mt->scheme, "urn") == 0)
+      strcpy(pre, "856  $u urn:");
+    else if (strcasecmp(mt->scheme, "isbn") == 0)
+      strcpy(pre, "020  $a");
+    else if (strcasecmp(mt->scheme, "issn") == 0)
+      strcpy(pre, "022  $a");
+    else
+      strcpy(pre, "0248 $a");
+  }
+
+  else if (strcasecmp(mt->name, "format") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(mr->fmat, mt->value);
+    else if (strcasecmp(mt->scheme, "imt") == 0)
+      strcpy(mr->fmat, mt->value);
+    else if (strcasecmp(mt->scheme, "mime") == 0)
+      strcpy(mr->fmat, mt->value);
+  }
+
+  else if (strcasecmp(mt->name, "subject") == 0)
+  {
+    if (!*mt->scheme)
+      strcpy(pre, "653  $a");
+    else if (strcasecmp(mt->scheme, "lcc") == 0)
+      strcpy(pre, "050  $a");
+    else if (strcasecmp(mt->scheme, "lcsh") == 0)
+      strcpy(pre, "650  $a");
+    else if (strcasecmp(mt->scheme, "ddc") == 0)
+      strcpy(pre, "082  $a");
+  }
+
+
+  else if (strcasecmp(mt->name, "language") == 0)
+  {
+    if (strcasecmp(mt->scheme, "z39.53") == 0)
+    {
+      if (strlen(mt->value) == 3)
+        put008(mr->s008, mt->value, F008_LANGUAGE);
+    }
+  }
+
+
+  else if (strcasecmp(mt->name, "date") == 0)
+  {
+    if ( (strncasecmp(mt->scheme, "ans", 3) == 0) ||
+         (strncasecmp(mt->scheme, "iso", 3) == 0) )
+    {
+      if ( (!*mt->type) || ( strcasecmp(mt->type, "current") == 0) )
+      {
+        if (find_year(mt->value))
+        {
+          strcpy(mr->year, mt->value);
+          put008(mr->s008, mt->value, F008_DATE1);
+        }
+      }
+    }
+  }
+
+  if (*pre)
+  {
+    strcat(mr->marcline, pre);
+    strcat(mr->marcline, " ");
+    strcat(mr->marcline, mt->value);
+    strcat(mr->marcline, "\n");
+  }
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/util.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/util.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/util.c	(revision 16561)
@@ -0,0 +1,472 @@
+/* ------------------------------------------------------------------- */
+/* util      : Several utilities for the d2m converter                 */
+/*                                                                     */
+/* Author    : Ole Husby                                               */
+/* Updated   : 1998-09-30                                              */
+/* ------------------------------------------------------------------- */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "d2m.h"
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Produce MARC line-format output                                     */
+/* ------------------------------------------------------------------- */
+
+void write_field(char *l, FILE *of, int linel, int llen, int slen)
+{
+  char pre[64], line[256], *p, *q, *n;
+  int more, i, spos, compact, ctr;
+
+  ctr = 0;
+  *pre = *line = 0;
+  p = strdup(l);
+  q = p;
+
+  more = TRUE;
+  compact = FALSE;  
+
+  while (more)
+  {
+
+    ctr++;
+    if (ctr > 20) more = FALSE;
+
+    if (strlen(q) <= linel - 8)           /* String fits into 1 line  */
+    {
+      if (compact)
+      {
+        fprintf(of, "%s%s\n", pre, q);
+        more = FALSE;
+      }
+      else
+      {
+        n = strstr(q + 6, "$");
+
+        if ( (n - q ) > 5)
+        {
+          spos = (n - q);
+          strncat(line, q, spos);
+          q = n;
+          fprintf(of, "%s%s\n", pre, line);
+          strcpy(pre, "     ");
+          *line = 0;
+        }
+        else
+        {
+          fprintf(of, "%s%s\n", pre, q);
+          more = FALSE;
+        }
+      }
+    }
+
+    else                                  /* Look for splitting point */
+    {
+      spos = 0;
+
+      if (!compact)
+      {
+        for (i = 6; i < linel - 8; i++)
+        {
+          if (q[i] == '$')
+          {
+            spos = i + 1;
+            break;
+          }
+        } 
+      }
+
+ 
+      if (!spos)
+      {
+        for (i = linel - 9; i > slen; i--)
+        {
+          if ( !compact && (q[i] == '$'))
+          {
+            spos = i + 1;
+            break;
+          }
+          else if (q[i] == ' ')
+          {
+            spos = i;
+            break;
+          }
+        }
+      }
+
+      if (spos)
+      {
+         strncat(line, q, spos);
+         q +=  spos + 1;
+      }
+      else
+      {
+         strncat(line, q, llen);
+         strcat(line, "-");
+         q += llen;
+      }
+
+      fprintf(of, "%s%s\n", pre, line);
+      strcpy(pre, "     ");
+      *line = 0;
+    }
+  }
+  free(p);
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Fetching HTTP POST data                                             */
+/* ------------------------------------------------------------------- */
+
+char *poq() 
+{
+    int m=0, c, cl;
+    char *rec;
+
+    if(strcmp(getenv("REQUEST_METHOD"),"POST"))
+      return NULL;
+
+    cl = atoi(getenv("CONTENT_LENGTH"));
+
+    if (cl)
+      rec = (char*) malloc(cl + 1);
+    else
+      return NULL;
+    
+    while (c = fgetc(stdin))
+    {
+      rec[m] = c;
+      m++;
+      if (m >= cl) break;
+    }
+
+    rec[m] = 0;
+
+    return rec;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* ------------------------------------------------------------------- */
+
+int unesc (char h, char l)
+{
+   char buf[3];
+   int i;
+
+   buf[0]=h;
+   buf[1]=l;
+   buf[2]='\0';
+   sscanf(buf,"%x",&i);
+   return (i);
+}
+
+
+/* ------------------------------------------------------------------- */
+/* Replace '+' with space and unescape hex coded characters.           */
+/* ------------------------------------------------------------------- */
+
+void unescape (char *s)
+{
+   char *rp, *wp;  /* read position and write position in string */
+   rp=wp=s;
+   while (*rp != '\0') {
+      if (*rp=='+') {
+         *(wp++)=' ';
+         rp++;
+      }
+      else if (*rp=='%') {
+         int a,b;
+         rp++;   /* skip the '%' */
+         a = (unsigned char) *(rp++);
+         b = (unsigned char) *(rp++);
+         *(wp++)=unesc (a,b);
+      } else
+         *(wp++)=*(rp++);
+    }
+    *wp = '\0';
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Case independent string search.                                     */
+/*                                                                     */
+/* Call syntax like strstr()                                           */
+/* ------------------------------------------------------------------- */
+
+char *cstr(char *h, char *n)
+{
+  char *nn;
+  char *hh;
+  char *p, *s;
+  long i;
+
+
+/* Returns NULL if either of strings are NULL                     */
+
+  if (!*h || !*n)
+    return (char *) NULL;
+
+
+/* Make a copy hh of h and nn of n                                */
+
+  hh = (char *) strdup(h);
+  nn = (char *) strdup(n);
+
+/* Converts hh and nn to lower                                    */
+
+  for (i = 0; i < strlen(nn); i++)
+    nn[i] = tolower(n[i]);
+
+  for (i = 0; i < strlen(hh); i++)
+    hh[i] = tolower(h[i]);
+
+/* Searches for nn in hh using strstr                             */
+
+  p = (char *) strstr(hh, nn);
+
+/* If found, calculates corresponding pointer into h              */
+
+  if (!p)
+    s = (char *) NULL;
+  else
+  {
+    i = strlen(hh) - strlen(p);
+    s = h + i;
+  }
+
+/* Frees string copies and returns                                */
+
+  free(hh);
+  free(nn);
+
+  return s; 
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Write and remove tracefile                                          */
+/* ------------------------------------------------------------------- */
+
+int write_trace(char *tracefile, FILE *of)
+{
+  char line[10000], *l;
+  FILE *tf;
+
+  tf = fopen(tracefile, "r");
+  if (!tf)
+    return 0;
+
+  while (l = fgets(line, 9999, tf))
+    fprintf(of, "%s", l);
+
+  fclose(tf);
+  remove(tracefile);  
+  return 1;
+}
+
+
+/* ------------------------------------------------------------------- */
+/* Insert field into 008                                               */
+/* ------------------------------------------------------------------- */
+
+int put008(char *s8, char *str, int f8)
+{
+   int i, len, pos, nlen, npos, append=0;
+
+   if (strlen(s8) != 40)
+     return 0;
+
+   switch (f8)
+   {
+     case (F008_DATE_ENTERED) :
+     {
+       pos = 0;
+       len = 6;
+       break;
+     }
+     case (F008_TYPE_OF_DATE) :
+     {
+       pos = 6;
+       len = 1;
+       break;
+     }
+     case (F008_DATE1) :
+     {
+       pos = 7;
+       len = 4;
+       break;
+     }
+     case (F008_FORM_OF_PUBLICATION) :
+     {
+       pos = 24;
+       len = 4;
+       append = 1;
+       break;
+     }
+     case (F008_LANGUAGE) :
+     {
+       pos = 35;
+       len = 3;
+       break;
+     }
+     default :
+       return 0;
+   }
+
+
+   npos = pos;
+   nlen = strlen(str);
+   if (nlen > len)
+     return 0;
+
+   if (nlen < len)
+   {
+     if (append)
+     {
+       npos = 0;
+       for (i = pos; i < pos + len; i++)
+       {
+         if ( s8[i] == ' ' )
+         {
+           npos = i;
+           break; 
+         }
+       }
+       if (npos == 0)
+         return 0;
+       if (npos >= pos + len)
+         return 0;
+       if (npos + nlen > pos + len)
+         return 0;
+     }
+     else
+       return 0;
+   }
+/* printf
+     ("pos %d len %d npos %d nlen %d str %s\n", 
+     pos, len, npos, nlen, str);
+*/
+
+   for (i = 0; i < nlen; i++)
+     s8[npos+i] = str[i];
+
+   return 1;
+}
+
+
+/* ------------------------------------------------------------------- */
+/* Converts date to YYYY                                               */
+/* ------------------------------------------------------------------- */
+
+int find_year(char *str)
+{
+  int i;
+
+  if (strlen(str) < 4)
+    return 0;
+
+  str[4] = 0;
+
+  for (i = 0; i < 4; i++)
+  {
+    if (!isdigit(str[i]))
+      return 0;
+  }
+ 
+  return 1;
+}
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* Splits personal name at first "," into $a and $h                    */
+/* ------------------------------------------------------------------- */
+
+int split_name(char *str)
+{
+  char *p, *q, subtag[5];
+
+  if (!strstr(str, ","))
+    return 1;
+
+  p = (char *) malloc(strlen(str) + 50);
+  *p = 0;
+  *subtag = 0;
+
+  q = strtok(str, ",");
+  if (q)
+  {
+    strcpy(p, q);
+    q = strtok(NULL, "\0");
+    if (q)
+    {
+      while ( ( q[0] == ' ' ) && ( strlen(q) ) )
+        q++;
+      strcat(p, " $h ");
+      strcat(p, q);
+    }
+    strcpy(str, p);
+  }
+
+  free(p);
+
+  return 1;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* Splits subject heading on "-". First occurence go into $a, second   */ 
+/* into $b, all the rest into $x                                       */
+/* ------------------------------------------------------------------- */
+
+int split_subj(char *str)
+{
+  char *p, *q, subtag[5];
+
+  if (!strstr(str, "-"))
+    return 1;
+
+  p = (char *) malloc(strlen(str) + 50);
+  *p = 0;
+  *subtag = 0;
+
+  q = strtok(str, "-");
+  while (q)
+  {
+    while ( ( q[0] == ' ' ) && ( strlen(q) ) )
+      q++;
+
+    while( ( q[strlen(q) - 1] == ' ' ) && ( strlen(q) ) )
+      q[strlen(q) - 1] = '\0';
+
+    if (*q)
+    {
+      strcat(p, subtag);
+      strcat(p, q);
+      if (!*subtag)
+        strcpy(subtag, " $b ");
+      else
+        strcpy(subtag, " $x ");
+    }
+    q = strtok(NULL, "-");
+  }
+
+  strcpy(str, p);
+  free(p);
+
+  return 1;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/wfetch-l.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/wfetch-l.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/wfetch-l.c	(revision 16561)
@@ -0,0 +1,19 @@
+/* ------------------------------------------------------------------- */
+/* wfetch    : Fetch a remote file using the htget() "HTTP client"     */
+/*                                                                     */
+/* syntax    : wfatch(char *url, char *filename)                       */
+/*                                                                     */
+/* Returns   : 0 ALWAYS!                                               */
+/*                                                                     */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-10-04                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+int wfetch(char *url, char *filename)
+{
+  return 0;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/wfetch.c
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/wfetch.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/wfetch.c	(revision 16561)
@@ -0,0 +1,53 @@
+/* ------------------------------------------------------------------- */
+/* wfetch    : Fetch a remote file using the htget() "HTTP client"     */
+/*                                                                     */
+/* syntax    : wfatch(char *url, char *filename)                       */
+/*                                                                     */
+/*             The file will be written to "filename"                  */
+/*                                                                     */
+/* Returns   : 0 if fail                                               */
+/*           : 1 if HTML file (text/html)                              */
+/*           : 2 if MARC file (application/marc)                       */
+/*                                                                     */
+/* NOTE      : Timeout period (HTTP) is set here.                      */
+/*                                                                     */
+/* Author    : Ole Husby, BIBSYS                                       */
+/* Updated   : 1998-08-11                                              */
+/* ------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define  TIMEOUT_SECONDS       10
+
+int wfetch(char *url, char *filename)
+{
+  int i, type, argtype, timeout, rc, retc;
+  char iurl[1024], content_type[128], location[128];
+
+  retc = 1;
+
+  strcpy(iurl, url);
+  timeout = TIMEOUT_SECONDS;
+
+  type = 6;                           /* Fetch HTML HEAD or MARC      */
+
+  rc = htget(iurl, type, timeout, filename, content_type, location);
+
+
+/* Try again if redirect                                              */
+
+  if ( ( ( rc == 301 ) || ( rc == 302 ) ) && *location )
+  {
+    strcpy(iurl, location);
+    rc = htget(iurl, type, timeout, filename, content_type, location);
+  }
+
+  if (rc)
+    retc = 0;
+  else if (strcmp(content_type, "application/marc") == 0)
+    retc = 2;
+
+  return retc;
+}
Index: /gsdl/trunk/gs2runtime/packages/d2m/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/packages/d2m/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/packages/d2m/win32.mak	(revision 16561)
@@ -0,0 +1,51 @@
+# ///////////////// d2m : Dublin Core to MARC converter /////////////////////
+#
+#
+# Generated for Greenstone kjdon May 2005.
+# Used by z3950server
+
+GSDLHOME = ..\..
+GSDL_VC4 = 0
+CC = cl
+AR = lib
+
+DLL = 0
+DLLDEBUG = 0
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD 
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+o = .obj
+
+DEFS=-DHAVE_CONFIG_H -D__WIN32__ -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES= -I"$(GSDLHOME)"
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+LIB_OBJS = MARC2709$o MARCtidy$o usMARC$o util$o strcasecmp$o
+
+all: libd2m.lib
+
+libd2m.lib: $(LIB_OBJS)
+	if EXIST libd2m.lib del libd2m.lib
+	$(AR) -nologo /out:libd2m.lib $(LIB_OBJS)
+
+install:
+
+clean:
+	del *$o 
+	del libd2m.lib
+
+distclean: clean
+	rm -f Makefile config.h
+
Index: /gsdl/trunk/gs2runtime/src/colservr/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/Makefile.in	(revision 16561)
@@ -0,0 +1,146 @@
+###########################################################################
+#
+# Makefile -- src/colservr
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_DEFINES = -DUSE_SQLITE
+SQLITE_INCLUDES = -I$(PACKAGES_DIR)/sqlite/include
+else
+SQLITE_DEFINES = 
+SQLITE_INCLUDES = 
+endif
+
+
+AR = ar
+CC = @CC@
+CXX = @CXX@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DGSDLSERVER $(SQLITE_DEFINES)
+# Do NOT add the "recpt" directory here: the colserver should be independent of the receptionist!
+INCLUDES = @GDBM_INCLUDE@ -I. -I$(GSDL_DIR) -I$(GSDL_DIR)/src/lib \
+	-I$(INDEXERS_DIR)/mg -I$(INDEXERS_DIR)/mg/lib -I$(INDEXERS_DIR)/mg/src/text \
+	-I$(INDEXERS_DIR)/mgpp/lib -I$(INDEXERS_DIR)/mgpp/text \
+	-I$(PACKAGES_DIR)/expat/include $(SQLITE_INCLUDES)
+RANLIB = @RANLIB@
+
+
+COMPILE = $(CC) -c $(CFLAGS) $(DEFS) $(INCLUDES)
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+        browsefilter.cpp \
+	collectserver.cpp \
+	collectset.cpp \
+        colservrconfig.cpp \
+	comtypes.cpp \
+	expat_document.cpp \
+	expat_resultset.cpp \
+	filter.cpp \
+	lucenequeryfilter.cpp \
+	lucenesearch.cpp \
+	lucenesource.cpp \
+	maptools.cpp \
+	mgq.c \
+	mgqueryfilter.cpp \
+	mgsearch.cpp \
+	mgsource.cpp \
+	mgppqueryfilter.cpp \
+	mgppsearch.cpp \
+	phrasequeryfilter.cpp \
+	phrasesearch.cpp \
+	querycache.cpp \
+	queryfilter.cpp \
+	queryinfo.cpp \
+	search.cpp \
+	source.cpp \
+	sqlbrowsefilter.cpp
+
+OBJECTS = \
+        browsefilter.o \
+	collectserver.o \
+	collectset.o \
+        colservrconfig.o \
+	comtypes.o \
+	expat_document.o \
+	expat_resultset.o \
+	filter.o \
+	lucenequeryfilter.o \
+	lucenesearch.o \
+	lucenesource.o \
+	maptools.o \
+	mgq.o \
+	mgqueryfilter.o \
+	mgsearch.o \
+	mgsource.o \
+	mgppqueryfilter.o \
+	mgppsearch.o \
+	phrasequeryfilter.o \
+	phrasesearch.o \
+	querycache.o \
+	queryfilter.o \
+	queryinfo.o \
+	search.o \
+	source.o \
+	sqlbrowsefilter.o
+
+LIBRARY = gsdlcolservr.a
+
+
+# Default target: make gsdlcolservr.a
+all: $(LIBRARY)
+
+gsdlcolservr.a: $(OBJECTS)
+	rm -f $(LIBRARY)
+	$(AR) cru $(LIBRARY) $(OBJECTS)
+	$(RANLIB) $(LIBRARY)
+
+install:
+
+clean:
+	rm -f $(OBJECTS) $(LIBRARY)
+
+distclean:
+	rm -f $(OBJECTS) $(LIBRARY) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2runtime/src/colservr/browsefilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/browsefilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/browsefilter.cpp	(revision 16561)
@@ -0,0 +1,309 @@
+/**********************************************************************
+ *
+ * browsefilter.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.
+ *
+ *********************************************************************/
+
+#include "browsefilter.h"
+#include "fileutil.h"
+
+
+browsefilterclass::browsefilterclass () {
+  db_ptr = NULL;
+
+  // -- onePerQuery StartResults   integer
+  FilterOption_t filtopt;
+  filtopt.name = "StartResults";
+  filtopt.type = FilterOption_t::integert;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "1";
+  filtopt.validValues.push_back("1");
+  filtopt.validValues.push_back("10000");
+  filterOptions["StartResults"] = filtopt;
+
+  // -- onePerQuery EndResults     integer
+  filtopt.clear();
+  filtopt.name = "EndResults";
+  filtopt.type = FilterOption_t::integert;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "-1";
+  filtopt.validValues.push_back("-1");
+  filtopt.validValues.push_back("10000");
+  filterOptions["EndResults"] = filtopt;
+
+  // -- onePerQuery ParentNode     string ("" will return the browsing available)
+  filtopt.clear();
+  filtopt.name = "ParentNode";
+  filtopt.type = FilterOption_t::stringt;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = g_EmptyText;
+  filterOptions["ParentNode"] = filtopt;
+}
+
+browsefilterclass::~browsefilterclass () {
+}
+
+void browsefilterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  filterclass::configure (key, cfgline);
+  if (key == "indexstem") {
+    indexstem = cfgline[0];
+  }
+}
+
+bool browsefilterclass::init (ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  if (!filterclass::init(logout)) return false;
+
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: browsefilter contains a null dbclass\n\n";
+    return false;
+  }
+
+  if (indexstem.empty()) {
+    indexstem = collection;
+  }
+
+  db_filename = resolve_db_filename(indexstem,db_ptr->getfileextension());
+  if (!file_exists(db_filename)) {
+    logout << text_t2ascii
+	   << "warning: database \"" << db_filename << "\" does not exist\n\n";
+    //    return false;
+  }
+
+  return true;
+}
+
+void browsefilterclass::filter (const FilterRequest_t &request,
+				FilterResponse_t &response,
+				comerror_t &err, ostream &logout) {
+  int numDocs = 0;
+  outconvertclass text_t2ascii;
+
+  response.clear ();
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: browsefilter contains a null dbclass\n\n";
+    err = configurationError;
+    return;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \""
+	   << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return;
+  }
+
+  // get the browse parameters
+  int startresults = filterOptions["StartResults"].defaultValue.getint();
+  int endresults = filterOptions["EndResults"].defaultValue.getint();
+  text_t parentnode = filterOptions["ParentNode"].defaultValue;
+  OptionValue_tarray::const_iterator options_here = request.filterOptions.begin();
+  OptionValue_tarray::const_iterator options_end = request.filterOptions.end();
+  while (options_here != options_end) {
+    if ((*options_here).name == "StartResults") 
+      startresults = (*options_here).value.getint();
+    else if ((*options_here).name == "EndResults")
+      endresults = (*options_here).value.getint();
+    else if ((*options_here).name == "ParentNode")
+      parentnode = (*options_here).value;
+    else {
+      logout << text_t2ascii
+	     << "warning: unknown browsefilter option \""
+	     << (*options_here).name
+	     << "\" ignored.\n\n";
+    }
+
+    ++options_here;
+  }
+
+  infodbclass info;
+
+  // translate any ".fc", ".pr" etc. stuff in the parentnode
+  parentnode = db_ptr->translate_OID (parentnode, info);
+
+  // adjust topmost browsing node
+  if (parentnode.empty()) parentnode = "browse";
+
+  // get the node
+  if ((request.filterResultOptions & FROID) ||
+      (request.filterResultOptions & FRmetadata)) {
+    if (!db_ptr->getinfo(parentnode, info)) {
+      // didn't find the node
+      logout << text_t2ascii
+	     << "warning: lookup for node \"" << parentnode 
+	     << "\" failed for browsefilter.\n\n";
+    } else {
+      // found the node
+
+      // replace " with the parent node name and split the contains string
+      // into the result set
+      text_tarray resultset;
+      text_t tmptext;
+      text_t &contains = info["contains"];
+      text_t::iterator contains_here = contains.begin();
+      text_t::iterator contains_end = contains.end();
+      while (contains_here != contains_end) {
+	if (*contains_here == '"') tmptext += parentnode;
+	else if (*contains_here == ';') {
+	  if (!tmptext.empty()) resultset.push_back (tmptext);
+	  tmptext.clear();
+	} else tmptext.push_back(*contains_here);
+	
+	++contains_here;
+      }
+      // insert the last result in the set
+      if (!tmptext.empty()) resultset.push_back (tmptext);
+
+      text_tarray offset_resultset;
+      text_t &md_type = info["mdtype"];
+      if (!md_type.empty())
+	{
+	  text_t &md_offset = info["mdoffset"];
+	  if (!md_offset.empty())
+	    {
+	      text_t offsettext;
+
+	      text_t::iterator offset_here = md_offset.begin();
+	      text_t::iterator offset_end = md_offset.end();
+	      while (offset_here != offset_end) 
+		{
+		  if (*offset_here == ';')
+		    {
+		      if (offsettext.empty())
+			{
+			  offset_resultset.push_back ("0");
+			}
+		      else
+			{
+			  offset_resultset.push_back (offsettext);
+			}
+		      offsettext.clear();
+		    } 
+		  else 
+		    {
+		      offsettext.push_back(*offset_here);
+		    }
+		  
+		  ++offset_here;
+		}
+	      // insert the last result in the set
+	      if (offsettext.empty())
+		{
+		  offset_resultset.push_back ("0");
+		}
+	      else
+		{
+		  offset_resultset.push_back (offsettext);
+		}
+	    }
+	  else
+	    {
+	      // add 0 offset for each 'contains' entry
+	      text_tarray::iterator result_here = resultset.begin();
+	      text_tarray::iterator result_end = resultset.end();
+	      while (result_here != result_end) {
+		offset_resultset.push_back("0");
+		++result_here;
+	      }
+	    }
+
+	  // do an intersection with the input set
+	  if (!request.docSet.empty()) {
+
+	    text_tarray intersect_resultset;
+	    text_tarray intersect_offset_resultset;
+	    
+	    text_tarray::const_iterator resultset_here = resultset.begin();
+	    text_tarray::const_iterator resultset_end = resultset.end();
+	    text_tarray::const_iterator offset_resultset_here = offset_resultset.begin();
+	    
+	    while (resultset_here != resultset_end) {
+	      if (in_set (request.docSet, *resultset_here))
+		{
+		  intersect_resultset.push_back (*resultset_here);
+		  intersect_offset_resultset.push_back (*offset_resultset_here);
+		}
+	      ++resultset_here;
+	      ++offset_resultset_here;
+	    }
+	    resultset = intersect_resultset;
+	    offset_resultset = intersect_offset_resultset;
+	  }
+	}
+      else
+	{
+	  // do an intersection with the input set
+	  if (!request.docSet.empty()) {
+	    intersect (resultset, request.docSet);
+	  }
+
+	  // add 0 offset for each 'contains' entry
+	  text_tarray::iterator result_here = resultset.begin();
+	  text_tarray::iterator result_end = resultset.end();
+	  while (result_here != result_end) {
+	    offset_resultset.push_back("0");
+	    ++result_here;
+	  }
+	}
+
+      // create the response
+      numDocs = resultset.size();
+      int resultnum = 1;
+      ResultDocInfo_t resultdoc;
+      text_tarray::iterator result_here = resultset.begin();
+      text_tarray::iterator result_end = resultset.end();
+      text_tarray::iterator offset_result_here = offset_resultset.begin();
+
+      while (result_here != result_end) {
+	// if endresults is -1 get all results
+	if ((endresults != -1) && (resultnum > endresults)) break;
+	if (resultnum >= startresults) {
+	  resultdoc.OID = (*result_here);
+	  if (!md_type.empty())
+	    {
+	      resultdoc.classifier_metadata_type = md_type;
+	      resultdoc.classifier_metadata_offset = offset_result_here->getint();
+	    }
+	  response.docInfo.push_back(resultdoc);
+	}
+
+	++resultnum;
+	++result_here;
+	if (!md_type.empty()) ++offset_result_here;
+      }
+    }
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  response.numDocs = numDocs;
+  response.isApprox = Exact;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/browsefilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/browsefilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/browsefilter.h	(revision 16561)
@@ -0,0 +1,64 @@
+/**********************************************************************
+ *
+ * browsefilter.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.
+ *
+ *********************************************************************/
+
+
+#ifndef BROWSEFILTER_H
+#define BROWSEFILTER_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+#include "filter.h"
+#include "dbclass.h"
+
+
+class browsefilterclass : public filterclass {
+protected:
+  text_t db_filename;
+  text_t indexstem;
+  dbclass *db_ptr;
+
+public:
+  browsefilterclass ();
+  virtual ~browsefilterclass ();
+
+  // configure should be called once for each configuration line
+  // default configures the default filter options
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // the db ptr remains the responsability of the calling code and
+  // should be destroyed after this browsefilter is destroyed
+  void set_db_ptr (dbclass *db_ptr_arg) { db_ptr = db_ptr_arg; }
+
+  bool init (ostream &logout);
+  text_t get_filter_name () {return "BrowseFilter";}
+  void filter (const FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/collectserver.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/collectserver.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/collectserver.cpp	(revision 16561)
@@ -0,0 +1,538 @@
+
+/**********************************************************************
+ *
+ * collectserver.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.
+ *
+ *********************************************************************/
+
+#include "collectserver.h"
+#include "OIDtools.h"
+#include <assert.h>
+#include "display.h"
+
+
+collectserver::collectserver () {
+  configinfo.collection = "null";
+}
+
+collectserver::~collectserver () {
+
+  // clean up the sources
+  sourcelistclass::iterator source_here = sources.begin();
+  sourcelistclass::iterator source_end = sources.end();
+  while (source_here != source_end) {
+    if ((*source_here).s != NULL)
+      delete (*source_here).s;
+    ++source_here;
+  }
+  sources.clear();
+
+  // clean up the filters
+  filtermapclass::iterator filter_here = filters.begin();
+  filtermapclass::iterator filter_end = filters.end();
+  while (filter_here != filter_end) {
+    if ((*filter_here).second.f != NULL) 
+      delete (*filter_here).second.f;
+    ++filter_here;
+  }
+  filters.clear();
+}
+
+// configure should be called for each line in the
+// configuration files to configure the collection server and everything
+// it contains. The configuration should take place just before initialisationhttps://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%3Fui%3Dhtml%26zy%3Dl&ltmpl=ca_tlsosm_video&ltmplcache=2&hl=en.
+void collectserver::configure (const text_t &key, const text_tarray &cfgline) {
+  if (cfgline.size() >= 1) {
+    const text_t &value = cfgline[0];
+    if (key == "plugin")
+    {
+    	//get the plugin name
+	const text_t &name = cfgline[0];
+	
+	if (name == "HTMLPlug")
+	{
+		for (int hI = 1; hI < cfgline.size(); hI++)
+		{
+			const text_t &plugOption = cfgline[hI];
+			
+			if (plugOption == "-tidy_html") 
+			{
+				collectinfo.useBook = true;
+				break;
+			}
+		}
+	}
+    }
+    else if (key == "gsdlhome") configinfo.gsdlhome = value;
+    else if (key == "gdbmhome") configinfo.dbhome = value;
+    else if (key == "collecthome") configinfo.collecthome = value;
+    else if (key == "collection") {
+      configinfo.collection = value;
+      collectinfo.shortInfo.name = value;
+    } 
+    else if (key == "collectdir") configinfo.collectdir = value;
+    else if (key == "host") collectinfo.shortInfo.host = value;
+    else if (key == "port") collectinfo.shortInfo.port = value.getint();
+    else if (key == "public") {
+      if (value == "true") collectinfo.isPublic = true;
+      else collectinfo.isPublic = false;
+    } else if (key == "beta") {
+      if (value == "true") collectinfo.isBeta = true;
+      else collectinfo.isBeta = false;
+    } else if (key == "collectgroup") {
+      if (value == "true") collectinfo.isCollectGroup = true;
+      else collectinfo.isCollectGroup = false;
+    } else if ((key == "ccscols") || (key == "supercollection")) collectinfo.ccsCols = cfgline;
+    else if (key == "supercollectionoptions") {
+      text_tarray::const_iterator begin = cfgline.begin();
+      text_tarray::const_iterator end = cfgline.end();
+      while(begin != end) {
+	
+	if (*begin == "uniform_search_results_formatting") {
+	  collectinfo.ccsOptions |= CCSUniformSearchResultsFormatting;
+	}
+	begin++;
+      }
+    }
+    else if (key == "builddate") collectinfo.buildDate = value.getint();
+    else if (key == "languages") collectinfo.languages = cfgline;
+    else if (key == "numdocs") collectinfo.numDocs = value.getint();
+    else if (key == "numsections") collectinfo.numSections = value.getint();
+    else if (key == "numwords") collectinfo.numWords = value.getint();
+    else if (key == "numbytes") collectinfo.numBytes = value.getint();
+    else if (key == "stemindexes") collectinfo.stemIndexes = value.getint();
+    else if (key == "collectionmeta") {
+      // genuine collmeta get added as collectionmeta and collection_macros
+      // .collmeta just get added as collection_macros
+      text_t params;
+      if (cfgline.size() == 3) {
+	// get the params for later
+	text_t::const_iterator first=cfgline[1].begin()+1;
+	text_t::const_iterator last=cfgline[1].end()-1;
+	params=substr(first, last);
+      }
+      
+      text_t meta_name = cfgline[0];
+      if (*(meta_name.begin())=='.') {
+	// a .xxx collectionmeta. strip off the . and
+	// look it up in the indexmap to get the actual value
+	
+	text_t name = substr(cfgline[0].begin()+1,cfgline[0].end());
+	text_t new_name;
+	if (indexmap.from2to(name, new_name)) {
+	  meta_name = new_name;
+	} 
+      } else {
+	// add them to collectionmeta
+	text_tmap lang_map = collectinfo.collectionmeta[cfgline[0]];
+	if (cfgline.size() == 2) {
+	  lang_map[g_EmptyText] = cfgline[1];
+	} else if (cfgline.size() == 3 ) {
+	  // get the lang out of params
+	  paramhashtype params_hash;
+	  splitparams(params, params_hash);
+	  
+	  text_t lang = params_hash["l"];
+	  lang_map[lang] = cfgline[2];
+	  if (lang_map[g_EmptyText].empty()) {
+	    // want the first one as the default if no default specified
+	    lang_map[g_EmptyText] = cfgline[2];
+	  }
+	}
+	collectinfo.collectionmeta[cfgline[0]] = lang_map;
+	
+      }
+      
+      // add all collectionmeta to macro list
+      text_tmap params_map = collectinfo.collection_macros[meta_name];
+      
+      if (cfgline.size() == 2) {// no params for this macrohttp://uk.sports.yahoo.com/13032007/13/news-brief.html
+	params_map[g_EmptyText] = cfgline[1];
+      }
+      else if (cfgline.size() == 3) {// has params
+	params_map[params] = cfgline[2];
+	if (params_map[g_EmptyText].empty()) {
+	  params_map[g_EmptyText] = cfgline[2];
+	}
+      }
+      collectinfo.collection_macros[meta_name] = params_map;
+    }
+    else if (key == "collectionmacro") {
+      text_t nobrackets;
+      text_tmap params_map = collectinfo.collection_macros[cfgline[0]];
+      // add all to macro list
+      if (cfgline.size() == 2) { // no params for this macro
+	params_map[g_EmptyText] = cfgline[1];
+      }
+      else if (cfgline.size() == 3) {// has params
+	// strip [ ] brackets from params
+	text_t::const_iterator first=cfgline[1].begin()+1;
+	text_t::const_iterator last=cfgline[1].end()-1;
+	nobrackets=substr(first, last);
+	params_map[nobrackets] = cfgline[2];
+      }
+      collectinfo.collection_macros[cfgline[0]] = params_map;
+      
+    } else if (key == "format" && cfgline.size() == 2)
+      collectinfo.format[cfgline[0]] = cfgline[1];
+    else if (key == "building" && cfgline.size() == 2)
+      collectinfo.building[cfgline[0]] = cfgline[1];
+    else if (key == "httpdomain") collectinfo.httpdomain = value;
+    else if (key == "httpprefix") collectinfo.httpprefix = value;
+    else if (key == "receptionist") collectinfo.receptionist = value;
+    else if (key == "buildtype") collectinfo.buildType = value;
+    // backwards compatibility - searchytpes is now a format statement
+    else if (key == "searchtype") { // means buildtype is mgpp
+      if (collectinfo.buildType.empty()) {
+	collectinfo.buildType = "mgpp";
+      }
+      joinchar(cfgline, ',', collectinfo.format["SearchTypes"]);
+      //collectinfo.searchTypes = cfgline;
+    }
+    else if (key == "separate_cjk") {
+      if (value == "true") collectinfo.isSegmented = true;
+      else collectinfo.isSegmented = false;
+    }
+    // What have we set in our collect.cfg file :  document or collection ?
+    else if (key == "authenticate") collectinfo.authenticate = value;
+
+    // What have we set for our group list
+    else if (key == "auth_group") joinchar(cfgline,',',collectinfo.auth_group);
+
+    // store all the mappings for use when collection meta is read later
+    // (build.cfg read before collect.cfg)
+    else if (key == "indexmap" || key == "indexfieldmap" || key == "subcollectionmap" || key == "languagemap" || key == "levelmap") {
+      indexmap.importmap (cfgline, true);
+      
+    } 
+    // In the map the key-value pair contain the same
+    // data i.e key == data, if key is 2 then data is 2
+    
+    // What have we set for our public_documents ACL
+    else if (key == "public_documents")
+       {
+	  text_tarray::const_iterator begin = cfgline.begin();
+	  text_tarray::const_iterator end = cfgline.end();
+	  while(begin != end)
+	     {
+		// key = data i.e if key is 2 then data is 2
+		// collectinfo.public_documents[*begin] is the key
+		// *begin is the data value
+
+		collectinfo.public_documents[*begin] = *begin;
+		++begin;
+	     }
+       }
+    
+    // What have we set for our private_documents ACL
+    else if (key == "private_documents")
+       {
+	  text_tarray::const_iterator begin = cfgline.begin();
+	  text_tarray::const_iterator end = cfgline.end();
+	  while(begin != end)
+	     {
+		// key = data i.e if key is 2 then data is 2
+		// collectinfo.public_documents[*begin] is the key
+		// *begin is the data value
+		
+		collectinfo.private_documents[*begin] = *begin;
+		++begin;
+	     }
+       }
+
+    // dynamic_classifier <UniqueID> "<Options>"
+    else if (key == "dynamic_classifier")
+    {
+      collectinfo.dynamic_classifiers[cfgline[0]] = cfgline[1];
+    }
+  }
+  
+  // configure the filters
+  filtermapclass::iterator filter_here = filters.begin();
+  filtermapclass::iterator filter_end = filters.end();
+  while (filter_here != filter_end) {
+    assert ((*filter_here).second.f != NULL);
+    if ((*filter_here).second.f != NULL) 
+      (*filter_here).second.f->configure(key, cfgline);
+
+    ++filter_here;
+  }
+
+  // configure the sources
+  sourcelistclass::iterator source_here = sources.begin();
+  sourcelistclass::iterator source_end = sources.end();
+  while (source_here != source_end) {
+    assert ((*source_here).s != NULL);
+    if ((*source_here).s != NULL)
+      (*source_here).s->configure(key, cfgline);
+    
+    ++source_here;
+  }
+}
+
+
+void collectserver::configure (const text_t &key, const text_t &value) {
+  text_tarray cfgline;
+  cfgline.push_back (value);
+  configure(key, cfgline);
+}
+
+void collectserver::ping (bool &wasSuccess, comerror_t &error, ostream &logout) {
+  // if we've not been properly configured, then it is a foregone
+  // conclusion that we cannot be active
+  if (this->configinfo.collection == "null")
+    {
+      wasSuccess = false;
+    }
+  // if no build date exists, then the collection was probably not built;
+  // ditto if the number of documents is zero, then something is pretty
+  // wrong
+  else if (this->collectinfo.buildDate == 0 ||
+      this->collectinfo.numDocs == 0)
+    {
+      wasSuccess =  false;
+    }
+  // it is probably okay
+  else
+    wasSuccess = true;
+}
+
+
+bool collectserver::init (ostream &logout) {
+  // delete the indexmap
+  indexmap.clear();
+
+  // init the filters
+  filtermapclass::iterator filter_here = filters.begin();
+  filtermapclass::iterator filter_end = filters.end();
+  while (filter_here != filter_end) {
+    assert ((*filter_here).second.f != NULL);
+    if (((*filter_here).second.f != NULL) &&
+	!(*filter_here).second.f->init(logout)) return false;
+    
+    ++filter_here;
+  }
+
+  // init the sources
+  sourcelistclass::iterator source_here = sources.begin();
+  sourcelistclass::iterator source_end = sources.end();
+  while (source_here != source_end) {
+    assert ((*source_here).s != NULL);
+    if (((*source_here).s != NULL) &&
+	!(*source_here).s->init(logout)) return false;
+    
+    ++source_here;
+  }
+
+  return true;
+}
+
+
+void collectserver::get_collectinfo (ColInfoResponse_t &reponse,
+		      comerror_t &err, ostream &/*logout*/) {
+  reponse = collectinfo; 
+  err = noError;
+}
+
+void collectserver::get_filterinfo (InfoFiltersResponse_t &response,
+				    comerror_t &err, ostream &/*logout*/) {
+  response.clear ();
+
+  // get a list of filter names
+  filtermapclass::iterator filter_here = filters.begin();
+  filtermapclass::iterator filter_end = filters.end();
+  while (filter_here != filter_end) {
+    response.filterNames.insert ((*filter_here).first);
+    ++filter_here;
+  }
+
+  err = noError;
+}
+
+void collectserver::get_filteroptions (const InfoFilterOptionsRequest_t &request,
+				       InfoFilterOptionsResponse_t &response, 
+				       comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  filterclass *thisfilter = filters.getfilter(request.filterName);
+  if (thisfilter != NULL) {
+    thisfilter->get_filteroptions (response, err, logout);
+  } else {
+    response.clear ();
+    err = protocolError;
+    logout << text_t2ascii << "Protocol Error: filter options requested for non-existent\n"
+	   << "filter \"" << request.filterName << "\".\n\n";
+  }
+}
+
+void collectserver::filter (FilterRequest_t &request,
+			    FilterResponse_t &response,
+			    comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  // translate any ".fc", ".pr" etc. stuff in the docSet
+  text_t translatedOID;
+  text_tarray translatedOIDs;
+  text_tarray::iterator doc_here = request.docSet.begin();
+  text_tarray::iterator doc_end = request.docSet.end();
+  while (doc_here != doc_end) {
+    if (needs_translating (*doc_here)) {
+      sourcelistclass::iterator source_here = sources.begin();
+      sourcelistclass::iterator source_end = sources.end();
+      while (source_here != source_end) {
+	assert ((*source_here).s != NULL);
+	if (((*source_here).s != NULL) &&
+	    ((*source_here).s->translate_OID (*doc_here, translatedOID, err, logout))) {
+	  if (err != noError) return;
+	  break;
+	}
+	++source_here;
+      }
+      translatedOIDs.push_back (translatedOID);
+    } else {
+      translatedOIDs.push_back (*doc_here);
+    }
+    ++doc_here;
+  }
+  request.docSet = translatedOIDs;
+
+  response.clear();
+
+  filterclass *thisfilter = filters.getfilter(request.filterName);
+  if (thisfilter != NULL) {
+    // filter the data
+    thisfilter->filter (request, response, err, logout);
+    if (err != noError) return;
+    // fill in the metadata for each of the OIDs (if it is requested)
+    if (request.filterResultOptions & FRmetadata) {
+      bool processed = false;
+      ResultDocInfo_tarray::iterator resultdoc_here = response.docInfo.begin();
+      ResultDocInfo_tarray::iterator resultdoc_end = response.docInfo.end();
+      while (resultdoc_here != resultdoc_end) {
+	// try each of the sources in turn
+	sourcelistclass::iterator source_here = sources.begin();
+	sourcelistclass::iterator source_end = sources.end();
+	while (source_here != source_end) {
+	  assert ((*source_here).s != NULL);
+	  if (((*source_here).s != NULL) &&
+	      ((*source_here).s->get_metadata(request.requestParams, request.refParams,
+					      request.getParents, request.fields, 
+					      (*resultdoc_here).OID, (*resultdoc_here).metadata, 
+					      err, logout))) {
+	    if (err != noError) return;
+	    processed = true;
+	    break;
+	  }
+	  ++source_here;
+	}
+	if (!processed) {
+
+	  logout << text_t2ascii << "Protocol Error: nothing processed for "
+	   << "filter \"" << request.filterName << "\".\n\n";
+
+	  err = protocolError;
+	  return;
+	}
+	++resultdoc_here;
+      }
+    }  
+  
+    err = noError;
+  }
+  else
+  {
+    response.clear ();
+    err = protocolError;
+    logout << text_t2ascii << "Protocol Error: filter options requested for non-existent\n"
+	   << "filter \"" << request.filterName << "\".\n\n";
+  }
+}
+
+void collectserver::get_document (const DocumentRequest_t &request,
+				  DocumentResponse_t &response,
+				  comerror_t &err, ostream &logout) {
+
+  sourcelistclass::iterator source_here = sources.begin();
+  sourcelistclass::iterator source_end = sources.end();
+  while (source_here != source_end) {
+    assert ((*source_here).s != NULL);
+    if (((*source_here).s != NULL) &&
+	((*source_here).s->get_document (request.OID, response.doc, err, logout))) {
+      if (err != noError) return;
+      break;
+    }
+    ++source_here;
+  }
+}
+
+void collectserver::is_searchable (bool &issearchable, comerror_t &err,
+				   ostream &logout) {
+
+  sourcelistclass::iterator source_here = sources.begin();
+  sourcelistclass::iterator source_end = sources.end();
+  while (source_here != source_end) {
+    assert ((*source_here).s != NULL);
+    if (((*source_here).s != NULL) &&
+	((*source_here).s->is_searchable (issearchable, err, logout))) {
+      if (err != noError) return;
+      break;
+    }
+    ++source_here;
+  }
+}
+
+
+bool operator==(const collectserverptr &x, const collectserverptr &y) {
+  return (x.c == y.c);
+}
+
+bool operator<(const collectserverptr &x, const collectserverptr &y) {
+  return (x.c < y.c);
+}
+
+
+// thecollectserver remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void collectservermapclass::addcollectserver (collectserver *thecollectserver) {
+  // can't add a null collection server
+  assert (thecollectserver != NULL);
+  if (thecollectserver == NULL) return;
+  
+  // can't add an collection server with no collection name
+  assert (!(thecollectserver->get_collection_name()).empty());
+  if ((thecollectserver->get_collection_name()).empty()) return;
+
+  collectserverptr cptr;
+  cptr.c = thecollectserver;
+  collectserverptrs[thecollectserver->get_collection_name()] = cptr;
+}
+
+// getcollectserver will return NULL if the collectserver could not be found
+collectserver *collectservermapclass::getcollectserver (const text_t &collection) {
+  // can't find a collection with no name
+  if (collection.empty()) return NULL;
+
+  iterator here = collectserverptrs.find (collection);
+  if (here == collectserverptrs.end()) return NULL;
+  
+  return (*here).second.c;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/collectserver.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/collectserver.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/collectserver.h	(revision 16561)
@@ -0,0 +1,201 @@
+/**********************************************************************
+ *
+ * collectserver.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 COLLECTSERVER_H
+#define COLLECTSERVER_H
+
+// Library header files
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+#include "filter.h"
+#include "source.h"
+#include "cfgread.h"
+#include "cnfgable.h" 
+#include "maptools.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+
+struct colservrconf {
+  text_t gsdlhome;
+  text_t collecthome;
+  text_t dbhome;
+  text_t collection;
+  text_t collectdir;
+};
+
+
+class collectserver : public configurable {
+protected:
+  colservrconf configinfo;
+  ColInfoResponse_t collectinfo;
+
+  filtermapclass filters;
+  sourcelistclass sources;
+
+  // some stuff to remember while we are configuring
+  stringmap indexmap;
+
+public:
+  collectserver ();
+  virtual ~collectserver ();
+
+  // add_filter makes another filter available to the collection server
+  // the filter remains the property of the calling code and that
+  // code should destroy the filter after the collection server has been
+  // destroyed.
+  void add_filter (filterclass *thefilter) {filters.addfilter(thefilter);}
+  filtermapclass *get_filtermap_ptr () {return &filters;}
+
+  // add_source makes another source available to the collection server
+  // the source remains the property of the calling code and that
+  // code should destroy the source after the collection server has been
+  // destroyed.
+  void add_source (sourceclass *thesource) {sources.addsource(thesource);}
+  sourcelistclass *get_sourcelist_ptr () {return &sources;}
+
+  // configure should be called for each line in the
+  // configuration files to configure the collection server and everything
+  // it contains. The configuration should take place just before initialisation.
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+  void configure (const text_t &key, const text_t &value);
+  const colservrconf &get_configinfo () {return configinfo;}
+  text_t get_collection_name () {return configinfo.collection;}
+  bool is_collection_group () {return collectinfo.isCollectGroup;}
+
+
+  // ping indicates whether the collection has appropriate data structures
+  // to be active or not
+  void ping(bool &wasSuccess, comerror_t &err, ostream &logout);
+
+  // init should be called after the collection name has been set and
+  // all initialisation is done. init returns true if the initialisation
+  // was successful, if false is returned then no other functions
+  // should be called (without producing meaningless output).
+  virtual bool init (ostream &logout);
+
+  void get_collectinfo (ColInfoResponse_t &reponse,
+			comerror_t &err, ostream &logout);
+
+  virtual void get_filterinfo (InfoFiltersResponse_t &response,
+			       comerror_t &err, ostream &logout);
+
+  virtual void get_filteroptions (const InfoFilterOptionsRequest_t &request,
+				  InfoFilterOptionsResponse_t &response, 
+				  comerror_t &err, ostream &logout);
+
+  virtual void filter (FilterRequest_t &request,
+		       FilterResponse_t &response,
+		       comerror_t &err, ostream &logout);
+
+
+  virtual void get_document (const DocumentRequest_t &request,
+			     DocumentResponse_t &response,
+			     comerror_t &err, ostream &logout);
+
+  virtual void is_searchable (bool &issearchable, comerror_t &err,
+			      ostream &logout);
+
+};
+
+
+// The collectserverptr function does not 'own' the collectserver. The
+// collectserver should be deleted by the code which created it.
+class collectserverptr {
+public:
+  collectserver *c;
+
+  collectserverptr () {c=NULL;}
+  ~collectserverptr () {}
+};
+
+bool operator==(const collectserverptr &x, const collectserverptr &y);
+bool operator<(const collectserverptr &x, const collectserverptr &y);
+
+typedef map<text_t, collectserverptr, lttext_t> collectserverptrmap;
+
+// contains a list of collectservers indexed by their name
+class collectservermapclass {
+protected:
+  collectserverptrmap collectserverptrs;
+
+public:
+  // type support for collectserverptrmap
+  typedef collectserverptrmap::iterator iterator;
+  typedef collectserverptrmap::const_iterator const_iterator;
+  typedef collectserverptrmap::reference reference;
+  typedef collectserverptrmap::const_reference const_reference;
+  typedef collectserverptrmap::size_type size_type;
+
+  typedef collectserverptrmap::difference_type difference_type;
+  typedef collectserverptrmap::const_reverse_iterator const_reverse_iterator;
+  typedef collectserverptrmap::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return collectserverptrs.begin();}
+  const_iterator begin () const {return collectserverptrs.begin();}
+  iterator end () {return collectserverptrs.end();}
+  const_iterator end () const {return collectserverptrs.end();}
+
+  void erase(iterator pos) {collectserverptrs.erase(pos);}
+  void erase(iterator first, iterator last) {collectserverptrs.erase(first, last);}
+  collectservermapclass &operator=(const collectservermapclass &x) 
+    {collectserverptrs=x.collectserverptrs;return *this;}
+
+  bool empty () const {return collectserverptrs.empty();}
+  size_type size() const {return collectserverptrs.size();}
+
+
+  // added functionality
+  void clear () {collectserverptrs.erase(collectserverptrs.begin(),collectserverptrs.end());}
+
+  // thecollectserver remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addcollectserver (collectserver *thecollectserver);
+
+  // getcollectserver will return NULL if the collectserver could not be found
+  collectserver *getcollectserver (const text_t &collection);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/collectset.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/collectset.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/collectset.cpp	(revision 16561)
@@ -0,0 +1,499 @@
+/**********************************************************************
+ *
+ * collectset.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.
+ *
+ *********************************************************************/
+
+
+#include "collectset.h"
+#include "collectserver.h"
+#include "colservrconfig.h"
+#include "gsdlsitecfg.h"
+#include "gdbmclass.h"
+#include "fileutil.h"
+#include "filter.h"
+#include "browsefilter.h"
+#include "sqlbrowsefilter.h"
+#include "queryfilter.h"
+#include "mgqueryfilter.h"
+#include "mgppqueryfilter.h"
+#include "mgsource.h"
+#include "lucenequeryfilter.h"
+#include "lucenesource.h"
+
+#include <assert.h>
+
+#ifdef USE_SQLITE
+#include "sqlitedbclass.h"
+#endif
+
+collectset::collectset (text_t& gsdlhome, text_t& collecthome) 
+{
+  // gsdlhome and collecthome will be set as a result of calling this function
+  // collecthome will default to "<gsdlhome>/collect" if not explicitly
+  // specified in config file
+
+  text_tarray collections;
+
+  // get gsdlhome (if we fail the error will be picked up later -- in
+  // cgiwrapper)
+
+  if (site_cfg_read (gsdlhome, collecthome, httpdomain, httpprefix)) {
+    if (!gsdlhome.empty() && directory_exists(gsdlhome)) {
+      if (read_dir (collecthome, collections)) {
+    
+	text_tarray::const_iterator thiscol = collections.begin();
+	text_tarray::const_iterator endcol = collections.end();
+
+	while (thiscol != endcol) {
+	  // ignore the modelcol
+	  if (*thiscol == "modelcol") {
+	    ++thiscol;
+	    continue;
+	  }
+
+	  this->add_collection (*thiscol, gsdlhome, collecthome);
+
+	  ++thiscol;
+	}
+
+	this->add_all_collection_groups(gsdlhome, collecthome);
+      }
+    }
+  }
+}
+
+collectset::~collectset () {
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+
+  while (here != end) {
+    if ((*here).second.c != NULL) {
+      delete (*here).second.c;
+    }
+    ++here;
+  }
+  cservers.clear();
+}
+
+bool collectset::init (ostream &logout) {
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+
+  while (here != end) {
+    assert ((*here).second.c != NULL);
+    if ((*here).second.c != NULL) {
+      const colservrconf &configinfo = (*here).second.c->get_configinfo ();
+
+      // configure this collection server
+
+      // note that we read build.cfg before collect.cfg so that the indexmaps
+      // are available to decode defaultindex, defaultsubcollection, and
+      // defaultlanguage
+
+      bool failed_build_cfg = false;
+      if (!build_cfg_read (*((*here).second.c), configinfo.gsdlhome,
+			   configinfo.collecthome, configinfo.collection)) {
+	failed_build_cfg = true;
+
+	outconvertclass text_t2ascii;
+	logout << text_t2ascii 
+	       << "Warning: couldn't read build.cfg file for collection \"" 
+	       << configinfo.collection << "\""
+	       << "  gsdlhome=\"" << configinfo.gsdlhome << "\"\n"
+	       << "  collecthome=\"" << configinfo.collecthome << "\"\n";
+      }
+
+      bool failed_collect_cfg = false;
+      if (!collect_cfg_read (*((*here).second.c), configinfo.gsdlhome,
+			     configinfo.collecthome, configinfo.collection)) {
+	failed_collect_cfg = true;
+	outconvertclass text_t2ascii;
+	logout << text_t2ascii 
+	       << "Warning: couldn't read collect.cfg file for collection \""
+	       << configinfo.collection << "\""
+	       << "  gsdlhome=\"" << configinfo.gsdlhome << "\"\n"
+	       << "  collecthome=\"" << configinfo.collecthome << "\"\n";
+      }
+
+      
+      bool is_colgroup = (*here).second.c->is_collection_group();
+
+      if (failed_collect_cfg) {
+	++here;
+	continue;
+      }
+
+      if (failed_build_cfg && (!is_colgroup)) {
+	++here;
+	continue;
+      }
+      // let a failed build.cfg through if its 'collect.cfg' marks it as 'collectgroup true'
+
+      if (!(*here).second.c->init (logout)) return false;
+
+      (*here).second.c->configure("httpdomain",httpdomain);
+      (*here).second.c->configure("httpprefix",httpprefix);
+    }
+    ++here;
+  }
+
+  return true;
+}
+
+collectservermapclass collectset::servers()
+{ return cservers;
+}
+
+
+void collectset::add_all_collections(const text_t &gsdlhome, 
+				     const text_t& collecthome) 
+{
+  text_tarray collections;
+
+  if (read_dir(collecthome, collections)) {
+
+    text_tarray::const_iterator thiscol = collections.begin();
+    text_tarray::const_iterator endcol = collections.end();
+
+    while (thiscol != endcol) {
+    
+      // ignore the modelcol
+      if (*thiscol == "modelcol") {
+	++thiscol;
+	continue;
+      }
+    
+      // create collection server for this collection
+      this->add_collection (*thiscol, gsdlhome, collecthome);
+    
+      ++thiscol;
+    }
+
+    this->add_all_collection_groups(gsdlhome,collecthome);
+  }
+}
+
+// add_collection sets up the collectionserver and calls
+// add_collectserver
+void collectset::add_collection (const text_t& collection, 
+				 const text_t& gsdlhome,
+				 const text_t& collecthome) 
+{
+
+  this->remove_collection(collection);
+  
+  // read config file to see if built with mg, mgpp, or lucene
+  text_t buildtype = "mg"; // mg is default
+  text_t infodbtype = "gdbm"; // gdbm is default
+
+  text_tarray cfgline;
+  text_t key;
+
+  text_t build_cfg = filename_cat(collecthome, collection, "index", "build.cfg");
+  char *build_cfgc = build_cfg.getcstr();
+  ifstream confin(build_cfgc);
+
+  if (confin) {
+    while (read_cfg_line(confin, cfgline) >= 0) {
+      if (cfgline.size() == 2) {
+	key = cfgline[0];
+	cfgline.erase(cfgline.begin());
+	if (key == "buildtype") {
+	  buildtype = cfgline[0];
+	}
+	if (key == "infodbtype") {
+	  infodbtype = cfgline[0];
+	}
+      }
+    }
+    confin.close();
+  }
+  delete []build_cfgc;
+
+  collectserver *cserver = new collectserver();
+
+  // Create a dbclass of the correct type
+  dbclass *db_ptr = NULL;
+
+#ifdef USE_SQLITE
+  if (infodbtype == "sqlite")
+  {
+    sqlitedbclass *sql_db_ptr = new sqlitedbclass();
+    db_ptr = sql_db_ptr;
+
+    // add a sql browse filter
+    sqlbrowsefilterclass *sqlbrowsefilter = new sqlbrowsefilterclass();
+    sqlbrowsefilter->set_sql_db_ptr(sql_db_ptr);
+    cserver->add_filter (sqlbrowsefilter);  
+  }
+#endif
+
+  // Use GDBM if the infodb type is empty or not one of the values above
+  if (db_ptr == NULL)
+  {
+    db_ptr = new gdbmclass();
+  }
+
+  // add a null filter
+  filterclass *filter = new filterclass ();
+  cserver->add_filter (filter);
+      
+  // add a browse filter
+  browsefilterclass *browsefilter = new browsefilterclass();
+  browsefilter->set_db_ptr(db_ptr);
+  cserver->add_filter (browsefilter);  
+ 
+  if (buildtype == "mg") {
+    mgsearch = new mgsearchclass();
+ 
+    // add a query filter
+    mgqueryfilterclass *queryfilter = new mgqueryfilterclass();
+    queryfilter->set_db_ptr(db_ptr);
+    queryfilter->set_textsearchptr (mgsearch);
+    cserver->add_filter (queryfilter);
+    
+    // add a mg source
+    mgsourceclass *mgsource = new mgsourceclass ();
+    mgsource->set_db_ptr(db_ptr);
+    mgsource->set_textsearchptr (mgsearch);
+    cserver->add_source (mgsource);
+  }
+  else if (buildtype == "mgpp") {
+    mgppsearch = new mgppsearchclass();
+
+    // add a query filter
+    mgppqueryfilterclass *queryfilter = new mgppqueryfilterclass();
+    queryfilter->set_db_ptr(db_ptr);
+    queryfilter->set_textsearchptr (mgppsearch);
+    cserver->add_filter (queryfilter);
+      
+    // add a mg source
+    mgsourceclass *mgsource = new mgsourceclass ();
+    mgsource->set_db_ptr(db_ptr);
+    mgsource->set_textsearchptr (mgppsearch);
+    cserver->add_source (mgsource);
+  }
+  else if (buildtype == "lucene") {
+    lucenesearch = new lucenesearchclass();
+    lucenesearch->set_gsdlhome(gsdlhome); 
+
+    // add a query filter
+    lucenequeryfilterclass *queryfilter = new lucenequeryfilterclass();
+    queryfilter->set_db_ptr(db_ptr);
+    queryfilter->set_textsearchptr (lucenesearch);
+    cserver->add_filter (queryfilter);
+      
+    // add a lucene source
+    lucenesourceclass *lucenesource = new lucenesourceclass ();
+    lucenesource->set_db_ptr(db_ptr);
+    lucenesource->set_textsearchptr (lucenesearch);
+    cserver->add_source (lucenesource);
+  }
+
+  // inform collection server and everything it contains about its
+  // collection name
+  cserver->configure ("collection", collection);
+  cserver->configure ("gsdlhome", gsdlhome);
+  cserver->configure ("collecthome", collecthome); 
+  cservers.addcollectserver (cserver);
+}
+
+void collectset::remove_all_collections () {
+
+  // first unload any cached mg databases
+  if (mgsearch != NULL) {
+    mgsearch->unload_database();
+  }
+
+  // now delete the collection server objects
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+
+  while (here != end) {
+    if ((*here).second.c != NULL) {
+      delete (*here).second.c;
+    }
+    ++here;
+  }
+  cservers.clear();
+}
+
+void collectset::add_collection_group(const text_t& collection, 
+				      const text_t& gsdlhome,
+				      const text_t& collecthome)
+{
+  text_tarray group;
+
+  text_t collect_group_dir = filename_cat (collecthome, collection);
+
+  // need to read collect.cfg for 'collectgroup' as class hasn't been initialised through 'init' yet
+  text_t is_collect_group;
+  text_tarray cfgline;
+  text_t key;
+  text_t build_cfg = filename_cat(collect_group_dir, "etc", "collect.cfg");
+  char *collect_cfgc = build_cfg.getcstr();
+  ifstream confin(collect_cfgc);
+
+  if (confin) {
+    while (read_cfg_line(confin, cfgline) >= 0) {
+      if (cfgline.size() == 2) {
+	key = cfgline[0];
+	cfgline.erase(cfgline.begin());
+	if (key == "collectgroup") {
+	  is_collect_group = cfgline[0];
+	  break;
+	}
+      }
+    }
+    confin.close();
+  }
+  delete []collect_cfgc;
+
+  if (is_collect_group == "true") {
+
+    if (read_dir (collect_group_dir, group)) {
+      
+      text_tarray::const_iterator thiscol = group.begin();
+      text_tarray::const_iterator endcol = group.end();
+      
+      while (thiscol != endcol) {
+	// ignore the modelcol
+	if (*thiscol == "etc") {
+	  ++thiscol;
+	  continue;
+	}
+	
+	text_t group_col = filename_cat(collection,*thiscol);
+	this->add_collection (group_col, gsdlhome, collecthome);
+	
+	++thiscol;
+      }
+    }
+  }
+}
+
+void collectset::add_all_collection_groups (const text_t& gsdlhome,
+					    const text_t& collecthome) 
+
+{
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+  
+  while (here != end) {
+    text_t collection = (*here).second.c->get_collection_name();
+    this->add_collection_group(collection,gsdlhome,collecthome);
+
+    ++here;
+  }
+}
+
+
+// remove_collection deletes the collection server of collection.
+// This only needs to be called if a collectionserver is to be
+// removed while the library is running. The destructor function
+// cleans up all collectservers when the program exits.
+void collectset::remove_collection (const text_t &collection) {
+
+  // do nothing if no collection server exists for this collection 
+  if (cservers.getcollectserver(collection) == NULL) return;
+
+  // first unload any cached mg databases - we may need to do something
+  // similar to this for mgpp and lucene too
+  if (mgsearch != NULL) {
+    mgsearch->unload_database();
+  }
+
+  // now delete the collection server object
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+
+  while (here != end) {
+    if ((*here).second.c != NULL && (*here).first == collection) {
+      delete (*here).second.c;
+      cservers.erase (here);
+      return;
+    }
+    ++here;
+  }
+}
+
+
+// remove_collection deletes the collection server of collection.
+// This only needs to be called if a collectionserver is to be
+// removed while the library is running. The destructor function
+// cleans up all collectservers when the program exits.
+void collectset::remove_collection (const text_t &collection, ostream &logout) {
+
+  remove_collection(collection);
+
+  outconvertclass text_t2ascii;
+  logout << text_t2ascii << "collectset::remove_collection: failed to remove collectserver for "
+	 << collection << "\n";
+}
+
+void collectset::configure(const text_t &key, const text_tarray &cfgline)
+{
+  if ((key == "collection") || (key == "collectdir")) return;
+
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+
+  while (here != end) {
+    assert ((*here).second.c != NULL);
+    if ((*here).second.c != NULL) {
+      if (key == "collectinfo") {
+	if ((*here).first == cfgline[0]) {
+	  if (cfgline.size()==3) {
+	    (*here).second.c->configure ("gsdlhome", cfgline[1]);
+	    (*here).second.c->configure ("gdbmhome", cfgline[2]);
+	  }
+	  else {
+	    (*here).second.c->configure ("gsdlhome", cfgline[1]);
+	    (*here).second.c->configure ("collecthome", cfgline[2]);
+	    (*here).second.c->configure ("gdbmhome", cfgline[3]);
+	  }
+	}
+      } else {
+	(*here).second.c->configure (key, cfgline);
+      }
+    }
+
+    ++here;
+  }
+}
+
+void collectset::getCollectionList (text_tarray &collist) 
+{
+  collist.erase(collist.begin(),collist.end());
+
+  collectservermapclass::iterator here = cservers.begin();
+  collectservermapclass::iterator end = cservers.end();
+  while (here != end) {
+    assert ((*here).second.c != NULL);
+    if ((*here).second.c != NULL) {
+      collist.push_back ((*here).second.c->get_collection_name());
+    }
+    ++here;
+  }
+}
+
Index: /gsdl/trunk/gs2runtime/src/colservr/collectset.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/collectset.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/collectset.h	(revision 16561)
@@ -0,0 +1,73 @@
+/************************************************************
+ * 
+ * Collection set
+ *
+ ************************************************************/
+
+#ifndef COLLECTSET_H
+#define COLLECTSET_H
+
+#include "collectserver.h"
+#include "cnfgable.h"
+#include "mgsearch.h"
+#include "mgppsearch.h"
+#include "lucenesearch.h"
+
+class collectset : public configurable
+{
+ protected:
+  collectservermapclass cservers;
+  mgsearchclass         *mgsearch;
+  mgppsearchclass       *mgppsearch;
+  lucenesearchclass     *lucenesearch;
+  int                   noofservers;
+  text_t                httpdomain;
+  text_t                httpprefix;
+
+ public:
+  collectset(text_t& gsdlhome, text_t& collecthome);
+  // this constructor is used by the local library
+  collectset() {}
+
+  ~collectset();
+
+  // initialise the collections
+  bool init(ostream &logout);
+
+  // configure all the servers as supplied
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  // Add/remove collections
+  void add_all_collections (const text_t &gsdlhome, const text_t& collecthome);
+  void remove_all_collections ();
+ 
+  void remove_collection (const text_t &collection);
+  void remove_collection (const text_t &collection, ostream& logout);
+
+  void add_collection (const text_t &collection, 
+		       const text_t &gsdlhome, 
+		       const text_t& collecthome);
+
+  void add_collection_group(const text_t& collection, 
+			    const text_t& gsdlhome, 
+			    const text_t& collecthome);
+
+  void add_all_collection_groups (const text_t& gsdlhome, 
+				  const text_t& collecthome);
+
+  // return the total number of servers in the set
+  int size();
+
+  void getCollectionList(text_tarray &collist);
+
+  // return the component map
+  collectservermapclass servers();
+
+  // return an individual collection server
+  collectserver *getCollectServer(const text_t &collection)
+    {
+      return cservers.getcollectserver(collection);
+    }
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.cpp	(revision 16561)
@@ -0,0 +1,78 @@
+/**********************************************************************
+ *
+ * colservrconfig.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$
+ *
+ *********************************************************************/
+
+#include "colservrconfig.h"
+#include "fileutil.h"
+#include "cfgread.h"
+
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+// collect_cfg_read reads collect.cfg returning true on success.
+bool collect_cfg_read (collectserver &cserver, const text_t &gsdlhome,
+		       const text_t& collecthome,
+		       const text_t &collection) 
+{
+  text_t filename = filename_cat (collecthome, collection);
+  filename = filename_cat (filename, "custom",collection,"etc","custom.cfg");
+
+  if (!file_exists (filename)) {
+    filename = filename_cat (collecthome, collection, "etc", "collect.cfg");
+
+    if (!file_exists (filename)) {
+      filename = filename_cat (gsdlhome, "etc", "collect.cfg");
+      if (!file_exists (filename)) return false;
+    }
+  }
+
+  return cserver.read_configfile(filename);
+}
+
+
+// build_cfg_read reads build.cfg and returns true on success.
+bool build_cfg_read (collectserver &cserver, const text_t &gsdlhome,
+		     const text_t& collecthome,
+		     const text_t &collection) {
+  text_t filename = filename_cat (collecthome, collection,"index","build.cfg");
+
+  if (!file_exists (filename)) {
+    filename = filename_cat (gsdlhome, "index", "build.cfg");
+    if (!file_exists (filename)) return false;
+  }
+
+  return cserver.read_configfile(filename);
+
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/colservrconfig.h	(revision 16561)
@@ -0,0 +1,45 @@
+/**********************************************************************
+ *
+ * colservrconfig.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.
+ *
+ *********************************************************************/
+
+
+#ifndef COLSERVRCONFIG_H
+#define COLSERVRCONFIG_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "collectserver.h"
+
+
+// collect_cfg_read reads collect.cfg returning true on success.
+bool collect_cfg_read (collectserver &cserver, const text_t &gsdlhome,
+		       const text_t& collecthome, const text_t &collection);
+
+// build_cfg_read reads build.cfg and returns true on success.
+bool build_cfg_read (collectserver &cserver, const text_t &gsdlhome,
+		     const text_t& collecthome, const text_t &collection);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/comtypes.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/comtypes.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/comtypes.cpp	(revision 16561)
@@ -0,0 +1,294 @@
+/**********************************************************************
+ *
+ * comtypes.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.
+ *
+ *********************************************************************/
+
+#include "comtypes.h"
+
+
+text_t get_comerror_string (comerror_t err) {
+  if (err == noError) return "no error";
+  else if (err == authenticationFailure) return "authentication failure";
+  else if (err == protocolError) return "protocol error";
+  else if (err == configurationError) return "configuration error";
+  else if (err == systemProblem) return "system problem";
+  else if (err == syntaxError) return "query syntax error";
+  return "unknown problem";
+}
+
+
+void ShortColInfo_t::clear() {
+  name.clear();
+  host.clear();
+  port = 0;
+}
+
+/*
+   isPublic now defaults to true  most values are default          
+   similar to a constructor in a class to make some initializations
+*/
+
+void ColInfoResponse_t::clear () {
+  shortInfo.clear();
+  useBook=false;
+  isPublic=true;
+  isBeta=false;
+  isSegmented=false;
+  isCollectGroup=false;
+  languages.erase(languages.begin(), languages.end());
+  ccsCols.erase(ccsCols.begin(), ccsCols.end());
+  ccsOptions = 0;
+  stemIndexes = 3; // old default was both provided
+  buildDate=0;
+  buildType.clear();
+  numDocs=0;
+  numSections=0;
+  numWords=0;
+  numBytes=0;
+  format.erase(format.begin(), format.end());
+  building.erase(building.begin(), building.end());
+  httpdomain.clear();
+  httpprefix.clear();
+  receptionist.clear();
+
+  // Setting some authentication parameters use with
+  // various parts of the code.
+  
+  authenticate.clear();         // turned off by default, individual collection authentication
+  auth_group.clear();              // turned off by default, authentication by groups method
+  public_documents.clear();          // turned off by default, allow access to these documents <list>
+  private_documents.clear();           // turned off by default, disallow access to these documents <list>
+    
+}
+
+text_t ColInfoResponse_t::get_collectionmeta(text_t name, text_t lang) {
+  text_t value = collectionmeta[name][lang];
+  if (value.empty()) {
+    value = collectionmeta[name][g_EmptyText];
+  }
+  return value;
+
+}
+
+void InfoFiltersResponse_t::clear () {
+  filterNames.erase(filterNames.begin(), filterNames.end());
+}
+
+
+void InfoFilterOptionsRequest_t::clear () {
+  filterName.clear();
+}
+
+
+void FilterOption_t::clear () {
+  name.clear();
+  type = booleant;
+  repeatable = onePerQuery;
+  defaultValue.clear();
+  validValues.erase(validValues.begin(), validValues.end());
+}
+
+void FilterOption_t::check_defaultValue () {
+  text_tarray::iterator here, end;
+
+  // how the default is interpreted depends on the option type
+  switch (type) {
+  case booleant: 
+  case enumeratedt: // has to be one of the validValues
+    here = validValues.begin ();
+    end = validValues.end ();
+    while (here != end) {
+      if (*here == defaultValue) return;
+      ++here;
+    }
+    
+    break;
+
+  case integert: // has to be in the validValues range
+    if ((validValues.size() >= 2) &&
+	(validValues[0].getint() <= defaultValue.getint()) &&
+	(validValues[1].getint() >= defaultValue.getint()))
+      return;
+    break;
+
+  case stringt: // any value is valid
+    return; 
+  }
+
+  // did not find the defaultValue
+  if (validValues.empty()) defaultValue.clear();
+  else defaultValue = validValues[0];
+}
+
+
+bool operator==(const FilterOption_t &x, const FilterOption_t &y) {
+  return ((x.name == y.name) &&
+	  (x.type == y.type) &&
+	  (x.repeatable == y.repeatable) &&
+	  (x.defaultValue == y.defaultValue) &&
+	  (x.validValues == y.validValues));
+}
+
+bool operator<(const FilterOption_t &x, const FilterOption_t &y) {
+  return ((x.name < y.name) ||
+	  ((x.name == y.name) &&
+	   ((x.type < y.type) ||
+	    ((x.type == y.type) &&
+	     ((x.repeatable < y.repeatable) ||
+	      ((x.repeatable == y.repeatable) &&
+	       ((x.defaultValue < y.defaultValue) ||
+		((x.defaultValue == y.defaultValue) &&
+		 (x.validValues < y.validValues)))))))));
+}
+
+
+
+void InfoFilterOptionsResponse_t::clear () {
+  filterOptions.erase(filterOptions.begin(), filterOptions.end());
+}
+
+
+void OptionValue_t::clear () {
+  name.clear();
+  value.clear();
+}
+
+
+void FilterRequest_t::clear () {
+  filterName.clear();
+  filterLang.clear();
+  filterOptions.erase(filterOptions.begin(), filterOptions.end());
+  docSet.erase(docSet.begin(), docSet.end());
+  filterResultOptions = 0;
+  requestParams.clear();
+  refParams.clear();
+  fields.erase(fields.begin(), fields.end());
+  getParents = false;
+}
+
+
+void TermInfo_t::clear () {
+  term.clear();
+  freq = 0;
+  matchTerms.erase (matchTerms.begin(), matchTerms.end());
+}
+
+
+void MetadataInfo_t::clear () {
+  params.clear();
+  isRef = false;
+  values.erase(values.begin(), values.end());
+  if (parent != NULL) {
+    delete parent;
+    parent = NULL;
+  }
+}
+
+MetadataInfo_t::MetadataInfo_t () {parent=NULL;clear();}
+
+// copy constructor
+MetadataInfo_t::MetadataInfo_t (const MetadataInfo_t &x) {
+  params = x.params;
+  isRef = x.isRef;
+  values = x.values;
+  if (x.parent == NULL) parent = NULL;
+  else {
+    parent = new MetadataInfo_t ();
+    *parent = *(x.parent);
+  }
+}
+
+MetadataInfo_t::~MetadataInfo_t () {
+  if (parent != NULL) {
+    delete parent;
+    parent = NULL;
+  }
+}
+
+MetadataInfo_t &MetadataInfo_t::operator=(const MetadataInfo_t &x) {
+  if (&x != this) {
+    params = x.params;
+    isRef = x.isRef;
+    values = x.values;
+    if (x.parent == NULL) parent = NULL;
+    else {
+      parent = new MetadataInfo_t ();
+      *parent = *(x.parent);
+    }
+  }
+  return *this;
+}
+
+void ResultDocInfo_t::clear () {
+  OID.clear ();
+  ranking = 0;
+  result_num = 0;
+  num_terms_matched = 0;
+  num_phrase_match = 0;
+  docFreq.erase(docFreq.begin(), docFreq.end());
+  metadata.erase(metadata.begin(), metadata.end());
+  classifier_metadata_type.erase(classifier_metadata_type.begin(), 
+				 classifier_metadata_type.end());
+  classifier_metadata_offset = 0;
+  
+}
+
+ResultDocInfo_t &ResultDocInfo_t::operator=(const ResultDocInfo_t &x) {
+  OID = x.OID; 
+  ranking = x.ranking;
+  result_num = x.result_num;
+  num_terms_matched = x.num_terms_matched;
+  num_phrase_match = x.num_phrase_match;
+  docFreq = x.docFreq; 
+  metadata = x.metadata;
+  return *this;
+}
+
+void FilterResponse_t::clear () {
+  error_message = g_EmptyText;
+  numDocs = 0;
+  isApprox = Exact;
+  termInfo.erase (termInfo.begin(), termInfo.end());
+  docInfo.erase (docInfo.begin(), docInfo.end());
+  stopwords.clear();
+}
+
+FilterResponse_t &FilterResponse_t::operator=(const FilterResponse_t &x) {
+  error_message = x.error_message;
+  numDocs = x.numDocs;
+  isApprox = x.isApprox;
+  termInfo = x.termInfo;
+  docInfo = x.docInfo;
+  stopwords = x.stopwords;
+  return *this;
+}
+
+void DocumentRequest_t::clear () {
+  OID.clear();
+  docType.clear();
+  docFormat.clear();
+}
+
+void DocumentResponse_t::clear () {
+  doc.clear();
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/comtypes.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/comtypes.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/comtypes.h	(revision 16561)
@@ -0,0 +1,378 @@
+/**********************************************************************
+ *
+ * comtypes.h -- 
+ * Copyright (C) 1999,2004  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.
+ *
+ *********************************************************************/
+
+
+#ifndef COMTYPES_H
+#define COMTYPES_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#  include <ospace\std\list>
+#  include <ospace\std\memory> // for pair
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#  include <list.h>
+#  include <memory.h> // for pair
+#  include <map.h>
+#else
+#  include <vector>
+#  include <list>
+#  include <memory> // for pair
+#  include <map>
+#endif
+
+
+enum comerror_t {noError, authenticationFailure, protocolError, 
+		 configurationError, systemProblem, syntaxError};
+text_t get_comerror_string (comerror_t err);
+
+
+// ShortColInfo ::= SEQUENCE {
+//   name GeneralString,
+//   host GeneralString,
+//   port INTEGER
+// }
+struct ShortColInfo_t {
+  void clear ();
+  ShortColInfo_t () {clear();}
+
+  text_t name;
+  text_t host;
+  int port;
+};
+
+// for collection metadata: metadata name -> lang -> value
+// for collection macros: macro name -> params -> value
+typedef map<text_t, text_tmap, lttext_t> collectionmeta_map; 
+
+#define CCSUniformSearchResultsFormatting 1
+
+#define SIcasefold 1
+#define SIstem 2
+#define SIaccentfold 4
+
+struct ColInfoResponse_t {
+  void clear ();
+  ColInfoResponse_t () {clear();} 
+  text_t get_collectionmeta(text_t name, text_t lang);
+  
+  ShortColInfo_t shortInfo;
+  bool useBook;	//whether we want to display the book
+  bool isPublic;
+  bool isBeta;
+  bool isSegmented;
+  bool isCollectGroup; // used to group a set of collections as one item on the home page
+  unsigned long buildDate;
+  text_tarray ccsCols;    // empty if collection does not use cross-collection searching
+  int ccsOptions; // uses CCS #defines from above
+  int stemIndexes; // uses SI #defines from above
+  text_tarray languages;
+  unsigned long numDocs;     // 0 if not known
+  unsigned long numSections; // 0 if not known
+  unsigned long numWords;    // 0 if not known
+  unsigned long numBytes;    // 0 if not known
+  collectionmeta_map  collectionmeta;
+  text_tmap      format;
+  collectionmeta_map	 collection_macros; // for collectionmacro config directive (> gsdl 2.53)
+  text_tmap      building;
+  text_t         httpdomain;      // GRB: could these two http items need removing
+  text_t         httpprefix;
+  text_t         receptionist;
+  text_t         buildType;           // 'mg' or 'mgpp' or 'lucene'
+  text_t         authenticate;      // 'document' or 'collection'
+  text_t         auth_group;          // 'mygroup' 'yourgroup'
+  text_tmap      public_documents;    // the acl to allow access to listed documents
+  text_tmap      private_documents;   // the acl to disallow access to listed documents
+  text_tmap      dynamic_classifiers;
+};
+
+
+// -- filter options which might be supported for the QueryFilter
+// --
+// -- onePerQuery StartResults   integer
+// -- onePerQuery EndResults     integer
+// -- onePerQuery QueryType      enumerated (boolean, ranked)
+// -- onePerTerm  Term           string ???
+// -- onePerTerm  Casefold       boolean
+// -- onePerTerm  Stem           boolean
+// -- onePerTerm  Index          enumerated
+// -- onePerTerm  Subcollection  enumerated
+// --
+// -- filter options which might be supported for the BrowseFilter
+// --
+// -- onePerQuery StartResults   integer
+// -- onePerQuery EndResults     integer
+// -- onePerQuery ParentNode     string ("" will return the browsing available)
+// --
+// -- The NullFilter always returns the set it was given, it doesn't have
+// -- any options
+
+// InfoFiltersResponse ::= SEQUENCE {
+//   filterNames StringSet
+// }
+struct InfoFiltersResponse_t {
+  void clear ();
+
+  text_tset filterNames;
+};
+
+// InfoFilterOptionsRequest ::= SEQUENCE {
+//   filterName GeneralString
+// }
+struct InfoFilterOptionsRequest_t {
+  void clear ();
+
+  text_t filterName;
+};
+
+// FilterOption ::= SEQUENCE {
+//   name         GeneralString,
+//   type         ENUMERATED {booleant(0), integert(1), enumeratedt(2), stringt(3)},
+//   repeatable   ENUMERATED {onePerQuery(0), onePerTerm(1), nPerTerm(2)},
+//   defaultValue GeneralString,
+//   -- the interpretation of the validValues depends on the type
+//   -- for boolean: the first value is the false value, the second is true value
+//   -- for integer: the first value is the minimum, the second the maximum
+//   -- for enumerated: all values a listed
+//   -- for string: this value is ignored
+//   validValues  StringSequence
+// }
+struct FilterOption_t {
+  void clear ();
+  void check_defaultValue ();
+  FilterOption_t () {clear();}
+
+  text_t name;
+
+  enum type_t {booleant=0, integert=1, enumeratedt=2, stringt=3};
+  type_t type;
+
+  enum repeatable_t {onePerQuery=0, onePerTerm=1, nPerTerm=2};
+  repeatable_t repeatable;
+
+  text_t defaultValue;
+  text_tarray validValues;
+};
+
+bool operator==(const FilterOption_t &x, const FilterOption_t &y);
+bool operator<(const FilterOption_t &x, const FilterOption_t &y);
+
+
+typedef map<text_t, FilterOption_t, lttext_t> FilterOption_tmap;
+
+
+// InfoFilterOptionsResponse ::= SEQUENCE {
+//   filterOptions SET OF FilterOption
+// }
+struct InfoFilterOptionsResponse_t {
+  void clear ();
+
+  FilterOption_tmap filterOptions;
+};
+
+
+// OptionValue ::= SEQUENCE {
+//   name  GeneralString,
+//   value GeneralString
+// }
+struct OptionValue_t {
+  void clear ();
+
+  text_t name;
+  text_t value;
+};
+
+typedef vector<OptionValue_t> OptionValue_tarray;
+
+
+// -- Terms are presented in the same order that they are requested,
+// -- any information relating to the terms is in reference to the
+// -- index specified for that term.
+//
+// FilterRequest ::= SEQUENCE {
+//   filterName          [0] GeneralString,
+//   filterOptions       [1] IMPLICIT SEQUENCE OF OptionValue,
+//   docSet              [2] IMPLICIT StringSequence, -- the OID "" represents everything
+//   filterResultOptions [3] IMPLICIT BIT STRING {termFreq(0), matchTerms(1), OID(2), 
+//                                                subCol(3), ranking(4), docFreq(5), 
+//                                                metadata(6)}
+// 
+//   -- the next set of options are for the metadata request, 
+//   -- they can be left blank if metadata is not wanted
+//   requestParams       [4] IMPLICIT GeneralString, -- used to negotiate the metadata content
+//   refParams           [5] IMPLICIT GeneralString, -- used to decide whether to return a 
+//                                                   -- reference to the data or the actual data
+//   fields              [6] IMPLICIT StringSet
+//   getParents          [7] IMPLICIT BOOLEAN        -- gets metadata of all parents too
+// }
+#define FRtermFreq   1
+#define FRmatchTerms 2
+#define FROID        4
+#define FRsubCol     8
+#define FRranking    16
+#define FRdocFreq    32
+#define FRmetadata   64
+// used to indicate a full text browse query for mgpp
+#define FRfullTextBrowse  128
+
+struct FilterRequest_t {
+  void clear ();
+  FilterRequest_t () {clear();}
+
+  text_t filterName;
+  text_t filterLang; // non empty if we want info to be retrived in language specific
+  OptionValue_tarray filterOptions;
+  text_tarray docSet;        // empty if not used
+  int filterResultOptions; // use the FR* defines above
+
+  text_t requestParams; // empty if not used
+  text_t refParams;     // empty if not used
+  text_tset fields;   // empty if not used
+  bool getParents;      // defaults to false
+};
+
+
+// TermInfo ::= SEQUENCE {
+//   term       [0] GeneralString,
+//   freq       [1] IMPLICIT INTEGER,       -- 0 if not requested
+//   matchTerms [2] IMPLICIT StringSequence -- empty if not requested
+// }
+struct TermInfo_t {
+  void clear ();
+  TermInfo_t () {clear();}
+
+  text_t term;
+  int freq; // 0 if not requested
+  text_tarray matchTerms; // empty if not requested
+};
+
+typedef vector<TermInfo_t> TermInfo_tarray;
+
+
+// MetadataInfo ::= SEQUENCE {
+//   params  [0] IMPLICIT GeneralString,
+//   isRef   [1] IMPLICIT BOOLEAN,
+//   values  [3] IMPLICIT SEQUENCE OF GeneralString,
+//   name    [4] IMPLICIT GeneralString
+// }
+struct MetadataInfo_t {
+  text_t params;
+  bool isRef;
+  text_tarray values;
+  MetadataInfo_t *parent;
+
+  void clear ();
+  MetadataInfo_t ();
+  MetadataInfo_t (const MetadataInfo_t &x); // copy constructor
+  ~MetadataInfo_t ();
+  MetadataInfo_t &operator=(const MetadataInfo_t &x);
+};
+
+typedef map<text_t, MetadataInfo_t, lttext_t> MetadataInfo_tmap;
+
+// ResultDocInfo ::= SEQUENCE {
+//   OID      [0] IMPLICIT GeneralString,
+//   ranking  [1] IMPLICIT INTEGER,             -- 0 if not requested, range 0-10000
+//   docFreq  [2] IMPLICIT SEQUENCE OF INTEGER, -- empty if not requested
+//   metadata [3] IMPLICIT SEQUENCE OF MetadataInfo, -- no longer a SEQUENCE (SET maybe??)
+//   classifier_metadata_type   [4] IMPLICIT GeneralString,       -- empty if not requested
+//   classifier_metadata_offset [5] IMPLICIT INTEGER,             -- 0 if not requested
+// }
+struct ResultDocInfo_t {
+  void clear ();
+  ResultDocInfo_t () {clear();}
+
+  text_t OID;
+  int result_num;          // place in results list
+  int ranking;             // 0 if not requested (real ranking*10000)
+  int num_terms_matched;   // not available on all versions of mg
+  int num_phrase_match;    // not available on all versions of mg
+  vector<int> docFreq;           // empty if not requested
+  MetadataInfo_tmap metadata;    // empty if not requested
+  text_t classifier_metadata_type;        // empty if not requested
+  int    classifier_metadata_offset;      // 0 if not requested
+  ResultDocInfo_t &operator=(const ResultDocInfo_t &x);
+};
+
+typedef vector<ResultDocInfo_t> ResultDocInfo_tarray;
+
+
+// FilterResponse ::= SEQUENCE {
+//   numDocs   [0] IMPLICIT INTEGER,
+//   isApprox  [1] ENUMERATED {Exact(0), Approximate(1), MoreThan(2)}, -- whether numDocs is approximate
+//   termInfo  [2] IMPLICIT SEQUENCE OF TermInfo,                      -- empty if not required
+//   docInfo   [3] IMPLICIT SEQUENCE OF ResultDocInfo                  -- empty if not required
+// }
+
+enum isapprox {Exact=0, Approximate=1, MoreThan=2};
+
+struct FilterResponse_t {
+  void clear ();
+  FilterResponse_t () {clear();}
+
+  text_t error_message;  // Currently only used by Lucene
+  int numDocs;
+  isapprox isApprox;
+  TermInfo_tarray termInfo; // empty if not requested
+  ResultDocInfo_tarray docInfo; // empty if not requested
+  text_tset stopwords;
+
+  FilterResponse_t &operator=(const FilterResponse_t &x);
+};
+
+
+// DocumentRequest ::= SEQUENCE {
+//   OID       GeneralString,
+//   docType   GeneralString,
+//   docFormat GeneralString
+// }
+struct DocumentRequest_t {
+  void clear ();
+  DocumentRequest_t () {clear();}
+
+  text_t OID;
+  text_t docType;
+  text_t docFormat;
+};
+
+
+// DocumentResponse ::= SEQUENCE {
+//   doc OCTET STRING
+// }
+
+struct DocumentResponse_t {
+  void clear ();
+  DocumentResponse_t () {clear();}
+
+  text_t doc;
+};
+
+
+#endif
+
Index: /gsdl/trunk/gs2runtime/src/colservr/expat_document.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/expat_document.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/expat_document.cpp	(revision 16561)
@@ -0,0 +1,101 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <expat.h>
+#include "gsdlunicode.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+using namespace std;
+#endif
+
+#include "queryinfo.h"
+
+#include "expat_document.h"
+
+#include "text_t.h"
+#include "fileutil.h"
+#include "expat_resultset.h"
+
+struct resultpack {
+  text_t *current_text;
+  text_t *section_num;
+  text_t *section_level;
+  bool store_text;
+};
+
+static void XMLCALL 
+startElement(void *userData, const char *name, const char **attributes)
+{
+  resultpack * rpack_ptr = (resultpack*)userData;
+  text_t element_name = (char*)name;
+  if (element_name == *(rpack_ptr->section_level)) {
+    text_t id_att = (char *)get_attribute(attributes,"gs2:id");
+    if (id_att == *(rpack_ptr->section_num)) {
+      rpack_ptr->store_text = true;
+    }
+    
+  }
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  resultpack * rpack_ptr = (resultpack*)userData;
+  text_t element_name = (char*)name;
+  if (element_name == *(rpack_ptr->section_level)) {
+    if (rpack_ptr->store_text == true) {
+      // we have finished now, can we quit this??
+      rpack_ptr->store_text = false;
+    }
+  }
+  
+}
+
+static void XMLCALL
+characterData(void *userData, const char * text, int len) {
+  resultpack * rpack_ptr = (resultpack*)userData;
+  if  (rpack_ptr->store_text) {
+    rpack_ptr->current_text->appendcarr(text, len);
+  }
+}
+
+
+int expat_document(const text_t &filename, const text_t &sec_level, const text_t &sec_num, text_t & doc_content)
+{
+  text_t current_text;
+  current_text.clear();
+  text_t section_num = sec_num;
+  text_t section_level = sec_level;
+
+  resultpack rpack = { &current_text, &section_num, &section_level, false};
+  //cerr << "sec num = "<<sec_num<<", sec level="<<sec_level<<", filename="<<filename<<endl;
+  text_t doc_text;
+  read_file(filename, doc_text);
+
+  char* c_doc_text = doc_text.getcstr();
+  XML_Parser parser = XML_ParserCreate(NULL);
+  
+  XML_SetUserData(parser, &rpack);
+  XML_SetElementHandler(parser, startElement, endElement);
+  XML_SetCharacterDataHandler(parser, characterData);
+  int return_status = 0;
+  const int parse_status 
+    = XML_Parse(parser, c_doc_text, strlen(c_doc_text), XML_TRUE);
+
+  if (parse_status == XML_STATUS_ERROR) {
+    cerr << "Error: " << XML_ErrorString(XML_GetErrorCode(parser)) << " at line " << XML_GetCurrentLineNumber(parser) << endl;
+    return_status = 1;
+  }
+  
+  XML_ParserFree(parser);
+  free(c_doc_text);
+  doc_content = to_uni(current_text);  // Vital for non-ASCII documents
+  return return_status;
+}
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/expat_document.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/expat_document.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/expat_document.h	(revision 16561)
@@ -0,0 +1,9 @@
+#ifndef gs_expat_document_h
+#define gs_expat_document_h
+
+class text_t;
+
+int expat_document(const text_t &filename, const text_t & section_level,
+		   const text_t &section_num, text_t & doc_content);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.cpp	(revision 16561)
@@ -0,0 +1,147 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <expat.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+using namespace std;
+#endif
+
+#include "text_t.h"
+#include "queryinfo.h"
+#include "gsdlunicode.h"
+
+struct queryresultpack {
+  queryresultsclass* queryresult_ptr;
+  int match_count;
+};
+
+char* get_attribute(const char** attr, char* att_name)
+{
+  char* att_val = NULL;
+
+  for (int i = 0; attr[i]; i += 2) {
+    if (strcmp(attr[i],att_name)==0) {
+      att_val = strdup(attr[i+1]);
+    }
+  }
+
+  return att_val;
+}
+
+static void XMLCALL 
+startElement(void *userData, const char *name, const char **attributes)
+{
+  queryresultpack* qrpack_ptr = (queryresultpack*)userData;
+  queryresultsclass* queryresult_ptr = qrpack_ptr->queryresult_ptr;
+
+  text_t element_name = (char*)name;
+
+  if (element_name == "ResultSet") {
+    char* cached_attribute_str = get_attribute(attributes, "cached");
+    if (cached_attribute_str != NULL) {
+      cerr << "Cached: " << cached_attribute_str << endl;
+    }
+  }
+
+  if (element_name == "Error") {
+    char* error_type_str = get_attribute(attributes, "type");
+    queryresult_ptr->error_message = error_type_str;
+    if ((text_t) error_type_str == "PARSE_EXCEPTION") {
+      queryresult_ptr->syntax_error = true;
+    }
+  }
+
+  if (element_name == "Term") {
+    char* term_value_str = get_attribute(attributes, "value");
+    char* term_frequency_str = get_attribute(attributes, "freq");
+
+    termfreqclass termfreqobj;
+    termfreqobj.termstr = to_uni(term_value_str);
+
+    termfreqobj.termfreq = atoi(term_frequency_str);
+    queryresult_ptr->orgterms.push_back(termfreqobj);
+  }
+
+  if (element_name == "StopWord") {
+    char* stop_word_value_str = get_attribute(attributes, "value");
+    queryresult_ptr->stopwords.insert((text_t) stop_word_value_str);
+  }
+
+  if (element_name=="MatchingDocsInfo") {
+    char* num_match_docs_str = get_attribute(attributes,"num");
+    if (num_match_docs_str != NULL) {
+      int num_match_docs = atoi(num_match_docs_str);
+
+      queryresult_ptr->docs_matched = num_match_docs;
+      free(num_match_docs_str);
+    }
+    else {
+      queryresult_ptr->docs_matched = 0;
+    }
+  }
+
+
+  if (element_name=="Match") {
+
+    char* id = get_attribute(attributes,"id");
+    if (id!=NULL) {
+      int id_num = atoi(id);
+    
+      docresultclass doc;
+      doc.clear();
+      doc.docnum = id_num;
+      doc.docweight = qrpack_ptr->match_count;
+      queryresult_ptr->docs.docset[doc.docnum] = doc;
+      queryresult_ptr->docs.docorder.push_back(doc.docnum);
+      ++qrpack_ptr->match_count;
+
+      free(id);
+    }
+    
+  }
+}
+
+static void XMLCALL
+endElement(void *userData, const char *name)
+{
+  // no need to do anything
+}
+
+
+
+
+int expat_resultset(text_t& xml_text, queryresultsclass& queryresult)
+{
+  queryresult.clear();
+  queryresultpack qrpack = { &queryresult, 0 };
+
+  char * xml_text_cstr = xml_text.getcstr();
+  
+  XML_Parser parser = XML_ParserCreate(NULL);
+  
+  XML_SetUserData(parser, &qrpack);
+  XML_SetElementHandler(parser, startElement, endElement);
+  
+  int return_status = 0;
+  const int parse_status 
+    = XML_Parse(parser, xml_text_cstr, strlen(xml_text_cstr), XML_TRUE);
+  
+  if (parse_status == XML_STATUS_ERROR) {
+    fprintf(stderr,
+	    "%s at line %d\n",
+	    XML_ErrorString(XML_GetErrorCode(parser)),
+	    XML_GetCurrentLineNumber(parser));
+    return_status = 1;
+  }
+  
+  XML_ParserFree(parser);
+  free(xml_text_cstr);
+  
+  return return_status;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/expat_resultset.h	(revision 16561)
@@ -0,0 +1,11 @@
+#ifndef gs_expat_resultset_h
+#define gs_expat_resultset_h
+
+class text_t;
+class queryresultsclass;
+
+char* get_attribute(const char** attr, char* att_name);
+int expat_resultset(text_t& xml_text, queryresultsclass& queryresult);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/filter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/filter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/filter.cpp	(revision 16561)
@@ -0,0 +1,338 @@
+/**********************************************************************
+ *
+ * filter.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.
+ *
+ *********************************************************************/
+
+#include "filter.h"
+#include "fileutil.h"
+#include <assert.h>
+
+#include <iostream>
+using namespace std;
+
+
+// default constructor does nothing
+filterclass::filterclass () {
+}
+
+// default destructor does nothing
+filterclass::~filterclass () {
+}
+
+// configure should be called once for each configuration line
+// default configures the default filter options
+void filterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  if (cfgline.size() >= 1) {
+    const text_t &value = cfgline[0];
+
+    if (key == "collection") collection = value;
+    else if (key == "collectdir") collectdir = value;
+    else if (key == "gsdlhome") gsdlhome = value;
+    else if (key == "collecthome") collecthome = value;
+    else if (key == "gdbmhome") dbhome = value;
+    else if ((key == "filteroptdefault") && (cfgline.size() == 2)) {
+      // see if this filter has an option with this name
+      FilterOption_tmap::iterator thisfilteroption = 
+	filterOptions.find(cfgline[0]);
+      if (thisfilteroption != filterOptions.end())
+	(*thisfilteroption).second.defaultValue = cfgline[1];
+    }
+  }
+}
+
+text_t filterclass::resolve_db_filename(const text_t& idx,
+					const text_t& file_ext)
+{
+  text_t resolved_filename;
+
+  if (gsdlhome==dbhome) {
+    // dbhome has defaulted to gsdlhome which we take to means the 
+    // database has been specifically moved out of gsdlhome area.
+    // => it should be whereever collecthome is set to
+    resolved_filename = filename_cat(collecthome, collection, "index", "text", idx);
+  }
+  else {
+    // dbhome is explicitly set to something other than gsdlhome
+    // => use dbhome
+    resolved_filename = filename_cat(dbhome, "collect", collection, "index", "text", idx);
+  }
+
+  resolved_filename += file_ext;
+
+  return resolved_filename;
+}
+
+
+// init should be called after all the configuration is done but
+// before any other methods are called
+// default checks all the filter option defaults
+bool filterclass::init (ostream &/*logout*/) {
+  // check all the filter defaults
+  FilterOption_tmap::iterator filteroption_here = filterOptions.begin();
+  FilterOption_tmap::iterator filteroption_end = filterOptions.end();
+  while (filteroption_here != filteroption_end) {
+    (*filteroption_here).second.check_defaultValue ();
+    
+    ++filteroption_here;
+  }
+
+  if (collecthome.empty()) collecthome = filename_cat(gsdlhome,"collect");
+  if (dbhome.empty()) dbhome = gsdlhome;
+  
+  // get the collection directory name
+  if (collectdir.empty()) {
+    collectdir = filename_cat (collecthome, collection);
+  }
+  
+  return true;
+}
+
+// returns the name of this filter
+// default returns "NullFilter"
+text_t filterclass::get_filter_name () {
+  return "NullFilter";
+}
+
+// returns the current filter options
+void filterclass::get_filteroptions (InfoFilterOptionsResponse_t &response,
+				     comerror_t &err, ostream &/*logout*/) {
+  response.clear();
+  response.filterOptions = filterOptions;
+  err = noError;
+}
+
+// default returns nothing
+void filterclass::filter (const FilterRequest_t &request,
+			  FilterResponse_t &response,
+			  comerror_t &err, ostream &/*logout*/) {
+  ResultDocInfo_t resultdoc;
+
+  response.clear();
+
+  if ((request.filterResultOptions & FROID) ||
+      (request.filterResultOptions & FRmetadata)) {
+    // copy the OIDs from the request to the response
+    text_tarray::const_iterator here = request.docSet.begin();
+    text_tarray::const_iterator end = request.docSet.end();
+    while (here != end) {
+      resultdoc.OID = (*here);
+      response.docInfo.push_back(resultdoc);
+
+      ++here;
+    }
+  }
+
+  response.numDocs = response.docInfo.size();
+  response.isApprox = Exact;
+  err = noError;
+}
+
+
+bool operator==(const filterptr &x, const filterptr &y) {
+  return (x.f == y.f);
+}
+
+bool operator<(const filterptr &x, const filterptr &y) {
+  return (x.f < y.f);
+}
+
+
+// thefilter remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void filtermapclass::addfilter (filterclass *thefilter) {
+  // can't add a null filter
+  assert (thefilter != NULL);
+  if (thefilter == NULL) return;
+  
+  // can't add an filter with no name
+  assert (!(thefilter->get_filter_name()).empty());
+  if ((thefilter->get_filter_name()).empty()) return;
+
+  filterptr fptr;
+  fptr.f = thefilter;
+  filterptrs[thefilter->get_filter_name()] = fptr;
+}
+
+// getfilter will return NULL if the filter could not be found
+filterclass *filtermapclass::getfilter (const text_t &key) {
+  // can't find an filter with no name
+  assert (!key.empty());
+  if (key.empty()) return NULL;
+
+  iterator here = filterptrs.find (key);
+  if (here == filterptrs.end()) return NULL;
+  
+  return (*here).second.f;
+}
+
+
+
+
+// some useful functions for dealing with document sets
+
+// returns -1 if t1 is a child of t2
+// returns 0  if t1 and t2 are not parent-child related
+// returns 1  if t1 is a parent of t2
+int child_compare (const text_t &t1, const text_t &t2) {
+  text_t::const_iterator t1_here = t1.begin();
+  text_t::const_iterator t1_end = t1.end();
+  text_t::const_iterator t2_here = t2.begin();
+  text_t::const_iterator t2_end = t2.end();
+
+  while ((t1_here != t1_end) && (t2_here != t2_end)) {
+    if (*t1_here != *t2_here) return 0; // unrelated
+    ++t1_here;
+    ++t2_here;
+  }
+
+  if ((t1_here == t1_end) && (t2_here == t2_end)) return 0; // equal
+  if (t1_here != t1_end) {
+    if (*t1_here == '.') return -1; // t1 is child
+    else return 0; // unrelated
+  }
+
+  if (t2_here != t2_end) {
+    if (*t2_here == '.') return 1; // t2 is child
+    else return 0; // unrelated
+  }
+
+  return 0; // shouldn't get here...
+}
+
+// intersect places the result in set1
+void intersect (text_tset &set1, const text_tset &set2) {
+  text_tset resultset;
+  int childcomp = 0;
+
+  text_tset::const_iterator set1_here = set1.begin();
+  text_tset::const_iterator set1_end = set1.end();
+  text_tset::const_iterator set2_here = set2.begin();
+  text_tset::const_iterator set2_end = set2.end();
+  while ((set1_here != set1_end) && (set2_here != set2_end)) {
+    if (*set1_here == *set2_here) {
+      // equal
+      resultset.insert (*set1_here);
+      ++set1_here;
+      ++set2_here;
+
+    } else if ((childcomp=child_compare(*set1_here, *set2_here)) != 0) {
+      if (childcomp < 0) {
+	// set1_here is child
+	resultset.insert (*set1_here);
+	++set1_here;
+      } else {
+	// set2_here is child
+	resultset.insert (*set2_here);
+	++set2_here;
+      }
+
+    } else if (*set1_here < *set2_here) {
+      // set1 is less
+      ++set1_here;
+
+    } else {
+      // set2 is less
+      ++set2_here;
+    }
+  }
+
+  set1 = resultset;
+}
+
+void intersect (text_tarray &set1, const text_tset &set2) {
+  text_tarray resultset;
+
+  text_tarray::const_iterator set1_here = set1.begin();
+  text_tarray::const_iterator set1_end = set1.end();
+
+  while (set1_here != set1_end) {
+    if (in_set (set2, *set1_here))
+      resultset.push_back (*set1_here);
+    ++set1_here;
+  }
+  set1 = resultset;
+}
+
+void intersect (text_tarray &set1, const text_tarray &set2) {
+  text_tarray resultset;
+
+  text_tarray::const_iterator set1_here = set1.begin();
+  text_tarray::const_iterator set1_end = set1.end();
+
+  while (set1_here != set1_end) {
+    if (in_set (set2, *set1_here))
+      resultset.push_back (*set1_here);
+    ++set1_here;
+  }
+  set1 = resultset;
+}
+
+// tests to see if el is in set
+bool in_set (const text_tset &set1, const text_t &el) {
+  text_t::const_iterator here = el.begin();
+  text_t::const_iterator end = el.end();
+  text_t tryel, tryel_add;
+  bool first = true;
+
+  // the element is in the set if any of its parents are
+  // in the set
+  do {
+    // get next possible element to try
+    here = getdelimitstr (here, end, '.', tryel_add);
+    if (!first) tryel += ".";
+    first = false;
+    tryel += tryel_add;
+
+    // see if this element is in the set
+    if (set1.find(tryel) != set1.end()) return true;
+  } while (here != end);
+
+  return false;
+}
+
+bool in_set (const text_tarray &set1, const text_t &el) {
+  text_t::const_iterator here = el.begin();
+  text_t::const_iterator end = el.end();
+  text_t tryel, tryel_add;
+  bool first = true;
+
+  // the element is in the set if any of its parents are
+  // in the set
+  do {
+    // get next possible element to try
+    here = getdelimitstr (here, end, '.', tryel_add);
+    if (!first) tryel += ".";
+    first = false;
+    tryel += tryel_add;
+
+    // see if this element is in the set
+    text_tarray::const_iterator h = set1.begin();
+    text_tarray::const_iterator e = set1.end();
+    while (h != e) {
+      if (*h == tryel) return true;
+      ++h;
+    }
+  } while (here != end);
+
+  return false;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/filter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/filter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/filter.h	(revision 16561)
@@ -0,0 +1,147 @@
+/**********************************************************************
+ *
+ * filter.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.
+ *
+ *********************************************************************/
+
+
+#ifndef FILTER_H
+#define FILTER_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+
+
+class filterclass {
+protected:
+  text_t gsdlhome;
+  text_t collecthome;
+  text_t dbhome;
+  text_t collection;
+  text_t collectdir;
+
+  FilterOption_tmap filterOptions;
+
+public:
+  filterclass ();
+  virtual ~filterclass ();
+
+  // configure should be called once for each configuration line
+  // default configures the default filter options
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // init should be called after all the configuration is done but
+  // before any other methods are called
+  // default checks all the filter option defaults
+  virtual bool init (ostream &logout);
+
+  // returns the name of this filter
+  virtual text_t get_filter_name ();
+
+  // returns the current filter options
+  virtual void get_filteroptions (InfoFilterOptionsResponse_t &response,
+				  comerror_t &err, ostream &logout);
+
+  virtual void filter (const FilterRequest_t &request,
+		       FilterResponse_t &response,
+		       comerror_t &err, ostream &logout);
+
+ protected:
+  text_t resolve_db_filename(const text_t& idx,const text_t& file_ext);
+
+
+};
+
+
+// The filterptr class does not 'own' the filter. The 
+// filter should be deleted by the code which created it.
+class filterptr {
+public:
+  filterclass *f;
+
+  filterptr () {f=NULL;}
+  ~filterptr () {}
+};
+
+bool operator==(const filterptr &x, const filterptr &y);
+bool operator<(const filterptr &x, const filterptr &y);
+
+
+typedef map<text_t, filterptr, lttext_t> filterptrmap;
+
+// contains a list of filters indexed by their name
+class filtermapclass {
+protected:
+  filterptrmap filterptrs;
+
+public:
+  // type support for filterptrmap
+  typedef filterptrmap::iterator iterator;
+  typedef filterptrmap::const_iterator const_iterator;
+  typedef filterptrmap::reference reference;
+  typedef filterptrmap::const_reference const_reference;
+  typedef filterptrmap::size_type size_type;
+
+  typedef filterptrmap::difference_type difference_type;
+  typedef filterptrmap::const_reverse_iterator const_reverse_iterator;
+  typedef filterptrmap::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return filterptrs.begin();}
+  const_iterator begin () const {return filterptrs.begin();}
+  iterator end () {return filterptrs.end();}
+  const_iterator end () const {return filterptrs.end();}
+
+  void erase(iterator pos) {filterptrs.erase(pos);}
+  void erase(iterator first, iterator last) {filterptrs.erase(first, last);}
+  filtermapclass &operator=(const filtermapclass &x) {filterptrs=x.filterptrs;return *this;}
+
+  bool empty () const {return filterptrs.empty();}
+  size_type size() const {return filterptrs.size();}
+
+
+  // added functionality
+  void clear () {filterptrs.erase(filterptrs.begin(),filterptrs.end());}
+
+  // thefilter remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addfilter (filterclass *thefilter);
+
+  // getfilter will return NULL if the filter could not be found
+  filterclass *getfilter (const text_t &key);
+};
+
+
+
+// some useful functions for dealing with document sets
+
+// intersect places the result in set1
+void intersect (text_tset &set1, const text_tset &set2);
+void intersect (text_tarray &set1, const text_tset &set2);
+void intersect (text_tarray &set1, const text_tarray &set2);
+
+// tests to see if el is in set
+bool in_set (const text_tset &set1, const text_t &el);
+bool in_set (const text_tarray &set1, const text_t &el);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.cpp	(revision 16561)
@@ -0,0 +1,405 @@
+/**********************************************************************
+ *
+ * lucenequeryfilter.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.
+ *
+ *********************************************************************/
+
+#include "lucenequeryfilter.h"
+#include "fileutil.h"
+#include "lucenesearch.h"
+
+/////////////////////////////////
+// functions for queryfilterclass
+/////////////////////////////////
+
+
+lucenequeryfilterclass::lucenequeryfilterclass () 
+  : queryfilterclass() {
+
+  
+  FilterOption_t filtopt;
+  
+  // -- onePerTerm  Level          enumerated
+  // likely to be Doc, Sec, Para, but we dont assume anything now
+  filtopt.clear();
+  filtopt.name = "Level";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filterOptions["Level"] = filtopt;
+
+  // --  IndexField, enumerated, used to list available fields
+  filtopt.clear();
+  filtopt.name = "IndexField";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["IndexField"] = filtopt;
+
+}
+
+lucenequeryfilterclass::~lucenequeryfilterclass () {
+}
+
+
+//whether a query is a full text browse
+bool lucenequeryfilterclass::full_text_browse (int filterRequestOptions) {
+  return (filterRequestOptions & FRfullTextBrowse);
+}
+
+void lucenequeryfilterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  queryfilterclass::configure(key, cfgline);
+
+  if (key == "indexfieldmap") {
+    indexfieldmap.importmap (cfgline);
+    
+    // update the list of indexes in the filter information
+    text_tarray options;
+    indexfieldmap.gettoarray (options);
+
+    text_tarray::const_iterator here = options.begin();
+    text_tarray::const_iterator end = options.end();
+    bool start = true;
+    while (here !=end) {
+      if (!(*here).empty()) {
+	filterOptions["IndexField"].validValues.push_back(*here);
+	if (start) {
+	  filterOptions["IndexField"].defaultValue = *here;
+	  start = false;
+	}
+      }
+      ++here;
+    }
+  } else if (key == "indexlevels") {
+    text_tarray::const_iterator here = cfgline.begin();
+    text_tarray::const_iterator end = cfgline.end();
+    bool first=true;
+    while (here != end) {
+      if (!(*here).empty()) {
+	if (first) {
+	  first = false;
+	  // the default is the first value
+	  filterOptions["Level"].defaultValue = *here;
+	}
+	filterOptions["Level"].validValues.push_back(*here);
+      }
+      ++here;
+    }
+  } else if (key == "textlevel") {
+      ((lucenesearchclass *)textsearchptr)->set_text_level(cfgline[0]);
+  }
+  
+}
+
+
+void lucenequeryfilterclass::filter(const FilterRequest_t &request,
+				  FilterResponse_t &response,
+				  comerror_t &err, ostream &logout) {  
+
+  outconvertclass text_t2ascii;
+
+  response.clear ();
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null dbclass\n\n";
+    err = configurationError;
+    return;
+  }
+  if (textsearchptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null textsearchclass (lucene)\n\n";
+    err = configurationError;
+    return;
+  }
+  if (full_text_browse(request.filterResultOptions)) {
+    browsefilter(request, response, err, logout);
+    return;
+  }
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return;
+  }
+
+
+  // get the query parameters
+  int startresults, endresults;
+  text_t phrasematch; // not used here any more
+  vector<queryparamclass> queryfilterparams;
+  parse_query_params (request, queryfilterparams, startresults, 
+		      endresults, phrasematch, logout);  
+ 
+   
+  // do query
+  queryresultsclass queryresults;
+  do_multi_query (request, queryfilterparams, queryresults, err, logout);
+  response.error_message = queryresults.error_message;
+  if (err != noError) return;
+
+  // assemble document results
+  if (need_matching_docs (request.filterResultOptions)) {
+    
+    int resultnum = 1;
+    ResultDocInfo_t resultdoc;
+    text_t trans_OID;
+    vector<text_t>::iterator docorder_here = queryresults.docs.docorder.begin();
+    vector<text_t>::iterator docorder_end = queryresults.docs.docorder.end();
+
+    // Now handled by Lucene directly
+    //if (endresults == -1) endresults = MAXNUMDOCS;
+
+    while (docorder_here != docorder_end)
+      {
+        // Now handled by Lucene directly
+        //if (resultnum > endresults) break;
+      
+        // translate the document number
+        if (!translate(db_ptr, *docorder_here, trans_OID))
+          {
+            logout << text_t2ascii
+                   << "warning: could not translate lucene document number \""
+                   << *docorder_here << "\" to OID.\n\n";
+            
+          } 
+        else 
+          {
+            docresultmap::iterator docset_here = queryresults.docs.docset.find (*docorder_here);
+
+            // see if there is a result for this number,
+            // if it is in the request set (or the request set is empty)
+            if (docset_here != queryresults.docs.docset.end() && (request.docSet.empty() || in_set(request.docSet, trans_OID)))
+              {
+                // Now handled by Lucene directly
+                //if (resultnum >= startresults) {
+
+                // add this document
+                resultdoc.OID = trans_OID;
+                resultdoc.result_num = resultnum;
+                resultdoc.ranking = (int)((*docset_here).second.docweight * 10000.0 + 0.5);
+                
+                response.docInfo.push_back (resultdoc);
+                //}
+                ++resultnum;
+              }
+          } // else
+        
+        ++docorder_here;
+      }
+  } // if need matching docs
+  
+  // assemble the term results
+  if (need_term_info(request.filterResultOptions)) {
+    // note: the terms have already been sorted and uniqued - ?? have they??
+
+    TermInfo_t terminfo;
+    bool terms_first = true;
+
+    termfreqclassarray::iterator terms_here = queryresults.terms.begin();
+    termfreqclassarray::iterator terms_end = queryresults.terms.end();
+
+    while (terms_here != terms_end) {
+      terminfo.clear();
+      terminfo.term = (*terms_here).termstr;
+      terminfo.freq = (*terms_here).termfreq;
+      // lucene doesn't return any termvariants at this stage, 
+      // so make sure the original term is set
+      terminfo.matchTerms.push_back(terminfo.term);
+      
+      // this bit gets the matchTerms ie the equivalent (stem/casefold) terms
+      if (terms_first) {
+	text_tset::iterator termvariants_here = queryresults.termvariants.begin();
+	text_tset::iterator termvariants_end = queryresults.termvariants.end();
+	while (termvariants_here != termvariants_end) {
+	  terminfo.matchTerms.push_back (*termvariants_here);
+	  ++termvariants_here;
+	}
+      }
+      terms_first = false;
+      
+      response.termInfo.push_back (terminfo);
+
+      ++terms_here;
+    }
+
+    // add the stop words
+    text_tset::iterator stopwords_here = queryresults.stopwords.begin();
+    text_tset::iterator stopwords_end = queryresults.stopwords.end();
+    while (stopwords_here != stopwords_end) {
+      response.stopwords.insert(*stopwords_here);
+      ++stopwords_here;
+    }
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  response.numDocs = queryresults.docs_matched;
+  response.isApprox = queryresults.is_approx;
+}
+
+void lucenequeryfilterclass::browsefilter(const FilterRequest_t &request,
+					FilterResponse_t &response,
+					comerror_t &err, ostream &logout) {  
+
+  outconvertclass text_t2ascii;
+
+  // get the query parameters
+  int startresults, endresults;
+  text_t phrasematch; // not used here any more, just have it so can use
+                      // parse_query_params function
+  
+  vector<queryparamclass> queryfilterparams;
+  parse_query_params (request, queryfilterparams, startresults, 
+		      endresults, phrasematch, logout);  
+
+    vector<queryparamclass>::const_iterator query_here = queryfilterparams.begin();
+   
+  // do query
+  queryresultsclass queryresults;
+  queryresults.clear();
+  
+  int numDocs = endresults-startresults;
+  textsearchptr->setcollectdir (collectdir);
+
+  if (!((lucenesearchclass*)textsearchptr)->browse_search((*query_here), startresults, numDocs, queryresults)) {
+    // most likely a system problem
+    logout << text_t2ascii
+	   << "system problem: could not do full text browse with lucene for index \""
+	   << (*query_here).index << (*query_here).subcollection
+	   << (*query_here).language << "\".\n\n";
+    err = systemProblem;
+    return;
+  }
+
+  // assemble the term results
+  TermInfo_t terminfo;
+  
+  termfreqclassarray::iterator terms_here = queryresults.terms.begin();
+  termfreqclassarray::iterator terms_end = queryresults.terms.end();
+
+  while (terms_here != terms_end) {
+    terminfo.clear();
+    terminfo.term = (*terms_here).termstr;
+    terminfo.freq = (*terms_here).termfreq;
+    
+    response.termInfo.push_back (terminfo);
+
+    ++terms_here;
+  }
+  
+
+}
+
+// lucenesearchptr and db_ptr are assumed to be valid
+void lucenequeryfilterclass::do_multi_query (const FilterRequest_t &request,
+					   const vector<queryparamclass> &query_params,
+					   queryresultsclass &multiresults,
+					   comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  err = noError;
+  textsearchptr->setcollectdir (collectdir);
+  multiresults.clear();
+  
+  vector<queryparamclass>::const_iterator query_here = query_params.begin();
+  vector<queryparamclass>::const_iterator query_end = query_params.end();
+  while (query_here != query_end) {
+    queryresultsclass thisqueryresults;
+    if (!textsearchptr->search((*query_here), thisqueryresults)) {
+      // most likely a system problem
+      logout << text_t2ascii
+	     << "system problem: could not do search with lucene for index \""
+	     << (*query_here).index << (*query_here).level 
+	     << (*query_here).subcollection
+	     << (*query_here).language << "\".\n\n";
+      err = systemProblem;
+      return;
+    }
+
+    // check for syntax error
+    if (thisqueryresults.syntax_error==true) {
+      logout << text_t2ascii
+	     << "syntax problem: invalid query string \""
+	     << (*query_here).querystring<<"\".\n";
+      err = syntaxError;
+      return;
+    }
+    // combine the results
+    if (need_matching_docs (request.filterResultOptions)) {
+            
+      if (query_params.size() == 1) {
+	multiresults.error_message = thisqueryresults.error_message;
+	multiresults.docs = thisqueryresults.docs; // just one set of results
+	multiresults.docs_matched = thisqueryresults.docs_matched;
+	multiresults.is_approx = thisqueryresults.is_approx;
+	
+      } else {
+	if ((*query_here).combinequery == "and") {
+	  multiresults.docs.combine_and (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "or") {
+	  multiresults.docs.combine_or (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "not") {
+	  multiresults.docs.combine_not (thisqueryresults.docs);
+	}
+	multiresults.docs_matched = multiresults.docs.docset.size();
+	multiresults.is_approx = Exact;
+      }
+    }
+
+    // combine the term information 
+    if (need_term_info (request.filterResultOptions)) {
+      // append the terms
+      multiresults.orgterms.insert(multiresults.orgterms.end(),
+				   thisqueryresults.orgterms.begin(),
+				   thisqueryresults.orgterms.end());
+
+      
+      // add the term variants - 
+      text_tset::iterator termvar_here = thisqueryresults.termvariants.begin();
+      text_tset::iterator termvar_end = thisqueryresults.termvariants.end();
+      while (termvar_here != termvar_end) {
+	multiresults.termvariants.insert(*termvar_here);
+	++termvar_here;
+      }
+
+      // add the stop words
+      text_tset::iterator stopwords_here = thisqueryresults.stopwords.begin();
+      text_tset::iterator stopwords_end = thisqueryresults.stopwords.end();
+      while (stopwords_here != stopwords_end) {
+	multiresults.stopwords.insert(*stopwords_here);
+	++stopwords_here;
+      }
+    }
+   
+    ++query_here;
+  }
+
+  // sort and unique the query terms
+  multiresults.sortuniqqueryterms ();
+}
+
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenequeryfilter.h	(revision 16561)
@@ -0,0 +1,67 @@
+/**********************************************************************
+ *
+ * lucenequeryfilter.h -- new queryfilter to use lucene 
+ * 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 LUCENEQUERYFILTER_H
+#define LUCENEQUERYFILTER_H
+
+#include "queryfilter.h"
+
+class lucenequeryfilterclass : public queryfilterclass {
+protected:
+
+  stringmap indexfieldmap;
+  bool full_text_browse (int filterRequestOptions);
+
+  // mgsearchptr and db_ptr are assumed to be valid
+  void do_multi_query (const FilterRequest_t &request,
+		       const vector<queryparamclass> &query_params,
+		       queryresultsclass &multiresults,
+		       comerror_t &err, ostream &logout);
+
+
+public:
+  lucenequeryfilterclass ();
+  virtual ~lucenequeryfilterclass ();
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  void filter (const FilterRequest_t &request,
+		       FilterResponse_t &response,
+		       comerror_t &err, ostream &logout);
+  
+  void browsefilter(const FilterRequest_t &request,
+		    FilterResponse_t &response,
+		    comerror_t &err, ostream &logout);  
+
+
+};
+
+#endif
+
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.cpp	(revision 16561)
@@ -0,0 +1,242 @@
+/**********************************************************************
+ *
+ * lucenesearch.cpp -- 
+ * Copyright (C) 1999-2002  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.
+ *
+ *********************************************************************/
+
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+
+#include "gsdlconf.h"
+#include "gsdltools.h"
+#include "lucenesearch.h"
+#include "fileutil.h"
+#include "queryinfo.h"
+#include "gsdlunicode.h"
+
+#include "expat_resultset.h"
+
+text_t lucenesearchclass::getindexsuffix(const queryparamclass &qp) {
+  text_t indexsuffix = "index";
+  // get the first char of the level to be the start of the index name
+  text_t suffix = substr(qp.level.begin(), qp.level.begin()+1);
+  lc(suffix);
+  text_t ind = qp.index;
+  text_t sub = qp.subcollection;
+  text_t lang = qp.language;
+   
+  // collection name not added for Lucene
+  indexsuffix = filename_cat(indexsuffix, suffix +ind + sub + lang);
+  return indexsuffix;
+
+}
+
+////////////////////
+// lucenesearch class //
+////////////////////
+
+lucenesearchclass::lucenesearchclass ()
+  : searchclass() {
+
+  textlevel = "Doc";
+}
+
+lucenesearchclass::~lucenesearchclass () 
+{
+  if (cache != NULL) 
+    {
+      delete cache;
+      cache = NULL;
+    }
+}
+
+void lucenesearchclass::set_text_level(const text_t &textlevel_arg)
+{
+  textlevel = textlevel_arg;
+}
+
+
+bool lucenesearchclass::search(const queryparamclass &queryparams, 
+			       queryresultsclass &queryresult) {
+ 
+#ifdef __WIN32__
+  char basepath[]="";
+#else
+  char basepath[] = "/";
+#endif
+ 
+  char *indexname = (filename_cat(collectdir, getindexsuffix(queryparams))).getcstr(); 
+
+  // set default stem method from values originally set on prefs page
+  int defaultStemMethod = 0;
+  if (queryparams.casefolding) {
+    defaultStemMethod |= 1;
+  }
+  if (queryparams.stemming) {
+    defaultStemMethod |= 2;
+  }
+
+  text_t utf8querystring = to_utf8(queryparams.querystring);
+
+  text_t escaped_utf8querystring = "";
+  text_t::const_iterator here = utf8querystring.begin();
+  while (here != utf8querystring.end()) {
+    if (*here == '"') escaped_utf8querystring.push_back('\\');
+    escaped_utf8querystring.push_back(*here);
+    ++here;
+  }
+
+  // Use this to skip lucene_query.pl and access GS2LuceneQuery directly (Java must be on path)
+  // text_t cmd = "java -classpath \"" + filename_cat(gsdlhome, "bin", "java", "LuceneWrapper.jar") + "\" org.greenstone.LuceneWrapper.GS2LuceneQuery ";
+  text_t cmd = "\"" + filename_cat(gsdlhome, "bin", "script", "lucene_query.pl") + "\"";
+  cmd += (text_t)" \""+indexname + (text_t)"\" \"" + escaped_utf8querystring + (text_t)"\""; 
+
+  if (!queryparams.filterstring.empty()) {
+    cmd += " -filter \"" + queryparams.filterstring + "\"";
+  }
+  if (!queryparams.sortfield.empty()) {
+    cmd += " -sort \"" + queryparams.sortfield + "\"";
+  }
+  if (!queryparams.fuzziness.empty()) {
+    cmd += " -fuzziness " + queryparams.fuzziness;
+  }
+
+  // New code to support configuration of the default conjuction operator
+  // set default Boolean combiner from all/some setting
+  // if match_mode == 1, ie all, default=1 ie AND
+  // if match_mode == 0, ie some, default=0, ie OR
+  if (queryparams.match_mode)
+    {
+      cmd += " -dco AND";
+    }
+
+  // New code to allow Lucene to do paging of search results. This should
+  // substantially improve performance as we don't have to return all 12000
+  // hits if we only need the first 20!
+  if (queryparams.startresults && queryparams.endresults)
+    {
+      cmd += (text_t)" -startresults " + queryparams.startresults;
+      cmd += (text_t)" -endresults " + queryparams.endresults;
+    }
+
+  text_t xml_text = "";
+
+  // I don't want to do this, but I have to.
+  text_t gsdlhome_env = "GSDLHOME=" + gsdlhome;
+  putenv(gsdlhome_env.getcstr());
+
+#ifdef __WIN32__
+  putenv("GSDLOS=windows");
+
+  //FILE *PIN = _popen(cmd.getcstr(), "r"); // didn't seem to work
+  cmd = (text_t)"perl -S "+cmd;
+  // we write the result to a file
+  clock_t this_time = clock();
+  text_t filename = "luc";
+  filename.append(this_time);
+  filename.append(".txt");
+ 
+  text_t out_file = filename_cat(collectdir, filename);
+  cmd += " -out \"" + out_file + "\"";
+  int rv = gsdl_system(cmd, true, cerr);
+  if (rv != 0) {
+    cerr << "tried to run command \""<<cmd<<"\", but it failed\n";
+  } else {
+    read_file(out_file, xml_text);
+    remove(out_file.getcstr()); // now delete it
+  }
+#else
+  putenv("GSDLOS=linux");
+
+  FILE *PIN = popen(cmd.getcstr(), "r");
+
+  if (PIN==NULL) {
+    perror("PIPE");
+    cerr << "Error: unable to open pipe to " << cmd << endl;
+    
+    return false;
+  }
+  while (!feof(PIN)) {
+    char buffer[256];
+    int num_bytes = fread(buffer,1,256,PIN);
+    xml_text.appendcarr(buffer,num_bytes);
+  }
+
+#endif
+
+  expat_resultset(xml_text,queryresult);
+  
+#ifdef __WIN32__
+  //  _pclose(PIN);
+#else
+  pclose(PIN);
+#endif
+
+  return true;
+}
+
+
+bool lucenesearchclass::browse_search(const queryparamclass &queryparams, 
+				      int start, int numDocs,
+				      queryresultsclass &queryresult) {
+
+  cerr << "**** Not sure what this function does!" << endl;
+  return false;
+}
+
+// the document text for 'docnum' is placed in 'output'
+// docTargetDocument returns 'true' if it was able to
+// try to get a document
+// collection is needed to see if an index from the 
+// collection is loaded. THe default index bits are just there cos
+// the mg version needs them
+
+bool lucenesearchclass::docTargetDocument(const text_t &/*defaultindex*/,
+					const text_t &/*defaultsubcollection*/,
+					const text_t &/*defaultlanguage*/,
+					const text_t &collection,
+					int docnum,
+					text_t &output) {
+
+  // we now get the document directly by lucenesource, so don't use this 
+  // method
+  return false;
+}
+
+// used to clear any cached databases for persistent versions of 
+// Greenstone like the Windows local library
+void lucenesearchclass::unload_database () {
+}
+
+void lucenesearchclass::set_gsdlhome (const text_t &gh)
+{
+  gsdlhome = gh;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenesearch.h	(revision 16561)
@@ -0,0 +1,96 @@
+/**********************************************************************
+ *
+ * lucenesearch.h -- implementation of search class for lucene
+ * 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.
+ *
+ * 
+ *
+ *********************************************************************/
+
+
+#ifndef LUCENESEARCH_H
+#define LUCENESEARCH_H
+
+#include "search.h"
+
+class lucenesearchclass : public searchclass {
+public:
+  lucenesearchclass ();
+  virtual ~lucenesearchclass ();
+
+  virtual text_t get_textindex_type() {return "lucene";}
+
+  // the search results are returned in queryresults
+  // search returns 'true' if it was able to do a search
+  virtual bool search(const queryparamclass &queryparams, 
+		      queryresultsclass &queryresults);
+  
+  // this returns a list of terms in queryresults - no document info is returned
+  // returns a list of numTerms terms, starting from the query string term (in queryparams)
+  // + start. used for full text browsing
+  virtual bool browse_search(const queryparamclass &queryparams, int start, int numTerms,
+			     queryresultsclass &queryresults);
+
+  // the document text for 'docnum' is placed in 'output'
+  // docTargetDocument returns 'true' if it was able to
+  // try to get a document
+  // collection is needed to see if an index from the 
+  // collection is loaded. If no index has been loaded
+  // defaultindex is needed to load one
+  virtual bool docTargetDocument(const text_t &defaultindex,
+			 const text_t &defaultsubcollection,
+			 const text_t &defaultlanguage,
+			 const text_t &collection,
+			 int docnum,
+			 text_t &output);
+
+  /*  // same as docTargetDocument except the document is returned
+  // in its raw form. UDoc remains the property of mg and may
+  // be destroyed on the next call to mg.
+  // not implemented for lucene
+  virtual bool mgdocument (const text_t &defaultindex,
+		   const text_t &defaultsubcollection,
+		   const text_t &defaultlanguage,
+		   const text_t &collection,
+		   int docnum,
+		   char *&UDoc, int &ULen);
+  */
+
+  void set_text_level(const text_t &textlevel_arg);
+  text_t textlevel;
+
+  // used to clear any cached databases for persistent versions of 
+  // Greenstone like the Windows local library
+  void unload_database ();
+
+  void set_gsdlhome (const text_t &gh);
+
+ protected:
+  text_t getindexsuffix(const queryparamclass &qp);
+
+ private:
+  text_t gsdlhome;
+
+};
+
+
+#endif
+
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenesource.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenesource.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenesource.cpp	(revision 16561)
@@ -0,0 +1,94 @@
+/**********************************************************************
+ *
+ * lucenesource.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.
+ *
+ *********************************************************************/
+
+#include "lucenesource.h"
+#include "fileutil.h"
+#include "OIDtools.h"
+#include "expat_document.h"
+#include "lucenesearch.h"
+
+lucenesourceclass::lucenesourceclass () 
+  : sourceclass()
+{
+  classname = "lucenesource";
+}
+
+lucenesourceclass::~lucenesourceclass () {
+}
+
+bool lucenesourceclass::get_document (const text_t &OID, text_t &doc, 
+				      comerror_t &err, ostream &logout) {
+
+  outconvertclass text_t2ascii;
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: lucenesource contains a null dbclass\n\n";
+    err = configurationError;
+    return true;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return true;
+  }
+
+  text_t tOID = OID;
+  if (needs_translating (OID)) 
+    translate_OID (OID, tOID, err, logout);
+  infodbclass info;
+  if (!db_ptr->getinfo(tOID, info)) {
+    db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+    return false;
+  }
+
+  if (info["hastxt"].getint() == 0) { // there is no text for this section
+    return false; // true??
+  }
+  int docnum = info["docnum"].getint();
+
+  // get the parent id
+  text_t parent_OID;
+  get_top(tOID, parent_OID);
+  
+  if (!db_ptr->getinfo(parent_OID, info)) {
+    db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+    return false;
+  }
+
+  text_t archive_dir = info["archivedir"];
+  text_t full_path_to_doc = filename_cat(collectdir, "index", "text", archive_dir, "doc.xml");
+  
+  doc.clear();
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  expat_document(full_path_to_doc, ((lucenesearchclass*)textsearchptr)->textlevel, text_t(docnum), doc);
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/lucenesource.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/lucenesource.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/lucenesource.h	(revision 16561)
@@ -0,0 +1,50 @@
+/**********************************************************************
+ *
+ * lucenesource.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.
+ *
+ *********************************************************************/
+
+
+#ifndef LUCENESOURCE_H
+#define LUCENESOURCE_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+#include "maptools.h"
+#include "search.h"
+#include "source.h"
+
+
+class lucenesourceclass : public sourceclass {
+public:
+  lucenesourceclass ();
+  virtual ~lucenesourceclass ();
+
+  bool get_document (const text_t &OID, text_t &doc, 
+		     comerror_t &err, ostream &logout);
+
+};
+
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/maptools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/maptools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/maptools.cpp	(revision 16561)
@@ -0,0 +1,183 @@
+/**********************************************************************
+ *
+ * maptools.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.
+ *
+ *********************************************************************/
+
+#include "maptools.h"
+
+
+
+void stringmap::clear () {
+  fromorder.erase (fromorder.begin(), fromorder.end());
+  mapfrom.erase (mapfrom.begin(), mapfrom.end());
+}
+
+void stringmap::importmap (const text_tarray &maparray) {
+  importmap(maparray, false);
+}
+
+void stringmap::importmap (const text_tarray &maparray, bool keepold) {
+  if (!keepold) {
+    clear ();
+  }
+  
+  text_tarray::const_iterator here = maparray.begin();
+  text_tarray::const_iterator end = maparray.end();
+  text_t from;
+  text_t to;
+
+  while (here != end) {
+    splitmapentry (*here, from, to);
+    fromorder.push_back (from);
+    mapfrom[from] = to;
+    ++here;
+  }
+}
+
+void stringmap::exportmap (text_tarray &maparray) const {
+  maparray.erase (maparray.begin(), maparray.end());
+
+  text_tarray::const_iterator here = fromorder.begin();
+  text_tarray::const_iterator end = fromorder.end();
+  text_tmap::const_iterator toptr;
+  text_t tempmap;
+
+  while (here != end) {
+    toptr = mapfrom.find (*here);
+    if (toptr != mapfrom.end()) {
+      joinmapentry ((*toptr).first, (*toptr).second, tempmap);
+      maparray.push_back (tempmap);
+    }
+
+    ++here;
+  }
+}
+
+void stringmap::getfromarray (text_tarray &fromarray) const {
+  fromarray = fromorder;
+}
+
+void stringmap::gettoarray (text_tarray &toarray) const {
+  toarray.erase(toarray.begin(), toarray.end());
+
+  text_tarray::const_iterator here = fromorder.begin();
+  text_tarray::const_iterator end = fromorder.end();
+  text_tmap::const_iterator toptr;
+
+  while (here != end) {
+    toptr = mapfrom.find (*here);
+    if (toptr != mapfrom.end()) {
+      toarray.push_back((*toptr).second);
+    }
+    ++here;
+  }
+}
+
+bool stringmap::fromexists (const text_t &from) const {
+  return (mapfrom.find (from) != mapfrom.end ());
+}
+
+bool stringmap::toexists (const text_t &to) const {
+  text_tarray::const_iterator here = fromorder.begin();
+  text_tarray::const_iterator end = fromorder.end();
+  text_tmap::const_iterator toptr;
+
+  while (here != end) {
+    toptr = mapfrom.find (*here);
+    if (toptr != mapfrom.end() && (*toptr).second == to) return true;
+    ++here;
+  }
+  
+  // it was not found
+  return false;
+}
+
+bool stringmap::from2to (const text_t &from, text_t &to) const {
+  text_tmap::const_iterator toptr = mapfrom.find (from);
+  if (toptr == mapfrom.end()) {
+    to.clear();
+    return false;
+  }
+
+  to = (*toptr).second;
+  return true;
+}
+
+bool stringmap::to2from (const text_t &to, text_t &from) const {
+  text_tarray::const_iterator here = fromorder.begin();
+  text_tarray::const_iterator end = fromorder.end();
+  text_tmap::const_iterator toptr;
+
+  while (here != end) {
+    toptr = mapfrom.find (*here);
+    if (toptr != mapfrom.end() && (*toptr).second == to) {
+      from = (*toptr).first;
+      return true;
+    }
+    ++here;
+  }
+  
+  // it was not found
+  from.clear();
+  return false;
+}
+
+
+
+void splitmapentry (const text_t &mapentry, text_t &from, text_t &to) {
+  from.clear ();
+  to.clear();
+
+  text_t::const_iterator here = mapentry.begin();
+  text_t::const_iterator end = mapentry.end();
+
+  // get the "from" part of the map
+  while (here != end) {
+    if (*here == '-') {
+      ++here;
+      if (here != end && *here == '>') {
+	++here;
+	break; // found "->"
+      }
+
+      // didn't find "->"
+      from.push_back('-');
+
+    } else {
+      from.push_back(*here);
+      ++here;
+    }
+  }
+
+  // get the "to" part of the map
+  while (here != end) {
+    to.push_back(*here);
+    ++here;
+  }
+}
+
+void joinmapentry (const text_t &from, const text_t &to, text_t &mapentry) {
+  mapentry = from;
+  mapentry += "->";
+  mapentry += to;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/maptools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/maptools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/maptools.h	(revision 16561)
@@ -0,0 +1,66 @@
+/**********************************************************************
+ *
+ * maptools.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.
+ *
+ *********************************************************************/
+
+
+#ifndef MAPTOOLS_H
+#define MAPTOOLS_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+// stringmap is a mapping which remembers the original order 
+// of the map, but is very fast to look up.
+class stringmap {
+protected:
+  text_tarray fromorder;
+  text_tmap mapfrom;
+
+public:
+  void clear ();
+
+  void importmap (const text_tarray &maparray);
+  void importmap (const text_tarray &maparray, bool keepold);
+  void exportmap (text_tarray &maparray) const;
+
+  void getfromarray (text_tarray &fromarray) const;
+  void gettoarray (text_tarray &toarray) const;
+  
+  bool fromexists (const text_t &from) const;
+  bool toexists (const text_t &to) const;
+
+  // from2to and to2from return false if the key (the "from"
+  // field or the "to" field) cannot be found
+  bool from2to (const text_t &from, text_t &to) const;
+  bool to2from (const text_t &to, text_t &from) const;
+};
+
+
+// a few useful functions used by stringmap
+void splitmapentry (const text_t &mapentry, text_t &from, text_t &to);
+void joinmapentry (const text_t &from, const text_t &to, text_t &mapentry);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.cpp	(revision 16561)
@@ -0,0 +1,385 @@
+/**********************************************************************
+ *
+ * queryfilter.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.
+ *
+ *********************************************************************/
+
+#include "mgppqueryfilter.h"
+#include "fileutil.h"
+#include "mgppsearch.h"
+
+/////////////////////////////////
+// functions for queryfilterclass
+/////////////////////////////////
+
+
+mgppqueryfilterclass::mgppqueryfilterclass () 
+  : queryfilterclass() {
+
+  
+  FilterOption_t filtopt;
+  
+  // -- onePerTerm  Level          enumerated
+  // likely to be Doc, Sec, Para, but we dont assume anything now
+  filtopt.clear();
+  filtopt.name = "Level";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filterOptions["Level"] = filtopt;
+
+  // --  IndexField, enumerated, used to list available fields
+  filtopt.clear();
+  filtopt.name = "IndexField";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["IndexField"] = filtopt;
+
+}
+
+mgppqueryfilterclass::~mgppqueryfilterclass () {
+}
+
+
+//whether a query is a full text browse
+bool mgppqueryfilterclass::full_text_browse (int filterRequestOptions) {
+  return (filterRequestOptions & FRfullTextBrowse);
+}
+
+void mgppqueryfilterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  queryfilterclass::configure(key, cfgline);
+
+  if (key == "indexfieldmap") {
+    indexfieldmap.importmap (cfgline);
+    
+    // update the list of indexes in the filter information
+    text_tarray options;
+    indexfieldmap.gettoarray (options);
+    filterOptions["IndexField"].validValues = options;
+    
+  } else if (key == "indexlevels") {
+    text_tarray::const_iterator here = cfgline.begin();
+    text_tarray::const_iterator end = cfgline.end();
+    bool first=true;
+    filterOptions["Level"].validValues.erase(filterOptions["Level"].validValues.begin(), filterOptions["Level"].validValues.end());
+    while (here != end) {
+      if (!(*here).empty()) {
+	if (first) {
+	  first = false;
+	  // the default is the first value
+	  filterOptions["Level"].defaultValue = *here;
+	}
+	filterOptions["Level"].validValues.push_back(*here);
+      }
+      ++here;
+    }
+  } else if (key == "textlevel") {
+      ((mgppsearchclass *)textsearchptr)->set_text_level(cfgline[0]);
+  } else if (key == "indexstem") {
+    ((mgppsearchclass *)textsearchptr)->set_indexstem (cfgline[0]);
+  } else if (key == "defaultindex") { // used for fields in mgpp
+    indexfieldmap.from2to (cfgline[0], filterOptions["IndexField"].defaultValue);
+  }
+  
+}
+
+bool mgppqueryfilterclass::init (ostream &logout) {
+  
+  if (!queryfilterclass::init(logout)) {
+    return false;
+  }
+  
+  if (filterOptions["IndexField"].defaultValue.empty()) {
+    // use first index in map as default if no default is set explicitly
+    text_tarray fromarray;
+    indexfieldmap.getfromarray(fromarray);
+    if (fromarray.size()) {
+      filterOptions["IndexField"].defaultValue = fromarray[0];
+    }
+  }
+  return true;
+}
+
+void mgppqueryfilterclass::filter(const FilterRequest_t &request,
+				  FilterResponse_t &response,
+				  comerror_t &err, ostream &logout) {  
+
+
+  outconvertclass text_t2ascii;
+
+  response.clear ();
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null dbclass\n\n";
+    err = configurationError;
+    return;
+  }
+  if (textsearchptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null textsearchclass for mgpp\n\n";
+    err = configurationError;
+    return;
+  }
+  if (full_text_browse(request.filterResultOptions)) {
+    browsefilter(request, response, err, logout);
+    return;
+  }
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return;
+  }
+
+
+  // get the query parameters
+  int startresults, endresults;
+  text_t phrasematch; // not used here any more
+  vector<queryparamclass> queryfilterparams;
+  parse_query_params (request, queryfilterparams, startresults, 
+		      endresults, phrasematch, logout);  
+ 
+   
+  // do query
+  queryresultsclass queryresults;
+  do_multi_query (request, queryfilterparams, queryresults, err, logout);
+  if (err != noError) return;
+  // assemble document results
+  if (need_matching_docs (request.filterResultOptions)) {
+    
+    int resultnum = 1;
+    ResultDocInfo_t resultdoc;
+    text_t trans_OID;
+    vector<text_t>::iterator docorder_here = queryresults.docs.docorder.begin();
+    vector<text_t>::iterator docorder_end = queryresults.docs.docorder.end();
+
+    if (endresults == -1) endresults = MAXNUMDOCS;
+    while (docorder_here != docorder_end) {
+      if (resultnum > endresults) break;
+      
+      // translate the document number
+      if (!translate(db_ptr, *docorder_here, trans_OID)) {
+	logout << text_t2ascii
+	       << "warning: could not translate mgpp document number \""
+	       << *docorder_here << "\"to OID.\n\n";
+	
+      } else {
+	docresultmap::iterator docset_here = queryresults.docs.docset.find (*docorder_here);
+
+	// see if there is a result for this number,
+	// if it is in the request set (or the request set is empty)
+	if (docset_here != queryresults.docs.docset.end() &&
+	    (request.docSet.empty() || in_set(request.docSet, trans_OID))) {
+	  if (resultnum >= startresults) {
+	    // add this document
+	    resultdoc.OID = trans_OID;
+	    resultdoc.result_num = resultnum;
+	    resultdoc.ranking = (int)((*docset_here).second.docweight * 10000.0 + 0.5);
+
+	    response.docInfo.push_back (resultdoc);
+	  }
+	  
+	  ++resultnum;
+	}
+      } // else
+      
+      ++docorder_here;
+    }
+  } // if need matching docs
+
+  // assemble the term results
+  if (need_term_info(request.filterResultOptions)) {
+    // note: the terms have already been sorted and uniqued - ?? have they??
+
+    TermInfo_t terminfo;
+    bool terms_first = true;
+
+    termfreqclassarray::iterator terms_here = queryresults.terms.begin();
+    termfreqclassarray::iterator terms_end = queryresults.terms.end();
+
+    while (terms_here != terms_end) {
+      terminfo.clear();
+      terminfo.term = (*terms_here).termstr;
+      terminfo.freq = (*terms_here).termfreq;
+
+      // this bit gets the matchTerms ie the equivalent (stem/casefold) terms
+      if (terms_first) {
+	text_tset::iterator termvariants_here = queryresults.termvariants.begin();
+	text_tset::iterator termvariants_end = queryresults.termvariants.end();
+	while (termvariants_here != termvariants_end) {
+	  terminfo.matchTerms.push_back (*termvariants_here);
+	  ++termvariants_here;
+	}
+      }
+      terms_first = false;
+      
+      response.termInfo.push_back (terminfo);
+
+      ++terms_here;
+    }
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  response.numDocs = queryresults.docs_matched;
+  response.isApprox = queryresults.is_approx;
+}
+
+void mgppqueryfilterclass::browsefilter(const FilterRequest_t &request,
+					FilterResponse_t &response,
+					comerror_t &err, ostream &logout) {  
+
+  outconvertclass text_t2ascii;
+
+  // get the query parameters
+  int startresults, endresults;
+  text_t phrasematch; // not used here any more, just have it so can use
+                      // parse_query_params function
+  
+  vector<queryparamclass> queryfilterparams;
+  parse_query_params (request, queryfilterparams, startresults, 
+		      endresults, phrasematch, logout);  
+
+    vector<queryparamclass>::const_iterator query_here = queryfilterparams.begin();
+   
+  // do query
+  queryresultsclass queryresults;
+  queryresults.clear();
+  
+  int numDocs = endresults-startresults;
+  textsearchptr->setcollectdir (collectdir);
+
+  if (!((mgppsearchclass*)textsearchptr)->browse_search((*query_here), startresults, numDocs, queryresults)) {
+    // most likely a system problem
+    logout << text_t2ascii
+	   << "system problem: could not do full text browse with mgpp for index \""
+	   << (*query_here).index << (*query_here).subcollection
+	   << (*query_here).language << "\".\n\n";
+    err = systemProblem;
+    return;
+  }
+
+  // assemble the term results
+  TermInfo_t terminfo;
+  
+  termfreqclassarray::iterator terms_here = queryresults.terms.begin();
+  termfreqclassarray::iterator terms_end = queryresults.terms.end();
+
+  while (terms_here != terms_end) {
+    terminfo.clear();
+    terminfo.term = (*terms_here).termstr;
+    terminfo.freq = (*terms_here).termfreq;
+    
+    response.termInfo.push_back (terminfo);
+
+    ++terms_here;
+  }
+  
+
+}
+
+// textsearchptr and db_ptr are assumed to be valid
+void mgppqueryfilterclass::do_multi_query (const FilterRequest_t &request,
+					   const vector<queryparamclass> &query_params,
+					   queryresultsclass &multiresults,
+					   comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  err = noError;
+  textsearchptr->setcollectdir (collectdir);
+  multiresults.clear();
+  
+  vector<queryparamclass>::const_iterator query_here = query_params.begin();
+  vector<queryparamclass>::const_iterator query_end = query_params.end();
+  while (query_here != query_end) {
+    queryresultsclass thisqueryresults;
+    text_t indx((*query_here).index);
+    if (!textsearchptr->search((*query_here), thisqueryresults)) {
+      // most likely a system problem
+      logout << text_t2ascii
+	     << "system problem: could not do search with mgpp for index \""
+	     << (*query_here).index << (*query_here).subcollection
+	     << (*query_here).language << "\".\n\n";
+      err = systemProblem;
+      return;
+    }
+
+    // check for syntax error
+    if (thisqueryresults.syntax_error==true) {
+      logout << text_t2ascii
+	     << "syntax problem: invalid query string \""
+	     << (*query_here).querystring<<"\".\n";
+      err = syntaxError;
+      return;
+    }
+    // combine the results
+    if (need_matching_docs (request.filterResultOptions)) {
+            
+      if (query_params.size() == 1) {
+	multiresults.docs = thisqueryresults.docs; // just one set of results
+	multiresults.docs_matched = thisqueryresults.docs_matched;
+	multiresults.is_approx = thisqueryresults.is_approx;
+	
+      } else {
+	if ((*query_here).combinequery == "and") {
+	  multiresults.docs.combine_and (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "or") {
+	  multiresults.docs.combine_or (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "not") {
+	  multiresults.docs.combine_not (thisqueryresults.docs);
+	}
+	multiresults.docs_matched = multiresults.docs.docset.size();
+	multiresults.is_approx = Exact;
+      }
+    }
+
+    // combine the term information 
+    if (need_term_info (request.filterResultOptions)) {
+      // append the terms
+      multiresults.orgterms.insert(multiresults.orgterms.end(),
+				   thisqueryresults.orgterms.begin(),
+				   thisqueryresults.orgterms.end());
+
+      
+      // add the term variants - 
+      text_tset::iterator termvar_here = thisqueryresults.termvariants.begin();
+      text_tset::iterator termvar_end = thisqueryresults.termvariants.end();
+      while (termvar_here != termvar_end) {
+	multiresults.termvariants.insert(*termvar_here);
+	++termvar_here;
+      }
+    }
+   
+    ++query_here;
+  }
+
+  // sort and unique the query terms
+  multiresults.sortuniqqueryterms ();
+}
+
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgppqueryfilter.h	(revision 16561)
@@ -0,0 +1,63 @@
+/**********************************************************************
+ *
+ * mgppqueryfilter.h -- new queryfilter to use mgpp 
+ * 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 MGPPQUERYFILTER_H
+#define MGPPQUERYFILTER_H
+
+#include "queryfilter.h"
+
+
+class mgppqueryfilterclass : public queryfilterclass {
+protected:
+
+  stringmap indexfieldmap;
+  bool full_text_browse (int filterRequestOptions);
+
+  // textsearchptr and db_ptr are assumed to be valid
+  void do_multi_query (const FilterRequest_t &request,
+		       const vector<queryparamclass> &query_params,
+		       queryresultsclass &multiresults,
+		       comerror_t &err, ostream &logout);
+
+
+public:
+  mgppqueryfilterclass ();
+  virtual ~mgppqueryfilterclass ();
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+  bool init (ostream &logout);
+  void filter (const FilterRequest_t &request,
+		       FilterResponse_t &response,
+		       comerror_t &err, ostream &logout);
+  
+  void browsefilter(const FilterRequest_t &request,
+		    FilterResponse_t &response,
+		    comerror_t &err, ostream &logout);  
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.cpp	(revision 16561)
@@ -0,0 +1,346 @@
+/**********************************************************************
+ *
+ * mgppsearch.cpp -- 
+ * Copyright (C) 1999-2002  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.
+ *
+ *********************************************************************/
+
+
+#include "gsdlconf.h"
+#include "mgppsearch.h"
+#include "fileutil.h"
+#include "GSDLQueryParser.h"
+#include "MGQuery.h"
+#include "TextGet.h"
+#include "queryinfo.h"
+#include "gsdlunicode.h"
+
+
+text_t mgppsearchclass::getindexsuffix(const queryparamclass &qp) {
+  return getindexsuffix(qp.collection, qp.index+qp.subcollection+qp.language);
+
+}
+
+text_t mgppsearchclass::getindexsuffix (const text_t &collection, 
+			      const text_t &index) {
+
+  text_t indexsuffix = "index";  
+  indexsuffix = filename_cat (indexsuffix, index);
+  if (indexstem.empty()) {
+    // no index stem, use the coll name
+    indexsuffix = filename_cat (indexsuffix, collection);
+  } else {
+    indexsuffix = filename_cat (indexsuffix, indexstem);
+  }
+  return indexsuffix;
+}
+
+////////////////////
+// mgppsearch class //
+////////////////////
+
+mgppsearchclass::mgppsearchclass ()
+  : searchclass() {
+
+  textlevel = "Doc";
+  indexData = NULL;
+}
+
+mgppsearchclass::~mgppsearchclass () 
+{
+  if (cache != NULL) 
+    {
+      delete cache;
+      cache = NULL;
+    }
+
+  if (indexData !=NULL) {
+    indexData->UnloadData();
+    delete indexData;
+    indexData = NULL;
+  }
+  
+}
+
+void mgppsearchclass::set_text_level(const text_t &textlevel_arg)
+{
+  textlevel = textlevel_arg;
+}
+
+void mgppsearchclass::set_indexstem(const text_t &stem)
+{
+  indexstem = stem;
+}
+
+
+bool mgppsearchclass::search(const queryparamclass &queryparams, 
+			     queryresultsclass &queryresult) {
+ 
+#ifdef __WIN32__
+  char basepath[]="";
+#else
+  char basepath[] = "/";
+#endif
+ 
+  char *indexname = (filename_cat(collectdir, getindexsuffix(queryparams))).getcstr(); 
+
+ // load index data
+  if (indexData == NULL) {
+    indexData = new IndexData();
+  }
+  if (!indexData->LoadData (basepath, indexname)) {
+    cerr<<"couldn't load index data\n"<<endl;
+    return false;
+  }
+
+  // set default stem method from values originally set on prefs page
+  int defaultStemMethod = 0;
+  if (queryparams.casefolding) {
+    defaultStemMethod |= STEM_CaseFolding;
+  }
+  if (queryparams.stemming) {
+    defaultStemMethod |= STEM_Stemming;
+  }
+  if (queryparams.accentfolding) {
+    defaultStemMethod |= STEM_AccentFolding;
+  }  	
+  
+  // set default Boolean combiner from all/some setting
+  // if match_mode == 1, ie all, default=1 ie AND
+  // if match_mode == 0, ie some, default=0, ie OR
+  int defaultBoolCombine = 0;
+  if (queryparams.match_mode){
+    defaultBoolCombine = 1;
+  }
+
+  // use default query info settings - change to reflect user preferences??
+  QueryInfo queryInfo;
+  
+  SetCStr (queryInfo.docLevel, (queryparams.level.getcstr())); 
+  queryInfo.maxDocs = (unsigned long)queryparams.maxdocs;
+  queryInfo.sortByRank = (queryparams.search_type == 1);
+  queryInfo.exactWeights = false;
+  queryInfo.needRankInfo = true; // used for overall term freq as well as ranking
+  queryInfo.needTermFreqs = true;
+  
+  ExtQueryResult queryResult;
+  
+  UCArray queryArray;
+  // greenstone gives us the query encoded in unicode. We want utf8.
+  char* utf8querystring=to_utf8(queryparams.querystring).getcstr();
+  SetCStr(queryArray, utf8querystring);
+  delete []utf8querystring;
+
+  // create the mgpp query tree
+  QueryNode *queryTree = NULL;
+  queryTree = ParseQuery(queryArray, defaultBoolCombine, defaultStemMethod, queryparams.maxnumeric);
+  if (queryTree == NULL) { // syntax error
+    queryresult.syntax_error = true;
+    return true; // should we return true or false?
+  }
+  UCArray level;
+  UCArrayClear(level);
+  
+  //set the level for results
+  SetCStr(level, textlevel.getcstr());
+
+  
+  // do the query
+  MGQuery(*indexData, queryInfo, queryTree, queryResult, level);
+  
+ 
+  // convert ExtQueryResult to queryresultclass
+  
+  queryresult.docs_matched = (int)queryResult.docs.size();
+
+  if (queryresult.docs_matched == (int)queryResult.actualNumDocs) {
+    queryresult.is_approx = Exact; 
+  }
+  else if (queryresult.docs_matched < (int)queryResult.actualNumDocs) {
+    queryresult.is_approx = MoreThan;
+  }
+  else {
+    queryresult.is_approx = Approximate;
+  }
+
+  docresultclass doc;
+  for (int i=0; i<(int)queryResult.docs.size(); ++i) {
+    doc.clear();
+    doc.docnum = (int)queryResult.levels[i];
+    doc.docweight = queryResult.ranks[i];
+    queryresult.docs.docset[doc.docnum] = doc;
+    queryresult.docs.docorder.push_back(doc.docnum);
+
+  }
+
+  // term info
+  termfreqclass term;
+  for (int k=0; k<(int)queryResult.termFreqs.size(); ++k) {
+    term.clear();
+    char* termfreq_cstr=GetCStr(queryResult.termFreqs[k].term);
+    term.termstr = to_uni(termfreq_cstr);
+    delete []termfreq_cstr;
+    term.termstemstr = term.termstr;
+    // we don't set term.utf8equivterms ?? - jrm21
+    term.termfreq = queryResult.termFreqs[k].termFreq;
+    queryresult.terms.push_back(term);
+    queryresult.orgterms.push_back(term); // should this change??
+    
+    for (int j=0; j<(int)queryResult.termFreqs[k].equivTerms.size(); ++j) {
+      char* equivterm_cstr=GetCStr(queryResult.termFreqs[k].equivTerms[j]);
+      queryresult.termvariants.insert(to_uni(equivterm_cstr));
+      delete []equivterm_cstr;
+    }
+
+  }
+  // clean up
+  unload_database();  // Important that local library doesn't leave any files open
+  delete []indexname;
+  return true;
+
+}
+
+
+bool mgppsearchclass::browse_search(const queryparamclass &queryparams, int start, int numDocs,
+				    queryresultsclass &queryresult) {
+
+#ifdef __WIN32__
+  char basepath[]="";
+#else
+  char basepath[] = "/";
+#endif
+
+  char *indexname = (filename_cat(collectdir, getindexsuffix(queryparams))).getcstr();
+ 
+  if (indexData == NULL) {
+    indexData = new IndexData();
+  }
+  if (!indexData->LoadData (basepath, indexname)) {
+    cerr<<"couldn't load index data\n"<<endl;
+    return false;
+  }
+
+  UCArray level;
+  UCArrayClear(level);
+  
+  //browse always at top level
+  SetCStr(level, "Doc"); // this name may change.
+  
+
+  BrowseQueryNode browseNode;
+  browseNode.startPosition = start;
+  browseNode.numTerms = numDocs;
+
+  BrowseQueryResult browseResult;
+
+  UCArrayClear(browseNode.term);
+  // greenstone gives us the query encoded in unicode. We want utf8.
+  char* utf8querystring=to_utf8(queryparams.querystring).getcstr();
+  SetCStr(browseNode.term, utf8querystring);
+  delete []utf8querystring;
+
+  // do the actual query
+  MGBrowseQuery(*indexData, level, browseNode, browseResult);
+
+  // load results into term info
+  termfreqclass term;
+  for (int i=0; i<(int)browseResult.termFreqs.size(); ++i) {
+    term.clear();
+    char* term_cstr = GetCStr(browseResult.termFreqs[i].term);
+    term.termstr = to_uni(term_cstr);
+    delete []term_cstr;
+    term.termstemstr = term.termstr;
+    term.termfreq = browseResult.termFreqs[i].termFreq;
+    queryresult.terms.push_back(term);
+    queryresult.orgterms.push_back(term);
+  }
+
+  // clean up
+  unload_database();  // Important that local library doesn't leave any files open
+  delete []indexname;
+
+  return true;
+}
+
+// the document text for 'docnum' is placed in 'output'
+// docTargetDocument returns 'true' if it was able to
+// try to get a document
+// collection is needed to see if an index from the 
+// collection is loaded. THe default index bits are just there cos
+// the mg version needs them
+
+bool mgppsearchclass::docTargetDocument(const text_t &/*defaultindex*/,
+					const text_t &/*defaultsubcollection*/,
+					const text_t &/*defaultlanguage*/,
+					const text_t &collection,
+					int docnum,
+					text_t &output) {
+  
+#ifdef __WIN32__
+  char basepath[]="";
+#else
+  char basepath[] = "/";
+#endif
+  char *textname = (filename_cat(collectdir, getindexsuffix(collection, "text"))).getcstr();
+ 
+  TextData textdata;
+  if(!textdata.LoadData(basepath, textname)) {
+    cout<<"couldn't load text data\n"<<endl;
+    return false;
+  }
+  UCArray doctext;
+  UCArray level;
+  SetCStr(level, textlevel.getcstr());
+  if (!GetDocText(textdata, level, (unsigned long)docnum, doctext)) {
+    cout<<"couldn't retrieve document text\n";
+    return false;
+  }
+
+  // convert UCArray to text_t
+  output.clear();
+  char* doctext_cstr = GetCStr(doctext);
+  output = to_uni(doctext_cstr); // convert from utf-8 to unicode
+  delete []doctext_cstr;
+
+  // here need to remove the <Document>, <Section>, <Paragraph> tags
+
+  
+  //clean up
+  textdata.UnloadData ();
+  delete []textname;
+
+  return true;
+
+}
+
+// used to clear any cached databases for persistent versions of 
+// Greenstone like the Windows local library
+void mgppsearchclass::unload_database () {
+
+  if (indexData !=NULL) {
+    indexData->UnloadData();
+  }
+}
+
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgppsearch.h	(revision 16561)
@@ -0,0 +1,100 @@
+/**********************************************************************
+ *
+ * mgppsearch.h -- implementation of search class for mgpp
+ * 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.
+ *
+ * 
+ *
+ *********************************************************************/
+
+
+#ifndef MGPPSEARCH_H
+#define MGPPSEARCH_H
+
+#include "search.h"
+#include "IndexData.h"
+
+class mgppsearchclass : public searchclass {
+public:
+  mgppsearchclass ();
+  virtual ~mgppsearchclass ();
+
+  virtual text_t get_textindex_type() {return "mgpp";}
+
+  // the search results are returned in queryresults
+  // search returns 'true' if it was able to do a search
+  virtual bool search(const queryparamclass &queryparams, 
+	      queryresultsclass &queryresults);
+  
+  // this returns a list of terms in queryresults - no document info is returned
+  // returns a list of numTerms terms, starting from the query string term (in queryparams)
+  // + start. used for full text browsing
+  virtual bool browse_search(const queryparamclass &queryparams, int start, int numTerms,
+			     queryresultsclass &queryresults);
+
+  // the document text for 'docnum' is placed in 'output'
+  // docTargetDocument returns 'true' if it was able to
+  // try to get a document
+  // collection is needed to see if an index from the 
+  // collection is loaded. If no index has been loaded
+  // defaultindex is needed to load one
+  virtual bool docTargetDocument(const text_t &defaultindex,
+			 const text_t &defaultsubcollection,
+			 const text_t &defaultlanguage,
+			 const text_t &collection,
+			 int docnum,
+			 text_t &output);
+
+  /*  // same as docTargetDocument except the document is returned
+  // in its raw form. UDoc remains the property of mg and may
+  // be destroyed on the next call to mg.
+  // not implemented for mgpp
+  virtual bool mgdocument (const text_t &defaultindex,
+		   const text_t &defaultsubcollection,
+		   const text_t &defaultlanguage,
+		   const text_t &collection,
+		   int docnum,
+		   char *&UDoc, int &ULen);
+  */
+  
+  void set_text_level(const text_t &textlevel_arg);
+  
+  void set_indexstem(const text_t &indexstem);
+
+  // used to clear any cached databases for persistent versions of 
+  // Greenstone like the Windows local library
+  void unload_database ();
+
+ protected:
+  
+  text_t textlevel;
+  text_t indexstem; // the stem of the index file names
+  IndexData *indexData; // the index data structure needed for MGQuery - keep
+  // as a cache
+  
+  text_t getindexsuffix(const queryparamclass &qp);
+  text_t getindexsuffix (const text_t &collection, const text_t &index);
+
+};
+
+
+#endif
+
Index: /gsdl/trunk/gs2runtime/src/colservr/mgq.c
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgq.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgq.c	(revision 16561)
@@ -0,0 +1,731 @@
+/**********************************************************************
+ *
+ * mgq.c -- cut-dowm version of mgquery
+ * 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.
+ *
+ *********************************************************************/
+
+#include "mgq.h"
+
+
+#include <stdio.h>
+#include <string.h>
+/* #include <io.h> */
+#include <fcntl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+#include "sysfuncs.h"
+  
+#include "messages.h"
+#include "memlib.h"
+  
+#include "invf.h"
+#include "text.h"
+#include "lists.h"
+#include "backend.h"
+#include "environment.h"
+#include "globals.h"
+#include "mg_errors.h"
+#include "commands.h"
+#include "text_get.h"
+#include "term_lists.h"
+#include "local_strings.h"
+
+#include "words.h"
+#include "stemmer.h"
+#include "stem_search.h"
+  
+#ifdef __cplusplus
+}
+#endif
+
+
+#include "mgq.h"
+
+/* get a reasonable database cache size */
+#ifndef MAXNUMDATABASEINFO
+#  ifdef GSDLSERVER
+#    define MAXNUMDATABASEINFO 10
+#  else
+#    define MAXNUMDATABASEINFO 2
+#  endif
+#endif
+
+#define MAXCOLLECTIONLEN  16
+#define MAXMGDIRLEN       256
+#define MAXGENSUFFIXLEN   256
+#define MAXTEXTSUFFIXLEN  256
+
+typedef struct DatabaseInfo {
+  int accessnum; /* -1 = invalid record */
+  char collection[MAXCOLLECTIONLEN];
+  char mgdir[MAXMGDIRLEN];
+  char gensuffix[MAXGENSUFFIXLEN];
+  char textsuffix[MAXTEXTSUFFIXLEN];
+  query_data *qd;
+} DatabaseInfo;
+
+
+/* globals needed by some vague part of mg... */
+FILE *OutFile = NULL, *InFile = NULL;
+int OutPipe = 0, InPipe = 0;
+int Quitting = 0;
+
+/* globals needed to handle loading of databases */
+static int cur_cachenum = -1;
+
+/* globals needed by the database cache */
+static DatabaseInfo dbcache[MAXNUMDATABASEINFO];
+static int cache_nextaccessnum = 0;
+static int cache_numloaded = 0;
+
+
+
+#if defined(PARADOCNUM) ||  defined(NZDL)
+static int GetDocNumFromParaNum(query_data *qd, int paranum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+  int low = 1, high = Documents;
+  int mid = (low+high)/2;
+
+  while ((mid = (low+high)/2) >=1 && mid <= Documents)
+    {
+      if (paranum > Paragraph[mid])
+        low = mid+1;
+      else if (paranum <= Paragraph[mid-1])
+        high = mid-1;
+      else
+        return mid;
+    }
+  FatalError(1, "Bad paragraph number.\n");
+  return 0;
+}
+
+static int GetParaNumFromDocNum(query_data *qd, int docnum) {
+  int Documents = qd->td->cth.num_of_docs;
+  int *Paragraph = qd->paragraph;
+
+  if (docnum > 0 && docnum <= Documents)
+    return Paragraph[docnum-1]+1;
+  return 0;
+}
+#endif
+
+
+/*****************************************************************************/
+
+static void MGQError(char *emsg)
+{
+  fprintf(stderr,"Fatal error: %s\n", emsg);
+  exit(1);
+}
+
+static int ProcessDocs (query_data * qd, int skip, int howmany, 
+			enum result_kinds kind, 
+			int (*sender)(char *,int,int,float,void *), void *ptr) {
+  int max_buf = 0, output_failure = 0;
+  int DocCount = 0;
+  int need_text = (kind == result_docs);
+
+  /* skip the requested number of documents */
+  while (skip > 0) {
+    if (!NextDoc(qd)) return 0;
+    skip--;
+  }
+
+  /* find out the maximum size for the text buffer */
+  if (need_text) max_buf = atoi (GetDefEnv ("buffer", "1048576"));
+
+  /* process each document */
+  do {
+    u_char *UDoc = NULL;
+    unsigned long ULen=0;
+
+#if defined(PARADOCNUM) ||  defined(NZDL)
+    /* adjust the document number for paragraph level result_docs */
+    /* this is a bit of a hack ... */
+    if (kind==result_docs && qd->id->ifh.InvfLevel == 3 &&
+	qd->DL != NULL && (int)qd->doc_pos < (int)qd->DL->num)
+      qd->DL->DE[qd->doc_pos].DocNum = GetParaNumFromDocNum(qd, qd->DL->DE[qd->doc_pos].DocNum);
+#endif
+    
+    if (need_text) {
+      /* load the compressed text */
+      if (LoadCompressedText (qd, max_buf))
+	MGQError("Unable to load compressed text(memory?).");
+
+      /* uncompress the loaded text */
+      UDoc = GetDocText (qd, &ULen);
+      if (UDoc == NULL) MGQError("UDoc is unexpectedly NULL");
+    }
+      
+    if (UDoc != NULL  ||  kind == result_docnums) {
+      int docnum = GetDocNum(qd);
+#if defined(PARADOCNUM) ||  defined(NZDL)
+      if (qd->id->ifh.InvfLevel == 3) docnum = GetDocNumFromParaNum(qd, docnum);
+#endif
+      switch (kind) {
+      case result_docnums:
+	if (sender != NULL)
+	  output_failure = (*sender)("",0,docnum,GetDocWeight(qd),ptr);
+	break;
+      case result_docs:
+	if (sender != NULL)
+	  output_failure = (*sender)((char *)UDoc,ULen,docnum,GetDocWeight(qd),ptr);
+	break;
+      default:
+	break;
+      }
+    }
+    DocCount++;
+      
+  } while (NextDoc (qd) && output_failure == 0 && --howmany > 0);
+  
+  /*  if (need_text) FreeTextBuffer (qd);*/
+  
+  return (DocCount);
+}
+
+
+static void send_query_term_freqs(QueryTermList *qtl,
+				  int (*sender)(char *,int,int,float,void *), void *ptr)
+{
+  int i = 0;
+  for (i = 0; i < qtl->num; i++)
+    if (sender != NULL) {
+      /*      word = word2str(qtl->QTE[i].Term); 
+	      (* sender)(word, strlen(word), qtl->QTE[i].Count, (float)0.0, ptr); */
+      (* sender)((char *)(qtl->QTE[i].Term+1), qtl->QTE[i].Term[0], 
+		 qtl->QTE[i].Count, (float)0.0, ptr);
+    }
+}
+
+
+static void send_terms (TermList * qtl, 
+			int (*sender)(char *,int,int,float,void *), void *ptr)
+{
+  int i = 0;
+  if (sender == NULL) return;
+  for (i = 0; i < qtl->num; i++)
+    {
+      /*      word = word2str(qtl->TE[i].Word);
+	      (* sender)(word, strlen(word), qtl->TE[i].Count, (float)0.0, ptr);*/
+      (* sender)((char *)(qtl->TE[i].Word+1), qtl->TE[i].Word[0], 
+		 qtl->TE[i].Count, (float)0.0, ptr);
+    }
+}
+
+
+/* MoreDocs () */
+/* Displays all documents in list  DocList. */
+/* Documents are fetched, then decompressed and displayed according to the */
+/* format implied in  FormString(). */
+
+static void
+MoreDocs (query_data * qd, enum result_kinds kind, 
+	  int skip, int howmany,
+	  int (*sender)(char *,int,int,float,void *), void *ptr)
+{
+  qd->num_of_ans = qd->DL->num;
+  switch (kind) {
+  case result_docs:
+  case result_docnums:
+    if (qd->num_of_ans > 0)
+      ProcessDocs (qd, skip, howmany, kind, sender, ptr);
+    break;
+  case result_termfreqs:
+    send_query_term_freqs(qd->QTL, sender, ptr);
+    break;
+  case result_terms:
+    send_terms(qd->TL, sender, ptr);
+    break;
+  }
+}
+
+
+
+
+
+
+/******************************************
+ * functions to handle the database cache *
+ ******************************************/
+
+/* init_dbcache should be called at the start of each */
+/* function which deals with the database cache */
+static void init_dbcache (void) {
+  static int dbcacheinited = 0;
+  int i = 0;
+
+  if (dbcacheinited) return;
+
+  cache_numloaded = 0;
+
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    dbcache[i].accessnum = -1;
+    dbcache[i].collection[0] = '\0';
+    dbcache[i].mgdir[0] = '\0';
+    dbcache[i].gensuffix[0] = '\0';
+    dbcache[i].textsuffix[0] = '\0';
+    dbcache[i].qd = NULL;
+  }
+
+  dbcacheinited = 1;
+}
+
+/* returns the next cache access number and increments it */
+static int get_next_accessnum (void) {
+  return cache_nextaccessnum++;
+}
+
+/* get_free_dbcache returns the cache number which */
+/* was used the longest time ago */
+/* init_dbcache should be called before this function */
+static int get_free_dbcache (void) {
+  int i = 0;
+  int minaccessnum = cache_nextaccessnum; /* the current max */
+  int minpos = 0;
+
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    if (dbcache[i].accessnum < minaccessnum) {
+      minaccessnum = dbcache[i].accessnum;
+      minpos = i;
+    }
+  }
+  
+  return minpos;
+}
+
+/* search_collect will search for an index which */
+/* belongs to a certain collection It returns -1 if none could be found. */
+/* init_dbcache should be called before this function */
+static int search_collect (char *collection) {
+  int i = 0;
+
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    if ((dbcache[i].accessnum >= 0) &&
+	(dbcache[i].qd != NULL) &&
+	(strcmp (collection, dbcache[i].collection) == 0)
+	/* && (dbcache[i].qd->id->ifh.InvfLevel == 2)*/
+	) {
+      dbcache[i].accessnum = get_next_accessnum ();
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+/* search_gensuffix will search for an index which */
+/* has a certain gensuffix. It returns -1 if none could be found. */
+/* init_dbcache should be called before this function */
+static int search_gensuffix (char *gensuffix) {
+  int i = 0;
+
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    if ((dbcache[i].accessnum >= 0) &&
+	(dbcache[i].qd != NULL) &&
+	(strcmp (gensuffix, dbcache[i].gensuffix) == 0)) {
+      dbcache[i].accessnum = get_next_accessnum ();
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+/* unload_database will unload a certain entry within */
+/* the database cache, clearing it for furture use. */
+static void unload_database (int i) {
+  /* check to see if it contains anything */
+  if (dbcache[i].accessnum < 0 || dbcache[i].qd == NULL) 
+    return;
+
+  /* unload all the query information  */
+  FinishQuerySystem(dbcache[i].qd);
+  
+  /* reset all the db info */
+  dbcache[i].accessnum = -1;
+  dbcache[i].collection[0] = '\0';
+  dbcache[i].mgdir[0] = '\0';
+  dbcache[i].gensuffix[0] = '\0';
+  dbcache[i].textsuffix[0] = '\0';
+  dbcache[i].qd = NULL;
+
+  cache_numloaded--;
+  if (cache_numloaded < 0) cache_numloaded = 0;
+}
+
+/* cache_database will store the information about */
+/* a database in the database cache. */
+static void cache_database (int i, char *collection, char *mgdir, char *gensuffix,
+			    char *textsuffix, query_data *qd) {
+  /* make sure this entry has been unloaded first */
+  if (dbcache[i].accessnum >= 0 && dbcache[i].qd != NULL) 
+    unload_database (i);
+
+  /* store the db info */
+  dbcache[i].accessnum = get_next_accessnum ();
+  strcpy (dbcache[i].collection, collection);
+  strcpy (dbcache[i].mgdir, mgdir);
+  strcpy (dbcache[i].gensuffix, gensuffix);
+  strcpy (dbcache[i].textsuffix, textsuffix);
+  dbcache[i].qd = qd;
+
+  cache_numloaded++;
+}
+
+static void make_current (int i) {
+  /* see if it is the current index */
+  if (i == cur_cachenum) return;
+ 
+  /* unload the old index */
+  if (cur_cachenum >= 0) UninitEnv ();
+  cur_cachenum = -1;
+
+  /* make sure the new one is ok */
+  if (i < 0 || dbcache[i].accessnum < 0 || dbcache[i].qd == NULL)
+    return;
+
+  /* load the new one */
+
+  /* Initialise the environment with default values */
+  InitEnv ();
+  
+  SetEnv("mgdir",dbcache[i].mgdir,NULL);
+  SetEnv("mgname",dbcache[i].gensuffix,NULL);
+  SetEnv("textname",dbcache[i].textsuffix,NULL);
+  
+  PushEnv ();
+  
+  cur_cachenum = i;
+}
+
+
+
+/********************
+ * public functions *
+ ********************/
+
+int mgq_ask(char *line)
+{
+  query_data *qd = (query_data *)NULL;
+  char QueryType = QUERY_BOOLEAN;
+  char OutputType = QUERY_DOCNUMS;
+  char *LinePtr = (char *)NULL;
+
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL) return 0;
+  
+  ResetFileStats (qd);
+  qd->max_mem_in_use = qd->mem_in_use = 0;
+  qd->tot_hops_taken += qd->hops_taken;
+  qd->tot_num_of_ptrs += qd->num_of_ptrs;
+  qd->tot_num_of_accum += qd->num_of_accum;
+  qd->tot_num_of_terms += qd->num_of_terms;
+  qd->tot_num_of_ans += qd->num_of_ans;
+  qd->tot_text_idx_lookups += qd->text_idx_lookups;
+  qd->hops_taken = qd->num_of_ptrs = 0;
+  qd->num_of_accum = qd->num_of_ans = qd->num_of_terms = 0;
+  qd->text_idx_lookups = 0;
+  
+  LinePtr = ProcessCommands (line, qd);
+  if (CommandsErrorStr) {
+    fprintf (stderr, "%s\n", CommandsErrorStr);
+    return 0;
+  }
+  if (*LinePtr == '\0') return 1;
+  
+  FreeQueryDocs (qd);
+  
+  QueryType = get_query_type ();
+  OutputType = get_output_type ();
+  /* No point in hiliting words on a docnum query */
+  if (OutputType == OUTPUT_HILITE && QueryType == QUERY_DOCNUMS)
+    OutputType = OUTPUT_TEXT;
+  
+  switch (QueryType)
+    {
+    case QUERY_BOOLEAN:
+      {
+	char *maxdocs = (char *)NULL;
+	BooleanQueryInfo bqi;
+	maxdocs = GetDefEnv ("maxdocs", "all");
+	bqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
+        if (qd->sd->sdh.indexed)
+	  BooleanQuery (qd, line, &bqi, (BooleanEnv (GetEnv ("casefold"), 0) |
+					 (BooleanEnv (GetEnv ("stem"), 0) << 1)));
+	else
+	  BooleanQuery (qd, line, &bqi, qd->sd->sdh.stem_method);
+	/*	if (qd->sd->sdh.indexed) BooleanQuery (qd, line, &bqi, 3);
+	else BooleanQuery (qd, line, &bqi, qd->sd->sdh.stem_method); */
+	break;
+      }
+    case QUERY_APPROX:
+    case QUERY_RANKED:
+      {
+	char *maxdocs = (char *)NULL;
+	char *maxterms = (char *)NULL;
+	char *maxaccum = (char *)NULL;
+	RankedQueryInfo rqi;
+	maxdocs = GetDefEnv ("maxdocs", "all");
+	maxterms = GetDefEnv ("max_terms", "all");
+	maxaccum = GetDefEnv ("max_accumulators", "all");
+	rqi.Sort = BooleanEnv (GetEnv ("sorted_terms"), 0);
+	rqi.QueryFreqs = BooleanEnv (GetEnv ("qfreq"), 1);
+	rqi.Exact = QueryType == QUERY_RANKED;
+	rqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
+	rqi.MaxTerms = strcmp (maxterms, "all") ? atoi (maxterms) : -1;
+	rqi.MaxParasToRetrieve = rqi.MaxDocsToRetrieve;
+	if (qd->id->ifh.InvfLevel == 3 && GetEnv ("maxparas"))
+	  rqi.MaxParasToRetrieve = atoi (GetEnv ("maxparas"));
+	rqi.AccumMethod = toupper (*GetDefEnv ("accumulator_method", "A"));
+	rqi.MaxAccums = strcmp (maxaccum, "all") ? atoi (maxaccum) : -1;
+	rqi.HashTblSize = IntEnv (GetEnv ("hash_tbl_size"), 1000);
+	rqi.StopAtMaxAccum = BooleanEnv (GetEnv ("stop_at_max_accum"), 0);
+	rqi.skip_dump = GetEnv ("skip_dump");
+	RankedQuery (qd, line, &rqi);
+	break;
+      }
+    case QUERY_DOCNUMS:
+      {
+	DocnumsQuery (qd, line);
+	break;
+      }
+    }
+
+  return 1;
+}
+
+int mgq_numdocs(void)
+{
+  query_data *qd = NULL;
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL) return 0;
+
+  if (qd->DL) return qd->DL->num;
+  else return 0;
+}
+
+int mgq_numterms(void)
+{
+  query_data *qd = NULL;
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL) return 0;
+
+  if (qd->QTL) return qd->QTL->num;
+  else return 0;
+}
+
+int mgq_results(enum result_kinds kind,int skip,int howmany, int (*sender)(char *, int, int, float, void *), void *ptr)
+{
+  query_data *qd = NULL;
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL) return 0;
+
+  if (qd->DL) {
+    qd->doc_pos = 0;
+    MoreDocs(qd, kind, skip, howmany, sender, ptr);
+  }
+  return 0;
+}
+
+/* get all the terms that match wordstem using the current stemmer and */
+/* stemming method. The callback is the same style used by mgq_results */
+int mgq_equivterms (unsigned char *wordstem, int (*sender)(char *, int, int, float, void *),
+		    void *ptr) {
+  int stem_method = 0;
+  query_data *qd = NULL;
+  TermList *equivterms = NULL;  /* used for equivalent terms */
+
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL || wordstem == NULL || sender == NULL) return 0;
+
+  if (qd->sd->sdh.indexed) {
+    stem_method = BooleanEnv(GetEnv("casefold"),0) | (BooleanEnv(GetEnv("stem"),0) << 1);
+  } else {
+    stem_method = qd->sd->sdh.stem_method;
+  }
+
+  /* make the term list */
+  equivterms = MakeTermList (0);
+  
+  /* expand out this word */
+  if (FindWords (qd->sd, wordstem, stem_method, &equivterms) > 0) {
+    int i;
+    for (i=0; i < equivterms->num; i++) {
+      (* sender)((char *)(equivterms->TE[i].Word+1), equivterms->TE[i].Word[0], 
+		 equivterms->TE[i].Count, (float)0.0, ptr);
+    }
+  }
+
+  /* free the term list */
+  if (equivterms != NULL) FreeTermList (&equivterms);
+
+  return 0;
+}
+
+/* gets the total number of documents retrieved. If this is not available */
+/* it will set total_retrieved to 0 (even when it obviously isn't) */
+int mgq_docsretrieved (int *total_retrieved, int *is_approx) {
+  query_data *qd = NULL;
+
+  if (cur_cachenum == -1) return 0;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL || total_retrieved == NULL || is_approx == NULL) return 0;
+
+  /* set default values */
+  *total_retrieved = 0;
+  *is_approx = 0;
+
+  if (qd->DL == NULL) return 0;
+
+  *total_retrieved = qd->DL->total_retrieved;
+  *is_approx = qd->DL->is_approx;
+
+  return 0;
+}
+
+
+/* use mgq_getmaxstemlen to determine the length of the word stems to pass */
+/* to mgq_stemword */
+int mgq_getmaxstemlen () {
+  return MAXSTEMLEN;
+}
+
+/* note: the stemming method and the stemmer come from the last query */
+/* "word" should be at least maxstemlen+1 long and it is a string that */
+/* starts with the string length */
+void mgq_stemword (unsigned char *word) {
+  int stem_method = 0;
+  query_data *qd = NULL;
+
+  if (cur_cachenum == -1) return;
+  qd = dbcache[cur_cachenum].qd;
+  if (qd == NULL || word == NULL) return;
+
+  if (qd->sd->sdh.indexed) {
+    stem_method = BooleanEnv(GetEnv("casefold"),0) | (BooleanEnv(GetEnv("stem"),0) << 1);
+  } else {
+    stem_method = qd->sd->sdh.stem_method;
+  }
+
+  stemmer (stem_method, qd->sd->sdh.stemmer_num, word);
+}
+
+
+
+int is_dbcache_full (void) {
+  init_dbcache ();
+  if (cache_numloaded >= MAXNUMDATABASEINFO) return 1;
+  return 0;
+}
+
+int load_database (char *collection, char *mgdir, 
+		   char *gensuffix, char *textsuffix) {
+  int i = 0;
+  query_data *qd = NULL;
+  /*  FILE *deb = NULL; */
+  init_dbcache ();
+
+  /* print out some debug information */
+/*  deb = fopen ("/home/rjmcnab/gsdl/etc/deb.txt", "a");
+  fprintf (deb, "\ncache_nextaccessnum: %i\n", cache_nextaccessnum);
+  fprintf (deb, "cache_numloaded: %i\n", cache_numloaded);
+  fprintf (deb, "cur_cachenum: %i\n", cur_cachenum);
+  fprintf (deb, "MAXNUMDATABASEINFO: %i\n\n", MAXNUMDATABASEINFO);
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    fprintf (deb, "Entry %i\n", i);
+    fprintf (deb, "  accessnum: %i\n", dbcache[i].accessnum);
+    fprintf (deb, "  collection: %s\n", dbcache[i].collection);
+    fprintf (deb, "  mgdir: %s\n", dbcache[i].mgdir);
+    fprintf (deb, "  gensuffix: %s\n", dbcache[i].gensuffix);
+    fprintf (deb, "  textsuffix: %s\n", dbcache[i].textsuffix);
+    fprintf (deb, "  qd: %x\n", (int)(dbcache[i].qd));
+  }
+  fclose (deb); */
+
+  /* search for the index */
+  i = search_gensuffix (gensuffix);
+  if (i >= 0) {
+    make_current (i);
+    return 1;
+  }
+
+  /* if there was a current database then the */
+  /* environment needs uninitialising */
+  make_current (-1);
+
+  /* get a free cache number */
+  i = get_free_dbcache ();
+  unload_database (i);
+
+  /* load the index */
+  qd = InitQuerySystem (mgdir, gensuffix, textsuffix, NULL);
+  if (qd == NULL) return 0;
+
+  /* cache this index */
+  cache_database (i, collection, mgdir, gensuffix, textsuffix, qd);
+
+  /* make this index current */
+  make_current (i);
+
+  return 1;
+}
+
+/* load_text_database tries to make an index of the */
+/* specified collection current */
+int load_text_database (char *collection) {
+  int i = 0;
+  init_dbcache ();
+
+  /* search for the index */
+  i = search_collect (collection);
+
+  /* return if none were found */
+  if (i < 0) return 0;
+  
+  /* make this index current */
+  make_current (i);
+  return 1;
+}
+
+void close_all_databases (void) {
+  int i = 0;
+  init_dbcache ();
+  
+  /* unload all active databases */
+  for (i=0; i<MAXNUMDATABASEINFO; i++) {
+    unload_database (i);
+  }
+
+  /* if there was a current database then the */
+  /* environment needs uninitialising */
+  make_current (-1);
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/mgq.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgq.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgq.h	(revision 16561)
@@ -0,0 +1,78 @@
+/**********************************************************************
+ *
+ * mgq.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.
+ *
+ *********************************************************************/
+
+
+#ifndef MGQ_H
+#define MGQ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  
+enum result_kinds {
+  result_docs,      /* Return the documents found in last search */
+  result_docnums,   /* Return document id numbers and weights */
+  result_termfreqs, /* Return terms and frequencies */
+  result_terms      /* Return matching query terms */
+};
+
+int mgq_ask(char *line);
+int mgq_results(enum result_kinds kind, int skip, int howmany,
+		int (*sender)(char *, int, int, float, void *), void *ptr);
+int mgq_numdocs(void);
+int mgq_numterms(void);
+
+/* get all the terms that match wordstem using the current stemmer and */
+/* stemming method. The callback is the same style used by mgq_results */
+int mgq_equivterms (unsigned char *wordstem, int (*sender)(char *, int, int, float, void *),
+		    void *ptr);
+
+/* gets the total number of documents retrieved. If this is not available */
+/* it will set total_retrieved to 0 (even when it obviously isn't) */
+int mgq_docsretrieved (int *total_retrieved, int *is_approx);
+  
+/* use mgq_getmaxstemlen to determine the length of the word stems to pass */
+/* to mgq_stemword */
+int mgq_getmaxstemlen ();
+
+/* note: the stemming method and the stemmer come from the last query */
+/* "word" should be at least maxstemlen+1 long and it is a string that */
+/* starts with the string length */
+void mgq_stemword (unsigned char *word);
+
+  
+int is_dbcache_full (void);
+int load_database (char *collection, char *mgdir, char *gensuffix, char *textsuffix);
+int load_text_database (char *collection);
+void close_all_databases (void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.cpp	(revision 16561)
@@ -0,0 +1,533 @@
+/**********************************************************************
+ *
+ * mgqueryfilter.cpp -- implementation of queryfilter for old mg 
+ * 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.
+ *
+ *********************************************************************/
+
+#include "mgqueryfilter.h"
+#include "fileutil.h"
+#include "phrasesearch.h"
+#include "mgsearch.h"
+#include "phrases.h"
+
+///////////////////////////////
+// methods for resultsorderer_t
+///////////////////////////////
+
+resultsorderer_t::resultsorderer_t() {
+  clear ();
+}
+
+void resultsorderer_t::clear() {
+  compare_phrase_match = false;
+  compare_terms_match = false;
+  compare_doc_weight = true;
+
+  docset = NULL;
+}
+
+bool resultsorderer_t::operator()(const text_t &t1, const text_t &t2) const {
+  if (docset == NULL) return t1>t2;
+
+  docresultmap::iterator t1_here = docset->find(t1);
+  docresultmap::iterator t2_here = docset->find(t2);
+  docresultmap::iterator end = docset->end();
+
+  // sort all the document numbers not in the document set to
+  // the end of the list
+  if (t1_here == end) {
+    if (t2_here == end) return t1>t2;
+    else return true;
+  } else if (t2_here == end) return false;
+  
+  if (compare_phrase_match) {
+    if ((*t1_here).second.num_phrase_match > (*t2_here).second.num_phrase_match) return true;
+    if ((*t1_here).second.num_phrase_match < (*t2_here).second.num_phrase_match) return false;
+  }
+
+  if (compare_terms_match) {
+    if ((*t1_here).second.num_query_terms_matched > (*t2_here).second.num_query_terms_matched) return true;
+    if ((*t1_here).second.num_query_terms_matched < (*t2_here).second.num_query_terms_matched) return false;
+  }
+
+  if (compare_doc_weight) {
+    if ((*t1_here).second.docweight > (*t2_here).second.docweight) return true;
+    if ((*t1_here).second.docweight < (*t2_here).second.docweight) return false;
+  }
+
+  return t1>t2;
+}
+
+
+
+
+/////////////////////////////////
+// functions for mgqueryfilterclass
+/////////////////////////////////
+
+
+void mgqueryfilterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  queryfilterclass::configure (key, cfgline);
+
+  if (key == "indexstem") {
+    ((mgsearchclass *)textsearchptr)->set_indexstem (cfgline[0]);
+  }
+  
+}
+
+// loads up phrases data structure with any phrases (that's the quoted bits)
+// occuring in the querystring
+void mgqueryfilterclass::get_phrase_terms (const text_t &querystring, 
+					 const termfreqclassarray &orgterms,
+					 vector<termfreqclassarray> &phrases) {
+
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+
+  termfreqclassarray tmpterms;
+
+  int termcount = 0;
+  bool foundquote = false;
+  bool foundbreak = false;
+  bool start = true;
+  while (here != end) {
+    if (*here == '\"') {
+      if (foundquote) {
+	if (!foundbreak && !start) {
+	  tmpterms.push_back (orgterms[termcount]);
+	  ++termcount;
+	}
+	if (tmpterms.size() > 1) {
+	  phrases.push_back (tmpterms);
+	}
+	tmpterms.erase (tmpterms.begin(), tmpterms.end());
+	
+	foundquote = false;
+	foundbreak = true;
+      } else foundquote = true;
+    } else if (!is_unicode_letdig(*here)) {
+      // found a break between terms
+      if (!foundbreak && !start) {
+	if (foundquote) {
+	  tmpterms.push_back (orgterms[termcount]);
+	}
+	++termcount;
+      }
+      foundbreak = true;
+    } else {
+      start = false;
+      foundbreak = false;
+    }      
+    ++here;
+  }
+}
+
+// do aditional query processing
+void mgqueryfilterclass::post_process (const queryparamclass &queryparams, 
+				     queryresultsclass &queryresults) {
+
+  // post-process the results if needed
+  if (queryresults.orgterms.size() > 1 && !queryresults.docs.docset.empty()) {
+
+    // get the terms between quotes (if any)
+    vector<termfreqclassarray> phrases;
+    get_phrase_terms (queryparams.querystring, queryresults.orgterms, phrases);
+
+    num_phrases = phrases.size();
+    if (num_phrases > 0) {
+
+      // get the long version of the index
+      text_t longindex;
+      indexmap.to2from (queryparams.index, longindex);
+      
+      vector<termfreqclassarray>::const_iterator this_phrase = phrases.begin();
+      vector<termfreqclassarray>::const_iterator end_phrase = phrases.end();
+
+      while (this_phrase != end_phrase) {
+
+	// process each of the matched documents
+	docresultmap::iterator docs_here = queryresults.docs.docset.begin();
+	docresultmap::iterator docs_end = queryresults.docs.docset.end();
+	while (docs_here != docs_end) {
+	  if (OID_phrase_search (*((mgsearchclass*)textsearchptr), *db_ptr, queryparams.index,
+				 queryparams.subcollection, queryparams.language, 
+				 longindex, queryparams.collection, *this_phrase,
+				 (*docs_here).second.docnum)) {
+	    ++docs_here->second.num_phrase_match;
+	  }
+	
+	  ++docs_here;
+	}
+	++this_phrase;
+      }
+    }
+  }
+}
+
+
+// do query that might involve multiple sub queries
+// textsearchptr and db_ptr are assumed to be valid
+void mgqueryfilterclass::do_multi_query (const FilterRequest_t &request,
+				       const vector<queryparamclass> &query_params,
+				       queryresultsclass &multiresults,
+				       comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  err = noError;
+  textsearchptr->setcollectdir (collectdir);
+
+  multiresults.clear();
+  
+  vector<queryparamclass>::const_iterator query_here = query_params.begin();
+  vector<queryparamclass>::const_iterator query_end = query_params.end();
+  while (query_here != query_end) {
+    queryresultsclass thisqueryresults;
+
+    if (!textsearchptr->search(*query_here, thisqueryresults)) {
+      // most likely a system problem
+      logout << text_t2ascii
+	     << "system problem: could not do search with mg for index \""
+	     << (*query_here).index << (*query_here).subcollection
+	     << (*query_here).language << "\".\n\n";
+      err = systemProblem;
+      return;
+    }
+
+    // combine the results
+    if (need_matching_docs (request.filterResultOptions)) {
+      // post-process the results if needed
+      if (!thisqueryresults.postprocessed && thisqueryresults.orgterms.size() > 1 &&
+	  !thisqueryresults.docs.docset.empty()) {
+	post_process (*query_here, thisqueryresults);
+	thisqueryresults.postprocessed = true;
+	multiresults.postprocessed = true;
+      } else {
+	num_phrases = 0;
+      }
+      
+      if (query_params.size() == 1) {
+	multiresults.docs = thisqueryresults.docs; // just one set of results
+	multiresults.docs_matched = thisqueryresults.docs_matched;
+	multiresults.is_approx = thisqueryresults.is_approx;
+	
+      } else {
+	if ((*query_here).combinequery == "and") {
+	  multiresults.docs.combine_and (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "or") {
+	  multiresults.docs.combine_or (thisqueryresults.docs);
+	} else if ((*query_here).combinequery == "not") {
+	  multiresults.docs.combine_not (thisqueryresults.docs);
+	}
+	multiresults.docs_matched = multiresults.docs.docset.size();
+	multiresults.is_approx = Exact;
+      }
+    }
+
+    // combine the term information 
+    if (need_term_info (request.filterResultOptions)) {
+      // append the terms
+      multiresults.orgterms.insert(multiresults.orgterms.end(),
+				   thisqueryresults.orgterms.begin(),
+				   thisqueryresults.orgterms.end());
+
+      // add the term variants
+      text_tset::iterator termvar_here = thisqueryresults.termvariants.begin();
+      text_tset::iterator termvar_end = thisqueryresults.termvariants.end();
+      while (termvar_here != termvar_end) {
+	multiresults.termvariants.insert(*termvar_here);
+	++termvar_here;
+      }
+    }
+   
+    ++query_here;
+  }
+
+  // sort and unique the query terms
+  multiresults.sortuniqqueryterms ();
+}
+
+
+void mgqueryfilterclass::sort_doc_results (const FilterRequest_t &/*request*/,
+					 docresultsclass &docs) {
+  resultsorderer_t resultsorderer;
+  resultsorderer.compare_phrase_match = true;
+  resultsorderer.docset = &(docs.docset);
+
+  // first get a list of document numbers
+  docs.docnum_order();
+
+  sort (docs.docorder.begin(), docs.docorder.end(), resultsorderer);
+}
+
+
+
+mgqueryfilterclass::mgqueryfilterclass () 
+  :queryfilterclass() {
+
+  num_phrases = 0;
+}
+
+mgqueryfilterclass::~mgqueryfilterclass () {
+}
+
+void mgqueryfilterclass::filter (const FilterRequest_t &request,
+			       FilterResponse_t &response,
+			       comerror_t &err, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  response.clear ();
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: mgqueryfilter contains a null dbclass\n\n";
+    err = configurationError;
+    return;
+  }
+  if (textsearchptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: mgqueryfilter contains a null textsearchclass (mg)\n\n";
+    err = configurationError;
+    return;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return;
+  }
+
+  // get the query parameters
+  int startresults = filterOptions["StartResults"].defaultValue.getint();
+  int endresults = filterOptions["EndResults"].defaultValue.getint();
+  text_t phrasematch = filterOptions["PhraseMatch"].defaultValue;
+
+  vector<queryparamclass> queryfilterparams;
+  parse_query_params (request, queryfilterparams, startresults, 
+		      endresults, phrasematch, logout);
+  // do any mg specific diddling with query parameters that may be required
+  mg_parse_query_params (request, queryfilterparams, startresults, 
+			 endresults, phrasematch, logout);
+
+
+  // do query
+  queryresultsclass queryresults;
+  do_multi_query (request, queryfilterparams, queryresults, err, logout);
+  if (err != noError) return;
+  
+  // assemble document results
+  if (need_matching_docs (request.filterResultOptions)) {
+    // sort the query results
+    // only want to sort the docs if we have done a ranked search or there were phrases
+    if (num_phrases > 0 || (request.filterResultOptions & FRranking)) {
+      sort_doc_results (request, queryresults.docs);
+    }
+    int resultnum = 1;
+    ResultDocInfo_t resultdoc;
+    text_t trans_OID;
+    vector<text_t>::iterator docorder_here = queryresults.docs.docorder.begin();
+    vector<text_t>::iterator docorder_end = queryresults.docs.docorder.end();
+
+    // documents containing matching phrases will be sorted to the top so
+    // we can break out once we're past those that match the PhraseMatch
+    // option -- "all_phrases" = return only those documents containing all
+    //	                     phrases in query string
+    //           "some_phrases" = return only those documents containing 
+    //                            at least 1 of the phrases in the document
+    //           "all_docs" = return all documents regardless
+    if (num_phrases > 0) {
+      int numdocs = 0;
+      while (docorder_here != docorder_end) {
+	docresultmap::iterator docset_here = queryresults.docs.docset.find (*docorder_here);
+	
+	if (((phrasematch == "all_phrases") && ((*docset_here).second.num_phrase_match < num_phrases)) ||
+	    ((phrasematch == "some_phrases") && ((*docset_here).second.num_phrase_match < 1))) {
+	  queryresults.docs_matched = numdocs;
+	  break;
+	}
+	++numdocs;
+	++docorder_here;
+      }
+    }
+
+    if (endresults == -1) endresults = MAXNUMDOCS;
+    docorder_here = queryresults.docs.docorder.begin();
+    while (docorder_here != docorder_end) {
+      if (resultnum > endresults || resultnum > queryresults.docs_matched) break;
+      
+      // translate the document number
+      if (!translate(db_ptr, *docorder_here, trans_OID)) {
+	logout << text_t2ascii
+	       << "warning: could not translate mg document number \""
+	       << *docorder_here << "\"to OID.\n\n";
+	
+      } else {
+	docresultmap::iterator docset_here = queryresults.docs.docset.find (*docorder_here);
+
+	// see if there is a result for this number,
+	// if it is in the request set (or the request set is empty)
+	if (docset_here != queryresults.docs.docset.end() &&
+	    (request.docSet.empty() || in_set(request.docSet, trans_OID))) {
+	  if (resultnum >= startresults) {
+	    // add this document
+	    resultdoc.OID = trans_OID;
+	    resultdoc.result_num = resultnum;
+	    resultdoc.ranking = (int)((*docset_here).second.docweight * 10000.0 + 0.5);
+
+	    // these next two are not available on all versions of mg
+	    resultdoc.num_terms_matched = (*docset_here).second.num_query_terms_matched; 
+	    resultdoc.num_phrase_match = (*docset_here).second.num_phrase_match;
+	    
+	    response.docInfo.push_back (resultdoc);
+	  }
+	  
+	  ++resultnum;
+	}
+      }
+      
+      ++docorder_here;
+    }
+  }
+
+  // assemble the term results
+  if (need_term_info(request.filterResultOptions)) {
+    // note: the terms have already been sorted and uniqued
+
+    TermInfo_t terminfo;
+    bool terms_first = true;
+    termfreqclassarray::iterator terms_here = queryresults.terms.begin();
+    termfreqclassarray::iterator terms_end = queryresults.terms.end();
+
+    while (terms_here != terms_end) {
+      terminfo.clear();
+      terminfo.term = (*terms_here).termstr;
+      terminfo.freq = (*terms_here).termfreq;
+      if (terms_first) {
+	text_tset::iterator termvariants_here = queryresults.termvariants.begin();
+	text_tset::iterator termvariants_end = queryresults.termvariants.end();
+	while (termvariants_here != termvariants_end) {
+	  terminfo.matchTerms.push_back (*termvariants_here);
+	  ++termvariants_here;
+	}
+      }
+      terms_first = false;
+
+      response.termInfo.push_back (terminfo);
+
+      ++terms_here;
+    }
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  response.numDocs = queryresults.docs_matched;
+  response.isApprox = queryresults.is_approx;
+}
+
+void mgqueryfilterclass::mg_parse_query_params (const FilterRequest_t &/*request*/,
+						vector<queryparamclass> &query_params,
+						int &/*startresults*/, int &/*endresults*/,
+						text_t &/*phrasematch*/, ostream &/*logout*/) {
+
+  //  outconvertclass text_t2ascii;
+  
+  vector<queryparamclass>::iterator query_here = query_params.begin();
+  vector<queryparamclass>::iterator query_end = query_params.end();
+  while (query_here != query_end) {
+
+    // if we're doing a phrase search we want to maximise hits by making it
+    // a boolean search on the index with the finest granularity - we'll
+    // also set maxdocs to "all" (realizing that this will cause searches
+    // like "and the" on a large collection to take a very very long time).
+
+    // we're deciding it's a phrase search based on if the querystring 
+    // contains at least 2 double quotes (not very scientific but 
+    // then neither is the rest of the mg phrase searching functionality :-)
+      //if (countchar ((*query_here).querystring.begin(), (*query_here).querystring.end(), '"') > 1) {
+
+    // [kjdon 12/2005] we don't want to do a phrase search if the only phrases are single words, so we'll parse out the phrases properly here
+    text_tarray phrases;
+    get_phrases((*query_here).querystring, phrases);
+
+    if (phrases.size() > 0) {
+      (*query_here).search_type = 0;
+
+      // set maxdocs to "all"
+      (*query_here).maxdocs = -1;
+
+      // Get the long version of the index and test to see if any indexes with
+      // finer granularity exist. Indexes must be the same type (i.e. same metadata
+      // or "text").
+      text_t longindex; text_tarray splitindex;
+      indexmap.to2from ((*query_here).index, longindex);
+      splitchar (longindex.begin(), longindex.end(), ':', splitindex);
+      text_t &granularity = splitindex[0];
+      text_t &indextype = splitindex[1];
+      bool found = false;
+      // currently supported granularity options are "document", "section" and "paragraph"
+      if (granularity == "document" || granularity == "section") {
+	text_t shortindex;
+	if (indexmap.fromexists ("paragraph:" + indextype)) {
+	  indexmap.from2to ("paragraph:" + indextype, shortindex);
+	  (*query_here).index = shortindex;
+	  found = true;
+	}
+	if (!found && granularity == "document" && indexmap.fromexists ("section:" + indextype)) {
+	  indexmap.from2to ("section:" + indextype, shortindex);
+	  (*query_here).index = shortindex;
+	}
+      }
+    }
+
+#ifdef GSDL_BBC_COLLECTION
+    // This is a special hack for the BBC collection's ProgNumber and zzabn
+    // indexes (they're built this way to prevent mg_perf_hash_build from
+    // dying at build time)
+
+    // if we're searching the ProgNumber index we want to
+    // remove all non-alphanumeric characters from the query string
+    text_t longindex; text_tarray splitindex;
+    indexmap.to2from ((*query_here).index, longindex);
+    splitchar (longindex.begin(), longindex.end(), ':', splitindex);
+    text_t &indextype = splitindex[1];
+    if (indextype == "ProgNumber") {
+      text_t new_querystring;
+      text_t::const_iterator here = (*query_here).querystring.begin();
+      text_t::const_iterator end = (*query_here).querystring.end();
+      while (here != end) {
+        if ((*here >= 'a' && *here <= 'z') || (*here >= 'A' && *here <= 'Z') ||
+            (*here >= '0' && *here <= '9')) {
+          new_querystring.push_back (*here);
+        }
+        ++here;
+      }
+      (*query_here).querystring = new_querystring;
+    }
+#endif
+    ++query_here;
+  }
+}
+    
Index: /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgqueryfilter.h	(revision 16561)
@@ -0,0 +1,89 @@
+/**********************************************************************
+ *
+ * mgqueryfilter.h -- queryfilter for old mg
+ * 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 MGQUERYFILTER_H
+#define MGQUERYFILTER_H
+
+#include "queryfilter.h"
+
+
+// resultsorderer_t is used to sort the query results
+struct resultsorderer_t {
+  bool compare_phrase_match;
+  bool compare_terms_match;
+  bool compare_doc_weight;
+  docresultmap *docset;
+
+  resultsorderer_t();
+  void clear();
+  bool operator()(const text_t &t1, const text_t &t2) const;
+};
+
+
+
+class mgqueryfilterclass : public queryfilterclass {
+protected:
+  
+  int num_phrases;
+
+  void get_phrase_terms (const text_t &querystring, 
+			 const termfreqclassarray &orgterms,
+			 vector<termfreqclassarray> &phrases);
+
+  // do aditional query processing
+  virtual void post_process (const queryparamclass &queryparams,
+			     queryresultsclass &queryresults);
+
+  // do query that might involve multiple sub queries
+  // mgsearchptr and db_ptr are assumed to be valid
+  void do_multi_query (const FilterRequest_t &request,
+		       const vector<queryparamclass> &query_params,
+		       queryresultsclass &multiresults,
+		       comerror_t &err, ostream &logout);
+
+  virtual void sort_doc_results (const FilterRequest_t &request,
+				 docresultsclass &docs);
+
+  virtual void mg_parse_query_params (const FilterRequest_t &request,
+				      vector<queryparamclass> &query_params,
+				      int &startresults, int &endresults,
+				      text_t &phrasematch, ostream &logout);
+
+  
+public:
+  mgqueryfilterclass ();
+  virtual ~mgqueryfilterclass ();
+
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  void filter (const FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/mgsearch.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgsearch.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgsearch.cpp	(revision 16561)
@@ -0,0 +1,569 @@
+/**********************************************************************
+ *
+ * mgsearch.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.
+ *
+ *********************************************************************/
+
+#include "gsdlconf.h"
+#include "mgsearch.h"
+#include "fileutil.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+  
+#include <assert.h>
+
+#include "mgq.h"
+// #include "locateinfo.h"
+#include "gsdlunicode.h"
+#include "unitool.h"
+
+
+/////////////
+// globals //
+/////////////
+
+static char *tempdoc = NULL;
+static int templen = 0;
+
+
+//////////////////////
+// useful functions //
+//////////////////////
+
+
+// input and output are in utf8
+text_t mgsearch_stemword (const text_t &word) {
+  // allocate working stem space
+  int maxstemlen = mgq_getmaxstemlen ();
+  unsigned char *word_stem = new unsigned char [maxstemlen + 2];
+  if (word_stem == NULL) return "";
+
+  // copy word to word_stem
+  int len = 0;
+  text_t::const_iterator here = word.begin();
+  text_t::const_iterator end = word.end();
+  while (len < maxstemlen && here != end) {
+    word_stem[len+1] = (unsigned char)(*here);
+    ++len; ++here;
+  }
+  word_stem[len+1] = '\0';
+  word_stem[0] = len;
+
+  mgq_stemword (word_stem);
+
+  // copy word_stem back to tempstr
+  text_t tempstr;
+  tempstr.setcarr((char *)(&word_stem[1]), word_stem[0]);
+
+  delete [] word_stem;
+  
+  return tempstr;
+}
+
+
+
+////////////////////////
+// callback functions //
+////////////////////////
+
+// This routine is called for each document found in a search
+// it assumes that cache_num is set up correctly to point to
+// a suitable result cache
+int ourquerycallback(char * /*UDoc*/, int /*ULen*/, int DocNum, 
+		     float Weight, void *info) {
+
+  
+  queryresultsclass *queryresults = (queryresultsclass * )info;
+
+  // append this entry to the document results
+  docresultclass docresult;
+  docresult.docnum = DocNum;
+  docresult.num_query_terms_matched = (int)(Weight/100.0); // will always be 0 on some versions of mg...
+  docresult.docweight = Weight - docresult.num_query_terms_matched*100;
+  
+  queryresults->docs.docset[DocNum] = docresult;
+  queryresults->docs.docorder.push_back(DocNum);
+  
+  return 0;
+}
+
+int termequivcallback(char *Word, int ULen,  int /*Freq*/, 
+		      float /*Weight*/,  void *info) {
+  text_tset *equivterms = (text_tset *)info;
+  if (equivterms == NULL) return 0;
+
+  text_t thisterm;
+  thisterm.setcarr(Word, ULen);
+
+  equivterms->insert(thisterm);
+  
+  return 0;
+}
+
+
+void mgsearch_equivterms (const text_t &word, text_tset &equivterms) {
+  // allocate working stem space
+  int maxstemlen = mgq_getmaxstemlen ();
+  unsigned char *word_stem = new unsigned char [maxstemlen + 2];
+  if (word_stem == NULL) return;
+
+  // copy word to word_stem
+  int len = 0;
+  text_t::const_iterator here = word.begin();
+  text_t::const_iterator end = word.end();
+  while (len < maxstemlen && here != end) {
+    word_stem[len+1] = (unsigned char)(*here);
+    ++len; ++here;
+  }
+  word_stem[len+1] = '\0';
+  word_stem[0] = len;
+
+  // get the equivalent terms
+  mgq_equivterms (word_stem, termequivcallback, (void *)(&equivterms));
+  
+  delete [] word_stem;
+
+  return;
+}
+
+  text_tset utf8equivterms; // kept as utf8 string for fast matching
+
+
+// This callback is called once for each term in the query
+int termfreqcallback(char *Word, int ULen,  int Freq, 
+		     float /*Weight*/,  void *info) {
+  queryresultsclass *queryresults = (queryresultsclass *)info;
+  if (queryresults == NULL) return 0;
+
+  text_t term;
+  term.setcarr(Word, ULen);
+  termfreqclass termfreq;
+
+  termfreq.termstr = to_uni(term);
+  text_t utf8termstem = mgsearch_stemword (term);
+  termfreq.termstemstr = to_uni (utf8termstem);
+
+  mgsearch_equivterms (utf8termstem, termfreq.utf8equivterms);
+  
+  termfreq.termfreq = Freq;
+  queryresults->orgterms.push_back(termfreq);
+  
+  return 0;
+}
+
+// this callback is called once for each variation of each term
+int termvariantscallback(char *Word, int ULen, int /*Freq*/,
+			 float /*Weight*/, void *info) {
+
+  text_t term;
+  term.setcarr(Word, ULen);
+  queryresultsclass *queryresults = (queryresultsclass *)info;
+  queryresults->termvariants.insert(to_uni(term));
+
+  return 0;
+}
+
+// This callback is for getting document text
+int doctextcallback(char *Doc, int ULen,  int /*Freq*/, 
+		    float /*Weight*/,  void * /*info*/) {
+  if (Doc != NULL) {
+    // Make a copy of this string so we can unload the database without losing it
+    tempdoc = new char[ULen + 1];
+    strcpy(tempdoc, Doc);
+  }
+  templen = ULen;
+  
+  return 0;
+}
+
+
+text_t mgsearchclass::getindexsuffix (const text_t &collection, 
+			      const text_t &index) {
+
+  text_t indexsuffix = "index";  
+  indexsuffix = filename_cat (indexsuffix, index);
+  if (indexstem.empty()) {
+    // no index stem, use the coll name
+    indexsuffix = filename_cat (indexsuffix, collection);
+  } else {
+    indexsuffix = filename_cat (indexsuffix, indexstem);
+  }
+  return indexsuffix;
+}
+
+
+
+
+////////////////////
+// mgsearch class //
+////////////////////
+
+mgsearchclass::mgsearchclass ()
+  : searchclass() {
+ 
+}
+
+mgsearchclass::~mgsearchclass () 
+{
+  if (cache != NULL) 
+    {
+      delete cache;
+      cache = NULL;
+    }
+}
+
+void mgsearchclass::set_indexstem(const text_t &stem) {
+  indexstem = stem;
+  
+}
+
+// you only need to use this function before doing any stemming
+// casefolding and stemming will be set if values for them are
+// provided (0 or 1).
+// makeindexcurrent returns true if it was able to load the database
+bool mgsearchclass::makeindexcurrent (const text_t &index,
+				      const text_t &subcollection,
+				      const text_t &language,
+				      const text_t &collection,
+				      int casefolding,
+				      int stemming) {
+  bool databaseloaded = true;
+
+  // get the names of the collection, index and text suffixes
+  char *ccollection = collection.getcstr();
+  assert (ccollection != NULL);
+  char *idxsuffix = (getindexsuffix (collection, (index+subcollection+language))).getcstr();
+  assert (idxsuffix != NULL);
+  char *txtsuffix = (getindexsuffix (collection, "text")).getcstr();
+  assert (txtsuffix != NULL);
+#ifdef __WIN32__
+  char *ccollectdir = (collectdir+"\\").getcstr(); assert (ccollectdir != NULL);
+#else
+  char *ccollectdir = collectdir.getcstr(); assert (ccollectdir != NULL);
+#endif
+
+  if (load_database(ccollection, ccollectdir, idxsuffix, txtsuffix)) {
+    if (casefolding == 0) mgq_ask(".set casefold off");
+    else if (casefolding > 0) mgq_ask(".set casefold on");
+    if (stemming == 0) mgq_ask(".set stem off");
+    else if (stemming > 0) mgq_ask(".set stem on");
+    
+  } else databaseloaded = false;
+
+  // free up the c strings
+  delete []ccollection;
+  delete []idxsuffix;
+  delete []txtsuffix;
+  delete []ccollectdir;
+
+  return databaseloaded;
+}
+
+
+// stem word uses the values set in the last call to makeindexcurrent
+// to stem the word. It is assumed that word is in unicode
+text_t mgsearchclass::stemword (const text_t &word) {
+  return to_uni (mgsearch_stemword (to_utf8 (word)));
+}
+
+text_t mgsearchclass::stemword (text_t::const_iterator here, text_t::const_iterator end) {
+  return to_uni (mgsearch_stemword (to_utf8 (here, end)));
+}
+
+/**
+ * search directs the whole execution of the search; a number of other
+ * functions in this class are called as a result, and precondition
+ * checks are also made
+ */
+bool mgsearchclass::search(const queryparamclass &queryparams, 
+			   queryresultsclass &queryresults) {
+  //  assert (cache != NULL);
+
+  // clear any previous results
+  queryresults.clear();
+  // first check the cache
+  if (cache != NULL) {
+    if (cache->find(queryparams, queryresults)) return true;
+  }
+  // make sure there is a query to be processed
+  if (!has_unicode_letdig(queryparams.querystring)) return true;
+
+  if (makeindexcurrent (queryparams.index, queryparams.subcollection,
+			queryparams.language, queryparams.collection)) {
+    // initialise the form of results
+    setsearchmode (queryparams);
+
+    // execute the query
+    submitquery (queryparams);
+
+    // retrieve the results
+    getresults (queryparams, queryresults);
+    unload_database();  // Important that local library doesn't leave any files open
+    return true;
+  }
+
+  return false;
+}
+
+/* accumulator_method has been changed to use array rather than list.
+list appears to be broken somewhat - for some ranked queries, it returned 
+fewer results than it should have (eg 45 instead of 50). The three other
+methods (array, splay_tree, hash_table) all return the same number of 
+documents, in the same order, with the same ranks. list returns what 
+appears to be the same documents (but less of them), but with different ranks,
+and in a different order. Minimal time tests dont show any speed improvement
+of list over array (maybe because its broken??).  [02/2001, kjm18]
+
+... [sjboddie, also 02/2001] turns out that changing the accumulator_method
+introduced a more serious bug than it fixed (i.e. occasionally when doing a
+ranked search for a very common word you get no results at all). I've
+changed it back to list for now, one day we should play with other
+accumulator_methods but for now I don't have time and don't want to risk
+introducing bugs (better the devil you know ;)
+*/
+void mgsearchclass::setsearchmode (const queryparamclass &queryparams)
+{
+  mgq_ask(".set expert true");
+  mgq_ask(".set sorted_terms true");
+  mgq_ask(".set accumulator_method list");
+  mgq_ask(".set max_accumulators 500000");
+  mgq_ask(".set maxparas 500000");
+  mgq_ask(".set verbatim true");
+  mgq_ask(".unset skip_dump");
+  mgq_ask(".set mode docnums");
+
+  switch (queryparams.search_type) 
+    {
+    case 0: mgq_ask(".set query boolean");  break;
+    case 1:  mgq_ask(".set query ranked"); break;
+    }
+  switch (queryparams.casefolding) 
+    {
+    case 1: mgq_ask(".set casefold on");  break;
+    case 0: mgq_ask(".set casefold off"); break;
+    }
+  switch (queryparams.stemming) 
+    {
+    case 1: mgq_ask(".set stem on");  break;
+    case 0: mgq_ask(".set stem off"); break;
+    }
+  mgq_ask(".set heads_length 150");
+  
+  if (queryparams.maxdocs == -1) {
+    mgq_ask(".set maxdocs all");
+  } else {
+    char maxdocstr[32];
+    sprintf(maxdocstr, ".set maxdocs %i", queryparams.maxdocs);
+    mgq_ask(maxdocstr);
+  }
+
+  char maxnumericstr[32];
+  sprintf(maxnumericstr, ".set maxnumeric %i", queryparams.maxnumeric);
+  mgq_ask(maxnumericstr);
+  
+}
+
+/**
+ * submitquery constructs the query string (into UTF8 encoding)
+ * and submits it using mgq_ask to the mg search engine.  Most
+ * of the processing will be done inside Greenstone
+ */
+void mgsearchclass::submitquery (const queryparamclass &queryparams)
+{
+  // sort out the query string; copy it, remove all special characters
+  // and then convert it to a string in UTF8 format
+  text_t ttquerystring = queryparams.querystring;
+  filterquery (ttquerystring);
+  char *querystring = to_utf8(ttquerystring).getcstr();
+
+  // submit the query
+  mgq_ask(querystring);
+
+  // destroy the temporary character array
+  delete []querystring;
+}
+
+/**
+ * getrults is called to retrieve the required data on the docs
+ * which responded to the query submitted in submitquery above.
+ *
+ * It calls the local mgquery (mgq) interface to MG several times,
+ * to obtain the document numbers, term frequencies, term variants
+ * etc.  All processing of the query will be done by Greenstone
+ * thereafter
+ */
+void mgsearchclass::getresults (const queryparamclass &queryparams,
+				queryresultsclass &queryresults) {
+  // get the configuration for the maximum number of documents to
+  // retrieve
+  int howmany = queryparams.maxdocs;
+  if (howmany == -1) howmany = MAXNUMDOCS;
+  mgq_results(result_docnums, 0, howmany, 
+	      ourquerycallback, (void *)(&queryresults));
+  
+  // get the term frequencies
+  mgq_results(result_termfreqs, 0, MAXNUMTERMS, 
+	      termfreqcallback, (void *)(&queryresults));
+  queryresults.sortuniqqueryterms();
+
+  // get term variants 
+  mgq_results(result_terms, 0, MAXNUMTERMS,
+              termvariantscallback, (void *)(&queryresults));
+
+  // get the number of documents retrieved
+  int total_retrieved = 0, is_approx = 0;
+  mgq_docsretrieved (&total_retrieved, &is_approx);
+
+  if (total_retrieved == 0) {
+    // not available (or really was zero)
+    queryresults.docs_matched = queryresults.docs.docset.size();
+    if ((queryparams.maxdocs == -1) ||
+	(queryresults.docs_matched < queryparams.maxdocs))
+      queryresults.is_approx = Exact;
+    else
+      queryresults.is_approx = MoreThan;
+  } else {
+    queryresults.docs_matched = total_retrieved;
+    if (is_approx) queryresults.is_approx = Approximate;
+    else queryresults.is_approx = Exact;
+  }
+}
+
+/**
+ * Tidies the given querystring, removing special characters
+ */
+void mgsearchclass::filterquery (text_t &ttquerystring) {
+  text_t::iterator ithere = ttquerystring.begin ();
+  text_t::iterator itend = ttquerystring.end ();
+  
+  // remove all non alphanumeric characters (except
+  // boolean operators
+  while (ithere != itend) {
+    if ((!is_unicode_letdig(*ithere)) && (*ithere != '!') &&
+	(*ithere != '&') && (*ithere != '|') && (*ithere != '(') &&
+	(*ithere != ')')) (*ithere) = ' ';
+    ++ithere;
+  }
+}
+
+
+// the document text for 'docnum' is placed in 'output'
+// docTargetDocument returns 'true' if it was able to
+// try to get a document
+// collection is needed to see if an index from the 
+// collection is loaded. If no index has been loaded
+// defaultindex is needed to load one
+bool mgsearchclass::docTargetDocument(const text_t &defaultindex,
+				      const text_t &defaultsubcollection,
+				      const text_t &defaultlanguage,
+				      const text_t &collection,
+				      int docnum,
+				      text_t &output) {
+  output.clear();
+
+  // get the mg version of the document
+  char *mgdoc = NULL;
+  int doclen = 0;
+  if (!mgdocument (defaultindex, defaultsubcollection, defaultlanguage,
+		   collection, docnum, mgdoc, doclen)) return false;
+  if (mgdoc == NULL) return false;
+
+  // replace all control-Cs with spaces
+  char *mgdoc_here = mgdoc;
+  char *mgdoc_end = mgdoc + doclen;
+  while (mgdoc_here < mgdoc_end) {
+    if (*mgdoc_here == '\x3') *mgdoc_here = ' ';
+    ++mgdoc_here;
+  }
+
+  // convert this document to unicode
+  utf8inconvertclass inconvert;
+  convertclass::status_t status;
+  inconvert.reset ();
+  inconvert.setinput (mgdoc, doclen);
+  inconvert.convert (output, status);
+
+  delete[] mgdoc;
+  return true;
+}
+
+
+bool mgsearchclass::mgdocument (const text_t &defaultindex, 
+				const text_t &defaultsubcollection,
+				const text_t &defaultlanguage,
+				const text_t &collection,
+				int docnum,
+				char *&UDoc, int &ULen) {
+  int databaseloaded = 0;
+
+  UDoc = NULL; ULen = 0;
+  
+  // see if we can make an appropriate database current
+//    char *ccollection = collection.getcstr();
+//    assert (ccollection != NULL);
+//    databaseloaded = load_text_database (ccollection);
+//    delete []ccollection;
+  
+  // try and load the database
+//    if (!databaseloaded)
+  databaseloaded = makeindexcurrent (defaultindex, defaultsubcollection,
+				     defaultlanguage, collection);
+  
+  if (databaseloaded) {
+    // retrieve the document from mg
+    char docstr[32];
+    sprintf(docstr, "%i", docnum);
+    
+    mgq_ask(".set mode text");
+    mgq_ask(".set query docnums");
+    mgq_ask(docstr);
+
+    tempdoc = NULL;
+    templen = 0;
+    mgq_results (result_docs, 0, 1, doctextcallback, (void *)NULL);
+    UDoc = tempdoc;
+    ULen = templen;
+  }
+
+  unload_database();  // Important that local library doesn't leave any files open
+  return (bool)databaseloaded;
+}
+
+// unload_database simply calls mgq's close_all_databases function to clear
+// any cached databases - this is useful when attempting to completely
+// remove all trace of a collectionserver at runtime (when using a
+// persistent version of Greenstone like the windows local library)
+void mgsearchclass::unload_database () {
+  close_all_databases();
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/mgsearch.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgsearch.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgsearch.h	(revision 16561)
@@ -0,0 +1,101 @@
+/**********************************************************************
+ *
+ * mgsearch.h --  implementation of search for old versions of mg
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef MGSEARCH_H
+#define MGSEARCH_H
+
+
+#include "search.h"
+
+class mgsearchclass : public searchclass {
+public:
+  mgsearchclass ();
+  virtual ~mgsearchclass ();
+
+  virtual text_t get_textindex_type() {return "mg";}
+
+  // you only need to use this function before doing any stemming
+  // casefolding and stemming will be set if values for them are
+  // provided (0 or 1).
+  // makeindexcurrent returns true if it was able to load the database
+  bool makeindexcurrent (const text_t &index, const text_t &subcollection,
+			 const text_t &language, const text_t &collection,
+			 int casefolding = -1, int stemming = -1);
+
+  // stem word uses the values set in the last call to makeindexcurrent
+  // to stem the word. It is assumed that word is in unicode
+  text_t stemword (const text_t &word);
+  text_t stemword (text_t::const_iterator here, text_t::const_iterator end);
+  
+  // the search results are returned in queryresults
+  // search returns 'true' if it was able to do a search
+  bool search(const queryparamclass &queryparams, 
+	      queryresultsclass &queryresults);
+  
+  // the document text for 'docnum' is placed in 'output'
+  // docTargetDocument returns 'true' if it was able to
+  // try to get a document
+  // collection is needed to see if an index from the 
+  // collection is loaded. If no index has been loaded
+  // defaultindex is needed to load one
+  bool docTargetDocument(const text_t &defaultindex,
+			 const text_t &defaultsubcollection,
+			 const text_t &defaultlanguage,
+			 const text_t &collection,
+			 int docnum,
+			 text_t &output);
+
+  // same as docTargetDocument except the document is returned
+  // in its raw form. UDoc remains the property of mg and may
+  // be destroyed on the next call to mg.
+  bool mgdocument (const text_t &defaultindex,
+		   const text_t &defaultsubcollection,
+		   const text_t &defaultlanguage,
+		   const text_t &collection,
+		   int docnum,
+		   char *&UDoc, int &ULen);
+
+  // unload_database simply calls mgq's close_all_databases function to
+  // clear any cached databases - this is useful when attempting to
+  // completely remove all trace of a collectionserver at runtime (when
+  // using a persistent version of Greenstone like the windows local
+  // library)
+  void unload_database ();
+
+  void set_indexstem(const text_t &indexstem);
+
+protected:
+
+  text_t indexstem; // the stem of the index files - defaults to the collectionname
+  void setsearchmode (const queryparamclass &queryparams);
+  void submitquery (const queryparamclass &queryparams);
+  void getresults (const queryparamclass &queryparams, queryresultsclass &queryresults);
+  text_t getindexsuffix (const text_t &collection, const text_t &index);
+  virtual void filterquery (text_t &ttquerystring);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/mgsource.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgsource.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgsource.cpp	(revision 16561)
@@ -0,0 +1,38 @@
+/**********************************************************************
+ *
+ * mgsource.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.
+ *
+ *********************************************************************/
+
+#include "mgsource.h"
+
+
+mgsourceclass::mgsourceclass () 
+  : sourceclass()
+{
+  classname = "mgsource";
+}
+
+mgsourceclass::~mgsourceclass () 
+{
+}
+
Index: /gsdl/trunk/gs2runtime/src/colservr/mgsource.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/mgsource.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/mgsource.h	(revision 16561)
@@ -0,0 +1,41 @@
+/**********************************************************************
+ *
+ * mgsource.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.
+ *
+ *********************************************************************/
+
+
+#ifndef MGSOURCE_H
+#define MGSOURCE_H
+
+#include "source.h"
+
+
+class mgsourceclass : public sourceclass {
+public:
+  mgsourceclass ();
+  virtual ~mgsourceclass ();
+
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.cpp	(revision 16561)
@@ -0,0 +1,84 @@
+/**********************************************************************
+ *
+ * phrasequeryfilter.cpp -- 
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "phrasequeryfilter.h"
+#include "queryinfo.h"
+
+#include "phrasesearch.h"
+
+
+
+// do aditional query processing
+void phrasequeryfilterclass::post_process (const queryparamclass &queryparams,
+					   queryresultsclass &queryresults) {
+  bool results_shrunk = false;
+
+  // post-process the results if needed
+  if (queryresults.orgterms.size() > 1 && !queryresults.docs.docset.empty()) {
+    // get the long version of the index
+    text_t longindex;
+    indexmap.to2from (queryparams.index, longindex);
+      
+    // process each of the matched documents
+    docresultmap::iterator docs_here = queryresults.docs.docset.begin();
+    docresultmap::iterator docs_end = queryresults.docs.docset.end();
+    while (docs_here != docs_end) {
+      if ((*docs_here).second.num_query_terms_matched == queryresults.orgterms.size() &&
+	  OID_phrase_search (*(mgsearchclass *)textsearchptr, *db_ptr, queryparams.index,
+			     queryparams.subcollection, queryparams.language, 
+			     longindex, queryparams.collection, queryresults.orgterms,
+			     (*docs_here).second.docnum)) {
+	++docs_here->second.num_phrase_match;
+	++docs_here;
+
+      } else {
+	docresultmap::iterator this_doc_here = docs_here;
+	++docs_here;
+	if (queryparams.match_mode == 1) {
+	  // need to delete this element, there was no exact match
+	  queryresults.docs.docset.erase(this_doc_here);
+	  results_shrunk = true;
+	}
+      }
+    }
+  }
+
+  if (results_shrunk) queryresults.docs_matched = queryresults.docs.docset.size();
+}
+
+
+void phrasequeryfilterclass::sort_doc_results (const FilterRequest_t &/*request*/,
+					 docresultsclass &docs) {
+  resultsorderer_t resultsorderer;
+  resultsorderer.compare_phrase_match = true;
+  resultsorderer.compare_terms_match = true;
+  resultsorderer.compare_doc_weight = true;
+  resultsorderer.docset = &(docs.docset);
+
+  // first get a list of document numbers
+  docs.docnum_order();
+
+  sort (docs.docorder.begin(), docs.docorder.end(), resultsorderer);
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/phrasequeryfilter.h	(revision 16561)
@@ -0,0 +1,43 @@
+/**********************************************************************
+ *
+ * phrasequeryfilter.h -- 
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef PHRASEQUERYFILTER_H
+#define PHRASEQUERYFILTER_H
+
+#include "mgqueryfilter.h"
+
+
+class phrasequeryfilterclass : public mgqueryfilterclass {
+protected:
+  void post_process (const queryparamclass &queryparams,
+		     queryresultsclass &queryresults);
+  void sort_doc_results (const FilterRequest_t &request,
+			 docresultsclass &docs);
+public:
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.cpp	(revision 16561)
@@ -0,0 +1,259 @@
+/**********************************************************************
+ *
+ * phrasesearch.cpp -- tools to search for a phrase in a larger text
+ * Copyright (C) 1999  DigiLib Systems Limited
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "phrasesearch.h"
+#include "gsdlunicode.h"
+
+inline unsigned char *parse_nonword_word (unsigned char *here, unsigned char *end,
+					  text_t &word) {
+  int c_len = 0;
+  unsigned short c = 0;
+
+  word.clear();
+
+  // parse non word
+  while (here <= end) {
+    c_len = parse_utf8_char (here, end, &c);
+    if (c == '(') {
+      // found a note, look for '}'
+      while (here <= end && c != ')') {
+	c_len = parse_utf8_char (here, end, &c);
+	here += c_len;
+      }
+    }
+    if (c == '{') {
+      // found a composite character, look for '}'
+      while (here <= end && c != '}') {
+	c_len = parse_utf8_char (here, end, &c);
+	here += c_len;
+      }
+    }
+    if (is_unicode_letdig(c)) {
+      while (c_len > 0) {
+	// this is in a word
+	word.push_back(*here);
+	++here; --c_len;
+      }
+      break;
+    }
+    here += c_len;
+  }
+
+  // parse word
+  while (here <= end) {
+    c_len = parse_utf8_char (here, end, &c);
+    if (!is_unicode_letdig(c)) {
+      here += c_len;  // it is ok to skip a nonword character
+      break;
+    }
+    while (c_len > 0) {
+      word.push_back(*here);
+      ++here; --c_len;
+    }
+  }
+
+  return here;
+}
+
+static void get_all_docnums (dbclass &db, text_t OID, vector<int> &docnum_list) {
+
+  infodbclass OID_info;
+  
+  // get OID
+  if (!db.getinfo (OID, OID_info)) return;
+  if (OID_info["hastxt"] == "1" && !OID_info["docnum"].empty()) {
+    docnum_list.push_back (OID_info["docnum"].getint());
+  }
+
+  // get contents set
+  if (OID_info["contains"].empty()) return;
+  text_tarray contains; text_t tmptext;
+  text_t::iterator contains_here = OID_info["contains"].begin();
+  text_t::iterator contains_end = OID_info["contains"].end();
+  while (contains_here != contains_end) {
+    if (*contains_here == '"') tmptext += OID;
+    else if (*contains_here == ';') {
+      if (!tmptext.empty()) contains.push_back (tmptext);
+      tmptext.clear();
+    } else tmptext.push_back(*contains_here);
+    ++contains_here;
+  }
+  if (!tmptext.empty()) contains.push_back (tmptext);
+
+  text_tarray::const_iterator here = contains.begin();
+  text_tarray::const_iterator end = contains.end();
+  while (here != end) {
+    get_all_docnums (db, *here, docnum_list);
+    ++here;
+  }
+}
+
+bool doc_phrase_search (unsigned char *doc, int doclen,
+			const termfreqclassarray &phrase) {
+  // note: this uses the most braindead search routine :-)
+  // however its not so bad as there shouldn't be many partial
+  // matches
+
+  // a null phrase matches anything
+  if (phrase.empty()) return true;
+
+  // if there is nothing then there can't be a match
+  if (doc == NULL || doclen == 0) return false;
+  
+  text_t doc_word;
+  doc_word.reserve (16);
+
+  bool first = true;
+
+  unsigned char *doc_here = doc;
+  unsigned char *doc_herefirstword = doc;
+  unsigned char *doc_end = doc+doclen-1; // unitool conventions :-/
+
+  while (doc_here <= doc_end) {
+    first = true;
+
+    // there will be at least one member of phrase (see above)
+    termfreqclassarray::const_iterator phrase_here = phrase.begin();
+    termfreqclassarray::const_iterator phrase_end = phrase.end();
+    do {
+      // get the next non-word ... and ignore it, then get the next word
+      doc_here = parse_nonword_word (doc_here, doc_end, doc_word);
+      if (first) {doc_herefirstword = doc_here; first = false;}
+
+      // break if this word is not the next in the phrase
+      if ((*phrase_here).utf8equivterms.find (doc_word) ==
+	  (*phrase_here).utf8equivterms.end()) break;
+
+      ++phrase_here;
+    } while (doc_here <= doc_end && phrase_here != phrase_end);
+
+    // see if we found a phrase
+    if (phrase_here == phrase_end) return true;
+    
+    doc_here = doc_herefirstword; // set the counter back
+  }
+
+  return false;
+}
+
+// looks for the stemmed phrase in the metadata or text associated with
+// an OID. This function has not been coded with all situations in mind
+bool OID_phrase_search (mgsearchclass &mgsearch,
+			dbclass &db,
+			const text_t &index,
+			const text_t &subcollection,
+			const text_t &language,
+			const text_t &longindex,
+			const text_t &collection,
+			const termfreqclassarray &phrase,
+			int docnum) {
+
+  // get OID
+  infodbclass docnum_info;
+  if (!db.getinfo (docnum, docnum_info)) return false;
+  text_t &OID = docnum_info["section"];
+  if (OID.empty()) return false;
+
+  // disect the long index to find out where the text should come from
+  text_t gran, type;
+  text_t::const_iterator longindex_here = longindex.begin();
+  text_t::const_iterator longindex_end = longindex.end();
+  longindex_here = getdelimitstr (longindex_here, longindex_end, ':', gran);
+  longindex_here = getdelimitstr (longindex_here, longindex_end, ':', type);
+
+  if (gran.empty()) return false;
+  
+  // note that we're treating indexes of type 'all' (i.e. text,Title,Creator)
+  // or other composite indexes that contain "text" as if they were simply 'text' indexes
+  if ((type == "text") || (type == "all") || (findword(type.begin(),type.end(),"text") != type.end())) {
+    char *doc = NULL;
+    int doclen = 0;
+  
+    // get text from mg.
+    if (gran == "document") {
+
+      // if this is a document level index (which should only happen if
+      // there are no matching indexes with a finer granularity -- see
+      // mgqueryfilterclass::mg_parse_query_params) then we must do the
+      // phrase search on the entire document (i.e. all the sections)
+      // -- this is going to make a slow process even slower
+      vector<int> docnum_list; text_t fulldoc;
+      get_all_docnums (db, OID, docnum_list);
+      vector<int>::const_iterator this_docnum = docnum_list.begin();
+      vector<int>::const_iterator end_docnum = docnum_list.end();
+      while (this_docnum != end_docnum) {
+	if (mgsearch.mgdocument (index, subcollection, language, collection,
+				 *this_docnum, doc, doclen)) {
+	  fulldoc.appendcstr (doc);
+	}
+	++this_docnum;
+      }
+      doc = fulldoc.getcstr();
+      doclen = fulldoc.size();
+      bool rv = doc_phrase_search ((unsigned char *)doc, doclen, phrase);
+      delete []doc;
+      return rv;
+
+    } else {
+
+      if (!mgsearch.mgdocument (index, subcollection, language, collection,
+				docnum, doc, doclen)) return false;
+      return doc_phrase_search ((unsigned char *)doc, doclen, phrase);
+    } 
+  }
+
+  char *metadata = NULL;
+  text_t::size_type metadata_len = 0;
+  infodbclass OID_info;
+  
+  // get field
+  if (!db.getinfo (OID, OID_info)) return false;
+
+  bool result = false;
+
+  // need to look through all the metadata values in the index
+  text_tarray keys;
+  splitchar(type.begin(), type.end(), ',', keys);
+
+  text_tarray::const_iterator keyhere = keys.begin();
+  text_tarray::const_iterator keyend = keys.end();
+  while (keyhere != keyend) {
+    text_tarray *tarr_ptr = OID_info.getmultinfo (*keyhere);
+    if (tarr_ptr != NULL ) {
+      text_tarray::const_iterator subvalue_here = (*tarr_ptr).begin();
+      text_tarray::const_iterator subvalue_end = (*tarr_ptr).end();
+      while (subvalue_here != subvalue_end) {
+	metadata = (to_utf8(*subvalue_here)).getcarr(metadata_len);
+	result = doc_phrase_search ((unsigned char *)metadata, metadata_len, phrase);
+	delete [] metadata;
+	
+	if (result) return true;
+	++subvalue_here;
+      }
+    }
+    ++keyhere;
+  }
+  return result;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/phrasesearch.h	(revision 16561)
@@ -0,0 +1,50 @@
+/**********************************************************************
+ *
+ * phrasesearch.h -- tools to search for a phrase in a larger text
+ * Copyright (C) 1999  DigiLib Systems Limited
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef PHRASESEARCH_H
+#define PHRASESEARCH_H
+
+
+#include "text_t.h"
+#include "unitool.h"
+#include "mgsearch.h"
+#include "dbclass.h"
+
+
+// looks for the phrase in the metadata or text associated with
+// a document number. This function has not been coded with all
+// situations in mind and expects mgsearch and db set up ready to go
+bool OID_phrase_search (mgsearchclass &mgsearch,
+			dbclass &db,
+			const text_t &index,
+			const text_t &subcollection,
+			const text_t &language,
+			const text_t &longindex,
+			const text_t &collection,
+			const termfreqclassarray &phrase,
+			int docnum);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/querycache.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/querycache.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/querycache.cpp	(revision 16561)
@@ -0,0 +1,104 @@
+/**********************************************************************
+ *
+ * querycache.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.
+ *
+ *********************************************************************/
+
+#include "querycache.h"
+
+
+
+
+resultcacheel::resultcacheel () 
+{
+  accessnum = -1;
+}
+
+
+
+querycache::querycache (int themaxcachesize)
+{
+  if (themaxcachesize < 1) themaxcachesize = 1;
+  
+  resultcache = new resultcacheel[themaxcachesize];
+
+  maxcachesize = themaxcachesize;
+  nextaccessnum = 1;
+}
+
+querycache::~querycache ()
+{
+  delete [] resultcache;
+}
+
+// returns true if the query was found in the cache
+// if the query was found then queryresults contains
+// the results
+bool querycache::find (const queryparamclass &queryparams, 
+		       queryresultsclass &queryresults)
+{
+  int i;
+
+  for (i=0; i < maxcachesize; ++i)
+    {
+      if (resultcache[i].queryparameters == queryparams)
+	{
+	  queryresults = resultcache[i].queryresults;
+	  resultcache[i].accessnum = getnextaccessnum ();
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+
+void querycache::cache (const queryparamclass &queryparams,
+	  const queryresultsclass &queryresults)
+{
+  int i = getfreecachenum();
+  resultcache[i].queryparameters = queryparams;
+  resultcache[i].queryresults = queryresults;
+  resultcache[i].accessnum = getnextaccessnum ();
+}
+
+int querycache::getnextaccessnum ()
+{
+  return nextaccessnum++;
+}
+
+int querycache::getfreecachenum ()
+{
+  int i;
+  int minaccessnum = 0;
+  int minaccessi = 0;
+  
+  for (i=0; i < maxcachesize; ++i)
+    {
+      if (resultcache[i].accessnum < minaccessnum) {
+	minaccessnum = resultcache[i].accessnum;
+	minaccessi = i;
+      }
+    }
+
+  return minaccessi;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/querycache.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/querycache.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/querycache.h	(revision 16561)
@@ -0,0 +1,70 @@
+/**********************************************************************
+ *
+ * querycache.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.
+ *
+ *********************************************************************/
+
+
+#ifndef QUERYCACHE_H
+#define QUERYCACHE_H
+
+
+#include "text_t.h"
+#include "queryinfo.h"
+
+class resultcacheel
+{
+public:
+  resultcacheel ();
+
+  int accessnum;
+  queryparamclass queryparameters;
+  queryresultsclass queryresults;
+};
+
+
+class querycache {
+public:
+  querycache (int themaxcachesize);
+  ~querycache ();
+
+  // returns true if the query was found in the cache
+  // if the query was found then queryresults contains
+  // the results
+  bool find (const queryparamclass &queryparams, 
+	     queryresultsclass &queryresults);
+
+  void cache (const queryparamclass &queryparams,
+	    const queryresultsclass &queryresults);
+
+protected:
+  resultcacheel *resultcache;
+
+  int maxcachesize;
+  int nextaccessnum;
+  
+  int getnextaccessnum ();
+  int getfreecachenum ();
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/queryfilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/queryfilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/queryfilter.cpp	(revision 16561)
@@ -0,0 +1,424 @@
+/**********************************************************************
+ *
+ * queryfilter.cpp -- base class for queryfilters
+ * 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.
+ *
+ *********************************************************************/
+
+#include "queryfilter.h"
+#include "fileutil.h"
+
+
+// translate will return true if successful
+bool queryfilterclass::translate (dbclass *db_ptr, text_t& docnum, text_t &trans_OID) {
+  infodbclass info;
+
+  trans_OID.clear();
+
+  // get the info
+  if (db_ptr == NULL) return false;
+  if (!db_ptr->getinfo(docnum, info)) return false;
+
+  // translate
+  if (info["section"].empty()) return false;
+
+  trans_OID = info["section"];
+  return true;
+}
+
+
+// whether document results are needed
+bool queryfilterclass::need_matching_docs (int filterResultOptions) {
+  return ((filterResultOptions & FROID) || (filterResultOptions & FRranking) ||
+	  (filterResultOptions & FRmetadata));
+}
+
+// whether term information is needed
+bool queryfilterclass::need_term_info (int filterResultOptions) {
+  return ((filterResultOptions & FRtermFreq) || (filterResultOptions & FRmatchTerms));
+}
+
+/////////////////////////////////
+// functions for queryfilterclass
+/////////////////////////////////
+
+// get the query parameters
+void queryfilterclass::parse_query_params (const FilterRequest_t &request,
+					   vector<queryparamclass> &query_params,
+					   int &startresults, int &endresults,
+					   text_t &phrasematch, ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  // set defaults for the return parameters
+  query_params.erase(query_params.begin(), query_params.end());
+  startresults = filterOptions["StartResults"].defaultValue.getint();
+  endresults = filterOptions["EndResults"].defaultValue.getint();
+  phrasematch = filterOptions["PhraseMatch"].defaultValue;
+
+  // set defaults for query parameters
+  queryparamclass query;
+  query.combinequery = "or"; // first one must be "or"
+  query.collection = collection;
+  query.index = filterOptions["Index"].defaultValue;
+  query.subcollection = filterOptions["Subcollection"].defaultValue;
+  query.language = filterOptions["Language"].defaultValue;
+  query.querystring.clear();
+  query.search_type = (filterOptions["QueryType"].defaultValue == "ranked");
+  query.match_mode = (filterOptions["MatchMode"].defaultValue == "all");
+  query.casefolding = (filterOptions["Casefold"].defaultValue == "true");
+  query.stemming = (filterOptions["Stem"].defaultValue == "true");
+  query.accentfolding = (filterOptions["AccentFold"].defaultValue == "true");
+  query.maxdocs = filterOptions["Maxdocs"].defaultValue.getint();
+  query.level = filterOptions["Level"].defaultValue;
+  query.filterstring = filterOptions["FilterString"].defaultValue;  // Lucene specific
+  query.sortfield = filterOptions["SortField"].defaultValue;  // Lucene specific
+  query.fuzziness = filterOptions["Fuzziness"].defaultValue;  // Lucene specific
+  query.maxnumeric = maxnumeric;
+  OptionValue_tarray::const_iterator options_here = request.filterOptions.begin();
+  OptionValue_tarray::const_iterator options_end = request.filterOptions.end();
+  while (options_here != options_end) {
+    if ((*options_here).name == "CombineQuery") {
+      // add this query
+      
+      // "all", needed when combining queries where the document results are needed
+      if (need_matching_docs (request.filterResultOptions)) query.maxdocs = -1; 
+      query_params.push_back (query);
+
+      // start on next query
+      query.clear();
+      query.combinequery = (*options_here).value;
+
+      // set defaults for query parameters
+      query.collection = collection;
+      query.index = filterOptions["Index"].defaultValue;
+      query.subcollection = filterOptions["Subcollection"].defaultValue;
+      query.language = filterOptions["Language"].defaultValue;
+      query.querystring.clear();
+      query.search_type = (filterOptions["QueryType"].defaultValue == "ranked");
+      query.match_mode = (filterOptions["MatchMode"].defaultValue == "all");
+      query.casefolding = (filterOptions["Casefold"].defaultValue == "true");
+      query.stemming = (filterOptions["Stem"].defaultValue == "true");
+      query.accentfolding = (filterOptions["AccentFold"].defaultValue == "true");
+      query.level = filterOptions["Level"].defaultValue;
+      query.filterstring = filterOptions["FilterString"].defaultValue;  // Lucene specific
+      query.sortfield = filterOptions["SortField"].defaultValue;  // Lucene specific
+      query.fuzziness = filterOptions["Fuzziness"].defaultValue;  // Lucene specific
+      query.maxnumeric = maxnumeric;
+      // "all", needed when combining queries where the document results are needed
+      if (need_matching_docs (request.filterResultOptions)) query.maxdocs = -1; 
+      else query.maxdocs = filterOptions["Maxdocs"].defaultValue.getint();
+      
+    } else if ((*options_here).name == "StartResults") {
+      startresults = (*options_here).value.getint();
+    } else if ((*options_here).name == "EndResults") {
+      endresults = (*options_here).value.getint();
+    } else if ((*options_here).name == "QueryType") {
+      query.search_type = ((*options_here).value == "ranked");
+    } else if ((*options_here).name == "MatchMode") {
+      query.match_mode = ((*options_here).value == "all");
+      if (query.match_mode == 1) query.maxdocs = -1;
+    } else if ((*options_here).name == "Term") {
+      query.querystring = (*options_here).value;
+    } else if ((*options_here).name == "Casefold") {
+      query.casefolding = ((*options_here).value == "true");
+    } else if ((*options_here).name == "Stem") {
+      query.stemming = ((*options_here).value == "true");
+    } else if ((*options_here).name == "AccentFold") {
+      query.accentfolding = ((*options_here).value == "true");
+    } else if ((*options_here).name == "Index"&& (*options_here).value !="") {
+      query.index = (*options_here).value;
+    } else if ((*options_here).name == "Subcollection") {
+      query.subcollection = (*options_here).value;
+    } else if ((*options_here).name == "Language") {
+      query.language = (*options_here).value;
+    } else if ((*options_here).name == "Maxdocs") {
+      query.maxdocs = (*options_here).value.getint();
+    } else if ((*options_here).name == "PhraseMatch") {
+      phrasematch = (*options_here).value;
+    } else if ((*options_here).name == "Level") {
+      query.level = (*options_here).value;
+    } else if ((*options_here).name == "FilterString") {
+      query.filterstring = (*options_here).value;
+    } else if ((*options_here).name == "SortField") {
+      query.sortfield = (*options_here).value;
+    } else if ((*options_here).name == "Fuzziness") {
+      query.fuzziness = (*options_here).value;
+    } else {
+      logout << text_t2ascii
+	     << "warning: unknown queryfilter option \""
+	     << (*options_here).name
+	     << "\" ignored.\n\n";
+    }
+
+    ++options_here;
+  }
+
+  // Store the start and end results in the query too, as lucene now needs to
+  // pass them through to the Java
+  query.startresults = startresults;
+  query.endresults = endresults;
+
+  // add the last query
+  query_params.push_back (query);
+}
+
+
+
+
+queryfilterclass::queryfilterclass () {
+  db_ptr = NULL;
+  textsearchptr = NULL;
+  maxnumeric = 4;
+
+  FilterOption_t filtopt;
+  filtopt.name = "CombineQuery";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "and";
+  filtopt.validValues.push_back("and");
+  filtopt.validValues.push_back("or");
+  filtopt.validValues.push_back("not");
+  filterOptions["CombineQuery"] = filtopt;
+
+  // -- onePerQuery StartResults   integer
+  filtopt.clear();
+  filtopt.name = "StartResults";
+  filtopt.type = FilterOption_t::integert;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "1";
+  filtopt.validValues.push_back("1");
+  filtopt.validValues.push_back("1000");
+  filterOptions["StartResults"] = filtopt;
+
+  // -- onePerQuery EndResults     integer
+  filtopt.clear();
+  filtopt.name = "EndResults";
+  filtopt.type = FilterOption_t::integert;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "10";
+  filtopt.validValues.push_back("-1");
+  filtopt.validValues.push_back("1000");
+  filterOptions["EndResults"] = filtopt;
+
+  // -- onePerQuery QueryType      enumerated (boolean, ranked)
+  filtopt.clear();
+  filtopt.name = "QueryType";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "ranked";
+  filtopt.validValues.push_back("boolean");
+  filtopt.validValues.push_back("ranked");
+  filterOptions["QueryType"] = filtopt;
+
+  // -- onePerQuery MatchMode      enumerated (some, all)
+  filtopt.clear();
+  filtopt.name = "MatchMode";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "some";
+  filtopt.validValues.push_back("some");
+  filtopt.validValues.push_back("all");
+  filterOptions["MatchMode"] = filtopt;
+
+  // -- onePerTerm  Term           string ???
+  filtopt.clear();
+  filtopt.name = "Term";
+  filtopt.type = FilterOption_t::stringt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["Term"] = filtopt;
+
+  // -- onePerTerm  Casefold       boolean
+  filtopt.clear();
+  filtopt.name = "Casefold";
+  filtopt.type = FilterOption_t::booleant;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "true";
+  filtopt.validValues.push_back("false");
+  filtopt.validValues.push_back("true");
+  filterOptions["Casefold"] = filtopt;
+
+  // -- onePerTerm  Stem           boolean
+  filtopt.clear();
+  filtopt.name = "Stem";
+  filtopt.type = FilterOption_t::booleant;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "false";
+  filtopt.validValues.push_back("false");
+  filtopt.validValues.push_back("true");
+  filterOptions["Stem"] = filtopt;
+
+  // -- onePerTerm  AccentFold           boolean
+  filtopt.clear();
+  filtopt.name = "AccentFold";
+  filtopt.type = FilterOption_t::booleant;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "false";
+  filtopt.validValues.push_back("false");
+  filtopt.validValues.push_back("true");
+  filterOptions["AccentFold"] = filtopt;
+  
+  // -- onePerTerm  Index          enumerated
+  filtopt.clear();
+  filtopt.name = "Index";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["Index"] = filtopt;
+
+  // -- onePerTerm  Subcollection  enumerated
+  filtopt.clear();
+  filtopt.name = "Subcollection";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["Subcollection"] = filtopt;
+
+  // -- onePerTerm  Language  enumerated
+  filtopt.clear();
+  filtopt.name = "Language";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerTerm;
+  filtopt.defaultValue = "";
+  filterOptions["Language"] = filtopt;
+
+  // -- onePerQuery  Maxdocs  integer
+  filtopt.clear();
+  filtopt.name = "Maxdocs";
+  filtopt.type = FilterOption_t::integert;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "200";
+  filtopt.validValues.push_back("-1");
+  filtopt.validValues.push_back("1000");
+  filterOptions["Maxdocs"] = filtopt;
+
+  // -- onePerQuery  PhraseMatch  enumerated
+  filtopt.clear();
+  filtopt.name = "PhraseMatch";
+  filtopt.type = FilterOption_t::enumeratedt;
+  filtopt.repeatable = FilterOption_t::onePerQuery;
+  filtopt.defaultValue = "some_phrases";
+  filtopt.validValues.push_back ("all_phrases");
+  filtopt.validValues.push_back ("some_phrases");
+  filtopt.validValues.push_back ("all_docs");
+  filterOptions["PhraseMatch"] = filtopt;
+}
+
+queryfilterclass::~queryfilterclass () {
+  // don't delete db_ptr or textsearchptr here, they'll be cleaned up by the source
+}
+
+void queryfilterclass::configure (const text_t &key, const text_tarray &cfgline) {
+  filterclass::configure (key, cfgline);
+
+  if (key == "indexmap") {
+    indexmap.importmap (cfgline);
+    
+    // update the list of indexes in the filter information
+    text_tarray options;
+    indexmap.gettoarray (options);
+    filterOptions["Index"].validValues = options;
+
+  } else if (key == "defaultindex") {
+    indexmap.from2to (cfgline[0], filterOptions["Index"].defaultValue);
+
+  } else if (key == "subcollectionmap") {
+    subcollectionmap.importmap (cfgline);
+
+    // update the list of subcollections in the filter information
+    text_tarray options;
+    subcollectionmap.gettoarray (options);
+    filterOptions["Subcollection"].validValues = options;
+
+  } else if (key == "defaultsubcollection") {
+    subcollectionmap.from2to (cfgline[0], filterOptions["Subcollection"].defaultValue);
+
+  } else if (key == "languagemap") {
+    languagemap.importmap (cfgline);
+
+    // update the list of languages in the filter information
+    text_tarray options;
+    languagemap.gettoarray (options);
+    filterOptions["Language"].validValues = options;
+
+  } else if (key == "defaultlanguage") {
+    languagemap.from2to (cfgline[0], filterOptions["Language"].defaultValue);
+  } else if (key == "indexstem") {
+    indexstem = cfgline[0];
+  } else if (key == "maxnumeric") {
+    maxnumeric = cfgline[0].getint();
+  }
+  
+}
+
+bool queryfilterclass::init (ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  if (!filterclass::init(logout)) return false;
+
+  if (filterOptions["Index"].defaultValue.empty()) {
+    // use first index in map as default if no default is set explicitly
+    text_tarray fromarray;
+    indexmap.getfromarray(fromarray);
+    if (fromarray.size()) {
+      filterOptions["Index"].defaultValue = fromarray[0];
+    }
+  }
+
+  if (filterOptions["Subcollection"].defaultValue.empty()) {
+    // use first subcollection in map as default if no default is set explicitly
+    text_tarray fromarray;
+    subcollectionmap.getfromarray(fromarray);
+    if (fromarray.size()) {
+      filterOptions["Subcollection"].defaultValue = fromarray[0];
+    }
+  }
+
+  if (filterOptions["Language"].defaultValue.empty()) {
+    // use first language in map as default if no default is set explicitly
+    text_tarray fromarray;
+    languagemap.getfromarray(fromarray);
+    if (fromarray.size()) {
+      filterOptions["Language"].defaultValue = fromarray[0];
+    }
+  }
+
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null dbclass\n\n";
+    return false;
+  }
+
+  // get the filename for the database and make sure it exists
+  if (indexstem.empty()) {
+    indexstem = collection;
+  }
+  db_filename = resolve_db_filename(indexstem,db_ptr->getfileextension());
+  if (!file_exists(db_filename)) {
+    logout << text_t2ascii
+	   << "warning: database \"" << db_filename << "\" does not exist\n\n";
+    //return false;
+  }
+
+  return true;
+}
+
Index: /gsdl/trunk/gs2runtime/src/colservr/queryfilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/queryfilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/queryfilter.h	(revision 16561)
@@ -0,0 +1,100 @@
+/**********************************************************************
+ *
+ * queryfilter.h -- abstract base class for queryfilters
+ *          mgqueryfilter, mgppqueryfilter and lucenequeryfilter 
+ *          implement this
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef QUERYFILTER_H
+#define QUERYFILTER_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+#include "filter.h"
+#include "dbclass.h"
+#include "maptools.h"
+#include "search.h"
+#include "queryinfo.h"
+
+
+class queryfilterclass : public filterclass {
+protected:
+
+  stringmap indexmap;
+  stringmap subcollectionmap;
+  stringmap languagemap;
+
+  text_t db_filename;
+  text_t indexstem;
+  dbclass *db_ptr;
+  
+  searchclass *textsearchptr;
+
+  int maxnumeric;
+
+   // get the query parameters
+  void parse_query_params (const FilterRequest_t &request,
+			   vector<queryparamclass> &query_params,
+			   int &startresults, int &endresults,
+			   text_t &phrasematch, ostream &logout);
+
+  // do query that might involve multiple sub queries
+  // textsearchptr and db_ptr are assumed to be valid
+  virtual void do_multi_query (const FilterRequest_t &request,
+		       const vector<queryparamclass> &query_params,
+		       queryresultsclass &multiresults,
+		       comerror_t &err, ostream &logout)=0;
+
+public:
+  queryfilterclass ();
+  virtual ~queryfilterclass ();
+
+  // the db_ptr remains the responsibility of the calling code
+  void set_db_ptr (dbclass *db_ptr_arg) { db_ptr = db_ptr_arg; }
+
+  // the testsearchptr remains the responsability of the calling code
+  void set_textsearchptr (searchclass *thetextsearchptr) {textsearchptr=thetextsearchptr;}
+
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+  virtual bool init (ostream &logout);
+
+  text_t get_filter_name () {return "QueryFilter";}
+  bool translate(dbclass *db_ptr, text_t& docnum, text_t &trans_OID);
+  bool need_matching_docs (int filterResultOptions);
+  bool need_term_info (int filterResultOptions);
+
+
+  virtual void filter (const FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout)=0;
+
+
+
+};
+
+
+#endif
+
Index: /gsdl/trunk/gs2runtime/src/colservr/queryinfo.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/queryinfo.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/queryinfo.cpp	(revision 16561)
@@ -0,0 +1,427 @@
+/**********************************************************************
+ *
+ * queryinfo.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.
+ *
+ *********************************************************************/
+
+#include "queryinfo.h"
+
+
+// query parameters
+
+queryparamclass::queryparamclass () {
+  clear ();
+}
+
+void queryparamclass::clear () {
+  combinequery.clear();
+  collection.clear();
+  index.clear();
+  subcollection.clear();
+  language.clear();
+  level.clear();
+  querystring.clear();
+  search_type = 0; // 0 = boolean, 1 = ranked
+  match_mode = 0; // 0 = some, 1 = all
+  casefolding = 0;
+  stemming = 0;
+  accentfolding = 0;
+  maxdocs = -1;    // all
+  maxnumeric = 4;  // must default to the same value as mg_passes
+  filterstring.clear();
+  sortfield.clear();
+  fuzziness.clear();
+  startresults = 1; // all
+  endresults = 10; // all
+}
+
+
+queryparamclass &queryparamclass::operator=(const queryparamclass &q) {
+  combinequery = q.combinequery;
+  collection = q.collection;
+  index = q.index;
+  subcollection = q.subcollection;
+  language = q.language;
+  level = q.level;
+  querystring = q.querystring;
+  search_type = q.search_type;
+  match_mode = q.match_mode;
+  casefolding = q.casefolding;
+  stemming = q.stemming;
+  accentfolding = q.accentfolding;
+  maxdocs = q.maxdocs;
+  maxnumeric = q.maxnumeric;
+  filterstring = q.filterstring;
+  sortfield = q.sortfield;
+  fuzziness = q.fuzziness;
+  startresults = q.startresults;
+  endresults = q.endresults;
+  return *this;
+}
+
+
+bool operator==(const queryparamclass &x, const queryparamclass &y) {
+  return ((x.combinequery == y.combinequery) &&
+	  (x.collection == y.collection) &&
+	  (x.index == y.index) &&
+	  (x.subcollection == y.subcollection) &&
+	  (x.language == y.language) &&
+	  (x.level == y.level) &&
+	  (x.querystring == y.querystring) &&
+	  (x.search_type == y.search_type) &&
+	  (x.match_mode == y.match_mode) &&
+	  (x.casefolding == y.casefolding) &&
+	  (x.stemming == y.stemming) &&
+	  (x.accentfolding == y.accentfolding) &&
+	  (x.maxdocs == y.maxdocs) &&
+	  (x.maxnumeric == y.maxnumeric) &&
+	  (x.filterstring == y.filterstring) &&
+          (x.sortfield == y.sortfield) &&
+          (x.fuzziness == y.fuzziness) &&
+          (x.startresults == y.startresults) &&
+          (x.startresults == y.startresults));
+}
+
+bool operator!=(const queryparamclass &x, const queryparamclass &y) {
+  return !(x == y);
+}
+
+
+ostream &operator<< (ostream &outs, queryparamclass &q) {
+  outconvertclass text_t2ascii;
+
+  outs << "*** queryparamclass\n";
+  outs << text_t2ascii << " combinequery = \"" << q.combinequery << "\"\n";
+  outs << text_t2ascii << " collection = \"" << q.collection << "\"\n";
+  outs << text_t2ascii << " index = \"" << q.index << "\"\n";
+  outs << text_t2ascii << " level = \"" << q.level << "\"\n";
+  outs << text_t2ascii << " subcollection = \"" << q.subcollection << "\"\n";
+  outs << text_t2ascii << " language = \"" << q.language << "\"\n";
+  outs << text_t2ascii << " querystring = \"" << q.querystring << "\"\n";
+  outs << " search_type = \"" << q.search_type << "\"\n";
+  outs << " match_mode = \"" << q.match_mode << "\"\n";
+  outs << " casefolding = \"" << q.casefolding << "\"\n";
+  outs << " stemming = \"" << q.stemming << "\"\n";
+  outs << " accentfolding = \"" << q.accentfolding << "\"\n";
+  outs << " maxdocs = \"" << q.maxdocs << "\"\n";
+  outs << " maxnumeric = \"" << q.maxnumeric << "\"\n";
+  outs << " filterstring = \"" << q.filterstring << "\"\n";
+  outs << " sortfield = \"" << q.sortfield << "\"\n";
+  outs << " fuzziness = \"" << q.fuzziness << "\"\n";
+  outs << " startresults = \"" << q.startresults << "\"\n";
+  outs << " endresults = \"" << q.endresults << "\"\n";
+  outs << "\n";
+
+  return outs;
+}
+
+
+
+
+// term frequencies
+
+termfreqclass::termfreqclass () {
+  clear();
+}
+
+void termfreqclass::clear() {
+  termstr.clear();
+  termstemstr.clear();
+  utf8equivterms.erase(utf8equivterms.begin(), utf8equivterms.end());
+  termfreq = 0;
+}
+
+termfreqclass &termfreqclass::operator=(const termfreqclass &t)  {
+  termstr = t.termstr;
+  termstemstr = t.termstemstr;
+  utf8equivterms = t.utf8equivterms;
+  termfreq = t.termfreq;
+
+  return *this;
+}
+	
+bool operator==(const termfreqclass &x, const termfreqclass &y) {
+  return ((x.termstr == y.termstr) &&
+	  (x.termstemstr == y.termstemstr) &&
+	  (x.termfreq == y.termfreq));
+}
+
+bool operator!=(const termfreqclass &x, const termfreqclass &y) {
+  return !(x == y);
+}
+
+// ordered by termfreq and then by termstr
+bool operator<(const termfreqclass &x, const termfreqclass &y) {
+  return ((x.termfreq < y.termfreq) ||
+	  ((x.termfreq == y.termfreq) && (x.termstemstr < y.termstemstr)) ||
+	  ((x.termfreq == y.termfreq) && (x.termstemstr == y.termstemstr) && (x.termstr < y.termstr)));
+}
+
+bool operator>(const termfreqclass &x, const termfreqclass &y) {
+  return ((x.termfreq > y.termfreq) ||
+	  ((x.termfreq == y.termfreq) && (x.termstemstr > y.termstemstr)) ||
+	  ((x.termfreq == y.termfreq) && (x.termstemstr == y.termstemstr) && (x.termstr > y.termstr)));
+}
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, termfreqclass &t) {
+  outconvertclass text_t2ascii;
+
+  outs << text_t2ascii << " t:\"" << t.termstr << "\"";
+  outs << text_t2ascii << " s:\"" << t.termstemstr << "\"";
+  outs << " f:" << t.termfreq << "\n";
+
+  return outs;
+}
+
+
+
+// one query result
+
+docresultclass::docresultclass() {
+  clear ();
+}
+
+void docresultclass::clear () {
+  docnum=-1;
+  docweight=0.0;
+  num_query_terms_matched=0;
+  num_phrase_match=0;
+}
+
+// merges two result classes relating to a single docnum
+docresultclass &docresultclass::combine(const docresultclass &d) {
+  docweight += d.docweight; // budget!
+  num_query_terms_matched += d.num_query_terms_matched;
+  num_phrase_match += d.num_phrase_match;
+
+  return *this;
+}
+
+docresultclass &docresultclass::operator=(const docresultclass &d) {
+  docnum = d.docnum;
+  docweight = d.docweight;
+  num_query_terms_matched = d.num_query_terms_matched;
+  num_phrase_match = d.num_phrase_match;
+
+  return *this;
+}
+
+
+bool operator==(const docresultclass &x, const docresultclass &y) {
+  return ((x.docnum == y.docnum) && (x.docweight == y.docweight) &&
+	  (x.num_query_terms_matched == y.num_query_terms_matched) &&
+	  (x.num_phrase_match == y.num_phrase_match));
+}
+
+bool operator<(const docresultclass &x, const docresultclass &y) {
+  return ((x.docnum < y.docnum) ||
+	  ((x.docnum == y.docnum) &&
+	   ((x.docweight < y.docweight) ||
+	    ((x.docweight == y.docweight) &&
+	     ((x.num_query_terms_matched < y.num_query_terms_matched) ||
+	      ((x.num_query_terms_matched == y.num_query_terms_matched) &&
+	       ((x.num_phrase_match < y.num_phrase_match))))))));
+}
+
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, docresultclass &a) {
+  outs << " d:" << a.docnum << " w:" << a.docweight << "\n";
+  return outs;
+}
+
+
+
+// many document results
+
+docresultsclass::docresultsclass () {
+  clear ();
+}
+
+void docresultsclass::clear () {
+  docset.erase(docset.begin(), docset.end());
+  docorder.erase(docorder.begin(), docorder.end());
+}
+
+void docresultsclass::docnum_order() {
+  docorder.erase(docorder.begin(), docorder.end());
+  
+  docresultmap::iterator here = docset.begin();
+  docresultmap::iterator end = docset.end();
+  while (here != end) {
+    docorder.push_back ((*here).first);
+    ++here;
+  }
+}
+
+void docresultsclass::combine_and (const docresultsclass &d) {
+  docorder.erase(docorder.begin(), docorder.end());
+
+  // put the resulting set in tempresults
+  docresultmap tempresults;
+  
+  docresultmap::const_iterator d_here = d.docset.begin();
+  docresultmap::const_iterator d_end = d.docset.end();
+  docresultmap::iterator found = docset.end();
+  while (d_here != d_end) {
+    found = docset.find((*d_here).first);
+    if (found != docset.end()) {
+      (*found).second.combine ((*d_here).second);
+      tempresults[(*found).first] = (*found).second;
+    }
+    ++d_here;
+  }
+
+  // then copy it back to docset
+  docset = tempresults;
+}
+
+void docresultsclass::combine_or (const docresultsclass &d) {
+  docorder.erase(docorder.begin(), docorder.end());
+    
+  docresultmap::const_iterator d_here = d.docset.begin();
+  docresultmap::const_iterator d_end = d.docset.end();
+  docresultmap::iterator found = docset.end();
+  while (d_here != d_end) {
+    found = docset.find((*d_here).first);
+    if (found != docset.end()) {
+      (*found).second.combine ((*d_here).second);
+    } else {
+      docset[(*d_here).first] = (*d_here).second;
+    }
+    ++d_here;
+  }
+}
+
+void docresultsclass::combine_not (const docresultsclass &d) {
+  docorder.erase(docorder.begin(), docorder.end());
+
+ docresultmap::const_iterator d_here = d.docset.begin();
+ docresultmap::const_iterator d_end = d.docset.end();
+ docresultmap::iterator found = docset.end();
+ while (d_here != d_end) {
+   found = docset.find((*d_here).first);
+   if (found != docset.end()) docset.erase (found);
+   ++d_here;
+ }
+}
+
+docresultsclass &docresultsclass::operator=(const docresultsclass &d) {
+  docset = d.docset;
+  docorder = d.docorder;
+
+  return *this;
+}
+
+
+
+
+// query results
+
+void queryresultsclass::clear () {
+  error_message = g_EmptyText;
+  docs_matched = 0;
+  is_approx = Exact;
+  syntax_error = false;
+  postprocessed = false;
+
+  docs.clear();
+  orgterms.erase(orgterms.begin(),orgterms.end());
+  terms.erase(terms.begin(),terms.end());
+}
+
+queryresultsclass &queryresultsclass::operator=(const queryresultsclass &q) {
+  error_message = q.error_message;
+  docs_matched = q.docs_matched;
+  is_approx = q.is_approx;
+  syntax_error = q.syntax_error;
+  postprocessed = q.postprocessed;
+
+  docs = q.docs;
+  terms = q.terms;
+  termvariants = q.termvariants;
+
+  return *this;
+}
+
+void queryresultsclass::sortuniqqueryterms() {
+  termfreqclassarray tempterms = orgterms;
+  text_tset seenterms;
+  terms.erase(terms.begin(), terms.end());
+
+  // sort the terms to get the frequencies in ascending order
+  sort (tempterms.begin(), tempterms.end());
+
+  // insert first occurance of each term (maximum)
+  termfreqclassarray::reverse_iterator here = tempterms.rbegin();
+  termfreqclassarray::reverse_iterator end = tempterms.rend();
+  while (here != end) {
+    if (seenterms.find((*here).termstr) == seenterms.end()) {
+      // the termstemstr and utf8equivterms might be different for
+      // different occurances of the term
+      (*here).termstemstr.clear();
+      (*here).utf8equivterms.erase((*here).utf8equivterms.begin(),
+				   (*here).utf8equivterms.end());
+      terms.push_back(*here);
+      seenterms.insert((*here).termstr);
+    }
+    ++here;
+  }
+
+  // now re-sort in ascending order
+  sort (terms.begin(), terms.end());
+}
+
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, queryresultsclass &q) {
+  outs << "*** queryresultsclass\n";
+  outs << "docs\n";
+
+  docresultmap::iterator docshere = q.docs.docset.begin();
+  docresultmap::iterator docsend = q.docs.docset.end();
+  while (docshere != docsend) {
+    outs << (*docshere).second;
+    ++docshere;
+  }
+
+  outs << "orgterms\n";
+  termfreqclassarray::iterator orgtermshere = q.orgterms.begin();
+  termfreqclassarray::iterator orgtermsend = q.orgterms.end();
+  while (orgtermshere != orgtermsend) {
+    outs << (*orgtermshere);
+    ++orgtermshere;
+  }
+
+  outs << "terms\n";
+  termfreqclassarray::iterator termshere = q.terms.begin();
+  termfreqclassarray::iterator termsend = q.terms.end();
+  while (termshere != termsend) {
+    outs << (*termshere);
+    ++termshere;
+  }
+
+  outs << "\n";
+
+  return outs;
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/queryinfo.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/queryinfo.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/queryinfo.h	(revision 16561)
@@ -0,0 +1,197 @@
+/**********************************************************************
+ *
+ * queryinfo.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.
+ *
+ *********************************************************************/
+
+
+#ifndef QUERYINFO_H
+#define QUERYINFO_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#  include <ospace\std\algorithm>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#  if defined(GSDL_USE_ALGO_H)
+#    include <algo.h>
+#  else
+#    include <algorithm.h>
+#  endif
+#else
+#  include <vector>
+#  include <algorithm>
+#endif
+
+// query parameters
+
+struct queryparamclass {
+  text_t combinequery;
+  text_t collection;
+
+  // search_index = index+subcollection+language
+  text_t index;
+  text_t subcollection;
+  text_t language;
+  text_t level; // for new mg stuff
+  text_t querystring;
+  int search_type; // 0 = boolean, 1 = ranked
+  int match_mode; // 0 = some, 1 = all
+  int casefolding;
+  int stemming;
+  int accentfolding;
+  int maxdocs;
+  int maxnumeric;
+  text_t filterstring; // Filter specified (currently only used by Lucene)
+  text_t sortfield; // Field to use for sorting result set (currently used by lucene)
+  text_t fuzziness; // Search fuzziness amount between 0.0 and 1.0 (only used by Lucene)
+
+  int startresults;
+  int endresults;
+
+  queryparamclass ();
+  void clear ();
+  queryparamclass &operator=(const queryparamclass &q);
+};
+
+bool operator==(const queryparamclass &x, const queryparamclass &y);
+bool operator!=(const queryparamclass &x, const queryparamclass &y);
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, queryparamclass &q);
+
+
+
+// term frequencies
+
+struct termfreqclass {
+  text_t termstr;
+  text_t termstemstr;
+  text_tset utf8equivterms; // kept as utf8 string for fast matching
+  unsigned int termfreq;
+
+  termfreqclass ();
+  void clear();
+  termfreqclass &operator=(const termfreqclass &t);
+};
+
+typedef vector<termfreqclass> termfreqclassarray;
+
+bool operator==(const termfreqclass &x, const termfreqclass &y);
+bool operator!=(const termfreqclass &x, const termfreqclass &y);
+bool operator<(const termfreqclass &x, const termfreqclass &y);
+bool operator>(const termfreqclass &x, const termfreqclass &y);
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, termfreqclass &q);
+
+
+
+// one query result
+
+struct docresultclass {
+  int docnum;
+  float docweight;
+  unsigned int num_query_terms_matched;  // not available on all versions of mg
+  int num_phrase_match;                  // not available on all versions of mg
+
+  docresultclass();
+  ~docresultclass() {}
+  void clear ();
+  
+  // merges two result classes relating to a single docnum
+  docresultclass &combine(const docresultclass &d);
+
+  docresultclass &operator=(const docresultclass &d);
+};
+
+bool operator==(const docresultclass &x, const docresultclass &y);
+bool operator<(const docresultclass &x, const docresultclass &y);
+
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, docresultclass &a);
+
+
+struct ltint {
+  bool operator()(const int &t1, const int &t2) const
+  { return t1 < t2; }
+};
+
+typedef map<text_t, docresultclass, lttext_t> docresultmap;
+
+
+
+// many document results
+
+struct docresultsclass {
+  docresultmap docset;
+  vector<text_t> docorder;
+
+  docresultsclass ();
+  void clear ();
+  void docnum_order();
+
+  void combine_and (const docresultsclass &d);
+  void combine_or (const docresultsclass &d);
+  void combine_not (const docresultsclass &d);
+  
+  docresultsclass &operator=(const docresultsclass &d);
+};
+
+
+
+
+// query results
+
+struct queryresultsclass {
+  queryresultsclass () {clear();}
+
+  text_t error_message;  // Currently only used by Lucene
+  int docs_matched; // not available on all versions of mg
+  isapprox is_approx;
+  //  bool is_approx;   // not available on all versions of mg
+  bool syntax_error; // whether the query string was invalid
+  bool postprocessed; // whether this record has been post-processed
+  
+  docresultsclass docs;
+  termfreqclassarray orgterms; // terms before they are sorted and uniqued
+  termfreqclassarray terms;
+  text_tset termvariants;
+  text_tset stopwords;
+
+  void clear ();
+  queryresultsclass &operator=(const queryresultsclass &q);
+  
+  void sortuniqqueryterms();
+};
+
+// stream output for debugging purposes
+ostream &operator<< (ostream &outs, queryresultsclass &q);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/search.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/search.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/search.cpp	(revision 16561)
@@ -0,0 +1,53 @@
+/**********************************************************************
+ *
+ * search.cpp -- abstract search class for mg
+ * 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.
+ *
+ *********************************************************************/
+
+#include "search.h"
+
+////////////////////
+// search class //
+////////////////////
+
+searchclass::searchclass ()
+{
+  cache = new querycache (RESULTCACHESIZE);
+ 
+}
+
+searchclass::~searchclass () 
+{
+  if (cache != NULL) 
+    {
+      delete cache;
+      cache = NULL;
+    }
+}
+
+
+void searchclass::setcollectdir (const text_t &thecollectdir)
+{
+  collectdir = thecollectdir;
+}
+
+
Index: /gsdl/trunk/gs2runtime/src/colservr/search.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/search.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/search.h	(revision 16561)
@@ -0,0 +1,80 @@
+/**********************************************************************
+ *
+ * search.h -- abstract search class for mg systems
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef SEARCH_H
+#define SEARCH_H
+
+#include "text_t.h"
+#include "querycache.h"
+
+#define RESULTCACHESIZE 10
+#define MAXNUMDOCS      1000000
+#define MAXNUMTERMS     100
+
+class searchclass {
+public:
+  searchclass ();
+  virtual ~searchclass ();
+
+  // in subclass, should be over-rided to return "mg" or "mgpp"
+  virtual text_t get_textindex_type()=0;
+  // the index directory must be set before any searching
+  // is done
+  virtual void setcollectdir (const text_t &thecollectdir);
+  
+  // the search results are returned in queryresults
+  // search returns 'true' if it was able to do a search
+   virtual bool search(const queryparamclass &queryparams, 
+	      queryresultsclass &queryresults)=0;
+  
+  // the document text for 'docnum' is placed in 'output'
+  // docTargetDocument returns 'true' if it was able to
+  // try to get a document
+  // collection is needed to see if an index from the 
+  // collection is loaded. If no index has been loaded
+  // defaultindex is needed to load one
+  virtual bool docTargetDocument(const text_t &defaultindex,
+			 const text_t &defaultsubcollection,
+			 const text_t &defaultlanguage,
+			 const text_t &collection,
+			 int docnum,
+			 text_t &output)=0;
+
+  // unload_database clears any cached databases - 
+  // this is useful when attempting to
+  // completely remove all trace of a collectionserver at runtime (when
+  // using a persistent version of Greenstone like the windows local
+  // library)
+  virtual void unload_database ()=0;
+
+protected:
+  querycache *cache;
+  text_t collectdir; // the collection directory
+
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/source.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/source.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/source.cpp	(revision 16561)
@@ -0,0 +1,456 @@
+/**********************************************************************
+ *
+ * source.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.
+ *
+ *********************************************************************/
+
+#include "source.h"
+#include "fileutil.h"
+#include "OIDtools.h"
+#include <assert.h>
+
+
+sourceclass::sourceclass ()
+{
+  db_ptr = NULL;
+  textsearchptr = NULL;
+  classname = "source";
+}
+
+sourceclass::~sourceclass ()
+{
+  if (db_ptr != NULL) delete db_ptr;
+  if (textsearchptr != NULL) delete textsearchptr;
+}
+
+// configure should be called once for each configuration line
+void sourceclass::configure (const text_t &key, const text_tarray &cfgline)
+{
+  if (cfgline.size() >= 1) {
+    const text_t &value = cfgline[0];
+
+    if (key == "collection") collection = value;
+    else if (key == "collectdir") collectdir = value;
+    else if (key == "gsdlhome") gsdlhome = value;
+    else if (key == "collecthome") collecthome = value;
+    else if (key == "gdbmhome") dbhome = value;
+  }
+
+  if (key == "indexmap") {
+    indexmap.importmap (cfgline);
+
+  } else if (key == "defaultindex") {
+    indexmap.from2to (cfgline[0], defaultindex);
+
+  } else if (key == "subcollectionmap") {
+    subcollectionmap.importmap (cfgline);
+
+  } else if (key == "defaultsubcollection") {
+    subcollectionmap.from2to (cfgline[0], defaultsubcollection);
+
+  } else if (key == "languagemap") {
+    languagemap.importmap (cfgline);
+
+  } else if (key == "defaultlanguage") {
+    languagemap.from2to (cfgline[0], defaultlanguage);
+  } else if (key == "indexstem") {
+    indexstem = cfgline[0];
+  }
+}
+
+text_t sourceclass::resolve_db_filename(const text_t& idx,
+					const text_t& file_ext)
+{
+  // This is an exact copy of the method (of the same name) in filterclass
+  // Makes sense to merge them, in which either gsdlhome, collecthome,
+  // dbhome, and collection need to also be passed in as parameters,
+  // or else there is some notion of a shared base class that both
+  // filter and source inherit from
+
+  // NB: there is an even greater opportunity to share more code in this
+  // function if sql_db_ptr/db_ptr and db_filename and sql_db_filename
+  // are also drawn from one object
+
+  text_t resolved_filename;
+
+  if (gsdlhome==dbhome) {
+    // dbhome has defaulted to gsdlhome which we take to means the 
+    // database has been specifically moved out of gsdlhome area.
+    // => it should be whereever collecthome is set to
+
+    resolved_filename = filename_cat(collecthome, collection, "index", "text", idx);
+  }
+  else {
+    // dbhome is explicitly set to something other than gsdlhome
+    // => use dbhome
+    resolved_filename = filename_cat(dbhome, "collect", collection, "index", "text", idx);
+  }
+
+  resolved_filename += file_ext;
+
+  return resolved_filename;
+}
+
+
+
+// init should be called after all the configuration is done but
+// before any other methods are called
+bool sourceclass::init (ostream &logout)
+{
+  outconvertclass text_t2ascii;
+  
+  if (collecthome.empty()) collecthome = filename_cat(gsdlhome,"collect");
+  if (dbhome.empty()) dbhome = gsdlhome;
+
+  if (defaultindex.empty()) {
+    // use first index in map as default if no default is set explicitly
+    text_tarray toarray;
+    indexmap.gettoarray(toarray);
+    if (toarray.size()) {
+      defaultindex = toarray[0];
+    }
+  }
+
+  if (defaultsubcollection.empty()) {
+    // use first subcollection in map as default if no default is set explicitly
+    text_tarray toarray;
+    subcollectionmap.gettoarray(toarray);
+    if (toarray.size()) {
+      defaultsubcollection = toarray[0];
+    }
+  }
+
+  if (defaultlanguage.empty()) {
+    // use first language in map as default if no default is set explicitly
+    text_tarray toarray;
+    languagemap.gettoarray(toarray);
+    if (toarray.size()) {
+      defaultlanguage = toarray[0];
+    }
+  }
+  
+  // get the collection directory name
+  if (collectdir.empty()) {
+    collectdir = filename_cat (collecthome, collection);
+  }
+
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: queryfilter contains a null dbclass\n\n";
+    return false;
+  }
+
+  // get the filename for the database and make sure it exists
+  if (indexstem.empty()) {
+    indexstem = collection;
+  }
+  db_filename = resolve_db_filename(indexstem, db_ptr->getfileextension());
+  if (!file_exists(db_filename)) {
+    logout << text_t2ascii
+	   << "warning: database \"" << db_filename << "\" does not exist\n\n";
+    // return false;
+  }
+  
+  return true;
+}
+
+
+// translate_OID translates OIDs using ".pr", ."fc" etc.
+bool sourceclass::translate_OID (const text_t &OIDin, text_t &OIDout, 
+				 comerror_t &err, ostream &logout)
+{
+  outconvertclass text_t2ascii;
+
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: " << classname << " contains a null dbclass\n\n";
+    err = configurationError;
+    return true;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return true;
+  }
+
+  infodbclass info;
+  OIDout = db_ptr->translate_OID (OIDin, info);
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  return true;
+}
+
+
+// get_metadata fills out the metadata if possible, if it is not responsable
+// for the given OID then it will return false.
+bool sourceclass::get_metadata (const text_t &requestParams, const text_t &refParams, 
+				bool getParents, const text_tset &fields, 
+				const text_t &OID, MetadataInfo_tmap &metadata, 
+				comerror_t &err, ostream &logout)
+{
+  outconvertclass text_t2ascii;
+
+  metadata.erase(metadata.begin(), metadata.end());
+
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: " << classname <<" contains a null dbclass\n\n";
+    err = configurationError;
+    return true;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return true;
+  }
+
+  // get the metadata - if getParents is set we need to get 
+  // info for all parents of OID as well as OID
+  vector<infodbclass> info_array;
+  text_tarray OIDs;
+  if (getParents) get_parents_array (OID, OIDs);
+  OIDs.push_back (OID);
+
+  text_tarray::const_iterator this_OID = OIDs.begin();
+  text_tarray::const_iterator end_OID = OIDs.end();
+
+  while (this_OID != end_OID) {
+    infodbclass info;
+    if (!db_ptr->getinfo(*this_OID, info)) return false;
+
+    // adjust the metadata
+    text_t &contains = info["contains"];
+    if (contains.empty()) info["haschildren"] = 0;
+    else info["haschildren"] = 1;
+    //contains.clear();
+
+    info_array.push_back(info);
+    ++this_OID;
+  }
+
+  // if fields set is empty we want to get all available metadata
+  text_tset tfields = fields;
+  if (tfields.empty() && !info_array.empty()) {
+    infodbclass::iterator t_info = info_array[0].begin(); 
+    infodbclass::iterator e_info = info_array[0].end(); 
+    while (t_info != e_info) {
+      if ((*t_info).first != "contains")
+	tfields.insert ((*t_info).first);
+      ++t_info;
+    }
+    tfields.insert ("hasnext");
+    tfields.insert ("hasprevious");
+  }
+    
+  // collect together the metadata
+  bool donenextprevtest = false;
+  bool hasnext=false, hasprevious=false;
+  MetadataInfo_t this_metadata;
+  text_tarray *pos_metadata;
+  text_tset::const_iterator fields_here = tfields.begin();
+  text_tset::const_iterator fields_end = tfields.end();
+
+  while (fields_here != fields_end) {
+    this_metadata.clear();
+    this_metadata.isRef = false;
+
+    vector<infodbclass>::reverse_iterator this_info = info_array.rbegin(); 
+    vector<infodbclass>::reverse_iterator end_info = info_array.rend(); 
+    MetadataInfo_t *tmetaptr = &this_metadata;
+    while (this_info != end_info) {
+
+      pos_metadata = (*this_info).getmultinfo(*fields_here);
+      if ((*fields_here == "hasnext" || *fields_here == "hasprevious")) {
+	
+	// collect metadata
+	if (!donenextprevtest) {
+	  donenextprevtest = true;
+	  
+	  // cache parent contents array
+	  text_t thisparent = get_parent (OID);
+	  if (!thisparent.empty()) {
+	    if (thisparent != parentOID) {
+	      parentOID = thisparent;
+	      parentcontents.erase(parentcontents.begin(), parentcontents.end());
+	      if (db_ptr->getinfo(parentOID, parentinfo)) {
+		text_t &parentinfocontains = parentinfo["contains"];
+		if (!parentinfocontains.empty())
+		  splitchar (parentinfocontains.begin(), parentinfocontains.end(), 
+			     ';', parentcontents);
+	      }
+	    }
+	    
+	    // do tests
+	    text_tarray::const_iterator parentcontents_here = parentcontents.begin();
+	    text_tarray::const_iterator parentcontents_end = parentcontents.end();
+	    text_t shrunk_OID = OID;
+	    shrink_parent (shrunk_OID);
+	    while (parentcontents_here != parentcontents_end) {
+	      if (*parentcontents_here == shrunk_OID) {
+		if (parentcontents_here == parentcontents.begin()) hasprevious = false;
+		else hasprevious = true;
+		
+		++parentcontents_here;
+		
+		if (parentcontents_here == parentcontents.end()) hasnext = false;
+		else hasnext = true;
+		
+		break;
+	      }  
+	      
+	      ++parentcontents_here;
+	    }
+	 
+	    // fill in metadata
+	    if ((*fields_here == "hasnext" && hasnext) ||
+		(*fields_here == "hasprevious" && hasprevious))
+	      tmetaptr->values.push_back("1");
+	    else
+	      tmetaptr->values.push_back("0");
+	  } else
+	    tmetaptr->values.push_back("0");
+	}
+      }
+      //else if (pos_metadata != NULL && *fields_here != "contains") {
+      else if (pos_metadata != NULL) {
+	tmetaptr->values = *pos_metadata;
+      }
+      else
+	tmetaptr->values.push_back("");
+
+      ++this_info;
+      if (this_info != end_info) {
+	tmetaptr->parent = new MetadataInfo_t();
+	tmetaptr = tmetaptr->parent;
+      }
+    }
+    metadata[*fields_here] = this_metadata;
+    ++fields_here;
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  return true;
+}
+
+bool sourceclass::get_document (const text_t &OID, text_t &doc, 
+				comerror_t &err, ostream &logout)
+{
+  outconvertclass text_t2ascii;
+
+  err = noError;
+  if (db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii 
+	   << "configuration error: " << classname << " contains a null dbclass\n\n";
+    err = configurationError;
+    return true;
+  }
+
+  // open the database
+  db_ptr->setlogout(&logout);
+  if (!db_ptr->opendatabase (db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii
+	   << "system problem: open on database \"" << db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return true;
+  }
+
+  text_t tOID = OID;
+  if (needs_translating (OID)) 
+    translate_OID (OID, tOID, err, logout);
+  infodbclass info;
+  if (!db_ptr->getinfo(tOID, info)) {
+    db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+    return false;
+  }
+  
+  if (info["hastxt"].getint() == 1) {
+    int docnum = info["docnum"].getint();
+    
+    // set the collection directory
+    textsearchptr->setcollectdir (collectdir);
+    
+    // get the text
+    textsearchptr->docTargetDocument(defaultindex, defaultsubcollection,
+				   defaultlanguage, collection, docnum, doc);
+  }
+
+  db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+  return true;
+}
+
+bool sourceclass::is_searchable(bool &issearchable, comerror_t &err, ostream &logout)
+{
+  err = noError;
+  issearchable = false;
+
+  text_tarray fromarray;
+  indexmap.getfromarray(fromarray);
+  if (fromarray.size() == 0) {
+    return true;
+  } else if (fromarray.size() == 1) {
+    if (fromarray[0] == "dummy:text") {
+      // always return true - issearchable is false here though
+      return true;
+    }
+  }
+  issearchable = true;
+  return true;
+}
+
+
+bool operator==(const sourceptr &x, const sourceptr &y) {
+  return (x.s == y.s);
+}
+
+bool operator<(const sourceptr &x, const sourceptr &y) {
+  return (x.s < y.s);
+}
+
+
+// thesource remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void sourcelistclass::addsource (sourceclass *thesource) {
+  // can't add a source that doesn't exist
+  assert (thesource != NULL);
+  if (thesource == NULL) return;
+
+  sourceptr sp;
+  sp.s = thesource;
+
+  sourceptrs.push_back(sp);
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/source.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/source.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/source.h	(revision 16561)
@@ -0,0 +1,164 @@
+/**********************************************************************
+ *
+ * source.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.
+ *
+ *********************************************************************/
+
+
+#ifndef SOURCE_H
+#define SOURCE_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "dbclass.h"
+#include "search.h"
+#include "comtypes.h"
+#include "maptools.h"
+
+
+class sourceclass {
+protected:
+  text_t classname;
+
+  text_t gsdlhome;
+  text_t collecthome;
+  text_t dbhome;
+  text_t collection;
+  text_t collectdir;
+
+  stringmap indexmap;
+  stringmap subcollectionmap;
+  stringmap languagemap;
+
+  text_t defaultindex;
+  text_t defaultsubcollection;
+  text_t defaultlanguage;
+
+  text_t indexstem;
+
+  text_t parentOID;
+  infodbclass parentinfo;
+  text_tarray parentcontents;
+
+  text_t db_filename;
+  dbclass *db_ptr;
+
+  searchclass *textsearchptr;
+
+public:
+  sourceclass ();
+  virtual ~sourceclass ();
+
+  // the DB ptr remains the responsibility of the calling code
+  void set_db_ptr (dbclass *db_ptr_arg) { db_ptr = db_ptr_arg; }
+
+  // the textsearchptr remains the responsibility of the calling code
+  void set_textsearchptr (searchclass *textsearchptr_arg) { textsearchptr = textsearchptr_arg; }
+
+  // configure should be called once for each configuration line
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // init should be called after all the configuration is done but
+  // before any other methods are called
+  virtual bool init (ostream &logout);
+
+  // translate_OID translates OIDs using ".pr", ."fc" etc.
+  virtual bool translate_OID (const text_t &OIDin, text_t &OIDout, 
+			      comerror_t &err, ostream &logout);
+
+  // get_metadata fills out the metadata if possible, if it is not responsable
+  // for the given OID then it will return false.
+  virtual bool get_metadata (const text_t &requestParams, const text_t &refParams, 
+			     bool getParents, const text_tset &fields,
+			     const text_t &OID, MetadataInfo_tmap &metadata, 
+			     comerror_t &err, ostream &logout);
+
+  virtual bool get_document (const text_t &OID, text_t &doc, 
+			     comerror_t &err, ostream &logout);
+
+  virtual bool is_searchable(bool &issearchable, comerror_t &err, ostream &logout);
+
+ protected:
+  text_t resolve_db_filename(const text_t& idx,
+			     const text_t& file_ext);
+
+};
+
+
+// The sourceptr class does not 'own' the source. The 
+// source should be deleted by the code which created it.
+class sourceptr {
+public:
+  sourceclass *s;
+
+  sourceptr () {s=NULL;}
+  ~sourceptr () {}
+};
+
+bool operator==(const sourceptr &x, const sourceptr &y);
+bool operator<(const sourceptr &x, const sourceptr &y);
+
+typedef vector<sourceptr> sourceptrlist;
+
+// contains a list of sources
+class sourcelistclass {
+protected:
+  sourceptrlist sourceptrs;
+
+public:
+  // type support for sourcelistclass
+  typedef sourceptrlist::iterator iterator;
+  typedef sourceptrlist::const_iterator const_iterator;
+  typedef sourceptrlist::reference reference;
+  typedef sourceptrlist::const_reference const_reference;
+  typedef sourceptrlist::size_type size_type;
+
+  typedef sourceptrlist::difference_type difference_type;
+  typedef sourceptrlist::const_reverse_iterator const_reverse_iterator;
+  typedef sourceptrlist::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return sourceptrs.begin();}
+  const_iterator begin () const {return sourceptrs.begin();}
+  iterator end () {return sourceptrs.end();}
+  const_iterator end () const {return sourceptrs.end();}
+
+  void erase(iterator pos) {sourceptrs.erase(pos);}
+  void erase(iterator first, iterator last) {sourceptrs.erase(first, last);}
+  sourcelistclass &operator=(const sourcelistclass &x) 
+    {sourceptrs=x.sourceptrs;return *this;}
+
+  bool empty () const {return sourceptrs.empty();}
+  size_type size() const {return sourceptrs.size();}
+
+
+  // added functionality
+  void clear () {sourceptrs.erase(sourceptrs.begin(),sourceptrs.end());}
+
+  // thesource remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addsource (sourceclass *thesource);
+};
+
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.cpp	(revision 16561)
@@ -0,0 +1,204 @@
+/**********************************************************************
+ *
+ * sqlbrowsefilter.cpp -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "sqlbrowsefilter.h"
+#include "fileutil.h"
+
+
+sqlbrowsefilterclass::sqlbrowsefilterclass ()
+{
+  sql_db_ptr = NULL;
+}
+
+
+sqlbrowsefilterclass::~sqlbrowsefilterclass ()
+{
+}
+
+
+void sqlbrowsefilterclass::configure (const text_t &key, const text_tarray &cfgline)
+{
+  filterclass::configure (key, cfgline);
+
+  if (key == "indexstem")
+  {
+    indexstem = cfgline[0];
+  }
+}
+
+
+bool sqlbrowsefilterclass::init (ostream &logout)
+{
+  outconvertclass text_t2ascii;
+
+  if (!filterclass::init(logout)) return false;
+
+  if (sql_db_ptr == NULL)
+  {
+    // most likely a configuration problem
+    logout << text_t2ascii << "configuration error: sqlbrowsefilter contains a null sqldbclass\n\n";
+    return false;
+  }
+
+  if (indexstem.empty())
+  {
+    indexstem = collection;
+  }
+
+  // get the filename for the database and make sure it exists
+  sql_db_filename = resolve_db_filename(indexstem,sql_db_ptr->getfileextension());
+  if (!file_exists(sql_db_filename))
+  {
+    logout << text_t2ascii << "warning: database \"" << sql_db_filename << "\" does not exist\n\n";
+    return false;
+  }
+
+  return true;
+}
+
+
+void sqlbrowsefilterclass::filter (const FilterRequest_t &request,
+				   FilterResponse_t &response,
+				   comerror_t &err, ostream &logout)
+{
+  outconvertclass text_t2ascii;
+
+  response.clear();
+  err = noError;
+
+  if (sql_db_ptr == NULL) {
+    // most likely a configuration problem
+    logout << text_t2ascii << "configuration error: sqlbrowsefilter contains a null sqldbclass\n\n";
+    err = configurationError;
+    return;
+  }
+
+  // open the database
+  sql_db_ptr->setlogout (&logout);
+  if (!sql_db_ptr->opendatabase (sql_db_filename, DB_READER, 100, false)) {
+    // most likely a system problem (we have already checked that the database exists)
+    logout << text_t2ascii << "system problem: open on database \"" << sql_db_filename << "\" failed\n\n";
+    err = systemProblem;
+    return;
+  }
+
+  // Request for the metadata values assigned to an element
+  if (request.requestParams == "GetMetadataValues")
+  {
+    text_tarray metadata_element_names;
+    text_t metadata_value_filter = "";
+    text_t metadata_value_grouping_expression = "";
+    OptionValue_tarray::const_iterator options_iterator = request.filterOptions.begin();
+    while (options_iterator != request.filterOptions.end())
+    {
+      if ((*options_iterator).name == "MetadataElements")
+      {
+	splitchar ((*options_iterator).value.begin(), (*options_iterator).value.end(), ',', metadata_element_names);
+      }
+      if ((*options_iterator).name == "MetadataValueFilter")
+      {
+	metadata_value_filter = (*options_iterator).value;
+      }
+      if ((*options_iterator).name == "MetadataValueGroupingExpression")
+      {
+	metadata_value_grouping_expression = (*options_iterator).value;
+      }
+      options_iterator++;
+    }
+
+    text_tarray metadata_values = sql_db_ptr->get_metadata_values (metadata_element_names, metadata_value_filter, metadata_value_grouping_expression);
+
+    // Create a map from metadata value to ResultDocInfo_t, to remove duplicate values and obtain occurrence counts
+    map<text_t, ResultDocInfo_t> unique_metadata_values_map;
+    text_tarray::iterator metadata_value_iterator = metadata_values.begin();
+    while (metadata_value_iterator != metadata_values.end())
+    {
+      text_t metadata_value = *metadata_value_iterator;
+
+      // If no ResultDocInfo_t has already been created for this metadata value, create one now
+      if (unique_metadata_values_map.find(metadata_value) == unique_metadata_values_map.end())
+      {
+	ResultDocInfo_t metadata_value_result_doc;
+	metadata_value_result_doc.OID = metadata_value;
+	metadata_value_result_doc.result_num = 1;
+	unique_metadata_values_map[metadata_value] = metadata_value_result_doc;
+      }
+      // Otherwise we've seen this value before, so just update the occurrence count
+      else
+      {
+	unique_metadata_values_map[metadata_value].result_num++;
+      }
+
+      metadata_value_iterator++;
+    }
+
+    // Fill in response.docInfo with the ResultDocInfo_t objects we've created above
+    map<text_t, ResultDocInfo_t>::iterator unique_metadata_values_iterator = unique_metadata_values_map.begin();
+    while (unique_metadata_values_iterator != unique_metadata_values_map.end())
+    {
+      response.docInfo.push_back ((*unique_metadata_values_iterator).second);
+      unique_metadata_values_iterator++;
+    }
+  }
+
+  // Request for the documents with a certain metadata value assigned
+  else if (request.requestParams == "GetDocumentsWithMetadataValue")
+  {
+    text_tarray metadata_element_names;
+    text_t metadata_value = "";
+    text_t sort_by_metadata_element_name = "";
+    OptionValue_tarray::const_iterator options_iterator = request.filterOptions.begin();
+    while (options_iterator != request.filterOptions.end())
+    {
+      if ((*options_iterator).name == "MetadataElements")
+      {
+	splitchar ((*options_iterator).value.begin(), (*options_iterator).value.end(), ',', metadata_element_names);
+      }
+      if ((*options_iterator).name == "MetadataValue")
+      {
+	metadata_value = (*options_iterator).value;
+      }
+      if ((*options_iterator).name == "SortByMetadataElement")
+      {
+	sort_by_metadata_element_name = (*options_iterator).value;
+      }
+      options_iterator++;
+    }
+
+    text_tarray document_OIDs = sql_db_ptr->get_documents_with_metadata_value (metadata_element_names, metadata_value, sort_by_metadata_element_name);
+
+    // Fill in response.docInfo with the document OIDs
+    text_tarray::iterator document_OID_iterator = document_OIDs.begin();
+    while (document_OID_iterator != document_OIDs.end())
+    {
+      ResultDocInfo_t document_result_doc;
+      document_result_doc.OID = *document_OID_iterator;
+      response.docInfo.push_back (document_result_doc);
+      document_OID_iterator++;
+    }
+  }
+
+  sql_db_ptr->closedatabase();  // Important that local library doesn't leave any files open
+}
Index: /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/sqlbrowsefilter.h	(revision 16561)
@@ -0,0 +1,63 @@
+/**********************************************************************
+ *
+ * sqlbrowsefilter.h -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef SQLBROWSEFILTER_H
+#define SQLBROWSEFILTER_H
+
+
+#include "filter.h"
+#include "comtypes.h"
+#include "sqldbclass.h"
+#include "text_t.h"
+
+
+class sqlbrowsefilterclass : public filterclass {
+protected:
+  text_t sql_db_filename;
+  text_t indexstem;
+  sqldbclass *sql_db_ptr;
+
+public:
+  sqlbrowsefilterclass ();
+  virtual ~sqlbrowsefilterclass ();
+
+  text_t get_filter_name () { return "SQLBrowseFilter"; }
+
+  // configure should be called once for each configuration line
+  // default configures the default filter options
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // the sql db ptr remains the responsibility of the calling code and
+  // should be destroyed after this sqlbrowsefilter is destroyed
+  void set_sql_db_ptr (sqldbclass *sql_db_ptr_arg) { sql_db_ptr = sql_db_ptr_arg; }
+
+  bool init (ostream &logout);
+  void filter (const FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/colservr/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/colservr/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/colservr/win32.mak	(revision 16561)
@@ -0,0 +1,160 @@
+###########################################################################
+#
+# win32 makefile -- src/colservr
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+
+
+GSDL_VC4 = 0
+DLL = 0
+DLLDEBUG = 0
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_DEFINES = -DUSE_SQLITE
+SQLITE_INCLUDES = -I"$(PACKAGES_DIR)\sqlite\include"
+!ELSE
+SQLITE_DEFINES = 
+SQLITE_INCLUDES = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE \
+        -DXML_STATIC $(SQLITE_DEFINES)
+# Do NOT add the "recpt" directory here: the colserver should be independent of the receptionist!
+INCLUDES = -I"$(GSDL_DIR)" -I"$(GSDL_DIR)\src\lib" \
+	-I"$(INDEXERS_DIR)\mg" -I"$(INDEXERS_DIR)\mg\lib" -I"$(INDEXERS_DIR)\mg\src\text" \
+	-I"$(INDEXERS_DIR)\mgpp\lib" -I"$(INDEXERS_DIR)\mgpp\text" \
+	-I"$(PACKAGES_DIR)\windows\expat\expat" -I"$(PACKAGES_DIR)\windows\gdbm\gdbm" $(SQLITE_INCLUDES)
+
+!IF $(GSDL_VC4)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+SOURCES = \
+        browsefilter.cpp \
+	collectserver.cpp \
+	collectset.cpp \
+        colservrconfig.cpp \
+	comtypes.cpp \
+	expat_document.cpp \
+	expat_resultset.cpp \
+	filter.cpp \
+	lucenequeryfilter.cpp \
+	lucenesearch.cpp \
+	lucenesource.cpp \
+	maptools.cpp \
+	mgq.c \
+	mgqueryfilter.cpp \
+	mgsearch.cpp \
+	mgsource.cpp \
+	mgppqueryfilter.cpp \
+	mgppsearch.cpp \
+	phrasequeryfilter.cpp \
+	phrasesearch.cpp \
+	querycache.cpp \
+	queryfilter.cpp \
+	queryinfo.cpp \
+	search.cpp \
+	source.cpp \
+	sqlbrowsefilter.cpp
+
+OBJECTS = \
+        browsefilter.obj \
+	collectserver.obj \
+	collectset.obj \
+        colservrconfig.obj \
+	comtypes.obj \
+	expat_document.obj \
+	expat_resultset.obj \
+	filter.obj \
+	lucenequeryfilter.obj \
+	lucenesearch.obj \
+	lucenesource.obj \
+	maptools.obj \
+	mgq.obj \
+	mgqueryfilter.obj \
+	mgsearch.obj \
+	mgsource.obj \
+	mgppqueryfilter.obj \
+	mgppsearch.obj \
+	phrasequeryfilter.obj \
+	phrasesearch.obj \
+	querycache.obj \
+	queryfilter.obj \
+	queryinfo.obj \
+	search.obj \
+	source.obj \
+	sqlbrowsefilter.obj
+
+LIBRARY = gsdlcolservr.lib	
+
+
+# Default target: make gsdlcolservr.lib
+all: $(LIBRARY)
+
+gsdlcolservr.lib: $(OBJECTS) 
+	if EXIST $(LIBRARY) del $(LIBRARY)
+	$(AR) -nologo /out:$(LIBRARY) $(OBJECTS)
+
+install:
+
+clean:
+	del *.obj
+	del $(LIBRARY)
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CURL.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CURL.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CURL.java	(revision 16561)
@@ -0,0 +1,677 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/** Examines html pages and extracts all the images and links */
+public class CURL {
+
+    private boolean url_valid = true;
+    private InputStream input = null;
+    private int peek_value = -1;
+    private String buffer = "";
+    private URL url = null;
+    private Vector href_links = null;
+    private Vector src_links = null;
+    private Vector link_links = null;
+    private Vector background_links = null;
+
+    /** Starts processing the given url for images and links
+     *  @param url_str The url to examine */
+    public CURL(String url_str) {
+	href_links = new Vector();
+	src_links = new Vector();
+	link_links = new Vector();
+	background_links = new Vector();
+
+	try {
+	     url = new URL(url_str);
+	     input = url.openStream();
+	     //  int value = 0;
+
+	     //            while ( (value=input.read())!=-1){
+	     //System.err.print((char)value);
+	     //}
+             
+	     //url = new URL(url_str);
+	     //input = url.openStream(); 
+
+	} 
+	catch (MalformedURLException e) {
+          
+	    url_valid = false;
+	} 
+	catch (IOException e) {
+	 
+	    url_valid = false;
+	}
+    }
+
+    /** Checks that a valid connection to the url has been made */
+    public boolean connected_ok()
+    {
+	return url_valid;
+    }
+
+    /** Gets any href links from this url
+     *  @return Vector of href links */
+    public Vector getHrefLinks() {
+	return href_links;
+    }
+    /** Gets any source links from this url
+     *  @return Vector of source links */
+    public Vector getSrcLinks() {
+	return src_links;
+    }
+    /** Gets any other links from this url
+     *  @return Vector of other links */
+    public Vector getLinkLinks() {
+	return link_links;
+    }
+    /** Gets any background links from this url
+     *  @return Vector of background links */
+    public Vector getBackgroundLinks() {
+	return background_links;
+    }
+    
+    /** Gets the url currently being processed */
+    public URL getURL() {
+	return url;
+    }
+    /** Checks that the content of the url is in html */
+    public boolean isHTML() {
+
+         String content_type = guessContentType(url.toString());
+   
+	if(content_type.startsWith("text/html")) {
+	    return true;
+	}
+	return false;
+    }
+    
+    /** Reads a value from the buffer
+     *  @return Value read if successful and -1 if not */
+    public int read() {
+	int value = -1;
+	if(isHTML()) {
+	    if(buffer.length() == 0) {
+		refill();
+	    }
+	    if(buffer.length() != 0) {
+		value = getBuffer();
+	    }
+	} else {
+	    value = getRaw();
+	}
+	return value;
+    }
+    /** Reads the entire URL */
+    public void readAll() {
+	int value;
+	while((value = read()) != -1) {
+	}
+    }
+    
+    // Gets the head of the buffered buffer.
+    private int getBuffer() {
+	if(buffer.length() > 0) {
+	    int value = buffer.charAt(0);
+	    buffer = buffer.substring(1, buffer.length());
+	    return value;
+	} else {
+	    System.err.println("Called getRaw on an empty string");
+	    return -1;
+	}
+    }
+    // Gets the head of the raw buffer.
+    private int getRaw() {
+	int value = -1;
+	if(peek_value != -1) {
+	    value = peek_value;
+	    peek_value = -1;
+	} 
+	else {
+	    try {
+		value = input.read();
+	    } catch (Exception e) {
+		e.printStackTrace();
+	    }
+	}
+	return value;
+    }
+    
+    private int peekRaw() {
+	if(peek_value == -1) {
+	    peek_value = getRaw();
+	}
+	return peek_value;
+    }
+    
+    // Refills the buffered buffer with the next tag or non-tag block
+    // The tag is checked for urls. Note a tag is taken to be < .. > or
+    // < .. < so comments are supported, but comment blocks are still
+    // scanned.
+    private void refill() {
+	int value = getRaw();
+	if(value != -1) {
+           
+	    if(value == '<') {
+		//System.err.println("Parsing a tag starting " + (char)value);
+		// Add opening < to buffer
+		setBuffer(value);
+		String tag = "";
+		value = getRaw();
+		while(value != -1 && peekRaw() != '<' && value != '>') {
+		    //System.err.print((char)value);
+		    tag = tag + (char) value;
+		    value = getRaw();
+		}
+	
+		//System.err.println("Read a " + (char)value);
+		//tag = smartLower(tag);
+		//System.err.println("tag "+tag);
+                tag = findURL(tag);
+		buffer = buffer + tag;
+		// Add closing > to buffer
+		setBuffer(value);
+		//System.err.println("Finished tag");
+	    } else {
+		//System.err.println("Parsing content");
+		//System.err.println("Value = " + value + " = '" + (char)value + "'");
+		while(value != -1 && value != '<') {
+		    //System.err.println("Read a '" + (char)value + "'");
+		    setBuffer(value);
+		    value = getRaw();
+		}
+		// If we've accidently read the '<' push it back in the stream by
+		// setting peek_value to value. Since the peek_value will be returned
+		// on the next read this has the desired effect.
+		if(value == '<') {
+		    peek_value = value;
+		}
+		//System.err.println("Read " + buffer);
+		//System.err.println("Finished Content");
+	    }
+	}
+    }
+    
+    // Sets the tail of the buffered buffer.
+    private void setBuffer(int value) {
+	buffer = buffer + (char) value;
+    }
+    
+    private String smartLower(String tag) {
+	boolean lower = true;
+	String new_tag = "";
+	for(int i = 0; i < tag.length(); i++) {
+				// Disable case lowering for value tags (bound by "")
+	    if (tag.charAt(i) == '"') {
+		if(lower) {
+		    lower = false;
+		} else {
+		    lower = true;
+		}
+	    }
+				// Lower everything else
+	    if(lower) {
+		new_tag = new_tag + Character.toLowerCase(tag.charAt(i));
+	    } else {
+		new_tag = new_tag + tag.charAt(i);
+	    }
+	}
+	return new_tag;
+    }
+    
+    private final static int GROUND           = 0;
+    private final static int COMMENT          = 5;
+    private final static int COMMENT_DASH     = 6;
+    private final static int COMMENT_FINAL    = 7;
+    private final static int H                = 11;
+    private final static int HR               = 12;
+    private final static int HRE              = 13;
+    private final static int HREF             = 14;
+    private final static int HREF_EQUAL       = 15;
+    private final static int HREF_Q           = 16;
+    private final static int HREF_NQ          = 17;
+    private final static int HREF_FINAL       = 18;
+    private final static int S                = 23;
+    private final static int SR               = 24;
+    private final static int SRC              = 25;
+    private final static int SRC_EQUAL        = 26;
+    private final static int SRC_Q            = 27;
+    private final static int SRC_NQ           = 28;
+    private final static int SRC_FINAL        = 29;
+    private final static int L                = 67;
+    private final static int LI               = 68;
+    private final static int LIN              = 69;
+    private final static int LINK             = 70;
+    private final static int LINK_QUOTE       = 72;
+    private final static int LINK_H           = 73;
+    private final static int LINK_HR          = 74;
+    private final static int LINK_HRE         = 75;
+    private final static int LINK_HREF        = 76;
+    private final static int LINK_EQUAL       = 77;
+    private final static int LINK_Q           = 78;
+    private final static int LINK_NQ          = 79;
+    private final static int LINK_FINAL       = 80;
+    private final static int B                = 85;
+    private final static int BA               = 86;
+    private final static int BAC              = 87;
+    private final static int BACK             = 88;
+    private final static int BACKG            = 89;
+    private final static int BACKGR           = 90;
+    private final static int BACKGRO          = 91;
+    private final static int BACKGROU         = 92;
+    private final static int BACKGROUN        = 93;
+    private final static int BACKGROUND       = 94;
+    private final static int BACKGROUND_EQUAL = 95;
+    private final static int BACKGROUND_Q     = 96;
+    private final static int BACKGROUND_NQ    = 97;
+    private final static int BACKGROUND_FINAL = 98;
+    private final static int FINAL            = 99;
+    
+    // Run the finite-state machine on a buffer-load.
+    private String findURL(String tail) {
+	int state = GROUND;
+	String head = "";
+	String url_str = "";
+	// Sift through the tag for urls
+	while(tail.length() > 0 && state != BACKGROUND_FINAL && state != COMMENT_FINAL && state != HREF_FINAL && state != LINK_FINAL && state != SRC_FINAL) {
+	    char ch = tail.charAt(0);
+	    String sch = "" + ch;
+	    sch = sch.toLowerCase();
+	    char lch = sch.charAt(0);
+	    tail = tail.substring(1);
+	    switch (state) {
+		// Initial state.
+	    case GROUND:
+		switch (lch) {
+		case '!': state = COMMENT; break;
+		case 'B': case 'b': state = B; break;
+		case 'H': case 'h': state = H; break;
+		case 'L': case 'l': state = L; break;
+		case 'S': case 's': state = S; break;
+		}
+		head = head + ch;
+		break;
+		// A possible comment
+	    case COMMENT:
+		switch(lch) {
+		case '-': state = COMMENT_DASH; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case COMMENT_DASH:
+		switch(lch) {
+		case '-': state = COMMENT_FINAL; break; // skip comments
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+		// A possible href
+	    case H:
+		switch(lch) {
+		case 'R': case 'r': state = HR; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case HR:
+		switch(lch) {
+		case 'E': case 'e': state = HRE; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case HRE:
+		switch(lch) {
+		case 'F': case 'f': state = HREF; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case HREF:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '=': state = HREF_EQUAL; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case HREF_EQUAL:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '\'': case '\"': state = HREF_Q; break;
+		default: state = HREF_NQ; break;
+		}
+		if(state == HREF_NQ) {
+		    url_str = url_str + ch;
+		} else {
+		    head = head + ch;
+		}
+		break;
+	    case HREF_NQ:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r':
+		    state = HREF_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+	    case HREF_Q:
+		switch(lch) {
+		case '\'': case '\"':
+		    state = HREF_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+		// A possible src
+	    case S:
+		switch(lch) {
+		case 'R': case 'r': state = SR; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case SR:
+		switch(lch) {
+		case 'C': case 'c': state = SRC; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case SRC:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '=': state = SRC_EQUAL; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case SRC_EQUAL:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip spaces
+		case '\'': case '\"': state = SRC_Q; break;
+		default: state = SRC_NQ; break;
+		}
+		if(state == SRC_NQ) {
+		    url_str = url_str + ch;
+		} else {
+		    head = head + ch;
+		}
+		break;
+	    case SRC_NQ:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r':
+		    state = SRC_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+	    case SRC_Q:
+		switch(lch) {
+		case '\'': case '\"':
+		    state = SRC_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+		// A possible link-src combo
+	    case L:
+		switch(lch) {
+		case 'I': case 'i': state = LI; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case LI:
+		switch(lch) {
+		case 'N': case 'n': state = LIN; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case LIN:
+		switch(lch) {
+		case 'K': case 'k': state = LINK; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK:
+		switch(lch) {
+		case 'H': case 'h': state = LINK_H; break;
+		default: state = LINK; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK_H:
+		switch(lch) {
+		case 'R': case 'r': state = LINK_HR; break;
+		default: state = LINK; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK_HR:
+		switch(lch) {
+		case 'E': case 'e': state = LINK_HRE; break;
+		default: state = LINK; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK_HRE:
+		switch(lch) {
+		case 'F': case 'f': state = LINK_HREF; break;
+		default: state = LINK; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK_HREF:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '=': state = LINK_EQUAL; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case LINK_EQUAL:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '\'': case '\"': state = LINK_Q; break;
+		default: state = LINK_NQ; break;
+		}
+		if(state == LINK_NQ) {
+		    url_str = url_str + ch;
+		} else {
+		    head = head + ch;
+		}
+		break;
+	    case LINK_NQ:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r':
+		    state = LINK_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+	    case LINK_Q:
+		switch(lch) {
+		case '\'': case '\"':
+		    state = LINK_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+		// A possible background
+	    case B:
+		switch(lch) {
+		case 'A': case 'a': state = BA; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BA:
+		switch(lch) {
+		case 'C': case 'c': state = BAC; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BAC:
+		switch(lch) {
+		case 'K': case 'k': state = BACK; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACK:
+		switch(lch) {
+		case 'G': case 'g': state = BACKG; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKG:
+		switch(lch) {
+		case 'R': case 'r': state = BACKGR; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGR:
+		switch(lch) {
+		case 'O': case 'o': state = BACKGRO; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGRO:
+		switch(lch) {
+		case 'U': case 'u': state = BACKGROU; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGROU:
+		switch(lch) {
+		case 'N': case 'n': state = BACKGROUN; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGROUN:
+		switch(lch) {
+		case 'D': case 'd': state = BACKGROUND; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGROUND:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '=': state = BACKGROUND_EQUAL; break;
+		default: state = GROUND; break;
+		}
+		head = head + ch;
+		break;
+	    case BACKGROUND_EQUAL:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r': break; // Skip blanks
+		case '\'': case '\"': state = BACKGROUND_Q; break;
+		default: state = BACKGROUND_NQ; break;
+		}
+		if(state == BACKGROUND_NQ) {
+		    url_str = url_str + ch;
+		} else {
+		    head = head + ch;
+		}
+		break;
+	    case BACKGROUND_NQ:
+		switch(lch) {
+		case ' ': case '\t': case '\n': case '\r':
+		    state = BACKGROUND_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+	    case BACKGROUND_Q:
+		switch(lch) {
+		case '\'': case '\"':
+		    state = BACKGROUND_FINAL;
+		    tail = ch + tail;
+		    break;
+		default: url_str = url_str + ch; break;
+		}
+		break;
+	    }
+	}
+       
+        url_str =   url_str.replaceAll("&amp;","&");
+    
+	if(state == HREF_FINAL ) {
+	    try {
+		URL new_url = new URL(url, url_str);
+		href_links.addElement(new_url);
+	    }
+	    catch (Exception e) {
+		e.printStackTrace();
+	    }
+        }
+
+	if(state == SRC_FINAL ) {
+	    try {
+		URL new_url = new URL(url, url_str);
+		src_links.addElement(new_url);
+	    }
+	    catch (Exception e) {
+		e.printStackTrace();
+	    }
+        }
+
+	if(state == LINK_FINAL ) {
+	    try {
+		URL new_url = new URL(url, url_str);
+		link_links.add(new_url);
+	    }
+	    catch (Exception e) {
+		e.printStackTrace();
+	    }
+        }
+
+	if(state == BACKGROUND_FINAL ) {
+	    try {
+		URL new_url = new URL(url, url_str);
+		background_links.add(new_url);
+	    }
+	    catch (Exception e) {
+		e.printStackTrace();
+	    }
+        }
+
+	return head + url + tail;
+    }
+    
+    static private String guessContentType(String text) {
+	if(text.endsWith("/")) {
+	    return "text/html";
+	} else if (text.endsWith(".html")) {
+	    return "text/html";
+	} else if (text.endsWith(".htm")) {
+	    return "text/html";
+	} else if (text.indexOf("?")>0) {
+	    return "text/html";
+	}
+	return "image/jpeg";
+    }
+    
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CollageImage.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CollageImage.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/CollageImage.java	(revision 16561)
@@ -0,0 +1,508 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.io.*;
+import java.net.*;
+import java.awt.image.*;
+
+
+/** 
+ *  @author Katrina Edgar
+ *  @author David Bainbridge
+ *
+ *  Data Structure to store an image once it is displayed in the applet.
+ *  This structure remembers the images graphical representation, source url,
+ *  name, width, height, position on the screen and whether or not it has
+ *  previously been displayed. It provides methods to set the dimensions and
+ *  position of the image. It also provides access methods to the translated
+ *  x and y co-ordinates of the image */
+public class CollageImage {
+
+     /** Decrements the alpha value between adjacent rows on the edge of an image<br>
+     *  Used to fade the edges faster than the rest of the image <br>
+     *  Only used when java2 parameter is true and advanced image processing
+     *  techniques may be applied */
+    static final int FADEVALUE = 3;
+
+    /** Defines the shades of white used to colour the backgrounds of white images <br>
+     *  These colours are used because when several white images appear in the collage
+     *  simultaneously it is difficult to distinguish their edges<br>
+     *  Only used when java2 parameter is true and advanced image processing
+     *  techniques may be applied */
+    int colorMask [] = {0xF8FFFF, 0xFFF8FF, 0xFFFFF8, 0xF8F8FF, 0xFFF8F8, 0xF8FFF8, 0xFAFAFA};
+
+      
+    /** Defines the number of shades of white used to colour the backgrounds of white images <br>
+     *  These colours are used because when several white images appear in the collage
+     *  simultaneously it is difficult to distinguish their edges<br>
+     *  Only used when java2 parameter is true and advanced image processing
+     *  techniques may be applied */
+    static final int NO_OF_COLORS = 7;
+
+    /** Will fire a removal operation when 4 images in the collage overlap a single area */
+    static final int NO_IMAGES_OF_OVERLAP = 4;
+
+
+    /** Source url of the image */
+    String from_url_    = null;
+    /** Name of the image */
+    String name_        = null;
+    /** Width of the image */
+    int image_x_dim_    = 0;
+    /** Height of the image */
+    int image_y_dim_    = 0;
+    /** Indicates whether or not the image has been drawn on the applet previously */
+    boolean fresh       = true;
+    
+    URL url_ = null;      
+ 
+    /** Reflects the translation of the image from the origin to its position on the screen */
+    AffineTransform af_ = null;
+    /** Left x co-ordinate */
+    protected int xl_ = 0;
+    /** Top y co-ordinate */
+    protected int yt_ = 0;
+    /** Right x co-ordinate */
+    protected int xr_ = 0;
+    /** Bottom y co-ordinate */
+    protected int yb_ = 0;
+ 
+     /** Refers to applet */
+    GsdlCollageApplet app_ = null;
+
+    boolean isJava2_;
+ 
+    Image image_; 
+   
+    /** Constructs an CollageImage from the three specified parameters
+     *
+     *  @param image The graphical representation of the image
+     *  @param from_url The source url for the image
+     *  @param name The file name of the image */
+    public CollageImage(GsdlCollageApplet app, boolean isJava2, DownloadImages.ImageUrlTriple iutriple)
+    {
+         
+         image_ = iutriple.image();
+	 from_url_ = iutriple.urlString();
+	 name_ = iutriple.name();
+	 url_ =  iutriple.url();
+         
+         isJava2_=isJava2;
+         app_=app;  
+
+	 process();
+    }
+
+    /** Sets the translation for the image from the origin
+     *  And regenerate the image as the scaled version
+     *
+     *  @param af The AffineTransform translation that has been calculated */
+    public void setAffineTransform(MyAffineTransform af, boolean is_java2_)
+    {	
+	image_x_dim_ = (int) (image_x_dim_ * af.scaleX);
+	image_y_dim_ = (int) (image_y_dim_ * af.scaleY);
+        
+	image_ = image_.getScaledInstance(image_x_dim_, image_y_dim_, Image.SCALE_DEFAULT);
+	
+	if (is_java2_) {
+	    af_ = new AffineTransform();
+	    af_.translate(af.translateX, af.translateY);
+	    af_.scale(1.0, 1.0);
+	
+	    calculate_rect();
+	}
+	else {
+	    double trans_x = af.translateX;
+	    double trans_y = af.translateY;
+	    
+	    xl_ = Math.round((float)trans_x);
+	    xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
+	    yt_ = Math.round((float)trans_y);
+	    yb_ = Math.round((float)(trans_y + image_y_dim_)) -1; 
+	}
+
+    }
+
+    public void expand () {
+	
+	// still expands a little too fast... how to fix this?
+	magnify(new Rectangle((int) (xl_ + (image_x_dim_ * 0.000001)),
+			      (int) (yt_ + (image_y_dim_ * 0.000001)),
+			      (int) (image_x_dim_ * 0.999998),
+			      (int) (image_y_dim_ * 0.999998)));
+	
+    }
+    
+    public void magnify(Rectangle border) {
+	
+	double magX = image_x_dim_/(double)border.width;
+	double magY = image_y_dim_/(double)border.height;
+	int x = border.x + border.width/2;
+	int y = border.y + border.height/2;
+	paintImage(x,y,magX, magY);
+    }
+    
+    public void paintImage(int magCenterX, int magCenterY, double magX, double magY){
+	
+	try {
+	    //Point2D mgp = null;
+	    //mgp = af_.inverseTransform((new Point(magCenterX, magCenterY)),(Point)mgp);
+	    //double x = (mgp.getX()*magX)-mgp.getX();
+	    //double y = (mgp.getY()*magY)-mgp.getY();
+	    //scale(-x,-y, magX, magY);
+	}catch (Exception e) {System.out.println(e); }
+    }
+    
+    public void scale(double magOffsetX, double magOffsetY, double magX, double magY){
+	
+	af_.translate(magOffsetX,magOffsetY);
+	af_.scale(magX, magY);
+    }
+    
+    /** Calculates the new image co-ordinates after translation has occurred */
+    protected void calculate_rect()
+    {
+	double trans_x = af_.getTranslateX();
+	double trans_y = af_.getTranslateY();
+
+	xl_ = Math.round((float)trans_x);
+	xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
+	yt_ = Math.round((float)trans_y);
+	yb_ = Math.round((float)(trans_y + image_y_dim_)) -1; 
+    }
+
+    /** Determines whether a given co-ordinate is inside this image
+     *
+     *  @param x The x co-ordinate
+     *  @param y The y co-ordinate */
+    public boolean inside(int x, int y)
+    {
+	return ((x>=xl_) && (x<=xr_)) && ((y>=yt_) && (y<=yb_));
+    }
+
+    /** Gets the width of the translated image */
+    public double getX() {
+	return af_.getTranslateX();
+    }
+
+    /** Gets the height of the translated image */
+    public double getY() {
+	return af_.getTranslateY();
+    }
+    
+  
+    /** Fades and colours the image on a pixel-by-pixel basis <br>
+     *  First it grabs the pixels of the entire image and stores them in a 2D array.
+     *  Then a bound is calculated to indicate the point from which edge
+     *  fading should occur. The position of the bound will change in proportion
+     *  to the age of the image so that older images have a larger faded edge.
+     *  The area within the bounds, that forms the center of the image, is processed
+     *  first and faded by a standard alpha value. Then each edge of the image
+     *  is processed separately, creating a gradient fade from the true edge to the
+     *  position of the bound. <br>
+     *  The faded pixel array is then turned into a new image a returned.
+     *
+     *  @param img the image that requires processing
+     *  @param x the x co-ordinate of the image
+     *  @param y the y co-ordinate of the image
+     *  @param w the width of the image
+     *  @param h the height of the image
+     *  @param p the position of the image in the applet (indicates age)
+     *  @param fresh whether the image is being processed for the first time */
+    public int[]  handlepixels(int p) {
+       
+	int h = image_y_dim_;
+	int w = image_x_dim_;       
+	
+	// declare an array to hold the pixels
+	int[] pixels = new int[w * h];
+	// get the pixels of the image into the pixels array
+	PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
+	
+	try {
+	    pg.grabPixels();
+	} catch (InterruptedException e) {
+	    System.err.println("interrupted waiting for pixels!");
+	}
+	
+	    
+	// check for any failures
+	if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+	    System.err.println("image fetch aborted or errored");
+		
+	}
+	
+   
+	// calculates the bound from which fading should begin
+	double bound = p * 0.01;
+	if (w > h)
+	    bound *= w;
+	else
+	    bound *= h;
+
+	int upperboundheight = h - (int) bound;
+	int upperboundwidth = w - (int) bound;
+	int lowerbound = (int) bound;
+
+      	// loop through every pixel in the picture and handle it
+	for (int j = lowerbound; j < upperboundheight; j++) {
+	    for (int i = lowerbound; i < upperboundwidth; i++) {
+		// width and height: x+i y+j
+		pixels[j * w + i] = handlesinglepixel(pixels[j * w + i], p, fresh, false, 255);
+	    }
+	}
+
+	int fade = 0;
+	int fader = 0;
+	int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
+	
+	// top edge
+	for (int n = lowerbound; n >= 0; n--) {
+	    fader = corealpha - fade;
+	    if (fader < 0)
+		fader = 0;
+	    
+	    for (int m = 0; m < w; m++){
+		int index = n* w + m;
+		if (index <pixels.length){
+		    pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
+		    
+		}
+	    }   
+	    
+	    fade+= FADEVALUE;
+	}
+
+	// bottom edge
+	fade = 0;
+	for (int n = upperboundheight; n < h; n++) {
+	    fader = corealpha - fade;
+	    if (fader < 0)
+		fader = 0;
+	    for (int m = 0; m < w; m++){
+		int index = n * w + m;
+		if (index <pixels.length){
+		    pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
+		    
+		}
+	    }
+	    
+	    fade += FADEVALUE;
+	}
+
+	// left edge
+	fade = 0;
+	for (int n = lowerbound; n >= 0; n--) {
+	    fader = corealpha - fade;
+	    if (fader < 0)
+		fader = 0;
+	    for (int m = 0; m < h; m++) {
+		if ( m < lowerbound && n > m);
+		else if ( m > upperboundheight && n > (h - m));
+		else {
+		    int index = m * w + n;
+                    if (index <pixels.length){
+			pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
+	
+		    }
+		}
+                   
+	    }
+	    fade += FADEVALUE;    
+	}
+	
+	// right edge
+	fade = 0;
+	for (int n = upperboundwidth; n < w; n++) {
+	    fader = corealpha - fade;
+	    if (fader < 0)
+		fader = 0;
+	    for (int m = 0; m < h; m++) {
+		if ( m < lowerbound && (w - n) > m);
+		else if ( m > upperboundheight && (w - n) > (h - m));
+		else {
+		    int index = m * w + n;
+                    if (index <pixels.length){
+			pixels[index] = handlesinglepixel(pixels[index], p, fresh, true, fader);
+	
+		    }
+		}
+		    
+	    }
+	    
+	    fade += FADEVALUE;
+	}  
+	
+       		// set the pixels of the whole picture to the pixels array
+       	pg.setPixels(0, 0, w, h, pg.getColorModel(), pixels, 0, w);
+
+	image_ =  app_.createImage(new MemoryImageSource(w, h, pixels, 0, w));
+
+	return pixels;
+    }
+
+    
+    /** Adjusts the colour and alpha value of an individual pixel <br>
+     *  If the image is being drawn for the first time, the RGB values are
+     *  extracted. If the pixel is close to white (RGB > 250) then an offwhite
+     *  colour is applied to this pixel.
+     *  This is done because when several white images appear in the collage
+     *  simultaneously it is difficult to distinguish their edges. <br>
+     *  This function also fades the alpha value of the pixel as the image ages.
+     *  The alpha value is more heavily decremented as pixels get closer to the
+     *  edge of the image
+     *
+     *  @param pixel the pixel to manipulate
+     *  @param p the position of the image in collage (representative of age)
+     *  @param fresh indicates whether or not the image is being drawn for the first time
+     *  @param edge indicates whether or not this pixel is near the edge of the image
+     *  @param fade the amount by which to fade this pixel
+     *  @return the adjusted pixel as an int */
+    public int handlesinglepixel(int pixel, int p, boolean fresh, boolean edge, int fade) {
+       	int newpixel = 0;
+
+        //changes the colour of the picture, only when first drawn
+	//and only if the pixel is close to white
+	if (fresh) {
+	    
+	    int red   = (pixel >> 16) & 0xff;
+	    int green = (pixel >>  8) & 0xff;
+	    int blue  = (pixel      ) & 0xff;
+	    
+	    if (red >= 250 && green >= 250 && blue >= 250) {
+		
+		int c = colorMask[((int) (p%NO_OF_COLORS))];
+
+		red = (c >> 16) & 0xff;
+		green = (c >> 8) & 0xff;
+		blue  = (c) & 0xff;
+	    }
+
+	    newpixel |= (red << 16) & 0x00ff0000;
+	    newpixel |= (green << 8) & 0x0000ff00;
+	    newpixel |= blue & 0x000000ff;
+	}
+
+	else {
+	    newpixel |= pixel & 0x00ffffff;
+	}
+
+	int alpha = (pixel >> 24) & 0xff;
+
+	if (edge) {
+	    // fade the edges more...
+	    alpha = fade;
+	}
+	else if (alpha > 10 && !fresh) {
+	    alpha -= 10;
+	}
+	
+	newpixel |= (alpha << 24) & 0xff000000;
+	
+	return (newpixel);
+    }
+
+
+  
+
+    /** Resets the alpha channel of an image so that it appears solid
+     *
+     *  @param img the image to restore
+     *  @param x the x co-ordinate of the image
+     *  @param y the y co-ordinate of the image
+     *  @param w the width of the image
+     *  @param h the height of the image */
+    public void restoreAlpha() {
+       	int h = image_y_dim_;
+	int w = image_x_dim_;       
+
+	// declare an array to hold the pixels
+	int[] pixels = new int[w * h];
+	// get the pixels of the image into the pixels array
+	PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
+	try {
+	    pg.grabPixels();
+	    
+		
+	} catch (InterruptedException e) {
+	    System.err.println("interrupted waiting for pixels!");
+	}
+	
+	
+	// check for any failures
+	if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+	    System.err.println("image fetch aborted or errored");
+	}
+	
+	
+	    
+	// loop through every pixel in the picture and handle it
+	for (int j = 0; j < h; j++) {
+	    for (int i = 0; i < w; i++) {
+		pixels[j * w + i] |= (255 << 24) & 0xff000000;
+	    }
+	}   
+ 
+	// set the pixels of the whole picture to the pixels array
+	pg.setPixels(0, 0, w, h, pg.getColorModel(), pixels, 0, w);
+
+	image_ =  app_.createImage(new MemoryImageSource(w, h, pixels, 0, w));       
+	
+    }
+    
+    /** Checks whether an image has faded to the point where it must be removed
+     *  from the collage.
+     *
+     *  @param img the image to restore
+     *  @param x the x co-ordinate of the image
+     *  @param y the y co-ordinate of the image
+     *  @param w the width of the image
+     *  @param h the height of the image */
+    public boolean checkFaded (int[] pixels) {
+	int h = image_y_dim_;
+	int w = image_x_dim_;
+
+	// get the alpha value of the middle pixel of the image
+	int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
+	
+	if (corealpha < 50)
+	    return true;
+	
+	return false;
+	
+    }
+
+    public boolean isValid(){
+	image_x_dim_ = image_.getWidth(app_);
+	image_y_dim_ = image_.getHeight(app_);
+
+	return  (image_x_dim_) >0 && ( image_x_dim_ >0);
+	
+    }
+
+    
+    public void process(){
+
+	// images x and y dimensions
+	image_x_dim_ = image_.getWidth(app_);
+	image_y_dim_ = image_.getHeight(app_);
+	 if (( image_x_dim_ >0) && ( image_x_dim_ >0))
+	     {		
+		 // places and sizes the image
+		 MyAffineTransform af = new MyAffineTransform(image_x_dim_,image_y_dim_);
+		 
+		 // sets location & size of collage image
+		 setAffineTransform(af, isJava2_);
+		 fresh = false;
+		 if (isJava2_ ) {      	 
+		     restoreAlpha(); 
+		 }
+		 	
+	     }
+  
+	
+    }
+
+}	
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Display.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Display.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Display.java	(revision 16561)
@@ -0,0 +1,44 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.geom.*;
+import java.util.*;
+
+/** 
+ *  @author Katrina Edgar */
+public class Display extends Thread {
+
+    GsdlCollageApplet app_ = null;
+
+     public Display(GsdlCollageApplet app) {
+	super("Display");
+	app_ = app;
+	
+    }
+
+
+    /** Runs display thread by repeatedly calling the next frame,
+     *  waiting for a specified delay period between calls */
+    public void run() {
+	try {
+
+	    System.err.println("Started display thread.");
+
+	    Thread curr_thread = Thread.currentThread();
+	    
+	    while (curr_thread == this) {
+		app_.repaint();
+		// can't make it zero or it hogs the whole processor
+		Thread.sleep(100);
+		curr_thread = Thread.currentThread();
+	    }
+
+	    System.err.println("Stopped display thread.");
+	    
+	} catch (Exception e) {
+	    e.printStackTrace();
+	}
+    }
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DisplayImages.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DisplayImages.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DisplayImages.java	(revision 16561)
@@ -0,0 +1,437 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.applet.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.geom.*;
+import java.util.*;
+
+/** 
+ *  @author Katrina Edgar
+ *  @author David Bainbridge
+ *
+ *  Retrieves images from the download thread, determines an appropriate position
+ *  for thes  images onscreen ensuring maximum whitespace coverage and minimal overlap
+ *  when possible. Draws images using advanced image processing techniques when applicable,
+ *  such as fading the edges of an image and discolouring backgrounds. Also re-uses images
+ *  from those that have previously been displayed, or are aging onscreen, in order to
+ *  to create movement when no new images have been downloaded. */
+public class DisplayImages extends Thread {
+
+
+    /** Will fire a removal operation when 4 images in the collage overlap a single area */
+    static final int NO_IMAGES_OF_OVERLAP = 4;
+    
+    /** To determine if there are several images overlapped, can remove the
+     *  layers underneath to save time in repainting */
+    boolean overlap = false;
+
+    /** Refers to download thread */
+    DownloadImages download_images_ = null;
+
+    /** Refers to applet */
+    GsdlCollageApplet app_ = null;
+
+    /** Applets width on screen */
+    public static int app_x_dim_  = 0;
+
+    /** Applets height on screen */
+    public static int app_y_dim_  = 0;
+    
+    /** Background colour of the applet screen */
+    protected Color bgcolor_        = null;
+
+    /** Indicates whether java2 functionality should be used <br>
+     *  If true will allow advanced image processing techniques to occur, 
+     *  such as fading and colouring using pixel manipulation <br>
+     *  If false, images will maintain an alpha value of 1 (appear solid), 
+     *  newer images will simply be pasted on top of existing images <br> */
+    protected boolean is_java2_     = false;
+
+  
+    /** Holds images currently on display in the applet */
+    protected Vector inuse_    = null;
+    
+     /** Holds images ready to be used */
+    protected Vector inuse_ready_    = null;
+
+    /** Holds previously displayed images for re-use */
+    protected Vector notinuse_ = null;
+   
+    protected Image    screen_buffer_    = null;
+    protected Graphics screen_graphic_   = null;
+    protected Image    finished_buffer_  = null;
+    protected Graphics finished_graphic_ = null;
+ 
+    Thread next_frame_ ;
+
+    String dots[] = new String[]{" ",".","..","...","....",".....","......"};
+    int dots_nums=0;
+    boolean start_paint = false;
+    /** Used to determine where to place new images on the applet to optimize
+     *  whitespace coverage <br>
+     *  A co-ordinate in the applet has a value of zero if it is whitespace,
+     *  one if it is covered by one image, two if it is covered by two images,
+     *  and so on. New images will be placed such that they cover the largest
+     *  area of whitespace (zero valued co-ordinates) */
+    public static int [] [] used_space = null;
+    
+  
+    /**
+     *  Starts the display images thread. Initialises variables using parameters and
+     *  static values. Creates the blank applet screen, initialises the used space
+     *  2D array to indicate that there are no images onscreen
+     *
+     *  @param app the applet to display the images on
+     *  @param download_images thread from which to retrieve downloaded images
+     *  @param is_java2 whether advanced image processing techniques should be used
+     *  @param bgcolor the background colour of the applet screen */
+    public DisplayImages(GsdlCollageApplet app, DownloadImages download_images,
+			 boolean is_java2, Color bgcolor)
+    {
+	super("DisplayImages");
+
+	// parameters saved locally
+	app_             = app;
+	is_java2_        = is_java2;
+	bgcolor_         = bgcolor;
+
+	inuse_    = new Vector();
+      	notinuse_ = new Vector();
+        inuse_ready_ = new Vector();
+	download_images_ = download_images;
+	// sets width and height
+	if (is_java2_)
+	{
+	    app_x_dim_  = app_.getWidth();
+	    app_y_dim_  = app_.getHeight();
+	}
+	else
+	{
+	    app_x_dim_  = 645;
+	    app_y_dim_  = 780;
+	}
+
+	// creates initial screen
+	screen_buffer_ = app_.createImage(app_x_dim_, app_y_dim_); 
+	screen_graphic_ = screen_buffer_.getGraphics();
+	screen_graphic_.setColor(bgcolor_);
+	screen_graphic_.fillRect(0,0,app_x_dim_,app_y_dim_);
+	
+	finished_buffer_  = app_.createImage(app_x_dim_, app_y_dim_);
+	finished_graphic_ = finished_buffer_.getGraphics();
+    
+	// initialises used space array to indicate an empty screen
+	used_space = new int [app_x_dim_][app_y_dim_];	
+	for (int n=0; n < app_x_dim_; n++)
+	    for (int m=0; m < app_y_dim_; m++)
+		used_space[n][m] = 0;
+
+        next_frame_ =  new Thread(new Runnable(){
+                public void run() {
+		  
+		    Thread curr_thread = Thread.currentThread();
+		    
+		    while (curr_thread == next_frame_) {
+			try {	    
+			    Runtime rt = Runtime.getRuntime();
+			    //System.out.println("total: "+rt.totalMemory());
+			    //System.out.println("free:  "+rt.freeMemory());
+			    rt.gc();             
+               		    //System.out.println("**************next frame...");
+			    next_frame();
+			    Thread.sleep(app_. refreshDelay_);
+			   
+			    curr_thread = Thread.currentThread();
+			} catch (Exception e) {
+                            e.printStackTrace(); 
+			    break;
+			}
+		    }
+		    	    
+		   
+		}
+
+
+	    });
+
+	 next_frame_.start();
+
+
+    }
+
+    /** Determines what the user has clicked on in the applet, either an image or whitespace
+     *  @param x the x co-ordinate of the mouse
+     *  @param y the y co-ordinate of the mouse
+     *  @return the image that the user has clicked on or null if the user has clicked whitespace */
+   public CollageImage clickedOnImage(int x, int y)
+    {
+	if (app_.verbosity() > 1)
+	{
+	    System.err.print("Checking for clicked on image: ("+x+","+y+")");
+	}
+
+	
+	// checks from last image down, as last images place will be at the top of the collage
+	for (int i= inuse_.size() - 1; i >= 0; i--)
+	{
+	    CollageImage collage_image = (CollageImage)inuse_.elementAt(i);
+	    if (collage_image.inside(x,y))
+	    {
+		return collage_image;
+	    }
+	}
+	// no image at these co-ordinates, just whitespace
+	return null;
+    }
+
+    /** Generates the images onscreen.
+     *  Loops through the vector of currently displayed images and draws each one in turn.
+     *  If the image is being drawn for the first time, its position will be determined
+     *  and its alpha channel set as solid prior to drawing. <br>
+     *  Drawing of the image will depend on whether or not java2 is enabled. If so then
+     *  the image will be processed so that fading occurs with age, so that the edges 
+     *  fade faster than the rest of the image and so that white images are slightly 
+     *  discoloured to allow distinction. If java2 is not enabled, new images will simply
+     *  be pasted on top of existing images without any special effects occuring<br>
+     *  This function also deletes old images. If java2 is enabled then images are deleted
+     *  once they have faded to a suitably low alpha value. If java2 is not enabled then
+     *  images will be removed once there are greater than 25 images onscreen, to ensure
+     *  the applet cannot be overloaded. */
+    public  void display_collage() 
+    {
+	
+	// if there is nothing new to add
+	if (inuse_.size()==0) return;
+	
+	// otherwise need to re-generate images
+	if (app_.verbosity() >= 2)
+	    {
+		//system.err.println("Regenerating images");
+	    }
+	
+	// get each image currently onscreen inturn
+	for (int i=0; i<inuse_.size();)
+	    {
+		CollageImage collage_image = (CollageImage)inuse_.elementAt(i);
+  
+                if (!collage_image.isValid()){
+		    continue;
+		}	    
+
+                int[] pixels = null;
+		// the first time the image is painted, determine its position and size
+		if (collage_image.fresh == true)
+		    {
+			collage_image.process();
+		    }
+		
+		// advanced image processing may be used
+		if (is_java2_)
+		    {
+			// conducts fading and colouring of image
+			pixels = collage_image.handlepixels(inuse_.size()-i); 
+		    }
+		
+		if (collage_image.fresh) {
+		    collage_image.fresh = false;
+		}
+		
+		
+		// if we remove when images have faded too much
+		if (is_java2_) {
+		    if (collage_image.checkFaded(pixels)|| overlap)
+			{
+			    overlap = false;
+			    
+			    CollageImage removed_image = (CollageImage)inuse_.elementAt(i);
+			    //System.out.println(removed_image.name_ + "is removed");
+			    inuse_.remove(i);
+			    // indicates space for this image is no longer used
+			    for (int n = removed_image.xl_; n < (removed_image.xl_ + removed_image.image_x_dim_); n++)
+				for (int m = removed_image.yt_; m < (removed_image.yt_ + removed_image.image_y_dim_); m++)
+				    if(used_space[n][m] != 0) {
+					used_space[n][m]--;
+					if (used_space[n][m] > NO_IMAGES_OF_OVERLAP)
+					    overlap = true;
+				    }
+			    
+			    // sets the image as fresh and adds it to the vector of images previously seen
+			    removed_image.fresh = true;
+			    notinuse_.addElement(removed_image);
+			}
+		    else{
+			i++;
+		    }
+		}
+		else 
+		    if (inuse_.size() > 25 || overlap) {
+			overlap = false;
+			
+			CollageImage removed_image = (CollageImage)inuse_.elementAt(i);
+			inuse_.remove(i);
+			
+			// indicates space for this image is no longer used
+			for (int n = removed_image.xl_; n < (removed_image.xl_ + removed_image.image_x_dim_); n++)
+			    for (int m = removed_image.yt_; m < (removed_image.yt_ + removed_image.image_y_dim_); m++)
+				if(used_space[n][m] != 0) {
+				    used_space[n][m]--;
+				if (used_space[n][m] > NO_IMAGES_OF_OVERLAP)
+				    overlap = true;
+				}
+			
+			// sets the image as fresh and adds it to the vector of images previously seen
+			removed_image.fresh = true;
+			notinuse_.addElement(removed_image);
+		    }
+		    else{
+			i++;
+		    }    
+		
+	    }
+    }
+    
+  
+    /** Alters the images currently on display in the applet <br>
+     *  Changes are prioritised in the following way:
+     *  1. If there are images that have been downloaded and are yet to be displayed 
+     *  they will be added to the list of images in use. <br>
+     *  2. Otherwise if there are images that have previously been displayed but
+     *  are not currently on the applet screen, re-use one of these images. <br>
+     *  3. Otherwise if there are any images on screen at all, take the oldest and
+     *  repaint it as the newest. <br>
+     *  Then the collage is asked to display and repaint with the new image set. */
+
+     public  void next_frame()
+    {
+	double random = Math.random();
+	
+     
+	 if (inuse_ready_.size() > 0){
+	    int pos = (int) ((inuse_ready_.size() - 1) * random);
+	    CollageImage collage_image = (CollageImage) inuse_ready_.remove(pos);
+	    inuse_.addElement(collage_image);
+ 	}
+	else if (notinuse_.size()>0)
+		{
+		    int position = (int) ((notinuse_.size() - 1) * random);
+		    
+		    CollageImage collage_image = (CollageImage)notinuse_.elementAt(position);
+		    notinuse_.remove(position);
+		    inuse_.addElement(collage_image);
+		    
+		    if (app_.verbosity()>3)
+		    {
+			System.err.println("Re-using an image not on screen");
+		    }
+		}
+	    else{
+		if (app_.verbosity()>=4)
+		    System.err.println("No images in download area");
+	    }
+
+	display_collage();
+	
+    }
+  
+  
+
+    /** Paints the applet on the screen */
+    public void paint(Graphics g)
+    {
+
+	screen_graphic_.setColor(bgcolor_);
+	screen_graphic_.fillRect(0,0,app_x_dim_,app_y_dim_);
+
+	// get each image currently onscreen inturn
+	
+	for (int i=0; i < inuse_.size(); i++)
+	    {
+          
+            CollageImage collage_image =null;
+           try{
+		   collage_image = (CollageImage)inuse_.elementAt(i);
+		   start_paint = true;
+		}
+            catch(Exception e){
+		  System.out.println("exception in paint");					
+              break;
+	     }
+	   // don't paint new images yet
+		if (collage_image.fresh) {
+		  if (app_.verbosity()>=1) {
+			System.err.println("doing nothing...  "+ collage_image.name_ +" is fresh.");
+		   }
+		}
+
+		// advanced image processing may be used
+		else if (is_java2_) {
+		  
+			//the alphacomposite controls the blending of these two images.
+			Graphics2D sg2d = (Graphics2D)screen_graphic_;
+			sg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+			sg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
+					      RenderingHints.VALUE_ANTIALIAS_ON); 
+			collage_image.expand();
+			sg2d.drawImage(collage_image.image_, collage_image.af_, app_);
+		   }
+		// simple image collage
+		else
+		    {			// add the image to the collage
+			
+			screen_graphic_.drawImage(collage_image.image_, 
+						  collage_image.xl_, collage_image.yt_,
+						  collage_image.xr_, collage_image.yb_,
+						  0,0,collage_image.image_x_dim_-1,
+						  collage_image.image_y_dim_-1,  app_);
+	 	    }
+	
+	    }
+	
+        
+	finished_graphic_.drawImage(screen_buffer_,0,0,app_);
+	
+
+	if (finished_buffer_ != null)
+	    {
+		g.drawImage(finished_buffer_, 0, 0, app_);
+		if (!start_paint){
+		    g.setColor(Color.white);
+		    g.setFont(new Font("font",Font.PLAIN+Font.BOLD,30));
+		    g.drawString("Downloading" + dots[(dots_nums++)%dots.length],app_x_dim_/3-20,app_y_dim_/2-2);
+		}
+	    }
+	
+
+    }
+
+    /** Runs display thread by repeatedly calling the next frame,
+     *  waiting for a specified delay period between calls */
+    public void run() {
+	try {
+	    
+	    Thread curr_thread = Thread.currentThread();
+	    
+	    while (curr_thread == this) {
+		if (inuse_ready_.size() >= 1){
+		    Thread.sleep(1000);		    
+		    continue;
+		}    
+		CollageImage collage_image = download_images_.getCollageImage();
+ 	
+		if (collage_image !=null){
+		    inuse_ready_.addElement(collage_image);	
+ 		}
+		curr_thread = Thread.currentThread();
+	    }
+	        
+	} catch (Exception e) {
+	   e.printStackTrace();
+           System.out.println("Display images thread is interrupted");
+	}
+
+	System.out.println("Display images thread stoped");
+          
+    }
+
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadImages.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadImages.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadImages.java	(revision 16561)
@@ -0,0 +1,166 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.awt.image.*;
+import java.awt.geom.*;
+
+/** 
+ *  @author Katrina Edgar
+ *  @author David Bainbridge
+ *
+ *  Stores images that are downloaded but have not yet been displayed
+ *  in the applet. Provides methods to add to and remove from this list
+ *  and check it's size. It uses an inner class to store the data for these
+ *  images, comprising of three parts: the image itself, it's source url
+ *  and the name of the image */
+public class DownloadImages {
+
+    /** Data structure to store an image that has been downloaded and is
+     *  waiting to be used. */
+    public class ImageUrlTriple {
+
+	Image image_;
+	String urlString_;
+	String name_;
+        URL url_;
+
+	/** Constructs a downloaded image structure from the given parameters
+	 *  @param image the graphical image
+	 *  @param url the source url for this image
+	 *  @param name the name of the image file */
+	public ImageUrlTriple (Image image, URL url, String urlString, String name)
+	{
+	    image_ = image;
+	    url_ = url;
+            urlString_ = urlString; 
+	    name_ = name;
+	}
+
+	Image image() {
+	    return image_;
+	}
+
+	String urlString() {
+	    return urlString_;
+	}
+
+	String name() {
+	    return name_;
+	}
+
+	URL url(){
+	    return url_;
+	}
+
+    }
+
+   
+    ImageUrlTriple downloaded_image;
+    CollageImage collage_image; 
+
+    /** Refers to applet */
+    GsdlCollageApplet app_ = null;
+    boolean isJava2_;
+    int num_of_downloads = 1;
+    boolean stopDownload;
+
+    MediaTracker tracker_;
+
+    /** Initialises an empty vector to store downloaded images in and sets
+     *  the verbosity for this class */
+    public DownloadImages(GsdlCollageApplet app, int verbosity, boolean isJava2)
+    {
+	
+        app_=app;
+        isJava2_= isJava2;
+    }
+
+    /** Push a new image into the vector of downloaded images
+     *  @param image the graphical image to add
+     *  @param url the source url of the image
+     *  @param name the name of the image to add */
+    public  synchronized int downloadImage(MediaTracker tracker, URL url, String urlString, String name)
+    {
+	
+	tracker_= tracker;
+
+	while (downloaded_image != null ){
+	    System.out.println(Thread.currentThread().getName() + " wait to download..."); 
+	    try{
+		wait();
+	    }
+	    catch(InterruptedException e){
+		return 0;
+	    }
+
+	}
+      
+	Image image = Toolkit.getDefaultToolkit().getImage(url);
+	tracker_.addImage(image,num_of_downloads);
+
+	try{
+	    tracker_.waitForID(num_of_downloads);
+	}
+	catch (InterruptedException e){
+	    notify();
+	    return num_of_downloads;
+	}
+	
+
+	downloaded_image  = new ImageUrlTriple(image,url,urlString,name);
+
+	notify();
+       
+	return num_of_downloads ;
+    }
+ 
+ 
+    /** Remove the image from the specified position in the array of downloaded images
+     *  @return an ImageUrlTriple containing the image, url and image name */
+    public synchronized CollageImage getCollageImage()
+    {
+	if (stopDownload)  return null;   
+
+	while (downloaded_image == null){
+	    System.out.println(Thread.currentThread().getName() + " wait to get Collage image...");
+	    try{
+		wait();
+	    }
+	    catch(InterruptedException e){
+		//e.printStackTrace();
+		downloaded_image = null;
+		notify();
+		return null;
+	    }
+	}
+         
+      	if (tracker_.statusID(num_of_downloads, false) == MediaTracker.COMPLETE){
+	    collage_image = new CollageImage(app_,isJava2_,downloaded_image);
+	    downloaded_image = null; 
+	    num_of_downloads++;
+	    notify();
+	    return collage_image;
+	}
+	else{
+	    if ((tracker_.statusAll(false) & MediaTracker.ERRORED) != 0){
+		//System.out.println(downloaded_image.name_);
+		downloaded_image = null;
+		num_of_downloads++;
+		notify();
+		return null;
+	    }
+	}
+	
+	return null;
+
+    }
+
+    public void stopDownload(){
+        stopDownload = true;
+    }
+
+ 
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadUrls.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadUrls.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/DownloadUrls.java	(revision 16561)
@@ -0,0 +1,596 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.swing.ImageIcon; //****
+
+
+/** 
+ *  @author Katrina Edgar
+ *  @author David Bainbridge
+ *
+ *  Controls retrieval of images from the specified starting url. Follows appropriate
+ *  links from this starting point, traversing in a tree-like state through several other
+ *  pages. Filters images and links based on specified parameters. Also controls the quantity
+ *  of downloading that occurs by restricting the number of downloaded images that are yet to
+ *  be displayed to 10, and the total number of downloads allowed is also restricted by
+ *  the applet application (to prevent downloading occuring infinitely). */
+
+public class DownloadUrls extends Thread {
+
+    /** Refers to applet */
+    GsdlCollageApplet app_          = null;
+    /** Refers to download thread */
+    DownloadImages download_images_ = null;
+
+    /** The address from which the application should start looking for images */
+    String starting_url_  = null;
+
+    /** the root directory of Greenstone*/  
+    String document_root_ = null;
+
+
+    /** CHRIS - Holds the contents of the collection's assoc directory */
+    //    File[] assocDir_ = null;
+
+    /** Restricts links followed from the starting url to links that contain this string */
+    String href_musthave_ = null;
+    /** Restricts links followed from the starting url to links that do not contain this string. 
+     *  Also prevents image names from containing this string */
+    String image_mustnothave_ = null;
+    /** Ignore images whose names begin with this string */
+    String image_ignore_  = null;
+    /** Restricts the types of images included in the collage, for example jpg, gif, etc. */
+    String image_type_ = null;
+
+    /** A static delay used when attempting to download more images into a full downloading buffer */
+    final int delay_       = 3000;
+    /** The maximum number of images to have downloaded and not yet displayed */ 
+    final int buffer_size_ = 1;
+
+    /** Used in cases where the image maps to a url outside of it's original location.
+     *  When used with Greenstone the collage images will refer to documents in the collections
+     *  from which the images are sourced. When used individually, the images may be saved into
+     *  a user directory and the pages they reference may be external hyperlinks. */
+    Hashtable external_links_ = null;
+
+    /** Records all urls which have already been examined */
+    Hashtable visited_url_  = null;
+    /** Determines whether there are still pages to examine and images to download */
+    boolean thread_running_ = true;
+
+    int verbosity_ = 0;
+
+     /** Records all images which have already been examined */
+    Hashtable visited_images_  = null;
+        
+    MediaTracker tracker;
+
+    /** Constructor to initialise a download thread from which images are found, 
+     *  saves parameters into local variables for use within the class.
+     *
+     *  @param app reference to the applet
+     *  @param download_images class which stores the images retrieved in triplets
+     *  @param starting_url the url from which the search for images should begin
+     *  @param href_musthave restricts links to only those containing this string
+     *  @param image_mustnothave restricts links and image names to only those that don't contain this string
+     *  @param image_ignore restricts the beginning of image names
+     *  @param image_type restricts the type of images included in the collage to those named */
+    public DownloadUrls(GsdlCollageApplet app, 
+			DownloadImages download_images, String starting_url, 
+			String href_musthave, String image_mustnothave, 
+			String image_ignore, String image_type, String document_root,int verbosity, MediaTracker trk)
+    {
+	super("DownloadUrls");
+	app_             = app;
+	download_images_ = download_images;
+
+	starting_url_  = starting_url;
+	href_musthave_ = href_musthave;
+	image_mustnothave_ = image_mustnothave;
+	image_ignore_  = image_ignore;
+	image_type_    = image_type;
+	document_root_ = document_root; 
+	verbosity_ = verbosity;
+        tracker = trk;
+
+	System.err.println("starting_url_ "  + starting_url +"\n"+
+	                   "href_musthave_ " +  href_musthave +"\n"+
+			   "image_mustnothave_ " + image_mustnothave+"\n"+
+			   "image_ignore_ "+  image_ignore+"\n"+
+			   "image_type_ "+ image_type+"\n"+
+			   "document root "+ document_root_
+			       );
+
+
+
+
+    }
+
+  
+    /** Determines whether or not a url has already been examined
+     *
+     *  @param url_string the url to check
+     *  @return true if the url has been visited, false if not */
+    public boolean already_visited(String url_string)
+    {
+	int hash_pos = url_string.indexOf("#");
+	if (hash_pos>0)
+	 {
+	     // strip off #anchor reference
+	     url_string = url_string.substring(0,hash_pos);
+	 }
+
+      	// if the url has been visited before, return true
+	if (visited_url_.containsKey(url_string))
+	{
+	    if (verbosity_ > 3)
+		{
+		    System.err.println("Visited " + url_string + " before!");
+		}
+	    return true;
+	}
+	
+	visited_url_.put(url_string,"visited");
+
+	return false;
+    }
+
+  /** Determines whether or not an images or its screenview has been visited)
+     *  has already been examined
+     *
+     *  @param url_string the url to check
+     *  @param img_name the image to check
+     *  @return true if the url has been visited, false if not */
+    public boolean image_visited(String url_string, String img_name)
+    {
+	String hash_dir = url_string.substring(0,url_string.lastIndexOf("/"));  
+    
+ 	if ( visited_images_.containsKey(hash_dir)){
+	    Hashtable hashed_images = (Hashtable)visited_images_.get(hash_dir);   
+	   
+            if (img_name.startsWith("screenview")){
+		return true;
+	    }
+
+	    if (hashed_images.containsKey(img_name)){
+		return true;
+	    }
+         
+            Enumeration enu = hashed_images.keys();
+            for(;enu.hasMoreElements();){
+		String name = (String)enu.nextElement();
+		if(name.startsWith("screenview")){
+		    return true;
+		}
+	    }
+    
+            hashed_images.put(img_name,"visited");           
+	}
+	else{
+	    Hashtable hashed_images = new Hashtable();
+	    hashed_images.put(img_name,"visited");
+	    visited_images_.put(hash_dir,hashed_images);	    
+	}
+	     
+	return false;
+    }
+
+   
+
+
+
+    /** Restricts the type of images that can be included in the collage
+     *
+     * @param url_string the url to check
+     * @return true if the image is of a specified type, false if not */
+    public boolean image_file_extension(String url_string)
+    {
+	// lower case comparisons
+	String url_lstring = url_string.toLowerCase();
+
+	if (image_type_ == null)
+	    return true;
+
+	String tmp = image_type_;
+	String original_image_type_ = image_type_;
+
+	while (image_type_ != null && image_type_.indexOf("%") >= 0) {
+	    
+	    tmp = image_type_.substring(0, image_type_.indexOf("%"));
+             
+	    if (image_type_.length() > image_type_.indexOf("%") + 1)
+		image_type_ = image_type_.substring(image_type_.indexOf("%") + 1, image_type_.length());                 
+	    else
+		image_type_ = null;
+            
+	    if (!tmp.trim().equals("") && url_lstring.endsWith(tmp)) {
+		image_type_ = original_image_type_;
+		return true;
+	    }
+	}
+
+	if (image_type_ != null && url_lstring.endsWith(image_type_)) {
+		image_type_ = original_image_type_;
+		return true;
+	}
+
+	image_type_ = original_image_type_;
+	return false;
+    }
+
+    /** Restricts images to only those that satisfy several specified conditions
+     *  regarding the content of the image name and url.
+     *
+     *  @param url_string the url to check
+     *  @return true if the image is satisfactory, false if not */
+    public boolean filter_image(String url_string)
+    {
+        
+	if (image_ignore_==null || !url_string.startsWith(image_ignore_))
+	{
+	    if (!already_visited(url_string))
+	    {
+		if (image_mustnothave_ != null) {
+		    
+		    String tmp = image_mustnothave_;
+		    String original_image_mustnothave_ = image_mustnothave_;
+
+		    while (image_mustnothave_ != null && image_mustnothave_.indexOf("%") >= 0) {
+	    
+			tmp = image_mustnothave_.substring(0, image_mustnothave_.indexOf("%"));
+			if (image_mustnothave_.length() > image_mustnothave_.indexOf("%") + 1)
+			    image_mustnothave_ = image_mustnothave_.substring(image_mustnothave_.indexOf("%") + 1, 
+									      image_mustnothave_.length());
+			else
+			    image_mustnothave_ = null;
+                          			
+                     
+                         
+			if (!tmp.trim().equals("") && url_string.indexOf(tmp) >= 0) {
+			   
+			    image_mustnothave_ = original_image_mustnothave_;
+			    return false;
+			}
+       		    }
+
+		    image_mustnothave_ = original_image_mustnothave_;
+                    
+		    if (image_mustnothave_ != null && url_string.indexOf(image_mustnothave_) >= 0) {
+			image_mustnothave_ = original_image_mustnothave_;
+			return false;
+		    }
+
+		    if (verbosity_ > 2) {
+			System.err.println("src url = "+ url_string);
+		    }
+		    
+		    image_mustnothave_ = original_image_mustnothave_;
+		    
+		}
+	
+	    }
+            
+	}
+    
+	return true;
+    }
+
+    /** Restricts links to only those that satisfy several specified conditions
+     *  regarding the address of the link.
+     *
+     *  @param url_string the url to check
+     *  @param new_url_string the url from which this link was found
+     *  @param depth the number of links followed on this path
+     *  @return true if the image is satisfactory, false if not */
+    public boolean filter_href(String url_string, String new_url_string, int depth)
+    {
+	boolean has_href = false;
+	String tmp = href_musthave_;
+	String original_href_musthave_ = href_musthave_;
+
+	// checks that it does contain this content
+	if (href_musthave_ !=  null) {
+
+	    while (href_musthave_ != null && href_musthave_.indexOf("%") >= 0) {
+		
+		tmp = href_musthave_.substring(0, href_musthave_.indexOf("%"));
+		if (href_musthave_.length() > href_musthave_.indexOf("%") + 1)
+		    href_musthave_ = href_musthave_.substring(href_musthave_.indexOf("%") + 1, href_musthave_.length());
+		else
+		    href_musthave_ = null;
+		
+		if (url_string.indexOf(tmp) >= 0)
+		    has_href = true;
+	    }
+	    
+	    if (href_musthave_ != null && url_string.indexOf(href_musthave_) >= 0)
+		has_href = true;
+
+	    href_musthave_ = original_href_musthave_;
+	}
+
+	tmp = image_mustnothave_;
+	String original_image_mustnothave_ = image_mustnothave_;
+
+	// checks that it doesn't contain this content
+	if (image_mustnothave_ != null) {
+
+	    while (image_mustnothave_ != null && image_mustnothave_.indexOf("%") >= 0) {
+		
+		tmp = image_mustnothave_.substring(0, image_mustnothave_.indexOf("%"));
+		if (image_mustnothave_.length() > image_mustnothave_.indexOf("%") + 1)
+		    image_mustnothave_ = image_mustnothave_.substring(image_mustnothave_.indexOf("%") + 1, image_mustnothave_.length());
+		else
+		    image_mustnothave_ = null;
+		
+		if (url_string.indexOf(tmp) >= 0)
+		    has_href = false;
+	    }
+	    if (image_mustnothave_ != null && url_string.indexOf(image_mustnothave_) >= 0)
+		has_href = false;
+	  
+	    image_mustnothave_ = original_image_mustnothave_;
+	}
+
+	// return true if the link is valid and false if not
+	if (href_musthave_==null || has_href)
+	{
+	    // might be another URL
+	    if (depth < app_.maxDepth())
+	    {
+		if (!new_url_string.startsWith(url_string))
+		{
+		    return true;
+		}
+	    }
+	}
+	return false;
+    }
+
+    /** Adds an image to the stored downloaded images as a triplet.
+     *  Ensures that the number of images downloaded but not displayed at
+     *  any one time is controlled by using a buffer. If the buffer is
+     *  full this function will wait until space becomes available before
+     *  continuing. It also restricts the
+     *  total number of images to download as specified by the applet.
+     *
+     *  @param url the image to download
+     *  @param from_url the url that this image was sourced from
+     *  @param img_name the name of the image */
+    public void add_image(URL url, String from_url, String img_name)
+    {
+	    // get the image from the url
+	    if (verbosity_>=2) {
+		System.err.println("  Downloading image URL: " + url.toString());
+	    }
+            
+           if (image_visited(url.toString(),img_name)) return;
+
+	   int size = download_images_.downloadImage(tracker,url, from_url, img_name);
+          
+	   try{
+	       // if have completed the maximum number of downloads for the
+	       // application then stop
+	       if (size == app_.maxDownloads()) {
+		   stop();
+	       }
+	       
+	   }
+	   catch (Exception e) {
+	       thread_running_ = false;
+	       stop();
+	       e.printStackTrace();
+	   }
+    }
+
+    /** Connects to the starting url and looks for all images and links from this
+     *  original page. Image links are processed first, so that any images found can be
+     *  downloaded immediately and placed on the applet. Secondly, the links to other
+     *  pages are recursively processed by this function and treated as a starting url
+     *
+     *  @param new_url the url from which to start searching for images and links
+     *  @param depth the number of links that have been followed on this path */
+    public void rec_add_images(String new_url, int depth)
+    {
+   
+	if (already_visited(new_url)) return;
+
+	// check if there is a scenario where external hyperlinks are being used
+	externalLinks();
+	String img_name = new String();
+
+	// connect to the url
+	CURL curl = new CURL(new_url);
+	if (curl.connected_ok())
+	{
+	    if (verbosity_ >= 1) {
+		System.err.print("Connected OK ... ");
+	    }
+
+	    // read the page
+	    curl.readAll();
+	    if (verbosity_ >= 1) {
+		System.err.println("URL read.");
+	    }
+
+	    // get all the <code><img src=</code> links into a vector
+	    Vector src_links = curl.getSrcLinks();
+
+        	    
+	    if (verbosity_ >= 2) {
+		System.err.println("  Got src links... there are " + src_links.size() + " of them.");
+	    }
+	    // process each of the image links according to the parameters given.
+	    for (int i = 0; i < src_links.size(); i++) 
+	    {
+		URL url = (URL)src_links.get(i);
+		String url_string  = url.toString();
+            
+		//System.err.println(" source links " + i + " ["  + url_string +"]");
+
+		if (verbosity_ >= 4) {
+		    System.err.println("    Unfiltered: src_link[" + i + "] = " + url_string);
+		}
+
+		if (image_file_extension(url_string))
+		    {
+			if (filter_image(url_string))
+			    {
+				img_name = url_string.substring(url_string.lastIndexOf("/") + 1, url_string.length());
+                                
+				if (verbosity_ >= 2) {
+				    System.err.println("    Filtered: src_link[" + i + "] = " + url_string);
+				}
+
+				if ((external_links_ != null) && (!external_links_.isEmpty())) {
+				    String ext = (String) external_links_.get(img_name);
+				          
+				           
+				    if (ext != null){
+					add_image(url, ext, img_name);
+                                       
+				    }
+				    else{
+				
+					add_image(url, new_url, img_name);
+				    }
+				}
+				else {
+				    
+				    add_image(url, new_url, img_name);
+				}
+                           
+				
+			    }
+		
+		    }
+		
+	    }
+
+	    // get all the <code><a href=</code> links into a vector
+	    Vector href_links = curl.getHrefLinks();
+	    
+	  
+	    if (verbosity_ >= 2) {
+		System.err.println("  Got href links... there are " + href_links.size() + " of them.");
+	    }
+	    
+
+	    // process each of the href links according to the parameters given.
+	    for (int i = 0; i < href_links.size(); i++) 
+		{
+           
+		    URL url = (URL)href_links.get(i);
+		    String url_string  = url.toString();
+		    //System.err.println(" href links " + i + "["  + url_string +"]");      
+
+		    if (image_file_extension(url_string))
+			{
+			                      
+			    if (filter_image(url_string))
+
+				{
+				 					    
+				    img_name = url_string.substring(url_string.lastIndexOf("/") + 1, url_string.length());
+
+				    if ((external_links_ != null) && (!external_links_.isEmpty())) {
+					String ext = (String) external_links_.get(img_name);
+
+					if (ext != null) 
+					    add_image(url, ext, img_name);
+					else
+					    add_image(url, new_url, img_name);
+				    }
+				    else {
+					add_image(url, url_string, img_name);
+				    }
+				}
+			}
+		    else
+			{
+			    if (filter_href(url_string,new_url,depth))
+				{
+		
+				    rec_add_images(url_string,depth+1);
+				    
+				}
+			}
+		}
+	}
+   
+	else {
+	    System.err.println("Unable able to download "+new_url);
+	}
+    }
+
+    
+    /** Used in cases where the image maps to a url outside of it's original location.
+     *  When used with Greenstone the collage images will refer to documents in the collections
+     *  from which the images are sourced. When used individually, the images may be saved into
+     *  a user directory and the pages they reference may be external hyperlinks. 
+     *  This function reads that external links file and creates a hash map of the image to
+     *  its external hyperlink. If the file does not exist the download thread will continue
+     *  and assume the first case, that links are internal. */
+    public void externalLinks() {
+	external_links_ = null;
+	try {
+
+	    if (starting_url_ == null || (document_root_ != null && starting_url_.indexOf(document_root_) >= 0) ){ 
+		return;
+	    }
+	    
+	    // open a url to the file written
+	    URL u = new URL(starting_url_ + "externallinks");
+
+	    BufferedReader r = new BufferedReader(new InputStreamReader(u.openStream()));
+	    
+	    external_links_ = new Hashtable();
+
+	    String l = r.readLine();
+	    // split the line of the space, first part is the image, second part the link
+	    while (l != null) {
+	
+		String tmp1 = new String();
+		String tmp2 = new String();
+
+		if (l.indexOf(" ") >= 0) {
+		    
+		    tmp1 = l.substring(0, l.indexOf(" "));
+		    if (l.length() > l.indexOf(" ") + 1)
+			tmp2 = l.substring(l.indexOf(" ") + 1, l.length());
+		    else
+			tmp2 = null;
+		    
+		    if (tmp2 != null) {
+			
+			external_links_.put(tmp1, tmp2);
+			//System.err.println(tmp1 + " " + tmp2);
+		    }
+		}
+		l = r.readLine();
+	    }
+		
+		r.close();
+
+	} catch (Exception e) {
+	    e.printStackTrace();
+	    return;
+	}
+    }
+
+    /** Controls the download thread */
+    public void run () 
+    {
+	System.err.println("Starting download thread.");
+	visited_url_ = new Hashtable();
+	visited_images_ = new Hashtable();
+
+       	rec_add_images(starting_url_,1);
+	download_images_.stopDownload();
+	System.err.println("Download thread finished.");
+    }
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/GsdlCollageApplet.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/GsdlCollageApplet.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/GsdlCollageApplet.java	(revision 16561)
@@ -0,0 +1,406 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.net.*;
+
+/**
+ * 
+ *  @author Katrina Edgar
+ *  @author David Bainbridge
+ *
+ * Main class for the GsdlCollageApplet<br>
+ * Processes the parameters passed through the Applet<br>
+ * Builds an appropriate starting url from these parameters<br>
+ * Creates a storage class for downloaded images<br>
+ * Starts thread to download images and their associated url<br>
+ * Starts thread to display downloaded images on the applet screen<br>
+ */
+ 
+public class GsdlCollageApplet extends Applet {
+
+
+    /** Amount of error checking output produced <br>
+     *  Ranges from 0 - no output to 3 - maximum output */ 
+    protected int verbosity_     = 0;
+    /** Indicates whether java2 functionality should be used <br>
+     *  If true will allow advanced image processing techniques to occur, 
+     *  such as fading and colouring using pixel manipulation <br>
+     *  If false, images will maintain an alpha value of 1 (appear solid), 
+     *  newer images will simply be pasted on top of existing images <br> */
+    protected boolean isJava2_  = true;
+    /** Number of nested links to follow When used with greenstone,
+     *  controls to which level of the document links will be followed For
+     *  example a maximum depth of 1 will mean only the top page of a
+     *  document will be examined, while a depth of 2 will include sections
+     *  within this document, 3 includes sections within sections, and so
+     *  on */
+    protected int maxDepth_     = 3;
+
+    /** Maximum number of images to display on the canvas */
+    protected int maxDisplay_ = 25;
+
+    /** Maximum number of downloaded images to store <br> Prevents applet
+     *  from using excessive amounts of bandwidth by downloading too many
+     *  images simulataneously*/
+    protected int maxDownloads_ = Integer.MAX_VALUE;
+
+    /** Types of images permitted in the collage, for example gif, jpg,
+     * png... */
+    protected String imageType_ = ".jpg%.png";
+
+    /** Caption of the collage */
+    protected String caption_ = "";
+
+    /** Background color of applet screen */
+    protected Color bgcolor_ = new Color(150, 193, 155);
+
+    /** Time lapse between repainting of the applet */
+    protected int refreshDelay_ = 2000;
+    
+    /** Stores an image and url pair and provides associated methods */
+    protected DownloadImages download_images_ = null;
+    /** Downloads images from a starting url, recursively follows nested
+     * links */
+    protected DownloadUrls download_thread_   = null;
+    /** Image processing and placement on applet screen */
+    protected DisplayImages display_thread_   = null;
+
+   
+
+    /** Gets verbosity */
+    public int verbosity()    { return verbosity_; }
+    /** Gets maximum depth for nested links */
+    public int maxDepth()     { return maxDepth_; }
+
+    /** Gets maximum number of images to display */
+    public int maxDisplay() { return maxDisplay_;}
+
+    /** Gets maximum number of images to store during downloading */
+    public int maxDownloads() { return maxDownloads_; }
+    /** Gets the refresh delay used between repaint */
+    public int refreshDelay() { return refreshDelay_; }
+
+    /** Gets parameters from the applet code and stores locally.
+     * Forms a starting url for image retrieval to begin from.
+     * The starting url is formed either by:
+     *  * Using the image_url parameter as provided, which is assumed to 
+     *     be a complete url
+     *  * If this parameter does not exist, the assumption is that the 
+     *     collage is being incorporated with the Greenstone Digital Library 
+     *     Software. The starting url is formed by concatenating
+     *     the gwcgi, collection and classifier parameters as provided.
+     * Then starts downloading and displaying images */
+
+    Thread paint ;
+
+    public void init() 
+    {	    
+	if (verbosity_ >= 4) { 
+	    System.err.println("Attempting to retrieve parameters...");
+	}
+
+	// gets the parameters specified
+	String verbosity_param      = getParameter("verbosity");
+	String is_java2_param       = getParameter("isJava2");
+	String max_depth_param      = getParameter("maxDepth");
+	String max_downloads_param  = getParameter("maxDownloads");
+	String max_display_param    = getParameter("maxDisplay");
+	String refresh_delay_param  = getParameter("refreshDelay");
+	String image_type_param     = getParameter("imageType");
+	String bgcolor_param        = getParameter("bgcolor");
+	String caption_param        = getParameter("caption");
+        String document_root        = getParameter("documentroot");
+        String gwcgi                = getParameter("gwcgi");        
+
+
+	// Check it isn't null
+	if ((verbosity_param!=null) && (!verbosity_param.startsWith("_"))) {
+	    verbosity_  = Integer.parseInt(verbosity_param);
+	}
+	else {
+	    verbosity_ = 1;
+	}
+
+
+	if (verbosity_ >= 4) { 
+	    System.err.println("Got parameters.");
+	}
+
+	if (caption_param != null && !caption_param.startsWith("_")) {
+	    caption_ = caption_param;
+	}
+	else {
+	    if (verbosity_ >= 4) { 
+		System.err.println("No Caption: setting to a space.");
+	    }
+	    caption_ = " ";
+	}
+
+	if ((bgcolor_param != null) && (!bgcolor_param.startsWith("_"))) {
+	    if (bgcolor_param.startsWith("#")) {
+		bgcolor_ = Color.decode(bgcolor_param);   
+	    }
+	    else {
+		String [] c = bgcolor_param.split(",");
+		if (c.length == 3)
+		    bgcolor_ = new Color(Integer.parseInt(c[0]), Integer.parseInt(c[1]), Integer.parseInt(c[2]));
+	    }
+	    if (verbosity_ >= 4){ 
+		System.err.println("Set BG to be " + bgcolor_.toString());
+	    }
+	}
+	else {
+	    if (verbosity_ >= 4) { 
+		System.err.println("No BG: setting to NZDL green.");
+	    }	   
+	    bgcolor_ = new Color(150, 193, 155);
+	}
+
+	if ((image_type_param != null) && (!image_type_param.startsWith("_"))) {
+	    imageType_ = image_type_param;
+	}
+
+	if ((is_java2_param == null) || (is_java2_param.equals("auto")) || (is_java2_param.startsWith("_"))) {
+	    String version = System.getProperty("java.version");
+	    version = version.substring(0, version.lastIndexOf("."));
+	    System.err.println("VERSION: " + version);
+	    float ver = (new Float(version)).floatValue();
+	    isJava2_ = (ver >= 1.2) ? true : false;
+	}
+	else {
+	    isJava2_ = (is_java2_param.equals("true")) ? true : false;
+	}
+
+	if ((max_depth_param != null) && (!max_depth_param.startsWith("_"))) {
+	    // System.err.println("maxDepth = " + max_depth_param);
+	    maxDepth_ = Integer.parseInt(max_depth_param);
+	}
+
+	if ((max_downloads_param!=null) && (!max_downloads_param.startsWith("_"))) {
+	    maxDownloads_ = Integer.parseInt(max_downloads_param);
+            System.out.println(" maxDownloads " + maxDownloads_ );
+	    maxDownloads_=50;
+	}
+
+	if ((max_display_param!=null) && (!max_display_param.startsWith("_"))) {
+	    maxDisplay_ = Integer.parseInt(max_display_param);
+              
+	}
+
+	if ((refresh_delay_param!=null) && (!refresh_delay_param.startsWith("_"))) {
+	    refreshDelay_ = Integer.parseInt(refresh_delay_param);
+	}
+
+       
+        if (document_root !=null){          
+	    document_root = document_root.substring(1);
+            if (document_root.indexOf("/") > 0 ){
+		document_root = document_root.substring(0, document_root.indexOf("/"));
+	    }
+	    else{
+		document_root = gwcgi; 	
+	    }
+	}
+
+
+	String image_url  = getParameter("imageURL"); 
+	String image_ignore  = getParameter("imageIgnorePrefix"); 
+	String href_musthave  = getParameter("hrefMustHave"); 
+	String image_mustnothave = getParameter("imageMustNotHave");
+
+	// builds starting url when incorporated with Greenstone
+	if (image_url==null)
+	{
+	    String gwcgi_param  = getParameter("gwcgi");
+	    gwcgi_param = tidy_URL(gwcgi_param, true);
+	
+	    String collection_param  = getParameter("collection");
+	    String classifier_param  = getParameter("classifier");
+
+	    image_url = gwcgi_param + "a=d";
+	    image_url += "&c=" + collection_param;
+	    image_url += "&cl=" + classifier_param;
+	}
+
+	MediaTracker trk = new  MediaTracker(this);
+
+	// creates a class to store the image and it's associated url
+	download_images_ = new DownloadImages( this, verbosity_,isJava2_ );
+	// starts the download image thread with the starting url
+
+        download_thread_ = new DownloadUrls(this, download_images_, 
+					    image_url, href_musthave, image_mustnothave, 
+					    image_ignore, imageType_,document_root,verbosity_,trk);
+	// starts the display image thread with the currently downloaded images
+	display_thread_ = new DisplayImages(this, download_images_,isJava2_, bgcolor_);
+	
+    }
+
+ 
+    /** Goes to the url associated with the image that is clicked on screen<br>
+     *  Displays the url containing the image in a new window */
+    public boolean mouseDown(Event event, int x, int y)
+    {
+	// determines which image was clicked on
+	CollageImage cimage = display_thread_.clickedOnImage(x,y);
+	// if they were clicking on an image (as opposed to background)
+	if (cimage != null)
+	{
+	    System.err.println("Click on image: from url = " + cimage.from_url_);
+	    try {
+		// displays the associated url in a new window
+                URL from_url = null;		
+		
+		java.util.regex.Pattern p = java.util.regex.Pattern.compile("cl=CL\\d(\\.\\d)*\\s");
+		java.util.regex.Matcher m = p.matcher(cimage.from_url_.trim()+" ");
+
+                if (m.find() ){
+		    from_url = cimage.url_;
+		}
+		else{
+		    from_url =  new URL(cimage.from_url_  + "#" + cimage.name_);
+		}
+		getAppletContext().showDocument(from_url,"gsdlDoc");
+	    } 
+	    catch (MalformedURLException e) { 
+		e.printStackTrace();
+	    }
+
+	}
+        return true;
+    }
+    
+    /** Start threads for downloading and displaying images */
+    public void start() 
+    {
+	
+	download_thread_.start();	
+
+	display_thread_.start();
+
+         paint =  new Thread(new Runnable(){
+                public void run() {
+		    try {
+
+			Thread curr_thread = Thread.currentThread();
+	    
+			while (curr_thread == paint) {
+	      
+			   repaint();
+			    
+			    Thread.sleep(2000);
+			    curr_thread = Thread.currentThread();
+			}
+
+	    
+		    } catch (Exception e) {
+			
+		    }
+		}
+
+
+	    });
+
+	    paint.start();
+   
+    }
+
+    /** Stops threads for downloading and displaying images */
+    public void stop() 
+    {
+	download_thread_.stop();
+	display_thread_.stop();
+	
+	download_thread_ = null;
+	display_thread_ = null;
+	
+    }
+
+    /** Destroys threads for downloading and displaying images */
+    public void destroyed() 
+    {
+	download_thread_ = null;
+	
+	display_thread_ = null;
+	
+    }
+
+
+    /** Repaints the applet */
+    public void update(Graphics g) 
+    { 
+	// System.err.println("Update called");
+	paint(g); 
+    } 
+    
+    /** Repaints the applet using the paint method of the thread that is
+     * currently displaying images */
+    public void paint(Graphics g) {
+	if (display_thread_!=null)
+	    {	
+	      
+	    	//display_thread_.display_collage();
+	    	  display_thread_.paint(g);
+	    }
+	else
+	    {
+		System.err.println("Applet still trying to paint!!");
+	    }
+
+	
+	
+    }
+
+  
+    
+    /** Ensures a URL address (as string) has a protocol, host, and file.
+     *
+     * If the URL is a CGI script URL, it should be tidied up so that it is
+     * appropriate to tage attrib=value pairs on the end.  This means it
+     * must either end with a "?" or (if it contains a question-mark
+     * internally) end with a "&". */
+    String tidy_URL(String address, boolean isCGI) {
+
+	// System.err.println("tidy URL: " + address);
+	
+	// make sure the URL has protocol, host, and file
+	if (address.startsWith("http")) {
+	    // the address has all the necessary components
+	} else if (address.startsWith("/")) {
+	    // there is not protocol and host
+	    URL document = getDocumentBase();
+	    String port = "";
+	    if (document.getPort()!=-1) {
+		port = ":" + document.getPort();
+	    }
+	    address = "http://" + document.getHost() + port  + address;
+	} else {
+	    // this URL is relative to the directory the document is in
+	    URL document = getDocumentBase();
+	    String directory = document.getFile();
+	    int end = directory.lastIndexOf('/');
+	    String port = "";
+	    if (document.getPort()!=-1) {
+		port = ":" + document.getPort();
+	    }
+	    directory = directory.substring(0,end + 1);
+	    address = "http://" + document.getHost() + port + directory + address;
+
+	}
+
+	// if the URL is a cgi script, make sure it has a "?" in ti,
+	// and that it ends with a "?" or "&"
+	if (isCGI) {
+	    if (address.indexOf((int) '?') == -1) { 
+		address = address + "?";
+	    } else if (!address.endsWith("?")) {
+		address = address + "&";
+	    }
+	}
+
+	return address;
+    }
+
+
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Makefile
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/Makefile	(revision 16561)
@@ -0,0 +1,20 @@
+
+
+
+CLASSES = CURL.class CollageImage.class DisplayImages.class \
+          DownloadImages.class DownloadUrls.class GsdlCollageApplet.class \
+          MyAffineTransform.class
+
+all: GsdlCollageApplet.jar
+
+install:
+	cp GsdlCollageApplet.jar ../../../../../../bin/java/
+
+clean:
+	rm -f $(CLASSES) GsdlCollageApplet.jar
+
+GsdlCollageApplet.jar: $(CLASSES)
+	jar cf GsdlCollageApplet.jar org
+
+%.class : %.java
+	javac -d . -classpath ../../../.. $<
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/MyAffineTransform.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/MyAffineTransform.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/GsdlCollageApplet/MyAffineTransform.java	(revision 16561)
@@ -0,0 +1,154 @@
+package org.nzdl.gsdl.GsdlCollageApplet;
+
+public class MyAffineTransform {
+
+    public double scaleX = 0;
+    public double scaleY = 0;
+    public double translateX = 0;
+    public double translateY = 0;
+    public boolean overlap = false;
+
+    public MyAffineTransform (int image_x_dim, int image_y_dim) {
+
+	random_set_pos(image_x_dim, image_y_dim);
+    }
+
+    /** The amount to adjust the size of an image so that it fits into the applet screen
+     *  @param image_x_dim the width of the image to scale
+     *  @param image_y_dim the height of the image to scale
+     *  @return a value by which to scale the image to ensure it will fit in the applet */
+    private double scaleFactor (int image_x_dim, int image_y_dim) {
+
+	double scale_factor = 1.0;
+
+	// three quarters of the application width & height
+	int tol_app_x_dim = 3*DisplayImages.app_x_dim_/4;
+	int tol_app_y_dim = 3*DisplayImages.app_y_dim_/4;
+
+	// if the image is too big, calculate a fraction
+	// by which to reduce the image
+	if (image_x_dim>tol_app_x_dim)
+	{
+	    scale_factor = image_x_dim/tol_app_x_dim + 1;
+	}
+
+	if (image_y_dim>tol_app_y_dim)
+	{
+	    int y_scale_factor = image_y_dim/tol_app_y_dim + 1;
+	    // take the biggest of the two as the same fraction must
+	    // be applied to both the x and y values to keep the image
+	    // in proportion
+	    if (y_scale_factor>scale_factor)
+	    {
+		scale_factor = y_scale_factor;
+	    }
+	}
+
+	return scale_factor;
+    }
+
+    /** Determines the placement of new images on the applet screen <br>
+     *  First the image is scaled, then the function loops until a satisfactory
+     *  image position is found. This is done by randomly selecting an x and y
+     *  co-ordinate for the image, then determining how much whitespace will be
+     *  covered if the image were to be placed at this position in the collage.
+     *  The standards for loop termination are as follows:<br>
+     *  1. On any occasion a whitespace coverage of greater than 70% will result 
+     *  in the loop being terminated.<br>
+     *  2. After three times through the loop, a whitespace coverage of 50% will
+     *  also allow the loop to terminate (a less stringent condition). <br>
+     *  3. After seven times through, the loop will terminate and the best
+     *  position will be used by default.<br>
+     *  This function also updates the used space array after image position has been selected.
+     *  
+     *  @param image_x_dim the width of the image
+     *  @param image_y_dim the height of the image
+     *  @return a transform operation by which to move the image from the origin */
+    protected void random_set_pos(int image_x_dim, int image_y_dim) 
+    {
+
+	double scale_factor = scaleFactor(image_x_dim, image_y_dim);
+
+	// scale the image (will be 1 and therefore no change as a default)
+	image_x_dim/=scale_factor;
+	image_y_dim/=scale_factor;
+	
+	// initialise variables required to determine image placement
+	int image_x_pos = 0;
+	int image_y_pos = 0;
+	int best_x = 0;
+	int best_y = 0;
+	double best_clear = 0;
+	double clear = 100;
+	int stoploop = 0;
+	
+	do {
+	    // increment the loop counter
+	    stoploop++;
+
+	    // the number of positions that the image could occupy on the x axis
+	    int safe_x_dim = DisplayImages.app_x_dim_ - image_x_dim;
+	    double rx = Math.random();
+	    // randomly pick an x co-ordinate for the image
+	    image_x_pos= ((int)(rx*safe_x_dim));
+	    
+	    // same process as for the x co-ordinate
+	    // ensuring the y co-ordinate will fit on the screen
+	    int safe_y_dim = DisplayImages.app_y_dim_ - image_y_dim;
+	    double ry = Math.random();
+	    // randomly pick a y co-ordinate for the image
+	    image_y_pos= ((int)(ry*safe_y_dim));
+	    
+	    double whitespace = 0;
+	    double space = 0;
+	    
+	    // determine how much whitespace the image will cover if placed in the
+	    // current position and how much overlap will occur
+	    for (int n = image_x_pos; n < (image_x_pos + image_x_dim); n++) {
+		for (int m = image_y_pos; m < (image_y_pos + image_y_dim); m++) {
+		    if (DisplayImages.used_space[n][m] == 0)
+			whitespace++;
+		    space++;
+		}
+	    }
+	    
+	    // divide the amount of whitespace covered by the amount of overlap
+	    clear = whitespace/space;
+	    // if this value is better than previous calculations, save these co-ordinates
+	    if (clear > best_clear) {
+		best_x = image_x_pos;
+		best_y = image_y_pos;
+		best_clear = clear;
+	    }
+
+	    if ((image_x_dim > (DisplayImages.app_x_dim_ / 2)) || (image_y_dim > (DisplayImages.app_y_dim_ / 2)))
+		break;
+
+	} while ((clear < 0.70 || (stoploop > 3 && clear < 0.50)) && stoploop < 7);
+	    
+	// if we only terminated because we went through so many times,
+	// then we set the position to the best value possible
+	if (stoploop == 7) {
+	    image_x_pos = best_x;
+	    image_y_pos = best_y;
+	}
+
+	// indicate that the space where the image is to be placed is now used
+	for (int n = image_x_pos; n < (image_x_pos + image_x_dim); n++)
+	    for (int m = image_y_pos; m < (image_y_pos + image_y_dim); m++) {
+		DisplayImages.used_space[n][m]++;
+		if (DisplayImages.used_space[n][m] > DisplayImages.NO_IMAGES_OF_OVERLAP)
+		    overlap = true;
+	    }
+	
+	translateX = image_x_pos;
+	translateY = image_y_pos;
+	
+	// scale the image appropriately
+	scaleX = 1.0/scale_factor;
+	scaleY = 1.0/scale_factor;
+	
+	return;
+    }
+
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Makefile
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Makefile	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Makefile	(revision 16561)
@@ -0,0 +1,18 @@
+
+
+
+CLASSES = Phind.class ResultCanvas.class ResultItemDocument.class \
+          ResultTitle.class PhindTitle.class ResultDisplay.class \
+          ResultItemLink.class ResultBox.class ResultItem.class \
+          ResultItemPhrase.class PhindTitle.class URLUTF8Encoder.class
+
+all: Phind.jar
+
+clean:
+	rm -f $(CLASSES) Phind.jar
+
+Phind.jar: $(CLASSES)
+	jar cf Phind.jar org
+
+%.class : %.java
+	javac -target 1.1 -d . -classpath ../../../.. $<
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Phind.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Phind.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/Phind.java	(revision 16561)
@@ -0,0 +1,1119 @@
+/**********************************************************************
+ *
+ * Phind.java -- the Phind java applet
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+
+/*********************************************************************
+
+To use the applet, you'll need to embed it in a web page like this:
+
+<APPLET CODE="Phind.class" WIDTH=500 HEIGHT=500>
+
+  <PARAM NAME=collection  VALUE="fao.org">
+  <PARAM NAME=classifier  VALUE="1">
+  <PARAM NAME=phindcgi    VALUE="http://kowhai/cgi-bin/phindcgi">
+  <PARAM NAME=library     VALUE="http://kowhai/cgi-bin/library">
+  <PARAM NAME=backdrop    VALUE="http://kowhai/~paynter/transfer/phindtest/green1.jpg">
+  The Phind java applet.
+</APPLET>
+
+There are a bunch of other parameters; these are described in the 
+getParameters method below.  It is all done for you in Greenstone
+in the document.dm macro file (the _phindapplet_ macro).
+
+You may have problems with Java applet security.  Java applet's can only
+open socket connections (including the HTTP connections the applet uses 
+to get data) to the same server the applet was loaded from.  This means
+that your phindcgi, library, and (optional) backdrop URLs must be on the
+same machine that your web page was loaded from.
+
+**********************************************************************
+
+The applet comprises several classes:
+
+1. Phind (this file) is the applet class, loaded by the browser.
+   It also handles network connections.
+2. ResultDisplay is a Panel that sits in the applet and displays 
+   things; displays are connected in a doubly-linked list.
+3. ResultBox holds the results of a query.  Result boxes are shown
+   to the user through ResultDisplays.  Another doubly-linked list.
+4. ResultTitle acts as a caption to a ResultBox describing its contents.
+5. ResultCanvas is what the ResultBox data is drawn on.  It draws the
+   words on the screen, and handles user input from the mouse.
+6. ResultItem represents a single result object (a phrase or document).
+7. PhindTitle is for drawing backdrops in ResultDisplays.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Choice;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.Image;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.TextField;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.io.DataInputStream;
+import java.io.InputStream;
+
+import java.net.Socket;
+//import java.net.SocketTimeoutException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+import java.util.Vector;
+import java.util.Date;
+
+import org.nzdl.gsdl.Phind.URLUTF8Encoder;
+
+public class Phind extends java.applet.Applet {
+    
+    // What is the collection called?
+    public String collection;
+
+    // Which phind classifier are we using? (There may be more than one.)
+    public String classifier;
+
+    // Internet address of phind resources
+    public String library_address, phindcgi_address;
+
+    // Initial serach term
+    public String initialSearch;
+
+    // Number of phrases to retrieve at any one time
+    public int phraseBlockSize;
+
+    // Appearance parameters
+    public boolean vertical;
+    public int depth;
+
+    // Font
+    public int fontSize;
+    public String fontName;
+    public Font plainFont, boldFont;
+
+    // Do we want a background image in the applet?
+    public boolean showImage;
+    public String backdrop_address;
+    public Image backgroundImage;
+    public boolean showBorder;
+
+    // Colours
+    public Color panel_fg, panel_bg,
+	column_1_fg, column_1_bg, 
+	column_2_fg, column_2_bg,
+	highlight_fg, highlight_bg,
+	thesaurus_fg, thesaurus_bg, thesaurus_bar_fg, thesaurus_bar_bg,
+	expansion_fg, expansion_bg, expansion_bar_fg, expansion_bar_bg,
+	document_fg, document_bg, document_bar_fg, document_bar_bg,
+	message_fg, message_bg;
+    
+    // Column dimensions
+    int column_1_width, column_2_width;
+
+    // Where do we open new windows
+    String searchWindowName, documentWindowName;
+
+    // the mode of operation
+    int mode;
+    final int initMode = 0;
+    final int idleMode = 1;
+    final int searchMode = 2;
+
+    // Elements of the control panel
+    boolean showControlPanel;
+    Label titleLabel;
+    TextField wordField;
+    Button searchButton, prevButton, nextButton;
+
+    // Holders for the downloaded data
+    Panel resultPanel;
+    ResultDisplay firstDisplay, lastDisplay;
+
+    // The time at which the last query finished
+    Date lastQueryEndTime;
+
+    // lastQueryEndTime is stored to ensure a 1 second gap between a query
+    // returning and a new one beginning.  It is needed because the FAO
+    // folks in Rome have a huge lag, and frquently click several times
+    // while they wait; these clicks are turned into new queries, which
+    // they await again.  It is no elegant solution, but it seems like the
+    // easiest, given that I don't know threads.
+    // 1. The search button is easy to disable, and is disabled when a
+    //    socket connection is in progress.
+    // 2. ResutCanvas widgets can'r be similarly disabled because the
+    //    browser hides or wipes them, which looks very bad.
+    // 3. I cannot just ignore clicks on canvasses because the browser
+    //    caches the clicks while the socket connection is going on, and then
+    //    sends them through afterwards, when the canvas is accepting clicks
+    //    again.
+    // 4. Current sequence of events is to record the time the last query
+    //    ends, then whenever a click happens make sure a second has past.  if
+    //    you double-click the the first query is sent, returns, end-tie is
+    //    set, and the second (and any others made during query time) is
+    //    *immediately* processed, but since 1 second hasn't past it is
+    //    ignored.
+
+    /** We're adding a timeout to the loop used to read the result from the
+     *  server - partially because of the 'fails to return anything' death
+     *  spiral - but also because its a good idea anyway. Why DataInputStream
+     *  doesn't have a TimeOutException, given its most often used to get data
+     *  from a URL, I don't know. Lets default it to 60 seconds.
+     *  DLConsulting 12-07-2004
+     */
+    private int timeout = 60;
+
+    public String getAppletInfo() {
+	return "Phind by Gordon Paynter (paynter@cs.waikato.ac.nz). Copyright 1997-2000.";
+    }
+
+
+    public void init() {
+
+	mode = initMode;
+	
+        System.out.println("Java vendor: " + System.getProperty("java.vendor"));
+        System.out.println("Java version: " + System.getProperty("java.version"));
+
+	// Read applet parameters
+	getParameters();
+
+	// Initialise the user interface
+	setBackground(panel_bg);
+	lastQueryEndTime = new Date();
+
+	// fonts used to output text
+	plainFont = new Font(fontName, Font.PLAIN, fontSize);
+	boldFont  = new Font(fontName, Font.BOLD, fontSize);
+
+	// The phind applet layout manager
+	setLayout(new BorderLayout());
+
+	// Panel containing the displays is in the center of the display
+	resultPanel = new Panel();
+	if (vertical) {
+	    resultPanel.setLayout(new GridLayout(depth,1,0,2));
+	} else {
+	    System.out.println("horizontal");
+	    resultPanel.setLayout(new GridLayout(1,depth,2,0));
+	}
+	add("Center", resultPanel);
+
+	// Create ResultDisplays and place into the interface
+	ResultDisplay d1, d2 = null;
+	firstDisplay = new ResultDisplay(this, null);
+	resultPanel.add(firstDisplay);
+
+	if (depth == 1) {
+	    lastDisplay = firstDisplay;
+	} else {
+	    d1 = firstDisplay;
+	    for (int i = 2; i <= depth; i++) {
+		d2 = new ResultDisplay(this, d1);
+		resultPanel.add(d2);
+		d1 = d2;
+	    }
+	    lastDisplay = d2;
+	}
+
+	// The control panel
+	initialiseControlPanel();
+
+	// lets get started then
+	setStatus("Welcome to Phind.");
+	mode = idleMode;
+
+	// Perform initial search, if requested
+	if (initialSearch.length() > 0) {
+	    searchForWord(initialSearch);
+	}
+    }
+
+
+    // Display a message in the status bar
+    void setStatus(String status) {
+	showStatus(status);
+    }
+
+    // The user performs an action in the interface
+    public boolean action(Event evt, Object arg) {
+
+	if (evt.target == searchButton) {
+	    searchForWord(getSearchTerm());
+	} else if (evt.target == wordField) {
+	    searchForWord(getSearchTerm());
+	} else if (evt.target == prevButton) {
+	    shiftPrevious();
+	} else if (evt.target == nextButton) {
+	    shiftNext();
+	} else {
+	    System.out.println("unknown action: " + evt.toString() 
+			       + ", object: " + arg.toString());
+	}
+	return true;
+    }
+
+
+    // Search for a word 
+    //
+    // Called on two occasions:
+    //   when the "Search" Button is pressed, or
+    //   to perform an "initial search"
+    void searchForWord(String searchWord) {
+
+	if (mode == idleMode) {
+	    
+	    setSearchTerm(searchWord);
+
+	    // Convert the String from UTF8 charaters into 
+	    // an encoding that is okay for a URL.
+	    searchWord = URLUTF8Encoder.encode(searchWord);
+
+	    // Look up the word
+	    if (searchWord.length() > 1) {
+		setStatus("searching for \"" + searchWord + "\""); 
+		firstDisplay.emptyContents();
+		ResultBox result = lookupPhraseOnServer(null, false, searchWord, searchWord, 2);
+
+		// if there is an error, return
+		if (result == null) {
+		    setStatus("No results for \"" + searchWord + "\""); 
+		    return;
+		}
+
+		// display the result
+		result.display = firstDisplay.display(result);
+		result.resize(result.display.size());
+		result.paintAll(result.getGraphics());
+	    }
+
+	    enablePreviousAndNext();
+	}
+    }
+  
+
+    // Search for a phrase
+    //
+    // If querymode is 2, the user has clicked on a phrase.
+    // If querymode is 3, the user has requested more phrases.
+    // If querymode is 4, the user has requested more documents.
+    void searchForPhrase(ResultBox source, String key, String phrase, int queryMode) {
+
+	// System.out.println("searchForPhrase: " + key + " " + phrase + " " + queryMode);
+
+	if (mode == idleMode) {
+
+	    // If we are going to replace the first ResultDisplay, then empty it
+	    if (queryMode <= 2) {
+		if (source.display.next != null) source.display.next.emptyContents();
+	    }
+
+	    // look up the word
+	    setStatus("Searching for \"" + phrase + "\""); 
+	    ResultBox result = lookupPhraseOnServer(source, true, key, phrase, queryMode);
+	    if (result == null) {
+		setStatus("No result for \"" + phrase + "\""); 
+		return;
+	    }
+
+	    // If this is not already displayed, display it in the last free spot
+	    if (queryMode <= 2) {
+		result.display = lastDisplay.display(result);
+		result.resize(result.display.size());
+		result.paintAll(result.getGraphics());
+	    }
+
+	    enablePreviousAndNext();
+	}
+    }
+
+
+    // Look up a phrase (or symbol) on the server
+    //
+    // Arguments are the source of the query (a ResultBox, or null if the
+    // query comes from hitting the search button), the key to search for
+    // (the text of a phrase or a symbol number), the phrase as a string, 
+    // and the query mode.
+    // Query modes are: 
+    //   0 = obsolete
+    //   1 = obsolete
+    //   2 = get first N phrases and URLs,
+    //   3 = get another N phrases into same window
+    //   4 = get another N documents into same window
+    //   5 = get another N thesaurus links into same window
+
+    ResultBox lookupPhraseOnServer(ResultBox source, 
+				   boolean keyKnown, String key, String phrase, 
+				   int queryMode) { 
+    	disableSearchButton();
+	mode = searchMode;
+	ResultBox r = null;
+
+	if (queryMode <= 2) {
+	    r = new ResultBox(this, collection, key, phrase, source);
+	} else if ((queryMode == 3) || (queryMode == 4) || (queryMode == 5)) {
+	    r = source;
+	}
+
+	try { 
+	    queryServer(keyKnown, key, queryMode, r);
+	} catch (Exception e) { 
+	    System.out.println("Phind query error: " + e.toString());
+	    setStatus("Query error: " + e.toString());
+	    mode = idleMode;
+	    enableSearchButton();
+	    return null;
+	}
+      
+	// The query is finished
+	setStatus(r.c.numberOfItems + " results for \"" + phrase + "\""); 
+	mode = idleMode;
+	enableSearchButton();
+	lastQueryEndTime = new Date();
+
+	return r;
+    }
+
+
+    // Query the phindcgi program
+    //
+    // Send a query (a word or symbol number) to the server
+    // and pass the response to a ResultBox.
+
+    void queryServer(boolean keyKnown, String word, int queryMode, ResultBox area) 
+	throws IOException {
+
+	// Build the query
+	String query = phindcgi_address + "pxml=1&c=" + collection + "&pc=" + classifier;
+
+	if (keyKnown) {
+	  query = query + "&ppnum=" + word;
+	} else {
+	  query = query + "&pptext=" + word;
+	}
+
+	
+	// Specify the set of results to return 
+	int first_e = 0;
+	int last_e = 0;
+	int first_d = 0;
+	int last_d = 0;
+	int first_l = 0;
+	int last_l = 0;
+
+	// the initial query
+	if (queryMode <= 2) {
+	  last_e = phraseBlockSize;
+	  last_d = phraseBlockSize;
+	  last_l = phraseBlockSize;
+	}
+
+	// add phrases to an existing result set
+	else if (queryMode == 3) {
+	  first_e = area.nextPhraseBlock * phraseBlockSize;
+	  area.nextPhraseBlock++;
+	  last_e = area.nextPhraseBlock * phraseBlockSize;
+	} 
+	
+	// add documents to existing result set
+	else if (queryMode == 4) {
+	  first_d = area.nextDocumentBlock * phraseBlockSize;
+	  area.nextDocumentBlock++;
+	  last_d = area.nextDocumentBlock * phraseBlockSize;
+	}
+
+	// add thesaurus links to existing result set
+	else if (queryMode == 5) {
+	  first_l = area.nextThesaurusLinkBlock * phraseBlockSize;
+	  area.nextThesaurusLinkBlock++;
+	  last_l = area.nextThesaurusLinkBlock * phraseBlockSize;
+	}
+
+	query = query + "&pfe=" + first_e + "&ple=" + last_e
+                      + "&pfd=" + first_d + "&pld=" + last_d
+                      + "&pfl=" + first_l + "&pll=" + last_l;
+
+	// Send the query to the phindcgi program 
+	System.out.println("sending query: " + query);
+	/** Start timeout counter.
+	 *  DLConsulting 12-07-2004
+	 */
+	int time = 0;
+	try {
+	    URL phindcgi = new URL(query);
+            //            DataInputStream in = new DataInputStream(phindcgi.openStream());
+
+            URLConnection conn = phindcgi.openConnection();
+            DataInputStream in = new DataInputStream(conn.getInputStream());
+
+	    byte[] buffer;
+	    int availableBytes = 0;
+
+	    
+	    while (!area.finished) {
+		// Lovely old method, which would work just fine if Sun supported their own API and made
+		// in.available() work.
+
+		/*
+		availableBytes = in.available();
+		if (availableBytes == 0) {
+		    // if i had threads i'd do a wait here for 1 second
+		    /** Ah-ha but we do have threads here - at least one thread 
+		     *  anyway! Note that we want to prevent interrupted 
+		     *  exceptions flinging us out of the read loop.
+		     *  We also check that the time count hasn't reached out
+		     *  timeout threshold yet. If so, make use of the nifty
+		     *  execption handling.
+		     *  DLConsulting 12-07-2004
+		     *  Start...
+		     
+                    System.err.println("No bytes available");
+		    if(time >= timeout) {
+			throw new InterruptedIOException(
+			    "No data recieved in " + time + 
+			    " seconds. Connection timed-out.");
+		    }
+		    try {
+			Thread.sleep(1000);
+		    }
+		    catch(InterruptedException exception) {
+			System.err.println("Unexpected InterruptedException. Non-fatal.");
+			exception.printStackTrace();
+		    }
+		    time++;
+		    /** ...End 
+		} else {
+                    System.err.println("Preparing to read " + availableBytes + " bytes");
+		    buffer = new byte[availableBytes];
+		    in.read(buffer);
+                    System.err.println("Done reading");
+		    area.parseBytes(buffer);
+		}
+		*/
+
+		// New - and hopefully safer - method.
+
+		// Create a new SafeReader, and then wait while it reads
+		// we can watch for timeouts as necessary
+		int timeout = 0;
+		SafeReader reader = new SafeReader(in);
+		reader.start();
+		while(!reader.isComplete()) {
+		    // Wait timeout time.
+		    try {
+			Thread.sleep(1000);
+		    }
+		    catch(InterruptedException exception) {
+			System.err.println("test.init(): Unexpected InterruptedException. Non-fatal.");
+			exception.printStackTrace();
+		    }
+		    // Then see if the readers read anything
+		    if(reader.poll() == 0) {
+			timeout++;
+			// Have we reached timeout?
+			if(timeout > reader.getTimeout()) {
+			    // Nothing been read. Kill the reader
+			    reader.interrupt();
+			    // And throw a hissy fit
+			    throw new InterruptedIOException("No data recieved in " + reader.getTimeout() + 
+							     " seconds. Connection timed-out.");
+			}
+			// Otherwise, wait a little longer.
+		    }
+		    // Otherwise we can continue waiting and polling
+		}
+		// Process the buffer
+		System.err.println("Done reading");
+		area.parseBytes(reader.getBuffer());
+            }
+	    
+	    in.close();
+	} catch (Exception e) {
+	    System.err.println( "Error sending query to phindcgi: " + e);
+	}
+	area.repaint();
+    }
+    
+
+    // Tidy up URLs
+    //
+    // Ensure a URL address (as string) has a protocol, host, and file.
+    //
+    // If the URL is a CGI script URL, it should be tidied up so that it is
+    // appropriate to tage attrib=value pairs on the end.  This means it
+    // must either end with a "?" or (if it contains a question-mark
+    // internally) end with a "&".
+    String tidy_URL(String address, boolean isCGI) {
+
+	// System.err.println("tidy URL: " + address);
+	
+	// make sure the URL has protocol, host, and file
+	if (address.startsWith("http")) {
+	    // the address has all the necessary components
+	} else if (address.startsWith("/")) {
+	    // there is no protocol and host
+	    URL document = getDocumentBase();
+	    String protocol = document.getProtocol();
+	    String host = document.getHost();
+	    String port = "";
+	    if (document.getPort()!=-1) {
+		port = ":" + document.getPort();
+	    }
+	    address = protocol + "://" + host + port + address;
+	} else {
+	    // this URL is relative to the directory the document is in
+	    URL document = getDocumentBase();
+	    String protocol = document.getProtocol();
+	    String host = document.getHost();
+	    String directory = document.getFile();
+	    int end = directory.lastIndexOf('/');
+	    String port = "";
+	    if (document.getPort() != -1) {
+		port = ":" + document.getPort();
+	    }
+	    directory = directory.substring(0, end + 1);
+	    address = protocol + "://" + host + port + directory + address;
+	}
+
+	// if the URL is a cgi script, make sure it has a "?" in it,
+	// and that it ends with a "?" or "&"
+	if (isCGI) {
+	    if (address.indexOf((int) '?') == -1) { 
+		address = address + "?";
+	    } else if (!address.endsWith("?")) {
+		address = address + "&";
+	    }
+	}
+
+	return address;
+    }
+
+
+
+    // Open an arbitrary web page
+    void displayWebPage(String address, String window) {
+	try { 
+	    URL url= new URL(address);
+	    if (window.length() > 0) {
+		getAppletContext().showDocument(url, window);
+	    } else {
+		getAppletContext().showDocument(url);
+	    }
+	} catch (Exception e) { 
+	    System.out.println("Cannot open web page: " + e.toString());
+	}
+    }
+
+
+    // Get the applet parameters
+    void getParameters() {
+
+	// What is this collection called?
+	collection = parameterValue("collection");
+	System.out.println("Phind collection: " + collection);
+
+	// Which of the collection's classifiers are we using?
+	classifier = parameterValue("classifier", "1");
+	System.out.println("Phind classifier: " + classifier);
+
+	// Where is the phind CGI script
+	phindcgi_address = parameterValue("phindcgi");
+	phindcgi_address = tidy_URL(phindcgi_address, true);
+	System.out.println("Phind phindcgi: " + phindcgi_address);
+
+	// Where is the Greenstone library
+	library_address = parameterValue("library");
+	library_address = tidy_URL(library_address, true);
+	System.out.println("Phind library: " + library_address);
+	
+	// Is there a default search term?
+	initialSearch = parameterValue("initial_search", "");
+
+	// Should we display the control panel
+	showControlPanel = true;
+	if (parameterValue("control_panel", "show").toLowerCase().equals("hide")) {
+	    showControlPanel = false;
+	}
+	
+	// Should we show a background image?
+	backdrop_address = parameterValue("backdrop", "");
+	if (backdrop_address.length() > 0) {
+	    backdrop_address = tidy_URL(backdrop_address, false);
+	    System.out.println("Phind backdrop URL: " + backdrop_address);
+
+	    try {
+		URL backdrop_url = new URL(backdrop_address);
+		backgroundImage = getImage(backdrop_url);
+		showImage = true;
+	    } catch (Exception e) { 
+		System.out.println("Phind could not load " + backdrop_address);
+		showImage = false;
+	    }
+	}
+
+	// Should we draw a border?
+	showBorder = parameterValue("border", "on").equals("off");
+
+	// Are the windows arranged vertically or horizontally
+	if (parameterValue("orientation", "vertical").toLowerCase().startsWith("hori")) {
+	    vertical = false;
+	} else {
+	    vertical = true;
+	}
+	
+	// How many phind windows are there?
+	depth = parameterValue("depth", 3);
+
+	// Result sort order
+	// Standard is "LlEeDd", expansion-first is "EeLlDd"
+	String order = parameterValue("resultorder", "standard");
+	if (!order.equals("standard")) {
+	    int next = 20;
+	    ResultItem.sortMessage = next;
+	    for (int x = 0; x < order.length(); x++) {
+		if (order.charAt(x) == ',') {
+		    next--;
+		} else if (order.charAt(x) == 'L') {
+		    ResultItem.sortLinkItem = next;
+		} else if (order.charAt(x) == 'l') {
+		    ResultItem.sortMoreLinks = next;
+		} else if (order.charAt(x) == 'E') {
+		    ResultItem.sortPhraseItem = next;
+		} else if (order.charAt(x) == 'e') {
+		    ResultItem.sortMorePhrases = next;
+		} else if (order.charAt(x) == 'D') {
+		    ResultItem.sortDocumentItem = next;
+		} else if (order.charAt(x) == 'd') {
+		    ResultItem.sortMoreDocuments = next;
+		}
+	    }
+	    System.out.println("link: " + ResultItem.sortLinkItem);
+	    System.out.println("exps: " + ResultItem.sortPhraseItem);
+	    System.out.println("docs: " + ResultItem.sortDocumentItem);
+
+	}
+
+	// How many phrases should we fetch at any given time?
+	phraseBlockSize = parameterValue("blocksize", 10);
+
+	// What font should we use?
+	fontSize = parameterValue("fontsize", 10);
+	fontName = parameterValue("fontname", "Helvetica");
+
+	// Column dimensions
+	column_1_width = parameterValue("first_column_width", 6);
+	column_2_width = parameterValue("second_column_width", column_1_width);
+	
+	// Where do we open new windows
+	searchWindowName = parameterValue("search_window", "phindsearch");
+	documentWindowName = parameterValue("document_window", "phinddoc");
+
+	// Colours
+	panel_fg = parameterValue("panel_fg", Color.black);
+	panel_bg = parameterValue("panel_bg", Color.white);
+
+	highlight_bg = parameterValue("highlight_bg", Color.yellow);
+
+	expansion_fg = parameterValue("expansion_fg", Color.black);
+	thesaurus_fg = parameterValue("thesaurus_fg", new Color(0, 100, 0));
+	document_fg = parameterValue("document_fg", Color.blue);
+
+	thesaurus_bar_fg = parameterValue("thesaurus_bar_fg", Color.black);
+	expansion_bar_fg = parameterValue("expansion_bar_fg", Color.black);
+	document_bar_fg = parameterValue("document_bar_fg", Color.black);
+
+	thesaurus_bar_bg = parameterValue("thesaurus_bar_bg", new Color(160, 160, 190));
+	expansion_bar_bg = parameterValue("expansion_bar_bg", new Color(255, 200, 200));
+	document_bar_bg = parameterValue("document_bar_bg", new Color(150, 193, 156));
+
+	column_1_fg = parameterValue("first_column_fg", Color.black);
+	column_1_bg = parameterValue("first_column_bg", new Color(235, 245, 235));
+	column_2_fg = parameterValue("second_column_fg", Color.black);
+	column_2_bg = parameterValue("second_column_bg", new Color(200, 220, 200));
+
+	message_fg = parameterValue("message_fg", Color.black);
+	message_bg = parameterValue("message_bg", Color.white);
+
+	// Colours I don't use, yet
+	// thesaurus_bg = parameterValue("thesaurus_bg", Color.white);
+	// expansion_bg = parameterValue("expansion_bg", Color.white);
+	// document_bg = parameterValue("document_bg", Color.white);
+
+	/** How long should the read loop wait before timing out?
+	 *  DLConsulting 12-07-2004
+	 */
+	timeout = parameterValue("timeout", 60);
+    }
+
+    // Get the value of a parameter given its name.
+    // There are many types of parameters, hence the variety of functions.
+
+    // Get a REQUIRED string.  Stop the applet if we cannot.
+    String parameterValue(String name) {
+	try { 
+	    return getParameter(name);
+	} catch (Exception e) {
+	    System.err.println("Phind: you must give a parameter for \"" 
+			       + name + "\".  Stopping.");
+	    stop();
+	}
+	return "";
+    }
+
+    // Get an optional parameter.  Return a default if we cannot.
+    String parameterValue(String name, String defaultValue) {
+	String text = getParameter(name);
+	if (text == null) {
+	    return defaultValue;
+	}
+	System.out.println("Phind " + name + ": " + text);
+	return text;
+    }
+
+    int parameterValue(String name, int defaultValue) {
+	int value;
+	try { 
+	    value = Integer.parseInt(getParameter(name));
+	} catch (Exception e) {
+	    return defaultValue;
+	}
+	System.out.println("Phind " + name + ": " + value);
+	return value;
+    }
+
+    Color parameterValue(String name, Color defaultValue) {
+
+	String text = getParameter(name);
+	if (text == null) {
+	    return defaultValue;
+	}
+	text = text.toLowerCase();
+
+	// a number of the form "#ddffee" 
+	if (text.startsWith("#") && (text.length() == 7)) {
+	    text = text.substring(1);
+	    int r, g, b;
+	    try {
+		r = Integer.parseInt(text.substring(0,2), 16);
+		g = Integer.parseInt(text.substring(2,4), 16);
+		b = Integer.parseInt(text.substring(4), 16);
+		return new Color(r, g, b);
+	    }  catch (Exception e) {
+		return defaultValue;
+	    }
+	}
+
+	// a known Java colour string
+	else if (text.equals("black")) { return Color.black; }
+	else if (text.equals("blue")) { return Color.blue; }
+	else if (text.equals("cyan")) { return Color.cyan; }
+	else if (text.equals("darkgray")) { return Color.darkGray; }
+	else if (text.equals("gray")) { return Color.gray; }
+	else if (text.equals("green")) { return Color.green; }
+	else if (text.equals("lightgray")) { return Color.lightGray; }
+	else if (text.equals("magenta")) { return Color.magenta; }
+	else if (text.equals("orange")) { return Color.orange; }
+	else if (text.equals("pink")) { return Color.pink; }
+	else if (text.equals("red")) { return Color.red; }
+	else if (text.equals("white")) { return Color.white; }
+	else if (text.equals("yellow")) { return Color.yellow; }
+
+	return defaultValue;
+    }
+
+
+    // Control panel operations
+
+    // Initialise the control panel
+    void initialiseControlPanel() {
+	
+	if (showControlPanel) {
+	    Panel p1 = new Panel();
+	    add("North", p1);
+	    p1.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
+	    
+	    searchButton = new Button("Search");
+	    searchButton.setFont(boldFont);
+	    p1.add(searchButton);
+	    
+	    Label tempLabel = new Label("  for");
+	    tempLabel.setFont(boldFont);
+	    p1.add(tempLabel);
+	    
+	    wordField = new TextField(12);
+	    wordField.setFont(boldFont);
+	    p1.add(wordField);
+	    
+	    Label temp2 = new Label("          ");
+	    p1.add(temp2);
+	    
+	    prevButton = new Button("Previous");
+	    prevButton.setFont(boldFont);
+	    prevButton.disable();
+	    
+	    p1.add(prevButton);
+	    
+	    nextButton = new Button("  Next  ");
+	    nextButton.setFont(boldFont);
+	    nextButton.disable();
+	    p1.add(nextButton);
+	    
+	}
+    }
+    
+    // Button and field functionality
+    
+    // Enable and disable the word field
+    void enableSearchButton() {
+	if (showControlPanel) {
+	    searchButton.enable();
+	}
+    }
+    void disableSearchButton() {
+	if (showControlPanel) {
+	    searchButton.disable();
+	}
+    }
+
+    // Get and set the search text in the wordField
+    String getSearchTerm() {
+	if (showControlPanel) {
+	    return wordField.getText();
+	} else {
+	    return initialSearch;
+	}
+    }
+    void setSearchTerm(String word) {
+	if (showControlPanel) {
+	    wordField.setText(word);
+	} 
+    }
+
+    // Enable or disable the "Previous" and "Next" buttons
+    void enablePreviousAndNext() {
+	if (showControlPanel) {
+	    Component c = firstDisplay.current;
+	    if (c.getClass().getName().endsWith("ResultBox")) {
+		if (((ResultBox) c).prevBoxExists()) {
+		    prevButton.enable();
+		} else {
+		    prevButton.disable();
+		}
+	    }
+	    
+	    c = lastDisplay.current;
+	    if (c.getClass().getName().endsWith("ResultBox")) {
+		if (((ResultBox) c).nextBoxExists()) {
+		    nextButton.enable();
+		} else {
+		    nextButton.disable();
+		}
+	    }
+	}
+    }
+
+    // Shift to previous box
+    //
+    // If the user clicks "Previous" then scroll up.
+    void shiftPrevious() {
+
+	Component c = firstDisplay.current;
+	if (c.getClass().getName().endsWith("ResultBox")) {
+
+	    ResultBox b = (ResultBox) c;
+	    if (b.prevBoxExists()) {
+		b = b.prev;
+
+		// empty all the displays
+		firstDisplay.emptyContents();
+
+		// add as many result boxes as there are displays
+		for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
+		    lastDisplay.display(b);
+		    b.resize(b.display.size());
+		    b.paintAll(b.getGraphics());
+		    b = b.next;
+		}
+	    }
+	}
+	enablePreviousAndNext();
+    }
+
+    // Shift to next box
+    //
+    // If the user clicks "Next" then scroll down if possible
+    void shiftNext() {
+
+	Component c = lastDisplay.current;
+	if (c.getClass().getName().endsWith("ResultBox")) {
+
+	    ResultBox b = (ResultBox) c;
+	    if (b.nextBoxExists()) {
+
+		// find the new "first" displayed box
+		c = firstDisplay.current;
+		b = (ResultBox) c;
+		b = b.next;
+
+		// empty all the displays
+		firstDisplay.emptyContents();
+
+		// add as many result boxes as there are displays
+		for (int i = 1 ; ((i <= depth) && (b != null)); i++) {
+		    lastDisplay.display(b);
+		    b.resize(b.display.size());
+		    b.paintAll(b.getGraphics());
+		    b = b.next;
+		}
+	    }
+	}
+	enablePreviousAndNext();
+    }
+
+    public class SafeReader 
+	extends Thread {
+	// Has the reader finished reading?
+	private boolean complete;
+	// The buffer in where the data read is stored
+	private byte[] buffer;
+	// The stream this reader reads from
+	private DataInputStream in;
+	// If the bytes_read hasn't changed in this time, then stop this thread
+	// and timeout
+	private int timeout; 
+	// The current buffer size
+	private int buffer_current_size;
+	// The maximum buffer size
+	private int buffer_max_size;
+	// The number of bytes read through this reader since the last poll
+	private int bytes_read;
+
+	/** Default constructor 
+	 *  @param in the DataInputStream to read from
+	 */
+	public SafeReader(DataInputStream in) {
+	    complete = false;
+	    buffer_current_size = 0;
+	    buffer_max_size = 16;
+	    buffer = new byte[buffer_max_size]; // Default size
+	    timeout = 60; // Sixty seconds
+	    bytes_read = 0;
+	    
+	    this.in = in;
+	}
+
+	/** Construct a new SafeReader
+	 *  @param in the DataInputStream to read from
+	 *  @param timeout how long this thread should wait when reading data
+	 */
+	public SafeReader(DataInputStream in, int timeout) {
+	    this(in);
+	    this.timeout = timeout;
+	}
+
+	/** Retrieve the current buffer
+	 *  @return a byte[] buffer trimmed to the appropriate size
+	 */
+	public byte[] getBuffer() {
+	    byte[] final_buffer = new byte[buffer_current_size];
+	    System.arraycopy(buffer, 0, final_buffer, 0, buffer_current_size);
+	    return final_buffer;
+	}
+
+	/** Retrieve the current timeout
+	 *  @return the timeout as an int
+	 */
+	public int getTimeout() {
+	    return timeout;
+	}
+
+	/** See whether the reader has finished reading
+	 *  @return true if the read is complete (-1 bytes read!) or false otherwise
+	 */
+	public synchronized boolean isComplete() {
+	    return complete;
+	}
+
+	/** Poll the reader to determine if it has read any bytes recently
+	 *  @return the number of bytes read
+	 */
+	public long poll() {
+	    // Maybe should test if complete - but its only one second right?
+	    long result = bytes_read;
+	    bytes_read = 0;
+	    return result;
+	}
+
+	/** Try to read bytes from the input stream. Note the caller is responsible for closing the datainputstream
+	 */
+	public void run() {
+	    try {
+		int i = 0;
+		byte[] read_buffer = new byte[8];
+		while ((i = in.read(read_buffer)) != -1) {
+		    // Check our storage buffer is big enough
+		    if(buffer_max_size - (buffer_current_size + i) <= 0) {
+			// Double the size
+			buffer_max_size = 2 * buffer_max_size;
+			byte[] new_buffer = new byte[buffer_max_size];
+			System.arraycopy(buffer, 0, new_buffer, 0, buffer_current_size);
+			buffer = new_buffer;
+		    }
+		    // Then copy in the read buffer
+		    System.arraycopy(read_buffer, 0, buffer, buffer_current_size, i);
+		    buffer_current_size += i;
+		    bytes_read += i;
+		}
+		setComplete(true);
+	    }
+	    catch(IOException exception) {
+		System.err.println("SafeReader.run(): Unexpected IOException. Fatal.");
+		exception.printStackTrace();
+	    }
+	}
+
+	/** Set the complete boolean to the desired state, synchronized to avoid race with isComplete()
+	 *  @param value the desired state
+	 */
+	private synchronized void setComplete(boolean value) {
+	    complete = value;
+	}
+    }
+}
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/PhindTitle.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/PhindTitle.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/PhindTitle.java	(revision 16561)
@@ -0,0 +1,84 @@
+/**********************************************************************
+ *
+ * PhindTitle.java -- backdrops to empty ResultDisplay panels.
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+
+/*********************************************************************
+
+PhindTitle is for drawing backdrops in empty ResultDisplay panels.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.applet.Applet;
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+
+public class PhindTitle extends Canvas {
+
+    Phind phind;
+    static Image backgroundImage;
+    boolean displayInfo;
+    
+    PhindTitle(Phind p) {
+	phind = p;
+    }
+
+    public void update(Graphics g) {
+	paint(g);
+    }
+
+    public void paint(Graphics g) {
+	Dimension canvasSize = size();
+
+	Color fore = phind.panel_fg;
+	Color back = phind.panel_bg;
+
+	// set the screen background
+	if (phind.showImage)
+	    try {
+		g.drawImage(phind.backgroundImage, 
+			    0, 0, canvasSize.width, canvasSize.height, back, null);
+	    } catch (Exception e) {
+		System.err.println("PhindTitle paint: "  + e);
+	    }
+	else {
+	    g.setColor(back);
+	    g.fillRect(0,0, canvasSize.width, canvasSize.height);
+	}
+    }
+}
+
+ 
+
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultBox.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultBox.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultBox.java	(revision 16561)
@@ -0,0 +1,774 @@
+/**********************************************************************
+ *
+ * ResultBox.java -- a list of phrases in the Phind java interface
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+A ResultBox holds the results of a query to phindcgi.  They deal mostly
+with the information content of the query, and have methods for parsing the
+input into phrase and document items.  They have little do with display:
+ResultBoxes are shown to the user through "ResultDisplay" panels, and are
+drawn using "ResultCanvas" objects.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.awt.Panel;
+import java.awt.BorderLayout;
+import java.awt.Scrollbar;
+
+import java.awt.Label;
+import java.awt.Event;
+
+import java.net.*;
+import java.applet.*;
+
+import java.util.Vector;
+
+
+public class ResultBox extends Panel {
+    
+    // Objects at a "higher" level than this one
+    Phind phind;
+    ResultDisplay display;
+
+    // Surroundig objects
+    ResultBox prev, next;
+
+    // The components contained by this one.
+    ResultCanvas c;
+    ResultTitle t;
+    Scrollbar s;
+    Panel label;
+
+    // The key identifying the phrase displayed, with its text and the
+    // collection from which it is drawn
+    String searchKey, searchPhrase, searchCollection;
+    
+    // The total frequency, expansion frequency, and document frequency
+    // of the phrase
+    int numberOfOccurances;
+    int numberOfExpansions;
+    int numberOfDocuments;
+    int numberOfThesaurusLinks;
+
+    // The number of phrases and documents retrieved, and the number of
+    // times the user has requested more phrases or documents.
+    int expansionsRetrieved;
+    int documentsRetrieved;
+    int thesaurusLinksRetrieved;
+    int nextPhraseBlock;
+    int nextDocumentBlock;
+    int nextThesaurusLinkBlock;
+    
+    int mode;
+    final int initMode = 0;
+    public final int emptyMode = 1;
+    final int loadingMode = 2;
+    final int finishedMode = 3;
+
+    String buffer;
+    boolean finished;
+
+
+    // Create a ResultBox
+    // given details of the search that generated it.
+    ResultBox(Phind p, String collect, String key, String phrase, ResultBox rb) {
+
+	super();
+	mode = initMode;
+
+	phind = p;
+	display = null;
+	next = null;
+	prev = rb;
+	if (prev != null) prev.next = this;
+
+	searchKey = key;
+	searchPhrase = phrase;
+	searchCollection = collect;
+
+	numberOfOccurances = -1;
+	numberOfExpansions = -1;
+	numberOfDocuments = -1;
+	numberOfThesaurusLinks = -1;
+
+	expansionsRetrieved = 0;
+	documentsRetrieved = 0;
+	thesaurusLinksRetrieved = 0;
+
+	nextPhraseBlock = 1;
+	nextDocumentBlock = 1;
+	nextThesaurusLinkBlock = 1;
+
+
+	setLayout(new BorderLayout());
+
+	s = new Scrollbar(Scrollbar.VERTICAL);
+	disableScrollbar();
+	add("East", s);
+
+	c = new ResultCanvas(this);
+	add("Center", c);
+
+	t = new ResultTitle(this);
+	add("North", t);
+
+	buffer = "";
+	finished = false;
+	mode = emptyMode;
+    }
+
+    static String describeContents(String phrase, String c) {
+	return( "\"" + phrase + "\" in " + c + ".");
+    }
+
+    String describeContents() {
+	return( describeContents(searchPhrase, searchCollection) );
+    }
+
+
+    // Reset the contents of the box
+    void resetBox( ) {
+	buffer = "";
+	finished = false;
+	c.resetCanvas();
+	disableScrollbar();
+	mode = emptyMode;
+    
+	numberOfExpansions = -1;
+	numberOfDocuments = -1;
+	numberOfThesaurusLinks = -1;
+
+	expansionsRetrieved = 0;
+	documentsRetrieved = 0;
+	thesaurusLinksRetrieved = 0;
+    }
+
+    void setStatus( String status ) {
+	phind.setStatus(status);
+    }
+
+    void disableScrollbar() {
+	if (s.isEnabled()) {
+	    s.setValues(0, 1, 0, 1);
+	    s.setLineIncrement(1);
+	    s.setPageIncrement(1);
+	    s.disable();
+	}
+    }
+
+    // Are there displays previous to and after this?
+    public boolean prevBoxExists () {
+	return (prev != null);
+    }
+    public boolean nextBoxExists () {
+	return (next != null);
+    }
+
+    
+    // Look up a phrase
+    // Phrase lookups are passed on to the Phind applet itself.
+    void lookupPhrase(String key, String phrase, int queryMode) {
+	buffer = "";
+	finished = false;
+	phind.searchForPhrase(this, key, phrase, queryMode);
+	t.repaint();
+    }
+
+    public boolean handleEvent(Event event) {
+	//    System.out.println("event: " + event.toString());
+	if ( (event.target == s) && 
+	     ( (event.id == Event.SCROLL_ABSOLUTE) ||
+	       (event.id == Event.SCROLL_LINE_DOWN) ||
+	       (event.id == Event.SCROLL_LINE_UP) ||
+	       (event.id == Event.SCROLL_PAGE_DOWN) ||
+	       (event.id == Event.SCROLL_PAGE_UP) ) ) {
+	    c.repaint();
+	    return true;
+	} else {
+	    return super.handleEvent(event);
+	}
+    }    
+
+
+    // a new section of the byte stream is read
+    void parseBytes(byte[] bytes) {
+	mode = loadingMode;
+
+	// update the buffer that holds the bytes read
+	String input;
+	try {
+	    input = new String(bytes, new String("UTF8"));
+	} catch (Exception e) {
+	    System.err.println(e);
+	    input = "";
+	}
+	buffer = buffer.concat(input);
+
+	// Parse each line until there are no more lines to parse
+	while(parseLine());
+
+	if (finished) {
+	    c.updateMarkers();
+	    buffer = "";
+	} else {
+	    // We haven't finished, give some feedback about progress
+	    setStatus("Connected to host, " + c.numberOfItems + " results read");
+	}
+    }
+
+    // Parse a line of XML
+    //
+    // This isn't necessarily real XML.  We assume every tag begins and
+    // ends on one line.  We read one line out of the buffer at a time,
+    // then "add" them to the box with the addLine method.
+    //
+    // Return true if there is a another line here to parse, otherwise false.
+
+    boolean parseLine() {
+
+        // System.out.print("parseLine " + "(length " + buffer.length() + ")");
+	String item;
+	int eol = buffer.indexOf((int) '\n');
+
+	// If there are no carriage returns, the line is incomplete
+	if (eol == -1) {
+	    return false;
+	}
+
+	// The </phinddata> tag concludes the dataset
+	else if (buffer.startsWith("</phinddata>")) {
+	    mode = finishedMode;
+	    finished = true;
+	    return false;
+	}
+	
+	// The <expansion> tag
+	else if (buffer.startsWith("<expansion ")) {
+	    item = buffer.substring(0, eol);
+	    buffer = buffer.substring(eol + 1);
+	    addExpansionTag(item);
+	    return true;
+	}
+ 
+	// The <document> tag
+	else if (buffer.startsWith("<document ")) {
+	    item = buffer.substring(0, eol);
+	    buffer = buffer.substring(eol + 1);
+	    addDocumentTag(item);
+	    return true;
+	}
+ 
+	// The <thesaurus> tag
+	else if (buffer.startsWith("<thesaurus ")) {
+	    item = buffer.substring(0, eol);
+	    buffer = buffer.substring(eol + 1);
+	    addThesaurusTag(item);
+	    return true;
+	}
+ 
+	// The <phinddata> tag introduces the dataset
+	else if (buffer.startsWith("<phinddata ")) {
+	    item = buffer.substring(0, eol);
+	    buffer = buffer.substring(eol + 1);
+	    addPhinddataTag(item);
+	    return true;
+	}
+
+	// The <phinderror> tag contains an error message
+	else if (buffer.startsWith("<phinderror")) {
+	    item = buffer.substring(0, eol);
+	    System.out.println("phind reported an error: " + item);
+	    buffer = buffer.substring(eol + 1);
+	    return true;
+	}
+ 
+	// Ignore the piece we've read
+	item = buffer.substring(0, eol);
+	// System.out.println("discarding: " + item);
+	buffer = buffer.substring(eol + 1);
+	return true;
+    }
+    
+
+
+    // Add a phinddata tag
+    //
+    // Given a string containing an XML phinddata tag of the form:
+    //   <phinddata id="35" text="FOREST" df="797" ef="134">
+    // Read the expansion information from the tag
+    //
+    // Return true if successful, otherwise false.
+
+    boolean addPhinddataTag( String line ) {
+
+	// System.out.println( "addExpansionListTag: " + line);
+
+	String idStr = "", text = "", tfStr = "", efStr = "", dfStr = "", lfStr = "";
+	int tf = 0, ef = 0, df = 0, lf = 0;
+
+	// Break the tag down into its component parts
+	int nextSplit;
+       	while (line.length() > 0) {
+	    line = line.trim();
+
+	    // Read the phrase identifier
+	    if (line.startsWith("id")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    idStr = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+	    
+	    // Read the total frequency
+	    else if (line.startsWith("tf")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    tfStr = line.substring(0, nextSplit);
+		    tf = Integer.valueOf(tfStr).intValue();
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the text of the phrase
+	    else if (line.startsWith("text")) {
+		line = line.substring(6);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    text = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the expansion frequency
+	    else if (line.startsWith("ef")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    efStr = line.substring(0, nextSplit);
+		    ef = Integer.valueOf(efStr).intValue();
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the document frequency
+	    else if (line.startsWith("df")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    dfStr = line.substring(0, nextSplit);
+		    df = Integer.valueOf(dfStr).intValue();
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the thesaurus link frequency
+	    else if (line.startsWith("lf")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    lfStr = line.substring(0, nextSplit);
+		    lf = Integer.valueOf(lfStr).intValue();
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("addExpansionListTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read and ignore some other tag
+	    else {
+		nextSplit = line.indexOf((int) ' ');
+		if (nextSplit >= 1) {
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    line = "";
+		}
+	    }
+	}
+
+	// Add the data to the ResultBox
+	searchKey = idStr;
+	searchPhrase = text;
+	numberOfOccurances = tf;
+	numberOfExpansions = ef;
+	numberOfDocuments = df;
+	numberOfThesaurusLinks = lf;
+	return true;
+    }
+
+
+    // Add an expansion tag
+    //
+    // Given a string containing an XML expansin tag of the form:
+    //   <expansion num="3" id="8421" prefix="PEOPLE and" suffix="" tf="3" df="3"/>
+    //   <expansion num="4" id="8696" prefix="" suffix="products" tf="3" df="3"/>
+    // Create a new ResultItemPhrase for display
+    //
+    // Return true if successful, otherwise false.
+
+    boolean addExpansionTag( String line ) {
+
+	// System.out.println( "addExpansionTag: " + line + " (" + expansionsRetrieved + ")");
+
+	String numStr = "", idStr = "", tfStr = "", dfStr = "", 
+	    prefix = "", body = "", suffix = "";
+
+	body = searchPhrase;
+
+	// Break the tag down into its component parts
+	line = line.substring(11);
+	int nextSplit;
+       	while (line.length() > 0) {
+	    line = line.trim();
+
+	    // Read the expansion number
+	    if (line.startsWith("num")) {
+		line = line.substring(5);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    numStr = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the phrase ID
+	    else if (line.startsWith("id")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    idStr = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the prefix of the phrase
+	    else if (line.startsWith("prefix")) {
+		line = line.substring(8);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 0) {
+		    prefix = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the suffix of the phrase
+	    else if (line.startsWith("suffix")) {
+		line = line.substring(8);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 0) {
+		    suffix = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the frequency
+	    else if (line.startsWith("tf")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    tfStr = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the document frequency
+	    else if (line.startsWith("df")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    dfStr = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read some other tag
+	    else {
+		nextSplit = line.indexOf((int) ' ');
+		if (nextSplit >= 1) {
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    line = "";
+		}
+	    }
+	}
+	
+	// Create a new ResultItem and add it to the display
+	ResultItemPhrase ri = new ResultItemPhrase(idStr, tfStr, dfStr, prefix, body, suffix);
+	
+	if (c.addResultItem(ri)) {
+	    expansionsRetrieved++;
+	    return true;
+	}
+	
+	return false;
+    }
+
+
+    // Add an document tag
+    //
+    // Given a string containing an XML document tag of the form:
+    //   <document num="2" hash="HASH424e64b811fdad933be69c" freq="1" title="CONTENTS"/>
+    //   <document num="3" hash="HASH01f08efd8752ad54ca0a99cf" freq="1" title="Tree mixtures"/>
+    //
+    // Create a new ResultItemDocument for display
+    //
+    // Return true if successful, otherwise false.
+
+    boolean addDocumentTag( String line ) {
+
+	// System.out.println( "addDocumentTag: " + line + " (" + documentsRetrieved + ")");
+	
+	String num = "", hash = "", freq = "", title = "";
+	line = line.substring(9);
+	
+	// Break the tag down into its component parts
+	int nextSplit;
+       	while (line.length() > 0) {
+	    line = line.trim();
+
+	    // Read the expansion number
+	    if (line.startsWith("num")) {
+		line = line.substring(5);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    num = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addDocumentTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the document OID hash
+	    else if (line.startsWith("hash")) {
+		line = line.substring(6);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    hash = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addDocumentTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the document frequency
+	    else if (line.startsWith("freq")) {
+		line = line.substring(6);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    freq = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addDocumentTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the title
+	    else if (line.startsWith("title")) {
+		line = line.substring(7);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    title = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addDocumentTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read some other tag or close the string off
+	    else {
+		nextSplit = line.indexOf((int) ' ');
+		if (nextSplit >= 1) {
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    line = "";
+		}
+	    }
+	}
+	
+	// Create a new ResultItem and add it to the display
+	ResultItemDocument ri = new ResultItemDocument(hash, title, freq);
+	
+	if (c.addResultItem(ri)) {
+	    documentsRetrieved++;
+	    return true;
+	}
+	
+	return false;
+    }
+
+
+    // Add a thesaurus tag
+    //
+    // Given a string containing an XML document tag of the form:
+    //
+    // <thesaurus num="3" id="36506" tf="0" df="0" type="RT" text="ANGLOPHONE AFRICA"/>
+    // <thesaurus num="4" id="28724" tf="0" df="0" type="RT" text="FRANCOPHONE AFRICA"/>
+    //
+    // Create a new ResultItemLink for display
+    //
+    // Return true if successful, otherwise false.
+
+    boolean addThesaurusTag( String line ) {
+    
+	// System.out.println( "addThesaurusTag: " + line + " (" + thesaurusLinksRetrieved + ")");
+
+	String num = "", id = "", tf = "", df = "", type = "", text = "";
+	line = line.substring(10);
+
+	// Break the tag down into its component parts
+	int nextSplit;
+       	while (line.length() > 0) {
+	    line = line.trim();
+
+	    // Read the expansion number
+	    if (line.startsWith("num")) {
+		line = line.substring(5);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    num = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addThesaurusTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the phrase ID
+	    else if (line.startsWith("id")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    id = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addThesaurusTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the phrase link type
+	    else if (line.startsWith("type")) {
+		line = line.substring(6);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    type = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addThesaurusTag: error parsing: " + line);
+ 		}
+	    } 
+
+
+	    // Read the phrase text
+	    else if (line.startsWith("text")) {
+		line = line.substring(6);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    text = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addThesaurusTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the frequency
+	    else if (line.startsWith("tf")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    tf = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+	    // Read the document frequency
+	    else if (line.startsWith("df")) {
+		line = line.substring(4);
+		nextSplit = line.indexOf((int) '"');
+		if (nextSplit >= 1) {
+		    df = line.substring(0, nextSplit);
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    System.err.println("ResultBox addExpansionTag: error parsing: " + line);
+ 		}
+	    } 
+
+
+	    // Read some other tag or close the string off
+	    else {
+		nextSplit = line.indexOf((int) ' ');
+		if (nextSplit >= 1) {
+		    line = line.substring(nextSplit + 1);
+		} else {
+		    line = "";
+		}
+	    }
+	}
+	
+	// Create a new ResultItem and add it to the display
+	ResultItemLink ri = new ResultItemLink(id, text, type, tf, df);
+
+	if (c.addResultItem(ri)) {
+	    thesaurusLinksRetrieved++;
+	    return true;
+	}
+	
+	return false;
+    }
+}
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultCanvas.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultCanvas.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultCanvas.java	(revision 16561)
@@ -0,0 +1,557 @@
+/**********************************************************************
+ *
+ * ResultCanvas.java -- a Canvas onto which a phrase list is drawn
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+The results of a query are displayed on a ResultCanvas object.  Each 
+entry in the result list is stored in a ResultItem, and the ResultCanvas 
+contains a Vector of ResultItems.
+
+Each ResultCanvas is embedded in a ResultBox alongside a Scrollbar.  
+When the ResultCanvas is drawn, it looks at the scrollbar, calculates 
+which ResultItems are visible, then draws them on the screen.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.awt.Canvas;
+import java.awt.Scrollbar;
+import java.awt.Graphics;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Dimension;
+import java.awt.Color;
+import java.util.Vector;
+import java.awt.Image;
+import java.awt.Event;
+
+import java.net.URL;
+import java.applet.Applet;
+import java.util.Date;
+
+public class ResultCanvas extends Canvas {
+
+    // Other objects associated with this 
+    Phind phind;
+    ResultBox parent;
+    Scrollbar scrollbar;
+    
+    // fonts and font spacings to use
+    Font areaPlain, areaBold;
+    int lineSpacing;
+    
+    // The items to display on this canvas
+    int numberOfItems;
+    int firstItemDisplayed;
+    int itemSelected;
+    Vector items;
+    
+    // the background image
+    public static Image backgroundImage;
+    
+    
+    // Create a ResultCanvas from the ResultBox which is its parent.
+    ResultCanvas(ResultBox p) {
+	
+	parent = p;
+	phind = p.phind;
+	scrollbar = p.s;
+	parent.disableScrollbar();
+
+	areaPlain = phind.plainFont;
+	areaBold  = phind.boldFont;
+
+	lineSpacing = phind.fontSize + 2;
+
+	items = new Vector();
+	numberOfItems = 0;
+	firstItemDisplayed = 0;
+	itemSelected = -1;
+    
+	if (backgroundImage == null) {
+	    backgroundImage = p.phind.backgroundImage;
+	}
+
+    }
+
+    void resetCanvas( ) {
+	items.removeAllElements();
+	numberOfItems = 0;
+	repaint();
+    }
+
+
+    // Add a new search result.
+    // Return true if successful, otherwise false.
+    boolean addResultItem( ResultItem item ) {
+ 
+	// Add a new result, in sorted order.
+	// First sort key is item kind (lowest first),
+	// second is frequency (highest first).
+	// This is not efficient, but I don't care right now.
+	int index = 0;
+	while ((index < numberOfItems) &&
+	       ((item.sort < ((ResultItem) items.elementAt(index)).sort) ||
+		((item.sort == ((ResultItem) items.elementAt(index)).sort) &&
+		 (item.frequency <= ((ResultItem) items.elementAt(index)).frequency)))) {
+	    index++;
+	}
+	items.insertElementAt(item,index);
+	numberOfItems++;
+	return true;
+    }
+
+
+    // Update more phrases/documents/links markers
+    void updateMarkers() {
+	updateMorePhrasesMarker();
+	updateMoreDocumentsMarker();
+	updateMoreLinksMarker();
+    }
+
+    // Make sure the more Phrases item only appears in the list
+    // if it is meant to.
+    void updateMorePhrasesMarker() {
+    
+	// System.out.println("updateMorePhrasesMarker() ");
+
+	// look for a marker
+	boolean found = false;
+	int index = 0;
+	while (!found && (index < numberOfItems)) {
+	    if (((ResultItem) items.elementAt(index)).isMorePhrases()) {
+		found = true;
+	    } else {
+		index++;
+	    }
+	}
+
+	if (parent.expansionsRetrieved == parent.numberOfExpansions) {
+	    // there should be no marker
+	    if (found) {
+		items.removeElementAt(index);
+		numberOfItems--;
+	    }
+
+	} else if (parent.expansionsRetrieved < parent.numberOfExpansions) {
+	    if (!found) {
+		ResultItem ri = new ResultItem(ResultItem.morePhrases);
+		addResultItem(ri);
+	    }
+	}
+    }
+
+
+    // Make sure the more documents marker appears when required
+    void updateMoreDocumentsMarker() {
+    
+	// System.out.println("updateMoreDocumentsMarker() ");
+
+	// look for a marker
+	boolean found = false;
+	int index = 0;
+	while (!found && (index < numberOfItems)) {
+	    if (((ResultItem) items.elementAt(index)).isMoreDocuments()) {
+		found = true;
+	    } else {
+		index++;
+	    }
+	}
+
+	if (parent.documentsRetrieved == parent.numberOfDocuments) {
+	    // there should be no marker
+	    if (found) {
+		items.removeElementAt(index);
+		numberOfItems--;
+	    }
+
+	} else if (parent.documentsRetrieved < parent.numberOfDocuments) {
+	    // there should be a marker
+	    if (!found) {
+		ResultItem ri = new ResultItem(ResultItem.moreDocuments);
+		addResultItem(ri);
+	    }
+	}
+    }
+
+      
+    // Make sure the more links marker appears when required
+    void updateMoreLinksMarker() {
+    
+	// System.out.println("updateMoreLinksMarker() ");
+
+	// look for a marker
+	boolean found = false;
+	int index = 0;
+	while (!found && (index < numberOfItems)) {
+	    if (((ResultItem) items.elementAt(index)).isMoreLinks()) {
+		found = true;
+	    } else {
+		index++;
+	    }
+	}
+
+	if (parent.thesaurusLinksRetrieved == parent.numberOfThesaurusLinks) {
+	    // there should be no marker
+	    if (found) {
+		items.removeElementAt(index);
+		numberOfItems--;
+	    }
+
+	} else if (parent.thesaurusLinksRetrieved < parent.numberOfThesaurusLinks) {
+	    // there should be a marker
+	    if (!found) {
+		ResultItem ri = new ResultItem(ResultItem.moreLinks);
+		addResultItem(ri);
+	    }
+	}
+    }
+
+      
+
+ 
+
+
+    public void update(Graphics g) {
+	paint(g);
+    }
+
+    public void paint(Graphics g) {
+
+	// calculate the canvas size, margins, and spacing
+	
+	// Note: the size() method is deprecated, but getSize fails in NetScape 4
+	// Dimension canvasSize = getSize();
+	Dimension canvasSize = size();
+
+	g.setFont(areaPlain);
+	int space = g.getFontMetrics().stringWidth(" ");
+	int nought = g.getFontMetrics().stringWidth("0");
+
+	int firstColumnWidth = phind.column_1_width * nought;
+	int secondColumnWidth = phind.column_2_width * nought;
+	int columnWidth = firstColumnWidth + secondColumnWidth;
+
+	int firstColumnLeft = canvasSize.width - columnWidth;
+	int secondColumnLeft = firstColumnLeft + firstColumnWidth;
+
+	int mainLeftMargin = 0;
+	int mainRightMargin = firstColumnLeft;
+
+	// the number of items that will fit on the canvas
+	int visible = canvasSize.height / lineSpacing;
+
+	// calculate the first item to output
+	int scrollValue = scrollbar.getValue();
+	if (numberOfItems <= visible) {
+	    scrollValue = 0;
+	} else if (scrollValue > (numberOfItems - visible)) {
+	    scrollValue = numberOfItems - visible;
+	} 
+	firstItemDisplayed = scrollValue;
+
+	// Draw the phrase area
+	Color fore = phind.panel_fg;
+	Color back = phind.panel_bg;
+
+
+	// draw the background
+	if (phind.showImage) {
+	    try {
+		g.drawImage(backgroundImage, 
+			    mainLeftMargin, 0, mainRightMargin, canvasSize.height, 
+			    back, null);
+	    } catch (Exception e) {
+		phind.showImage = false;
+		System.err.println("ResultCanvas paint error: " + e); 
+		g.setColor(back);
+		g.fillRect(0, 0, canvasSize.width, canvasSize.height);
+	    }
+	} else {
+	    g.setColor(back);
+	    g.fillRect(0, 0, canvasSize.width, canvasSize.height);
+	}
+    
+	// If there are no phrases, output a brief explanation.
+	if (numberOfItems == 0) {
+	    g.drawString("No phrases match this query.", mainLeftMargin + 10, lineSpacing);
+	}
+
+	// Output each of the visible ResultItems
+	ResultItem result;
+	int tab, i, y = 0;
+	int center = mainLeftMargin 
+	    + ((mainRightMargin - mainLeftMargin 
+		- g.getFontMetrics().stringWidth(parent.searchPhrase)) / 2);
+	int thesColumn = mainLeftMargin + firstColumnWidth 
+	    + g.getFontMetrics().stringWidth("Narrower term");
+
+	String body, prefix, suffix;
+	
+	for (i = scrollValue; 
+	     (i < numberOfItems) && (y + lineSpacing < canvasSize.height); i++) {
+
+	    // Get the resultItem to output
+	    result = (ResultItem) items.elementAt(i);
+	    
+	    // Graphics settings for drawing this line
+	    y += lineSpacing;
+	    g.setFont(areaPlain);
+
+	    // Highlight the selected phrase.
+	    if (i == itemSelected) {
+		g.setColor(phind.highlight_bg);
+		g.fillRect(mainLeftMargin, y-lineSpacing+2, mainRightMargin, lineSpacing);
+	    }
+
+	    // Draw the item
+
+	    // Draw a phrase item
+	    if (result.isPhrase()) {
+		prefix = result.prefixText();
+		body = result.mainText();
+		suffix = result.suffixText();
+		
+		g.setColor(phind.expansion_fg);
+		g.setFont(areaPlain);
+		tab = center - g.getFontMetrics().stringWidth(prefix) - space;
+		g.drawString(prefix, tab, y);
+		
+		g.setFont(areaBold);
+		g.drawString(body, center, y);
+		
+		tab = center + space + g.getFontMetrics().stringWidth(body);
+		g.setFont(areaPlain);
+		g.drawString(suffix, tab, y);
+	
+	    } 
+
+	    // Draw a document item
+	    else if (result.isDocument()){
+		body = result.mainText();
+
+		g.setColor(phind.document_fg);
+		g.setFont(areaPlain);
+		tab = (mainRightMargin - g.getFontMetrics().stringWidth(body)) / 2;
+		g.drawString(body, tab, y);
+      
+	    } 
+	
+	    // Draw a thesaurus link item
+	    else if (result.isLink()){
+		prefix = result.prefixText() + ":";
+		body = result.mainText();
+		tab = thesColumn - g.getFontMetrics().stringWidth(prefix) - space;
+
+		g.setColor(phind.thesaurus_fg);
+		g.setFont(areaPlain);
+		g.drawString(prefix, tab, y);
+		g.setFont(areaBold);
+		g.drawString(body, thesColumn, y);
+	    } 
+	    
+	    // Draw a "more phrases/documents/links" marker
+	    else if (result.isMorePhrases() 
+		     || result.isMoreDocuments() 
+		     || result.isMoreLinks()){
+		body = result.mainText();
+		
+		if (result.isMorePhrases()) {
+		    g.setColor(phind.expansion_bar_bg);
+		} else if (result.isMoreDocuments()) {
+		    g.setColor(phind.document_bar_bg);
+		} else {
+		    g.setColor(phind.thesaurus_bar_bg);		    
+		}
+		g.fillRect(mainLeftMargin, y-lineSpacing+2, mainRightMargin, lineSpacing);
+
+		if (result.isMorePhrases()) {
+		    g.setColor(phind.expansion_bar_fg);
+		} else if (result.isMoreDocuments()) {
+		    g.setColor(phind.document_bar_fg);
+		} else {
+		    g.setColor(phind.thesaurus_bar_fg);		    
+		}
+		g.setFont(areaPlain);
+		tab = (mainRightMargin - g.getFontMetrics().stringWidth(body)) / 2;
+		g.drawString(body, tab, y);
+	    } 
+	}
+
+	// Draw the frequecy columns
+
+	// column backgrounds
+	g.setColor(phind.column_1_bg);
+	g.fillRect(firstColumnLeft, 0, firstColumnWidth, canvasSize.height);
+	g.setColor(phind.column_2_bg);
+	g.fillRect(secondColumnLeft, 0, secondColumnWidth, canvasSize.height);
+
+	// fill in the numbers
+	g.setColor(phind.column_1_fg);
+	g.setFont(areaPlain);
+	y = 0;
+	String docsText, freqText;
+
+	for (i = scrollValue; 
+	     (i < numberOfItems) && (y + lineSpacing < canvasSize.height); i++) {
+	    
+	    // Get the resultItem to output
+	    result = (ResultItem) items.elementAt(i);
+	    docsText = result.docsText();
+	    freqText = result.freqText();
+	    
+	    // Graphics settings for drawing this line
+	    y += lineSpacing;
+
+	    // Write the document frequency
+	    if (docsText.length() > 0) {
+		tab = secondColumnLeft - space - g.getFontMetrics().stringWidth(docsText);
+		g.drawString(docsText, tab, y);
+	    }
+
+	    // Write the term frequency
+	    if (freqText.length() > 0) {
+		tab = secondColumnLeft + secondColumnWidth 
+		    - space - g.getFontMetrics().stringWidth(freqText);
+		g.drawString(freqText, tab, y);
+	    }
+	}
+
+	// Adjust the scrollbar
+	if (visible >= numberOfItems) {
+	    parent.disableScrollbar();
+	} else {
+	    scrollbar.setValues(scrollValue, visible, 0, numberOfItems);
+	    // Deprecated - setPageIncrement() has been replaced by
+	    // setBlockIncrement(), but the latter doesn't work on some
+	    // browsers.  Damn.
+	    // scrollbar.setBlockIncrement(visible - 1);
+	    scrollbar.setPageIncrement(visible - 1);
+	    // Deprecated - more nonsense: enable() has been replaced by
+	    // setEnabled(true), but this doesn't work on some browsers.
+	    //scrollbar.setEnabled(true);
+	    scrollbar.enable();
+	}
+
+	
+	// draw the border
+	if (phind.showBorder) {
+	    g.setColor(phind.panel_fg);
+	    g.drawRect(0,0, canvasSize.width - 1, canvasSize.height - 1);
+	}
+    }
+
+    // User interaction
+    //
+    // All interaction with the ResultCanvas is therough mouse clicks, and
+    // is handles in this method.  Note we ignore clicks that follow
+    // another too closely to avoid problems with slow connections.
+    public boolean handleEvent(Event event) {
+    
+	if (event.id == Event.MOUSE_UP) {
+
+	    // ignore actions that occur within 1 second of the last
+	    Date now = new Date();
+	    // System.out.println("Click time: " + now.toString());
+	    if (now.getTime() < (phind.lastQueryEndTime.getTime() + 1000)) {
+		System.out.println("Ignoring click - too close to last query.");
+		return true;
+	    }
+
+	    // which Item is selected?
+	    int rowSelected = event.y / lineSpacing;
+	    itemSelected = rowSelected + firstItemDisplayed;
+	    ResultItem item = (ResultItem) items.elementAt(itemSelected);
+
+	    if (itemSelected <= numberOfItems) {
+	
+		//User clicks on a phrase
+		if (item.isPhrase()) {
+		    itemSelected = itemSelected;
+		    update(getGraphics());
+		    parent.lookupPhrase(item.hiddenText(), item.toString(), 2);
+
+		    // If meta key is held down, send query to search engine
+		    if (event.metaDown() && !phind.library_address.equals("")) {
+			String address = phind.library_address  
+			    + "a=q&c=" + phind.collection
+			    + "&q=%22" + item.toString().replace(' ', '+') + "%22";
+			phind.displayWebPage(address, phind.searchWindowName);
+		    }
+		} 
+
+		// Click on a thesaurus link
+		else if (item.isLink()) {
+		    itemSelected = itemSelected;
+		    update(getGraphics());
+		    parent.lookupPhrase(item.hiddenText(), item.toString(), 2);
+
+		    // If meta key is held down, send query to search engine
+		    if (event.metaDown() && !phind.library_address.equals("")) {
+			String address = phind.library_address  
+			    + "a=q&c=" + phind.collection
+			    + "&q=%22" + item.toString().replace(' ', '+') + "%22";
+			phind.displayWebPage(address, phind.searchWindowName);
+		    }
+		} 
+
+		// The user clicks on a URL; display it.
+		else if (item.isDocument()) {
+		    itemSelected = itemSelected;
+		    update(getGraphics());
+
+		    String address = phind.library_address 
+			+ "a=d&c=" + phind.collection 
+			+ "&d=" + item.hiddenText()
+			+ "&q=" + parent.searchPhrase.replace(' ', '+');
+		    phind.displayWebPage(address, phind.documentWindowName);
+		}
+		 
+		// When the user clicks on "get more phrases" or other marker,
+		// we have to send a new query to the host
+		else if (item.isMorePhrases()){
+		    parent.lookupPhrase(parent.searchKey, parent.searchPhrase, 3);
+		} else if (item.isMoreDocuments()){
+		    parent.lookupPhrase(parent.searchKey, parent.searchPhrase, 4);
+		} else if (item.isMoreLinks()){
+		    parent.lookupPhrase(parent.searchKey, parent.searchPhrase, 5);
+
+
+		}
+		repaint();
+	    }
+	}
+	return true;
+    }    
+  
+}
+
+
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultDisplay.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultDisplay.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultDisplay.java	(revision 16561)
@@ -0,0 +1,126 @@
+/**********************************************************************
+ *
+ * ResultDisplay.java -- Phrase list holder for Phind java applet
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+A ResultDisplay is a Panel that can display a single Component, which will
+be a ResultBox or PhindTitle object.
+
+ResultBoxes can be "chained" together with their "prev" and "next" fields,
+allowing us to have an arbitrary number of "displays" in which to show
+phrase lists.
+
+Main methods:
+* emptyContents will remove the component and emptyAll the "next" display
+* display will add an item to the first (ie: most "prev") available box
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.awt.Panel;
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+class ResultDisplay extends Panel {
+
+  Phind phind;
+  ResultDisplay prev, next;
+  Component current;
+  boolean empty;
+
+  // Create a new Display
+  ResultDisplay(Phind ph, ResultDisplay pr) {
+    phind = ph;
+    prev = pr;
+    next = null;
+    if (prev != null) prev.next = this;
+    setLayout(new BorderLayout());
+
+    empty = true;
+    current = new PhindTitle(ph);
+    add(current);
+  }
+
+  // Empty the contents of the display,
+  // and all the next displays
+  void emptyContents() {
+    if (empty == false) {
+      empty = true;
+      current = new PhindTitle(phind);
+      removeAll();
+      add(current);
+    }
+    if (next != null) next.emptyContents();
+  }
+
+  // Set the contents of this display
+  Component setContents( Component r ) {
+    removeAll();
+    add("Center", r);
+    current = r;
+    empty = false;
+    if (r.getClass().getName().equals("ResultBox")) {
+      ((ResultBox) r).display = this;
+    }
+    return r;
+  }
+
+
+  
+  // Display a component in the "most previous" display
+  ResultDisplay display( Component r ) {
+
+    if (prev == null) {
+      // if there is no previous box, add the component here
+      setContents(r);
+      return this;
+
+    } else if (prev.empty == true) {
+      // if the previous box is empty, add the component to it instead
+      return prev.display(r);
+
+    } else if (empty == true) {
+      // if this box is empty, add the compnent to it
+      setContents(r);
+      return this;
+
+    } else {
+      // if this box is occupied,
+      // shift it's contents prev-wards and add here.
+      prev.display(current);
+      setContents(r);
+      return this;
+    }
+  }
+
+}
+
+
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItem.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItem.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItem.java	(revision 16561)
@@ -0,0 +1,128 @@
+/**********************************************************************
+ *
+ * ResultItemDocument.java -- a result in the Phind applet
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+A result item holds the data describing a single line returned from a phind
+query.  The complex functionality of phrase and URL items is implemented in
+subclasses; the base class handles simpler cases like the "get more phrases" 
+marker.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+public class ResultItem {
+
+    // A text string describing the item
+    String text = "Generic ResultItem";
+
+    // There are several kinds of ResultItem, identified by their
+    // "kind" field, which has an integer value from the list below.
+    final static int message = 8;
+    final static int linkItem = 7;
+    final static int moreLinks = 6;
+    final static int phraseItem = 5;
+    final static int morePhrases = 4;
+    final static int documentItem = 3;
+    final static int moreDocuments = 2;
+    final static int unknownResultItem = 1;
+
+    // The kind of the item
+    int kind = unknownResultItem;
+
+    // When results are displayed, they are loosely sorted by their
+    // kind.  The user can change the sort order numbers with the 
+    // ResultOrder parameter in Phind.java.
+    static int sortMessage = 8;
+    static int sortLinkItem = 7;
+    static int sortMoreLinks = 6;
+    static int sortPhraseItem = 5;
+    static int sortMorePhrases = 4;
+    static int sortDocumentItem = 3;
+    static int sortMoreDocuments = 2;
+    static int sortUnknownResultItem = 1;
+
+    // the primary sort key
+    int sort = 0;
+    
+    // The frequency of the item (secondary sort key)
+    int frequency = 0;
+
+
+    // Create a blank ResultItem
+    ResultItem() {
+	text = "Unknown result type";
+	kind = unknownResultItem;
+	frequency = 0;
+    }
+
+    // Create a new ResultItem of some given kind
+    ResultItem(int newKind) {
+	kind = newKind;
+	frequency = 0;
+
+	if (kind == moreLinks) {
+	    text = "get more thesaurus links";
+	    sort = sortMoreLinks;
+	} else if (kind == morePhrases) {
+	    text = "get more phrases";
+	    sort = sortMorePhrases;
+	} else if (kind == moreDocuments) {
+	    text = "get more documents";
+	    sort = sortMoreDocuments;
+	} else {
+	    text = "Unknown result type";
+	    sort = sortUnknownResultItem;
+	}
+    }
+
+    // Test the type of a ResultItem
+    public boolean isLink() { return false; }
+    public boolean isMoreLinks() { return (kind == moreLinks); }
+    public boolean isPhrase() { return false; }
+    public boolean isMorePhrases() { return (kind == morePhrases); }
+    public boolean isDocument() { return false; }
+    public boolean isMoreDocuments() { return (kind == moreDocuments); }
+
+    // Return the bare text of the item
+    public String toString() { return(text.trim()); }
+
+    // Return the text of the item components
+    public String mainText() { return text; }
+    public String freqText() { 
+	if (frequency > 0) { return Integer.toString(frequency); } 
+	else { return ""; }
+    }
+    public String docsText() { return ""; }
+    public String prefixText() { return ""; }
+    public String suffixText() { return ""; }
+    public String hiddenText() { return ""; }
+}
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemDocument.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemDocument.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemDocument.java	(revision 16561)
@@ -0,0 +1,70 @@
+/**********************************************************************
+ *
+ * ResultItemDocument.java -- a document result in the Phind applet
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+It contains information describing a document in which a search term 
+occurs.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+public class ResultItemDocument extends ResultItem {
+
+    // The document's title is stored in the superclasses 
+    // text variable, and the number of times the phrase occurs
+    // in the frequency variable. 
+
+    // The document's unique object identifier (hash value).
+    String hash;
+
+    // Create a ResultItem for a document
+    ResultItemDocument(String newHash, String newTitle, String newFrequency) {
+	
+	kind = documentItem;
+	text = newTitle;
+
+	sort = sortDocumentItem;
+	frequency = Integer.valueOf(newFrequency).intValue();
+	hash = newHash;
+    }
+
+    // Is this item a document?
+    public boolean isDocument() { return true; }
+
+    // Return the bare text of the item
+    public String toString() { return(text); }
+
+    // Return the text of the item components
+    public String hiddenText() { return hash; }
+ 
+
+}
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemLink.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemLink.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemLink.java	(revision 16561)
@@ -0,0 +1,86 @@
+/**********************************************************************
+ *
+ * ResultItemLink.java -- a thesaurus link in the Phind applet
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+It contains information describing a thesaurus link.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+public class ResultItemLink extends ResultItem {
+
+    // The phrase number, stored as a string and as a number
+    String id;
+    int symbol;
+
+    // The text of the link is stored in the inherited text field.
+
+    // The type of thesaurus link
+    String linkType;
+
+    // The total frequency is stored in the inherited frequency field.
+
+    // The document frequency of the thesaurus item
+    int documentFrequency;
+
+    // Create a ResultItem for a thesaurus link
+    ResultItemLink(String newId, String newText, String newLinkType, 
+		   String tf, String df) { 
+
+	kind = linkItem;
+	id = newId;
+	symbol = Integer.valueOf(newId).intValue();
+	text = newText.trim();
+	sort = sortLinkItem;
+	linkType = newLinkType;
+	frequency = Integer.valueOf(tf).intValue();
+	documentFrequency = Integer.valueOf(df).intValue();
+    }
+
+    // Is this item a phrase?
+    public boolean isLink() { return true; }
+
+    // Return the text of the item components
+    public String hiddenText() { return id; }
+    public String docsText() { 
+	if (documentFrequency <= 0) { return ""; }
+	else { return Integer.toString(documentFrequency); }
+    }
+    public String prefixText() { 
+	if (linkType.equals("RT")) { return "Related term"; }
+	else if (linkType.equals("BT")) { return "Broader term"; }
+	else if (linkType.equals("NT")) { return "Narrower term"; }
+	else if (linkType.equals("USE")) { return "Use"; }
+	else if (linkType.startsWith("UF")) { return "Used for"; }
+	else { return linkType; }
+    }
+}
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemPhrase.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemPhrase.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultItemPhrase.java	(revision 16561)
@@ -0,0 +1,85 @@
+/**********************************************************************
+ *
+ * ResultItemPhrase.java -- a phrase result in the Phind applet
+ *
+ * Copyright 2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+It contains information describing a phrase.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+public class ResultItemPhrase extends ResultItem {
+
+    // The phrase number, stored as a string and as a number
+    String id;
+    int symbol;
+
+    // The text of the phrase is split into a prefix, body, and suffix.
+    // The body is the same as the parent phrase (or search term) and 
+    // the prefix and suffix are the surrounding text.
+    String prefix;
+    String body;
+    String suffix;
+
+    // The document frequency of the phrase
+    int documentFrequency;
+
+    // Create a ResultItem for a phrase
+    ResultItemPhrase(String newId, String tf, String df, 
+		     String p, String b, String s) {
+
+	kind = phraseItem;
+
+	id = newId;
+	symbol = Integer.valueOf(newId).intValue();
+
+	sort = sortPhraseItem;
+	frequency = Integer.valueOf(tf).intValue();
+	documentFrequency = Integer.valueOf(df).intValue();
+
+	prefix = p;
+	body = b;
+	suffix = s;
+	text = (prefix + " " + body + " " + suffix);
+
+    }
+
+    // Is this item a phrase?
+    public boolean isPhrase() { return true; }
+
+    // Return the text of the item components
+    public String mainText() { return body; }
+    public String docsText() { return Integer.toString(documentFrequency); }
+    public String prefixText() { return prefix; }
+    public String suffixText() { return suffix; }
+    public String hiddenText() { return id; }
+
+}
+
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultTitle.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultTitle.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/ResultTitle.java	(revision 16561)
@@ -0,0 +1,163 @@
+/**********************************************************************
+ *
+ * ResultTitle.java -- describe the contents of a ResultBox
+ *
+ * Copyright 1997-2000 Gordon W. Paynter
+ * Copyright 2000 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.
+ *
+ *********************************************************************/
+
+/*********************************************************************
+
+This class is used in the Phind java applet (Phind.java).
+
+ResultTitle is part of the result list displayed to the user.  It lists the
+phrase in the Result box, the number of expansions it occurs in, and the
+number of documents it occurs in.
+
+**********************************************************************/
+
+package org.nzdl.gsdl.Phind;
+
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Dimension;
+import java.awt.Color;
+
+
+public class ResultTitle extends Canvas {
+
+  // Other objects associated with this 
+  Phind phind;
+  ResultBox parent;
+  ResultCanvas canvas;
+
+  // fonts and font spacings to use
+  Font plain, bold;
+  Graphics g;
+
+  // Create a ResultTitle from the ResultBox which is its parent.
+  ResultTitle(ResultBox p) {
+
+    parent = p;
+    phind = p.phind;
+    canvas = p.c;
+    g = getGraphics();
+
+    plain = phind.plainFont;
+    bold  = phind.boldFont;
+    
+    // Note: the size() and resize() methods are deprecated, but the newer
+    // getSize() and setSize() methods fail in NetScape 4
+    //Dimension d = getSize();
+    Dimension d = size();
+    d.height = phind.fontSize + 10;
+    //setSize(d);
+    resize(d);
+    
+  }
+
+  public void update(Graphics g) {
+    paint(g);
+  }
+
+  public void paint(Graphics g) {
+
+    // calculate the canvas size, margins, and spacing
+    Dimension canvasSize = size();
+    
+    g.setFont(plain);
+    int margin = g.getFontMetrics().stringWidth(" 8888 ");
+    int y = phind.fontSize + 5;
+
+    int rightMargin = canvas.size().width;
+    int secondColumn = rightMargin - margin;
+    int firstColumn = secondColumn - margin;
+
+    g.setColor(Color.white);
+    g.fillRect(0, 0, canvasSize.width, canvasSize.height);
+    g.setColor(Color.black);
+
+
+    // What is the phrase we are searching for?
+    String phrase = parent.searchPhrase.replace('+', ' ');
+ 
+    // Construct the description phrase
+    String links = "";
+    if (parent.numberOfThesaurusLinks <= 0) {
+	links = "";
+    } else if (parent.numberOfThesaurusLinks == 1) { 
+	links = "1 link";
+    } else if (parent.thesaurusLinksRetrieved == parent.numberOfThesaurusLinks) {
+	links = parent.numberOfThesaurusLinks + " links";
+    } else {
+	links = parent.thesaurusLinksRetrieved + " of " 
+	    + parent.numberOfThesaurusLinks + " links";
+    }
+
+    String expansions = "";
+    if (parent.numberOfExpansions <= 0) {
+	expansions = "no phrases";
+    } else if (parent.numberOfExpansions == 1) { 
+	expansions = "1 phrase";
+    } else if (parent.expansionsRetrieved == parent.numberOfExpansions) {
+	expansions = parent.numberOfExpansions + " phrases";
+    } else {
+	expansions = parent.expansionsRetrieved + " of " 
+	    + parent.numberOfExpansions + " phrases";
+    }
+
+    String documents = "";
+    if (parent.numberOfDocuments <= 0) { 
+	documents = "no documents";
+    } else if (parent.documentsRetrieved == 1) {
+	documents = "1 document";
+    } else if (parent.documentsRetrieved == parent.numberOfDocuments) {
+	documents = parent.numberOfDocuments + " documents";
+    } else {
+	documents = parent.documentsRetrieved + " of " 
+	    + parent.numberOfDocuments + " documents";
+    }
+
+    String status = "(";
+    if (parent.numberOfThesaurusLinks > 0) {
+	status = status + links + ", ";
+    } 
+    status = status + expansions + ", " + documents + ")";
+
+
+    // Draw the text 
+    g.setFont(bold);
+    g.drawString(phrase, 0, y);
+    int tab = g.getFontMetrics().stringWidth(phrase + "  ");
+
+    g.setFont(plain);
+    g.drawString(status, tab, y);
+    tab = tab + g.getFontMetrics().stringWidth(status);
+    
+    if (tab < firstColumn) {
+      g.drawString("docs", firstColumn, y);
+      g.drawString("freq", secondColumn, y);
+    }
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/URLUTF8Encoder.java
===================================================================
--- /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/URLUTF8Encoder.java	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/java/org/nzdl/gsdl/Phind/URLUTF8Encoder.java	(revision 16561)
@@ -0,0 +1,159 @@
+/**********************************************************************
+ *
+ *
+ * URLUTF8Encoder.java -- encode UTF8 String into a URL form
+ *
+ * W3C® SOFTWARE NOTICE AND LICENSE
+ *
+ * Copyright © 1994-2000 World Wide Web Consortium, 
+ * (Massachusetts Institute of Technology, Institut National de 
+ * Recherche en Informatique et en Automatique, Keio University). 
+ * All Rights Reserved.  http://www.w3.org/Consortium/Legal/
+ * 
+ * This W3C work (including software, documents, or other related items) is
+ * being provided by the copyright holders under the following license. By
+ * obtaining, using and/or copying this work, you (the licensee) agree that
+ * you have read, understood, and will comply with the following terms and
+ * conditions:
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation, with or without modification, for any purpose and without
+ * fee or royalty is hereby granted, provided that you include the following
+ * on ALL copies of the software and documentation or portions thereof,
+ * including modifications, that you make:
+ * 
+ *   1. The full text of this NOTICE in a location viewable to users of the
+ *      redistributed or derivative work.
+ * 
+ *   2. Any pre-existing intellectual property disclaimers, notices, or terms
+ *      and conditions. If none exist, a short notice of the following form
+ *      (hypertext is preferred, text is permitted) should be used within the
+ *      body of any redistributed or derivative code: "Copyright ©
+ *      [$date-of-software] World Wide Web Consortium, (Massachusetts
+ *      Institute of Technology, Institut National de Recherche en
+ *      Informatique et en Automatique, Keio University). All Rights
+ *      Reserved. http://www.w3.org/Consortium/Legal/"
+ * 
+ *   3. Notice of any changes or modifications to the W3C files, including the
+ *      date changes were made. (We recommend you provide URIs to the location
+ *      from which the code is derived.)
+ * 
+ * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS
+ * MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
+ * PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE
+ * ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+ * 
+ * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
+ * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR
+ * DOCUMENTATION.
+ * 
+ * The name and trademarks of copyright holders may NOT be used in advertising
+ * or publicity pertaining to the software without specific, written prior
+ * permission. Title to copyright in this software and any associated
+ * documentation will at all times remain with copyright holders.
+ *
+ *********************************************************************/
+
+/**
+ * Provides a method to encode any string into a URL-safe
+ * form, the so-called "x-www-form-urlencoded" form.
+ * Non-ASCII characters are first encoded as sequences of
+ * two or three bytes, using the UTF-8 algorithm, before being
+ * encoded in "x-www-form-urlencoded".
+ */
+
+package org.nzdl.gsdl.Phind;
+
+public class URLUTF8Encoder
+{
+
+  final static String[] hex = {
+    "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
+    "%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
+    "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
+    "%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
+    "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
+    "%28", "%29", "%2a", "%2b", "%2c", "%2d", "%2e", "%2f",
+    "%30", "%31", "%32", "%33", "%34", "%35", "%36", "%37",
+    "%38", "%39", "%3a", "%3b", "%3c", "%3d", "%3e", "%3f",
+    "%40", "%41", "%42", "%43", "%44", "%45", "%46", "%47",
+    "%48", "%49", "%4a", "%4b", "%4c", "%4d", "%4e", "%4f",
+    "%50", "%51", "%52", "%53", "%54", "%55", "%56", "%57",
+    "%58", "%59", "%5a", "%5b", "%5c", "%5d", "%5e", "%5f",
+    "%60", "%61", "%62", "%63", "%64", "%65", "%66", "%67",
+    "%68", "%69", "%6a", "%6b", "%6c", "%6d", "%6e", "%6f",
+    "%70", "%71", "%72", "%73", "%74", "%75", "%76", "%77",
+    "%78", "%79", "%7a", "%7b", "%7c", "%7d", "%7e", "%7f",
+    "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
+    "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
+    "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
+    "%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
+    "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+    "%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
+    "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
+    "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
+    "%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
+    "%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
+    "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+    "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
+    "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
+    "%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
+    "%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
+    "%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
+  };
+
+  /**
+   * Encode a string to the "x-www-form-urlencoded" form, enhanced
+   * with the UTF-8-in-URL proposal. This is what happens:
+   *
+   * <ul>
+   * <li><p>The ASCII characters 'a' through 'z', 'A' through 'Z',
+   *        and '0' through '9' remain the same.
+   *
+   * <li><p>The space character ' ' is converted into a plus sign '+'.
+   *
+   * <li><p>All other ASCII characters are converted into the
+   *        3-character string "%xy", where xy is
+   *        the two-digit hexadecimal representation of the character
+   *        code
+   *
+   * <li><p>All non-ASCII characters are encoded in two steps: first
+   *        to a sequence of 2 or 3 bytes, using the UTF-8 algorithm;
+   *        secondly each of these bytes is encoded as "%xx".
+   * </ul>
+   *
+   * @param s The string to be encoded
+   * @return The encoded string
+   */
+  public static String encode(String s)
+  {
+    StringBuffer sbuf = new StringBuffer();
+    int len = s.length();
+    for (int i = 0; i < len; i++) {
+      int ch = s.charAt(i);
+      if ('A' <= ch && ch <= 'Z') {		// 'A'..'Z'
+	sbuf.append((char)ch);
+      } else if ('a' <= ch && ch <= 'z') {	// 'a'..'z'
+	sbuf.append((char)ch);
+      } else if ('0' <= ch && ch <= '9') {	// '0'..'9'
+	sbuf.append((char)ch);
+      } else if (ch == ' ') {			// space
+	sbuf.append('+');
+      } else if (ch <= 0x007f) {		// other ASCII
+	sbuf.append(hex[ch]);
+      } else if (ch <= 0x07FF) {		// non-ASCII <= 0x7FF
+	sbuf.append(hex[0xc0 | (ch >> 6)]);
+	sbuf.append(hex[0x80 | (ch & 0x3F)]);
+      } else {					// 0x7FF < ch <= 0xFFFF
+	sbuf.append(hex[0xe0 | (ch >> 12)]);
+	sbuf.append(hex[0x80 | ((ch >> 6) & 0x3F)]);
+	sbuf.append(hex[0x80 | (ch & 0x3F)]);
+      }
+    }
+    return sbuf.toString();
+  }
+
+}
+
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/Makefile.in	(revision 16561)
@@ -0,0 +1,146 @@
+###########################################################################
+#
+# Makefile -- src/oaiservr
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+COLSERVR_DIR = $(GSDL_DIR)/src/colservr
+PROTOCOL_DIR = $(GSDL_DIR)/src/protocol
+RECPT_DIR = $(GSDL_DIR)/src/recpt
+
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+ACCENTFOLD_LIBS = $(INDEXERS_DIR)/packages/unac/libunac.a 
+else
+ACCENTFOLD_LIBS = 
+endif
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_LIBS = $(PACKAGES_DIR)/sqlite/lib/libsqlite3.a -ldl -lpthread
+else
+SQLITE_LIBS = 
+endif
+
+
+CC = @CC@
+CFLAGS = @CFLAGS@ @COMPAT32BITFLAGS@
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+INCLUDES = -I$(GSDL_DIR)/src/lib -I$(COLSERVR_DIR) -I$(PROTOCOL_DIR) -I$(RECPT_DIR) -I$(INDEXERS_DIR)/mgpp/text
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@ @GDBM_LIBPATH@ -lgdbm -L$(PACKAGES_DIR)/expat/lib -lexpat $(ACCENTFOLD_LIBS) $(SQLITE_LIBS)
+INSTALL = @INSTALL@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) -o $@ $< 
+
+
+RECPT_OBJECTS = \
+	$(RECPT_DIR)/cgiargs.o \
+	$(RECPT_DIR)/cgiutils.o \
+	$(RECPT_DIR)/htmlutils.o
+
+SOURCES = \
+	abstractlistaction.cpp \
+	dublincore.cpp \
+	identifyaction.cpp \
+	listidsaction.cpp \
+	listrecsaction.cpp \
+	listsetsaction.cpp \
+	metaformat.cpp \
+	metaformatsaction.cpp \
+	oaiaction.cpp \
+	oaiargs.cpp \
+	oaiconfig.cpp \
+	oaidispatcher.cpp \
+	oaimain.cpp \
+	oaitools.cpp \
+	recordaction.cpp \
+	resumptiontoken.cpp \
+	rfc1807.cpp
+
+OBJECTS = \
+	abstractlistaction.o \
+	dublincore.o \
+	identifyaction.o \
+	listidsaction.o \
+	listrecsaction.o \
+	listsetsaction.o \
+	metaformat.o \
+	metaformatsaction.o \
+	oaiaction.o \
+	oaiargs.o \
+	oaiconfig.o \
+	oaidispatcher.o \
+	oaimain.o \
+	oaitools.o \
+	recordaction.o \
+	resumptiontoken.o \
+	rfc1807.o
+
+GSDL_LIBS = \
+	$(PROTOCOL_DIR)/gsdlprotocol.a \
+	$(COLSERVR_DIR)/gsdlcolservr.a \
+	$(INDEXERS_DIR)/mg/src/text/libmgtext.a \
+	$(INDEXERS_DIR)/mg/lib/libmglib.a \
+	$(INDEXERS_DIR)/mgpp/text/libmgpptext.a \
+	$(INDEXERS_DIR)/mgpp/lib/libmgpplib.a \
+	$(GSDL_DIR)/src/lib/gsdllib.a
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(RECPT_OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = oaiserver
+
+
+# Default target: make the oaiserver executable
+all: $(EXECUTABLE)
+
+oaiserver: $(EXECUTABLE_OBJECTS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/cgi-bin
+
+clean:
+	rm -f $(OBJECTS) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2runtime/src/oaiservr/README.txt
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/README.txt	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/README.txt	(revision 16561)
@@ -0,0 +1,101 @@
+OAI Server Configuration Instructions
+-------------------------------------
+
+Greenstone needs some configuration before successfully supporting OAI serving.  This consists of three steps:
+
+1) Server-wide configuration to support vital OAI commands such as Identify.
+   This allows the OAI client to obtain information on the maintainer of
+   the server, etc.  See the OAI documentation for further information on
+   the Identify verb.  These configurations are kept in gsdl/etc/oai.cfg.
+
+2) Configuring collections to be 'active' OAI collections; just as collections
+   in Greenstone can be 'private' or 'public', collections can similarly be
+   'visible' or 'invisible' to OAI.  By default, collections are NOT served
+   via OAI - you have to enable them by adding each collection's name to a
+   list in gsdl/etc/oai.cfg.
+
+3) Mapping your collection metadata to a "Namespace" supported by OAI.  At
+   present, Greenstone supports the Dublin Core metadata format alone in OAI,
+   and metadata fields in a Greenstone collection must be mapped into the
+   Dublin Core namespace.  This configuration can be done either server-wide,
+   for all collections, in gsdl/etc/oai.cfg or in the etc/collect.cfg of
+   each individual collection.
+
+This documentation explains how to set up Greenstone's configuration for steps 1-3 above, install, deplay and test OAI on your Greenstone system.
+
+Part 1: Server-Wide Configuration Information
+---------------------------------------------
+
+Your OAI server should provide some basic information on the collections it holds as a whole.  For this purpose, all collections on your server are treated the same: you cannot have different information for each collection.  In this section, we describe what information you should provide, and how to configure Greenstone correctly to provide it.
+
+The basic server-wide information is:
+  * The OAI repository (server) name - this is a human readable form such
+    as "The Research Authority's OAI Server".
+  * The central administrative email address for the OAI repository.
+  * The base URL for the repository.
+  * Which version of OAI the repository supports/delivers.
+  * A free text item of information about the OAI repository - e.g. its
+    purpose, size, origins, etc.
+
+These items are kept in the following items of gsdl/etc/oai.cfg.  The syntax rules for these configuration items are the same as for gsdl/etc/main.cfg -  e.g. strings containing spaces should be enclosed in quotation marks.  See the main Greenstone configuration documentation for further information.
+
+  * repositoryName
+       One item only, free text.
+  * maintainer
+       One item only, as a valid email address
+  * baseURL
+       One item only, see the example in the oai.cfg file.
+  * httpdomain 
+       NB: in the gsdlsite.cfg file, NOT oai.cfg
+       Should not require specific configuration if your Greenstone install
+       is already operational through your web server.
+  * oaiversion
+       One item only, which should be either "1.1 or "2.0"
+  * oaiinfo
+       Numerous items, which are usually in a label and value pair.
+       An example item may be:
+          oaiinfo sponsor "The Big Cheese Corporation"
+
+Sample items are already provided in the default oai.cfg file - please ensure that you complete these in your preferred text editor.
+
+
+Part 2: Enabling Collections
+----------------------------
+
+In addition to the items above, one further sort of item needs to be added to gsdl/etc/oai.cfg - a list of the collections that should be made available to OAI clients.  This list is supplied in one or more lines in gsdl/etc/oai.cfg in the form:
+
+  oaicollection OAIDemo HDL
+
+The first item in each line should be the same - "oaicollection" - followed by one or more collection names.  Each collection name should be the name of the folder/directory in the gsdl/collect directory which holds the collection.
+
+Once these items have been added to gsdl/etc/oai.cfg, the final stage of the OAI configuration can begin: mapping from a collection's own metdata fields into the Dublin Core metadata fields used by OAI.  This will be detailed in the folloowing section of this document.
+
+
+Part 3: Mapping fields
+----------------------
+
+In Greenstone, metadata fields are created by the collection designer.  These may follow an accepted standard - e.g. Dublin Core - be a modification or extension of an accepted standard, or a bespoke format specific to the needs of the particular collection.  As Greenstone installer, you should have decided upon one of these three approaches already.  You may have chosen one of the standard metadata structures incorporated into the Greenstone Librarian Interface (GLI), or created your own using your own metadata files and automatically extracted information from your document files.  Whatever the approach you have used for obtaining the metadata in your collection, or whatever the fields are that you have used in your collection, the approach to linking these fields (mapping them) to the OAI Dublin Core format is the same.
+
+The mapping of fields is done in two ways: first, globally for all collections, and secondly specifically for one collection.  The syntax for the configuration items to achieve these different mappings is the same.  The file in which they are placed determines whether they apply to all collections, are just one.  Global mappings are found with the other OAI configuration items in gsdl/etc/main.cfg whilst collection-specific mappings are placed in the etc/collect.cfg file of the collection itself.
+
+Mapping items in the configuration file take the following format:
+
+  oaimapping <greenstone fieldname> <dublin core fieldname>
+
+The syntax of each of the two fieldnames given above requires a little more detail; we will take the Dubline Core fieldname first.  
+
+The Dublin Core fieldsname should be given in the form "DC:<fieldname>", as OAI can support other metadata formats, and thus we use "DC:" to be clear that we are mapping to Dublin Core.  The fieldnames available for Dublin Core can be found on the central Dublin Core website at http://www.dublincore.org/.
+
+The Greenstone fieldname can be in two formats.  The simpler one is simply the name of the field to map from.  The more complex one has the name of the collection placed in front of the field name, followed by a colon.  e.g. "demo:Title" would mean the metadata field "Title" in the collection called "demo".  Using this, all mappings can be stored in the central gsdl/etc/oai.cfg file if you wish, and we recommend this approach.
+
+Let us recap at this point with a few example items for a gsdl/etc/oai.cfg:
+
+oaimapping Author dc.Creator
+oaimapping dls.Title  dc.Title
+oaimapping Photos:fileformat dc.Format
+
+The first item maps any occurrence of 'Author' in any collection to the Dublin Core 'Creator' field (there is no 'Author' field in DC).  The second item maps 'dls.Title' to 'dc.Title'. Finally, in the specific collection 'Photos', we will map the unique metadata field 'fileformat' to the Dublin Core 'Format' field.
+
+
+Installation and Testing
+------------------------
Index: /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.cpp	(revision 16561)
@@ -0,0 +1,454 @@
+#include "abstractlistaction.h"
+#include "recptprototools.h"
+
+#include "oaitools.h"
+
+bool abstractlistaction::validateAction(recptproto *protocol, oaiargs &params, int &numArgs)
+{ 
+  // Remove any parameters that aren't valid for this action
+  text_tmap::const_iterator param_iterator = params.begin();
+  while (param_iterator != params.end())
+  {
+    if (param_iterator->first != "verb" &&
+	param_iterator->first != "from" &&
+	param_iterator->first != "until" &&
+	param_iterator->first != "set" &&
+	param_iterator->first != "resumptionToken" &&
+	param_iterator->first != "metadataPrefix")
+    {
+      params.erase(param_iterator->first);
+    }
+
+    param_iterator++;
+  }
+
+  text_t from  = params["from"];
+  text_t until = params["until"];
+
+  // from date must be less than, or equal to, until date
+  if ((from != "") && (until != "") && !(from <= until)){
+    this->errorType = "badArgument";
+    return false;
+  }
+
+  if (from != ""){
+    // Must be in the form YYYY-MM-DD
+    if(from.size() != 10){
+      this->errorType = "badArgument";
+      params.erase("from");
+    }
+    else{
+      if(from[4] != '-' || from[7] != '-'){
+	this->errorType = "badArgument";
+	params.erase("from");
+      }
+    }
+    ++numArgs; // Increase valid args count
+  }
+
+  if (until != ""){
+    // Must be in the form YYYY-MM-DD
+    if(until.size() != 10){
+      this->errorType = "badArgument";
+      params.erase("until");
+    }
+    else{
+      if(until[4] != '-' || until[7] != '-'){
+	this->errorType = "badArgument";
+	params.erase("until");
+      }
+    }
+    ++numArgs; // Increase valid args count
+  }
+
+  if (this->errorType == "badArgument")
+  {
+    return false;
+  }
+
+  if (params["set"] != "") { 
+    text_t gsdlSet = params["set"];
+    text_t gsdlCollect = "";
+
+    // given 'demo:CL2', toGSDL returns 'demo' in gsdlCollect and 'CL2' in gsdlSet. If there is no further
+    // set specified after the name of the collection however, then gsdlSet is empty.
+    oaiclassifier::toGSDL(gsdlCollect, gsdlSet);
+
+    comerror_t err;
+    ColInfoResponse_t cinfo;
+
+    // check that the collection is accessible
+    protocol->get_collectinfo(gsdlCollect, cinfo, err, cerr);
+    if (err != noError) {
+      this->errorType = "badArgument";
+      return false;
+    }
+
+    // exclude collections that are not listed in the configured OAI list
+    text_tarray &collections = this->configuration->getCollectionsList();
+    int          c;
+    for (c = 0; c < collections.size(); c ++) {
+      if (collections[c] == gsdlCollect)
+	break;
+    }
+    if (c == collections.size()) {
+      this->errorType = "badArgument";
+      return false;
+    }
+    
+    if (gsdlSet != "") {
+      // check the child set if it is given
+      if (!this->check_classifier(protocol, gsdlCollect, gsdlSet)) {
+	this->errorType = "badArgument";
+	return false;
+      }
+    }
+    ++numArgs;
+  }
+
+  if (params["resumptionToken"] != "") {
+    ResumptionToken token(params["resumptionToken"]);
+
+    if (!token.isValid()) {
+      this->errorType = "badResumptionToken";
+      return false;
+    }
+    ++numArgs;
+  }
+  
+  this->errorType = "";
+  return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool abstractlistaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  text_t from = params["from"];
+  text_t until = params["until"];
+  text_t metaFormat = params["metadataPrefix"];
+  bool   prevDocSeen;
+  ResumptionToken *token = NULL;
+
+  // start the call; clear down the total number of output documents
+  this->outputDocs = 0;
+
+  // We don't actually handle resumptionTokens yet; if we get one, ignore it
+  if (params["resumptionToken"] != "") {
+    token = new ResumptionToken(params["resumptionToken"]);
+  }
+
+  this->replyToken = NULL;
+
+  // if we've been asked for a set, then use it!
+  if (params["set"] != "") {
+    // get the children of this set
+    text_t gsdlSet = params["set"];
+    text_t gsdlCollect = "";
+
+    // given 'demo:CL2', toGSDL returns 'demo' in gsdlCollect and 'CL2' in gsdlSet. If there is no further
+    // set specified after the name of the collection however, then gsdlSet is empty.
+    oaiclassifier::toGSDL(gsdlCollect, gsdlSet);
+
+    // If gsdlSet is empty, then the user is requesting all the identifiers for the collection, so 
+    // we simply output all docs via their oai_id tag. But if a specific subset IS requested, then
+    // use recurse_set() to traverse any sub classifiers to find the relevant docs.
+    if(gsdlSet == ""){
+      ColInfoResponse_t cinfo;
+      comerror_t err;
+      this->output_content_for_col(output, protocol, gsdlCollect, cinfo, err, params);
+    }
+    else {
+      if (this->check_classifier(protocol, gsdlCollect, gsdlSet)) {
+	this->recurse_set(output, protocol, gsdlCollect, gsdlSet, params, token);
+      }
+    }
+  }
+  // output all records in all hierarchies
+  else {
+    this->output_content_for_all(output, protocol, params);
+  }
+
+  // If - regardless of set required - no documents have been seen, throw an error.
+  if (this->configuration->getOAIVersion() >= 200 && this->prevDocSeen == false) {
+    errorType = "noRecordsMatch";
+    this->output_error(output, errorType);
+
+    return false;
+  }
+
+  // do a resumption token if required; errors cancel a token...
+  if (this->replyToken != NULL && this->errorType == "") {
+    output << "  <resumptionToken>" << endl;
+    output << "    " << this->replyToken->getToken() << endl;
+    output << "  </resumptionToken>" << endl;
+  }
+
+  return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+void abstractlistaction::output_content_for_col(ostream &output, recptproto *protocol, text_t &gsdlCollect,
+						ColInfoResponse_t &cinfo, comerror_t &err, oaiargs &params)
+{ int startDoc = 0;
+
+  // get the collection information
+  protocol->get_collectinfo(gsdlCollect, cinfo, err, *this->logout);
+
+  // check resumption token
+  if (params["resumptionToken"] != "") {
+    ResumptionToken token(params["resumptionToken"]);
+    if (token.getCollection() == gsdlCollect) {
+      startDoc = token.getPosition() - 1; // first document is said to be 1..
+    }
+  }
+  
+  // If numDocs is 0, do nothing - this->prevDocSeen will stay false if this is the only collection
+  // looked at, or will keep whatever value it had prior to this col (ensures that if the flag has
+  // been set to true by a previous collection that this won't overwrite it to be false). 
+  if (cinfo.numDocs > 0) {
+    int errorCount      = 0; // Count the number of errors found in the given collection
+    text_t from         = params["from"];
+    text_t until        = params["until"];
+
+    for (long i = startDoc; i < cinfo.numDocs; ++i) {
+      if (errorCount > 3) { // If num errors reaches the cut-off value, bail.
+	cerr << "Error: too many records(" << errorCount << ") in the " << gsdlCollect 
+	     << " collection have invalid or non-existant oai_ids - skipping remainder of collection.\n";
+	return;
+      }
+      
+      text_t oai_id = "oai.";
+      oai_id += i;
+      
+      text_t gsdl_id = oaiclassifier::getGSDL_OID(gsdlCollect, oai_id, protocol, *this->logout);
+      
+      if (gsdl_id == "") { // If the string is empty, then the document didn't have an oai_id, so
+	++errorCount;     // increase error count
+	continue;
+      }
+
+
+      // Check that the item with the 0ID 'gsdl_id' has a lastmodified field that fits within
+      // the required date range (if specified).
+      if (this->inDateRange(params["from"], params["until"], gsdlCollect, gsdl_id, protocol, output)) {
+	if (this->output_document(output, protocol, gsdlCollect, gsdl_id, params["metadataPrefix"])) {
+	  // this should be an IF statement, where prevDocSeen is only set to true if the above
+	  // function call returns true (indicating that the doc supported the metadata prefix) but
+	  // for some reason this is always false. This means that if no doc in the requested set supports
+	  // the metadata format, the "no records match" error that should be thrown won't be...
+	  //
+	  // GRB: the above comment is no longer true; proper checks are made
+	  this->prevDocSeen = true;
+	  ++this->outputDocs;
+	}
+      }
+
+      // if we've output the number of resumption documents; prepare a resumptionToken
+      if (this->outputDocs == this->configuration->resumeAfter()) {
+	this->replyToken = new ResumptionToken(gsdlCollect, "", "");
+	this->replyToken->setPosition("", i+2);
+	break;
+      }
+    }
+
+    cinfo.clear(); // Clear for next collection to use (if there is one).
+  }
+}
+
+//--------------------------------------------------------------------------------------------
+// Returns true if at least one document record is found
+void abstractlistaction::output_content_for_all(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  ColInfoResponse_t cinfo;
+  comerror_t        err;
+  text_tarray       collections;
+  text_t            gsdlCollect = "";
+  ResumptionToken   *token = NULL;
+
+  // get a list of the collections available
+  collections = this->configuration->getCollectionsList();
+  //  protocol->get_collection_list(collections, err, output); 
+  
+  if (params["resumptionToken"] != "") {
+    token = new ResumptionToken(params["resumptionToken"]);
+  }
+
+  for(int current_col = 0; current_col < collections.size(); ++current_col){
+    gsdlCollect = collections[current_col];
+
+    // ignore all leading collections before the one that matches the resumptiontoken
+    if (token != NULL &&
+	token->getCollection() != gsdlCollect)
+    { continue;
+    }
+
+    this->output_content_for_col(output, protocol, gsdlCollect, cinfo, err, params);
+
+    // once we've output at least one collection, continue 
+    // outputting all others until the resumption total hits
+    token = NULL; 
+
+    if (this->outputDocs == this->configuration->resumeAfter()) {
+      break;
+    }
+  }
+}
+
+//-------------------------------------------------------------------------------------------------
+// Check that the requested from/until dates don't include a time, as this would be asking for too
+// fine a level of granularity, one that greenstone doesn't support. An OAI error must be thrown.
+/*
+bool abstractlistaction::granularityTooFine(text_t &from, text_t &until)
+{
+  if (from != "" && from.){
+    
+  }
+  
+}
+*/
+//-------------------------------------------------------------------------------------------------
+
+bool abstractlistaction::check_classifier(recptproto *protocol, const text_t &collection,
+					  const text_t &classifier)
+{ text_t topClass;
+  FilterResponse_t response;
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+
+  // exclude false children of a top-level classifier immediately...
+  if (!get_info(classifier, collection, "", metadata, false, protocol, response, logout)) {
+    return false;
+  }
+  
+  // now check the top-level parent
+  metadata.insert("supportsmemberof");
+
+  text_t::const_iterator dot = findchar(classifier.begin(), classifier.end(), '.');
+  if (dot != classifier.end()) {
+    topClass = substr(classifier.begin(), dot);
+  }
+  else {
+    topClass = classifier;
+  }
+
+  if (!get_info(topClass, collection, "", metadata, false, protocol, response, logout)) {
+    return false;
+  }
+  
+  if (response.docInfo[0].metadata["supportsmemberof"].values.size() == 0) {
+    return false;
+  }
+
+  if (response.docInfo[0].metadata["supportsmemberof"].values[0] != "true") {
+    return false;
+  }
+
+  return true;
+}
+
+void abstractlistaction::recurse_set(ostream &output, recptproto *protocol, const text_t &collection, 
+				     const text_t &classifier, oaiargs &params, ResumptionToken *resumptionToken)
+{
+  // metadata for this call
+  FilterResponse_t response;
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+  text_t           from = params["from"];
+  text_t           until = params["until"];
+  text_t           metadataPrefix = params["metadataPrefix"];
+  //  ResumptionToken  resumptionToken(params["resumptionToken"]);
+  int              startPos = 0;
+
+  // This is a recursive function, and so just because the current set is empty doesn't mean we necessarily
+  // want to throw a 'noRecordsMatch' error; another set (parent/sibling/child) may have had documents. It
+  // is therefore not enough to check that the response object in the current iteration has no docs - we
+  // must also verify that NO OTHER set has had any documents. This is done with the 'prevDocSeen' flag.
+  // It is set to FALSE initially, but as soon as we see a set that isn't empty, it is set to TRUE. The 
+  // 'noRecordsMatch' error will only be thrown if, after all appropriate sets have been recursed into, 
+  // the 'prevDocSeen' flag is still FALSE. The function returns false if no docs were seen, allowing us to 
+  // throw the noRecordsMatch error.
+  
+  //  bool prevDocSeen = false;
+
+  get_children(classifier, collection, "", metadata, false, protocol, response, *this->logout);
+
+  if (params["resumptionToken"] != "") {
+    // if we're at a resumptionToken
+    if (classifier == resumptionToken->getNode()) {
+      startPos = resumptionToken->getPosition();
+    }
+    else {
+      text_t fullNode = resumptionToken->getNode();
+      text_t::iterator leafIter = fullNode.begin() + classifier.size();
+
+      // if the next character isn't a dot, blow up!
+      if (*leafIter != '.') {
+	// fatal error;
+	exit(1);
+      }
+      
+      // get the first '.' after the current classifier point;
+      text_t::iterator separator = findchar(leafIter + 1, fullNode.end(), '.');
+
+      // now, create a new subpath
+      text_t nextNode = substr(fullNode.begin(), separator);
+
+      // seek forward; TODO: improve performance of this 
+      for (int c = 0; c < response.numDocs; ++c) {
+	if (response.docInfo[c].OID == nextNode) {
+	  startPos = c;
+	  break;
+	}
+      }
+    }
+
+    // We need to subtract one from the startPos value to turn it into an index value
+    startPos--;
+  }
+  
+  for (int c = startPos; c < response.numDocs; ++c) {
+    text_t child = response.docInfo[c].OID;
+    
+    // distinguish classifiers and documents by checking whether OID 
+    // starts with CL or not
+    text_t childHead;
+    text_t::const_iterator start = child.begin();
+    text_t::const_iterator here  = child.begin();
+    here += 2;
+    childHead = substr(start, here);
+    
+    // documents we output now
+    if (childHead != "CL") {
+      // Check that the item with the 0ID 'gsdl_id' has a lastmodified field that fits
+      // within the required date range (if specified)
+      if (this->inDateRange(from, until, collection, child, protocol, output)) {
+	// TODO: check that the document can be disseminated in the required metadataPrefix
+
+	if (this->output_document(output, protocol, collection, child, metadataPrefix)) {
+	  this->prevDocSeen = true;
+	  ++this->outputDocs;
+	}
+      }
+    }
+    // children which are classifiers are recursed
+    else {
+      if (resumptionToken != NULL) {
+	int depth = countchar(classifier.begin(), classifier.end(), '.');
+	resumptionToken->setOffset(depth, c+2);
+      }
+      this->recurse_set(output, protocol, collection, child, params, resumptionToken);
+    }
+
+    if (this->outputDocs == this->configuration->resumeAfter()) {
+      this->replyToken = new ResumptionToken(collection, params["set"], "");
+      this->replyToken->setPosition(classifier, c+2);      
+      break;
+    }
+  }
+}
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/abstractlistaction.h	(revision 16561)
@@ -0,0 +1,26 @@
+#ifndef _ABSTRACTLISTACTION_H_
+#define _ABSTRACTLISTACTION_H_
+
+#include "oaiaction.h"
+#include "resumptiontoken.h"
+
+class abstractlistaction : public oaiaction
+{ 
+ public:
+  abstractlistaction(const text_t &name) : oaiaction(name) {this->prevDocSeen = false;}
+  virtual bool validateAction(recptproto *protocol, oaiargs &params, int &numArgs); // Check that this is a valid action
+  virtual bool output_document(ostream &output, recptproto *protocol, const text_t &collection, 
+			       const text_t &OID, const text_t &metadataPrefix) = 0;
+  virtual bool output_content(ostream &output, recptproto *protocol, oaiargs &params);
+  virtual void output_content_for_all(ostream &output, recptproto *protocol, oaiargs &params);
+  virtual void output_content_for_col(ostream &output, recptproto *protocol, text_t &gsdlCollect, 
+				      ColInfoResponse_t &cinfo, comerror_t &err, oaiargs &params);
+  virtual void recurse_set(ostream &output, recptproto *protocol, const text_t &collection, 
+			   const text_t &classifier, oaiargs &params, ResumptionToken *resumptionToken);
+ protected:
+  bool prevDocSeen;
+  int  outputDocs;
+  ResumptionToken *replyToken;
+  bool check_classifier(recptproto *protocol, const text_t &collection, const text_t &classifier);
+};
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.cpp	(revision 16561)
@@ -0,0 +1,67 @@
+
+#include "dublincore.h"
+
+const text_t dublin_core::formatName() {
+  return "oai_dc";
+}
+
+const text_t dublin_core::formatPrefix() {
+  return "dc";
+}
+
+bool dublin_core::output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+				const text_t &record_OID) 
+{
+  return metaformat::output_record(output, protocol, collection, record_OID);
+}
+
+void dublin_core::output_metadata_header(ostream &output)
+{ 
+  output << "    <metadata>\n";
+  
+  if (this->oaiConfigure->getOAIVersion() <= 110){
+    // output dublin core wrapper for OAI v1.1
+    output << "      <dc xmlns=\"http://purl.org/dc/elements/1.1/\"\n"
+	   << "          xmlns:xsi=\"http://www.w3c.org/2001/XMLSchema-instance\"\n"
+	   << "          xsi:schemaLocation=\"http://purl.org/dc/elements/1.1/\n"
+	   << "                              http://www.openarchives.org/OAI/1.1/dc.xsd\">\n";
+  }
+  else { 
+    output << "      <oai_dc:dc\n"
+	   << "          xmlns:oai_dc=\"http://www.openarchives.org/OAI/2.0/oai_dc/\"\n"
+	   << "          xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"
+	   << "          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+	   << "          xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/oai_dc/\n"
+	   << "                              http://www.openarchives.org/OAI/2.0/oai_dc.xsd\">\n";
+  }
+}
+
+void dublin_core::output_metadata_footer(ostream &output)
+{
+  if (this->oaiConfigure->getOAIVersion() <= 110) {
+    output << "      </dc>" << endl;
+  }
+  else {
+    output << "      </oai_dc:dc>" << endl;
+  }
+
+  output << "    </metadata>" << endl; 
+  output.flush();
+}
+
+bool dublin_core::output_formatdata(ostream &output)
+{
+  output << "    <metadataPrefix>oai_dc</metadataPrefix>" << endl;
+
+  if (this->oaiConfigure->getOAIVersion() <= 110) {
+    output << "    <schema>http://www.openarchives.org/OAI/1.1/dc.xsd</schema>" << endl
+	   << "    <metadataNamespace>http://purl.org/dc/elements/1.1/</metadataNamespace>" << endl;
+  }
+  else {
+    output << "    <schema>http://www.openarchives.org/OAI/2.0/oai_dc.xsd</schema>" << endl
+	   << "    <metadataNamespace>http://www.openarchives.org/OAI/2.0/oai_dc/</metadataNamespace>" 
+	   << endl;
+  }
+  return true;
+}
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/dublincore.h	(revision 16561)
@@ -0,0 +1,17 @@
+#include "metaformat.h"
+
+class dublin_core : public metaformat
+{
+ protected:
+  virtual void output_metadata_header(ostream &output);
+  virtual void output_metadata_footer(ostream &output);
+ public:
+  dublin_core() : metaformat(){};
+  virtual const text_t formatName();
+  virtual const text_t formatPrefix();
+  virtual bool output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+			     const text_t &record_OID);
+  virtual bool output_formatdata(ostream &output);
+};
+
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.cpp	(revision 16561)
@@ -0,0 +1,64 @@
+#include "identifyaction.h"
+
+bool identifyaction::validateAction(recptproto *protocol, oaiargs &params)
+{ 
+  int params_size = params.getSize();
+
+  // Remove any parameters that aren't valid for this action
+  text_tmap::const_iterator param_iterator = params.begin();
+  while (param_iterator != params.end())
+  {
+    if (param_iterator->first != "verb")
+    {
+      params.erase(param_iterator->first);
+    }
+
+    param_iterator++;
+  }
+  
+  //"Verb=Identify" should be the only parameter - if there are others, throw an error.
+  // Don't need to check that the param we have is "Verb=Identify", as it has to be to get to here.
+  if(params_size != 1){
+    this->errorType = "badArgument";
+    return false;
+  }
+  
+  return true;
+}
+
+bool identifyaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  // Get the repository name (some human-readable name for the site, or superset of collections)
+  text_t repositoryName = this->configuration->getCollectionConfig("", "repositoryName");
+  // Get admin's email address (i.e. the site maintainer)
+  text_t maintainer = this->configuration->getCollectionConfig("", "maintainer");
+  text_t version = (this->configuration->getOAIVersion() <= 110) ? (text_t)"1.1":(text_t)"2.0";
+
+  text_t baseURL = this->configuration->getCollectionConfig("", "baseURL");
+  
+  output << "  <repositoryName>"  << repositoryName << "</repositoryName>" << endl;
+  output << "  <baseURL>"         << baseURL     << "</baseURL>" << endl; // Tack on the app name
+  output << "  <protocolVersion>" << version        << "</protocolVersion>" << endl;
+  output << "  <adminEmail>"      << maintainer     << "</adminEmail>" << endl;
+
+  if(version == "2.0"){
+    // earliestDatestamp *should* be the YYYY-MM-DD format of the oldest lastmodified record in the
+    // repository, but we're just setting it to be the default oldest possible date - ugly, but judged
+    // not to be worth the effort of trolling through all the lastmodified dates (by others with more 
+    // say than me)
+    output << "  <earliestDatestamp>1970-01-01</earliestDatestamp>\n"
+	   << "  <deletedRecord>no</deletedRecord>\n"
+	   << "  <granularity>YYYY-MM-DD</granularity>\n";
+  }
+  // list all configuration information
+  text_tmap::iterator here = this->configuration->getInformation()->begin();
+  text_tmap::iterator end  = this->configuration->getInformation()->end();
+  while (here != end) {
+    output << "  <" << here->first << ">" << here->second;
+    output << "  </" << here->first << ">" << endl;
+    ++here;
+  }
+  
+  return true;
+}
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/identifyaction.h	(revision 16561)
@@ -0,0 +1,13 @@
+#include "oaiaction.h"
+
+#include "oaiconfig.h"
+
+class identifyaction : public oaiaction
+{
+ public:
+  identifyaction() : oaiaction("Identify") {}
+  virtual bool validateAction(recptproto *protocol, oaiargs &params);
+ protected:
+  bool output_content(ostream &output, recptproto *protocol, oaiargs &params);
+};
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.cpp	(revision 16561)
@@ -0,0 +1,72 @@
+#include "listidsaction.h"
+#include "recptprototools.h"
+#include "oaitools.h"
+
+//--------------------------------------------------------------------------------------------------
+
+bool listidsaction::validateAction(recptproto *protocol, oaiargs &params)
+{ 
+  int params_size = params.getSize();
+  int numArgs  = 1; // For "verb" arg
+
+  if (!abstractlistaction::validateAction(protocol, params, numArgs)) {
+    return false;
+  }
+
+  // In OAI v1.1 metadataPrefix is not allowed.
+  if (this->configuration->getOAIVersion() <= 110){
+    if(numArgs != params_size){
+      this->errorType = "badArgument";
+      return false;
+    }
+  }
+  else { // In OAI v2.0, it is compulsory
+    text_t metaFormat = params["metadataPrefix"];
+    if (metaFormat == ""){
+      this->errorType = "badArgument";
+      return false;
+    }
+    else { // If it isn't empty, increase the valid args count and compare this to the total num of args
+      if (++numArgs != params_size){
+	this->errorType = "badArgument";
+	return false;
+      }
+    }
+    // Check that the metadataPrefix is a format we support
+    if (this->formatNotSupported(metaFormat)){
+      this->errorType = "cannotDisseminateFormat";
+      return false;
+    }
+  }
+  this->errorType = "";
+  return true;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+bool listidsaction::output_document(ostream& output, recptproto *protocol, const text_t &collection, 
+				    const text_t &OID, const text_t &metadataPrefix /* ignored */)
+{
+  FilterResponse_t response;
+  ResultDocInfo_t  doc_info;
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+  text_t           lastModified;
+  int              oaiVersion = this->configuration->getOAIVersion();
+
+  get_info(OID, collection, "", metadata, false, protocol, response, logout);
+  doc_info = response.docInfo[0];
+  this->getLastModifiedDate(doc_info, lastModified);
+  
+  // output the record for this document
+  text_t oaiLabel = OID;
+  oaiclassifier::toOAI(collection, oaiLabel);
+
+  if(oaiVersion <= 110)
+    output << "  <identifier>" << oaiLabel << "</identifier>\n";
+  else
+    this->output_record_header(output, oaiLabel, lastModified, 
+			       doc_info.metadata["memberof"].values, oaiVersion);
+
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listidsaction.h	(revision 16561)
@@ -0,0 +1,12 @@
+#include "abstractlistaction.h"
+
+class listidsaction : public abstractlistaction
+{
+ public:
+  listidsaction() : abstractlistaction("ListIdentifiers") { };
+ protected:
+  bool validateAction(recptproto *protocol, oaiargs &params);
+  bool output_document(ostream &output, recptproto *protocol, const text_t &collection, const text_t &OID,
+		       const text_t &metadataPrefix = "");
+};
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.cpp	(revision 16561)
@@ -0,0 +1,56 @@
+#include "listrecsaction.h"
+
+#include "oaitools.h"
+#include "recordaction.h"
+
+bool listrecsaction::validateAction(recptproto *protocol, oaiargs &params)
+{ 
+  int params_size = params.getSize();
+
+  text_t meta    = params["metadataPrefix"];
+  int    numArgs = 1;                // 1st arg (Verb=ListRecords) must be present for us to be here
+
+  if (!abstractlistaction::validateAction(protocol, params, numArgs)) {
+    return false;
+  }
+
+  // metadataPrefix is required - if it's not there, throw an error. Note that the arg isn't 
+  // required (or allowed) if a resumptionToken is given, but as we don't handle resumption
+  // tokens this is sufficient. When we DO handle the tokens, this will have to change.
+  if (meta == "") { 
+    this->errorType = "badArgument";
+    return false;
+  }
+  else {
+    ++numArgs; // If it's there, increase arg count
+  }
+  
+  // The number of valid args should be exactly equal to the number of args actually there.
+  // If not, throw a 'badArgument' error.
+  if (numArgs != params_size) {
+    this->errorType = "badArgument";
+    return false;
+  }
+     
+  if (this->formatNotSupported(meta)) { // Check that the metadataPrefix is supported
+    this->errorType = "cannotDisseminateFormat";
+    this->record_action->setErrorType(this->errorType);
+    return false;
+  }
+
+  return true;
+}
+
+//-----------------------------------------------------------------------------------------------
+
+bool listrecsaction::output_document(ostream &output, recptproto *protocol, const text_t &collection, 
+		     const text_t &OID, const text_t &metadataPrefix)
+{
+  this->record_action->output_record(output, protocol, collection, OID, metadataPrefix);
+  return true;
+}
+  
+//-----------------------------------------------------------------------------------------------
+
+
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listrecsaction.h	(revision 16561)
@@ -0,0 +1,16 @@
+#include "abstractlistaction.h"
+
+#include "recordaction.h"
+
+class listrecsaction : public abstractlistaction
+{
+ public:
+  void set_recordMaker(recordaction *ptr) { this->record_action = ptr; }
+  listrecsaction() : abstractlistaction("ListRecords") { };
+  
+ protected:
+  recordaction *record_action;
+  virtual bool validateAction(recptproto *protocol, oaiargs &params);
+  bool output_document(ostream &output, recptproto *protocol, const text_t &collection, 
+		     const text_t &OID, const text_t &metadataPrefix);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.cpp	(revision 16561)
@@ -0,0 +1,228 @@
+#include "listsetsaction.h"
+
+#if defined(GSDL_USE_STL_H)
+#include <fstream.h>
+#else
+#include <fstream>
+#endif
+
+#include "recptprototools.h"
+#include "oaitools.h"
+
+bool listsetsaction::validateAction(recptproto *protocol, oaiargs &params)
+{
+  int params_size = params.getSize();
+
+  // Remove any parameters that aren't valid for this action
+  text_tmap::const_iterator param_iterator = params.begin();
+  while (param_iterator != params.end())
+  {
+    if (param_iterator->first != "verb" && param_iterator->first != "resumptionToken")
+    {
+      params.erase(param_iterator->first);
+    }
+
+    param_iterator++;
+  }
+
+  if (params_size != params.getSize()) {
+    this->errorType = "badArgument";
+    return false;
+  }
+
+  if (params["resumptionToken"] != "") {
+    ResumptionToken token(params["resumptionToken"]);
+    // TO DO: Resumption token validation checking (the token.isValid() function is useless for ListSets)
+    // if (!token.isValid()) {
+    //   this->errorType = "badResumptionToken";
+    //   return false;
+    // }
+  }
+
+  return true;
+}
+
+bool listsetsaction::output_content(ostream &output, recptproto *protocol, text_tset &collections, oaiargs &params)
+{
+  text_tset::iterator here = collections.begin();
+  text_tset::iterator end  = collections.end();
+  while (here != end) {
+    text_t collect = *here;
+    this->output_content(output, protocol, params);
+    ++here;
+  }
+  return true;
+}
+
+bool listsetsaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  // output the total list of classifier points
+
+  // variables required
+  text_t browseOID = "browse";
+  FilterResponse_t response;
+  comerror_t       err;
+  text_tarray &    collections = this->configuration->getCollectionsList();
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+  
+  // get a list of the collections available
+  //  protocol->get_collection_list(collections, err, output); 
+  if (collections.size() == 0) {
+    logout << "Found *no* OAI collections - check main.cfg for oaicollection items and read the OAI documentation.\n";
+  }
+
+  // check resumption token
+  int startSet = 0;
+  if (params["resumptionToken"] != "") {
+    ResumptionToken token(params["resumptionToken"]);
+    startSet = token.getPosition() - 1; // first document is said to be 1..
+  }
+  this->replyToken = NULL;
+
+  this->setNumber = 0;
+  this->setsOutput = 0;
+  for(int current_col = 0; current_col < collections.size(); ++current_col) {
+    // output the collection as a set, first, then its children
+    text_t gsdlCollect = collections[current_col];
+
+    if (this->setsOutput == this->configuration->resumeAfter())
+    {
+      this->replyToken = new ResumptionToken("", "", "");
+      this->replyToken->setPosition("", this->setNumber+1);
+      break;
+    }
+
+    if (this->setNumber >= startSet)
+    {
+      output << "  <set>" << endl;
+      output << "    <setSpec>" << gsdlCollect << "</setSpec>" << endl;;
+      output << "    <setName>" << gsdlCollect << "</setName>" << endl;
+      output << "  </set>" << endl;
+      this->setsOutput++;
+    }
+    setNumber++;
+
+    // get all the children of the (relevant) classifier data structures
+    get_children(browseOID, gsdlCollect, "", metadata, false, protocol, response, logout);
+    // and send them to the "recurse_content" list
+    for (int c = 0; c < response.numDocs; ++c) {
+      this->recurse_content(output, protocol, gsdlCollect, response.docInfo[c].OID, gsdlCollect, startSet);
+    }
+  }
+
+  // do a resumption token if required; errors cancel a token...
+  if (this->replyToken != NULL && this->errorType == "") {
+    output << "  <resumptionToken>" << endl;
+    output << "    " << this->replyToken->getToken() << endl;
+    output << "  </resumptionToken>" << endl;
+  }
+
+  return true;
+}
+
+void listsetsaction::recurse_content(ostream &output, recptproto *protocol, text_t &collection, 
+				     const text_t &classifier, text_t setHierarchy, int startSet)
+{
+  // metadata for this call
+  FilterResponse_t response;
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+
+  if (this->setsOutput == this->configuration->resumeAfter())
+  {
+    this->replyToken = new ResumptionToken("", "", "");
+    this->replyToken->setPosition("", this->setNumber+1);
+    return;
+  }
+
+  metadata.insert("contains");
+  metadata.insert("Title");
+  metadata.insert("supportsmemberof");
+
+  // get the document information
+  if (!get_info(classifier, collection, "", metadata, false, protocol, response, logout)) {
+    //cerr << "recurse content: Bad identifier or protocol " << classifier << endl;
+    return;
+  }
+
+  // check for top-level classifiers, check if the set name includes a '.'; if 
+  // not, it is a top-level classifier: check for memberof support.  Those without
+  // memberof support will not be supported on OAI
+  if (findchar(classifier.begin(), classifier.end(), '.') == classifier.end()) {
+    if (response.docInfo[0].metadata["supportsmemberof"].values.size() > 0) {
+      text_t memberOf = response.docInfo[0].metadata["supportsmemberof"].values[0];
+      if (memberOf != "true") {
+	return;
+      }
+    }
+    else {
+      return;
+    }
+  }
+
+  MetadataInfo_tmap::iterator here = response.docInfo[0].metadata.begin();
+  MetadataInfo_tmap::iterator end  = response.docInfo[0].metadata.end();
+  text_t title;
+
+  while (here != end)
+  {
+    // Each set should only have one title - hence we only output one title here
+    // (it is a set title, not a collection)
+    if (here->first == "Title" && here->second.values.size() > 0) {	
+      title = here->second.values[0];
+    }
+
+    ++here;
+  }
+
+  // output the xml for this set; use the classifier id for the name
+  // if the title is blank
+  // curSet holds the colon-separated sequence of parent sets of the current set
+  text_t curSet;
+  if (this->setNumber >= startSet)
+  {
+    output << "  <set>" << endl;
+    text_t oai_classifier = classifier;
+    oaiclassifier::toOAI(collection, oai_classifier);
+    output << "    <setSpec>" << oai_classifier << "</setSpec>" << endl;
+    output << "    <setName>";
+    if (!title.empty()) {
+      curSet = setHierarchy + ":" + title;
+    }
+    else {
+      curSet = classifier; // Pretty much never gets here (shouldn't, at least)
+    }
+    output << curSet;
+    output << "</setName>" << endl;
+    output << "  </set>" << endl;
+    this->setsOutput++;
+  }
+  this->setNumber++;
+
+  // get the children of this classifier and iterate them
+  get_children(classifier, collection, "", metadata, false, protocol, response, logout);
+  for (int c = 0; c < response.numDocs; ++c) {
+    text_t child = response.docInfo[c].OID;
+
+    if (child == classifier)
+      continue;
+
+    // check for non classifier items and exclude them
+    text_t childHead;
+    text_t::const_iterator start = child.begin();
+    text_t::const_iterator here  = child.begin();
+    here += 2;
+    childHead = substr(start, here);
+
+    if (childHead != "CL")
+      continue;
+
+    // Recurse for "proper" classifier children. Pass curSet, the colon-separated list of 
+    // parent sets. curSet is pass-by-value, so that as we step out of recursion we remember
+    // old set hierarchies.
+    this->recurse_content(output, protocol, collection, child, curSet, startSet);
+  }
+  
+  return;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/listsetsaction.h	(revision 16561)
@@ -0,0 +1,17 @@
+#include "oaiaction.h"
+#include "resumptiontoken.h"
+
+class listsetsaction : public oaiaction
+{
+ public:
+  listsetsaction() : oaiaction("ListSets") { };
+  virtual bool validateAction(recptproto *protocol, oaiargs &params);
+  bool output_content(ostream &output, recptproto *protocol, text_tset &collections, oaiargs &params);
+ protected:
+  int  setNumber;
+  int  setsOutput;
+  ResumptionToken *replyToken;
+  bool output_content(ostream &output, recptproto *protocol, oaiargs &params);
+  void recurse_content(ostream &output, recptproto *protocol, text_t &collection, const text_t &classifier, 
+		       text_t setHierarchy, int startSet);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.cpp	(revision 16561)
@@ -0,0 +1,171 @@
+#include "metaformat.h"
+#include "gsdlunicode.h"
+#include <fstream>
+
+#include "recptprototools.h"
+
+metaformat::metaformat()
+{
+}
+
+text_t metaformat::get_mapping(const text_t &collection, const text_t &collectionField)
+{
+  if (this->oaiConfigure == NULL) {
+    return "";
+  }
+
+  return this->oaiConfigure->getMapping(collection, collectionField, this->formatPrefix());
+}
+
+void metaformat::output_item(ostream &output, outconvertclass &outconvert, 
+			     bool &headerDone, const text_t &label, 
+			     const text_tarray &values)
+{ 
+
+  if (!headerDone && (values.size() > 0)) {
+    this->output_metadata_header(output);
+    headerDone = true;
+  }
+
+  for (int item = 0; item < values.size(); ++item) {
+    if (this->oaiConfigure->getOAIVersion() >= 200) { // TODO: GRB: This code may need to be subclassed by dc for 200 and later...
+      output << outconvert << "        <" << this->formatPrefix() << ":" << label << ">" << values[item] << "</" << this->formatPrefix() << ":" << label << ">\n";
+    }
+    else {
+      output << outconvert << "        <" << label << ">" << values[item] << "</" << label << ">\n";
+    }
+  }
+}
+
+bool metaformat::scan_metadata(ostream &output, const text_t &collection, ResultDocInfo_t &docInfo,
+			       bool doOutput) 
+{ 
+  bool headerDone = false;
+  MetadataInfo_tmap::iterator here = docInfo.metadata.begin();
+  MetadataInfo_tmap::iterator end = docInfo.metadata.end();
+
+  utf8outconvertclass utf8convert; // we want to output metadata in utf8
+
+  // metaItem is used initially to identify the rfc1807 (etc) metadata items. It is 
+  // then used to hold the name of the metadata item, such as "title" or "subject".
+  text_t metaItem;
+  text_t::const_iterator start, last; // Use two iterators to go through metaItem
+  
+  while (here != end) {
+    start = last = here->first.begin();
+
+    if (here->first.size() < this->formatPrefix().size() ||
+	here->first[this->formatPrefix().size()] != '.') {
+      metaItem == "";
+    }
+    else {
+      last += this->formatPrefix().size() + 1; // Move last so that it is one place beyond where the "." should be.
+      metaItem = substr(start, last);          // Gets the substring starting at start and going up to (but
+                                               // not including) last. This should be "dc." (for example)
+    }
+
+    if (metaItem == this->formatPrefix()) {
+      metaItem = substr(last, here->first.end()); // Get the rest of the metadata tag (it's name)
+      lc(metaItem); // Convert it to lowercase for putting in the xml tags
+
+      if (doOutput) {
+	this->output_item(output, utf8convert, headerDone, metaItem, here->second.values);
+      }
+      else {
+	if (here->second.values.size() > 0) {
+	  return true;
+	}
+      }
+    }  
+    else {
+      text_t mapTo = this->get_mapping(collection, here->first);
+      if (mapTo != "") {
+	// Do we actually want to do anything here? Doesn't getting here imply that this 
+	// particular metadata is stuff we don't want?
+	if (doOutput) {
+	  this->output_item(output, utf8convert, headerDone, mapTo, here->second.values);
+	}
+	else {
+	  if (here->second.values.size() > 0) {
+	    return true;
+	  }
+	}
+      }
+    }
+
+    ++here;
+  }
+
+  if (!doOutput) {
+    return false;
+  }
+  
+  if (headerDone) {
+    this->output_metadata_footer(output);
+  }
+  
+  return headerDone;
+}
+
+
+bool metaformat::is_available(const text_t &collection, ResultDocInfo_t &docInfo)
+{ 
+  ofstream o("dummy", ios::out);
+  return this->scan_metadata(o, collection, docInfo, false);
+}
+
+bool metaformat::output_metadata(ostream &output, const text_t &collection, ResultDocInfo_t &docInfo) 
+{ 
+  return this->scan_metadata(output, collection, docInfo, true);
+}
+
+bool metaformat::output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+			       const text_t &OID) 
+{
+  FilterResponse_t response;
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+
+  // get the document information
+  if (!get_info(OID, collection, "", metadata, false, protocol, response, logout)) {
+    // TODO: error, bad request
+    //   cerr << "Bad identifier or protocol " << OID << endl;
+    return false;
+  }
+
+  // check to see if it's a classifier
+  text_t childHead;
+  //  int oaiVersion = this->oaiConfigure->getOAIVersion();
+  text_t::const_iterator start = OID.begin();
+  text_t::const_iterator here  = OID.begin();
+  here += 2;
+  childHead = substr(start, here);
+
+  // if it isn't a document, kill it now
+  if (childHead == "CL") {
+    //    cerr << "Not a document" << endl;
+    return false;
+  }
+
+  // output record header
+  output << "<record>\n";
+
+  // output header part of oai response
+  output << "<header>" << endl;
+  output << "  <identifier>" << OID << "</identifier>" << endl;
+  // TODO: add modified date
+
+  output << "</header>" << endl;
+
+  // output metadata part of oai response
+  this->output_metadata(output, collection, response.docInfo[0]);
+
+  // output the description of the document
+  //  output << "<about>\n";
+  //  output << "</about>\n";
+
+  // close record
+  output << "</record>\n";
+    
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformat.h	(revision 16561)
@@ -0,0 +1,56 @@
+/**
+ *  A framework class for metadata formats.
+ *
+ *  Example child classes include oaimeta and rfc8507meta
+ */
+
+#ifndef _METAFORMAT_H_
+#define _METAFORMAT_H_
+#include <stdio.h>
+
+#include "text_t.h"
+#include "comtypes.h"
+#include "recptproto.h"
+
+#include "oaiconfig.h"
+
+class metaformat
+{  
+ protected:
+  virtual void output_metadata_header(ostream &output) = 0;
+  virtual void output_item(ostream &output, outconvertclass &outconvert, bool &headerDone, const text_t &label, 
+			   const text_tarray &values);
+  virtual void output_metadata_footer(ostream &output) = 0;
+  virtual bool scan_metadata(ostream &output, const text_t &collection,  ResultDocInfo_t &docInfo, 
+			     bool doOutput);
+  oaiconfig *    oaiConfigure;
+
+ public:
+  metaformat(); // Must be given the desired oai version when created 
+  virtual const text_t formatName() = 0;
+  virtual const text_t formatPrefix() = 0;
+  virtual bool output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+			     const text_t &record_OID);
+  virtual bool is_available(const text_t &collection, ResultDocInfo_t &docInfo);
+  virtual bool output_metadata(ostream &output, const text_t &collection,  ResultDocInfo_t &docInfo);
+  virtual bool output_formatdata(ostream &output) = 0;
+  void    set_configuration(oaiconfig *config) { this->oaiConfigure = config; }
+  text_t  get_mapping(const text_t &collection, const text_t &collectionField);
+
+};
+
+class metaformatptr
+{
+ public:
+  metaformat *ptr;
+
+  metaformatptr() { this->ptr = NULL; }
+  metaformat *get_class() { return this->ptr; }
+  void set_class(metaformat *ptr) { this->ptr = ptr; }
+  void set_configuration(oaiconfig *config) { this->ptr->set_configuration(config);}
+  void clear() { delete this->ptr; this->ptr = NULL; }
+};
+
+typedef	map<text_t, metaformatptr, lttext_t> metaformat_map;
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.cpp	(revision 16561)
@@ -0,0 +1,120 @@
+#include "metaformatsaction.h"
+#include "metaformat.h"
+#include "recptprototools.h"
+#include "oaitools.h"
+
+bool metaformatsaction::validateAction(recptproto *protocol, oaiargs &params)
+{ 
+  int params_size = params.getSize();
+
+  // Remove any parameters that aren't valid for this action
+  text_tmap::const_iterator param_iterator = params.begin();
+  while (param_iterator != params.end())
+  {
+    if (param_iterator->first != "verb" &&
+	param_iterator->first != "identifier")
+    {
+      params.erase(param_iterator->first);
+    }
+
+    param_iterator++;
+  }
+
+  int numArgs = 1; // the number of expected arguments
+  
+  // increase the number of expected arguments if the optional 'identifier' argument
+  // is given 
+  if (params["identifier"] != "") {
+    ++numArgs;
+  }
+
+  // If the total number of params isn't equal to the number of valid
+  // args (i.e. we have an arg but it isn't the identifier), throw an error  
+  if((params_size != numArgs)){
+    this->errorType = "badArgument";
+    return false;
+  }
+
+  // Variables to hold identifier in Greenstone format
+  text_t docID = params["identifier"]; 
+  text_t collection = "";
+
+  // The following two variables are used solely by the get_info() function call to test if
+  // the identifier supplied is an actual document. If it isn't, we need to throw an exception.
+  const text_tset metadata;
+
+  // Check for the existence, etc. of the record if an identifier is given.
+  if (docID != "") {
+    oaiclassifier::toGSDL(collection, docID);
+    
+    // Only throw an error if we're using v2.0. 
+    // v1.1 should simply return a response without a metadataFormat container. 1.1
+    // should still set the errorType so we know that the error was encountered
+
+    // the information retrieved here is retained for the output_content function below
+    if (!get_info(docID, collection, "", metadata, false, protocol, this->gsdlResponse, cerr)){
+      this->errorType = "idDoesNotExist";
+      if(this->configuration->getOAIVersion() >= 200) {
+	return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool metaformatsaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  // if an identifier parameter is being used, then respond to it - currently we reject anything other
+  // than dublin core (unqualified)
+  //
+  // TODO: only return the selected identifier (if given) and don't validate the entry; just
+  //       return nothing if an invalid or unknown metadata format is given
+  
+  // give the required xml response
+
+  text_t gsdlId = params["identifier"];
+  text_t gsdlCollect;
+
+  // convert record identifier into GSDL format from OAI
+  if (gsdlId != "") {
+    oaiclassifier::toGSDL(gsdlCollect, gsdlId);
+  }
+  
+  metaformat_map::iterator here = this->formats->begin();
+  metaformat_map::iterator end  = this->formats->end();
+
+  // v1.1 should check that the id - if supplied - didn't cause an error. If it did,
+  // don't output the metadataFormat tags. OAI v2.0 should already have bailed.
+  if(this->errorType != "idDoesNotExist"){
+    while (here != end) { // Loop through the repository's supported metadata formats
+      bool doOutput = true;
+      
+      // if we were given an identifier, then check if it supports the metadata format in question
+      if (gsdlId != "") {
+	doOutput = here->second.get_class()->is_available(gsdlCollect, this->gsdlResponse.docInfo[0]);
+      }
+
+      if (doOutput) {
+	output << "  <metadataFormat>" << endl;
+	here->second.get_class()->output_formatdata(output);
+	output << "  </metadataFormat>\n";
+      }
+      ++here;
+    }
+  }
+
+  return true;
+}
+
+void metaformatsaction::setConfiguration(oaiconfig *configuration)
+{
+  this->configuration = configuration;
+
+  metaformat_map::iterator here = this->formats->begin();
+  metaformat_map::iterator end  = this->formats->end();
+  while (here != end) {    
+    here->second.set_configuration((oaiconfig *) configuration);
+    ++here;
+  }
+}
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformatsaction.h	(revision 16561)
@@ -0,0 +1,15 @@
+#include "oaiaction.h"
+#include "metaformat.h"
+
+class metaformatsaction : public oaiaction
+{
+ public:
+  metaformatsaction() : oaiaction("ListMetadataFormats") { };
+  void set_formats(metaformat_map *formats) { this->formats = formats; }
+  virtual void setConfiguration(oaiconfig *configuration);
+  virtual bool validateAction(recptproto *protocol, oaiargs &params);
+ protected:
+  metaformat_map * formats;
+  FilterResponse_t gsdlResponse;
+  bool output_content(ostream &output, recptproto *protocol, oaiargs &params);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.cpp	(revision 16561)
@@ -0,0 +1,36 @@
+#include "metaformatters.h"
+
+#include "dublincore.h"
+
+metaformatters::metaformatters() {
+  this->addFormat(new dublin_core);
+}
+
+/**
+ * Destructor code for the metaformatters class
+ */
+metaformatters::~metaformatters() {
+  vector<metaformat *>::iterator here = this->formatters.begin();
+  vector<metaformat *>::iterator end = this->formatters.end();
+  
+  // destroy the formatters themselves
+  while (here != end)
+    {
+      delete here->second;
+      ++here;
+    }
+}
+
+bool metaformatters::output_record(ostream &output, recptproto *protocol, text_t & collection, text_t &record_OID, text_t &format) {
+  
+}
+
+metaformat *metaformatters::getFormat(text_t &name)
+{
+  return this->formatters[name];
+}
+
+void metaformatters::addFormat(metaformat *formatter)
+{
+  this->formatters.push_back(formatter->getName(), formatter);
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/metaformatters.h	(revision 16561)
@@ -0,0 +1,14 @@
+#include "metaformat.h"
+
+#include <map>
+
+class metaformatters {
+ public:
+  metaformatters();
+  ~metaformatters();
+  bool output_record(ostream &output, recptproto *protocol, text_t &collection, text_t &record_OID, text_t formatName);
+ protected:
+  map<text_t, metaformat *> formatters;
+  addFormat(metaformat *formatter);
+  metaformat *getFormat(text_t &formatName);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.cpp	(revision 16561)
@@ -0,0 +1,466 @@
+#include "oaiaction.h"
+#include "oaitools.h"
+#include "recptprototools.h"
+
+#include <string>
+#if defined(GSDL_USE_IOS_H)
+#  if defined(__WIN32__)
+#    include <strstrea.h> // vc4
+#  else
+#    include <strstream.h>
+#  endif
+#else
+#  include <sstream>
+#endif
+
+#include <time.h>
+
+oaiaction::oaiaction(const text_t &name)
+{
+  this->logout = new ofstream("oai.log", ios::app);
+  this->configuration = NULL;
+  this->name = name;
+}
+
+//----------------------------------------------------------------------------------------------
+
+// Over-ridden by child classes
+bool oaiaction::validateAction(recptproto *protocol, oaiargs &params)
+{ 
+  this->errorType = ""; 
+  return true;
+}
+
+//----------------------------------------------------------------------------------------------
+
+/**********
+ * Compare the supplied metadataPrefix to all those that
+ * are supported. If there is NO match, return true. If 
+ * it DOES match one, return false.
+ */
+bool oaiaction::formatNotSupported(text_t &metaFormat)
+{
+  if (metaFormat == "oai_dc") return false;
+  else if (metaFormat == "rfc1807") return false;
+
+  return true;
+}
+
+//----------------------------------------------------------------------------------------------
+
+/**********
+ * Function for outputting the appropriate error(s) with the (version 2.0) request.
+ * The error(s) MUST be included in the response, and take the form:
+ * <error code="errorType">Description of error</error>
+ */
+void oaiaction::output_error(ostream &output, text_t &errorType)
+{
+  text_t description = "";
+
+  if(errorType == "badArgument"){
+    description = "The request includes illegal arguments, is missing required arguments, repeats arguments or the value for an argument has an illegal syntax";
+  }
+  else if(errorType == "noRecordsMatch"){
+    description = "No record matches all the requested parameters";
+  }
+  else if(errorType == "cannotDisseminateFormat"){
+    description = "The metadata format specified is not supported by the item or by the repository";
+  }
+  else if(errorType == "idDoesNotExist"){
+    description = "The value of the identifier is unknown or illegal in this repository";
+  }
+  else if(errorType == "badVerb"){
+    description = "Value of the verb argument is illegal, missing, or repeated";
+  }
+  else if(errorType == "noMetadataFormats"){
+    description = "There are no metadata formats available for the item";
+  }
+  else if(errorType == "badResumptionToken"){
+    description = "The value of the resumptionToken argument is invalid or expired";
+  }
+  else if(errorType == "noSetHierarchy"){
+    description = "The repository does not support sets";
+  }
+
+  output << "  <error code=\"" << errorType << "\">" << description << "</error>\n";
+}
+
+//----------------------------------------------------------------------------------------------
+
+text_t oaiaction::getName()
+{
+  return this->name;
+}
+
+//----------------------------------------------------------------------------------------------
+
+/**********
+ * Used in version 2.0 to provide the <datestamp> tag for each document. The function is passed
+ * the 'last modified' date of the file in epoch time (time in seconds since 1970-01-01 on Unix 
+ * systems) and converts it to YYYY-MM-DD format. 
+ */
+text_t oaiaction::parseDatestamp(time_t &rawtime)
+{
+  text_t year, month, day, lastModified;
+  tm *ptm;
+  ptm = gmtime(&rawtime);
+  int raw_month = ptm->tm_mon + 1;
+  int raw_day = ptm->tm_mday;
+
+  year = (ptm->tm_year + 1900); // Takes off 1900 for year by default, so replace it to get YYYY format
+
+  // Need the month in MM format, so if month is 1..9, add a 0 to the front
+  if(raw_month < 10){    
+    month = "0";
+    month += raw_month;
+  }
+  else month = raw_month;
+
+  if(raw_day < 10){    
+    day  = "0";
+    day += raw_day;
+  }
+  else day = raw_day;
+  
+  lastModified = year + "-" + month + "-" + day;
+
+  return lastModified;
+}
+
+//----------------------------------------------------------------------------------------------
+/**********
+ * Used by both versions to get the date & time of the client's request. The <responseDate> tag is
+ * in the format YYYY-MM-DDTHH:MM:SSZ, where T is simply the letter 'T' and Z is the local offset from
+ * GMT (now UTC). Examples of Z are +12:00 (NZ) or -03:00 for version 1.1. In version 2.0, the time
+ * is expected to be in UTC, and Z should simply be the character 'Z'.
+ */
+void oaiaction::getResponseDate(text_t &date)
+{
+  time_t rawtime;
+  tm    *ptm;
+
+  time(&rawtime);         // Get the epoch time
+
+  ptm = gmtime(&rawtime); // Convert to UTC-time formatted tm object
+
+  text_t month, day, hour, minute, second;
+  int    raw_month  = ptm->tm_mon + 1;  // Note Jan = 0 ... Dec = 11, so add 1
+  int    raw_day    = ptm->tm_mday;
+  int    raw_hour   = ptm->tm_hour;
+  int    raw_minute = ptm->tm_min;
+  int    raw_second = ptm->tm_sec;
+
+  // Need the month in MM format, so if month is 1..9, add a 0 to the front
+  if(raw_month < 10){    
+    month = "0";
+  }
+  month += raw_month;
+  
+  // Same for days, hours, minutes and seconds
+  if(raw_day < 10){    
+    day = "0";
+  }
+  day += raw_day;
+
+  if(raw_hour < 10){    
+    hour = "0";
+  }
+  hour += raw_hour;
+
+  if(raw_minute < 10){    
+    minute = "0";
+  }
+  minute += raw_minute;
+  
+  if(raw_second < 10){    
+    second = "0";
+  }
+  second += raw_second;
+ 
+  // Want YYYY-MM-DDTHH:MM:SS+UTC, where UTC is num hours from UTC(GMT) to localtime
+  date += (ptm->tm_year + 1900); // Takes off 1900 for year, so replace it to get YYYY format
+  date += "-";
+  date += month;
+  date += "-";
+  date += day;
+  date += "T";
+  date += hour;
+  date += ":";
+  date += minute;
+  date += ":";
+  date += second;
+  // If we're using v1.1, then tack on local time offset, otherwise don't
+  if(this->configuration->getOAIVersion() == 110){
+    date += _LOCALTIME_; // Defined in oaiaction.h. States how many hours localtime is from 
+                         // UTC (GMT), e.g. "+8:00", "-5:00"
+  }
+  else
+    date += "Z";         // If v2.0, we put 'Z' on the end rather than the localtime offset
+}
+
+//----------------------------------------------------------------------------------------------
+/**********
+ * Does different request tags depending on the version of the OAI protocol running
+ */
+void oaiaction::getRequestURL(oaiargs &params, text_t &requestURL)
+{
+  // Iterators for moving through the list of parameters (keys) specified
+  text_tmap::const_iterator here;
+  text_tmap::const_iterator end;
+  int numArgs = params.getSize();
+
+  here = params.begin();
+  end  = params.end();
+  
+  text_t baseURL = this->configuration->getCollectionConfig("", "baseURL");
+
+  int version = this->configuration->getOAIVersion();
+  
+  switch(version){
+  case 110:
+    /* Takes the form:
+     * <requestURL>http://baseURL.com/oaimain?verb="someVerb"&amp;key=value</requestURL>
+     */
+    requestURL = "  <requestURL>" + baseURL;
+    
+    if(numArgs == 0) break; // If no args, all done - the error will be picked up later 
+    
+    // The following lines will give us the "label=value" syntax
+    requestURL += "?";
+    requestURL += here->first;
+    requestURL += "=";
+    requestURL += html_safe(here->second); // parse the argument to ensure it is URL-encoding compliant
+    ++here;
+    
+    while(here != end){
+      requestURL +="&amp;"; // Stick in the ampersand in URL encoding
+      requestURL += (here->first + "=" + html_safe(here->second));
+      ++here;
+    }
+    requestURL += "</requestURL>\n";
+    break;
+  
+  case 200:
+  default:
+    /* Takes the form:
+     * <request verb="someVerb" key="value" key="value">
+     *          http://baseURL.com/oaimain</request>
+     */
+    if(numArgs == 0) {
+      requestURL = "  <request>" + baseURL + "</request>\n";
+      break;
+    }
+    requestURL = "  <request " + here->first + "=\"" + html_safe(here->second) + "\"";
+    ++here;
+    while(here != end){
+      requestURL += (" " + here->first + "=\"" + html_safe(here->second) + "\"");      
+      ++here;
+    }
+    requestURL += ">\n           " + baseURL + "</request>\n";
+    break;
+  }
+}
+
+//----------------------------------------------------------------------------------------------
+/**********
+ * Send the (OAI version-dependent) response text to the output stream
+ */
+void oaiaction::getResponse(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  bool   error;
+  text_t date, requestURL;
+
+  // Write the response date & time into 'date'
+  this->getResponseDate(date);
+  int version = this->configuration->getOAIVersion();
+
+  // validate the action
+  error = !this->validateAction(protocol, params);
+
+  // raise an error for duplicated arguments and set the
+  // error type "manually" here...
+  if (params.hasDuplicateArg() && !error) {
+    this->errorType = "badArgument";
+    error = true;
+  }
+
+  // start with the required http header
+  if (version <= 110 && error){
+    output << "Status: 400 " << this->errorType << "\n";
+    output << "Content-Type: text/xml\n\n";
+    return;
+  }
+  
+  output << "Status: 200\n";
+  output << "Content-Type: text/xml\n\n";
+  
+  // output xml header parts
+  output << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+
+  if(version <= 110){
+    // output OAI v1.1 action header tag
+    output << "<" << this->name;
+    output << "\n       xmlns=\"http://www.openarchives.org/OAI/1.1/OAI_" << this->name << "\" ";
+    output << "\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ";
+    output << "\n       xsi:schemaLocation=\"http://www.openarchives.org/OAI/1.1/OAI_" << this->name;
+    output << "\n           http://www.openarchives.org/OAI/1.1/OAI_" << this->name << ".xsd\">\n";
+  }
+  else { 
+    // output OAI v2.0 action header tag
+    output << "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\"\n"
+	   << "         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+	   << "         xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/\n"
+	   << "             http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n";
+  }
+  // output current time for response
+  output << "  <responseDate>" << date << "</responseDate>\n";
+ 
+  // output request URL. This differs depending on the OAI protocol version currently running, so
+  // the entire field - including tags - must be put into the text_t variable by getRequestURL()
+  this->getRequestURL(params, requestURL);
+
+  output << requestURL ;
+
+  if (error == false) {
+    // a string stream to write the content of the action to; this is done so that we can
+    // avoid outputting the action tag if the action's body is unsuccessful, in which
+    // case the leading tag must be suppressed
+#if defined(GSDL_USE_IOS_H)
+    ostrstream outstream;
+#else
+    ostringstream outstream;
+#endif
+
+    // Version 2.0 needs an <Identify>, etc. tag after the OAI-PMH header, IF there is no error
+    //
+    // An action that outputs no content should raise an error state to suppress the 
+    // matching opening and close tags if it outputs no content in OAI 2.0
+    error = !this->output_content(outstream, protocol, params);
+
+    // output the leading tag if no error occurred
+    if (error == false) {
+      if (version >= 200) {
+	this->output_action_tag(output, true);
+      }
+    }
+
+    // now output the body of the action content
+#if defined(GSDL_USE_IOS_H)
+    outstream << ends;  // Ensure outstream is null-terminated correctly
+#endif
+    output << outstream.str();
+  }
+  else {
+    if (version >= 200) {
+      this->output_error(output, this->errorType);
+    }
+  }
+
+  // close out our response - both versions need this line, but v2.0 only needs it if there was no error
+  if((version == 110) || (version >= 200 && error == false)){
+    this->output_action_tag(output, false);
+  }
+  if(version >= 200){
+    output << "</OAI-PMH>\n";
+  }
+}
+
+void oaiaction::output_action_tag(ostream &output, bool openTag)
+{
+  output << " <";
+  if (!openTag) {
+    output << "/";
+  }
+  output << this->name << ">" << endl;
+}
+
+void oaiaction::output_record_header(ostream &output, const text_t &oaiLabel, const text_t &lastModified, 
+				     const text_tarray &memberOf, int oaiVersion)
+{
+    output << "    <header>" << endl;
+    output << "      <identifier>" << oaiLabel     << "</identifier>" << endl;
+    output << "      <datestamp>"  << lastModified << "</datestamp>" << endl;
+    
+    // copy the collection name off the front of oaiLabel
+    text_t::const_iterator colon = findchar(oaiLabel.begin(), oaiLabel.end(), ':');
+    text_t collection = substr(oaiLabel.begin(), colon);
+    
+    if(oaiVersion >= 200){
+      text_tarray::const_iterator member = memberOf.begin();
+      text_tarray::const_iterator memEnd = memberOf.end();
+
+      // As well as all the subsets that a doc appears in, it is also a member of the 'collection' set
+      output << "      <setSpec>" << collection << "</setSpec>" << endl;
+      while (member != memEnd) {
+	text_t oaiSet = *member;
+	oaiclassifier::toOAI(collection, oaiSet);
+	output << "      <setSpec>" << oaiSet << "</setSpec>" << endl;
+	++member;
+      }
+    }
+    output << "    </header>" << endl;
+}
+
+void oaiaction::getLastModifiedDate(ResultDocInfo_t &doc_info, text_t &lastModified)
+{
+  text_t temp;
+
+  
+  MetadataInfo_tmap::iterator current = doc_info.metadata.begin();
+  MetadataInfo_tmap::iterator end = doc_info.metadata.end();
+
+  while(current != end){
+    temp = current->first;
+    lc(temp);
+    if(temp == "dc.date"){
+      lastModified = current->second.values[0];
+      return;
+    }
+    else{ 
+      if (temp == "lastmodified" && lastModified == "" && current->second.values.size() >= 1) {
+	lastModified = current->second.values[0];
+	time_t raw_time = (time_t)lastModified.getint();
+	lastModified = this->parseDatestamp(raw_time);
+	return;
+      }
+    }
+    ++current;
+  }  
+}
+
+bool oaiaction::inDateRange(const text_t &from, const text_t &until, const text_t &collection, 
+			    const text_t &OID, recptproto *protocol, ostream &logout)
+{
+  FilterResponse_t response;
+  text_tset        metadata;
+  bool status_ok = get_info(OID, collection, "", metadata, false, protocol, response, logout);
+  bool not_too_early = false, not_too_recent = false;
+
+  if(status_ok) {
+    ResultDocInfo_t doc_info = response.docInfo[0];
+    text_t lastModDate;
+    this->getLastModifiedDate(doc_info, lastModDate);
+    
+    // All date fields should be in the form YYYY-MM-DD, which allows for a direct comparison
+    if(from != ""){
+      if(from <= lastModDate)
+	not_too_early = true;
+    }
+    else 
+      not_too_early = true; // If there's no FROM field, then the record can't be too early
+
+    if(until != ""){
+      if(lastModDate <= until)
+	not_too_recent = true;
+    }
+    else 
+      not_too_recent = true; // If there's no UNTIL field, then the record can't be too recent    
+    
+    if(not_too_early && not_too_recent)
+      return true;
+    else
+      return false;
+  }
+  else
+    return false;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiaction.h	(revision 16561)
@@ -0,0 +1,64 @@
+#ifndef _oaiaction_h_
+#define _oaiaction_h_
+// standard c++ includes
+//#include <ostream.h>
+
+// generic gsdl stuff
+#include "text_t.h"
+#include "htmlutils.h" // For converting characters in a string to URL encoding. 
+
+// connection to a receptionist
+#include "receptionist.h"
+
+// oaistuff
+#include "oaiargs.h"
+#include "oaiconfig.h"
+#include <time.h>
+
+// Define hours & mins that separate local time from UTC (GMT) time; +ve is ahead of UTC, -ve behind
+#define _LOCALTIME_ "+12:00"
+
+class oaiaction
+{
+ public:
+  oaiaction(const text_t &name);
+
+  virtual void setConfiguration(oaiconfig *config) { this->configuration = config; }
+  text_t getName();
+  text_t parseDatestamp(time_t &rawtime);// Convert date from raw time_t format to the string YYYY-MM-DD
+  void   getResponseDate(text_t &date);  // Get the response date in UTC (GMT) time, plus local offset
+  void   getRequestURL(oaiargs &params, text_t &requestURL); // Get the URL of the request
+  void   getResponse(ostream &output, recptproto *protocol, oaiargs &params);
+  void   output_action_tag(ostream &output, bool openTag);
+  void   output_record_header(ostream &output, const text_t &oaiLabel, const text_t &lastModified, 
+			      const text_tarray &memberOf, int oaiVersion);
+  void   output_error(ostream &output, text_t &errorType); // Output any error conditions
+  bool   formatNotSupported(text_t &metaFormat);           // Return true if the supplied metadataPrefix
+                                                           // format is not supported
+  void   setErrorType(text_t &err){this->errorType = err;};
+  text_t getErrorType(){return this->errorType;};
+  void   getLastModifiedDate(ResultDocInfo_t &doc_info, text_t &lastModified);
+  bool   inDateRange(const text_t &from, const text_t &until, const text_t &collection, 
+		     const text_t &OID, recptproto *protocol, ostream &logout);
+  /******
+   * The following function sets the isInnerObject flag. It is used by the nested recordaction
+   * object in the listrecs object. This flag provides a means for identifying whether the 
+   * recordaction object is acting on its own or through a listrecsaction object. This is 
+   * to allow for appropriate handling of errors regarding unsupported metadata formats. If 
+   * we are using verb=GetRecord and the supplied metadata format is not supported by this 
+   * particular item, then an error must be thrown. But if were using verb=ListRecords, we only
+   * want to exclude the item in question from the list, not throw an error.
+   */
+  void       setAsInnerObject() { this->isInnerObject = true;}
+
+ protected:
+  oaiconfig *configuration;
+  virtual    bool validateAction(recptproto *protocol, oaiargs &params); // Check that this is a valid action
+  virtual    bool output_content(ostream &output, recptproto *protocol, oaiargs &params) = 0;
+  bool       isInnerObject;
+  text_t     name;
+  text_t     errorType;
+  ostream  * logout;
+};
+#endif
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.cpp	(revision 16561)
@@ -0,0 +1,100 @@
+#include "oaiargs.h"
+#include "cgiutils.h"
+
+// read the arguments from a stream for an OAI action
+void oaiargs::readArgs(istream &in)
+{
+  unsigned char  buffer[256];
+  unsigned char *end = buffer + 256;
+  unsigned char *here = end;
+  unsigned char *start, *equals;
+
+  // if no parameters, return now.
+  if (in.eof()) {
+    cerr << "Aborting on EOF" << endl;
+    return;
+  }
+
+  // fill the buffer with the stream
+  this->fillBuffer(in, buffer, &here, end);
+
+  // start at the beginning of the buffer, read
+  // the stream until exhausted
+  here = buffer;
+  while (*here != '\0')
+  {
+    // each iteration of the outer loop reads a single argument
+
+    // we start an argument and continue until the buffer is
+    // exhausted or the next argument is found
+    start = here;
+    equals = NULL;
+    while (*here != '&' && *here != '\0') {
+      // remember where the equals sign was if necessary
+      if (equals == NULL && *here == '=') {
+	equals = here;
+      }
+
+      // move to the next character
+      ++here;
+
+      // and refill the buffer as necessary
+      if (here == end) {
+	this->fillBuffer(in, buffer, &here, end);
+      }
+    }
+    
+    // we've now got the scope of one argument, where it begins, ends
+    // and where the equals sign is; we now split it up accordingly
+    // to create the actual text_t type elements for the label and the
+    // value.
+    text_t label, value;
+    if (equals == NULL) {
+      label.setcarr((char *) start, (int) here - (int) start);
+      value = label;
+    }
+    else {
+      label.setcarr((char *) start, (int) equals - (int) start);
+      ++equals;
+      value.setcarr((char *) equals, (int) here - (int) equals);
+    }
+
+    decode_cgi_arg(value);
+    
+    // output to the screen for tracing purposes
+    // cerr << label << "?" << value << endl;
+
+    // if the argument already exists, raise an error
+    if (this->arguments.count(label) > 0) {
+      this->duplicateArg = true;
+    }
+    else {
+      // add this argument to the argument map
+      this->arguments[label] = value;
+    }
+
+    // don't choke on an argument separator; swallow it so we don't get
+    // inifinite loops above
+    if (*here == '&') {
+      ++here;
+    }
+  }
+}
+
+void oaiargs::fillBuffer(istream &in, unsigned char *buffer, unsigned char **currentPtr, unsigned char *end)
+{
+  unsigned char *fill = buffer;
+
+  // copy out the remaining buffer space
+  while (*currentPtr != end) {
+    *fill = **currentPtr;
+    *currentPtr++;
+  }
+
+  // set the buffer appropriately
+  *currentPtr = fill;
+
+  // refill the buffer
+  in.get((char *)*currentPtr, (char *)end - (char *)*currentPtr);
+}
+
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiargs.h	(revision 16561)
@@ -0,0 +1,32 @@
+#include <map>
+
+#include "text_t.h"
+
+class oaiargs
+{
+ private:
+  text_tmap arguments;
+  text_t    dummy;
+  bool      duplicateArg;
+  // fill the input buffer to parse the arguments
+  void fillBuffer(istream &stream, unsigned char *buffer, unsigned char **currentPtr, unsigned char *end);
+ public:
+  oaiargs() { this->duplicateArg = false; }
+
+  const text_t &operator [](const text_t &label) { 
+    if (this->arguments.count(label) > 0) {
+      return this->arguments[label]; 
+    }
+    return dummy; // Dodgy, but text_t's are initialised to "", so should be fine
+  };
+
+  void erase(const text_t &label) {
+    this->arguments.erase(label);
+  }
+
+  void readArgs(istream &stream);
+  bool hasDuplicateArg() { return this->duplicateArg; }
+  int getSize(){return arguments.size();}
+  text_tmap::const_iterator begin(){return arguments.begin();};
+  text_tmap::const_iterator end(){return arguments.end();};
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.cpp	(revision 16561)
@@ -0,0 +1,336 @@
+#include "oaiconfig.h"
+
+#include <algorithm>
+#include <iostream>
+
+#include "fileutil.h"
+
+/**
+ *  The mapping works as follows in the collect.cfg file.
+ *
+ *  A line is in the format oaimapping <collection field> <oai field>
+ *
+ *  The map here is used to look up the "Greenstone" name which is mapped from
+ *  a given OAI field name, the reverse direction to that given in the 
+ *  Greenstone collect.cfg file.  The oairecordaction class instance which
+ *  produces output for an OAI record information request thus uses the map
+ *  to work from the field in the collection it has on hand which OAI
+ *  record name it should use instead.
+ *
+ *  An extension is to be used for this in which the OAI field name in the
+ *  collect.cfg file can be made specific for a particular record format.
+ *  This is done using the OAI field name in the format of 
+ *    <OAI format>:<OAI field name>
+ *  Thus, an rfc1807 Title field would be referred to as rfc1807:Title
+ *
+ *  A collection-level mapping is not needed - to configure the behaviour
+ *  of a collection, ensure that you place the appropriate configuration in
+ *  its etc/collect.cfg file - the oaimapping stuff IS NOT picked up from 
+ *  the central main.cfg file at the moment.
+ *
+ *  In the absence of a particular format name, the mapping is taken to be
+ *  universal.
+ */
+
+oaiconfig::oaiconfig() : configurable () {
+  this->resumptionSize = -1; // Default = do not use resumption tokens
+}
+
+oaiconfig::oaiconfig(text_t &gsdlhome, text_t &gsdlcollect) 
+{
+  // read main configuration file to get oai collections
+  text_t mainconfig = filename_cat(gsdlhome, "etc", "oai.cfg");
+  this->collection = "";
+  this->resumptionSize = -1;
+  this->read_configfile(mainconfig);
+
+  // then if we've not got a specified collection in the gsdlcollect
+  // parameter, read in all the collection's individual configurations
+  if (gsdlcollect == "") {
+    text_tarray::iterator here = this->collectList.begin();
+    text_tarray::iterator end  = this->collectList.end();
+    while (here != end) {		
+      this->configureCollection(gsdlhome, *here);
+      ++here;
+    }
+  }
+  else {
+    this->configureCollection(gsdlhome, gsdlcollect);
+  }
+}
+
+oaiconfig::~oaiconfig() 
+{
+  oaicollectmap::iterator here = this->collectMap.begin();
+  oaicollectmap::iterator end  = this->collectMap.end();
+  while (here != end) {
+    delete here->second;
+    ++here;
+  }	
+}
+
+int oaiconfig::resumeAfter()
+{ return this->resumptionSize;
+}
+
+int oaiconfig::getOAIVersion()
+{ 
+  if (this->oaiVersion == "1.1") {
+    return 110;
+  }
+  return 200; 
+}
+
+void oaiconfig::configureCollection(const text_t &gsdlhome, const text_t &gsdlcollect)
+{
+  text_t cnfgfile = filename_cat(gsdlhome, "collect", gsdlcollect, "etc", "collect.cfg");
+  this->collection = gsdlcollect;
+  this->read_configfile(cnfgfile);
+}
+
+void oaiconfig::configure (const text_t &key, const text_tarray &cfgline)
+{
+  // we've got an oai mapping item, and at least two fields
+  if (key == "oaimapping" && cfgline.size() > 1) {
+    text_t::const_iterator colonAt;
+    text_t index, name, configCollection;
+
+    // Take a default collection as being whatever the collection being configured is...
+    configCollection = this->collection;
+    
+    // get the name of the (collection) field to map; this may actually
+    // be in a colon separated format of the type 
+    // <collection name>:<field name>
+    index = cfgline[0];
+    if ((colonAt = find(index.begin(), index.end(), ':')) != index.end()) {
+      configCollection = substr(index.begin(), colonAt);
+
+      if (this->collection != "" && configCollection != this->collection) {
+	cerr << "Attempt to configure OAI mappings for " << configCollection << " in " << this->collection << endl;
+      }
+
+      colonAt += 1;
+      index = substr(colonAt, index.end());
+    }
+    
+    // the second parameter is the metadata field to map the collection
+    // field onto.  It may be provided with a metadata protocol (which
+    // will be given first and separated by a period or full stop).  In 
+    // the case of format.field name, the splitting is done here.
+    if ((colonAt = find(cfgline[1].begin(), cfgline[1].end(), '.')) != cfgline[1].end()) {
+      text_t stub = substr(cfgline[1].begin(), colonAt);
+      colonAt += 1;
+      name = substr(colonAt, cfgline[1].end());
+      index.append(":");
+      index.append(stub);
+    }
+    else {
+      name = cfgline[1];
+    }
+    
+    // now 'index' is in the form <collectionfield>:(formatname) 
+    //     'name' is simply the fieldname within the format
+    //     'configCollection' is the collection to be configured
+    
+    // now simply map the field name (index) onto the collection name (name)
+    if (this->collectMap[configCollection] == NULL) {
+      this->collectMap[configCollection] = new oaicollectconfig(configCollection);
+    }
+    this->collectMap[configCollection]->fieldMap[index] = name;
+    
+    //    cerr << "Mapping " << index << " to " << name << " in " << configCollection << endl;
+    
+    // TODO: check that the mapped field is actually in use
+  }
+  else if (key == "oaicollection" && cfgline.size() >= 1) {
+    // Configure a collection to be used as part of the OAI archive.
+    // This line should read:
+    //
+    // oaicollection <collectionname>
+    //
+    // Where <collectionname> is the name of the directory inside the
+    // gsdl/collect folder which contains the collection.
+    //
+    // To configure several collections, merely repeat this line,
+    // or alternatively use additional collection names after the
+    // first one.
+    //
+    // This configuration should only appear in main.cfg
+    //
+    if (this->collection != "") {
+      cerr << "Attempt to configure an oai collection outside of main.cfg" << endl;
+      cerr << "Configuration attempted in " << this->collection << " collection." << endl;
+      exit(1);
+    }
+    for (int c = 0; c < cfgline.size(); ++c) {
+      this->collectList.push_back(cfgline[c]);
+    }
+  }
+  else if (key == "oaiinfo" && cfgline.size() >= 1) {
+    // Get a piece of information for the oai repository information
+    // request.  The line should read:
+    //
+    // oaiinfo <information field name> <value>
+    //
+    // This configuration should only be attempted in main.cfg
+    //
+    if (this->collection != "") {
+      cerr << "Attempt to set oai information outside of main.cfg" << endl;
+      cerr << "Configuration attempted in " << this->collection << " collection." << endl;
+      exit(1);
+    }
+    
+    // if no second parameter is given, then the first parameter
+    if (cfgline.size() == 1) {
+      this->infoMap[cfgline[0]] = cfgline[0];
+    }
+    else {
+      this->infoMap[cfgline[0]] = cfgline[1];
+    }
+  }
+  else if (key == "oaiversion" && cfgline.size() >= 1) {
+    this->oaiVersion = cfgline[0];
+  }
+  else if (key == "resumeafter" && cfgline.size() >= 1) {
+    this->resumptionSize = cfgline[0].getint();
+  }
+  // get and note a maintainer item to support the Identify Verb of OAI
+  else if (key == "maintainer" && cfgline.size() >= 1) {
+    int line = 0;
+    
+    // TODO: exhaustive checks for empty or default values of maintainer
+    while (line < cfgline.size()) {
+      if (cfgline[line] != "NULL" &&
+	  cfgline[line] != "") {
+	// do something
+	break;
+      }
+      else {
+	++line;
+      }
+    }
+    
+    // Only try to set the configuration if we have a legitimate value ...
+    if (line < cfgline.size()) {
+      // ensure we have a map to write to
+      if (this->collectMap[this->collection] == NULL) {
+	this->collectMap[this->collection] = new oaicollectconfig(this->collection);
+      }
+      this->collectMap[this->collection]->maintainer = cfgline[line];
+    }
+  }
+  else if (key == "repositoryName" && cfgline.size() >= 1) {
+    int line = 0;
+    
+    // TODO: exhaustive checks for empty or default values of repositoryName
+    while (line < cfgline.size()) {
+      if (cfgline[line] != "NULL" &&
+	  cfgline[line] != "") {
+	// do something
+	break;
+      }
+      else {
+	++line;
+      }
+    }
+    
+    // Only try to set the configuration if we have a legitimate value ...
+    if (line < cfgline.size()) {
+      // ensure we have a map to write to
+      if (this->collectMap[this->collection] == NULL) {
+	this->collectMap[this->collection] = new oaicollectconfig(this->collection);
+      }
+      this->collectMap[this->collection]->repositoryName = cfgline[line];
+    }
+  }
+  else if (key == "baseURL" && cfgline.size() >= 1) {
+    int line = 0;
+    
+    while (line < cfgline.size()) {
+      if (cfgline[line] != "NULL" &&
+	  cfgline[line] != "") {
+	// do something
+	break;
+      }
+      else {
+	++line;
+      }
+    }
+    
+    // Only try to set the configuration if we have a legitimate value ...
+    if (line < cfgline.size()) {
+      // ensure we have a map to write to
+      if (this->collectMap[this->collection] == NULL) {
+	this->collectMap[this->collection] = new oaicollectconfig(this->collection);
+      }
+      this->collectMap[this->collection]->baseURL = cfgline[line];
+    }
+  }
+}
+
+/** 
+ *  TODO: store all field values in a map per collection
+ */
+text_t oaiconfig::getCollectionConfig(const text_t &collection, const text_t &field)
+{
+  if (this->collectMap[collection] == NULL) {
+    return "";
+  }
+  if (field == "maintainer") {
+    return this->collectMap[collection]->maintainer;
+  }
+
+  if (field == "repositoryName") {
+    return this->collectMap[collection]->repositoryName;
+  }
+
+  if (field == "baseURL") {
+    return this->collectMap[collection]->baseURL;
+  }
+
+  return "";
+}
+
+text_t oaiconfig::getMapping(const text_t &collection, const text_t &collectfield)
+{
+  if (this->collectMap[collection] == NULL) {
+    return "";
+  }
+  return this->collectMap[collection]->fieldMap[collectfield];
+}
+
+/**
+ *  Get the mapping for a field in a given collection; if no mapping
+ *  exists, the result will be a blank string.
+ */
+text_t oaiconfig::getMapping(const text_t &collection, const text_t &collectfield, const text_t &formatname)
+{
+  text_t fullName = collectfield;
+  fullName.append(":");
+  fullName.append(formatname);
+
+  // try the collection-specific options first
+  if (this->collectMap[collection] != NULL) {
+    // first try the most specific item - this collection, and given that protocol
+    if (this->collectMap[collection]->fieldMap.count(fullName) >= 1) {
+      return this->collectMap[collection]->fieldMap[fullName];
+    }
+    // otherwise, fall back to this collection, and all protocols
+    else if (this->collectMap[collection]->fieldMap.count(collectfield) >= 1) {
+      return this->collectMap[collection]->fieldMap[collectfield];
+    }
+  }
+
+  // if no mappings exist, return an empty item
+  if (this->collectMap[""] == NULL) {
+    return "";
+  }
+
+  // then try generic rules
+  if (this->collectMap[""]->fieldMap.count(fullName) >= 1) {
+    return this->collectMap[""]->fieldMap[fullName];
+  }
+  else {
+    return this->collectMap[""]->fieldMap[collectfield];
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaiconfig.h	(revision 16561)
@@ -0,0 +1,48 @@
+#ifndef _OAICONFIG_H_
+#define _OAICONFIG_H_
+
+#include "cnfgable.h"
+
+#include <map>
+
+// TODO: sort out the members of oaicollectconfig as private/public and add
+//       corresponding set/get functions
+
+class oaicollectconfig {
+ public:
+  oaicollectconfig(const text_t &collectname) { this->collection = collectname; }
+  text_t    collection;
+  text_t    maintainer;
+  text_t    repositoryName;
+  text_t    baseURL;
+  text_tmap fieldMap;    // maps from OAI name to GSDL name
+};
+
+typedef map<text_t, oaicollectconfig *, lttext_t> oaicollectmap;
+
+class oaiconfig : configurable {
+ public:
+  oaiconfig();
+  oaiconfig(text_t &gsdlhome, text_t &gsdlcollect);
+  ~oaiconfig();
+  virtual void  configure(const text_t &key, const text_tarray &cfgline);
+  text_t        getCollectionConfig(const text_t &collection, const text_t &field);
+  text_tarray & getCollectionsList() { return this->collectList; }
+  text_t        getMapping(const text_t &collection, const text_t &collectfield);
+  text_t        getMapping(const text_t &collection, const text_t &collectfield, const text_t &format);
+  text_tmap   * getInformation() { return &this->infoMap; }
+  int           getOAIVersion();
+  int           resumeAfter();
+ private:
+  void configureCollection(const text_t &gsdlhome, const text_t &collection);
+  
+  text_tarray collectList;  // The list of collections to be taken in hand
+  text_tmap infoMap;        // Holds the information to be given in the case
+                            // of an OAI identify request
+  oaicollectmap collectMap; // The configuration of collections
+  text_t collection;        // Used to track which collection is being configured
+  text_t oaiVersion;        // The version of OAI running
+  int    resumptionSize;    // The number of items to produce before spitting out a
+                            // resumptionToken
+};
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.cpp	(revision 16561)
@@ -0,0 +1,80 @@
+#include "oaidispatcher.h"
+
+oaidispatcher::oaidispatcher()
+{ this->configuration = NULL;
+}
+
+oaidispatcher::~oaidispatcher()
+{
+  oaiactionmap::iterator here = this->actions.begin();
+  oaiactionmap::iterator end  = this->actions.end();
+
+  while (here != end) {
+    delete here->second;
+    ++here;
+  }
+}
+
+void oaidispatcher::addAction(oaiaction *thisAction) {
+  thisAction->setConfiguration(this->configuration);
+  this->actions[thisAction->getName()] = thisAction;
+}
+
+void oaidispatcher::doAction(ostream &output, recptproto *protocol, oaiargs &args) {
+
+  text_t verb = args["Verb"];
+  if(verb == "") verb = args["verb"];
+  
+  oaiaction *action = actions[verb];
+  
+  if (action != NULL) {
+    action->getResponse(output, protocol, args);
+  }
+  else {  
+    // If the verb isn't correct, send an error.
+    action = actions["Identify"];
+    text_t responseDate, requestURL;
+    int version = this->configuration->getOAIVersion();
+    action->getResponseDate(responseDate);
+    oaiargs emptySet;
+    action->getRequestURL(emptySet, requestURL);
+
+    // Bad verb is signified by a status 400 response for OAI 1.1
+    if (version <= 110) {
+      output << "Status: 400\n";
+    }
+    else {
+      output << "Status: 200\n";
+    }
+    output << "Content-type: text/xml" << endl << endl;
+    output << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+    
+    if(version <= 110){
+      // output OAI v1.1 action header tag - nil, as there is no valid heeader for the action
+      /*
+	output << "<" << action->name;
+	output << "\n       xmlns=\"http://www.openarchives.org/OAI/1.1/OAI_" << action->name << "\" ";
+	output << "\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ";
+	output << "\n       xsi:schemaLocation=\"http://www.openarchives.org/OAI/1.1/OAI_" << action->name;
+	output << "\n           http://www.openarchives.org/OAI/1.1/OAI_" << action->name << ".xsd\">\n";
+      */    
+    }
+    else { 
+      // output OAI v2.0 action header tag
+      output << "<OAI-PMH xmlns=\"http://www.openarchives.org/OAI/2.0/\"\n"
+	     << "         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+	     << "         xsi:schemaLocation=\"http://www.openarchives.org/OAI/2.0/\n"
+	     << "             http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd\">\n"
+	     << "  <responseDate>" << responseDate << "</responseDate>\n"
+	     << requestURL 
+	     << "  <error code=\"badVerb\">Illegal OAI verb</error>\n"
+	     << "</OAI-PMH>\n";
+    }
+
+    /*
+      cout << "<title>Bad OAI request</title>" << endl;
+      cout << "<h1>Bad OAI request</h1>" << endl;
+    */
+  }
+  output.flush();
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaidispatcher.h	(revision 16561)
@@ -0,0 +1,24 @@
+#include "oaiaction.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+typedef map<text_t, oaiaction *, lttext_t> oaiactionmap;
+
+class oaidispatcher
+{
+ private:
+  oaiactionmap actions;
+  oaiconfig   *configuration;
+ public:
+  oaidispatcher();
+  ~oaidispatcher();
+  void setConfiguration(oaiconfig *configuration) { this->configuration = configuration; }
+  void addAction(oaiaction *thisAction);
+  void doAction(ostream &stream, recptproto *protocol, oaiargs &args);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.cpp	(revision 16561)
@@ -0,0 +1,165 @@
+#include "oaimain.h"
+
+/**********************************************************************
+ *
+ * oaimain.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.
+ *
+ *********************************************************************/
+
+#include "gsdlconf.h"
+
+#if defined(GSDL_USE_IOS_H)
+#  include <fstream.h>
+#  if defined(__WIN32__)
+#    include <strstrea.h> // vc4
+#  else
+#    include <strstream.h>
+#  endif
+#else
+#  include <fstream>
+#  include <sstream>
+#endif
+
+#include "fileutil.h"
+#include "nullproto.h"
+#include "collectset.h"
+#include "cgiargs.h"
+
+#include "identifyaction.h"
+#include "recordaction.h"
+#include "listsetsaction.h"
+#include "listrecsaction.h"
+#include "listidsaction.h"
+#include "metaformatsaction.h"
+#include "oaidispatcher.h"
+#include "oaiconfig.h"
+
+void get_cgi_args(text_t &argstring)
+{
+  // get the query string if it is not being run as a fastcgi
+  // script
+  text_t argstr = "";
+  char *aURIStr;
+  char *request_method_str = getenv("REQUEST_METHOD");
+  char *content_length_str = getenv("CONTENT_LENGTH");
+  if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
+      content_length_str != NULL)  {
+    // POST form data
+    int content_length = text_t(content_length_str).getint();
+    if (content_length > 0) {
+      char c;
+      do {
+	cin.get(c);
+	if (cin.eof()) 
+	  break;
+	argstr.push_back (c);
+	--content_length;
+      } while (content_length > 0);
+    }
+    
+  } else {
+    aURIStr = getenv("QUERY_STRING");
+    if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
+	|| aURIStr != NULL) {
+      // GET form data
+      if (aURIStr != NULL) 
+	argstr = aURIStr;
+    } else {
+      // debugging from command line
+      // debug = true;
+      char inStr[1024];
+      
+      cin.get(inStr, 1024);
+      argstr = inStr;
+    }
+  }
+  argstring = argstr;
+}
+
+
+int main(int argc, char *argv[])
+{
+  text_t        cgiargstring;
+  nullproto     nproto;
+  collectset   *cservers;
+  text_t        gsdlhome;
+  text_t        collecthome;
+  text_t        gsdlcollect = "";
+  oaiargs       args;
+  ofstream      logout("oai.log", ios::app);
+  
+  // convert cgi to oai map
+  get_cgi_args(cgiargstring);
+  
+  char *cstr = cgiargstring.getcstr();
+#if defined(GSDL_USE_IOS_H)
+  istrstream cgi_in(cstr, cgiargstring.size());
+#else
+  istringstream cgi_in(cstr);
+#endif
+
+  // parse the arguments
+  args.readArgs(cgi_in);
+
+  delete []cstr;
+  
+  cservers = new collectset(gsdlhome,collecthome);
+
+  // set up the null protocol
+  nproto.set_collectset(cservers);
+  
+  // get the oai configuration
+  oaiconfig config(gsdlhome, gsdlcollect);
+
+  // set up the dispatcher
+  oaidispatcher dispatcher;
+  dispatcher.setConfiguration(&config);
+
+  identifyaction *id   = new identifyaction;
+  recordaction   *rec  = new recordaction;
+  listsetsaction *sets = new listsetsaction;
+  listrecsaction *records = new listrecsaction;
+  records->set_recordMaker(rec);
+
+  listidsaction  *ids     = new listidsaction;
+  metaformatsaction *formats = new metaformatsaction;
+  formats->set_formats(rec->get_formats());
+
+  dispatcher.addAction(id);
+  dispatcher.addAction(rec);
+  dispatcher.addAction(sets);
+  dispatcher.addAction(records);
+  dispatcher.addAction(ids);
+  dispatcher.addAction(formats);
+
+  // deal with this request
+  comerror_t error;
+  nproto.init(error, logout);
+
+  // GRB: any checks that a collection exists would be done here
+  //      At present, I foresee no useful application of that requirement.
+
+  dispatcher.doAction(cout, &nproto, args);
+  
+  delete cservers;
+  return 0;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaimain.h	(revision 16561)
@@ -0,0 +1,1 @@
+// nothing
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.cpp	(revision 16561)
@@ -0,0 +1,82 @@
+#include "oaitools.h"
+
+#include <algorithm>
+
+#include "recptprototools.h"
+
+void oaiclassifier::swapColonsAndPeriods(text_t &classifier)
+{
+  for (int i = 1; i <= classifier.size(); ++i) {
+    if (classifier[i] == '.') {
+      classifier[i] = ':';
+    }
+    else if (classifier[i] == ':') {
+      classifier[i] = '.';
+    }
+  }
+
+}
+
+text_t oaiclassifier::getGSDL_OID(const text_t &collection, const text_t& oai_id, 
+				  recptproto *protocol, ostream &logout)
+{
+  FilterResponse_t response;
+  text_tset        metadata;
+
+  // metadata.insert("gsdl_id");
+
+  bool status_ok = get_children(oai_id, collection, "", metadata, false, protocol, response, logout);
+  text_t gsdl_id = "";
+  if (status_ok) {
+    if(response.docInfo.size() > 0){
+      gsdl_id = response.docInfo[0].OID;
+    }
+    else {
+      // Response.docInfo is empty, meaning the doc in question didn't have an oai_id number.
+      return gsdl_id; // Return the empty string to indicate this.
+    }
+  }
+
+  return gsdl_id;
+}
+
+/**
+ *  Called to convert GS classifier/document identifiers that use
+ *  a '.' to separate levels in the hierarchy into OAI identifiers
+ *  that use ':' instead.
+ */
+void oaiclassifier::toOAI(const text_t &collection, text_t &classifier)
+{
+  oaiclassifier::swapColonsAndPeriods(classifier);
+
+  // prepend the collection identifier to the beginning of the
+  // OAI identifier.
+  text_t tmp = collection;
+  tmp.append(":");
+  tmp.append(classifier);
+  
+  classifier = tmp;
+}
+
+/**
+ *  Called to convert OAI classifier/document identifiers that use
+ *  a ':' to separate levels in the hierarchy into GS identifiers
+ *  that use '.' instead.
+ */
+void oaiclassifier::toGSDL(text_t &collection, text_t &classifier)
+{
+  oaiclassifier::swapColonsAndPeriods(classifier);
+
+  // separate out the collection identifier that should be
+  // found at the beginning of the OAI identifier from the
+  // item identifier within the collection
+  text_t::iterator colon = find(classifier.begin(), classifier.end(), '.');
+  if (colon != classifier.end()) {
+    collection = substr(classifier.begin(), colon);
+    classifier = substr(colon + 1, classifier.end());
+  }
+  else{
+    collection = classifier;
+    classifier = "";
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/oaitools.h	(revision 16561)
@@ -0,0 +1,17 @@
+
+#ifndef _OAITOOLS_H_
+#define _OAITOOLS_H_
+
+#include "text_t.h"
+#include "receptionist.h"
+
+class oaiclassifier {
+ public:
+  static text_t getGSDL_OID(const text_t &collection, const text_t& oai_id, recptproto *protocol, 
+			    ostream &logout);
+  static void   toOAI(const text_t &collection, text_t &classifier);
+  static void   toGSDL(text_t &collection, text_t &classifier);
+ private:
+  static void   swapColonsAndPeriods(text_t &classifier);
+};
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.cpp	(revision 16561)
@@ -0,0 +1,170 @@
+#include "recordaction.h"
+#include "recptprototools.h"
+#include "dublincore.h"
+#include "rfc1807.h"
+#include "oaitools.h"
+#include <time.h>
+
+
+recordaction::recordaction() : oaiaction("GetRecord") {
+  metaformatptr fptr;
+
+  fptr.set_class(new dublin_core());  
+  this->formatMap[fptr.get_class()->formatName()] = fptr;
+  
+  fptr.set_class(new rfc1807());
+  this->formatMap[fptr.get_class()->formatName()] = fptr;
+}
+
+recordaction::~recordaction() {
+  metaformat_map::iterator here = this->formatMap.begin();
+  metaformat_map::iterator end  = this->formatMap.end();
+
+  while (here != end) {
+    here->second.clear();
+    ++here;
+  }
+}
+
+
+bool recordaction::validateAction(recptproto *protocol, oaiargs &params)
+{
+  int params_size = params.getSize();
+
+  // Remove any parameters that aren't valid for this action
+  text_tmap::const_iterator param_iterator = params.begin();
+  while (param_iterator != params.end())
+  {
+    if (param_iterator->first != "verb" &&
+	param_iterator->first != "identifier" &&
+	param_iterator->first != "metadataPrefix")
+    {
+      params.erase(param_iterator->first);
+    }
+
+    param_iterator++;
+  }
+
+  text_t meta = params["metadataPrefix"];
+  text_t gsdlId = params["identifier"];
+  text_t gsdlCollect;
+  text_t language = "";
+  int oaiVersion = this->configuration->getOAIVersion();
+
+  // The identifier and metadataPrefix args MUST be supplied, and are the only 
+  // args allowed (excluding verb arg). If we don't have them, throw an error.
+  if(gsdlId == "" || meta == "" || params_size != 3){
+    this->errorType = "badArgument";
+    return false;
+  }
+
+  // Check to see if the metadataPrefix supplied is supported
+  if(this->formatNotSupported(meta)) {
+    this->errorType = "cannotDisseminateFormat";
+    if(oaiVersion == 200) 
+      return false;
+  }
+
+  // convert record identifier into GSDL format from OAI
+  oaiclassifier::toGSDL(gsdlCollect, gsdlId);
+  
+  // get the document information
+  text_tset        metadata;
+  if (!get_info(gsdlId, gsdlCollect, language, metadata, false, protocol, this->gsdlResponse, *logout)) {
+    this->errorType = "idDoesNotExist";
+    if(oaiVersion == 200) 
+      return false;
+  }
+
+  return true;
+}
+
+//
+// Output the content of a GetRecord request; in this case, the static member 
+// output_record below is used to fulfill most of the request
+//
+bool recordaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
+{
+  // validateAction will already have set up the correct response content
+  text_t gsdlId = params["identifier"];
+  text_t gsdlCollect;
+
+  // convert record identifier into GSDL format from OAI
+  oaiclassifier::toGSDL(gsdlCollect, gsdlId);
+  
+  // go direct to output_record
+  return output_record(output, gsdlCollect, gsdlId, params["metadataPrefix"]);
+}
+
+//
+// A static member that does everything the output_content method above needs,
+// but can be called when individual records are being output for another 
+// action.
+//
+bool recordaction::output_record(ostream &output, recptproto *protocol, const text_t &collection, const text_t &OID, const text_t &metadataPrefix)
+{
+  text_tset        metadata;
+  ofstream         logout("oai.log", ios::app);
+
+  // get the document information
+  if (!get_info(OID, collection, "", metadata, false, protocol, this->gsdlResponse, logout)) {
+
+    this->errorType = "idDoesNotExist";
+
+    if(this->configuration->getOAIVersion() >= 200) {
+      this->output_error(output, errorType);
+      return false;
+    }
+  }
+
+  return this->output_record(output, collection, OID, metadataPrefix);
+}  
+
+bool recordaction::output_record(ostream &output, const text_t &collection, const text_t &OID, 
+				 const text_t &metadataPrefix)
+{ int oaiVersion = this->configuration->getOAIVersion();
+
+  // check to see if it's a classifier
+  text_t childHead;
+  text_t::const_iterator start = OID.begin();
+  text_t::const_iterator here  = OID.begin();
+  here += 2;
+  childHead = substr(start, here);
+  
+  // if it isn't a document, kill it now
+  if (childHead == "CL") {
+    cerr << "Not a document" << endl;
+    return false;
+  }
+
+  ResultDocInfo_t doc_info = this->gsdlResponse.docInfo[0];
+  text_t lastModified = "";
+
+  // Fills lastModified with the date from the document in doc_info, in the format YYYY-MM-DD
+  this->getLastModifiedDate(doc_info, lastModified);
+
+  // If the ID exists, output record for oai response (OAI v1.1)
+  // OAI v2.0 will already have bailed if ID doesn't exist (yes?)
+  if (this->errorType != "idDoesNotExist") {
+    text_t oaiLabel = OID;
+    oaiclassifier::toOAI(collection, oaiLabel); // Concatenates HASH id to collection, which OAI needs
+    
+    // output a record
+    output << "  <record>\n";
+    
+    // output header part of oai response
+    this->output_record_header(output, oaiLabel, lastModified, 
+			       doc_info.metadata["memberof"].values, oaiVersion);
+    
+    if (this->errorType != "cannotDisseminateFormat"){
+      if (this->formatMap[metadataPrefix].get_class()->output_metadata(output, collection, doc_info)) {
+	//  output 'about' part of oai response - we probably won't ever use this
+	//output << "    <about>\n";
+	//output << "    </about>\n";
+      } 
+    }
+    // close record
+    output << "  </record>\n\n";
+  }
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/recordaction.h	(revision 16561)
@@ -0,0 +1,29 @@
+#ifndef _RECORDACTION_H_
+#define _RECORDACTION_H_
+#include "oaiaction.h"
+
+#include <map>
+
+#include "comtypes.h"
+
+#include "metaformat.h"
+
+class recordaction : public oaiaction
+{
+ private:
+  metaformat_map formatMap;
+  FilterResponse_t gsdlResponse;
+  
+ public:
+  recordaction();
+  ~recordaction();
+  
+  virtual bool validateAction(recptproto *protocol, oaiargs &params);
+  bool output_content(ostream &output, recptproto *protocol, oaiargs &params);
+  bool output_record(ostream &output, const text_t &collection, const text_t &OID, 
+		     const text_t &metadataPrefix);
+  bool output_record(ostream &output, recptproto *protocol, const text_t &collection, const text_t &OID, 
+		     const text_t &metadataPrefix);
+  metaformat_map *get_formats() { return &this->formatMap; }
+};
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.cpp	(revision 16561)
@@ -0,0 +1,157 @@
+#include "resumptiontoken.h"
+#include "oaitools.h"
+
+/**
+ *  Generate an initial resumption token from some basic details.
+ *
+ *  TODO: add optional argument to set the server name.
+ */
+ResumptionToken::ResumptionToken(const text_t &collection, const text_t &node,
+				 const text_t &buildDate)
+{ this->collection = collection;
+  this->browseNode = node;
+  this->buildDate  = buildDate;
+  this->startItem  = 0;
+}
+
+/**
+ *  Generate a resumption token from a URN-style format.
+ *
+ *  See getToken() for details of the format.
+ *
+ *  TODO: support inclusion of an optional server name.
+ */
+ResumptionToken::ResumptionToken(const text_t &URN)
+{ text_t::const_iterator first = URN.begin();
+  text_t::const_iterator last = URN.end(); 
+  text_t::const_iterator second;
+
+  this->collection = "";
+  this->browseNode = "";
+  this->startItem  = -1;
+  
+  text_t::const_iterator here = findchar(first, last, ':');
+  if (here == first) {
+    return;
+  }
+  
+  text_t oainamespace = substr(first, here);
+  if (oainamespace != "gsdloai") {
+    return;
+  }
+
+  // increment past the first colon to get the location
+  first = ++here;  
+
+  // get the collection, browseNode
+  here = findchar(first, last, ',');
+  if (here == last) {
+    return;
+  }
+
+  second = findchar(first, here,'.');
+  this->collection = substr(first, second);
+
+  //  cerr << "Collection " << this->collection << endl;
+
+  if (second != here) {
+    // get past the '.'
+    ++second;
+    this->browseNode = substr(second, here);
+  }
+  else {
+    first = here;
+  }
+  // get past the ','
+  first = ++here;
+
+  // find the second ',' to delimit the position stack
+  second = findchar(first, last, ',');
+
+  // if not found, then get build and start item
+  if (second != first) {
+    // extract list and step past it
+    text_t offsetList = substr(first, second);
+    first = ++second;
+    
+    do {
+      second = findchar(offsetList.begin(), offsetList.end(), '.');
+      if (second == offsetList.end()) 
+	break;
+      
+      // extract and push the next position
+      text_t thisPos = substr(offsetList.begin(), second);
+      this->browsePosition.push_back(thisPos.getint());
+
+      // pop the position from the list
+      offsetList = substr(++second, offsetList.end());
+    } while (true);    
+    this->browsePosition.push_back(offsetList.getint());
+  }
+  else {
+    first ++;
+  }
+
+  // now find the build date marker
+  here = findchar(first, last, '-');
+  if (here == first) {
+    this->startItem = substr(first, last).getint();
+  }
+  else {
+    this->startItem = substr(first, here).getint();
+    this->buildDate = substr(++here, last); 
+  }
+}
+
+/**
+ *  Get a resumption token in text_t format.
+ * 
+ *  Resumption tokens are in the format:
+ *
+ *    gsdloai:<serverName>:collectionname.browseNode,startItem-BuildDate
+ *
+ *  The resumption token format does not currently implement the use of
+ *  the optional <serverName> item; it is taken to default to the name of
+ *  the receiving server.
+ *
+ *  TODO: add server identity as an optional argument; also change
+ *        ResumptionToken(text_t &) accordingly.
+ */
+text_t ResumptionToken::getToken()
+{ text_t reply = "gsdloai:";
+  reply = reply + this->collection;
+  if (this->browseNode != "") {
+    reply = reply + "." + this->browseNode;
+  }
+  reply = reply + ",";
+  for (int i = 0; i < this->browsePosition.size(); i++) {
+    if (i != 0) {
+      reply.append(".");
+    }
+    reply.appendint(i);
+  }
+  reply = reply + ",";
+  reply.append(this->startItem);
+  reply = reply + "-" + buildDate;
+
+  return reply;
+}
+
+/**
+ *  Update the position of an existing resumption token
+ */
+void ResumptionToken::setPosition(const text_t &node, int startItem)
+{ this->browseNode = node;
+  this->startItem  = startItem; 
+}
+
+/**
+ *  Check if the resumption token is valid - only a very primitive
+ *  check is done here; one ought to check for an existing collection
+ *  and valid browse Node, build date and startItem
+ *
+ *  TODO: implement improved validation checking.
+ */
+bool ResumptionToken::isValid()
+{ return this->collection != "";
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/resumptiontoken.h	(revision 16561)
@@ -0,0 +1,42 @@
+#ifndef _RESUMPTIONTOKEN_H_
+#define _RESUMPTIONTOKEN_H_
+
+#include <vector>
+
+#include "text_t.h"
+
+// use the standard namespace
+#if !defined (GSDL_NAMESPACE_BROKEN)
+#if defined(GSDL_USE_OBJECTSPACE)
+using namespace ospace::std;
+#else
+using namespace std;
+#endif
+#endif
+
+class ResumptionToken
+{ private:
+  text_t  collection;
+  text_t  browseNode;
+  vector<int> browsePosition;
+  int     startItem;
+  text_t  buildDate;
+  // TODO: add a server name to the variables list; see getToken in resumptionToken.cpp
+
+ public:
+  ResumptionToken(const text_t &collection, const text_t &rootNode, const text_t &buildDate);
+  ResumptionToken(const text_t &URN);
+  text_t  getToken();
+
+  void setPosition(const text_t &node, int startItem);
+
+  text_t  getCollection() { return collection; }
+  text_t  getNode() { return browseNode; }
+  int     getPosition() { return startItem; }
+  int     getOffsetDepth() { return this->browsePosition.size(); }
+  void    setOffset(int depth, int position) { this->browsePosition[depth] = position; }
+  int getOffset(int offset) { return this->browsePosition[offset]; }
+  bool isValid();
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.cpp	(revision 16561)
@@ -0,0 +1,57 @@
+#include "rfc1807.h"
+
+const text_t rfc1807::formatName() 
+{
+  return "rfc1807";
+}
+
+const text_t rfc1807::formatPrefix() {
+  return "rfc1807";
+}
+
+bool rfc1807::output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+			    const text_t &record_OID) 
+{
+  return metaformat::output_record(output, protocol, collection, record_OID);
+}
+
+void rfc1807::output_metadata_header(ostream &output)
+{
+  output << "<metadata>\n";
+  
+  if(this->oaiConfigure->getOAIVersion() <= 110){
+  // output rfc1807 wrapper for OAI v1.1
+    output << "<rfc1807 xmlns=\"http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt\"" << endl;
+    output << "         xmlns:xsi=\"http://www.w3c.org/2001/XMLSchema-instance\"" << endl;
+    output << "         xsi:schemaLocation=\"http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt" 
+	   << endl;
+    output << "                            http://www.openarchives.org/OAI/1.1/rfc1807.xsd\">" << endl;
+  }
+  else {
+    // TODO: output rfc1807 wrapper for OAI v2.0
+    output << "<rfc1807 xmlns=\"http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt\"" << endl;
+    output << "         xmlns:xsi=\"http://www.w3c.org/2001/XMLSchema-instance\"" << endl;
+    output << "         xsi:schemaLocation=\"http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1807.txt"
+	   << endl;
+    output << "                            http://www.openarchives.org/OAI/1.1/rfc1807.xsd\">" << endl;
+  } 
+}
+
+void rfc1807::output_metadata_footer(ostream &output)
+{
+  // end rfc1807 wrapper
+  output << "</rfc1807>" << endl;
+    
+  output << "</metadata>\n";
+}
+
+bool rfc1807::output_formatdata(ostream &output)
+{
+  // Both versions of the protocol use the same schema (yes?)
+  output << "    <metadataPrefix>rfc1807</metadataPrefix>" << endl;
+  output << "    <schema>http://www.openarchives.org/OAI/1.1/rfc1807.xsd</schema>" << endl;
+  // N.B.: no namespace for this format
+  output << "    <metadataNamespace></metadataNamespace>" << endl;
+
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/rfc1807.h	(revision 16561)
@@ -0,0 +1,14 @@
+#include "metaformat.h"
+
+class rfc1807 : public metaformat {
+ protected:
+  void output_metadata_header(ostream &output);
+  virtual void output_metadata_footer(ostream &output);
+ public:
+  rfc1807() : metaformat(){}; // call parent constructor
+  virtual const text_t formatName();
+  virtual const text_t formatPrefix();
+  virtual bool output_record(ostream &output, recptproto *protocol, const text_t &collection, 
+			     const text_t &record_OID);
+  virtual bool output_formatdata(ostream &output);
+};
Index: /gsdl/trunk/gs2runtime/src/oaiservr/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/oaiservr/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/oaiservr/win32.mak	(revision 16561)
@@ -0,0 +1,171 @@
+###########################################################################
+#
+# win32 makefile -- src/oaiserver
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+COLSERVR_DIR = $(GSDL_DIR)\src\colservr
+PROTOCOL_DIR = $(GSDL_DIR)\src\protocol
+RECPT_DIR = $(GSDL_DIR)\src\recpt
+
+
+GSDL_VC4 = 0
+LOCAL_LIBRARY = 0
+DLL = 0
+DLLDEBUG = 0
+ENABLE_ACCENTFOLD = 1
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+!IF $(ENABLE_ACCENTFOLD)
+ACCENTFOLD_LIBS = "$(INDEXERS_DIR)\packages\unac\libunac.lib" "$(INDEXERS_DIR)\packages\unac\iconv.lib"
+!ELSE
+ACCENTFOLD_LIBS = 
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_LIBS = "$(PACKAGES_DIR)\sqlite\lib\sqlite3.lib"
+!ELSE
+SQLITE_LIBS = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE \
+        -DGSDL_NOCACHE
+INCLUDES = -I"$(GSDL_DIR)\src\lib" -I"$(COLSERVR_DIR)" -I"$(PROTOCOL_DIR)" -I"$(RECPT_DIR)" \
+        -I"$(INDEXERS_DIR)\mgpp\text" -I"$(PACKAGES_DIR)\windows\crypt\crypt" \
+	-I"$(PACKAGES_DIR)\windows\expat\expat" -I"$(PACKAGES_DIR)\windows\gdbm\gdbm"
+LDFLAGS = 
+LIBS = "$(PACKAGES_DIR)\windows\crypt\crypt\crypt.lib" "$(PACKAGES_DIR)\windows\expat\expat\libexpat.lib" \
+        "$(PACKAGES_DIR)\windows\gdbm\gdbm\gdbm.lib" $(ACCENTFOLD_LIBS) $(SQLITE_LIBS)
+
+!IF $(GSDL_VC4)
+DEFS = $(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+!IF $(LOCAL_LIBRARY)
+DEFS = $(DEFS) -DGSDL_LOCAL_LIBRARY
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo -Fe"$@" 
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+RECPT_OBJECTS = \
+	"$(RECPT_DIR)\cgiargs.obj" \
+	"$(RECPT_DIR)\cgiutils.obj" \
+	"$(RECPT_DIR)\htmlutils.obj"
+
+SOURCES = \
+	abstractlistaction.cpp \
+	dublincore.cpp \
+	identifyaction.cpp \
+	listidsaction.cpp \
+	listrecsaction.cpp \
+	listsetsaction.cpp \
+	metaformat.cpp \
+	metaformatsaction.cpp \
+	oaiaction.cpp \
+	oaiargs.cpp \
+	oaiconfig.cpp \
+	oaidispatcher.cpp \
+	oaimain.cpp \
+	oaitools.cpp \
+	recordaction.cpp \
+	resumptiontoken.cpp \
+	rfc1807.cpp
+
+OBJECTS = \
+	abstractlistaction.obj \
+	dublincore.obj \
+	identifyaction.obj \
+	listidsaction.obj \
+	listrecsaction.obj \
+	listsetsaction.obj \
+	metaformat.obj \
+	metaformatsaction.obj \
+	oaiaction.obj \
+	oaiargs.obj \
+	oaiconfig.obj \
+	oaidispatcher.obj \
+	oaimain.obj \
+	oaitools.obj \
+	recordaction.obj \
+	resumptiontoken.obj \
+	rfc1807.obj
+
+GSDL_LIBS = \
+	"$(PROTOCOL_DIR)\gsdlprotocol.lib" \
+	"$(COLSERVR_DIR)\gsdlcolservr.lib" \
+	"$(INDEXERS_DIR)\mg\src\text\libmgtext.lib" \
+	"$(INDEXERS_DIR)\mg\lib\libmglib.lib" \
+	"$(INDEXERS_DIR)\mgpp\text\libmgpptext.lib" \
+	"$(INDEXERS_DIR)\mgpp\lib\libmgpplib.lib" \
+	"$(GSDL_DIR)\src\lib\gsdllib.lib"
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(RECPT_OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = oaiserver.exe
+
+
+# Default target: make the oaiserver executable
+all: $(EXECUTABLE)
+
+oaiserver.exe: $(EXECUTABLE_OBJECTS)
+	$(LINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	copy $(EXECUTABLE) "$(GSDL_DIR)\cgi-bin"
+
+clean:
+	del *.obj
+	del $(EXECUTABLE)
Index: /gsdl/trunk/gs2runtime/src/protocol/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/Makefile.in	(revision 16561)
@@ -0,0 +1,81 @@
+###########################################################################
+#
+# Makefile -- src/protocol
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+COLSERVR_DIR = $(GSDL_DIR)/src/colservr
+
+
+AR = ar
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DGSDLSERVER
+# Do NOT add the "recpt" directory here: the protocol should be independent of the receptionist!
+INCLUDES = @GDBM_INCLUDE@ -I. -I$(GSDL_DIR) -I$(GSDL_DIR)/src/lib -I$(COLSERVR_DIR) -I$(INDEXERS_DIR)/mgpp/text
+RANLIB = @RANLIB@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	nullproto.cpp \
+	recptproto.cpp \
+	recptprototools.cpp
+
+OBJECTS = \
+	nullproto.o \
+	recptproto.o \
+	recptprototools.o
+
+LIBRARY = gsdlprotocol.a
+
+
+# Default target: make gsdlprotocol.a
+all: $(LIBRARY)
+
+gsdlprotocol.a: $(OBJECTS)
+	rm -f $(LIBRARY)
+	$(AR) cru $(LIBRARY) $(OBJECTS)
+	$(RANLIB) $(LIBRARY)
+
+install:
+
+clean:
+	rm -f $(OBJECTS) $(LIBRARY)
+
+distclean:
+	rm -f $(OBJECTS) $(LIBRARY) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2runtime/src/protocol/nullproto.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/nullproto.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/nullproto.cpp	(revision 16561)
@@ -0,0 +1,164 @@
+/**********************************************************************
+ *
+ * nullproto.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.
+ *
+ *********************************************************************/
+
+#include "nullproto.h"
+#include <assert.h>
+
+#include "filter.h"
+#include "browsefilter.h"
+#include "mgqueryfilter.h"
+#include "mgppqueryfilter.h"
+#include "mgsearch.h"
+#include "mgppsearch.h"
+#include "fileutil.h"
+
+nullproto::nullproto() {
+  cset = NULL;
+}
+
+nullproto::~nullproto() {
+}
+
+// add collection to collectset (this should not be called until after the
+// collectset has been added to the protocol with set_collectset()
+void nullproto::add_collection (const text_t &collection, void *recpt, 
+				const text_t &gsdlhome, const text_t& collecthome,
+				const text_t &dbhome) 
+{      
+  
+  if (cset != NULL) {
+    this->cset->add_collection (collection, gsdlhome, collecthome);
+  }
+}
+
+
+// remove_collection deletes the collection server of collection.
+// This only needs to be called if a collectionserver is to be
+// removed while the library is running. The destructor function
+// cleans up all collectservers when the program exits.
+void nullproto::remove_collection (const text_t &collection, ostream &logout) {
+  cset->remove_collection(collection, logout);
+}
+
+// this configure will configure each of the collection servers
+void nullproto::configure (const text_t &key, const text_tarray &cfgline,
+			   comerror_t &err) {
+  // the naming of the collection should not be done here,
+  // it should be done just after the collection server has been 
+  // created
+
+  // the protocol should not configure the collection set; it should be
+  // done direct to the collection server set
+  if (key == "gsdlhome" || key == "gdbmhome"
+      || key == "collecthome" || key == "collectdir") {
+    cset->configure(key, cfgline);
+  }
+}
+
+// this init will configure and init each of the collection servers
+bool nullproto::init (comerror_t &err, ostream &logout) {
+  return cset->init(logout);
+}
+
+text_t nullproto::get_site_name (comerror_t &err) {
+  return "localhost";
+}
+
+text_t nullproto::get_protocol_name (comerror_t &err) {
+  return "nullproto";
+}
+
+
+void nullproto::get_collection_list (text_tarray &collist, comerror_t &err, 
+				     ostream &/*logout*/) {
+  cset->getCollectionList(collist);
+  err = noError; // no error is guaranteed - collection server shouldn't
+                 // about receptionist error states, logout is irrelevant
+}
+
+void nullproto::has_collection (const text_t &collection, bool &hascollection, 
+				comerror_t &err, ostream &/*logout*/) {
+  hascollection = (cset->getCollectServer(collection) != NULL);
+  err = noError;
+}
+
+void nullproto::ping (const text_t &collection, bool &wassuccess, 
+		      comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer(collection);
+  if (cserver != NULL) cserver->ping(wassuccess, err, logout);
+  else err = protocolError;
+}
+
+void nullproto::get_collectinfo (const text_t &collection, 
+				 ColInfoResponse_t &collectinfo,
+				 comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->get_collectinfo (collectinfo, err, logout);
+  else err = protocolError;
+}
+
+
+void nullproto::get_filterinfo (const text_t &collection,
+				InfoFiltersResponse_t &response,
+				comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->get_filterinfo (response, err, logout);
+  else err = protocolError;
+}
+
+void nullproto::get_filteroptions (const text_t &collection,
+				   const InfoFilterOptionsRequest_t &request,
+				   InfoFilterOptionsResponse_t &response, 
+				   comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->get_filteroptions (request, response, err, logout);
+  else err = protocolError;
+}
+
+void nullproto::filter (const text_t &collection,
+			FilterRequest_t &request,
+			FilterResponse_t &response,
+			comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->filter (request, response, err, logout);
+  else err = protocolError;
+}
+
+void nullproto::get_document (const text_t &collection,
+			      const DocumentRequest_t &request,
+			      DocumentResponse_t &response,
+			      comerror_t &err, ostream &logout) {
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->get_document (request, response, err, logout);
+  else err = protocolError;
+}
+
+void nullproto::is_searchable (const text_t &collection, bool &issearchable, 
+			       comerror_t &err, ostream &logout) {
+  issearchable = false;
+  collectserver *cserver = cset->getCollectServer (collection);
+  if (cserver != NULL) cserver->is_searchable (issearchable, err, logout);
+  else err = protocolError;
+}
Index: /gsdl/trunk/gs2runtime/src/protocol/nullproto.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/nullproto.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/nullproto.h	(revision 16561)
@@ -0,0 +1,100 @@
+/**********************************************************************
+ *
+ * nullproto.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.
+ *
+ *********************************************************************/
+
+
+#ifndef NULLPROTO_H
+#define NULLPROTO_H
+
+#include "gsdlconf.h"
+#include "collectserver.h"
+#include "collectset.h"
+#include "recptproto.h"
+
+
+class nullproto : public recptproto {
+protected:
+  collectset           *cset;
+public:
+  nullproto();
+  virtual ~nullproto();
+
+  // add_collection sets up the collectionserver and calls
+  // add_collectserver
+  virtual void add_collection (const text_t &collection, void *recpt, 
+			       const text_t &gsdlhome, const text_t& collecthome,
+			       const text_t &dbhome);
+
+  void set_collectset(collectset *set) { this->cset = set; }
+
+  // remove_collection deletes the collection server of collection.
+  // This only needs to be called if a collectionserver is to be
+  // removed while the library is running as the destructor function
+  // cleans up all collectservers when the program exits.
+  void remove_collection (const text_t &collection, ostream &logout);
+
+  // this configure will configure each of the collection servers
+  void configure (const text_t &key, const text_tarray &cfgline,
+		  comerror_t &err);
+
+  // this init will init each of the collection servers
+  bool init (comerror_t &err, ostream &logout);
+
+  text_t get_protocol_name (comerror_t &err);
+  text_t get_site_name (comerror_t &err);
+
+  void get_collection_list (text_tarray &collist, comerror_t &err, 
+			    ostream &logout);
+  void has_collection (const text_t &collection, bool &hascollection, 
+		       comerror_t &err, ostream &logout);
+  void ping (const text_t &collection, bool &wassuccess, 
+	     comerror_t &err, ostream &logout);
+  void get_collectinfo (const text_t &collection, 
+			ColInfoResponse_t &collectinfo,
+			comerror_t &err, ostream &logout);
+  void get_filterinfo (const text_t &collection,
+		       InfoFiltersResponse_t &response,
+		       comerror_t &err, ostream &logout);
+  void get_filteroptions (const text_t &collection,
+			  const InfoFilterOptionsRequest_t &request,
+			  InfoFilterOptionsResponse_t &response, 
+			  comerror_t &err, ostream &logout);
+  void filter (const text_t &collection,
+	       FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+
+  void get_document (const text_t &collection,
+		     const DocumentRequest_t &request,
+		     DocumentResponse_t &response,
+		     comerror_t &err, ostream &logout);
+
+  void is_searchable (const text_t &collection, bool &issearchable, 
+		      comerror_t &err, ostream &logout);
+
+
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/protocol/recptproto.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/recptproto.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/recptproto.cpp	(revision 16561)
@@ -0,0 +1,176 @@
+/**********************************************************************
+ *
+ * recptproto.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.
+ *
+ *********************************************************************/
+
+#include "recptproto.h"
+#include <assert.h>
+
+
+// add collection server to protocol
+void recptproto::add_collection (const text_t &/*collection*/, void * /*recpt*/, 
+				 const text_t &/*gsdlhome*/, const text_t& /*collecthome */, 
+				 const text_t &/*dbhome*/) 
+{
+}
+
+void recptproto::remove_collection (const text_t &/*collection*/, ostream &/*logout*/) {
+}
+
+// configure should be called for each line in the configuration file
+void recptproto::configure (const text_t &/*key*/, const text_tarray &/*cfgline*/, comerror_t &) {
+}
+
+// init should be called after the configuration but before any other
+// functions are called. If init returns false a message will be written
+// out to the log file and no other output should be produced.
+bool recptproto::init (comerror_t &err, ostream &/*logout*/) {
+  return true;
+}
+
+// get site name should return the name of the site used.
+// This is trivially empty in the case of a null protocol.  If a remote
+// connection to a site is being used then this should return the name
+// used to label a site
+text_t recptproto::get_site_name(comerror_t &err) {
+  return "localhost";
+}
+
+// get_protocol_name should return the name of this protocol (e.g. recptproto)
+// that can be used to do run time type identification and display information
+// about the protocol.
+text_t recptproto::get_protocol_name (comerror_t &err) {
+  return "recptproto";
+}
+
+// get_collection_list returns the list of collections that
+// this protocol knows about
+void recptproto::get_collection_list (text_tarray &collist, comerror_t &err, 
+				      ostream &/*logout*/) {
+  collist.erase(collist.begin(),collist.end());
+  err = noError;
+}
+
+// has_collection sets 'hascollection' to be true if the protocol
+// can communicate with the collection (i.e. it is within its 
+// collection list). This function should be implemented in as
+// efficient time as possible as it will be called for each page
+// access and for each protocol.
+void recptproto::has_collection (const text_t &/*collection*/, bool &hascollection, 
+				 comerror_t &err, ostream &/*logout*/) {
+  hascollection = false;
+  err = noError;
+}
+
+// sets 'wassuccess' to be true if a successful ping was done to
+// the given collection.
+void recptproto::ping (const text_t &/*collection*/, bool &wassuccess, 
+		       comerror_t &err, ostream &/*logout*/) {
+  wassuccess = false; // no collections are supported by this class
+  err = noError;
+}
+
+// obtains general information about the collection
+void recptproto::get_collectinfo (const text_t &/*collection*/, 
+				  ColInfoResponse_t &/*collectinfo*/,
+				  comerror_t &err, ostream &/*logout*/) {
+  err = protocolError;
+}
+
+// gets a list of all the filters
+void recptproto::get_filterinfo (const text_t &/*collection*/,
+				 InfoFiltersResponse_t &/*reponse*/,
+				 comerror_t &err, ostream &/*logout*/) {
+  err = protocolError;
+}
+
+// gets all the filter options for a particular filter
+void recptproto::get_filteroptions (const text_t &/*collection*/,
+				    const InfoFilterOptionsRequest_t &/*request*/,
+				    InfoFilterOptionsResponse_t &/*response*/, 
+				    comerror_t &err, ostream &/*logout*/) {
+  err = protocolError;
+}
+
+// filters (search or browse) a result set
+void recptproto::filter (const text_t &/*collection*/,
+			 FilterRequest_t &/*request*/,
+			 FilterResponse_t &/*response*/,
+			 comerror_t &err, ostream &/*logout*/) {
+  err = protocolError;
+}
+
+void recptproto::get_document (const text_t &/*collection*/,
+			       const DocumentRequest_t &/*request*/,
+			       DocumentResponse_t &/*response*/,
+			       comerror_t &err, ostream &/*logout*/) {
+  err = protocolError;
+}
+
+// sets issearchable to true if the given colection is searchable
+void recptproto::is_searchable (const text_t &/*collection*/, bool &issearchable, 
+				comerror_t &err, ostream &/*logout*/) {
+  issearchable = false; // no collections are supported by this class
+  err = noError;
+}
+
+
+
+
+
+
+
+// therecptproto remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void recptprotolistclass::addrecptproto (recptproto *therecptproto) {
+  // can't add a protocol that doesn't exist
+  assert (therecptproto != NULL);
+  if (therecptproto == NULL) return;
+
+  recptprotoptr rpp;
+  rpp.p = therecptproto;
+
+  recptprotoptrs.push_back(rpp);
+}
+
+// getrecptproto will return NULL if a recptproto for the given colelction
+// could not be found
+recptproto *recptprotolistclass::getrecptproto (const text_t &collection, 
+						ostream &logout) {
+  iterator here = recptprotoptrs.begin ();
+  iterator end = recptprotoptrs.end ();
+  bool hascollection;
+  comerror_t err;
+
+  while (here != end) {
+    assert ((*here).p != NULL);
+    if ((*here).p != NULL) {
+      (*here).p->has_collection (collection, hascollection, err, logout);
+      if ((err == noError) && (hascollection == true)) return (*here).p;
+    }
+    
+    ++here;
+  }
+  
+  return NULL;
+}
Index: /gsdl/trunk/gs2runtime/src/protocol/recptproto.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/recptproto.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/recptproto.h	(revision 16561)
@@ -0,0 +1,194 @@
+/**********************************************************************
+ *
+ * recptproto.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.
+ *
+ *********************************************************************/
+
+
+#ifndef RECPTPROTO_H
+#define RECPTPROTO_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "comtypes.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\vector>
+#elif defined(GSDL_USE_STL_H)
+#  include <vector.h>
+#else
+#  include <vector>
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+
+// recptproto is a generalisation of a protocol for communicating
+// with a collection server.
+class recptproto {
+public:
+
+  // add collection server to protocol
+  virtual void add_collection (const text_t &collection, void *recpt, 
+			       const text_t &gsdlhome, const text_t& collecthome,
+			       const text_t &dbhome);
+
+  virtual void remove_collection (const text_t &collection, ostream &logout);
+
+  // configure should be called for each line in the configuration file
+  virtual void configure (const text_t &key, const text_tarray &cfgline, comerror_t &err);
+
+  // init should be called after the configuration but before any other
+  // functions are called. If init returns false a message will be written
+  // out to the log file and no other output should be produced.
+  virtual bool init (comerror_t &err, ostream &logout);
+
+  // get_site_name should return the name of the site used.
+  // This is trivially empty in the case of a null protocol.  If a remote
+  // connection to a site is being used then this should return the name
+  // used to label a site
+  virtual text_t get_site_name(comerror_t &err);
+
+  // get_protocol_name should return the name of this protocol (e.g. recptproto)
+  // that can be used to do run time type identification and display information
+  // about the protocol.
+  virtual text_t get_protocol_name (comerror_t &err);
+
+  // get_collection_list returns the list of collections that
+  // this protocol knows about
+  virtual void get_collection_list (text_tarray &collist, comerror_t &err, 
+				    ostream &logout);
+
+  // has_collection sets 'hascollection' to be true if the protocol
+  // can communicate with the collection (i.e. it is within its 
+  // collection list). This function should be implemented in as
+  // efficient time as possible as it will be called for each page
+  // access and for each protocol.
+  virtual void has_collection (const text_t &collection, bool &hascollection, 
+			       comerror_t &err, ostream &logout);
+
+  // sets 'wassuccess' to be true if a successful ping was done to
+  // the given collection.
+  virtual void ping (const text_t &collection, bool &wassuccess, 
+		     comerror_t &err, ostream &logout);
+
+  // obtains general information about the collection
+  virtual void get_collectinfo (const text_t &collection, 
+				ColInfoResponse_t &collectinfo,
+				comerror_t &err, ostream &logout);
+
+  // gets a list of all the filters
+  virtual void get_filterinfo (const text_t &collection,
+			       InfoFiltersResponse_t &response,
+			       comerror_t &err, ostream &logout);
+
+  // gets all the filter options for a particular filter
+  virtual void get_filteroptions (const text_t &collection,
+				  const InfoFilterOptionsRequest_t &request,
+				  InfoFilterOptionsResponse_t &response, 
+				  comerror_t &err, ostream &logout);
+
+  // filters (search or browse) a result set and returns information
+  // about the filtered set including term frequency information and 
+  // metadata
+  virtual void filter (const text_t &collection,
+		       FilterRequest_t &request,
+		       FilterResponse_t &response,
+		       comerror_t &err, ostream &logout);
+
+  // gets a document (duh!)
+  virtual void get_document (const text_t &collection,
+			     const DocumentRequest_t &request,
+			     DocumentResponse_t &response,
+			     comerror_t &err, ostream &logout);
+
+  virtual void is_searchable (const text_t &collection, bool &issearchable, 
+			      comerror_t &err, ostream &logout);
+
+};
+
+
+// The recptprotoptr function does not 'own' the recptproto. The 
+// recptproto should be deleted by the code which created it.
+class recptprotoptr {
+public:
+  recptproto *p;
+
+  recptprotoptr () {p=NULL;}
+  ~recptprotoptr () {}
+};
+
+typedef vector<recptprotoptr> recptprotoptrlist;
+
+
+// contains a list of recptprotos
+class recptprotolistclass {
+protected:
+  recptprotoptrlist recptprotoptrs;
+
+public:
+  // type support for recptprotolistclass
+  typedef recptprotoptrlist::iterator iterator;
+  typedef recptprotoptrlist::const_iterator const_iterator;
+  typedef recptprotoptrlist::reference reference;
+  typedef recptprotoptrlist::const_reference const_reference;
+  typedef recptprotoptrlist::size_type size_type;
+
+  typedef recptprotoptrlist::difference_type difference_type;
+  typedef recptprotoptrlist::const_reverse_iterator const_reverse_iterator;
+  typedef recptprotoptrlist::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return recptprotoptrs.begin();}
+  const_iterator begin () const {return recptprotoptrs.begin();}
+  iterator end () {return recptprotoptrs.end();}
+  const_iterator end () const {return recptprotoptrs.end();}
+
+  void erase(iterator pos) {recptprotoptrs.erase(pos);}
+  void erase(iterator first, iterator last) {recptprotoptrs.erase(first, last);}
+  recptprotolistclass &operator=(const recptprotolistclass &x) 
+    {recptprotoptrs=x.recptprotoptrs;return *this;}
+
+  bool empty () const {return recptprotoptrs.empty();}
+  size_type size() const {return recptprotoptrs.size();}
+
+
+  // added functionality
+  void clear () {recptprotoptrs.erase(recptprotoptrs.begin(),recptprotoptrs.end());}
+
+  // therecptproto remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addrecptproto (recptproto *therecptproto);
+
+  // getrecptproto will return NULL if a recptproto for the given collection
+  // could not be found
+  recptproto *getrecptproto (const text_t &collection, ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/protocol/recptprototools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/recptprototools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/recptprototools.cpp	(revision 16561)
@@ -0,0 +1,330 @@
+/**********************************************************************
+ *
+ * recptprototools.cpp -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#include "recptprototools.h"
+#include <assert.h>
+
+
+// get_info does a protocol call and returns (in response) the metadata 
+// associated with OID. Metadata should be loaded with whatever
+// metadata fields are to be requested.
+
+bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout)
+{
+  response.clear();
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+  request.clear();
+
+  request.filterName = "NullFilter";
+  request.filterLang = lang;
+  request.filterResultOptions = FRmetadata;
+  request.getParents = getParents;
+  request.fields = metadata;
+  request.docSet.push_back (OID);
+  assert (collectproto != NULL);
+
+  collectproto->filter (collection, request, response, err, logout);
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed for " << OID
+	   << " in recptprototools::get_info (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+  
+  return true;
+}
+
+
+// overloaded, to allow "custom" filter options.
+bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, const OptionValue_tarray &options,
+	       bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout)
+{
+  response.clear();
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+
+  request.filterName = "NullFilter";
+  request.filterLang = lang;
+  request.filterResultOptions = FRmetadata;
+  request.getParents = getParents;
+  request.filterOptions = options;
+  request.fields = metadata;
+  request.docSet.push_back (OID);
+
+  assert (collectproto != NULL);
+  collectproto->filter (collection, request, response, err, logout);
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed for " << OID
+	   << " in recptprototools::get_info (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+  
+  return true;
+}
+
+
+bool get_info (const text_tarray &OIDs, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout)
+{
+  response.clear();
+  if (OIDs.empty()) return true;
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+
+  request.filterName = "NullFilter";
+  request.filterLang = lang;
+  request.filterResultOptions = FRmetadata;
+  request.getParents = getParents;
+  request.fields = metadata;
+
+  request.docSet = OIDs;
+  
+  collectproto->filter (collection, request, response, err, logout);
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed in recptprototools::get_info (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+
+  return true;
+}
+
+
+// has_children returns true if OID has children
+bool has_children (const text_t &OID, const text_t &collection, const text_t &lang,
+		   recptproto *collectproto, ostream &logout) {
+
+  FilterResponse_t response;
+  text_tset metadata;
+  metadata.insert ("haschildren");
+
+  if (get_info (OID, collection, lang, metadata, false, collectproto,	response, logout))
+  {
+    if (response.docInfo[0].metadata["haschildren"].values[0] == "1")
+    {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+
+// get_children does a protocol call and returns (in response) the OIDs and 
+// metadata of all the children of OID. The metadata set should be loaded
+// with whatever metadata fields are to be requested.
+
+bool get_children (const text_t &OID, const text_t &collection, const text_t &lang,
+		   const text_tset &metadata, bool getParents,
+		   recptproto *collectproto, FilterResponse_t &response, 
+		   ostream &logout)
+{
+  response.clear();
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+  OptionValue_t option;
+
+  option.name = "ParentNode";
+  option.value = OID;
+  request.filterOptions.push_back (option);
+  request.filterName = "BrowseFilter";
+  request.filterLang = lang;
+  request.filterResultOptions = FROID;
+
+  // Efficiency improvement: only get the filter to retrieve metadata if some has been requested
+  // Otherwise, the filter makes an unnecessary request to the database for each child node
+  // By Michael Dewsnip, DL Consulting Ltd
+  if (metadata.size() > 0)
+  {
+    request.filterResultOptions |= FRmetadata;
+  }
+
+  request.fields = metadata;
+  request.getParents = getParents;
+
+  collectproto->filter (collection, request, response, err, logout);
+
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed for " << OID 
+	   << " in recptprototools::get_children (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+
+  return true;
+}
+
+
+static void recurse_contents (ResultDocInfo_t section, const bool &is_classify, 
+			      const text_t &collection, const text_t &lang,
+			      const text_tset &metadata, 
+			      recptproto *collectproto, FilterResponse_t &response, 
+			      ostream &logout)
+{
+  int haschildren = section.metadata["haschildren"].values[0].getint();
+  const text_t &doctype = section.metadata["doctype"].values[0];
+
+  if ((haschildren == 1) && ((!is_classify) || (doctype == "classify")))
+  {
+    FilterResponse_t tmp;
+    bool getParents = false;
+    get_children (section.OID, collection, lang, metadata, getParents, collectproto, tmp, logout);
+    ResultDocInfo_tarray::iterator thisdoc = tmp.docInfo.begin();
+    ResultDocInfo_tarray::iterator lastdoc = tmp.docInfo.end();
+    while (thisdoc != lastdoc)
+    {
+      response.docInfo.push_back (*thisdoc);
+      recurse_contents (*thisdoc, is_classify, collection, lang, metadata, collectproto, response, logout);
+      ++thisdoc;
+    }
+  }
+}
+
+
+// get_contents returns OIDs and metadata of all contents 
+// below (and including) OID.
+void get_contents (const text_t &topOID, const bool &is_classify, 
+		   text_tset &metadata, const text_t &collection, const text_t &lang,
+		   recptproto *collectproto, FilterResponse_t &response, 
+		   ostream &logout)
+{
+  if (topOID.empty()) return;
+  response.clear();
+
+  metadata.insert ("haschildren");
+  metadata.insert ("doctype");
+
+  // get topOIDs info
+  if (get_info (topOID, collection, lang, metadata, false, collectproto, response, logout))
+  {
+    recurse_contents (response.docInfo[0], is_classify, collection, lang, metadata, collectproto, response, logout);
+  }
+}
+
+
+bool get_metadata_values (const text_t metadata_elements, const text_t metadata_value_filter,
+			  const text_t metadata_value_grouping_expression, const text_t &collection,
+			  recptproto *collectproto, FilterResponse_t &response, ostream &logout)
+{
+  response.clear();
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+  request.clear();
+
+  request.filterName = "SQLBrowseFilter";
+  request.requestParams = "GetMetadataValues";
+
+  OptionValue_t request_option;
+  request_option.name = "MetadataElements";
+  request_option.value = metadata_elements;
+  request.filterOptions.push_back (request_option);
+  request_option.name = "MetadataValueFilter";
+  request_option.value = metadata_value_filter;
+  request.filterOptions.push_back (request_option);
+  request_option.name = "MetadataValueGroupingExpression";
+  request_option.value = metadata_value_grouping_expression;
+  request.filterOptions.push_back (request_option);
+
+  assert (collectproto != NULL);
+  collectproto->filter (collection, request, response, err, logout);
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed for " << metadata_elements
+	   << " in recptprototools::get_metadata_values (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+  
+  return true;
+}
+
+
+bool get_documents_with_metadata_value (const text_t metadata_elements, const text_t metadata_value,
+					const text_t sort_by_metadata_element_name, const text_t &collection,
+					recptproto *collectproto, FilterResponse_t &response, ostream &logout)
+{
+  response.clear();
+
+  comerror_t err = noError;
+  FilterRequest_t request;
+  request.clear();
+
+  request.filterName = "SQLBrowseFilter";
+  request.requestParams = "GetDocumentsWithMetadataValue";
+
+  OptionValue_t request_option;
+  request_option.name = "MetadataElements";
+  request_option.value = metadata_elements;
+  request.filterOptions.push_back (request_option);
+  request_option.name = "MetadataValue";
+  request_option.value = metadata_value;
+  request.filterOptions.push_back (request_option);
+  request_option.name = "SortByMetadataElement";
+  request_option.value = sort_by_metadata_element_name;
+  request.filterOptions.push_back (request_option);
+
+  assert (collectproto != NULL);
+  collectproto->filter (collection, request, response, err, logout);
+  if (err != noError)
+  {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii
+	   << "Error: call to filter failed for " << metadata_elements << " = " << metadata_value
+	   << " in recptprototools::get_documents_with_metadata_value (" 
+	   << get_comerror_string (err) << ")\n";
+    return false;
+  }
+  
+  return true;
+}
Index: /gsdl/trunk/gs2runtime/src/protocol/recptprototools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/recptprototools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/recptprototools.h	(revision 16561)
@@ -0,0 +1,78 @@
+/**********************************************************************
+ *
+ * recptprototools.h -- 
+ * Copyright (C) 1999-2008  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.
+ *
+ *********************************************************************/
+
+#ifndef RECPTPROTOTOOLS_H
+#define RECPTPROTOTOOLS_H
+
+
+#include "text_t.h"
+#include "recptproto.h"
+
+
+// get_info does a protocol call and returns (in response) the info 
+// associated with OID. Metadata should be loaded with whatever
+// metadata fields are to be requested
+bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout);
+bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, const OptionValue_tarray &options,
+	       bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout);
+bool get_info (const text_tarray &OIDs, const text_t &collection, const text_t &lang,
+	       const text_tset &metadata, bool getParents,
+	       recptproto *collectproto, FilterResponse_t &response, 
+	       ostream &logout);
+
+// has_children returns true if OID has children
+bool has_children (const text_t &OID, const text_t &collection, const text_t &lang, 
+		   recptproto *collectproto, ostream &logout);
+
+// get_children does a protocol call and returns (in response) the OIDs and 
+// metadata of all the children of OID. The metadata set should be loaded
+// with whatever metadata fields are to be requested.
+bool get_children (const text_t &OID, const text_t &collection, const text_t &lang, 
+		   const text_tset &metadata, bool getParents,
+		   recptproto *collectproto, FilterResponse_t &response, 
+		   ostream &logout);
+
+// get_contents returns OIDs and metadata of all contents 
+// below (and including) OID.
+void get_contents (const text_t &topOID, const bool &is_classify, 
+		   text_tset &metadata, const text_t &collection, const text_t &lang, 
+		   recptproto *collectproto, FilterResponse_t &response, 
+		   ostream &logout);
+
+bool get_metadata_values (const text_t metadata_elements, const text_t metadata_value_filter,
+			  const text_t metadata_value_grouping_expression, const text_t &collection,
+			  recptproto *collectproto, FilterResponse_t &response, ostream &logout);
+
+bool get_documents_with_metadata_value (const text_t metadata_elements, const text_t metadata_value,
+					const text_t sort_by_metadata_element_name, const text_t &collection,
+					recptproto *collectproto, FilterResponse_t &response, ostream &logout);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/protocol/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/protocol/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/protocol/win32.mak	(revision 16561)
@@ -0,0 +1,99 @@
+###########################################################################
+#
+# win32 makefile -- src/protocol
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+COLSERVR_DIR = $(GSDL_DIR)\src\colservr
+
+
+GSDL_VC4 = 0
+DLL = 0
+DLLDEBUG = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+# Do NOT add the "recpt" directory here: the protocol should be independent of the receptionist!
+INCLUDES = -I"$(GSDL_DIR)" -I"$(GSDL_DIR)\src\lib" -I"$(COLSERVR_DIR)" -I"$(INDEXERS_DIR)\mgpp\text" \
+	 -I"$(PACKAGES_DIR)\windows\gdbm\gdbm"
+
+!IF $(GSDL_VC4)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+SOURCES = \
+	nullproto.cpp \
+	recptproto.cpp \
+	recptprototools.cpp
+
+OBJECTS = \
+	nullproto.obj \
+	recptproto.obj \
+	recptprototools.obj
+
+LIBRARY = gsdlprotocol.lib	
+	
+
+# Default target: make gsdlprotocol.lib
+all: $(LIBRARY)
+
+gsdlprotocol.lib: $(OBJECTS) 
+	if EXIST $(LIBRARY) del $(LIBRARY)
+	$(AR) -nologo /out:$(LIBRARY) $(OBJECTS)
+
+install:
+
+clean:
+	del *.obj
+	del $(LIBRARY)
Index: /gsdl/trunk/gs2runtime/src/recpt/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/Makefile.in	(revision 16561)
@@ -0,0 +1,226 @@
+###########################################################################
+#
+# Makefile -- src/recpt
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+COLSERVR_DIR = $(GSDL_DIR)/src/colservr
+PROTOCOL_DIR = $(GSDL_DIR)/src/protocol
+Z3950_DIR = $(GSDL_DIR)/src/z3950
+
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+ACCENTFOLD_LIBS = $(INDEXERS_DIR)/packages/unac/libunac.a 
+else
+ACCENTFOLD_LIBS = 
+endif
+
+USE_FASTCGI = @USE_FASTCGI@
+ifeq ($(USE_FASTCGI), 1)
+FASTCGI_DEFS = -DUSE_FASTCGI
+FASTCGI_INCLUDES = -I$(PACKAGES_DIR)/fcgi/include
+FASTCGI_LIBS = $(PACKAGES_DIR)/fcgi/libfcgi/libfcgi.a
+else
+FASTCGI_DEFS =
+FASTCGI_INCLUDES =
+FASTCGI_LIBS = 
+endif
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_LIBS = $(PACKAGES_DIR)/sqlite/lib/libsqlite3.a -ldl -lpthread
+else
+SQLITE_LIBS = 
+endif
+
+USE_Z3950 = @USE_Z3950@
+ifeq ($(USE_Z3950), 1)
+Z3950_DEFS = -DUSE_Z3950
+Z3950_INCLUDES = -I$(Z3950_DIR)
+Z3950_LIBS = $(Z3950_DIR)/gsdlz3950.a $(PACKAGES_DIR)/yaz/lib/libyaz.a
+else
+Z3950_DEFS =
+Z3950_INCLUDES = 
+Z3950_LIBS = 
+endif
+
+
+AR = ar
+CC = @CC@
+CFLAGS=@CFLAGS@ @COMPAT32BITFLAGS@
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@ @COMPAT32BITFLAGS@
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DHAVE_CONFIG_H $(FASTCGI_DEFS) $(Z3950_DEFS)
+INCLUDES = @GDBM_INCLUDE@ -I. -I$(GSDL_DIR) -I$(GSDL_DIR)/src/lib \
+        -I$(COLSERVR_DIR) -I$(PROTOCOL_DIR) \
+	-I$(INDEXERS_DIR)/mg -I$(INDEXERS_DIR)/mg/lib -I$(INDEXERS_DIR)/mgpp/text \
+        -I$(PACKAGES_DIR)/expat/include $(FASTCGI_INCLUDES) $(Z3950_INCLUDES)
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@ @COMPAT32BITFLAGS@
+LIBS = @LIBS@ @GDBM_LIBPATH@ -lgdbm -L$(PACKAGES_DIR)/expat/lib -lexpat $(ACCENTFOLD_LIBS) \
+	$(FASTCGI_LIBS) $(SQLITE_LIBS) $(Z3950_LIBS)
+RANLIB = @RANLIB@
+
+
+CXXCOMPILE = $(CXX) -c $(CXXFLAGS) $(DEFS) $(INCLUDES)
+CXXLINK = $(CXX) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CXXCOMPILE) $< 
+
+
+SOURCES = \
+	action.cpp \
+	argdb.cpp \
+	authenaction.cpp \
+	browseaction.cpp \
+	browseactiontools.cpp \
+	browserclass.cpp \
+	browsetoolsclass.cpp \
+	cgiargs.cpp \
+	cgiutils.cpp \
+	cgiwrapper.cpp \
+	collectoraction.cpp \
+        converter.cpp \
+	datelistbrowserclass.cpp \
+	depositoraction.cpp \
+	documentaction.cpp \
+	dynamicclassifieraction.cpp \
+	extlinkaction.cpp \
+	formattools.cpp \
+	gtiaction.cpp \
+	highlighttext.cpp \
+	historydb.cpp \
+	hlistbrowserclass.cpp \
+	htmlbrowserclass.cpp \
+	htmlutils.cpp \
+	invbrowserclass.cpp \
+	librarymain.cpp \
+	maincfg.cpp \
+	pageaction.cpp \
+	pagedbrowserclass.cpp \
+	phindaction.cpp \
+	phindbrowserclass.cpp \
+	pingaction.cpp \
+	queryaction.cpp \
+	querytools.cpp \
+	receptionist.cpp \
+	statusaction.cpp \
+	summarise.cpp \
+	tipaction.cpp \
+	userdb.cpp \
+	usersaction.cpp \
+	vlistbrowserclass.cpp \
+	wizardaction.cpp
+
+OBJECTS = \
+	action.o \
+	argdb.o \
+	authenaction.o \
+	browseaction.o \
+	browseactiontools.o \
+	browserclass.o \
+	browsetoolsclass.o \
+	cgiargs.o \
+	cgiutils.o \
+	cgiwrapper.o \
+	collectoraction.o \
+        converter.o \
+	datelistbrowserclass.o \
+	depositoraction.o \
+	documentaction.o \
+	dynamicclassifieraction.o \
+	extlinkaction.o \
+	formattools.o \
+	gtiaction.o \
+	highlighttext.o \
+	historydb.o \
+	hlistbrowserclass.o \
+	htmlbrowserclass.o \
+	htmlutils.o \
+	invbrowserclass.o \
+	librarymain.o \
+	maincfg.o \
+	pageaction.o \
+	pagedbrowserclass.o \
+	phindaction.o \
+	phindbrowserclass.o \
+	pingaction.o \
+	queryaction.o \
+	querytools.o \
+	receptionist.o \
+	statusaction.o \
+	summarise.o \
+	tipaction.o \
+	userdb.o \
+	usersaction.o \
+	vlistbrowserclass.o \
+	wizardaction.o
+
+LIBRARY = gsdlrecpt.a
+
+GSDL_LIBS = \
+	$(PROTOCOL_DIR)/gsdlprotocol.a \
+	$(COLSERVR_DIR)/gsdlcolservr.a \
+	$(INDEXERS_DIR)/mg/src/text/libmgtext.a \
+	$(INDEXERS_DIR)/mg/lib/libmglib.a \
+	$(INDEXERS_DIR)/mgpp/text/libmgpptext.a \
+	$(INDEXERS_DIR)/mgpp/lib/libmgpplib.a \
+	$(GSDL_DIR)/src/lib/gsdllib.a
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = library
+
+
+# Default target: make both gsdlrecpt.a and the library executable
+all: $(LIBRARY) $(EXECUTABLE)
+
+gsdlrecpt.a: $(OBJECTS)
+	rm -f $(LIBRARY)
+	$(AR) cru $(LIBRARY) $(OBJECTS)
+	$(RANLIB) $(LIBRARY)
+
+library: $(EXECUTABLE_OBJECTS) $(GSDL_LIBS)
+	$(CXXLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/cgi-bin
+
+clean:
+	rm -f $(OBJECTS) $(LIBRARY) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(LIBRARY) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CXXFLAGS) -- $(SOURCES)
Index: /gsdl/trunk/gs2runtime/src/recpt/TODO
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/TODO	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/TODO	(revision 16561)
@@ -0,0 +1,57 @@
+For Z 39.50 Stuff
+
+pageaction.cpp
+--------------
+Macros:
+ * for "about" page, use a custom _textbrowseoptions_ macro (ie
+   create a new macro, maybe create a z3950.dm macro file ??)
+   (also maybe _numbrowseoptions_
+
+(Irrelevant?) (c. line 332)
+    disp.setmacro ("numdocs", "Global", cinfo.numDocs);
+    disp.setmacro ("builddate", "Global", builddate);
+
+That message "This collection contains..." is in the _collectionextra_
+macro.
+could get numdocs from the z39.50 server, as well as any other status
+information.
+
+win32.mak
+---------
+Needs updating for all added files (z*, parse.fl)
+
+yaz
+---
+Maybe put libyaz.a in $GSDLHOME/lib ?
+
+
+DESIGN ISSUES
+for z39.50 (and eventually nullproto when corba allows recept. and
+coll server to be physically separate), if the server is unavailable,
+should this be determined at:
+  * "home page" creation time, in which case the recept. must ping and
+test each collection it knows about, which may cause unacceptable delay, or
+  * "about page" creation time (ie individual collection), in which case
+the collection can not customise the page layout by modifying macros.
+
+Eg currently, the z39.50 collection will not create a TCP connection to the
+"target" (ie z39.50 server somewhere around the world) until it tries to
+get the "About this collection" information for that collection. 
+If the connection fails, the protocol can not change _queryform_ part of
+the page.
+
+
+MULTI-LINGUAL SUPPORT
+Multi-lingual - eg macros - want to set _collectionextra_ using
+metadata["collectionextra"] based on the chosen interface language.
+Should be done by OIDtools.cpp:get_info() and any action that calls
+proto->filter().
+queryaction.cpp (c. line 1097):
+  // add the requested language as an option to the query
+  if (!args["l"].empty()) {
+    OptionValue_t option;
+    option.name="Language";
+    option.value=args["l"];
+    request.filterOptions.push_back(option);
+  }
+
Index: /gsdl/trunk/gs2runtime/src/recpt/action.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/action.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/action.cpp	(revision 16561)
@@ -0,0 +1,161 @@
+/**********************************************************************
+ *
+ * action.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.
+ *
+ *********************************************************************/
+
+#include "fileutil.h"
+
+#include "action.h"
+#include <assert.h>
+
+
+// define all the macros which are related to pages generated
+// by this action
+void action::define_internal_macros (displayclass &/*disp*/, cgiargsclass &/*args*/, 
+				     recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+}
+
+action::action () {
+}
+
+action::~action () {
+}
+
+// configure should be called once for each configuration line
+void action::configure (const text_t &key, const text_tarray &cfgline) {
+  if (key == "gsdlhome") {
+    gsdlhome = cfgline[0];
+    if (collecthome.empty()) collecthome = filename_cat(gsdlhome,"collect");
+    if (dbhome.empty()) dbhome = cfgline[0];
+  }
+  if (key == "collecthome") {collecthome = cfgline[0];}
+  if (key == "gdbmhome") {dbhome = cfgline[0];}
+}
+
+// init should be called after all the configuration is done but
+// before any other methods are called
+bool action::init (ostream &/*logout*/) {
+  return true;
+}
+
+// returns the "a" argument value that will specify this action
+// this name should be short but does not have to be one character
+// long
+text_t action::get_action_name () {
+  return "nzdl";
+}
+
+// check_cgiargs should be called before get_cgihead_info, 
+// define_external_macros, and do_action. If an error is found
+// a message will be written to logout, if the error is severe
+// then the function will return false and no page content
+// should be produced based on the arguments.
+bool action::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &/*args*/, 
+			    recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+  return true;
+}
+
+// check_external_cgiargs should be called after check_cgiargs
+// for all actions. It should only be used to override some other
+// normal behaviour, for example, producing a login page when
+// the requested page needs authentication.
+bool action::check_external_cgiargs (cgiargsinfoclass &/*argsinfo*/,
+				     cgiargsclass &/*args*/,
+				     outconvertclass &/*outconvert*/,
+				     const text_t &/*saveconf*/,
+				     ostream &/*logout*/) {
+  return true;
+}
+
+// get_cgihead_info determines the cgi header information for
+// a set of cgi arguments. If response contains location then
+// response_data contains the redirect address. If reponse
+// contains content then reponse_data contains the content-type.
+// Note that images can now be produced by the receptionist.
+void action::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+			       response_t &response, text_t &response_data, 
+			       ostream &/*logout*/) {
+  response = location;
+  response_data = "http://www.nzdl.org";
+}
+  
+// uses_display should return true if the receptionist should return
+// true if the display class is needed to output the page content
+// The default is to return true.
+bool action::uses_display (cgiargsclass &/*args*/) {
+  return true;
+}
+
+
+// define all the macros which might be used by other actions
+// to produce pages. These macros should be well documented.
+void action::define_external_macros (displayclass &/*disp*/, cgiargsclass &/*args*/, 
+				     recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+}
+
+// returns false if there was an error which prevented the action
+// from outputing anything.
+bool action::do_action (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/, 
+			browsermapclass * /*browsers*/, displayclass &/*disp*/, 
+			outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+			ostream &/*logout*/) {
+  return true;
+}
+
+
+bool operator==(const actionptr &x, const actionptr &y) {
+  return (x.a == y.a);
+}
+
+bool operator<(const actionptr &x, const actionptr &y) {
+  return (x.a < y.a);
+}
+
+
+// theaction remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void actionmapclass::addaction (action *theaction) {
+  // can't add a null action
+  assert (theaction != NULL);
+  if (theaction == NULL) return;
+  
+  // can't add an action with no name
+  assert (!(theaction->get_action_name()).empty());
+  if ((theaction->get_action_name()).empty()) return;
+
+  actionptr aptr;
+  aptr.a = theaction;
+  actionptrs[theaction->get_action_name()] = aptr;
+}
+
+// getaction will return NULL if the action could not be found
+action *actionmapclass::getaction (const text_t &key) {
+  // can't find an action with no name
+  assert (!key.empty());
+  if (key.empty()) return NULL;
+
+  iterator here = actionptrs.find (key);
+  if (here == actionptrs.end()) return NULL;
+  
+  return (*here).second.a;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/action.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/action.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/action.h	(revision 16561)
@@ -0,0 +1,184 @@
+/**********************************************************************
+ *
+ * action.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.
+ *
+ *********************************************************************/
+
+
+#ifndef ACTION_H
+#define ACTION_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+#include "display.h"
+#include "recptproto.h"
+#include "browserclass.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+
+class action {
+protected:
+  cgiargsinfoclass argsinfo;
+  text_t gsdlhome;
+  text_t collecthome;
+  text_t dbhome;
+
+public:
+  action ();
+  virtual ~action ();
+
+  // configure should be called once for each configuration line
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // init should be called after all the configuration is done but
+  // before any other methods are called
+  virtual bool init (ostream &logout);
+
+  // returns the "a" argument value that will specify this action
+  // this name should be short but does not have to be one character
+  // long
+  virtual text_t get_action_name ();
+
+  // check_cgiargs should be called before get_cgihead_info, 
+  // define_external_macros, and do_action. If an error is found
+  // a message will be written to logout, if the error is severe
+  // then the function will return false and no page content
+  // should be produced based on the arguments.
+  virtual bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+			      recptprotolistclass *protos, ostream &logout);
+
+  // check_external_cgiargs should be called after check_cgiargs
+  // for all actions. It should only be used to override some other
+  // normal behaviour, for example, producing a login page when
+  // the requested page needs authentication.
+  virtual bool check_external_cgiargs (cgiargsinfoclass &argsinfo,
+				       cgiargsclass &args,
+				       outconvertclass &outconvert,
+				       const text_t &saveconf,
+				       ostream &logout);
+
+  // get_cgihead_info determines the cgi header information for
+  // a set of cgi arguments. If response contains location then
+  // response_data contains the redirect address. If reponse
+  // contains content then reponse_data contains the content-type.
+  // Note that images can now be produced by the receptionist.
+  virtual void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+				 response_t &response, text_t &response_data, 
+				 ostream &logout);
+  
+  // uses_display should return true if the receptionist should return
+  // true if the display class is needed to output the page content.
+  // The default is to return true.
+  virtual bool uses_display (cgiargsclass &args);
+
+  // define all the macros which are related to pages generated
+  // by this action
+  virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+				       recptprotolistclass *protos, ostream &logout);
+
+  // define all the macros which might be used by other actions
+  // to produce pages. These macros should be well documented.
+  virtual void define_external_macros (displayclass &disp, cgiargsclass &args, 
+				       recptprotolistclass *protos, ostream &logout);
+
+  // returns false if there was an error which prevented the action
+  // from outputing anything.
+  virtual bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			  browsermapclass *browsers, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout);
+
+  // return true if the specified top classifier is handled by this action
+  virtual bool is_my_classifier(const text_t& topcl) { return false; }
+
+  // getargsinfo should be called after all configuration files
+  // have been read
+  cgiargsinfoclass *getargsinfo() {return &argsinfo;};
+};
+
+
+// The actionptr function does not 'own' the action. The 
+// action should be deleted by the code which created it.
+class actionptr {
+public:
+  action *a;
+
+  actionptr () {a=NULL;}
+};
+
+bool operator==(const actionptr &x, const actionptr &y);
+bool operator<(const actionptr &x, const actionptr &y);
+
+typedef map<text_t, actionptr, lttext_t> actionptrmap;
+
+// contains a list of actions indexed by their name
+class actionmapclass {
+protected:
+  actionptrmap actionptrs;
+
+public:
+  // type support for actionptrmap
+  typedef actionptrmap::iterator iterator;
+  typedef actionptrmap::const_iterator const_iterator;
+  typedef actionptrmap::reference reference;
+  typedef actionptrmap::const_reference const_reference;
+  typedef actionptrmap::size_type size_type;
+
+  typedef actionptrmap::difference_type difference_type;
+  typedef actionptrmap::const_reverse_iterator const_reverse_iterator;
+  typedef actionptrmap::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return actionptrs.begin();}
+  const_iterator begin () const {return actionptrs.begin();}
+  iterator end () {return actionptrs.end();}
+  const_iterator end () const {return actionptrs.end();}
+
+  void erase(iterator pos) {actionptrs.erase(pos);}
+  void erase(iterator first, iterator last) {actionptrs.erase(first, last);}
+  actionmapclass &operator=(const actionmapclass &x) {actionptrs=x.actionptrs;return *this;}
+
+  bool empty () const {return actionptrs.empty();}
+  size_type size() const {return actionptrs.size();}
+
+
+  // added functionality
+  void clear () {actionptrs.erase(actionptrs.begin(),actionptrs.end());}
+
+  // theaction remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addaction (action *theaction);
+
+  // getaction will return NULL if the action could not be found
+  action *getaction (const text_t &key);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/argdb.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/argdb.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/argdb.cpp	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * argdb.cpp -- 
+ * Copyright (C) 2001  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_COLLECTOR_ACTION
+
+// argdb is used by the collector for storing cgi arguments on disk (in a database)
+
+#include "argdb.h"
+#include "gdbmclass.h"
+
+argdb::argdb (const text_t &filename) {
+  dbfile = filename;
+}
+  
+bool argdb::update_args (text_tmap &argmap) {
+  gdbmclass argdb;
+  if (!argdb.opendatabase(dbfile, DB_WRITER_CREATE, 1000, true)) return false;
+  infodbclass info;
+  
+  argdb.getinfo("savedargs", info);
+
+  text_tmap::iterator here = argmap.begin();
+  text_tmap::iterator end = argmap.end();
+  while (here != end) {
+    if (!(*here).second.empty()) {
+      info.setinfo((*here).first, (*here).second);
+    }
+    ++here;
+  }
+
+  bool rv = argdb.setinfo ("savedargs", info);
+  argdb.closedatabase();
+  return rv;
+}
+
+bool argdb::get_args (text_tmap &argmap) {
+  argmap.erase(argmap.begin(), argmap.end());
+  gdbmclass argdb;
+  if (!argdb.opendatabase(dbfile, DB_READER, 1000, true)) return false;
+  infodbclass info;
+  
+  if (!argdb.getinfo("savedargs", info)) return false;
+  
+  infodbclass::const_iterator here = info.begin();
+  infodbclass::const_iterator end = info.end();
+  while (here != end) {
+    argmap[(*here).first] = info[(*here).first];
+    ++here;
+  }
+  argdb.closedatabase();
+  return true;
+}
+
+#endif //GSDL_USE_COLLECTOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/argdb.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/argdb.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/argdb.h	(revision 16561)
@@ -0,0 +1,50 @@
+/**********************************************************************
+ *
+ * argdb.h -- 
+ * Copyright (C) 2001  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_COLLECTOR_ACTION
+
+#ifndef ARGDB_H
+#define ARGDB_H
+
+#include "text_t.h"
+
+class argdb {
+
+protected:
+
+  text_t dbfile;
+
+public:
+  
+  argdb (const text_t &filename);
+  virtual ~argdb () {}
+  
+  bool update_args (text_tmap &argmap);
+  bool get_args (text_tmap &argmap);
+};
+
+#endif //!ARGDB_H
+#endif //GSDL_USE_COLLECTOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/authenaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/authenaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/authenaction.cpp	(revision 16561)
@@ -0,0 +1,345 @@
+/**********************************************************************
+ *
+ * authenaction.cpp -- authenticating users
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_AUTHEN_ACTION
+
+#include "authenaction.h"
+#include "fileutil.h"
+#include "cfgread.h"
+#include "cgiutils.h"
+#include "gsdltimes.h"
+
+
+///////////////
+// authenaction
+///////////////
+
+authenaction::authenaction () {
+  keydecay = 1800; // 30 minutes
+  recpt = NULL;
+
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "a";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "us"
+  arg_ainfo.shortname = "us";
+  arg_ainfo.longname = "user account status";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "invalid";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ug"
+  arg_ainfo.shortname = "ug";
+  arg_ainfo.longname = "user groups"; // comma seperated list
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "un"
+  arg_ainfo.shortname = "un";
+  arg_ainfo.longname = "user name";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "pw"
+  arg_ainfo.shortname = "pw";
+  arg_ainfo.longname = "password";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ky" - gives a specific user authentication for a
+  // limited amount of time
+  arg_ainfo.shortname = "ky";
+  arg_ainfo.longname = "user time key";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ua" - ""=no, "1"=yes 
+  arg_ainfo.shortname = "ua";
+  arg_ainfo.longname = "whether a user has been authenticated";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "er" - compressed arguments for the referer page
+  arg_ainfo.shortname = "er";
+  arg_ainfo.longname = "the compressed args of the refer page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "uan" - whether user authentication is needed
+  arg_ainfo.shortname = "uan";
+  arg_ainfo.longname = "whether user authentication is needed";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+void authenaction::configure (const text_t &key, const text_tarray &cfgline) {
+  action::configure (key, cfgline);
+}
+
+bool authenaction::init (ostream &logout) {
+  if (dbhome.empty()) {
+    logout << "ERROR (authenaction::init) dbhome is not set\n";
+    return false;
+  }
+
+  return action::init (logout);
+}
+
+bool authenaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &/*args*/, 
+				  recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+  return true;
+}
+
+// returns false if there is a major problem with the cgi arguments -- not
+// if authentication fails. If the authentication fails "un" will be empty
+bool authenaction::check_external_cgiargs (cgiargsinfoclass &argsinfo,
+					   cgiargsclass &args,
+					   outconvertclass &outconvert,
+					   const text_t &saveconf,
+					   ostream &logout) {
+
+  // no need to go further unless authentication is
+  // required by this page
+  if (args["uan"].empty()) return true;
+
+  // failure means we have to redirect to this action to get authentication
+  // (if we are not already doing this)
+
+  userinfo_t thisuser;
+
+  text_t &args_uan = args["uan"];  text_t &args_un = args["un"];
+  text_t &args_pw = args["pw"];    text_t &args_us = args["us"];
+  text_t &args_ug = args["ug"];    text_t &args_ky = args["ky"];
+  text_t &args_ua = args["ua"];    text_t &args_a = args["a"];
+
+  // we must have a username and a password or a key to
+  // do any authentication
+  args_ua.clear(); // default = false;
+  if (args_un.empty() || args_pw.empty()) args_us = "invalid";
+  else args_us = "failed";
+
+  // make sure we have a username                                                                                                 
+  int status = user_database->get_user_info (args_un, thisuser);
+  if (!args_un.empty() && (status == ERRNO_SUCCEED)) {
+    if (!args_pw.empty()) {
+      // we are authenticating using a password
+      if (user_database->check_passwd (thisuser.username, args_pw) == ERRNO_SUCCEED) args_ua = "1"; // succeeded
+      
+    } else if (!args_ky.empty()) {
+      // we are authenticating using a key
+      if (key_database->check_key(thisuser, args_ky, args_ug, keydecay)) args_ua = "1";
+      else args_us = "stalekey";
+    }
+  }
+
+  args_pw.clear(); // password goes no further
+  if (!args_ua.empty()) {
+    if (thisuser.enabled) {
+      bool haspermission = true;
+
+      // check to make sure the user is in the required group
+      // one group is available only at the moment.
+      // this is what we are changing !
+      
+      if (!args_ug.empty()) {
+
+	 // Since we recieve a comma seperated list
+	 // of groups like mygroup,yourgroup,ourgroup
+	 // we want to split them into individual groups
+	 // and examine them. This is what is done here.
+	 
+	 text_tset splitgrps;
+	 text_t::const_iterator split_here = args_ug.begin();
+	 text_t::const_iterator split_end = args_ug.end();
+
+	 splitchar(split_here,split_end,',',splitgrps);
+
+	 haspermission = false;
+
+	 // This examines the current user to be authenticated and
+	 // tries to see if he or she is in the group that we have in
+	 // thisuser structure. We compare args_ua contents with that
+	 // of the user database.
+	 
+	 text_t::const_iterator group_here = thisuser.groups.begin();
+	 text_t::const_iterator group_end = thisuser.groups.end();
+	 text_t thisgroup;
+	while (group_here != group_end) {
+	   group_here = getdelimitstr (group_here, group_end, ',', thisgroup);
+	   if (splitgrps.find(thisgroup) != splitgrps.end() )
+	      {
+		 haspermission = true;
+		 break;
+	      }
+	}
+      }
+      
+      if (haspermission) {
+	 // succeeded, get info about this user
+	 // note: we don't need to set "ug" as it is already set to what it needs to be
+	 args_us = "enabled";
+	 args_ky = key_database->generate_key(args_un); // new key
+	
+	// delete old keys around every 50 accesses
+	if (rand()%50 == 1) key_database->remove_old_keys(keydecay);
+	
+      } else {
+	// succeeded, however, the user is not in the correct group
+	 args_ua.clear();
+	 args_us = "permissiondenied";
+	 args_ky.clear();
+      }
+      
+    } else {
+       // succeeded, however, the account is disabled
+      args_ua.clear();
+      args_us = "disabled";
+      args_ky.clear();
+    }
+
+  } else {
+    // failure, reset info about the user
+    args_ky.clear();
+  }
+
+  // we will have to redirect the user if authentication is needed,
+  // it failed, and we weren't on our way to be authenticated anyway
+  if ((!args_uan.empty()) && (args_ua.empty()) && (args_a != "a")) {
+    // need to save the current arguments in "er"
+    text_t &arg_er = args["er"];
+    if (!compress_save_args(argsinfo, saveconf, args, arg_er, outconvert, logout))
+      arg_er.clear();
+    
+    // needs to be decoded for use within forms
+    decode_cgi_arg (arg_er);    
+
+    // redirect to this action
+    args_a = "a";
+  }
+
+  return true;
+}
+
+void authenaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				     response_t &response, text_t &response_data, 
+				     ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+void authenaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					   recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+  // sets _authen:messageextra_ based on the value of args["us"]
+  //      _authen:hiddenargs_   to contain all the arguments that were
+  //                            explicitly set
+  disp.setmacro ("messagestatus", "authen", ("_authen:message" + args["us"]
+					     + "_"));
+  // change style of header and footer if page is a frame
+  if ((args["p"].empty()) || (args["p"] == "frameset")) {
+    disp.setmacro ("header", "authen", "_status:infoheader_(Log in)");
+    disp.setmacro ("header", "authenok", "_status:infoheader_(Log in)");
+    disp.setmacro ("footer", "authen", "_status:infofooter_(Log in)");
+    disp.setmacro ("footer", "authenok", "_status:infofooter_(Log in)");
+  }
+
+  // get a list of saved configuration arguments (if possible)
+  text_t saveconf;
+  text_tset saveconfset;
+  if (recpt != NULL) {
+    saveconf = recpt->get_configinfo().saveconf;
+    splitchar (saveconf.begin(), saveconf.end(), '-', saveconfset);
+  }
+  
+  text_t hiddenargs;
+  cgiargsclass::const_iterator args_here = args.begin();
+  cgiargsclass::const_iterator args_end = args.end();
+  while (args_here != args_end) {
+    // set this as a hidden argument if it came from the cgi arguments,
+    // its not the compressed arguments, the query string, a user name or
+    // password, or collect.cfg, and if it is not in the compressed arguments
+    if ((*args_here).second.source == cgiarg_t::cgi_arg &&
+	(*args_here).first != "e" && (*args_here).first != "q" &&
+	(*args_here).first != "un" && (*args_here).first != "pw" &&
+	(*args_here).first != "cfgfile" && 
+	saveconfset.find((*args_here).first) == saveconfset.end()) {
+      hiddenargs += "<input type=hidden name=\"" + (*args_here).first +
+	"\" value=\"_cgiarg" + (*args_here).first + "_\">\n";
+    }
+    ++args_here;
+  }
+
+  disp.setmacro ("hiddenargs", "authen", hiddenargs);
+}
+
+bool authenaction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
+			      browsermapclass * /*browsers*/, displayclass &disp, 
+			      outconvertclass &outconvert, ostream &textout, 
+			      ostream &/*logout*/) {
+  if (args["us"] == "enabled") {
+    // have been authenticated
+    textout << outconvert << disp
+	    << "_authenok:header_\n_authenok:content_\n_authenok:footer_\n";
+    return true;
+  }
+  
+  // need to be authenticated
+  textout << outconvert << disp
+	  << "_authen:header_\n_authen:content_\n_authen:footer_\n";
+
+  return true;
+}
+
+#endif //GSDL_USE_AUTHEN_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/authenaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/authenaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/authenaction.h	(revision 16561)
@@ -0,0 +1,89 @@
+/**********************************************************************
+ *
+ * authenaction.h -- authenticating users
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_AUTHEN_ACTION
+
+#ifndef AUTHENACTION_H
+#define AUTHENACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+#include "text_t.h"
+#include "userdb.h"
+#include "receptionist.h"
+
+
+// note: you need to tell this action about the receptionist
+// using set_receptionist
+
+class authenaction : public action {
+protected:
+  userdbclass *user_database;
+  keydbclass *key_database;
+  int keydecay;
+
+  receptionist *recpt;
+  
+public:
+  authenaction ();
+  virtual ~authenaction () {}
+
+  void set_userdb(userdbclass *udb) {user_database = udb;}
+
+  void set_keydb (keydbclass *kdb) {key_database = kdb;}
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+  
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  bool init (ostream &logout);
+  
+  text_t get_action_name () {return "a";}
+  
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  bool check_external_cgiargs (cgiargsinfoclass &argsinfo,
+			       cgiargsclass &args,
+			       outconvertclass &outconvert,
+			       const text_t &saveconf,
+			       ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+  
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+  
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+};
+
+#endif //!AUTHENACTION_H
+#endif //GSDL_USE_AUTHEN_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/browseaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browseaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browseaction.cpp	(revision 16561)
@@ -0,0 +1,249 @@
+/**********************************************************************
+ *
+ * browseaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_BROWSE_ACTION
+
+#include "browseaction.h"
+#include "querytools.h"
+#include "browseactiontools.h"
+#include "recptprototools.h"
+
+browseaction::browseaction(){
+
+ 
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "br";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //the first browsing criterion -- collection metadata types
+  arg_ainfo.shortname = "b1";
+  arg_ainfo.longname = "browse criterion 1";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+ 
+  //the second browsing criterion -- collection metadata types + none - meaning
+  //only one browsing criterion.  None is the default.
+  arg_ainfo.shortname = "b2";
+  arg_ainfo.longname = "browse criterion 2";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  //the number of documents to show per page -- if the box is empty then
+  //only the most basic divisions are made (so, first letter for A-Z, year 
+  //for date).
+  arg_ainfo.shortname = "bnu";
+  arg_ainfo.longname = "browse docs per page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::good;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //The text to filter documents for browsing (only documents containing these 
+  //words will be browsed).
+  arg_ainfo.shortname = "bft";
+  arg_ainfo.longname = "browse filter text";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+
+  //Whether all or just some of the words in the filter box have to be present
+  //0 - any 1- all
+  arg_ainfo.shortname = "bt";
+  arg_ainfo.longname = "browse all or any";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  
+}
+
+browseaction::~browseaction(){}
+
+bool browseaction::init (ostream &logout){
+  return true;
+}
+
+void browseaction::configure (const text_t &key, const text_tarray &cfgline){
+  action::configure(key, cfgline);
+}
+
+
+
+bool browseaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &/*args*/, 
+				  recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+  // don't want to check anything yet.
+  
+  return true;
+}
+
+  
+void browseaction::get_cgihead_info (cgiargsclass &args, 
+				     recptprotolistclass *protos,
+				     response_t &response, 
+				     text_t &response_data, 
+				     ostream &logout){
+  response = content;
+  response_data = "text/html";
+
+  
+}
+
+void browseaction::define_internal_macros (displayclass &disp, 
+					   cgiargsclass &args, 
+					   recptprotolistclass *protos, 
+					   ostream &logout){
+ 
+}
+
+void browseaction::define_external_macros (displayclass &disp, 
+					   cgiargsclass &args, 
+					   recptprotolistclass *protos, 
+					   ostream &logout){
+  
+}
+
+bool browseaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			      browsermapclass *browsers, displayclass &disp, 
+			      outconvertclass &outconvert, ostream &textout, 
+			      ostream &logout){
+ 
+  if(args["c"].empty()) return false;
+  else if(get_document_list(args, protos, browsers, disp, outconvert, textout,
+			    logout)) return true;
+  else return false;
+}
+
+bool browseaction::get_document_list(cgiargsclass &args, 
+		recptprotolistclass *protos, browsermapclass *browsers, 
+		displayclass &disp, outconvertclass &outconvert, 
+		ostream &textout, ostream &logout){ 
+  
+  text_t collection = args["c"];
+  comerror_t err;
+  text_t list_type = "VList";  //result display type
+
+  //check that the protocol is alive
+  recptproto* colproto = protos->getrecptproto (collection, logout);
+  if(colproto == NULL) {
+    logout << outconvert << "ERROR: Null collection protocol trying to browse "
+	   << collection << "\n";
+    return false;
+  }
+  
+  //check the collection is responding/in place
+  ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(colproto, collection,
+							  logout);
+  if(colinfo == NULL){
+    logout << outconvert << "ERROR: Null returned for get_collectinfo_ptr on "
+	   << collection << "in browseaction\n";
+    return false;
+  }
+
+  browserclass* br_ptr = browsers->getbrowser(list_type);
+
+  FilterRequest_t request;
+  FilterResponse_t response;
+
+  //set up browser structure 
+  br_ptr->set_filter_options(request, args);
+  request.filterLang = args["l"];
+  br_ptr->load_metadata_defaults(request.fields);
+
+  //set up format information
+  format_t* formatptr = new format_t();
+  parse_formatstring(br_ptr->get_default_formatstring(), formatptr, 
+		     request.fields, request.getParents);
+
+  request.filterResultOptions = FROID | FRmetadata;
+  
+  text_t bf_string = g_EmptyText;
+  format_browsestring(args["bft"],bf_string,args.getintarg("bt"));
+  
+  set_browsefilter_options(request, bf_string, args);
+  
+  //set up browse filter string;
+  if(!args["bft"].empty()){
+
+    colproto->filter (collection, request, response, err, logout);
+    if (err != noError) {
+      outconvertclass text_t2ascii;
+      logout << text_t2ascii
+	     << "browseaction::get_document_list QueryFilter failed "
+	     << "for " << collection << " collection (" 
+	     << get_comerror_string (err) << ")\n";
+      return false;
+    }// if (err != noError) 
+
+  }// end if(!args["bft"].empty())
+  else {
+    text_tset metadata_items;
+    text_t temp = "Title";
+    metadata_items.insert(temp);
+    temp = "browselist";
+    if (!get_children(temp, args["c"], args["l"], metadata_items, false, colproto,
+		     response,logout)) return false;
+  }
+
+  //get rid of documents that do not have the metadata element classified on
+  if(!remove_no_meta_results("Title",response, outconvert)) {
+    return false;
+    logout<<"ERROR: browseactiontools::remove_no_meta_results failed"<<endl;
+  }
+
+  textout << outconvert << disp << "_browse:header_\n" <<"_browse:content_";
+
+  
+  bool use_table = is_table_content (formatptr);
+  br_ptr->output_section_group (response, args, collection, 0, formatptr, 
+			      use_table, request.fields, request.getParents, 
+			      colproto, disp, outconvert, textout, logout);
+
+  textout << outconvert << disp << "_browse:footer_";
+    
+  
+  
+  return true;
+}
+
+#endif //GSDL_USE_BROWSE_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/browseaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browseaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browseaction.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * browseaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_BROWSE_ACTION
+
+#ifndef BROWSEACTION_H
+#define BROWSEACTION_H
+
+#include "action.h"
+#include "receptionist.h"
+
+class browseaction : public action{
+ protected:
+  receptionist* recpt;
+  bool get_document_list(cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+ public:
+  browseaction();
+  virtual ~browseaction();
+
+  bool init (ostream &logout);
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  text_t get_action_name() {return "br";}
+  
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+				 response_t &response, text_t &response_data, 
+				 ostream &logout);
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+  
+  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  const cgiargsinfoclass& getargsinfo() const {return argsinfo;};
+};
+
+#endif //!BROWSEACTION_H
+#endif //GSDL_USE_BROWSE_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.cpp	(revision 16561)
@@ -0,0 +1,129 @@
+/**********************************************************************
+ *
+ * browseactiontools.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.
+ *
+ *********************************************************************/
+
+
+#include "browseactiontools.h"
+#include <ctype.h>
+
+void format_browsestring(const text_t& orig, text_t& formatted, int and_or)
+{
+  
+ 
+  text_t conj = g_EmptyText;
+  
+  if(and_or == 0){ conj = " | "; }
+  else { conj = " & "; }
+  text_t::const_iterator here = orig.begin();
+  text_t::const_iterator end = orig.end();
+  
+  //while not at the end of the filter string
+  while(here!=end){
+    //if seeing text, add it to the formatted string
+    if(!isspace(*here)){
+      formatted.push_back(*here);
+      ++here;
+    }
+    //else, if seeing spaces, check that there is following text and add
+    //the ncessary conjugation if there is.
+    else if(isspace(*here)){
+      while(isspace(*here) && here!=end) ++here;
+      if(here!=end) formatted += conj;
+    }
+  }
+
+}
+
+// this stuff is all copied from the queryoptions code - don't ask how it all
+// hangs in with the filters - those things are a mystery 
+void set_browsefilter_options(FilterRequest_t &request, const text_t &browsestring,
+			      cgiargsclass &args)
+{
+  request.filterName = "QueryFilter";
+
+   OptionValue_t option;
+
+   option.name = "Term";
+   option.value = browsestring;
+   request.filterOptions.push_back (option);
+   
+   option.name = "QueryType";
+   option.value = "boolean";
+   request.filterOptions.push_back (option);
+  
+   option.name = "MatchMode";
+   option.value = (args.getintarg("bt")) ? "some" : "all";
+   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);
+
+   option.name = "Maxdocs";
+   option.value = -1;
+   request.filterOptions.push_back (option);
+   
+   option.name = "Index";
+   option.value = "dtx";
+   request.filterOptions.push_back (option);
+     
+   
+}
+
+bool remove_no_meta_results(const text_t& metaname, FilterResponse_t &response, 
+			    outconvertclass &outconvert)
+{
+  
+  ResultDocInfo_tarray::iterator check = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator done = response.docInfo.end();
+  // look through all the returned documents
+  while(check != response.docInfo.end()){
+   
+    //if the document doesn't have the metadata item classified on 
+    //remove it.
+    text_t title = *((*check).metadata.find(metaname)->second).values.begin();
+    if(title == g_EmptyText)
+      {
+	
+	response.docInfo.erase(check);
+	
+      }
+    else {
+     
+      ++check;
+     
+    }
+ 
+  }
+  return true;
+}
+
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browseactiontools.h	(revision 16561)
@@ -0,0 +1,48 @@
+/********************************************************************
+ *
+ * browseactiontools.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.
+ *
+ *********************************************************************/
+
+
+#ifndef BROWSEACTIONTOOLS_H
+#define BROWSEACTIONTOOLS_H
+
+#include "cgiargs.h"
+#include "recptproto.h"
+
+//this puts ands or ors into the querystring as necessary
+void format_browsestring(const text_t& orig, text_t& formatted, int and_or);
+
+//this thing sets up the search associated with a browse.  It's programmed by 
+//witchcraft, so I don't really know how/why it works - I lifted the code from
+//somewhere else.
+void set_browsefilter_options(FilterRequest_t &request, const text_t &browsestring,
+			      cgiargsclass &args);
+
+//this thing filters out all the results of a request to the collection 
+//(whether it be for all documents or just the ones with specified terms)
+//that don't have the required "metaname" metadata element.
+bool remove_no_meta_results(const text_t &metaname, FilterResponse_t &response,
+			    outconvertclass &outconvert);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/browserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browserclass.cpp	(revision 16561)
@@ -0,0 +1,198 @@
+/**********************************************************************
+ *
+ * browserclass.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.
+ *
+ *********************************************************************/
+
+#include "browserclass.h"
+#include <assert.h>
+
+
+browserclass::browserclass () {
+}
+
+browserclass::~browserclass () {
+}
+
+// configure should be called once for each configuration line
+// the default version does nothing
+void browserclass::configure (const text_t &/*key*/, const text_tarray &/*cfgline*/) {
+}
+
+// init should be called after all the configuration is done but
+// before any other methods are called
+bool browserclass::init (ostream &/*logout*/) {
+  return true;
+}
+
+// returns the name that specifies the browserclass type
+text_t browserclass::get_browser_name () {
+  return g_EmptyText;
+}
+
+void browserclass::processOID (cgiargsclass &/*args*/, recptproto * /*collectproto*/, ostream &/*logout*/) {
+}
+
+void browserclass::load_metadata_defaults (text_tset &/*metadata*/) {
+}
+
+text_t browserclass::get_default_formatstring () {
+  return g_EmptyText;
+}
+
+void browserclass::set_filter_options (FilterRequest_t &request, cgiargsclass &args) {
+  
+  OptionValue_t option;
+
+  if (args["a"] == "q") {
+    int arg_m = args.getintarg("m");
+    
+    option.name = "StartResults";
+    option.value = args["r"];
+    request.filterOptions.push_back (option);
+    
+    option.name = "EndResults";
+    int endresults = args.getintarg("o") + (args.getintarg("r") - 1);
+    //    int endresults = (args.getintarg("o")*3) + (args.getintarg("r") - 1);
+    if ((endresults > arg_m) && (arg_m != -1)) endresults = arg_m;
+    option.value = endresults;
+    request.filterOptions.push_back (option);
+
+  } else {
+    option.name = "StartResults";
+    option.value = args["r"];
+    request.filterOptions.push_back (option);
+    
+    option.name = "EndResults";
+    int endresults = 20 + (args.getintarg("r") - 1);
+    option.value = endresults;
+    request.filterOptions.push_back (option);
+  }
+}
+
+int browserclass::output_section_group (ResultDocInfo_t &/*section*/, cgiargsclass &/*args*/,
+					const text_t &/*collection*/, int /*colnumber*/, 
+					format_t * /*formatlistptr*/, bool /*use_table*/, 
+					text_tset &/*metadata*/, bool &/*getParents*/,
+					recptproto * /*collectproto*/, displayclass &/*disp*/, 
+					outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+					ostream &/*logout*/) {
+  return 0;
+}
+
+int browserclass::output_section_group (ResultDocInfo_t &/*section*/, cgiargsclass &/*args*/,
+					const text_t &/*labels*/, const text_t &/*collection*/, int /*colnumber*/, 
+					format_t * /*formatlistptr*/, bool /*use_table*/, 
+					text_tset &/*metadata*/, bool &/*getParents*/,
+					recptproto * /*collectproto*/, displayclass &/*disp*/, 
+					outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+					ostream &/*logout*/) {
+  return 0;
+}
+
+int browserclass::output_section_group (FilterResponse_t &/*sections*/, cgiargsclass &/*args*/,
+					const text_t &/*collection*/, int /*colnumber*/, 
+					format_t * /*formatlistptr*/, bool /*use_table*/, 
+					text_tset &/*metadata*/, bool &/*getParents*/,
+					recptproto * /*collectproto*/, displayclass &/*disp*/, 
+					outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+					ostream &/*logout*/) {
+  return 0;
+}
+
+int browserclass::output_section_group (FilterResponse_t &/*sections*/, cgiargsclass &/*args*/,
+					const text_t& /*currentSection*/, const text_t &/*labels*/, const text_t &/*collection*/, int /*colnumber*/, 
+					format_t * /*formatlistptr*/, bool /*use_table*/, 
+					text_tset &/*metadata*/, bool &/*getParents*/,
+					recptproto * /*collectproto*/, displayclass &/*disp*/, 
+					outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+					ostream &/*logout*/)
+{
+  return 0;
+}
+
+bool operator==(const browserptr &x, const browserptr &y) {
+  return (x.b == y.b);
+}
+
+bool operator<(const browserptr &x, const browserptr &y) {
+  return (x.b < y.b);
+}
+
+// thebrowserclass remains the property of the calling code but
+// should not be deleted until it is removed from this list.
+void browsermapclass::addbrowser (browserclass *thebrowserclass) {
+  // can't add a null browserclass
+  assert (thebrowserclass != NULL);
+  if (thebrowserclass == NULL) return;
+  
+  // can't add a browserclass with no name
+  assert (!(thebrowserclass->get_browser_name()).empty());
+  if ((thebrowserclass->get_browser_name()).empty()) return;
+
+  browserptr aptr;
+  aptr.b = thebrowserclass;
+  browserptrs[thebrowserclass->get_browser_name()] = aptr;
+}
+
+// getbrowser will return the default browser if the browser 
+// could not be found
+browserclass *browsermapclass::getbrowser (const text_t &key) {
+  // can't find a browser with no name
+  if (key.empty()) return get_default_browser();
+
+  iterator here = browserptrs.find (key);
+  if (here == browserptrs.end()) return get_default_browser();
+  
+  return (*here).second.b;
+}
+
+browserclass *browsermapclass::clonebrowser (const text_t &key) {
+  browserclass *browser = NULL;
+  if (key.empty()) {
+    browser = get_default_browser();
+  } else {
+    iterator here = browserptrs.find (key);
+    if (here == browserptrs.end()) 
+      browser = get_default_browser();
+    else
+      browser = here->second.b;
+  }
+  if (browser != NULL) {
+    browser = browser->clone();
+  }
+  return browser;
+}
+
+void browsermapclass::setdefaultbrowser (const text_t &browsername) {
+  defaultbrowser = browsername;
+}
+
+browserclass *browsermapclass::get_default_browser () {
+
+  iterator here = browserptrs.find (defaultbrowser);
+  if (here == browserptrs.end())
+    // we'll just take the first in the list if
+    // no valid default was set
+    here = browserptrs.begin();
+  return (*here).second.b;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/browserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browserclass.h	(revision 16561)
@@ -0,0 +1,187 @@
+/**********************************************************************
+ *
+ * browserclass.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 BROWSERCLASS_H
+#define BROWSERCLASS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+#include "display.h"
+#include "recptproto.h"
+#include "formattools.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+
+class browserclass {
+protected:
+
+public:
+  browserclass ();
+  virtual ~browserclass ();
+
+  // configure should be called once for each configuration line
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+
+  // init should be called after all the configuration is done but
+  // before any other methods are called
+  virtual bool init (ostream &logout);
+
+  virtual browserclass* clone() = 0;
+
+  // returns the name that specifies the browserclass type
+  virtual text_t get_browser_name ();
+  
+  virtual void processOID (cgiargsclass &args, recptproto *collectproto, ostream &logout);
+  
+  // load_metadata_defaults should be called before
+  // doing protocol call to get metadata for OID to
+  // be displayed by this browserclass. it adds fields
+  // to the metadata set that are needed by browserclass
+  // during display
+  virtual void load_metadata_defaults (text_tset &metadata);
+
+  virtual text_t get_default_formatstring ();
+
+  void set_filter_options (FilterRequest_t &request, cgiargsclass &args);
+  
+  // output_section_group returns the number of tabs to add before displaying the
+  // next section group
+  // The first version takes one result of a search/query action, the
+  // second takes all the results; ResultDocInfo_t is actually a child struct
+  // of FilterResponse_t items.  The second version tends to output parts of an
+  // abstract structure (e.g. for a table of contents/classifier list, the first
+  // for an individual document
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &collection, int colnumber, 
+				    format_t *formatlistptr, bool use_table, 
+				    text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, 
+				    ostream &logout);
+
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &labels, const text_t &collection, int colnumber, 
+				    format_t *formatlistptr, bool use_table, 
+				    text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, 
+				    ostream &logout);
+
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t &collection, int colnumber, 
+				    format_t *formatlistptr, bool use_table, 
+				    text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, 
+				    ostream &logout);
+
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t& currentSection, const text_t &labels,
+				    const text_t &collection, int colnumber, 
+				    format_t *formatlistptr, bool use_table, 
+				    text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, 
+				    ostream &logout);
+};
+
+
+// The browserptr function does not 'own' the browser. The 
+// browser should be deleted by the code which created it.
+class browserptr {
+public:
+  browserclass *b;
+
+  browserptr () {b=NULL;}
+};
+
+bool operator==(const browserptr &x, const browserptr &y);
+bool operator<(const browserptr &x, const browserptr &y);
+
+typedef map<text_t, browserptr, lttext_t> browserptrmap;
+
+// contains a list of browsers indexed by their name
+class browsermapclass {
+protected:
+  browserptrmap browserptrs;
+  text_t defaultbrowser;
+
+public:
+  // type support for browserptrmap
+  typedef browserptrmap::iterator iterator;
+  typedef browserptrmap::const_iterator const_iterator;
+  typedef browserptrmap::reference reference;
+  typedef browserptrmap::const_reference const_reference;
+  typedef browserptrmap::size_type size_type;
+
+  typedef browserptrmap::difference_type difference_type;
+  typedef browserptrmap::const_reverse_iterator const_reverse_iterator;
+  typedef browserptrmap::reverse_iterator reverse_iterator;
+  
+  // basic container support
+  iterator begin () {return browserptrs.begin();}
+  const_iterator begin () const {return browserptrs.begin();}
+  iterator end () {return browserptrs.end();}
+  const_iterator end () const {return browserptrs.end();}
+
+  void erase(iterator pos) {browserptrs.erase(pos);}
+  void erase(iterator first, iterator last) {browserptrs.erase(first, last);}
+  browsermapclass &operator=(const browsermapclass &x) {browserptrs=x.browserptrs;return *this;}
+
+  bool empty () const {return browserptrs.empty();}
+  size_type size() const {return browserptrs.size();}
+
+
+  // added functionality
+  void clear () {browserptrs.erase(browserptrs.begin(),browserptrs.end());}
+
+  // thebrowserclass remains the property of the calling code but
+  // should not be deleted until it is removed from this list.
+  void addbrowser (browserclass *thebrowserclass);
+
+  // getbrowser will return NULL if the browser could not be found
+  browserclass *getbrowser (const text_t &key);
+
+  // clone a browser; if not default and browser not found will return NULL
+  browserclass *clonebrowser (const text_t &key);
+
+  void setdefaultbrowser (const text_t &browsername);
+
+  browserclass *get_default_browser ();
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.cpp	(revision 16561)
@@ -0,0 +1,921 @@
+
+/**********************************************************************
+ *
+ * browsetools.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$
+ *
+ *********************************************************************/
+
+#include "browsetoolsclass.h"
+#include "recptprototools.h"
+#include "OIDtools.h"
+#include "gsdlunicode.h"
+
+#if defined(GSDL_USE_IOS_H)
+#  if defined(__WIN32__)
+#    include <strstrea.h> // vc4
+#  else
+#    include <strstream.h>
+#  endif
+#else
+#  include <sstream>
+#endif
+
+
+browsetoolsclass::browsetoolsclass()
+{
+}
+
+browsetoolsclass::~browsetoolsclass()
+{
+}
+
+// output_controls displays the detach, expand/contract contents,
+// expand/contract text and highlighting/no highlighting buttons
+
+void browsetoolsclass::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_tarray metadata;
+    text_tarray buttons;
+    
+    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 == "Print")
+        buttons.push_back ("_document:imageprint_"); 
+      else if (*here == "Highlight") {
+	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") {
+	if (args.getintarg("gt"))
+	  buttons.push_back ("_document:imagecontracttext_");
+	else 
+	  buttons.push_back ("_document:imageexpandtext_");
+      }
+      ++here;
+    }
+    
+    here = buttons.begin();
+    end = buttons.end();
+    while (here != end) {
+      textout << outconvert << disp << *here;
+      ++here;
+    }
+  }
+}
+
+text_t browsetoolsclass::get_cover_image()
+{
+  return "_httpprefix_/collect/[collection]/index/assoc/{Or}{[parent(Top):archivedir],[archivedir]}/cover.jpg";
+}
+
+// at the moment this just writes out the html to display 
+// the cover image (assuming it's called cover.jpg)
+// this whole thing should be done with a call to the collection
+// server which would send a link to the cover image if there
+// was one otherwise send title, author and stuff
+void browsetoolsclass::output_cover_image(cgiargsclass &args, recptproto *collectproto, 
+                                          displayclass &disp, outconvertclass &outconvert, 
+                                          ostream &textout, ostream &logout)
+{
+  if (args["d"].empty()) return;
+  
+  textout << outconvert << disp << "_document:coverimage_";
+}
+
+void browsetoolsclass::output_titles(cgiargsclass &args, recptproto *collectproto, 
+                                     browsermapclass *browsermap, formatinfo_t &formatinfo,
+                                     displayclass &disp, outconvertclass &outconvert,
+                                     ostream &textout, ostream &logout)
+{
+  if (args["d"].empty()) return;
+  
+  text_tset metadata;
+  bool getParents;
+  FilterResponse_t response;
+
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatinfo.DocumentHeading, formatlistptr, metadata, getParents);
+
+  if (!get_info (args["d"], args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+    return;
+
+  text_tmap options;
+  if (formatinfo.AllowExtendedOptions) {
+    load_extended_options(options, args, browsermap, formatinfo, 
+			  collectproto, disp, outconvert, logout);
+  }
+  textout << outconvert << disp 
+	  << get_formatted_string (args["c"],collectproto, response.docInfo[0], disp,
+				   formatlistptr, options, logout);
+}
+
+void browsetoolsclass::recurse_contents(ResultDocInfo_t &section, cgiargsclass &args, bool fulltoc,
+                                        browserclass *bptr, text_tset &metadata, bool &getParents, 
+                                        format_t *formatlistptr, format_tmap &formatlistmap, 
+                                        formatinfo_t &formatinfo, browsermapclass *browsermap, 
+                                        int tabcount, recptproto *collectproto, displayclass &disp, 
+                                        outconvertclass &outconvert, ostream &textout, ostream &logout)
+{
+  text_t formatstring;
+
+  bool is_classify = false;
+  if (args["d"].empty() || fulltoc) is_classify = true;
+
+  // output this section 
+  bool use_table = is_table_content (formatlistptr);
+  tabcount += bptr->output_section_group (section, args, args["c"], tabcount, formatlistptr, use_table, 
+					  metadata, getParents, collectproto, disp, outconvert, 
+					  textout, logout);
+
+  text_t classification;
+  if (!is_classify) classification = "Document";
+  else get_top (args["cl"], classification);
+
+  int haschildren = section.metadata["haschildren"].values[0].getint();
+  const text_t &doctype = section.metadata["doctype"].values[0];
+  text_t classifytype = section.metadata["childtype"].values[0];
+  // HLists and DateLists are displayed as VLists when contents 
+  // are expanded, Paged documents are displayed as HLists
+  if (classifytype == "HList" || classifytype == "DateList") classifytype = "VList";
+  if (classifytype == "Paged") classifytype = "HList";
+
+  // recurse through children
+  if ((haschildren == 1) && (!is_classify || fulltoc || doctype == "classify")) {
+
+    // get browser for displaying children
+    bptr = browsermap->getbrowser (classifytype);
+    bptr->load_metadata_defaults (metadata);
+
+    // get the formatstring if there is one 
+    if (!get_formatstring (classification, classifytype, 
+			   formatinfo.formatstrings, formatstring))
+      formatstring = bptr->get_default_formatstring();
+
+    format_tmap::const_iterator it = formatlistmap.find (formatstring);
+    // check if formatlistptr is cached
+    if (it != formatlistmap.end()) formatlistptr = (*it).second;
+    else {
+      formatlistptr = new format_t();
+      parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+      formatlistmap[formatstring] = formatlistptr;
+    }
+
+    FilterResponse_t tmp;
+    get_children (section.OID, args["c"], args["l"], metadata, getParents, collectproto, tmp, logout);
+    ResultDocInfo_tarray::iterator thisdoc = tmp.docInfo.begin();
+    ResultDocInfo_tarray::iterator lastdoc = tmp.docInfo.end();
+
+    while (thisdoc != lastdoc) {
+      recurse_contents (*thisdoc, args, fulltoc, bptr, metadata, getParents, 
+			formatlistptr, formatlistmap, formatinfo, browsermap, 
+			tabcount, collectproto, disp, outconvert, textout, logout);
+      ++thisdoc;
+    }
+  }
+}
+
+
+// expanded_contents recurses through all contents. there's a special case
+// for an HList when contents are expanded (i.e. it's displayed as a VList)
+//
+// if we're inside a document we expand all contents from the top (unless
+// expand_from_current_level is true), if we're at classification level we'll just
+// expand out those contents below the current one
+
+void browsetoolsclass::expanded_contents(cgiargsclass &args, int tabcount, bool fulltoc,
+                                         browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                         recptproto *collectproto, displayclass &disp, 
+                                         outconvertclass &outconvert, ostream &textout, 
+                                         ostream &logout, bool expand_from_current_level)
+{
+  if (args["d"].empty() && args["cl"].empty()) return;
+  text_t OID;
+  
+  FilterResponse_t response;
+  bool getParents = false;
+  text_tset metadata;
+  text_t classifytype, classification, formatstring;
+
+  if (!args["d"].empty())
+    {
+      // document level
+      if (fulltoc)
+        {
+          get_top(args["cl"], OID);
+          classification = OID;
+        }
+      else 
+        {
+          classification = "Document";
+          if (expand_from_current_level)
+            {
+              OID = args["d"];
+            }
+          else
+            {
+              // Always expand document level from top, unless
+              // expand_from_current_level is true.
+              get_top(args["d"], OID);
+            }
+        }
+    }
+  else
+    {
+      // classification level
+      OID = args["cl"];
+      get_top(args["cl"], classification);
+    }
+
+  // get classifytype of this level
+  text_t tOID;
+  if (is_top(OID)) {
+    classifytype = "thistype";
+    tOID = OID;
+  } else {
+    classifytype = "childtype";
+    tOID = get_parent (OID);
+  }
+  metadata.insert (classifytype);
+
+  if (!get_info (tOID, args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+    return;
+  classifytype = response.docInfo[0].metadata[classifytype].values[0];
+  // if we still don't have a classifytype we'll use the default
+  if (classifytype.empty()) {
+    browserclass *bptr = browsermap->get_default_browser ();
+    classifytype = bptr->get_browser_name ();
+  }
+
+  // HLists are displayed as VLists when contents are expanded,
+  // Paged documents are displayed as HLists
+  if (classifytype == "HList") {
+    classifytype = "VList";
+    text_t pOID = get_parent (OID);
+    if (!pOID.empty()) {
+      OID = pOID;
+      // this is assuming that top levels are always 'Invisible' !!!
+      if (is_top (OID)) classifytype = "Invisible";
+    }
+  }
+  if (classifytype == "Paged") classifytype = "HList";
+
+  metadata.erase (metadata.begin(), metadata.end());
+		  
+  // metadata elements needed by recurse_contents
+  metadata.insert ("childtype");
+  metadata.insert ("doctype");
+  metadata.insert ("haschildren");
+
+  // load up metadata array with browser defaults
+  browserclass *bptr = browsermap->getbrowser (classifytype);
+  bptr->load_metadata_defaults (metadata);
+
+  // get the formatstring if there is one or use the browsers default
+  if (!get_formatstring (classification, classifytype, 
+			 formatinfo.formatstrings, formatstring))
+    formatstring = bptr->get_default_formatstring();
+
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+
+  // protocol call
+  if (!get_info (OID, args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+    return;
+
+  format_tmap formatlistmap;
+  formatlistmap[formatstring] = formatlistptr;
+
+  recurse_contents (response.docInfo[0], args, fulltoc, bptr, metadata, 
+		    getParents, formatlistptr, formatlistmap, formatinfo, browsermap, 
+		    tabcount, collectproto, disp, outconvert, textout, logout);
+
+  // clean up format list pointers
+  format_tmap::const_iterator here = formatlistmap.begin();
+  format_tmap::const_iterator end = formatlistmap.end();
+  while (here != end) {
+    delete (*here).second;
+    ++here;
+  }
+}
+
+void browsetoolsclass::load_formatstring(const text_t &classifytype, text_tset &metadata,
+                                         bool &getParents, const text_t &classification, 
+                                         browsermapclass *browsermap, formatinfo_t &formatinfo,
+                                         format_tmap &formatlistmap)
+{
+  text_t formatstring;
+
+  // load up metadata array with browser defaults
+  browserclass *bptr = browsermap->getbrowser (classifytype);
+  bptr->load_metadata_defaults (metadata);
+
+  // get the formatstring if there is one or use the browsers default
+  if (!get_formatstring (classification, classifytype, 
+			 formatinfo.formatstrings, formatstring))
+    formatstring = bptr->get_default_formatstring();
+
+  // see if it's cached
+  format_tmap::const_iterator it = formatlistmap.find (formatstring);
+  if (it == formatlistmap.end()) {
+    format_t *formatlistptr = new format_t();
+    parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+    formatlistmap[formatstring] = formatlistptr;
+  }
+}
+
+void browsetoolsclass::load_formatstrings(FilterResponse_t &response, text_tset &metadata,
+                                          bool &getParents, const text_t &classification,
+                                          browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                          format_tmap &formatlistmap)
+{
+  text_tset cache;
+
+  ResultDocInfo_tarray::iterator thisdoc = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator lastdoc = response.docInfo.end();
+
+  while (thisdoc != lastdoc) {
+
+    if (is_top ((*thisdoc).OID)) {
+      load_formatstring ((*thisdoc).metadata["thistype"].values[0], metadata, 
+			 getParents, classification, browsermap, formatinfo, 
+			 formatlistmap);
+    }
+    
+    // sometimes childtype is not set - why??
+    if ((*thisdoc).metadata.find("childtype") != (*thisdoc).metadata.end()) {
+      text_t &childtype = (*thisdoc).metadata["childtype"].values[0];
+
+      text_tset::const_iterator it = cache.find (childtype);
+      if (it == cache.end()) {
+	load_formatstring (childtype, metadata, getParents, classification,
+			   browsermap, formatinfo, formatlistmap);
+	cache.insert (childtype);
+      }
+    }
+    ++thisdoc;
+  }
+}
+
+void browsetoolsclass::output_parents(FilterResponse_t &response, cgiargsclass &args, 
+                                      browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                      format_tmap &formatlistmap, const text_t &classification,
+                                      int &tabcount, text_tset &metadata, bool &getParents,
+                                      recptproto *collectproto, displayclass &disp, 
+                                      outconvertclass &outconvert, ostream &textout, 
+                                      ostream &logout)
+{
+  format_t *formatlistptr = NULL;
+  text_t classifytype, formatstring;
+  bool use_table, first = true;
+  ResultDocInfo_tarray::iterator thisparent = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator lastparent = response.docInfo.end();
+  while (thisparent != lastparent) {
+
+    // get classifytype of this level
+    if (is_top ((*thisparent).OID)) classifytype = (*thisparent).metadata["thistype"].values[0];
+    else if (!first && (*(thisparent-1)).metadata.find("childtype") != (*(thisparent-1)).metadata.end()) classifytype = (*(thisparent-1)).metadata["childtype"].values[0];
+    
+    // if we still don't have a classifytype we'll use the default
+    if (classifytype.empty()) {
+      browserclass *bptr = browsermap->get_default_browser ();
+      classifytype = bptr->get_browser_name ();
+    }
+    
+    browserclass *bptr = browsermap->getbrowser (classifytype);
+
+    // get the formatstring if there is one or use the browsers default
+    if (!get_formatstring (classification, classifytype, 
+			   formatinfo.formatstrings, formatstring))
+      formatstring = bptr->get_default_formatstring();
+    
+    // see if it's cached
+    format_tmap::const_iterator it = formatlistmap.find (formatstring);
+    if (it != formatlistmap.end()) formatlistptr = (*it).second;
+    else {
+      logout << "browsetools error\n";
+      return;
+    }
+    
+    use_table = is_table_content (formatlistptr);
+    tabcount += bptr->output_section_group (*thisparent, args, args["c"], tabcount, formatlistptr, 
+					    use_table, metadata, getParents, collectproto, 
+					    disp, outconvert, textout, logout);
+    first = false;
+    ++thisparent;
+  }
+}
+
+void browsetoolsclass::contracted_contents(cgiargsclass &args, int tabcount, bool fulltoc, 
+                                           browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                           recptproto *collectproto, displayclass &disp, 
+                                           outconvertclass &outconvert, ostream &textout, 
+                                           ostream &logout)
+{
+  FilterResponse_t response;
+  text_tset metadata;
+  bool getParents = false;
+  text_t formatstring;
+  text_tarray parents;
+  text_t OID = args["d"];
+  text_t classification = "Document";
+
+  // if we're not outputting the TOC for a valid OID, then we should be giving
+  // the TOC of a classification
+  if (OID.empty()) {
+    OID = args["cl"];
+    get_top (OID, classification);
+  } 
+  // if we're to give the full TOC of a document, get the parent for the whole
+  // document now
+  else if (fulltoc)
+    get_top (args["cl"], classification);    
+
+  bool haschildren = has_children (OID, args["c"], args["l"], collectproto, logout);
+
+  if ((!args["d"].empty()) && fulltoc)
+    get_parents_array (args["cl"] + ".fc", parents);
+  if (haschildren) get_parents_array (OID + ".fc", parents);
+  else get_parents_array (OID, parents);
+
+  if (!parents.empty()) {
+    // get classifytypes of each parent
+    metadata.insert ("thistype");
+    metadata.insert ("childtype");
+    metadata.insert("mdtype");
+    
+    if (!get_info (parents, args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+      return;
+    
+    // get formatstrings for all parents
+    format_tmap formatlistmap;  
+    load_formatstrings (response, metadata, getParents, classification, 
+			browsermap, formatinfo, formatlistmap);
+
+    if (!get_info (parents, args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+      return;
+    
+    // display each parent
+    output_parents (response, args, browsermap, formatinfo, formatlistmap, 
+		    classification, tabcount, metadata, getParents, 
+		    collectproto, disp, outconvert, textout, logout);
+    
+    metadata.erase (metadata.begin(), metadata.end());
+    
+    // clean up cached format list pointers
+    format_tmap::const_iterator here = formatlistmap.begin();
+    format_tmap::const_iterator end = formatlistmap.end();
+    while (here != end) {
+      delete (*here).second;
+      ++here;
+    }
+  }
+
+  // get childrens classifytype
+  text_t classifytype;
+  int numparents = response.docInfo.size();
+  if (!parents.empty() && (response.docInfo[numparents-1].metadata.find("childtype") != response.docInfo[numparents-1].metadata.end()))
+    classifytype = response.docInfo[numparents-1].metadata["childtype"].values[0];
+  else {
+    // use the default
+    browserclass *bptr = browsermap->get_default_browser ();
+    classifytype = bptr->get_browser_name ();
+  }
+    
+  // load up metadata array with browser defaults
+  browserclass *bptr = browsermap->getbrowser (classifytype);
+  bptr->load_metadata_defaults (metadata);
+
+  if (classifytype == "DateList") {
+    // get the mdtype
+    text_t datelist = "Date";
+    if (!parents.empty() && (response.docInfo[0].metadata.find("mdtype") != response.docInfo[0].metadata.end())) {
+      datelist = response.docInfo[0].metadata["mdtype"].values[0];
+    }
+    args.setarg("dm", datelist);
+    text_tarray dates;
+    splitchar(datelist.begin(), datelist.end(), ',', dates);
+    text_tarray::iterator begin = dates.begin();
+    text_tarray::iterator end = dates.end();
+    while (begin!= end) {
+      metadata.insert(*begin);
+      begin++;
+    }
+    
+  }
+
+  // get the formatstring if there is one or use the browsers default
+  if (!get_formatstring (classification, classifytype, 
+			 formatinfo.formatstrings, formatstring))
+    formatstring = bptr->get_default_formatstring();
+
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+
+  if (haschildren) 
+    get_children (OID, args["c"], args["l"], metadata, getParents,
+		  collectproto, response, logout);
+  else if (!is_top(OID)) {
+    get_children (OID + ".pr", args["c"], args["l"], metadata, getParents, 
+		  collectproto, response, logout);
+    haschildren = true;
+  }
+
+  // display children
+  if (haschildren) {
+    bool use_table = is_table_content (formatlistptr);
+    bptr->output_section_group (response, args, args["c"], tabcount, formatlistptr, use_table, 
+				metadata, getParents, collectproto, disp, outconvert, 
+				textout, logout);
+  }
+  delete formatlistptr;
+}
+
+/**
+ * This function outputs contents of a classifier to the reader
+ * including parent levels
+ */
+void browsetoolsclass::expand_show_contents(cgiargsclass &args, int tabcount, bool fulltoc,
+                                            browsermapclass *browsermap, formatinfo_t &formatinfo,
+                                            recptproto *collectproto, displayclass &disp,
+                                            outconvertclass &outconvert, ostream &textout, 
+                                            ostream &logout)
+{
+  int coloffset = 0;
+  text_tarray parents;
+  FilterResponse_t response;
+  text_t OID = args["d"];
+  if (OID.empty()) OID = args["cl"];
+
+  bool haschildren = has_children(OID, args["c"], args["l"], collectproto, logout);
+
+  // get parents list
+  if (!is_top(OID)) get_parents_array(OID, parents);
+  if (args["d"].empty() || haschildren || parents.empty()) parents.push_back(OID);
+
+  // if inside a book top title is needed
+  if (!args["d"].empty())
+    {
+      coloffset = 1;
+      text_t classification, classifytype, formatstring, topOID = parents[0];
+      text_tset metadata;
+      format_t *formatlistptr = new format_t();
+      bool use_table, getParents = false;
+		
+      get_top(args["cl"], classification);
+
+      // getting information about top OID
+      metadata.insert("thistype");
+
+      get_info(topOID, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+      if (!response.docInfo[0].metadata["thistype"].values.empty()) 
+        {
+          classifytype = response.docInfo[0].metadata["thistype"].values[0];
+        }
+
+      browserclass *bptr = browsermap->getbrowser (classifytype);
+
+      if (classifytype.empty())
+        {
+          bptr = browsermap->get_default_browser();
+        }
+
+      // get the formatstring if there is one or use the browsers default
+      if (!get_formatstring(classification, classifytype, formatinfo.formatstrings, formatstring))
+        {
+          formatstring = bptr->get_default_formatstring();
+        }
+
+      parse_formatstring(formatstring, formatlistptr, metadata, getParents);
+
+      metadata.insert("doctype");
+      metadata.insert("haschildren");
+
+      get_info(topOID, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+
+      // ouput top OID section
+      use_table = is_table_content(formatlistptr);
+      bptr->output_section_group(response.docInfo[0], args, args["c"], tabcount, formatlistptr, use_table,
+                                 metadata, getParents, collectproto, disp, outconvert, textout, logout);
+    }
+
+  recurse_contents_levels(parents, args, coloffset, tabcount, fulltoc, browsermap, 
+                          formatinfo, collectproto, disp, outconvert, textout, logout);
+}
+
+void browsetoolsclass::recurse_contents_levels(text_tarray &parents, cgiargsclass &args, int coloffset, int tabcount,
+                                               bool fulltoc, browsermapclass *browsermap, formatinfo_t &formatinfo,
+                                               recptproto *collectproto, displayclass &disp, outconvertclass &outconvert,
+                                               ostream &textout, ostream &logout)
+{
+  FilterResponse_t response;
+  text_tset metadata;
+  text_t OID, formatstring, classification, classifytype;;
+  bool use_table, getParents = false;
+  int haschildren = 0;
+  format_t *formatlistptr = new format_t();
+
+  // display children for last level
+  if (tabcount == (parents.size() - 1))
+    {
+      // setting metadata fields 
+      metadata.insert ("thistype");
+      metadata.insert ("childtype");
+      metadata.insert ("haschildren");
+
+      OID = parents[tabcount];
+      get_info(OID, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+      get_top(OID, classification);
+
+      if (!response.docInfo.empty())
+        {
+          haschildren = response.docInfo[0].metadata["haschildren"].values[0].getint();
+        }
+
+      // get childrens classifytype
+      if (!response.docInfo.empty())
+        {
+          classifytype = response.docInfo[0].metadata["childtype"].values[0];
+        }
+      else
+        {
+          // use the default
+          browserclass *bptr = browsermap->get_default_browser();
+          classifytype = bptr->get_browser_name();
+        }
+
+      // load up metadata array with browser defaults
+      browserclass *bptr = browsermap->getbrowser(classifytype);
+      bptr->load_metadata_defaults(metadata);
+
+      // get the formatstring if there is one or use the browsers default
+      if (!get_formatstring(classification, classifytype, formatinfo.formatstrings, formatstring))
+        {
+          formatstring = bptr->get_default_formatstring();
+        }
+
+      parse_formatstring(formatstring, formatlistptr, metadata, getParents);
+
+      if (haschildren) 
+        {
+          get_children(OID, args["c"], args["l"], metadata, getParents,
+                       collectproto, response, logout);
+        }
+      else if (!is_top(OID))
+        {
+          get_children(OID + ".pr", args["c"], args["l"], metadata, getParents, 
+                       collectproto, response, logout);
+          haschildren = true;
+        }
+
+      // display children
+      if (haschildren)
+        {
+          use_table = is_table_content (formatlistptr);
+          bptr->output_section_group(response, args, args["c"], coloffset + tabcount, formatlistptr, use_table, 
+                                     metadata, getParents, collectproto, disp, outconvert, textout, logout);
+        }
+    }
+  else
+    {
+      text_t pOID;
+      OID = parents[tabcount];
+      get_top(OID, classification);
+
+      // load metadata fields
+      metadata.insert("thistype");
+      metadata.insert("childtype");
+      metadata.insert("haschildren");
+      metadata.insert("doctype");
+
+      if (tabcount) pOID = parents[tabcount-1];
+      else pOID = OID;
+      get_info(pOID, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+
+      // get classifytype of this level
+      if (is_top (pOID)) classifytype = response.docInfo[0].metadata["childtype"].values[0];
+      else classifytype = response.docInfo[0].metadata["thistype"].values[0];
+
+      // if we still don't have a classifytype we'll use the default
+      if (classifytype.empty())
+        {
+          browserclass *bptr = browsermap->get_default_browser();
+          classifytype = bptr->get_browser_name();
+        }
+
+      browserclass *bptr = browsermap->getbrowser (classifytype);
+
+      // get the formatstring if there is one or use the browsers default
+      if (!get_formatstring (classification, classifytype, formatinfo.formatstrings, formatstring))
+        {
+          formatstring = bptr->get_default_formatstring();
+        }
+
+      // parse format string 
+      parse_formatstring(formatstring, formatlistptr, metadata, getParents);
+
+      use_table = is_table_content(formatlistptr);
+
+      get_children(OID, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+
+      ResultDocInfo_tarray::iterator thissibling = response.docInfo.begin();
+      ResultDocInfo_tarray::iterator lastsibling = response.docInfo.end();
+
+      while (thissibling != lastsibling)
+        {
+          bptr->output_section_group(*thissibling, args, args["c"], coloffset + tabcount, formatlistptr, 
+                                     use_table, metadata, getParents, collectproto, 
+                                     disp, outconvert, textout, logout);
+			
+          if ((*thissibling).OID == parents[tabcount+1])
+            {
+              recurse_contents_levels(parents, args, coloffset, tabcount+1, fulltoc, browsermap,
+                                      formatinfo, collectproto, disp, outconvert,textout, logout);
+            }
+          ++thissibling;
+        }
+    }
+  delete formatlistptr;
+}
+
+/**
+ * This function outputs the contents of a classifier list to the reader - 
+ * the document will in fact be empty, so this does the "real" output
+ */
+void browsetoolsclass::output_toc(cgiargsclass &args, browsermapclass *browsermap,
+                                  formatinfo_t &formatinfo, recptproto *collectproto,
+                                  displayclass &disp, outconvertclass &outconvert,
+                                  ostream &textout, ostream &logout)
+{
+  if (!args["d"].empty() && formatinfo.AllowExtendedOptions) {
+    // If AllowExtendedOptions is set and we're viewing a document,
+    // DocumentButtons, DocumentContent, and DocumentImages are effectively
+    // disabled. We just output the DocumentHeading format string and
+    // return
+    output_titles(args, collectproto, browsermap, formatinfo, disp, outconvert, textout, logout);    
+    return;
+  }
+
+  int tabcount = 0;
+  bool fulltoc = false;
+  text_t cl_top, full_toc;
+  if (args["cl"] != "search") {
+    // see if there's a FullTOC string
+    get_top (args["cl"], cl_top);
+    if (get_formatstring (cl_top, "FullTOC", formatinfo.formatstrings, full_toc))
+      if (full_toc == "true") fulltoc = true;
+  }
+  
+  // get the cover image (if there is one) and the control buttons
+  // if we're inside a book
+  if ((!fulltoc) && (!args["d"].empty())) {
+    if (formatinfo.DocumentImages) {
+      textout << outconvert << "<div class=\"heading_image\">\n";
+      output_cover_image(args, collectproto, disp, outconvert, textout, logout);
+      textout << outconvert << "</div>\n";
+    } else if (formatinfo.DocumentTitles) {
+      textout << outconvert << "<div class=\"heading_title\">\n";
+      output_titles(args, collectproto, browsermap, formatinfo, disp, outconvert, textout, logout);
+      textout << outconvert << "</div>\n";
+    }
+    textout << outconvert << "<div class=\"buttons\" id=\"toc_buttons\">\n"; 
+    if (args["u"] != "1") {
+      output_controls (args, formatinfo.DocumentButtons, collectproto, disp, 
+		       outconvert, textout, logout);
+    }
+    textout << outconvert << "</div>\n\n"; 
+  }
+
+  if (formatinfo.DocumentContents || args["d"].empty()) {
+    if (args["d"].empty()) {
+      disp.setmacro("tocopenextra", "document", "id=\"" + cl_top + "\"");
+    } else {
+      disp.setmacro("tocopenextra", "document", "");
+    }
+
+    textout << outconvert << disp << "_document:tocopen_\n";
+    if (args.getintarg("gc") == 1)
+      {
+        // expanded table of contents
+        expanded_contents(args, tabcount, fulltoc, browsermap, formatinfo, 
+                          collectproto, disp, outconvert, textout, logout, false);
+      }
+    else if (args.getintarg("gc") == 2)
+      {
+        // expand visible levels of table of contents
+        expand_show_contents(args, tabcount, fulltoc, browsermap, formatinfo,
+                             collectproto, disp, outconvert, textout, logout);
+      }
+    else
+      {
+        // contracted table of contents
+        contracted_contents(args, tabcount, fulltoc, browsermap, formatinfo, 
+                            collectproto, disp, outconvert, textout, logout);
+      }
+    textout << outconvert << disp << "_document:tocclose_\n";
+  }
+}
+
+void browsetoolsclass::load_extended_options(text_tmap &options, cgiargsclass &args, browsermapclass *browsers, 
+                                             formatinfo_t &formatinfo, recptproto *collectproto, 
+                                             displayclass &disp, outconvertclass &outconvert, ostream &logout)
+{
+  options["DocImage"] = get_cover_image();
+
+#if defined(GSDL_USE_IOS_H)
+  ostrstream *tmpstr = new ostrstream();
+#else
+  ostringstream *tmpstr = new ostringstream(ostringstream::binary);
+#endif
+
+  if (args["gc"] == "1")
+    {
+      expanded_contents(args, 0, false, browsers, formatinfo, collectproto,
+                        disp, outconvert, *tmpstr, logout, false);
+    }
+  else
+    {
+      contracted_contents(args, 0, false, browsers, formatinfo, 
+                          collectproto, disp, outconvert, *tmpstr, logout);
+    }
+#if defined(GSDL_USE_IOS_H)
+  char *t = tmpstr->str();
+  text_t tmp;
+  tmp.setcarr(t, tmpstr->pcount());
+  delete [] t;
+#else
+  text_t tmp = (char *)(tmpstr->str().c_str());
+#endif
+  int len = tmp.size();
+  char *ctmp = tmp.getcstr();
+  utf8inconvertclass utf82text_t;
+  utf82text_t.setinput(ctmp, len);
+  convertclass::status_t status;
+  utf82text_t.convert(tmp, status);
+  options["DocTOC"] = tmp;
+  delete []ctmp;
+  delete tmpstr;
+
+  options["DocumentButtonDetach"] = "_document:imagedetach_";
+  if (args["hl"] == "1")
+    {
+      options["DocumentButtonHighlight"] = "_document:imagenohighlight_";
+    }
+  else
+    {
+      options["DocumentButtonHighlight"] = "_document:imagehighlight_";
+    }
+  if (args["gc"] == "1")
+    {
+      options["DocumentButtonExpandContents"] = "_document:imagecontracttoc_";
+    }
+  else
+    {
+      options["DocumentButtonExpandContents"] = "_document:imageexpandtoc_";
+    }
+  if (args["gt"] == "1")
+    {
+      options["DocumentButtonExpandText"] = "_document:imagecontracttext_";
+    }
+  else
+    {
+      options["DocumentButtonExpandText"] = "_document:imageexpandtext_";
+    }
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/browsetoolsclass.h	(revision 16561)
@@ -0,0 +1,124 @@
+/**********************************************************************
+ *
+ * browsetools.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.
+ *
+ *********************************************************************/
+
+
+#ifndef BROWSETOOLSCLASS_H
+#define BROWSETOOLSCLASS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+#include "display.h"
+#include "recptproto.h"
+#include "formattools.h"
+#include "browserclass.h"
+
+class browsetoolsclass
+{
+public:
+  
+  browsetoolsclass();
+  virtual ~browsetoolsclass();
+
+  virtual void output_toc(cgiargsclass &args, browsermapclass *browsermap,
+                          formatinfo_t &formatinfo, recptproto *collectproto,
+                          displayclass &disp, outconvertclass &outconvert,
+                          ostream &textout, ostream &logout);
+
+  virtual text_t get_cover_image();
+
+  virtual void load_extended_options(text_tmap &options, cgiargsclass &args, browsermapclass *browsers, 
+                                     formatinfo_t &formatinfo, recptproto *collectproto, 
+                                     displayclass &disp, outconvertclass &outconvert, ostream &logout);
+
+  virtual void expanded_contents(cgiargsclass &args, int tabcount, bool fulltoc,
+                                 browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                 recptproto *collectproto, displayclass &disp, 
+                                 outconvertclass &outconvert, ostream &textout, 
+                                 ostream &logout, bool expand_from_current_level);
+
+  virtual void expand_show_contents(cgiargsclass &args, int tabcount, bool fulltoc,
+                                    browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                    recptproto *collectproto, displayclass &disp, 
+                                    outconvertclass &outconvert, ostream &textout, 
+                                    ostream &logout);
+
+  virtual void contracted_contents(cgiargsclass &args, int tabcount, bool fulltoc, 
+                                   browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                   recptproto *collectproto, displayclass &disp, 
+                                   outconvertclass &outconvert, ostream &textout, 
+                                   ostream &logout);
+
+protected:
+
+  typedef map<text_t, format_t*, lttext_t> format_tmap; 
+
+  virtual void recurse_contents_levels(text_tarray &parents, cgiargsclass &args, int coloffset,
+                                       int tabcount, bool fulltoc, browsermapclass *browsermap,
+                                       formatinfo_t &formatinfo, recptproto *collectproto,
+                                       displayclass &disp, outconvertclass &outconvert,
+                                       ostream &textout, ostream &logout);
+
+  virtual void output_titles(cgiargsclass &args, recptproto *collectproto, 
+                             browsermapclass *browsermap, formatinfo_t &formatinfo,
+                             displayclass &disp, outconvertclass &outconvert,
+                             ostream &textout, ostream &logout);
+
+  virtual void output_controls(cgiargsclass &args, const text_tarray &ibuttons,
+                               recptproto *collectproto, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout);
+  
+  virtual void output_cover_image(cgiargsclass &args, recptproto *collectproto, 
+                                  displayclass &disp, outconvertclass &outconvert, 
+                                  ostream &textout, ostream &logout);
+
+  virtual void recurse_contents(ResultDocInfo_t &section, cgiargsclass &args, bool fulltoc,
+                                browserclass *bptr, text_tset &metadata, bool &getParents, 
+                                format_t *formatlistptr, format_tmap &formatlistmap, 
+                                formatinfo_t &formatinfo, browsermapclass *browsermap, 
+                                int tabcount, recptproto *collectproto, displayclass &disp, 
+                                outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+  virtual void load_formatstring(const text_t &classifytype, text_tset &metadata,
+                                 bool &getParents, const text_t &classification, 
+                                 browsermapclass *browsermap, formatinfo_t &formatinfo,
+                                 format_tmap &formatlistmap);
+
+  virtual void load_formatstrings(FilterResponse_t &response, text_tset &metadata,
+                                  bool &getParents, const text_t &classification,
+                                  browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                                  format_tmap &formatlistmap);
+
+  virtual void output_parents(FilterResponse_t &response, cgiargsclass &args, 
+                              browsermapclass *browsermap, formatinfo_t &formatinfo, 
+                              format_tmap &formatlistmap, const text_t &classification,
+                              int &tabcount, text_tset &metadata, bool &getParents,
+                              recptproto *collectproto, displayclass &disp, 
+                              outconvertclass &outconvert, ostream &textout, 
+                              ostream &logout);
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiargs.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiargs.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiargs.cpp	(revision 16561)
@@ -0,0 +1,421 @@
+/**********************************************************************
+ *
+ * cgiargs.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.
+ *
+ *********************************************************************/
+
+#include "cgiargs.h"
+#include "gsdlunicode.h"
+
+
+
+void cgiarg_t::clear () {
+  value.clear();
+  source = program_arg;
+  fileupload.clear();
+}
+
+cgiarg_t &cgiarg_t::operator=(const cgiarg_t &x) {
+  value=x.value;
+  source=x.source;
+  return *this;
+}
+
+
+bool operator==(const cgiarg_t &x, const cgiarg_t &y) {
+  return ((x.value == y.value) && (x.source == y.source));
+}
+
+bool operator<(const cgiarg_t &x, const cgiarg_t &y) {
+  return ((x.value < y.value) ||
+	  ((x.value == y.value) && (x.source == y.source)));
+}
+
+
+void fileupload_t::clear() {
+  name.clear();
+  type.clear();
+  size = 0;
+  tmp_name.clear();
+}
+
+ostream &operator<<(ostream &outs, const fileupload_t &fu) {
+  utf8outconvertclass text_t2utf8;
+  outs << "*** fileupload\n";
+  
+  outs << text_t2utf8 << "name=" << fu.name << ", type="<< fu.type<<", size="<<fu.size<<", tmp_name="<<fu.tmp_name<<"\n";
+  return outs;
+}
+
+
+
+// constructors
+cgiargsclass::cgiargsclass () {
+}
+
+
+// this returns NULL if there isn't an entry with a value of
+// 'key' already defined
+
+void cgiargsclass::setarg (const text_t &key, const text_t &value,
+			   cgiarg_t::source_t source) {
+  cgiarg_t temparg;
+  temparg.value = value;
+  temparg.source = source;
+  args[key] = temparg;
+}
+
+void cgiargsclass::setdefaultarg (const text_t &key, const text_t &value,
+				  cgiarg_t::source_t source) {
+  if (getarg(key) == NULL) setarg (key, value, source);
+}
+
+void cgiargsclass::setintarg (const text_t &key, int value,
+			      cgiarg_t::source_t source) {
+  setarg (key, value, source);
+}
+
+void cgiargsclass::setdefaultintarg (const text_t &key, int value,
+				     cgiarg_t::source_t source) {
+  if (getarg(key) == NULL) setintarg (key, value, source);
+}
+
+void cgiargsclass::setcarg (const text_t &key, unsigned short c,
+			    cgiarg_t::source_t source) {
+  text_t t; 
+  t.push_back(c);
+  setarg(key,t, source);
+}
+
+void cgiargsclass::setdefaultcarg (const text_t &key, unsigned short c,
+				   cgiarg_t::source_t source) {
+  if (getarg(key) == NULL) setcarg (key, c, source);
+}
+
+void cgiargsclass::setargfile (const text_t &key, const fileupload_t &fileupload) {
+  iterator here = args.find (key);
+  if (here != args.end()) {
+    (*here).second.fileupload = fileupload;
+  }
+}
+
+text_t *cgiargsclass::getarg (const text_t &key) {
+  iterator here = args.find (key);
+  if (here == args.end()) return NULL;
+
+  return &((*here).second.value);
+}
+
+int cgiargsclass::getintarg (const text_t &key) {
+  text_t *text = getarg (key);
+  if (text == NULL) return 0;
+  return text->getint();
+}
+
+fileupload_t *cgiargsclass::getargfile (const text_t &key) {
+  iterator here = args.find (key);
+  if (here == args.end()) return NULL;
+  return &((*here).second.fileupload);
+}
+
+
+
+
+// stream operators to print cgi arguments for debugging purposes
+ostream &operator<<(ostream &outs, const cgiargsclass &args) {
+  utf8outconvertclass text_t2utf8;
+  cgiargsclass::const_iterator here = args.begin ();
+  cgiargsclass::const_iterator end = args.end ();
+
+  outs << "*** cgiargsclass\n";
+  
+  while (here != end) {
+    outs << text_t2utf8 << " \"" << (*here).first << "\"=\"" << 
+      (*here).second.value << "\"\n";
+    ++here;
+  }
+  outs << "\n";
+
+  return outs;
+}
+
+
+
+cgiarginfo::cgiarginfo () {
+  multiplechar = false;
+  multiplevalue = false;
+  fileupload = false;
+  defaultstatus = weak;
+  argdefault.clear();
+  savedarginfo = can;
+}
+
+
+bool operator==(const cgiarginfo &x, const cgiarginfo &y) {
+  return ((x.shortname == y.shortname) &&
+	  (x.longname == y.longname) &&
+	  (x.multiplechar == y.multiplechar) &&
+	  (x.multiplevalue == y.multiplevalue) &&
+	  (x.fileupload == y.fileupload) &&
+	  (x.defaultstatus == y.defaultstatus) &&
+	  (x.argdefault == y.argdefault) &&
+	  (x.savedarginfo == y.savedarginfo));
+}
+
+bool operator<(const cgiarginfo &x, const cgiarginfo &y) {
+  return ((x.shortname < y.shortname) ||
+	  ((x.shortname == y.shortname) &&
+	   ((x.longname < y.longname) ||
+	    ((x.longname == y.longname) &&
+	     ((x.multiplevalue < y.multiplevalue) ||
+	      ((x.multiplevalue == y.multiplevalue) &&
+	       ((x.fileupload == y.fileupload) &&
+		((x.fileupload < y.fileupload) ||
+		 ((x.multiplechar < y.multiplechar) ||
+		  ((x.multiplechar == y.multiplechar) &&
+		   ((x.defaultstatus < y.defaultstatus) ||
+		    ((x.defaultstatus == y.defaultstatus) &&
+		     ((x.argdefault < y.argdefault) ||
+		      ((x.argdefault == y.argdefault) &&
+		       ((x.savedarginfo < y.savedarginfo))))))))))))))));
+}
+
+
+// constructor
+cgiargsinfoclass::cgiargsinfoclass () {
+}
+
+// addarginfo will combine the information with the present
+// information. If name clashes were detected then the information
+// will be written to logout (or stderr) and addarginfo will return false. No
+// processing with the arguments should be done if this happens
+// as the results will be meaningless.
+bool cgiargsinfoclass::addarginfo (ostream *logout, cgiarginfo &info) {
+
+  // if info.fileupload is set certain other fields are implicitly
+  // overridden
+  if (info.fileupload) {
+    info.multiplechar = false;
+    info.defaultstatus = cgiarginfo::weak;
+    info.argdefault = "";
+    info.savedarginfo = cgiarginfo::mustnot;
+  }
+  cgiarginfo *orginfo = getarginfo (info.shortname);
+  if (orginfo == NULL) {
+    argsinfo[info.shortname] = info;
+    return true; // no clashes
+  }
+
+  if (orginfo->longname != info.longname) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: cgi argument name clash for argument \""
+		<< info.shortname << "\".\nOne long name was\n  \"" << orginfo->longname 
+		<< "\"\nand the other was\n  \"" << info.longname << "\".\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: cgi argument name clash for argument \""
+	   << info.shortname << "\".\nOne long name was\n  \"" << orginfo->longname 
+	   << "\"\nand the other was\n  \"" << info.longname << "\".\n\n";
+    }
+    return false; // found a clash
+  }
+
+  if (orginfo->multiplevalue != info.multiplevalue) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+		<< "\" was given as being a single value option\n"
+		<< "and a multiple value option.\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+		<< "\" was given as being a single value option\n"
+		<< "and a multiple value option.\n\n";
+    }
+    return false; // found a clash
+  }
+
+  if (orginfo->fileupload != info.fileupload) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+		<< "\" was given as being a file upload argument\n"
+		<< "and a non file upload argument.\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+	   << "\" was given as being a file upload argument\n"
+	   << "and a non file upload argument.\n\n";
+    }
+    return false; // found a clash
+  }
+
+  if (orginfo->multiplechar != info.multiplechar) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+		<< "\" was given as being a single character option\n"
+		<< "and a multiple character option.\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname 
+	   << "\" was given as being a single character option\n"
+	   << "and a multiple character option.\n\n";
+    } 
+    return false; // found a clash
+  }
+  
+  if (!info.multiplechar && info.argdefault.size() > 1) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: cgi argument \"" << info.shortname
+		<< "\" was defined as being a single character option\n"
+		<< "but a multiple character default was given.\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: cgi argument \"" << info.shortname
+	   << "\" was defined as being a single character option\n"
+	   << "but a multiple character default was given.\n\n";
+    }
+    return false;  // found a problem
+  }
+
+  // make sure there is no clashes in the savedarginfo
+  if ((orginfo->savedarginfo==cgiarginfo::mustnot && 
+       info.savedarginfo==cgiarginfo::must) ||
+      (orginfo->savedarginfo==cgiarginfo::must && 
+       info.savedarginfo==cgiarginfo::mustnot)) {
+    outconvertclass text_t2ascii;
+    if (logout != NULL) {
+      (*logout) << text_t2ascii << "Error: it was specified that cgi argument \"" 
+		<< info.shortname << "\" should be saved in the state\n"
+		<< "information and that it should not be save in the state information.\n\n";
+    } else {
+      cerr << text_t2ascii << "Error: it was specified that cgi argument \"" 
+	   << info.shortname << "\" should be saved in the state\n"
+	   << "information and that it should not be save in the state information.\n\n";
+    }
+    return false; // found a clash
+  }
+  // the only time orginfo->savedarginfo can change is when it is set
+  // to "can"
+  if (orginfo->savedarginfo == cgiarginfo::can) 
+    orginfo->savedarginfo = info.savedarginfo;
+
+
+  if (orginfo->defaultstatus > info.defaultstatus) {
+    return true;
+  }
+  orginfo->defaultstatus = info.defaultstatus;
+  orginfo->argdefault = info.argdefault;
+
+  return true;
+}
+
+bool cgiargsinfoclass::addarginfo (ostream *logout, cgiargsinfoclass &info) {
+  
+  iterator here = info.begin ();
+  iterator end = info.end ();
+
+  while (here != end) {
+    if (!addarginfo (NULL, (*here).second)) {
+      cerr << "returning false\n";
+      return false;
+    }
+    ++here;
+  }
+  
+  return true; // made it, no clashes
+}
+
+bool cgiargsinfoclass::addarginfo (ostream *logout, const text_t& argshortname, const text_tmap& mapinfo)
+{
+  cgiarginfo *arginfo = getarginfo (argshortname);
+  if (arginfo == NULL) {
+    arginfo = &(argsinfo[argshortname]);
+    arginfo->shortname = argshortname;
+    arginfo->defaultstatus = cgiarginfo::config;
+  }
+  text_tmap::const_iterator thisInfo = mapinfo.begin();
+  text_tmap::const_iterator endInfo = mapinfo.end();
+  while (thisInfo != endInfo) {
+    if (thisInfo->first == "longname") arginfo->longname = thisInfo->second;
+    else if (thisInfo->first == "multiplechar") arginfo->multiplechar = (thisInfo->second == "true");
+    else if (thisInfo->first == "defaultstatus") {
+      if (thisInfo->second == "none") arginfo->defaultstatus = cgiarginfo::none;
+      else if (thisInfo->second == "weak") arginfo->defaultstatus = cgiarginfo::weak;
+      else if (thisInfo->second == "good") arginfo->defaultstatus = cgiarginfo::good;
+      else if (thisInfo->second == "config") arginfo->defaultstatus = cgiarginfo::config;
+      else if (thisInfo->second == "imperative") arginfo->defaultstatus = cgiarginfo::imperative;
+    }
+    else if (thisInfo->first == "argdefault") {
+      arginfo->argdefault = thisInfo->second;
+    }
+    else if (thisInfo->first == "savedarginfo") {
+      if (thisInfo->second == "mustnot") arginfo->savedarginfo = cgiarginfo::mustnot;
+      else if (thisInfo->second == "can") arginfo->savedarginfo = cgiarginfo::can;
+      else if (thisInfo->second == "must") arginfo->savedarginfo = cgiarginfo::must;
+    } else if (logout != NULL) {
+      (*logout) << ("Invalid argument for cgiarg for " + argshortname + " (" + thisInfo->first + ")\n");
+    }
+    ++thisInfo;
+  }
+  return true;
+}
+
+bool cgiargsinfoclass::addarginfo (ostream *logout, confcgiarg_tmap& info)
+{
+  confcgiarg_tmap::const_iterator thisArg = info.begin();
+  confcgiarg_tmap::const_iterator endArg = info.end();
+  while (thisArg != endArg) {
+    if (!addarginfo(logout, thisArg->first, thisArg->second)) return false;
+    ++thisArg;
+  }
+  return true;
+}
+
+cgiarginfo *cgiargsinfoclass::getarginfo (const text_t &key) {
+  iterator here = argsinfo.find (key);
+  if (here == argsinfo.end()) return NULL;
+
+  return &((*here).second);
+}
+
+const cgiarginfo *cgiargsinfoclass::getarginfo (const text_t &key) const {
+  const_iterator here = argsinfo.find (key);
+  if (here == argsinfo.end()) return NULL;
+
+  return &((*here).second);
+}
+
+// stream operators to print cgiarginfo for debugging purposes
+ostream &operator<<(ostream &outs, const cgiargsinfoclass &argsinfo) {
+  utf8outconvertclass text_t2utf8;
+  cgiargsinfoclass::const_iterator here = argsinfo.begin ();
+  cgiargsinfoclass::const_iterator end = argsinfo.end ();
+
+  outs << "*** cgiargsinfoclass\n";
+  
+  while (here != end) {
+    outs << text_t2utf8 << (*here).first << ", ";
+    ++here;
+  }
+  outs << "\n";
+
+  return outs;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiargs.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiargs.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiargs.h	(revision 16561)
@@ -0,0 +1,260 @@
+/**********************************************************************
+ *
+ * cgiargs.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.
+ *
+ *********************************************************************/
+
+
+#ifndef CGIARGS_H
+#define CGIARGS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+
+// response_t is used to define what type of cgi header 
+// should be produced
+// fullcontent same as content but the action should put content-type as last header.
+enum response_t {location, content, fullcontent, undecided_location};
+
+struct fileupload_t {
+  void clear();
+  fileupload_t() {clear();}
+  
+  text_t name;
+  text_t type;
+  int size;
+  text_t tmp_name;
+};
+
+typedef map<text_t, fileupload_t, lttext_t> fileupload_tmap;
+
+struct cgiarg_t {
+  void clear ();
+  cgiarg_t () {clear();}
+  cgiarg_t &operator=(const cgiarg_t &x);
+  
+  enum source_t {default_arg, compressed_arg, cgi_arg, program_arg};
+  
+  text_t value;
+  source_t source;
+  fileupload_t fileupload;
+};
+
+ostream &operator<<(ostream &outs, const fileupload_t &fu);
+
+bool operator==(const cgiarg_t &x, const cgiarg_t &y);
+bool operator<(const cgiarg_t &x, const cgiarg_t &y);
+
+typedef map<text_t, cgiarg_t, lttext_t> cgiarg_tmap;
+typedef map<text_t, text_tmap, lttext_t> confcgiarg_tmap;
+
+// cgiargsclass is used to store a particular set
+// of cgi arguments.
+class cgiargsclass {
+protected:
+  cgiarg_tmap args;
+
+public:
+  //type support for cgiarg_tmap
+  typedef cgiarg_tmap::iterator iterator;
+  typedef cgiarg_tmap::const_iterator const_iterator;
+  typedef cgiarg_tmap::reference reference;
+  typedef cgiarg_tmap::const_reference const_reference;
+  typedef cgiarg_tmap::size_type size_type;
+  typedef cgiarg_tmap::difference_type difference_type;
+  typedef cgiarg_tmap::const_reverse_iterator const_reverse_iterator;
+  typedef cgiarg_tmap::reverse_iterator reverse_iterator;
+  
+  // constructors
+  cgiargsclass ();
+
+  // basic container support
+  iterator begin () {return args.begin();}
+  const_iterator begin () const {return args.begin();}
+  iterator end () {return args.end();}
+  const_iterator end () const {return args.end();}
+
+  void erase(iterator pos) {args.erase(pos);}
+  void erase(iterator first, iterator last) {args.erase(first, last);}
+  cgiargsclass &operator=(const cgiargsclass &x) {args=x.args;return *this;}
+
+  bool empty () const {return args.empty();}
+  size_type size() const {return args.size();}
+
+
+  // added functionality
+  void clear () {args.erase(args.begin(),args.end());}
+
+  // setdefaultarg, setdefaultintarg, and setdefaultcarg will 
+  // only set the argument if it is not already set
+  // getargs returns NULL if there isn't an entry with
+  // 'key' already defined, getintarg returns 0 if there wasn't an
+  // entry with 'key' defined and operator[] returns "" if
+  // 'key' wasn't already defined (and sets 'key'="").
+  void setarg (const text_t &key, const text_t &value,
+	       cgiarg_t::source_t source=cgiarg_t::program_arg);
+  void setdefaultarg (const text_t &key, const text_t &value,
+		      cgiarg_t::source_t source=cgiarg_t::default_arg);
+  void setintarg (const text_t &key, int value,
+		  cgiarg_t::source_t source=cgiarg_t::program_arg);
+  void setdefaultintarg (const text_t &key, int value,
+			 cgiarg_t::source_t source=cgiarg_t::default_arg);
+  void setcarg (const text_t &key, unsigned short c,
+		cgiarg_t::source_t source=cgiarg_t::program_arg);
+  void setdefaultcarg (const text_t &key, unsigned short c,
+		       cgiarg_t::source_t source=cgiarg_t::default_arg);
+  void setargfile (const text_t &key, const fileupload_t &fileupload);
+  text_t *getarg (const text_t &key);
+  int getintarg (const text_t &key);
+  fileupload_t *getargfile (const text_t &key);
+  text_t &operator[] (const text_t &key) {return args[key].value;}
+  cgiarg_t &lookupcgiarg (const text_t &key) {return args[key];}
+};
+
+// stream operators to print cgi arguments for debugging purposes
+ostream &operator<<(ostream &outs, const cgiargsclass &args);
+
+
+
+// cgiarginfo holds information relating to a cgi argument
+class cgiarginfo {
+public:
+  cgiarginfo ();
+
+  // shortname is the name used in cgi arguments
+  text_t shortname;
+
+  // longname is the name used when giving information 
+  // about the argument and to spot name duplication
+  text_t longname;
+
+  // multiplechar should be set to true if the value can be
+  // more than one character long
+  bool multiplechar;
+
+  // multiplevalue should be set to true the argument may have 
+  // multiple values (as is the case when multiple checkboxes
+  // use the same name)
+  bool multiplevalue;
+
+  // fileupload should be set if the argument is to be used to upload files
+  bool fileupload;
+
+  // defaultstatus_t indicates how good the default is when different
+  // defaults are given for one argument. "none" means there is no default
+  // (a default might be given elsewhere). "weak" is a basic guess that
+  // doesn't hold much weight, "good" is a better guess, "config" was a default
+  // given in a configuration file, and "imperative" means it must not
+  // be overriden at any costs (unless there is another "imperative"...)
+  enum defaultstatus_t {none=0, weak=1, good=2, config=3, imperative=4};
+
+  defaultstatus_t defaultstatus;
+  text_t argdefault;   // a default value
+
+  // savedarginfo_t indicates whether the argument should be saved
+  // between pages (e.g. using the compressed argument) or not. The
+  // value of an argument can change from "can" to "mustnot" and 
+  // "can" to "must" but not "mustnot" to "must" or "must" to "mustnot".
+  enum savedarginfo_t {mustnot, can, must};
+
+  savedarginfo_t savedarginfo;
+};
+
+bool operator==(const cgiarginfo &x, const cgiarginfo &y);
+bool operator<(const cgiarginfo &x, const cgiarginfo &y);
+
+typedef map<text_t, cgiarginfo, lttext_t> argsinfomap;
+
+// contains a list of cgi argument information
+class cgiargsinfoclass {
+protected:
+  argsinfomap argsinfo;
+
+public:
+  // type support for arginfomap
+  typedef argsinfomap::iterator iterator;
+  typedef argsinfomap::const_iterator const_iterator;
+  typedef argsinfomap::reference reference;
+  typedef argsinfomap::const_reference const_reference;
+  typedef argsinfomap::size_type size_type;
+  typedef argsinfomap::difference_type difference_type;
+  typedef argsinfomap::const_reverse_iterator const_reverse_iterator;
+  typedef argsinfomap::reverse_iterator reverse_iterator;
+  
+  // constructors
+  cgiargsinfoclass ();
+
+  // basic container support
+  iterator begin () {return argsinfo.begin();}
+  const_iterator begin () const {return argsinfo.begin();}
+  iterator end () {return argsinfo.end();}
+  const_iterator end () const {return argsinfo.end();}
+
+  void erase(iterator pos) {argsinfo.erase(pos);}
+  void erase(iterator first, iterator last) {argsinfo.erase(first, last);}
+  cgiargsinfoclass &operator=(const cgiargsinfoclass &x) {argsinfo=x.argsinfo;return *this;}
+
+  bool empty () const {return argsinfo.empty();}
+  size_type size() const {return argsinfo.size();}
+
+
+  // added functionality
+  void clear () {argsinfo.erase(argsinfo.begin(),argsinfo.end());}
+
+  // addarginfo will combine the information with the present
+  // information. If name clashes were detected then the information
+  // will be written to logout and addarginfo will return false. No
+  // processing with the arguments should be done if this happens
+  // as the results will be meaningless.
+  bool addarginfo (ostream *logout, cgiarginfo &info);
+  bool addarginfo (ostream *logout, cgiargsinfoclass &info);
+
+  // addarginfo will override args info with info loaded from config files
+  // if the args do not exists will be created
+  bool addarginfo (ostream *logout, const text_t& argshortname, const text_tmap& mapinfo);
+  bool addarginfo (ostream *logout, confcgiarg_tmap& info);
+
+  cgiarginfo *getarginfo (const text_t &key);
+  const cgiarginfo *getarginfo (const text_t &key) const;
+  cgiarginfo &operator[] (const text_t &key) {return argsinfo[key];}
+};
+
+ostream &operator<<(ostream &outs, const cgiargsinfoclass &argsinfo);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiutils.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiutils.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiutils.cpp	(revision 16561)
@@ -0,0 +1,847 @@
+/**********************************************************************
+ *
+ * cgiutils.cpp -- general cgi utilities
+ * 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.
+ *
+ *********************************************************************/
+
+#include "cgiutils.h"
+#include "md5.h"
+#include "fileutil.h"
+#include "gsdlunicode.h"
+#include "fileutil.h"
+#include "unitool.h" // in mg, for output_utf8_char
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+static unsigned short hexdigit (unsigned short c) {
+  if (c >= '0' && c <= '9') return (c-'0');
+  if (c >= 'a' && c <= 'f') return (c-'a'+10);
+  if (c >= 'A' && c <= 'F') return (c-'A'+10);
+  return c;
+}
+
+
+static void c2hex (unsigned short c, text_t &t) {
+  t.clear();
+  
+  if (c >= 256) {
+    t = "20"; // ' '
+    return;
+  }
+  
+  unsigned short o1, o2;
+  
+  o1 = (c/16) % 16;
+  o2 = c % 16;
+  if (o1 >= 10) o1 += 'a' - 10;
+  else o1 += '0';
+  if (o2 >= 10) o2 += 'a' - 10;
+  else o2 += '0';
+  
+  t.push_back(o1);
+  t.push_back(o2);
+}
+
+static text_t::iterator getline (text_t::iterator first, 
+				 text_t::iterator last,
+				 bool include_crlf) {
+  while (first != last) {
+    if (((first+1) != last) && (*first == 13) && (*(first+1) == 10)) {
+      // found <CRLF>
+      if (include_crlf) first += 2;
+      break;
+    }
+
+    first++;
+  }
+
+  return first;
+}
+
+static void process_post_section (text_t &argname, text_t &argdata, text_t &filename, text_t &filedata,
+				  text_t &filetype, bool &isfile, text_t &argstr,
+				  fileupload_tmap &fileuploads, const text_t &gsdlhome) {
+
+  if (!argname.empty()) {
+
+    if (!isfile) {
+      // argdata includes a trailing <CRLF> that we must remove
+      if ((argdata.size() > 1) && (*(argdata.end()-2) == 13) && (*(argdata.end()-1) == 10)) {
+	argdata.erase(argdata.end()-2, argdata.end());
+      }
+      if (!argstr.empty()) argstr += "&";
+      argstr += argname + "=" + argdata;
+
+    } else if (!filename.empty()) {
+      // filedata includes a trailing <CRLF> that we must remove
+      if ((filedata.size() > 1) && (*(filedata.end()-2) == 13) && (*(filedata.end()-1) == 10)) {
+	filedata.erase(filedata.end()-2, filedata.end());
+      }
+
+      // create tmp_name for storing the file on disk
+      text_t tmp_name = md5data(filedata);
+      tmp_name = filename_cat(gsdlhome, "tmp", tmp_name);
+
+      char *tmp_name_c = tmp_name.getcstr();
+
+      // write the file data to disk
+      outconvertclass out;
+      ofstream filestream(tmp_name_c, ios::out | ios::binary);
+      filestream << out << filedata;
+      filestream.close();
+      delete tmp_name_c;
+
+      // populate the fields of a fileupload_t and put it in the
+      // fileuploads map
+      fileupload_t fu;
+      // note that filename currently may or may not include the path since
+      // some browsers (e.g. IE) include the path while others
+      // (e.g. mozilla) do not. we should probably remove the path from
+      // this field here to get a consistent value across all browsers.
+      text_t::iterator slash = findlastchar(filename.begin(), filename.end(), '\\');
+      if (slash != filename.end()) {
+	filename = substr(slash+1, filename.end());
+      }
+      fu.name = filename;
+      fu.type = filetype;
+      // size has yet to be implemented
+      fu.size = filedata.size();
+
+      fu.tmp_name = tmp_name;
+      fileuploads[argname] = fu;
+    }
+  }
+  isfile = false;
+  argname.clear();
+  argdata.clear();
+  filename.clear();
+  filedata.clear();
+  filetype.clear();
+}
+
+// parse data obtained through a CGI POST request
+text_t parse_post_data (text_t &content_type, text_t &raw_post_data, 
+			fileupload_tmap &fileuploads, const text_t &gsdlhome) {
+
+  text_t argstr;
+
+  text_t::iterator content_type_begin = content_type.begin();
+  text_t::iterator content_type_end = content_type.end();
+  if (findword(content_type_begin, content_type_end, "multipart/form-data") == content_type_end) {
+    // a simple post request
+
+    return raw_post_data;
+
+  } else {
+    // multipart/form data - may contain one or more uploaded files
+
+    /*
+      content_type should look something like the following
+        multipart/form-data; boundary=---------------------------7d411e1a50330
+	
+      while raw_post_data will be as follows
+        -----------------------------7d43e73450330CRLF
+	Content-Disposition: form-data; name="e"<CRLF>
+	<CRLF>
+	d-0testss--1-0-00---4----0--0-110--1en-Zz-1---10-about-0--00031-001utfZz-8-0<CRLF>
+	-----------------------------7d43e73450330<CRLF>
+	Content-Disposition: form-data; name="afile"; filename="C:\somedoc.doc"<CRLF>
+	Content-Type: application/msword<CRLF>
+	<CRLF>
+	<Content of file><CRLF>
+	
+    */
+
+    // first get the boundary from content-type
+    text_t::iterator boundary_begin = findword(content_type_begin, content_type_end, "boundary=");
+    if (boundary_begin+9 < content_type_end) 
+      {
+	// skip over "boundary=" part of string
+	boundary_begin += 9;
+      }
+    else {
+      // error
+      cerr << "Error: malformed boundary? '" <<  content_type << "'" << endl;
+      return "";
+    }
+    text_t boundary = substr(boundary_begin, getline(boundary_begin, content_type_end, false));
+    int boundary_len = boundary.size();
+
+
+    text_t argname, argdata, filename, filedata, filetype;
+    bool isfile = false;
+    text_t::iterator data_here = raw_post_data.begin();
+    text_t::iterator data_end = raw_post_data.end();
+    while (data_here != data_end) {
+      
+      // get the next available line (including the trailing <CRLF>
+      text_t line = substr(data_here, getline(data_here, data_end, true));
+
+      data_here += line.size();
+      text_t::iterator line_begin = line.begin();
+      text_t::iterator line_end = line.end();
+      if (findword(line_begin, line_end, boundary) != line_end) {
+	// we've found a boundary
+	process_post_section(argname, argdata, filename, filedata, filetype,
+			     isfile, argstr, fileuploads, gsdlhome);
+
+      } else if (findword(line_begin, line_end, "Content-Disposition: form-data") != line_end) {
+	// we've found the the beginning of a new section
+	argname.clear();
+	argdata.clear();
+
+	// get the name of this piece of form data
+	text_t::iterator it = findword(line_begin, line_end, "name=\"");
+	if (it == line_end) break; // error - this shouldn't happen
+	it = findchar(it, line_end, '"');
+	if ((it != line_end) && (it+1 != line_end)) {
+	  argname = substr(it+1, findchar(it+1, line_end, '"'));
+	}
+
+	// if this piece of form data contains filename="" it's a file
+	// upload and needs to be treated special
+	it = (findword(line_begin, line_end, "filename=\""));
+	if (it != line_end) {
+	  // we've found a file upload
+	  isfile = true;
+	  it = findchar(it, line_end, '"');
+	  if ((it != line_end) && (it+1 != line_end)) {
+	    filename = substr(it+1, findchar(it+1, line_end, '"'));
+	  }
+
+	  // the next line is the content-type of this section
+	  line = substr(data_here, getline(data_here, data_end, true));
+	  data_here += line.size();
+	  line_begin = line.begin();
+	  line_end = line.end();
+	  it = (findword(line_begin, line_end, "Content-Type: "));
+	  if (it != line_end) {
+	    filetype = substr(it+14, getline(it, line_end, false));
+	  }
+	}
+
+	// eat up the next line as it's just a <CRLF> on it's own
+	data_here += 2;
+
+      } else {
+	if (isfile) filedata += line;
+	else argdata += line;
+      }
+
+    }
+
+    // process last section
+    process_post_section(argname, argdata, filename, filedata, filetype,
+			 isfile, argstr, fileuploads, gsdlhome);
+
+    return argstr;
+  }
+}
+
+// convert %xx and + to their appropriate equivalents
+// IE 6.0 and later use "%u" followed by 4 hex digits... MS IIS extension!
+// NOTE: this method is crap. It assumes the input encoding is utf-8. If it 
+// actually was, then this returns utf-8, and needs to_uni on the 
+// result to get it back to unicode. If the encoding wasn't utf-8, then the 
+// output may be crap. Seems to work for 8 bit encodings.
+// Really, this should be given the encoding, and should always return unicode. 
+void decode_cgi_arg (text_t &argstr) {
+  text_t::iterator in = argstr.begin();
+  text_t::iterator out = in;
+  text_t::iterator end = argstr.end();
+  
+  while (in != end) {
+    if (*in == '+') *out = ' ';
+    
+    else if (*in == '%') {
+      unsigned short c = '%';
+      ++in;
+      if (in != end) { // this is an encoding...
+	if (*in == 'u') { // convert %uHHHH to unicode then current encoding
+	  // this assumes a short int is at least 16 bits...
+	  ++in;  
+	  if (in != end)
+	    c=hexdigit(*in++) << 12;
+	  if (in != end)
+	    c+=hexdigit(*in++) << 8;
+	  if (in != end)
+	    c+=hexdigit(*in++) << 4;
+	  if (in != end)
+	    c+=hexdigit(*in);
+	  /* BAD!! The following assumes the interface is using utf-8. But
+	     at this point we don't know what encoding we are using, unless
+	     we can parse it out of the string we are currently decoding... */
+	  text_t uni=" ";
+	  uni[0]=c;
+	  text_t utf8=to_utf8(uni);
+	  int last_byte=utf8.size()-1;
+	  for (int i=0;i<last_byte;++i)
+	    *out++ = utf8[i];
+	  c=utf8[last_byte];
+	} else {  // convert %HH to hex value
+	  c = hexdigit (*in);
+	  ++in;
+	  if (in != end && c < 16) { // sanity check on the previous character
+	    c = c*16 + hexdigit (*in);
+	  }
+	}
+      }
+      *out = c;
+    } else *out = *in;
+    
+    if (in != end) ++in;
+    ++out;
+  }
+  
+  // remove the excess characters
+  argstr.erase (out, end);
+  
+}
+
+
+// split up the cgi arguments
+void split_cgi_args (const cgiargsinfoclass &argsinfo, text_t argstr, 
+		     cgiargsclass &args) {
+  args.clear();
+  
+  text_t::const_iterator here = argstr.begin();
+  text_t::const_iterator end = argstr.end();
+  text_t key, value;
+ 
+  // extract out the key=value pairs
+  while (here != end) {
+    // get the next key and value pair
+    here = getdelimitstr (here, end, '=', key);
+    here = getdelimitstr (here, end, '&', value);
+    
+    // convert %xx and + to their appropriate equivalents
+    decode_cgi_arg (value);
+    value.setencoding(1); // other encoding
+    // store this key=value pair
+    if (!key.empty()) {
+
+      // if arg occurs multiple times (as is the case with multiple
+      // checkboxes using the same name) we'll create a comma separated
+      // list of all the values (this uses a hack that encodes naturally
+      // occurring commas as %2C - values will therefore need to be decoded
+      // again before use) - it should use an array instead
+      const cgiarginfo *info = argsinfo.getarginfo (key);
+      if (info==NULL) {
+	// If info is NULL, we can't tell if the arg is multiple value or not
+	// Because we need to have dynamically named arguments multivalued, we
+	// will always assume multiplevalue = true
+	// If the arg is not multi valued, then you need to decode the commas.
+	if (args.getarg(key)==NULL) {
+	  args.setarg (key, encode_commas(value), cgiarg_t::cgi_arg);
+	}
+	else {
+	  text_t newvalue = args[key];
+
+	  newvalue += "," + encode_commas(value);
+	  args.setarg (key, newvalue, cgiarg_t::cgi_arg);
+	}
+      }
+      else {
+	if (info->multiplevalue) {
+
+	  text_t newvalue = args[key];
+	  if (args.lookupcgiarg(key).source == cgiarg_t::cgi_arg) newvalue += ",";
+	  newvalue += encode_commas(value);
+	  args.setarg (key, newvalue, cgiarg_t::cgi_arg);
+
+	} else {
+	  args.setarg (key, value, cgiarg_t::cgi_arg);
+	}
+      }
+    }
+  }
+}
+
+text_t encode_commas (const text_t &intext) {
+
+  text_t outtext;
+
+  text_t::const_iterator here = intext.begin ();
+  text_t::const_iterator end = intext.end ();
+
+  while (here != end) {
+    if (*here == ',') outtext += "%2C";
+    else outtext.push_back (*here);
+    ++here;
+  }
+  return outtext;
+}
+
+text_t decode_commas (const text_t &intext) {
+
+  text_t outtext;
+  
+  text_t::const_iterator here = intext.begin ();
+  text_t::const_iterator end = intext.end ();
+  
+  while (here != end) {
+    if ((here+2<end) && *here == '%' && *(here+1) == '2' &&
+	(*(here+2) == 'C' || *(here+2) == 'c')) {
+      here += 2;
+      outtext.push_back(',');
+      
+    }else outtext.push_back (*here);
+    ++here;
+  }
+  return outtext;
+}
+
+// set utf8 to true if input is in utf-8, otherwise expects input in unicode
+text_t minus_safe (const text_t &intext, bool utf8) {
+
+  text_t outtext;
+
+  text_t::const_iterator here = intext.begin ();
+  text_t::const_iterator end = intext.end ();
+
+  while (here != end) {
+    if (*here == '-') outtext += "Zz-";
+    else outtext.push_back (*here);
+    ++here;
+  }
+  if (utf8) {
+    outtext = cgi_safe_utf8 (outtext);
+  } else {
+    outtext = cgi_safe_unicode (outtext);
+  }
+  return outtext;
+}
+
+// takes utf-8 input
+text_t cgi_safe_utf8 (const text_t &intext) {
+  text_t outtext;
+  
+  text_t::const_iterator here = intext.begin ();
+  text_t::const_iterator end = intext.end ();
+  unsigned short c;
+  text_t ttmp;
+  
+  while (here != end) {
+    c = *here;
+    if (((c >= 'a') && (c <= 'z')) ||
+	((c >= 'A') && (c <= 'Z')) ||
+	((c >= '0') && (c <= '9')) ||
+	(c == '%') || (c == '-')) {
+      // alphanumeric character
+      outtext.push_back(c);
+    } else if (c == ' ') {
+      // space
+      outtext.push_back('+');
+    } else if (c > 255) { // not utf-8 character
+      cerr << "WARNING: expected utf-8 char, but got unicode!!\n";
+    } else {
+      // everything else
+      outtext.push_back('%');
+      c2hex(c, ttmp);
+      outtext += ttmp;
+    }
+    
+    ++here;
+  }
+  
+  return outtext;
+}
+// takes unicode input
+text_t cgi_safe_unicode (const text_t &intext) {
+  text_t outtext;
+  
+  text_t::const_iterator here = intext.begin ();
+  text_t::const_iterator end = intext.end ();
+  unsigned short c;
+  text_t ttmp;
+  
+  while (here != end) {
+    c = *here;
+    if (((c >= 'a') && (c <= 'z')) ||
+	((c >= 'A') && (c <= 'Z')) ||
+	((c >= '0') && (c <= '9')) ||
+	(c == '%') || (c == '-')) {
+      // alphanumeric character
+      outtext.push_back(c);
+    } else if (c == ' ') {
+      // space
+      outtext.push_back('+');
+    } else if (c > 127) { // unicode character
+      unsigned char buf[3]; // up to 3 bytes
+      buf[0]='\0';buf[1]='\0';buf[2]='\0';
+      output_utf8_char(c,buf, buf+2);
+      outtext.push_back('%');
+      c2hex(buf[0], ttmp);
+      outtext += ttmp;
+      outtext.push_back('%');
+      c2hex(buf[1], ttmp);
+      outtext += ttmp;
+      if (buf[2]) {
+      outtext.push_back('%');
+      c2hex(buf[2], ttmp);
+      outtext += ttmp;
+      }
+    } else {
+      // everything else
+      outtext.push_back('%');
+      c2hex(c, ttmp);
+      outtext += ttmp;
+    }
+    
+    ++here;
+  }
+  
+  return outtext;
+}
+
+
+
+
+static text_t::const_iterator get_next_save_arg (text_t::const_iterator first, 
+					   text_t::const_iterator last,
+					   text_t &argname) {
+  first = getdelimitstr (first, last, '-', argname);
+  return first;
+}
+
+
+// check_save_conf_str checks the configuration string for
+// the saved args and makes sure it does not conflict with
+// the information about the arguments. If an error is encountered
+// it will return false and the program should not produce any
+// output.
+bool check_save_conf_str (const text_t &saveconf, 
+			  const cgiargsinfoclass &argsinfo,
+			  ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  text_tset argsset;
+  text_t::const_iterator saveconfhere = saveconf.begin ();
+  text_t::const_iterator saveconfend = saveconf.end ();
+  text_t argname;
+  const cgiarginfo *info;
+
+  // first check to make sure all saved arguments can be saved
+  
+  while (saveconfhere != saveconfend) {
+    saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
+
+    if (!argname.empty()) {
+      // save the argument name for later
+      argsset.insert (argname);
+
+      // check the argument
+      info =  argsinfo.getarginfo (argname);
+      if (info == NULL) {
+	logout << text_t2ascii << "Error: the cgi argument \"" << argname 
+	       << "\" is used in the configuration string for the\n"
+	       << "saved arguments but does not exist as a valid argument.\n\n";
+	return false;
+      }
+      if (info->savedarginfo == cgiarginfo::mustnot) {
+	logout << text_t2ascii << "Error: the cgi argument \"" << argname
+	       << "\" is used in the configuration string for the\n"
+	       << "saved arguments but has been specified as an argument whose\n"
+	       << "state must not be saved.\n\n";
+	return false;
+      }
+    }
+  }
+
+
+  // next check that all saved arguments that should be saved
+  // are saved
+  cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
+  cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
+
+  while (argsinfohere != argsinfoend) {
+    if (((*argsinfohere).second.savedarginfo == cgiarginfo::must) &&
+	(argsset.find((*argsinfohere).second.shortname) == argsset.end())) {
+      logout << text_t2ascii << "Error: the cgi argument \"" 
+	     << (*argsinfohere).second.shortname << "\" was specified as needing to\n"
+	     << "be save but was not listed in the saved arguments.\n\n";
+      return false;
+    }
+
+    ++argsinfohere;
+  }
+  
+  return true; // made it, no clashes
+}
+
+
+// create_save_conf_str will create a configuration string
+// based on the information in argsinfo. This method of configuration
+// is not recomended as small changes can produce large changes in
+// the resulting configuration string (for instance a totally different
+// ordering). Only arguments which "must" be saved are included in
+// the resulting string.
+text_t create_save_conf_str (const cgiargsinfoclass &argsinfo,
+			     ostream &/*logout*/) {
+  cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
+  cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
+  text_t saveconf;
+  bool first = true;
+
+  while (argsinfohere != argsinfoend) {
+    // save this argument if it must be saved
+    if ((*argsinfohere).second.savedarginfo == cgiarginfo::must) {
+      if (!first) saveconf.push_back ('-');
+      else first = false;
+      saveconf += (*argsinfohere).second.shortname;
+    }
+
+    ++argsinfohere;
+  }
+  
+  return saveconf;
+}
+
+
+// expand_save_args will expand the saved arguments based
+// on saveconf placing the results in args if they are not
+// already defined. If it encounters an error it will return false
+// and output more information to logout.
+bool expand_save_args (const cgiargsinfoclass &argsinfo,
+		       const text_t &saveconf, 
+		       cgiargsclass &args,
+		       ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  text_t *arg_e = args.getarg("e");
+  if (arg_e == NULL) return true; // no compressed arguments
+  if (arg_e->empty()) return true; // no compressed arguments
+
+  text_t argname, argvalue;
+  const cgiarginfo *argnameinfo;
+  
+  text_t::const_iterator saveconfhere = saveconf.begin();
+  text_t::const_iterator saveconfend = saveconf.end();
+  
+  text_t::iterator arg_ebegin = arg_e->begin();
+  text_t::iterator arg_eend = arg_e->end();
+  text_t::iterator arg_ehere = arg_ebegin;
+  while (saveconfhere != saveconfend && arg_ehere != arg_eend) {
+    saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
+
+    if (!argname.empty()) {
+      // found another entry
+      argnameinfo = argsinfo.getarginfo (argname);
+
+      if (argnameinfo == NULL) {
+	// no information about the argument could be found
+	// we can't keep going because we don't know whether
+	// this argument is a single or multiple character value
+	logout << text_t2ascii << "Error: the cgi argument \"" << argname
+	       << "\" was specified as being a compressed argument\n"
+	       << "but no information about it could be found within the "
+	       << "cgiargsinfoclass.\n";
+	return false;
+
+      } else {
+
+	// found the argument information
+	if (argnameinfo->multiplechar) {
+	  text_t::const_iterator sav = arg_ehere;
+	  arg_ehere = getdelimitstr (arg_ehere, arg_eend, '-', argvalue);
+	  if (distance(arg_ebegin, arg_ehere) > 2) {
+	    // replace any '-' chars escaped with 'Zz'
+	    bool first = true;
+	    while ((*(arg_ehere-3) == 'Z') && (*(arg_ehere-2) == 'z')) {
+	      if (first) argvalue.clear();
+	      arg_ehere = (findchar (arg_ehere, arg_eend, '-')) + 1;
+	      while (sav != (arg_ehere-1)) {
+		if (!((*sav == 'Z') && (*(sav+1) == 'z') && (*(sav+2) == '-')) &&
+		    !((*(sav-1) == 'Z') && (*sav == 'z') && (*(sav+1) == '-'))) argvalue.push_back (*sav);
+		++sav;
+	      }
+	      first = false;
+	    }
+	  }
+	  argvalue.setencoding(1); // other encoding
+	  if (!argvalue.empty()) args.setdefaultarg (argname, argvalue, cgiarg_t::compressed_arg);
+	} else {
+	  args.setdefaultcarg (argname,*arg_ehere, cgiarg_t::compressed_arg);
+	  ++arg_ehere;
+	}
+      }
+    }
+  }
+
+  return true;
+}
+
+
+// adds the default values for those arguments which have not
+// been specified
+void add_default_args (const cgiargsinfoclass &argsinfo,
+		       cgiargsclass &args,
+		       ostream &/*logout*/) {
+  cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
+  cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
+
+  while (argsinfohere != argsinfoend) {
+    if ((*argsinfohere).second.defaultstatus != cgiarginfo::none) {
+      args.setdefaultarg ((*argsinfohere).second.shortname,
+			  (*argsinfohere).second.argdefault, cgiarg_t::default_arg);
+    }
+    ++argsinfohere;
+  }
+}
+
+void add_fileupload_args (const cgiargsinfoclass &argsinfo,
+			  cgiargsclass &args,
+			  fileupload_tmap &fileuploads,
+			  ostream &logout) {
+ 
+  const cgiarginfo *info = argsinfo.getarginfo("a");
+  fileupload_tmap::const_iterator this_file = fileuploads.begin();
+  fileupload_tmap::const_iterator end_file = fileuploads.end();
+  while (this_file != end_file) {
+    const cgiarginfo *info = argsinfo.getarginfo((*this_file).first);
+    if (info != NULL) {
+
+      if ((*info).fileupload && (file_exists((*this_file).second.tmp_name))) {
+
+	args.setargfile((*this_file).first, (*this_file).second);
+      }
+    }
+    this_file++;
+  }
+}
+
+// compress_save_args will compress the arguments and return
+// them in compressed_args. If an error was encountered
+// compressed_args will be set to to "", an error will be
+// written to logout, and the function will return false.
+bool compress_save_args (const cgiargsinfoclass &argsinfo,
+			 const text_t &saveconf,
+			 cgiargsclass &args,
+			 text_t &compressed_args,
+			 outconvertclass &outconvert,
+			 ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  compressed_args.clear();
+
+  text_t argname, argvalue;
+  const cgiarginfo *argnameinfo;
+  
+  text_t::const_iterator saveconfhere = saveconf.begin();
+  text_t::const_iterator saveconfend = saveconf.end();
+  
+  while (saveconfhere != saveconfend) {
+    saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
+
+    if (!argname.empty()) {
+      // found another entry
+      argnameinfo = argsinfo.getarginfo (argname);
+
+      if (argnameinfo == NULL) {
+	// no information about the argument could be found
+	// we can't keep going because we don't know whether
+	// this argument is a single or multiple character value
+	logout << text_t2ascii << "Error: the cgi argument \"" << argname
+	       << "\" was specified as being a compressed argument\n"
+	       << "but no information about it could be found within the "
+	       << "cgiargsinfoclass.\n";
+	compressed_args.clear();
+	return false;
+
+      } else {
+	// found the argument information
+	if (argnameinfo->multiplechar) {
+	  // multiple character argument -- sort out any '-' chars
+	  if (args["w"]=="utf-16be") // browsers don't like \0 in urls...
+	    compressed_args += minus_safe (args[argname], false);
+	  else
+	    compressed_args += minus_safe (outconvert.convert(args[argname]), true);
+	  
+	  if (saveconfhere != saveconfend) compressed_args.push_back ('-');
+
+	} else {
+	  // single character argument
+	  if (args[argname].size() == 0) {
+	    logout << text_t2ascii << "Error: the cgi argument \"" << argname
+		   << "\" was specified as being a compressed argument which\n"
+		   << "should have a one character value but it was empty.\n\n";
+	    compressed_args.clear ();
+	    return false;
+
+	  } else if (args[argname].size() > 1) {
+	    logout << text_t2ascii << "Error: the cgi argument \"" << argname
+		   << "\" was specified as being a compressed argument which\n"
+		   << "should have a one character value but it had multiple characters.\n\n";
+	    compressed_args.clear ();
+	    return false;
+	  }
+
+	  // everything is ok
+	  compressed_args += args[argname];
+	}
+      }
+    }
+  }
+
+  return true;
+}
+
+
+// args_tounicode converts any arguments which are not in unicode
+// to unicode using inconvert
+void args_tounicode (cgiargsclass &args, inconvertclass &inconvert) {
+  cgiargsclass::iterator here = args.begin();
+  cgiargsclass::iterator end = args.end();
+
+  while (here != end) {
+    if ((*here).second.value.getencoding() > 0) {
+      (*here).second.value = inconvert.convert((*here).second.value);
+    }
+    
+    ++here;
+  }
+}
+
+// fcgienv will be loaded with environment name-value pairs
+// if using fastcgi (had to do this as getenv doesn't work
+// with our implementation of fastcgi). if fcgienv is empty
+// we'll simply use getenv
+text_t gsdl_getenv (const text_t &name, text_tmap &fcgienv) {
+  if (fcgienv.empty()) {
+    char *n = name.getcstr();
+    char *v = getenv(n);
+    delete []n;
+    if (v != NULL) return v;
+    return g_EmptyText;
+
+  } else return fcgienv[name];
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiutils.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiutils.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiutils.h	(revision 16561)
@@ -0,0 +1,113 @@
+/**********************************************************************
+ *
+ * cgiutils.h -- general cgi utilities
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef CGIUTILS_H
+#define CGIUTILS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+
+// parse data obtained through a CGI POST request
+text_t parse_post_data (text_t &content_type, text_t &raw_post_data, 
+			fileupload_tmap &fileuploads, const text_t &gsdlhome);
+
+// convert %xx and + to their appropriate equivalents
+void decode_cgi_arg (text_t &argstr);
+
+// split up the cgi arguments
+void split_cgi_args (const cgiargsinfoclass &argsinfo, text_t argstr, 
+		     cgiargsclass &args);
+
+text_t encode_commas (const text_t &intext);
+text_t decode_commas (const text_t &intext);
+
+// turns any '-' in a cgi argument into "(-)"
+// set utf8 to true if input is in utf-8, otherwise expects input in unicode
+text_t minus_safe (const text_t &intext, bool utf8);
+
+// returns the encoded version of a cgi argument
+// the original text should be in utf8
+text_t cgi_safe_utf8 (const text_t &intext);
+// the original text should be in unicode
+text_t cgi_safe_unicode (const text_t &intext);
+
+// check_save_conf_str checks the configuration string for
+// the saved args and makes sure it does not conflict with
+// the information about the arguments. If an error is encountered
+// it will return false and the program should not produce any
+// output.
+bool check_save_conf_str (const text_t &saveconf, 
+			  const cgiargsinfoclass &argsinfo,
+			  ostream &logout);
+
+// create_save_conf_str will create a configuration string
+// based on the information in argsinfo. This method of configuration
+// is not recomended as small changes can produce large changes in
+// the resulting configuration string (for instance a totally different
+// ordering). Only arguments which "must" be saved are included in
+// the resulting string.
+text_t create_save_conf_str (const cgiargsinfoclass &argsinfo,
+			     ostream &logout);
+
+// expand_compress_args will expand the compressed arguments based
+// on compressconf placing the results in args if they are not
+// already defined. If it encounters an error it will return false
+// and output more information to logout.
+bool expand_save_args (const cgiargsinfoclass &argsinfo,
+		       const text_t &saveconf, 
+		       cgiargsclass &args,
+		       ostream &logout);
+
+// adds the default values for those arguments which have not
+// been specified
+void add_default_args (const cgiargsinfoclass &argsinfo,
+		       cgiargsclass &args,
+		       ostream &logout);
+
+void add_fileupload_args (const cgiargsinfoclass &argsinfo,
+			  cgiargsclass &args,
+			  fileupload_tmap &fileuploads,
+			  ostream &logout);
+
+// compress_save_args will compress the arguments and return
+// them in compressed_args. If an error was encountered
+// compressed_args will be set to to "", an error will be
+// written to logout, and the function will return false.
+bool compress_save_args (const cgiargsinfoclass &argsinfo,
+			 const text_t &saveconf,
+			 cgiargsclass &args,
+			 text_t &compressed_args,
+			 outconvertclass &outconvert,
+			 ostream &logout);
+
+// args_tounicode converts any arguments which are not in unicode
+// to unicode using inconvert
+void args_tounicode (cgiargsclass &args, inconvertclass &inconvert);
+
+text_t gsdl_getenv (const text_t &name, text_tmap &fcgienv);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.cpp	(revision 16561)
@@ -0,0 +1,747 @@
+/**********************************************************************
+ *
+ * cgiwrapper.cpp -- output pages using the cgi protocol
+ * 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.
+ *
+ *********************************************************************/
+
+#include <stdio.h>
+#ifdef __WIN32__
+#include <fcntl.h>
+#endif
+
+#include "gsdlconf.h"
+#include "cgiwrapper.h"
+#include "gsdlsitecfg.h"
+#include "maincfg.h"
+#include "fileutil.h"
+#include "cgiutils.h"
+#include <stdlib.h>
+#include <assert.h>
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+#ifdef USE_FASTCGI
+#include "fcgiapp.h"
+#endif
+
+
+#ifdef USE_FASTCGI
+// used to output the text from receptionist
+class fcgistreambuf : public streambuf {
+public:
+  fcgistreambuf ();
+  int sync ();
+  int overflow (int ch);
+  int underflow () {return EOF;}
+  
+  void fcgisbreset() {fcgx_stream = NULL; other_ostream = NULL;};
+  void set_fcgx_stream(FCGX_Stream *newone) {fcgx_stream=newone;};
+  void set_other_ostream(ostream *newone) {other_ostream=newone;};
+  
+private:
+  FCGX_Stream *fcgx_stream;
+  ostream *other_ostream;
+};
+
+fcgistreambuf::fcgistreambuf() {
+  fcgisbreset();
+  if (base() == ebuf()) allocate();
+  setp (base(), ebuf());
+};
+
+int fcgistreambuf::sync () {
+  if ((fcgx_stream != NULL) &&
+      (FCGX_PutStr (pbase(), out_waiting(), fcgx_stream) < 0)) {
+    fcgx_stream = NULL;
+  }
+
+  if (other_ostream != NULL) {
+    char *thepbase=pbase();
+    for (int i=0;i<out_waiting();++i) (*other_ostream).put(thepbase[i]);
+  }
+  
+  setp (pbase(), epptr());
+  
+  return 0;
+}
+
+int fcgistreambuf::overflow (int ch) {
+  if (sync () == EOF) return EOF;
+  if (ch != EOF) sputc (ch);
+  return 0;
+}
+
+#endif
+
+static void format_error_string (text_t &errorpage, const text_t &errortext, bool debug) {
+
+  errorpage.clear();
+
+  if (debug) {
+    errorpage += "\n";
+    errorpage += "ERROR: " + errortext;
+    errorpage += "\n";
+    
+  } else {
+
+    errorpage += "Content-type: text/html\n\n";
+    
+    errorpage += "<html>\n";
+    errorpage += "<head>\n";
+    errorpage += "<title>Error</title>\n";
+    errorpage += "</head>\n";
+    errorpage += "<body>\n";
+    errorpage += "<h2>Oops!</h2>\n";
+    errorpage += errortext;
+    errorpage += "</body>\n";
+    errorpage += "</html>\n";
+  }
+}
+
+static void page_errorcollect (const text_t &gsdlhome, text_t &errorpage, bool debug) {
+
+  text_t collecthome = filename_cat (gsdlhome, "collect");
+
+  text_t errortext = "No valid collections were found: Check that your collect directory\n";
+  errortext += "(" + collecthome + ") is readable and contains at least one valid collection.\n";
+  errortext += "Note that modelcol is NOT a valid collection.\n";
+  errortext += "If the path to your collect directory is wrong edit the 'gsdlhome' field\n";
+  errortext += "in your gsdlsite.cfg configuration file.\n";
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+static void page_errorsitecfg (text_t &errorpage, bool debug, int mode) {
+
+  text_t errortext;
+
+  if (mode == 0) {
+    errortext += "The gsdlsite.cfg configuration file could not be found. This\n";
+    errortext += "file should contain configuration information relating to this\n";
+    errortext += "site's setup.\n";
+
+  } else if (mode == 1) {
+    errortext += "The gsdlsite.cfg configuration file does not contain a valid\n";
+    errortext += "gsdlhome entry.\n";
+  }
+
+  if (debug) {
+    errortext += "gsdlsite.cfg should reside in the directory from which the\n";
+    errortext += "library executable was run.\n";
+  } else {
+    errortext += "gsdlsite.cfg should reside in the same directory as the library\n";
+    errortext += "executable file.\n";
+  }
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+
+static void page_errormaincfg (const text_t &gsdlhome, const text_t &collection,
+			       bool debug, text_t &errorpage) {
+
+  text_t errortext;
+
+  if (collection.empty()) {
+    text_t main_cfg_file = filename_cat (gsdlhome, "etc", "main.cfg");
+    errortext += "The main.cfg configuration file could not be found. This file\n";
+    errortext += "should contain configuration information relating to the\n";
+    errortext += "setup of the interface. As this receptionist is not being run\n";
+    errortext += "in collection specific mode the file should reside at\n";
+    errortext += main_cfg_file + ".\n";
+  } else {
+    text_t collect_cfg_file = filename_cat (gsdlhome, "collect", collection, "etc", "collect.cfg");
+    text_t main_collect_cfg_file = filename_cat (gsdlhome, "etc", "collect.cfg");
+    text_t main_cfg_file = filename_cat (gsdlhome, "etc", "main.cfg");
+    errortext += "Either the collect.cfg or main.cfg configuration file could\n";
+    errortext += "not be found. This file should contain configuration information\n";
+    errortext += "relating to the setup of the interface. As this receptionist is\n";
+    errortext += "being run in collection specific mode the file should reside\n";
+    errortext += "at either " + collect_cfg_file + ",\n";
+    errortext += main_collect_cfg_file + " or " + main_cfg_file + ".\n";
+  }
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+
+static void page_errorinit (const text_t &gsdlhome, bool debug, text_t &errorpage) {
+
+  text_t errortext = "An error occurred during the initialisation of the Greenstone Digital\n";
+  errortext += "Library software. It is likely that the software has not been setup\n";
+  errortext += "correctly.\n";
+
+  text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
+  char *efile = error_file.getcstr();
+  ifstream errin (efile);
+  delete []efile;
+  if (errin) {
+    errortext += "The error log, " + error_file + ", contains the\n";
+    errortext += "following information:\n\n";
+    if (!debug) errortext += "<pre>\n";
+
+    char c;
+    errin.get(c);
+    while (!errin.eof ()) {
+      errortext.push_back(c);
+      errin.get(c);
+    }
+    
+    if (!debug) errortext += "</pre>\n";
+
+    errin.close();
+
+  } else {
+    errortext += "Please consult " + error_file + " for more information.\n";
+  }
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+static void page_errorparseargs (const text_t &gsdlhome, bool debug, text_t &errorpage) {
+
+  text_t errortext = "An error occurred during the parsing of the cgi arguments.\n";
+
+  text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
+  char *efile = error_file.getcstr();
+  ifstream errin (efile);
+  delete []efile;
+  if (errin) {
+    errortext += "The error log, " + error_file + ", contains the\n";
+    errortext += "following information:\n\n";
+    if (!debug) errortext += "<pre>\n";
+
+    char c;
+    errin.get(c);
+    while (!errin.eof ()) {
+      errortext.push_back(c);
+      errin.get(c);
+    }
+    if (!debug) errortext += "</pre>\n";
+    errin.close();
+
+  } else {
+    errortext += "Please consult " + error_file + " for more information.\n";
+  }
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+static void page_errorcgipage (const text_t &gsdlhome, bool debug, text_t &errorpage) {
+
+  text_t errortext = "An error occurred during the construction of the cgi page.\n";
+
+  text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
+  char *efile = error_file.getcstr();
+  ifstream errin (efile);
+  delete []efile;
+  if (errin) {
+    errortext += "The error log, " + error_file + ", contains the\n";
+    errortext += "following information:\n\n";
+    if (!debug) errortext += "<pre>\n";
+
+    char c;
+    errin.get(c);
+    while (!errin.eof ()) {
+      errortext.push_back(c);
+      errin.get(c);
+    }
+    if (!debug) errortext += "</pre>\n";
+    errin.close();
+
+  } else {
+    errortext += "Please consult " + error_file + " for more information.\n";
+  }
+
+  format_error_string (errorpage, errortext, debug);
+}
+
+static void print_debug_info (receptionist &recpt) {
+
+  outconvertclass text_t2ascii;
+  const recptconf &configinfo = recpt.get_configinfo ();
+  text_t etc_dir = filename_cat (configinfo.gsdlhome, "etc");
+
+  cout << "\n";
+  cout << text_t2ascii
+       << "------------------------------------------------------------\n"
+       << "Configuration and initialization completed successfully.\n"
+       << "  Note that more debug information may be available in the\n"
+       << "  initialization and error log error.txt in " << etc_dir << ".\n"
+       << "------------------------------------------------------------\n\n";
+
+  bool colspec = false;
+  if (configinfo.collection.empty()) {
+    cout << "Receptionist is running in \"general\" (i.e. not \"collection "
+	 << "specific\") mode.\n";
+  } else {
+    cout << text_t2ascii 
+	 << "Receptionist is running in \"collection specific\" mode.\n"
+	 << "  collection=" << configinfo.collection << "\n"
+	 << "  collection directory=" << configinfo.collectdir << "\n";
+    colspec = true;
+  }
+  
+  cout << text_t2ascii << "  gsdlhome=" << configinfo.gsdlhome << "\n";
+  if (!configinfo.collecthome.empty())
+    cout << text_t2ascii << "  collecthome=" << configinfo.collecthome << "\n";
+  if (!configinfo.dbhome.empty())
+    cout << text_t2ascii << "  dbhome=" << configinfo.dbhome << "\n";
+  cout << text_t2ascii << "  httpprefix=" << configinfo.httpprefix << "\n";
+  cout << text_t2ascii << "  httpimg=" << configinfo.httpimg << "\n";
+  cout << text_t2ascii << "  gwcgi=" << configinfo.gwcgi << "\n\n"
+       << "  Note that unless gwcgi has been set from a configuration\n"
+       << "  file it is dependent on environment variables set by your\n"
+       << "  webserver. Therefore it may not have the same value when run\n"
+       << "  from the command line as it would be when run from your\n"
+       << "  web server.\n";
+  if (configinfo.usecookies) 
+    cout << "cookies are enabled\n";
+  else 
+    cout << "cookies are disabled\n";
+  if (configinfo.logcgiargs) 
+    cout << "logging is enabled\n";
+  else 
+    cout << "logging is disabled\n";
+  cout << "------------------------------------------------------------\n\n";
+
+  text_tset::const_iterator this_mfile = configinfo.macrofiles.begin();
+  text_tset::const_iterator end_mfile = configinfo.macrofiles.end();
+  cout << "Macro Files:\n"
+       << "------------\n";
+  text_t mfile;
+  bool found;
+  while (this_mfile != end_mfile) {
+    cout << text_t2ascii << *this_mfile;
+    int spaces = (22 - (*this_mfile).size());
+    if (spaces < 2) spaces = 2;
+    text_t outspaces;
+    for (int i = 0; i < spaces; ++i) outspaces.push_back (' ');
+    cout << text_t2ascii << outspaces;
+
+    found = false;
+    if (colspec) {
+      // collection specific - try collectdir/macros first
+      mfile = filename_cat (configinfo.collectdir, "macros", *this_mfile);
+      if (file_exists (mfile)) {
+	cout << text_t2ascii << "found (" << mfile << ")\n";
+	found = true;
+      }
+    }
+ 
+    if (!found) {
+      // try main macro directory
+      mfile = filename_cat (configinfo.gsdlhome, "macros", *this_mfile);
+      if (file_exists (mfile)) {
+	cout << text_t2ascii << "found (" << mfile << ")\n";
+	found = true;
+      }
+    }
+
+    if (!found)
+      cout << text_t2ascii << "NOT FOUND\n";
+
+    ++this_mfile;
+  }
+
+  cout << "------------------------------------------------------------\n\n"
+       << "Collections:\n"
+       << "------------\n"
+       << "  Note that collections will only appear as \"running\" if\n"
+       << "  their build.cfg files exist, are readable, contain a valid\n"
+       << "  builddate field (i.e. > 0), and are in the collection's\n"
+       << "  index directory (i.e. NOT the building directory)\n\n";
+
+  recptprotolistclass *protos = recpt.get_recptprotolist_ptr();
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+
+  bool is_z3950 = false;
+  bool found_valid_col = false;
+
+
+  while (rprotolist_here != rprotolist_end) {
+    comerror_t err;    
+    if ((*rprotolist_here).p == NULL) continue;
+    else if (is_z3950==false &&
+	     (*rprotolist_here).p->get_protocol_name(err) == "z3950proto") {
+      cout << "\nZ39.50 Servers:   (always public)\n"
+	   << "---------------\n";
+      is_z3950=true;
+    }
+
+    text_tarray collist;
+    (*rprotolist_here).p->get_collection_list (collist, err, cerr);
+    if (err == noError) {
+      text_tarray::iterator collist_here = collist.begin();
+      text_tarray::iterator collist_end = collist.end();
+      
+      while (collist_here != collist_end) {
+	
+	cout << text_t2ascii << *collist_here;
+	
+	int spaces = (22 - (*collist_here).size());
+	if (spaces < 2) spaces = 2;
+	text_t outspaces;
+	for (int i = 0; i < spaces; ++i) outspaces.push_back (' ');
+	cout << text_t2ascii << outspaces;
+	
+	  ColInfoResponse_t *cinfo = recpt.get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, cerr);
+	  if (cinfo != NULL) {
+	    if (cinfo->isPublic) cout << "public ";
+	    else cout << "private";
+
+	    if (cinfo->buildDate > 0) {
+	      cout << "   running    ";
+	      found_valid_col = true;
+	    } else {
+	      cout << "   not running";
+	    }
+	  }
+
+	  cout << "\n";
+
+	  ++collist_here;
+      }
+    }
+    is_z3950=false;
+    ++rprotolist_here;
+  } // end of while loop
+
+  if (!found_valid_col) {
+    cout << "WARNING: No \"running\" collections were found. You need to\n";
+    cout << "         build one of the above collections\n";
+  }
+
+  cout << "\n------------------------------------------------------------\n";
+  cout << "------------------------------------------------------------\n\n";
+  cout << "receptionist running in command line debug mode\n";
+  cout << "enter cgi arguments as name=value pairs (e.g. 'a=p&p=home'):\n";
+
+}
+
+// cgiwrapper does everything necessary to output a page
+// using the cgi protocol. If this is being run for a particular
+// collection then "collection" should be set, otherwise it
+// should equal "".
+void cgiwrapper (receptionist &recpt, text_t collection) {
+  int numrequests = 0;
+  bool debug = false;
+  const recptconf &configinfo = recpt.get_configinfo ();
+
+  // find out whether this is being run as a cgi-script
+  // or a fastcgi script
+#ifdef USE_FASTCGI
+  fcgistreambuf outbuf;
+  int isfastcgi = !FCGX_IsCGI();
+  FCGX_Stream *fcgiin, *fcgiout, *fcgierr;
+  FCGX_ParamArray fcgienvp;
+#else
+  int isfastcgi = 0;
+#endif
+
+  // we need gsdlhome to do fileupload stuff, so moved this configure stuff before the get argstr stuff
+  // init stuff - we can't output error pages directly with
+  // fastcgi so the pages are stored until we can output them
+  text_t errorpage;
+  outconvertclass text_t2ascii;
+
+  // set defaults
+  int maxrequests = 10000;
+  recpt.configure ("collection", collection);
+  recpt.configure ("httpimg", "/gsdl/images");
+  char *script_name = getenv("SCRIPT_NAME");
+  if (script_name != NULL) recpt.configure("gwcgi", script_name);
+  else recpt.configure("gwcgi", "/gsdl");
+
+  // read in the configuration files.
+  text_t gsdlhome;
+  text_t collecthome;
+  configurator gsdlconfigurator(&recpt);
+  if (!site_cfg_read (gsdlconfigurator, gsdlhome, collecthome, maxrequests)) {
+    // couldn't find the site configuration file
+    page_errorsitecfg (errorpage, debug, 0);
+  } else if (gsdlhome.empty()) {
+    // no gsdlhome in gsdlsite.cfg
+    page_errorsitecfg (errorpage, debug, 1);
+  } else if (!directory_exists(gsdlhome)) {
+    // gsdlhome not a valid directory
+    page_errorsitecfg (errorpage, debug, 1);
+  } else if (!main_cfg_read (recpt, gsdlhome, collecthome, collection)) {
+    // couldn't find the main configuration file
+    page_errormaincfg (gsdlhome, collection, debug, errorpage);
+  } else  if (configinfo.collectinfo.empty() && false) { // commented out for corba
+    // don't have any collections
+    page_errorcollect (gsdlhome, errorpage, debug);
+  }
+  // get the query string if it is not being run as a fastcgi
+  // script
+  text_t argstr = g_EmptyText;
+  fileupload_tmap fileuploads;
+  cgiargsclass args;
+  char *aURIStr;
+  if (!isfastcgi) {
+    char *request_method_str = getenv("REQUEST_METHOD");
+    char *content_length_str = getenv("CONTENT_LENGTH");
+    if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
+	content_length_str != NULL)  {
+      // POST form data
+      long content_length = (content_length_str ? atoi(content_length_str) : 0);
+      if (content_length > 0) {
+#ifdef __WIN32__
+	// On Windows it is important that standard input be read in binary
+	// mode, otherwise end of line "<CR><LF>" is turned into <LF> only
+	// which breaks the MIME standard (and our parsing code!)
+
+	int result = _setmode( _fileno( stdin ), _O_BINARY );
+	if( result == -1 ) {
+	  cerr << "Warning: Failed to set standard input to binary mode." << endl;
+	  cerr << "         Parsing of multi-part MIME will most likely fail" << endl;
+	}
+#endif
+
+	long length = content_length;
+	unsigned char * buffer = new unsigned char[content_length];
+
+	int chars_read = fread(buffer,1,content_length,stdin);
+
+	if (chars_read != content_length) {
+	  cerr << "Warning: mismatch between CONTENT_LENGTH and data read from standard in" << endl;
+	}
+
+	argstr.setcarr((char *)buffer, content_length);     
+
+	text_t content_type;
+	char *content_type_str = getenv("CONTENT_TYPE");
+	if (content_type_str) content_type = content_type_str;
+	argstr = parse_post_data(content_type, argstr, fileuploads, gsdlhome);
+      }
+    } else {
+      aURIStr = getenv("QUERY_STRING");
+      if ((request_method_str != NULL && strcmp(request_method_str, "GET") == 0)
+	  || aURIStr != NULL) {
+	// GET form data
+	if (aURIStr != NULL) argstr = aURIStr;
+      } else {
+	// debugging from command line
+	debug = true;
+      }
+    }
+  }
+
+  if (debug) {
+    cout << "Configuring Greenstone...\n";
+    cout << flush;
+  }
+
+
+  if (errorpage.empty()) {
+
+    // initialise the library software
+    if (debug) {
+      cout << "Initializing...\n";
+      cout << flush;
+    }
+
+    text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
+    char *eout = error_file.getcstr();
+    ofstream errout (eout, ios::app);
+    delete []eout;
+    if (!recpt.init(errout)) {
+      // an error occurred during the initialisation
+      errout.close();
+      page_errorinit(gsdlhome, debug, errorpage);
+    }
+    errout.close();
+  }
+
+  if (debug && errorpage.empty()) {
+    // get query string from command line
+    print_debug_info (recpt);
+    char cinURIStr[1024];
+    cin.get(cinURIStr, 1024);
+    argstr = cinURIStr;
+  }
+
+  // cgi scripts only deal with one request
+  if (!isfastcgi) maxrequests = 1;
+
+  // Page-request loop. If this is not being run as a fastcgi
+  // process then only one request will be processed and then
+  // the process will exit.
+  while (numrequests < maxrequests) {
+#ifdef USE_FASTCGI
+    if (isfastcgi) {
+      if (FCGX_Accept(&fcgiin, &fcgiout, &fcgierr, &fcgienvp) < 0) break;
+
+      char *request_method_str = FCGX_GetParam ("REQUEST_METHOD", fcgienvp);
+      char *content_length_str = FCGX_GetParam ("CONTENT_LENGTH", fcgienvp);
+
+      if (request_method_str != NULL && strcmp(request_method_str, "POST") == 0 &&
+	  content_length_str != NULL)  {
+	// POST form data
+	int content_length = text_t(content_length_str).getint();
+	if (content_length > 0) {
+	  argstr.clear();
+	  int c;
+	  do {
+	    c = FCGX_GetChar (fcgiin);
+	    if (c < 0) break;
+	    argstr.push_back (c);
+	    --content_length;
+	  } while (content_length > 0);
+	}
+
+      } else {
+	// GET form data
+	aURIStr = FCGX_GetParam("QUERY_STRING", fcgienvp);
+	if (aURIStr != NULL) argstr = aURIStr;
+	else argstr = g_EmptyText;
+      }
+    }
+#endif
+
+    // get output streams ready
+#ifdef USE_FASTCGI
+    outbuf.fcgisbreset ();
+    if (isfastcgi) outbuf.set_fcgx_stream (fcgiout);
+    else outbuf.set_other_ostream (&cout);
+    ostream pageout (&outbuf);
+#else
+#define pageout cout
+#endif
+
+    // if using fastcgi we'll load environment into a map,
+    // otherwise simply pass empty map (can't get environment
+    // variables using getenv() while using FCGX versions
+    // of fastcgi - at least I can't ;-) - Stefan)
+    text_tmap fastcgienv;
+#ifdef USE_FASTCGI
+    if (isfastcgi) {
+      for(; *fcgienvp != NULL; ++fcgienvp) {
+	text_t fvalue = *fcgienvp;
+	text_t::const_iterator begin = fvalue.begin();
+	text_t::const_iterator end = fvalue.end();
+	text_t::const_iterator equals_sign = findchar (begin, end, '=');
+	if (equals_sign != end)
+	  fastcgienv[substr(begin, equals_sign)] = substr(equals_sign+1, end);
+      }
+    }
+#endif
+
+    // temporarily need to configure gwcgi here when using fastcgi as I can't
+    // get it to pass the SCRIPT_NAME environment variable to the initial 
+    // environment (if anyone can work out how to do this using the apache 
+    // server, let me know). Note that this overrides the gwcgi field in 
+    // site.cfg (which it shouldn't do) but I can't at present set gwcgi 
+    // from site.cfg as I have old receptionists laying around that wouldn't
+    // appreciate it. The following 5 lines of code should be deleted once
+    // I either a: get the server to pass SCRIPT_NAME at initialization
+    // time or b: convert all the collections using old receptionists over
+    // to this version and uncomment gwcgi in the site.cfg file -- Stefan.
+#ifdef USE_FASTCGI
+    if (isfastcgi) {
+      recpt.configure("gwcgi", fastcgienv["SCRIPT_NAME"]);
+    }
+#endif
+
+
+    // if there has been no error so far, perform the production of the
+    // output page
+    if (errorpage.empty()) {
+      text_t error_file = filename_cat (gsdlhome, "etc", "error.txt");
+      char *eout = error_file.getcstr();
+      ofstream errout (eout, ios::app);
+      delete []eout;
+
+#if defined(__WIN32__) && defined(GSDL_USE_IOS_H)
+      // old Windows compilers (VC++4.2)
+      cerr = errout;
+#else
+      // can't do this anymore according to c++ standard...
+      // cerr = errout;
+      // ... but can do this instead
+      streambuf* errbuf = cerr.rdbuf(errout.rdbuf());
+#endif
+
+      // parse the cgi arguments and produce the resulting page if there
+      // has been no errors so far
+      if (!recpt.parse_cgi_args (argstr, fileuploads, args, errout, fastcgienv)) {
+	errout.close ();
+	page_errorparseargs(gsdlhome, debug, errorpage);
+      } else {
+	// produce the output page
+
+	if (!recpt.produce_cgi_page (args, pageout, errout, fastcgienv)) {
+	  errout.close ();
+	  page_errorcgipage(gsdlhome, debug, errorpage);
+	}
+	recpt.log_cgi_args (args, errout, fastcgienv);
+	errout.close ();
+      }
+
+#if !defined(__WIN32__) || !defined(GSDL_USE_IOS_H)
+    // restore the cerr buffer
+    cerr.rdbuf(errbuf);
+#endif
+    }
+    // clean up any files that were uploaded 
+    fileupload_tmap::const_iterator this_file = fileuploads.begin();
+    fileupload_tmap::const_iterator end_file = fileuploads.end();
+    while (this_file != end_file)
+      {
+	if (file_exists((*this_file).second.tmp_name)) 
+	  {
+	    char *thefile = (*this_file).second.tmp_name.getcstr();
+	    unlink(thefile);
+	    delete [] thefile;
+	  }
+	++this_file;
+      }
+    
+    // there was an error, output the error page
+    if (!errorpage.empty()) {
+      pageout << text_t2ascii << errorpage;
+      errorpage.clear();
+      numrequests = maxrequests; // make this the last page
+    }
+    pageout << flush;
+    
+    // finish with the output streams
+#ifdef USE_FASTCGI
+    if (isfastcgi) FCGX_Finish();
+#endif
+
+    ++numrequests;
+  }
+
+  return;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/cgiwrapper.h	(revision 16561)
@@ -0,0 +1,44 @@
+/**********************************************************************
+ *
+ * cgiwrapper.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.
+ *
+ *********************************************************************/
+
+
+#ifndef CGIWRAPPER_H
+#define CGIWRAPPER_H
+
+
+#include "receptionist.h"
+
+// cgiwrapper does everything necessary to output a page
+// using the cgi protocol. If this is being run for a particular
+// collection then "collection" should be set, otherwise it
+// should equal "".
+//
+// NB: the collectset MAY NOT exist for remote protocols (e.g.
+//     the corba protocol; as references must point to a non-
+//     null instance, we must therefore use a pointer - sorry!
+void cgiwrapper (receptionist &recpt, text_t collection);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/collectoraction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/collectoraction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/collectoraction.cpp	(revision 16561)
@@ -0,0 +1,1476 @@
+/**********************************************************************
+ *
+ * collectoraction.cpp -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_COLLECTOR_ACTION
+
+// note that the collectoraction relies on having direct access to a
+// collections configuration file. this breaks the separation between
+// receptionist and collection server and so is not suitable (at least
+// in its current form) for use when collection servers are separate 
+// from the receptionist (e.g. when using the CORBA protocol).
+
+// following line required to get fstream.filedesc() on darwin (Mac OS X)
+#define _STREAM_COMPAT  1
+// required for utsname on solaris???
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include "collectoraction.h"
+#include "OIDtools.h"
+#include "fileutil.h"
+#include "cfgread.h"
+#include "gsdltools.h"
+#include "gsdltimes.h"
+#include "argdb.h"
+#include "cgiutils.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if !defined (__WIN32__)
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+collectoraction::collectoraction () 
+  : wizardaction()
+{
+  macro_prefix = "bc1";
+
+  do_mkcol = false;
+  badsources = false;
+  failedsources.erase(failedsources.begin(), failedsources.end());
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "collector";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "p";
+  arg_ainfo.longname = "page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "intro";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // temporary directory name for this collector
+  // session
+  arg_ainfo.shortname = "bc1tmp";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1fullname";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1dirname";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1contactemail";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1aboutdesc";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1clone";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1clonecol";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // set when cloning option has changed
+  arg_ainfo.shortname = "bc1clonechanged";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; 
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // only set when one of the fields was changed in
+  // the "collection info" page
+  arg_ainfo.shortname = "bc1infochanged";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // only set when cfg file is altered from within
+  // "configure collection" page
+  arg_ainfo.shortname = "bc1cfgchanged";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "cfgfile";
+  arg_ainfo.longname = "configuration file contents";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1dodelete";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // will be set if we arrived at the "configure collection" page
+  // via the "changing an existing collection" page
+  arg_ainfo.shortname = "bc1econf";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // will be set if we arrived at the "source data" page
+  // via the "changing an existing collection" page
+  arg_ainfo.shortname = "bc1esrce";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1inputnum";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "3";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1input";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.multiplevalue = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bc1inputtype";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.multiplevalue = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // will be set when we've just come from the "source data" page
+  arg_ainfo.shortname = "bc1fromsrce";
+  arg_ainfo.longname = "collector specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.multiplevalue = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+collectoraction::~collectoraction () {
+}
+
+
+
+bool collectoraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+				     recptprotolistclass * /*protos*/, ostream &logout) {
+
+  text_t &current_page = args["p"];
+
+  // note that the "bildstatus" and "bildframe1" pages don't actually do anything
+  // functional so we don't need to worry about authenticating them (it's the 
+  // underlying "bild" page that does the building (and creates the frameset))
+  // This helps us overcome a bit of a problem we have with multiple pages trying
+  // to read from the key.db database at the same time.
+  if (current_page != "intro" && current_page != "bildstatus" && current_page != "bildframe1") {
+    // authenticate the user if authentication is available
+    args["uan"] = 1;
+    args["ug"] = "all-collections-editor";
+  }
+  
+  if (current_page == "new" || current_page == "existing") {
+
+    // assign (and create) a temporary directory 
+    if (assign_tmpname (args, logout)==false) {
+      // there was an error creating the tmp dir
+      message="tmpfail";
+      return true; // true because we could still parse the arguments
+    }
+
+    // clean up any old builds left laying about in the tmp directory
+    // (note that it's possible this could take some time if there's a huge
+    // partially built collecton laying about so we'll make it an asynchronous
+    // system call)
+    gsdl_system ("perl -S cleantmp.pl", false, logout);
+  }
+
+  if (current_page != "intro" && current_page != "bildstatus" && 
+      current_page != "bildframe1" && current_page != "new") {
+    // update arguments that were saved to the harddrive
+    text_tmap saved_args;
+    saved_args["bc1fullname"] = g_EmptyText;
+    saved_args["bc1contactemail"] = g_EmptyText;
+    saved_args["bc1aboutdesc"] = g_EmptyText;
+    saved_args["bc1clone"] = g_EmptyText;
+    saved_args["bc1clonecol"] = g_EmptyText;
+    saved_args["bc1inputnum"] = g_EmptyText;
+    saved_args["bc1input"] = g_EmptyText;
+    saved_args["bc1inputtype"] = g_EmptyText;
+
+    // update the argdb database with any arguments that were set
+    // by previous page
+    text_tmap::iterator here = saved_args.begin();
+    text_tmap::iterator end = saved_args.end();
+    while (here != end) {
+      if (args.lookupcgiarg((*here).first).source != cgiarg_t::default_arg) {
+	(*here).second = args[(*here).first];
+      }
+      ++here;
+    }
+
+    text_t argfile = filename_cat(gsdlhome, "tmp", args["bc1tmp"], "argdb.db");
+    argdb *args_on_disk = new argdb(argfile);
+    if (!args_on_disk->update_args(saved_args)) {
+      // error
+      logout << "collectoraction: argdb::update_args failed (" << argfile << ")\n";
+    }
+
+    // update args from argdb
+    saved_args.erase(saved_args.begin(), saved_args.end());
+    if (!args_on_disk->get_args(saved_args)) {
+      // error
+      logout << "collectoraction: argdb::get_args failed (" << argfile << ")\n";
+    }
+    delete args_on_disk;
+    here = saved_args.begin();
+    end = saved_args.end();
+    while (here != end) {
+      if (!(*here).second.empty()) {
+	args[(*here).first] = (*here).second;
+      }
+      ++here;
+    }
+  }
+
+  if (args["bc1infochanged"] == "1") {
+    
+    if (args["bc1dirname"].empty()) {
+      // we've just come from the "collection information" page for the 
+      // first time so we'll need to create the collection with mkcol.pl
+      // and set up bc1dirname - we do this part here instead of in do_action
+      // because the bc1dirname argument must be set to its new value before
+      // the compressedoptions macros are set.
+      args["bc1dirname"] = get_directory_name (args["bc1fullname"]);
+
+      text_t createfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], ".create");
+      if (!file_exists (createfile)) {
+	// we could do the mkcol.pl here but I guess it's nicer to do it in do_action()
+	do_mkcol = true; 
+      } else {
+	// .create file already exists but bc1dirname wasn't set ... this should only be
+	// able to occur when the "reload" (and possibly the "back" and "forward" buttons)
+	// have been used to get us here.
+	// we'll check that the bc1dirname directory exists (in case of the unlikely 
+	// possibility that get_directory_name returned a different value this time
+	// than it did originally).
+	text_t coldir = filename_cat (get_collectdir(args), args["bc1dirname"]);
+	if (!directory_exists (coldir)) {
+	  message = "reloaderror";
+	  return true;
+	}
+      }
+    } else {
+      // "collection information" has been changed after collection already exists
+      // so we'll need to update the cfg file.
+      update_cfgfile_partial (args, false, logout);
+    }
+  }
+
+  if (args["bc1cfgchanged"] == "1") {
+    // configuration file has been changed from the "configure collection"
+    // page. we need to update the file on disk and catch bc1 arguments up
+    // with changes.
+    update_cfgfile_complete (args, logout);
+  }
+  
+  if (args["bc1clonechanged"] == "1") {
+    // cloning option has been changed on "source data" page. if it was turned 
+    // on we want to create a new collect.cfg file using the bc1clonecol cfg file
+    // as a model (we'll save the old file as collect.cfg.org). if cloning was
+    // turned off we'll revert to using the collect.cfg.org file (which will need
+    // updating in case the bc1 arguments have been altered since cloning was 
+    // turned on).
+    update_cfgfile_clone (args, logout);
+
+    // if cloning has just been turned on we'll also copy the rest of the files 
+    // (excluding collect.cfg which we've already done) from the cloned collections
+    // etc directory to the new collection.
+    if (args["bc1clone"] == "1") {
+      text_t clone_etc = filename_cat(collecthome, args["bc1clonecol"], "etc");
+      text_t new_etc = filename_cat(get_collectdir(args), args["bc1dirname"], "etc");
+      text_tarray files;
+
+      if (read_dir (clone_etc, files)) {
+	text_tarray::const_iterator here = files.begin();
+	text_tarray::const_iterator end = files.end();
+	while (here != end) {
+	  if (*here != "collect.cfg" && *here != "collect.cfg.org") {
+	    file_copy (filename_cat(clone_etc, *here), filename_cat(new_etc, *here));
+	  }
+	  ++here;
+	}
+      } else {
+	outconvertclass text_t2ascii;
+	logout <<text_t2ascii << "collectoraction::check_cgiargs couldn't read from " 
+	       << clone_etc << " directory\n";
+      }
+    }
+  }
+  
+  if (current_page == "bildstatus" || current_page == "bildcancel") {
+    // if .final file exists then build has finished
+    text_t fbld = filename_cat (gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld.final");
+    if (file_exists (fbld)) {
+      char *fbldc = fbld.getcstr();
+      ifstream fbld_in (fbldc);
+      if (fbld_in) {
+	failcode = fbld_in.get();
+	fbld_in.close();
+	if (failcode == '0') {
+	  // success - we need to create and configure a collection server for the
+	  // newly built collection (for fastcgi and local library where
+	  // initialization isn't going to be redone when the user clicks the
+	  // "view your new collection" button
+	  create_colserver (args["bc1dirname"], logout);
+	  current_page = "bilddone";
+	}
+	else current_page = "bildfail";
+      } else {
+	// assume build failed (we shouldn't get here though ... right?)
+	current_page = "bildfail";
+      }
+      delete []fbldc;
+    }
+  }
+
+  if (args["bc1fromsrce"] == "1") {
+
+    // we've just come from the "source data" page so we need to check that
+    // input sources are valid
+    if (!check_sources(args, logout)) {
+      args["p"] = "srce";
+    }
+  }
+
+  return true;
+}
+
+void collectoraction::update_cfgfile_clone (cgiargsclass &args, ostream &logout) {
+
+  text_t tmpdir = filename_cat(gsdlhome, "tmp", args["bc1tmp"]);
+  text_t cfgfile = filename_cat(tmpdir, args["bc1dirname"], "etc", "collect.cfg");
+  if (!file_exists (cfgfile)) {
+    message = "tmpfail";
+    return;
+  }
+
+  text_t cfgfile_org = filename_cat (tmpdir, "collect.cfg.org");
+
+  if (args["bc1clone"] == "1") {
+    // cloning was turned on
+
+    text_t cfgfile_clone = filename_cat(collecthome, args["bc1clonecol"], "etc", "collect.cfg");
+    if (file_exists (cfgfile_clone)) {
+      // if .org file doesn't exist already create it
+      if (!file_exists (cfgfile_org)) {
+	if (!file_copy (cfgfile, cfgfile_org)) {
+	  message = "tmpfail";
+	  return;
+	}
+      }
+      // copy clone collections cfg file to new collection
+      if (!file_copy (cfgfile_clone, cfgfile)) {
+	message = "tmpfail";
+	return;
+      }
+      // update the new cfg file
+      update_cfgfile_partial (args, true, logout);
+      
+    } else {
+      // can't clone non-existant or read-protected collection
+      message = "clonefail";
+    }
+
+  } else {
+    // cloning has been turned off having been on at some point. the .org file
+    // should exist, if it doesn't we'll bail out and leave the user with the 
+    // cloned copy
+    if (file_exists (cfgfile_org)) {
+      // copy original back again and update it with any recent changes
+      if (file_copy (cfgfile_org, cfgfile)) {
+	update_cfgfile_partial (args, false, logout);
+      } else {
+	message = "tmpfail";
+      }
+    }
+  }
+}
+
+// update configuration file on disk to match bc1 arguments
+// there's a special case if the clone option is true as certain parts of a
+// config file should not be cloned (e.g. the iconcollection stuff)
+void collectoraction::update_cfgfile_partial (cgiargsclass &args, bool clone, ostream &logout) {
+
+  text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
+  char *cfgfilec = cfgfile.getcstr();
+
+#if defined (__WIN32__)
+  // make sure collect.cfg isn't read-only
+  _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
+#endif
+
+  vector<text_tarray> cfgarray;
+
+  // read in cfg file
+  ifstream cfg_in (cfgfilec);
+  if (cfg_in) {
+    text_tarray cfgline;
+    while (read_cfg_line(cfg_in, cfgline) >= 0) {
+      if (cfgline.size () >= 2) {
+	if (cfgline[0] == "creator" || cfgline[0] == "maintainer") {
+	  cfgline[1] = args["bc1contactemail"];
+	} else if (cfgline[0] == "collectionmeta") {
+	  if (cfgline[1] == "collectionname") {
+	    cfgline[2] = args["bc1fullname"];
+	  } else if (cfgline[1] == "collectionextra") {
+	    cfgline[2] = carriage_replace (args["bc1aboutdesc"], 0);
+	  } else if (clone && (cfgline[1] == "iconcollection" ||
+			       cfgline[1] == "iconcollectionsmall")) {
+	    cfgline[2] = g_EmptyText;
+	  }
+	}
+      }
+      cfgarray.push_back (cfgline);
+    }
+    cfg_in.close();
+    
+    // now write cfg file back out
+    int fd=open(cfgfilec, O_WRONLY | O_CREAT | O_TRUNC
+#if defined(__WIN32__)
+		| O_BINARY
+#endif
+		);
+
+    if (fd != -1) {
+      // lock the file
+      int lock_val = 1;
+      GSDL_LOCK_FILE (fd);
+      if (lock_val != 0) {
+	logout << "Error: Couldn't lock file " << cfgfilec << "\n";
+	close(fd);
+	message = "tmpfail";
+	
+      } else {
+
+	vector<text_tarray>::const_iterator this_line = cfgarray.begin();
+	vector<text_tarray>::const_iterator end_line = cfgarray.end();
+	while (this_line != end_line) {
+	  write_cfg_line (fd, *this_line);
+	  ++this_line;
+	}
+	GSDL_UNLOCK_FILE (fd);
+	close(fd);
+      }
+
+    } else {
+      logout << "collectoraction::update_cfgfile_partial: unable to open "
+	     << cfgfilec << " for output\n";
+      message = "tmpfail";
+    }
+    
+  } else {
+    logout << "collectoraction::update_cfgfile_partial: unable to open "
+	   << cfgfilec << " for input\n";
+    message = "tmpfail";
+  }
+
+  delete []cfgfilec;
+}
+
+// replace configuration file on disk with that in the cfgfile argument and 
+// catch other bc1 arguments up with those the new cfgfile contains
+void collectoraction::update_cfgfile_complete (cgiargsclass &args, ostream &logout) {
+
+  text_t cfgfile = filename_cat(get_collectdir(args), args["bc1dirname"], "etc", "collect.cfg");
+  char *cfgfilec = cfgfile.getcstr();
+
+#ifdef __WIN32__
+  // make sure collect.cfg isn't read-only
+  _chmod (cfgfilec, _S_IREAD | _S_IWRITE);
+#endif
+
+  int fd=open(cfgfilec, O_WRONLY | O_CREAT | O_TRUNC
+#if defined(__WIN32__)
+	      | O_BINARY
+#endif
+	      );
+
+  if (fd) {
+    // lock the file
+    int lock_val = 1;
+    GSDL_LOCK_FILE (fd);
+    if (lock_val != 0) {
+      logout << "Error: Couldn't lock file " << cfgfilec << "\n";
+      close(fd);
+      message = "tmpfail";
+      
+    } else {
+      
+      outconvertclass text_t2ascii;
+      text_t2ascii.setinput(&args["cfgfile"]);
+      size_t buffersize=args["cfgfile"].size();
+      char *buffer=new char[buffersize];
+      buffer[0]='\n'; // just in case something goes wrong...
+      size_t num_chars;
+      convertclass::status_t status;
+      text_t2ascii.convert(buffer, buffersize, num_chars, status);
+      // ignore status - assume it is "finished" as buffer is big enough
+      write(fd, buffer, num_chars);
+      GSDL_UNLOCK_FILE (fd);
+      close(fd);
+      delete []buffer;
+      
+      // now that we've written the file we'll read it back again and
+      // update our bc1 arguments
+      ifstream cfg_in (cfgfilec);
+      if (cfg_in) {
+	text_tarray cfgline;
+	while (read_cfg_line(cfg_in, cfgline) >= 0) {
+	  if (cfgline.size () >= 2) {
+	    if (cfgline[0] == "creator") {
+	      args["bc1contactemail"] = cfgline[1];
+	    } else if (cfgline[0] == "collectionmeta") {
+	      if (cfgline[1] == "collectionname") {
+		args["bc1fullname"] = cfgline[2];
+	      } else if (cfgline[1] == "collectionextra") {
+		args["bc1aboutdesc"] = carriage_replace (cfgline[2], 1);
+	      }
+	    }
+	  }
+	}
+	cfg_in.close();
+      } else {
+	logout << "collectoraction::update_cfgfile_complete: unable to open " 
+	       << cfgfilec << " for input\n";
+	message = "tmpfail";
+      }
+    }
+  } else {
+    logout << "collectoraction::update_cfgfile_complete: unable to open " 
+	   << cfgfilec << " for output\n";
+    message = "tmpfail";
+  }
+
+  delete []cfgfilec;
+}
+
+
+// return html for buttons used in collector bar
+// color may be "green", "grey", or "yellow"
+// type may be:
+//   "info" --> "collection information" button
+//   "srce" --> "source data" button
+//   "conf" --> "configure collection" button
+//   "bild" --> "build collection" button
+//   "view" --> "view collection" button
+// if enabled is true button will be flashy rollover type and 
+// will be hyperlinked
+
+// Wendy left a comment suggesting this be move, but where to
+// was not specified.  Into wizardaction.cpp?
+
+// set the _fullnamemenu_ macro (and _warnindex_ and _selectedindex_ if
+// we're on the "srce" page)
+void collectoraction::set_fullnamemenu (displayclass &disp, cgiargsclass &args, 
+					recptprotolistclass *protos, ostream &logout) {
+
+  if (recpt == NULL) {
+    logout << "ERROR (collectoraction::set_fullnamemenu): This action does not contain\n"
+	   << "       information about any receptionists. The method set_receptionist was\n"
+	   << "       probably not called from the module which instantiated this action.\n";
+    return;
+  }
+
+  text_t &current_page = args["p"];
+  text_t currentname = args[macro_prefix+"dirname"];
+  if (current_page == "srce") currentname = args[macro_prefix + "clonecol"];
+
+  text_tarray dirnames;
+  text_tarray fullnames;
+  vector<bool> write_protected;
+  bool is_selected = false;
+  int selected_index = 0;
+  int index = 0;
+
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+
+      // don't include z39.50 collections
+      comerror_t err = noError;
+      if ((*rprotolist_here).p->get_protocol_name (err) == "z3950proto") {
+	++rprotolist_here;
+	continue;
+      }
+
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+	while (collist_here != collist_end) {
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  if (cinfo != NULL) {
+	    text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	    if (collectionname.empty()) {
+	      collectionname = *collist_here;
+	    }
+	    dirnames.push_back(*collist_here);
+	    fullnames.push_back(collectionname);
+	    // check to see if the collection is writable
+	    if (collection_protected (*collist_here)) write_protected.push_back(true);
+	    else write_protected.push_back(false);
+
+	    if (*collist_here == currentname) {
+	      is_selected = true;
+	      selected_index = index; 	    
+	    }
+	    ++index;
+	  }
+	  ++collist_here;
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  bool first = true;
+
+  text_t warnindex;
+  text_t fullnamemenu = "<select name=\""+macro_prefix+"dirname\" onChange=\"menuchange();\">\n";
+
+  if (current_page == "srce") {
+    fullnamemenu = "<select name=\""+macro_prefix+"clonecol\" onChange=\"menuchange();\">\n";
+    fullnamemenu += "<option value=defaultstructure";
+    if (!is_selected) fullnamemenu += " selected>";
+    else fullnamemenu.push_back('>');
+    fullnamemenu += "_collector:textdefaultstructure_\n";
+  }
+
+  fullnamemenu += "<option value=\"\"></option>\n";
+
+  for (int i = 0; i < index; ++i) {
+    // don't want write protected collections in list on "change existing
+    // collection" page
+    if (write_protected[i] && current_page == "existing") continue;
+    fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
+    if ((i == 0 && !is_selected && current_page != "srce") || 
+	(is_selected && i == selected_index)) {
+      fullnamemenu += " selected";
+      ++selected_index;
+      is_selected = false;
+    }
+    fullnamemenu.push_back ('>');
+    fullnamemenu += fullnames[i];
+    fullnamemenu.push_back ('\n');
+    
+    // add to Warnindex if collection uses any dubious plugins 
+    // (if creating clone collection list)
+    if (current_page == "srce") {
+      if (first) warnindex += "0,";
+      else warnindex.push_back(',');
+      if (uses_weird_plugin (dirnames[i])) {
+	warnindex += text_t (1);
+      } else {
+	warnindex += text_t (0);
+      }
+    }
+    first = false;
+  }
+  fullnamemenu += "</select>\n";
+  text_t action_name = get_action_name();
+  disp.setmacro ("fullnamemenu", action_name, fullnamemenu);
+  if (current_page == "srce") {
+    disp.setmacro ("warnindex",action_name , warnindex);
+    disp.setmacro ("selectedindex", action_name, text_t(selected_index));
+  }
+}
+
+
+// set _sourcelist_ and _badsources_ macros
+void collectoraction::set_inputsourceboxes (displayclass &disp, cgiargsclass &args, 
+					    ostream &logout) {
+  
+  if (badsources) disp.setmacro ("badsources", "collector", "1");
+
+  text_t sourcelist = get_source_box(args["bc1input"], args["bc1inputnum"].getint(),
+				     args["bc1inputtype"]);
+
+  disp.setmacro("sourcelist", "collector", sourcelist);
+
+  // reset badsources and failedsources variables
+  badsources = false;
+  failedsources.erase(failedsources.begin(), failedsources.end());
+}
+
+text_t collectoraction::get_source_box (text_t inputarglist, int numboxes,
+					text_t inputtypelist) {
+
+  text_tarray inputvalues;
+  splitchar (inputarglist.begin(), inputarglist.end(), ',', inputvalues);
+  // remove any empty values from the end of the array
+  if (inputvalues.size()) {
+    text_tarray::iterator l = inputvalues.end() - 1;
+    text_tarray::iterator b = inputvalues.begin();
+    while ((*l).empty() && l >= b) {
+      --l;
+    }
+    inputvalues.erase(l+1, inputvalues.end());
+  }
+
+  text_tarray inputtypes;
+  splitchar (inputtypelist.begin(), inputtypelist.end(), ',', inputtypes);
+
+  int numvalues = inputvalues.size();
+  int numtypes = inputtypes.size();
+
+  text_t last = "file://";
+  text_t rv;
+  for (int i = 0; i < numboxes; ++i) {
+    rv += "<nobr><select name=\"bc1inputtype\">\n";
+    rv += "<option value=\"file://\"";
+    if ((i < numtypes && inputtypes[i] == "file://") ||
+	(numboxes == 3 && i == 0 && numvalues == 0) ||
+	(i >= 3 && i >= numvalues && last == "file://")) {
+      rv += " selected";
+      last = "file://";
+    }
+    rv += ">file://\n";
+    rv += "<option value=\"http://\"";
+    if ((i < numtypes && inputtypes[i] == "http://") ||
+	(numboxes == 3 && i == 1 && numvalues == 0) ||
+	(i >= 3 && i >= numvalues && last == "http://")) {
+      rv += " selected";
+      last = "http://";
+    }
+    rv += ">http://\n";
+    rv += "<option value=\"ftp://\"";
+    if ((i < numtypes && inputtypes[i] == "ftp://") ||
+	(numboxes == 3 && i == 2 && numvalues == 0) ||
+	(i >= 3 && i >= numvalues && last == "ftp://")) {
+      rv += " selected";
+      last = "ftp://";
+    }
+    rv += ">ftp://\n";
+    rv += "</select>\n";
+    rv += "<input type=text name=\"bc1input\" value=\"";
+    if (i < numvalues) {
+      rv += dm_safe(decode_commas(inputvalues[i]));
+    }
+    rv += "\" size=50>";
+    if (badsources) {
+      if ((i < numvalues) && (!inputvalues[i].empty())) {
+	if (failedsources[decode_commas(inputvalues[i])] == "1") {
+	  rv += "_iconcross_";
+	} else {
+	  rv += "_icontick_";
+	}
+      } else {
+	rv += "_iconblank_";
+      }
+    }
+    if (i+1 == numboxes) {
+      if (!badsources) rv += "_iconblank_";
+      rv += "_imagemore_</nobr><br>";
+    } else {
+      rv += "</nobr><br>\n";
+    }
+  }
+  
+  return rv;
+}
+
+// set the _cfgfile_ macro
+void collectoraction::set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout) {
+
+  text_t &collection = args["bc1dirname"];
+  if (collection.empty()) {
+    message = "nocollection";
+    return;
+  }
+
+  // read in collect.cfg      
+  text_t cfgfile = filename_cat(get_collectdir(args), collection, "etc", "collect.cfg");  
+  char *cfgfilec = cfgfile.getcstr();
+
+#ifdef GSDL_USE_IOS_H
+  ifstream cfg_ifs (cfgfilec, ios::in | ios::nocreate);
+#else
+  ifstream cfg_ifs (cfgfilec, ios::in);
+#endif
+  
+  if (cfg_ifs) {  
+    // read in collect.cfg      
+    text_t cfgtext;
+    char c;
+    cfg_ifs.get(c);
+    while (!cfg_ifs.eof ()) {
+      cfgtext.push_back(c);
+      cfg_ifs.get(c);
+    }
+    cfg_ifs.close();
+    
+    // define it as a macro
+    disp.setmacro("cfgfile", "collector", dm_safe(cfgtext));
+
+  } else {
+    logout << "collectoraction::set_cfgfile: couldn't open configuration file (" 
+	   << cfgfilec << ") for reading\n";
+    message = "tmpfail";
+  }    
+  delete []cfgfilec;
+}
+
+
+void collectoraction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					      recptprotolistclass *protos, ostream &logout) {
+
+  // define_internal_macros sets the following macros:
+  // _collectorbar_
+  // _pagescriptextra_
+  // _fullnamemenu_ -- if displaying the "source data" page or the "changing existing 
+  //                   collection" page
+  // _cfgfile_ -- if displaying the "configure collection" page
+  // _statusline_ -- if displaying the bildstatus page
+  // _header_ -- may be set for pages that require it
+  // _textfailmsg_ -- set to different messages depending on failcode returned
+  //                  by build script (if build fails)
+  // _faillog_ - set to last 6 lines of .bld file if build failed
+  // _gsdlhome_ - the gsdlhome path (dm_safe)
+  // _sourcelist_ -- "input source" text boxes
+  // _badsources_ -- will be set to "1" if we've come from the 
+  //                 "source data" page and there's a problem 
+  //                 with the input sources
+
+  text_t &collector_page = args["p"];
+  int esrce = args["bc1esrce"].getint();
+  int econf = args["bc1econf"].getint();
+
+  // set _pagescriptextra_ macro to _cpagescriptextra_
+  disp.setmacro ("pagescriptextra", "collector", "_" + collector_page + "scriptextra_");
+
+  if (collector_page == "bildstatus" || collector_page == "bilddone" || 
+      collector_page == "bildfail" || collector_page == "bildframe1") {
+    disp.setmacro ("header", "collector", "_" + collector_page + "header_");
+  }
+
+  // set the collectorbar macro
+  text_t collectorbar = "<table class=wizardbar border=0 cellspacing=4 cellpadding=0><tr>\n";
+
+  if (collector_page == "new") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "info", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "srce", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "conf", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "bild", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "view", false);
+
+  } else if (collector_page == "info") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "info", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "srce", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "conf", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "bild", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "view", false);
+    collectorbar += "</tr><tr><td></td><td align=center>_icongreyuparrow_</td><td colspan=8></td>\n";
+
+  } else if (collector_page == "srce") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    if (esrce == 1) {
+      // if we came from the "change an existing collection" page previous button(s)
+      // are disabled
+      collectorbar += get_button (args,collector_page, "grey", "info", false);
+    } else {
+      collectorbar += get_button (args,collector_page, "yellow", "info", true);
+    }
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "srce", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "conf", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "bild", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "view", false);
+    collectorbar += "</tr><tr><td colspan=3></td><td align=center>_icongreyuparrow_</td><td colspan=6></td>\n";
+
+  } else if (collector_page == "conf") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    // disable appropriate buttons if we came from "change an existing collection"
+    // page
+    if (esrce == 1 || econf == 1) {
+      collectorbar += get_button (args,collector_page, "grey", "info", false);
+    } else {
+      collectorbar += get_button (args,collector_page, "yellow", "info", true);
+    }
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    if (econf == 1) {
+      collectorbar += get_button (args,collector_page, "grey", "srce", false);
+    } else {
+      collectorbar += get_button (args,collector_page, "yellow", "srce", true);
+    }
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "conf", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "bild", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "view", false);
+    collectorbar += "</tr><tr><td colspan=5></td><td align=center>_icongreyuparrow_</td><td colspan=4></td>\n";
+
+  } else if (collector_page == "bilddone") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    // all previous buttons grey after build was completed
+    collectorbar += get_button (args,collector_page, "grey", "info", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "srce", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "conf", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "bild", false);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "green", "view", true);
+    collectorbar += "</tr><tr><td colspan=7></td><td align=center>_icongreyuparrow_</td><td colspan=2></td>\n";
+
+  } else if (collector_page == "bildcancel" || collector_page == "bildfail") {
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    // disable appropriate buttons if we came from "change an existing collection"
+    // page
+    if (esrce == 1 || econf == 1) {
+      collectorbar += get_button (args,collector_page, "grey", "info", false);
+    } else {
+      collectorbar += get_button (args,collector_page, "yellow", "info", true);
+    }
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    if (econf == 1) {
+      collectorbar += get_button (args,collector_page, "grey", "srce", false);
+    } else {
+      collectorbar += get_button (args,collector_page, "yellow", "srce", true);
+    }
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "conf", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "yellow", "bild", true);
+    collectorbar += "<td>_icongreyarrow_</td>\n";
+    collectorbar += get_button (args,collector_page, "grey", "view", false);
+  }
+
+  collectorbar += "</tr></table>\n";
+  disp.setmacro ("collectorbar", "collector", collectorbar);
+
+  if (collector_page == "bildfail") {
+
+    text_t textfailmsg = "_textfailmsg";
+    textfailmsg.push_back(failcode);
+    textfailmsg.push_back('_');
+    disp.setmacro("textfailmsg", "collector", textfailmsg);
+
+    text_t bldlog = filename_cat(gsdlhome, "tmp", args["bc1tmp"], args["bc1dirname"] + ".bld");
+    text_t rawlog = file_tail (bldlog, 6, 0);
+    // we'll shove in some <br> tags where \n's occur
+    text_t faillog;
+    text_t::const_iterator here = rawlog.begin();
+    text_t::const_iterator end = rawlog.end();
+    while (here != end) {
+      if (*here == '\n') faillog += "<br>";
+      faillog.push_back (*here);
+      ++here;
+    }
+    disp.setmacro ("faillog", "collector", dm_safe(faillog));
+  }
+
+  if (collector_page == "srce" || collector_page == "existing") 
+    set_fullnamemenu (disp, args, protos, logout);
+  if (collector_page == "conf") 
+    set_cfgfile (disp, args, logout);
+  if (collector_page == "bildstatus")
+    set_statusline (disp, args, logout);
+  if (collector_page == "srce") {
+    set_inputsourceboxes (disp, args, logout);
+  }
+
+  disp.setmacro ("gsdlhome", "collector", dm_safe(gsdlhome));
+}
+
+
+bool collectoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
+				 browsermapclass * /*browsers*/, displayclass &disp, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout) {
+
+  // make sure the collector is enabled
+  if (disabled) {
+    textout << outconvert 
+	    << "<html>\n"
+	    << "<head>\n"
+	    << "<title>Collector disabled</title>\n"
+	    << "</head>\n"
+	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	    << "<h2>Facility disabled</h2>\n" 
+	    << "Sorry, the Collector end-user collection building facility is currently disabled\n"
+	    << "\n</body>\n"
+	    << "</html>\n";
+    return true;
+  }
+
+  text_t &collector_page = args["p"];
+  text_t &collection = args["bc1dirname"];
+
+  // make sure we have perl (we won't bother with this check for the 
+  // building status pages to avoid slowing things down unneccessarily)
+  if (collector_page != "bildstatus" && collector_page != "bildframe1" && !perl_ok(logout)) {
+    textout << outconvert 
+	    << "<html>\n"
+	    << "<head>\n"
+	    << "<title>Perl not found</title>\n"
+	    << "</head>\n"
+	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	    << "<h2>Perl not found</h2>\n" 
+	    << "Greenstone could not detect perl on this system. It is therefore not\n"
+	    << "possible to build a Greenstone collection, either from the Collector or the \n"
+	    << "command-line tools, or to use the Collector for any other task.\n"
+	    << "<p>Please refer to the Greenstone Installer's Guide for details on\n"
+	    << "installing perl on your system.\n"
+	    << "\n</body>\n"
+	    << "</html>\n";
+    return true;
+
+  }
+
+  if (collector_page == "bild") {
+    // do the work (download, import, build)
+    gsdl_build (args, logout);
+
+    if (message.empty()) {
+      // bild page is a frameset so we don't want headers and stuff
+      textout << outconvert << disp << ("_collector:bildcontent_\n");
+    }
+  }
+
+  if (do_mkcol == true) {
+    // execute mkcol.pl (do_mkcol is set from within check_cgiargs)
+    gsdl_mkcol (args, logout);
+    do_mkcol = false; // reset for fast-cgi
+  }
+
+  if (args["bc1dodelete"] == "1") {
+    // delete bcidirname collection
+    if (collection_protected (collection)) {
+      message = "delinvalid";
+	
+    } else {
+
+      const recptconf &rcinfo = recpt->get_configinfo ();
+      bool emailuserevents = rcinfo.EmailUserEvents;
+
+      // get collection maintainer email from collect.cfg before we
+      // delete it
+      text_t colmaintainer;
+      text_t cfgfile = filename_cat(collecthome, collection, "etc", "collect.cfg");
+      char *cfgfilec = cfgfile.getcstr();
+      ifstream cfg_in (cfgfilec);
+      delete []cfgfilec;
+      if (cfg_in) {
+	text_tarray cfgline;
+	while (read_cfg_line(cfg_in, cfgline) >= 0) {
+	  if (cfgline.size () == 2 && cfgline[0] == "maintainer") {
+	    colmaintainer = cfgline[1];
+	    break;
+	  }
+	}
+	cfg_in.close();
+      }
+      if (colmaintainer.empty()) {
+	logout << outconvert 
+	       << "collectoraction::do_action WARNING: Collection being deleted (" 
+	       << collection << ") has no maintainer address. EmailUserEvents "
+	       << "disabled\n";
+	emailuserevents = false;
+      }
+      
+      // first we need to free up the collection's collection server
+      // we must do this for the local library (and I guess when using
+      // fastcgi too) as you can't delete the database file while it's
+      // being kept open by the collection server
+      remove_colservr (collection, logout);
+
+      text_t delete_cmd = "perl -S delcol.pl -f " + collection;
+      int rv = gsdl_system (delete_cmd, true, logout);
+      if (rv != 0) {
+	// deletion failed -- permissions?
+	message = "delpermission";
+      } else {
+	message = "delsuccess";
+      }
+
+      // log the event
+      if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) {
+	
+	text_t eventlog = filename_cat (gsdlhome, "etc", "events.txt");
+	char *eventlogt = eventlog.getcstr();
+	ofstream eventl (eventlogt, ios::app);
+	delete []eventlogt;
+
+	if (eventl) {
+	  eventl << outconvert << "[Collector Event]\n"
+		 << "Date: " << get_date (true) << "\n"
+		 << "Greenstone Username: " << args["un"] << "\n"
+		 << "Collection: " << collection << "\n"
+		 << "Collection Maintainer: " << colmaintainer << "\n"
+		 << "GSDLHOME: " << gsdlhome << "\n";
+
+	  if (message == "delsuccess") {
+	    eventl << outconvert 
+		   << "The " << collection << " collection was successfully deleted\n\n";
+	  } else {
+	    eventl << outconvert 
+		   << "Attempt to delete the " << collection << " collection failed\n\n";
+	  }
+	  eventl.close();
+
+	} else {
+	  logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
+		 << "event log file " << eventlog << " for appending during collection "
+		 << "deletion. LogEvents disabled\n";
+	}
+      }
+      
+      if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents || emailuserevents) {
+	// use sendmail.pl perl script to send email events
+	text_t tmpmailfile = filename_cat (gsdlhome, "tmp", args["bc1tmp"], "event.txt");
+	char *tmpmailfilec = tmpmailfile.getcstr();
+	ofstream tmpfile (tmpmailfilec);
+	delete []tmpmailfilec;
+	if (tmpfile) {
+	  tmpfile << outconvert << "[Collector Event]\n"
+	    	  << "Date: " << get_date (true) << "\n"
+		  << "Greenstone Username: " << args["un"] << "\n"
+		  << "Collection: " << collection << "\n"
+		  << "Collection Maintainer: " << colmaintainer << "\n"
+		  << "GSDLHOME: " << gsdlhome << "\n";
+	  if (message == "delsuccess") {
+	    tmpfile << outconvert 
+		    << "The " << collection << " collection was successfully deleted\n\n";
+	  } else {
+	    tmpfile << outconvert 
+		    << "Attempt to delete the " << collection << " collection failed\n\n";
+	  }
+	  tmpfile.close();
+	  text_t to;
+	  if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) to += rcinfo.maintainer;
+	  if (emailuserevents) {
+	    if (!to.empty()) to.push_back (',');
+	    to += colmaintainer;
+	  }
+	  text_t sendmail_cmd = "perl -S sendmail.pl -to \"" + to + "\" -from \"" + rcinfo.maintainer;
+	  sendmail_cmd += "\" -smtp \"" + rcinfo.MailServer + "\" -subject \"Greenstone Collector Event\"";
+	  sendmail_cmd += " -msgfile \"" + tmpmailfile + "\"";
+
+	  gsdl_system (sendmail_cmd, false, logout);
+	  
+	} else {
+	  logout << outconvert << "collectoraction::do_action ERROR: Couldn't open "
+		 << "temporary event log file " << tmpmailfile << " during collection "
+		 << "deletion. EmailEvents and EmailUserEvents disabled\n";
+	}
+      }
+    }
+  }
+  
+  if (collector_page == "bildcancel" || collector_page == "bildfail") {
+    // cancel the build (we'll also use the cancel_build script to tidy
+    // up if the build failed)
+    gsdl_cancel_build (args, logout);
+  }
+
+  if (collector_page == "expt") {
+
+    // export the collection - we'll do a synchronous system call to
+    // exportcol.pl as that's the easiest way to do it. if it becomes a
+    // problem that it's taking too long to export a large collection then
+    // we may have to revisit this.
+    text_t tmpfile = filename_cat (gsdlhome, "tmp", collection + "_export.txt");
+    text_t export_cmd = "perl -S exportcol.pl -out \"" + tmpfile + "\" " + collection;
+    gsdl_system (export_cmd, true, logout);
+    if (file_exists (tmpfile)) {
+      text_t returnline = file_tail (tmpfile, 1, 0);
+      if (returnline.size() > 23 && (substr(returnline.begin(), returnline.begin()+23) == "exportcol.pl succeeded:")) {
+	// success
+	message = "exptsuccess";
+      } else {
+	message = "exptfail";
+      }
+    } else {
+      message = "exptfail";
+    }
+  }
+
+  if (message.empty()) {
+    if (collector_page != "bild") {
+      // output page ("bild" page was already output above)
+      textout << outconvert << disp << ("_collector:header_\n")
+	      << ("_collector:" + collector_page + "content_\n")
+	      << ("_collector:footer_\n");
+    }
+  } else {
+    // message was set somewhere (probably an error), output message page
+    textout << outconvert << disp << ("_collector:header_\n")
+	    << ("_collector:" + message + "content_\n")
+	    << ("_collector:footer_\n");
+    message.clear();
+  }
+  return true;
+}
+
+void collectoraction::gsdl_mkcol (cgiargsclass &args, ostream &logout) {
+
+  text_t tmpdir = filename_cat (gsdlhome, "tmp", args["bc1tmp"]);
+  if (!directory_exists (tmpdir)) {
+    message = "tmpfail";
+    cerr << "WE CANNOT CREATE THE DIRECTORY!!! " << endl; 
+    return;
+  }
+
+  text_t &collection = args["bc1dirname"];
+  if (collection.empty()) {
+    message = "nocollection";
+    return;
+  }
+
+  // check for a .create file - if it exists then we've already created the collection
+  text_t createfile = filename_cat (tmpdir, ".create");
+  if (file_exists (createfile)) {
+    return;
+  }
+
+  // set up options
+  text_t options = "-quiet -creator \"" + args["bc1contactemail"] + "\"";
+  options += " -title \"" + args["bc1fullname"] + "\"";
+  options += " -about \"" + carriage_replace (args["bc1aboutdesc"] + "_collectorextra_", 0) + "\"";
+  options += " -collectdir \"" + remove_trailing_slashes(tmpdir) + "\" ";
+
+  text_t optionfile = filename_cat (tmpdir, "mkcol.opt");
+  char *optionfilec = optionfile.getcstr();
+  ofstream ofile_out (optionfilec);
+  delete []optionfilec;
+  if (!ofile_out) {
+    message = "tmpfail";
+    return;
+  }
+  outconvertclass text_t2ascii;
+  ofile_out << text_t2ascii << options << "\n";
+  ofile_out.close();
+
+  // run mkcol.pl
+  text_t mkcol_cmd = "perl -S mkcol.pl -optionfile \"" + optionfile;
+  mkcol_cmd += "\" " + collection;
+
+
+  gsdl_system (mkcol_cmd, true, logout);
+
+  // make sure it went ok
+  text_t cfgfile = filename_cat (tmpdir, collection, "etc", "collect.cfg");
+  if (!file_writable (cfgfile)) {
+    message = "mkcolfail";
+  } else {
+    // create the .create file (this file is just a place holder to let any future
+    // pages know that the collection already exists).
+    char *createfilec = createfile.getcstr();
+    ofstream cfile_out (createfilec);
+    delete []createfilec;
+    if (cfile_out) {
+      cfile_out << "collection created\n";
+      cfile_out.close();
+    } else {
+      message = "tmpfail";
+      return;
+    }
+  }
+}
+
+
+bool collectoraction::check_sources (cgiargsclass &args, ostream &logout) {
+
+  bool found = false;
+
+  text_tarray inputvalues;
+  splitchar (args["bc1input"].begin(), args["bc1input"].end(), ',', inputvalues);
+
+  text_tarray inputtypes;
+  splitchar (args["bc1inputtype"].begin(), args["bc1inputtype"].end(), ',', inputtypes);
+
+  int numvalues = inputvalues.size();
+  int numtypes = inputtypes.size();
+
+  for (int i = 0; i < numvalues; ++i) {
+    text_t value = format_url(decode_commas(inputvalues[i]));
+    text_t type = "file://"; // default
+    if (!value.empty()) {
+      found = true;
+      if (i >= numtypes || inputtypes[i].empty()) {
+	logout << "collectoraction::check_sources: WARNING type not set\n";
+      } else {
+	type = inputtypes[i];
+      }
+      if (type == "file://") {
+	if (!file_exists(value) && !directory_exists(value)) {
+	  failedsources[decode_commas(inputvalues[i])] = "1";
+	  badsources = true;
+	}
+      } else if (type == "http://") {
+	if (gsdl_system ("perl -S ping.pl -quiet http://" + value, true, logout)) {
+	  failedsources[decode_commas(inputvalues[i])] = "1";
+	  badsources = true;
+	}
+      } else if (type == "ftp://") {
+	if (gsdl_system ("perl -S ping.pl -quiet ftp://" + value, true, logout)) {
+	  failedsources[decode_commas(inputvalues[i])] = "1";
+	  badsources = true;
+	}
+      }
+    }
+  }
+
+  // set badsources if there weren't any sources at all
+  if (!found) badsources = true;
+
+  if (badsources) return false;
+  return true;
+}
+
+text_t collectoraction::get_button(cgiargsclass &args, const text_t &thispage, 
+				   const text_t &color, 
+				   const text_t &type, bool enabled) 
+{
+
+  if ((color != "green" && color != "grey" && color != "yellow") ||
+      (type != "info" && type != "srce" && type != "conf" && type != "bild" && type != "view"))
+    return g_EmptyText;
+
+  text_t href = "_http"+type+"_";
+  text_t target = "";
+
+  if (thispage == "info" || thispage == "srce" || thispage == "conf" || 
+      thispage == "bildcancel" || thispage == "bildfail") {
+    // call the check submit macro instead of linking directly to the page
+    href="\"javascript:check_submit('"+type+"');\"";
+  } else if (type == "view") {
+    // view button is special case as it needs a target=_top
+    target = " target=_top";
+  }
+
+  text_t tdclass = "wizardbar"+color; 
+  if (enabled) {
+    // link to the appropriate page
+    return "<td class="+tdclass+"><a href="+href+target+">_text"+type+"_</a></td>";
+  }
+  else {
+    // just display the text
+    return "<td class="+tdclass+">_text"+type+"_</td>";
+  }
+}
+
+#endif //GSDL_USE_COLLECTOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/collectoraction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/collectoraction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/collectoraction.h	(revision 16561)
@@ -0,0 +1,95 @@
+/**********************************************************************
+ *
+ * collectoraction.h -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_COLLECTOR_ACTION
+
+#ifndef COLLECTORACTION_H
+#define COLLECTORACTION_H
+
+#include "wizardaction.h"
+#include "receptionist.h"
+
+class collectoraction : public wizardaction {
+  
+protected:
+
+  bool do_mkcol;
+
+  bool badsources;
+  text_tmap failedsources;
+
+  void set_fullnamemenu (displayclass &disp, cgiargsclass &args, 
+			 recptprotolistclass *protos, ostream &logout);
+
+
+  void set_inputsourceboxes (displayclass &disp, cgiargsclass &args, 
+			     ostream &logout);
+
+  text_t get_source_box (text_t inputarglist, int numboxes, 
+			 text_t inputtypelist);
+
+  void set_cfgfile (displayclass &disp, cgiargsclass &args, ostream &logout);
+
+
+  void update_cfgfile_clone (cgiargsclass &args, ostream &logout);
+
+  void update_cfgfile_partial (cgiargsclass &args, bool clone, ostream &logout);
+
+  void update_cfgfile_complete (cgiargsclass &args, ostream &logout);
+
+
+  void gsdl_mkcol (cgiargsclass &args, ostream &logout);
+
+
+
+  bool check_sources (cgiargsclass &args, ostream &logout);
+
+  text_t get_button (cgiargsclass &args, const text_t &thispage, 
+		     const text_t &color, 
+		     const text_t &type, bool enabled);
+
+
+public:
+  collectoraction ();
+  virtual ~collectoraction ();
+
+  text_t get_action_name () {return "collector";}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+};
+
+#endif //!COLLECTORACTION_H
+#endif //GSDL_USE_COLLECTOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/comproto.asn
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/comproto.asn	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/comproto.asn	(revision 16561)
@@ -0,0 +1,376 @@
+-- The protocol data unit (PDU) is the
+-- message that is transmitted between receptionists and
+-- collectionservers (colservers?).
+--
+-- Comments in ASN.1 start with two dashes and continue to the end
+-- of a line (or to the next two dashes, whichever is first).
+-- 
+-- Z39.50 has inspired much of this but I have tried to keep
+-- it simple. Its hard to tell if I have succeeded ;-)
+
+
+GSDL-APDU DEFINITIONS ::=
+BEGIN
+
+
+-- PDU is the basic message type. Note: in ASN.1 the
+-- field name comes first and the field type second (opposite
+-- to C). Ignore the numbers in the square brackets (e.g. [0]),
+-- they're just tags to disambiguate certain circumstances and to
+-- make the protocol easier to extend. The keyword 'IMPLICIT' just
+-- makes the tags a little more efficient.
+--
+-- A SEQUENCE is a list of fields where the order is important
+
+PDU ::= SEQUENCE {
+  header  StandardHeader,
+  message Message
+}
+
+
+-- ????? Is there anything else we want in the header ?????
+--
+-- Anonymous transactions should leave out the idAuthentication field
+
+StandardHeader ::= SEQUENCE {
+  protocolVersion       [0] IMPLICIT INTEGER,
+  idAuthentication      [1] IMPLICIT IdAuthentication OPTIONAL
+}
+
+IdAuthentication ::= SEQUENCE {
+  userId   GeneralString,
+  groupId  GeneralString,
+  password GeneralString
+}
+
+
+-- A CHOICE is like a union in C.
+-- 
+-- Short forms: Auth (Authority), Mir (Mirror), Col (Collection),
+--   Doc (Document), Info (Information)
+
+Message ::= CHOICE {
+  -- General
+  error                      [0]  IMPLICIT Error,
+  pingRequest                [1]  IMPLICIT PingRequest,
+  pingResponse               [2]  IMPLICIT PingResponse,
+
+  -- Authority
+--  authListRequest            [12] IMPLICIT AuthListRequest,
+--  authListResponse           [13] IMPLICIT AuthListResponse,
+--  editAuthListRequest        [14] IMPLICIT EditAuthListRequest,
+--  editAuthListResponse       [15] IMPLICIT EditAuthListResponse,
+
+  -- an authority mirror is a mirror of THIS authority
+--  authMirListRequest         [16] IMPLICIT AuthMirListRequest,
+--  authMirListResponse        [17] IMPLICIT AuthMirListResponse,
+--  editAuthMirListRequest     [18] IMPLICIT EditAuthMirListRequest,
+--  editAuthMirListResponse    [19] IMPLICIT EditAuthMirListResponse,
+
+  colListRequest             [20] IMPLICIT ColListRequest,
+  colListResponse            [21] IMPLICIT ColListResponse,
+--  editColListRequest         [22] IMPLICIT EditColListRequest,
+--  editColListResponse        [23] IMPLICIT EditColListResponse,
+
+--  colMirListRequest          [24] IMPLICIT ColMirListRequest,
+--  colMirListResponse         [25] IMPLICIT ColMirListResponse, 
+--  editColMirListRequest      [26] IMPLICIT EditColMirListRequest, 
+--  editColMirListResponse     [27] IMPLICIT EditColMirListResponse,
+
+  -- Collection
+  colInfoRequest             [40] IMPLICIT ColInfoRequest,
+  colInfoResponse            [41] IMPLICIT ColInfoResponse,
+
+  -- Document
+  documentRequest            [52] IMPLICIT DocumentRequest,
+  documentResponse           [53] IMPLICIT DocumentResponse,
+
+  -- Access (searching, browsing, and metadata information)
+  infoFiltersRequest         [60] IMPLICIT InfoFiltersRequest,
+  infoFiltersResponse        [61] IMPLICIT InfoFiltersResponse,
+  infoFilterOptionsRequest   [62] IMPLICIT InfoFilterOptionsRequest,
+  infoFilterOptionsResponse  [63] IMPLICIT InfoFilterOptionsResponse,
+  filterRequest              [64] IMPLICIT FilterRequest,
+  filterResponse             [65] IMPLICIT FilterResponse
+}
+
+
+
+-- ------------------------------
+-- GENERAL
+-- ------------------------------
+
+
+-- The error list is not complete yet ...
+
+Error ::= ENUMERATED {
+  authenticationFailure(0), protocolError(1), systemProblem(2)
+}
+
+
+PingRequest ::= SEQUENCE {
+}
+
+PingResponse ::= SEQUENCE {
+}
+
+
+
+-- ------------------------------
+-- AUTHORITY
+-- ------------------------------
+
+
+-- Support for subscription is optional for both the clients and
+-- the servers. It is recomended that it is not implemented yet
+-- for simplicity.
+
+-- AuthListRequest ::= SEQUENCE {
+-- }
+
+-- AuthListResponse ::= SEQUENCE {
+--   authList  [1] IMPLICIT StringSet
+-- }
+
+
+-- EditAuthListRequest ::= SEQUENCE {
+--   editAction EditAction,
+--   authority  GeneralString
+-- }
+
+-- EditAuthListResponse ::= SEQUENCE {
+-- }
+
+
+-- AuthMirListRequest ::= SEQUENCE {
+--   limitNumber [1] IMPLICIT INTEGER OPTIONAL,
+--   limitDomain [2] IMPLICIT GeneralString OPTIONAL
+-- }
+
+-- AuthMirListResponse ::= SEQUENCE {
+--   authMirList [1] IMPLICIT HostPortSet
+-- }
+
+
+-- EditAuthMirListRequest ::= SEQUENCE {
+--   editAction EditAction,
+--   authMir    HostPort
+-- }
+
+-- EditAuthMirListResponse ::= SEQUENCE {
+-- }
+
+
+ColListRequest ::= SEQUENCE {
+}
+
+ColListResponse ::= SEQUENCE {
+  colList   [1] IMPLICIT ShortColInfoSet
+}
+
+
+-- EditColListRequest ::= SEQUENCE {
+--   editAction EditAction,
+--   colInfo    ShortColInfo
+-- }
+
+-- EditColListResponse ::= SEQUENCE {
+-- }
+
+
+-- ColMirListRequest ::= SEQUENCE {
+--   limitNumber [1] IMPLICIT INTEGER OPTIONAL,
+--   limitDomain [2] IMPLICIT GeneralString OPTIONAL
+-- }
+
+-- ColMirListResponse ::= SEQUENCE {
+--   colMirList [1] IMPLICIT ShortColInfoSet
+-- }
+
+
+-- EditColMirListRequest ::= SEQUENCE {
+--   editAction EditAction,
+--   colMirInfo ShortColInfo
+-- }
+
+-- EditColMirListResponse ::= SEQUENCE {
+-- }
+
+
+
+
+-- ------------------------------
+-- COLLECTION
+-- ------------------------------
+
+
+ColInfoRequest ::= SEQUENCE {
+}
+
+ColInfoResponse ::= SEQUENCE {
+  shortInfo   [0] IMPLICIT ShortCollectionInfo,
+  isPublic    [2] IMPLICIT BOOLEAN,    -- whether has anonymous access
+  isBeta      [3] IMPLICIT BOOLEAN,    -- beta if still under development
+  builddate   [4] IMPLICIT GeneralizedTime,
+  languages   [5] IMPLICIT StringSet,  -- languages in the collection
+  numDocs     [6] IMPLICIT INTEGER,
+  numWords    [7] IMPLICIT INTEGER OPTIONAL,
+  numBytes    [8] IMPLICIT INTEGER OPTIONAL
+}
+
+
+
+-- ------------------------------
+-- DOCUMENT
+-- ------------------------------
+
+
+
+DocumentRequest ::= SEQUENCE {
+  OID       GeneralString,
+  docType   GeneralString,
+  docFormat GeneralString
+}
+
+DocumentResponse ::= SEQUENCE {
+  doc OCTET STRING
+}
+
+
+-- ------------------------------
+-- ACCESS (SEARCH AND BROWSE)
+-- ------------------------------
+
+
+-- filter options which might be supported for the QueryFilter
+--
+-- onePerQuery StartResults   integer
+-- onePerQuery EndResults     integer
+-- onePerQuery QueryType      enumerated (boolean, ranked)
+-- onePerTerm  Term           string ???
+-- onePerTerm  Casefold       boolean
+-- onePerTerm  Stem           boolean
+-- onePerTerm  Index          enumerated
+-- onePerTerm  Subcollection  enumerated
+--
+-- filter options which might be supported for the BrowseFilter
+--
+-- onePerQuery StartResults   integer
+-- onePerQuery EndResults     integer
+-- onePerQuery ParentNode     string ("" will return the browsing available)
+--
+-- The NullFilter always returns the set it was given, it doesn't have
+-- any options
+
+InfoFiltersRequest ::= SEQUENCE {
+}
+
+InfoFiltersResponse ::= SEQUENCE {
+  filterNames StringSet
+}
+
+InfoFilterOptionsRequest ::= SEQUENCE {
+  filterName GeneralString
+}
+
+InfoFilterOptionsResponse ::= SEQUENCE {
+  filterOptions SET OF FilterOption
+}
+
+FilterOption ::= SEQUENCE {
+  name         GeneralString,
+  type         ENUMERATED {booleant(0), integert(1), enumeratedt(2), stringt(3)},
+  repeatable   ENUMERATED {onePerQuery(0), onePerTerm(1), nPerTerm(2)},
+  defaultValue GeneralString,
+  -- the interpretation of the validValues depends on the type
+  -- for boolean: the first value is the false value, the second is true value
+  -- for integer: the first value is the minimum, the second the maximum
+  -- for enumerated: all values a listed
+  -- for string: this value is ignored
+  validValues  StringSequence
+}
+
+
+-- Terms are presented in the same order that they are requested,
+-- any information relating to the terms is in reference to the
+-- index specified for that term.
+-- Metadata is presented in the same order that it is requested.
+
+FilterRequest ::= SEQUENCE {
+  filterName          [0] GeneralString,
+  filterOptions       [1] IMPLICIT SEQUENCE OF OptionValue,
+  docSet              [2] IMPLICIT StringSequence, -- the OID "" represents everything
+  filterResultOptions [3] IMPLICIT BIT STRING {termFreq(0), matchTerms(1), OID(2), 
+                                               subCol(3), ranking(4), docFreq(5), 
+                                               metadata(6)}
+
+  -- the next set of options are for the metadata request, 
+  -- they can be left blank if metadata is not wanted
+  requestParams       [4] IMPLICIT GeneralString, -- used to negotiate the metadata content
+  refParams           [5] IMPLICIT GeneralString, -- used to decide whether to return a 
+                                                  -- reference to the data or the actual data
+  fields              [6] IMPLICIT StringSequence
+}
+
+OptionValue ::= SEQUENCE {
+  name  GeneralString,
+  value GeneralString
+}
+
+FilterResponse ::= SEQUENCE {
+  numDocs   [0] IMPLICIT INTEGER,
+  isApprox  [1] IMPLICIT BOOLEAN,                  -- whether numDocs is approximate
+  termInfo  [2] IMPLICIT SEQUENCE OF TermInfo,     -- empty if not required
+  docInfo   [3] IMPLICIT SEQUENCE OF ResultDocInfo -- empty if not required
+}
+
+TermInfo ::= SEQUENCE {
+  term       [0] GeneralString,
+  freq       [1] IMPLICIT INTEGER,       -- 0 if not requested
+  matchTerms [2] IMPLICIT StringSequence -- empty if not requested
+}
+
+ResultDocInfo ::= SEQUENCE {
+  OID      [0] IMPLICIT GeneralString,
+  ranking  [1] IMPLICIT INTEGER,             -- 0 if not requested, range 0-10000
+  docFreq  [2] IMPLICIT SEQUENCE OF INTEGER, -- empty if not requested
+  metadata [3] IMPLICIT SEQUENCE OF MetadataInfo
+}
+
+
+MetadataInfo ::= SEQUENCE {
+  params  [0] IMPLICIT GeneralString,
+  isRef   [1] IMPLICIT BOOLEAN,
+  field   [3] IMPLICIT GeneralString,
+  value   [4] IMPLICIT GeneralString
+}
+
+
+-- ------------------------------
+-- Useful Types
+-- ------------------------------
+
+-- HostPortSet ::= SET OF HostPort
+
+-- HostPort ::= SEQUENCE {
+--   host [0] IMPLICIT GeneralString,
+--   port [1] IMPLICIT INTEGER
+-- }
+
+ShortColInfoSet ::= SET OF ShortColInfo
+
+ShortColInfo ::= SEQUENCE {
+  name GeneralString,
+  host GeneralString,
+  port INTEGER
+}
+
+-- EditAction ::= ENUMERATED {
+--   update(0), delete(1)
+-- }
+
+StringSequence ::= SEQUENCE OF GeneralString
+StringSet ::= SET OF GeneralString
+
+
+END
Index: /gsdl/trunk/gs2runtime/src/recpt/configaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/configaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/configaction.cpp	(revision 16561)
@@ -0,0 +1,151 @@
+/**********************************************************************
+ *
+ * configaction.cpp -- 
+ * Copyright (C) 2003 DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#include "configaction.h"
+
+#ifdef GSDL_LOCAL_LIBRARY
+// for PostMessage()
+#  include <windows.h>
+#endif
+
+configaction::configaction () {
+
+  recpt = NULL;
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "config";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  /* 
+     The cmd argument may be one of the following:
+     -- release-collection - release the collection specified by the "c"
+        argument
+     -- add-collection - add and configure a collection server for the
+        collection specified by the "c" argument
+     -- kill - if running as the local library this will kill the server
+  */
+  arg_ainfo.shortname = "cmd";
+  arg_ainfo.longname = "config command";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+configaction::~configaction () {
+}
+
+void configaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+				     response_t &response, text_t &response_data, 
+				     ostream &logout) {
+  response = content;
+  response_data = "text/html";
+}
+
+bool configaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			      browsermapclass *browsers, displayclass &disp,
+			      outconvertclass &outconvert, ostream &textout,
+			      ostream &logout) {
+
+  if (recpt == NULL) return false;
+
+  if ((args["cmd"] == "add-collection") && (!args["c"].empty())) {
+    create_colservr(args["c"], logout);
+    textout << "configured add-collection\n";
+
+  } else if ((args["cmd"] == "release-collection") && (!args["c"].empty())) {
+    remove_colservr(args["c"], logout);
+    textout << "configured release-collection\n";
+
+  } else if (args["cmd"] == "kill") {
+    textout << "killed\n";
+#ifdef GSDL_LOCAL_LIBRARY
+    HWND gw = FindWindow("Greenstone Digital Library Software",
+			 "Greenstone Digital Library Software");
+    PostMessage(gw, WM_DESTROY, 0, 0);
+#endif
+  }
+
+  return true;
+}
+
+
+// create and initialize a new collection server and
+// add it to the null protocol.
+void configaction::create_colservr (const text_t &collection, ostream &logout) {
+
+  // just in case we're adding a collection that already exists
+  recpt->uncache_collection(collection);
+
+  recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    comerror_t err = noError;
+    if ((*rprotolist_here).p != NULL) {
+      if ((*rprotolist_here).p->get_protocol_name(err) == "nullproto") {
+	// create collection server and add it to nullproto
+	(*rprotolist_here).p->add_collection(collection, recpt, gsdlhome, collecthome, gsdlhome);
+	// re-initialize the null protocol
+	if (!(*rprotolist_here).p->init(err, logout)) {
+	  logout << "configaction::create_colservr: nullproto init failed\n";
+	}
+	return;
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  logout << "configaction::create_colservr: no valid nullproto found\n";
+}
+
+// delete a collection server from the null protocol
+void configaction::remove_colservr (const text_t &collection, ostream &logout) {
+
+  recpt->uncache_collection(collection);
+
+  recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    comerror_t err = noError;
+    if ((*rprotolist_here).p != NULL) {
+      if ((*rprotolist_here).p->get_protocol_name(err) == "nullproto") {
+	(*rprotolist_here).p->remove_collection(collection, logout);
+	return;
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  logout << "configaction::create_colserver: no valid nullproto found\n";
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/configaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/configaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/configaction.h	(revision 16561)
@@ -0,0 +1,60 @@
+/**********************************************************************
+ *
+ * configaction.h -- 
+ * Copyright (C) 2003 DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef CONFIGACTION_H
+#define CONFIGACTION_H
+
+#include "action.h"
+#include "receptionist.h"
+
+class configaction : public action {
+
+protected:
+  receptionist *recpt;
+
+  void create_colservr (const text_t &collection, ostream &logout);
+  void remove_colservr (const text_t &collection, ostream &logout);
+
+public:
+  configaction ();
+  virtual ~configaction ();
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "config";}
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp,
+		  outconvertclass &outconvert, ostream &textout,
+		  ostream &logout);
+};
+
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/converter.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/converter.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/converter.cpp	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * converter.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.
+ *
+ *********************************************************************/
+
+#include "converter.h"
+
+
+
+// the converters within converterinfo become the property of this class
+// after add_converter has been called. The converters remain the
+// responsability of the calling code and will not be deleted by this
+// class.
+void convertinfoclass::add_converter (const text_t &name, inconvertclass *inconverter,
+				      rzwsoutconvertclass *outconverter) {
+  // can't add a null converter or an action with no name
+  if (inconverter == NULL || outconverter == NULL || name.empty()) {
+    return;
+  }
+
+  converterinfo info;
+  info.name = name;
+  info.inconverter = inconverter;
+  info.outconverter = outconverter;
+  converters[name] = info;
+}
+
+
+bool operator==(const converterinfo &x, const converterinfo &y) {
+  return ((x.name == y.name) && (x.inconverter == y.inconverter) &&
+	  (x.outconverter == y.outconverter));
+}
+
+bool operator<(const converterinfo &x, const converterinfo &y) {
+  return ((x.name < y.name) ||
+	  ((x.name == y.name) &&
+	   ((x.inconverter < y.inconverter) ||
+	    ((x.inconverter == y.inconverter) &&
+	     ((x.outconverter < y.outconverter))))));
+}
+
+
+// get_inconverter will return NULL if the convert could not be found
+inconvertclass *convertinfoclass::get_inconverter (const text_t &name) {
+  iterator here = converters.find (name);
+  if (here == converters.end()) return NULL;
+
+  return (*here).second.inconverter;
+}
+
+// get_outconverter will return NULL if the convert could not be found
+rzwsoutconvertclass *convertinfoclass::get_outconverter (const text_t &name) {
+  iterator here = converters.find (name);
+  if (here == converters.end()) return NULL;
+
+  return (*here).second.outconverter;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/converter.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/converter.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/converter.h	(revision 16561)
@@ -0,0 +1,122 @@
+/**********************************************************************
+ *
+ * converter.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.
+ *
+ *********************************************************************/
+
+
+#ifndef CONVERTER_H
+#define CONVERTER_H
+
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "gsdlunicode.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\map>
+#elif defined(GSDL_USE_STL_H)
+#  include <map.h>
+#else
+#  include <map>
+#endif
+
+
+// inconverter and outconverter ARE NOT destroyed by this class
+// they should be destroyed by the code which created them after
+// no more pointers to the class exist.
+//
+// The above condition enables the class to be copied without
+// copying the converters which might contain a lot of data (and
+// be of an unknown derived type).
+class converterinfo {
+public:
+  text_t name;
+  inconvertclass *inconverter;
+  rzwsoutconvertclass *outconverter;
+
+  converterinfo() {inconverter=NULL; outconverter=NULL;}
+};
+
+bool operator==(const converterinfo &x, const converterinfo &y);
+bool operator<(const converterinfo &x, const converterinfo &y);
+
+
+typedef map<text_t, converterinfo, lttext_t> convertmap;
+
+// convertinfoclass is used to store a number of converters
+// indexing them by their name
+class convertinfoclass {
+protected:
+  convertmap converters;
+
+public:
+  // type support for convertmap
+  typedef convertmap::iterator iterator;
+  typedef convertmap::const_iterator const_iterator;
+  typedef convertmap::reference reference;
+  typedef convertmap::const_reference const_reference;
+  typedef convertmap::size_type size_type;
+  typedef convertmap::difference_type difference_type;
+  typedef convertmap::const_reverse_iterator const_reverse_iterator;
+  typedef convertmap::reverse_iterator reverse_iterator;
+  
+  // constructors
+  convertinfoclass () {}
+
+  // basic container support
+  iterator begin () {return converters.begin();}
+  const_iterator begin () const {return converters.begin();}
+  iterator end () {return converters.end();}
+  const_iterator end () const {return converters.end();}
+
+  void erase(iterator pos) {converters.erase(pos);}
+  void erase(iterator first, iterator last) {converters.erase(first, last);}
+  convertinfoclass &operator=(const convertinfoclass &x) 
+    {converters=x.converters;return *this;}
+
+  bool empty () const {return converters.empty();}
+  size_type size() const {return converters.size();}
+
+  const_iterator find(const text_t &key) {return converters.find(key);}
+
+  // added functionality
+  void clear () {converters.erase(converters.begin(),converters.end());}
+
+  // the converters within converterinfo become the property of
+  // of this class after add_converter has been called. The converters
+  // remain the responsability of the calling code and will not be
+  // deleted by this class.
+  void add_converter (const text_t &name, inconvertclass *inconverter,
+		      rzwsoutconvertclass *outconverter);
+
+  // get_inconverter will return NULL if the convert could not be found
+  inconvertclass *get_inconverter (const text_t &name);
+
+  // get_outconverter will return NULL if the convert could not be found
+  rzwsoutconvertclass *get_outconverter (const text_t &name);
+};
+
+
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,186 @@
+/**********************************************************************
+ *
+ * datelistbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_DATELIST_BROWSER
+
+#include "datelistbrowserclass.h"
+#include <assert.h>
+
+
+datelistbrowserclass::datelistbrowserclass () {
+}
+
+datelistbrowserclass::~datelistbrowserclass () {
+}
+
+text_t datelistbrowserclass::get_browser_name () {
+  return "DateList";
+}
+
+browserclass* datelistbrowserclass::clone()
+{
+  return new datelistbrowserclass();
+}
+
+void datelistbrowserclass::load_metadata_defaults (text_tset &metadata) {
+  metadata.insert ("Date");
+  metadata.insert ("doctype");
+}
+
+text_t datelistbrowserclass::get_default_formatstring () {
+  return "<td>[link][icon][/link]</td><td>[highlight]{Or}{[dls.Title],[dc.Title],[Title],Untitled}[/highlight]</td><td>[Date]</td>";
+}
+
+int datelistbrowserclass::output_section_group (ResultDocInfo_t &/*section*/, cgiargsclass &/*args*/,
+						const text_t &/*collection*/, int /*colnumber*/, 
+						format_t * /*formatlistptr*/, bool /*use_table*/, 
+						text_tset &/*metadata*/, bool &/*getParents*/,
+						recptproto * /*collectproto*/, displayclass &/*disp*/, 
+						outconvertclass &/*outconvert*/, ostream &/*textout*/, 
+						ostream &/*logout*/) {
+  return 0;
+}
+
+int datelistbrowserclass::output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+						const text_t &collection, int colnumber, format_t *formatlistptr, 
+						bool use_table, text_tset &/*metadata*/, bool &/*getParents*/,
+						recptproto * collectproto, displayclass &disp, 
+						outconvertclass &outconvert, ostream &textout, ostream &logout) {
+
+  text_t lastyear = "0000";
+  text_t lastmonth = "00";
+  text_t tabbing, endtabbing;
+
+  if (use_table || colnumber > 0) {
+    textout << outconvert << "<table><tr><td>";
+    // get tab size
+    text_t tab; int itab;
+    disp.expandstring (displayclass::defaultpackage, "_tabwidth_", tab);
+    itab = tab.getint();
+    if (colnumber > 0) 
+      textout << outconvert << "<img alt=\"\" src=\"_httpimg_/space.gif\" width=\"" << (itab*colnumber) << "\"/>";
+    textout << outconvert << "</td><td>";
+  }
+
+  textout << outconvert << "<table class=\"date_list\">\n";
+
+  ResultDocInfo_tarray::iterator thissection = sections.docInfo.begin();
+  ResultDocInfo_tarray::iterator endsection = sections.docInfo.end();
+  
+  // use the dm arg as date metadata or else Date
+  text_t date_meta = args["dm"];
+  if (date_meta == "") {
+    date_meta = "Date";
+  }
+  text_tarray dates;
+  splitchar(date_meta.begin(), date_meta.end(), ',', dates);
+
+  while (thissection != endsection) {
+
+    text_t &doctype = (*thissection).metadata["doctype"].values[0];
+
+    text_tarray::iterator begin = dates.begin();
+    text_tarray::iterator end = dates.end();
+    text_t date;
+    while (begin!= end && date.empty()) {
+      date = (*thissection).metadata[*begin].values[0];
+      begin++;
+    }
+    text_t thisyear = "";
+    text_t thismonth = "";
+    if (!date.empty()) {
+      text_t::const_iterator datebegin = date.begin();
+      int datesize = date.size();
+    
+      if (datesize >=4) {
+	thisyear = substr (datebegin, datebegin+4);
+	thismonth = "00";
+	int pos = 4;
+	// allow for yyyy-mm-dd
+	if (date[pos]=='-') {
+	  ++pos;
+	}
+	if (datesize >= pos+2)
+	  thismonth = substr (datebegin+pos, datebegin+pos+2); 
+      }
+    }
+    
+    text_t link = "<a href=\"_httpdocument_&cl=";
+    text_t icon = "_document:iconclosedbook_";
+      
+    if (doctype == "classify") {
+      icon = "_document:iconclosedbookshelf_";
+      link += (*thissection).OID + "\">";
+    } else {
+      link += args["cl"] + "&d=" + (*thissection).OID; // + "\">";
+      // [modification to allow default document detach settings -- kjdon]
+      if (args["xx"]=="1") {
+	// documents should be detached
+	link += "&x=1\" target=\\_blank>";
+      } else {
+	link += "\">";
+      }
+    }
+
+    textout << outconvert << "<tr valign=top>";
+
+    if (thisyear != "" && thisyear != lastyear) {
+      textout << outconvert << "<td><span class=\"date_list_year\">" << thisyear << "</span></td>";
+      lastyear = thisyear;
+    } else {
+      textout << outconvert << "<td></td>";
+    }
+    if (thismonth != "" && thismonth != lastmonth) {
+      textout << outconvert << disp << ("<td><span class=\"date_list_month\">_textmonth" + thismonth + "_</span></td>");
+      lastmonth = thismonth;
+    } else {
+      textout << outconvert << "<td></td>";
+    }
+    if (!use_table) textout << outconvert << "<td>\n";
+
+    text_tmap options;
+    options["link"] = link;
+    options["icon"] = icon;
+    options["highlight"] = "0";
+    textout << outconvert << disp 
+            << get_formatted_string (collection, collectproto,
+				     *thissection, disp, formatlistptr, 
+				     options, logout) << "\n";
+    
+    if (!use_table) textout << outconvert << "</td>";
+    
+    textout << outconvert << "</tr>\n";
+    
+    ++thissection;
+  }
+
+  textout << outconvert << "</table>\n";
+
+  if (use_table  || colnumber > 0) textout << outconvert << "</td></tr></table>\n";
+  return 1;
+}
+
+#endif //GSDL_USE_DATELIST_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/datelistbrowserclass.h	(revision 16561)
@@ -0,0 +1,63 @@
+/**********************************************************************
+ *
+ * datelistbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_DATELIST_BROWSER
+
+#ifndef DATELISTBROWSERCLASS_H
+#define DATELISTBROWSERCLASS_H
+
+#include "browserclass.h"
+
+class datelistbrowserclass : public browserclass {
+protected:
+
+public:
+  datelistbrowserclass ();
+  virtual ~datelistbrowserclass ();
+
+  text_t get_browser_name ();
+
+  virtual browserclass* clone();
+
+  virtual void load_metadata_defaults (text_tset &metadata);
+
+  virtual text_t get_default_formatstring ();
+
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+  
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+};
+
+#endif //DATELISTBROWSERCLASS_H
+#endif //GSDL_USE_DATELIST_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/depositoraction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/depositoraction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/depositoraction.cpp	(revision 16561)
@@ -0,0 +1,858 @@
+/**********************************************************************
+ *
+ * depositoraction.cpp -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_DEPOSITOR_ACTION
+
+// note that the collectoraction relies on having direct access to a
+// collections configuration file. this breaks the separation between
+// receptionist and collection server and so is not suitable (at least
+// in its current form) for use when collection servers are separate 
+// from the receptionist (e.g. when using the CORBA protocol).
+
+// following line required to get fstream.filedesc() on darwin (Mac OS X)
+#define _STREAM_COMPAT  1
+// required for utsname on solaris???
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include "depositoraction.h"
+#include "OIDtools.h"
+#include "fileutil.h"
+#include "cfgread.h"
+#include "gsdltools.h"
+#include "gsdltimes.h"
+#include "argdb.h"
+#include "cgiutils.h"
+
+#include <stdio.h>
+//#include <strings.h> // seems to be needed for gcc 2.95, but not particularly happy it
+#include <fcntl.h>
+#include <time.h>
+
+#if !defined (__WIN32__)
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+#include <new>
+//#include <string>
+#include <vector>
+#include <stdexcept>
+#include <iostream>
+//#include <cstdlib>
+
+depositoraction::depositoraction () 
+  : wizardaction()
+{
+  macro_prefix = "di1";  //deposit item
+  lastpage = 0; 
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "depositor";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "p";
+  arg_ainfo.longname = "page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "select";   
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //furthest page that has been visited
+  arg_ainfo.shortname = "di1lastpage";
+  arg_ainfo.longname = "depositor specific"; 
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";   
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  // the fileupload info that cgiwrapper produces - we parse the fileupload_t
+  // and set di1userfile, di1userfilesize
+  arg_ainfo.shortname = "di1userfileinfo";
+  arg_ainfo.longname = "depositor specific"; 
+  arg_ainfo.fileupload = true;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;  
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // essential: reset fileupload to default as we are not setting 
+  // it for each arg
+  arg_ainfo.fileupload = false;
+
+  //the name of the file to be added
+  arg_ainfo.shortname = "di1userfile";
+  arg_ainfo.longname = "depositor specific"; 
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;   
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //the file size
+  arg_ainfo.shortname = "di1userfilesize";
+  arg_ainfo.longname = "depositor specific"; 
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";   
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+ //the file timestamp
+  arg_ainfo.shortname = "di1timestamp";
+  arg_ainfo.longname = "depositor specific"; 
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";   
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // temporary directory name for this collector
+  // session
+  arg_ainfo.shortname = "di1tmp";
+  arg_ainfo.longname = "depositor specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   arg_ainfo.shortname = "di1fullname";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = true;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = g_EmptyText;
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "di1dirname";
+  arg_ainfo.longname = "depositor specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "di1contactemail";
+  arg_ainfo.longname = "depositor specific";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   arg_ainfo.shortname = "di1clone";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = false;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = "0";
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   arg_ainfo.shortname = "di1clonecol";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = true;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = g_EmptyText;
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   arg_ainfo.shortname = "cfgfile";
+//   arg_ainfo.longname = "configuration file contents";
+//   arg_ainfo.multiplechar = true;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = g_EmptyText;
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   // will be set if we arrived at the "configure collection" page
+//   // via the "changing an existing collection" page
+//   arg_ainfo.shortname = "di1econf";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = false;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = "0";
+//   arg_ainfo.savedarginfo = cgiarginfo::must;
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // wizard uses this to see if we are working with an existing collection:
+  // set the default to 1
+  arg_ainfo.shortname = "di1esrce";
+  arg_ainfo.longname = "depositor specific";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   arg_ainfo.shortname = "di1input";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = true;
+//   arg_ainfo.multiplevalue = true;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = g_EmptyText;
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+//   //we don't need this in the depositor
+//   arg_ainfo.shortname = "di1inputtype";
+//   arg_ainfo.longname = "depositor specific";
+//   arg_ainfo.multiplechar = true;
+//   arg_ainfo.multiplevalue = true;
+//   arg_ainfo.defaultstatus = cgiarginfo::weak;
+//   arg_ainfo.argdefault = g_EmptyText;
+//   arg_ainfo.savedarginfo = cgiarginfo::mustnot; // saved on disk
+//   argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+depositoraction::~depositoraction () {
+}
+
+
+bool depositoraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, recptprotolistclass * protos, ostream &logout) {
+
+  wizardaction::check_cgiargs(argsinfo,args,protos,logout);
+
+  text_t &current_page = args["p"];
+
+  // note that the "bildstatus" and "bildframe1" pages don't actually do anything
+  // functional so we don't need to worry about authenticating them (it's the 
+  // underlying "bild" page that does the building (and creates the frameset))
+  // This helps us overcome a bit of a problem we have with multiple pages trying
+  // to read from the key.db database at the same time.
+
+  //right now, anyone can do anything until it's time to build
+  if (current_page == "select") {
+    //authenticate the user if authentication is available
+    args["uan"] = 1;
+    args["ug"] = "all-collections-editor";
+  }
+
+
+  //Check to see if a file was specified
+  //if so, upload it and set the di1userfile, di1userfilesize, and 
+  //di1timestamp args.
+  //every time a new file is specified, a new timestamped folder is 
+  //created.  
+
+  // This doesn't create a problem but is inefficient (wasted space)
+  // Consider recoding at some point in the future.
+
+  fileupload_t *fileupload = args.getargfile("di1userfileinfo");
+
+  if (fileupload != NULL) {
+
+    if (!(*fileupload).tmp_name.empty() && file_exists((*fileupload).tmp_name)) {
+      // create the timestamp
+      time_t timestamp = time(NULL);
+      text_t timestamp_str(timestamp); 
+
+      args["di1timestamp"] = timestamp_str;
+
+      // set filename and size from the fileupload struct
+      args["di1userfilesize"] = (*fileupload).size;
+      args["di1userfile"] = (*fileupload).name;
+      // copy the file into its temporary location
+      text_t tmpdir = filename_cat(gsdlhome,"tmp",args["di1tmp"],timestamp_str);
+      bool tflag = mk_dir(tmpdir);
+      text_t tmpfile = filename_cat(tmpdir,(*fileupload).name);
+      if (!file_copy((*fileupload).tmp_name, tmpfile)) {
+	cerr << "Depositor error: cannot save uploaded fileto  "<<tmpfile<<endl;
+      } else {
+	cerr << "Depositor: saved uploaded file to "<<tmpfile<<endl;
+      }
+    } 
+  }
+  
+  if (current_page == "select") {  
+ 
+    //make sure the last page arg is re-set to zero 
+    //since a new collection is being chosen
+    args["di1lastpage"] = "0";
+
+    // assign (and create) a temporary directory This will create a new
+    // tbuild directory every time the user goes back to select a new
+    // collection.  It causes no problems except wasted space.  Should be
+    // addressed in the future
+    if (assign_tmpname (args, logout)==false) {
+      // there was an error creating the tmp dir
+      message="tmpfail";
+      return true; // true because we could still parse the arguments
+    }
+
+    // clean up any old builds left laying about in the tmp directory
+    // (note that it's possible this could take some time if there's a huge
+    // partially built collecton laying about so we'll make it an asynchronous
+    // system call)
+    gsdl_system ("perl -S cleantmp.pl", false, logout);
+
+  }
+
+  if (current_page == "bildstatus" || current_page == "bildcancel") {
+    // if .final file exists then build has finished
+    text_t fbld = filename_cat (gsdlhome, "tmp", args[macro_prefix + "tmp"], args[macro_prefix + "dirname"] + ".bld.final");
+    if (file_exists (fbld)) {
+      char *fbldc = fbld.getcstr();
+      ifstream fbld_in (fbldc);
+      if (fbld_in) {
+	failcode = fbld_in.get();
+	fbld_in.close();
+	if (failcode == '0') {
+	  // success - we need to create and configure a collection server for the
+	  // newly built collection (for fastcgi and local library where
+	  // initialization isn't going to be redone when the user clicks the
+	  // "view your new collection" button
+	  create_colserver (args[macro_prefix + "dirname"], logout);
+	  current_page = "bilddone";
+	}
+	else current_page = "bildfail";
+      } else {
+	// assume build failed (we shouldn't get here though ... right?)
+	current_page = "bildfail";
+      }
+      delete []fbldc;
+    }
+  }
+
+  //is it a step page?
+  text_t::const_iterator here = current_page.begin();
+  text_t::const_iterator end = current_page.end();
+  text_t stepstring = substr(here,here+4);
+
+  //if so, increment the step count 
+  //this should not be going up more than one at a time. 
+  if(stepstring == "step") {
+    text_t currpage_textt = substr(here+4,here+5);
+    int currpage = currpage_textt.getint();
+
+    text_t lastpage_textt = args["di1lastpage"]; 
+    int lastpage = lastpage_textt.getint(); 
+    if (currpage > lastpage) {
+      lastpage++; 
+      text_t lastpage_textt(lastpage); 
+      args["di1lastpage"] = lastpage_textt;      
+    }
+  }
+  return true;
+}  
+
+
+void depositoraction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					      recptprotolistclass *protos, ostream &logout) {
+
+  // define_internal_macros sets some/all of the following macros (depending
+  // on cgiargs):
+  //
+  // _pagescriptextra_
+  // _header_
+  // _depositorbar_
+  // _textfailmsg_
+  // _di1userfile_
+  // _di1userfilesize_
+
+  text_t &depositor_page = args["p"];
+  
+  // set _pagescriptextra_ macro to _cpagescriptextra_
+  disp.setmacro ("pagescriptextra", "depositor", "_" + depositor_page + "scriptextra_");
+
+  if (depositor_page == "bildstatus" || depositor_page == "bilddone" || 
+      depositor_page == "bildfail" || depositor_page == "bildframe1" || 
+      depositor_page == "select") {
+    disp.setmacro ("header", "depositor", "_" + depositor_page + "header_");
+  }
+
+  if (depositor_page == "bildstatus") {
+    set_statusline (disp, args, logout);
+  }
+
+  if (depositor_page == "select") {
+    set_fullnamemenu (disp, args, protos, logout);
+  }
+
+  //how many pages in collection?
+  //we need a _depositorbar_ for each
+
+  text_t numsteps_str;
+  disp.expandstring("depositor", "_numsteps_", numsteps_str);
+  int numsteps = numsteps_str.getint();
+   
+  //is the page a step page? 
+
+  text_t::const_iterator here = depositor_page.begin();
+  text_t::const_iterator end = depositor_page.end();
+  text_t stepstring = substr(here,here+4);
+
+  if((stepstring) == "step" || (depositor_page == "depositonly") ) {
+    disp.setmacro("di1userfile","depositor",args["di1userfile"]);
+    disp.setmacro("di1userfilesize","depositor",args["di1userfilesize"]);
+  }
+
+  //set up the depositor bar
+  text_t depositorbar = "<table class=wizardbar border=0 cellspacing=4 cellpadding=0><tr>\n";
+
+  if(stepstring == "step") {
+
+    here = depositor_page.begin();
+    text_t stepnums = substr(here+4,here+5); 
+    int stepnum = stepnums.getint();
+
+    text_t lastpage_textt = args["di1lastpage"];
+    int lastpage = lastpage_textt.getint(); 
+    
+    // again, begin with the select bar...
+    //the first button - selecting a collection
+    for(int i = 1; i <= numsteps; i++) {
+      text_t numstr(i); 
+      if(i <= lastpage && i == stepnum) {
+	depositorbar += get_button (args,depositor_page, "yellow", "step"+numstr, false);
+      } else if (i <= lastpage) {
+	depositorbar += get_button (args,depositor_page, "yellow", "step"+numstr, true); 
+      } else if((i == lastpage+1)){
+	depositorbar += get_button (args,depositor_page, "green", "step"+numstr, true);       
+      } else { 
+	depositorbar += get_button (args,depositor_page, "grey", "step"+numstr, false);       
+      }
+      depositorbar += "<td>_icongreyarrow_</td>\n";
+
+    }
+
+    //the build and preview pages are always last
+    if(lastpage == numsteps) {
+      depositorbar += get_button (args,depositor_page, "green", "laststep", true);
+    } else {
+      depositorbar += get_button (args,depositor_page, "grey", "laststep", false);
+    }
+
+    text_t laststep_textt;
+    disp.expandstring("depositor", "_laststep_", laststep_textt);
+    if(laststep_textt == "bild") {
+      depositorbar += "<td>_icongreyarrow_</td>\n";
+      depositorbar += get_button (args,depositor_page, "grey", "view", false);
+    }
+    depositorbar += "</tr><tr>"; 
+
+    for(int j = 1; j <= 2*(stepnum-1); j++) {
+      depositorbar += "<td></td>";
+    }
+    depositorbar += "<td align=center>_icongreyuparrow_</td>\n";    
+
+    depositorbar += "</tr></table>\n";
+    
+  }
+  
+  if ((depositor_page == "bildcancel") || (depositor_page == "bildfail")) {
+  
+    for(int i = 1; i <= numsteps; i++) {
+
+      text_t numstr(i); 
+      depositorbar += get_button (args,depositor_page, "yellow", "step"+numstr, true);       
+      depositorbar += "<td>_icongreyarrow_</td>\n";
+
+    }
+
+    //the build and preview pages are always last
+    depositorbar += get_button (args,depositor_page, "green", "laststep", true);
+    depositorbar += "<td>_icongreyarrow_</td>\n";
+    depositorbar += get_button (args,depositor_page, "grey", "view", false); 
+
+    depositorbar += "</tr><tr>"; 
+
+    for(int j = 1; j <= 2*numsteps; j++) {
+      depositorbar += "<td></td>";
+    }
+    depositorbar += "<td align=center>_icongreyuparrow_</td>\n";    
+
+    depositorbar += "</tr></table>\n";
+
+  }
+
+  if(depositor_page == "bilddone"){
+    
+    for(int i = 1; i <= numsteps; i++) {
+
+      text_t numstr(i); 
+      depositorbar += get_button (args, depositor_page, "grey", "step"+numstr, false);       
+      depositorbar += "<td>_icongreyarrow_</td>\n";
+
+    }
+
+    //the build and preview pages are always last
+    depositorbar += get_button (args,depositor_page, "yellow", "laststep", false);
+    depositorbar += "<td>_icongreyarrow_</td>\n";
+    depositorbar += get_button (args,depositor_page, "green", "view", true);
+
+    depositorbar += "</tr><tr>"; 
+
+    for(int j = 1; j <= 2*numsteps; j++) {
+      depositorbar += "<td></td>";
+    }
+    depositorbar += "<td align=center>_icongreyuparrow_</td>\n";    
+
+    depositorbar += "</tr></table>\n";
+  }
+  disp.setmacro ("depositorbar", "depositor", depositorbar);
+
+  if (depositor_page == "bildfail") {
+ 
+    text_t textfailmsg = "_textfailmsg";
+    textfailmsg.push_back(failcode);
+    textfailmsg.push_back('_');
+    disp.setmacro("textfailmsg", "depositor", textfailmsg);
+
+    text_t bldlog = filename_cat(gsdlhome, "tmp", args["di1tmp"], args["di1dirname"] + ".bld");
+    text_t rawlog = file_tail (bldlog, 6, 0);
+    // we'll shove in some <br> tags where \n's occur
+    text_t faillog;
+    text_t::const_iterator here = rawlog.begin();
+    text_t::const_iterator end = rawlog.end();
+    while (here != end) {
+      if (*here == '\n') faillog += "<br>";
+      faillog.push_back (*here);
+      ++here;
+    }
+    disp.setmacro ("faillog", "depositor", dm_safe(faillog));
+  }
+}
+
+
+//basic framework
+bool depositoraction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
+				 browsermapclass * /*browsers*/, displayclass &disp, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout) {
+  // make sure the depositor is enabled
+  if (disabled) {
+    textout << outconvert 
+	    << "<html>\n"
+	    << "<head>\n"
+	    << "<title>Depositor disabled</title>\n"
+	    << "</head>\n"
+	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	    << "<h2>Facility disabled</h2>\n" 
+	    << "Sorry, the Depositor end-user collecation update facility is currently disabled\n"
+	    << "\n</body>\n"
+	    << "</html>\n";
+    return true;
+  }
+
+  text_t &depositor_page = args["p"];
+  text_t &collection = args["di1dirname"];
+
+  // make sure we have perl (we won't bother with this check for the 
+  // building status pages to avoid slowing things down unneccessarily)
+  if (depositor_page != "bildstatus" && depositor_page != "bildframe1" && !perl_ok(logout)) {
+    textout << outconvert 
+	    << "<html>\n"
+	    << "<head>\n"
+	    << "<title>Perl not found</title>\n"
+	    << "</head>\n"
+	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	    << "<h2>Perl not found</h2>\n" 
+	    << "Greenstone could not detect perl on this system. It is therefore not\n"
+	    << "possible to build a Greenstone collection, either from the Collector or the \n"
+	    << "command-line tools, or to use the Collector for any other task.\n"
+	    << "<p>Please refer to the Greenstone Installer's Guide for details on\n"
+	    << "installing perl on your system.\n"
+	    << "\n</body>\n"
+	    << "</html>\n";
+    return true;
+
+  }
+
+  text_t::const_iterator here = depositor_page.begin();
+  text_t::const_iterator end = depositor_page.end();
+  text_t stepstring = substr(here,here+4);
+
+  if ((depositor_page == "select") || (stepstring == "step")) {
+          textout << outconvert << disp << ("_depositor:header_\n")
+	      << ("_depositor:" + depositor_page + "content_\n")
+	      << ("_depositor:footer_\n");
+	  
+  }
+
+  if ((depositor_page == "bild") || (depositor_page == "depositoronly")) {
+
+    text_t filename_textt = args["di1userfile"];
+    text_t timestamp_str = args["di1timestamp"];
+    //check to make sure a file was uploaded
+    //if more than one upload occurred, the last one is taken
+    if(filename_textt == "") {
+      textout << outconvert << disp << "<p> no file available</p>\n"; 
+    } else {
+    
+      text_t col_dirname = filename_cat(collecthome,args[macro_prefix+"dirname"]);
+      text_t import_dirname = filename_cat(col_dirname,"import");
+      if(!directory_exists(import_dirname)) {
+	bool flag = mk_dir(import_dirname);
+	if(!flag) {
+	  cerr << "Error: unable to make directory " << import_dirname << endl;
+	  return true; 
+	}
+      } 
+
+      text_t dirname = filename_cat(import_dirname,timestamp_str);
+      bool flag = mk_dir(dirname);
+      if(!flag) {
+	cerr << "Error: unable to make timestamp directory " << dirname << endl;
+	return true; 
+      }
+
+      text_t filename_textt = args["di1userfile"]; 
+      text_t tmpdir = filename_cat(gsdlhome,"tmp",args["di1tmp"],timestamp_str);
+      text_t tmpfile = filename_cat(tmpdir,filename_textt);
+      text_t filename = filename_cat(dirname, filename_textt);  
+      if(!file_copy(tmpfile, filename)) {
+	cerr << "Unable to copy " << tmpfile << " to " << filename << endl; 
+      }
+      
+      //write the metadata file
+      write_metadata_file(args, filename_textt, timestamp_str); 
+
+      if(depositor_page == "bild"){
+
+	//create a manifest file 
+	write_manifest_file(args, filename_textt, timestamp_str);
+	
+	// do the work (download, import, build) 
+	gsdl_build (args, logout);
+
+	if (message.empty()) {
+	  // bild page is a frameset so we don't want headers and stuff
+	  textout << outconvert << disp << ("_depositor:bildcontent_\n");
+	}
+      }
+    }
+  }
+
+  if (message.empty()) {
+
+    if (depositor_page != "bild" && stepstring != "step" && depositor_page != "select") {
+      // output page ("bild" page was already output above)
+      textout << outconvert << disp << ("_depositor:header_\n")
+	      << ("_depositor:" + depositor_page + "content_\n")
+	      << ("_depositor:footer_\n");
+    }
+  } else {
+    // message was set somewhere (probably an error), output message page
+    textout << outconvert << disp << ("_depositor:header_\n")
+	    << ("_depositor:" + message + "content_\n")
+	    << ("_depositor:footer_\n");
+    message.clear();
+  }
+ 
+  return true;
+
+}
+
+
+//This function creates a metadata.xml file
+void depositoraction::write_metadata_file(cgiargsclass &args, text_t filename_str, text_t& timestamp_str)
+{
+
+    //build metadata file
+    //the front
+    text_t metadata_file = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+    metadata_file += "<!DOCTYPE DirectoryMetadata SYSTEM \"http://greenstone.org/dtd/DirectoryMetadata/1.0/DirectoryMetadata.dtd\">\n"; 
+    metadata_file += "<DirectoryMetadata>\n";
+    metadata_file += "    <FileSet>\n";
+    metadata_file += "        <FileName>";
+
+    // Generate 'regular expression' save version of filename
+    // for now that means escaping any '.' characters
+    // consider generalising as function in gsdltools.h/cpp?
+
+    text_t filename_re;
+    text_t::const_iterator here = filename_str.begin();
+    text_t::const_iterator end = filename_str.end();
+    while (here != end) {
+      if (*here == '.') {
+	filename_re.push_back('\\');
+      }
+
+      filename_re.push_back(*here);
+      ++here;
+    }
+
+    metadata_file += filename_re;
+
+    metadata_file += "</FileName>\n"; 
+    metadata_file += "        <Description>\n";
+
+
+    cgiargsclass::const_iterator args_here = args.begin();
+    cgiargsclass::const_iterator args_end = args.end();
+
+    while (args_here != args_end) {
+      text_t args_name = (*args_here).first;
+
+      int prefix_len = macro_prefix.size();
+      
+      text_t args_prefix = substr(args_name.begin(),args_name.begin()+prefix_len+3);
+      
+      if (args_prefix == (macro_prefix+"md.")) {     
+        text_t args_val = args[args_name];
+
+	text_t args_suffix = substr(args_name.begin()+prefix_len+3,args_name.end()); 
+
+	text_tarray mdvalues;
+	splitchar (args_val.begin(), args_val.end(), ',', mdvalues);
+	int numvalues = mdvalues.size();
+
+	for (int i = 0; i < numvalues; ++i) {
+	  if (!mdvalues[i].empty()) {
+
+	    decode_cgi_arg(mdvalues[i]);
+
+	    metadata_file += "            <Metadata mode=\"accumulate\" name=\"";
+	    metadata_file += args_suffix;
+	    metadata_file += "\">";
+	    metadata_file += mdvalues[i];
+	    metadata_file += "</Metadata>\n";
+
+	  }
+	}
+
+      }	
+
+
+      ++args_here;
+    }
+    
+    //the end of the file
+    metadata_file += "        </Description>\n"; 
+    metadata_file += "    </FileSet>\n"; 
+    metadata_file += "</DirectoryMetadata>\n"; 
+
+    //create metadata.xml file
+    text_t metadata_path = filename_cat(collecthome, args[macro_prefix+"dirname"], "import", timestamp_str, "metadata.xml");
+    text_t my_path = filename_cat(gsdlhome,"tmp", "metadata.xml");
+
+    ofstream metadata(metadata_path.getcstr());
+    ofstream my_tmp(my_path.getcstr()); 
+
+    if(!metadata.is_open()) {
+      cerr << "Cannot open metadata.xml!" << endl; 
+    } else {
+
+      //write metadata.xml
+      metadata.write(metadata_file.getcstr(), metadata_file.size()); 
+      my_tmp.write(metadata_file.getcstr(), metadata_file.size()); 
+    }
+}
+
+//This function creates a manifest.xml file
+void depositoraction::write_manifest_file(cgiargsclass &args, text_t filename, 
+					  text_t& timestamp_str)
+{
+
+    //build manifest file
+
+    //the front
+    text_t manifest_file = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+    manifest_file += "<Manifest>\n";
+    manifest_file += "    <Index>\n";
+    manifest_file += "        <Filename>\n";
+    manifest_file += "          " + timestamp_str + "\n";
+    manifest_file += "        </Filename>\n"; 
+    
+    //the end of the file
+    manifest_file += "    </Index>\n"; 
+    manifest_file += "</Manifest>\n"; 
+
+    //create manifest.xml file
+    text_t manifest_path = filename_cat(collecthome, args[macro_prefix+"dirname"], "manifest.xml");
+
+    char* manifest_cstr = manifest_path.getcstr();
+
+    ofstream manifest(manifest_cstr);
+
+    if(!manifest.is_open()) {
+      cerr << "Cannot open" << manifest_cstr << endl; 
+    } else {
+
+      //write manifest.xml
+      char *manifest_file_cstr = manifest_file.getcstr();
+      manifest.write(manifest_file_cstr, manifest_file.size()); 
+      delete [] manifest_file_cstr;
+    }
+
+    delete [] manifest_cstr;
+}
+
+
+
+text_t depositoraction::get_button(cgiargsclass &args, const text_t &thispage, 
+				   const text_t &color, 
+				   const text_t &type, bool enabled) 
+{
+
+  text_t::const_iterator here = type.begin();
+  text_t::const_iterator end = type.end();
+  text_t stepstring = substr(here,here+4);
+  if ((color != "green" && color != "grey" && color != "yellow") ||
+      (type != "select" && type != "laststep" && stepstring != "step" && type != "view"))
+    return g_EmptyText;
+
+  text_t href = "_http"+type+"_";
+  text_t target = "";
+
+  here = thispage.begin();
+  stepstring = substr(here,here+4);  
+  if (thispage == "bildcancel" || thispage == "bildfail" || thispage == "select"|| stepstring == "step") {
+    // call the check submit macro instead of linking directly to the page
+
+    if(type == "laststep") {
+      href="\"javascript:check_submit('_"+type+"_');\"";
+    } else {
+      href="\"javascript:check_submit('"+type+"');\"";
+    }
+
+  } else if (type == "view") {
+   //target = " target=_top";
+    target = " target=_blank";
+  }
+
+  text_t tdclass = "wizardbar"+color; 
+  if (enabled) {
+    // link to the appropriate page
+    return "<td class="+tdclass+"><a href="+href+target+">_text"+type+"_</a></td>";
+  }
+  else {
+    // just display the text
+    return "<td class="+tdclass+">_text"+type+"_</td>";
+  }
+}
+
+
+#endif //GSDL_USE_DEPOSITOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/depositoraction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/depositoraction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/depositoraction.h	(revision 16561)
@@ -0,0 +1,71 @@
+/**********************************************************************
+ *
+ * depositoraction.h -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_DEPOSITOR_ACTION
+
+#ifndef DEPOSITORACTION_H
+#define DEPOSITORACTION_H
+
+#include "wizardaction.h"
+#include "receptionist.h"
+
+class depositoraction : public wizardaction {
+  
+protected:
+
+  int lastpage; 
+
+public:
+  depositoraction ();
+  virtual ~depositoraction ();
+
+  void write_metadata_file(cgiargsclass &args, text_t filename_str, text_t& timestamp_str);
+
+  void write_manifest_file(cgiargsclass &args, text_t filename, text_t& timestamp_str);
+
+  text_t get_action_name () {return "depositor";}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  //we need to redefine this
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  //and this
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  text_t get_button(cgiargsclass &args, const text_t &thispage, 
+		    const text_t &color, 
+		    const text_t &type, bool enabled);
+
+};
+
+#endif //!DEPOSITORACTION_H
+#endif //GSDL_USE_DEPOSITOR_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/documentaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/documentaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/documentaction.cpp	(revision 16561)
@@ -0,0 +1,1639 @@
+/**********************************************************************
+ *
+ * documentaction.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.
+ *
+ *********************************************************************/
+
+#include <string.h>
+
+#include "documentaction.h"
+#include "recptprototools.h"
+#include "OIDtools.h"
+#include "querytools.h"
+#include "unitool.h"
+#include "gsdltools.h"
+#include "highlighttext.h"
+#include "browsetoolsclass.h"
+
+documentaction::documentaction () {
+  recpt = NULL;
+
+  // this action uses cgi variables "a", "d", "cl",
+  // "x", "gc", "gt", "gp", and "hl"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "p";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "d";
+  arg_ainfo.longname = "document OID";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // whether or not a document should be retrieved from the 
+  // library or the Web.
+  arg_ainfo.shortname = "il";
+  arg_ainfo.longname = "internal link preference";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "l";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "cl";
+  arg_ainfo.longname = "classification OID";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // in this action "gc" controls the expand/contract
+  // contents function
+  arg_ainfo.shortname = "gc";
+  arg_ainfo.longname = "expand contents";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // in this action "gt" controls the expand/contract
+  // text function 0 = not expanded, 1 = expand unless 
+  // there are more than 10 sections containing text,
+  // 2 = expand all
+  arg_ainfo.shortname = "gt";
+  arg_ainfo.longname = "expand text";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // in this action "gp" is the "go to page" control
+  // used by the Book type of toc
+  arg_ainfo.shortname = "gp";
+  arg_ainfo.longname = "go to page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // in this action "hl" is the "highlighting on/
+  // highlighting off control ("hl" == "2" is a
+  // special case that forces phrase highlighting
+  // to be used even if the query string doesn't
+  // appear to be a phrase)
+  arg_ainfo.shortname = "hl";
+  arg_ainfo.longname = "highlighting on/off";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "x" is 0 normally or 1 if page
+  // has been "detached"
+  arg_ainfo.shortname = "x";
+  arg_ainfo.longname = "detached page";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "xx" is 0 normally or 1 if documents should be detached by default
+  arg_ainfo.shortname = "xx";
+  arg_ainfo.longname = "detach all doc pages";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  
+  // f arg is set to 1 if document is to 
+  // be displayed in a frame
+  arg_ainfo.shortname = "f";
+  arg_ainfo.longname = "frame";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // fc arg is "1" if search bar is to be included (i.e. if "fc" == 1
+  // the httpdocument macro will include "&f=1"
+  arg_ainfo.shortname = "fc";
+  arg_ainfo.longname = "include search bar";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //rd is whether a document will be displayed
+  //with a relevant document list
+  arg_ainfo.shortname = "rd";
+  arg_ainfo.longname = "include relevant documents";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  //dm is the metadata that has been used for the datelist
+  arg_ainfo.shortname = "dm";
+  arg_ainfo.longname = "date metadata";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+documentaction::~documentaction()
+{
+}
+
+bool documentaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+				    recptprotolistclass *protos, ostream &logout) {
+
+   if(!args["d"].empty())
+      {
+	 text_t docTop;
+	 get_top(args["d"],docTop);
+	 
+	 recptproto* collectproto = protos->getrecptproto (args["c"], logout);
+	 if (collectproto != NULL)
+	    {
+	       ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
+	       
+	       if(cinfo->authenticate == "document") 
+		  {
+		     // both are either commented out or uncomment and are empty
+		     if (cinfo->public_documents.empty() && cinfo->private_documents.empty())
+			{
+			   //deny everything
+			   args["uan"] = "1";
+			   args["ug"] = cinfo->auth_group;
+			}
+
+		     // both public_documents and private_documents are turned on !
+		     else if (!cinfo->public_documents.empty() && !cinfo->private_documents.empty())
+			{
+			   //deny everything
+			   args["uan"] = "1";
+			   args["ug"] = cinfo->auth_group;
+			}
+		     
+		     // if public_documents is set but this document isn't
+		     // listed in it we need to authenticate
+		     else if ((!cinfo->public_documents.empty()) && 
+			      (cinfo->public_documents.find(docTop) == cinfo->public_documents.end()))
+		       {
+			 args["uan"] = "1";
+			 args["ug"] = cinfo->auth_group;
+		       }
+		     
+		     // if private_documents is set and this document is
+		     // listed in it we need to authenticate
+		     else if ((!cinfo->private_documents.empty()) &&
+			      (cinfo->private_documents.find(docTop) != cinfo->private_documents.end()))
+		       {
+			 args["uan"] = "1";
+			 args["ug"] = cinfo->auth_group;
+		       }
+		     
+		  }
+	    }
+      }
+   // check gc argument
+   int arg_gc = args.getintarg("gc");
+  if (arg_gc < 0 || arg_gc > 2) {
+    logout << "Warning: \"gc\" argument out of range (" << arg_gc << ")\n";
+    cgiarginfo *gcinfo = argsinfo.getarginfo ("gc");
+    if (gcinfo != NULL) args["gc"] = gcinfo->argdefault;
+  }
+
+  // check gt argument (may be either 0, 1 or 2)
+  int arg_gt = args.getintarg("gt");
+  if (arg_gt != 0 && arg_gt != 1 && arg_gt != 2) {
+    logout << "Warning: \"gt\" argument out of range (" << arg_gt << ")\n";
+    cgiarginfo *gtinfo = argsinfo.getarginfo ("gt");
+    if (gtinfo != NULL) args["gt"] = gtinfo->argdefault;
+  }
+
+  // check hl argument
+  int arg_hl = args.getintarg("hl");
+  if (arg_hl < 0 || arg_hl > 2) {
+    logout << "Warning: \"hl\" argument out of range (" << arg_hl << ")\n";
+    cgiarginfo *hlinfo = argsinfo.getarginfo ("hl");
+    if (hlinfo != NULL) args["hl"] = hlinfo->argdefault;
+  }
+
+  // check x argument
+  int arg_x = args.getintarg("x");
+  if (arg_x != 0 && arg_x != 1) {
+    logout << "Warning: \"x\" argument out of range (" << arg_x << ")\n";
+    cgiarginfo *xinfo = argsinfo.getarginfo ("x");
+    if (xinfo != NULL) args["x"] = xinfo->argdefault;
+  }
+
+  //checks whether rd arg is valid
+  int arg_rd = args.getintarg("rd");
+  if (arg_rd != 0 && arg_rd != 1) {
+    logout << "Warning: \"rd\" argument out of range (" << arg_rd << ")\n";
+    cgiarginfo *rdinfo = argsinfo.getarginfo ("rd");
+    if (rdinfo != NULL) args["rd"] = rdinfo->argdefault;
+  }
+
+
+  return true;
+}
+
+void documentaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+				       response_t &response,text_t &response_data, 
+				       ostream &logout) {
+
+  if ((args["il"] == "w") && (!args["d"].empty())) {
+
+    recptproto* collectproto = protos->getrecptproto (args["c"], logout);
+    if (collectproto != NULL) {
+      
+      text_tset metadata;
+      FilterResponse_t filt_response;
+      text_t top;
+
+      metadata.insert ("URL");
+      
+      // get metadata for parent document
+      get_top (args["d"], top);
+      if (get_info (top, args["c"], args["l"], metadata, false, collectproto, filt_response, logout)) {
+	text_t url = filt_response.docInfo[0].metadata["URL"].values[0];
+	
+	response = location;
+	response_data = url;
+	return;
+      } else {
+	// error, no URL
+	logout << "Error: documentaction::get_cgihead_info failed on get_info" << endl;
+      }
+    }
+  }
+  response = content;
+  response_data = "text/html";
+}
+
+// set_widthtspace calculates how wide the spaces in the nav bar should
+// be and sets the appropriate macro
+void documentaction::set_spacemacro (displayclass &disp, FilterResponse_t &response,
+				     bool has_search_button) {
+
+  text_t width;
+  int twidth, swidth, iwidth = 0;
+
+  int numc = response.docInfo.size();
+  ResultDocInfo_tarray::iterator dochere = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator docend = response.docInfo.end();
+	
+  disp.expandstring (displayclass::defaultpackage, "_pagewidth_", width);
+  twidth = width.getint();
+
+  if (has_search_button) {
+    disp.expandstring ("query", "_searchwidth_", width);
+    iwidth += width.getint();
+  } else {
+    numc -= 1;
+  }
+  
+  while (dochere != docend) {
+    const text_t &title = (*dochere).metadata["Title"].values[0];
+
+    disp.expandstring ("document", "_" + title + "width_", width);
+    if (width == ("_" + title + "width_")) {
+      disp.expandstring ("document", "_defaultwidth_", width);
+    }
+    iwidth += width.getint();
+    ++dochere;
+  }
+
+  if ((twidth - iwidth) < numc) swidth = 2;
+  else {
+    swidth = twidth - iwidth;
+    if (numc > 0) swidth = swidth / numc;
+  }
+  disp.setmacro ("widthtspace", displayclass::defaultpackage, swidth);
+}
+
+// set_navbarmacros sets _navigationbar_ and _httpbrowseXXX_ macros
+// reponse contains 1 metadata field (Title)
+void documentaction::set_navbarmacros (displayclass &disp, FilterResponse_t &response, 
+				       bool has_search_button, cgiargsclass &args,
+				       ColInfoResponse_t &cinfo) {
+
+
+   bool use_pulldown = false;
+   text_tmap::iterator it = cinfo.format.find("NavigationBar");
+   if (it != cinfo.format.end()) {
+      if (it->second == "pulldown") {
+	 use_pulldown = true;
+      }
+   }
+
+   text_t topparent;
+   text_t &arg_d = args["d"];
+   text_t navigationbar = "<!-- Navigation Bar -->\n";
+
+   get_top (args["cl"], topparent);
+   int numc = response.docInfo.size();
+   ResultDocInfo_tarray::iterator dochere = response.docInfo.begin();
+   ResultDocInfo_tarray::iterator docend = response.docInfo.end();
+
+   if (!use_pulldown) {
+      if (has_search_button) {
+	// search uses its own navtab, as it may be suppressed based on other args
+	navigationbar += "_navtabsearch_";
+	if (args["a"] == "q") {
+	  navigationbar += "(selected)";
+	}
+      }
+      
+      if (has_search_button || numc == 0) navigationbar += "_navbarspacer_";
+      
+      bool first = true;
+      while (dochere != docend) {
+	if (!first) navigationbar += "_navbarspacer_";
+	
+	text_t title = (*dochere).metadata["Title"].values[0];
+	
+	navigationbar += "_navtab_(_httpbrowse"+(*dochere).OID+"_,"+title;
+	// if we are at this classifier
+	if (arg_d.empty() && ((*dochere).OID == topparent)) {
+	   // don't link to this classifier as it is the current one
+	   navigationbar += ",selected";
+	 }
+	 navigationbar += ")";
+
+	// set the _httpbrowseCLX_ macro for this classification
+	disp.setmacro ("httpbrowse" + (*dochere).OID, displayclass::defaultpackage, "_httpdocument_&amp;cl=" + (*dochere).OID);
+	 ++dochere;
+	 first = false;
+      }
+      
+      navigationbar += "_dynamicclassifiernavbarentries_";
+      navigationbar += "\n<!-- End of Navigation Bar -->\n";
+
+   } else {
+
+      navigationbar = "<form method=\"get\" name=\"navform\">\n";
+      navigationbar += "<select name=\"navbar\" onChange=\"location.href=";
+      navigationbar += "document.navform.navbar.options[document.navform.navbar.selectedIndex].value\">\n";
+
+      if (args["a"] != "q" && args["cl"].empty()) {
+	 navigationbar += "<option value=\"\" selected>_textselectpage_</option>\n";
+      }
+      
+      if (has_search_button) {
+	 navigationbar += "<option value=\"_httpquery_\"";
+	 if (args["a"] == "q") {
+	    navigationbar += " selected";
+	 }
+	 navigationbar += ">_labelSearch_</option>\n";
+      }
+
+      while (dochere != docend) {
+	 text_t title = dochere->metadata["Title"].values[0];	 
+
+	 navigationbar += "<option value=\"_httpdocument_&amp;cl=" + dochere->OID + "\"";
+	 if (topparent == dochere->OID) {
+	    navigationbar += " selected";
+	 }
+	 navigationbar += ">_navlinktitle_(" + title + ")</option>\n";
+	 ++dochere;
+      }
+
+      navigationbar += "</select>\n";
+      navigationbar += "</form>\n";
+   }
+
+   disp.setmacro ("navigationbar", displayclass::defaultpackage, navigationbar);
+}
+
+// define all the macros which might be used by other actions
+// to produce pages. 
+void documentaction::define_external_macros (displayclass &disp, cgiargsclass &args, 
+					     recptprotolistclass *protos, ostream &logout) {
+  
+  // define_external_macros sets the following macros:
+
+  // _navigationbar_     this is the navigation bar containing the search button
+  //                     and any classification buttons - it goes at the top of 
+  //                     most pages. for now we're assuming that there'll always 
+  //                     be a search button - we should probably check that there
+  //                     is a query action before making this assumption
+
+  // _httpbrowseXXX_     the http macros for each classification (i.e. if there
+  //                     are Title and Creator classifications _httpbrowseTitle_
+  //                     and _httpbrowseCreator_ will be set
+
+  // _widthtspace_       the width of the spacers between buttons in navigation
+  //                     bar
+  
+  // _httpdocument_      has '&f=1' added if displaying document inside a frame
+
+  // _gsdltop_           macro to replace _top targets with
+
+  // _httppagehome_      overridden home url if html collections have own homepage
+
+  // _usability_         macros for remote usability reporting: if
+  // _usabinterface_     "format Usability [multi|textonly|stepwise]" is in 
+  // _usabilityscript_   collect.cfg
+
+  // _versionnnum_   greenstone version number (hard-coded)
+  // must have a valid collection server to continue
+   
+  text_t &collection = args["c"];
+  if (collection.empty()) return;
+  recptproto *collectproto = protos->getrecptproto (collection, logout);
+  if (collectproto == NULL) return;
+  
+  if (recpt == NULL) {
+    logout << "ERROR (documentaction::define_external_macros): This action does not contain\n"
+	   << "      information about any receptionists. The method set_receptionist was\n"
+	   << "      probably not called from the module which instantiated this action.\n";
+    return;
+  }
+
+  outconvertclass text_t2ascii;
+  comerror_t err;
+  InfoFiltersResponse_t filterinfo;
+  FilterResponse_t response;
+  text_tset metadata;
+
+  // get info on current collection and load up formatinfo
+  // I'd prefer not to do this here as we're getting 
+  // collection info every time (and probably also getting
+  // it in other places some of the time) - One day I'll
+  // fix it ... maybe - Stefan.
+  ColInfoResponse_t cinfo;
+
+  collectproto->get_collectinfo (collection, cinfo, err, logout);
+  load_formatinfo (cinfo.format, args.getintarg("gt"));
+  //  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
+  //  if (cinfo == NULL) {
+  //    logout << "ERROR (documentaction::define_external_macros): get_collectinfo_ptr returned NULL\n";
+  //    return;
+  //  }
+  //load_formatinfo (cinfo->format, args.getintarg("gt"));
+
+  if (formatinfo.DocumentUseHTML) {
+
+    // frame stuff
+    if (args["fc"] == "1") {
+      text_t httpdocument;
+      disp.expandstring (displayclass::defaultpackage, "_httpdocument_", httpdocument);
+      httpdocument += "&amp;f=1";
+      disp.setmacro ("httpdocument", displayclass::defaultpackage, httpdocument);
+      disp.setmacro ("gsdltop", displayclass::defaultpackage, "documenttop");
+      formatinfo.DocumentText = "[Text]";
+    }
+    text_tmap::iterator it = cinfo.format.find ("homepage");
+    if (it != cinfo.format.end()) {
+      text_t httppagehome;
+      if (get_link (args, protos, (*it).second, httppagehome, logout))
+	disp.setmacro ("httppagehome", displayclass::defaultpackage, httppagehome);
+    } 
+  }
+
+  // don't want navigation bar if page is 'detached'
+  if (!args.getintarg("x")) {
+
+    collectproto->get_filterinfo (collection, filterinfo, err, logout);
+    if (err == noError) {
+      // check that there's a browse filter
+      if (filterinfo.filterNames.find ("BrowseFilter") != filterinfo.filterNames.end()) {
+	
+	metadata.insert ("Title");
+	bool getParents = false;
+	get_children ("", collection, args["l"], metadata, getParents, collectproto, response, logout);
+	
+	bool has_search_button = true;
+	collectproto->is_searchable(collection, has_search_button, err, logout);
+	if (err != noError) has_search_button = false;
+
+	// calculate width of spacers and set _widthtspace_ macro
+	if (args.getintarg("v") == 0) set_spacemacro (disp, response, has_search_button);
+
+	// set _navigationbar_ macro
+	set_navbarmacros (disp, response, has_search_button, args, cinfo);
+   
+      }
+    } else {
+      logout << text_t2ascii 
+	     << "Error (documentaction::define_external_macros()) in call to get_filterinfo() " 
+	     << get_comerror_string (err);
+    }
+  }
+
+  // send feedback button and pages
+  text_tmap::iterator usability = cinfo.format.find("Usability");
+  if(usability!=cinfo.format.end()){
+    disp.setmacro("usability",displayclass::defaultpackage,"_usablink_");
+    disp.setmacro("usabinterface",displayclass::defaultpackage,("_usab"+(*usability).second+"_"));
+    disp.setmacro("usabilityscript", displayclass::defaultpackage, "_usabshowscript_");
+  }
+
+  // version num
+  disp.setmacro("versionnum", displayclass::defaultpackage, GSDL_VERSION);
+
+}
+
+bool documentaction::get_link (cgiargsclass &args, recptprotolistclass *protos,
+			       const text_t &inlink, text_t &outlink, ostream &logout) {
+
+  FilterResponse_t response;
+  text_tset metadata;
+  metadata.insert ("section");
+  
+  // check current collection first
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+
+  if (get_info (inlink, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
+    if (!response.docInfo[0].metadata["section"].values[0].empty()) {
+#ifndef DOCHANDLE
+      outlink = "_httpdocument_&amp;d=" + response.docInfo[0].metadata["section"].values[0];
+#else
+      outlink = "_httpdocumenthandle_("+args["c"]+","+response.docInfo[0].metadata["section"].values[0]+")";
+#endif
+
+      return true;
+    }
+  }
+
+  // check all the other enabled collections
+
+  if (args["ccs"] == "1" && !args["cc"].empty()) {
+    text_tarray collections;
+    splitchar (args["cc"].begin(), args["cc"].end(), ',', collections);
+    
+    text_tarray::const_iterator col_here = collections.begin();
+    text_tarray::const_iterator col_end = collections.end();
+
+    while (col_here != col_end) {
+	  
+      // don't need to check current collection again
+      if (*col_here == args["c"]) {++col_here; continue;}
+	  
+      collectproto = protos->getrecptproto (*col_here, logout);
+      
+      if (get_info (inlink, *col_here, args["l"], metadata, false, collectproto, response, logout)) {
+	if (!response.docInfo[0].metadata["section"].values[0].empty()) {
+#ifndef DOCHANDLE
+	  outlink = "_httpdocument_&amp;c=" + *col_here + "&amp;d=" +
+	    response.docInfo[0].metadata["section"].values[0];
+#else
+	  outlink = "_httpdocumenthandle_("+*col_here+","+response.docInfo[0].metadata["section"].values[0]+")";
+#endif
+
+	  return true;
+	}
+      }
+      ++col_here;
+    }
+  }
+  return false;
+}
+
+void documentaction::load_formatinfo (const text_tmap &colformat, int gt) {
+
+  formatinfo.clear();
+  text_tmap::const_iterator format_here = colformat.begin();
+  text_tmap::const_iterator format_end = colformat.end();
+
+  while (format_here != format_end) {
+    if (((*format_here).first == "DocumentImages") && 
+	((*format_here).second == "true"))
+      formatinfo.DocumentImages = true;
+    else if (((*format_here).first == "DocumentTitles") && 
+	     ((*format_here).second == "false"))
+      formatinfo.DocumentTitles = false;
+    else if ((*format_here).first == "DocumentHeading") 
+      formatinfo.DocumentHeading = (*format_here).second;
+    else if (((*format_here).first == "DocumentContents") && 
+	     ((*format_here).second == "false"))
+      formatinfo.DocumentContents = false;
+    else if (((*format_here).first == "DocumentArrowsBottom") && 
+	     ((*format_here).second == "false"))
+      formatinfo.DocumentArrowsBottom = false;
+    else if (((*format_here).first == "DocumentArrowsTop") && 
+	     ((*format_here).second == "true"))
+      formatinfo.DocumentArrowsTop = true;
+    else if ((*format_here).first == "DocumentButtons")
+      splitchar ((*format_here).second.begin(), (*format_here).second.end(),
+		 '|', formatinfo.DocumentButtons);
+    else if ((*format_here).first == "DocumentText") 
+      formatinfo.DocumentText = (*format_here).second;  
+    else if ((*format_here).first == "RelatedDocuments")
+      formatinfo.RelatedDocuments = (*format_here).second; 
+    else if (((*format_here).first == "DocumentUseHTML") && 
+	     ((*format_here).second == "true"))
+      formatinfo.DocumentUseHTML = true;
+    else if (((*format_here).first == "AllowExtendedOptions") && 
+	     ((*format_here).second == "true"))
+      formatinfo.AllowExtendedOptions = true;
+    else 
+      formatinfo.formatstrings[(*format_here).first] = (*format_here).second;
+    
+    ++format_here;
+  }
+
+  // never want arrows when text is expanded
+  if (gt) {
+    formatinfo.DocumentArrowsBottom = false;
+    formatinfo.DocumentArrowsTop = false;
+  }
+}
+
+
+// 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 documentaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					     recptprotolistclass *protos, 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
+
+  // _navarrowsbottom_      this may be overridden to "" when format option 
+  //                        DocumentArrowsBottom is false
+
+  // _navarrowstop_         likewise for DocumentArrowsTop
+
+  // _httpprevarrow_        links to next and previous sections of document - used
+  // _httpnextarrow_        by DocumentArrowsBottom
+
+  // _header_               the header macro is overridden if we're not at a top level 
+  //                        classification to remove the title block
+
+  // _thisOID_              the OID (directory) of the current document - this corresponds
+  //                        to the archivedir metadata element
+
+  // _documentheader_       Custom header info for the specified document
+  // must have a valid collection server to continue
+
+   text_t &collection = args["c"];
+  if (collection.empty()) return;
+  recptproto *collectproto = protos->getrecptproto (collection, logout);
+  if (collectproto == NULL) return;
+
+  text_tset metadata;
+  FilterResponse_t response;
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+
+  if (!formatinfo.DocumentArrowsBottom) {
+    disp.setmacro("navarrowsbottom", "document", "");
+  } else if (!formatinfo.DocumentArrowsBottom) {
+    disp.setmacro("navarrowstop", "document", "");
+  } 
+
+  if (!arg_d.empty() && (formatinfo.DocumentArrowsBottom || formatinfo.DocumentArrowsTop)) {
+    // set _httpprevarrow_ and _httpnextarrow_
+    set_arrow_macros (args, collectproto, disp, logout);
+  }
+
+  metadata.insert ("Title");
+  metadata.insert ("gs.DocumentHeader");
+  metadata.insert ("DocumentHeader");
+  
+  bool fulltoc = false;
+
+  if (args["cl"] != "search") {
+    // see if there's a FullTOC string
+    text_t cl_top, full_toc;
+    get_top (arg_cl, cl_top);
+    if (get_formatstring (cl_top, "FullTOC", formatinfo.formatstrings, full_toc))
+      if (full_toc == "true") fulltoc = true;
+  }
+  
+  if (!arg_d.empty() && !fulltoc) {
+    // we're at document level
+
+    metadata.insert ("archivedir");
+
+    comerror_t err;
+    OptionValue_tarray options;
+    // we need to do the query again for the z3950proto
+    if (collectproto->get_protocol_name(err)=="z3950proto") {
+      OptionValue_t opt;
+      opt.name="Term";opt.value=args["q"];options.push_back(opt);
+      opt.name="QueryType";
+      opt.value=(args.getintarg("t")) ? "ranked" : "boolean";
+      options.push_back(opt);
+      opt.name="Index";opt.value=args["h"];options.push_back(opt);
+    }
+    
+    //do not display relation metadata
+    disp.setmacro ("relateddoc", "document", "");  
+       
+    //if preferences indicate relevant docs should be collected
+    //and there is no particular format specified then display
+    //this default format.
+     if(args["rd"] == "1" && formatinfo.RelatedDocuments.empty()){
+
+       text_t relation = g_EmptyText; //string for displaying relation metadata
+
+       //call function in formattools.cpp which will return the text of the 
+       //related documents in a vertical list. This is the default format.
+
+       if (get_info (arg_d, collection, args["l"], metadata, options, false, collectproto, response, logout)) 
+	 relation += get_related_docs(collection, collectproto, response.docInfo[0], logout);
+       
+       //set macro to be the related document string
+
+       disp.setmacro ("relateddoc", "document", relation); 
+     }
+     
+  
+    // get metadata for this document and it's parents
+    if (get_info (arg_d, collection, args["l"], metadata, options, 
+		  true, collectproto, response, logout)) {
+
+      disp.setmacro ("header", "document", "_textheader_");
+      text_tarray pagetitlearray;
+      if (!response.docInfo[0].metadata["Title"].values[0].empty())
+	pagetitlearray.push_back (response.docInfo[0].metadata["Title"].values[0]);
+      
+      if (args["gt"] != "1") {
+	MetadataInfo_t *parenttitle = response.docInfo[0].metadata["Title"].parent;
+	while (parenttitle != NULL) {
+	  if (!parenttitle->values[0].empty())
+	    pagetitlearray.push_back (parenttitle->values[0]);
+	  parenttitle = parenttitle->parent;
+	}
+      }
+      reverse (pagetitlearray.begin(), pagetitlearray.end());
+      text_t pagetitle;
+      joinchar (pagetitlearray, ": ", pagetitle);
+      // remove html tags from the title
+      text_t::iterator open_tag=pagetitle.begin();
+      while (open_tag < pagetitle.end()) {
+	if (*open_tag == '<') {
+	  text_t::iterator close_tag=open_tag+1;
+	  text_t::iterator donechar=pagetitle.end();
+	  while (close_tag < donechar) {
+	    if (*close_tag == '>')
+	      break;
+	    ++close_tag;
+	  }
+	  if (close_tag < donechar) // remove this html tag, replace with space
+	    *close_tag=' ';
+	  pagetitle.erase(open_tag, close_tag);
+	}
+	++open_tag;
+      }
+      disp.setmacro ("pagetitle", "document", pagetitle);
+
+      // custom header info
+      text_t custom_header;
+      if (is_top (arg_d)) {
+	custom_header = response.docInfo[0].metadata["gs.DocumentHeader"].values[0];
+	if (custom_header.empty()) {
+	  // try ex header
+	  custom_header = response.docInfo[0].metadata["DocumentHeader"].values[0]; 
+	}
+      } else {
+	MetadataInfo_t *parentch = response.docInfo[0].metadata["gs.DocumentHeader"].parent;
+	while (parentch != NULL) {
+	  custom_header = parentch->values[0];
+	  parentch = parentch->parent;
+	}
+	if (custom_header.empty()) {
+	  // try ex header
+	  MetadataInfo_t *parentch = response.docInfo[0].metadata["DocumentHeader"].parent;
+	  while (parentch != NULL) {
+	    custom_header = parentch->values[0];
+	    parentch = parentch->parent;
+	  }
+	}
+      }
+      if (!custom_header.empty()) {
+	disp.setmacro("documentheader", "document", custom_header);
+      }
+	
+      if (is_top (arg_d))
+	disp.setmacro ("thisOID", displayclass::defaultpackage, dm_safe(response.docInfo[0].metadata["archivedir"].values[0]));
+      else {
+	MetadataInfo_t *parentad = response.docInfo[0].metadata["archivedir"].parent;
+	text_t thisOID;
+	while (parentad != NULL) {
+	  thisOID = parentad->values[0];
+	  parentad = parentad->parent;
+	}
+	disp.setmacro ("thisOID", displayclass::defaultpackage, dm_safe(thisOID));
+      }
+    }
+  } else {
+    if (!arg_cl.empty()) {
+	
+      // create the currentnodevalue macro - this is the value of the node
+      // in the hierarchy that is currently open.
+      if (get_info (arg_cl, collection, args["l"], metadata, false, collectproto, response, logout)) {
+	text_t &title = response.docInfo[0].metadata["Title"].values[0];
+	disp.setmacro ("currentnodevalue", "document", title);
+      }
+      // get metadata for top level classification
+      text_t classtop;
+      get_top (arg_cl, classtop);
+      metadata.insert ("childtype");
+      metadata.insert ("parameters");
+
+      if (get_info (classtop, collection, args["l"], metadata, false, collectproto, response, logout)) {
+	  
+	text_t &title = response.docInfo[0].metadata["Title"].values[0];
+	bool unknown = false;
+
+	// test if we have macros defined for this classification's
+	// metadata name - if not we'll just display the name
+	text_t tmp;
+	text_t macroname="_label" + title + "_";
+	disp.expandstring ("Global", macroname, tmp);
+	if (tmp == macroname) unknown = true; // it wasn't expanded
+
+	if (unknown) {
+	  disp.setmacro ("pagetitle", "document", title);
+	} else {
+	  disp.setmacro ("pagetitle", "document", macroname);
+	}
+	/* for ns4/image layout compatibility. when this is no longer
+	 * needed, set imagethispage to _label+title+_ */
+	text_t titlemacroname="_titleimage" + title + "_";
+	disp.expandstring ("Global", titlemacroname, tmp);
+	if (tmp == titlemacroname) { /* _titleimage$META_ isn't defined */
+	  if (!unknown) /* _label$META_ is defined */
+	    disp.setmacro ("imagethispage", "document", macroname);
+	  else
+	    disp.setmacro ("imagethispage", "document", title);
+	} else { /* _titleimage$META_ is defined */
+	  disp.setmacro ("imagethispage", "document", titlemacroname);
+	}
+
+      	//if the document is not a document from a collection
+	//we must set the macro to be an empty string
+	disp.setmacro ("relateddoc", "document", ""); 
+
+	// Add macros specific to the Collage/Phind classifiers 
+	text_t &childtype = response.docInfo[0].metadata["childtype"].values[0];
+	if (childtype == "Collage") {
+
+	  text_t::iterator a = arg_cl.begin();
+	  text_t::iterator b = arg_cl.end();
+
+	  bool collage = true;
+	  while (a != b) {
+	    if (*a == 46) collage = false;
+	    ++a;
+	  }
+
+	  if (collage) {
+	    disp.setmacro ("collageclassifier", "document", "_collageapplet_");
+
+            // Next, macros that control the way the classifier is displayed
+            text_t parameters = response.docInfo[0].metadata["parameters"].values[0];
+
+            // extract key=value pairs and set as macros
+            text_t::iterator here = parameters.begin();
+            text_t::iterator end = parameters.end();
+            text_t key, value;
+
+            while (here != end) {
+              // get the next key and value pair
+              here = getdelimitstr (here, end, '=', key);
+              here = getdelimitstr (here, end, ';', value);
+
+              // store this key=value pair
+              if (!key.empty() && !value.empty()) {
+                disp.setmacro ("collage"+key, "document", value);
+              }
+            }
+
+	    
+	  }
+	}
+
+	if (childtype == "Phind") {
+
+	  // First, a macro to display the phind classifier
+	  disp.setmacro ("phindclassifier", "document", "_phindapplet_");
+
+	  // Next, macros that control the way the classifier is displayed
+	  text_t parameters = response.docInfo[0].metadata["parameters"].values[0];
+
+	  // extract key=value pairs and set as macros
+	  text_t::iterator here = parameters.begin();
+	  text_t::iterator end = parameters.end();
+	  text_t key, value;
+	  
+	  while (here != end) {
+	    // get the next key and value pair
+	    here = getdelimitstr (here, end, '=', key);
+	    here = getdelimitstr (here, end, ';', value);
+
+	    // store this key=value pair
+	    if (!key.empty() && !value.empty()) {
+	      disp.setmacro (key, "document", value);
+	    }
+	  }
+	} // end if (childtype == "Phind")
+      }
+    } // end if (!arg_cl.empty()) {
+  }
+}
+
+
+bool documentaction::do_action(cgiargsclass &args, recptprotolistclass *protos, 
+                               browsermapclass *browsers, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout)
+{
+  if ((args["book"] == "flashxml") && (!args["d"].empty()))
+  {
+  	return do_action_flashxml(args,protos,browsers,disp,outconvert,textout,logout);
+  }
+  else if ((args["book"] == "on") && (!args["d"].empty())) 
+  {
+  	return do_action_book(args,protos,browsers,disp,outconvert,textout,logout);
+  }
+  else 
+  {
+   	return do_action_html(args,protos,browsers,disp,outconvert,textout,logout);
+  }
+
+}
+
+//displaying the normal display when bookswitch=0(html)
+bool documentaction::do_action_html(cgiargsclass &args, recptprotolistclass *protos, 
+                               browsermapclass *browsers, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout)
+{
+  // must have a valid collection server
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL) {
+    logout << "documentaction::do_action called with NULL collectproto\n";
+    textout << outconvert << disp << "_document:header_\n" 
+	    << "Error: Attempt to get document without setting collection\n"
+	    << "_document:footer_\n";
+  } else {  
+    
+    text_t OID = args["d"];
+    if (OID.empty()) OID = args["cl"];
+    if (OID.empty()) {
+      textout << outconvert << disp << "Document contains no data_document:footer_\n";
+      return true;
+    }
+
+  
+    if (formatinfo.DocumentUseHTML && !args["d"].empty()) {
+      
+      if (args["f"] == "1") {
+	textout << outconvert << disp 
+		<< "<html><head></head>\n"
+		<< "<frameset rows=\"68,*\" noresize border=0>\n"
+		<< "<frame scrolling=no frameborder=0 src=\"_gwcgi_?_optsite_e=_compressedoptions_&a=p&p=nav\">\n"
+#ifndef DOCHANDLE
+		<< "<frame name=\"documenttop\" frameborder=0 src=\"_gwcgi_?_optsite_e=_compressedoptions_&a=d&d=" 
+		<< args["d"] << "\">"
+#else
+		<< "<frame name=\"documenttop\" frameborder=0 src=\"_httpdocumenthandle_(" 
+		<< args["c"] << "," << args["d"] << ")\">"
+#endif
+		<< "<noframes>\n"
+		<< "<p>You must have a frame enabled browser to view this.</p>\n"
+		<< "</noframes>\n"
+		<< "</frameset>\n"
+		<< "</html>\n";
+      } else {
+	output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout);	
+      }
+      return true;
+    }
+
+   
+    textout << outconvert << disp << "_document:header_\n"
+	    << "_document:content_\n";
+      
+    // output the table of contents
+    browsetoolsclass b;
+    b.output_toc(args, browsers, formatinfo, collectproto, 
+                 disp, outconvert, textout, logout);
+   
+    if (formatinfo.DocumentArrowsTop && !args["d"].empty()) {
+      textout << outconvert << disp << "_document:navarrowstop_\n";
+    }
+
+    //output the related documents (may be the empty string)
+    //will not output the docs if a format string is specified
+    textout << outconvert << disp << "_document:relateddoc_\n";
+
+    // output the document text
+    if (!args["d"].empty()) {
+      output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout);
+    }
+
+    textout << outconvert << disp << "_document:footer_\n";
+  }
+  return true;
+}
+
+//displaying the book display when bookswitch=on
+bool documentaction::do_action_book(cgiargsclass &args, recptprotolistclass *protos, 
+                               browsermapclass *browsers, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout)
+{
+  // must have a valid collection server
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL) 
+  {
+    	logout << "documentaction::do_action called with NULL collectproto\n";
+    	textout << outconvert << disp << "_document:header_\n" 
+		<< "Error: Attempt to get document without setting collection\n";
+  } 
+  else 
+  {  
+    	text_t OID = args["d"];
+    	if (OID.empty()) OID = args["cl"];
+    	if (OID.empty()) 
+	{
+      		textout << outconvert << disp << "Document contains no data\n";
+      		return true;
+    	}
+
+    	if (formatinfo.DocumentUseHTML && !args["d"].empty()) 
+	{
+      		if (args["f"] == "1") 
+		{
+			textout << outconvert << disp 
+				<< "<html><head></head>\n"
+				<< "<frameset rows=\"68,*\" noresize border=0>\n"
+				<< "<frame scrolling=no frameborder=0 src=\"_gwcgi_?_optsite_e=_compressedoptions_&a=p&p=nav\">\n"
+			#ifndef DOCHANDLE
+				<< "<frame name=\"documenttop\" frameborder=0 src=\"_gwcgi_?_optsite_e=_compressedoptions_&a=d&d=" 
+				<< args["d"] << "\">"
+			#else
+				<< "<frame name=\"documenttop\" frameborder=0 src=\"_httpdocumenthandle_(" 
+				<< args["c"] << "," << args["d"] << ")\">"
+			#endif
+				<< "<noframes>\n"
+				<< "<p>You must have a frame enabled browser to view this.</p>\n"
+				<< "</noframes>\n"
+				<< "</frameset>\n"
+				<< "</html>\n";
+      		} 
+		else 
+		{
+			output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout);	
+      		}
+      		
+		return true;
+    	}
+
+
+	//the header bit and the navigation button   
+    	textout << outconvert << disp << "_document:header_\n" << "_document:content_\n";
+       
+	//display the book
+	textout << outconvert << disp << "_document:flashbook_\n";
+	
+	//the footer bit without the document arrow
+	textout << outconvert << disp << "</div> <!-- id=document -->\n";
+	textout << outconvert << disp << "_endspacer__htmlfooter_\n";
+  }
+  
+  return true;
+}
+
+//Displaying the xml when bookswitch=flashxml
+bool documentaction::do_action_flashxml(cgiargsclass &args, recptprotolistclass *protos, 
+                               browsermapclass *browsers, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout)
+{
+  // must have a valid collection server
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL) 
+  {
+    logout << "documentaction::do_action called with NULL collectproto\n";
+    textout << outconvert << disp 
+	    << "<Message>\n"
+	    << "  Error: Attempt to get document without setting collection\n"
+	    << "</Message>\n";
+  } 
+  else 
+  {  
+    text_t OID = args["d"];
+    if (OID.empty()) OID = args["cl"];
+    if (OID.empty()) 
+    {
+      textout << outconvert << disp 
+	      << "<Message>\n"
+	      << "  Document contains no data\n"
+	      << "</Message>\n";
+      return true;
+    }
+
+    if (formatinfo.DocumentUseHTML && !args["d"].empty())
+    {  
+      if (args["f"] == "1") 
+      {
+	textout << outconvert << disp 
+		<< "<Message>\n"
+		<< "  Using frames makes no sense for XML output\n"
+		<< "</Message>\n";
+      } 
+      else 
+      {
+	output_document_flashxml (OID, args, collectproto, browsers, disp, outconvert, textout, logout);	
+      }
+
+      return true;
+    }
+
+    // output the document text
+    if (!args["d"].empty()) 
+    {
+      output_document_flashxml (OID, args, collectproto, browsers, disp, outconvert, textout, logout);
+    }
+  }
+
+  return true;
+}
+
+
+
+void documentaction::output_text (ResultDocInfo_t &docinfo, format_t *formatlistptr,
+				  const TermInfo_tarray &terminfo, const text_t &OID,
+				  bool highlight, int hastxt, int wanttext, 
+				  text_t &collection, recptproto *collectproto,
+				  browsermapclass *browsers, displayclass &disp,
+				  outconvertclass &outconvert, ostream &textout,
+				  ostream &logout, cgiargsclass &args) {
+  
+  DocumentRequest_t docrequest;
+  DocumentResponse_t docresponse;
+  comerror_t err;
+  if (hastxt == 1) {
+
+    if (wanttext) {
+      // get the text
+      docrequest.OID = OID;
+      collectproto->get_document (collection, docrequest, docresponse, err, logout);
+
+      // cut down on overhead by not using formattools if we only want the text
+      // (wanttext will equal 2 if we want text and other stuff too)
+      if (wanttext == 1)
+	if (highlight) {
+	  highlighttext(docresponse.doc, args, terminfo, disp, outconvert, textout);
+	} else {
+	  textout << outconvert << disp << docresponse.doc;
+	}
+    }
+  
+    if (wanttext != 1) {
+      text_tmap options;
+      options["text"] = docresponse.doc;
+
+      if (formatinfo.AllowExtendedOptions) {
+        browsetoolsclass b;
+	b.load_extended_options(options, args, browsers, formatinfo, 
+                                collectproto, disp, outconvert, logout);
+      }
+      
+      text_t doctext 
+	 = get_formatted_string (collection, collectproto, docinfo, disp,
+				 formatlistptr, options, logout); 
+      
+      if (highlight) {
+	highlighttext(doctext, args, terminfo, disp, outconvert, textout);
+      } else {
+	textout << outconvert << disp << doctext;
+      }
+    }
+  }
+}
+
+
+void documentaction::output_document (const text_t &OID, cgiargsclass &args, 
+				      recptproto *collectproto, browsermapclass *browsers,
+				      displayclass &disp, outconvertclass &outconvert,
+				      ostream &textout, ostream &logout) {
+  FilterResponse_t inforesponse;
+  FilterResponse_t queryresponse;
+  text_tset metadata;
+  bool getParents = false;
+  bool highlight = false;
+  int wanttext = 0;
+  int arg_gt = args.getintarg("gt");
+  text_t &collection = args["c"];
+    
+  // 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")) {
+
+    ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
+    bool segment = false;
+    if (cinfo != NULL) {
+      segment = cinfo->isSegmented;
+    }
+    FilterRequest_t request;
+    comerror_t err;
+    request.filterResultOptions = FRmatchTerms;
+    text_t formattedstring = args["q"];
+    format_querystring (formattedstring, args.getintarg("b"), segment);
+    set_queryfilter_options (request, formattedstring, args);
+    args["q"] = formattedstring; // need to set this here for phrase 
+                   // highlighting, where we look at the querystring
+    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;
+  }
+    
+  textout << outconvert << "<div class=\"documenttext\">\n";
+
+  if (get_info (OID, collection, args["l"], 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, 
+		   browsers, disp, outconvert, textout, logout, args);
+
+    } 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 "is_classify = true" 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, args["l"], 
+		    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 << "<div class=\"warning\">_document:textltwarning_</div>\n";
+
+	  output_text (thisdocinfo, formatlistptr, queryresponse.termInfo, 
+		       OID, highlight, hastxt, wanttext, collection, 
+		       collectproto, browsers, disp, outconvert, textout, logout, args);
+	  
+	}
+	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=" << (*sechere).OID << "></a>\n";
+
+	  int shastxt = (*sechere).metadata["hastxt"].values[0].getint();
+
+	  output_text (*sechere, formatlistptr, queryresponse.termInfo, 
+		       (*sechere).OID, highlight, shastxt, wanttext, collection, 
+		       collectproto, browsers, disp, outconvert, textout, logout, args);
+	  ++count;
+	  ++sechere;
+	}
+      }
+    }
+  }
+  textout << outconvert << "</div>\n";
+  delete formatlistptr;
+}
+
+void documentaction::output_document_flashxml(const text_t &OID, cgiargsclass &args, 
+				      recptproto *collectproto, browsermapclass *browsermap,
+				      displayclass &disp, outconvertclass &outconvert,
+				      ostream &textout, ostream &logout) {
+	//if this is not at the document level --> do Nothing!!
+	if (args["d"].empty())
+		return;
+		
+	//if we have a query string and highlighting is turned on we need
+  	//to redo the query to get the terms for highlighting
+	FilterResponse_t queryresponse;
+  	bool highlight = false;
+	if (!args["q"].empty() && args.getintarg("hl")) 
+	{
+		text_t &collection = args["c"];
+    		ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
+    		bool segment = false;
+    		if (cinfo != NULL) 
+		{
+      			segment = cinfo->isSegmented;
+    		}
+    		FilterRequest_t request;
+    		comerror_t err;
+    		request.filterResultOptions = FRmatchTerms;
+    		text_t formattedstring = args["q"];
+    		format_querystring (formattedstring, args.getintarg("b"), segment);
+    		set_queryfilter_options (request, formattedstring, args);
+    		args["q"] = formattedstring; 	//need to set this here for phrase 
+                   				//highlighting, where we look at the querystring
+    		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;
+    		}
+  	}
+	
+	FilterResponse_t response;
+	bool getParents = false;
+	text_tset metadata;
+	text_t classifytype, classification, formatstring;
+	
+	//metadata elements needed by recurse_contents
+  	metadata.insert ("Title");
+	metadata.insert ("haschildren");
+
+  	//protocol call
+  	if (!get_info (OID, args["c"], args["l"], metadata, getParents, collectproto, response, logout))
+    		return;
+
+  	recurse_contents (response.docInfo[0], args, metadata, 
+			    getParents, collectproto, disp, outconvert, textout, logout, highlight, queryresponse.termInfo);
+}
+
+void documentaction::recurse_contents(ResultDocInfo_t &section, cgiargsclass &args,
+                                        text_tset &metadata, bool &getParents, 
+                                        recptproto *collectproto, displayclass &disp, 
+                                        outconvertclass &outconvert, ostream &textout, ostream &logout, 
+					bool highlight, const TermInfo_tarray &terminfo)
+{
+ 	text_t formatstring;
+
+  	bool is_classify = false;
+  	
+  	//output this section 
+  	text_t classification = "Document";
+  	
+	textout << outconvert << disp << "<Section>\n";
+	
+	//get the title
+	textout << outconvert << disp << "<Description>\n<Metadata name=\"Title\">";
+	text_t t_title = section.metadata["Title"].values[0];
+	//if it is highlighted
+	/*if (highlight == true)
+	{
+		highlighttext(t_title, args, terminfo, disp, outconvert, textout);
+	}
+	else
+	{*/
+		textout << outconvert << disp << t_title;
+	//}
+	textout << outconvert << disp << "</Metadata>\n</Description>\n";
+	
+	//get the text
+	DocumentRequest_t docrequest;
+	DocumentResponse_t docresponse;
+	comerror_t err;
+	docrequest.OID = section.OID;
+	text_t &collection = args["c"];
+	collectproto->get_document(collection, docrequest, docresponse, err, logout);
+	//if it is highlighted
+	if (highlight == true)
+	{
+		highlighttext(docresponse.doc, args, terminfo, disp, outconvert, textout);
+	}
+	else
+	{
+		textout << outconvert << disp << docresponse.doc;
+	}
+	textout << outconvert << disp << "\n";
+	
+  	int haschildren = section.metadata["haschildren"].values[0].getint();
+  	
+  	// recurse through children
+  	if (haschildren == 1)
+	{
+    		FilterResponse_t tmp;
+    		get_children (section.OID, args["c"], args["l"], metadata, getParents, collectproto, tmp, logout);
+    		ResultDocInfo_tarray::iterator thisdoc = tmp.docInfo.begin();
+    		ResultDocInfo_tarray::iterator lastdoc = tmp.docInfo.end();
+
+    		while (thisdoc != lastdoc) 
+		{
+      			recurse_contents (*thisdoc, args, metadata, getParents, collectproto, disp, outconvert, textout, logout, highlight, terminfo);
+      			++thisdoc;
+    		}
+  	}
+	
+	textout << outconvert << disp << "</Section>\n";
+}
+
+void documentaction::set_arrow_macros (cgiargsclass &args, recptproto *collectproto,
+				       displayclass &disp, ostream &logout) {
+
+  text_tset metadata;
+  FilterResponse_t response;
+  FilterResponse_t presponse;
+
+  int haschildren = 0;
+  text_tarray next_siblings;
+  text_t previous_sibling;
+  text_t &arg_d = args["d"];
+
+  // get info on current section
+  metadata.insert("haschildren");
+  if (!get_info(arg_d, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
+    logout << "error 1 in documentaction::set_arrow_macros\n";
+    return;
+  }
+  haschildren = response.docInfo[0].metadata["haschildren"].values[0].getint();
+      
+  // get OIDs of next and previous siblings of current section and
+  // all it's parents
+  int parentcount = countchar(arg_d.begin(), arg_d.end(), '.');
+  text_t thisoid = arg_d;
+  while (parentcount > 0) {
+    get_children (get_parent(thisoid), args["c"], args["l"], metadata, false, 
+		  collectproto, response, logout);
+    ResultDocInfo_tarray::iterator this_sibling = response.docInfo.begin();
+    ResultDocInfo_tarray::iterator last_sibling = response.docInfo.end();
+    bool first = true;
+    while (this_sibling != last_sibling) {
+      if ((*this_sibling).OID == thisoid) {
+	if (!first && next_siblings.empty()) {
+	  previous_sibling = (*(this_sibling-1)).OID;
+	  int section_has_children = (*(this_sibling-1)).metadata["haschildren"].values[0].getint();
+	  // if previous sibling has children we need to recurse
+	  // down to the last descendant
+	  while (section_has_children) {
+	    get_children (previous_sibling, args["c"], args["l"], metadata, false, 
+			  collectproto, presponse, logout);
+	    if (!presponse.docInfo.empty()) {
+	      ResultDocInfo_tarray::iterator it = presponse.docInfo.end() - 1;
+	      previous_sibling = (*it).OID;
+	      section_has_children = (*it).metadata["haschildren"].values[0].getint();
+	    } else {
+	      section_has_children = 0; // this should never happen
+	    }
+	  }
+	}
+	    
+	if ((this_sibling+1) != last_sibling) {
+	  next_siblings.push_back((*(this_sibling+1)).OID);
+	} else {
+	  next_siblings.push_back("");
+	}
+	break;
+      }
+      ++this_sibling;
+      first = false;
+    }
+    thisoid = get_parent(thisoid);
+    --parentcount;
+  }
+
+  // work out values for next link
+  if (haschildren) {
+#ifndef DOCHANLE
+    disp.setmacro ("httpnextarrow", "document", "_httpdocument_&amp;cl=" + args["cl"] +
+		   "&amp;d=" + arg_d + ".fc");
+#else
+    disp.setmacro ("httpnextarrow", "document", "_httpdocumenthandle_("+args["c"]+","+arg_d + ".fc)";
+
+#endif
+
+  } else {
+    text_tarray::const_iterator h = next_siblings.begin();
+    text_tarray::const_iterator e = next_siblings.end();
+    while (h != e) {
+      if (!(*h).empty()) {
+#ifndef DOCHANLE
+	disp.setmacro ("httpnextarrow", "document", "_httpdocument_&amp;cl=" + args["cl"] +
+		       "&amp;d=" + *h);
+#else
+	disp.setmacro ("httpnextarrow", "document", "_httpdocumenthandle_("+args["c"]+","+*h+")";
+
+#endif
+
+	break;
+      }
+      ++h;
+    }
+  }
+
+  // work out value for previous link
+  if (!previous_sibling.empty()) {
+#ifndef DOCHANDLE
+    disp.setmacro ("httpprevarrow", "document", "_httpdocument_&amp;cl=" + args["cl"] +
+		   "&amp;d=" + previous_sibling);
+#else
+    disp.setmacro ("httpprevarrow", "document", "_httpdocumenthandle_("+args["c"]+","+ previous_sibling+")");
+
+#endif
+
+  } else {
+    if (countchar(arg_d.begin(), arg_d.end(), '.')) {
+#ifndef DOCHANDLE
+      disp.setmacro ("httpprevarrow", "document", "_httpdocument_&amp;cl=" + args["cl"] +
+		     "&amp;d=" + get_parent(arg_d));
+#else
+      disp.setmacro ("httpprevarrow", "document", "_httpdocumenthandle_("+args["c"]+","+get_parent(arg_d)+")");
+
+#endif
+
+    }
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/documentaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/documentaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/documentaction.h	(revision 16561)
@@ -0,0 +1,129 @@
+/**********************************************************************
+ *
+ * documentaction.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.
+ *
+ *********************************************************************/
+
+
+#ifndef DOCUMENTACTION_H
+#define DOCUMENTACTION_H
+
+#include "action.h"
+#include "gsdlconf.h"
+#include "formattools.h"
+#include "receptionist.h"
+#include "browserclass.h"
+
+class documentaction : public action {
+
+protected:
+
+  receptionist *recpt;
+
+  formatinfo_t formatinfo;
+  typedef map<text_t, format_t*, lttext_t> format_tmap; 
+  
+  bool get_link (cgiargsclass &args, recptprotolistclass *protos,
+		 const text_t &inlink, text_t &outlink, ostream &logout);
+
+  void load_formatinfo (const text_tmap &colformat, int gt);
+
+  void set_spacemacro (displayclass &disp, FilterResponse_t &response,
+		       bool has_search_button);
+
+  void set_navbarmacros (displayclass &disp, FilterResponse_t &response, 
+			 bool has_search_button, cgiargsclass &args,
+			 ColInfoResponse_t &cinfo);
+
+  virtual void output_text (ResultDocInfo_t &docinfo, format_t *formatlistptr,
+			    const TermInfo_tarray &terminfo, const text_t &OID,
+			    bool highlight, int hastxt, int wanttext, 
+			    text_t &collection, recptproto *collectproto,
+			    browsermapclass *browsers, displayclass &disp,
+			    outconvertclass &outconvert, ostream &textout,
+			    ostream &logout, cgiargsclass &args);
+
+  virtual void output_document (const text_t &OID, cgiargsclass &args, 
+				recptproto *collectproto, browsermapclass *browsers,
+				displayclass &disp,  outconvertclass &outconvert,
+				ostream &textout,  ostream &logout);
+
+
+  virtual void output_document_flashxml (const text_t &OID, cgiargsclass &args, 
+				    recptproto *collectproto, browsermapclass *browsers,
+				    displayclass &disp, outconvertclass &outconvert,
+				    ostream &textout, ostream &logout);
+
+  virtual void recurse_contents(ResultDocInfo_t &section, cgiargsclass &args,
+                                        text_tset &metadata, bool &getParents, 
+                                        recptproto *collectproto, displayclass &disp, 
+                                        outconvertclass &outconvert, ostream &textout, ostream &logout, 
+					bool highlight, const TermInfo_tarray &terminfo);
+				    
+  void set_arrow_macros (cgiargsclass &args, recptproto *collectproto,
+			 displayclass &disp, ostream &logout);
+
+public:
+  documentaction ();
+  virtual ~documentaction ();
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "d";}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+				       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  bool do_action_html(cgiargsclass &args, recptprotolistclass *protos, 
+		      browsermapclass *browsers, displayclass &disp, 
+		      outconvertclass &outconvert, ostream &textout, 
+		      ostream &logout);
+
+  bool do_action_flashxml(cgiargsclass &args, recptprotolistclass *protos, 
+		     browsermapclass *browsers, displayclass &disp, 
+		     outconvertclass &outconvert, ostream &textout, 
+		     ostream &logout);
+
+  bool do_action_book(cgiargsclass &args, recptprotolistclass *protos, 
+                               browsermapclass *browsers, displayclass &disp, 
+                               outconvertclass &outconvert, ostream &textout, 
+                               ostream &logout);
+};
+
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.cpp	(revision 16561)
@@ -0,0 +1,618 @@
+/**********************************************************************
+ *
+ * dynamicclassifieraction.cpp -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "dynamicclassifieraction.h"
+#include "recptprototools.h"
+
+
+dynamicclassifieraction::dynamicclassifieraction ()
+{
+  recpt = NULL;
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "dcl";
+  arg_ainfo.longname = "dynamic classifier ID";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "dcn";
+  arg_ainfo.longname = "dynamic classifier node";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+
+dynamicclassifieraction::~dynamicclassifieraction()
+{
+}
+
+
+bool dynamicclassifieraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+					     recptprotolistclass *protos, ostream &logout)
+{
+  return true;
+}
+
+
+void dynamicclassifieraction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+						response_t &response,text_t &response_data, 
+						ostream &logout)
+{
+  response = content;
+  response_data = "text/html";
+}
+
+
+// define all the macros which might be used by other actions to produce pages. 
+void dynamicclassifieraction::define_external_macros (displayclass &disp, cgiargsclass &args, 
+						      recptprotolistclass *protos, ostream &logout)
+{
+  // A valid collection server is vital
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL)
+  {
+    logout << "dynamicclassifieraction::define_external_macros called with NULL collectproto\n";
+    return;
+  }
+
+  // Define _dynamicclassifiernavbarentries_ to add buttons to the navigation bar for the dynamic classifiers
+  text_t navigation_bar_entries = "";
+  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
+  text_tmap::iterator dynamic_classifier_iterator = cinfo->dynamic_classifiers.begin();
+  while (dynamic_classifier_iterator != cinfo->dynamic_classifiers.end())
+  {
+    text_t dynamic_classifier_id = (*dynamic_classifier_iterator).first;
+    navigation_bar_entries += "_navbarspacer_";
+    navigation_bar_entries += "_navtab_(_gwcgi_?c=" + args["c"] + "&amp;a=dc&amp;dcl=" + dynamic_classifier_id + "," + dynamic_classifier_id;
+    if (args["a"] == "dc" && args["dcl"] == dynamic_classifier_id)
+    {
+      navigation_bar_entries += ",selected";
+    }
+    navigation_bar_entries += ")";
+    dynamic_classifier_iterator++;
+  }
+
+  disp.setmacro("dynamicclassifiernavbarentries", displayclass::defaultpackage, navigation_bar_entries);
+}
+
+
+// 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 dynamicclassifieraction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+						      recptprotolistclass *protos, ostream &logout)
+{
+  // define_internal_macros sets the following macros:
+}
+
+
+bool dynamicclassifieraction::do_action(cgiargsclass &args, recptprotolistclass *protos, 
+					browsermapclass *browsers, displayclass &disp, 
+					outconvertclass &outconvert, ostream &textout, 
+					ostream &logout)
+{
+  // A valid collection server is vital
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL)
+  {
+    logout << "dynamicclassifieraction::do_action called with NULL collectproto\n";
+    return false;
+  }
+
+  textout << outconvert << disp << "_dynamicclassifier:header_\n";
+  textout << outconvert << disp << "_dynamicclassifier:content_\n";
+
+  // Check a dynamic classifier ID has been specified
+  text_t arg_dcl = args["dcl"];
+  if (arg_dcl.empty())
+  {
+    textout << outconvert << disp << "Error: Missing dcl argument.\n";
+    textout << outconvert << disp << "_dynamicclassifier:footer_\n";
+    return true;
+  }
+
+  // Check the dynamic classifier ID is valid (ie. there is an entry in the collect.cfg file for it)
+  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
+  if (cinfo->dynamic_classifiers.find(arg_dcl) == cinfo->dynamic_classifiers.end())
+  {
+    textout << outconvert << disp << "Error: Invalid dcl value \"" << arg_dcl << "\".\n";
+    textout << outconvert << disp << "_dynamicclassifier:footer_\n";
+    return true;
+  }
+
+  // Parse the classifier options from the specification
+  text_t classifier_specification = cinfo->dynamic_classifiers[arg_dcl];
+  text_tmap classifier_options = parse_classifier_options (classifier_specification, args);
+
+  // Output the "<ID>Header" format statement if there is one
+  text_t classifier_header_format_statement = "";
+  get_formatstring (arg_dcl + "Header", cinfo->format, classifier_header_format_statement);
+  textout << outconvert << disp << classifier_header_format_statement << "\n";
+
+  // Resolve any ".pr" bits at the end of the "dcn" argument
+  text_t classifier_node_separator = classifier_options["-split_using_hierarchy_separator"];
+  if (ends_with (args["dcn"], ".pr"))
+  {
+    // Change the "dcn" argument to be the OID of the parent of the specified classifier node
+    text_tlist args_dcn_parts;
+    splitword (args["dcn"].begin(), args["dcn"].end(), classifier_node_separator, args_dcn_parts);
+    args_dcn_parts.pop_back();  // Remove the last part
+    joinchar (args_dcn_parts, classifier_node_separator, args["dcn"]);
+  }
+
+  // Prepare to output the dynamic classifier
+  text_t current_classifier_node_OID = "";
+  text_t current_metadata_value_filter = "";
+  int classifier_node_indent = 0;
+
+  // (Optional) Output an hlist to group the classifier nodes into buckets based on the first character
+  if (classifier_options["-group_by_first_character"] == "1")
+  {
+    text_t selected_grouping_node_OID = output_hlist_classifier_nodes (current_classifier_node_OID, current_metadata_value_filter, "substr(value,1,1)", classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
+
+    current_classifier_node_OID = selected_grouping_node_OID + ".";
+    current_metadata_value_filter = selected_grouping_node_OID + "*";
+  }
+
+  // (Optional) Output an hlist instead of a vlist at the top level
+  if (classifier_options["-use_hlist_at_top"] == "1")
+  {
+    text_t selected_hlist_node_OID = output_hlist_classifier_nodes (current_classifier_node_OID, current_metadata_value_filter, "", classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
+
+    current_classifier_node_OID = selected_hlist_node_OID + classifier_node_separator;
+  }
+
+  // args["dcn"] may have been modified by output_hlist_classifier_nodes() above
+  text_t classifier_node_OID = args["dcn"];
+
+  // If grouping is enabled remove the grouping node bit from the start of the OID
+  text_t classifier_node_OID_sans_grouping = classifier_node_OID;
+  if (classifier_options["-group_by_first_character"] == "1")
+  {
+    text_t::iterator grouping_node_separator = findchar (classifier_node_OID.begin(), classifier_node_OID.end(), '.');
+    if (grouping_node_separator != classifier_node_OID.end())
+    {
+      classifier_node_OID_sans_grouping = substr (grouping_node_separator + 1, classifier_node_OID.end());
+    }
+    else
+    {
+      classifier_node_OID_sans_grouping = "";
+    }
+  }
+
+  // Split the classifier node OID into its hierarchical parts, then remove any we've already dealt with (HLists)
+  text_tlist classifier_node_OID_parts_remaining;
+  splitword (classifier_node_OID_sans_grouping.begin(), classifier_node_OID_sans_grouping.end(), classifier_node_separator, classifier_node_OID_parts_remaining);
+  if (classifier_options["-use_hlist_at_top"] == "1")
+  {
+    classifier_node_OID_parts_remaining.pop_front();
+  }
+
+  text_t classifier_node_metadata_value = classifier_node_OID_sans_grouping;
+  text_t classifier_node_metadata_value_filter = classifier_node_metadata_value + classifier_node_separator + "*";
+
+  // If "-split_greenstone_dates" is on the metadata value and filter doesn't quite match the OID -- fix this
+  if (classifier_options["-split_greenstone_dates"] == "1")
+  {
+    classifier_node_metadata_value.replace (classifier_node_separator, "");
+    classifier_node_metadata_value_filter = classifier_node_metadata_value;
+    if (classifier_node_metadata_value_filter.size() == 8)
+    {
+      classifier_node_metadata_value_filter += classifier_node_separator;
+    }
+    classifier_node_metadata_value_filter += "*";
+  }
+
+  // Simple case at the top level: just output the child classifier nodes
+  if (classifier_node_metadata_value == "")
+  {
+    output_child_classifier_nodes (current_classifier_node_OID, "", current_metadata_value_filter, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
+  }
+
+  // More complex case below the top level
+  else
+  {
+    // Output the parent classifier nodes and the current classifier node
+    output_upper_classifier_nodes (current_classifier_node_OID, classifier_node_OID_parts_remaining, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
+
+    // Output the child classifier nodes
+    current_classifier_node_OID = classifier_node_OID + classifier_node_separator;
+    output_child_classifier_nodes (current_classifier_node_OID, classifier_node_metadata_value, classifier_node_metadata_value_filter, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
+
+    // Get the document nodes at this level
+    text_t metadata_elements = classifier_options["metadata_elements"];
+    text_t sort_documents_by = classifier_options["-sort_documents_by"];
+    FilterResponse_t documents_response;
+    get_documents_with_metadata_value (metadata_elements, classifier_node_metadata_value, sort_documents_by, args["c"], collectproto, documents_response, logout);
+
+    // Display the document nodes
+    display_document_nodes (documents_response, classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
+  }
+
+  // Output the "<ID>Footer" format statement if there is one
+  text_t classifier_footer_format_statement = "";
+  get_formatstring (arg_dcl + "Footer", cinfo->format, classifier_footer_format_statement);
+  textout << outconvert << disp << classifier_footer_format_statement << "\n";
+
+  textout << outconvert << disp << "_dynamicclassifier:footer_\n";
+  return true;
+}
+
+
+text_tmap dynamicclassifieraction::parse_classifier_options (text_t classifier_specification, cgiargsclass &args)
+{
+  text_tmap classifier_options;
+
+  // Default values
+  classifier_options["-split_using_hierarchy_separator"] = "|";
+
+  // Split the classifier specification string by spaces
+  text_tlist classifier_specification_parts;
+  splitchar (classifier_specification.begin(), classifier_specification.end(), ' ', classifier_specification_parts);
+
+  // The metadata element(s) to classify by should be the first value
+  classifier_options["metadata_elements"] = classifier_specification_parts.front();
+  classifier_specification_parts.pop_front();
+
+  // Parse options from the remainder of the classifier specification
+  while (!classifier_specification_parts.empty())
+  {
+    // Parse the option name
+    text_t classifier_option_name = classifier_specification_parts.front();
+    classifier_specification_parts.pop_front();
+
+    // Check if the option has a value (it may just be a flag, in which case we use "1" as the value)
+    text_t classifier_option_value = "1";
+    if (!classifier_specification_parts.empty() && !starts_with(classifier_specification_parts.front(), "-"))
+    {
+      classifier_option_value = classifier_specification_parts.front();
+      classifier_specification_parts.pop_front();
+    }
+
+    // Record the option
+    classifier_options[classifier_option_name] = classifier_option_value;
+  }
+
+  return classifier_options;
+}
+
+
+text_t dynamicclassifieraction::output_hlist_classifier_nodes (text_t parent_classifier_node_OID,
+							       text_t metadata_value_filter,
+							       text_t metadata_value_grouping_expression,
+							       text_tmap classifier_options, cgiargsclass &args,
+							       recptproto *collectproto, browsermapclass *browsers,
+							       displayclass &disp, outconvertclass &outconvert,
+							       ostream &textout, ostream &logout)
+{
+  // Get all the metadata values for the specified element(s) that match the filter
+  text_t metadata_elements = classifier_options["metadata_elements"];
+  FilterResponse_t metadata_values_response;
+  bool request_success = get_metadata_values (metadata_elements, metadata_value_filter, metadata_value_grouping_expression, args["c"], collectproto, metadata_values_response, logout);
+
+  // If the request failed then it's probably because the collection isn't using an SQL infodbtype
+  if (request_success == false)
+  {
+    textout << outconvert << disp << "Error: Dynamic classifier functionality is not available. Please check you are using an SQL infodbtype and the collection has been rebuilt.\n";
+    return "";
+  }
+
+  // Check some metadata values were returned
+  if (metadata_values_response.docInfo.empty())
+  {
+    return "";
+  }
+
+  // After processing any hierarchical metadata values we're left with the hlist classifer nodes
+  map<text_t, int, lttext_t> hlist_classifier_nodes;
+  ResultDocInfo_tarray::iterator metadata_value_iterator = metadata_values_response.docInfo.begin();
+  while (metadata_value_iterator != metadata_values_response.docInfo.end())
+  {
+    text_t hierarchical_metadata_value = split_metadata_value ((*metadata_value_iterator).OID, classifier_options);
+
+    // Assume for now we're always at the top
+    text_t hlist_metadata_value = hierarchical_metadata_value;
+
+    // Determine the label for the hlist classifier node from the metadata value
+    text_tlist hlist_metadata_value_parts;
+    text_t hlist_node_separator = classifier_options["-split_using_hierarchy_separator"];
+    splitword (hlist_metadata_value.begin(), hlist_metadata_value.end(), hlist_node_separator, hlist_metadata_value_parts);
+    text_t hlist_classifier_node_label = hlist_metadata_value_parts.front();
+
+    // Create a node for this value if we haven't seen it before
+    if (hlist_classifier_nodes.find(hlist_classifier_node_label) == hlist_classifier_nodes.end())
+    {
+      hlist_classifier_nodes[hlist_classifier_node_label] = 0;
+    }
+
+    // Increment the occurrence count
+    hlist_classifier_nodes[hlist_classifier_node_label] += (*metadata_value_iterator).result_num;
+
+    metadata_value_iterator++;
+  }
+
+  // Add the necessary metadata to the hlist classifier nodes
+  text_t selected_hlist_node_OID = "";
+  FilterResponse_t hlist_classifier_nodes_response;
+  map<text_t, int, lttext_t>::iterator hlist_classifier_nodes_iterator = hlist_classifier_nodes.begin();
+  while (hlist_classifier_nodes_iterator != hlist_classifier_nodes.end())
+  {
+    text_t hlist_classifier_node_OID = parent_classifier_node_OID + (*hlist_classifier_nodes_iterator).first;
+
+    // Is this the hlist node that is currently selected?
+    if (starts_with (args["dcn"], hlist_classifier_node_OID))
+    {
+      selected_hlist_node_OID = hlist_classifier_node_OID;
+    }
+
+    // Add the necessary metadata required to display the hlist nodes correctly
+    ResultDocInfo_t hlist_classifier_node;
+    hlist_classifier_node.OID = hlist_classifier_node_OID;
+    hlist_classifier_node.metadata["doctype"].values.push_back ("classify");
+    hlist_classifier_node.metadata["haschildren"].values.push_back ("1");
+    hlist_classifier_node.metadata["numleafdocs"].values.push_back ("?");  // We can't determine this without more database requests
+    hlist_classifier_node.metadata["Title"].values.push_back ((*hlist_classifier_nodes_iterator).first);
+    hlist_classifier_nodes_response.docInfo.push_back (hlist_classifier_node);
+
+    hlist_classifier_nodes_iterator++;
+  }
+
+  // Automatically select the first hlist node if necessary
+  if (selected_hlist_node_OID == "")
+  {
+    selected_hlist_node_OID = (*hlist_classifier_nodes_response.docInfo.begin()).OID;
+
+    // Don't really like messing with this here, but it needs to be done before display_classifier_nodes() below
+    if (starts_with (parent_classifier_node_OID, args["dcn"]))
+    {
+      args["dcn"] = selected_hlist_node_OID;
+    }
+  }
+
+  // Display the hlist nodes
+  display_classifier_nodes (hlist_classifier_nodes_response, "HList", 0, args, collectproto, browsers, disp, outconvert, textout, logout);
+
+  return selected_hlist_node_OID;
+}
+
+
+void dynamicclassifieraction::output_upper_classifier_nodes (text_t root_classifier_node_OID,
+							     text_tlist upper_classifier_node_labels,
+							     int& classifier_node_indent,
+							     text_tmap classifier_options, cgiargsclass &args,
+							     recptproto *collectproto, browsermapclass *browsers,
+							     displayclass &disp, outconvertclass &outconvert,
+							     ostream &textout, ostream &logout)
+{
+  // Display the upper classifier nodes
+  text_t upper_classifier_node_OID = root_classifier_node_OID;
+  text_tlist::iterator upper_classifier_node_labels_iterator = upper_classifier_node_labels.begin();
+  while (upper_classifier_node_labels_iterator != upper_classifier_node_labels.end())
+  {
+    upper_classifier_node_OID += *upper_classifier_node_labels_iterator;
+
+    ResultDocInfo_t upper_classifier_node;
+    upper_classifier_node.OID = upper_classifier_node_OID;
+    upper_classifier_node.metadata["doctype"].values.push_back ("classify");
+    upper_classifier_node.metadata["haschildren"].values.push_back ("1");
+    upper_classifier_node.metadata["numleafdocs"].values.push_back ("?");  // We can't determine this without more database requests
+    upper_classifier_node.metadata["Title"].values.push_back (*upper_classifier_node_labels_iterator);
+
+    FilterResponse_t upper_classifier_node_response;
+    upper_classifier_node_response.docInfo.push_back(upper_classifier_node);
+    display_classifier_nodes (upper_classifier_node_response, "VList", classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
+    classifier_node_indent++;
+
+    upper_classifier_node_OID += classifier_options["-split_using_hierarchy_separator"];
+    upper_classifier_node_labels_iterator++;
+  }
+}
+
+
+void dynamicclassifieraction::output_child_classifier_nodes (text_t classifier_node_OID,
+							     text_t classifier_node_metadata_value,
+							     text_t metadata_value_filter,
+							     int& classifier_node_indent,
+							     text_tmap classifier_options, cgiargsclass &args,
+							     recptproto *collectproto, browsermapclass *browsers,
+							     displayclass &disp, outconvertclass &outconvert,
+							     ostream &textout, ostream &logout)
+{
+  // Get all the metadata values for the specified element(s) that match the filter
+  text_t metadata_elements = classifier_options["metadata_elements"];
+  FilterResponse_t metadata_values_response;
+  bool request_success = get_metadata_values (metadata_elements, metadata_value_filter, "", args["c"], collectproto, metadata_values_response, logout);
+
+  // If the request failed then it's probably because the collection isn't using an SQL infodbtype
+  if (request_success == false)
+  {
+    textout << outconvert << disp << "Error: Dynamic classifier functionality is not available. Please check you are using an SQL infodbtype and the collection has been rebuilt.\n";
+    return;
+  }
+
+  // After processing any hierarchical metadata values we're left with the child classifer nodes
+  map<text_t, int, lttext_t> child_classifier_nodes;
+  ResultDocInfo_tarray::iterator metadata_value_iterator = metadata_values_response.docInfo.begin();
+  while (metadata_value_iterator != metadata_values_response.docInfo.end())
+  {
+    text_t hierarchical_metadata_value = split_metadata_value ((*metadata_value_iterator).OID, classifier_options);
+    text_t classifier_node_hierarchical_metadata_value = split_metadata_value (classifier_node_metadata_value, classifier_options);
+
+    // We need to remove the current position from the metadata value to leave the child metadata value
+    text_t child_metadata_value = hierarchical_metadata_value;
+    text_t child_node_separator = classifier_options["-split_using_hierarchy_separator"];
+    if (starts_with(hierarchical_metadata_value, classifier_node_hierarchical_metadata_value + child_node_separator))
+    {
+      child_metadata_value = substr(hierarchical_metadata_value.begin() + (classifier_node_hierarchical_metadata_value + child_node_separator).size(), hierarchical_metadata_value.end());
+    }
+
+    // Determine the label for the child classifier node from the metadata value
+    text_tlist child_metadata_value_parts;
+    splitword (child_metadata_value.begin(), child_metadata_value.end(), child_node_separator, child_metadata_value_parts);
+    text_t child_classifier_node_label = child_metadata_value_parts.front();
+
+    // Create a node for this value if we haven't seen it before
+    if (child_classifier_nodes.find(child_classifier_node_label) == child_classifier_nodes.end())
+    {
+      child_classifier_nodes[child_classifier_node_label] = 0;
+    }
+
+    // Increment the occurrence count
+    child_classifier_nodes[child_classifier_node_label] += (*metadata_value_iterator).result_num;
+
+    metadata_value_iterator++;
+  }
+
+  // Add the necessary metadata to the child classifier nodes
+  FilterResponse_t child_classifier_nodes_response;
+  map<text_t, int, lttext_t>::iterator child_classifier_nodes_iterator = child_classifier_nodes.begin();
+  while (child_classifier_nodes_iterator != child_classifier_nodes.end())
+  {
+    text_t child_classifier_node_OID = classifier_node_OID + (*child_classifier_nodes_iterator).first;
+
+    ResultDocInfo_t child_classifier_node;
+    child_classifier_node.OID = child_classifier_node_OID;
+    child_classifier_node.metadata["doctype"].values.push_back ("classify");
+    child_classifier_node.metadata["haschildren"].values.push_back ("1");
+    child_classifier_node.metadata["numleafdocs"].values.push_back ((*child_classifier_nodes_iterator).second);
+    child_classifier_node.metadata["Title"].values.push_back ((*child_classifier_nodes_iterator).first);
+    child_classifier_nodes_response.docInfo.push_back (child_classifier_node);
+
+    child_classifier_nodes_iterator++;
+  }
+
+  // Display the child classifier nodes
+  display_classifier_nodes (child_classifier_nodes_response, "VList", classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
+}
+
+
+text_t dynamicclassifieraction::split_metadata_value (text_t metadata_value, text_tmap classifier_options)
+{
+  text_t hierarchical_metadata_value = metadata_value;
+  text_t hierarchy_separator = classifier_options["-split_using_hierarchy_separator"];
+
+  // Add hierarchy separators into Greenstone date values (e.g. YYYYMMDD -> YYYY|MM|DD)
+  if (classifier_options["-split_greenstone_dates"] == "1")
+  {
+    if (metadata_value.size() == 4 || metadata_value.size() == 6 || metadata_value.size() == 8)
+    {
+      // Add year
+      hierarchical_metadata_value = substr (metadata_value.begin(), metadata_value.begin() + 4);
+      if (metadata_value.size() == 6 || metadata_value.size() == 8)
+      {
+	// Add month
+	hierarchical_metadata_value += hierarchy_separator;
+	hierarchical_metadata_value += substr (metadata_value.begin() + 4, metadata_value.begin() + 6);
+	if (metadata_value.size() == 8)
+	{
+	  // Add day
+	  hierarchical_metadata_value += hierarchy_separator;
+	  hierarchical_metadata_value += substr (metadata_value.begin() + 6, metadata_value.begin() + 8);
+	}
+      }
+    }
+  }
+
+  return hierarchical_metadata_value;
+}
+
+
+void dynamicclassifieraction::display_classifier_nodes (FilterResponse_t classifier_nodes_response,
+						        text_t classifier_nodes_type,
+							int classifier_nodes_indent,
+							cgiargsclass &args, recptproto *collectproto,
+							browsermapclass *browsers, displayclass &disp,
+							outconvertclass &outconvert, ostream &textout,
+							ostream &logout)
+{
+  // Check there are some classifier nodes to display
+  if (classifier_nodes_response.docInfo.empty()) return;
+
+  // Get the format statement for this classifier if there is one, or use the browser's default otherwise
+  text_t formatstring;
+  browserclass *bptr = browsers->getbrowser (classifier_nodes_type);
+  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
+  if (!get_formatstring (args["dcl"], classifier_nodes_type, cinfo->format, formatstring))
+  {
+    formatstring = bptr->get_default_formatstring();
+  }
+  format_t *formatlistptr = new format_t();
+  text_tset metadata;
+  bool getParents = false;
+  parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+  bool use_table = is_table_content (formatlistptr);
+
+  // Display the classifier nodes
+  bptr->output_section_group (classifier_nodes_response, args, args["c"], classifier_nodes_indent, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
+}
+
+
+void dynamicclassifieraction::display_document_nodes (FilterResponse_t documents_response,
+						      int document_nodes_indent,
+						      cgiargsclass &args, recptproto *collectproto,
+						      browsermapclass *browsers, displayclass &disp,
+						      outconvertclass &outconvert, ostream &textout,
+						      ostream &logout)
+{
+  // Check there are some documents to display
+  if (documents_response.docInfo.empty()) return;
+
+  // Get the format statement for the document nodes if there is one, or use the browser's default otherwise
+  text_t formatstring;
+  browserclass *bptr = browsers->getbrowser ("VList");
+  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
+  if (!get_formatstring (args["dcl"], "DocumentNodes", cinfo->format, formatstring))
+  {
+    if (!get_formatstring (args["dcl"], "VList", cinfo->format, formatstring))
+    {
+      formatstring = bptr->get_default_formatstring();
+    }
+  }
+  format_t *formatlistptr = new format_t();
+  text_tset metadata;
+  bool getParents = false;
+  parse_formatstring (formatstring, formatlistptr, metadata, getParents);
+  bool use_table = is_table_content (formatlistptr);
+
+  // Request the necessary metadata for displaying the documents
+  text_tarray document_OIDs;
+  ResultDocInfo_tarray::iterator document_iterator = documents_response.docInfo.begin();
+  while (document_iterator != documents_response.docInfo.end())
+  {
+    document_OIDs.push_back ((*document_iterator).OID);
+    document_iterator++;
+  }
+  FilterResponse_t document_nodes_response;
+  get_info (document_OIDs, args["c"], args["l"], metadata, getParents, collectproto, document_nodes_response, logout);
+
+  // Display the document nodes
+  bptr->output_section_group (document_nodes_response, args, args["c"], document_nodes_indent, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/dynamicclassifieraction.h	(revision 16561)
@@ -0,0 +1,111 @@
+/**********************************************************************
+ *
+ * dynamicclassifieraction.h -- 
+ * Copyright (C) 2008  DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef DYNAMICCLASSIFIERACTION_H
+#define DYNAMICCLASSIFIERACTION_H
+
+
+#include "action.h"
+#include "receptionist.h"
+
+
+class dynamicclassifieraction : public action
+{
+protected:
+  receptionist *recpt;
+
+public:
+  dynamicclassifieraction ();
+  virtual ~dynamicclassifieraction ();
+
+  void set_receptionist (receptionist *recpt_arg) { recpt = recpt_arg; }
+
+  text_t get_action_name () { return "dc"; }
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+				       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  text_tmap parse_classifier_options (text_t classifier_specification, cgiargsclass &args);
+
+  text_t output_hlist_classifier_nodes (text_t parent_classifier_node_OID,
+					text_t metadata_value_filter,
+					text_t metadata_value_grouping_expression,
+					text_tmap classifier_options, cgiargsclass &args,
+					recptproto *collectproto, browsermapclass *browsers,
+					displayclass &disp, outconvertclass &outconvert,
+					ostream &textout, ostream &logout);
+
+  void output_upper_classifier_nodes (text_t root_classifier_node_OID,
+				      text_tlist parent_classifier_node_labels,
+				      int& classifier_node_indent,
+				      text_tmap classifier_options, cgiargsclass &args,
+				      recptproto *collectproto, browsermapclass *browsers,
+				      displayclass &disp, outconvertclass &outconvert,
+				      ostream &textout, ostream &logout);
+
+  void output_child_classifier_nodes (text_t classifier_node_OID,
+				      text_t classifier_node_metadata_value,
+				      text_t metadata_value_filter,
+				      int& classifier_node_indent,
+				      text_tmap classifier_options, cgiargsclass &args,
+				      recptproto *collectproto, browsermapclass *browsers,
+				      displayclass &disp, outconvertclass &outconvert,
+				      ostream &textout, ostream &logout);
+
+  text_t split_metadata_value (text_t metadata_value, text_tmap classifier_options);
+
+  void display_classifier_nodes (FilterResponse_t classifier_nodes_response,
+				 text_t classifier_nodes_type,
+				 int classifier_nodes_indent,
+				 cgiargsclass &args, recptproto *collectproto,
+				 browsermapclass *browsers, displayclass &disp,
+				 outconvertclass &outconvert, ostream &textout,
+				 ostream &logout);
+
+  void display_document_nodes (FilterResponse_t documents_response,
+			       int document_nodes_indent,
+			       cgiargsclass &args, recptproto *collectproto,
+			       browsermapclass *browsers, displayclass &disp,
+			       outconvertclass &outconvert, ostream &textout,
+			       ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.cpp	(revision 16561)
@@ -0,0 +1,252 @@
+/**********************************************************************
+ *
+ * extlinkaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_EXTLINK_ACTION
+
+#include <string.h>
+#include "extlinkaction.h"
+#include "recptprototools.h"
+#include "cgiutils.h"
+
+extlinkaction::extlinkaction () {
+
+  // this action uses cgi variables "a", "d", and "href"
+
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "extlink";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "el";
+  arg_ainfo.longname = "external link preference";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "prompt";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "d";
+  arg_ainfo.longname = "document OID";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "href";
+  arg_ainfo.longname = "URL of external link";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "rl";
+  arg_ainfo.longname = "is relative link";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+extlinkaction::~extlinkaction () {
+}
+
+
+void extlinkaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+				      response_t &response, text_t &response_data, 
+				      ostream &logout) {
+
+  text_t link;
+  if (get_link (args, protos, link, logout)) {
+    response = location;
+    response_data = link;
+    return;
+  }
+
+  // external link
+  if (!link.empty()) {
+    if (args["el"] == "direct") {
+      response = location;
+      response_data = link;
+      return;
+    }
+  }
+
+  response = content;
+  response_data = "text/html";
+}
+
+void extlinkaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					    recptprotolistclass * /*protos*/, 
+					    ostream &/*logout*/) {
+  
+  // define_internal_macros sets the following macros:
+
+  // _nexturl_             link to external page
+  // _prevdoc_             link to previous document
+  //  disp.setmacro("nexturl", "extlink", cgi_safe(args["href"]));
+  // problem in whist, above line changed.  Perhaps decode_cgi_arg ??
+  // see also HTML plugin
+  disp.setmacro("nexturl", "extlink", args["href"]);
+  disp.setmacro("prevdoc", "extlink", args["d"]);
+}
+
+
+// if link is found returns true and url in link, otherwise returns
+// false
+bool extlinkaction::get_link (cgiargsclass &args, recptprotolistclass *protos,
+			      text_t &link, ostream &logout) {
+
+    text_t &arg_href = args["href"];
+    text_t &thiscollection = args["c"];
+    if (arg_href.empty()) return false;
+
+    text_t httpdoc = "_gwcgi_?e=_compressedoptions_&a=d";
+    if (args["rl"] == "1") {
+
+      FilterResponse_t response;
+      text_tset metadata;
+      metadata.insert ("section");
+	  
+      recptproto *collectproto = protos->getrecptproto (thiscollection, logout);
+
+      if (get_info (arg_href, thiscollection, args["l"], metadata, false, collectproto, response, logout)) {
+	if (!response.docInfo[0].metadata["section"].values[0].empty()) {
+	  link = httpdoc+"&d=" + response.docInfo[0].metadata["section"].values[0];
+	  return true;
+	}
+      }
+
+      // need to see if link exists in any other collection
+      // if cross-collection searching/browsing is turned on 
+      if (args["ccs"] == "1" && !args["cc"].empty()) {
+	text_tarray collections;
+	splitchar (args["cc"].begin(), args["cc"].end(), ',', collections);
+
+	text_tarray::const_iterator col_here = collections.begin();
+	text_tarray::const_iterator col_end = collections.end();
+
+	while (col_here != col_end) {
+	  
+	  // don't need to check current collection again
+	  if (*col_here == thiscollection) {++col_here; continue;}
+	  
+	  recptproto *collectproto = protos->getrecptproto (*col_here, logout);
+	  if (collectproto == NULL) {++col_here; continue;}
+
+	  if (get_info (arg_href, *col_here, args["l"], metadata, false, collectproto, response, logout)) {
+	    if (!response.docInfo[0].metadata["section"].values[0].empty()) {
+	      link = httpdoc+"&c=" + *col_here + "&d=" +
+		response.docInfo[0].metadata["section"].values[0];
+	      return true;
+	    }
+	  }
+	  ++col_here;
+	}
+      }
+      return false;
+      
+    } else {
+      // link is external 
+      link = arg_href;
+      return false;
+    }
+}
+
+
+bool extlinkaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			       browsermapclass * /*browsers*/, displayclass &disp,
+			       outconvertclass &outconvert, ostream &textout,
+			       ostream &logout) {
+ 
+  if (args["href"].empty()) {
+     // oops, this shouldn't happen
+    textout << outconvert << disp << ("_extlink:header_\n")
+	    << ("_extlink:notfoundcontent_\n")
+	    << ("_extlink:footer_\n");
+    return true;
+  }
+
+  if (args["rl"] == "1") {
+    // need to see if link exists in any other collection
+    // if cross-collection searching/browsing is turned on 
+    if (args["ccs"] == "1" && !args["cc"].empty()) {
+      
+      FilterResponse_t response;
+      text_tset metadata;
+      metadata.insert ("section");
+      text_tarray collections;
+      splitchar (args["cc"].begin(), args["cc"].end(), ',', collections);
+      
+      text_tarray::const_iterator col_here = collections.begin();
+      text_tarray::const_iterator col_end = collections.end();
+      
+      while (col_here != col_end) {
+	
+	// don't need to check current collection
+	if (*col_here == args["c"]) {++col_here; continue;}
+	
+	recptproto *collectproto = protos->getrecptproto (*col_here, logout);
+	if (get_info (args["href"], *col_here, args["l"], metadata, false, collectproto, response, logout)) {
+	  if (!response.docInfo[0].metadata["section"].values[0].empty()) {
+	    ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
+	    text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]); //
+	    if (collectionname.empty()) {
+	      collectionname = *col_here;
+	    }
+	    textout << outconvert << disp << ("_extlink:header_\n")
+		    << ("_extlink:foundintcontent_(" + *col_here + ", " + collectionname + 
+			", " + response.docInfo[0].metadata["section"].values[0] + "\n")
+		    << ("_extlink:footer_\n");
+	    return true;
+	  }
+	}
+	++col_here;
+      }
+    }
+    textout << outconvert << disp << ("_extlink:header_\n")
+	    << ("_extlink:notfoundcontent_\n")
+	    << ("_extlink:footer_\n");
+    
+    
+  } else {
+    // link is external 
+    textout << outconvert << disp << ("_extlink:header_\n")
+	    << ("_extlink:foundcontent_\n")
+	    << ("_extlink:footer_\n");
+  }
+
+  return true;
+}
+
+#endif //GSDL_USE_EXTLINK_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/extlinkaction.h	(revision 16561)
@@ -0,0 +1,69 @@
+/**********************************************************************
+ *
+ * extlinkaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_EXTLINK_ACTION
+
+#ifndef EXTLINKACTION_H
+#define EXTLINKACTION_H
+
+#include "action.h"
+#include "receptionist.h"
+
+class extlinkaction : public action {
+
+
+public:
+  extlinkaction ();
+  virtual ~extlinkaction ();
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "extlink";}
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, 
+			       ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp,
+		  outconvertclass &outconvert, ostream &textout,
+		  ostream &logout);
+
+
+protected:
+  
+  receptionist *recpt;
+  
+  bool get_link (cgiargsclass &args, recptprotolistclass *protos,
+		 text_t &link, ostream &logout);
+
+};
+
+#endif //!EXTLINKACTION_H
+#endif //GSDL_USE_EXTLINK_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/formattools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/formattools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/formattools.cpp	(revision 16561)
@@ -0,0 +1,1573 @@
+/**********************************************************************
+ *
+ * formattools.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.
+ *
+ *********************************************************************/
+
+#include "formattools.h"
+#include "cgiutils.h"
+#include "recptprototools.h"
+#include "OIDtools.h"
+#include "summarise.h"
+
+#include <assert.h>
+
+// a few function prototypes
+
+static text_t format_string (const text_t& collection, recptproto* collectproto,
+			     ResultDocInfo_t &docinfo, displayclass &disp, 
+			     format_t *formatlistptr, text_tmap &options,
+			     ostream& logout);
+
+static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end, 
+			  format_t *formatlistptr, text_tset &metadata, bool &getParents);
+
+static text_t format_summary (const text_t& collection, recptproto* collectproto,
+			      ResultDocInfo_t &docinfo, displayclass &disp, 
+			      text_tmap &options, ostream& logout);
+static text_t format_text (const text_t& collection, recptproto* collectproto,
+			      ResultDocInfo_t &docinfo, displayclass &disp, 
+			      text_tmap &options, ostream& logout);
+
+static text_t expand_metadata(const text_t &metavalue, const text_t& collection,
+			      recptproto* collectproto, ResultDocInfo_t &docinfo,
+			      displayclass &disp, text_tmap &options,
+			      ostream &logout);
+
+
+void metadata_t::clear() {
+  metaname.clear();
+  metacommand = mNone;
+  mqualifier.parent  = pNone;
+  mqualifier.sibling = sNone;
+  mqualifier.child   = cNone;
+  parentoptions.clear();
+  siblingoptions.clear();
+  childoptions.clear();
+}
+
+void decision_t::clear() {
+  command = dMeta;
+  meta.clear();
+  text.clear();
+}
+
+void format_t::clear() {
+  command = comText;
+  decision.clear();
+  text.clear();
+  meta.clear();
+  nextptr = NULL;
+  ifptr = NULL;
+  elseptr = NULL;
+  orptr = NULL;
+}
+
+void formatinfo_t::clear() {
+  DocumentImages = false;
+  DocumentTitles = true;
+  DocumentHeading = "{Or}{[parent(Top):Title],[Title],untitled}<br>";
+  DocumentContents = true;
+  DocumentArrowsBottom = true; 
+  DocumentArrowsTop = false; 
+  DocumentSearchResultLinks = false;
+  DocumentButtons.erase (DocumentButtons.begin(), DocumentButtons.end());
+  //  DocumentButtons.push_back ("Expand Text");
+  //  DocumentButtons.push_back ("Expand Contents");
+  DocumentButtons.push_back ("Detach");
+  DocumentButtons.push_back ("Highlight");
+  RelatedDocuments = "";
+  DocumentText = "[Text]";
+  formatstrings.erase (formatstrings.begin(), formatstrings.end());
+  DocumentUseHTML = false;
+  AllowExtendedOptions = false;
+}
+
+// simply checks to see if formatstring begins with a <td> tag
+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 == '<') && ((here+3) < end)) {
+	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;
+}
+
+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;
+}
+
+// returns false if key isn't in formatstringmap
+bool get_formatstring (const text_t &key, const text_tmap &formatstringmap,
+		       text_t &formatstring) {
+
+  formatstring.clear();
+  text_tmap::const_iterator it = formatstringmap.find(key);
+  if (it == formatstringmap.end()) return false;
+  formatstring = (*it).second;
+  return true;
+}
+
+// tries to find "key1key2" then "key1" then "key2"
+bool get_formatstring (const text_t &key1, const text_t &key2,  
+		       const text_tmap &formatstringmap,
+		       text_t &formatstring) {
+
+  formatstring.clear();
+  text_tmap::const_iterator it = formatstringmap.find(key1 + key2);
+  if (it != formatstringmap.end()) {
+    formatstring = (*it).second;
+    return true;
+  }
+  it = formatstringmap.find(key1);
+  if (it != formatstringmap.end()) {
+    formatstring = (*it).second;
+    return true;
+  }
+  it = formatstringmap.find(key2);
+  if (it != formatstringmap.end()) {
+    formatstring = (*it).second;
+    return true;
+  }
+  return false;
+}
+
+
+text_t remove_namespace(const text_t &meta_name) {
+  text_t::const_iterator end = meta_name.end();
+  text_t::const_iterator it = findchar(meta_name.begin(), end, '.');
+  if (it != end) {
+    return substr(it+1, end);
+  }
+
+  return meta_name;
+
+}
+// returns a date of form _format:date_(year, month, day)
+// input is date of type yyyy-?mm-?dd
+// at least the year must be present in date
+text_t format_date (const text_t &date) {
+
+  if (date.size() < 4) return "";
+
+  text_t::const_iterator datebegin = date.begin();
+
+  text_t year = substr (datebegin, datebegin+4);
+  int chars_seen_so_far = 4;
+
+  if (date[chars_seen_so_far] == '-') ++chars_seen_so_far ;
+  if (date.size() < chars_seen_so_far+2) return "_format:date_("+year+")";
+ 
+  text_t month = substr (datebegin+chars_seen_so_far, datebegin+chars_seen_so_far+2);
+  int imonth = month.getint();
+  if (imonth <= 0 || imonth > 12) return "_format:date_("+year+")";
+ 
+  chars_seen_so_far += 2;
+  if (date[chars_seen_so_far] == '-') ++chars_seen_so_far ;
+  
+  if (date.size() < chars_seen_so_far+2) return "_format:date_("+year+","+month+")";
+
+  text_t day = substr (datebegin+chars_seen_so_far, datebegin+chars_seen_so_far+2);
+  if (day[0] == '0') day = substr (day.begin()+1, day.end());
+  int iday = day.getint();
+  if (iday <= 0 || iday > 31) return "_format:date_("+year+","+month+")";
+    
+  return "_format:date_("+year+","+month+","+day+")";
+}  
+
+// converts an iso639 language code to its English equivalent
+// should we be checking that the macro exists??
+text_t iso639 (const text_t &langcode) {
+  if (langcode.empty()) return "";
+  return "_iso639:iso639"+langcode+"_";
+}
+
+
+text_t get_href (const text_t &link) {
+
+  text_t href;
+
+  text_t::const_iterator here = findchar(link.begin(), link.end(), '"');
+  text_t::const_iterator end = link.end();
+  if (here == end) return g_EmptyText;
+  
+  ++here;
+  while (here != end) {
+    if (*here == '"') break;
+    href.push_back(*here);
+    ++here;
+  }
+
+  return href;
+}
+
+//this function gets the information associated with the relation
+//metadata for the document associated with 'docinfo'. This relation
+//metadata consists of a line of pairs containing 'collection, document OID'
+//(this is the OID of the document related to the current document, and
+//the collection the related document belongs to). For each of these pairs
+//the title metadata is obtained and then an html link between the title
+//of the related doc and the document's position (the document will be
+//found in "<a href=\"_httpdocument_&c=collection&cl=search&d=OID">
+//(where collection is the related documents collection, and OID is the
+//related documents OID).  A list of these html links are made for as many
+//related documents as there are. This list is then returned. If there are
+//no related documents available for the current document then the string
+//'.. no related documents .. ' is returned.
+text_t get_related_docs(const text_t& collection, recptproto* collectproto,
+			ResultDocInfo_t &docinfo, ostream& logout){
+  
+  text_tset metadata;
+
+  //insert the metadata we wish to collect
+  metadata.insert("dc.Relation"); 
+  metadata.insert("Title");  
+  metadata.insert("Subject"); //for emails, where title data doesn't apply
+ 
+  FilterResponse_t response;
+  text_t relation = ""; //string for displaying relation metadata
+  text_t relationTitle = ""; //the related documents Title (or subject)
+  text_t relationOID = ""; //the related documents OID  
+
+  //get the information associated with the metadata for current doc
+  if (get_info (docinfo.OID, collection, "", metadata, 
+		false, collectproto, response, logout)) {
+    
+    //if the relation metadata exists, store for displaying
+    if(!response.docInfo[0].metadata["dc.Relation"].values.empty()){
+      relationOID += response.docInfo[0].metadata["dc.Relation"].values[0];
+
+      //split relation data into pairs of collectionname,ID number
+      text_tarray relationpairs;
+      splitchar (relationOID.begin(), relationOID.end(), ' ', relationpairs);
+      
+      text_tarray::const_iterator currDoc = relationpairs.begin();  
+      text_tarray::const_iterator lastDoc = relationpairs.end();
+
+      //iterate through the pairs to split and display
+      while(currDoc != lastDoc){
+	
+	//split pairs into collectionname and ID 
+	text_tarray relationdata;
+	splitchar ((*currDoc).begin(), (*currDoc).end(), ',', relationdata);
+	
+	//get first element in the array (collection)
+	text_tarray::const_iterator doc_data = relationdata.begin();
+	text_t document_collection = *doc_data;
+	++doc_data; //increment to get next item in array (oid)
+	text_t document_OID = *doc_data;
+	
+	//create html link to related document
+	relation += "<a href=\"_httpdocument_&c=" + document_collection;
+	relation += "&cl=search&d=" + document_OID;
+	   
+	//get the information associated with the metadata for related doc
+	if (get_info (document_OID, document_collection, "", metadata,
+		      false, collectproto, response, logout)) {
+	  
+	  //if title metadata doesn't exist, collect subject metadata
+	  //if that doesn't exist, just call it 'related document'
+	  if (!response.docInfo[0].metadata["Title"].values[0].empty())
+		relationTitle = response.docInfo[0].metadata["Title"].values[0];
+	  else if (!response.docInfo[0].metadata["Subject"].values.empty())
+	    relationTitle = response.docInfo[0].metadata["Subject"].values[0];
+	  else relationTitle =  "RELATED DOCUMENT";
+	  
+	}
+	
+	//link the related document's title to its page
+	relation += "\">" + relationTitle + "</a>"; 
+	relation += "  (" + document_collection + ")<br>";
+	
+	++currDoc;
+      }
+    }
+    
+  } 
+
+  if(relation.empty()) //no relation data for documnet
+    relation = ".. no related documents .. ";
+
+  return relation;
+}
+
+
+
+static void get_parent_options (text_t &instring, metadata_t &metaoption) {
+
+  assert (instring.size() > 7);
+  if (instring.size() <= 7) return;
+
+  text_t meta, com, op;
+  bool inbraces = false;
+  bool inquotes = false;
+  bool foundcolon = false;
+  text_t::const_iterator here = instring.begin()+6;
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (foundcolon) meta.push_back (*here);
+    else if (*here == '(') inbraces = true;
+    else if (*here == ')') inbraces = false;
+    else if (*here == '\'' && !inquotes) inquotes = true;
+    else if (*here == '\'' && inquotes) inquotes = false;
+    else if (*here == ':' && !inbraces) foundcolon = true;
+    else if (inquotes) op.push_back (*here);
+    else com.push_back (*here);
+    ++here;
+  }
+
+  instring = meta;
+  if (com.empty())
+    metaoption.mqualifier.parent = pImmediate;
+  else if (com == "Top")
+    metaoption.mqualifier.parent = pTop;
+  else if (com == "All") {
+    metaoption.mqualifier.parent = pAll;
+    metaoption.parentoptions = op;
+  }
+}
+
+
+static void get_sibling_options (text_t &instring, metadata_t &metaoption) {
+
+  assert (instring.size() > 8);
+  if (instring.size() <= 8) return;
+  text_t meta, com, op;
+  bool inbraces = false;
+  bool inquotes = false;
+  bool foundcolon = false;
+  text_t::const_iterator here = instring.begin()+7;
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (foundcolon) meta.push_back (*here);
+    else if (*here == '(') inbraces = true;
+    else if (*here == ')') inbraces = false;
+    else if (*here == '\'' && !inquotes) inquotes = true;
+    else if (*here == '\'' && inquotes) inquotes = false;
+    else if (*here == ':' && !inbraces) foundcolon = true;    
+    else if (inquotes) op.push_back (*here);
+    else com.push_back (*here);
+    ++here;
+  }
+
+  instring = meta;
+  metaoption.siblingoptions.clear();
+
+  if (com.empty()) {
+    metaoption.mqualifier.sibling = sAll;
+    metaoption.siblingoptions = " ";
+  }
+  else if (com == "first") {
+    metaoption.mqualifier.sibling = sNum;
+    metaoption.siblingoptions = "0";
+  }
+  else if (com == "last") {
+    metaoption.mqualifier.sibling = sNum;
+    metaoption.siblingoptions = "-2"; // == last
+  }
+  else if (com.getint()>0) {
+    metaoption.mqualifier.sibling = sNum;
+    int pos = com.getint()-1;
+    metaoption.siblingoptions +=pos;
+  }
+  else {
+    metaoption.mqualifier.sibling = sAll;
+    metaoption.siblingoptions = op;
+  }
+}
+
+static void get_child_options (text_t &instring, metadata_t &metaoption) {
+
+  assert (instring.size() > 6);
+  if (instring.size() <= 6) return;
+  text_t meta, com, op;
+  bool inbraces = false;
+  bool inquotes = false;
+  bool foundcolon = false;
+  text_t::const_iterator here = instring.begin()+5;
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (foundcolon) meta.push_back (*here);
+    else if (*here == '(') inbraces = true;
+    else if (*here == ')') inbraces = false;
+    else if (*here == '\'' && !inquotes) inquotes = true;
+    else if (*here == '\'' && inquotes) inquotes = false;
+    else if (*here == ':' && !inbraces) foundcolon = true;
+    else if (inquotes) op.push_back (*here);
+    else com.push_back (*here);
+    ++here;
+  }
+
+  instring = meta;
+  if (com.empty()) {
+    metaoption.mqualifier.child = cAll;
+    metaoption.childoptions = " ";
+  }
+  else if (com == "first") {
+    metaoption.mqualifier.child = cNum;
+    metaoption.childoptions = ".fc";
+  }
+  else if (com == "last") {
+    metaoption.mqualifier.child = cNum;
+    metaoption.childoptions = ".lc";
+  }
+  else if (com.getint()>0) {
+    metaoption.mqualifier.child = cNum;
+    metaoption.childoptions = "."+com;
+  }
+  else {
+    metaoption.mqualifier.child = cAll;
+    metaoption.childoptions = op;
+  }
+}
+
+
+
+static void parse_meta (text_t &meta, metadata_t &metaoption,
+			text_tset &metadata, bool &getParents) {
+
+  if (meta.size() > 8 && (substr(meta.begin(), meta.begin()+8) == "cgisafe:")) {
+    metaoption.metacommand |= mCgiSafe;
+    meta = substr (meta.begin()+8, meta.end());
+  }
+  if (meta.size() > 7 && (substr(meta.begin(), meta.begin()+7) == "format:")) {    
+    metaoption.metacommand |= mSpecial;
+    meta = substr (meta.begin()+7, meta.end());
+  }
+
+  if (meta.size() > 7 && (substr (meta.begin(), meta.begin()+6) == "parent")) {
+    getParents = true;
+    metaoption.metacommand |= mParent;
+    get_parent_options (meta, metaoption);
+  } 
+  else if (meta.size() > 6 && (substr (meta.begin(), meta.begin()+5) == "child")) {
+    metaoption.metacommand |= mChild;
+    get_child_options (meta, metaoption);
+    metadata.insert("contains");
+  } 
+  // parent and child can have sibling also
+  if (meta.size() > 8 && (substr (meta.begin(), meta.begin()+7) == "sibling")) {
+    metaoption.metacommand |= mSibling;
+    get_sibling_options (meta, metaoption);
+  } 
+  
+  // check for ex. which may occur in format statements
+  if (meta.size()>3 && (substr(meta.begin(), meta.begin()+3) == "ex.")) {
+    meta = substr (meta.begin()+3, meta.end());
+  }
+  metadata.insert (meta);
+  metaoption.metaname = meta;
+}
+
+static void parse_coll_meta(text_t &meta, metadata_t &metaoption) {
+  if (meta == "collection") {
+    // no qualifiers
+    metaoption.metaname = g_EmptyText;
+    return;
+  }
+  meta = substr (meta.begin()+11, meta.end());
+  metaoption.metaname = meta;
+  
+}
+
+static void parse_meta (text_t &meta, format_t *formatlistptr,
+			text_tset &metadata, bool &getParents) {
+  
+  if (meta == "link")
+    formatlistptr->command = comLink;
+  else if (meta == "/link")
+    formatlistptr->command = comEndLink;
+
+  else if (meta == "href")
+    formatlistptr->command = comHref;
+
+  else if (meta == "num")
+    formatlistptr->command = comNum;
+
+  else if (meta == "icon")
+    formatlistptr->command = comIcon;
+
+  else if (meta == "Text")
+    formatlistptr->command = comDoc;
+  
+  else if (meta == "RelatedDocuments")
+   formatlistptr->command = comRel;
+
+  else if (meta == "highlight")
+    formatlistptr->command = comHighlight;
+
+  else if (meta == "/highlight")
+    formatlistptr->command = comEndHighlight;
+
+  else if (meta == "Summary")
+    formatlistptr->command = comSummary;
+
+  else if (meta == "DocImage")
+    formatlistptr->command = comImage; 
+
+  else if (meta == "DocTOC")
+    formatlistptr->command = comTOC; 
+
+  else if (meta == "DocumentButtonDetach")
+     formatlistptr->command = comDocumentButtonDetach;
+  
+  else if (meta == "DocumentButtonHighlight")
+     formatlistptr->command = comDocumentButtonHighlight;
+  
+  else if (meta == "DocumentButtonExpandContents")
+    formatlistptr->command = comDocumentButtonExpandContents; 
+
+  else if (meta == "DocumentButtonExpandText")
+     formatlistptr->command = comDocumentButtonExpandText; 
+
+  else if (meta == "DocOID")
+     formatlistptr->command = comOID; 
+  else if (meta == "DocTopOID")
+    formatlistptr->command = comTopOID;
+  else if (meta == "DocRank")
+    formatlistptr->command = comRank;
+  else if (meta.size() >= 10 && (substr(meta.begin(), meta.begin()+10) == "collection")) {
+    formatlistptr->command = comCollection;
+    parse_coll_meta(meta, formatlistptr->meta);
+  }
+  else {
+    formatlistptr->command = comMeta;
+    parse_meta (meta, formatlistptr->meta, metadata, getParents);
+  }
+}
+
+
+static bool parse_string (const text_t &formatstring, format_t *formatlistptr, 
+			  text_tset &metadata, bool &getParents) {
+
+  text_t text;
+  text_t::const_iterator here = formatstring.begin();
+  text_t::const_iterator end = formatstring.end();
+
+  while (here != end) {
+
+    if (*here == '\\') {
+      ++here;
+      if (here != end) text.push_back (*here);
+
+    } else if (*here == '{') {
+      if (!text.empty()) {
+	formatlistptr->command = comText;
+	formatlistptr->text = text;
+	formatlistptr->nextptr = new format_t();
+	formatlistptr = formatlistptr->nextptr;
+	
+	text.clear();
+      }
+      if (parse_action (++here, end, formatlistptr, metadata, getParents)) {
+
+	formatlistptr->nextptr = new format_t();
+	formatlistptr = formatlistptr->nextptr;
+	if (here == end) break;
+      }
+    } else if (*here == '[') {
+      if (!text.empty()) {
+	formatlistptr->command = comText;
+	formatlistptr->text = text;
+	formatlistptr->nextptr = new format_t();
+	formatlistptr = formatlistptr->nextptr;
+
+	text.clear();
+      }
+      text_t meta;
+      ++here;
+      while (*here != ']') {
+	if (here == end) return false;
+	meta.push_back (*here);
+	++here;
+      }
+      parse_meta (meta, formatlistptr, metadata, getParents);
+      formatlistptr->nextptr = new format_t();
+      formatlistptr = formatlistptr->nextptr;
+
+    } else 
+      text.push_back (*here);
+
+    if (here != end) ++here;
+  }
+  if (!text.empty()) {
+    formatlistptr->command = comText;
+    formatlistptr->text = text;
+    formatlistptr->nextptr = new format_t();
+    formatlistptr = formatlistptr->nextptr;
+
+  }
+  return true;
+}
+
+
+static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end, 
+			  format_t *formatlistptr, text_tset &metadata, bool &getParents) {
+
+  text_t::const_iterator it = findchar (here, end, '}');
+  if (it == end) return false;
+
+  text_t com = substr (here, it);
+  here = findchar (it, end, '{');
+  if (here == end) return false;
+  else ++here;
+
+  if (com == "If" || com == "if" || com == "IF") formatlistptr->command = comIf;
+  else if (com == "Or" || com == "or" || com == "OR") formatlistptr->command = comOr;
+  else return false;
+
+  int commacount = 0;
+  text_t text;
+  while (here != end) {
+
+    if (*here == '\\') {
+      ++here;
+      if (here != end) text.push_back(*here);
+      
+    } 
+  
+    else if (*here == ',' || *here == '}' || *here == '{') {
+
+      if (formatlistptr->command == comOr) {
+	// the {Or}{this, or this, or this, or this} statement
+	format_t *or_ptr;
+	
+	// find the next unused orptr
+	if (formatlistptr->orptr == NULL) {
+	  formatlistptr->orptr = new format_t();
+	  or_ptr = formatlistptr->orptr;
+	} else {
+	  or_ptr = formatlistptr->orptr;
+	  while (or_ptr->nextptr != NULL)
+	    or_ptr = or_ptr->nextptr;
+	  or_ptr->nextptr = new format_t();
+	  or_ptr = or_ptr->nextptr;
+	}
+
+	if (!text.empty())
+	  {
+	    if (!parse_string(text, or_ptr, metadata, getParents)) { return false; }
+	  }
+
+	if (*here == '{')
+	  {
+	    // Supports: {Or}{[Booktitle],[Title],{If}{[XXXX],aaa,bbb}}
+	    // but not : {Or}{[Booktitle],[Title]{If}{[XXXX],aaa,bbb}}
+	    // The latter can always be re-written:
+	    // {Or}{[Booktitle],{If}{[Title],[Title]{If}{[XXXX],aaa,bbb}}}
+	    
+	    if (!text.empty()) // already used up allocated format_t
+	      {
+		// => allocate new one for detected action
+		or_ptr->nextptr = new format_t();
+		or_ptr = or_ptr->nextptr;
+	      }
+	    if (!parse_action(++here, end, or_ptr, metadata, getParents))
+	      { 
+		return false;
+	      }
+	  }
+	else
+	  {
+	    if (*here == '}') break;
+	  }
+	text.clear();
+
+      } 
+
+      // Parse an {If}{decide,do,else} statement
+      else {
+	
+	// Read the decision component.  
+	if (commacount == 0) {
+	  // Decsion can be a metadata element, or a piece of text.
+	  // Originally Stefan's code, updated 25/10/2000 by Gordon.
+
+	  text_t::const_iterator beginbracket = text.begin();
+	  text_t::const_iterator endbracket = (text.end() - 1);
+
+	  // Decision is based on a metadata element
+	  if ((*beginbracket == '[') && (*endbracket == ']')) {
+	    // Ignore the surrounding square brackets
+	    text_t meta = substr (beginbracket+1, endbracket);
+	    parse_meta (meta, formatlistptr->decision.meta, metadata, getParents);
+	    ++commacount;
+	    text.clear();
+	  } 
+
+	  // Decision is a piece of text (probably a macro like _cgiargmode_).
+	  else {
+
+	    // hunt for any metadata in string, which might be uses in
+	    // to test a condition, e.g. [Format] eq 'PDF'
+	    format_t* dummyformat = new format_t();
+	    // update which metadata fields needed 
+	    // (not interested in updatng formatlistptr)
+	    parse_string (text, dummyformat, metadata, getParents);
+	    delete dummyformat;
+
+	    formatlistptr->decision.command = dText;
+	    formatlistptr->decision.text = text;
+	    ++commacount;
+	    text.clear();
+	  }
+	} 
+
+	// Read the "then" and "else" components of the {If} statement.
+	else {
+	  format_t** nextlistptr = NULL;
+	  if (commacount == 1) {
+	    nextlistptr = &formatlistptr->ifptr;
+	  } else if (commacount == 2 ) {
+	    nextlistptr = &formatlistptr->elseptr;
+	  } else {
+	    return false;
+	  }
+
+	  if (!text.empty()) {
+	    if (*nextlistptr == NULL) {
+	      *nextlistptr = new format_t();
+	    } else {
+
+	      // skip to the end of any format_t statements already added
+	      while ((*nextlistptr)->nextptr != NULL)
+	      {
+		nextlistptr = &(*nextlistptr)->nextptr;
+	      }
+
+	      (*nextlistptr)->nextptr = new format_t();
+	      nextlistptr = &(*nextlistptr)->nextptr;
+	    }
+
+	    if (!parse_string (text, *nextlistptr, metadata, getParents)) 
+	      { 
+		return false; 
+	      }
+	    text.clear();
+	  }
+	  
+	  if (*here == '{')
+	    {
+	      if (*nextlistptr == NULL) {
+		*nextlistptr = new format_t();
+	      } else {
+		// skip to the end of any format_t statements already added
+		while ((*nextlistptr)->nextptr != NULL)
+		  {
+		    nextlistptr = &(*nextlistptr)->nextptr;
+		  }
+
+		(*nextlistptr)->nextptr = new format_t();
+		nextlistptr = &(*nextlistptr)->nextptr;
+	      }
+
+	      if (!parse_action(++here, end, *nextlistptr, metadata, getParents))
+		{ 
+		  return false;
+		}
+	    }
+	  else 
+	    { 
+	      if (*here == '}') break;
+	      ++commacount; 
+	    }
+	}
+      }
+      
+    } else text.push_back(*here);
+    
+    if (here != end) ++here;
+  }
+
+  return true;
+}
+
+
+bool parse_formatstring (const text_t &formatstring, format_t *formatlistptr, 
+			 text_tset &metadata, bool &getParents) {
+
+  formatlistptr->clear();
+  getParents = false;
+
+  return (parse_string (formatstring, formatlistptr, metadata, getParents));
+}
+
+// position -1 for all, -2 for the last, 0 for the first, or x for a particular piece
+// metainfo has all the values for the metadata, meta has the request metadata name and options, position tells which values to get
+static text_t get_formatted_meta_text(MetadataInfo_t &metainfo, const metadata_t &meta, int position, bool no_cgisafe = false) 
+{
+  text_t no_ns_metaname = remove_namespace(meta.metaname);
+  text_t tmp;
+  bool first = true;
+  
+  const int start_i=0;
+  const int end_i = metainfo.values.size()-1;
+  
+  if (position == -1) { // all
+    for (int i=start_i; i<=end_i; ++i) {
+      if (!first) tmp += meta.siblingoptions;
+      if (meta.metacommand & mSpecial) {
+	// special formatting
+	if (no_ns_metaname == "Date") tmp += format_date (metainfo.values[i]);
+	else if (no_ns_metaname == "Language") tmp += iso639(metainfo.values[i]);
+	else tmp += "_format:"+meta.metaname+"_("+metainfo.values[i]+")";
+      }
+      else tmp += metainfo.values[i];
+      first = false;
+      
+    }
+  } else {
+    if (position == -2) { // end
+      position = end_i;
+    } else if (position < start_i || position > end_i) {
+      return "";
+    }
+    if (meta.metacommand & mSpecial) {
+      // special formatting
+      if (no_ns_metaname == "Date") tmp += format_date (metainfo.values[position]);
+      else if (no_ns_metaname == "Language") tmp += iso639(metainfo.values[position]);
+      else tmp += "_format:"+meta.metaname+"_("+metainfo.values[position]+")";
+    } 
+    else tmp += metainfo.values[position];
+  }
+  if (meta.metacommand & mCgiSafe && !no_cgisafe) return cgi_safe_unicode (tmp);
+  else return tmp;
+}
+
+static text_t get_parent_meta (ResultDocInfo_t &docinfo, const metadata_t &meta, int siblings_values) 
+{
+  
+  MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
+  switch (meta.mqualifier.parent) {
+  case pNone:
+    return "Nothing!!";
+    break;
+
+  case pImmediate:
+    if (parent != NULL) {
+      return get_formatted_meta_text(*parent, meta, siblings_values);
+    }
+    break;
+
+  case pTop:
+    if (parent != NULL) {
+      while (parent->parent != NULL) parent = parent->parent;
+      return get_formatted_meta_text(*parent, meta, siblings_values);
+    }
+    break;
+
+  case pAll:
+    MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
+    if (parent != NULL) {
+      text_tarray tmparray;
+      while (parent != NULL) {
+	tmparray.push_back (get_formatted_meta_text(*parent, meta, siblings_values, true)); // set no_cgisafe to true, as we'll do it once we have all the metadata
+	parent = parent->parent;
+      }
+      // now join them up - use teh parent separator
+      bool first = true;
+      text_t tmp;
+      text_tarray::reverse_iterator here = tmparray.rbegin();
+      text_tarray::reverse_iterator end = tmparray.rend();
+      while (here != end) {
+	if (!first) tmp += meta.parentoptions;
+	tmp += *here;
+	first = false;
+	++here;
+      }
+      if (meta.metacommand & mCgiSafe) return cgi_safe_unicode (tmp);
+      else return tmp;
+    }
+  }
+  return "";
+
+}
+
+static text_t get_child_meta (const text_t& collection, 
+			      recptproto* collectproto,
+			      ResultDocInfo_t &docinfo, displayclass &disp,
+			      const metadata_t &meta, text_tmap &options,
+			      ostream& logout, int siblings_values) 
+{
+  if (docinfo.metadata["contains"].values[0].size()==0) return ""; // no children
+  
+  const text_t& child_metaname = meta.metaname;
+  const text_t& child_field = meta.childoptions;
+  text_tset child_metadata;
+  child_metadata.insert(child_metaname); 
+
+  FilterResponse_t child_response;
+  if (meta.mqualifier.child == cNum) {
+    // just one child
+    //get the information associated with the metadata for child doc
+    if (!get_info (docinfo.OID+child_field, collection, "", child_metadata, 
+		   false, collectproto, child_response, logout)) return ""; // invalid child number
+
+      if (child_response.docInfo.empty()) return false; // no info for the child
+  
+      ResultDocInfo_t& child_docinfo = child_response.docInfo[0];
+      MetadataInfo_t& metaname_rec = child_docinfo.metadata[child_metaname];
+  
+      text_t child_metavalue = get_formatted_meta_text(metaname_rec,meta,siblings_values);
+      return expand_metadata(child_metavalue,collection,collectproto,
+			 child_docinfo,disp,options,logout);
+  }
+  
+    
+  if (meta.mqualifier.child != cAll) return false; // invalid qualifier
+  
+  // we need to get all children
+  text_t result = "";
+  text_tarray children;
+  text_t contains = docinfo.metadata["contains"].values[0];
+  splitchar (contains.begin(), contains.end(), ';', children);
+  text_tarray::const_iterator here = children.begin();
+  text_tarray::const_iterator end = children.end();
+  bool first = true;
+  while (here !=end) {
+    text_t oid = *here;
+    here++;
+    if (*(oid.begin()) == '"') translate_parent (oid, docinfo.OID);
+    
+    
+    //get the information associated with the metadata for child doc
+    if (!get_info (oid, collection, "", child_metadata, 
+		   false, collectproto, child_response, logout) ||
+	child_response.docInfo.empty()) {
+      first = false;
+      continue;
+    }
+    
+    
+    ResultDocInfo_t& child_docinfo = child_response.docInfo[0];
+    MetadataInfo_t& metaname_rec = child_docinfo.metadata[child_metaname];
+    
+    text_t child_metavalue = get_formatted_meta_text(metaname_rec,meta,siblings_values);
+    if (!first) result += child_field;
+    first = false;
+    // need to do this here cos otherwise we are in the wrong document
+    result +=  expand_metadata(child_metavalue,collection,collectproto,
+			       child_docinfo,disp,options,logout);
+  }
+  return result;
+    
+}
+
+static text_t get_meta (const text_t& collection, recptproto* collectproto,
+			ResultDocInfo_t &docinfo, displayclass &disp,
+			const metadata_t &meta, text_tmap &options,
+			ostream& logout) {
+  
+  // make sure we have the requested metadata
+  MetadataInfo_tmap::iterator it = docinfo.metadata.find (meta.metaname);
+  if (it == docinfo.metadata.end()) return "";
+
+  int siblings_values = 0; // default is no siblings, just the first metadata available
+  if (meta.metacommand & mSibling) {
+    if (meta.mqualifier.sibling == sAll) {
+      siblings_values = -1; //all
+    } else if (meta.mqualifier.sibling == sNum) {
+      siblings_values = meta.siblingoptions.getint();
+    }
+  }
+  if (meta.metacommand & mParent) {
+    return get_parent_meta(docinfo,meta,siblings_values);
+  }
+
+  else if (meta.metacommand & mChild) {
+    return get_child_meta(collection,collectproto,docinfo,disp,meta,
+			    options,logout, siblings_values);
+  }
+  else if (meta.metacommand & mSibling) { // only siblings
+    MetadataInfo_t& metaname_rec = docinfo.metadata[meta.metaname];
+    return get_formatted_meta_text(docinfo.metadata[meta.metaname],meta, siblings_values);
+  }
+  else {
+	
+    // straightforward metadata request (nothing fancy)
+
+    text_t classifier_metaname = docinfo.classifier_metadata_type;
+    int metaname_index 
+      = (classifier_metaname == meta.metaname) ? docinfo.classifier_metadata_offset : 0;
+    return get_formatted_meta_text(docinfo.metadata[meta.metaname], meta, metaname_index);
+  }
+  
+  return "";
+}
+
+static text_t get_or (const text_t& collection, recptproto* collectproto,
+		      ResultDocInfo_t &docinfo, displayclass &disp,
+		      format_t *orptr, text_tmap &options,
+		      ostream& logout) {
+
+  text_t tmp;
+  while (orptr != NULL) {
+
+    tmp = format_string (collection,collectproto,docinfo, disp, orptr, 
+			 options, logout);
+    if (!tmp.empty()) return tmp;
+
+    orptr = orptr->nextptr;
+  }
+  return "";
+}
+
+static bool char_is_whitespace(const char c) 
+{
+  return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'));
+
+}
+
+static int scan_over_whitespace(const text_t& outstring, const int start_pos)
+{
+  int pos = start_pos;
+  while (pos<outstring.size()) {
+    if (!char_is_whitespace(outstring[pos])) {
+      break;
+    }
+    ++pos;
+  }
+
+  return pos;
+}
+
+static int rscan_over_whitespace(const text_t& outstring, const int start_pos)
+{
+  int pos = start_pos;
+  while (pos>=0) {
+    if (!char_is_whitespace(outstring[pos])) {
+      break;
+    }
+    --pos;
+  }
+
+  return pos;
+}
+
+static int rscan_for_whitespace(const text_t& outstring, const int start_pos)
+{
+  int pos = start_pos;
+  while (pos>=0) {
+    if (char_is_whitespace(outstring[pos])) {
+      break;
+    }
+    --pos;
+  }
+
+  return pos;
+}
+
+
+static int rscan_for(const text_t& outstring, const int start_pos, 
+		     const char find_c)
+{
+  int pos = start_pos;
+  while (pos>=0) {
+    char c = outstring[pos];
+    if (outstring[pos] == find_c) {
+      break;
+    }
+    --pos;
+  }
+
+  return pos;
+}
+
+text_t extract_substr(const text_t& outstring, const int start_pos,
+		      const int end_pos)
+{
+  text_t extracted_str;
+  extracted_str.clear();
+
+  for (int pos=start_pos; pos<=end_pos; ++pos) {
+    extracted_str.push_back(outstring[pos]);
+  }
+
+  return extracted_str;
+}
+
+
+static text_t expand_potential_metadata(const text_t& collection, 
+					recptproto* collectproto,
+					ResultDocInfo_t &docinfo, 
+					displayclass &disp,
+					const text_t& intext,
+					text_tmap &options,
+					ostream& logout)
+{
+  text_t outtext;
+
+  // decide if dealing with metadata or text
+
+  text_t::const_iterator beginbracket = intext.begin();
+  text_t::const_iterator endbracket = (intext.end() - 1);
+
+  // Decision is based on a metadata element
+  if ((*beginbracket == '[') && (*endbracket == ']')) {
+    // Ignore the surrounding square brackets
+    text_t meta_text = substr (beginbracket+1, endbracket);
+
+    if (meta_text == "Text") {
+      outtext = format_text(collection, collectproto, docinfo, disp, options, logout);
+    } else {
+
+      text_tset metadata;
+      bool getParents =false;
+      metadata_t meta;
+      
+      parse_meta (meta_text, meta, metadata, getParents);	
+      outtext 
+	= get_meta (collection,collectproto,docinfo,disp,meta,options,logout);
+    }
+
+  }
+  else {
+    outtext = intext;
+  }
+
+  return outtext;
+}
+
+
+
+
+static bool uses_expression(const text_t& collection, recptproto* collectproto,
+			    ResultDocInfo_t &docinfo,
+			    displayclass &disp,
+			    const text_t& outstring, text_t& lhs_expr,
+			    text_t& op_expr, text_t& rhs_expr,
+			    text_tmap &options,
+			    ostream& logout)
+{
+  // Note: the string may not be of the form: str1 op str2, however
+  // to deterine this we have to process it on the assumption it is,
+  // and if at any point an 'erroneous' value is encountered, return
+  // false and let something else have a go at evaluating it
+
+  // Starting at the end of the string and working backwards ..
+
+  const int outstring_len = outstring.size();
+
+  // skip over white space
+  int rhs_end = rscan_over_whitespace(outstring,outstring_len-1);
+
+  if (rhs_end<=0) { 
+    // no meaningful text or (rhs_end==0) no room for operator
+    return false;
+  }
+
+  // check for ' or " and then scan over token
+  const char potential_quote = outstring[rhs_end];
+  int rhs_start=rhs_end;
+  bool quoted = false;
+
+  if ((potential_quote == '\'') || (potential_quote == '\"')) {
+    --rhs_end;
+    rhs_start = rscan_for(outstring,rhs_end-1,potential_quote) +1;
+    quoted = true;
+  }
+  else {
+    rhs_start = rscan_for_whitespace(outstring,rhs_end-1) +1;
+  }
+
+  if ((rhs_end-rhs_start)<0) {
+    // no meaningful rhs expression
+    return false;
+  }
+
+  // form rhs_expr
+  rhs_expr = extract_substr(outstring,rhs_start,rhs_end);
+
+  // skip over white space
+  const int to_whitespace = (quoted) ? 2 : 1;
+
+  int op_end = rscan_over_whitespace(outstring,rhs_start-to_whitespace);
+  int op_start = rscan_for_whitespace(outstring,op_end-1)+1;
+
+
+  if (op_end-op_start<0) {
+    // no meaningful expression operator
+    return false;
+  }
+
+  op_expr = extract_substr(outstring,op_start,op_end);
+
+
+  // check for operator
+  if ((op_expr != "eq") && (op_expr != "ne") && (op_expr != "gt") &&
+     (op_expr != "ge") && (op_expr != "lt") && (op_expr != "le") && (op_expr != "==") && (op_expr != "!=") &&  (op_expr != ">") && (op_expr != ">=") && (op_expr != "<") && (op_expr != "<=") && (op_expr != "sw") && (op_expr != "ew")) {
+
+    // not a valid operator
+    return false;
+  }
+
+  int lhs_end = rscan_over_whitespace(outstring,op_start-1);
+  if (lhs_end<0) {
+    // no meaningful lhs expression
+    return false;
+  }
+
+  int lhs_start = scan_over_whitespace(outstring,0);
+
+  // form lhs_expr from remainder of string
+  lhs_expr = extract_substr(outstring,lhs_start,lhs_end);
+
+  // Now we know we have a valid expression, look up any 
+  // metadata terms
+
+  rhs_expr = expand_potential_metadata(collection,collectproto,docinfo,
+				       disp,rhs_expr,options,logout);
+  lhs_expr = expand_potential_metadata(collection,collectproto,docinfo,
+				       disp,lhs_expr,options,logout);
+
+  return true;
+}
+
+static bool eval_expression_true(const text_t& lhs_expr,const text_t& op_expr,
+				 const text_t& rhs_expr, ostream& logout)
+{
+  if (op_expr == "eq") return (lhs_expr == rhs_expr);
+  else if (op_expr == "ne" ) return (lhs_expr != rhs_expr);
+  else if (op_expr == "gt") return (lhs_expr > rhs_expr);
+  else if (op_expr == "ge") return (lhs_expr >= rhs_expr);
+  else if (op_expr == "lt") return (lhs_expr < rhs_expr);
+  else if (op_expr == "le") return (lhs_expr <= rhs_expr);
+  else if (op_expr == "==") return (lhs_expr.getint() == rhs_expr.getint());
+  else if (op_expr == "!=") return (lhs_expr.getint() != rhs_expr.getint());
+  else if (op_expr == ">") return (lhs_expr.getint() > rhs_expr.getint());
+  else if (op_expr == ">=") return (lhs_expr.getint() >= rhs_expr.getint());
+  else if (op_expr == "<") return (lhs_expr.getint() < rhs_expr.getint());
+  else if (op_expr == "<=") return (lhs_expr.getint() <= rhs_expr.getint());
+  else if (op_expr == "sw") return (starts_with(lhs_expr,rhs_expr));
+  else if (op_expr == "ew") return (ends_with(lhs_expr,rhs_expr));
+  else {
+    logout << "Error: '" << op_expr << "' is not a recognised operator." << endl;
+  }
+  
+  return false;
+}
+
+
+static text_t get_if (const text_t& collection, recptproto* collectproto,
+		      ResultDocInfo_t &docinfo, displayclass &disp, 
+		      const decision_t &decision,
+		      format_t *ifptr, format_t *elseptr,
+		      text_tmap &options, ostream& logout) 
+{
+  // If the decision component is a metadata element, then evaluate it
+  // to see whether we output the "then" or the "else" clause
+  if (decision.command == dMeta) {
+    if (get_meta (collection,collectproto,docinfo,disp,decision.meta,options,
+		  logout) != "") {
+      if (ifptr != NULL)
+	return get_formatted_string (collection,collectproto,docinfo, disp, ifptr, 
+				     options, logout);
+    }
+    else {
+      if (elseptr != NULL)
+	return get_formatted_string (collection,collectproto,docinfo, disp, elseptr,
+				     options, logout);
+    }
+  }
+
+  // If the decision component is text, then evaluate it (it is probably a 
+  // macro like _cgiargmode_) to decide what to output.
+  else if (decision.command == dText) {
+
+    text_t outstring;
+    disp.expandstring (decision.text, outstring);
+
+    // Check for if expression in form: str1 op str2 
+    // (such as [x] eq "y")
+    text_t lhs_expr, op_expr, rhs_expr;
+    if (uses_expression(collection,collectproto,docinfo, disp, outstring,lhs_expr,op_expr,rhs_expr, options,logout)) {
+      if (eval_expression_true(lhs_expr,op_expr,rhs_expr,logout)) {
+	if (ifptr != NULL) {
+	  return get_formatted_string (collection, collectproto, docinfo, disp, ifptr, 
+				       options, logout);
+	}
+	else {
+	  return "";
+	}
+      } else {
+	if (elseptr != NULL) {
+	  return get_formatted_string (collection, collectproto, docinfo, disp, elseptr,
+				       options, logout);
+	}
+	else {
+	  return "";
+	}
+      }
+    }
+
+
+    // This is a tad tricky.  When we expand a string like _cgiargmode_, that is
+    // a cgi argument macro that has not been set, it evaluates to itself.
+    // Therefore, were have to say that a piece of text evalautes true if
+    // it is non-empty and if it is a cgi argument evaulating to itself.
+
+    if ((outstring != "") && !((outstring == decision.text) && (outstring[0] == '_'))) {
+      if (ifptr != NULL)
+	return get_formatted_string (collection, collectproto, docinfo, disp, ifptr, 
+				     options, logout);
+    } else {
+      if (elseptr != NULL)
+	return get_formatted_string (collection, collectproto, docinfo, disp, elseptr,
+				     options, logout);
+    }
+  }
+  
+  return "";
+}
+
+bool includes_metadata(const text_t& text)
+{
+  text_t::const_iterator here = text.begin();
+  text_t::const_iterator end = text.end();
+  while (here != end) {
+    if (*here == '[') return true;
+    ++here;
+  }
+
+  return false;
+}
+
+static text_t expand_metadata(const text_t &metavalue, const text_t& collection,
+			      recptproto* collectproto,
+			      ResultDocInfo_t &docinfo,
+			      displayclass &disp, text_tmap &options,
+			      ostream &logout) {
+      
+  if (includes_metadata(metavalue)) {
+    
+    // text has embedded metadata in it => expand it
+    FilterRequest_t request;
+    FilterResponse_t response;
+    
+    request.getParents = false;
+    
+    format_t *expanded_formatlistptr = new format_t();
+    parse_formatstring (metavalue, expanded_formatlistptr, 
+			request.fields, request.getParents);
+    
+    // retrieve metadata
+    get_info(docinfo.OID, collection, "", request.fields, request.getParents,
+	     collectproto, response, logout);
+    
+    if (!response.docInfo.empty()) {
+      
+      text_t expanded_metavalue
+	= get_formatted_string(collection, collectproto,
+			       response.docInfo[0], disp, expanded_formatlistptr, 
+			       options, logout);
+      
+      return expanded_metavalue;
+    }
+    else {
+      return metavalue;
+    }
+  }
+  else {
+    
+    return metavalue;
+  }
+}
+
+text_t get_collection_meta(const text_t& collection, recptproto* collectproto,
+			   displayclass &disp, 
+			   text_t meta_name, ostream& logout) {
+  
+  ColInfoResponse_t collectinfo;
+  comerror_t err;
+  collectproto->get_collectinfo (collection, collectinfo,err,logout);
+  text_t meta_value = "";
+  text_t lang;
+  disp.expandstring("_cgiargl_",lang);
+  if (lang.empty()) {
+    lang = "en";
+  }
+
+  if (err == noError) {
+    meta_value = collectinfo.get_collectionmeta(meta_name, lang);
+  }
+  return meta_value;
+  
+
+}
+text_t format_string (const text_t& collection, recptproto* collectproto,
+		      ResultDocInfo_t &docinfo, displayclass &disp, 
+		      format_t *formatlistptr, text_tmap &options,
+		      ostream& logout) {
+
+  if (formatlistptr == NULL) return "";
+
+  switch (formatlistptr->command) {
+     case comOID:
+	return docinfo.OID;
+  case comTopOID:
+    {
+      text_t top_id;
+      get_top(docinfo.OID, top_id);
+      return top_id;
+    }
+  case comRank:
+    return text_t(docinfo.ranking);
+     case comText:
+	return formatlistptr->text;
+     case comLink:
+	return options["link"];
+     case comEndLink:
+	if (options["link"].empty()) return "";
+	else return "</a>";
+     case comHref:
+	return get_href(options["link"]);
+     case comIcon:
+	return options["icon"];
+     case comNum:
+	return docinfo.result_num;
+     case comRel: //if [RelatedDocuments] appears in format string, collect relation data
+	return get_related_docs(collection, collectproto, docinfo, logout);
+     case comSummary:
+	return format_summary(collection, collectproto, docinfo, disp, options, logout);
+     case comMeta:
+	{
+	   const text_t& metavalue =  get_meta (collection,collectproto, docinfo, disp,formatlistptr->meta,options,logout);
+	   return expand_metadata(metavalue, collection, collectproto, docinfo, disp, options, logout);
+	}
+     case comDoc:
+	return format_text(collection, collectproto, docinfo, disp, options, logout);
+	//return options["text"];
+     case comImage:
+	return expand_metadata(options["DocImage"], collection, collectproto, docinfo, disp, options, logout);
+     case comTOC:
+	return options["DocTOC"];
+     case comDocumentButtonDetach:
+	return options["DocumentButtonDetach"];
+     case comDocumentButtonHighlight:
+	return options["DocumentButtonHighlight"];
+     case comDocumentButtonExpandContents:
+	return options["DocumentButtonExpandContents"];
+     case comDocumentButtonExpandText:
+	return options["DocumentButtonExpandText"];
+     case comHighlight:
+	if (options["highlight"] == "1") return "<b>";
+	break;
+     case comEndHighlight:
+	if (options["highlight"] == "1") return "</b>";
+	break;
+     case comIf:
+	return get_if (collection, collectproto, docinfo, disp, 
+		       formatlistptr->decision, formatlistptr->ifptr, 
+		       formatlistptr->elseptr, options, logout);
+     case comOr:
+	return get_or (collection,collectproto, docinfo, disp, formatlistptr->orptr, 
+		       options, logout);
+     case comCollection:
+       if (formatlistptr->meta.metaname == g_EmptyText) {
+	 return collection;
+       }
+       return get_collection_meta(collection, collectproto, disp, formatlistptr->meta.metaname, logout);
+    
+  }
+  return "";
+}
+
+text_t get_formatted_string (const text_t& collection, recptproto* collectproto,
+			     ResultDocInfo_t &docinfo, displayclass &disp,
+			     format_t *formatlistptr, text_tmap &options,
+			     ostream& logout) {
+
+   text_t ft;
+   while (formatlistptr != NULL) 
+      {
+	 ft += format_string (collection, collectproto, docinfo, disp, formatlistptr, 
+			      options, logout);
+	 formatlistptr = formatlistptr->nextptr;
+      }
+   
+   return ft;
+}
+
+
+// we have only preloaded the text in DocumentAction. But you may want to get the text in query. so copy what we have done with format_summary and get the text here. probably is quite expensive?
+text_t format_text (const text_t& collection, recptproto* collectproto,
+		    ResultDocInfo_t &docinfo, displayclass &disp, 
+		    text_tmap &options, ostream& logout) {
+  if(!options["text"].empty()) { 
+    return options["text"];
+  }
+  // else get document text here
+  DocumentRequest_t docrequest;
+  DocumentResponse_t docresponse;
+  comerror_t err;
+  docrequest.OID = docinfo.OID;
+  collectproto->get_document (collection, docrequest, docresponse, err, logout);
+  return docresponse.doc;
+
+}
+  
+/* FUNCTION NAME: format_summary
+ * DESC: this is invoked when a [Summary] special metadata is processed.
+ * RETURNS: a query-biased summary for the document */
+
+text_t format_summary (const text_t& collection, recptproto* collectproto,
+		       ResultDocInfo_t &docinfo, displayclass &disp, 
+		       text_tmap &options, ostream& logout) {
+
+  // GRB: added code here to ensure that the cstr (and other collections)
+  //      uses the document metadata item Summary, rather than compressing
+  //      the text of the document, processed via the methods in
+  //      summarise.cpp
+  if (docinfo.metadata.count("Summary") > 0 &&
+      docinfo.metadata["Summary"].values.size() > 0) {
+    return docinfo.metadata["Summary"].values[0];
+  }
+
+  text_t textToSummarise, query;
+  if(options["text"].empty()) { // get document text
+     DocumentRequest_t docrequest;
+     DocumentResponse_t docresponse;
+     comerror_t err;
+     docrequest.OID = docinfo.OID;
+     collectproto->get_document (collection, docrequest, docresponse, err, logout);
+     textToSummarise = docresponse.doc;
+  } else // in practice, this would not happen, because text is only 
+         // loaded with the [Text] command
+     textToSummarise = options["text"];
+  disp.expandstring("_cgiargq_",query);
+  return summarise(textToSummarise,query,80);
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/formattools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/formattools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/formattools.h	(revision 16561)
@@ -0,0 +1,145 @@
+/**********************************************************************
+ *
+ * formattools.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.
+ *
+ *********************************************************************/
+
+
+#ifndef FORMATTOOLS_H
+#define FORMATTOOLS_H
+
+#include "text_t.h"
+#include "recptproto.h"
+#include "display.h"
+
+enum command_t {comIf, comOr, comMeta, comText, comLink, comEndLink, comNum, comIcon, 
+		comDoc, comHighlight, comEndHighlight, comRel, comHref, comSummary,
+		comTOC, comImage, comDocumentButtonDetach, comDocumentButtonHighlight,
+		comDocumentButtonExpandContents, comDocumentButtonExpandText, comOID, comTopOID, comRank, comCollection};
+
+enum mcommand_t {mNone=0, mCgiSafe=1, mParent=2, mSibling=4, mChild=8, mSpecial=16};
+
+enum pcommand_t {pNone, pImmediate, pTop, pAll};
+enum scommand_t {sNone, sAll, sNum};
+enum ccommand_t {cNone, cAll, cNum};
+enum dcommand_t {dMeta, dText};
+
+struct metadata_t {
+  void clear();
+  metadata_t () {clear();}
+
+  text_t metaname;
+  int metacommand; // used to hold flags from mcommand_t
+  struct {
+    pcommand_t parent;
+    scommand_t sibling;
+    ccommand_t child;
+  } mqualifier;
+
+  text_t parentoptions;
+  text_t siblingoptions;
+  text_t childoptions;
+};
+
+
+// The decision component of an {If}{decision,true-text,false-text}
+// formatstring.  The decision can be based on metadata or on text;
+// normally that text would be a macro like _cgiargmode_.  --gordon
+struct decision_t {
+  void clear();
+  decision_t () {clear();}
+
+  dcommand_t command;
+  metadata_t meta;
+  text_t text;
+};
+
+struct format_t {
+  void clear();
+  format_t () {clear();}
+
+  command_t command;
+  decision_t decision;
+  text_t text;
+  metadata_t meta;
+  format_t *nextptr;
+  format_t *ifptr;
+  format_t *elseptr;
+  format_t *orptr;
+};
+
+// the format structure used by this receptionist
+struct formatinfo_t {
+  void clear();
+  formatinfo_t () {clear();}
+
+  bool DocumentImages;
+  bool DocumentTitles;
+  text_t DocumentHeading;
+  bool DocumentContents;
+  bool DocumentArrowsBottom;
+  bool DocumentArrowsTop;
+  bool DocumentSearchResultLinks;
+  text_tarray DocumentButtons;
+  text_t DocumentText;
+  //-----------------
+  text_t RelatedDocuments;
+  //----------------
+  text_tmap formatstrings;
+  bool DocumentUseHTML;
+  bool AllowExtendedOptions;
+};
+
+bool is_table_content (const text_t &formatstring);
+bool is_table_content (const format_t *formatlistptr);
+
+bool get_formatstring (const text_t &key, const text_tmap &formatstringmap,
+		       text_t &formatstring);
+bool get_formatstring (const text_t &key1, const text_t &key2,  
+		       const text_tmap &formatstringmap,
+		       text_t &formatstring);
+
+text_t format_date (const text_t &date);
+
+text_t remove_namespace(const text_t &meta_name);
+
+text_t iso639 (const text_t &langcode);
+
+text_t get_href (const text_t &link);
+
+text_t get_related_docs(const text_t& collection, recptproto* collectproto,
+			  ResultDocInfo_t &docinfo, ostream& logout);
+
+
+bool parse_formatstring (const text_t &formatstring, format_t *formatlistptr, 
+			 text_tset &metadata, bool &getParents);
+
+
+
+text_t get_formatted_string (const text_t& collection, recptproto* collectproto,
+			     ResultDocInfo_t &docinfo, displayclass &disp,
+			     format_t *formatlistptr, text_tmap &options,
+			     ostream& logout);
+
+#endif
+
+
Index: /gsdl/trunk/gs2runtime/src/recpt/gsdl_compatibility_cfg.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/gsdl_compatibility_cfg.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/gsdl_compatibility_cfg.h	(revision 16561)
@@ -0,0 +1,38 @@
+#ifndef GSDL_COMPATIBILITY_CFG__H
+#define GSDL_COMPATIBILITY_CFG__H
+
+/**********************************************************************
+ *
+ * gsdl_compatibility_cfg.h -- 
+ * Copyright (C) 2004  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.
+ *
+ *********************************************************************/
+
+//OLD NAME	NEW NAME
+#define __USE_MULTILANGUAGE_INTERFACE_         GSDL_USE_MULTILANGUAGE_INTERFACE
+#define GSDL_USE_GENERATOR_FEATURE             GSDL_USE_GENERATOR_ACTION
+#define GSDL_USE_EXPORT_FEATURE                GSDL_USE_EXPORT_ACTION
+#define __USE_CLASSIFIER_BROWSER_              GSDL_USE_CLASSIFIER_BROWSER
+#define GSDL_USE_CLONEBROWSERCLASS             GSDL_USE_CLONE_BROWSER
+#define __SOURCE_DIRECTORY_                    GSDL_SOURCE_DIRECTORY
+
+
+#endif //!GSDL_COMPATIBILITY_CFG__H
Index: /gsdl/trunk/gs2runtime/src/recpt/gsdl_modules_cfg.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/gsdl_modules_cfg.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/gsdl_modules_cfg.h	(revision 16561)
@@ -0,0 +1,90 @@
+#ifndef GSDL_MODULES_CFG__H
+#define GSDL_MODULES_CFG__H
+
+/**********************************************************************
+ *
+ * gsdl_modules_cfg.h -- 
+ * Copyright (C) 2004  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.
+ *
+ *********************************************************************/
+
+// BASE CONFIG
+#define GSDL_USE_PER_COLLECTION_SETTINGS
+#define GSDL_SOURCE_DIRECTORY            "files"
+
+// FNORD CONFIG
+#define GSDL_USE_MULTILANGUAGE_INTERFACE
+
+//RECEPTIONIST CONFIG
+#define GSDL_USE_SESSIONS
+
+//QUERY CONFIG
+#define GSDL_USE_REMOVE_TAG_TERMS
+#undef  GSDL_USE_SAFEM_QUERY_FORMAT
+
+//DOCUMENT CONFIG
+// ONLY ONE GSDL_SKIP_* option can be enabled
+#define GSDL_SKIP_ONLY_EMPTY_DOC
+#undef  GSDL_SKIP_ALL_EMPTY_DOC_PARTS
+#undef  GSDL_USE_DOCUMENT_ASSOC_LANGS
+#define GSDL_USE_CL_SINGLE_DOCUMENT
+
+
+//ACTION MODULES
+#define GSDL_USE_AUTHEN_ACTION
+#define GSDL_USE_BROWSE_ACTION
+#define GSDL_USE_COLLECTOR_ACTION
+#define GSDL_USE_DEPOSITOR_ACTION
+#define GSDL_USE_EXTLINK_ACTION
+#undef  GSDL_USE_GTI_ACTION
+#define GSDL_USE_PHIND_ACTION
+#define GSDL_USE_PING_ACTION
+#define GSDL_USE_STATUS_ACTION
+#define GSDL_USE_TIP_ACTION
+#define GSDL_USE_USERS_ACTION
+
+#undef  GSDL_USE_CL_DISPLAY_ACTION
+#undef  GSDL_USE_COURSES_ACTION
+#undef  GSDL_USE_DIR_BROWSE_ACTION
+#undef  GSDL_USE_EXPORT_ACTION
+#undef  GSDL_USE_GENERATOR_ACTION
+#undef  GSDL_USE_INDEX_BROWSE_ACTION
+
+
+//BROWSER MODULES
+#undef  GSDL_USE_CLASSIFIER_BROWSER
+#undef  GSDL_USE_TREE_EX_CLASSIFIER_BROWSER
+#undef  GSDL_USE_CLONE_BROWSER
+#define GSDL_USE_DATELIST_BROWSER
+#define GSDL_USE_HTML_BROWSER
+#define GSDL_USE_PAGED_BROWSER
+#define GSDL_USE_PHIND_BROWSER
+
+//FILTERS
+#undef  GSDL_USE_DIR_BROWSEFILTER
+
+//BROWSERS ADD-IN
+#define GSDL_USE_BY_BOOK_MACROS
+#undef  GSDL_USE_FLOAT_TOC
+
+#include "gsdl_compatibility_cfg.h"
+
+#endif //!GSDL_MODULES_CFG__H
Index: /gsdl/trunk/gs2runtime/src/recpt/gtiaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/gtiaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/gtiaction.cpp	(revision 16561)
@@ -0,0 +1,839 @@
+/**********************************************************************
+ *
+ * gtiaction.cpp -- 
+ * Copyright (C) 2005  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.
+ *
+ *********************************************************************/
+
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_GTI_ACTION
+
+
+#include <expat.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+#include "gtiaction.h"
+#include "cgiutils.h"
+#include "fileutil.h"
+#include "gsdlunicode.h"
+
+
+
+gtiaction::gtiaction()
+{
+  cgiarginfo arg_ainfo;
+
+  arg_ainfo.shortname = "tlc";
+  arg_ainfo.longname = "translation target language code";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (&cerr, arg_ainfo);
+
+  arg_ainfo.shortname = "tfk";
+  arg_ainfo.longname = "translation file key";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (&cerr, arg_ainfo);
+
+  arg_ainfo.shortname = "ncpp";
+  arg_ainfo.longname = "number of chunks per page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (&cerr, arg_ainfo);
+}
+
+
+
+gtiaction::~gtiaction()
+{
+  delete[] set_gsdlhome_cstr;
+  delete[] set_gsdlos_cstr;
+}
+
+
+
+bool gtiaction::init (ostream& /*logout*/)
+{
+  // Set GSDLHOME and GSDLOS environment variables
+  text_t set_gsdlhome = "GSDLHOME=" + gsdlhome;
+  text_t set_gsdlos = "GSDLOS=";
+
+#if defined (__WIN32__)
+  set_gsdlos += "windows";
+#else
+  struct utsname *buf = new struct utsname();
+  if (uname(buf) == -1) {
+    // uname failed, so this must be linux
+    set_gsdlos += "linux";
+  }
+  else {
+    text_t gsdlos = buf->sysname;
+    lc(gsdlos);
+    set_gsdlos += gsdlos;
+  }
+  delete buf;
+#endif
+
+  // These will be cleaned up in the destructor
+  set_gsdlhome_cstr = set_gsdlhome.getcstr();
+  set_gsdlos_cstr = set_gsdlos.getcstr();
+  putenv(set_gsdlhome_cstr);
+  putenv(set_gsdlos_cstr);
+
+  return true;
+}
+
+
+
+bool gtiaction::check_cgiargs(cgiargsinfoclass& /*argsinfo*/, cgiargsclass& args,
+			      recptprotolistclass* /*protos*/, ostream& logout)
+{
+  // Authenticate the user, except for the "home" and "lang" pages
+  if (args["p"] != "home" && args["p"] != "lang" && args["p"] != "status") {
+    args["uan"] = 1;
+    args["ug"] = "langadmin_" + args["tlc"];
+  }
+
+  return true;
+}
+
+
+
+bool gtiaction::do_action(cgiargsclass& args, recptprotolistclass* /*protos*/, 
+			  browsermapclass* /*browsers*/, displayclass& disp, 
+			  outconvertclass& outconvert, ostream& textout, 
+			  ostream& logout)
+{
+  // Special case for producing Excel spreadsheets, as these are downloaded
+  if (args["p"] == "excel") {
+    return produce_excel_spreadsheet(args, logout);
+  }
+
+  textout << outconvert << disp << ("_gti:header_\n") << ("_gti:content_\n") << ("_gti:footer_\n");
+  return true;
+}
+
+
+
+void gtiaction::get_cgihead_info(cgiargsclass& args, recptprotolistclass* /*protos*/,
+				 response_t& response, text_t& response_data, 
+				 ostream& logout)
+{
+  // Special case for producing Excel spreadsheets, as these are downloaded
+  if (args["p"] == "excel") {
+    printf("Content-Disposition: attachment; filename=\"Greenstone-%s-%s.xml\"\n", args["tlc"].getcstr(), args["tfk"].getcstr());
+    response = content;
+    response_data = "text/xml";
+    return;
+  }
+
+  response = content;
+  response_data = "text/html";
+}
+
+
+
+void gtiaction::define_internal_macros(displayclass& disp, cgiargsclass& args, 
+				       recptprotolistclass* protos, ostream& logout)
+{
+  // logout << endl << "Arguments: " << args << endl;
+  logout << endl << "CGI arg p: " << args["p"] << endl;
+
+  // For some reason this must be done as well as setting the macro in gti.dm
+  // is that still true??
+  disp.setmacro("preflink", displayclass::defaultpackage, "_gti:preflink_");
+  
+  // Define the page content for the GTI home page
+  if (args["p"] == "home") {
+    define_gti_home_page(disp, args, logout);
+    return;
+  }
+
+  // Define the page content for the GTI language page
+  if (args["p"] == "lang") {
+    define_gti_lang_page(disp, args, logout);
+    return;
+  }
+
+  // Define the page content for the GTI search page
+  if (args["p"] == "find") {
+    define_gti_find_page(disp, args, logout);
+    return;
+  }
+
+  // Define the page content for the GTI offline page
+  if (args["p"] == "offline") {
+    define_gti_offline_page(disp, args, logout);
+    return;
+  }
+  
+  // Define the page content for the GTI view status page
+	if (args["p"] == "status") {
+		define_gti_status_page(disp, args, logout);
+		return;
+	}
+
+  // Process user translations
+  if (args["p"] == "submit") {
+    process_gti_submissions(disp, args, logout, true);
+  }
+  	
+  // Define the page content for the GTI core pages (containing the translation input forms)
+  define_gti_core_page(disp, args, logout);
+}
+
+
+
+void gtiaction::define_gti_home_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  disp.setmacro("gtiformcontent", "gti", "_gti:gtihome_");
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+
+  // Get the languages specified in the main.cfg file, and put them into a map to sort by name
+  text_tmap gti_languages_name_code_mapping;
+  languageinfo_tmap::const_iterator loaded_language = loaded_languages.begin();
+  while (loaded_language != loaded_languages.end()) {
+    // English is not a valid GTI target language, since it is the source language
+    if (loaded_language->first != "en") {
+      gti_languages_name_code_mapping[loaded_language->second.longname] = loaded_language->first;
+    }
+    ++loaded_language;
+  }
+
+  // Set the gtitlcselection macro
+  text_t gti_tlc_selection = "<select name=\"tlc\">\n";
+  text_tmap::iterator gti_language = gti_languages_name_code_mapping.begin();
+  while (gti_language != gti_languages_name_code_mapping.end()) {
+    gti_tlc_selection += "<option value=\"" + gti_language->second + "\">" + gti_language->first + "</option>\n";
+    ++gti_language;
+  }
+  gti_tlc_selection += "</select>";
+  disp.setmacro("gtitlcselection", "gti", gti_tlc_selection);
+}
+
+
+
+void gtiaction::define_gti_lang_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  // Get the target language code from the CGI arguments
+  text_t target_language_code = args["tlc"];
+
+  disp.setmacro("gtiformcontent", "gti", "_gti:gtilang_");
+
+  // Send a request to gti.pl to get the valid translation files
+  text_t gti_arguments = "get-language-status " + target_language_code;
+  GTI_Response gti_response = parse_gti_response(do_gti_request(gti_arguments, logout), logout);
+  if (gti_response.error_message != "") {
+    // An error has occurred
+    disp.setmacro("gtiformcontent", "gti", "_gti:gtierror_");
+    disp.setmacro("gtierrormessage", "gti", gti_response.error_message);
+    return;
+  }
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+  disp.setmacro("gtitargetlanguagename", "gti", loaded_languages[target_language_code].longname);
+
+  // Set the gtitfkselection macro
+  text_t gti_tfk_selection = "<table>";
+  text_tmap::iterator translation_file = gti_response.translation_files_index_to_key_mapping.begin();
+  while (translation_file != gti_response.translation_files_index_to_key_mapping.end()) {
+    text_t translation_file_key = translation_file->second;
+
+    gti_tfk_selection += "<tr><td>";
+    gti_tfk_selection += "<input type=\"radio\" name=\"tfk\" value=\"" + translation_file_key + "\"></td>\n";
+    gti_tfk_selection += "<td>_textgti" + translation_file_key + "_</td></tr>\n";
+
+    text_t num_chunks_translated = gti_response.translation_files_key_to_num_chunks_translated_mapping[translation_file_key];
+    text_t num_chunks_requiring_translation = gti_response.translation_files_key_to_num_chunks_requiring_translation_mapping[translation_file_key];
+    text_t num_chunks_requiring_updating = gti_response.translation_files_key_to_num_chunks_requiring_updating_mapping[translation_file_key];
+    gti_tfk_selection += "<tr><td></td><td>_gtitranslationfilestatus_(" + num_chunks_translated + "," + num_chunks_requiring_translation + "," + num_chunks_requiring_updating + ")</td></tr>\n";
+    ++translation_file;
+  }
+  gti_tfk_selection += "</table>";
+  disp.setmacro("gtitfkselection", "gti", gti_tfk_selection);
+}
+
+
+
+void gtiaction::define_gti_status_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  disp.setmacro("gtiformcontent", "gti", "_gti:gtistatus_");
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+
+  // Get the languages specified in the main.cfg file, and put them into a map to sort by name
+  text_tmap gti_languages_name_code_mapping;
+  languageinfo_tmap::const_iterator loaded_language = loaded_languages.begin();
+  while (loaded_language != loaded_languages.end()) {
+    // English is not a valid GTI target language, since it is the source language
+    if (loaded_language->first != "en") {
+      gti_languages_name_code_mapping[loaded_language->second.longname] = loaded_language->first;
+    }
+    ++loaded_language;
+  }
+
+  // Get the languages, for each language, send a request to gti.pl to get the valid translation files and the current status for each file
+  text_t gti_status_table = "<table class=\"status\">\n";
+  text_tmap::iterator gti_language = gti_languages_name_code_mapping.begin();
+  bool first_lang = true;
+  while (gti_language != gti_languages_name_code_mapping.end()) {    
+    // Send a request to gti.pl to get the valid translation files
+    text_t gti_arguments = "get-language-status " + gti_language->second;
+    GTI_Response gti_response = parse_gti_response(do_gti_request(gti_arguments, logout), logout);
+    if (gti_response.error_message != "") {
+      // An error has occurred
+      disp.setmacro("gtiformcontent", "gti", "_gti:gtierror_");
+      disp.setmacro("gtierrormessage", "gti", gti_response.error_message);
+      return;
+    }
+
+    text_tmap::iterator translation_file = gti_response.translation_files_index_to_key_mapping.begin();
+        
+    text_t lang_status_temp = "<tr><td class=\"first\">" + gti_language->first + "</td>\n";
+    text_t files_temp = "<tr><th class=\"status\">_textgtilanguage_</th>\n";
+    text_t number_of_strings_temp = "<tr><td class=\"first\"><b>_textgtitotalnumberoftranslations_</b></td>\n";
+    
+    while (translation_file != gti_response.translation_files_index_to_key_mapping.end()) {
+      text_t translation_file_key = translation_file->second;                     
+
+      text_t num_chunks_translated = gti_response.translation_files_key_to_num_chunks_translated_mapping[translation_file_key];
+      text_t num_chunks_requiring_translation = gti_response.translation_files_key_to_num_chunks_requiring_translation_mapping[translation_file_key];
+    	text_t num_chunks_requiring_updating = gti_response.translation_files_key_to_num_chunks_requiring_updating_mapping[translation_file_key];   		
+    	
+    	lang_status_temp += "<td class=\"status\">";
+    	if(num_chunks_translated.getint() > 0){
+    		lang_status_temp += "<div class=\"nowrap\"><div class=\"done\">";
+    		lang_status_temp += num_chunks_translated+"</div><div class=\"plus\">+</div><div class=\"update\">";
+  	  	lang_status_temp += num_chunks_requiring_updating+"</div><div class=\"plus\">+</div><div class=\"todo\">";
+  	  	lang_status_temp += num_chunks_requiring_translation+"</div></div>";
+    	}
+    	lang_status_temp += "</td>\n";
+    	
+    	
+   		//lang_status_temp += "<td valign=\"top\" nowrap>_gtitranslationfilestatus2_(" + num_chunks_translated + "," + num_chunks_requiring_translation + "," + num_chunks_requiring_updating + ")</td>";   		
+   		
+   		// List the file names as the first row of the status table 
+   		// Add up number of strings need to be translate in each file, as the second line of the status table
+      if (first_lang) {
+        files_temp += "<th class=\"status\">_textgti" + translation_file_key + "_</th>\n";
+        int int_number_of_strings = num_chunks_translated.getint() + num_chunks_requiring_translation.getint();
+        number_of_strings_temp += "<td class=\"status\"><b>";
+        number_of_strings_temp.appendint(int_number_of_strings);
+        number_of_strings_temp += "</b></td>\n";
+      }
+    	++translation_file;
+    }    
+    
+    if(first_lang) {     
+     gti_status_table += files_temp + "</tr>" + number_of_strings_temp + "</tr>";   
+     first_lang = false;
+    }
+    
+    gti_status_table += lang_status_temp + "</tr>";    
+    ++gti_language;
+  }
+  gti_status_table += "\n</table>";
+  disp.setmacro("gtistatustable", "gti", gti_status_table);
+}
+
+
+
+void gtiaction::define_gti_find_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  // Get the target language code and file to translate from the CGI arguments
+  text_t target_language_code = args["tlc"];
+  text_t translation_file_key = args["tfk"];
+  text_t query_string = to_utf8(args["q"]);
+
+  // Process user corrections
+  if (args["sp"] != "") {
+    process_gti_submissions(disp, args, logout, false);
+  }
+
+  disp.setmacro("gtiformcontent", "gti", "_gti:gtifind_");
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+  disp.setmacro("gtitargetlanguagename", "gti", loaded_languages[target_language_code].longname);
+  disp.setmacro("gtitranslationfiledesc", "gti", "_gti:textgti" + translation_file_key + "_");
+
+  if (query_string == "") {
+    // No query, so no search results
+    disp.setmacro("gtifindformcontent", "gti", "");
+    return;
+  }
+
+  // Display text right to left if target language is Arabic or Farsi or Urdu
+  if (target_language_code == "ar" || target_language_code == "fa" || target_language_code == "ur") {
+    disp.setmacro("gtitextdirection", "gti", "rtl");
+  }
+  else {
+    disp.setmacro("gtitextdirection", "gti", "ltr");
+  }
+
+  // Send a request to gti.pl to get the valid translation files
+  logout << "Query argument: " << query_string << endl;
+  text_t gti_arguments = "search-chunks " + target_language_code + " " + translation_file_key + " " + query_string;
+  GTI_Response gti_response = parse_gti_response(do_gti_request(gti_arguments, logout), logout);
+  if (gti_response.error_message != "") {
+    // An error has occurred
+    disp.setmacro("gtiformcontent", "gti", "_gti:gtierror_");
+    disp.setmacro("gtierrormessage", "gti", gti_response.error_message);
+    return;
+  }
+
+  disp.setmacro("gtinumchunksmatchingquery", "gti", gti_response.num_chunks_matching_query);
+
+  // Loop through the chunks returned, displaying them on the page
+  text_t gti_find_form_content = "_gtifindformheader_\n";
+  text_tmap::iterator chunk_key_iterator = gti_response.target_file_chunks_key_to_text_mapping.begin();
+  while (chunk_key_iterator != gti_response.target_file_chunks_key_to_text_mapping.end()) {
+    text_t chunk_key = chunk_key_iterator->first;
+
+    // Need to escape any underscores in the chunk key to show it correctly on the page
+    text_t chunk_key_escaped = escape_all(chunk_key, '_');
+
+    // Need to escape any backslashes, underscores, commas, parentheses, and single quotes in the chunk text
+    text_t target_file_chunk_text = gti_response.target_file_chunks_key_to_text_mapping[chunk_key];
+    text_t target_file_chunk_text_escaped = escape_all(target_file_chunk_text, '\\');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '_');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, ',');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '(');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, ')');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '"');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '\'');
+
+    // This chunk matches the query
+    gti_find_form_content += "_gtichunkmatchingquery_(" + chunk_key_escaped + "," + target_file_chunk_text_escaped + ")\n";
+
+    chunk_key_iterator++;
+  }
+  gti_find_form_content += "_gtifindformfooter_\n";
+
+  disp.setmacro("gtifindformcontent", "gti", gti_find_form_content);
+}
+
+
+
+void gtiaction::define_gti_offline_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  // Get the target language code and file to translate from the CGI arguments
+  text_t target_language_code = args["tlc"];
+  text_t translation_file_key = args["tfk"];
+
+  disp.setmacro("gtiformcontent", "gti", "_gti:gtioffline_");
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+  disp.setmacro("gtitargetlanguagename", "gti", loaded_languages[target_language_code].longname);
+  disp.setmacro("gtitranslationfiledesc", "gti", "_gti:textgti" + translation_file_key + "_");
+}
+
+
+
+void gtiaction::define_gti_core_page(displayclass& disp, cgiargsclass& args, ostream& logout)
+{
+  // Get the target language code and file to translate from the CGI arguments
+  text_t target_language_code = args["tlc"];
+  text_t translation_file_key = args["tfk"];
+  text_t num_chunks_per_page = args["ncpp"];
+  logout << "Num chunks per page: " << num_chunks_per_page << endl;
+
+  disp.setmacro("gtiformcontent", "gti", "_gti:gticore_");
+
+  // Display text right to left if target language is Arabic or Farsi or Urdu
+  if (target_language_code == "ar" || target_language_code == "fa" || target_language_code == "ur") {
+    disp.setmacro("gtitextdirection", "gti", "rtl");
+  }
+  else {
+    disp.setmacro("gtitextdirection", "gti", "ltr");
+  }
+
+  // Send a request to gti.pl to get the first string to translate
+  text_t gti_arguments = "get-first-n-chunks-requiring-work " + target_language_code + " " + translation_file_key + " " + num_chunks_per_page;
+  GTI_Response gti_response = parse_gti_response(do_gti_request(gti_arguments, logout), logout);
+  if (gti_response.error_message != "") {
+    // An error has occurred
+    disp.setmacro("gtiformcontent", "gti", "_gti:gtierror_");
+    disp.setmacro("gtierrormessage", "gti", gti_response.error_message);
+    return;
+  }
+
+  languageinfo_tmap loaded_languages = recpt->get_configinfo().languages;
+  disp.setmacro("gtitargetlanguagename", "gti", loaded_languages[target_language_code].longname);
+  disp.setmacro("gtitargetfilepath", "gti", gti_response.translation_files_key_to_target_file_path_mapping[translation_file_key]);
+  disp.setmacro("gtitranslationfiledesc", "gti", "_gti:textgti" + translation_file_key + "_");
+  disp.setmacro("gtiviewtranslationfileinaction", "gti", "_gti:gtiview" + translation_file_key + "inaction_");
+
+  disp.setmacro("gtinumchunkstranslated", "gti", gti_response.translation_files_key_to_num_chunks_translated_mapping[translation_file_key]);
+  disp.setmacro("gtinumchunksrequiringtranslation", "gti", gti_response.translation_files_key_to_num_chunks_requiring_translation_mapping[translation_file_key]);
+  disp.setmacro("gtinumchunksrequiringupdating", "gti", gti_response.translation_files_key_to_num_chunks_requiring_updating_mapping[translation_file_key]);
+
+  // Check if the translation file is finished
+  if (gti_response.translation_files_key_to_num_chunks_requiring_translation_mapping[translation_file_key] == "0" && gti_response.translation_files_key_to_num_chunks_requiring_updating_mapping[translation_file_key] == "0") {
+    disp.setmacro("gtiformcontent", "gti", "_gti:gtidone_");
+    return;
+  }
+
+  // Loop through the chunks returned, displaying them on the page
+  text_t gti_core_form_content = "";
+  text_tmap::iterator chunk_key_iterator = gti_response.source_file_chunks_key_to_text_mapping.begin();
+  while (chunk_key_iterator != gti_response.source_file_chunks_key_to_text_mapping.end()) {
+    text_t chunk_key = chunk_key_iterator->first;
+
+    // Need to escape any underscores in the chunk key to show it correctly on the page
+    text_t chunk_key_escaped = escape_all(chunk_key, '_');
+
+    // Need to escape any backslashes, underscores, commas, parentheses, and single quotes in the chunk text
+    text_t source_file_chunk_text = gti_response.source_file_chunks_key_to_text_mapping[chunk_key];
+    text_t source_file_chunk_text_escaped = escape_all(source_file_chunk_text, '\\');
+    source_file_chunk_text_escaped = escape_all(source_file_chunk_text_escaped, '_');
+    source_file_chunk_text_escaped = escape_all(source_file_chunk_text_escaped, ',');
+    source_file_chunk_text_escaped = escape_all(source_file_chunk_text_escaped, '(');
+    source_file_chunk_text_escaped = escape_all(source_file_chunk_text_escaped, ')');
+    source_file_chunk_text_escaped = escape_all(source_file_chunk_text_escaped, '\'');
+    text_t target_file_chunk_text = gti_response.target_file_chunks_key_to_text_mapping[chunk_key];
+    text_t target_file_chunk_text_escaped = escape_all(target_file_chunk_text, '\\');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '_');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, ',');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '(');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, ')');
+    target_file_chunk_text_escaped = escape_all(target_file_chunk_text_escaped, '\'');
+
+    text_t source_file_chunk_date = gti_response.source_file_chunks_key_to_date_mapping[chunk_key];
+    text_t target_file_chunk_date = gti_response.target_file_chunks_key_to_date_mapping[chunk_key];
+
+    // This chunk requires translation
+    if (target_file_chunk_text == "") {
+      gti_core_form_content += "_gtichunkrequiringtranslation_(" + chunk_key_escaped + "," + source_file_chunk_text_escaped + "," + source_file_chunk_date + ")\n";
+    }
+    // This chunk requires updating
+    else {
+      gti_core_form_content += "_gtichunkrequiringupdating_(" + chunk_key_escaped + "," + source_file_chunk_text_escaped + "," + source_file_chunk_date + "," + target_file_chunk_text_escaped + "," + target_file_chunk_date + ")\n";
+    }
+
+    chunk_key_iterator++;
+  }
+
+  disp.setmacro("gticoreformcontent", "gti", gti_core_form_content);
+}
+
+
+
+void gtiaction::process_gti_submissions(displayclass& disp, cgiargsclass& args, ostream& logout, bool force_submission)
+{
+  // Get the target language code and file to translate from the CGI arguments
+  text_t target_language_code = args["tlc"];
+  text_t translation_file_key = args["tfk"];
+  text_t submitter_username = args["un"];
+
+  // Submitted chunk arguments contain the language code followed by "::"
+  char* source_chunk_key_start_cstr = ((text_t) "en" + "%3A%3A").getcstr();
+  char* target_chunk_key_start_cstr = (target_language_code + "%3A%3A").getcstr();
+
+  // Find the cgi arguments with submitted chunk information
+  text_t submission_text;
+  cgiargsclass::const_iterator cgi_argument = args.begin();
+  while (cgi_argument != args.end()) {
+    char* cgi_argument_name_cstr = cgi_argument->first.getcstr();
+
+    // Source file text
+    if (strncmp(cgi_argument_name_cstr, source_chunk_key_start_cstr, strlen(source_chunk_key_start_cstr)) == 0) {
+      submission_text += "<SourceFileText key=\"";
+      text_t source_key = &cgi_argument_name_cstr[strlen(source_chunk_key_start_cstr)];
+      decode_cgi_arg(source_key);
+      submission_text += source_key;
+      //submission_text += &cgi_argument_name_cstr[strlen(source_chunk_key_start_cstr)];
+      submission_text += "\">\n";
+
+      text_t source_value = xml_safe(decode_commas(args[cgi_argument->first]));
+      // if (args["w"] != "utf-8") {
+      // source_value = to_utf8(source_value);
+      // }
+      submission_text += source_value + "\n";
+      submission_text += "</SourceFileText>\n";
+    }
+    // Target file text
+    if (strncmp(cgi_argument_name_cstr, target_chunk_key_start_cstr, strlen(target_chunk_key_start_cstr)) == 0) {
+      submission_text += "<TargetFileText key=\"";
+      text_t target_key = &cgi_argument_name_cstr[strlen(target_chunk_key_start_cstr)];
+      decode_cgi_arg(target_key);
+      submission_text += target_key;
+      //submission_text += &cgi_argument_name_cstr[strlen(target_chunk_key_start_cstr)];
+      submission_text += "\">\n";
+
+      text_t target_value = xml_safe(decode_commas(args[cgi_argument->first]));
+      // if (args["w"] != "utf-8") {
+      // target_value = to_utf8(target_value);
+      // }
+      submission_text += target_value + "\n";
+      submission_text += "</TargetFileText>\n";
+    }
+
+    delete[] cgi_argument_name_cstr;
+    ++cgi_argument;
+  }
+
+  logout << "Submission text: " << submission_text << endl;
+
+  // Send the submission to gti.pl
+  text_t gti_arguments = "submit-translations " + target_language_code + " " + translation_file_key + " " + submitter_username;
+  if (force_submission) {
+    gti_arguments += " -force_submission";
+  }
+  do_gti_submission(gti_arguments, submission_text, logout);
+  logout << "Done." << endl;
+
+  delete[] source_chunk_key_start_cstr;
+  delete[] target_chunk_key_start_cstr;
+}
+
+
+
+bool gtiaction::produce_excel_spreadsheet(cgiargsclass& args, ostream& logout)
+{
+  // Get the target language code and file to translate from the CGI arguments
+  text_t target_language_code = args["tlc"];
+  text_t translation_file_key = args["tfk"];
+
+  // Send a request to gti.pl to get the Excel spreadsheet data
+  text_t gti_arguments = "get-first-n-chunks-requiring-work " + target_language_code + " " + translation_file_key + " " + "10000" + " | java -cp /home/nzdl/gti ApplyXSLT /home/nzdl/gti/gti-generate-excel-xml.xsl -";
+  text_t gti_response_xml_text = do_gti_request(gti_arguments, logout);
+  if (gti_response_xml_text == "") {
+    // An error has occurred
+    return false;
+  }
+
+  // Write the Excel spreadsheet data to the browser
+  char* gti_response_xml_text_cstr = gti_response_xml_text.getcstr();
+  printf(gti_response_xml_text_cstr);
+  delete[] gti_response_xml_text_cstr;
+
+  return true;
+}
+
+
+
+text_t gtiaction::escape_all(text_t text_string, char character_to_escape)
+{
+  text_t text_string_escaped = "";
+
+  text_t::iterator text_string_character = text_string.begin();
+  while (text_string_character != text_string.end()) {
+    if (*text_string_character == character_to_escape) {
+      text_string_escaped += "\\";
+    }
+    text_string_escaped.push_back(*text_string_character);
+    text_string_character++;
+  }
+
+  return text_string_escaped;
+}
+
+
+
+text_t gtiaction::xml_safe(const text_t &text_string)
+{
+  text_t text_string_safe;
+  text_t::const_iterator here = text_string.begin();
+  text_t::const_iterator end = text_string.end();
+  while (here != end) {
+    if (*here == '&') text_string_safe += "&amp;";
+    else if (*here == '<') text_string_safe += "&lt;";
+    else if (*here == '>') text_string_safe += "&gt;";
+    else text_string_safe.push_back(*here);
+    ++here;
+  }
+
+  return text_string_safe;
+}
+
+
+
+char* xml_get_attribute(const char** attributes, char* attribute_name)
+{
+  for (int i = 0; (attributes[i] != NULL); i += 2) {
+    if (strcmp(attribute_name, attributes[i]) == 0) {
+      return strdup(attributes[i+1]);
+    }
+  }
+
+  return NULL;
+}
+
+
+static void XMLCALL gti_response_xml_start_element(void* user_data, const char* element_name_cstr, const char** attributes)
+{
+  GTI_Response* gti_response = (GTI_Response*) user_data;
+  text_t element_name = element_name_cstr;
+  cerr << "In startElement() for " << element_name << endl;
+
+  if (element_name == "GTIError") {
+    gti_response->recorded_text = "";
+    gti_response->is_recording_text = true;
+  }
+
+  if (element_name == "TranslationFile") {
+    int translation_file_index = gti_response->translation_files_index_to_key_mapping.size();
+    gti_response->translation_file_key = xml_get_attribute(attributes, "key");
+    gti_response->translation_files_index_to_key_mapping[translation_file_index] = gti_response->translation_file_key;
+    gti_response->translation_files_key_to_target_file_path_mapping[gti_response->translation_file_key] = xml_get_attribute(attributes, "target_file_path");
+    gti_response->translation_files_key_to_num_chunks_translated_mapping[gti_response->translation_file_key] = xml_get_attribute(attributes, "num_chunks_translated");
+    gti_response->translation_files_key_to_num_chunks_requiring_translation_mapping[gti_response->translation_file_key] = xml_get_attribute(attributes, "num_chunks_requiring_translation");
+    gti_response->translation_files_key_to_num_chunks_requiring_updating_mapping[gti_response->translation_file_key] = xml_get_attribute(attributes, "num_chunks_requiring_updating");
+  }
+
+  if (element_name == "ChunksMatchingQuery") {
+    gti_response->num_chunks_matching_query = xml_get_attribute(attributes, "size");
+  }
+
+  if (element_name == "Chunk") {
+    gti_response->chunk_key = xml_get_attribute(attributes, "key");
+  }
+
+  if (element_name == "SourceFileText") {
+    gti_response->source_file_chunks_key_to_date_mapping[gti_response->chunk_key] = xml_get_attribute(attributes, "date");
+    gti_response->recorded_text = "";
+    gti_response->is_recording_text = true;
+  }
+
+  if (element_name == "TargetFileText") {
+    if (xml_get_attribute(attributes, "date") != NULL) {
+      gti_response->target_file_chunks_key_to_date_mapping[gti_response->chunk_key] = xml_get_attribute(attributes, "date");
+    }
+    gti_response->recorded_text = "";
+    gti_response->is_recording_text = true;
+  }
+}
+
+
+static void XMLCALL gti_response_xml_end_element(void* user_data, const char* element_name_cstr)
+{
+  GTI_Response* gti_response = (GTI_Response*) user_data;
+  text_t element_name = element_name_cstr;
+
+  if (element_name == "GTIError") {
+    gti_response->error_message = to_uni(gti_response->recorded_text);
+    gti_response->is_recording_text = false;
+  }
+
+  if (element_name == "SourceFileText") {
+    gti_response->source_file_chunks_key_to_text_mapping[gti_response->chunk_key] = to_uni(gti_response->recorded_text);
+    gti_response->is_recording_text = false;
+  }
+
+  if (element_name == "TargetFileText") {
+    gti_response->target_file_chunks_key_to_text_mapping[gti_response->chunk_key] = to_uni(gti_response->recorded_text);
+    gti_response->is_recording_text = false;
+  }
+}
+
+
+static void XMLCALL gti_response_xml_character_data(void *user_data, const char* text_cstr, int text_length)
+{
+  GTI_Response* gti_response = (GTI_Response*) user_data;
+  if (gti_response->is_recording_text) {
+    gti_response->recorded_text.appendcarr(text_cstr, text_length);
+  }
+}
+
+
+
+text_t gtiaction::do_gti_request(text_t gti_arguments, ostream& logout)
+{
+  // Send the request to gti.pl and read the XML output
+  text_t gti_command = "perl -S " + filename_cat(gsdlhome, "bin", "script", "gti.pl") + " " + gti_arguments;
+  char* gti_command_cstr = gti_command.getcstr();
+  FILE *gti_pipe = popen(gti_command_cstr, "r");
+  delete[] gti_command_cstr;
+  if (gti_pipe == NULL) {
+    logout << "Error: Could not open pipe for GTI command " << gti_command << endl;
+    return "";
+  }
+
+  // Read the gti.pl response
+  text_t gti_response_xml_text;
+  while (!feof(gti_pipe)) {
+    char buffer[1024];
+    gti_response_xml_text.appendcarr(buffer, fread(buffer, 1, 1024, gti_pipe));
+  }
+  pclose(gti_pipe);
+
+  return gti_response_xml_text;
+}
+
+
+
+GTI_Response gtiaction::parse_gti_response(text_t gti_response_xml_text, ostream& logout)
+{
+  GTI_Response gti_response;
+  gti_response.is_recording_text = false;
+
+  // Parse the gti.pl response (XML)
+  logout << "Parsing GTI command response: " << gti_response_xml_text << endl;
+  XML_Parser xml_parser = XML_ParserCreate(NULL);
+  XML_SetUserData(xml_parser, &gti_response);
+  XML_SetElementHandler(xml_parser, gti_response_xml_start_element, gti_response_xml_end_element);
+  XML_SetCharacterDataHandler(xml_parser, gti_response_xml_character_data);
+
+  char* gti_response_xml_text_cstr = gti_response_xml_text.getcstr();
+  int parse_status = XML_Parse(xml_parser, gti_response_xml_text_cstr, strlen(gti_response_xml_text_cstr), XML_TRUE);
+  delete[] gti_response_xml_text_cstr;
+  if (parse_status == XML_STATUS_ERROR) {
+    logout << "Parse error " << XML_ErrorString(XML_GetErrorCode(xml_parser)) << " at line " << XML_GetCurrentLineNumber(xml_parser) << endl;
+    return gti_response;
+  }
+
+  XML_ParserFree(xml_parser);
+
+  logout << "Finished parse." << endl;
+  return gti_response;
+}
+
+
+
+void gtiaction::do_gti_submission(text_t gti_arguments, text_t gti_submission, ostream& logout)
+{
+  // Send the submission to gti.pl
+  text_t gti_command = "perl -S " + filename_cat(gsdlhome, "bin", "script", "gti.pl") + " " + gti_arguments;
+  char* gti_command_cstr = gti_command.getcstr();
+  FILE *gti_pipe = popen(gti_command_cstr, "w");
+  delete[] gti_command_cstr;
+  if (gti_pipe == NULL) {
+    logout << "Error: Could not open pipe for GTI command " << gti_command << endl;
+    return;
+  }
+
+  // Write the gti.pl submission
+  char* gti_submission_cstr = gti_submission.getcstr();
+  fwrite(gti_submission_cstr, 1, strlen(gti_submission_cstr), gti_pipe);
+  delete[] gti_submission_cstr;
+
+  pclose(gti_pipe);
+}
+
+
+
+#endif  // GSDL_USE_GTI_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/gtiaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/gtiaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/gtiaction.h	(revision 16561)
@@ -0,0 +1,125 @@
+/**********************************************************************
+ *
+ * gtiaction.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.
+ *
+ *********************************************************************/
+
+
+#ifndef GTIACTION_H
+#define GTIACTION_H
+
+
+#include "action.h"
+#include "receptionist.h"
+
+
+
+struct GTI_Response
+{
+  text_t translation_file_key;
+  text_tmap translation_files_index_to_key_mapping;
+  text_tmap translation_files_key_to_num_chunks_translated_mapping;
+  text_tmap translation_files_key_to_num_chunks_requiring_translation_mapping;
+  text_tmap translation_files_key_to_num_chunks_requiring_updating_mapping;
+  text_tmap translation_files_key_to_target_file_path_mapping;
+
+  text_t chunk_key;
+  text_tmap source_file_chunks_key_to_text_mapping;
+  text_tmap source_file_chunks_key_to_date_mapping;
+
+  // For chunks requiring updating
+  text_tmap target_file_chunks_key_to_text_mapping;
+  text_tmap target_file_chunks_key_to_date_mapping;
+
+  text_t num_chunks_matching_query;
+
+  bool is_recording_text;
+  text_t recorded_text;
+
+  text_t error_message;
+};
+
+
+class gtiaction : public action {
+
+protected:
+  receptionist *recpt;
+
+  char* set_gsdlhome_cstr;
+  char* set_gsdlos_cstr;
+
+public:
+  gtiaction ();
+  virtual ~gtiaction ();
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "gti";}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  bool init (ostream & /*logout*/);
+
+
+  // The interesting functions
+  void define_gti_home_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  void define_gti_lang_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  void define_gti_find_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  void define_gti_offline_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  void define_gti_core_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  text_t escape_all(text_t source_string, char character_to_escape);
+
+  text_t xml_safe(const text_t &text_string);
+
+  void process_gti_submissions(displayclass& disp, cgiargsclass& args, ostream& logout, bool force_submission);
+
+  text_t do_gti_request(text_t gti_arguments, ostream& logout);
+
+  GTI_Response parse_gti_response(text_t gti_response_xml_text, ostream& logout);
+
+  void do_gti_submission(text_t gti_arguments, text_t gti_submission, ostream& logout);
+
+  void define_gti_status_page(displayclass& disp, cgiargsclass& args, ostream& logout);
+
+  bool produce_excel_spreadsheet(cgiargsclass& args, ostream& logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/highlighttext.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/highlighttext.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/highlighttext.cpp	(revision 16561)
@@ -0,0 +1,288 @@
+/**********************************************************************
+ *
+ * highlighttext.cpp -- 
+ * Copyright (C) 2002   D L Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "highlighttext.h"
+#include "unitool.h"
+
+
+static void highlight_terms(const text_t &text, const TermInfo_tarray &terms, const text_t &shl,
+			    const text_t &ehl, displayclass &disp, outconvertclass &outconvert,
+			    ostream &textout);
+
+static void highlight_phrases(const text_t &text, const text_t &querystring, const TermInfo_tarray &terms, 
+			      const text_t &shl, const text_t &ehl, displayclass &disp, 
+			      outconvertclass &outconvert, ostream &textout);
+
+static void get_phrase_terms (const text_t &querystring, text_tarray &phrase_terms);
+
+static void remove_space (text_t &qstring);
+
+// highlights text string by adding _starthighlight_ and _endhightlight_
+// around terms and/or phrases that match querystring 
+
+// - at present this only handles phrase searches where the first and last
+// characters are double quotes (i.e. it won't correctly handle a mixture
+// of phrase and non-phrase terms or queries containing multiple phrases) -
+// it also doesn't highlight stemmed variations of terms within a phrase
+// because the terminfo returned by mgqueryfilter doesn't currently tell
+// you which term variants belong to which term
+
+// - this function can be forced to treat the querystring like a phrase
+// even if it isn't one by setting the "hl" cgi argument to "2"
+void highlighttext(const text_t &text, cgiargsclass &args, const TermInfo_tarray &terms, 
+		   displayclass &disp, outconvertclass &outconvert, ostream &textout) {
+
+
+  text_t &querystring = args["q"];
+
+  // get the text to start and end a hightlight
+  text_t shl = "<b><u>";
+  text_t ehl = "</u></b>";
+  if (disp.isdefaultmacro(displayclass::defaultpackage, "starthighlight")) {
+    disp.expandstring(displayclass::defaultpackage, "_starthighlight_", shl);
+  }
+  if (disp.isdefaultmacro(displayclass::defaultpackage, "endhighlight")) {
+    disp.expandstring(displayclass::defaultpackage, "_endhighlight_", ehl);
+  }
+
+  // remove leading and trailing whitespace
+  remove_space(querystring);
+
+  // Expand macros before highlighting -- by Jens Wille
+  text_t text_expanded = "";
+  disp.expandstring(text, text_expanded);
+
+  if ((args["hl"] == 2) || ((*(querystring.begin()) == '"') && (*(querystring.end()-1) == '"'))) {
+    highlight_phrases(text_expanded, querystring, terms, shl, ehl, disp, outconvert, textout);
+  } else {
+    highlight_terms(text_expanded, terms, shl, ehl, disp, outconvert, textout);
+  }
+}
+
+void highlight_terms(const text_t &text, const TermInfo_tarray &terms, const text_t &shl,
+		     const text_t &ehl, displayclass &disp, outconvertclass &outconvert,
+		     ostream &textout) {
+
+  text_tmap allterms;
+  text_tmap::const_iterator it;
+
+  // first load all the term variations into a map
+  TermInfo_tarray::const_iterator this_term = terms.begin();
+  TermInfo_tarray::const_iterator last_term = terms.end();
+  while (this_term != last_term) {
+    text_tarray::const_iterator this_var = (*this_term).matchTerms.begin();
+    text_tarray::const_iterator last_var = (*this_term).matchTerms.end();
+    while (this_var != last_var) {
+      allterms[*this_var] = 1;
+      ++this_var;
+    }
+    ++this_term;
+  }
+
+  text_t::const_iterator here = text.begin();
+  text_t::const_iterator end = text.end();
+
+  text_t word, buffer;
+  while (here != end) {
+    if (is_unicode_letdig(*here)) {
+      // not word boundary
+      word.push_back(*here);
+      ++here;
+
+    } else {
+      // found word boundary
+      // add last word if there was one
+      if (!word.empty()) {
+	it = allterms.find(word);
+	if (it != allterms.end()) {
+	  word = shl + word + ehl;
+	}
+	buffer += word;
+        word.clear();
+      }
+
+      if (*here == '<') {
+        // skip over rest of html tag
+	while ((here != end) && (*here != '>')) {
+	  buffer.push_back(*here);
+	  ++here;
+	}
+      }
+
+      buffer.push_back(*here);
+      ++here;
+
+      if (buffer.size() > 1024) {
+	textout << outconvert << disp << buffer;
+	buffer.clear();
+      }
+    }
+  }
+  textout << outconvert << disp << buffer;
+}
+
+void highlight_phrases(const text_t &text, const text_t &querystring, const TermInfo_tarray &terms, 
+		       const text_t &shl, const text_t &ehl, displayclass &disp, 
+		       outconvertclass &outconvert, ostream &textout) {
+
+  text_tmap allterms;
+  text_tarray phrase_terms;
+  text_tmap::const_iterator it;
+
+  get_phrase_terms(querystring, phrase_terms);
+  int phraselen = phrase_terms.size();
+
+  TermInfo_tarray::const_iterator this_term = terms.begin();
+  TermInfo_tarray::const_iterator last_term = terms.end();
+  bool first = true;
+  while (this_term != last_term) {
+    text_tarray::const_iterator this_var = (*this_term).matchTerms.begin();
+    text_tarray::const_iterator last_var = (*this_term).matchTerms.end();
+    while (this_var != last_var) {
+      allterms[*this_var] = 1;
+      ++this_var;
+    }
+    first = false;
+    ++this_term;
+  }
+
+  text_t::const_iterator here = text.begin();
+  text_t::const_iterator end = text.end();
+
+  text_t word, buffer;
+  int phrasecount = 0;
+  while (here != end) {
+    if (is_unicode_letdig(*here)) {
+      // not word boundary
+      word.push_back(*here);
+      ++here;
+
+    } else {
+      // found word boundary
+      // add last word if there was one
+      if (!word.empty()) {
+	it = allterms.find(word);
+	if (it != allterms.end()) {
+	  // found a word that matches somewhere in the phrase
+
+	  text_t lcword = word; lc(lcword);
+	  if (lcword == phrase_terms[phrasecount]) {
+
+	    if (phrasecount == 0) {
+	      // clear the buffer (from here on buffer will contain the phrase
+	      // as it's built up)
+	      textout << outconvert << disp << buffer;
+	      buffer.clear();
+	    }
+	    ++phrasecount;
+	  } else {
+	    phrasecount = 0;
+	  }
+	} else {
+	  phrasecount = 0;
+	}
+	buffer += word;
+        word.clear();
+	
+	if (phrasecount == phraselen) {
+	  // have found entire phrase
+	  textout << outconvert << disp << shl << buffer << ehl;
+	  buffer.clear();
+	  phrasecount = 0;
+	}
+      }
+
+      if (*here == '<') {
+        // skip over rest of html tag
+	while ((here != end) && (*here != '>')) {
+	  buffer.push_back(*here);
+	  ++here;
+	}
+      }
+
+      buffer.push_back(*here);
+      ++here;
+
+      if (buffer.size() > 1024 && phrasecount == 0) {
+	textout << outconvert << disp << buffer;
+	buffer.clear();
+      }
+    }
+  }
+  textout << outconvert << disp << buffer;
+}
+
+void get_phrase_terms (const text_t &querystring, text_tarray &phrase_terms) {
+
+  phrase_terms.erase(phrase_terms.begin(), phrase_terms.end());
+
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+
+  text_t word;
+  while (here != end) {
+    if (is_unicode_letdig(*here)) {
+      // not word boundary
+      word.push_back(*here);
+
+    } else {
+      // found word boundary
+      if (!word.empty()) {
+	lc(word);
+	phrase_terms.push_back(word);
+	word.clear();
+      }
+    }
+    ++here;
+  }
+
+  if (!word.empty()) {
+    lc(word);
+    phrase_terms.push_back(word);
+  }
+}
+
+void remove_space (text_t &qstring) {
+
+  text_t altered_string;
+  text_t space;
+
+  text_t::const_iterator here = qstring.begin();
+  text_t::const_iterator end = qstring.end();
+  while (here != end) {
+    if (is_unicode_space(*here)) {
+      space.push_back(*here);
+    } else {
+      if (!altered_string.empty()) {
+	altered_string += space;
+      }
+      space.clear();
+      altered_string.push_back(*here);
+    }
+    ++here;
+  }
+
+  qstring = altered_string;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/highlighttext.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/highlighttext.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/highlighttext.h	(revision 16561)
@@ -0,0 +1,46 @@
+/**********************************************************************
+ *
+ * highlighttext.h -- 
+ * Copyright (C) 2002   D L Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef HIGHLIGHTTEXT_H
+#define HIGHLIGHTTEXT_H
+
+#include "text_t.h"
+#include "comtypes.h"
+#include "display.h"
+#include "cgiargs.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#else
+#  include <iostream>
+#endif
+
+void highlighttext(const text_t &text, cgiargsclass &args, const TermInfo_tarray &terms, 
+		   displayclass &disp, outconvertclass &outconvert, ostream &textout);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/historydb.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/historydb.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/historydb.cpp	(revision 16561)
@@ -0,0 +1,302 @@
+/**********************************************************************
+ *
+ * historydb.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.
+ *
+ *********************************************************************/
+
+#include "historydb.h"
+#include "fileutil.h"
+#include "cgiutils.h"
+#include "recptproto.h"
+#include "recptprototools.h"
+#include "gdbmclass.h"
+
+#define MAX_RECORDS 20
+#define HIST_SEP ';'
+
+// returns true on success (in which case historyinfo will contain
+// the information for this history)
+bool get_history_info (const text_t &userid, text_tarray &historyinfo, 
+		       const text_t &gsdlhome, ostream &logout) {
+
+  text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
+  
+  bool result = false;
+  // open the history database  
+  gdbmclass historydb;
+  
+  if (historydb.opendatabase(historyfile, DB_READER, 1000, true)) {
+    // get history list
+    text_t historyresult;
+    
+    historydb.getkeydata(userid, historyresult);
+    
+    if (historyresult != "") { //  there are entries, process them
+      
+      splitchar(historyresult.begin(), historyresult.end(), '\n', historyinfo);
+      result = true;
+    }
+    historydb.closedatabase();
+    
+  } else {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii << "couldn't open history database " << historyfile << "\n";
+  }
+  return result;
+}
+
+
+// returns true on success
+// changed to only save 20 records per user, numbers not included
+// only save if there are already entries there, or if display=true
+bool set_history_info (const text_t &userid, const text_t &history, const text_t &gsdlhome, bool display) {
+
+  text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
+  
+  bool result = false;
+  // open the history database  
+  gdbmclass historydb;
+  
+  text_t oldhistoryresult;
+  text_t newhistoryresult;
+  int numentries=0;
+
+  if (!historydb.opendatabase(historyfile, DB_READER, 1000, true)) {
+    // not created yet
+    oldhistoryresult="";
+    if (!display) return true; // dont need to save
+  }
+  else {
+
+    // get history list
+   if (! historydb.getkeydata(userid, oldhistoryresult)) {
+     oldhistoryresult="";
+     if (!display) return true; // dont need to save
+   }
+   historydb.closedatabase();
+  }
+
+  text_tarray entries;
+  
+  if (oldhistoryresult!="") {
+    splitchar(oldhistoryresult.begin(), oldhistoryresult.end(), '\n', entries);
+    numentries = entries.size();
+    if (numentries >= MAX_RECORDS) numentries = MAX_RECORDS-1;
+  }
+  
+  // open for writing
+  if (!historydb.opendatabase(historyfile, DB_WRITER_CREATE, 1000, true)) return false;
+  
+  // add on new linethe new record to the front of the list, then add the
+  // appropriate entries from the old stuff 
+  newhistoryresult += history;
+  newhistoryresult += "\n";
+  for (int i=0; i<numentries;++i) {
+    newhistoryresult +=  entries[i]+"\n";
+  }
+  
+  if (historydb.setkeydata(userid, newhistoryresult)) 
+    result=true; 
+  
+  historydb.closedatabase();
+  return result;
+}
+
+// deletes all a users history
+bool delete_all_history_info (const text_t &userid, const text_t &gsdlhome) {
+
+  text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
+  
+  // open the history database  
+  gdbmclass historydb;
+  
+  if (!historydb.opendatabase(historyfile, DB_WRITER, 1000, true)) return false;
+
+  historydb.deletekey(userid);
+  historydb.closedatabase();
+  return true;
+  
+}
+
+// retrieves the value of one of the arguments
+void parse_saved_args(text_t &args, text_t key, text_t &value)
+{
+  text_t::iterator here = args.begin();
+  text_t::iterator end = args.end();
+  text_t::iterator it;
+  while (here != end) {
+    if(*here==key[0]) {
+      it=findchar(here, end, '=');
+      if (it==end) {
+	value=""; return;
+      }
+      text_t entry = substr(here, it);
+      if (entry==key) {
+	here=it+1;
+      	it=findchar(here, end, HIST_SEP);
+	value = substr(here, it);
+	return;
+      }
+    }
+    ++here;
+  }// while
+}
+
+// retrieves the value of all of the arguments
+void parse_saved_args(text_t &args, infodbclass &info)
+{
+  text_t::iterator here = args.begin();
+  text_t::iterator end = args.end();
+  text_t::iterator it;
+  text_tarray values;
+
+  splitchar(here, end, HIST_SEP, values);
+  
+  text_tarray::iterator start = values.begin();
+  text_tarray::iterator stop = values.end();
+
+  text_t key;
+  text_t value;
+  while(start!=stop) {
+
+    here=(*start).begin();
+    end=(*start).end();
+    it=findchar(here, end, '=');
+    if (it!=end) {
+      key = substr(here, it);
+      value = substr(it+1, end);
+      
+      info[key]=value;
+    }
+    ++start;
+  }
+}
+
+
+void split_saved_query(text_t &query, text_t &numdocs, text_t &cgiargs)
+{
+  text_t::iterator begin = query.begin();
+  text_t::iterator end = query.end();
+  
+  while (*begin >='0'&& *begin <='9') { // get the digits for numdocs
+    numdocs.push_back(*begin);
+    ++begin;
+  }          
+                 
+  if (*begin == '+') {  // get the + if there
+    numdocs.push_back(*begin);
+    ++begin;
+  }
+  if (*begin == ':') { // have the old format - previous bit was record number
+    numdocs.clear();
+    ++begin;
+
+    while(*begin >='0' && *begin <='9') { // get the digits
+      numdocs.push_back(*begin);
+      ++begin;
+    }
+    if (*begin == '+') { // get the + if there
+      numdocs.push_back(*begin);
+      ++begin;
+    }
+  }
+  cgiargs += (substr(begin, end));  // put rest of query into cgiargs
+
+}
+
+void format_user_info (text_t &historyargs, text_t &userinfo,  
+		       cgiargsclass &args,
+		       recptprotolistclass *protos, ostream &logout) 
+{
+  text_tset metadata;
+  userinfo.clear();
+
+  infodbclass argsinfo;
+  parse_saved_args(historyargs, argsinfo);
+  text_t collect = argsinfo["c"];
+  if (collect=="") {
+    userinfo="";
+    return;
+  }
+ 
+  if (collect != args["c"]) {
+    userinfo += collect+", ";
+  }
+  
+  if (argsinfo["h"] != args["h"]) {
+  
+    recptproto *collectproto = protos->getrecptproto(collect,logout);
+    if (collectproto == NULL) {
+      userinfo="";
+      return;
+    }
+    metadata.insert(argsinfo["h"]);
+    FilterResponse_t response;
+    
+    get_info("collection", collect, args["l"], metadata, false, collectproto, response, logout);
+    text_t index = response.docInfo[0].metadata[argsinfo["h"]].values[0];
+    if (!index.empty()) {
+      userinfo += index+", ";
+    }
+  }
+
+  if (argsinfo["b"] != args["b"] || argsinfo["t"] != args["t"]) {
+    if (argsinfo["b"]=="0") { // simple mode
+      if (argsinfo["t"]=="0") {
+	userinfo += " _texthallwords_, ";
+      }
+      else if (argsinfo["t"]=="1"){
+	userinfo += " _texthsomewords_, ";
+      }
+      
+    } 
+    else if (argsinfo["b"] == "1") { // advanced mode
+      if (argsinfo["t"]=="0") {
+	userinfo += " _texthboolean_, ";
+      }
+      else if (argsinfo["t"]=="1") {
+	userinfo += " _texthranked_, ";
+      }
+    }
+  }
+  
+  if (argsinfo["k"] != "" && argsinfo["k"] != args["k"]) {
+    text_t options;
+    // the text translations are the wrong way round
+    if (argsinfo["k"]=="1") {
+      userinfo += " _texthcaseoff_, ";
+    }
+    else if (argsinfo["k"]=="0") {
+      userinfo += " _texthcaseon_, ";
+    }
+  }
+
+  if (argsinfo["s"] != "" && argsinfo["s"] != args["s"]) {
+    if (argsinfo["s"]=="0") {
+      userinfo += " _texthstemoff_, ";
+    }
+    else if (argsinfo["s"]=="1") {
+      userinfo += " _texthstemon_, ";
+    }
+  }
+
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/historydb.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/historydb.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/historydb.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * historydb.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.
+ *
+ *********************************************************************/
+
+
+#ifndef HISTORYDB_H
+#define HISTORYDB_H
+
+#include "text_t.h"
+#include "recptproto.h"
+#include "cgiargs.h"
+
+/*
+struct HistRecord {
+  text_t entry;
+  bool save;
+};
+
+struct ltinttext_t
+{
+  bool operator()(const text_t &t1, const text_t &t2) const
+  { return t1.getint() < t2.getint(); }
+};
+
+
+typedef map<text_t, HistRecord, ltinttext_t> HistRecordmap;
+
+*/
+// returns true on success (in which case historyinfo will contain
+// the information for this history)
+bool get_history_info (const text_t &userid, text_tarray &historyinfo, 
+		       const text_t &gsdlhome, ostream &logout);
+
+// returns true on success
+bool set_history_info (const text_t &userid, const text_t &historyinfo, 
+		       const text_t &gsdlhome, bool display);
+
+// delete all history for one user
+bool delete_all_history_info (const text_t &userid, const text_t &gsdlhome);
+
+// retrieves the value of an arg (key) from a list of cgi args
+// in the form a=q&b=1&...
+void parse_saved_args(text_t &args, text_t key, text_t &value);
+
+// extracts out the components of an entry in the database
+// currently:
+// numdocs cgiargs
+void split_saved_query(text_t &query,  text_t &numdocs, text_t &cgiargs);
+
+// takes the cgiarg string, and outputs it in some form useful
+// to the user
+void format_user_info (text_t &cgiargs, text_t &info, 
+		       cgiargsclass &args,
+		       recptprotolistclass *protos, ostream &logout);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,221 @@
+/**********************************************************************
+ *
+ * hlistbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "hlistbrowserclass.h"
+#include "OIDtools.h"
+#include "recptprototools.h"
+#include "cgiutils.h"
+
+
+hlistbrowserclass::hlistbrowserclass () {
+}
+
+hlistbrowserclass::~hlistbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t hlistbrowserclass::get_browser_name () {
+  return "HList";
+}
+
+browserclass* hlistbrowserclass::clone()
+{
+  return new hlistbrowserclass();
+}
+
+void hlistbrowserclass::load_metadata_defaults (text_tset &metadata) {
+  metadata.insert ("doctype");
+}
+
+text_t hlistbrowserclass::get_default_formatstring () {
+  return "[link][highlight][Title][/highlight][/link]";
+}
+
+// need to change OID to it's child 
+void hlistbrowserclass::processOID (cgiargsclass &args, recptproto *collectproto,
+				    ostream &logout) {
+
+  text_t OID = args["d"];
+  bool is_classify = false;
+  if (OID.empty()) {
+    is_classify = true;
+    OID = args["cl"];
+  }
+
+  FilterResponse_t response;
+  text_tset metadata;
+  metadata.insert ("childtype");
+  text_t classifytype = "Invisible";
+  text_tarray OIDs;
+
+  while (classifytype == "Invisible" || classifytype == "HList") {
+    OIDs.erase (OIDs.begin(), OIDs.end());
+    
+    OIDs.push_back (OID);
+    OIDs.push_back (OID + ".fc");
+
+    get_info (OIDs, args["c"], args["l"], metadata, false, collectproto, response, logout);
+
+    // Avoid infinite loops in strange situations by checking that the OID has changed
+    if (response.docInfo[1].OID == OID) {
+      break;
+    }
+    OID = response.docInfo[1].OID;
+    classifytype = response.docInfo[1].metadata["childtype"].values[0];
+  }
+  if (is_classify) args["cl"] = OID;
+  else args["d"] = OID;
+}
+
+int hlistbrowserclass::output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+					     const text_t& collection, int colnumber, 
+					     format_t *formatlistptr, bool use_table, 
+					     text_tset &metadata, bool &getParents, 
+					     recptproto *collectproto, displayclass &disp, 
+					     outconvertclass &outconvert, ostream &textout, 
+					     ostream &logout)
+{
+  // expanded contents are going to cause some nasty recursions
+  // so we'll only continue if being passed the current section
+  if ((args["gc"] == 1) && (section.OID != args["d"])) return 0;
+
+  // get all siblings
+  FilterResponse_t response;
+  get_children (section.OID + ".pr", args["c"], args["l"], metadata, getParents, 
+		collectproto, response, logout);
+
+  return output_section_group (response, args, collection, colnumber, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
+}
+
+int hlistbrowserclass::output_section_group (FilterResponse_t &response, cgiargsclass &args,
+					     const text_t &collection, int colnumber, 
+					     format_t * formatlistptr, bool use_table, 
+					     text_tset &metadata, bool &getParents,
+					     recptproto * collectproto, displayclass &disp, 
+					     outconvertclass &outconvert, ostream &textout, 
+					     ostream &logout)
+{
+  /* use_table is set to true if the format string starts with <td> */
+  if (use_table || colnumber > 0) {
+    textout << outconvert << "<table><tr><td>";
+    // get tab size
+    text_t tab; int itab;
+    disp.expandstring (displayclass::defaultpackage, "_tabwidth_", tab);
+    itab = tab.getint();
+    if (colnumber > 0) textout << outconvert << disp 
+			       << "<img alt=\"\" src=\"_httpimg_/space.gif\" width=\"" 
+			       << (itab*colnumber) << "\"/>";
+    textout << outconvert << "</td>\n";
+  }
+
+  if (use_table)
+    textout << outconvert << "<table class=\"h_list\"><tr>\n";
+  else
+    textout << outconvert << "<div class=\"h_list\">\n";
+
+  ResultDocInfo_tarray::iterator tsibling = response.docInfo.begin();
+  ResultDocInfo_tarray::iterator esibling = response.docInfo.end();
+
+  text_t icon, link;
+  
+  while (tsibling != esibling)
+  {
+    get_link_icon (*tsibling, args, link, icon);
+
+    bool highlight = false;
+    if ((*tsibling).OID == args["cl"] || is_child_of ((*tsibling).OID, args["cl"]) ||
+	(*tsibling).OID == args["dcn"] || starts_with (args["dcn"], (*tsibling).OID))
+    {
+      link.clear();
+      highlight = true;
+    }
+
+    text_tmap options;
+    options["link"] = link;
+    options["icon"] = icon;
+    if (highlight) options["highlight"] = "1";
+    else options["highlight"] = "0";
+    
+    text_t fmt_str=get_formatted_string (collection, collectproto,
+					  *tsibling, disp, formatlistptr,
+					  options, logout);
+
+    text_t span_class = "h_item";
+    if (highlight) {
+      // the current option
+      span_class += " h_item_current";
+    }
+      
+    textout << outconvert << "<span class=\""<<span_class<<"\">" << disp << fmt_str << "</span>\n";
+    ++tsibling;
+  }
+
+  if (use_table)
+    textout << outconvert << "</tr></table>\n";
+  else
+    textout << outconvert << "</div>\n";
+
+  if (use_table  || colnumber > 0) textout << outconvert << "</tr></table>\n";
+
+  return 0;
+}
+
+
+void hlistbrowserclass::get_link_icon (ResultDocInfo_t &section, cgiargsclass &args, text_t &link, text_t &icon)
+{
+  link = "<a href=\"_httpdocument_";
+
+  text_t &doctype = section.metadata["doctype"].values[0];
+  if (doctype == "classify")
+  {
+    // Dynamic classifier nodes
+    if (args["a"] == "dc")
+    {
+      // The node label is going into the URL, so make it CGI-safe
+      text_t classifier_node_dcn_cgi_safe = cgi_safe_utf8(section.OID);
+      link = "<a href=\"_gwcgi_?c=" + args["c"] + "&amp;a=dc&amp;dcl=" + args["dcl"] + "&amp;dcn=" + classifier_node_dcn_cgi_safe + "\">";
+    }
+    // Static classifier nodes
+    else
+    {
+      link += "&amp;cl=" + section.OID + "\">";
+    }
+  }
+  else
+  {
+    link += "&amp;cl=" + args["cl"] + "&amp;d=" + section.OID;
+
+    // [modification to allow default document detach settings -- kjdon]
+    if (args["xx"] == "1")
+    {
+      // documents should be detached
+      link += "&amp;x=1\" target=\"_blank\">";
+    }
+    else
+    {
+      link += "\">";
+    }
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/hlistbrowserclass.h	(revision 16561)
@@ -0,0 +1,73 @@
+/**********************************************************************
+ *
+ * hlistbrowserclass.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.
+ *
+ *********************************************************************/
+
+
+#ifndef HLISTBROWSERCLASS_H
+#define HLISTBROWSERCLASS_H
+
+#include "browserclass.h"
+#include "receptionist.h"
+
+class hlistbrowserclass : public browserclass {
+protected:
+
+public:
+  hlistbrowserclass ();
+  virtual ~hlistbrowserclass ();
+
+  void set_receptionist(receptionist *therecpt) {recpt=therecpt;}
+
+  // returns the name that specifies the browserclass type
+  text_t get_browser_name ();
+
+  virtual browserclass* clone();
+
+  virtual void load_metadata_defaults (text_tset &metadata);
+
+  virtual text_t get_default_formatstring ();
+  
+  virtual void processOID (cgiargsclass &args, recptproto *collectproto,
+			   ostream &logout);
+
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+protected:
+
+  virtual void get_link_icon (ResultDocInfo_t &section, cgiargsclass &args, text_t &link, text_t &icon);
+
+  receptionist *recpt;
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,73 @@
+/**********************************************************************
+ *
+ * htmlbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_HTML_BROWSER
+
+#include "htmlbrowserclass.h"
+#include <assert.h>
+#include "OIDtools.h"
+
+htmlbrowserclass::htmlbrowserclass () {
+}
+
+htmlbrowserclass::~htmlbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t htmlbrowserclass::get_browser_name () {
+  return "HTML";
+}
+
+browserclass* htmlbrowserclass::clone()
+{
+  return new htmlbrowserclass();
+}
+
+// redirect to html page
+void htmlbrowserclass::processOID (cgiargsclass &args, recptproto *collectproto,
+				   ostream &logout) {
+
+  // should only reach here from within a classification
+  if (!args["d"].empty()) return;
+  if (args["cl"].empty()) return;
+
+  FilterRequest_t request;
+  FilterResponse_t response;
+  comerror_t err;
+  request.filterName = "NullFilter";
+  request.filterResultOptions = FROID;
+  request.docSet.push_back (args["cl"] + ".fc");
+  collectproto->filter (args["c"], request, response, err, logout);
+
+  if (err != noError || response.docInfo[0].OID.empty()) return;
+
+  // redirect to html page
+  args["a"] = "p";
+  args["p"] = "html";
+  args["hp"] = response.docInfo[0].OID;
+}
+
+#endif //GSDL_USE_HTML_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/htmlbrowserclass.h	(revision 16561)
@@ -0,0 +1,52 @@
+/**********************************************************************
+ *
+ * htmlbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_HTML_BROWSER
+
+#ifndef HTMLBROWSERCLASS_H
+#define HTMLBROWSERCLASS_H
+
+#include "browserclass.h"
+
+class htmlbrowserclass : public browserclass {
+protected:
+
+public:
+
+  htmlbrowserclass ();
+  virtual ~htmlbrowserclass ();
+
+  // returns the name that specifies the browserclass type
+  virtual text_t get_browser_name ();
+
+  void processOID (cgiargsclass &args, recptproto *collectproto,
+		   ostream &logout);
+
+  virtual browserclass* clone();
+};
+
+#endif //!HTMLBROWSERCLASS_H
+#endif //GSDL_USE_HTML_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/htmlutils.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/htmlutils.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/htmlutils.cpp	(revision 16561)
@@ -0,0 +1,58 @@
+/**********************************************************************
+ *
+ * htmlutils.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.
+ *
+ *********************************************************************/
+
+#include "htmlutils.h"
+
+text_t html_safe (const text_t &instring) {
+  
+  text_t outstring;
+  text_t::const_iterator here = instring.begin();
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (*here == '"') outstring += "&quot;";
+    else if (*here == '&') outstring += "&amp;";
+    else if (*here == '<') outstring += "&lt;";
+    else if (*here == '>') outstring += "&gt;";
+    else if (*here == '#') outstring += "&#35;";
+    else outstring.push_back(*here);
+    ++here;
+  }
+  return outstring;
+}
+
+
+text_t javascript_safe (const text_t &instring) {
+
+  text_t outstring;
+  text_t::const_iterator here = instring.begin();
+  text_t::const_iterator end = instring.end();
+  while (here != end) {
+    if (*here == '"') outstring += "&quot;";
+    else if (*here == '\'') outstring += "\\'";
+    else outstring.push_back(*here);
+    ++here;
+  }
+  return outstring;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/htmlutils.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/htmlutils.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/htmlutils.h	(revision 16561)
@@ -0,0 +1,36 @@
+/**********************************************************************
+ *
+ * htmlutils.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.
+ *
+ *********************************************************************/
+
+
+#ifndef HTMLUTILS_H
+#define HTMLUTILS_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+
+text_t html_safe (const text_t &instring);
+text_t javascript_safe (const text_t &instring);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,44 @@
+/**********************************************************************
+ *
+ * invbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "invbrowserclass.h"
+#include <assert.h>
+#include "OIDtools.h"
+
+invbrowserclass::invbrowserclass () {
+}
+
+invbrowserclass::~invbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t invbrowserclass::get_browser_name () {
+  return "Invisible";
+}
+
+browserclass* invbrowserclass::clone()
+{
+  return new invbrowserclass();
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/invbrowserclass.h	(revision 16561)
@@ -0,0 +1,50 @@
+/**********************************************************************
+ *
+ * invbrowserclass.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.
+ *
+ *********************************************************************/
+
+
+#ifndef INVBROWSERCLASS_H
+#define INVBROWSERCLASS_H
+
+#include "browserclass.h"
+
+class invbrowserclass : public browserclass {
+protected:
+
+public:
+
+  // this browser class does nothing by default - it should
+  // be overridden for receptionists wanting to display 
+  // 'Invisible' classifications in some way
+
+  invbrowserclass ();
+  virtual ~invbrowserclass ();
+
+  // returns the name that specifies the browserclass type
+  text_t get_browser_name ();
+
+  virtual browserclass* clone();
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/librarymain.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/librarymain.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/librarymain.cpp	(revision 16561)
@@ -0,0 +1,255 @@
+/**********************************************************************
+ *
+ * librarymain.cpp -- 
+ * Copyright (C) 1999  The New Zaland 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#include "receptionist.h"
+#include "cgiwrapper.h"
+#include "fileutil.h"
+#include "nullproto.h"
+// z39.50 stuff - johnmcp
+#if defined(USE_Z3950)
+#include "z3950proto.h"
+#endif
+#include "collectset.h"
+
+#include "action.h"
+#include "authenaction.h"
+#include "browseaction.h"
+#include "collectoraction.h"
+#include "depositoraction.h"
+#include "documentaction.h"
+#include "dynamicclassifieraction.h"
+#include "extlinkaction.h"
+#include "pageaction.h"
+#include "phindaction.h"
+#include "pingaction.h"
+#include "queryaction.h"
+#include "tipaction.h"
+#include "statusaction.h"
+#include "usersaction.h"
+
+#include "browserclass.h"
+#include "vlistbrowserclass.h"
+#include "hlistbrowserclass.h"
+#include "datelistbrowserclass.h"
+#include "invbrowserclass.h"
+#include "pagedbrowserclass.h"
+#include "htmlbrowserclass.h"
+#include "phindbrowserclass.h"
+
+
+int main ()
+{
+  receptionist recpt;
+  nullproto    nproto;
+  collectset  *cservers;
+#if defined(USE_Z3950)
+  z3950proto   zproto;
+#endif
+  text_t       gsdlhome;
+  text_t       collecthome;
+
+  cservers = new collectset(gsdlhome,collecthome);
+
+  // configure the receptionist server list
+  text_tarray collection_list;
+  cservers->getCollectionList(collection_list);
+  text_tarray::iterator collection_iterator = collection_list.begin();
+  while (collection_iterator != collection_list.end())
+  {
+    text_tarray colinfo;
+    colinfo.push_back(*collection_iterator);
+    colinfo.push_back(gsdlhome);
+    colinfo.push_back(collecthome);
+    colinfo.push_back(gsdlhome);
+    recpt.configure("collectinfo", colinfo);
+    collection_iterator++;
+  }
+
+  // set up the null protocol
+  nproto.set_collectset(cservers);
+
+  // add the protocol to the receptionist
+  recpt.add_protocol (&nproto);
+
+  // z39.50 stuff - johnmcp
+#if defined(USE_Z3950)
+  // add the z39.50 server information. Read in the file
+  // etc/recpt/z3950.cfg for list of servers and their databases.
+  text_t z3950cfg = filename_cat (gsdlhome, "etc");
+  z3950cfg = filename_cat (z3950cfg, "packages");
+  z3950cfg = filename_cat (z3950cfg, "z3950.cfg");
+#ifdef USE_FASTCGI
+  // currently can't use z39.50 if fastcgi used
+#warning  "Disabling z39.50 support as fastcgi is in use"
+  if (0) {
+#else
+  if (file_exists(z3950cfg)) {
+#endif
+    text_t z3950err = filename_cat (gsdlhome, "etc", "z3950log.txt");
+    zproto.read_config_file(z3950cfg,z3950err);
+    // only add this protocol if we have any servers configured.
+    if (zproto.getServerCount()>0)
+      recpt.add_protocol (&zproto);
+  }
+#endif
+
+  // the list of actions. Note: these actions will become invalid
+  // at the end of this function. We will clean them.
+
+#ifdef GSDL_USE_TIP_ACTION
+  recpt.add_action (new tipaction());
+#endif
+
+#ifdef GSDL_USE_STATUS_ACTION
+  statusaction *astatusaction = new statusaction();
+  astatusaction->set_receptionist (&recpt);
+  recpt.add_action (astatusaction);
+#endif
+
+  pageaction *apageaction = new pageaction();
+  apageaction->set_receptionist (&recpt);
+  recpt.add_action (apageaction);
+
+#ifdef GSDL_USE_PING_ACTION
+  recpt.add_action (new pingaction());
+#endif
+
+  queryaction *aqueryaction = new queryaction();
+  aqueryaction->set_receptionist (&recpt);
+  recpt.add_action (aqueryaction);
+		
+  documentaction *adocumentaction = new documentaction();
+  adocumentaction->set_receptionist (&recpt);
+  recpt.add_action (adocumentaction);
+
+  text_t userdbfile = filename_cat(gsdlhome, "etc", "users.db");
+  userdbclass *udb = new userdbclass(userdbfile);
+
+  text_t keydbfile = filename_cat(gsdlhome, "etc", "key.db");
+  keydbclass *kdb = new keydbclass(keydbfile);
+
+#ifdef GSDL_USE_USERS_ACTION
+  usersaction *ausersaction = new usersaction();
+  ausersaction->set_userdb(udb);
+  recpt.add_action (ausersaction);
+#endif
+
+#ifdef GSDL_USE_EXTLINK_ACTION
+  extlinkaction *aextlinkaction = new extlinkaction();
+  aextlinkaction->set_receptionist(&recpt);
+  recpt.add_action (aextlinkaction);
+#endif
+		
+#ifdef GSDL_USE_AUTHEN_ACTION
+  authenaction *aauthenaction = new authenaction();
+  aauthenaction->set_userdb(udb);
+  aauthenaction->set_keydb(kdb);
+  aauthenaction->set_receptionist(&recpt);
+  recpt.add_action (aauthenaction);
+#endif
+
+#ifdef GSDL_USE_COLLECTOR_ACTION
+  collectoraction *acollectoraction = new collectoraction();
+  acollectoraction->set_receptionist (&recpt);
+  recpt.add_action(acollectoraction);
+#endif
+
+#ifdef GSDL_USE_DEPOSITOR_ACTION
+  depositoraction *adepositoraction = new depositoraction();
+  adepositoraction->set_receptionist (&recpt);
+  recpt.add_action(adepositoraction);
+#endif
+
+#ifdef GSDL_USE_BROWSE_ACTION
+  browseaction *abrowseaction = new browseaction();
+  abrowseaction->set_receptionist (&recpt);
+  recpt.add_action(abrowseaction);
+#endif
+
+#ifdef GSDL_USE_PHIND_ACTION
+  recpt.add_action(new phindaction());
+#endif
+
+#ifdef GSDL_USE_GTI_ACTION
+  gtiaction *agtiaction = new gtiaction();
+  agtiaction->set_receptionist(&recpt);
+  recpt.add_action(agtiaction);
+#endif
+
+  dynamicclassifieraction *adynamicclassifieraction = new dynamicclassifieraction();
+  adynamicclassifieraction->set_receptionist(&recpt);
+  recpt.add_action(adynamicclassifieraction);  
+
+  // list of browsers
+  vlistbrowserclass avlistbrowserclass;
+  avlistbrowserclass.set_receptionist(&recpt);
+  recpt.add_browser (&avlistbrowserclass);
+  recpt.setdefaultbrowser ("VList");
+
+  hlistbrowserclass ahlistbrowserclass;
+  ahlistbrowserclass.set_receptionist(&recpt);
+  recpt.add_browser (&ahlistbrowserclass);
+
+#ifdef GSDL_USE_DATELIST_BROWSER
+  datelistbrowserclass adatelistbrowserclass;
+  recpt.add_browser (&adatelistbrowserclass);
+#endif
+
+  invbrowserclass ainvbrowserclass;
+  recpt.add_browser (&ainvbrowserclass);
+
+#ifdef GSDL_USE_PAGED_BROWSER
+  pagedbrowserclass apagedbrowserclass;
+  recpt.add_browser (&apagedbrowserclass);
+#endif
+
+#ifdef GSDL_USE_HTML_BROWSER
+  htmlbrowserclass ahtmlbrowserclass;
+  recpt.add_browser (&ahtmlbrowserclass);
+#endif
+
+#ifdef GSDL_USE_PHIND_BROWSER
+  phindbrowserclass aphindbrowserclass;
+  recpt.add_browser (&aphindbrowserclass);
+#endif
+
+  cgiwrapper (recpt, "");
+  delete cservers;
+  delete udb;
+  delete kdb;
+
+  // clean up the actions
+  actionmapclass::iterator thisAction = recpt.get_actionmap_ptr()->begin();
+  actionmapclass::iterator endAction = recpt.get_actionmap_ptr()->begin();
+  while (thisAction != endAction) {
+    delete thisAction->second.a; thisAction->second.a = NULL;
+    ++thisAction;
+  }
+  thisAction = recpt.get_actionmap_ptr()->begin();
+  recpt.get_actionmap_ptr()->erase(thisAction, endAction);
+
+  return 0;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/maincfg.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/maincfg.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/maincfg.cpp	(revision 16561)
@@ -0,0 +1,91 @@
+/**********************************************************************
+ *
+ * maincfg.cpp -- 
+ * Copyright (C) 2008  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.
+ *
+ *********************************************************************/
+
+#include "maincfg.h"
+#include "fileutil.h"
+
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace/std/iostream>
+#  include <ospace/std/fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#endif
+
+
+bool main_cfg_read (receptionist &recpt, const text_t &gsdlhome,
+		    const text_t &collection) 
+{
+
+  cerr << "Warning: Using version of main_cfg_read() that does not support 'collecthome'" << endl;
+
+  text_t collecthome = filename_cat(gsdlhome,"collect");
+
+  cerr << "Setting collecthome to be " << collecthome << endl;
+
+
+  return main_cfg_read(recpt,gsdlhome,collecthome,collection);
+}
+
+
+// main_cfg_read reads both main.cfg and collect.cfg. It attempts
+// to read main.cfg first so values in collect.cfg override those
+// set earlier by main.cfg
+bool main_cfg_read (receptionist &recpt, const text_t &gsdlhome,
+		    const text_t& collecthome, const text_t &collection) {
+
+  // read in the main configuration file
+  bool rv = false;
+
+  text_t key, filename;
+  text_tarray cfgline;
+  filename = filename_cat (gsdlhome, "etc", "main.cfg");
+  if (!collection.empty() && file_exists(filename)) {
+    rv = recpt.read_configfile(filename);
+  } else {
+    if (file_exists (filename)) {
+      rv = recpt.read_configfile(filename);
+    }
+  }
+
+  // Look for collect.cfg in <collecthome>/collection-name/etc directory 
+  // (if this is for a particular collection), and then GSDLHOME/etc.
+  if (!collection.empty()) {
+    filename = filename_cat (collecthome, collection, "etc", "collect.cfg");
+    if (!file_exists (filename)) {
+      filename = filename_cat (gsdlhome, "etc", "collect.cfg");
+      if (!file_exists (filename)) filename.clear();
+    }
+
+    if (!filename.empty()) {
+      rv |= recpt.read_configfile(filename);
+    }
+  }
+  return rv;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/maincfg.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/maincfg.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/maincfg.h	(revision 16561)
@@ -0,0 +1,48 @@
+/**********************************************************************
+ *
+ * maincfg.h -- 
+ * Copyright (C) 2008  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.
+ *
+ *********************************************************************/
+
+
+#ifndef MAINCFG_H
+#define MAINCFG_H
+
+
+#include "text_t.h"
+#include "receptionist.h"
+
+
+// read main.cfg for the actions and httpprefix
+void main_cfg_read (const text_t& collection, const text_t &gsdlhome, text_t &httpprefix, text_tset& actions, text_tset& browsers);
+
+// main_cfg_read reads either collect.cfg or main.cfg and returning 
+// true on success. It attempts to read collect.cfg first if a
+// collection is specified and then it tries to read in main.cfg
+bool main_cfg_read (receptionist &recpt, const text_t &gsdlhome,
+		    const text_t& collecthome, const text_t &collection);
+
+bool main_cfg_read (receptionist &recpt, const text_t &gsdlhome,
+		    const text_t &collection);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/pageaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pageaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pageaction.cpp	(revision 16561)
@@ -0,0 +1,1037 @@
+/**********************************************************************
+ *
+ * pageaction.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.
+ *
+ *********************************************************************/
+
+// Changelog:
+
+// 11th July 2003:
+
+//   Added 3 functions to deal with the collection style
+//   A deciding function and then 2 functions that actually
+//   do the work for us, either images or pulldown.
+
+
+#include "pageaction.h"
+#include "receptionist.h"
+#include "recptprototools.h"
+#include "fileutil.h"
+#include "gsdltools.h"
+#include "querytools.h"
+#include <time.h>
+
+pageaction::pageaction () {
+
+  status_disabled = true;
+  collector_disabled = true;
+  depositor_disabled = true; 
+  translator_disabled = true;
+  gliapplet_disabled = true;
+  recpt = NULL;
+
+  // this action uses cgi variables "a", "p", and "hp"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "p";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "p";
+  arg_ainfo.longname = "page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "home";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "hp";
+  arg_ainfo.longname = "html page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "bp";
+  arg_ainfo.longname = "set preferences button";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // the "u" argument will disable the search facility, remove links to the
+  // home and preferences pages, and disable the DocumentButton buttons
+  // (for use when generating static html versions of collections)
+  arg_ainfo.shortname = "u";
+  arg_ainfo.longname = "static page";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "book";
+  arg_ainfo.longname = "book switch";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "off";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+pageaction::~pageaction () {
+}
+
+bool pageaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &args, 
+				recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+
+  if (args["p"] == "preferences" && !args["bp"].empty()) {
+    if (args["hd"] != "0") args["hd"] = args["hdn"];
+  }
+
+  return true;
+}
+
+void pageaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				   response_t &response,text_t &response_data, 
+				   ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+// This function helps decide whether we want a
+// images or pulldown style collection display
+// We refer to the macro files for the options
+// for images or pulldown menu and use that to
+// switch to the appropriate function to do the job
+
+// Aly Dharshi
+// 11th July 2003
+
+void pageaction::homepagestyle(displayclass &disp, recptprotolistclass *protos,
+			       cgiargsclass &args, ostream &logout)
+{
+
+  const recptconf &configinfo = recpt->get_configinfo();
+
+  if (configinfo.HomePageType == "images") {
+     home_images(disp, protos, args, configinfo, logout);
+
+  } else
+     {
+	home_pulldown(disp, protos, args, configinfo, logout);
+     }
+}
+
+// This function allows for the Greenstone
+// collection to be displayed in a pull down
+// menu similar to that in documentaction.cpp
+
+// Aly Dharshi
+// 11th July 2003
+
+void pageaction::home_pulldown(displayclass &disp, recptprotolistclass *protos, 
+			       cgiargsclass &args, const recptconf &configinfo,
+			       ostream &logout) {
+
+   text_t homeextra = "<form method=\"get\" name=\"browse\">\n";
+   homeextra += "<select name=\"collections\" onChange=\"location.href=";
+   homeextra += "document.browse.collections.options[document.browse.collections.selectedIndex].value\">\n";
+   homeextra += "<option value=\"\">_textdescrselcol_</option>\n";
+   
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+
+      text_tarray collist;
+      comerror_t err;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+
+	int count = 0;
+	bool first = true;
+	while (collist_here != collist_end) {
+
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  
+	  if (cinfo != NULL) {
+	    if (cinfo->isPublic && (cinfo->buildDate > 0)) {
+
+	      text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	      if (collectionname.empty()) {
+		collectionname = *collist_here;
+	      }
+	      
+	      comerror_t err;
+	      text_t optsite = g_EmptyText;
+	      text_t site_name = (*rprotolist_here).p->get_site_name (err);
+	      if (!site_name.empty()) { optsite = "site="+site_name+"&amp;"; }
+	      
+	      text_t link = "_gwcgi_?"+optsite+"a=p&amp;p=about&amp;c=" + *collist_here;
+	      link += "&amp;l=" + args["l"] + "&amp;w=" + args["w"];
+
+	      // We are "dynamically" overriding so to speak the
+	      // link to the collection aka receptionist.
+	      
+	      if (!cinfo->receptionist.empty()) 
+		 link = cinfo->receptionist;
+	      
+	      
+	      homeextra += "<option value=\"" + link + "\"";
+	      homeextra += ">" + collectionname + "</option>\n";
+	      	    
+	    }
+
+	    ++collist_here;
+	    }
+	    
+	  }
+      }
+	++rprotolist_here;
+    }
+    
+    }
+  homeextra += "</select>\n";
+  homeextra += "</form>\n";
+  
+  disp.setmacro ("homeextra", "home", homeextra); 
+}
+  
+// originally called set_homeextra_macro
+// this function displays the Greenstone
+// main page as usual with the graphics for
+// the collections.
+
+// Aly Dharshi
+// 11th July 2003
+
+void pageaction::home_images(displayclass &disp, recptprotolistclass *protos, 
+			     cgiargsclass &args, const recptconf &configinfo,
+			     ostream &logout) {
+  
+  text_t homeextra = "<table class=\"collections\">\n";
+  bool found_valid_col = false;
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+
+      text_tarray collist;
+      comerror_t err;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+
+	int count = 0;
+	while (collist_here != collist_end) {
+
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+
+	  text_t arg_g = args["g"];
+	  text_t colname_dir = *collist_here;
+	  
+	  if (arg_g.empty()) {
+	    // normal top-level home page
+	    text_t::const_iterator begin = colname_dir.begin();
+	    text_t::const_iterator end = colname_dir.end();
+
+	    if (findchar(begin,end,'/')!=end) {
+	      // no g argument, so generating top level home page
+	      // => if colname_dir has "/" in collection name, filter it out
+
+	      ++collist_here;
+	      continue;
+	    }
+	  }
+	  else {
+	    // show only collections in the named group
+
+	    
+	    if (colname_dir == arg_g) {
+	      // display about this collect text
+
+	      text_t collectionextra = cinfo->get_collectionmeta("collectionextra", args["l"]);
+	      if (!collectionextra.empty()) {
+		// prepend text on at start
+		homeextra = "<p>" + collectionextra + "</p>" + homeextra;
+	      }
+
+	      ++collist_here;
+	      continue;
+	    }
+
+
+	    // => filter out anything that does not start with the group prefix
+	    
+	    if (!starts_with(colname_dir,arg_g + "/")) {
+	      ++collist_here;
+	      continue;
+	    }
+	  }
+
+
+	  if (cinfo != NULL) {
+	    if (cinfo->isPublic && ((cinfo->buildDate > 0)) || (cinfo->isCollectGroup)) {
+
+	      found_valid_col = true;
+	      text_t collectionname = *collist_here;
+	      text_t alt = cinfo->get_collectionmeta("collectionname", args["l"]);
+	      if (alt.empty()) {
+		alt = collectionname;
+	      }
+
+
+	      comerror_t err;
+	      text_t optsite = g_EmptyText;
+	      text_t site_name = (*rprotolist_here).p->get_site_name (err);
+	      if (!site_name.empty()) { optsite = "site="+site_name+"&amp;"; }
+
+	      text_t link;
+
+	      if (cinfo->isCollectGroup) {
+		link = "<a class=\"collectiontitle\" href=\"_gwcgi_?"+optsite+"a=p&amp;p=home&amp;g=" + *collist_here;
+		link += "&amp;l=" + args["l"] + "&amp;w=" + args["w"] + "\">";
+	      }
+	      else {
+		link = "<a class=\"collectiontitle\"href=\"_gwcgi_?"+optsite+"a=p&amp;p=about&amp;c=" + *collist_here;
+		link += "&amp;l=" + args["l"] + "&amp;w=" + args["w"] + "\">";
+	      }
+	      
+	      if (!cinfo->receptionist.empty()) 
+		link = "<a class=\"collectiontitle\"href=\"" + cinfo->receptionist + "\">";
+	      
+	      
+	      // url to image: try iconcollectionsmall, then iconcollection
+	      text_t iconurl = cinfo->get_collectionmeta("iconcollectionsmall", args["l"]);
+	      if (iconurl.empty()) {
+		iconurl = cinfo->get_collectionmeta("iconcollection", args["l"]);
+	      }
+	      
+	      if (!iconurl.empty()) {
+		
+		// check to see URL is local to colserver
+		text_t::iterator iconurl_head = iconurl.begin();
+		text_t iconhead = substr(iconurl_head,iconurl_head+16);
+		if (iconhead=="_httpcollection_") {
+		  
+		  // local and using _httpcollection_
+		  text_t icontail = substr(iconurl_head+16,iconurl.end());
+		  iconurl = "http://" + cinfo->httpdomain 
+		    + cinfo->httpprefix + "/collect/" 
+		    + *collist_here + "/" + icontail;
+		}
+		else if (iconurl[0]=='/') {
+		  
+		  // local but with full path
+		  iconurl = "http://" + cinfo->httpdomain + iconurl;
+		}
+		
+		collectionname 
+		  = link + "<img width=\"150\" src=\"" + iconurl + "\" alt=\"" + alt + "\">" + "</a>";
+	      } else {		
+
+		collectionname = "<div class=\"collecttitle\" style=\"width: 150px; border: 1px solid; \"><p class=\"collectiontitle\" style=\"font-size: 18px; width: 150px; white-space:normal; text-align: left;\">";
+		collectionname += alt + "</p></div>";
+
+		collectionname = link + collectionname + "</a>";
+
+	      }
+	      
+	      if (count%configinfo.HomePageCols == 0)
+		homeextra += "<tr valign=\"top\">\n";	      
+
+	      homeextra += "<td align=\"center\">" + collectionname + "</td>\n";
+	      
+	      ++count;
+	      if (count%configinfo.HomePageCols == 0)
+		homeextra += "</tr>\n";
+	    }
+	  }
+	  ++collist_here;
+	}
+
+	// Finish off the last row, if necessary
+	if (count%configinfo.HomePageCols != 0) {
+	  while (count%configinfo.HomePageCols != 0) {
+	    homeextra += "<td></td>\n";
+	    ++count;
+	  }
+	  homeextra += "</tr>\n";
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  if (!found_valid_col) {
+    homeextra += "<tr><td>_textnocollections_</td></tr>\n";
+  }
+  homeextra += "</table>\n";
+  disp.setmacro ("homeextra", "home", homeextra); 
+}
+
+void pageaction::set_collectionlist_macro (displayclass &disp, 
+					   recptprotolistclass *protos, 
+					   cgiargsclass &args, 
+					   ostream &logout) {
+
+  text_t collectionlist;
+  int count = 0;
+  
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+
+      text_tarray collist;
+      comerror_t err;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+
+	while (collist_here != collist_end) {
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  
+	  if (cinfo != NULL) {
+	    if (cinfo->isPublic && (cinfo->buildDate > 0)) {
+
+	      ++count;
+
+	      text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	      if (collectionname.empty()) {
+		collectionname = *collist_here;
+	      }
+	      
+	      comerror_t err;
+	      text_t optsite = g_EmptyText;
+	      text_t site_name = (*rprotolist_here).p->get_site_name (err);
+	      if (!site_name.empty()) { optsite = "site="+site_name+"&amp;"; }
+
+	      text_t link = "<a href=\"_gwcgi_?"+optsite+"a=p&p=about&c=" + *collist_here + "\">";
+
+	      if (!cinfo->receptionist.empty()) 
+		link = "<a href=\"" + cinfo->receptionist + "\">";
+	      
+	     collectionlist += "<li>" + link + collectionname + "</a>\n";
+	    }
+	  }
+	  ++collist_here;
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  if (count == 1) {
+    collectionlist = "<p>_text1coll_\n<ul>" +
+      collectionlist + "</ul>\n";
+  } else if (count > 1) {
+    collectionlist = "<p>_textmorecolls_(" + text_t(count) + 
+      ")\n<ul>" + collectionlist + "</ul>\n";
+  }
+
+  disp.setmacro ("collectionlist", "homehelp", collectionlist); 
+}
+
+void pageaction::set_documentation_macro (displayclass &disp) {
+
+  text_t documentation;
+  text_t docsdir = filename_cat(gsdlhome, "docs");
+  
+  if (file_exists(filename_cat(docsdir, "User.pdf"))) {
+    documentation += "<tr valign=middle><td><a href=\"_httpdocs_/User.pdf\">_iconpdf_"
+      "</a></td><td>_textuserguide_</td></tr>";
+  }
+  
+  if (file_exists(filename_cat(docsdir, "Install.pdf"))) {
+    documentation += "<tr valign=middle><td><a href=\"_httpdocs_/Install.pdf\">_iconpdf_"
+      "</a></td><td>_textinstallerguide_</td></tr>";
+  }
+  
+  if (file_exists(filename_cat(docsdir, "Develop.pdf"))) {
+    documentation += "<tr valign=middle><td><a href=\"_httpdocs_/Develop.pdf\">_iconpdf_"
+      "</a></td><td>_textdeveloperguide_</td></tr>";
+  }
+  
+  if (file_exists(filename_cat(docsdir, "Paper.pdf"))) {
+    documentation += "<tr valign=middle><td><a href=\"_httpdocs_/Paper.pdf\">_iconpdf_"
+      "</a></td><td>_textpaperguide_</td></tr>";
+  }
+  
+  if (file_exists(filename_cat(docsdir, "Organize.pdf"))) {
+    documentation += "<tr valign=middle><td><a href=\"_httpdocs_/Organize.pdf\">_iconpdf_"
+      "</a></td><td>_textorganizerguide_</td></tr>";
+  }
+
+  if (!documentation.empty()) {
+    disp.setmacro("documentation", "docs", "<p>\n<table border=0>\n" + documentation + "\n</table>\n");
+  }
+}
+
+void pageaction::set_macro_to_file_contents (displayclass &disp, const text_t &macroname,
+					     const text_t &packagename, const text_t &filename) {
+
+  text_t filecontent;
+  char *filenamec = filename.getcstr();
+  ifstream file_in (filenamec);
+  delete []filenamec;
+  if (file_in) {
+    char c;
+    file_in.get(c);
+    while (!file_in.eof ()) {
+      if (c == '\n') filecontent += "<br>";
+      filecontent.push_back(c);
+      file_in.get(c);
+    }
+    file_in.close();
+  }
+  disp.setmacro (macroname, packagename, dm_safe(filecontent));
+}
+
+void pageaction::set_language_encoding_macros(displayclass &disp, cgiargsclass &args,
+					      recptprotolistclass *protos, ColInfoResponse_t *cinfo,
+					      ostream &logout) {
+  // _languageoption_
+  // Create the "interface language" selection box for the preferences
+  // pages.  You can use something like "format PreferenceLanguages
+  // en|fr|zn" from within a collect.cfg file to use only a subset of
+  // the available languages for any given collection (for
+  // collection-specific preferences pages). This facility is kind of
+  // ugly though and should be replaced by something better when the
+  // configuration files are tidied up (as should all the other
+  // "format Preference..." options).
+  text_t &arg_l = args["l"];
+  const recptconf &configinfo = recpt->get_configinfo();
+  // put languages in another map to sort them by longname
+  text_tmap languages;
+  languageinfo_tmap::const_iterator thislang = configinfo.languages.begin();
+  languageinfo_tmap::const_iterator endlang = configinfo.languages.end();
+  while (thislang != endlang) {
+    languages[(*thislang).second.longname] = (*thislang).first;
+    ++thislang;
+  }
+  text_tmap::iterator tlang = languages.begin();
+  text_tmap::iterator elang = languages.end();
+
+  text_t languageoption;
+  bool collection_specific = false;
+
+  if (cinfo != NULL) {
+    text_tmap::const_iterator it = cinfo->format.find ("PreferenceLanguages");
+    if ((it != cinfo->format.end()) && (!(*it).second.empty())) {
+      collection_specific = true;
+      text_tset pref_langs;
+      splitchar ((*it).second.begin(), (*it).second.end(), '|', pref_langs);
+      if (pref_langs.size() > 1) {
+	while (tlang != elang) {
+	  if (pref_langs.find((*tlang).second) != pref_langs.end()) {
+	    languageoption += "<option value=\"" + (*tlang).second + "\"";
+	    if ((*tlang).second == arg_l) languageoption += " selected";
+	    languageoption += ">" + (*tlang).first + "</option>\n";
+	  }
+	  ++tlang;
+	}
+      }
+    }
+  }
+
+  if (!collection_specific) {
+    while (tlang != elang) {
+      languageoption += "<option value=\"" + (*tlang).second + "\"";
+      if ((*tlang).second == arg_l) languageoption += " selected";
+      languageoption += ">" + (*tlang).first + "</option>\n";
+      ++tlang;
+    }
+  }
+
+  if (!languageoption.empty()) {
+    languageoption = "<select name=\"l\" onChange=\"updatel();\">\n" + languageoption;
+    languageoption += "</select>\n";
+    disp.setmacro ("languageoption", args["p"], languageoption);
+  }
+
+  // _encodingoption_
+  // create the "encoding" selection box for the preferences page
+  if (configinfo.encodings.size() > 1) {
+    text_t &arg_w = args["w"];
+    text_t encodingoption;
+    text_tmap::const_iterator thisenc = configinfo.encodings.begin();
+    text_tmap::const_iterator endenc = configinfo.encodings.end();
+    while (thisenc != endenc) {
+      encodingoption += "<option value=\"" + (*thisenc).second + "\"";
+      if ((*thisenc).second == arg_w) encodingoption += " selected";
+      encodingoption += ">" + (*thisenc).first + "</option>\n";
+      ++thisenc;
+    }
+
+    encodingoption = "<select name=\"w\" onChange=\"updatew();\">\n" + encodingoption;
+    encodingoption += "</select>\n";
+    disp.setmacro ("encodingoption", args["p"], encodingoption);
+  } else if (configinfo.encodings.size() == 1) {
+    text_t encodingoption;
+    text_tmap::const_iterator thisenc = configinfo.encodings.begin();
+    encodingoption = (*thisenc).first;
+    disp.setmacro ("encodingoption", args["p"], encodingoption);
+  } else { // size == 0. shouldn't really happen, but allow it
+    disp.setmacro ("encodingoption", args["p"], "None");
+  }
+
+}
+
+
+void pageaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					 recptprotolistclass *protos, ostream &logout) {
+
+  // define_internal_macros sets the following macros:
+
+  // _numdocs_             the number of documents in the collection
+
+  // _builddate_           the date last built
+
+  // if page is "home"
+  // _homeextra_           this is the list of available collections and collection info
+  //                       to be displayed on the home page
+
+
+  // if page is "preferences"
+  // _collectionoption_    collections to search/browse (if cross-collection-searching is on)
+
+  // _htmloptions_         set to _htmloptionson_ if DocumentUseHTML is set
+
+  // _PreferencesDocsFromWeb_ set to 1 if corresponding format option is set
+
+
+  // if page is "preferences" or "homepref"
+  // _languageoption_      interface languages to select from (dependant on PreferenceLanguages)
+
+  // _encodingoption_      encodings to select from
+
+
+  // if page is "about"
+  // _textsubcollections_  the text on which subcollections make up the collection (if 
+  //                       cross-collection searching is being used
+
+  // _textbrowseoptions_   the 'how to find information' text in the about and help pages
+
+  // _numbrowseoptions_    the number of browsing options 
+
+  // _prefschanged_        will be set to _textprefschanged_ if the "set preferences" button
+  //                       was pressed
+
+  // _aboutqueryform_      will be set to "" if the collection isn't searchable
+
+  // if page is "help"
+  // _textbrowseoptions_   the 'how to find information' text in the about and help pages
+
+  // _numbrowseoptions_    the number of browsing options 
+
+  // _topicreadingdocs_    this section of the help text differs depending on what type of 
+  // _textreadingdocs_     collection it is (e.g. html collection, bibliographic collection etc.)
+  // _texthelpreadingdocs_ 
+
+  // if page is "home" or "homehelp"
+  // _textgocollector_     set to "" if collector is disabled in main.cfg
+  // _textgodepositor_     set to "" if depositor is disabled in main.cfg
+  // _textgoadmin_         set to "" if status is disabled in main.cfg
+  // _textgotranslator_    set to "" if translator is disabled in main.cfg
+  // _textgogliapplet_     set to "" if gliapplet is disabled in main.cfg
+
+
+  // if page is "homehelp"
+  // _collectionlist_      list of available collections to be displayed on the homehelp page
+
+  
+  // if page is "docs"
+  // _documentation_       links to PDF documents if they're available
+
+
+  // if page is "bsummary"
+  // _importlog_           set to contents of collections import.log file
+  // _faillog_             set to contents of collections fail.log file
+  //_buildlog_             set to contents of collections build.log file
+  
+  if (recpt == NULL) {
+    logout << "ERROR (pageaction::define_internal_macros): This action does not contain\n"
+	   << "      information about any receptionists. The method set_receptionist was\n"
+	   << "      probably not called from the module which instantiated this action.\n";
+    return;
+  }
+  // _versionnnum_   greenstone version number (hard-coded)
+  disp.setmacro("versionnum", displayclass::defaultpackage, GSDL_VERSION);
+
+  text_t &arg_p = args["p"];
+  text_t &arg_c = args["c"];
+  ColInfoResponse_t *cinfo = NULL;
+
+  recptproto* collectproto = protos->getrecptproto (arg_c, logout);
+  if (collectproto != NULL) {
+    cinfo = recpt->get_collectinfo_ptr (collectproto, arg_c, logout);
+    
+    //set the tidyoption
+    if (cinfo->useBook == false)
+    {
+        disp.setmacro ("tidyoption", displayclass::defaultpackage, "untidy");
+    }
+    else
+    {
+        disp.setmacro ("tidyoption", displayclass::defaultpackage, "tidy");
+    }
+    
+    disp.setmacro ("numdocs", displayclass::defaultpackage, cinfo->numDocs);
+    disp.setmacro ("numsections", displayclass::defaultpackage, cinfo->numSections);
+    disp.setmacro ("numwords", displayclass::defaultpackage, cinfo->numWords);
+    unsigned long current_time = time(NULL);
+    unsigned long builddate = (current_time - cinfo->buildDate) / 86400;
+    disp.setmacro ("builddate", displayclass::defaultpackage, builddate);
+    
+    text_t numbytes;
+    if ((cinfo->numBytes/(1024*1024)) > 1) {
+      numbytes = (text_t)(cinfo->numBytes/(1024*1024)) + " Mb";
+    } else if ((cinfo->numBytes/1024) > 1) {
+      numbytes = (text_t)(cinfo->numBytes/1024) + " kb";
+    } else {
+      numbytes = (text_t)cinfo->numBytes + " bytes";
+    }
+    disp.setmacro("numbytes", displayclass::defaultpackage, numbytes);
+
+    // set up ct, qt, qto
+    set_query_type_args(cinfo, args);
+    // set up ks, ss, afs
+    set_stem_index_args(cinfo, args);
+  
+  }
+  
+  //setting _queryformcontent_ so that the query interface is consistent.
+  //also adding usability button if necessary
+  if (arg_p == "about") {
+    if (cinfo == NULL) {
+      disp.setmacro("cvariable", displayclass::defaultpackage, arg_c);
+      disp.setmacro("content", arg_p, "<p>_textbadcollection_<p>");
+      return;
+    }
+    else {
+
+      text_tmap::iterator check = cinfo->format.find("QueryInterface");
+      if(check != cinfo->format.end()){
+	if((*check).second=="DateSearch"){
+	  text_t current = "_datesearch_";
+	  disp.setmacro("optdatesearch","query",current);
+	}
+      }
+      check = cinfo->format.find("Usability");
+      if(check != cinfo->format.end()){
+	disp.setmacro("usability", displayclass::defaultpackage, "_usablink_");
+	disp.setmacro("usabinterface", displayclass::defaultpackage, ("_usab"+(*check).second+"_"));
+	disp.setmacro("usabilityscript", displayclass::defaultpackage, "_usabshowscript_");
+      }
+    }
+  }
+  
+  
+  if (arg_p == "home" || arg_p == "homehelp") {
+    if (status_disabled) disp.setmacro ("textgoadmin", "home", "");
+    if (collector_disabled) disp.setmacro ("textgocollector", "home", "");
+    if (depositor_disabled) disp.setmacro ("textgodepositor", "home", "");
+    if (translator_disabled) disp.setmacro ("textgotranslator", "home", "");
+
+    if (arg_p == "home") {
+      homepagestyle (disp, protos, args, logout);
+    } else if (arg_p == "homehelp") {
+      set_collectionlist_macro (disp, protos, args, logout);
+    }
+
+  } else if (arg_p == "gli") {
+    if (gliapplet_disabled) disp.setmacro ("gliapplet", "gli", "");
+
+  } else if (arg_p == "homepref") {
+  
+    // set _languageoption_ and _encodingoption_
+    set_language_encoding_macros(disp, args, protos, cinfo, logout);
+
+  } else if (arg_p == "preferences") {
+  
+    if (cinfo == NULL) {
+      disp.setmacro("cvariable", displayclass::defaultpackage, arg_c);
+      disp.setmacro("content", arg_p, "<p>_textbadcollection_<p>");
+      return;
+    }
+
+    if (collectproto == NULL) {return;}
+    
+    // set _languageoption_ and _encodingoption_
+    set_language_encoding_macros(disp, args, protos, cinfo, logout);
+
+    // _collectionoption_
+    if ((args["ccs"] == "1") && (cinfo->ccsCols.size() > 1)) {
+      text_t collectionoption = "_textcollectionoption_";
+      text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
+      text_tarray::const_iterator col_end = cinfo->ccsCols.end();
+      int count = 0;
+      while (col_here != col_end) {
+	text_t colname;
+	if (*col_here == arg_c) {
+	    colname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	} else {
+	  ColInfoResponse_t *this_cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
+	  if (this_cinfo == NULL) {++col_here; continue;}
+	  colname = this_cinfo->get_collectionmeta("collectionname", args["l"]);
+	}
+	if (colname.empty()) {
+	  colname = *col_here;
+	}
+	++count;
+	collectionoption += "<input type=checkbox name=\"cc\" value=\"" + 
+	  *col_here + "\" onClick=\"updatecc(\'" + *col_here + "\');\"> " +
+	  colname + "<br>\n";
+	++col_here;
+      }
+      
+      if (count > 1)
+	disp.setmacro ("collectionoption", "preferences", collectionoption);
+    }
+
+    // _htmloptions_
+    
+    text_tmap::const_iterator it = cinfo->format.find ("DocumentUseHTML");
+    if ((it != cinfo->format.end()) && ((*it).second == "true")) {
+      disp.setmacro ("htmloptions", "preferences", "_htmloptionson_");
+
+
+      // _PreferenceDocsFromWeb_
+
+      it = cinfo->format.find ("PreferenceDocsFromWeb");
+      if ((it == cinfo->format.end()) || ((*it).second == "true"))
+	disp.setmacro ("PreferenceDocsFromWeb", "preferences", "1");
+    }
+
+    // _prefschanged_
+    if (!args["bp"].empty()) {
+      disp.setmacro ("prefschanged", "preferences", "_textprefschanged_");
+    }
+    
+  } else if (arg_p == "about" || arg_p == "help") {
+    if (collectproto == NULL) return;
+
+    comerror_t err;
+    bool has_search_button = true;
+    collectproto->is_searchable(args["c"], has_search_button, err, logout);
+    if (err != noError) has_search_button = true;
+    
+    // _textbrowseoptions_ and _numbrowseoptions_
+    
+    FilterResponse_t response;
+    text_tset metadata;
+    metadata.insert ("Title");
+    //****************
+    metadata.insert ("childtype");
+    //****************
+    bool getParents = false;
+    get_children ("", args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+
+    int numbrowseoptions = response.docInfo.size();
+    if (has_search_button) numbrowseoptions += 1;
+    disp.setmacro ("numbrowseoptions", "help", numbrowseoptions);
+
+    ResultDocInfo_tarray::iterator here = response.docInfo.begin();
+    ResultDocInfo_tarray::iterator end = response.docInfo.end();
+
+    text_t helptext;
+    if (has_search_button) {
+      helptext = "<ul><li>_textSearchhelp_\n";
+    }
+
+    // ********************************      
+    int classifiernumber = 0;
+    bool collage = false;
+
+    while (here != end) {
+      
+      ++classifiernumber;
+      text_t childtype = (*here).metadata["childtype"].values[0];
+      if (childtype == "Collage" && arg_p == "about") {
+	// get the classifier number
+	disp.setmacro ("classifier", "about", classifiernumber);
+	disp.setmacro ("aboutCollage", "about", "_collageapplet_");
+	collage = true;
+      }
+      ++here;
+    } 
+    if (! collage)
+      disp.setmacro ("aboutCollage", "about", "_collageempty_");
+	
+    // ********************************
+     
+    here = response.docInfo.begin();
+
+    while (here != end) {
+      
+      text_t title = (*here).metadata["Title"].values[0];
+    
+      text_t stext;
+      disp.expandstring ("help", "_text" + title + "help_", stext);
+      if (stext == ("_text" + title + "help_")) {
+	text_t ltext, ttext;
+	disp.expandstring("Global", "_label"+title+"_", ltext);
+	if (ltext == ("_label"+title+"_")) {
+	  ltext = title;
+	}
+	disp.expandstring("Global", "_text"+title+"_", ttext);
+	if (ttext == ("_text"+title+"_")) {
+	  ttext = title;
+	}
+	helptext += "<li>_help:textdefaulthelp_("+ttext+","+ltext+")";
+      } else {
+	helptext += "<li>_help:text" + title + "help_";
+      }
+
+      ++here;
+    }
+    helptext += "</ul>\n";
+    disp.setmacro ("textbrowseoptions", "help", helptext);
+
+    if (arg_p == "help") {
+      // do we need to add in the datesearch help??
+      text_tmap::iterator check = cinfo->format.find("QueryInterface");
+      if(check != cinfo->format.end()){
+	if((*check).second=="DateSearch"){
+	  text_t current = "_datesearch_";
+	  disp.setmacro("optdatesearchhelp","help","_datesearchhelp_");
+	  disp.setmacro("optdatesearchhelpcontents","help","_datesearchhelpcontents_");
+	}
+      }
+      
+    }
+    
+    if (arg_p == "about") {
+
+      // _textsubcollections_
+      if (args["ccs"] == "1" && (cinfo->ccsCols.size() > 1)) {
+	text_t textsubcollections = "_textsubcols1_(" + text_t(cinfo->ccsCols.size()) + ")";
+	text_tarray::const_iterator here = cinfo->ccsCols.begin();
+	text_tarray::const_iterator end = cinfo->ccsCols.end();
+	bool first = true;
+	int count = 0;
+	while (here != end) {
+	  if (*here == arg_c) {
+	    if (!first) textsubcollections += "<br>";
+	    textsubcollections += "\n" + cinfo->get_collectionmeta("collectionname", args["l"]) + "\n";
+	  } else {
+	    ColInfoResponse_t *this_cinfo = recpt->get_collectinfo_ptr (collectproto, *here, logout);
+	    if (this_cinfo == NULL) {++here; continue;}
+	    if (!first) textsubcollections += "<br>";
+	    textsubcollections += "\n" + this_cinfo->get_collectionmeta("collectionname", args["l"]) + "\n";
+	  }
+	  ++count;
+	  first = false;
+	  ++here;
+	}
+	textsubcollections += "_textsubcols2_";
+	if (count > 1) {
+	  disp.setmacro ("textsubcollections", "about", textsubcollections);
+	}
+      }
+
+      comerror_t err;
+      bool issearchable = true;
+      collectproto->is_searchable(args["c"], issearchable, err, logout);
+      if (err != noError) issearchable = true;
+      outconvertclass t;
+      if (!issearchable ) {
+	disp.setmacro ("aboutqueryform", "about", "");
+      }
+    }
+
+  } else if (arg_p == "docs") {
+
+    set_documentation_macro (disp);
+
+  } else if (arg_p == "bsummary" && !arg_c.empty()) {
+
+    set_macro_to_file_contents (disp, "importlog", "bsummary",
+				filename_cat(collecthome, arg_c, "etc", "import.log"));
+    set_macro_to_file_contents (disp, "faillog", "bsummary",
+				filename_cat(collecthome, arg_c, "etc", "fail.log"));
+    set_macro_to_file_contents (disp, "buildlog", "bsummary",
+				filename_cat(collecthome, arg_c, "etc", "build.log"));
+  }
+}
+
+bool pageaction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
+			    browsermapclass * /*browsers*/, displayclass &disp, 
+			    outconvertclass &outconvert, ostream &textout, 
+			    ostream &/*logout*/) {
+
+  text_t &arg_p = args["p"];
+
+  textout << outconvert << disp << ("_" + arg_p + ":header_\n")
+	  << ("_" + arg_p + ":content_\n")
+	  << ("_" + arg_p + ":footer_\n");
+
+  return true;
+}
+
+void pageaction::configure (const text_t &key, const text_tarray &cfgline) {
+  if ((key == "status") && (cfgline.size() == 1) && 
+      (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    status_disabled = false;
+  } else if ((key == "collector") && (cfgline.size() == 1) && 
+	     (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    collector_disabled = false;
+  }
+ else if ((key == "depositor") && (cfgline.size() == 1) && 
+	     (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    depositor_disabled = false;
+  }
+    else if ((key == "translator") && (cfgline.size() == 1) && 
+	     (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    translator_disabled = false;
+  } else if ((key == "gliapplet") && (cfgline.size() == 1) && 
+	     (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    gliapplet_disabled = false;
+  }
+  else {
+    // call the parent class to deal with the things which
+    // are not dealt with here
+    action::configure (key, cfgline);
+  }
+}
+
+
+
+
+
+
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/recpt/pageaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pageaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pageaction.h	(revision 16561)
@@ -0,0 +1,104 @@
+/**********************************************************************
+ *
+ * pageaction.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.
+ *
+ *********************************************************************/
+
+
+#ifndef PAGEACTION_H
+#define PAGEACTION_H
+
+
+#include "gsdlconf.h"
+#include "action.h"
+#include "receptionist.h"
+
+
+class pageaction : public action {
+  
+protected:
+  receptionist *recpt;
+
+  text_tset classify_meta;
+
+public:
+  pageaction ();
+  virtual ~pageaction ();
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "p";}
+  
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+ protected:
+  
+  void home_images (displayclass &disp, recptprotolistclass *protos, 
+		   cgiargsclass &args, const recptconf &configinfo, ostream &logout);
+  
+  void home_pulldown (displayclass &disp, recptprotolistclass *protos, 
+		      cgiargsclass &args, const recptconf &configinfo, ostream &logout);
+  
+  void homepagestyle (displayclass &disp, recptprotolistclass *protos, 
+		      cgiargsclass &args, ostream &logout);
+  
+
+  
+  void set_collectionlist_macro (displayclass &disp, recptprotolistclass *protos, 
+				 cgiargsclass &args, ostream &logout);
+
+  void set_documentation_macro (displayclass &disp);
+
+  void set_macro_to_file_contents (displayclass &disp, const text_t &macroname,
+				   const text_t &packagename, const text_t &filename);
+
+  void set_language_encoding_macros(displayclass &disp, cgiargsclass &args,
+				    recptprotolistclass *protos, ColInfoResponse_t *cinfo,
+				    ostream &logout);
+
+
+  
+  bool status_disabled;
+  bool collector_disabled;
+  bool depositor_disabled;
+  bool translator_disabled;
+  bool gliapplet_disabled;
+
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,208 @@
+/**********************************************************************
+ *
+ * pagedbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PAGED_BROWSER
+
+#include "pagedbrowserclass.h"
+#include <assert.h>
+#include "recptprototools.h"
+#include "OIDtools.h"
+#include "gsdltools.h"
+
+pagedbrowserclass::pagedbrowserclass () {
+}
+
+pagedbrowserclass::~pagedbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t pagedbrowserclass::get_browser_name () {
+  return "Paged";
+}
+
+browserclass* pagedbrowserclass::clone()
+{
+  return new pagedbrowserclass();
+}
+
+void pagedbrowserclass::load_metadata_defaults (text_tset &metadata) {
+  metadata.insert ("Title");
+}
+
+// if the "gp" (go to page) argument is set we need to set
+// the "d" argument to the corresponding page
+// also want to set "d" argument to first child if we're at
+// an 'Invisible' top level
+void pagedbrowserclass::processOID (cgiargsclass &args, recptproto *collectproto,
+                                    ostream &logout) {
+
+  text_t &arg_d = args["d"];
+  text_t &arg_gp = args["gp"];
+  text_tset metadata;
+  bool getParents = false;
+  FilterResponse_t response;
+
+  if (!arg_d.empty() && !arg_gp.empty()) {
+    text_t top;
+    get_top (arg_d, top);
+    metadata.insert ("Title");
+    get_children (top, args["c"], args["l"], metadata, getParents, collectproto, response, logout);
+    ResultDocInfo_tarray::iterator dochere = response.docInfo.begin();
+    ResultDocInfo_tarray::iterator docend = response.docInfo.end();
+    while (dochere != docend) {
+      if ((*dochere).metadata["Title"].values[0] == arg_gp) {
+        arg_d = (*dochere).OID;
+        return;
+      }
+      ++dochere;
+    }
+  }
+
+  // The "gp" argument was either empty or invalid, so display the first child if given a document OID
+  if (!arg_d.empty() && is_top(arg_d)) { // if top level doc, check if not invisible
+    metadata.insert("thistype");
+    if (get_info(arg_d, args["c"], args["l"], metadata, getParents, collectproto, response, logout)) {
+      text_t type = response.docInfo[0].metadata["thistype"].values[0];
+      if (type=="Invisible") { // display first child
+        arg_d = arg_d + ".fc";
+      }
+    }
+  }
+}
+
+int pagedbrowserclass::output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+					     const text_t &/*collection*/, int /*colnumber*/, 
+					     format_t * /*formatlistptr*/, bool /*use_table*/, 
+					     text_tset &/*metadata*/, bool &/*getParents*/,
+					     recptproto * /*collectproto*/, displayclass &disp, 
+					     outconvertclass &outconvert, ostream &textout, 
+					     ostream &/*logout*/) {
+
+  // this browser class only handles document levels
+  if (args["d"].empty()) return 0;
+
+  if (section.OID != args["d"]) {
+    text_t httpprevarrow = "_httpdocument_&cl=" + args["cl"] + "&d=" + dm_safe(section.OID);
+    text_t parentarrow = "<a href=\"" + httpprevarrow + "\">_iconprev_</a>\n";
+    disp.setmacro ("httpprevarrow", "document", httpprevarrow);
+    disp.setmacro ("parentarrow", "document", parentarrow);
+    return 0;
+  }
+
+  // must be at top level to get to here!
+  textout << outconvert << disp << "<b>" << section.metadata["Title"].values[0] 
+	  << "_document:textintro_</b>\n";
+  
+  return 0;
+}
+
+int pagedbrowserclass::output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+					     const text_t &/*collection*/, int /*colnumber*/, 
+					     format_t * /*formatlistptr*/, bool /*use_table*/, 
+					     text_tset &/*metadata*/, bool &/*getParents*/,
+					     recptproto * /*collectproto*/, displayclass &disp, 
+					     outconvertclass &outconvert, ostream &textout, 
+					     ostream &/*logout*/) {
+
+  text_t &arg_d = args["d"];
+
+  // this browser class only handles document levels
+  if (arg_d.empty()) return 0;
+
+  text_t previousOID, previoustitle, nextOID, nexttitle;
+  text_t prevarrow, nextarrow, httpprevarrow, httpnextarrow;
+  bool found = false;
+
+  // this should be our list of pages
+
+  ResultDocInfo_tarray::iterator thissection = sections.docInfo.begin();
+  ResultDocInfo_tarray::iterator endsection = sections.docInfo.end();
+
+  while (thissection != endsection) {
+    if (arg_d == (*thissection).OID) {
+      found = true;
+      textout << outconvert << disp 
+	      << "<table><tr valign=top><td colspan=3><center><b>_page_"
+	      << (*thissection).metadata["Title"].values[0] << "</b>\n";
+      if (thissection != sections.docInfo.begin()) {
+	previousOID = (*(thissection-1)).OID;
+	previoustitle = (*(thissection-1)).metadata["Title"].values[0];
+      } else {
+	prevarrow = "_document:parentarrow_";
+      }
+
+      if ((thissection+1) != endsection) {
+	nextOID = (*(thissection+1)).OID;
+	nexttitle = (*(thissection+1)).metadata["Title"].values[0];
+      }
+      break;
+    }
+    ++thissection;
+  }
+
+  if (!found) {
+    textout << outconvert << disp 
+	    << "<table><tr valign=top> <td colspan=3><center>\n";
+  }
+  int numpages = sections.docInfo.size();
+  textout << outconvert << disp 
+	  << ("_document:textnumpages_(" + text_t(numpages) + ")</center></td></tr>\n");
+
+  disp.setmacro ("numpages", "document", numpages);
+
+  if (!found) {
+    httpnextarrow = "_httpdocument_&cl=" + args["cl"] + "&d=" + sections.docInfo[0].OID;
+    nextarrow = "<a href=\"" + httpnextarrow + "\">" + 
+      sections.docInfo[0].metadata["Title"].values[0] + "_iconnext_</a>\n";
+
+  } else {
+    if (!previousOID.empty()) {
+      httpprevarrow = "_httpdocument_&cl=" + args["cl"] + "&d=" + dm_safe(previousOID);
+      prevarrow = "<a href=\"" + httpprevarrow + "\">_iconprev_" + previoustitle + "</a>\n";
+    }
+    if (!nextOID.empty()) {
+      httpnextarrow = "_httpdocument_&cl=" + args["cl"] + "&d=" + dm_safe(nextOID);
+      nextarrow = "<a href=\"" + httpnextarrow + "\">" + nexttitle + "_iconnext_</a>\n";
+    }
+  }
+
+  if (!httpprevarrow.empty()) disp.setmacro ("httpprevarrow", "document", httpprevarrow);
+  if (!httpnextarrow.empty()) disp.setmacro ("httpnextarrow", "document", httpnextarrow);
+
+  if (!prevarrow.empty()) disp.setmacro ("prevarrow", "document", prevarrow);
+  if (!nextarrow.empty()) disp.setmacro ("nextarrow", "document", nextarrow);
+
+
+  textout << outconvert << disp << "<tr valign=middle>\n"
+	  << "<td align=right>" << prevarrow << "</td>\n"
+	  << "<td align=center valign=top>_document:gotoform_</td>"
+	  << "<td align=left>" << nextarrow << "</td>\n"
+	  << "</tr></table>\n";
+
+  return 0;
+}
+
+#endif //GSDL_USE_PAGED_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pagedbrowserclass.h	(revision 16561)
@@ -0,0 +1,68 @@
+/**********************************************************************
+ *
+ * pagedbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PAGED_BROWSER
+
+#ifndef PAGEDBROWSERCLASS_H
+#define PAGEDBROWSERCLASS_H
+
+#include "browserclass.h"
+
+class pagedbrowserclass : public browserclass {
+protected:
+
+public:
+
+  pagedbrowserclass ();
+  virtual ~pagedbrowserclass ();
+
+  // returns the name that specifies the browserclass type
+  virtual text_t get_browser_name ();
+
+  virtual browserclass* clone();
+
+  void load_metadata_defaults (text_tset &metadata);
+
+  virtual void processOID (cgiargsclass &args, recptproto *collectproto,
+			   ostream &logout);
+
+
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+};
+
+#endif //!PAGEDBROWSERCLASS_H
+#endif //GSDL_USE_PAGED_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/phindaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/phindaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/phindaction.cpp	(revision 16561)
@@ -0,0 +1,1062 @@
+/**********************************************************************
+ *
+ * phindaction.cpp -- 
+ *
+ * Copyright 2001 Gordon W. Paynter
+ * Copyright 2001 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PHIND_ACTION
+
+// Note that this action uses mgpp to retrieve phind info, calling MGQuery
+// etc. directly, not through the protocol. This breaks our receptionist -
+// collection server separation and should be fixed some day I guess.
+
+#include "phindaction.h"
+#include "fileutil.h"
+
+phindaction::phindaction () {
+
+  cgiarginfo arg_ainfo;
+
+  arg_ainfo.shortname = "pc";
+  arg_ainfo.longname = "phind classifier";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pxml";
+  arg_ainfo.longname = "phind XML mode";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "ppnum";
+  arg_ainfo.longname = "phind phrase number";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pptext";
+  arg_ainfo.longname = "phind phrase text";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pfe";
+  arg_ainfo.longname = "phind first_e";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "ple";
+  arg_ainfo.longname = "phind last_e";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "10";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pfl";
+  arg_ainfo.longname = "phind first_l";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pll";
+  arg_ainfo.longname = "phind last_l";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "10";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pfd";
+  arg_ainfo.longname = "phind first_d";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "pld";
+  arg_ainfo.longname = "phind last_d";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "10";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+phindaction::~phindaction () {
+}
+
+void phindaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
+				    response_t &response,text_t &response_data, 
+				    ostream &/*logout*/) {
+  response = content;
+  if (args["pxml"] == "1") {
+    response_data = "text/xml";
+  } else {
+    response_data = "text/html";
+  }
+}
+
+bool phindaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			     browsermapclass * /*browsers*/, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &logout) {
+
+  unsigned long count_l, count_e, count_d;
+  unsigned long phrase = args["ppnum"].getulong();
+  text_t &word = args["pptext"];
+  unsigned long first_e = args["pfe"].getulong();
+  unsigned long last_e = args["ple"].getulong();
+  unsigned long first_l = args["pfl"].getulong();
+  unsigned long last_l = args["pll"].getulong();
+  unsigned long first_d = args["pfd"].getulong();
+  unsigned long last_d = args["pld"].getulong();
+  bool XMLmode = false;
+  if (args["pxml"] == "1") XMLmode = true;
+
+  // must have a valid collection server
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL) {
+    output_error("phindaction: ERROR: collection not set", textout, 
+		 outconvert, disp, logout, XMLmode);
+    return true;
+  }
+
+  // the frequency and occurances of the phrase
+  unsigned long tf;
+  vector <unsigned long> el, linkdest, docNums, docfreq;
+  vector <UCArray> linktype;
+
+  // the number of occurances to display
+  unsigned long ef, lf, df;
+  
+  text_t basepath = filename_cat(collecthome, args["c"], 
+				 "index", "phind" + args["pc"]);
+
+  // If we don't know the phrase number, look it up
+  if (phrase == 0) {
+    
+    if (word.empty()) {
+      output_error("phindaction: ERROR: no phrase number or word", textout, 
+		   outconvert, disp, logout, XMLmode);
+      return true;
+    }
+
+    DocNumArray result;
+    /** In order to prevent browser crashing problems, any method which
+     *  previously suffered a silent fatal error, now instead returns false
+     *  to indicate a fatal error has occured. We can then dispatch an
+     *  appropriate error tag to the Phind applet (rather than leave it
+     *  whiling away the milliseconds until the end of existence - or at
+     *  least your browser - in an infinite loop!)
+     *  DLConsulting 12-07-2004
+     */
+    if(!find_phrase_number_from_word(basepath, word, result)) {
+	output_error("phindaction: Fatal Error! Couldn't load index information in find_phrase_number_from_word()",
+		     textout, outconvert, disp, logout, XMLmode);
+	return true;
+    }
+    
+    if (result.empty()) {
+      output_error("phindaction: The search term does not occur in the collection",
+		   textout, outconvert, disp, logout, XMLmode);
+      return true;
+    } else {
+      phrase = result[0];
+    }
+  } 
+
+  // Create a TextData object to read the phrase data (pdata)
+  TextData textdata;
+
+  text_t fullpath = filename_cat(basepath, "pdata");
+  char *fullpathc = fullpath.getcstr();
+#if defined __WIN32__
+  char *base = "";
+#else
+  char *base = "/";
+#endif
+
+  if (!textdata.LoadData (base, fullpathc)) {
+    //    FatalError (1, "Couldn't load text information for \"%s\"", fullpathc);
+    //exit(0);
+    /** We must return something to the client, whether this error is fatal or 
+     *  no, otherwise we risk sending their browser into an infinite loop!
+     *  DLConsulting 12-07-2004
+     */
+    output_error("phindaction: Fatal Error! Couldn't load text information for collection",
+		 textout, outconvert, disp, logout, XMLmode);
+    return true;
+  }
+
+  delete []fullpathc;
+
+  /** Another previously silent method can now cry out.
+   *  DLConsulting 12-07-2004
+   */
+  if(!get_phrase_all_data(textdata, phrase, word, tf, ef, lf, df, el, 
+			  linkdest, linktype, docNums, docfreq)) {
+    output_error(
+      "phindaction: Fatal Error! Couldn't parse phrase in get_phrase_all_data()",
+      textout, outconvert, disp, logout, XMLmode);
+      return true;    
+  }
+
+  // Output the header
+  if (XMLmode) {
+    textout << "<phinddata id=\"" << phrase 
+	    << "\" text=\"" << word 
+	    << "\" tf=\"" << tf 
+	    << "\" ef=\"" << ef 
+	    << "\" df=\"" << df 
+	    << "\" lf=\"" << lf 
+	    << "\">\n";
+  } else {
+    textout << "<html><head><title>" << word << "</title></head>\n"
+	    << "<body><center>\n"
+	    << "<p><h1>" << word << "</h1>\n"
+	    << "<p><b>"<< word << "</b> occurs " 
+	    << tf << " times in " << df << " documents\n";
+  }
+
+  // Output the thesaurus links
+  if ((lf > 0) && (first_l < last_l)) {
+
+    // figure out the number of phrases to output
+    if (last_l > lf) {
+      last_l = lf;
+    }
+    count_l = last_l - first_l;
+    
+    if (XMLmode) {
+      textout << "<thesauruslist length=\"" << lf 
+	      << "\" start=\"" << first_l
+	      << "\" end=\"" << last_l << "\">\n";
+      /** DLConsulting 12-07-2004 */
+      if(!print_thesaurus_links(args["c"], XMLmode, textdata, linkdest, linktype,
+			       first_l, last_l, disp, outconvert, textout)) {
+	output_error(
+		     "phindaction: Fatal Error! Couldn't get phrase in get_phrase_freq_data()",
+		     textout, outconvert, disp, logout, XMLmode);
+	return true;	
+      }
+      textout << "</thesauruslist>\n";
+    }
+
+    // output links as HTML
+    else {
+      if (count_l == lf) {
+	textout << "<p><b> " << count_l << " thesaurus links</b>\n";
+      } else {
+	textout << "<p><b>" << count_l << " of " << lf << " thesaurus links</b>\n";
+      }
+
+      textout << "<p><table border=1><tr><th>type</th><th>topic</th><th>freq</th><th>docs</th></tr>\n";
+      /** DLConsulting 12-07-2004 */
+      if(!print_thesaurus_links(args["c"], XMLmode, textdata, linkdest, linktype, 
+			       first_l, last_l, disp, outconvert, textout)) {
+	output_error(
+		     "phindaction: Fatal Error! Couldn't get phrase in get_phrase_freq_data()",
+		     textout, outconvert, disp, logout, XMLmode);
+	return true;
+      }
+      textout << "</table>\n";
+
+      if (last_l < lf) {
+	if ((last_l + 10) < lf) {
+	  textout << outconvert << disp 
+		  << "<br><a href=\"_gwcgi_?"
+		  << "c=" << args["c"]
+		  << "&ppnum=" << phrase 
+		  << "&pfe=" << first_e 
+		  << "&ple=" << last_e 
+		  << "&pfd=" << first_d 
+		  << "&pld=" << last_d
+		  << "&pfl=" << first_l 
+		  << "&pll=" << (last_l + 10)
+		  << "\">Get more thesaurus links</a>\n"; 
+	}
+	textout << outconvert << disp
+		<< "<br><a href=\"_gwcgi_?"
+		<< "c=" << args["c"]
+		<< "&ppnum=" << phrase 
+		<< "&pfe=" << first_e 
+		<< "&ple=" << last_e 
+		<< "&pfd=" << first_d 
+		<< "&pld=" << last_d 
+		<< "&pfl=" << first_l 
+		<< "&pll=" << lf
+		<< "\">Get every thesaurus link</a>\n" ;
+      }
+    }
+  }
+  
+  // Output the expansions
+  if ((ef > 0) && (first_e < last_e)) {
+
+    // figure out the number of phrases to output
+    if (last_e > el.size()) {
+      last_e = el.size();
+    }
+    count_e = last_e - first_e;
+
+    // output expansions as XML
+    if (XMLmode) {
+      textout << "<expansionlist length=\"" << ef 
+	      << "\" start=\"" << first_e
+	      << "\" end=\"" << last_e << "\">" << endl;
+
+      print_expansions(args["c"], XMLmode, word, textdata, el, first_e,
+		       last_e, disp, outconvert, textout);
+
+      textout << "</expansionlist>\n";
+    }
+
+    // output expansions as HTML
+    else {
+      if (count_e == el.size()) {
+	textout << "<p><b> " << count_e << " expansions</b>\n";
+      } else {
+	textout << "<p><b>" << count_e << " of " << ef << " expansions</b>\n";
+      }
+
+      textout << "<p><table border=1><tr><th colspan=3>phrase</th><th>freq</th><th>docs</th></tr>\n";
+      print_expansions(args["c"], XMLmode, word, textdata, el, first_e, 
+		       last_e, disp, outconvert, textout);
+      textout << "</table>\n";
+
+      if (last_e < ef) {
+	if ((last_e + 10) < ef) {
+	  textout << outconvert << disp 
+		  << "<br><a href=\"_gwcgi_?"
+		  << "c=" << args["c"]
+		  << "&ppnum=" << phrase 
+		  << "&pfe=" << first_e 
+		  << "&ple=" << (last_e + 10) 
+		  << "&pfd=" << first_d 
+		  << "&pld=" << last_d 
+		  << "&pfl=" << first_l 
+		  << "&pll=" << last_l
+		  << "\">Get more expansions</a>\n";
+	}
+	textout << outconvert << disp
+		<< "<br><a href=\"_gwcgi_?"
+		<< "c=" << args["c"]
+		<< "&ppnum=" << phrase 
+		<< "&pfe=" << first_e 
+		<< "&ple=" << ef
+		<< "&pfd=" << first_d 
+		<< "&pld=" << last_d 
+		<< "&pfl=" << first_l 
+		<< "&pll=" << last_l
+		<< "\">Get every expansion</a>\n";
+      }
+    }
+  }
+
+  // Output the document occurances
+  if ((df > 0) && (first_d < last_d)) {
+
+    // figure out the phrases to output
+    if (last_d > docNums.size()) {
+      last_d = docNums.size();
+    }
+    count_d = last_d - first_d;
+
+    // output document list as XML
+    if (XMLmode) {
+      textout << "<documentlist length=\"" << df 
+	      << "\" start=\"" << first_d
+	      << "\" end=\"" << last_d << "\">\n";
+      
+      if(!print_documents(XMLmode, basepath, args["c"], docNums, docfreq,
+			  first_d, last_d, disp, outconvert, textout)) {
+	output_error(
+		     "phindaction: Fatal Error! Couldn't load text information in print_documents() or get_document_all_data()",
+		     textout, outconvert, disp, logout, XMLmode);
+	return true;	
+      }
+
+      textout << "</documentlist>\n";
+    }
+
+    // output document list as HTML 
+    else {
+      
+      if (count_d == docNums.size()) {
+	textout << "<p><b> " << count_d << " documents</b>\n";
+      } else {
+	textout << "<p><b>" << count_d << " of " << df << " documents</b>\n";
+      }
+
+      textout << "<p><table border=1><tr><th align=left>document</th><th>freq</th></tr>\n";
+      if(!print_documents(XMLmode, basepath, args["c"], docNums, docfreq, 
+			  first_d, last_d, disp, outconvert, textout)) {
+	output_error(
+		     "phindaction: Fatal Error! Couldn't load text information in print_documents()",
+		     textout, outconvert, disp, logout, XMLmode);
+	return true;
+      }
+      textout << "</table>\n";
+      
+      if (last_d < df) {
+	if ((last_d + 10) < df) {
+	  textout << outconvert << disp
+		  << "<br><a href=\"_gwcgi_?"
+		  << "c=" << args["c"]
+		  << "&ppnum=" << phrase 
+		  << "&pfe=" << first_e 
+		  << "&ple=" << last_e 
+		  << "&pfd=" << first_d
+		  << "&pld=" << (last_d + 10)  
+		  << "&pfl=" << first_l 
+		  << "&pll=" << last_l
+		  << "\">Get more documents</a>\n";
+	}
+	textout << outconvert << disp 
+		<< "<br><a href=\"_gwcgi_?"
+		<< "c=" << args["c"]
+		<< "&ppnum=" << phrase 
+		<< "&pfe=" << first_e 
+		<< "&ple=" << last_e 
+		<< "&pfd=" << first_d 
+		<< "&pld=" << df
+		<< "&pfl=" << first_l 
+		<< "&pll=" << last_l
+		<< "\">Get every document</a>\n";
+      }
+    }
+  }
+
+  // Close the document
+  if (XMLmode) {
+    textout << "</phinddata>\n";
+  } else {
+    textout << "</center></body></html>\n";
+  }
+
+  textdata.UnloadData ();
+
+  return true;
+}
+
+// Find the phrase number of a word in the index file
+bool phindaction::find_phrase_number_from_word(const text_t &basepath, 
+					       const text_t &query, 
+					       DocNumArray &result) {
+
+  // Open the index file for searching
+  IndexData indexData;
+
+  text_t fullpath = filename_cat(basepath, "pword");
+  char *fullpathc = fullpath.getcstr();
+#if defined __WIN32__
+  char *base = "";
+#else
+  char *base = "/";
+#endif
+
+  if (!indexData.LoadData (base, fullpathc)) {
+    //    FatalError (1, "Couldn't load index information for \"%s\"", fullpathc);
+    //exit(0);
+    /** Don't handle fatal errors here anymore.
+     *  DLConsulting 12-07-2004
+     */
+    return false; // Indicates something very bad has happened
+  }
+
+  delete []fullpathc;
+
+  // set up the query object
+  QueryInfo queryInfo;
+  SetCStr (queryInfo.docLevel, "Document", 8);
+  queryInfo.maxDocs = 5;
+  queryInfo.sortByRank = true;
+  queryInfo.exactWeights = false;
+  queryInfo.needRankInfo = true;
+  queryInfo.needTermFreqs = true;
+  
+  // mode 1 = casefolded, unstemmed search
+  UCArray ucquery;
+  toUCArray(query, ucquery);
+  QueryNode *queryTree = ParseQuery(ucquery, 1, 1, 4);
+      
+  // perform the query
+  ExtQueryResult queryResult;
+  MGQuery (indexData, queryInfo, queryTree, queryResult);
+  // cout << "-- word lookup result -- " << endl << queryResult << endl ;
+
+  result.clear();
+  result = queryResult.docs;
+
+  // delete the query
+  if (queryTree != NULL) delete queryTree;
+
+  indexData.UnloadData();
+
+  /** This method now returns a boolean, so...
+   *  DLConsulting 12-07-2004
+   */
+  return true; // Indicates that what happened is all good, baby.
+}
+
+// Get all the data about a phrase
+//
+// The phrase is stored in textData as record phrase.
+// We retrieve:
+//   word - the text of the phrase
+//   tf - the total frequency of the phrase
+//   ef - the expansion frequency of the phrase
+//   lf - the thesaurus link frequency of the phrase
+//   df - the document frequency of the phrase
+//   el - the list of phrases that are expansions of phrase
+//   ll - the list of phrases that are thesaurus links 
+//   dl - the list of documents that contain phrase
+bool phindaction::get_phrase_all_data(TextData &textdata, unsigned long phrase,
+				      text_t &word, unsigned long &tf, unsigned long &ef,
+				      unsigned long &lf, unsigned long &df,
+				      vector <unsigned long> &el,
+				      vector <unsigned long> &linkdest,
+				      vector <UCArray> &linktype,
+				      vector <unsigned long> &docnum, 
+				      vector <unsigned long> &docfrq) {
+  UCArray text;
+  UCArray docLevel;
+  SetCStr(docLevel, "Document", 8);
+
+  // Look the word up in the textData
+  if (!GetDocText (textdata, docLevel, phrase, text)) {
+    //    FatalError (1, "Error while trying to get phrase %u", phrase);
+    //exit(0);
+    return false; // Something very bad has happened.
+  }
+
+  // Ignore everything up to the first colon
+  UCArray::iterator next = text.begin();
+  while (*next++ != ':');
+
+  // ignore training carriage returns
+  while (text.back() == '\n') {
+    text.pop_back();
+  }
+  
+  // Get the word
+  word.clear();
+  for (; *next != ':'; ++next) {
+    word.push_back(*next);
+  }
+  
+  // Get total frequency
+  tf = 0;
+  for (++next; *next != ':'; ++next) {
+    tf *= 10;
+    tf += (*next - '0');
+  }
+  
+  // Get expansion frequency
+  ef = 0;
+  for (++next; *next != ':'; ++next) {
+    ef *= 10;
+    ef += (*next - '0');
+  }
+  
+  // Get document frequency
+  df = 0;
+  for (++next; *next != ':'; ++next) {
+    df *= 10;
+    df += (*next - '0');
+  }
+  
+  // Get expansion list
+  el.clear();
+  unsigned long e = 0;
+  for (++next; *next != ':'; ++next) {
+    if (*next == ',') {
+      el.push_back(e);
+      e = 0;
+    } else {
+      e *= 10;
+      e += (*next - '0');
+    }
+  }
+
+  // Get document list & the document frequency list
+  docnum.clear();
+  docfrq.clear();
+  bool readnum = false;
+  unsigned long d = 0;
+  for (++next; *next != ':'; ++next) {
+    if (*next == ',') {
+      docnum.push_back(d);
+      readnum = true;
+      d = 0;
+    } else if (*next == ';') {
+      if (readnum) {
+	docfrq.push_back(d);
+      } else {
+	docnum.push_back(d);
+	docfrq.push_back(1);
+      }
+      readnum = false;
+      d = 0;
+    } else {
+      d *= 10;
+      d += (*next - '0');
+    }
+  }
+
+  // Get thesaurus link frequency & link list
+  text.push_back(':');
+  text.push_back(':');
+
+  // link frequency
+  lf = 0;
+  for (++next; *next != ':'; ++next) {
+    lf *= 10;
+    lf += (*next - '0');
+  }
+
+  // two lists of link data
+  linkdest.clear();
+  linktype.clear();
+  
+  UCArray thistype;
+  thistype.clear();
+  bool typedone = false;
+  unsigned long l = 0;
+  for (++next; *next != ':'; ++next) {
+    
+    if (!typedone) {
+      // first read the link type, a charactor string
+      if (*next == ',') {
+	typedone = true;
+      } else {
+	thistype.push_back(*next);
+      }
+    } else {
+      // having read the link type, read the list of link destinations
+      if (*next == ',') {
+	linkdest.push_back(l);
+	linktype.push_back(thistype);
+	l = 0;
+      } else if (*next == ';') {
+	linkdest.push_back(l);
+	linktype.push_back(thistype);
+	l = 0;
+	thistype.clear();
+	typedone = false;
+      } else {
+	l *= 10;
+	l += (*next - '0');
+      }
+    }
+  }
+
+  return true; // Indicates that what happened is all good, baby.
+}
+
+bool phindaction::print_thesaurus_links(const text_t &collection, bool XMLmode, 
+					TextData &textdata, vector <unsigned long> &linkdest,
+					vector <UCArray> &linktype, unsigned long first,
+					unsigned long last, displayclass &disp, 
+					outconvertclass &outconvert, ostream &textout) {
+
+  // information describing each link in the list
+  unsigned long phrase, tf, ef, df;
+  UCArray type, text;
+  
+  for (unsigned long l = first; l < last; ++l) {
+
+    // get the phrase data
+    phrase = linkdest[l];
+    type = linktype[l];
+
+    /** DLConsulting 12-07-2004 */
+    if(!get_phrase_freq_data(textdata, phrase, text, tf, ef, df)) {
+      return false;
+    }
+    
+    if (XMLmode) {
+      textout << "<thesaurus num=\"" << l 
+	      << "\" id=\"" << phrase
+	      << "\" tf=\"" << tf 
+	      << "\" df=\"" << df
+	      << "\" type=\"" << type
+	      << "\" text=\"" << text
+	      << "\"/>\n";
+    } else {
+      textout << "<tr valign=top><td>" << type << "</td><td>";
+      textout << outconvert << disp
+	      << "<a href=\"_gwcgi_?c=" << collection;
+      textout << "&ppnum=" << phrase << "\">" << text << "</a>"
+	      << "</td><td>" << tf << "</td><td>" << df << "</td></tr>\n";
+    }
+  }
+
+  /** DLConsulting 12-07-2004 */
+  return true;
+}
+
+// Get the frequency data about a phrase
+//
+// The phrase is stored in textData as record phrase.
+// We retrieve:
+//   word - the text of the phrase
+//   tf - the total frequency of the phrase
+//   ef - the expansion frequency of the phrase
+//   df - the document frequency of the phrase
+/**
+ *   Returns:
+ *     false if the method suffered a fatal error, true otherwise
+ */
+bool phindaction::get_phrase_freq_data(TextData &textdata, unsigned long phrase,
+				       UCArray &word, unsigned long &tf, 
+				       unsigned long &ef, unsigned long &df) {
+  
+  UCArray text;
+  UCArray docLevel;
+  SetCStr(docLevel, "Document", 8);
+
+  // Look the word up in the textData
+  if (!GetDocText (textdata, docLevel, phrase, text)) {
+    //    FatalError (1, "Error while trying to get phrase %u", phrase);
+    //exit(0);
+    /** DLConsulting 12-07-2004 */
+    return false;
+  }
+
+  // Ignore everything up to the first colon
+  UCArray::iterator next = text.begin();
+  while (*next++ != ':');
+  
+  // Get the word
+  word.clear();
+  for (; *next != ':'; ++next) {
+    word.push_back(*next);
+  }
+  
+  // Get total frequency
+  tf = 0;
+  for (++next; *next != ':'; ++next) {
+    tf *= 10;
+    tf += (*next - '0');
+  }
+  
+  // Get expansion frequency
+  ef = 0;
+  for (++next; *next != ':'; ++next) {
+    ef *= 10;
+    ef += (*next - '0');
+  }
+  
+  // Get document frequency
+  df = 0;
+  for (++next; *next != ':'; ++next) {
+    df *= 10;
+    df += (*next - '0');
+  }
+
+  /** DLConsulting 12-07-2004 */
+  return true;
+}
+
+// Print a list of expansions
+//
+// Given the textData and a list of phrase numbers, print out each of the
+// expansions.
+void phindaction::print_expansions(const text_t &collection, bool XMLmode, 
+				   const text_t &body, TextData &textdata, 
+				   const vector <unsigned long> &elist,
+				   unsigned long first, unsigned long last,
+				   displayclass &disp, outconvertclass &outconvert,
+				   ostream &textout) {
+  
+  UCArray word;
+  unsigned long phrase, tf, df, ef;
+
+  UCArray suffix, prefix, ucbody;
+  
+  toUCArray(body, ucbody);
+
+  for (unsigned long e = first; e < last; ++e) {
+
+    phrase = elist[e];
+    get_phrase_freq_data(textdata, phrase, word, tf, ef, df);
+
+    split_phrase(word, ucbody, prefix, suffix);
+    
+    if (XMLmode) {
+      // body is always the same as the text of the phrase, so no need to send it
+      textout << "<expansion num=\"" << e 
+	      << "\" id=\"" << phrase
+	      << "\" tf=\"" << tf 
+	      << "\" df=\"" << df;
+      if (!prefix.empty()) {
+	textout << "\" prefix=\"" << prefix;
+      }
+      if (!suffix.empty()) {
+	textout << "\" suffix=\"" << suffix;
+      }
+      textout << "\"/>\n";
+    } else {
+      textout << outconvert << disp 
+	      << "<tr valign=top><td align=right><a href=\"_gwcgi_?"
+	      << "c=" << collection << "&ppnum=" << phrase << "\">";
+      textout << prefix << "</a></td>";
+      textout <<outconvert << disp
+	      << "<td align=center><a href=\"_gwcgi_?"
+	      << "c=" << collection << "&ppnum=" << phrase << "\">"
+	      << body << "</a></td>"
+	      << "<td align=left><a href=\"_gwcgi_?"
+	      << "c=" << collection << "&ppnum=" << phrase << "\">";
+      textout << suffix << "</a></td>"
+	      << "<td>" << tf << "</td><td>" << df << "</td></tr>\n";
+    }
+  }
+}
+
+// split an expansion into prefix and suffix
+void phindaction::split_phrase(const UCArray &word, const UCArray &body, 
+			       UCArray &prefix, UCArray &suffix) {
+
+  prefix.clear();
+  suffix.clear();
+
+  bool readingPrefix = true;
+  UCArray::const_iterator here = word.begin();
+  UCArray::const_iterator end = word.end();
+  
+  while (here != end) {
+
+    // if we've not read all the prefix, add the next char to the prefix
+    if (readingPrefix) {
+      if (phrase_match(body, here, end)) {
+	readingPrefix = false;
+	// trim whitespace from end of prefix & start of suffix
+	if (!prefix.empty()) {
+	  prefix.pop_back();
+	}
+	if ((here != end) && (*here == ' ')) {
+	  ++here;
+	}
+      } else {
+	prefix.push_back(*here);
+	++here;
+      }
+    }
+    // if we've finished with the prefix, update the suffix
+    else {
+      suffix.push_back(*here);
+      ++here;
+    }
+  }
+}
+
+// phrase_match
+//
+// compare two strings, one represented as an UCArray, the other as two
+// UCArray iterators.
+//
+// Return true if the UCArray is the same as the phrase the iterators point
+// to for the length of the UCArray.
+bool phindaction::phrase_match(const UCArray &text, UCArray::const_iterator &here, 
+			       UCArray::const_iterator end) {
+
+  UCArray::const_iterator one_here = text.begin();
+  UCArray::const_iterator one_end  = text.end();
+  UCArray::const_iterator two_here = here;
+
+  // iterate over the length of the first string, comparing each element to
+  // the corresponding element in the second string.
+  while (one_here != one_end) {
+   
+      if (two_here == end) {
+	  return false;
+      } else if (*one_here != *two_here) {
+	  return false;
+      }
+      ++one_here;
+      ++two_here;
+  }
+
+  here = two_here;
+  return true;
+}
+
+bool phindaction::print_documents(bool XMLmode, const text_t &basepath, 
+				  const text_t &collection,
+				  const vector <unsigned long> &docNums, 
+				  const vector <unsigned long> &docFreq,
+				  unsigned long first, unsigned long last,
+				  displayclass &disp, outconvertclass &outconvert,
+				  ostream &textout) {
+  
+  // Create a TextData object to read the document data
+  TextData docdata;
+
+  text_t fullpath = filename_cat(basepath, "docs");
+  char *fullpathc = fullpath.getcstr();
+#if defined __WIN32__
+  char *base = "";
+#else
+  char *base = "/";
+#endif
+
+  if (!docdata.LoadData (base, fullpathc)) {
+    //    FatalError (1, "Couldn't load text information for \"%s\"", fullpathc);
+    //exit(0);
+    /** DLConsulting 12-07-2004 */
+    return false;
+  }
+
+  delete []fullpathc;
+
+  UCArray title, hash;
+  unsigned long freq, doc;
+
+  for (unsigned long d = first; d < last; ++d) {
+    doc = docNums[d];
+    freq = docFreq[d];
+
+    /** DLConsulting 13-07-2004 */
+    if(!get_document_all_data(docdata, doc, title, hash)) {
+      return false;
+    }
+
+    if (XMLmode) {
+      textout << "<document num=\"" << d
+	      << "\" hash=\"" << hash 
+	      << "\" freq=\"" << freq 
+	      << "\" title=\"" << title << "\"/>\n";
+    } else {
+      textout << outconvert << disp
+	      << "<tr valign=top><td><a href=\"_gwcgi_?"
+	      << "c=" << collection;
+      textout << "&a=d&d=" << hash << "\">" << title << "</a>"
+	      << "</td><td>" << freq << "</td></tr>\n"; 
+    }
+  }
+
+  docdata.UnloadData();
+
+  /** DLConsulting 12-07-2004 */
+  return true;
+}
+
+// Get all the data about a docment
+//
+// The document's details are stored in docData as record docNum.
+// We retrieve:
+//   title - the document's title
+//   hash - the document's unique OID
+/** Returns:
+ *   false if a fatal error occured, true otherwise
+ *  DLConsulting 12-07-2004
+ */
+bool phindaction::get_document_all_data(TextData &docdata, unsigned long docNum,
+					UCArray &title, UCArray &hash) {
+
+  UCArray text;
+  UCArray docLevel;
+  SetCStr(docLevel, "Document", 8);
+
+  // Look the word up in the textData
+  if (!GetDocText (docdata, docLevel, docNum, text)) {
+    //    FatalError (1, "Error while trying to get document %u", docNum);
+    //exit(0);
+    /** DLConsulting 13-07-2004 */
+    return false;
+  }
+
+  // Ignore everything up to the first colon
+  UCArray::iterator next = text.begin();
+  while (*next++ != '\t');
+  
+  // Get the document OID (hash)
+  hash.clear();
+  for (; *next != '\t'; ++next) {
+    hash.push_back(*next);
+  }
+
+  // Get the title
+  text.push_back('\n');
+  title.clear();
+  for (++next; *next != '\n'; ++next) {
+    title.push_back(*next);
+  }
+
+  /** DLConsulting 13-07-2004 */
+  return true;
+}
+
+void phindaction::toUCArray(const text_t &in, UCArray &out) {
+  out.clear();
+  if (out.capacity() < in.size() + 1) {
+    out.reserve(in.size() + 1);
+  }
+  text_t::const_iterator here = in.begin();
+  text_t::const_iterator end = in.end();
+  while (here != end) {
+    out.push_back((unsigned char) *here);
+    ++here;
+  }
+}
+
+void phindaction::output_error (const text_t &message, ostream &textout, 
+				outconvertclass &outconvert,
+				displayclass & disp, ostream &logout,
+				bool XMLmode) {
+
+  logout << outconvert << message << "\n";
+  if (XMLmode) {
+    textout << outconvert
+	    << "<phinddata>\n"
+	    << "<phinderror>" << message << "</phinderror>\n"
+	    << "</phinddata>\n";
+  } else {
+    textout << outconvert << disp 
+	    << "_header_\n" 
+	    << message
+	    << "_footer_\n";
+  }
+}
+
+#endif //GSDL_USE_PHIND_ACTION
+
Index: /gsdl/trunk/gs2runtime/src/recpt/phindaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/phindaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/phindaction.h	(revision 16561)
@@ -0,0 +1,122 @@
+/**********************************************************************
+ *
+ * phindaction.h -- 
+ *
+ * Copyright 2001 Gordon W. Paynter
+ * Copyright 2001 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PHIND_ACTION
+
+#ifndef PHINDACTION_H
+#define PHINDACTION_H
+
+#include "action.h"
+
+// Include MGPP functionality.
+#include <TextGet.h>
+#include <MGQuery.h>
+#include <Terms.h>
+#include <messages.h>
+#include <GSDLQueryParser.h>
+
+class phindaction : public action {
+  
+protected:
+
+  bool find_phrase_number_from_word(const text_t &basepath, 
+				    const text_t &query, 
+				    DocNumArray &result);
+
+  bool get_phrase_all_data(TextData &textdata, unsigned long phrase,
+			   text_t &word, unsigned long &tf, unsigned long &ef,
+			   unsigned long &lf, unsigned long &df,
+			   vector <unsigned long> &el,
+			   vector <unsigned long> &linkdest,
+			   vector <UCArray> &linktype,
+			   vector <unsigned long> &docnum, 
+			   vector <unsigned long> &docfrq);
+
+  bool print_thesaurus_links(const text_t &collection, bool XMLmode, 
+			     TextData &textdata, vector <unsigned long> &linkdest,
+			     vector <UCArray> &linktype, unsigned long first,
+			     unsigned long last, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout);
+
+  bool get_phrase_freq_data(TextData &textdata, unsigned long phrase,
+			    UCArray &word, unsigned long &tf, 
+			    unsigned long &ef, unsigned long &df);
+
+  void print_expansions(const text_t &collection, bool XMLmode, 
+			const text_t &body, TextData &textdata, 
+			const vector <unsigned long> &elist,
+			unsigned long first, unsigned long last,
+			displayclass &disp, outconvertclass &outconvert,
+			ostream &textout);
+
+  void split_phrase(const UCArray &word, const UCArray &body, 
+		    UCArray &prefix, UCArray &suffix);
+
+  bool phrase_match(const UCArray &text, UCArray::const_iterator &here, 
+		    UCArray::const_iterator end);
+
+  bool print_documents(bool XMLmode, const text_t &basepath, 
+		       const text_t &collection,
+		       const vector <unsigned long> &docNums, 
+		       const vector <unsigned long> &docFreq,
+		       unsigned long first, unsigned long last,
+		       displayclass &disp, outconvertclass &outconvert,
+		       ostream &textout);
+
+  bool get_document_all_data(TextData &docdata, unsigned long docNum,
+			     UCArray &title, UCArray &hash);
+
+  void toUCArray(const text_t &in, UCArray &out);
+
+  void output_error (const text_t &message, ostream &textout, 
+		     outconvertclass &outconvert,
+		     displayclass & disp, ostream &logout,
+		     bool XMLmode);
+
+
+public:
+  phindaction ();
+  virtual ~phindaction ();
+
+  text_t get_action_name () {return "phind";}
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+
+};
+
+#endif //!PHINDACTION_H
+#endif //GSDL_USE_PHIND_ACTION
+
Index: /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,51 @@
+/**********************************************************************
+ *
+ * phindbrowserclass.cpp -- 
+ *
+ * Copyright (C) 2000 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PHIND_BROWSER
+
+#include "phindbrowserclass.h"
+#include <assert.h>
+#include "OIDtools.h"
+
+phindbrowserclass::phindbrowserclass () {
+}
+
+phindbrowserclass::~phindbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t phindbrowserclass::get_browser_name () {
+  return "Phind";
+}
+
+browserclass* phindbrowserclass::clone()
+{
+  return new phindbrowserclass();
+}
+
+#endif //GSDL_USE_PHIND_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/phindbrowserclass.h	(revision 16561)
@@ -0,0 +1,59 @@
+/**********************************************************************
+ *
+ * phindbrowserclass.h -- 
+ *
+ * Copyright (C) 2000 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PHIND_BROWSER
+
+// The phindbrowserclass is necessary to add a phind classifier to the user
+// interface and navigation bar, but is basically empty - all the work is
+// done in documentaction.cpp and in the document.dm macro file.  What
+// happens is that documentaction.cpp has a section that sets various
+// macros when the document in question is a phind classifier; thse are in
+// turn used by the macro file to load the applet into the web page.
+
+#ifndef PHINDBROWSERCLASS_H
+#define PHINDBROWSERCLASS_H
+
+#include "browserclass.h"
+
+class phindbrowserclass : public browserclass {
+protected:
+
+public:
+
+  phindbrowserclass ();
+  virtual ~phindbrowserclass ();
+
+  // returns the name that specifies the browserclass type
+  virtual text_t get_browser_name ();
+
+  virtual browserclass* clone();
+
+};
+
+#endif //!PHINDBROWSERCLASS_H
+#endif //GSDL_USE_PHIND_BROWSER
Index: /gsdl/trunk/gs2runtime/src/recpt/pingaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pingaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pingaction.cpp	(revision 16561)
@@ -0,0 +1,87 @@
+/**********************************************************************
+ *
+ * pingaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PING_ACTION
+
+#include "pingaction.h"
+
+pingaction::pingaction () {
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "ping";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+}
+
+void pingaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				   response_t &response, text_t &response_data, 
+				   ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+
+bool pingaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			    browsermapclass * /*browsers*/, displayclass &/*disp*/, 
+			    outconvertclass &outconvert, ostream &textout, 
+			    ostream &logout) {
+  bool wassuccess = false;
+  comerror_t err;
+  
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (!args["c"].empty() && (collectproto != NULL)) {
+    collectproto->ping (args["c"], wassuccess, err, logout);
+    if (err != noError) wassuccess = false; // a communication error
+  }
+
+  textout << outconvert
+	  << "<html>\n"
+	  << "<head>\n"
+	  << "<title>Ping</title>\n"
+	  << "</head>\n"
+	  << "<body>\n"
+	  << "Ping for \"" << args["c"] << "\" ";
+
+  if (wassuccess) {
+    textout << outconvert << " succeeded.\n";
+  } else {
+    textout << outconvert << " did not succeed.\n";
+  }
+
+  textout << outconvert 
+	  << "</body>\n"
+	  << "</html>\n";
+
+  return true;
+};
+
+#endif //GSDL_USE_PING_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/pingaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/pingaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/pingaction.h	(revision 16561)
@@ -0,0 +1,55 @@
+/**********************************************************************
+ *
+ * pingaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_PING_ACTION
+
+#ifndef PINGACTION_H
+#define PINGACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+
+
+class pingaction : public action {
+public:
+  pingaction ();
+  virtual ~pingaction () {}
+
+  text_t get_action_name () {return "ping";}
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+};
+
+
+#endif //!PINGACTION_H
+#endif //GSDL_USE_PING_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/queryaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/queryaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/queryaction.cpp	(revision 16561)
@@ -0,0 +1,1819 @@
+/**********************************************************************
+ *
+ * queryaction.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.
+ *
+ *********************************************************************/
+
+#include "queryaction.h"
+#include "querytools.h"
+#include "formattools.h"
+#include "cgiutils.h"
+#include "OIDtools.h"
+#include "fileutil.h"
+#include "text_t.h"
+#include "historydb.h"
+#include "htmlutils.h" // for html_safe in do_action
+#include "gsdltools.h"
+#include "phrases.h" // for get_phrases
+#include <stdlib.h> // for strtol
+#include <assert.h>
+
+void colinfo_t::clear () {
+  formatlistptr = NULL;
+  browserptr = NULL;
+}
+
+void QueryResult_t::clear() {
+  doc.clear();
+  collection.clear();
+}
+
+queryaction::queryaction () {
+
+  recpt = NULL;
+  num_phrases = 0;
+
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "q";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+   // "ct" - 0 = mg, 1 = mgpp, 2=lucene
+  arg_ainfo.shortname = "ct";
+  arg_ainfo.longname = "collection type";
+  arg_ainfo.multiplechar = true; // can be empty or single char
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+ 
+  // "b" - 0 = simple, 1 = advanced
+  arg_ainfo.shortname = "b";
+  arg_ainfo.longname = "query mode";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "h"
+  arg_ainfo.shortname = "h";
+  arg_ainfo.longname = "main index";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "h2"
+  arg_ainfo.shortname = "h2";
+  arg_ainfo.longname = "main index for second query";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "j"
+  arg_ainfo.shortname = "j";
+  arg_ainfo.longname = "sub collection index";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "j2"
+  arg_ainfo.shortname = "j2";
+  arg_ainfo.longname = "sub collection index for second query";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "n"
+  arg_ainfo.shortname = "n";
+  arg_ainfo.longname = "language index";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "n2"
+  arg_ainfo.shortname = "n2";
+  arg_ainfo.longname = "language index for second query";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+
+  // "q"
+  arg_ainfo.shortname = "q";
+  arg_ainfo.longname = "query string";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "q2"
+  arg_ainfo.shortname = "q2";
+  arg_ainfo.longname = "query string for second query";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "cq2" ""=don't combine, "and", "or", "not"
+  arg_ainfo.shortname = "cq2";
+  arg_ainfo.longname = "combine queries";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "t" - 1 = ranked 0 = boolean
+  arg_ainfo.shortname = "t";
+  arg_ainfo.longname = "search type";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "k"
+  arg_ainfo.shortname = "k";
+  arg_ainfo.longname = "casefolding";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ks"
+  arg_ainfo.shortname = "ks";
+  arg_ainfo.longname = "casefolding support";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  // "s"
+  arg_ainfo.shortname = "s";
+  arg_ainfo.longname = "stemming";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ss"
+  arg_ainfo.shortname = "ss";
+  arg_ainfo.longname = "stemming support";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "af"
+  arg_ainfo.shortname = "af";
+  arg_ainfo.longname = "accentfolding";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "afs"
+  arg_ainfo.shortname = "afs";
+  arg_ainfo.longname = "accentfolding support";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+ 
+  // "m"
+  arg_ainfo.shortname = "m";
+  arg_ainfo.longname = "maximum number of documents";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "50";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "o"
+  arg_ainfo.shortname = "o";
+  arg_ainfo.longname = "hits per page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "20";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "r"
+  arg_ainfo.shortname = "r";
+  arg_ainfo.longname = "start results from";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ccs"
+  arg_ainfo.shortname = "ccs";
+  arg_ainfo.longname = "cross collection searching";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ccp"
+  arg_ainfo.shortname = "ccp";
+  arg_ainfo.longname = "cross collection page";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "cc"
+  arg_ainfo.shortname = "cc";
+  arg_ainfo.longname = "collections to search";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.multiplevalue = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "hd" history display  - search history only displayed when
+  // this var set to something other than 0
+  // this number of records is displayed
+  arg_ainfo.shortname = "hd";
+  arg_ainfo.longname = "history display";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.multiplevalue = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+	    
+  // "hs"  save - set to 1 in query form, so only save when submit
+  // query
+  // 0 = no save 1 = save
+  arg_ainfo.shortname = "hs";
+  arg_ainfo.longname = "history save";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "g" - new arg for granularity, for mgpp collections
+  arg_ainfo.shortname = "g";
+  arg_ainfo.longname = "granularity";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText; 
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ds" - start date
+  arg_ainfo.shortname = "ds";
+  arg_ainfo.longname = "start date";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "de" - end date
+  arg_ainfo.shortname = "de";
+  arg_ainfo.longname = "end date";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "dsbc" - whether or not start date is prechristian
+  arg_ainfo.shortname = "dsbc";
+  arg_ainfo.longname = "start date bc";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "debc" - whether or not end date is prechristian
+  arg_ainfo.shortname = "debc";
+  arg_ainfo.longname = "end date bc";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "qt" - 0 = text, 1 = form
+  arg_ainfo.shortname = "qt";
+  arg_ainfo.longname = "query type";
+  arg_ainfo.multiplechar = true; // can be empty or single char
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "qto" - 1 = text only, 2 = form only, 3 = text and form
+  arg_ainfo.shortname = "qto";
+  arg_ainfo.longname = "query type options";
+  arg_ainfo.multiplechar = true; // can be empty or single char
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+    
+  // "qb" - 0 = regular, 1 = large
+  arg_ainfo.shortname = "qb";
+  arg_ainfo.longname = "query box type";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "fqn" - number of fields in the query form
+  arg_ainfo.shortname = "fqn";
+  arg_ainfo.longname = "form query num fields";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "4";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "fqf" - the list of field names in the form query
+  // - a comma separated list
+  arg_ainfo.shortname = "fqf";
+  arg_ainfo.longname = "form query fields";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  // "fqv" - the list of values in the form query
+  // - a comma separated list
+  arg_ainfo.shortname = "fqv";
+  arg_ainfo.longname = "form query values";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+
+  // "fqs" - the list of stemming options in the form query
+  // - a comma separated list
+  arg_ainfo.shortname = "fqs";
+  arg_ainfo.longname = "form query stems";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+
+  // "fqk" - the list of casefolding options in the form query
+  // - a comma separated list
+  arg_ainfo.shortname = "fqk";
+  arg_ainfo.longname = "form query casefolds";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+  
+  // "fqc" - the list of boolean operators in the form query
+  // - a comma separated list
+  arg_ainfo.shortname = "fqc";
+  arg_ainfo.longname = "form query combines";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "fqa" - form query advanced - for "run query"
+  arg_ainfo.shortname = "fqa";
+  arg_ainfo.longname = "form query advanced query";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ifl" - I'm feeling lucky! (Go directly to a matching document)
+  arg_ainfo.shortname = "ifl";
+  arg_ainfo.longname = "i'm feeling lucky";
+  arg_ainfo.multiplechar = false;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "ifln" - I'm feeling lucky number (Go directly to the nth matching document)
+  arg_ainfo.shortname = "ifln";
+  arg_ainfo.longname = "i'm feeling lucky number";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "srn" - the next search result
+  arg_ainfo.shortname = "srn";
+  arg_ainfo.longname = "the next search result number";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "srp" - the previous search result
+  arg_ainfo.shortname = "srp";
+  arg_ainfo.longname = "the previous search result number";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "0";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "sf" - Sort field. Set to field to be used for sorting search reult
+  // set (only implemented for lucene collections at present).
+  arg_ainfo.shortname = "sf";
+  arg_ainfo.longname = "sort field";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "fuzziness" controls how closely the search terms must match
+  // 100 = exact match, 0 = very inexact match (only implemented for Lucene)
+  arg_ainfo.shortname = "fuzziness";
+  arg_ainfo.longname = "Lucene fuzziness value";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
+  action::configure (key, cfgline);
+}
+
+bool queryaction::init (ostream &logout) {
+  return action::init (logout);
+}
+
+bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+				 recptprotolistclass * /*protos*/, ostream &logout) {
+
+  // check t argument
+  int arg_t = args.getintarg("t");
+  if (arg_t != 0 && arg_t != 1) {
+    logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
+    cgiarginfo *tinfo = argsinfo.getarginfo ("t");
+    if (tinfo != NULL) args["t"] = tinfo->argdefault;
+  }
+
+  // check k argument
+  int arg_k = args.getintarg("k");
+  if (arg_k != 0 && arg_k != 1) {
+    logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
+    cgiarginfo *kinfo = argsinfo.getarginfo ("k");
+    if (kinfo != NULL) args["k"] = kinfo->argdefault;
+  }
+
+  // check s argument
+  int arg_s = args.getintarg("s");
+  if (arg_s != 0 && arg_s != 1) {
+    logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
+    cgiarginfo *sinfo = argsinfo.getarginfo ("s");
+    if (sinfo != NULL) args["s"] = sinfo->argdefault;
+  }
+
+  // check m argument
+  int arg_m = args.getintarg("m");
+  if (arg_m < -1) {
+    logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
+    cgiarginfo *minfo = argsinfo.getarginfo ("m");
+    if (minfo != NULL) args["m"] = minfo->argdefault;
+  }
+
+  // check o argument
+  int arg_o = args.getintarg("o");
+  if (arg_o < -1) {
+    logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
+    cgiarginfo *oinfo = argsinfo.getarginfo ("o");
+    if (oinfo != NULL) args["o"] = oinfo->argdefault;
+  }
+
+  // check r argument
+  int arg_r = args.getintarg("r");
+  if (arg_r < 1) {
+    logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
+    cgiarginfo *rinfo = argsinfo.getarginfo ("r");
+    if (rinfo != NULL) args["r"] = rinfo->argdefault;
+  }
+  //check hd argument
+  int arg_hd = args.getintarg("hd");
+  if (arg_hd <0 ) {
+    logout << "Warning: \"hd\" argument less than 0 (" << arg_hd << ")\n";
+    cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
+    if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
+  }  
+  
+  //check hs argument
+  int arg_hs = args.getintarg("hs");
+  if (arg_hs !=0 && arg_hs !=1) {
+    logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
+    cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
+    if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
+  }
+
+  // check ct argument
+  int arg_ct = args.getintarg("ct");
+  if (arg_ct < 0 || arg_ct > 2) {
+    logout << "Warning: \"ct\" argument out of range (" << arg_ct << ")\n";
+    cgiarginfo *ctinfo = argsinfo.getarginfo ("ct");
+    if (ctinfo != NULL) args["ct"] = ctinfo->argdefault;
+  }
+
+  // check qt argument
+  int arg_qt = args.getintarg("qt");
+  if (arg_qt !=0 && arg_qt !=1) {
+    logout << "Warning: \"qt\" argument out of range (" << arg_qt << ")\n";
+    cgiarginfo *qtinfo = argsinfo.getarginfo ("qt");
+    if (qtinfo != NULL) args["qt"] = qtinfo->argdefault;
+  }
+
+  // check qb argument
+  int arg_qb = args.getintarg("qb");
+  if (arg_qb !=0 && arg_qb !=1) {
+    logout << "Warning: \"qb\" argument out of range (" << arg_qb << ")\n";
+    cgiarginfo *qbinfo = argsinfo.getarginfo ("qb");
+    if (qbinfo != NULL) args["qb"] = qbinfo->argdefault;
+  }
+
+  // check fqa argument
+  int arg_fqa = args.getintarg("fqa");
+  if (arg_fqa !=0 && arg_fqa !=1) {
+    logout << "Warning: \"fqa\" argument out of range (" << arg_fqa << ")\n";
+    cgiarginfo *fqainfo = argsinfo.getarginfo ("fqa");
+    if (fqainfo != NULL) args["fqa"] = fqainfo->argdefault;
+  }
+
+  // check fqn argument
+  int arg_fqn = args.getintarg("fqn");
+  if (arg_fqn < -1) {
+    logout << "Warning: \"fqn\" argument less than -1 (" << arg_fqn << ")\n";
+    cgiarginfo *fqninfo = argsinfo.getarginfo ("fqn");
+    if (fqninfo != NULL) args["fqn"] = fqninfo->argdefault;
+  }
+
+  return true;
+}
+
+void queryaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
+				    response_t &response, text_t &response_data, 
+				    ostream &/*logout*/) {
+  // If this is an "I'm feeling lucky" request, we don't know the target location until later
+  if (!args["ifl"].empty()) {
+    response = undecided_location;
+    return;
+  }
+
+  response = content;
+  response_data = "text/html";
+}
+
+void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					  recptprotolistclass * protos, 
+					  ostream &logout) {
+
+  // define_internal_macros sets the following macros:
+
+  // The following macros are set later (in define_query_macros) as they can't be set until 
+  // the query has been done.
+  // _quotedquery_   the part of the query string that was quoted for post-processing
+  // _freqmsg_      the term frequency string
+
+  // _resultline_   the "x documents matched the query" string
+
+  // _prevfirst_    these are used when setting up the links to previous/next
+  // _prevlast_     pages of results (_thisfirst_ and _thislast_ are used to set
+  // _nextfirst_    the 'results x-x for query: xxxx' string in the title bar)
+  // _nextlast_
+  // _thisfirst_
+  // _thislast_
+
+  
+  define_form_macros(disp, args, protos, logout);
+  
+  define_query_interface(disp, args, protos, logout);
+
+
+}
+
+void queryaction::define_query_interface(displayclass &disp, 
+					 cgiargsclass &args, 
+					 recptprotolistclass * protos, 
+					 ostream &logout){
+  text_t collection = args["c"];
+
+  //check that the protocol is alive
+  recptproto* colproto = protos->getrecptproto (collection, logout);
+  if(colproto == NULL) {
+    logout << "ERROR: Null collection protocol trying to query"
+	   << collection.getcstr() << "\n";
+    return;
+  }
+  
+  //check the collection is responding/in place
+  ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(colproto, collection,
+							  logout);
+  if(colinfo == NULL){
+    logout << "ERROR: Null returned for get_collectinfo_ptr on "
+	   << collection.getcstr() << "in queryaction::define_query_interface\n";
+    return;
+  }
+   
+  text_tmap::iterator check = colinfo->format.find("QueryInterface");
+  if(check != colinfo->format.end()){
+    if((*check).second=="DateSearch"){
+      text_t current = "_datesearch_";
+      disp.setmacro("optdatesearch","query",current);
+    }
+  }
+}
+
+
+// sets the selection box macros _hselection_, _jselection_, _nselection_ _gselection_, fqfselection_ 
+void queryaction::set_option_macro (const text_t &macroname, 
+				    text_t current_value, 
+				    bool display_single, 
+				    bool add_js_update,
+				    const FilterOption_t &option, 
+				    displayclass &disp) {
+  
+  if (option.validValues.empty()) return;
+  if (option.validValues.size() == 1) {
+    if (display_single) {
+      disp.setmacro (macroname + "selection", displayclass::defaultpackage, "_" + option.defaultValue + "_");
+    }
+    return;    
+  } 
+  if (option.validValues.size() < 2) return;
+  
+  text_t macrovalue = "<select name=\"" + macroname + "\"";
+  if (add_js_update) {
+    macrovalue += " onChange=\"update"+macroname+"();\"";
+  }
+  macrovalue += ">\n";
+  
+  if (current_value.empty()) current_value = option.defaultValue;
+  
+  text_tarray::const_iterator thisvalue = option.validValues.begin();
+  text_tarray::const_iterator endvalue = option.validValues.end();
+
+  while (thisvalue != endvalue) {
+    macrovalue += "<option value=\"" + *thisvalue + "\"";
+    if (*thisvalue == current_value)
+      macrovalue += " selected";
+    macrovalue += ">_" + *thisvalue + "_\n";
+    ++thisvalue;
+  }
+  macrovalue += "</select>\n";
+  disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
+}
+
+
+void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args, 
+					  recptprotolistclass *protos, ostream &logout) {
+
+  // define_external_macros sets the following macros:
+
+  // some or all of these may not be required to be set
+  // _hselection_, _h2selection_  the selection box for the main part of the index
+  // _jselection_, _j2selection_  the selection box for the subcollection part of the index
+  // _nselection_, _n2selection_  the selection box for the language part of the index
+  // _cq2selection                the selection box for combining two queries
+  
+  // _gselection_, the selection box forlevels (mgpp)
+  // _fqfselection_, the selection box for index/fields (mgpp)
+  // can't do anything if collectproto is null (i.e. no collection was specified)
+  recptproto *collectproto = protos->getrecptproto (args["c"], logout);
+  if (collectproto == NULL) return;
+
+  ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto, 
+							  args["c"],
+							  logout);
+  set_query_type_args(colinfo, args);
+  set_stem_index_args(colinfo, args);
+
+  comerror_t err;
+  InfoFilterOptionsResponse_t response;
+  InfoFilterOptionsRequest_t request;
+  request.filterName = "QueryFilter";
+  
+  collectproto->get_filteroptions (args["c"], request, response, err, logout);
+  if (err == noError) {
+    
+    FilterOption_tmap::const_iterator it;
+    FilterOption_tmap::const_iterator end = response.filterOptions.end();
+    
+    // _hselection_ and _h2selection_ (Index)
+    it = response.filterOptions.find ("Index");
+    if (it != end) set_option_macro ("h", args["h"], true, false, (*it).second, disp);
+    if (it != end) set_option_macro ("h2", args["h2"], true,false, (*it).second, disp);
+    
+    // _jselection_ and _j2selection_ (Subcollection)
+    it = response.filterOptions.find ("Subcollection");
+    if (it != end) set_option_macro ("j", args["j"], true,false, (*it).second, disp);
+    if (it != end) set_option_macro ("j2", args["j2"], true,false, (*it).second, disp);
+    
+    // _nselection_ and _n2selection_ (Language)
+    it = response.filterOptions.find ("Language");
+    if (it != end) set_option_macro ("n", args["n"], true,false, (*it).second, disp);
+    if (it != end) set_option_macro ("n2", args["n2"], true,false, (*it).second, disp);
+
+    // _cq2selection_ (CombineQuery)
+    it = response.filterOptions.find ("CombineQuery");
+    if (it != end) set_option_macro ("cq2", args["cq2"], true,false, (*it).second, disp);
+
+    if ((args["ct"] == "1") || (args["ct"] == "2"))  { // mgpp/lucene collections
+      // _gselection_ (Level) 
+      it = response.filterOptions.find("Level");
+      if (it!=end) {
+	set_option_macro("g", args["g"], false, false, (*it).second, disp);
+	if (args["qt"]=="1") { // form search
+	  set_gformselection_macro(args["g"], (*it).second, disp);
+	}
+      }
+      // _fqfselection_ field list
+      it = response.filterOptions.find("IndexField");
+      if (it!=end) {
+	bool form_search = false;
+	if (args["qto"]=="2" || args["qt"]=="1") { 
+	  form_search = true;
+	}
+	set_option_macro ("fqf", args["fqf"], true, form_search, (*it).second, disp);
+	if (args["ct"] == "2") {// lucene
+	  // set the sort field macro
+	  set_sfselection_macro(args["sf"], (*it).second, disp);
+	}
+      }
+    }
+  }
+} // define external macros
+  
+void queryaction::set_sfselection_macro(text_t current_value, 
+				       const FilterOption_t &option, 
+				       displayclass &disp) {
+
+  // we need at least one option here to continue
+  if (option.validValues.size() < 1) {
+    return;
+  }
+
+  text_t macrovalue = "<select name=\"sf\">\n";
+  
+  if (current_value.empty()) current_value = "";
+  
+  // we give a rank option first
+  macrovalue += "<option value=\"\"";
+  if (current_value == "") {
+    macrovalue += " selected";
+  }
+  macrovalue += ">_query:textsortbyrank_\n";
+
+  text_tarray::const_iterator thisvalue = option.validValues.begin();
+  text_tarray::const_iterator endvalue = option.validValues.end();
+  int valid_count = 0;
+  while (thisvalue != endvalue) {
+    if (*thisvalue != "ZZ" && *thisvalue != "TX") {
+      ++valid_count;
+      macrovalue += "<option value=\"by" + *thisvalue + "\"";
+      if (current_value == "by"+*thisvalue)
+	macrovalue += " selected";
+      macrovalue += ">_" + *thisvalue + "_\n";
+    }
+    ++thisvalue;
+  }
+  macrovalue += "</select>";
+  if (valid_count > 0) {
+    disp.setmacro ("sfselection", displayclass::defaultpackage, macrovalue);
+  }
+  
+}
+ 
+// sets the selection box macro _gformselection_.
+// the default for _gformselection_ is _gselection_
+void queryaction::set_gformselection_macro (text_t current_value,
+					const FilterOption_t &option,
+					displayclass &disp) {
+  
+  if (option.validValues.size() <= 1) {
+    return;
+  }
+  // we need to check to see if there is paragraph present
+  text_tarray::const_iterator thisvalue = option.validValues.begin();
+  text_tarray::const_iterator endvalue = option.validValues.end();
+
+  bool has_paras = false;
+  while (thisvalue != endvalue) {
+    if (*thisvalue == "Para") {
+      has_paras = true;
+      break;
+    }
+    ++thisvalue;
+  }
+  if (!has_paras) return; // there is no difference between the form selection and the normal one
+
+  if (option.validValues.size() == 2) {
+    // we will only have one value, but we will still put it in as a text string
+    int opt = 0;
+    if (option.validValues[0] == "Para") {
+      opt = 1;
+    }
+    disp.setmacro ("gformselection", displayclass::defaultpackage, "_"+option.validValues[opt]+"_");
+    return;
+  }
+
+  // there will be a select box
+  text_t macrovalue = "<select name=\"g\">\n";
+
+  if (current_value.empty()) current_value = option.defaultValue;
+  
+  thisvalue = option.validValues.begin();
+  
+  while (thisvalue != endvalue) {
+    if (*thisvalue != "Para") {
+      macrovalue += "<option value=\"" + *thisvalue + "\"";
+      if (*thisvalue == current_value)
+	macrovalue += " selected";
+      macrovalue += ">_" + *thisvalue + "_\n";
+    }
+    ++thisvalue;
+  }
+  macrovalue += "</select>\n";
+  disp.setmacro ("gformselection", displayclass::defaultpackage, macrovalue);
+}
+void queryaction::define_form_macros (displayclass &disp, cgiargsclass &args, 
+			   recptprotolistclass *protos, ostream &logout) {
+
+  // defines the following macros
+  // _regformlist_
+  // _advformlist_
+
+  if (args["ct"]=="0" || args["qto"]=="1" || (args["qto"]=="3" && args["qt"] == "0") ) // mg, or mgpp/lucene with plain only, or mgpp with both, but set to plain
+    return; // dont need these macros
+
+  text_t form = "";
+  int argfqn = args.getintarg("fqn");
+  
+  if (args["b"] == "1") { // advanced form
+    form += "_firstadvformelement_\n";
+    for (int i=1; i<argfqn; ++i) {      
+	form += "_advformelement_\n";
+    }
+    disp.setmacro("advformlist", "query", form);
+  }
+  else { // simple form
+    for (int i=0; i<argfqn; ++i) {
+      form += "_regformelement_\n"; 
+    }
+    disp.setmacro("regformlist", "query", form);
+  }
+  
+}
+
+void queryaction::define_history_macros (displayclass &disp, cgiargsclass &args, 
+					 recptprotolistclass *protos, ostream &logout) {
+
+  // defines the following macros
+  // _searchhistorylist_
+
+  text_t historylist;
+  int arghd = args.getintarg("hd");
+  if (arghd == 0) {
+    historylist="";
+  }
+  else {
+    historylist = "<!-- Search History List -->\n";
+   
+    text_t userid = args["z"];
+    text_tarray entries;
+    if (get_history_info (userid, entries, dbhome, logout)) {
+      int count = 1;
+      text_tarray::iterator here = entries.begin();
+      text_tarray::iterator end = entries.end();
+      int numrecords=(int)entries.size();
+      if (numrecords>arghd) { // only display some of them 
+	numrecords = arghd;
+      }
+      historylist += "<form action=\"_gwcgi_\" name=\"HistoryForm\"><table width=\"537\">\n";
+
+      for (int i=0; i<numrecords;++i) {
+	text_t query;
+	text_t numdocs;
+	text_t cgiargs;
+	text_t userinfo;
+	text_t escquery;
+	split_saved_query(entries[i],numdocs,cgiargs);
+	parse_saved_args(cgiargs, "q", query); // get query string out
+	decode_cgi_arg(query); // un cgisafe it
+	escquery = escape_quotes(query); // escape the quotes and newlines
+	text_t histvalue = "histvalue";
+	histvalue += i;
+	disp.setmacro(histvalue, "query", escquery);
+	format_user_info(cgiargs, userinfo, args, protos, logout);
+	
+	historylist += "<tr><td align=\"right\">_imagehistbutton_(";
+	historylist += i;
+	historylist += ")</td>\n";
+	historylist += "<td><table border=\"1\" cellspacing=\"0\" ";
+	historylist += "cellpadding=\"0\"><tr><td width=\"365\" align=\"left\">"
+	  + query 
+	  + "</td></tr></table></td><td width=\"110\" align=\"center\"><small>"
+	  + numdocs;
+	if (numdocs == 1) historylist += " _texthresult_";
+	else historylist += " _texthresults_";
+	if (!userinfo.empty()) {
+	  historylist += "<br>( "+userinfo+" )";
+	}
+	historylist += "</small></td>\n";
+      }
+      historylist+="</table></form>\n\n";
+     
+    } // if get history info 
+    else {
+      historylist += "_textnohistory_";
+    }
+    historylist += "<! ---- end of history list ----->\n";
+  } // else display list
+  disp.setmacro("searchhistorylist", "query", historylist);
+  
+} // define history macros
+
+void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
+			      displayclass &disp, outconvertclass &outconvert,
+			      ostream &textout, ostream &logout) {
+
+  ColInfoResponse_t *cinfo = NULL;
+  comerror_t err;
+  InfoFilterOptionsResponse_t fresponse;
+  InfoFilterOptionsRequest_t frequest;
+  frequest.filterName = "QueryFilter";
+
+  text_t &index = args["h"];
+  text_t &subcollection = args["j"];
+  text_t &language = args["n"];
+
+  text_tset collections;
+  text_t arg_cc = args["cc"];
+  decode_cgi_arg (arg_cc);
+  splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
+
+  textout << outconvert << disp << "_query:header_\n"
+	  << "<center>_navigationbar_</center><br>\n"
+	  << "<form name=\"QueryForm\" method=\"get\" action=\"_gwcgi_\">\n"
+	  << "<input type=\"hidden\" name=\"a\" value=\"q\">\n"
+          << "<input type=\"hidden\" name=\"site\" value=\"_cgiargsite_\"\n"
+	  << "<input type=\"hidden\" name=\"e\" value=\"_compressedoptions_\">\n"
+	  << "<input type=\"hidden\" name=\"ccp\" value=\"1\">\n"
+	  << "<center><table width=\"_pagewidth_\"><tr valign=\"top\">\n"
+	  << "<td>Select collections to search for \"" << args["q"] 
+	  << "\" <i>(index=" << index << " subcollection=" << subcollection
+	  << " language=" << language << ")</i></td>\n"
+	  << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
+	  << "</tr></table></center>\n"
+	  << "<center><table width=\"_pagewidth_\">\n"
+	  << "<tr><td>\n";
+  
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+      
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+	while (collist_here != collist_end) {
+	  
+	  cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  //	  if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
+	  if (cinfo != NULL && (cinfo->buildDate > 0)) {
+	    
+	    (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
+	    if (err == noError) {
+	      
+	      FilterOption_tmap::const_iterator it;
+	      FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
+	      if (!index.empty()) {
+		it = fresponse.filterOptions.find ("Index");
+		if (it == end) {++collist_here; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == index) break;
+		  ++there;
+		}
+		if (there == tend) {++collist_here; continue;}
+	      }
+	      if (!subcollection.empty()) {
+		it = fresponse.filterOptions.find ("Subcollection");
+		if (it == end) {++collist_here; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == subcollection) break;
+		  ++there;
+		}
+		if (there == tend) {++collist_here; continue;}
+	      }
+	      if (!language.empty()) {
+		it = fresponse.filterOptions.find ("Language");
+		if (it == end) {++collist_here; continue;}
+		text_tarray::const_iterator there = (*it).second.validValues.begin();
+		text_tarray::const_iterator tend = (*it).second.validValues.end();
+		while (there != tend) {
+		  if (*there == language) break;
+		  ++there;
+		}
+		if (there == tend) {++collist_here; continue;}
+	      }
+	    
+	      // we've got a matching collection
+	      textout << outconvert << "<input type=\"checkbox\"";
+
+	      text_tset::const_iterator t = collections.find (*collist_here);
+	      if (t != collections.end()) textout << outconvert << " checked";
+	      
+	      text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	      if (collectionname.empty()) {
+		collectionname = *collist_here;
+	      }
+	      textout << outconvert << disp 
+		      << " name=\"cc\" value=\"" << *collist_here << "\">"
+		      << collectionname << "<br>\n";
+
+	      
+	    }
+	  }
+	  ++collist_here;
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+  textout << outconvert << disp 
+	  << "</td></tr></table></center>\n"
+	  << "</form>\n"
+	  << "_query:footer_\n";
+  
+}
+
+bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			     browsermapclass *browsers, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &logout) {
+  
+  if (recpt == NULL) {
+    logout << "ERROR (queryaction::do_action): This action does not contain information\n"
+	   << "      about any receptionists. The method set_receptionist was probably\n"
+	   << "      not called from the module which instantiated this action.\n";
+    return true;
+  }
+
+
+
+  if (args["ccs"] == "1") {
+    if (!args["cc"].empty()) {
+      // query the selected collections
+      text_t::const_iterator b = args["cc"].begin();
+      text_t::const_iterator e = args["cc"].end();
+      if (findchar (b, e, ',') != e) {
+	if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
+					  textout, logout)) return false;
+	return true;
+      } else {
+	if (!search_single_collection (args, args["cc"], protos, browsers, disp, 
+				       outconvert, textout, logout)) return false;
+  	return true;
+      }
+    }
+  }
+
+  // simply query the current collection
+  if (!search_single_collection (args, args["c"], protos, browsers, disp, 
+				 outconvert, textout, logout)) return false;
+  return true;
+}
+
+bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
+					       browsermapclass *browsers, displayclass &disp,
+					       outconvertclass &outconvert, ostream &textout,
+					       ostream &logout) {
+
+  text_tarray collections;
+  
+  text_t arg_cc = args["cc"];
+  decode_cgi_arg (arg_cc);
+  splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
+
+  if (collections.empty()) {
+    logout << "queryaction::search_multiple_collections: No collections "
+	   << "set for doing multiple query - will search current collection\n";
+    textout << outconvert << disp << "_query:textwarningnocollections_\n";
+    return search_single_collection (args, args["c"], protos, browsers, disp, 
+				     outconvert, textout, logout);
+  }
+
+  // queryaction uses "VList" browser to display results,
+  // a queries clasification is "Search"
+  text_t browsertype = "VList";
+  text_t classification = "Search";
+
+  QueryResult_tset results;
+  map<text_t, colinfo_t, lttext_t> colinfomap;
+
+  ColInfoResponse_t *cinfo = NULL;
+  recptproto *collectproto = NULL;
+  comerror_t err;
+  FilterRequest_t request;
+  FilterResponse_t response;
+  request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
+  text_t freqmsg = "_textfreqmsg1_";
+  int numdocs = 0;
+  isapprox isApprox = Exact;
+
+  // what to do about segmentation for multiple colls??
+  bool segment = false;
+  text_t formattedstring = "";
+  get_formatted_query_string(formattedstring, segment, args, disp, logout);
+
+  if (formattedstring.empty()) {
+    // dont bother doing a query if no query string
+    define_history_macros (disp, args, protos, logout);
+    textout << outconvert << disp << "_query:header_\n"
+	  << "_query:content_";
+    textout << outconvert << disp << "_query:footer_";
+
+    return true;
+  }
+  bool syntax_error = false;
+
+  set_queryfilter_options (request, formattedstring, args);
+
+  // need to retrieve maxdocs matches for each collection
+  // (will eventually want to tidy this up, do so caching etc.)
+  OptionValue_t option;
+  option.name = "StartResults";
+  option.value = "1";
+  request.filterOptions.push_back (option);
+  
+  option.name = "EndResults";
+  option.value = args["m"];
+  request.filterOptions.push_back (option);
+
+  text_tarray::iterator col_here = collections.begin();
+  text_tarray::iterator col_end = collections.end();
+
+  map<text_t, int, lttext_t> termfreqs;
+  
+  // just check the main col for formatting info - use individual format statements, or the main one?
+  
+  browserclass *bptr = browsers->getbrowser (browsertype);
+  
+  text_t main_col = args["c"];
+  cinfo = recpt->get_collectinfo_ptr (collectproto, main_col, logout);
+  if (cinfo == NULL) {
+    logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL for '"<<main_col<<"'\n";
+    return false;
+  }
+  
+  bool use_main_col_format = false;
+  if (cinfo->ccsOptions & CCSUniformSearchResultsFormatting) {
+    use_main_col_format = true;
+  }
+ 
+  request.fields.erase (request.fields.begin(), request.fields.end());
+  request.getParents = false;
+  bptr->load_metadata_defaults (request.fields);
+  
+  text_t formatstring;
+  format_t *formatlistptr = new format_t();
+  if (use_main_col_format) {
+    // just get one format for main coll and use it for each subcol
+    if (!get_formatstring (classification, browsertype, 
+			   cinfo->format, formatstring)) {
+      formatstring = bptr->get_default_formatstring();
+    }
+    
+    parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
+  }
+  
+  while (col_here != col_end) {
+
+    collectproto = protos->getrecptproto (*col_here, logout);
+    if (collectproto == NULL) {
+      logout << outconvert << "queryaction::search_multiple_collections: " << *col_here 
+	     << " collection has a NULL collectproto, ignoring\n";
+      ++col_here;
+      continue;
+    }
+    cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
+    if (cinfo == NULL) {
+      logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL\n";
+      ++col_here;
+      continue;
+    }
+
+    if (!use_main_col_format) {
+      request.fields.erase (request.fields.begin(), request.fields.end());
+      request.getParents = false;
+      bptr->load_metadata_defaults (request.fields);
+      
+    //browserclass *bptr = browsers->getbrowser (browsertype);
+
+    // get the formatstring if there is one 
+      if (!get_formatstring (classification, browsertype, 
+			     cinfo->format, formatstring)) {
+	formatstring = bptr->get_default_formatstring();
+      }
+
+      formatlistptr = new format_t();
+      parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
+    }
+    
+    colinfo_t thiscolinfo;
+    thiscolinfo.formatlistptr = formatlistptr;
+    thiscolinfo.browserptr = bptr;
+    colinfomap[*col_here] = thiscolinfo;
+    
+    // do the query
+    collectproto->filter (*col_here, request, response, err, logout);
+    if (err != noError && err != syntaxError) {
+      outconvertclass text_t2ascii;
+      logout << text_t2ascii
+	     << "queryaction::search_multiple_collections: call to QueryFilter failed "
+	     << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
+      return false;
+    }
+
+    if (err == syntaxError) {
+      syntax_error = true;
+      freqmsg = "_textinvalidquery_";
+      // assume the syntax will be invalid for all colls
+      break;
+    }
+    if (response.error_message == "TOO_MANY_CLAUSES") {
+      freqmsg = "_textlucenetoomanyclauses_";
+      break;
+    }
+    if (isApprox == Exact)
+      isApprox = response.isApprox;
+    else if (isApprox == MoreThan)
+      if (response.isApprox == Approximate)
+	isApprox = response.isApprox;
+
+    TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
+    TermInfo_tarray::const_iterator end_term = response.termInfo.end();
+    while (this_term != end_term) {
+      termfreqs[(*this_term).term] += (*this_term).freq;
+      if ((col_here+1) == col_end) {
+	freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
+	if ((this_term+1) != end_term) freqmsg += ", ";
+      }
+      ++this_term;
+    }
+
+    if (response.numDocs > 0) {
+      numdocs += response.numDocs;
+    
+      QueryResult_t thisresult;
+      thisresult.collection = *col_here;
+      ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
+      ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
+      while (doc_here != doc_end) {
+	thisresult.doc = *doc_here;
+	results.insert (thisresult);
+	++doc_here;
+      }
+    }
+    ++col_here;
+  } // for each coll
+  
+  text_t numdocs_t = numdocs;  
+  args["nmd"] = numdocs_t;
+
+  disp.setmacro ("freqmsg", "query", freqmsg);
+
+  define_query_macros( args, disp, numdocs, isApprox);
+  // save the query if appropriate
+  save_search_history(args, numdocs, isApprox);
+  define_history_macros (disp, args, protos, logout);
+
+  textout << outconvert << disp << "_query:header_\n"
+	  << "_query:content_";
+
+  if (!syntax_error) {
+    
+    // now go through each result and output it
+    QueryResult_tset::iterator res_here = results.begin();
+    QueryResult_tset::iterator res_end = results.end();
+    text_tset metadata; // empty !!
+    bool getParents = false; // don't care !!
+    bool use_table;
+    ResultDocInfo_t thisdoc;
+    format_t *formatlistptr = NULL;
+    browserclass *browserptr = NULL;
+    
+    int count = 1;
+    int firstdoc = args.getintarg("r");
+    int hitsperpage = args.getintarg("o");
+    int thislast = firstdoc + (hitsperpage - 1);
+    
+    // output results
+    while (res_here != res_end) {
+      if (count < firstdoc) {++count; ++res_here; continue;}
+      if (count > thislast) break;
+      
+      formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
+      browserptr = colinfomap[(*res_here).collection].browserptr;
+      thisdoc = (*res_here).doc;
+      use_table = is_table_content (formatlistptr);
+      
+      collectproto = protos->getrecptproto ((*res_here).collection, logout);
+      if (collectproto == NULL) {
+	logout << outconvert << "queryaction::search_multiple_collections: " << (*res_here).collection
+	       << " collection has a NULL collectproto, ignoring results\n";
+	++res_here;
+	continue;
+      }
+      
+      browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0, 
+					formatlistptr, use_table, metadata, getParents, 
+					collectproto, disp, outconvert, textout, logout);
+      //    textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
+      ++res_here;
+      ++count;
+    }
+  }
+  textout << outconvert << disp << "_query:footer_";
+  
+  // clean up the format_t pointers
+  map<text_t, colinfo_t, lttext_t>::iterator here =  colinfomap.begin();
+  map<text_t, colinfo_t, lttext_t>::iterator end =  colinfomap.end();
+  while (here != end) {
+    delete ((*here).second.formatlistptr);
+    ++here;
+  }
+  return true;
+}
+
+bool queryaction::search_single_collection (cgiargsclass &args, const text_t &collection,
+					    recptprotolistclass *protos, browsermapclass *browsers, 
+					    displayclass &disp, outconvertclass &outconvert, 
+					    ostream &textout, ostream &logout) {
+
+  recptproto *collectproto = protos->getrecptproto (collection, logout);
+  if (collectproto == NULL) {
+    logout << outconvert << "queryaction::search_single_collection: " << collection
+	   << " collection has a NULL collectproto\n";
+
+    // Display the "this collection is not installed on this system" page
+    disp.setmacro("cvariable", displayclass::defaultpackage, collection);
+    disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
+
+    textout << outconvert << disp << "_query:header_\n"
+	    << "_query:content_\n" << "_query:footer_\n";
+    return true;
+  }
+
+  // queryaction uses "VList" browser to display results,
+  // a queries clasification is "Search"
+  text_t browsertype = "VList";
+  text_t classification = "Search";
+
+  comerror_t err;
+  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
+
+  if (cinfo == NULL) {
+    logout << "ERROR (query_action::search_single_collection): get_collectinfo_ptr returned NULL\n";
+    return false;
+  }
+    
+  bool segment = cinfo->isSegmented;
+  browserclass *bptr = browsers->getbrowser (browsertype);
+
+  // get the formatstring if there is one 
+  text_t formatstring;
+  if (!get_formatstring (classification, browsertype, 
+			 cinfo->format, formatstring)) {
+    formatstring = bptr->get_default_formatstring();
+  }
+  FilterRequest_t request;
+  FilterResponse_t response;
+
+  text_t maxDoc_old = args["o"];
+  text_t starts_old = args["r"];  
+
+    //if the "ifl" argument is set ignore the "r" and "o" arg
+  if(!args["ifl"].empty()){
+    args["r"] = 1;
+    args["o"] = args["m"];
+  }
+ 
+
+  bptr->set_filter_options (request, args);
+  bptr->load_metadata_defaults (request.fields);
+
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
+    
+  // do the query
+  request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
+  text_t formattedstring = "";
+  get_formatted_query_string(formattedstring, segment, args, disp, logout);
+
+
+  if (!formattedstring.empty()) { // do the query
+    // note! formattedstring is in unicode! mg and mgpp must convert!
+    set_queryfilter_options (request, formattedstring, args);
+
+    collectproto->filter (collection, request, response, err, logout);
+
+    if (err != noError) {
+      outconvertclass text_t2ascii;
+      logout << text_t2ascii
+	     << "queryaction::search_single_collections: call to QueryFilter failed "
+	     << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
+     
+    }
+
+    // Perform the "I'm feeling lucky" trick if the "ifl" argument is set
+    if (err == noError && !args["ifl"].empty()) {
+      //Restore the "r" and "o" arg
+      args["r"] = starts_old;
+      args["o"] = maxDoc_old ;
+
+      //Find whether DocumentSearchResultLinks is enabled
+      bool show_links = false;
+      text_tmap::const_iterator format_here = cinfo->format.begin();
+      text_tmap::const_iterator format_end = cinfo->format.end();
+
+       while (format_here != format_end) {
+	 if (((*format_here).first == "DocumentSearchResultLinks") && 
+	     ((*format_here).second == "true")){
+	   show_links = true;
+           break;
+	 }
+         ++format_here;
+       } 
+
+       if (args["ifl"] == 1 || (args["ifl"] == 2 && response.numDocs == 1))
+       {
+	 // Find the search result specified by the ifln argument   
+	 ResultDocInfo_tarray::iterator thissection = response.docInfo.begin();
+	 int section_number = 1;
+	 int ifln = args["ifln"].getint();
+              
+	 while (section_number != ifln  && thissection != response.docInfo.end()) {   	
+	   thissection++;
+	   section_number++;
+	 }
+	 // If the search result exists, go directly to it
+	 if (section_number == ifln && thissection != response.docInfo.end()) {
+	   // Location response (this URL must have "&" and not "&amp;"!)
+	   int num_docs =  response.numDocs;
+	   int srn = 0;
+	   int srp = 0;
+	   if (show_links && section_number < num_docs ) {
+	     srn = section_number + 1;
+	   }
+	   if (show_links && section_number > 1 ) {
+	     srp = section_number - 1;
+	   }
+        
+	   textout << outconvert << disp << "Location: _gwcgi_?e=_compressedoptions_&a=d&c=" << collection << "&cl=search&d=" << (*thissection).OID <<"&srn="<<srn<<"&srp="<<srp<< "\n\n";
+	   textout << flush;
+	   
+	   return true;
+	 }
+       }
+       
+      // There weren't enough (or any) matching documents
+      // We'll just carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
+      // done the right thing (because ifl was set), so we need to make sure the output is html
+      textout << "Content-type: text/html\n\n";
+      
+    }
+
+    if (err != noError) {
+      disp.setmacro("resultline", "query", "_textnodocs_");
+      if (err == syntaxError) {
+	disp.setmacro ("freqmsg", "query", "_textinvalidquery_");
+      } else {
+	disp.setmacro ("freqmsg", "query", "");
+      }
+    } else {
+      
+      define_query_macros (args, disp, response.numDocs, response.isApprox);
+      define_single_query_macros(args, disp, response);
+      // save the query if appropriate
+      save_search_history(args, response.numDocs, response.isApprox);
+    }
+
+    // If Lucene threw a TooManyClauses exception, tell the user about it
+    if (args["ct"] == 2 && response.error_message == "TOO_MANY_CLAUSES") {
+      disp.setmacro ("freqmsg", "query", "_textlucenetoomanyclauses_");
+    }
+  }
+  define_history_macros (disp, args, protos, logout);
+
+  textout << outconvert << disp << "_query:header_\n"
+	  << "_query:content_";
+  
+  if (err == noError) {
+    // output the results
+    text_t numdocs_t = response.numDocs;  
+    args["nmd"] = numdocs_t;
+    bool use_table = is_table_content (formatlistptr);
+    bptr->output_section_group (response, args, collection, 0, formatlistptr, 
+				use_table, request.fields, request.getParents, 
+				collectproto, disp, outconvert, textout, logout);
+  }
+
+  textout << outconvert << disp << "_query:footer_";
+  
+  delete (formatlistptr);
+  
+  return true;
+}
+ 
+// does the formatting of the query string - either uses q for a text search
+// or the form values for an form search
+// also adds dates if appropriate in text search
+void queryaction::get_formatted_query_string (text_t &formattedstring, 
+					      bool segment,
+					      cgiargsclass &args, 
+					      displayclass &disp,
+					      ostream &logout) {
+  if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
+    formattedstring = args["q"];
+    // remove & | ! for simple search,do segmentation if necessary
+    format_querystring (formattedstring, args.getintarg("b"), segment);
+    if (args["ct"]!=0) { // mgpp and lucene - need to add in tag info if appropriate
+      format_field_info(formattedstring, args["fqf"], args.getintarg("ct"), 
+			args.getintarg("t"), args.getintarg("b"));
+    }
+
+    add_dates(formattedstring, args.getintarg("ds"), args.getintarg("de"),
+	      args.getintarg("dsbc"), args.getintarg("debc"),
+	      args.getintarg("ct"));
+    args["q"] = formattedstring;
+    
+  }
+  else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
+
+    if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
+      formattedstring = args["q"];
+    }
+    else { // form search
+      if (args["b"]=="0") { // regular form
+	parse_reg_query_form(formattedstring, args, segment);
+      }
+      else  { // advanced form
+	parse_adv_query_form(formattedstring, args, segment);
+      }
+      args["q"] = formattedstring;
+      
+      // reset the cgiargfqv macro - need to escape any quotes in it
+      disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
+
+      // also reset the _cgiargq_ macro as it has changed now
+      disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
+      
+      // reset the compressed options to include the q arg
+      text_t compressedoptions = recpt->get_compressed_arg(args, logout);
+      if (!compressedoptions.empty()) {
+	disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
+	// need a decoded version of compressedoptions for use within forms
+	// as browsers encode values from forms before sending to server
+	// (e.g. %25 becomes %2525)
+	decode_cgi_arg (compressedoptions); 
+	if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
+    // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
+	  compressedoptions = to_uni(compressedoptions);
+	}
+
+	disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
+      }
+    } // form search
+  } // args["qt"]=1
+  else {
+    logout << "ERROR (query_action::get_formatted_query_string): querytype not defined\n";
+  }
+}
+
+
+// define_query_macros sets the macros that couldn't be set until the
+// query had been done. Those macros are 
+// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
+// _thisfirst_, and _thislast_ and _quotedquery_ 
+// this has been simplified so it can be used with both search_single_coll
+// and search_multiple_coll
+void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
+				       int numdocs, isapprox isApprox) {
+  
+  // set up _resultline_ macro
+  text_t resline;
+  int maxdocs = args.getintarg("m");
+  if (num_phrases > 0) isApprox = Exact;
+  if (maxdocs == -1) maxdocs = numdocs;
+  else if (numdocs > maxdocs) {
+    numdocs = maxdocs;
+    isApprox = MoreThan;
+  }
+
+  if (isApprox == Approximate) resline = "_textapprox_";
+  else if (isApprox == MoreThan) resline = "_textmorethan_";
+  
+  if (numdocs == 0) resline = "_textnodocs_";
+  else if (numdocs == 1) resline += "_text1doc_";
+  else resline += text_t(numdocs) + " _textlotsdocs_";
+  
+  disp.setmacro("resultline", "query", resline);
+
+  int firstdoc = args.getintarg("r");
+  int hitsperpage = args.getintarg("o");
+  if (hitsperpage == -1) hitsperpage = numdocs;
+
+  // set up _thisfirst_ and _thislast_ macros
+  disp.setmacro ("thisfirst", "query", firstdoc);
+  int thislast = firstdoc + (hitsperpage - 1);
+  if (thislast > numdocs) thislast = numdocs;
+  disp.setmacro ("thislast", "query", thislast);
+
+  // set up _prevfirst_ and _prevlast_ macros
+  if (firstdoc > 1) {
+    disp.setmacro ("prevlast", "query", firstdoc - 1);
+    int prevfirst = firstdoc - hitsperpage;
+    if (prevfirst < 1) prevfirst = 1;
+    disp.setmacro ("prevfirst", "query", prevfirst);
+  }
+
+  // set up _nextfirst_ and _nextlast_ macros
+  if (thislast < numdocs) {
+    disp.setmacro ("nextfirst", "query", thislast + 1);
+    int nextlast = thislast + hitsperpage;
+    if (nextlast > numdocs) nextlast = numdocs;
+    disp.setmacro ("nextlast", "query", nextlast);
+  }
+
+  // do quoted query here cos we may have added quotes during query pre-processing
+  if (args["ct"]==0) { // mg queries only, not mgpp
+    // get the quoted bits of the query string and set _quotedquery_
+    text_tarray phrases;
+    get_phrases (args["q"], phrases);
+    num_phrases = phrases.size();
+    text_tarray::const_iterator phere = phrases.begin();
+    text_tarray::const_iterator pend = phrases.end();
+    bool first = true;
+    text_t quotedquery;
+    while (phere != pend) {
+      if (!first)
+	if ((phere +1) == pend) quotedquery += " and ";
+	else quotedquery += ", ";
+      
+      quotedquery += "\"" + *phere + "\"";
+      first = false;
+      ++phere;
+    }
+    if (args.getintarg("s") && !quotedquery.empty()) quotedquery += "_textstemon_";
+    disp.setmacro ("quotedquery", "query", quotedquery);
+  }
+
+}
+
+// define_single_query_macros sets the extra macros for search_single_coll 
+// that couldn't be set until the query had been done. Those macros are 
+// _freqmsg_ and _stopwordsmsg_
+void queryaction::define_single_query_macros (cgiargsclass &args, 
+					      displayclass &disp, 
+					      const FilterResponse_t &response) {
+  // set up _freqmsg_ and _stopwordsmsg_ macros
+
+  text_t freqmsg = "";
+  freqmsg = "_textfreqmsg1_";
+  TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
+  TermInfo_tarray::const_iterator end_term = response.termInfo.end();
+  while (this_term != end_term) {
+    freqmsg += (*this_term).term + ": " + (*this_term).freq;
+    if ((this_term + 1) != end_term)
+      freqmsg += ", ";
+    ++this_term;
+  }
+  disp.setmacro ("freqmsg", "query", freqmsg);
+
+  text_tset::const_iterator this_stopword = response.stopwords.begin();
+  text_tset::const_iterator end_stopword = response.stopwords.end();
+  if (this_stopword != end_stopword) {
+    text_t stopwordsmsg = "_textstopwordsmsg_ ";
+    while (this_stopword != end_stopword) {
+      if (stopwordsmsg != "_textstopwordsmsg_ ") {
+	stopwordsmsg += ", ";
+      }
+      stopwordsmsg += (*this_stopword);
+      ++this_stopword;
+    }
+    disp.setmacro("stopwordsmsg", "query", stopwordsmsg);
+  }
+}
+
+// should this change for cross coll search??
+bool queryaction::save_search_history (cgiargsclass &args, int numdocs, 
+				       isapprox isApprox) {
+  if (args["q"]=="") return true; // null query, dont save
+  if (args["hs"]=="0") return true; // only save when submit query pressed
+  
+  // get userid
+  text_t userid = args["z"];
+
+  // the number of docs goes on the front of the query string
+  text_t query = text_t(numdocs);
+  if (isApprox==MoreThan) { // there were more docs found
+    query.push_back('+');
+  }
+  query += "c="+args["c"];
+  query += ";h="+args["h"];
+  query += ";t="+args["t"];
+  query += ";b="+args["b"];
+  query += ";j="+args["j"];
+  query += ";n="+args["n"];
+  query += ";s="+args["s"];
+  query += ";k="+args["k"];
+  query += ";g="+args["g"]; 
+  
+  text_t qstring = args["q"];
+  //text_t formattedquery =cgi_safe(qstring);
+  //query += "&amp;q="+formattedquery;
+  query += ";q="+qstring;
+  bool display=false;
+  int hd = args.getintarg("hd");
+  if (hd > 0) display=true;
+  if (set_history_info(userid, query, dbhome, display)) return true;
+  else return false;
+
+
+}
+
Index: /gsdl/trunk/gs2runtime/src/recpt/queryaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/queryaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/queryaction.h	(revision 16561)
@@ -0,0 +1,153 @@
+/**********************************************************************
+ *
+ * queryaction.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.
+ *
+ *********************************************************************/
+
+
+#ifndef QUERYACTION_H
+#define QUERYACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+#include "receptionist.h"
+
+struct colinfo_t {
+  void clear();
+  colinfo_t () {clear();}
+
+  format_t *formatlistptr;
+  browserclass *browserptr;
+};
+
+struct QueryResult_t {
+  void clear ();
+  QueryResult_t () {clear();}
+
+  ResultDocInfo_t doc;
+  text_t collection;
+};
+
+struct gteqQueryResult_t 
+{
+  bool operator()(const QueryResult_t &t1, const QueryResult_t &t2) const
+  { return t1.doc.ranking >= t2.doc.ranking; }
+};
+
+typedef set<QueryResult_t, gteqQueryResult_t> QueryResult_tset;
+
+
+class queryaction : public action {
+
+protected:
+
+  receptionist *recpt;
+
+  text_t m_strUseInterfaceLanguageForQuery;
+  text_t formatstring;
+  text_tmap reqfields; // required fields
+  int num_phrases;
+
+  virtual void define_single_query_macros (cgiargsclass &args, 
+					   displayclass &disp, 
+					   const FilterResponse_t &response); 
+  virtual void define_query_macros (cgiargsclass &args, displayclass &disp, 
+			    int numDocs, isapprox isApprox); 
+
+  void get_formatted_query_string (text_t &formattedstring, bool segment, 
+				   cgiargsclass &args, 
+				   displayclass &disp, ostream &logout);
+  void define_query_interface(displayclass &disp, cgiargsclass &args, 
+			      recptprotolistclass *protos, ostream &logout);
+  
+  void define_form_macros (displayclass &disp, cgiargsclass &args, 
+			   recptprotolistclass *protos, ostream &logout);
+
+  void define_history_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  virtual void set_option_macro (const text_t &macroname, text_t current_value,
+				 bool display_single, bool add_js_update,
+			 const FilterOption_t &option, displayclass &disp);
+
+  void set_gformselection_macro (text_t current_value,
+				 const FilterOption_t &option,
+				 displayclass &disp);
+  void set_sfselection_macro (text_t current_value, 
+			      const FilterOption_t &option, 
+			      displayclass &disp);
+
+  void output_ccp (cgiargsclass &args, recptprotolistclass *protos,
+		   displayclass &disp, outconvertclass &outconvert,
+		   ostream &textout, ostream &logout);
+  
+  bool search_multiple_collections (cgiargsclass &args, 
+				    recptprotolistclass *protos,
+				    browsermapclass *browsers, 
+				    displayclass &disp,
+				    outconvertclass &outconvert, 
+				    ostream &textout,
+				    ostream &logout);
+  
+  virtual bool search_single_collection (cgiargsclass &args, 
+					 const text_t &collection,
+					 recptprotolistclass *protos, 
+					 browsermapclass *browsers, 
+					 displayclass &disp, 
+					 outconvertclass &outconvert, 
+					 ostream &textout, ostream &logout);
+
+
+  bool save_search_history(cgiargsclass &args, int numdocs, isapprox isApprox);
+
+public:
+  queryaction ();
+  virtual ~queryaction () {}
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+  
+  void configure (const text_t &key, const text_tarray &cfgline);
+  bool init (ostream &logout);
+  
+  virtual text_t get_action_name () {return "q";}
+  
+  virtual bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+			      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+  
+  virtual void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  virtual void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+  
+  virtual bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/querytools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/querytools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/querytools.cpp	(revision 16561)
@@ -0,0 +1,897 @@
+/**********************************************************************
+ *
+ * querytools.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.
+ *
+ *********************************************************************/
+
+#include "querytools.h"
+#include <ctype.h>
+#include "unitool.h" // for is_unicode_letdig
+
+// sets the ct, qt, qto arguments
+void set_query_type_args(ColInfoResponse_t *cinfo, cgiargsclass &args) {
+
+  if (args["ct"].empty()) {
+    text_t build_type = cinfo->buildType;
+    if (build_type == "mgpp") {
+      args["ct"] = "1";
+    } else if (build_type == "lucene") {
+      args["ct"] = "2";
+    } else {
+      args["ct"] = "0";
+    }
+  }
+  text_t arg_ct = args["ct"];
+  if (arg_ct == "0") {
+    // mg
+    args["qt"] = "0";
+    args["qto"] = "0";
+    return;
+  }
+
+  if (!args["qt"].empty() && !args["qto"].empty()) {
+    return;
+  }
+  
+  text_tmap::iterator check = cinfo->format.find("SearchTypes");
+  text_t search_types;
+  if(check != cinfo->format.end() && !(*check).second.empty()){
+    search_types = (*check).second;
+  } else {
+    // assume plain,form
+    if (args["qto"].empty()) args["qto"] = "3";
+    if (args["qt"].empty()) {
+      int arg_qto = args.getintarg("qto");
+      if (arg_qto == 2) {
+	args["qt"] = "1";
+      } else {
+	args["qt"] = "0";
+      }
+    }
+    return;
+  }
+  
+  
+  if (args["qto"].empty()) {
+    unsigned int type = 0;
+    if (findword(search_types.begin(), search_types.end(), "form") != search_types.end()) {
+      type |= 2;
+    }
+    if (findword(search_types.begin(), search_types.end(), "plain") != search_types.end()) {
+      type |= 1;
+    }
+    args.setintarg("qto", type);
+  } 
+  
+  if (args["qt"].empty()) {
+    int arg_qto = args.getintarg("qto");
+    if (arg_qto == 2 || (arg_qto == 3 && starts_with(search_types, "form"))) {
+      args["qt"] = "1";
+    } else {
+      args["qt"] = "0";
+    }
+  }
+}
+
+// sets the ks, ss, afs (casesupport, stemsupport, accentfoldsupport) args
+void set_stem_index_args(ColInfoResponse_t *cinfo, cgiargsclass &args) {
+  int stemIndexes = cinfo->stemIndexes;
+
+  if (stemIndexes & SIcasefold) {
+    args["ks"] = 1;
+  }
+  if (stemIndexes & SIstem) {
+    args["ss"] = 1;
+  }
+  if (stemIndexes & SIaccentfold) {
+    args["afs"] = 1;
+  }
+
+}
+
+// request.filterResultOptions and request.fields (if required) should
+// be set from the calling code
+void set_queryfilter_options (FilterRequest_t &request, 
+			      const text_t &querystring,
+			      cgiargsclass &args) {
+
+  request.filterName = "QueryFilter";
+
+  OptionValue_t option;
+
+  option.name = "Term";
+  option.value = querystring;
+  request.filterOptions.push_back (option);
+
+  option.name = "QueryType";
+  option.value = (args.getintarg("t")) ? "ranked" : "boolean";
+  request.filterOptions.push_back (option);
+
+  option.name = "MatchMode";
+  // mgpp in advanced mode, always use some query
+  if (args.getintarg("ct") == 1 && args.getintarg("b") == 1) {
+    option.value = "some";
+  } else {
+    option.value = (args.getintarg("t")) ? "some" : "all";
+  }
+  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);
+
+  option.name = "AccentFold";
+  option.value = (args.getintarg("af")) ? "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);
+  }
+  
+  if (!args["g"].empty()) { // granularity for mgpp
+    option.name = "Level";
+    option.value = args["g"];
+    request.filterOptions.push_back (option);
+  }
+
+  if (!args["fs"].empty()) { // filter string for lucene
+    option.name = "FilterString";
+    option.value = args["fs"];
+    request.filterOptions.push_back (option);
+  }
+
+  if (!args["sf"].empty()) { // sort field for lucene
+    option.name = "SortField";
+    option.value = args["sf"];
+    request.filterOptions.push_back (option);
+  }
+
+  if (!args["fuzziness"].empty() && args["fuzziness"] != "100") { // fuzziness value for lucene
+    option.name = "Fuzziness";
+    option.value = (text_t) "0." + args["fuzziness"];
+    request.filterOptions.push_back (option);
+  }
+
+  set_more_queryfilter_options (request, args);
+}
+
+void set_queryfilter_options (FilterRequest_t &request, 
+			      const text_t &querystring1,
+			      const text_t &querystring2, cgiargsclass &args) {
+
+  set_queryfilter_options (request, querystring1, args);
+
+  // fill in the second query if needed
+  if (!args["cq2"].empty()) {
+    OptionValue_t option;
+
+    option.name = "CombineQuery";
+    option.value = args["cq2"];
+    request.filterOptions.push_back (option);
+    
+    option.name = "Term";
+    option.value = querystring2;
+    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);
+
+    option.name = "AccentFold";
+    option.value = (args.getintarg("af")) ? "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);
+    }
+  }
+  set_more_queryfilter_options (request, args);
+}
+
+void set_more_queryfilter_options (FilterRequest_t &request, 
+				   cgiargsclass &args) {
+
+  OptionValue_t option;
+  int arg_m = args.getintarg("m");
+  
+  option.name = "Maxdocs";
+  option.value = arg_m;
+  request.filterOptions.push_back (option);
+
+  //  option.name = "StartResults";
+  //  option.value = args["r"];
+  //  request.filterOptions.push_back (option);
+
+  //  option.name = "EndResults";
+  //  int endresults = args.getintarg("o") + (args.getintarg("r") - 1);
+  //  if ((endresults > arg_m) && (arg_m != -1)) endresults = arg_m;
+  //  option.value = endresults;
+  //  request.filterOptions.push_back (option);
+}
+
+bool is_special_character(int indexer_type, unsigned short character) {
+  // mgpp
+  if (indexer_type == 1) {
+    return (character == '#' || character == '/' || character == '*');
+  }
+  // lucene
+  else if (indexer_type == 2) {
+    return (character == '?' || character == '*' || character == '~' || 
+	    character == '^');
+  }
+  return false;
+}
+
+// This function removes boolean operators from simple searches, and segments
+// chinese characters if segment=true
+void format_querystring (text_t &querystring, int querymode, bool segment) {
+  text_t formattedstring;
+
+  // advanced search, no segmenting, don't need to do anything
+  if (querymode == 1 && !segment) return;
+  
+  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 (segment) {
+      if ((*here >= 0x4e00 && *here <= 0x9fa5) ||
+	  (*here >= 0xf900 && *here <= 0xfa2d)) {
+	// Chinese character
+	if (!space) formattedstring.push_back (0x200b); // zero width space
+	formattedstring.push_back (*here);
+	formattedstring.push_back (0x200b);
+	space = true;
+      } else {
+	
+	// non-Chinese character
+	formattedstring.push_back (*here);
+	space = false;
+	
+      }
+    
+    } else {
+      formattedstring.push_back (*here);
+    }
+    ++here;
+  }
+  querystring = formattedstring;
+}
+
+
+	
+
+// search history tool 
+// also used for form query macros
+text_t escape_quotes(const text_t &querystring) {
+
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+ 
+  text_t escquery = "";
+  while (here != end) {
+    if (*here != '\'' && *here != '\"' && *here != '\n' && *here != '\r') escquery.push_back(*here);
+    else if (*here == '\n' || *here == '\r') {
+      escquery.push_back(' ');
+    } else {
+      escquery +="\\\\";
+      escquery.push_back(*here);
+    }
+
+    ++here;
+  }
+  return escquery;
+
+}
+
+// Parses the terms into words, and adds #si if necessary
+text_t addstemcase(const text_t &terms, const text_t &stem, const text_t &fold,
+		   const int indexer_type) {
+  
+  // the default stem and case are set to 0 if this is being used, so we are only adding on qualifiers if stem or fold is 1.
+  if (stem == "0" && fold == "0") {
+    return terms;
+  }
+  // this is only for mgpp collections, shouldn't be called for anything else
+  if (indexer_type != 1) {
+    return terms;
+  }
+  
+  text_t outtext;
+  text_t word;
+
+  text_t::const_iterator here = terms.begin();
+  text_t::const_iterator end = terms.end();
+
+  while (here !=end) {
+
+    if (is_unicode_letdig(*here) || is_special_character(indexer_type, *here)) {
+      // not word boundary
+      word.push_back(*here);
+      ++here;    
+    }
+    else {
+      // found word boundary   
+      if (!word.empty() ) {
+	if (starts_with(word, "NEAR") || starts_with(word, "WITHIN")) {
+	  outtext += word;
+	  word.clear();
+	}
+	else {
+	  word += "#";
+	  if (stem == "1") word += "s";
+	  if (fold == "1") word += "i";
+	  outtext += word;
+	  word.clear();
+	}
+      }
+      // this only used in advanced form, so we leave in boolean operators
+      if (*here == '\"' || *here == '&' || *here == '|' || *here == '!' || 
+	  *here == '(' || *here == ')' || is_unicode_space(*here)) {
+	outtext.push_back(*here);
+      }
+      ++here;
+    }
+  }
+   
+  // get last word
+  if (!word.empty()) {
+    word += "#";
+    if (stem == "1") word += "s";
+    if (fold == "1") word += "i";
+    word += " ";
+    outtext += word;
+  }
+  return outtext;
+}
+
+
+// some query form parsing functions for use with mgpp & lucene
+
+void parse_reg_query_form(text_t &querystring, cgiargsclass &args, bool segment)
+{
+  querystring.clear();
+
+  int argct = args.getintarg("ct");
+  int argt = args.getintarg("t");// t=0 -and, t=1 - or
+  int argb = args.getintarg("b");
+    
+  text_t combine;
+
+  // lucene uses global combine, so only need this for mgpp
+  if (argct==1) {
+    if (argt == 0) combine = "&";
+    else combine = "|";
+  }
+  
+  text_t field = args["fqf"];
+  if (field.empty()) return; // no query
+  text_tarray fields;
+  splitchar(field.begin(), field.end(), ',', fields); 
+  
+  text_t value = args["fqv"];
+  if (value.empty()) return; // somethings wrong
+  text_tarray values;
+  splitchar(value.begin(), value.end(), ',', values);
+
+
+  for (int i=0; i< values.size(); ++i) {
+    if (!values[i].empty()) {
+      text_t this_value = values[i];
+      // remove operators for simple search, segments text if necessary
+      format_querystring(this_value, argb, segment); 
+      // add tag info for this field (and other processing)
+      format_field_info(this_value, fields[i], argct, argt, argb);
+      // add into query string
+      if (argct == 2) {
+	// lucene
+	// we don't worry about AND/OR, cos this is done by defaultcombineoperator
+	querystring += this_value+" ";
+      } else {
+	// mgpp
+	if (!querystring.empty()) {
+	  querystring += " "+ combine+ " ";
+	}
+	querystring += this_value;
+      }
+    }
+  }
+}
+
+
+void parse_adv_query_form(text_t &querystring, cgiargsclass &args, bool segment){
+  querystring.clear();
+
+  const int argct = args.getintarg("ct");
+  int argt = 0;// arg t is either not used (lucene) or used for natural/ranked (mgpp), so we set it to 0 = AND, by default
+  int argb = args.getintarg("b");
+  text_t combine;
+  if (argct==1) {
+    combine = "&";
+  }
+  else { // lucene
+    combine = "AND";
+  }
+
+  text_t field = args["fqf"];
+  if (field.empty()) return; // no query
+  text_tarray fields;
+  splitchar(field.begin(), field.end(), ',', fields); 
+  
+  text_t value = args["fqv"];
+  if (value.empty()) return; // somethings wrong
+  text_tarray values;
+  splitchar(value.begin(), value.end(), ',', values);
+
+  text_t comb = args["fqc"];
+  if (comb.empty()) return; //somethings wrong
+  text_tarray combs;
+  splitchar(comb.begin(), comb.end(), ',', combs);
+
+  text_tarray stems;
+  text_tarray folds;
+  if (argct == 1) {// mgpp - lucene doesn't do stem/case
+    text_t stem = args["fqs"];
+    if (stem.empty()) return; // somethings wrong
+    splitchar(stem.begin(), stem.end(), ',', stems);
+    
+    text_t fold = args["fqk"];
+    if (fold.empty()) return; // somethings wrong
+    splitchar(fold.begin(), fold.end(), ',', folds);
+  }
+  
+  for(int i=0; i< values.size(); ++i) {
+    if (!values[i].empty()) {
+      if (i!=0) {
+	if (argct==1) {
+	  if (combs[i-1]=="and") combine = "&";
+	  else if (combs[i-1]=="or")combine = "|";
+	  else if (combs[i-1]=="not")combine = "!";
+	}
+	else { // lucene
+	  if (combs[i-1]=="and") combine = "AND";
+	  else if (combs[i-1]=="or")combine = "OR";
+	  else if (combs[i-1]=="not")combine = "NOT";
+	}
+      }
+      text_t this_value = values[i];
+      // remove operators for simple search, segments text if necessary
+      format_querystring(this_value, argb, segment); 
+      if (argct == 1) { // mgpp only
+	this_value = addstemcase(this_value, stems[i], folds[i], argct);
+      }
+      // add tag info for this field (and other processing)
+      format_field_info(this_value, fields[i], argct, argt, argb);
+      // add into query string
+      if (!querystring.empty()) {
+	querystring += " "+ combine+ " ";
+      }
+      querystring += this_value;
+      
+    }
+  }
+}
+
+// Extended addqueryelem for Human Info project
+void addqueryelem_ex(text_t &querystring, const text_t &tag, 
+		     const text_t &terms, const text_t &stem, 
+		     const text_t &fold,
+		     const text_t& combine, const text_t& word_combine) {
+
+  if (!querystring.empty()) { // have to put and/or
+    querystring += " " + combine + " ";
+  }
+  text_t outtext; outtext.reserve(512);
+  text_t word; word.reserve(100);
+  //unsigned short c;                                                            
+  text_t::const_iterator here = terms.begin();
+  text_t::const_iterator end = terms.end();
+  bool inquote = false, firstword = true;
+
+  text_t word2; word2.reserve(256);
+	
+  while (here !=end) {
+    if (is_unicode_space(*here)) {
+      if (word2 == "AND") { word2.clear(); word2.push_back(7527); word2.appendcarr("AND", 3); word2.push_back(7527); }
+      else if (word2 == "OR") { word2.clear(); word2.push_back(7527); word2.appendcarr("OR", 2); word2.push_back(7527); }
+      else if (word2 == "NOT") { word2.clear(); word2.push_back(7527); word2.appendcarr("NOT", 3); word2.push_back(7527); }
+      else if (word2 == "NEAR") { word2.clear(); word2.push_back(7527); word2.appendcarr("NEAR", 4); word2.push_back(7527); }
+      else if (word2 == "WITHIN") { word2.clear(); word2.push_back(7527); word2.appendcarr("WITHIN", 6); word2.push_back(7527); }
+      if (inquote) {
+	word2.push_back(*here);
+      }
+      word.append(word2); word2.clear();
+			
+      if (!inquote && !word.empty() ) {
+	// found word boundary   
+				
+	if (stem == "1" || fold =="1") {
+	  word += "#";
+	  if (stem == "1") word += "s";
+	  //else word += "u";
+					
+	  if (fold == "1") word += "i";
+	  //else word += "c";
+	}
+	if (firstword) {
+	  firstword = false;
+	} else {
+	  outtext += " " + word_combine + " ";
+	}
+	outtext += "[" + word + "]:"+tag;
+	word.clear();
+      }
+      ++here;
+    } else if (*here == '\"') {
+      word2.push_back(*here);
+      inquote = !inquote;
+      ++here;
+    } else {
+      // not word boundary
+      word2.push_back(*here);
+      ++here;    
+    }
+  }
+	
+  // get last word
+  if (!word2.empty()) {
+    if (word2 == "AND") { word2.clear(); word2.push_back(7527); word2.appendcarr("AND", 3); word2.push_back(7527); }
+    else if (word2 == "OR") { word2.clear(); word2.push_back(7527); word2.appendcarr("OR", 2); word2.push_back(7527); }
+    else if (word2 == "NOT") { word2.clear(); word2.push_back(7527); word2.appendcarr("NOT", 3); word2.push_back(7527); }
+    else if (word2 == "NEAR") { word2.clear(); word2.push_back(7527); word2.appendcarr("NEAR", 4); word2.push_back(7527); }
+    else if (word2 == "WITHIN") { word2.clear(); word2.push_back(7527); word2.appendcarr("WITHIN", 6); word2.push_back(7527); }
+    word.append(word2); word2.clear();
+		
+    if (stem == "1"|| fold == "1") {
+      word += "#";
+      if (stem == "1") word += "s";
+      //else word += "u";
+			
+      if (fold == "1") word += "i";
+      //else word += "c";
+    }
+    if (!outtext.empty()) outtext += " " + word_combine + " ";
+    outtext += "[" + word + "]:"+tag;
+  }
+  querystring += "(" + outtext + ")";
+}
+
+void add_field_info(text_t &querystring, const text_t &tag, int type) {
+
+  if (tag == "" || tag == "ZZ") return; // do nothing
+  if (type == 1) { //mgpp
+    querystring = "["+querystring+"]:"+tag;
+  } else if (type == 2) { // lucene
+    querystring = tag+":("+querystring+")";
+  }
+    
+}
+
+
+void format_field_info_lucene(text_t &querystring, text_t tag, int argt, int argb) {
+  if (tag == "ZZ") tag = ""; // ZZ is a special tag meaning no tag (all fields)
+  int type = 2; //lucene
+
+  if (argb==0) { // simple
+    // there will be no & or | as they should have already been removed
+    // just tag the entire thing
+    if (tag != "") {
+      add_field_info(querystring, tag, type);
+    }
+    return;
+  }
+
+  // need to replace & with &&, | with ||
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+
+  text_t finalquery = "";
+  while (here != end) {
+    if (*here ==  '&') {
+      finalquery.push_back('&');
+      finalquery.push_back('&');
+      while (*(here+1) == '&') {
+	++here;
+      }
+    } 
+    else if (*here == '|') {
+      finalquery.push_back('|');
+      finalquery.push_back('|');
+      while (*(here+1) == '|') {
+	++here;
+      }
+    } 
+    else {
+      finalquery.push_back(*here);
+    }
+    ++here;
+  }
+  querystring = finalquery;
+  add_field_info(querystring, tag, type);
+}
+
+
+void format_field_info_mgpp(text_t &querystring, text_t tag, int argt, int argb) {
+
+  if (tag == "ZZ") tag = ""; // ZZ is a special tag meaning no tag (all fields)
+  if (tag == "" && argb == 1) {
+    return; // no field specifier, advanced mode, the query stays as written
+  }
+
+  int type = 1; // mgpp
+
+  bool simple_and = (argb==0 && argt==0); 
+  text_t finalquery = "";
+  text_t fieldpart ="";
+  text_t queryelem = "";
+  bool in_phrase = false;
+  bool in_field = false;
+
+  text_t::const_iterator here = querystring.begin();
+  text_t::const_iterator end = querystring.end();
+  while (here != end) {
+    if (is_unicode_letdig(*here)  || *here == '&' || is_special_character(type, *here)) {
+      queryelem.push_back(*here);
+    }
+    else if (*here == '|') {
+      in_field = false;
+    }
+    else if (*here == '!' || *here == '(' || *here == ')') {
+      if (!in_phrase) { // ignore these if in_phrase
+	// output field, then output operator
+	in_field = false;
+	if (!queryelem.empty()) {
+	  if (!simple_and && !fieldpart.empty()) {
+	    add_field_info(fieldpart, tag, type);
+	    finalquery += fieldpart;
+	    finalquery.push_back(' ');
+	    fieldpart.clear();
+	  }
+	  fieldpart += queryelem;
+	}
+	if (!fieldpart.empty()) {
+	  add_field_info(fieldpart, tag, type);
+	  finalquery += fieldpart;
+	  finalquery.push_back(' ');
+	}
+	fieldpart.clear();
+	queryelem.clear();
+	finalquery.push_back(*here);
+	finalquery.push_back(' ');
+      }
+    }
+    else if (*here == '"') {
+      queryelem.push_back(*here);
+      if (in_phrase == false) in_phrase = true;
+      else {
+	in_phrase = false;
+      }
+    }
+
+    // Found word boundary, in a phrase
+    else if (in_phrase) {
+      queryelem.push_back(*here);
+    }
+    // Found a word boundary
+    else {
+      if (!queryelem.empty()) {
+	if (queryelem == "&") {
+	  in_field = true;
+	  queryelem.clear();
+	}
+	else if (starts_with(queryelem, "NEAR") || starts_with(queryelem, "WITHIN")) {
+	  
+	  if (argb==1) {
+	    // simple search, these not allowed
+	    in_field = true;
+	    fieldpart += queryelem;
+	    fieldpart.push_back(' ');
+	  }
+	  queryelem.clear();
+	  
+	} 
+	else {
+	  if (!simple_and && !in_field) {
+	    if (!fieldpart.empty()) {
+	      add_field_info(fieldpart, tag, type);
+	      finalquery += fieldpart;
+	      finalquery.push_back(' ');
+	      fieldpart.clear();
+	    } 
+	  }
+	  
+	  fieldpart += queryelem;
+	  fieldpart.push_back(' ');
+	  queryelem.clear();
+	}
+      }
+    }
+    ++here;
+  }
+  // at the end
+  if (!queryelem.empty()) {
+    if (!simple_and && !in_field && !fieldpart.empty()) {
+      add_field_info(fieldpart, tag, type);
+      finalquery += fieldpart;
+      finalquery.push_back(' ');
+      fieldpart.clear();
+    }
+    fieldpart += queryelem;
+  }
+  if (!fieldpart.empty()) {
+    add_field_info(fieldpart, tag, type);
+    finalquery += fieldpart;
+    fieldpart.clear();
+    finalquery.push_back(' ');
+  }
+
+  querystring  = finalquery;
+}
+
+
+void format_field_info(text_t &querystring, text_t tag, int argct, int argt, int argb) {  
+  if (argct == 1) {
+    format_field_info_mgpp(querystring, tag, argt, argb);
+  } else if (argct == 2) {
+    format_field_info_lucene(querystring, tag, argt, argb);
+  }
+}
+
+void mgpp_adddateelem(text_t& querystring, const int date)
+{
+  querystring.appendcstr(" [");
+  if(date<0) {
+      querystring.appendcstr("bc");
+      querystring.appendint((date*-1));
+  }
+  else {
+    querystring.appendint(date);
+  }
+  querystring.appendcstr("]:CV");
+}
+
+void lucene_adddateelem(text_t& querystring, const int date)
+{
+  querystring.appendcstr(" CV:(");
+  if(date<0) {
+      querystring.appendcstr("bc");
+      querystring.appendint((date*-1));
+  }
+  else {
+    querystring.appendint(date);
+  }
+  querystring.appendcstr(")");
+}
+
+
+void add_dates(text_t &querystring, int startdate, int enddate, 
+	       int startbc, int endbc, int ct)
+{
+  if(startdate)
+    {
+      int querystringis = 0;
+      text_t::const_iterator here = querystring.begin();
+      text_t::const_iterator end = querystring.end();
+      while(here!=end)
+	{
+	  if(!(isspace((*here)))){
+	    here = end;
+	    querystringis = 1;
+	  }
+	  else
+	    ++here;
+	}
+      //converting BCE dates
+      if(startbc && startdate > 0)
+	{
+	  startdate *= -1;
+	}
+      if(endbc && enddate > 0)
+	{
+	  enddate *= -1;
+	}
+       if(enddate != 0 && enddate<startdate)
+	{
+	  cout<<"enddate too small"<<endl;
+	  return;
+	}
+       if(querystringis)
+	 querystring.appendcstr(" AND");
+       if(!enddate)
+	 {
+	   if (ct==1) {
+	     mgpp_adddateelem(querystring,startdate);
+	   }
+	   else { // lucene
+	     lucene_adddateelem(querystring,startdate);
+	   }
+	 }
+       else{
+	 int nextdate = startdate;
+	 querystring.appendcstr(" (");
+	 while(nextdate<=enddate)
+	   {
+	     if(nextdate!=0) {
+	       if (ct==1) {
+		 mgpp_adddateelem(querystring,nextdate);
+	       }
+	       else { // lucene
+		 lucene_adddateelem(querystring,nextdate);
+	       }
+	     }
+	     ++nextdate;
+	   }
+	 querystring.appendcstr(" )");
+       }
+    }
+
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/querytools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/querytools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/querytools.h	(revision 16561)
@@ -0,0 +1,65 @@
+/**********************************************************************
+ *
+ * querytools.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.
+ *
+ *********************************************************************/
+
+
+#ifndef QUERYTOOLS_H
+#define QUERYTOOLS_H
+
+#include "cgiargs.h"
+#include "recptproto.h"
+
+void set_query_type_args(ColInfoResponse_t *cinfo, cgiargsclass &args);
+void set_stem_index_args(ColInfoResponse_t *cinfo, cgiargsclass &args);
+
+void set_queryfilter_options (FilterRequest_t &request, const text_t &querystring,
+			      cgiargsclass &args);
+
+void set_queryfilter_options (FilterRequest_t &request, const text_t &querystring1,
+			      const text_t &querystring2, cgiargsclass &args);
+
+// don't hassle the function name ... it's been a long day ;)
+void set_more_queryfilter_options (FilterRequest_t &request, cgiargsclass &args);
+
+void format_querystring (text_t &querystring, int querymode, bool segment);
+
+void add_dates(text_t &querystring, int startdate, int enddate, 
+	       int startbc, int endbc, int ct);
+
+// search history tool
+text_t escape_quotes(const text_t &querystring);
+
+// some query parsing functions for form processing (only for mgpp/lucene collections)
+void parse_reg_query_form(text_t &formattedstring, cgiargsclass &args, bool segment);
+void parse_adv_query_form(text_t &formattedstring, cgiargsclass &args, bool segment);
+
+// Added for Human Info project
+void addqueryelem_ex(text_t &querystring, const text_t &tag, 
+		     const text_t &terms, const text_t &stem, 
+		     const text_t &fold,
+		     const text_t& combine, const text_t& word_combine);
+
+void format_field_info(text_t &querystring, text_t tag, int argct, int argt, int argb);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/receptionist.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/receptionist.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/receptionist.cpp	(revision 16561)
@@ -0,0 +1,1742 @@
+/**********************************************************************
+ *
+ * receptionist.cpp -- a web interface for the gsdl
+ * 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.
+ *
+ *********************************************************************/
+
+// following line required to get fstream.filedesc() on darwin (Mac OS X)
+// gcc 2.91 automatically defines this in stream.h
+#define _STREAM_COMPAT  1
+
+#include "receptionist.h"
+#include "recptprototools.h"
+#include "fileutil.h"
+#include "cgiutils.h"
+#include "htmlutils.h"
+#include "gsdltools.h"
+#include "gsdltimes.h"
+#include "OIDtools.h"
+#include <assert.h>
+#include <time.h>
+#include <stdio.h> // for open()
+#include <fcntl.h> // for open() flags
+// following 2 are for printing Last-Modified http header.
+#include <sys/stat.h>
+#include <time.h>
+
+#if defined (GSDL_USE_IOS_H)
+#include <fstream.h>
+#else 
+#include <fstream>
+#endif
+
+void recptconf::clear () {
+  gsdlhome.clear();
+  collecthome.clear();
+  dbhome.clear();
+  collectinfo.erase(collectinfo.begin(), collectinfo.end());
+  collection.clear();
+  collectdir.clear();
+  httpprefix.clear();
+  httpimg = "/images";
+  gwcgi.clear();
+  macrofiles.erase(macrofiles.begin(), macrofiles.end());
+  saveconf.clear();
+  usecookies = false;
+  logcgiargs = false;
+  LogDateFormat = LocalTime;
+
+  maintainer.clear();
+  MailServer.clear();
+  LogEvents = Disabled;
+  EmailEvents = Disabled;
+  EmailUserEvents = false;
+
+  languages.erase(languages.begin(), languages.end());
+  encodings.erase(encodings.begin(), encodings.end());
+
+  site_auth = false;
+  HomePageType = "images";
+  HomePageCols = 3;
+  
+  // these default page parameters can always be overriden
+  // in the configuration file
+  pageparams.erase(pageparams.begin(), pageparams.end());
+  pageparams["c"] = ""; 
+  pageparams["l"] = "en"; 
+
+#ifdef MACROPRECEDENCE
+  macroprecedence = MACROPRECEDENCE;
+#else
+  macroprecedence.clear();
+#endif
+}
+
+void collectioninfo_t::clear () {
+  gsdl_gsdlhome.clear();
+  gsdl_dbhome.clear();
+
+  info_loaded = false;
+  info.clear();
+}
+
+void languageinfo_t::clear () {
+  longname.clear();
+  defaultencoding.clear();
+}
+
+receptionist::receptionist () {
+  // create a list of cgi arguments
+  // this must be done before the configuration
+
+  cgiarginfo ainfo;
+
+  ainfo.shortname = "e";
+  ainfo.longname = "compressed arguments";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::good;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, ainfo);
+
+  ainfo.shortname = "a";
+  ainfo.longname = "action";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, ainfo);
+
+  // w=western
+  ainfo.shortname = "w";
+  ainfo.longname = "encoding";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, ainfo);
+  
+  ainfo.shortname = "nw";
+  ainfo.longname = "new encoding";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, ainfo);
+  
+  ainfo.shortname = "c";
+  ainfo.longname = "collection";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, ainfo);
+  
+  // the interface language name should use the ISO 639
+  // standard
+  ainfo.shortname = "l";
+  ainfo.longname = "interface language";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::weak;
+  ainfo.argdefault = "en";
+  ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, ainfo);
+  
+  ainfo.shortname = "nl";
+  ainfo.longname = "new language";
+  ainfo.multiplechar = false;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = "0";
+  ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, ainfo);
+  
+  // the GSDL_UID (cookie)
+  ainfo.shortname = "z";
+  ainfo.longname = "gsdl uid";
+  ainfo.multiplechar = true;
+  ainfo.defaultstatus = cgiarginfo::none;
+  ainfo.argdefault = g_EmptyText;
+  ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, ainfo);
+}
+
+
+void receptionist::add_action (action *theaction) {
+  // make sure we have an action to add
+  if (theaction == NULL) return;
+
+  // add this action to the list of actions
+  actions.addaction(theaction);
+  
+  // add the cgi arguments from this action
+  argsinfo.addarginfo (NULL, *(theaction->getargsinfo()));
+}
+
+
+void receptionist::add_browser (browserclass *thebrowser) {
+  // make sure we have a browser to add
+  if (thebrowser == NULL) return;
+
+  // add this browser to the list of browsers
+  browsers.addbrowser(thebrowser);
+}
+
+
+void receptionist::setdefaultbrowser (const text_t &browsername) {
+  browsers.setdefaultbrowser (browsername);
+}
+
+
+// configure should be called for each line in the
+// configuration files to configure the receptionist and everything
+// it contains. The configuration should take place after everything
+// has been added but before the initialisation.
+
+void receptionist::configure (const text_t &key, const text_tarray &cfgline) {
+  // configure the receptionist
+
+   
+   
+  if (cfgline.size() >= 1) {
+    cgiarginfo *info = NULL;
+    if (key == "gsdlhome") {
+      configinfo.gsdlhome = cfgline[0];
+      if (configinfo.dbhome.empty()) configinfo.dbhome = cfgline[0];
+    }
+    else if (key == "collecthome") configinfo.collecthome = cfgline[0];
+    else if (key == "gdbmhome") configinfo.dbhome = cfgline[0];
+    else if (key == "collection") {
+      configinfo.collection = cfgline[0];
+      // also need to set the default arg to this collection
+      if ((info = argsinfo.getarginfo("c")) != NULL) {
+	info->defaultstatus = cgiarginfo::good;
+	info->argdefault = cfgline[0];
+      }
+      
+    }
+    else if (key == "collectdir") configinfo.collectdir = cfgline[0];
+    else if (key == "httpprefix") configinfo.httpprefix = cfgline[0];
+    else if (key == "httpimg") configinfo.httpimg = cfgline[0];
+    else if (key == "gwcgi") configinfo.gwcgi = cfgline[0];
+    else if (key == "macrofiles") {
+      // want to append to macrofiles (i.e. may be several config files
+      // contributing, maybe from several collections).
+      text_tarray::const_iterator here = cfgline.begin();
+      text_tarray::const_iterator end = cfgline.end();
+      while (here != end) {
+	configinfo.macrofiles.insert (*here);
+	++here;
+      }
+    }
+    else if (key == "saveconf") configinfo.saveconf = cfgline[0];
+    else if (key == "usecookies") configinfo.usecookies = (cfgline[0] == "true");
+    else if (key == "logcgiargs") configinfo.logcgiargs = (cfgline[0] == "true");
+    else if (key == "maintainer") configinfo.maintainer = cfgline[0];
+    else if (key == "MailServer") configinfo.MailServer = cfgline[0];
+    else if (key == "LogDateFormat") {
+      if (cfgline[0] == "UTCTime") configinfo.LogDateFormat = UTCTime;
+      else if (cfgline[0] == "Absolute") configinfo.LogDateFormat = Absolute;
+    } 
+    else if (key == "LogEvents") {
+      if (cfgline[0] == "CollectorEvents") configinfo.LogEvents = CollectorEvents;
+      else if (cfgline[0] == "AllEvents") configinfo.LogEvents = AllEvents;
+    } 
+    else if (key == "EmailEvents") {
+      if (cfgline[0] == "CollectorEvents") configinfo.EmailEvents = CollectorEvents;
+      else if (cfgline[0] == "AllEvents") configinfo.EmailEvents = AllEvents;
+    } 
+    else if (key == "EmailUserEvents") configinfo.EmailUserEvents = (cfgline[0] == "true");
+    else if (key == "pageparam") {
+      if (cfgline.size() >= 2) configinfo.pageparams[cfgline[0]] = cfgline[1];
+      else configinfo.pageparams[cfgline[0]] = "";
+    }
+    else if (key == "macroprecedence") configinfo.macroprecedence = cfgline[0];
+    else if (key == "collectinfo") {
+      if (cfgline.size() == 3) {
+	// for backwards compatability with older collections that only use
+	// gsdlhome and dbhome
+	collectioninfo_t cinfo;
+	cinfo.gsdl_gsdlhome = cfgline[1];
+	cinfo.gsdl_collecthome = filename_cat(cfgline[1],"collect");
+	cinfo.gsdl_dbhome = cfgline[2];
+	configinfo.collectinfo[cfgline[0]] = cinfo;
+      }
+      else if (cfgline.size() >= 4) {
+	collectioninfo_t cinfo;
+	cinfo.gsdl_gsdlhome = cfgline[1];
+	cinfo.gsdl_collecthome = cfgline[2];
+	cinfo.gsdl_dbhome = cfgline[3];
+	configinfo.collectinfo[cfgline[0]] = cinfo;
+      }
+    }
+
+    // Read in the value for the site_auth directive either true or false
+    else if (key == "site_auth") configinfo.site_auth = (cfgline[0] == "true");
+
+    else if (key == "site_group")
+       joinchar(cfgline,',',configinfo.site_group);
+       
+    else if (key == "SiteFormat") {
+       if (cfgline[0] == "HomePageType") {
+	  configinfo.HomePageType = cfgline[1];
+       } else if (cfgline[0] == "HomePageCols") {
+	  configinfo.HomePageCols = cfgline[1].getint();
+       }
+    }
+       
+    else if (key == "cgiarg") {
+      // get shortname
+      bool seen_defaultstatus = false;
+      text_t subkey, subvalue;
+      text_t shortname;
+      text_t::const_iterator cfglinesub_here;
+      text_tarray::const_iterator cfgline_here = cfgline.begin();
+      text_tarray::const_iterator cfgline_end = cfgline.end();
+      while (cfgline_here != cfgline_end) {
+	cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
+					(*cfgline_here).end(), '=', subkey);
+	if (subkey == "shortname") {
+	  shortname = substr (cfglinesub_here, (*cfgline_here).end());
+	}
+	++cfgline_here;
+      }
+
+      // if we found the shortname process the line again filling in values
+      if (!shortname.empty()) {
+	cgiarginfo &chinfo = argsinfo[shortname];
+	chinfo.shortname = shortname; // in case this is a new argument
+	
+	cfgline_here = cfgline.begin();
+	while (cfgline_here != cfgline_end) {
+	  cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
+					  (*cfgline_here).end(), '=', subkey);
+	  subvalue = substr (cfglinesub_here, (*cfgline_here).end());
+
+	  if (subkey == "longname") chinfo.longname = subvalue;
+	  else if (subkey == "multiplechar") chinfo.multiplechar = (subvalue == "true");
+	  else if (subkey == "defaultstatus") {
+	    seen_defaultstatus = true;
+	    if (subvalue == "none") chinfo.defaultstatus = cgiarginfo::none;
+	    else if (subvalue == "weak") chinfo.defaultstatus = cgiarginfo::weak;
+	    else if (subvalue == "good") chinfo.defaultstatus = cgiarginfo::good;
+	    else if (subvalue == "config") chinfo.defaultstatus = cgiarginfo::config;
+	    else if (subvalue == "imperative") chinfo.defaultstatus = cgiarginfo::imperative;
+	  }
+	  else if (subkey == "argdefault") {
+	    chinfo.argdefault = subvalue;
+	    if (!seen_defaultstatus) chinfo.defaultstatus = cgiarginfo::config;
+	  }
+	  else if (subkey == "savedarginfo") {
+	    if (subvalue == "mustnot") chinfo.savedarginfo = cgiarginfo::mustnot;
+	    else if (subvalue == "can") chinfo.savedarginfo = cgiarginfo::can;
+	    else if (subvalue == "must") chinfo.savedarginfo = cgiarginfo::must;
+	  }
+	  
+	  ++cfgline_here;
+	}
+      }
+
+    } else if (key == "Encoding") {
+
+      configure_encoding (cfgline);
+
+    } else if (key == "Language") {
+      text_t subkey, subvalue, shortname;
+      languageinfo_t lang;
+      text_t::const_iterator cfglinesub_here;
+      text_tarray::const_iterator cfgline_here = cfgline.begin();
+      text_tarray::const_iterator cfgline_end = cfgline.end();
+      while (cfgline_here != cfgline_end) {
+	cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
+					(*cfgline_here).end(), '=', subkey);
+	if (subkey == "shortname") {
+	  shortname = substr (cfglinesub_here, (*cfgline_here).end());
+	} else if (subkey == "longname") {
+	  lang.longname = substr (cfglinesub_here, (*cfgline_here).end());
+	} else if (subkey == "default_encoding") {
+	  lang.defaultencoding = substr (cfglinesub_here, (*cfgline_here).end());
+	}
+	++cfgline_here;
+      }
+      if (!shortname.empty()) {
+	if (lang.longname.empty()) lang.longname = shortname;
+	configinfo.languages[shortname] = lang;
+      }
+    }
+  }
+  
+  // configure the actions
+  actionptrmap::iterator actionhere = actions.begin ();
+  actionptrmap::iterator actionend = actions.end ();
+
+  while (actionhere != actionend) {
+    assert ((*actionhere).second.a != NULL);
+    if ((*actionhere).second.a != NULL) 
+      (*actionhere).second.a->configure(key, cfgline);
+
+    ++actionhere;
+  }
+
+  // configure the protocols
+  recptprotolistclass::iterator protohere = protocols.begin ();
+  recptprotolistclass::iterator protoend = protocols.end ();
+
+  while (protohere != protoend) {
+    assert ((*protohere).p != NULL);
+    comerror_t err;
+    if ((*protohere).p != NULL) 
+      (*protohere).p->configure(key, cfgline, err);
+    
+    ++protohere;
+  }
+
+  // configure the browsers
+  browserptrmap::iterator browserhere = browsers.begin ();
+  browserptrmap::iterator browserend = browsers.end ();
+
+  while (browserhere != browserend) {
+    assert ((*browserhere).second.b != NULL);
+    if ((*browserhere).second.b != NULL) 
+      (*browserhere).second.b->configure(key, cfgline);
+    
+    ++browserhere;
+  }
+}
+
+
+void receptionist::configure (const text_t &key, const text_t &value) {
+  text_tarray cfgline;
+  cfgline.push_back (value);
+  configure(key, cfgline);
+}
+
+
+// init should be called after all the actions and protocols have been
+// added to the receptionist and after everything has been configured but
+// before any pages are created.  It returns true on success and false on
+// failure. If false is returned getpage should not be called (without
+// producing meaningless output), instead an error page should be produced
+// by the calling code.
+bool receptionist::init (ostream &logout) {
+
+  // first configure collectdir
+  if (!configinfo.collection.empty()) {
+
+    // collection specific mode
+
+    text_t collectdir = configinfo.gsdlhome;
+
+    if (!configinfo.collectdir.empty()) {
+      // has already been configured
+      collectdir = configinfo.collectdir;
+    } else {
+
+      // decide where collectdir is by searching for collect.cfg
+      // look in $GSDLHOME/collect/collection-name/etc/collect.cfg and
+      // then $GSDLHOME/etc/collect.cfg
+      collectdir = filename_cat (configinfo.gsdlhome, "collect");
+      collectdir = filename_cat (collectdir, configinfo.collection);
+      text_t filename = filename_cat (collectdir, "etc");
+      filename = filename_cat (filename, "collect.cfg");
+      
+      if (!file_exists(filename)) collectdir = configinfo.gsdlhome;
+    }
+
+    configure("collectdir", collectdir);
+
+  }
+  else {
+
+    text_t collecthome;
+    if (configinfo.collecthome.empty()) {
+      collecthome = filename_cat(configinfo.gsdlhome,"collect");
+    }
+    else {
+      collecthome = configinfo.collecthome;
+    }
+
+    configure("collecthome", collecthome);
+
+    // for backwards compatability collectdir set to gsdlhome
+    // (possible it could now be removed)
+    configure("collectdir", configinfo.gsdlhome); 
+  } 
+
+
+  // read in the macro files
+  if (!read_macrofiles (logout)) return false;
+
+  // there must be at least one action defined
+  if (actions.empty()) {
+    logout << "Error: no actions have been added to the receptionist\n";
+    return false;
+  }
+
+  // there must be at least one browser defined
+  if (browsers.empty()) {
+    logout << "Error: no browsers have been added to the receptionist\n";
+    return false;
+  }
+
+  // create a saveconf string if there isn't one already
+  if (configinfo.saveconf.empty())
+    configinfo.saveconf = create_save_conf_str (argsinfo, logout);
+
+  // check the saveconf string
+  if (!check_save_conf_str (configinfo.saveconf, argsinfo, logout))
+    return false;
+
+  // set a random seed
+  srand (time(NULL));
+
+  // if maintainer email address is something dodgy (for now I'll define
+  // dodgy as being anything that doesn't contain '@') disable EmailEvents
+  // and EmailUserEvents (we don't strictly need to disable EmailUserEvents
+  // in this case but we will as it seems likely that MailServer will also
+  // be screwed up if maintainer is).
+  text_t::const_iterator maintainer_end = configinfo.maintainer.end ();
+  text_t::const_iterator maintainer_here = findchar ((text_t::const_iterator)configinfo.maintainer.begin(),
+						     maintainer_end, '@');
+  if (maintainer_here == maintainer_end) {
+    configinfo.EmailEvents = Disabled;
+    configinfo.EmailUserEvents = Disabled;
+  } else {
+    // if MailServer isn't set it should default to mail.maintainer-domain
+    if (configinfo.MailServer.empty()) {
+      configinfo.MailServer = "mail." + substr (maintainer_here+1, maintainer_end);
+    }
+  }
+
+  // init the actions
+  actionptrmap::iterator actionhere = actions.begin ();
+  actionptrmap::iterator actionend = actions.end ();
+  while (actionhere != actionend) {
+    if (((*actionhere).second.a == NULL) || 
+	!(*actionhere).second.a->init(logout)) return false;
+    ++actionhere;
+  }
+
+  // init the protocols
+  recptprotolistclass::iterator protohere = protocols.begin ();
+  recptprotolistclass::iterator protoend = protocols.end ();
+  while (protohere != protoend) {
+    comerror_t err;    
+    if (((*protohere).p == NULL) ||
+	!(*protohere).p->init(err, logout)) return false;
+    ++protohere;
+  }
+
+  // init the browsers
+  browserptrmap::iterator browserhere = browsers.begin ();
+  browserptrmap::iterator browserend = browsers.end ();
+  while (browserhere != browserend) {
+    if (((*browserhere).second.b == NULL) || 
+	!(*browserhere).second.b->init(logout)) return false;
+    ++browserhere;
+  }
+
+  return true;
+}
+
+// get the default encoding for the given language - if it fails for any
+// reason return ""
+text_t receptionist::get_default_encoding (const text_t &language) {
+  
+  // make sure language is valid
+  if (configinfo.languages.find(language) == configinfo.languages.end()) return "";
+
+  text_t default_encoding = configinfo.languages[language].defaultencoding;
+
+  // make sure the encoding is valid
+  if (converters.find(default_encoding) == converters.end()) {
+    // we don't support the encoding specified as default for this language
+    if (configinfo.encodings.size()==1) {
+      // only 1 encoding specified in main.cfg, so use it
+      return configinfo.encodings.begin()->second;
+    }
+    return "";
+  }
+
+  return default_encoding;
+}
+
+// parse_cgi_args parses cgi arguments into an argument class.
+// This function should be called for each page request. It returns false
+// if there was a major problem with the cgi arguments.
+bool receptionist::parse_cgi_args (const text_t &argstr, 
+				   fileupload_tmap &fileuploads, 
+				   cgiargsclass &args, 
+				   ostream &logout, text_tmap &fcgienv) {
+
+  // get an initial list of cgi arguments
+  args.clear();
+  split_cgi_args (argsinfo, argstr, args);
+
+  // expand the compressed argument (if there was one)
+  if (!expand_save_args (argsinfo, configinfo.saveconf, args, logout)) return false;
+
+  // add the defaults
+  add_default_args (argsinfo, args, logout);
+
+  // add any file upload arguments
+  add_fileupload_args(argsinfo, args, fileuploads, logout);
+
+  // get the cookie
+  if (configinfo.usecookies) get_cookie(args["z"], fcgienv);
+  
+  // if we're changing languages, set the encoding to the default for the new language
+  if (args["nl"] == "1") {
+    args["nw"] = get_default_encoding(args["l"]);
+  }
+
+  // get the input encoding
+  // if encoding isn't set, set it to the default for the current language
+  if ((args.getarg("w") == NULL) || args["w"].empty()) {
+    args["w"] = get_default_encoding(args["l"]);
+  }
+
+  text_t &arg_w = args["w"];
+
+  inconvertclass defaultinconvert;
+  inconvertclass *inconvert = converters.get_inconverter (arg_w);
+  if (inconvert == NULL) inconvert = &defaultinconvert;
+
+  // see if the next page will have a different encoding
+  if (args.getarg("nw") != NULL) arg_w = args["nw"];
+
+  // convert arguments which aren't in unicode to unicode
+  args_tounicode (args, *inconvert);
+
+
+  // decide on the output conversion class (needed for checking the external
+  // cgi arguments)
+  rzwsoutconvertclass defaultoutconverter;
+  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
+  if (outconverter == NULL) outconverter = &defaultoutconverter;
+  outconverter->reset();
+
+  // check the main cgi arguments
+  if (!check_mainargs (args, logout)) return false;
+
+  // check the arguments for the action
+  action *a = actions.getaction (args["a"]);
+  if (a != NULL) {
+    if (!a->check_cgiargs (argsinfo, args, &protocols, logout)) return false;
+  } else {
+    // the action was not found!!
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii << "Error: the action \"" << args["a"] 
+	   << "\" could not be found.\n";
+    return false;
+  }
+
+  // check external cgi arguments for each action
+  actionptrmap::iterator actionhere = actions.begin ();
+  actionptrmap::iterator actionend = actions.end ();
+  while (actionhere != actionend) {
+    assert ((*actionhere).second.a != NULL);
+    if ((*actionhere).second.a != NULL) {
+      if (!(*actionhere).second.a->check_external_cgiargs (argsinfo, args, *outconverter,
+							   configinfo.saveconf, logout))
+	return false;
+    }
+    ++actionhere;
+  }
+
+  // the action might have changed but we will assume that
+  // the cgiargs were checked properly when the change was made
+
+  return true;
+}
+
+// returns true if cookie already existed, false
+// if it was generated
+bool receptionist::get_cookie (text_t &cookie, text_tmap &fcgienv) {
+
+  text_t cookiestring = gsdl_getenv ("HTTP_COOKIE", fcgienv);
+
+  text_t::const_iterator end = cookiestring.end();  
+  text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
+
+  while (here+9 < end) { 
+    
+    if (substr(here, here+8) == "GSDL_UID") {
+      cookie = substr (here+9, findchar (here+9, end, ';')); 
+      return true;
+    }
+    ++here;
+    here = findchar (here, end, 'G');
+  }
+
+  cookie.clear();
+  text_t host = gsdl_getenv("REMOTE_ADDR", fcgienv);
+  time_t ttime = time(NULL);
+  if (!host.empty()) {
+    cookie += host;
+    cookie.push_back ('-');
+  }
+  cookie += text_t(ttime);
+  
+  return false;
+}
+
+// as above but just tests if cookie exists
+bool receptionist::get_cookie (text_tmap &fcgienv) {
+
+  text_t c = gsdl_getenv("HTTP_COOKIE", fcgienv);
+  if (!c.empty()) {
+    text_t cookiestring = c;
+    
+    text_t::const_iterator end = cookiestring.end();  
+    text_t::const_iterator here = findchar ((text_t::const_iterator)cookiestring.begin(), end, 'G');
+
+    while (here+9 < end) { 
+      if (substr(here, here+8) == "GSDL_UID") return true;
+      ++here;
+      here = findchar (here, end, 'G');
+    }
+  }
+  return false;
+}
+
+bool receptionist::log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv) {
+
+  // see if we want to log the cgi arguments
+  if (!configinfo.logcgiargs) return true;
+  
+  text_t host = gsdl_getenv ("REMOTE_HOST", fcgienv);
+  text_t script_name = gsdl_getenv ("SCRIPT_NAME", fcgienv);
+  if (host.empty()) host = gsdl_getenv ("REMOTE_ADDR", fcgienv);
+  text_t browser = gsdl_getenv ("HTTP_USER_AGENT", fcgienv);
+
+  cgiargsclass::const_iterator args_here = args.begin();
+  cgiargsclass::const_iterator args_end = args.end();
+
+  text_t argstr;
+  bool first = true;
+  while (args_here != args_end) {
+    if (!first) argstr += ", ";
+    argstr += (*args_here).first + "=" + (*args_here).second.value;
+    first = false;
+    ++args_here;
+  }
+
+  text_t logfile = filename_cat (configinfo.dbhome, "etc", "usage.txt");
+
+  text_t logstr = script_name;
+  logstr += " " + host;
+  logstr += " [";
+  if (configinfo.LogDateFormat == UTCTime) {
+    logstr += get_date (false);
+  } else if (configinfo.LogDateFormat == Absolute) {
+    time_t ttime = time(NULL);
+    logstr += ttime;
+  } else {
+    // LocalTime
+    logstr += get_date (true);
+  }
+  logstr += "] (" + argstr + ") \"";
+  logstr += browser;
+  logstr += "\"\n";
+
+  return append_logstr (logfile, logstr, logout);
+}
+
+bool receptionist::append_logstr (const text_t &filename, const text_t &logstr,
+				  ostream &logout) {
+
+  utf8outconvertclass text_t2utf8;
+  char *lfile = filename.getcstr();
+
+  int fd = open(lfile, O_WRONLY | O_APPEND);
+  
+  if (fd == -1) {
+    logout << "Error: Couldn't open file " << lfile << "\n";
+    delete []lfile;
+    return false;
+  }
+
+  // lock_val is set to 0 if file is locked successfully
+  int lock_val = 1;
+  GSDL_LOCK_FILE (fd);
+  if (lock_val == 0) {
+    text_t tmp_log_str(logstr); // so we don't pass a const to setinput...
+    text_t2utf8.setinput(&tmp_log_str);
+    char *buffer=new char[logstr.size()];
+    size_t num_chars;
+    convertclass::status_t status;
+    text_t2utf8.convert(buffer, logstr.size(), num_chars, status);
+    // ignore status - assume it is "finished" as buffer is big enough
+    write(fd, buffer, num_chars);
+    GSDL_UNLOCK_FILE (fd);
+    delete []buffer;
+  } else {
+    logout << "Error: Couldn't lock file " << lfile << "\n";
+    close(fd);
+    delete []lfile;
+    return false;
+  }
+
+  close(fd);
+		
+  delete []lfile;
+  return true;
+}
+
+text_t receptionist::expandmacros (const text_t &astring, cgiargsclass &args, 
+				   ostream &logout) {
+  text_t outstring;
+  outconvertclass text_t2ascii;
+
+  action *a = actions.getaction (args["a"]);
+  prepare_page (a, args, text_t2ascii, logout);
+  disp.expandstring (displayclass::defaultpackage, astring, outstring);
+  return outstring;
+}
+
+// produce_cgi_page will call get_cgihead_info and 
+// produce_content in the appropriate way to output a cgi header and
+// the page content (if needed). If a page could not be created it
+// will return false
+bool receptionist::produce_cgi_page (cgiargsclass &args, ostream &contentout, 
+				     ostream &logout, text_tmap &fcgienv) {
+  outconvertclass text_t2ascii;
+
+  response_t response;
+  text_t response_data;
+
+  // produce cgi header
+  get_cgihead_info (args, response, response_data, logout, fcgienv);
+  if (response == location) {
+    // location response (url may contain macros!!)
+    response_data = expandmacros (response_data, args, logout);
+    contentout << text_t2ascii << "Location: " << response_data << "\n\n";
+    contentout << flush;
+    return true;
+  } else if (response == content) {
+    // content response
+
+#ifdef GSDL_NOCACHE
+    contentout << "Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"; // date in the past
+    tm *tm_ptr = NULL;
+    time_t t = time(NULL);
+    tm_ptr = gmtime (&t);
+    if (tm_ptr != NULL) {
+      char *timestr = new char[128];
+      strftime (timestr, 128, "%a, %d %b %Y %H:%M:%S", tm_ptr);
+      contentout << "Last-Modified: " << timestr << " GMT\n"; // always modified
+      delete []timestr;
+    }
+    contentout << "Cache-Control: no-cache, must-revalidate\n"; // HTTP/1.1
+    contentout << "Pragma: no-cache\n"; // HTTP/1.0
+
+#else
+
+    // use the later of build.cfg and collect.cfg modification times
+    // as the Last-Modified: header, for caching values
+    struct stat file_info;
+    time_t latest=0;
+
+    text_t collectname="";
+    collectname=args["c"];
+    if (collectname != "") {
+
+      text_t collecthome;
+      if (!configinfo.collecthome.empty()) {
+	collecthome = configinfo.collecthome;
+      }    
+      else {
+	collecthome=filename_cat(configinfo.gsdlhome,"collect");
+      }
+      text_t collectdir=filename_cat(collecthome,collectname);
+      
+      text_t buildcfg=filename_cat(collectdir,"index");
+      buildcfg=filename_cat(buildcfg,"build.cfg");
+      char *buildcfg_ptr=buildcfg.getcstr();
+      text_t collectcfg=filename_cat(collectdir,"etc");
+      collectcfg=filename_cat(collectcfg,"collect.cfg");
+      char *collectcfg_ptr=collectcfg.getcstr();
+
+      if (stat(buildcfg_ptr, &file_info)) {
+	// we got an error. Currently don't handle error :(
+	//	logout << 
+      } else {
+	latest=file_info.st_mtime;
+      }
+    
+      if (stat(collectcfg_ptr, &file_info)) {
+	// error - unhandled for now
+      } else {
+	if (latest<file_info.st_mtime) latest=file_info.st_mtime;
+      }
+      delete []buildcfg_ptr;
+      delete []collectcfg_ptr;
+
+      if (latest>0) {
+	// print out modified time, "DDD, dd MMM YYYY hh:mm:ss" format
+	// c library takes care of mem for this string... (has \n at end!!!!)
+	// latest is currently local time, convert to UTC.
+	struct tm* utc_latest;
+	utc_latest=gmtime(&latest);
+	contentout << "Last-Modified: " << asctime(utc_latest);
+      }
+    } // end of collection != ""
+
+#endif
+
+    contentout << text_t2ascii << "Content-type: " << response_data << "\n\n";
+  }
+  else if (response == undecided_location) {
+    // Wait until later to output the target location
+    // Used for the "I'm feeling lucky" functionality
+  }
+  else {
+    // unknown response
+    logout << "Error: get_cgihead_info returned an unknown response type.\n";
+    return false;
+  }
+
+  // produce cgi page
+  if (!produce_content (args, contentout, logout)) return false;
+
+  // flush contentout
+  contentout << flush;
+  return true;
+}
+
+
+// get_cgihead_info determines the cgi header information for
+// a set of cgi arguments. If response contains location then
+// response_data contains the redirect address. If reponse
+// contains content then reponse_data contains the content-type.
+// Note that images can now be produced by the receptionist.
+// Note also, alternative for get_cgihead_info below which
+// stores the information in a text_tmap so it is more easily digested
+
+void receptionist::get_cgihead_info (cgiargsclass &args, response_t &response,
+				     text_t &response_data, ostream &logout, 
+				     text_tmap &fcgienv) {
+  outconvertclass text_t2ascii;
+
+  // get the action
+  action *a = actions.getaction (args["a"]);
+  if (a != NULL) {
+    a->get_cgihead_info (args, &protocols, response, response_data, logout);
+
+  } else {
+    // the action was not found!!
+    logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \"" 
+	   << args["a"] << "\" could not be found.\n";
+    response = content;
+    response_data = "text/html";
+  }
+
+  // add the encoding information
+  if (response == content) {
+    if (converters.find(args["w"]) != converters.end()) {
+      response_data += "; charset=" + args["w"];
+    } else {
+      // default to latin 1
+      response_data += "; charset=ISO-8859-1";
+    }
+
+    // add cookie if required
+    if (configinfo.usecookies && !get_cookie(fcgienv))
+      response_data += "\nSet-Cookie: GSDL_UID=" + args["z"]
+	+ "; expires=25-Dec-37 00:00:00 GMT";
+  }
+}
+
+
+// Alternative version of get_cgihead_info, stores fielded infomation
+// in text_tmap rather than concatenated string
+void receptionist::get_cgihead_info (cgiargsclass &args, text_tmap &headers,
+				     ostream &logout, text_tmap &fcgienv) {
+
+  response_t response;
+  text_t response_data;
+
+  // get the action
+  action *a = actions.getaction (args["a"]);
+  if (a != NULL) {
+    a->get_cgihead_info (args, &protocols, response, response_data, logout);
+
+  } else {
+    // the action was not found!!
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii << "Error receptionist::get_cgihead_info: the action \"" 
+	   << args["a"] << "\" could not be found.\n";
+    response = content;
+    response_data = "text/html";
+  }
+
+  if (response == location) {
+    response_data = expandmacros(response_data, args, logout);
+    headers["Location"] = response_data;
+    return;
+  }
+
+  // add the encoding information
+  if (response == content) {
+
+    if (converters.find(args["w"]) != converters.end()) {
+      headers["content-encoding"] = args["w"];
+      response_data += "; charset=" + args["w"];
+    } else {
+      // default to utf-8
+      headers["content-encoding"] = "utf-8";
+      response_data += "; charset=utf-8";
+    }
+
+    headers["content-type"] = response_data;
+
+  }
+
+}
+
+
+
+// produce the page content
+bool receptionist::produce_content (cgiargsclass &args, ostream &contentout,
+				    ostream &logout) {
+
+  // decide on the output conversion class
+  text_t &arg_w = args["w"];
+  rzwsoutconvertclass defaultoutconverter;
+  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
+  if (outconverter == NULL) outconverter = &defaultoutconverter;
+  outconverter->reset();
+
+  // needed for 16-bit unicode only - big endian marker 0xfeff (RFC 2781)
+  if (arg_w=="utf-16be") {
+    contentout << '\xfe' << '\xff' ;
+  }
+
+  recptproto *collectproto = protocols.getrecptproto (args["c"], logout);
+  if (collectproto != NULL) {
+    // get browsers to process OID
+    text_t OID = args["d"];
+    if (OID.empty()) OID = args["cl"];
+    if (!OID.empty()) {
+      text_tset metadata;
+      text_tarray OIDs;
+      OIDs.push_back (OID);
+      if (!is_top(OID)) OIDs.push_back (OID + ".pr");
+      FilterResponse_t response;
+      metadata.insert ("childtype");
+      if (get_info (OIDs, args["c"], args["l"], metadata, false, collectproto, response, logout)) {
+	text_t classifytype;
+	if (!response.docInfo[0].metadata["childtype"].values[0].empty())
+	  classifytype = response.docInfo[0].metadata["childtype"].values[0];
+	else if (!is_top (OID)) {
+	  // not sure why this is occasionally not set, but it will
+	  // cause a segfault... possibly if built with no_text? jrm21
+	  if (response.docInfo[1].metadata.find("childtype")
+	      == response.docInfo[1].metadata.end()) {
+	    cerr << "receptionist: no childtype element in metadata map!"
+		 << endl;
+	  } else {
+	    if (!response.docInfo[1].metadata["childtype"].values[0].empty())
+	      classifytype = response.docInfo[1].metadata["childtype"].values[0];
+	  }
+	}
+	browserclass *b = browsers.getbrowser (classifytype);
+	b->processOID (args, collectproto, logout);
+      }
+    }
+  
+    // translate "d" and "cl" arguments if required
+    translate_OIDs (args, collectproto, logout);
+  }
+  
+  // produce the page using the desired action
+  action *a = actions.getaction (args["a"]);
+  if (a != NULL) {
+    if (a->uses_display(args)) prepare_page (a, args, (*outconverter), logout);
+    if (!a->do_action (args, &protocols, &browsers, disp, (*outconverter), contentout, logout))
+      return false;
+  } else {
+    // the action was not found!!
+    outconvertclass text_t2ascii;
+
+    logout << text_t2ascii << "Error receptionist::produce_content: the action \"" 
+	   << args["a"] << "\" could not be found.\n";
+    
+    contentout << (*outconverter) 
+	       << "<html>\n"
+	       << "<head>\n"
+	       << "<title>Error</title>\n"
+	       << "</head>\n"
+	       << "<body>\n"
+	       << "<h2>Oops!</h2>\n"
+	       << "Undefined Page. The action \"" 
+	       << args["a"] << "\" could not be found.\n"
+	       << "</body>\n"
+	       << "</html>\n";
+  }
+  return true;
+}
+
+
+// returns the compressed argument ("e") corresponding to the argument
+// list. This can be used to save preferences between sessions.
+text_t receptionist::get_compressed_arg (cgiargsclass &args, ostream &logout) {
+  // decide on the output conversion class
+  text_t &arg_w = args["w"];
+  rzwsoutconvertclass defaultoutconverter;
+  rzwsoutconvertclass *outconverter = converters.get_outconverter (arg_w);
+  if (outconverter == NULL) outconverter = &defaultoutconverter;
+  outconverter->reset();
+
+  text_t compressed_args;
+  if (compress_save_args (argsinfo, configinfo.saveconf, args, 
+			  compressed_args, *outconverter, logout))
+    return compressed_args;
+
+  return g_EmptyText;
+}
+
+
+// will read in all the macro files. If one is not found an
+// error message will be written to logout and the method will
+// return false.
+bool receptionist::read_macrofiles (ostream &logout) {
+  outconvertclass text_t2ascii;
+
+  // redirect the error output to logout
+  ostream *savedlogout = disp.setlogout (&logout);
+
+  // unload any macros that were previously loaded - this allows us to call
+  // this function a second time to reload all the macro files (useful for
+  // reading in changed macro files in server versions of greenstone)
+  disp.unloaddefaultmacros();
+
+  // load up the default macro files, the collection directory
+  // is searched first for the file (if this is being used in
+  // collection specific mode) and then the main directory(s)
+  text_t colmacrodir = filename_cat (configinfo.collectdir, "macros");
+
+  text_tset maindirs;
+  text_t gsdlmacrodir = filename_cat (configinfo.gsdlhome, "macros");
+  maindirs.insert (gsdlmacrodir);
+  colinfo_tmap::iterator colhere = configinfo.collectinfo.begin();
+  colinfo_tmap::iterator colend = configinfo.collectinfo.end();
+  while (colhere != colend) {
+    if (!((*colhere).second.gsdl_gsdlhome).empty()) {
+      gsdlmacrodir = filename_cat ((*colhere).second.gsdl_gsdlhome, "macros");
+      maindirs.insert (gsdlmacrodir);
+    }
+    ++colhere;
+  }
+
+  text_tset::iterator arrhere = configinfo.macrofiles.begin();
+  text_tset::iterator arrend = configinfo.macrofiles.end();
+  text_t filename;
+  while (arrhere != arrend) {
+    bool foundfile = false;
+
+    // try in the collection directory if this is being
+    // run in collection specific mode
+    if (!configinfo.collection.empty()) {
+      filename = filename_cat (colmacrodir, *arrhere);
+      if (file_exists (filename)) {
+	disp.loaddefaultmacros(filename);
+	foundfile = true;
+      }
+    }
+
+    // if we haven't found the macro file yet try in
+    // the main macro directory(s)
+    // if file is found in more than one main directory
+    // we'll load all copies
+    if (!foundfile) {
+      text_tset::const_iterator dirhere = maindirs.begin();
+      text_tset::const_iterator dirend = maindirs.end();
+      while (dirhere != dirend) {
+	filename = filename_cat (*dirhere, *arrhere);
+	if (file_exists (filename)) {
+	  disp.loaddefaultmacros(filename);
+	  foundfile = true;
+	}
+	++dirhere;
+      }
+    }
+
+    // see if we found the file or not
+    if (!foundfile) {
+      logout << text_t2ascii 
+	     << "Error: the macro file \"" << *arrhere << "\" could not be found.\n";
+      if (configinfo.collection.empty()) {
+	text_t dirs;
+	joinchar (maindirs, ", ", dirs);
+	logout << text_t2ascii 
+	       << "It should be in either of the following directories ("
+	       << dirs << ").\n\n";
+
+      } else {
+	logout << text_t2ascii
+	       << "It should be in either " << colmacrodir << " or in " 
+	       << gsdlmacrodir << ".\n\n";
+      }
+      // don't crap out if a macro file is missing
+      //disp.setlogout (savedlogout);
+      //return false;
+    }
+    ++arrhere;
+  }
+
+  // success
+
+  // reset logout to what it was
+  disp.setlogout (savedlogout);
+  return true;
+}
+
+
+
+
+// Go through the list of macro files looking to see
+// if any exist in the collectoin specific area.  If they
+// do then read them in and add them to the set of existing
+// current macros
+
+void receptionist::read_collection_macrofiles (const text_t& collection, ostream &logout) 
+{
+  outconvertclass text_t2ascii;
+
+  // disp.unloadcollectionmacros();
+
+  // redirect the error output to logout
+  ostream *savedlogout = disp.setlogout (&logout);
+
+  text_t colmacrodir 
+    = filename_cat (configinfo.collecthome,collection, "macros");
+
+  if (directory_exists (colmacrodir)) {
+
+    text_tset::iterator arrhere = configinfo.macrofiles.begin();
+    text_tset::iterator arrend = configinfo.macrofiles.end();
+    text_t filename;
+    while (arrhere != arrend) {
+
+      filename = filename_cat (colmacrodir, *arrhere);
+      if (file_exists (filename)) {
+	disp.loadcollectionmacros(filename);
+      }
+      
+      ++arrhere;
+    }
+  }
+
+  // reset logout to what it was
+  disp.setlogout (savedlogout);
+}
+
+
+
+
+// check_mainargs will check all the main arguments. If a major
+// error is found it will return false and no cgi page should
+// be created using the arguments.
+
+bool receptionist::check_mainargs (cgiargsclass &args, ostream &logout) {
+   
+   if(configinfo.site_auth)
+      {
+	 args["uan"] = "1";
+	 args["ug"] = configinfo.site_group;
+      }
+   
+   
+   // if this receptionist is running in collection dependant mode
+   // then it should always set the collection argument to the
+   // collection
+   if (!configinfo.collection.empty()) args["c"] = configinfo.collection;
+   
+   // if current collection uses ccscols make sure
+   // "ccs" argument is set and make "cc" default to
+   // all collections in "ccs"
+   if (args["a"] != "config" && !args["c"].empty()) {
+      
+      text_t &arg_c = args["c"];
+      recptproto *collectproto = protocols.getrecptproto (arg_c, logout);
+      if (collectproto == NULL) {
+	 // oops, this collection isn't valid
+	 outconvertclass text_t2ascii;
+	 logout << text_t2ascii << "ERROR: Invalid collection: " << arg_c << "\n";
+	 // args["c"].clear();
+	 
+      } else {
+	 
+	 ColInfoResponse_t *cinfo = get_collectinfo_ptr (collectproto, arg_c, logout);
+	 
+	 if(cinfo->authenticate == "collection")
+	    {
+	       args["uan"] = "1";
+	       args["ug"] = cinfo->auth_group;
+	    }
+	 
+	 
+      if (cinfo != NULL) {
+	 if (!cinfo->ccsCols.empty()) {
+	    args["ccs"] = 1;
+	    if (args["cc"].empty()) {
+	       text_tarray::const_iterator col_here = cinfo->ccsCols.begin();
+	       text_tarray::const_iterator col_end = cinfo->ccsCols.end();
+	       bool first = true;
+	       while (col_here != col_end) {
+		  // make sure it's a valid collection
+		  if (protocols.getrecptproto (*col_here, logout) != NULL) {
+		     if (!first) args["cc"].push_back (',');
+		     args["cc"] += *col_here;
+		     first = false;
+		  }
+	      ++col_here;
+	    }
+	  }
+	}
+      } else {
+	logout << "ERROR (receptionist::check_mainargs): get_collectinfo_ptr returned NULL\n";
+      }
+    }
+  }
+
+  // argument "v" can only be 0 or 1. Use the default value
+  // if it is out of range
+  int arg_v = args.getintarg ("v");
+  if (arg_v != 0 && arg_v != 1) {
+    cgiarginfo *vinfo = argsinfo.getarginfo ("v");
+    if (vinfo != NULL) args["v"] = vinfo->argdefault;
+  }
+
+  // argument "f" can only be 0 or 1. Use the default value
+  // if it is out of range
+  int arg_f = args.getintarg ("f");
+  if (arg_f != 0 && arg_f != 1) {
+    cgiarginfo *finfo = argsinfo.getarginfo ("f");
+    if (finfo != NULL) args["f"] = finfo->argdefault;
+  }
+
+  return true;
+}
+
+// translate_OIDs translates the "d" and "cl" arguments to their correct values
+// if they use the tricky ".fc", ".lc" type syntax.
+void receptionist::translate_OIDs (cgiargsclass &args, recptproto *collectproto, 
+				   ostream &logout) {
+
+  FilterResponse_t response;
+  FilterRequest_t request;
+  comerror_t err;
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+  text_t &collection = args["c"];
+  
+  // do a call to translate OIDs if required
+  request.filterName = "NullFilter";
+  request.filterResultOptions = FROID;
+  if (!arg_d.empty() && needs_translating (arg_d)) {
+    request.docSet.push_back (arg_d);
+    collectproto->filter (collection, request, response, err, logout);
+    arg_d = response.docInfo[0].OID;
+    request.clear();
+  }
+  // we'll also check here that the "cl" argument has a "classify" doctype 
+  // (in case ".fc" or ".lc" have screwed up)
+  if (needs_translating (arg_cl)) {
+    request.fields.insert ("doctype");
+    request.docSet.push_back (arg_cl);
+    request.filterResultOptions = FRmetadata;
+    collectproto->filter (collection, request, response, err, logout);
+    // set to original value (without .xx stuff) if doctype isn't "classify"
+    if (response.docInfo[0].metadata["doctype"].values[0] != "classify")
+      strip_suffix (arg_cl);
+    else
+      arg_cl = response.docInfo[0].OID;
+  }
+}
+
+// prepare_page sets up page parameters, sets display macros
+// and opens the page ready for output 
+void receptionist::prepare_page (action *a, cgiargsclass &args, 
+				 outconvertclass &outconvert, 
+				 ostream &logout) {
+  // set up page parameters
+  text_t pageparams;
+  bool first = true;
+
+  text_tmap::iterator params_here = configinfo.pageparams.begin();
+  text_tmap::iterator params_end = configinfo.pageparams.end();
+  while (params_here != params_end) {
+    // page params are those from main.cfg (eg pageparam v 0) plus
+    // two defaults set in recptconf.clear() (c="" and l=en)
+    // This used to check if the current value of the page param
+    // == the default value, then don't add in it the list
+    // but if l=en, and there is a macro with [l=en], then it doesn't
+    // find it.
+    // so now all page params will go into the list. I assume this will 
+    // mean more attempts to find each macro, but nothing worsee than 
+    // that.  --kjdon
+    //if (args[(*params_here).first] != (*params_here).second) {
+      if (first)
+	first = false;
+      else
+	pageparams += ",";
+
+      pageparams += (*params_here).first;
+      pageparams += "=";
+      pageparams += args[(*params_here).first];
+      // }
+    
+    ++params_here;
+  }
+  
+
+  // open the page
+  disp.openpage(pageparams, configinfo.macroprecedence);
+
+  disp.unloadcollectionmacros();
+
+  text_t collection = args["c"];
+  if (!collection.empty()) {
+    read_collection_macrofiles(collection,logout);
+  }
+
+  // define external macros for each action
+  actionptrmap::iterator actionhere = actions.begin ();
+  actionptrmap::iterator actionend = actions.end ();
+
+  while (actionhere != actionend) {
+    assert ((*actionhere).second.a != NULL);
+    if ((*actionhere).second.a != NULL) {
+      (*actionhere).second.a->define_external_macros (disp, args, &protocols, logout);
+    }
+    ++actionhere;
+  }
+
+
+  // define internal macros for the current action
+  a->define_internal_macros (disp, args, &protocols, logout);
+  
+  // define general macros. the defining of general macros is done here so that
+  // the last possible version of the cgi arguments are used
+  define_general_macros (args, outconvert, logout);
+}
+
+void receptionist::define_general_macros (cgiargsclass &args, outconvertclass &/*outconvert*/, 
+					  ostream &logout) {
+
+  text_t &collection = args["c"];
+
+  disp.setmacro ("gsdlhome", displayclass::defaultpackage, dm_safe(configinfo.gsdlhome));
+  disp.setmacro ("gwcgi", displayclass::defaultpackage, configinfo.gwcgi);
+  disp.setmacro ("httpimg", displayclass::defaultpackage, configinfo.httpimg);
+  disp.setmacro ("httpprefix", displayclass::defaultpackage, configinfo.httpprefix);
+
+
+  text_t compressedoptions = get_compressed_arg(args, logout);
+  disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
+  // need a decoded version of compressedoptions for use within forms
+  // as browsers encode values from forms before sending to server
+  // (e.g. %25 becomes %2525)
+  decode_cgi_arg (compressedoptions); 
+  if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
+    // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
+    compressedoptions = to_uni(compressedoptions);
+  }
+  disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions)); 
+
+#if defined (__WIN32__)
+  disp.setmacro ("win32", displayclass::defaultpackage, "1");
+#endif
+
+  // set _cgiargX_ macros for each cgi argument
+  cgiargsclass::const_iterator argshere = args.begin();
+  cgiargsclass::const_iterator argsend = args.end();
+  while (argshere != argsend) {
+    if (((*argshere).first == "q") ||
+	((*argshere).first == "qa") ||
+	((*argshere).first == "qtt") ||
+	((*argshere).first == "qty") ||
+	((*argshere).first == "qp") ||
+	((*argshere).first == "qpl") ||
+	((*argshere).first == "qr") ||
+	((*argshere).first == "q2"))
+      // need to escape special characters from query string
+      disp.setmacro ("cgiarg" + (*argshere).first, 
+		     displayclass::defaultpackage, html_safe((*argshere).second.value));
+    else if ((*argshere).first == "hp") {
+      disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, (*argshere).second.value);
+    } else {
+      disp.setmacro ("cgiarg" + (*argshere).first, displayclass::defaultpackage, dm_safe((*argshere).second.value));
+    }
+    ++argshere;
+  }
+
+  // set collection specific macros
+  if (!collection.empty()) {
+    recptproto *collectproto = protocols.getrecptproto (collection, logout);
+    if (collectproto != NULL) {
+      FilterResponse_t response;
+      text_tset metadata;
+      get_info ("collection", collection, args["l"], metadata, false, 
+		collectproto, response, logout);
+      
+      if (!response.docInfo[0].metadata.empty()) {
+	MetadataInfo_tmap::const_iterator here = response.docInfo[0].metadata.begin();
+	MetadataInfo_tmap::const_iterator end = response.docInfo[0].metadata.end();
+	while (here != end) {
+	  if (((*here).first != "haschildren") && ((*here).first != "hasnext") &&
+	      ((*here).first != "hasprevious")) {
+	    // check for args in form name:lang
+	    text_t name = g_EmptyText;
+	    text_t lang = g_EmptyText;
+	    bool colonfound=false;
+	    text_t::const_iterator a = (*here).first.begin();
+	    text_t::const_iterator b = (*here).first.end();
+	    while (a !=b) {
+	      if (*a==':') {
+		colonfound=true;
+	      }
+	      else {
+		if (colonfound)
+		  lang.push_back(*a);
+		else name.push_back(*a);
+	      }
+	      ++a;
+	    }
+	    if (!lang.empty()) {
+	      if (args["l"]==lang) {
+		disp.setcollectionmacro(displayclass::defaultpackage, name, "", (*here).second.values[0]);
+	      }
+	    }
+	    else { // the default one
+	      disp.setcollectionmacro(displayclass::defaultpackage, (*here).first,  "", (*here).second.values[0]);
+	    }
+	  }
+	  ++here;
+	}
+      }
+
+      text_t iconcollection;
+      disp.expandstring (displayclass::defaultpackage, "_iconcollection_", iconcollection);
+      if (!iconcollection.empty())
+	{
+	  ColInfoResponse_t cinfo;
+	  comerror_t err;
+	  collectproto->get_collectinfo (collection, cinfo, err, logout);
+	  if (iconcollection[0]=='/' && !cinfo.httpdomain.empty())
+	    {
+	      // local but with full path
+	      iconcollection = "http://" + cinfo.httpdomain + iconcollection;
+	      disp.setmacro("iconcollection", displayclass::defaultpackage, iconcollection);
+	    }
+	}
+    }
+  }
+
+  if (!collection.empty()) {
+    ColInfoResponse_t cinfo;
+    comerror_t err;
+    recptproto *collectproto = protocols.getrecptproto (collection, logout);
+    if (collectproto != NULL) {
+      collectproto->get_collectinfo (collection, cinfo, err, logout);
+      text_t httpcollection;
+      if (!cinfo.httpdomain.empty()) httpcollection = "http://";
+      httpcollection += cinfo.httpdomain + cinfo.httpprefix + "/collect/"
+	+ collection; 
+      disp.setmacro ("httpcollection", displayclass::defaultpackage,
+		     httpcollection); 
+      // as of gsdl 2.53, collect.cfg can specify macros
+      if (cinfo.collection_macros.size() > 0) {
+	collectionmeta_map::const_iterator this_macro=cinfo.collection_macros.begin();
+	collectionmeta_map::const_iterator done_macro=cinfo.collection_macros.end();
+	while (this_macro != done_macro) {
+	  text_t package = "Global";
+	  text_t macroname = this_macro->first;
+	  // if this macro name is AAA:bbb then extract the package name
+	  text_t::const_iterator thischar, donechar;
+	  thischar = macroname.begin();
+	  donechar = macroname.end();
+	  while (thischar < donechar) {
+	    if (*thischar == ':') {
+	      package = substr(macroname.begin(),thischar);
+	      macroname = substr(thischar+1,donechar);
+	      break;
+	    }
+	    ++thischar;
+	  }
+
+	  text_tmap params_map = this_macro->second;
+	  text_tmap::const_iterator this_param = params_map.begin();
+	  text_tmap::const_iterator done_param = params_map.end();
+	  while (this_param != done_param) {
+	    disp.setcollectionmacro(package,
+				    macroname,
+				    this_param->first,
+				    this_param->second);
+	    ++this_param;
+	  }
+	  
+	  ++this_macro;
+	}
+      } // col macros
+    } // collectproto != NULL
+  }
+
+}
+
+// gets collection info from cache if found or 
+// calls collection server (and updates cache)
+// returns NULL if there's an error
+ColInfoResponse_t *receptionist::get_collectinfo_ptr (recptproto *collectproto, 
+						      const text_t &collection, 
+						      ostream &logout) {
+  
+  // check the cache
+  colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
+  if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
+    // found it
+    return &((*it).second.info);
+  }
+
+  // not cached, get info from collection server
+  if (collectproto == NULL) {
+    logout << "ERROR: receptionist::get_collectinfo_ptr passed null collectproto\n";
+    return NULL;
+  }
+   
+  comerror_t err;
+  if (it == configinfo.collectinfo.end()) {
+    collectioninfo_t cinfo;
+    collectproto->get_collectinfo (collection, cinfo.info, err, logout);
+    if (err != noError) {
+      outconvertclass text_t2ascii;
+      logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
+	     << get_comerror_string (err) << "\"while getting collectinfo\n";
+      return NULL;
+    }
+    cinfo.info_loaded = true;
+    configinfo.collectinfo[collection] = cinfo;
+    return &(configinfo.collectinfo[collection].info);
+  } else {
+    collectproto->get_collectinfo (collection, (*it).second.info, err, logout);
+    if (err != noError) {
+      outconvertclass text_t2ascii;
+      logout << text_t2ascii << "ERROR (receptionist::getcollectinfo_ptr): \""
+	     << get_comerror_string (err) << "\"while getting collectinfo\n";
+      return NULL;
+    }
+    (*it).second.info_loaded = true;
+    return &((*it).second.info);
+  }
+}
+
+// removes a collection from the cache so that the next
+// call to get_collectinfo_ptr() for that collection will
+// retrieve the collection info from the collection server
+void receptionist::uncache_collection (const text_t &collection) {
+
+  colinfo_tmap::iterator it = configinfo.collectinfo.find (collection);
+  if ((it != configinfo.collectinfo.end()) && ((*it).second.info_loaded)) {
+
+    (*it).second.info_loaded = false;
+
+  }
+}
+
+// Handles an "Encoding" line from a configuration file - note that the
+// configinfo.encodings map is a bit of a hack (to be fixed when the
+// configuration files are tidied up).
+void receptionist::configure_encoding (const text_tarray &cfgline) {
+
+  text_t subkey, subvalue, shortname, longname, mapfile;
+  int multibyte = 0;
+  text_t::const_iterator cfglinesub_here;
+  text_tarray::const_iterator cfgline_here = cfgline.begin();
+  text_tarray::const_iterator cfgline_end = cfgline.end();
+  while (cfgline_here != cfgline_end) {
+    if (*cfgline_here == "multibyte") {
+      multibyte = 1;
+    } else {
+      cfglinesub_here = getdelimitstr((*cfgline_here).begin(),
+				      (*cfgline_here).end(), '=', subkey);
+      if (subkey == "shortname") {
+	shortname = substr (cfglinesub_here, (*cfgline_here).end());
+      } else if (subkey == "longname") {
+	longname = substr (cfglinesub_here, (*cfgline_here).end());
+      } else if (subkey == "map") {
+	mapfile = substr (cfglinesub_here, (*cfgline_here).end());
+      }
+    }
+    ++cfgline_here;
+  }
+  if (!shortname.empty()) {
+    if (longname.empty()) longname = shortname;
+
+    // add the converter
+    if (shortname == "utf-8") {
+      utf8inconvertclass *utf8inconvert = new utf8inconvertclass();
+      utf8outconvertclass *utf8outconvert = new utf8outconvertclass();
+      utf8outconvert->set_rzws(1);
+      add_converter (shortname, utf8inconvert, utf8outconvert);
+      configinfo.encodings[longname] = shortname;
+
+    } else if (shortname == "utf-16be") {
+      // we use the default input converter as this shouldn't ever be used
+      // for converting from unicode...
+      inconvertclass *inconverter = new inconvertclass();
+      utf16outconvertclass *outconverter = new utf16outconvertclass();
+      add_converter (shortname, inconverter, outconverter);
+      configinfo.encodings[longname] = shortname;
+	
+    } else if (!mapfile.empty()) {
+
+      if (mapfile == "8859_1.ump") {
+	// iso-8859-1 is a special case as it'll always be supported by the
+	// standard converter class and therefore doesn't need to use its
+	// mapping file
+	inconvertclass *inconvert = new inconvertclass();
+	rzwsoutconvertclass *outconvert = new rzwsoutconvertclass();
+	outconvert->set_rzws(1);
+	add_converter (shortname, inconvert, outconvert);  
+	configinfo.encodings[longname] = shortname;
+
+      } else {
+	text_t to_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "to_uc", mapfile);
+	text_t from_uc_map = filename_cat(configinfo.gsdlhome, "mappings", "from_uc", mapfile);
+	if (file_exists(to_uc_map) && file_exists(from_uc_map)) {
+
+	  mapinconvertclass *mapinconvert = new mapinconvertclass();
+	  mapinconvert->setmapfile (to_uc_map, 0x003F);
+	  mapinconvert->set_multibyte (multibyte);
+	  mapoutconvertclass *mapoutconvert = new mapoutconvertclass();
+	  mapoutconvert->setmapfile (from_uc_map, 0x3F);
+	  mapoutconvert->set_multibyte (multibyte);
+	  mapoutconvert->set_rzws(1);
+	  add_converter (shortname, mapinconvert, mapoutconvert);
+	  configinfo.encodings[longname] = shortname;
+	}
+      }
+    }
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/receptionist.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/receptionist.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/receptionist.h	(revision 16561)
@@ -0,0 +1,284 @@
+/**********************************************************************
+ *
+ * receptionist.h -- a web interface for the gsdl
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef RECEPTIONIST_H
+#define RECEPTIONIST_H
+
+#include "gsdlconf.h"
+#include "text_t.h"
+#include "cgiargs.h"
+#include "display.h"
+#include "browserclass.h"
+#include "recptproto.h"
+#include "converter.h"
+#include "cfgread.h"
+#include "action.h"
+#include "cnfgable.h"
+
+// the MACROPRECEDENCE macro is used as a default. override
+// it using something like:
+//
+// macroprecedence c,l
+//
+// in your configuration file
+#ifndef MACROPRECEDENCE
+#define MACROPRECEDENCE "c,l"
+#endif
+
+
+struct collectioninfo_t {
+  void clear ();
+  collectioninfo_t () {clear();}
+
+  text_t gsdl_gsdlhome;
+  text_t gsdl_collecthome;
+  text_t gsdl_dbhome;
+
+  bool info_loaded;
+  ColInfoResponse_t info;
+};
+
+struct languageinfo_t {
+  void clear();
+  languageinfo_t () {clear();}
+
+  text_t longname;
+  text_t defaultencoding;
+};
+
+typedef map<text_t, collectioninfo_t, lttext_t> colinfo_tmap;
+typedef map<text_t, languageinfo_t, lttext_t> languageinfo_tmap;
+
+enum events_t {Disabled, CollectorEvents, AllEvents};
+enum ldformat_t {LocalTime, UTCTime, Absolute};
+
+struct recptconf {
+   text_t gsdlhome;
+   text_t collecthome;
+   text_t dbhome;   // will equal gsdlhome if not set
+   text_t collection; // will equal "" in 'general' mode
+   text_t collectdir; // will equal gsdlhome in 'general' mode
+   colinfo_tmap collectinfo;
+   text_t httpprefix;
+   text_t httpimg;    // will equal /images if not set
+   text_t gwcgi;
+   text_tset macrofiles;
+   text_t saveconf;
+   bool usecookies;  // true if we want cookies set
+   bool logcgiargs;  // true if we want to log cgi arguments
+   
+   ldformat_t LogDateFormat;
+   
+   text_t maintainer; // email address of maintainer
+   text_t MailServer; // SMTP mail server to use when sending event messages by email
+                     // defaults to mail.maintainer-domain
+   events_t LogEvents;
+   events_t EmailEvents;
+   bool EmailUserEvents;
+
+   bool    site_auth;   // Complete site authentication, useful to lockout site for example.
+   text_t  site_group;  // Similar to the auth_groups functionality, infact exactly the same
+   
+   text_t HomePageType; 
+   int HomePageCols;
+   
+   text_tmap pageparams;
+   text_t macroprecedence;
+   
+   languageinfo_tmap languages;
+   
+   // encodings is just a simple mapping from encoding longnames to
+   // shortnames.  It's useful for now for creating the pulldown menu of
+   // encodings on the preferences page but isn't intended to be permanent.
+   text_tmap encodings;
+   
+   void clear ();
+   recptconf () {clear();}
+};
+
+class receptionist : public configurable {
+protected:
+
+   recptconf configinfo;
+   
+   cgiargsinfoclass argsinfo;
+   actionmapclass actions;
+   browsermapclass browsers;
+   recptprotolistclass protocols;
+   displayclass disp;
+   convertinfoclass converters;
+   ColInfoResponse_t collectinfo;
+   
+   // prepare_page sets up page parameters, sets display macros
+   // and opens the page ready for output 
+   virtual void prepare_page (action *a, cgiargsclass &args, 
+			      outconvertclass &outconvert, 
+			     ostream &logout);
+   
+   virtual void translate_OIDs (cgiargsclass &args, recptproto *collectproto,
+				ostream &logout);
+   
+   bool get_cookie (text_t &cookie, text_tmap &fcgienv);
+   bool get_cookie (text_tmap &fcgienv);
+   
+   // get the default encoding for the given language - if it fails for any
+   // reason, return ""
+   text_t get_default_encoding (const text_t &language);
+   
+   virtual void define_general_macros (cgiargsclass &args, outconvertclass &outconvert, 
+				       ostream &logout);
+   
+   virtual bool append_logstr (const text_t &filename, const text_t &logstr,
+                               ostream &logout);
+
+   virtual void configure_encoding (const text_tarray &cfgline);
+   
+ public:
+   receptionist ();
+   virtual ~receptionist() {}
+
+#ifdef _APACHE_MOD
+   bool loaded;
+#endif   
+   // add_action makes another action available to the receptionist
+   // the action remains the property of the calling code and that
+   // code should destroy the action after the recptionist has been
+   // destroyed.
+   void add_action (action *theaction);
+   actionmapclass *get_actionmap_ptr () {return &actions;}
+   
+   // add_browser makes another browser available to the receptionist
+   // the browser remains the property of the calling code and that
+   // code should destroy it after the recptionist has been
+   // destroyed.
+   void add_browser (browserclass *thebrowser);
+   browsermapclass *get_browsermap_ptr () {return &browsers;}
+   void setdefaultbrowser (const text_t &browsername);
+   
+   // add_protocol makes another protocol available to the receptionist
+   // without any protocols, no collections will be available. The
+   // protocols remain the property of the calling code.
+   void add_protocol (recptproto *theprotocol) {protocols.addrecptproto(theprotocol);}
+   recptprotolistclass *get_recptprotolist_ptr () {return &protocols;}
+   
+   // add_converter makes another converter available to the receptionist.
+   // Converters are needed to display pages using different encodings.
+   // The converters remain the property of the calling code.
+   void add_converter (const text_t &name, inconvertclass *inconverter,
+		       rzwsoutconvertclass *outconverter) {
+      converters.add_converter(name, inconverter, outconverter);}
+   convertinfoclass *get_convertinfo_ptr () {return &converters;}
+   
+   
+   // configure should be called for each line in the
+  // configuration files to configure the receptionist and everything
+  // it contains. The configuration should take place after everything
+  // has been added but before the initialisation.
+  virtual void configure (const text_t &key, const text_tarray &cfgline);
+  virtual void configure (const text_t &key, const text_t &value);
+  const recptconf &get_configinfo () const {return configinfo;}
+  cgiargsinfoclass *get_cgiargsinfo_ptr () {return &argsinfo;}
+  
+
+  // init should be called after all the actions, protocols, and
+  // converters have been added to the receptionist and after everything
+  // has been configured but before any pages are created.
+  // It returns true on success and false on failure. If false is
+  // returned getpage should not be called (without producing
+  // meaningless output), instead an error page should be
+  // produced by the calling code.
+  virtual bool init (ostream &logout);
+  
+  // There are two ways to produce a page. You can either call parse_cgi_args,
+  // get_cgihead_info, and produce_content or you can just call parse_cgi_args and
+  // produce_cgi_page (which will be satisfactory in most cases). You might want to call
+  // parse_cgi_args, get_cgihead_info, and produce_content when you want to
+  // interface directly with a web server for which the standard header is inappropriate.
+
+  // parse_cgi_args parses cgi arguments into an argument class.
+  // This function should be called for each page request. It returns false
+  // if there was a major problem with the cgi arguments.
+  virtual bool parse_cgi_args (const text_t &argstr, 
+			       fileupload_tmap &fileuploads,
+			       cgiargsclass &args, 
+			       ostream &logout, text_tmap &fcgienv);
+
+  virtual bool log_cgi_args (cgiargsclass &args, ostream &logout, text_tmap &fcgienv);
+
+  text_t expandmacros (const text_t &astring, cgiargsclass &args, 
+		       ostream &logout);
+
+  // produce_cgi_page will call get_cgihead_info and 
+  // produce_content in the appropriate way to output a cgi header and
+  // the page content (if needed). If a page could not be created it
+  // will return false
+  virtual bool produce_cgi_page (cgiargsclass &args, ostream &contentout, 
+				 ostream &logout, text_tmap &fcgienv);
+  
+  // get_cgihead_info determines the cgi header information for
+  // a set of cgi arguments. If response contains location then
+  // response_data contains the redirect address. If reponse
+  // contains content then reponse_data contains the content-type.
+  // Note that images can now be produced by the receptionist.
+  virtual void get_cgihead_info (cgiargsclass &args, response_t &response,
+				 text_t &response_data, ostream &logout,
+				 text_tmap &fcgienv);
+  virtual void get_cgihead_info (cgiargsclass &args, text_tmap &headers,
+				 ostream &logout, text_tmap &fcgienv);
+
+  // produce the page content
+  virtual bool produce_content (cgiargsclass &args, ostream &contentout,
+				ostream &logout);
+  
+  // returns the compressed argument ("e") corresponding to the argument
+  // list. This can be used to save preferences between sessions.
+  virtual text_t get_compressed_arg (cgiargsclass &args, ostream &logout);
+
+  // gets collection info from cache if found or 
+  // calls collection server (and updates cache)
+  // returns NULL if there's an error
+  ColInfoResponse_t *get_collectinfo_ptr (recptproto *collectproto, 
+					  const text_t &collection, 
+					  ostream &logout);
+
+  void uncache_collection (const text_t &collection);
+  
+protected:
+  // will read in all the macro files. If one is not found an
+  // error message will be written to logout and the method will
+  // return false.
+  virtual bool read_macrofiles (ostream &logout);
+  
+  virtual void read_collection_macrofiles (const text_t& collection, ostream &logout);
+
+  // check_mainargs will check all the main arguments. If a major
+  // error is found it will return false and no cgi page should
+  // be created using the arguments.
+  virtual bool check_mainargs (cgiargsclass &args, ostream &logout);
+};
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/recptmain.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/recptmain.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/recptmain.cpp	(revision 16561)
@@ -0,0 +1,35 @@
+/**********************************************************************
+ *
+ * recptmain.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.
+ *
+ *********************************************************************/
+
+#include "receptionist.h"
+#include "cgiwrapper.h"
+#include <assert.h>
+
+int main () {
+  receptionist recpt;
+  cgiwrapper (recpt, g_EmptyText);
+  return 0;
+}
+
Index: /gsdl/trunk/gs2runtime/src/recpt/statusaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/statusaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/statusaction.cpp	(revision 16561)
@@ -0,0 +1,1147 @@
+/**********************************************************************
+ *
+ * statusaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_STATUS_ACTION
+
+// following line required to get fstream.filedesc() on darwin (Mac OS X)
+#define _STREAM_COMPAT  1
+
+#include "statusaction.h"
+#include "fileutil.h"
+#include "htmlutils.h"
+#include "gsdltools.h"
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h> // for open() stuff
+
+void statusaction::output_frameset (cgiargsclass &/*args*/, displayclass &disp, 
+				    outconvertclass &outconvert, 
+				    ostream &textout, ostream &/*logout*/) {
+  textout << outconvert << disp << "_status:frameset_\n";
+}
+
+void statusaction::output_select (cgiargsclass &/*args*/, displayclass &disp, 
+				  outconvertclass &outconvert, 
+				  ostream &textout, ostream &/*logout*/) {
+  textout << outconvert << disp << "_status:header_(selector)\n"
+    "_status:select_\n"
+    "_status:footer_\n";
+}
+
+void statusaction::output_welcome (cgiargsclass &args, recptprotolistclass *protos, 
+				   displayclass &disp, outconvertclass &outconvert, 
+				   ostream &textout, ostream &logout) {
+
+  if (recpt == NULL) return;
+
+  textout << outconvert << disp 
+	  << "_status:infoheader_(_titlewelcome_)\n"
+	  << "_status:welcome_"
+	  << "<table>\n"
+	  << "<th align=left>abbrev.</th><th align=left>collection</th>"
+	  << "<th align=left>public?</th><th align=left>running?</th></tr>\n";
+
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+      comerror_t err;
+      text_t protoname = (*rprotolist_here).p->get_protocol_name(err);
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+
+	while (collist_here != collist_end) {
+
+	    textout << outconvert << disp
+		    << "<tr><td><a href=\"_gwcgi_?_optsite_e=_compressedoptions_&a=status&p=collectioninfo&pr="
+		    << protoname
+		    << "&c="
+		    << *collist_here
+		    << "\">"
+		    << *collist_here 
+		    << "</a></td>";
+
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  if (cinfo != NULL) {
+	    text_t collname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	    if (collname.empty()) {
+	      collname = *collist_here;
+	    }
+
+	    textout << "<td>";
+	    if (cinfo->buildDate > 0)
+	      textout << outconvert << disp 
+		      << "<a href=\"_httppagex_(about)&c=" << *collist_here
+		      << "\" target=\\_top>";
+
+	    textout << outconvert << disp << collname;
+
+	    if (cinfo->buildDate > 0) textout << "</a>";
+
+	    textout << "</td>";
+
+	    if (cinfo->isPublic) textout << "<td>yes</td>";
+	    else textout << "<td>no</td>";
+	    
+	    if (cinfo->buildDate > 0)
+	      textout << outconvert << "<td>yes</td>";
+	    else
+	      textout << "<td>no</td>";
+	    
+	  } else {
+	    textout << "<td></td><td></td><td></td>";
+	  }
+
+	  textout << "</tr>\n";
+	  ++collist_here;
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  textout << "</table>\n";
+  textout << outconvert << disp << "_status:infofooter_\n";
+}
+
+void statusaction::output_generalinfo (cgiargsclass &/*args*/, displayclass &disp, 
+				       outconvertclass &outconvert, 
+				       ostream &textout, ostream &/*logout*/) {
+  if (recpt == NULL) return;
+  const recptconf &rcinfo = recpt->get_configinfo ();
+  
+  textout << outconvert << disp << "_status:infoheader_(General Information)\n";
+  textout << outconvert 
+	  << "<h2>General information</h2>\n"
+	  << "<table>\n"
+	  << "<tr valign=top><th>gsdlhome</th><td>\"" << rcinfo.gsdlhome
+	  << "\"</td></tr>\n"
+	  << "<tr valign=top><th>collection</th><td>\"" << rcinfo.collection
+	  << "\"</td></tr>\n"
+	  << "<tr valign=top><th>collectdir</th><td>\"" << rcinfo.collectdir
+	  << "\"</td></tr>\n"
+	  << "<tr valign=top><th>httpprefix</th><td>\"" << rcinfo.httpprefix
+	  << "\"</td></tr>\n"
+	  << "<tr valign=top><th>httpimg</th><td>\"" << rcinfo.httpimg
+	  << "\"</td></tr>\n"
+	  << "<tr valign=top><th>gwcgi</th><td>\"" << rcinfo.gwcgi
+	  << "\"</td></tr>\n";
+    
+  // macrofiles
+  textout << outconvert << "<tr valign=top><th>macrofiles</th><td>";
+  text_tset::const_iterator macrohere = rcinfo.macrofiles.begin ();
+  text_tset::const_iterator macroend = rcinfo.macrofiles.end ();
+  bool macrofirst = true;
+  while (macrohere != macroend) {
+    if (!macrofirst) textout << outconvert << ", ";
+    macrofirst = false;
+    textout << outconvert << "\"" << *macrohere << "\"";
+    ++macrohere;
+  }
+  textout << outconvert << "</td></tr>\n";
+  
+  // saveconf
+  textout << outconvert << "<tr valign=top><th>saveconf</th><td>\"" << rcinfo.saveconf
+	  << "\"</td></tr>\n";
+
+  // usecookies
+  textout << outconvert << "<tr valign=top><th>usecookies</th><td>\"";
+  if (rcinfo.usecookies) textout << outconvert << "true";
+  else textout << outconvert << "false";
+  textout << outconvert << "\"</td></tr>\n";
+  
+  // logcgiargs
+  textout << outconvert << "<tr valign=top><th>logcgiargs</th><td>\"";
+  if (rcinfo.logcgiargs) textout << outconvert << "true";
+  else textout << outconvert << "false";
+  textout << outconvert << "\"</td></tr>\n";
+
+  // pageparams
+  textout << outconvert << "<tr valign=top><th>pageparams</th><td>";
+  text_tmap::const_iterator params_here = rcinfo.pageparams.begin();
+  text_tmap::const_iterator params_end = rcinfo.pageparams.end();
+  bool params_first = true;
+  while (params_here != params_end) {
+    if (!params_first) textout << outconvert << ", ";
+    params_first = false;
+    textout << outconvert << "\"" << (*params_here).first << "\" != \""
+	    << (*params_here).second << "\"";
+    
+    ++params_here;
+  }
+  textout << outconvert << "</td></tr>\n";
+
+  // macroprecedence
+  textout << outconvert << "<tr valign=top><th>macroprecedence</th><td>\"" << rcinfo.macroprecedence
+	  << "\"</td></tr>\n";
+
+  // arguments
+  cgiargsinfoclass *rcargsinfo = recpt->get_cgiargsinfo_ptr ();
+  if (rcargsinfo != NULL) {
+    textout << outconvert << "<tr valign=top><th>arguments</th><td>";
+    
+    cgiargsinfoclass::const_iterator argsinfohere = rcargsinfo->begin ();
+    cgiargsinfoclass::const_iterator argsinfoend = rcargsinfo->end ();
+    bool argsinfofirst = true;
+    while (argsinfohere != argsinfoend) {
+      if (!argsinfofirst) textout << outconvert << ", ";
+      argsinfofirst = false;
+      textout << outconvert << "\"" << (*argsinfohere).second.shortname << "\"";
+      ++argsinfohere;
+    }
+    
+    textout << outconvert << "</td></tr>\n";
+  }      
+  
+  // actions
+  actionmapclass *actions = recpt->get_actionmap_ptr();
+  if (actions != NULL) {
+    textout << outconvert << "<tr valign=top><th>actions</th><td>";
+    
+    actionptrmap::iterator actionshere = actions->begin ();
+    actionptrmap::iterator actionsend = actions->end ();
+    bool actionsfirst = true;
+    while (actionshere != actionsend) {
+      if (!actionsfirst) textout << outconvert << ", ";
+      actionsfirst = false;
+      assert ((*actionshere).second.a != NULL);
+      if ((*actionshere).second.a != NULL) {
+	textout << outconvert << "\"" << (*actionshere).second.a->get_action_name() << "\"";
+      }
+      ++actionshere;
+    }
+    
+    textout << outconvert << "</td></tr>\n";
+  }
+  
+  // browsers
+  browsermapclass *browsers = recpt->get_browsermap_ptr();
+  if (browsers != NULL) {
+    textout << outconvert << "<tr valign=top><th>browsers</th><td>";
+    
+    browserptrmap::iterator browsershere = browsers->begin ();
+    browserptrmap::iterator browsersend = browsers->end ();
+    bool browsersfirst = true;
+    while (browsershere != browsersend) {
+      if (!browsersfirst) textout << outconvert << ", ";
+      browsersfirst = false;
+      assert ((*browsershere).second.b != NULL);
+      if ((*browsershere).second.b != NULL) {
+	textout << outconvert << "\"" << (*browsershere).second.b->get_browser_name() << "\"";
+      }
+      ++browsershere;
+    }
+    
+    textout << outconvert << "</td></tr>\n";
+  }
+
+  // protocols
+  recptprotolistclass *protocols = recpt->get_recptprotolist_ptr ();
+  if (protocols != NULL) {
+    textout << outconvert << "<tr valign=top><th>protocols</th><td>";
+    
+    recptprotolistclass::iterator protohere = protocols->begin ();
+    recptprotolistclass::iterator protoend = protocols->end ();
+    bool protofirst = true;
+    while (protohere != protoend) {
+      comerror_t err;
+      if (!protofirst) textout << outconvert << ", ";
+      protofirst = false;
+      if ((*protohere).p != NULL) {
+	textout << outconvert << "\"" << (*protohere).p->get_protocol_name(err) << "\"";
+      }
+      ++protohere;
+    }
+    
+    textout << outconvert << "</td></tr>\n";
+  }
+  
+  // converters
+  convertinfoclass *converters = recpt->get_convertinfo_ptr ();
+  if (converters != NULL) {
+    textout << outconvert << "<tr valign=top><th>converters</th><td>";
+    
+    convertinfoclass::iterator converthere = converters->begin ();
+    convertinfoclass::iterator convertend = converters->end ();
+    bool convertfirst = true;
+    while (converthere != convertend) {
+      if (!convertfirst) textout << outconvert << ", ";
+      convertfirst = false;
+      textout << outconvert << "\"" << (*converthere).second.name << "\"";
+      ++converthere;
+    }
+    
+    textout << outconvert << "</td></tr>\n";
+  }
+  
+  textout << outconvert << disp << "</table>\n_status:infofooter_\n";
+}
+
+void statusaction::output_argumentinfo (cgiargsclass &args, displayclass &disp, 
+					outconvertclass &outconvert, 
+					ostream &textout, ostream &/*logout*/) {
+  if (recpt == NULL) return;
+  cgiargsinfoclass *rcargsinfo = recpt->get_cgiargsinfo_ptr ();
+  if (rcargsinfo == NULL) return;
+
+  textout << outconvert << disp << "_status:infoheader_(Argument Information)\n";
+  textout << outconvert
+	  << "<h2>Argument information</h2>\n"
+	  << "<table>";
+
+  // argument information
+  textout << outconvert << "<tr valign=top><th>short name</th><th>long name</th>"
+	  << "<th>multiple char?</th>"
+	  << "<th>multiple value?</th>"
+	  << "<th>default</th><th>default status</th><th>saved args</th>"
+	  << "<th>current value</th></tr>\n";
+  
+
+  cgiargsinfoclass::const_iterator argsinfohere = rcargsinfo->begin();
+  cgiargsinfoclass::const_iterator argsinfoend = rcargsinfo->end();
+  text_t *arg_value;
+  while (argsinfohere != argsinfoend) {
+    const cgiarginfo &ainfo = (*argsinfohere).second;
+    textout << outconvert 
+	    << "<tr valign=top><td>" << ainfo.shortname << "</td>\n";
+    
+    textout << outconvert << "<td>" << ainfo.longname << "</td>\n";
+    if (ainfo.multiplechar) textout << outconvert << "<td>yes</td>\n";
+    else textout << outconvert << "<td>no</td>\n";
+    if (ainfo.multiplevalue) textout << outconvert << "<td>yes</td>\n";
+    else textout << outconvert << "<td>no</td>\n";
+    textout << outconvert << "<td>" << ainfo.argdefault << "</td>\n";
+    switch (ainfo.defaultstatus) {
+    case cgiarginfo::none: textout << outconvert << "<td>none</td>\n"; break;
+    case cgiarginfo::weak: textout << outconvert << "<td>weak</td>\n"; break;
+    case cgiarginfo::good: textout << outconvert << "<td>good</td>\n"; break;
+    case cgiarginfo::config: textout << outconvert << "<td>config</td>\n"; break;
+    case cgiarginfo::imperative: textout << outconvert << "<td>imperative</td>\n"; break;
+    }
+    switch (ainfo.savedarginfo) {
+    case cgiarginfo::mustnot: textout << outconvert << "<td>mustnot</td>\n"; break;
+    case cgiarginfo::can: textout << outconvert << "<td>can</td>\n"; break;
+    case cgiarginfo::must: textout << outconvert << "<td>must</td>\n"; break;
+    }
+    
+    arg_value = args.getarg (ainfo.shortname);
+    if (arg_value == NULL) textout << outconvert << "<td></td></tr>\n";
+    else textout << outconvert << "<td>\"" << *arg_value << "\"</td></tr>\n";
+    
+    ++argsinfohere;
+  }
+  
+  textout << outconvert << disp << "</table>\n_status:infofooter_\n";
+}
+
+void statusaction::output_actioninfo (cgiargsclass &/*args*/, displayclass &disp, 
+				      outconvertclass &outconvert, 
+				      ostream &textout, ostream &/*logout*/) {
+  if (recpt == NULL) return;
+  actionmapclass *actions = recpt->get_actionmap_ptr();
+
+  textout << outconvert << disp << "_status:infoheader_(Action Information)\n";
+  textout << outconvert
+	  << "<h2>Action information</h2>\n"
+	  << "<table>";
+
+  // action information
+  if (actions != NULL) {
+    textout << outconvert 
+	    << "<tr><th>action name</th><th>cgi arguments</th></tr>\n";
+	
+    actionptrmap::iterator actionshere = actions->begin ();
+    actionptrmap::iterator actionsend = actions->end ();
+    while (actionshere != actionsend) {
+      assert ((*actionshere).second.a != NULL);
+      if ((*actionshere).second.a != NULL) {
+	textout << outconvert 
+		<< "<tr><td>" << (*actionshere).second.a->get_action_name()
+		<< "</td><td>";
+	
+	cgiargsinfoclass *argsinfo = (*actionshere).second.a->getargsinfo();
+	cgiargsinfoclass::const_iterator argsinfohere = argsinfo->begin ();
+	cgiargsinfoclass::const_iterator argsinfoend = argsinfo->end ();
+	bool aifirst = true;
+	while (argsinfohere != argsinfoend) {
+	  if (!aifirst) textout << outconvert << ", ";
+	  aifirst = false;
+	  textout << outconvert << (*argsinfohere).second.shortname;
+	  ++argsinfohere;
+	}
+	
+	textout << outconvert << "</td></tr>\n";
+      }
+      ++actionshere;
+    }
+  }
+  
+  textout << outconvert << disp << "</table>\n_status:infofooter_\n";
+}
+
+void statusaction::output_browserinfo (cgiargsclass &/*args*/, displayclass &disp, 
+				       outconvertclass &outconvert, 
+				       ostream &textout, ostream &/*logout*/) {
+  if (recpt == NULL) return;
+  browsermapclass *browsers = recpt->get_browsermap_ptr();
+
+  textout << outconvert << disp << "_status:infoheader_(Browser Information)\n";
+  textout << outconvert
+	  << "<h2>Browser information</h2>\n"
+	  << "<table>";
+
+  // browser information
+  if (browsers != NULL) {
+    textout << outconvert 
+	    << "<tr><th>browser name</th><th>default formatstring</th></tr>\n";
+	
+    browserptrmap::iterator browsershere = browsers->begin ();
+    browserptrmap::iterator browsersend = browsers->end ();
+    while (browsershere != browsersend) {
+      assert ((*browsershere).second.b != NULL);
+      if ((*browsershere).second.b != NULL) {
+	textout << outconvert 
+		<< "<tr><td>" << (*browsershere).second.b->get_browser_name()
+		<< "</td><td>" << html_safe ((*browsershere).second.b->get_default_formatstring())
+		<< "</td></tr>\n";
+      }
+      ++browsershere;
+    }
+  }
+  
+  textout << outconvert << disp << "</table>\n_status:infofooter_\n";
+}
+
+void statusaction::output_protocolinfo (cgiargsclass &/*args*/, displayclass &disp, 
+					outconvertclass &outconvert, 
+					ostream &textout, ostream &logout) {
+  if (recpt == NULL) return;
+  const recptconf &rcinfo = recpt->get_configinfo ();
+  bool colspecific = !rcinfo.collection.empty();
+  bool unreachablecol = false;
+
+  recptprotolistclass *rprotolist = recpt->get_recptprotolist_ptr ();
+  if (rprotolist == NULL) return;
+
+  textout << outconvert << disp << "_status:infoheader_(Protocol Information)\n";
+  textout << outconvert
+	  << "<h2>Protocol information</h2>\n"
+	  << "<table>\n"
+	  << "<tr><th>protocol</th><th>collections</th></tr>\n";
+
+  text_t protoname;
+  recptprotolistclass::iterator rprotolist_here = rprotolist->begin();
+  recptprotolistclass::iterator rprotolist_end = rprotolist->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+      comerror_t err;
+      protoname = (*rprotolist_here).p->get_protocol_name(err);
+      textout << outconvert
+	      << "<tr><td>"
+	      << protoname
+	      << "</td><td>";
+
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+	bool first = true;
+	while (collist_here != collist_end) {
+	  if (!first) textout << outconvert << ", ";
+	  first = false;
+
+	  if (colspecific && *collist_here != rcinfo.collection) {
+	    unreachablecol = true;
+	    textout << outconvert << "\"" << *collist_here << "\"";
+	  } else {
+	    textout << outconvert << disp
+		    << "\"<a href=\"_gwcgi_?e=_compressedoptions_&a=status&p=collectioninfo&pr="
+		    << protoname
+		    << "&c="
+		    << *collist_here
+		    << "\">"
+		    << *collist_here 
+		    << "</a>\"";
+	  }
+
+	  ++collist_here;
+	}
+
+      } else {
+	textout << outconvert << "Error (" << get_comerror_string (err) 
+		<< ") while getting collect list\n";
+      }
+
+      textout << outconvert 
+	      << "</td></tr>\n";
+    }
+
+    ++rprotolist_here;
+  }
+
+  textout << outconvert << "</table>\n";
+  if (unreachablecol) {
+    textout << outconvert 
+	    << "<b>Warning:</b> the receptionist is running in collection specific\n"
+	    << "mode making some of the collections unreachable.\n";
+  }
+  textout << outconvert << disp << "_status:infofooter_\n";
+}
+
+void statusaction::output_collectioninfo (cgiargsclass &args, displayclass &disp, 
+					  outconvertclass &outconvert, 
+					  ostream &textout, ostream &logout) {
+  if (recpt == NULL) return;
+
+  textout << outconvert << disp << "_status:infoheader_(Collection info)\n";
+  textout << outconvert << "<h2>Collection info</h2>\n";
+
+  // get the list of protocols
+  recptprotolistclass *rprotolist = recpt->get_recptprotolist_ptr ();
+  if (rprotolist == NULL) return;
+
+  // look for the desired protocol
+  text_t &arg_pr = args["pr"];
+  text_t &arg_c = args["c"];
+  recptproto *rproto = NULL;
+  recptprotolistclass::iterator rprotolist_here = rprotolist->begin();
+  recptprotolistclass::iterator rprotolist_end = rprotolist->end();
+  while (rprotolist_here != rprotolist_end) {
+    rproto = (*rprotolist_here).p;
+    comerror_t err;
+    if (rproto != NULL && rproto->get_protocol_name(err) == arg_pr) {
+      // see if the protocol has the collection
+      bool hascollection;
+      rproto->has_collection (arg_c, hascollection, err, logout);
+      if (err == noError && hascollection) break;
+    }
+    ++rprotolist_here;
+  }
+  
+  if (rprotolist_here == rprotolist_end) {
+    textout << outconvert << "Protocol \"" << arg_pr << "\" with collection \""
+	    << arg_c << "\" was not found\n";
+
+  } else {
+    // rproto can't be NULL to get here
+    ColInfoResponse_t *collectinfo = recpt->get_collectinfo_ptr (rproto, arg_c, logout);
+    if (collectinfo != NULL) {
+      textout << outconvert << "<table>\n"
+	      << "<tr><th>collection name</th><td>\""
+	      << collectinfo->shortInfo.name
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>host</th><td>\""
+	      << collectinfo->shortInfo.host
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>port</th><td>\""
+	      << collectinfo->shortInfo.port
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>is public?</th><td>";
+      if (collectinfo->isPublic) textout << outconvert << "true";
+      else textout << outconvert << "false";
+      textout << outconvert 
+	      << "</td></tr>\n"
+
+	      << "<tr><th>is beta?</th><td>";
+      if (collectinfo->isBeta) textout << outconvert << "true";
+      else textout << outconvert << "false";
+      textout << outconvert 
+	      << "</td></tr>\n"
+
+	      << "<tr><th>build date</th><td>\""
+	      << collectinfo->buildDate
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>interface languages</th><td>";
+      text_tarray::iterator languages_here = collectinfo->languages.begin();
+      text_tarray::iterator languages_end = collectinfo->languages.end();
+      bool languages_first = true;
+      while (languages_here != languages_end) {
+	if (!languages_first) textout << outconvert << ", ";
+	languages_first = false;
+	textout << outconvert << "\"" << *languages_here << "\"";
+	++languages_here;
+      }
+
+      textout << "<tr><th valign=top>collection metadata</th><td><table>\n";
+      collectionmeta_map::iterator meta_here = collectinfo->collectionmeta.begin();
+      collectionmeta_map::iterator meta_end = collectinfo->collectionmeta.end();
+      
+      while (meta_here != meta_end) {
+	textout << outconvert << "<tr><td valign=top>" << (*meta_here).first
+		<< "</td><td><table>" ;
+      	text_tmap lang_map = (*meta_here).second;
+	text_tmap::iterator lang_here = lang_map.begin();
+	text_tmap::iterator lang_end = lang_map.end();
+	while (lang_here != lang_end) {
+	  textout << outconvert << "<tr><td>" << (*lang_here).first
+		    << "</td><td>" << (*lang_here).second << "</td></tr>\n";
+	  
+	  ++lang_here;
+	}
+	textout << outconvert << "</table></td></tr>\n";
+	++meta_here;
+      }
+      textout << "</table></td></tr>\n";
+
+      textout << "<tr><th valign=top>format info</th><td><table>\n";
+      text_tmap::iterator format_here = collectinfo->format.begin();
+      text_tmap::iterator format_end = collectinfo->format.end();
+      while (format_here != format_end) {
+	textout << outconvert << "<tr><td valign=top>" << (*format_here).first
+		<< "</td><td>" << html_safe((*format_here).second) << "</td></tr>\n";
+	++format_here;
+      }
+      textout << "</table></td></tr>\n";
+
+      textout << "<tr><th valign=top>building info</th><td><table>\n";
+      text_tmap::iterator building_here = collectinfo->building.begin();
+      text_tmap::iterator building_end = collectinfo->building.end();
+      while (building_here != building_end) {
+	textout << outconvert << "<tr><td>" << (*building_here).first
+		<< "</td><td>" << (*building_here).second << "</td></tr>\n";
+	++building_here;
+      }
+      textout << "</table></td></tr>\n";
+
+      textout << outconvert 
+	      << "</td></tr>\n"
+
+	      << "<tr><th>number of documents</th><td>\""
+	      << collectinfo->numDocs
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>number of sections</th><td>\""
+	      << collectinfo->numSections
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>number of words</th><td>\""
+	      << collectinfo->numWords
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>number of bytes</th><td>\""
+	      << collectinfo->numBytes
+	      << "\"</td></tr>\n"
+
+	      << "<tr><th>preferred receptionist</th><td>\""
+	      << collectinfo->receptionist
+	      << "\"</td></tr>\n"
+
+	      << "</table>";
+      
+    } else {
+      textout << "ERROR (statusaction::output_collectioninfo): while getting collect information\n";
+    }
+
+
+    InfoFiltersResponse_t filterinfo;
+    InfoFilterOptionsRequest_t filteroptions_request;
+    InfoFilterOptionsResponse_t filteroptions;
+    comerror_t err;
+    rproto->get_filterinfo (arg_c, filterinfo, err, logout);
+    if (err == noError) {
+      text_tset::iterator filternames_here = filterinfo.filterNames.begin();
+      text_tset::iterator filternames_end = filterinfo.filterNames.end();
+      while (filternames_here != filternames_end) {
+	textout << outconvert 
+		<< "<hr>\n"
+		<< "<h3>Filter options for \"" << (*filternames_here) << "\"</h3>\n"
+		<< "<table>\n"
+		<< "<tr><th>option name</th><th>type</th><th>repeatable</th>"
+		<< "<th>default value</th><th>valid values</th></tr>\n";
+
+	filteroptions_request.clear();
+	filteroptions_request.filterName = *filternames_here;
+	rproto->get_filteroptions (arg_c, filteroptions_request,
+				   filteroptions, err, logout);
+	if (err == noError) {
+	  FilterOption_tmap::iterator filteropt_here = 
+	    filteroptions.filterOptions.begin();
+	  FilterOption_tmap::iterator filteropt_end = 
+	    filteroptions.filterOptions.end();
+	  while (filteropt_here != filteropt_end) {
+	    textout << outconvert
+		    << "<tr><td>\""
+		    << (*filteropt_here).second.name
+		    << "\"</td>\n"
+	      
+		    << "<td>";
+	    text_t type_string;
+	    switch ((*filteropt_here).second.type) {
+	    case FilterOption_t::booleant: type_string = "boolean"; break;
+	    case FilterOption_t::integert: type_string = "integer"; break;
+	    case FilterOption_t::enumeratedt: type_string = "enumerated"; break;
+	    case FilterOption_t::stringt: type_string = "string"; break;
+	    }
+	    textout << outconvert
+		    << type_string
+		    << "</td>\n"
+	      
+		    << "<td>";
+	    text_t repeat_string;
+	    switch ((*filteropt_here).second.repeatable) {
+	    case FilterOption_t::onePerQuery: repeat_string = "one per query"; break;
+	    case FilterOption_t::onePerTerm: repeat_string = "one per term"; break;
+	    case FilterOption_t::nPerTerm: repeat_string = "n per term"; break;
+	    }
+	    textout << outconvert
+		    << repeat_string
+		    << "</td>\n"
+	      
+		    << "<td>\""
+		    << (*filteropt_here).second.defaultValue
+		    << "\"</td>\n"
+	      
+		    << "<td>";
+	    
+	    text_tarray::iterator valid_here = 
+	      (*filteropt_here).second.validValues.begin();
+	    text_tarray::iterator valid_end = 
+	      (*filteropt_here).second.validValues.end();
+	    bool valid_first = true;
+	    while (valid_here != valid_end) {
+	      if (!valid_first) textout << outconvert << ", ";
+	      valid_first = false;
+	      textout << outconvert << "\"" << *valid_here << "\"";
+	      ++valid_here;
+	    }
+	    textout << outconvert
+		    << "</td></tr>\n";
+
+	    ++filteropt_here;
+	  }
+
+	  textout << outconvert
+		  << "</table>\n";
+
+	} else {
+	  textout << outconvert << "Error (" << get_comerror_string (err) 
+		  << ") while getting filter option information\n";
+	}
+
+	++filternames_here;
+      }
+
+    } else {
+      textout << outconvert << "Error (" << get_comerror_string (err) 
+	      << ") while getting filter information\n";
+    }
+  }
+  
+  textout << outconvert << disp << "_status:infofooter_\n";
+}
+
+void statusaction::output_usagelog (cgiargsclass &/*args*/, displayclass &disp,
+				    outconvertclass &outconvert, 
+				    ostream &textout, ostream &/*logout*/) {
+
+  // output last 100 lines of usage.txt
+
+  text_t logfilename = filename_cat (gsdlhome, "etc", "usage.txt");
+
+  textout << outconvert << disp << "_status:infoheader_(Usage log)\n";
+  textout << outconvert << "<h2>Usage log</h2>\n";
+
+  textout << outconvert << "<p>The usage log, "
+	  << logfilename << ", contains the\n"
+	  << "following information:\n"
+	  << "<p>(note that if this file is more than 100 lines long only the last 100 lines will\n"
+	  << "be displayed on this page)\n\n"
+	  << "<pre>\n";
+
+  // note that we're expecting lines to be no more than 1500 characters on
+  // average - should fix this file_tail() thing sometime
+  textout << outconvert << file_tail (logfilename, 100, 1500);
+
+  textout << outconvert << disp << "</pre>\n"
+	  << "_status:infofooter_\n";
+}
+
+void statusaction::output_errorlog (cgiargsclass &/*args*/, displayclass &disp, 
+				    outconvertclass &outconvert, 
+				    ostream &textout, ostream &logout) {
+
+  text_t errfilename = filename_cat (gsdlhome, "etc", "error.txt");
+  char *cerrfilename = errfilename.getcstr();
+  if (cerrfilename == NULL) return;
+
+  textout << outconvert << disp << "_status:infoheader_(Error log)\n";
+  textout << outconvert << "<h2>Error log</h2>\n";
+  logout << flush;
+
+#ifdef GSDL_USE_IOS_H
+  ifstream errin (cerrfilename, ios::in | ios::nocreate);
+#else
+  ifstream errin (cerrfilename, ios::in);
+#endif
+
+  delete []cerrfilename;
+  if (errin) {
+    textout << outconvert << "<p>The error log, "
+		<< errfilename << ", contains the\n";
+    textout << outconvert << "following information:\n\n";
+    text_t errorpage = "<p><pre>\n";
+
+    char c;
+    errin.get(c);
+    while (!errin.eof ()) {
+      errorpage.push_back(c);
+      errin.get(c);
+    }
+    
+    errorpage += "</pre>\n";
+    errin.close();
+    textout << outconvert << errorpage;
+
+  } else {
+    textout << outconvert << "Couldn't read error log, " 
+		<< errfilename << ".\n";
+  }
+
+  textout << outconvert << disp << "_status:infofooter_\n";
+}
+
+void statusaction::output_gsdlsite (cgiargsclass &/*args*/, displayclass &disp, 
+				    outconvertclass &outconvert, 
+				    ostream &textout, ostream &logout) {
+
+  textout << outconvert << disp << "_status:infoheader_(gsdlsite.cfg)\n";
+  textout << outconvert << "<h2>gsdlsite.cfg</h2>\n";
+  logout << flush;
+
+#ifdef GSDL_LOCAL_LIBRARY
+  text_t gsdlsite_cfg = filename_cat (gsdlhome, "gsdlsite.cfg");
+#else
+  text_t gsdlsite_cfg = "gsdlsite.cfg";
+#endif
+
+  char *gsdlsite_cfgc = gsdlsite_cfg.getcstr();
+#ifdef GSDL_USE_IOS_H
+  ifstream gsdlsitein (gsdlsite_cfgc, ios::in | ios::nocreate);
+#else
+  ifstream gsdlsitein (gsdlsite_cfgc, ios::in);
+#endif
+
+  delete []gsdlsite_cfgc;
+
+  if (gsdlsitein) {
+    textout << "<p>The gsdlsite.cfg configuration file contains the\n"
+	    << "following information:\n\n";
+    text_t gsdlsite = "<p><pre>\n";
+
+    char c;
+    gsdlsitein.get(c);
+    while (!gsdlsitein.eof ()) {
+      gsdlsite.push_back(c);
+      gsdlsitein.get(c);
+    }
+    
+    gsdlsite += "</pre>\n";
+    gsdlsitein.close();
+    textout << outconvert << gsdlsite;
+
+  } else {
+    textout << "Couldn't read gsdlsite.cfg configuration file\n";
+  }
+
+  textout << outconvert << disp << "_status:infofooter_\n";
+}
+
+void statusaction::output_maincfg (cgiargsclass &/*args*/, displayclass &disp, 
+				   outconvertclass &outconvert, 
+				   ostream &textout, ostream &/*logout*/) {
+
+  text_t maincfgfile = filename_cat (dbhome, "etc", "main.cfg");
+
+  textout << outconvert << disp << "_status:infoheader_(main.cfg)\n"
+	  << "<h2>main.cfg</h2>\n"
+	  << "<p>The main configuration file, "
+	  << dm_safe(maincfgfile) << ", contains the following information:<br>\n\n"
+	  << "(Note that only users belonging to the \"administrator\" group "
+	  << "may edit this file)<br>\n"
+	  << "_status:maincfg_<br>\n"
+	  << "_status:infofooter_\n";
+}
+
+void statusaction::change_maincfg (cgiargsclass &args, displayclass &disp, 
+				   outconvertclass &outconvert, 
+				   ostream &textout, ostream &logout) {
+
+  // write out the contents of the cfgfile argument to main.cfg
+  text_t cfgfile = filename_cat(gsdlhome, "etc", "main.cfg");
+  char *cfgfilec = cfgfile.getcstr();
+
+  int fd=open(cfgfilec, O_WRONLY | O_CREAT | O_TRUNC
+#if defined(__WIN32__)
+	      | O_BINARY
+#endif
+	      );
+
+  if (fd != -1) {
+
+    int lock_val = 1;
+    GSDL_LOCK_FILE (fd);
+    if (lock_val != 0) {
+      logout << "statusaction::change_maincfg: Error: Couldn't lock file " << cfgfilec << "\n";
+      textout << outconvert << disp << "_status:changemaincfgfail_";
+      close (fd);
+    } else {
+      outconvertclass text_t2ascii;
+      text_t2ascii.setinput(&args["cfgfile"]);
+      size_t buffersize=args["cfgfile"].size();
+      char *buffer=new char[buffersize];
+      size_t num_chars;
+      convertclass::status_t status;
+      text_t2ascii.convert(buffer, buffersize, num_chars, status);
+      // ignore status - assume it is "finished" as buffer is big enough
+      write(fd, buffer, num_chars);
+      GSDL_UNLOCK_FILE (fd);
+      delete []buffer;
+      close (fd);
+      textout << outconvert << disp << "_status:changemaincfgsuccess_";
+    }
+  } else {
+    logout << "statusaction::change_maincfg: Error: Couldn't open file " << cfgfilec << "for writing\n";
+    textout << outconvert << disp << "_status:changemaincfgfail_";
+  }
+  delete []cfgfilec;
+}
+
+void statusaction::output_errorpage (outconvertclass &outconvert, 
+				     ostream &textout, ostream &/*logout*/,
+				     text_t message) {
+  textout << outconvert 
+	  << "<html>\n"
+	  << "<head>\n"
+	  << "<title>Error</title>\n"
+	  << "</head>\n"
+	  << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	  << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	  << "<h2>Oops!</h2>\n" 
+	  << message
+	  << "\n</body>\n"
+	  << "</html>\n";
+  return;
+}
+
+
+
+statusaction::statusaction () {
+  disabled = true;
+  recpt = NULL;
+
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "status";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "p" -- status page
+  arg_ainfo.shortname = "p";
+  arg_ainfo.longname = "page";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "frameset";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "pr" -- protocol
+  arg_ainfo.shortname = "pr";
+  arg_ainfo.longname = "protocol";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::none;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::can;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  arg_ainfo.shortname = "cfgfile";
+  arg_ainfo.longname = "configuration file contents";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+statusaction::~statusaction () {
+}
+
+bool statusaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &args, 
+				  recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+
+  // only users belonging to the "administrator" group may edit 
+  // the main.cfg file
+  if (args["p"] == "maincfg" || args["p"] == "changemaincfg") {
+    args["uan"] = "1";
+    args["ug"] = "administrator";
+  }
+  return true;
+}
+
+void statusaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				     response_t &response, text_t &response_data, 
+				     ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+
+void statusaction::define_internal_macros (displayclass &disp, cgiargsclass &args, 
+					   recptprotolistclass * /*protos*/, ostream &logout) {
+
+  // define_internal_macros sets the following macros:
+  // _maincfgfile_   the contents of the main.cfg configuration file
+
+  // _versionnnum_   greenstone version number (hard-coded)
+
+  disp.setmacro("versionnum", "status", GSDL_VERSION);
+
+  if (args["p"] == "maincfg") {
+
+    // read in main.cfg
+    text_t maincfg = filename_cat(gsdlhome, "etc", "main.cfg");  
+    char *maincfgc = maincfg.getcstr();
+    
+#ifdef GSDL_USE_IOS_H
+    ifstream cfg_ifs (maincfgc, ios::in | ios::nocreate);
+#else
+    ifstream cfg_ifs (maincfgc, ios::in);
+#endif
+    
+    if (cfg_ifs) {  
+      text_t cfgtext;
+      char c;
+      cfg_ifs.get(c);
+      while (!cfg_ifs.eof ()) {
+	cfgtext.push_back(c);
+	cfg_ifs.get(c);
+      }
+      cfg_ifs.close();
+      
+      // define it as a macro
+      disp.setmacro("maincfgfile", "status", dm_safe(cfgtext));
+      
+    } else {
+      logout << "statusaction::define_internal_macros: couldn't open configuration file (" 
+	     << maincfgc << ") for reading\n";
+      disp.setmacro("maincfgfile", "status", g_EmptyText);
+    }    
+    delete []maincfgc;
+  }
+}
+
+bool statusaction::do_action (cgiargsclass &args, recptprotolistclass *protos, 
+			      browsermapclass * /*browsers*/, displayclass &disp, 
+			      outconvertclass &outconvert, ostream &textout, 
+			      ostream &logout) {
+  // make sure the status function is enabled
+  if (disabled) {
+    textout << outconvert 
+	    << "<html>\n"
+	    << "<head>\n"
+	    << "<title>Status disabled</title>\n"
+	    << "</head>\n"
+	    << "<body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#006666\" "
+	    << "alink=\"#cc9900\" vlink=\"#666633\">\n"
+	    << "<h2>Facility disabled</h2>\n" 
+	    << "Sorry, the Maintenance and Administration facility is currently disabled\n"
+	    << "\n</body>\n"
+	    << "</html>\n";
+    return true;
+  }
+  
+  // make sure we know about a receptionist
+  if (recpt == NULL) {
+    output_errorpage (outconvert, textout, logout, 
+		      "The status action does not contain information\n"
+		      "about any receptionists. The method set_receptionist\n"
+		      "was probably not called from the module which instantiated\n"
+		      "this status action.\n");
+    return true;
+  }
+
+  // check to make sure status.dm was read in
+  const recptconf &rcinfo = recpt->get_configinfo ();
+  text_tset::const_iterator macrohere = rcinfo.macrofiles.begin ();
+  text_tset::const_iterator macroend = rcinfo.macrofiles.end ();
+  while (macrohere != macroend) {
+    if (*macrohere == "status.dm") break;
+    ++macrohere;
+  }
+  if (macrohere == macroend) {
+    output_errorpage (outconvert, textout, logout, 
+		      "The status.dm file was not read in. This macro file is\n" 
+		      "needed to display configuration and status information.\n");
+    return true;
+  }
+
+  // output the required status page
+  text_t &arg_p = args["p"];
+  if (arg_p == "frameset") output_frameset (args, disp, outconvert, textout, logout);
+  else if (arg_p == "select") output_select (args, disp, outconvert, textout, logout);
+  else if (arg_p == "welcome") output_welcome (args, protos, disp, outconvert, textout, logout);
+  else if (arg_p == "generalinfo") output_generalinfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "argumentinfo") output_argumentinfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "actioninfo") output_actioninfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "browserinfo") output_browserinfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "protocolinfo") output_protocolinfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "collectioninfo") output_collectioninfo (args, disp, outconvert, textout, logout);
+  else if (arg_p == "usagelog") output_usagelog (args, disp, outconvert, textout, logout);
+  else if (arg_p == "errorlog") output_errorlog (args, disp, outconvert, textout, logout);
+  else if (arg_p == "gsdlsite") output_gsdlsite (args, disp, outconvert, textout, logout);
+  else if (arg_p == "maincfg") output_maincfg (args, disp, outconvert, textout, logout);
+  else if (arg_p == "changemaincfg") change_maincfg (args, disp, outconvert, textout, logout);
+  else {
+    output_errorpage (outconvert, textout, logout, 
+		      "Unknown page \"" + arg_p + "\".\n");
+  }
+
+  return true;
+}
+
+void statusaction::configure (const text_t &key, const text_tarray &cfgline) {
+  if ((key == "status") && (cfgline.size() == 1) && 
+      (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    disabled = false;
+  } else {
+    // call the parent class to deal with the things which
+    // are not dealt with here
+    action::configure (key, cfgline);
+  }
+}
+
+#endif //GSDL_USE_STATUS_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/statusaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/statusaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/statusaction.h	(revision 16561)
@@ -0,0 +1,132 @@
+/**********************************************************************
+ *
+ * statusaction.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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_STATUS_ACTION
+
+#ifndef STATUSACTION_H
+#define STATUSACTION_H
+
+
+#include "gsdlconf.h"
+#include "action.h"
+#include "receptionist.h"
+
+
+class statusaction : public action {
+protected:
+  bool disabled;
+  receptionist *recpt;
+
+  void output_frameset (cgiargsclass &args, displayclass &disp, 
+			outconvertclass &outconvert, 
+			ostream &textout, ostream &logout);
+
+  void output_select (cgiargsclass &args, displayclass &disp, 
+		      outconvertclass &outconvert, 
+		      ostream &textout, ostream &logout);
+
+  void output_welcome (cgiargsclass &args, recptprotolistclass *protos,
+		       displayclass &disp, outconvertclass &outconvert, 
+		       ostream &textout, ostream &logout);
+
+  void output_generalinfo (cgiargsclass &args, displayclass &disp, 
+			   outconvertclass &outconvert, 
+			   ostream &textout, ostream &logout);
+
+  void output_argumentinfo (cgiargsclass &args, displayclass &disp, 
+			    outconvertclass &outconvert, 
+			    ostream &textout, ostream &logout);
+
+  void output_actioninfo (cgiargsclass &args, displayclass &disp, 
+			  outconvertclass &outconvert, 
+			  ostream &textout, ostream &logout);
+
+  void output_browserinfo (cgiargsclass &args, displayclass &disp, 
+			   outconvertclass &outconvert, 
+			   ostream &textout, ostream &logout);
+
+  void output_protocolinfo (cgiargsclass &args, displayclass &disp, 
+			    outconvertclass &outconvert, 
+			    ostream &textout, ostream &logout);
+
+  void output_collectioninfo (cgiargsclass &args, displayclass &disp, 
+			      outconvertclass &outconvert, 
+			      ostream &textout, ostream &logout);
+
+  void output_usagelog (cgiargsclass &args, displayclass &disp,
+			outconvertclass &outconvert, 
+			ostream &textout, ostream &logout);
+
+  void output_errorlog (cgiargsclass &args, displayclass &disp, 
+			outconvertclass &outconvert, 
+			ostream &textout, ostream &logout);
+
+  void output_gsdlsite (cgiargsclass &args, displayclass &disp, 
+			outconvertclass &outconvert, 
+			ostream &textout, ostream &logout);
+
+  void output_maincfg (cgiargsclass &args, displayclass &disp, 
+		       outconvertclass &outconvert, 
+		       ostream &textout, ostream &logout);
+
+  void change_maincfg (cgiargsclass &args, displayclass &disp, 
+		       outconvertclass &outconvert, 
+		       ostream &textout, ostream &logout);
+
+  void output_errorpage (outconvertclass &outconvert, 
+			 ostream &textout, ostream &logout,
+			 text_t message);
+
+public:
+  statusaction ();
+  virtual ~statusaction ();
+
+  text_t get_action_name () {return "status";}
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+
+
+  void define_internal_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+};
+
+
+#endif //!STATUSACTION_H
+#endif //GSDL_USE_STATUS_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/string_pool.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/string_pool.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/string_pool.cpp	(revision 16561)
@@ -0,0 +1,44 @@
+/**********************************************************************
+ *
+ * string_pool.cpp -- 
+ * Copyright (C) 2003 DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "string_pool.h"
+
+string_pool::string_pool () {
+}
+
+string_pool::~string_pool () {
+  vector<char *>::const_iterator here = pool.begin();
+  vector<char *>::const_iterator end = pool.end();
+  while (here != end) {
+    delete(*here);
+    ++here;
+  }
+}
+
+char *string_pool::get_cstr_from_pool(const text_t &str) {
+  char *cstr = str.getcstr();
+  pool.push_back(cstr);
+  return cstr;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/string_pool.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/string_pool.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/string_pool.h	(revision 16561)
@@ -0,0 +1,45 @@
+/**********************************************************************
+ *
+ * string_pool.h -- 
+ * Copyright (C) 2003 DL Consulting Ltd
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef STRING_POOL_H
+#define STRING_POOL_H
+
+#include "text_t.h"
+
+class string_pool {
+protected:
+  
+  vector<char *> pool;
+
+public:
+  string_pool();
+  virtual ~string_pool();
+
+  char* get_cstr_from_pool (const text_t &str);
+
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/summarise.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/summarise.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/summarise.cpp	(revision 16561)
@@ -0,0 +1,270 @@
+/**********************************************************************
+ *
+ * summarise.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.
+ *
+ *********************************************************************/
+
+/* The function 'summarise' produces, given a document text and a query,
+ * a (query-biased) summary. In the future, several types of summaries will
+ * be supported.
+ */
+
+#include "summarise.h"
+#include "unitool.h"
+#include <string.h>
+
+
+/* **************** LOCAL PROTOTYPES **************** */
+
+text_t summarise_startend(text_t &htmlstr, int summaryLength);
+text_t summarise_keywords(text_t &htmlstr, text_t &query, int summaryLength);
+
+text_t next_sentence(text_t::iterator& start, text_t::iterator& end);
+text_t previous_sentence(text_t::iterator& start, text_t::iterator& end);
+bool paragraph_tag(text_t::iterator start);
+
+
+/****************************************************
+ NAME: summarise
+ DESC: produce a summary for a document
+*****************************************************/
+
+text_t summarise(text_t &htmlstr, text_t &query, int summaryLength) {
+  //  return summarise_startend(htmlstr,summaryLength);
+  return summarise_keywords(htmlstr,query,summaryLength);
+}
+
+
+/****************************************************
+ NAME: summarise_startend
+ DESC: return first and last sentences of a document
+*****************************************************/
+
+text_t summarise_startend(text_t &htmlstr, int summaryLength) {
+  text_t::iterator str_start = htmlstr.begin(), str_end = htmlstr.end();
+  text_t answer;
+
+  // add first sentences up to half the summary length
+
+  text_t::iterator str_current = str_start;
+  while(str_current<str_end && answer.size()<(summaryLength/2)) {
+    text_t sentence = next_sentence(str_current,str_end);
+    answer.append(sentence);
+  }
+
+  summaryLength -= answer.size(); // summary length left for last sentences
+  if(summaryLength<0)
+    summaryLength = 0;
+
+  str_end = str_current;
+  str_current = htmlstr.end()-1;
+  text_t lastSentence;
+  while(str_current>str_end && 
+        lastSentence.size()<summaryLength) {
+    text_t sentence = previous_sentence(str_current,str_end);
+    lastSentence = sentence + lastSentence;
+  }
+
+  answer += " ... ";
+  answer += lastSentence;
+
+  return answer;
+}
+
+
+/****************************************************
+ NAME: summarise_keywords
+ DESC: build a summary with sentences containing keyword(s).
+       the sentences with most matching keywords are returned 
+       first.
+*****************************************************/
+
+text_t summarise_keywords(text_t &htmlstr, text_t &query, int summaryLength) {
+
+  text_tarray allterms, terms;
+  splitchar(query.begin(),query.end(),' ',allterms);
+
+  // consider only non-empty terms
+  for(text_tarray::iterator term = allterms.begin();
+      term < allterms.end(); ++term) {
+    if(!(*term).empty())
+      terms.push_back(*term);
+  }
+
+  text_tarray::iterator terms_start = terms.begin(), terms_end = terms.end();
+
+  //text_tarray::iterator terms_current = terms_start;
+  //strstr("merde",*terms_current);
+
+  text_t::iterator str_start = htmlstr.begin(), str_end = htmlstr.end();
+
+  vector<text_tarray> answers(terms.size()); 
+    // an array of array of sentences for the summary:
+    //   answers[0] contains sentences with 1 keyword
+    //   answers[1] contains sentences with 2 keywords, etc.
+  vector<int> answersSize(terms.size());
+    // answersSize[0] is the combined size of sentences with 1 keyword, etc.
+  for(vector<int>::iterator size = answersSize.begin();
+      size<answersSize.end(); ++size)
+    *size = 0; // initialise sentence size
+
+  int totfound  = 0;
+  text_t::iterator str_current = str_start;
+  while(str_current<str_end && answersSize[terms.size()-1]<summaryLength) {
+    // if the size of best sentences is greater than summary, that's enough!
+    text_t sentence = next_sentence(str_current,str_end);
+
+    text_tarray::iterator terms_current = terms_start;
+    int nFound = 0;
+    while(terms_current!=terms_end) {
+      text_t::iterator word = findword(sentence.begin(),sentence.end(),
+				       *terms_current);
+      if(word!=sentence.end())
+	{        ++nFound; ++totfound; }
+      ++terms_current;
+    }
+
+    if(nFound>0 && answersSize[nFound-1]<summaryLength) {
+      answers[nFound-1].push_back(sentence);
+      answersSize[nFound-1] += sentence.size();
+    }
+  }
+
+  text_t answer;
+  for(vector<text_tarray>::iterator sentarray = answers.end()-1;
+      sentarray>=answers.begin(); --sentarray)
+    for(text_tarray::iterator sentence = (*sentarray).begin(); 
+        sentence < (*sentarray).end(); ++sentence) {
+      answer.append(*sentence);
+      if(answer.size()>=summaryLength)
+        return answer;
+    }
+
+  if(!answer.empty())
+    return answer;
+
+  return summarise_startend(htmlstr,summaryLength);
+}
+
+
+/* *********************** LOCAL FUNCTIONS ******************* */
+
+/* NAME: next_sentence
+   DESC: returns next sentence, text-only (ie. HTML markup is removed)
+ */
+
+text_t next_sentence(text_t::iterator& start, text_t::iterator& end) {
+  text_t sentence;                 // the sentence to be returned
+  bool   foundPunctuation = false; // set to true by '.', '!' or '?'
+  while(start<end && !foundPunctuation) {
+    switch (*start) {
+    case '<': // skip over rest of html tag
+      if(paragraph_tag(start) && has_unicode_letdig(sentence))
+        foundPunctuation = true;
+      while ((start<end) && (*start!='>'))
+	++start;
+      if(start<end) ++start;
+      break;
+    case '.':
+    case '!':
+    case '?':
+      sentence.push_back(*start);
+      ++start;
+      if(start>=end || 
+         (is_unicode_space(*start) && (*(start-2)<'A' || *(start-2)>'Z'))) {
+        foundPunctuation = true;
+      }
+      break;
+    default:
+      sentence.push_back(*start);
+      ++start;
+      break;
+    }
+  }
+  return sentence;
+}
+
+
+/* NAME: previous_sentence
+   DESC: returns previous sentence, text-only (ie. HTML markup is removed)
+ */
+
+text_t previous_sentence(text_t::iterator& start, text_t::iterator& end) {
+  text_t sentence;                    // the sentence to be returned
+  bool   found1stPunctuation = false, // set to true by '.', '!' or '?'
+                                      // first punct. is included in results
+         found2ndPunctuation = false; // second punct. is stop condition,
+                                      // and is not included in results
+  while(start>end && !found2ndPunctuation) {
+    switch (*start) {
+    case '>': // skip over rest of html tag
+      while ((start>end) && (*start!='<')) // backtrack to beginning of tag
+	--start;
+      if(start>end) {
+        if(paragraph_tag(start) && has_unicode_letdig(sentence))
+          found2ndPunctuation = true;
+        --start;
+      }
+      break;
+    case '.':
+    case '!':
+    case '?':
+      if(!is_unicode_space(*(start+1)) ||
+         (start-1>end && *(start-1)>='A' && *(start-1)<='Z')) {
+        // if next character is not a blank, or preceding character is
+	// a capital letter, we guess it's an acronym (e.g. "U.S.A.")
+        sentence.text_as_usvector().insert(sentence.text_as_usvector().begin(),
+          start,start+1);
+        --start;
+      } else
+        if(has_unicode_letdig(sentence) || found1stPunctuation)
+          found2ndPunctuation = true;
+        else {
+          sentence.text_as_usvector().insert(
+              sentence.text_as_usvector().begin(),start,start+1);
+          --start;
+          found1stPunctuation = true;
+        }
+      break;
+    default:
+      sentence.text_as_usvector().insert(
+        sentence.text_as_usvector().begin(),start,start+1);
+      --start;
+      break;
+    }
+  }
+  return sentence;
+}
+
+
+// start is positioned on the '<'
+bool paragraph_tag(text_t::iterator start) {
+  if(*start=='<') {
+    ++start;
+    if(*start=='p' || *start=='P') {
+      ++start;
+      if(is_unicode_space(*start) || *start=='>')
+        return true;
+    }
+  }
+  return false;
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/summarise.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/summarise.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/summarise.h	(revision 16561)
@@ -0,0 +1,34 @@
+/**********************************************************************
+ *
+ * summarise.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.
+ *
+ *********************************************************************/
+
+
+#ifndef SUMMARISE_H
+#define SUMMARISE_H
+
+#include "text_t.h"
+
+text_t summarise(text_t &htmlstr, text_t &query, int summaryLength);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/tipaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/tipaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/tipaction.cpp	(revision 16561)
@@ -0,0 +1,73 @@
+/**********************************************************************
+ *
+ * tipaction.cpp -- define random tip macros
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_TIP_ACTION
+
+#include "tipaction.h"
+#include <stdlib.h>
+
+
+tipaction::tipaction () {
+  // "st" show tips - "0"=no, "1"=yes
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "st";
+  arg_ainfo.longname = "show tips";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "1";
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+void tipaction::define_external_macros (displayclass &disp, cgiargsclass &args, 
+					recptprotolistclass * /*protos*/, ostream &logout) {
+
+  // define_external_macros sets the following macros:
+
+  // _tip:thistip_  - a random tip (defined if st == "1")
+
+  if (args["st"].getint()) {
+    // get the maximum tip number (_tip:tipmax_)
+    text_t tipmax_str;
+    disp.expandstring ("tip", "_tipmax_", tipmax_str);
+    if (tipmax_str.empty()) {
+      // don't bother filling up the logs if tip.dm isn't loaded!
+      // logout << "Warning: _tip:tipmax_ not set. This macro is needed for displaying tips.\n";
+      return;
+    }
+    
+    int tipmax = tipmax_str.getint();
+    if (tipmax < 1) {
+      // don't bother filling up the logs if tip.dm isn't loaded!
+      // logout << "Warning: _tip:tipmax_ was less than 1. No tips were set.\n";
+      return;
+    }
+    
+    disp.setmacro("thistip", "tip", "_tip" + text_t((rand()%tipmax) + 1) + "_");
+  }
+}
+
+#endif //GSDL_USE_TIP_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/tipaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/tipaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/tipaction.h	(revision 16561)
@@ -0,0 +1,49 @@
+/**********************************************************************
+ *
+ * tipaction.h -- define tip macros
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_TIP_ACTION
+
+#ifndef TIPACTION_H
+#define TIPACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+
+
+// tipaction exists just for the benefit of defining external macros
+class tipaction : public action {
+public:
+  tipaction ();
+  virtual ~tipaction () {}
+  text_t get_action_name () {return "tip";}
+  
+  void define_external_macros (displayclass &disp, cgiargsclass &args, 
+			       recptprotolistclass *protos, ostream &logout);
+};
+
+
+#endif //!TIPACTION_H
+#endif //GSDL_USE_TIP_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/userdb.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/userdb.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/userdb.cpp	(revision 16561)
@@ -0,0 +1,525 @@
+/**********************************************************************
+ *
+ * userdb.cpp -- functions to handle a user database
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdlconf.h"
+#include "userdb.h"
+#include "gdbmclass.h"
+#include "gsdltimes.h"
+#include "fileutil.h"
+#include <stdlib.h>
+
+// include crypt
+#if defined(__WIN32__)
+#include "crypt.h"
+#else
+#if defined(HAVE_CRYPT_H)
+#include <crypt.h>
+#else 
+#include <unistd.h>
+#endif
+#endif
+
+//==========================================//
+//      userinfo_t functions (Start)        //
+//==========================================//
+userinfo_t::userinfo_t()
+{
+  clear();
+}
+
+userinfo_t::~userinfo_t(){};
+
+void userinfo_t::clear () {
+  username.clear();
+  password.clear();
+  enabled = false;
+  groups.clear();
+  comment.clear();
+}
+
+userinfo_t &userinfo_t::operator=(const userinfo_t &x) {
+  username = x.username;
+  password = x.password;
+  enabled = x.enabled;
+  groups = x.groups;
+  comment = x.comment;
+  
+  return *this;
+}
+//==========================================//
+//       userinfo_t functions (END)         //
+//==========================================//
+
+//==========================================//
+//      userdbclass functions (Start)       //
+//==========================================//
+userdbclass::userdbclass(const text_t &userdbfilename)
+{
+  storeduserdbfilename = userdbfilename;
+  userdb = new gdbmclass();
+  activated = (!userdb->opendatabase(storeduserdbfilename, DB_READER, 1000, true)) ? false : true;
+  if (activated == false) 
+    {
+      activated = (!userdb->opendatabase(storeduserdbfilename, DB_WRITER_CREATE, 1000, true)) ? false : true;
+      if (activated == true)
+        {
+          userdb->closedatabase();
+          activated = (!userdb->opendatabase(storeduserdbfilename, DB_READER, 1000, true)) ? false : true;
+        }
+    }
+    
+  external_db = false;
+}
+
+userdbclass::~userdbclass()
+{
+  if (external_db == false)
+  {
+    userdb->closedatabase();
+    delete userdb;
+  }
+}
+
+// few useful functions
+text_t userdbclass::crypt_text (const text_t &text) 
+{
+  static const char *salt = "Tp";
+  text_t crypt_password;
+  
+  if (text.empty()) return g_EmptyText;
+  
+  // encrypt the password
+  char *text_cstr = text.getcstr();
+  if (text_cstr == NULL) return g_EmptyText;
+  crypt_password = crypt(text_cstr, salt);
+  delete []text_cstr;
+
+  return crypt_password;
+}
+
+// username_ok tests to make sure a username is ok. a username
+// must be at least 2 characters long, but no longer than 30
+// characters long. it can contain the characters a-z A-Z 0-9
+// . and _
+bool userdbclass::username_ok (const text_t &username) 
+{
+  if (username.size() < 2 || username.size() > 30) return false;
+
+  text_t::const_iterator here = username.begin();
+  text_t::const_iterator end = username.end();
+  while (here != end) 
+    {
+      if ((*here >= 'a' && *here <= 'z') ||
+          (*here >= 'A' && *here <= 'Z') ||
+          (*here >= '0' && *here <= '9') ||
+          *here == '.' ||
+          *here == '_') 
+        {
+          // ok
+        } else return false;
+      ++here;
+    }
+  
+  return true;
+}
+
+// password_ok tests to make sure a password is ok. a password
+// must be at least 3 characters long but no longer than 8 characters
+// long. it can contain any character in the range 0x20-0x7e
+bool userdbclass::password_ok (const text_t &password) 
+{
+  // DB: 20/02/08. Why mustn't the password exceed 8 chars, 
+  // windows 3.1 complicance?  Even then this doesn't make sense to me
+  // as storing this in GDBM wouldn't trigger a 8-char limit
+
+  // Have increased this to 128.  Not because I think someone will type
+  // in something that long, but some encryptions schemes (e.g. wireless
+  // networks) work with such lengths and someone one day might like
+  // to use such a generated password in Greenstone.
+  // 
+
+  if (password.size() < 3 || password.size() > 128) return false;
+
+  text_t::const_iterator here = password.begin();
+  text_t::const_iterator end = password.end();
+  while (here != end) {
+    if (*here >= 0x20 && *here <= 0x7e) 
+      {
+        // ok
+      } else return false;
+    ++here;
+  }
+
+  return true;
+}
+
+// removes spaces from user groups
+text_t userdbclass::format_user_groups(const text_t user_groups)
+{
+  text_t new_groups = g_EmptyText;
+  text_t::const_iterator here = user_groups.begin();
+  text_t::const_iterator end = user_groups.end();
+  while (here != end) {
+    if (*here != ' '&& *here != '\t' && *here != '\n') 
+      {
+        new_groups.push_back(*here);
+      }
+    ++here;
+  }
+  return new_groups;
+}
+
+// functions dealing with user databases
+// returns true on success (in which case userinfo will contain
+// the information for this user)
+// @return 0 success
+// @return -1 database is not currently connected
+// @return -2 not defined username
+int userdbclass::get_user_info (const text_t &username, userinfo_t &userinfo) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      userinfo.clear();
+      infodbclass info;
+      // See if we can get the user's infomration
+      if (userdb->getinfo (username, info)) 
+        {
+          userinfo.username = info["username"];
+          userinfo.password = info["password"];
+          userinfo.enabled = (info["enabled"] == "true");
+          userinfo.groups = info["groups"];
+          userinfo.comment = info["comment"];
+          return ERRNO_SUCCEED;
+        }
+      // If we failed to retrieve the users information, we should check if the username is admin or not.
+      // If it is the admin user, let's create a new account for the admin user.
+      else if (username == "admin") 
+        {
+          userinfo.clear();
+          userinfo.username = "admin";
+          userinfo.password = crypt_text("admin");
+          userinfo.enabled = true;
+          userinfo.groups = "administrator,all-collections-editor";
+          userinfo.comment = "change the password for this account as soon as possible";
+          // Return the set result.
+          return set_user_info (username, userinfo);
+        }
+      // The username is not found, return false
+      return ERRNO_USERNOTFOUND;
+    }
+  // Failed to connect to the database, return false.
+  return ERRNO_CONNECTIONFAILED;
+}
+
+// returns true on success
+int userdbclass::set_user_info (const text_t &username, const userinfo_t &userinfo) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      infodbclass info;
+      info["username"] = userinfo.username;
+      info["password"] = userinfo.password;
+      info["enabled"] = userinfo.enabled ? "true" : "false";
+      info["groups"] = userinfo.groups;
+      info["comment"] = userinfo.comment;
+      userdb->closedatabase();
+      userdb->opendatabase(storeduserdbfilename, DB_WRITER_CREATE, 1000, true);
+      int result = (userdb->setinfo (username, info)) ? ERRNO_SUCCEED : ERRNO_DBACTIONFAILED;
+      userdb->closedatabase();
+      userdb->opendatabase(storeduserdbfilename, DB_READER, 1000, true);         
+      return  result;
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+
+// returns true if the user's password is correct.
+int userdbclass::check_passwd (const text_t &username, const text_t &password) 
+{
+  userinfo_t thisuser;
+  int returned = get_user_info(username, thisuser);
+  if(returned != ERRNO_SUCCEED) return returned;
+  // couple of basic checks
+  if (thisuser.username.empty() || thisuser.password.empty() ||
+      password.empty()) return ERRNO_MISSINGPASSWORD;
+
+  text_t crypt_password = crypt_text(password);
+  return (thisuser.password == crypt_password) ? ERRNO_SUCCEED : ERRNO_PASSWORDMISMATCH;
+}
+
+int userdbclass::add_user (const userinfo_t &userinfo) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      infodbclass info;
+      if (userdb->getinfo (userinfo.username, info)) 
+        {
+          // There is an existing username already
+          return ERRNO_EXISTINGUSERNAME;
+        }
+      else
+        {
+          return set_user_info(userinfo.username, userinfo);
+        }      
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+
+int userdbclass::edit_user (const userinfo_t &userinfo) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      infodbclass info;
+      if (userdb->getinfo (userinfo.username, info)) 
+        {
+          return set_user_info(userinfo.username, userinfo);
+        }
+      else
+        {
+          // The user does not exist in the database.
+          return ERRNO_USERNOTFOUND;
+        }      
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+
+int userdbclass::delete_user (const text_t &username) 
+{  
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      userdb->closedatabase();
+      userdb->opendatabase(storeduserdbfilename, DB_WRITER_CREATE, 1000, true);
+      userdb->deletekey (username);
+      userdb->closedatabase();
+      userdb->opendatabase(storeduserdbfilename, DB_READER, 1000, true);
+      return ERRNO_SUCCEED;
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+
+// gets all the users' information in the database. returns true
+// on success
+int userdbclass::get_all_users(userinfo_tarray &userinfo_array) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      userinfo_array.erase(userinfo_array.begin(), userinfo_array.end());
+      text_tarray userlist = userdb->getkeys();
+      text_tarray::iterator user_iterator = userlist.begin();
+      while (user_iterator != userlist.end())
+      {
+        userinfo_t one_userinfo;
+        int returned = get_user_info(*user_iterator, one_userinfo);
+        if (returned != ERRNO_SUCCEED) return returned;
+        userinfo_array.push_back(one_userinfo);
+	user_iterator++;
+      }
+      return ERRNO_SUCCEED;
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+
+// gets a list of all the users in the database. returns true
+// on success
+int userdbclass::get_user_list (text_tarray &userlist) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      userlist.erase (userlist.begin(), userlist.end());
+      userlist = userdb->getkeys();
+      return ERRNO_SUCCEED;
+    }
+  return ERRNO_CONNECTIONFAILED;
+}
+//==========================================//
+//       userdbclass functions (End)        //
+//==========================================//
+
+//==========================================//
+//      keydbclass functions (Start)        //
+//==========================================//
+keydbclass::keydbclass(const text_t &keydbfilename)
+{
+  storedkeydbfilename = keydbfilename;
+  keydb = new gdbmclass();
+  activated = keydb->opendatabase(storedkeydbfilename, DB_READER, 1000, true);
+  if (activated == false) 
+    {
+      activated = keydb->opendatabase(storedkeydbfilename, DB_WRITER_CREATE, 1000, true);
+      if (activated == true)
+        {
+          keydb->closedatabase();
+          activated = keydb->opendatabase(storedkeydbfilename, DB_READER, 1000, true);
+        }
+    }
+  external_db = false;
+}
+
+keydbclass::~keydbclass()
+{
+  if (external_db == false)
+  {
+    keydb->closedatabase();
+    delete keydb;
+  }
+}
+
+// generates a random key for the user, stores it in the database and
+// returns it so that it can be used in page generation
+// returns "" on failure
+text_t keydbclass::generate_key (const text_t &username) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      static const char *numconvert = "0123456789abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+      
+      // loop looking for a suitable new key
+      text_t userkey;
+      text_t crypt_userkey;
+      do {
+        // convert to base 62 :-)
+        int userkey_int = rand ();
+        while (userkey_int > 0) 
+          {
+            userkey.push_back (numconvert[userkey_int%62]);
+            userkey_int /= 62;
+          }
+        
+        // make sure this key is not in the database
+        crypt_userkey = userdbclass::crypt_text(userkey);
+        if (keydb->exists (crypt_userkey)) userkey.clear();
+      } while (userkey.empty());
+      
+      // enter the key into the database
+      infodbclass keydata;
+      keydata["user"] = username;
+      keydata["time"] = time2text(time(NULL));
+      
+      keydb->closedatabase();
+      keydb->opendatabase(storedkeydbfilename, DB_WRITER_CREATE, 1000, true);
+      if (!keydb->setinfo (crypt_userkey, keydata)) 
+        {
+          userkey.clear(); // failed
+        }
+      keydb->closedatabase();
+      keydb->opendatabase(storedkeydbfilename, DB_READER, 1000, true); 
+      
+      return userkey;
+    }
+  return "";
+}
+
+// checks to see if there is a key for this particular user in the
+// database that hasn't decayed. a short decay is used when group
+// is set to administrator
+bool keydbclass::check_key (const userinfo_t &thisuser, const text_t &key, const text_t &group, int keydecay) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {
+      if (thisuser.username.empty() || key.empty()) return false;
+      
+      // the keydecay is set to 5 minute for things requiring the
+      // administrator
+      //  if (group == "administrator") keydecay = 300;
+      
+      // success if there is a key in the key database that is owned by this
+      // user whose creation time is less that keydecay
+      text_t crypt_key = userdbclass::crypt_text(key);
+      infodbclass info;
+      if (keydb->getinfo (crypt_key, info))  {
+        if (info["user"] == thisuser.username) {
+          time_t keycreation = text2time (info["time"]);
+          if (keycreation != (time_t)-1 && difftime (text2time(time2text(time(NULL))),
+                                                     keycreation) <= keydecay) {
+            // succeeded, update the key's time
+            info["time"] = time2text(time(NULL));
+            keydb->closedatabase();
+            keydb->opendatabase(storedkeydbfilename, DB_WRITER_CREATE, 1000, true);
+            keydb->setinfo (crypt_key, info);
+            keydb->closedatabase();
+            keydb->opendatabase(storedkeydbfilename, DB_READER, 1000, true); 
+            return true;
+          }
+        }
+      }
+    }
+  return false;
+}
+
+// remove_old_keys will remove all keys created more than keydecay ago.
+// use sparingly, it can be quite an expensive function
+void keydbclass::remove_old_keys (int keydecay) 
+{
+  // Let's make sure the connection has been established.
+  if (activated == true)
+    {  
+      // get a list of keys created more than keydecay seconds agon
+      text_tarray oldkeys;
+      infodbclass info;
+      time_t timenow = text2time(time2text(time(NULL)));
+      time_t keycreation = (time_t)-1;
+
+      text_tarray keylist = keydb->getkeys();
+      text_tarray::iterator key_iterator = keylist.begin();
+      while (key_iterator != keylist.end())
+      {
+        if (keydb->getinfo (*key_iterator, info))  {
+          keycreation = text2time (info["time"]);
+          if (keycreation != (time_t)-1 && difftime (timenow, keycreation) > keydecay) {
+            // found an old key
+            oldkeys.push_back(*key_iterator);
+          }
+        }
+        
+	key_iterator++;
+      }
+      
+      // delete the old keys
+      if (oldkeys.size() > 0) {
+	keydb->closedatabase();
+	keydb->opendatabase(storedkeydbfilename, DB_WRITER_CREATE, 1000, true);
+	text_tarray::iterator keys_here = oldkeys.begin();
+	text_tarray::iterator keys_end = oldkeys.end();
+	while (keys_here != keys_end) {
+	  keydb->deletekey(*keys_here);
+	  ++keys_here;
+	}
+	keydb->closedatabase();
+	keydb->opendatabase(storedkeydbfilename, DB_READER, 1000, true);
+      }
+    }
+}
+//==========================================//
+//       keydbclass functions (End)         //
+//==========================================//
Index: /gsdl/trunk/gs2runtime/src/recpt/userdb.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/userdb.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/userdb.h	(revision 16561)
@@ -0,0 +1,147 @@
+/**********************************************************************
+ *
+ * userdb.h -- functions to handle a user database
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+
+#ifndef USERDB_H
+#define USERDB_H
+
+#define ERRNO_SUCCEED           0 
+#define ERRNO_CONNECTIONFAILED -1
+#define ERRNO_USERNOTFOUND     -2
+#define ERRNO_EXISTINGUSERNAME -3
+#define ERRNO_MISSINGPASSWORD  -4
+#define ERRNO_PASSWORDMISMATCH -5
+#define ERRNO_DBACTIONFAILED   -6
+
+
+#include "dbclass.h"
+
+// The user information object and it provides basic functions for the information object, such as clear and copy (=) functions;
+class userinfo_t
+{
+ public:
+  text_t username;
+  text_t password;
+  bool enabled;
+  text_t groups; // comma separated list
+  text_t comment;
+
+  userinfo_t ();
+  virtual ~userinfo_t();
+  userinfo_t &operator=(const userinfo_t &x);
+  void clear ();
+};
+
+typedef vector<userinfo_t> userinfo_tarray; // more space efficient than text_tlist
+
+class userdbclass
+{
+ protected:
+  // The internal storage for the userdbfilename and the userdb object
+  dbclass* userdb;
+  bool external_db;
+  bool activated;
+  text_t storeduserdbfilename; 
+  
+ public:
+  userdbclass (const text_t &userdbfilename);
+  virtual ~userdbclass();
+
+  //======== the static methods========//
+  // the password line in the userinfo_t must be encrypted using
+  // this function
+  static text_t crypt_text (const text_t &text);
+  // username_ok tests to make sure a username is ok. a username
+  // must be at least 2 characters long, but no longer than 30
+  // characters long. it can contain the characters a-z A-Z 0-9
+  // . and _
+  static bool username_ok (const text_t &username);
+  // password_ok tests to make sure a password is ok. a password
+  // must be at least 3 characters long but no longer than 8 characters
+  // long. it can contain any character in the range 0x20-0x7e
+  static bool password_ok (const text_t &password);
+  // removes spaces from user groups
+  static text_t format_user_groups(const text_t user_groups);
+  
+  //======== functions dealing with single user in the user databases ========//
+  // returns true on success (in which case userinfo will contain
+  // the information for this user)
+  int get_user_info (const text_t &username, userinfo_t &userinfo);
+  
+  // returns true on success
+  int set_user_info (const text_t &username, const userinfo_t &userinfo);
+  
+  // returns true if the user's password is correct.
+  int check_passwd (const text_t &username, const text_t &password);
+  
+  // adds a user from the user database -- forever
+  int add_user (const userinfo_t &userinfo);
+  
+  // edits a user from the user database -- forever
+  int edit_user (const userinfo_t &userinfo);
+  
+  // removes a user from the user database -- forever
+  int delete_user (const text_t &username);
+  
+  //======== functions dealing with multiple users in the user databases ========//
+  // gets all the users' information in the database. returns true
+  // on success
+  int get_all_users(userinfo_tarray &userinfo_array);
+  
+  // gets a list of all the users in the database. returns true
+  // on success
+  int get_user_list (text_tarray &userlist);
+};
+
+class keydbclass
+{
+ protected:
+  // The internal storage for the userdbfilename and the userdb object
+  dbclass* keydb;
+  bool external_db;
+  bool activated;
+   text_t storedkeydbfilename; 
+  
+ public:
+  keydbclass (const text_t &keydbfilename);
+  virtual ~keydbclass();
+  // functions dealing with databases of temporary keys
+  
+  // generates a random key for the user, stores it in the database and
+  // returns it so that it can be used in page generation
+  // returns "" on failure
+  text_t generate_key (const text_t &username);
+  
+  // checks to see if there is a key for this particular user in the
+  // database that hasn't decayed. a short decay is used when group
+  // is set to administrator
+  bool check_key (const userinfo_t &thisuser, const text_t &key, const text_t &group, int keydecay);
+  
+  // remove_old_keys will remove all keys created more than keydecay ago.
+  // use sparingly, it can be quite an expensive function
+  void remove_old_keys (int keydecay);
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/usersaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/usersaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/usersaction.cpp	(revision 16561)
@@ -0,0 +1,412 @@
+/**********************************************************************
+ *
+ * usersaction.cpp -- managing users
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_USERS_ACTION
+
+#include "usersaction.h"
+#include "fileutil.h"
+
+
+///////////////
+// usersaction
+///////////////
+
+usersaction::usersaction () {
+  // this action uses cgi variable "a"
+  cgiarginfo arg_ainfo;
+  arg_ainfo.shortname = "a";
+  arg_ainfo.longname = "action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "um"; // user management
+  arg_ainfo.savedarginfo = cgiarginfo::must;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "uma"
+  arg_ainfo.shortname = "uma";
+  arg_ainfo.longname = "user management action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = "listusers";
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umun"
+  arg_ainfo.shortname = "umun";
+  arg_ainfo.longname = "user management user name";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umpw"
+  arg_ainfo.shortname = "umpw";
+  arg_ainfo.longname = "user management password";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umnpw1"
+  arg_ainfo.shortname = "umnpw1";
+  arg_ainfo.longname = "user management new password 1";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umnpw2"
+  arg_ainfo.shortname = "umnpw2";
+  arg_ainfo.longname = "user management new password 2";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umus"
+  arg_ainfo.shortname = "umus";
+  arg_ainfo.longname = "user management account status";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umug"
+  arg_ainfo.shortname = "umug";
+  arg_ainfo.longname = "user management groups"; // comma seperated list
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "umc"
+  arg_ainfo.shortname = "umc";
+  arg_ainfo.longname = "user management comment";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "bcp"
+  arg_ainfo.shortname = "bcp";
+  arg_ainfo.longname = "change password submit button";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "beu"
+  arg_ainfo.shortname = "beu";
+  arg_ainfo.longname = "edit user submit button";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+
+  // "cm"
+  arg_ainfo.shortname = "cm";
+  arg_ainfo.longname = "confirm an action";
+  arg_ainfo.multiplechar = true;
+  arg_ainfo.defaultstatus = cgiarginfo::weak;
+  arg_ainfo.argdefault = g_EmptyText;
+  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
+  argsinfo.addarginfo (NULL, arg_ainfo);
+}
+
+void usersaction::configure (const text_t &key, const text_tarray &cfgline) {
+  action::configure (key, cfgline);
+}
+
+bool usersaction::init (ostream &logout) {
+
+  if (dbhome.empty()) {
+    logout << "ERROR (usersaction::init) dbhome is not set\n";
+    return false;
+  }
+
+  return action::init (logout);
+}
+
+bool usersaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &args, 
+				 recptprotolistclass * /*protos*/, ostream &/*logout*/) {
+  args["uan"] = "1"; // user authentication is needed
+
+  if (args["uma"] == "changepasswd") {
+    // no particular group is needed to change a password
+    args["ug"].clear(); 
+  } else {
+    // administrator is needed for all other management tasks
+    args["ug"] = "administrator";
+  }
+  
+  return true;
+}
+
+void usersaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+				    response_t &response, text_t &response_data, 
+				    ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+bool usersaction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
+			     browsermapclass * /*browsers*/, displayclass &disp, 
+			     outconvertclass &outconvert, ostream &textout, 
+			     ostream &logout) {
+
+  if (args["uma"] == "adduser" || args["uma"] == "edituser") {
+    // adduser is handled by edituser
+    return do_edituser (args, disp, outconvert, textout, logout);
+    
+  } else if (args["uma"] == "deleteuser") {
+    return do_deleteuser (args, disp, outconvert, textout, logout);
+    
+  } else if (args["uma"] == "changepasswd") {
+    return do_changepasswd (args, disp, outconvert, textout, logout);  
+  }
+
+  // default
+  return do_listusers (args, disp, outconvert, textout, logout);
+}
+
+bool usersaction::do_listusers (cgiargsclass &/*args*/, displayclass &disp, 
+				outconvertclass &outconvert, ostream &textout, 
+				ostream &/*logout*/) {
+
+  textout << outconvert << disp
+	  << "_userslistusers:header_\n_userslistusers:contentstart_\n";
+
+  text_tarray userlist;
+  // get user list
+  user_database->get_user_list (userlist);
+
+  // sort the list
+  sort(userlist.begin(), userlist.end());
+  
+  // output the information for each user
+  userinfo_t userinfo;
+  text_tarray::iterator users_here = userlist.begin();
+  text_tarray::iterator users_end = userlist.end();
+  while (users_here != users_end) {
+    if (user_database->get_user_info(*users_here, userinfo) == ERRNO_SUCCEED) {
+      textout << outconvert << disp
+	      << "<tr><td bgcolor=\"\\#eeeeee\">" << userinfo.username << "</td>\n"
+       	      << "<td bgcolor=\"\\#eeeeee\">" << (char *) (userinfo.enabled ? "enabled" : "disabled") << "</td>\n"
+	      << "<td bgcolor=\"\\#eeeeee\">" << userinfo.groups << "&nbsp;</td>\n"
+	      << "<td bgcolor=\"\\#eeeeee\">" << userinfo.comment << "&nbsp;</td>\n"
+	      << "<td><a href=\"_httpcurrentdocument_&a=um&uma=edituser&umun="
+	      << userinfo.username << "\">_userslistusers:textedituser_</a> "
+	      << "<a href=\"_httpcurrentdocument_&a=um&uma=deleteuser&umun="
+	      << userinfo.username << "\">_userslistusers:textdeleteuser_</a>"
+	      << "</td></tr>\n\n";
+
+    } else {
+      textout << outconvert << disp
+	      << "<tr><td bgcolor=\"\\#eeeeee\">" << *users_here << "</td>\n"
+	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
+	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
+	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
+	      << "<td>&nbsp;</td></tr>\n\n";
+    }
+    
+    ++users_here;
+  }
+  
+  textout << outconvert << disp
+	  << "_userslistusers:contentend_\n_userslistusers:footer_\n";
+  return true;
+}
+
+void usersaction::define_user_macros (cgiargsclass &args, displayclass &disp) {
+  disp.setmacro ("usersargun", "users", args["umun"]);
+  disp.setmacro ("usersargpw", "users", args["umpw"]);
+  disp.setmacro ("usersargus", "users", args["umus"]);
+  disp.setmacro ("usersargug", "users", args["umug"]);
+  disp.setmacro ("usersargc", "users", args["umc"]);
+}
+
+
+bool usersaction::do_edituser (cgiargsclass &args, displayclass &disp, 
+			       outconvertclass &outconvert, ostream &textout, 
+			       ostream &logout) {
+
+  userinfo_t userinfo;
+  text_t messagestatus;
+  bool noproblems = true;
+  
+  // fill in defaults from user database (if appropriate)
+  if (args["umun"].empty()) {
+    noproblems = false;
+    
+  } else if (!userdbclass::username_ok (args["umun"])) {
+    // problem with username
+    noproblems = false;
+    messagestatus += "_users:messageinvalidusername_";
+    
+  } else if (user_database->get_user_info (args["umun"], userinfo) == ERRNO_SUCCEED) {
+    if (args["uma"] == "adduser") {
+      // must not add a user that has the same name as another user
+      noproblems = false;
+      messagestatus += "_users:messageuserexists_";
+      
+    } else {
+      // only fill in the data if there is no user status defined
+      if (args["umus"].empty()) {
+	args["umus"] = userinfo.enabled ? "enabled" : "disabled";
+	if (args["umug"].empty()) args["umug"] = userinfo.groups;
+	if (args["umc"].empty()) args["umc"] = userinfo.comment;
+      }
+    }
+  }
+
+  // fill in the user status default
+  if (args["umus"].empty()) {
+    noproblems = false;
+    args["umus"] = "enabled";
+  }
+  
+  // make sure the password is ok
+  if (args["umpw"].empty()) {
+    // password must not be empty if none were supplied from database
+    // and we have had no other problems
+    if (userinfo.password.empty() && noproblems) {
+      noproblems = false;
+      messagestatus += "_users:messageemptypassword_";
+    }
+    
+  } else if (!userdbclass::password_ok(args["umpw"])) {
+    noproblems = false;
+    messagestatus += "_users:messageinvalidpassword_";
+  }
+
+  // set this info if no problems have been encounted
+  // and the submit button was pressed
+  if (noproblems && !args["beu"].empty()) {
+    userinfo.username = args["umun"];
+    if (!args["umpw"].empty()) {
+      // only set the password if it is not empty
+      userinfo.password = userdbclass::crypt_text(args["umpw"]);
+    }
+    userinfo.enabled = (args["umus"] == "enabled");    
+    userinfo.groups = userdbclass::format_user_groups(args["umug"]);
+    userinfo.comment = args["umc"];
+    
+    user_database->set_user_info (args["umun"], userinfo);
+    
+    // show list of users
+    return do_listusers (args, disp, outconvert, textout, logout);
+  }
+  
+  // define the macros for the user
+  define_user_macros (args, disp);
+  disp.setmacro ("messagestatus", "users", messagestatus);
+  
+  textout << outconvert << disp
+	  << "_usersedituser:header_\n_usersedituser:content_\n_usersedituser:footer_\n";
+
+  return true;
+}
+
+bool usersaction::do_deleteuser (cgiargsclass &args, displayclass &disp, 
+				 outconvertclass &outconvert, ostream &textout, 
+				 ostream &logout) {
+  if (!args["cm"].empty()) {
+    if (args["cm"] == "yes" && !args["umun"].empty()) {
+      // user confirmed the deletion of the user
+      user_database->delete_user(args["umun"]);
+    }
+    
+    // redirect the user back to the listusers page
+    return do_listusers (args, disp, outconvert, textout, logout);
+  }
+
+  define_user_macros (args, disp);
+  textout << outconvert << disp
+	  << "_usersdeleteuser:header_\n_usersdeleteuser:content_\n_usersdeleteuser:footer_\n";
+  
+  return true;
+}
+
+bool usersaction::do_changepasswd (cgiargsclass &args, displayclass &disp, 
+				   outconvertclass &outconvert, ostream &textout, 
+				   ostream &/*logout*/) {
+  text_t messagestatus;
+  
+  if (!args["bcp"].empty()) {
+    if (args["un"].empty()) {
+      messagestatus = "_users:messageusernameempty_";
+    } else if (args["umpw"].empty()) {
+      messagestatus = "_users:messagepasswordempty_";
+    } else if (args["umnpw1"].empty()) {
+      messagestatus = "_users:messagenewpass1empty_";
+    } else if (args["umnpw2"].empty()) {
+      messagestatus = "_users:messagenewpass2empty_";
+    } else if (args["umnpw1"] != args["umnpw2"]) {
+      messagestatus = "_users:messagenewpassmismatch_";
+    } else if (!userdbclass::password_ok(args["umnpw1"])) {
+      messagestatus = "_users:messageinvalidpassword_";
+    } else {
+      userinfo_t userinfo;
+      if (user_database->get_user_info (args["un"], userinfo) == ERRNO_SUCCEED) {
+	// check old password
+	if (userinfo.password != userdbclass::crypt_text(args["umpw"])) {
+	  messagestatus = "_users:messagefailed_";
+	  
+	} else {
+	  userinfo.password = userdbclass::crypt_text(args["umnpw1"]);
+	  if (user_database->set_user_info (args["un"], userinfo) == ERRNO_SUCCEED) {
+	    // everything is ok
+	    textout << outconvert << disp
+		    << "_userschangepasswdok:header_\n"
+	      "_userschangepasswdok:content_\n"
+	      "_userschangepasswdok:footer_\n";
+	    return true;
+	  }
+	}
+      }
+    }
+  }
+
+  disp.setmacro ("messagestatus", "users", messagestatus);
+
+  textout << outconvert << disp
+	  << "_userschangepasswd:header_\n"
+    "_userschangepasswd:content_\n"
+    "_userschangepasswd:footer_\n";
+
+  return true;
+}
+
+#endif //GSDL_USE_USERS_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/usersaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/usersaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/usersaction.h	(revision 16561)
@@ -0,0 +1,87 @@
+/**********************************************************************
+ *
+ * usersaction.h -- managing users
+ * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
+ *
+ * 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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+#ifdef GSDL_USE_USERS_ACTION
+
+#ifndef USERSACTION_H
+#define USERSACTION_H
+
+#include "gsdlconf.h"
+#include "action.h"
+#include "userdb.h"
+#include "text_t.h"
+
+
+class usersaction : public action {
+protected:
+  userdbclass *user_database;
+
+public:
+  usersaction ();
+  virtual ~usersaction () {}
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  bool init (ostream &logout);
+
+  void set_userdb(userdbclass *udb) {user_database = udb;}
+  
+  text_t get_action_name () {return "um";}
+  
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass *protos, ostream &logout);
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response, text_t &response_data, 
+			 ostream &logout);
+  
+  bool do_action (cgiargsclass &args, recptprotolistclass *protos, 
+		  browsermapclass *browsers, displayclass &disp, 
+		  outconvertclass &outconvert, ostream &textout, 
+		  ostream &logout);
+
+protected:
+  void define_user_macros (cgiargsclass &args, displayclass &disp);
+  
+  bool do_listusers (cgiargsclass &args, displayclass &disp, 
+		     outconvertclass &outconvert, ostream &textout, 
+		     ostream &logout);
+
+  bool do_edituser (cgiargsclass &args, displayclass &disp, 
+		    outconvertclass &outconvert, ostream &textout, 
+		    ostream &logout);
+
+  bool do_deleteuser (cgiargsclass &args, displayclass &disp, 
+		      outconvertclass &outconvert, ostream &textout, 
+		      ostream &logout);
+
+  bool do_changepasswd (cgiargsclass &args, displayclass &disp, 
+			outconvertclass &outconvert, ostream &textout, 
+			ostream &logout);
+};
+
+#endif //!USERSACTION_H
+#endif //GSDL_USE_USERS_ACTION
Index: /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.cpp	(revision 16561)
@@ -0,0 +1,463 @@
+/**********************************************************************
+ *
+ * vlistbrowserclass.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.
+ *
+ *********************************************************************/
+
+#include "vlistbrowserclass.h"
+#include "OIDtools.h"
+#include "browsetoolsclass.h"
+#include "cgiutils.h"
+
+
+vlistbrowserclass::vlistbrowserclass () {
+}
+
+vlistbrowserclass::~vlistbrowserclass () {
+}
+
+// returns the name that specifies the browserclass type
+text_t vlistbrowserclass::get_browser_name () {
+  return "VList";
+}
+
+browserclass* vlistbrowserclass::clone()
+{
+  vlistbrowserclass* browser = new vlistbrowserclass();
+  if (browser != NULL) {
+#ifdef GSDL_USE_BY_BOOK_MACROS
+    browser->m_oByBookMacros.Copy(m_oByBookMacros);
+#endif
+  }
+  return browser;
+}
+
+void vlistbrowserclass::load_metadata_defaults (text_tset &metadata) {
+  metadata.insert ("haschildren");
+  metadata.insert ("doctype");
+}
+
+text_t vlistbrowserclass::get_default_formatstring () {
+  return "<td valign=\"top\">[link][icon][/link]</td>"
+    "<td valign=\"top\">[srclink]{Or}{[thumbicon],[srcicon]}[/srclink]</td>"
+    "<td valign=\"top\">[highlight]{Or}{[dls.Title],[dc.Title],[Title],Untitled}[/highlight]"
+    "{If}{[Source],<br><i>([Source])</i>}</td>";
+}
+
+bool vlistbrowserclass::show_search_result_links(recptproto * collectproto,const text_t &collection,ostream& logout){
+     ColInfoResponse_t cinfo;
+     comerror_t err;
+     collectproto->get_collectinfo (collection, cinfo, err, logout);
+     bool show_links = false;
+
+     text_tmap::const_iterator format_here = cinfo.format.begin();
+     text_tmap::const_iterator format_end = cinfo.format.end();
+     
+     while (format_here != format_end) {
+       if (((*format_here).first == "DocumentSearchResultLinks") && 
+	   ((*format_here).second == "true")){
+	 show_links = true;
+         break;
+       }
+       ++format_here;
+     } 
+     
+     return show_links;
+}
+
+int vlistbrowserclass::output_section_group(ResultDocInfo_t &section, cgiargsclass &args,
+                                            const text_t &collection, int colnumber, 
+                                            format_t *formatlistptr, bool use_table, 
+                                            text_tset &metadata, bool &getParents, 
+                                            recptproto * collectproto, displayclass &disp, 
+                                            outconvertclass &outconvert, ostream &textout, 
+                                            ostream& logout)
+{
+  browsetoolsclass btools;
+  text_t link, icon;
+  text_t collink = collection;
+  bool show_links = show_search_result_links(collectproto,collection,logout);
+
+
+#ifndef DOCHANDLE
+  if (!collink.empty()) collink = "&amp;c=" + collink;
+#endif
+
+  get_link_icon (section, args, collink, link, icon, show_links);
+
+  //get_link_icon (section, args, collink, link, icon);
+
+  bool highlight = false;
+  if (!args["d"].empty()) {
+    if (args["d"] == section.OID) highlight = true;
+  } else 
+    if (args["cl"] == section.OID) highlight = true;
+
+
+  /* use_table is set to true if the format string starts with <td> */
+  if (use_table || colnumber > 0) {
+    if (colnumber > 0 ){
+      textout << outconvert << "<table  class=\"v_list\"><tr valign=\"top\">";
+    }
+    else{
+      textout << outconvert << "<table id=\"toc_top\" class=\"v_list\"><tr valign=\"top\">";
+    }
+
+    // get tab size
+    text_t tab; int itab;
+    disp.expandstring (displayclass::defaultpackage, "_tabwidth_", tab);
+    itab = tab.getint();
+    if (colnumber > 0) textout << outconvert << disp 
+			       << "<td><img alt=\"\" src=\"_httpimg_/space.gif\" width=\"" 
+			       << (itab*colnumber) << "\"/></td>";
+  } else {
+    textout << outconvert << "<div class=\"v_list\">\n";
+  }
+
+  text_tmap options;
+  options["link"] = link;
+  options["icon"] = icon;
+  if (highlight) options["highlight"] = "1";
+  else options["highlight"] = "0";
+  options["DocImage"] = btools.get_cover_image();
+  textout << outconvert << disp 
+	  << get_formatted_string (collection, collectproto, 
+				   section, disp, formatlistptr, 
+				   options, logout);
+  
+  if (use_table  || colnumber > 0) {
+    textout << outconvert << "</tr></table>\n";
+  } else {
+    textout << outconvert << "</div>\n";
+  }
+
+  return 1;
+}
+
+int vlistbrowserclass::output_section_group(FilterResponse_t &sections, cgiargsclass &args,
+                                            const text_t &collection, int colnumber, 
+                                            format_t *formatlistptr, bool use_table, 
+                                            text_tset &/*metadata*/, bool &/*getParents*/, 
+                                            recptproto * collectproto, displayclass &disp, 
+                                            outconvertclass &outconvert, ostream &textout, 
+                                            ostream& logout)
+{
+  browsetoolsclass btools;
+  text_t link, icon;
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+  
+  text_t collink = collection;
+  bool show_links = show_search_result_links(collectproto,collection,logout);
+
+#ifndef DOCHANDLE
+  if (!collink.empty()) collink = "&amp;c=" + collink;
+#endif
+
+  if (colnumber > 0) {
+    textout << outconvert << "<table><tr valign=\"top\">";
+    // get tab size
+    text_t tab; int itab;
+    disp.expandstring (displayclass::defaultpackage, "_tabwidth_", tab);
+    itab = tab.getint();
+    if (colnumber > 0) textout << outconvert << disp 
+			       << "<td><img alt=\"\" src=\"_httpimg_/space.gif\" width=\"" 
+			       << (itab*colnumber) << "\"/></td>";
+    textout << outconvert << "<td>";
+  }
+
+  /* use table set to true if format string starts with <td> */
+  if (use_table) {
+    textout << outconvert << "<table class=\"v_list\">\n";
+  } else {
+    textout << outconvert << "<div class=\"v_list\">\n";
+  }
+
+  ResultDocInfo_tarray::iterator thissection = sections.docInfo.begin();
+  ResultDocInfo_tarray::iterator endsection = sections.docInfo.end();
+
+  // ** here lies Alan's hack to get 4 results per table row
+  int item = 0, perline; // set perline to 1 normally
+  text_t fstring;
+
+  text_tmap options;
+  options["link"] = link;
+  options["icon"] = icon;
+  options["highlight"] = "0";
+  options["DocImage"] = btools.get_cover_image();
+  
+  if ( thissection != endsection ) { // only if there are results (else corrupts display)
+    fstring = get_formatted_string (collection, collectproto, *thissection,
+                                    disp, formatlistptr, options, logout);
+    // if format string
+    // has <td><t (e.g. <td><table>) then
+    // we assume it is an image and put it in 4 columns
+    if (fstring[5] == 't') 
+      perline = 4;
+    else
+      perline = 1;
+  }
+
+  int count = 0; 
+
+  while (thissection != endsection) {
+    ++item;
+    get_link_icon (*thissection, args, collink, link, icon,show_links);
+    //get_link_icon (*thissection, args, collink, link, icon);
+    bool highlight = false;
+    if (!arg_d.empty()) {
+      if (arg_d == (*thissection).OID) highlight = true;
+    } else 
+      if (arg_cl == (*thissection).OID) highlight = true;
+
+    if ((item == 1 && use_table) || colnumber > 0 )
+      textout << outconvert << "<tr valign=top>\n";
+
+    options["link"] = link;
+    options["icon"] = icon;
+    if (highlight) options["highlight"] = "1";
+    else options["highlight"] = "0";
+    options["DocImage"] = btools.get_cover_image();
+    
+    textout << outconvert << disp 
+	    << " " << get_formatted_string (collection, collectproto, *thissection, disp,
+					    formatlistptr, options, logout);
+
+    if ((item >= perline && use_table) || colnumber > 0) {
+      textout << outconvert << "\n</tr>";
+      item = 0;
+    }
+    textout << outconvert << "\n";
+    ++thissection;
+  }
+
+  if (use_table)
+    textout << outconvert << "</table>\n";
+  else
+    textout << outconvert << "</div>\n";
+  if (colnumber > 0) textout << outconvert << "</td></tr></table>\n";
+
+  return 1;
+}
+
+
+// get_link_icon attempts to work out what type of icon should be displayed for
+// the given section and what it should link to.
+// [modification to allow default document detach settings -- kjdon]
+void vlistbrowserclass::get_link_icon (ResultDocInfo_t &section, cgiargsclass &args,
+				       const text_t &collink, text_t &link, text_t &icon, bool showresultlinks) {
+
+ 
+
+#ifndef DOCHANDLE
+  link = "<a href=\"_httpdocument_" + collink;
+#else
+  link = "<a href=\"_httpdocumenthandle_(" + collink;
+#endif
+
+  text_t link_end = "\">";
+
+#ifndef DOCHANDLE
+  text_t detach_link_end = "&amp;x=1\" target=\\_blank>"; // to detach the link, and x=1 signifies it is a detached page
+#else
+  text_t detach_link_end = "\" target=\\_blank>"; // to detach the link, and x=1 signifies it is a detached page
+#endif
+
+  icon = "_document:icontext_";
+
+  text_t doctype;
+  int haschildren = 0;
+
+  if (section.metadata.count("haschildren")>0)
+    haschildren = section.metadata["haschildren"].values[0].getint();
+  if (section.metadata.count("doctype")>0)
+    doctype = section.metadata["doctype"].values[0];
+
+  text_t &arg_d = args["d"];
+  text_t &arg_cl = args["cl"];
+
+  if (args["a"] == "q")
+  {
+    // query results list
+#ifndef DOCHANDLE
+    int result_num = section.result_num;
+    int num_docs = args["nmd"].getint();
+    int srn = 0;
+    int srp = 0;
+    if (showresultlinks && result_num < num_docs) {
+      srn = result_num  + 1;
+    }    
+    if (showresultlinks && result_num > 1 ) {
+      srp = result_num  - 1;
+    }    
+        
+    text_t srp_t = srp; 
+    text_t srn_t = srn; 
+    link +="&amp;srp="+srp_t;
+    link +="&amp;srn="+srn_t;
+    link +="&amp;cl=search&amp;d=" + section.OID;
+#else
+    link += "," + section.OID+")";
+#endif
+ 
+    if (args["xx"] == "1") {
+      link += detach_link_end;
+    }
+    else {
+      link += link_end;
+    }
+    if ((is_top (section.OID)) && (haschildren == 1)) icon = "_document:iconclosedbook_";
+    else if (haschildren == 1) icon = "_document:iconclosedfolder_";  
+  }
+  else if (!arg_d.empty()) {
+    // we are in a document - none of the links should be detach links
+    if (is_top(section.OID)) {
+      // we link back to the originating page eg search page or classifier page
+      icon = "_document:iconopenbook_";
+      if (arg_cl == "search") { 
+	link = "<a href=\"_httpquery_\">";
+      }
+      else {
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "\">";
+#else
+	link += ","+arg_cl + ")\">";
+#endif
+      }
+    }
+    else if (haschildren == 1) {
+      if ((args["gc"] == "1") || 
+	  (is_child_of (section.OID, arg_d)) ||
+	  (section.OID == arg_d)) {
+	icon = "_document:iconopenfolder_";
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID + ".pr\">";
+#else
+	link += ","+section.OID + ".pr)\">";
+#endif
+      }
+      else {
+	icon = "_document:iconclosedfolder_";
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID + "\">";
+#else
+	link += ","+section.OID + ")\">";
+#endif
+      }
+    }
+    else {
+      if (args["gt"] == "0") {
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID + "\">";
+#else
+	link += ","+section.OID + ")\">";
+#endif
+      }
+      else {
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID + "#" + section.OID + "\">";
+#else
+	link += ","+section.OID + "#" + section.OID + ")\">";
+#endif
+      }
+    }
+  }
+  else {
+    // classification level - links to docs may be detaching
+    if (haschildren == 1) {
+      if (doctype == "classify") {
+	// Dynamic classifier nodes
+	if (args["a"] == "dc")
+	{
+	  // Parent node or current node
+	  text_t classifier_node_dcn = "";
+	  if (!starts_with (section.OID, args["dcn"]) || section.OID == args["dcn"])
+	  {
+	    icon = "_document:iconopenbookshelf_";
+	    classifier_node_dcn = section.OID + ".pr";
+	  }
+	  // Child node
+	  else
+	  {
+	    icon = "_document:iconclosedbookshelf_";
+	    classifier_node_dcn = section.OID;
+	  }
+
+	  // The node label is going into the URL, so make it CGI-safe
+	  text_t classifier_node_dcn_cgi_safe = cgi_safe_utf8(classifier_node_dcn);
+	  link = "<a href=\"_gwcgi_?c=" + args["c"] + "&amp;a=dc&amp;dcl=" + args["dcl"] + "&amp;dcn=" + classifier_node_dcn_cgi_safe + "\">";
+	}
+	// Static classifier nodes
+	else if ((args["gc"] == "1") || 
+	    (is_child_of (section.OID, arg_cl)) || 
+	    (section.OID == arg_cl)) {
+	  icon = "_document:iconopenbookshelf_";
+
+#ifndef DOCHANDLE
+	  link += "&amp;cl=" + section.OID + ".pr\">";
+#else
+	  link += ","+section.OID + ".pr)\">";
+#endif
+	}
+	else {
+	  icon = "_document:iconclosedbookshelf_";
+
+#ifndef DOCHANDLE
+	  link += "&amp;cl=" + section.OID + "\">";
+#else
+	  link += ","+section.OID + ")\">";
+#endif
+	}
+      }
+      else {
+	icon = "_document:iconclosedbook_";
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID;
+#else
+	link += ","+section.OID+")";
+#endif
+	if (args["xx"] == "1") {
+	  link +=  detach_link_end;
+	}
+	else {
+	  link += link_end;
+	}
+      }
+    }
+    else {
+      if (doctype == "classify") link = "";
+      else {
+#ifndef DOCHANDLE
+	link += "&amp;cl=" + arg_cl + "&amp;d=" + section.OID;
+#else
+	link += "," + section.OID +")";
+#endif
+	if (args["xx"] == "1") {
+	  link +=  detach_link_end;
+	}
+	else {
+	  link += link_end;
+	}
+      }
+    }
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/vlistbrowserclass.h	(revision 16561)
@@ -0,0 +1,73 @@
+/**********************************************************************
+ *
+ * vlistbrowserclass.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.
+ *
+ *********************************************************************/
+
+
+#ifndef VLISTBROWSERCLASS_H
+#define VLISTBROWSERCLASS_H
+
+#include "browserclass.h"
+#include "receptionist.h"
+
+class vlistbrowserclass : public browserclass {
+protected:
+
+public:
+  vlistbrowserclass ();
+  virtual ~vlistbrowserclass ();
+
+  void set_receptionist(receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_browser_name ();
+
+  virtual browserclass* clone();
+
+  virtual void load_metadata_defaults (text_tset &metadata);
+
+  virtual text_t get_default_formatstring ();
+
+  virtual int output_section_group (ResultDocInfo_t &section, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+  virtual int output_section_group (FilterResponse_t &sections, cgiargsclass &args,
+				    const text_t &collection, int colnumber, format_t *formatlistptr, 
+				    bool use_table, text_tset &metadata, bool &getParents,
+				    recptproto *collectproto, displayclass &disp, 
+				    outconvertclass &outconvert, ostream &textout, ostream &logout);
+
+protected:
+
+ 
+  virtual void get_link_icon (ResultDocInfo_t &section, cgiargsclass &args,
+			     const text_t &collink, text_t &link, text_t &icon, bool showresultlinks);
+
+  virtual bool show_search_result_links (recptproto * collectproto,const text_t &collection,ostream& logout);
+
+  receptionist *recpt;
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/recpt/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/win32.mak	(revision 16561)
@@ -0,0 +1,251 @@
+###########################################################################
+#
+# win32 makefile -- src/recpt
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+COLSERVR_DIR = $(GSDL_DIR)\src\colservr
+PROTOCOL_DIR = $(GSDL_DIR)\src\protocol
+Z3950_DIR = $(GSDL_DIR)\src\z3950
+
+
+GSDL_VC4 = 0
+LOCAL_LIBRARY = 0
+USE_Z3950 = 0
+DLL = 0
+DLLDEBUG = 0
+ENABLE_ACCENTFOLD = 1
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+!IF $(ENABLE_ACCENTFOLD)
+ACCENTFOLD_LIBS = "$(INDEXERS_DIR)\packages\unac\libunac.lib" "$(INDEXERS_DIR)\packages\unac\iconv.lib"
+!ELSE
+ACCENTFOLD_LIBS = 
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_LIBS = "$(PACKAGES_DIR)\sqlite\lib\sqlite3.lib"
+!ELSE
+SQLITE_LIBS = 
+!ENDIF
+
+!IF $(USE_Z3950)
+Z3950_DEFS = -DUSE_Z3950
+Z3950_INCLUDES = -I$(Z3950_DIR)
+Z3950_LIBS = $(Z3950_DIR)\gsdlz3950.lib $(PACKAGES_DIR)\yaz\yaz-2.1.4\lib\yaz.lib
+!ELSE
+Z3950_DEFS = 
+Z3950_INCLUDES = 
+Z3950_LIBS = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE \
+        -DGSDL_NOCACHE $(Z3950_DEFS)
+INCLUDES = -I. -I"$(GSDL_DIR)" -I"$(GSDL_DIR)\src\lib" -I"$(COLSERVR_DIR)" -I"$(PROTOCOL_DIR)" \
+	-I"$(INDEXERS_DIR)\mg" -I"$(INDEXERS_DIR)\mg\lib" -I"$(INDEXERS_DIR)\mgpp\text" \
+	-I"$(PACKAGES_DIR)\windows\crypt\crypt" -I"$(PACKAGES_DIR)\windows\expat\expat" \
+	-I"$(PACKAGES_DIR)\windows\gdbm\gdbm" $(Z3950_INCLUDES)
+LDFLAGS = 
+LIBS = "$(PACKAGES_DIR)\windows\crypt\crypt\crypt.lib" "$(PACKAGES_DIR)\windows\expat\expat\libexpat.lib" \
+        "$(PACKAGES_DIR)\windows\gdbm\gdbm\gdbm.lib" $(ACCENTFOLD_LIBS) $(SQLITE_LIBS) $(Z3950_LIBS)
+
+!IF $(GSDL_VC4)
+DEFS = $(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+!IF $(LOCAL_LIBRARY)
+DEFS = $(DEFS) -DGSDL_LOCAL_LIBRARY
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo -Fe"$@" $(LDFLAGS)
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+SOURCES = \
+	action.cpp \
+	argdb.cpp \
+	authenaction.cpp \
+	browseaction.cpp \
+	browseactiontools.cpp \
+	browserclass.cpp \
+	browsetoolsclass.cpp \
+	cgiargs.cpp \
+	cgiutils.cpp \
+	cgiwrapper.cpp \
+	collectoraction.cpp \
+	configaction.cpp \
+        converter.cpp \
+	datelistbrowserclass.cpp \
+	depositoraction.cpp \
+	documentaction.cpp \
+	dynamicclassifieraction.cpp \
+	extlinkaction.cpp \
+	formattools.cpp \
+	gtiaction.cpp \
+	highlighttext.cpp \
+	historydb.cpp \
+	hlistbrowserclass.cpp \
+	htmlbrowserclass.cpp \
+	htmlutils.cpp \
+	invbrowserclass.cpp \
+	librarymain.cpp \
+	maincfg.cpp \
+	pageaction.cpp \
+	pagedbrowserclass.cpp \
+	phindaction.cpp \
+	phindbrowserclass.cpp \
+	pingaction.cpp \
+	queryaction.cpp \
+	querytools.cpp \
+	receptionist.cpp \
+	statusaction.cpp \
+	summarise.cpp \
+	tipaction.cpp \
+	userdb.cpp \
+	usersaction.cpp \
+	vlistbrowserclass.cpp \
+	wizardaction.cpp
+
+OBJECTS = \
+	action.obj \
+	argdb.obj \
+	authenaction.obj \
+	browseaction.obj \
+	browseactiontools.obj \
+	browserclass.obj \
+	browsetoolsclass.obj \
+	cgiargs.obj \
+	cgiutils.obj \
+	cgiwrapper.obj \
+	collectoraction.obj \
+	configaction.obj \
+        converter.obj \
+	datelistbrowserclass.obj \
+	depositoraction.obj \
+	documentaction.obj \
+	dynamicclassifieraction.obj \
+	extlinkaction.obj \
+	formattools.obj \
+	gtiaction.obj \
+	highlighttext.obj \
+	historydb.obj \
+	hlistbrowserclass.obj \
+	htmlbrowserclass.obj \
+	htmlutils.obj \
+	invbrowserclass.obj \
+	librarymain.obj \
+	maincfg.obj \
+	pageaction.obj \
+	pagedbrowserclass.obj \
+	phindaction.obj \
+	phindbrowserclass.obj \
+	pingaction.obj \
+	queryaction.obj \
+	querytools.obj \
+	receptionist.obj \
+	statusaction.obj \
+	summarise.obj \
+	tipaction.obj \
+	userdb.obj \
+	usersaction.obj \
+	vlistbrowserclass.obj \
+	wizardaction.obj
+
+LIBRARY = gsdlrecpt.lib
+
+GSDL_LIBS = \
+	"$(PROTOCOL_DIR)\gsdlprotocol.lib" \
+	"$(COLSERVR_DIR)\gsdlcolservr.lib" \
+	"$(INDEXERS_DIR)\mg\src\text\libmgtext.lib" \
+	"$(INDEXERS_DIR)\mg\lib\libmglib.lib" \
+	"$(INDEXERS_DIR)\mgpp\text\libmgpptext.lib" \
+	"$(INDEXERS_DIR)\mgpp\lib\libmgpplib.lib" \
+	"$(GSDL_DIR)\src\lib\gsdllib.lib"
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = library.exe
+
+
+# Default target: make gsdlrecpt.lib if LOCAL_LIBRARY is set, otherwise both gsdlrecpt.lib and library.exe
+!IF $(LOCAL_LIBRARY)
+all: $(LIBRARY)
+!ELSE
+all: $(LIBRARY) $(EXECUTABLE)
+!ENDIF
+
+gsdlrecpt.lib: $(OBJECTS) 
+	if EXIST $(LIBRARY) del $(LIBRARY)
+	$(AR) -nologo /out:$(LIBRARY) $(OBJECTS)
+
+library.exe: $(EXECUTABLE_OBJECTS)
+	$(LINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+!IF $(LOCAL_LIBRARY)
+install:
+!ELSE
+install: $(EXECUTABLE)
+	copy $(EXECUTABLE) "$(GSDL_DIR)\cgi-bin"
+!ENDIF
+
+clean:
+	del *.obj
+	del $(LIBRARY)
+	del $(EXECUTABLE)
+
+
+# Always recompile configaction and statusaction in case GSDL_LOCAL_LIBRARY has changed
+statusaction.obj: FORCE
+configaction.obj: FORCE
+
+FORCE:
Index: /gsdl/trunk/gs2runtime/src/recpt/wizardaction.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/wizardaction.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/wizardaction.cpp	(revision 16561)
@@ -0,0 +1,772 @@
+/**********************************************************************
+ *
+ * wizardaction.cpp -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+
+// note that the wizardaction relies on having direct access to a
+// collections configuration file. this breaks the separation between
+// receptionist and collection server and so is not suitable (at least
+// in its current form) for use when collection servers are separate 
+// from the receptionist (e.g. when using the CORBA protocol).
+
+// following line required to get fstream.filedesc() on darwin (Mac OS X)
+#define _STREAM_COMPAT  1
+// required for utsname on solaris???
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+
+#include "wizardaction.h"
+#include "OIDtools.h"
+#include "fileutil.h"
+#include "cfgread.h"
+#include "gsdltools.h"
+#include "gsdltimes.h"
+#include "argdb.h"
+#include "cgiutils.h"
+#include <stdio.h>
+#include <fcntl.h>
+
+#if !defined (__WIN32__)
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
+wizardaction::wizardaction () {
+
+  recpt = NULL;
+  disabled = true;
+  gsdlosc = NULL;
+  gsdlhomec = NULL;
+  pathc = NULL;
+
+  macro_prefix = "";
+}
+
+wizardaction::~wizardaction () {
+  if (gsdlosc != NULL) delete []gsdlosc;
+  if (gsdlhomec != NULL) delete []gsdlhomec;
+  if (pathc != NULL) delete []pathc;
+}
+
+void wizardaction::configure (const text_t &key, const text_tarray &cfgline) {
+  
+  const text_t& action_name = get_action_name();
+
+  if ((key == action_name) && (cfgline.size() == 1) && 
+      (cfgline[0] == "true" || cfgline[0] == "on" || cfgline[0] == "enabled")) {
+    disabled = false;
+  } else {
+    // call the parent class to deal with the things which
+    // are not dealt with here
+    action::configure (key, cfgline);
+  }
+}
+
+
+bool wizardaction::init (ostream & /*logout*/) {
+
+  // set up GSDLOS, GSDLHOME and PATH environment variables
+  text_t gsdlos, path;
+  unsigned int path_separator = ':';
+#if defined (__WIN32__)
+  gsdlos = "windows";
+  path_separator = ';';
+
+  path = filename_cat (gsdlhome, "bin", "windows", "perl", "bin;");
+
+#else
+  struct utsname *buf = new struct utsname();
+  int i = uname (buf);
+  if (i == -1) gsdlos = "linux"; // uname failed
+  else gsdlos.setcstr (buf->sysname);
+  delete buf;
+  lc (gsdlos);
+#endif
+
+  pathc = getenv ("PATH");
+  path += filename_cat (gsdlhome, "bin", gsdlos);
+  path.push_back (path_separator);
+  path += filename_cat (gsdlhome, "bin", "script");
+  if (pathc != NULL) {
+    path.push_back (path_separator);
+    path += pathc;
+  }
+  path = "PATH=" + path;
+
+  gsdlos = "GSDLOS=" + gsdlos;
+  text_t setgsdlhome = "GSDLHOME=" + gsdlhome;
+
+  // these will be cleaned up in the destructor
+  gsdlosc = gsdlos.getcstr();
+  gsdlhomec = setgsdlhome.getcstr();
+  pathc = path.getcstr();
+  
+  putenv (gsdlosc);
+  putenv (gsdlhomec);
+  putenv (pathc);
+
+  return true;
+}
+
+
+void wizardaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
+					response_t &response,text_t &response_data, 
+					ostream &/*logout*/) {
+  response = content;
+  response_data = "text/html";
+}
+
+
+bool wizardaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+				     recptprotolistclass * /*protos*/, ostream &logout) {
+  
+  text_t current_page = args["p"];
+
+  if (current_page != "intro" && 
+      current_page != "bildframe1" && current_page != "new" && current_page != "select") {
+    // update arguments that were saved to the harddrive
+    text_tmap saved_args;
+    
+    // update the argdb database with any arguments that were set
+    // by previous page as denoted by prefix (eg. di1 or bc1)
+
+    cgiargsclass::const_iterator args_here = args.begin();
+    cgiargsclass::const_iterator args_end = args.end();
+
+    while (args_here != args_end) {
+      text_t args_name = (*args_here).first;
+      
+      int prefix_len = macro_prefix.size();
+      
+      text_t args_prefix = substr(args_name.begin(),args_name.begin()+prefix_len);
+      
+      if (args_prefix == macro_prefix) {
+	saved_args[args_name] = args[args_name];
+      }	
+      ++args_here;
+    }
+      
+    text_t argfile = filename_cat(gsdlhome, "tmp", args[macro_prefix+"tmp"], "argdb.db");
+    argdb *args_on_disk = new argdb(argfile);
+    if (!args_on_disk->update_args(saved_args)) {
+      // error
+      logout << "wizardaction: argdb::update_args failed (" << argfile << ")\n";
+    }
+    
+    // update args from argdb
+    saved_args.erase(saved_args.begin(), saved_args.end());
+    if (!args_on_disk->get_args(saved_args)) {
+      // error
+      logout << "wizardaction: argdb::get_args failed (" << argfile << ")\n";
+    }
+    delete args_on_disk;
+    
+    text_tmap::iterator sa_here = saved_args.begin();
+    text_tmap::iterator sa_end = saved_args.end();
+    while (sa_here != sa_end) {
+      if (!(*sa_here).second.empty()) {
+	args[(*sa_here).first] = (*sa_here).second;
+      }
+      ++sa_here;
+    }  bool first = true;
+
+  }
+
+  return true;
+}
+
+// tests if collection is write protected (currently just checks if
+// collect.cfg file is writable
+bool wizardaction::collection_protected (const text_t &collection) {
+  text_t cfgfile = filename_cat(collecthome, collection, "etc", "collect.cfg");
+  if (file_writable(cfgfile)) return false;
+  return true;
+}
+
+// set the _statusline_ macro
+void wizardaction::set_statusline (displayclass &disp, cgiargsclass &args, ostream & /*logout*/) {
+
+  // the build command creates .bld.download, .bld.import, and .bld.build files (in that
+  // order) and deletes them (also in that order) when each stage is complete. the .bld
+  // file is the concatenation of all these files.
+  text_t bld_file = filename_cat (gsdlhome, "tmp", args[macro_prefix+"tmp"], args[macro_prefix+"dirname"] + ".bld");
+  text_t statusline;
+
+  if (file_exists (bld_file + ".download")) {
+    statusline = "_collector:textdownloadingfiles_<br>\n";
+    statusline += dm_safe(file_tail(bld_file + ".download", 1, 0));
+  } else if (file_exists (bld_file + ".import")) {
+    statusline = "_collector:textimportingcollection_<br>\n";
+    statusline += dm_safe(file_tail(bld_file + ".import", 1, 0));
+  } else if (file_exists (bld_file + ".build")) {
+    statusline = "_collector:textbuildingcollection_<br>\n";
+    statusline += dm_safe(file_tail(bld_file + ".build", 1, 0));
+  } else {
+    statusline += "_collector:textcreatingcollection_<br>\n";
+    statusline += dm_safe(file_tail(bld_file, 1, 0));
+  }
+
+  disp.setmacro ("statusline", "collector", statusline);
+  disp.setmacro ("statusline", "depositor", statusline); 
+}
+
+
+// if sw = 0 replace all carriage returns in intext with the string "\n"
+// else replace all occurances of "\n" with a carriage return
+text_t wizardaction::carriage_replace (const text_t &intext, int sw) {
+   
+  text_t outtext;
+  text_t::const_iterator here = intext.begin();
+  text_t::const_iterator end = intext.end();
+  while (here != end) {
+    if (sw == 0) {
+      if (*here == '\n') {
+	if ((here+1) != end && *(here+1) == '\r') ++here;
+	outtext += "\\n";
+      } else if (*here == '\r') {
+	if ((here+1) != end && *(here+1) == '\n') ++here;
+	outtext += "\\n";
+      } else {
+	outtext.push_back (*here);
+      }
+    } else if (*here == '\\' && (here+1) != end && *(here+1) == 'n') {
+      outtext.push_back ('\n');
+      ++here;
+    } else {
+      outtext.push_back (*here);
+    }
+    ++here;
+  }
+  return outtext;
+}
+
+
+// create a short directory name from fullname
+text_t wizardaction::get_directory_name (const text_t &fullname) {
+
+  text_t shortname;
+  if (fullname.empty()) {
+    shortname = "coll";
+
+  } else {
+    
+    // first make all lowercase and remove any dodgy characters
+    // (i.e. anything not [a-z]
+    text_t::const_iterator here = fullname.begin();
+    text_t::const_iterator end = fullname.end();
+    while (here != end) {
+      if ((*here >= 'A' && *here <= 'Z') || (*here >= 'a' && *here <= 'z') ||
+	  (*here == ' ')) {
+	if (*here >= 'A' && *here <= 'Z') shortname.push_back (*here+32);
+	else if (*here == ' ') {
+	  while ((*(here+1)) == ' ') ++here;
+	  shortname.push_back (*here);
+	} else shortname.push_back (*here);
+      }
+      ++here;
+    }
+
+    text_tarray words;
+    splitchar (shortname.begin(), shortname.end(), ' ', words);
+    int num_words = words.size();
+
+    if (num_words == 0) {
+      shortname = "coll";
+
+    } else {
+
+      shortname.clear();
+      int use_words = (num_words <= 6) ? num_words : 6;
+      unsigned int substr_len = 6 / use_words;
+      
+      for (int i = 0; i < use_words; ++i) {
+	if (words[i].size() < substr_len) shortname += words[i];
+	else shortname += substr (words[i].begin(), words[i].begin()+substr_len);
+      }
+    }
+  }
+
+  // check to see if shortname is unique
+  text_t fulldirname = filename_cat (collecthome, shortname);
+  if (directory_exists (fulldirname)) {
+    int version = 0;
+    text_t newname;
+    do {
+      ++version;
+      newname = shortname;
+      newname.push_back ('v');
+      newname.appendint (version);
+      fulldirname = filename_cat (collecthome, newname);
+    } while (directory_exists (fulldirname));
+
+    shortname = newname;
+  }
+  
+  return shortname;
+}
+
+// assigns a temporary directory name for this collector session
+// and creates temporary directory
+// returns false if it couldn't create the directory
+bool wizardaction::assign_tmpname (cgiargsclass &args, ostream &logout) {
+
+  int i = 0;
+  text_t tmpname = "tbuild";
+  while (directory_exists (filename_cat (gsdlhome, "tmp", tmpname + text_t(i)))) {
+    ++i;
+  }
+  tmpname.appendint (i);
+
+  text_t fulltmpdir = filename_cat (gsdlhome, "tmp", tmpname);
+  if (!mk_dir (fulltmpdir)) {
+    outconvertclass text_t2ascii;
+    logout << text_t2ascii << "wizardaction::assign_tmpname unable to create directory ("
+	   << fulltmpdir << ")\n";
+    return false;
+  }
+
+  args[macro_prefix + "tmp"] = tmpname;
+  return true;
+}
+
+// set the _fullnamemenu_ macro (and _warnindex_ and _selectedindex_ if
+// we're on the "srce" page)
+void wizardaction::set_fullnamemenu (displayclass &disp, cgiargsclass &args, 
+					recptprotolistclass *protos, ostream &logout) {
+
+  if (recpt == NULL) {
+    logout << "ERROR (wizardaction::set_fullnamemenu): This action does not contain\n"
+	   << "       information about any receptionists. The method set_receptionist was\n"
+	   << "       probably not called from the module which instantiated this action.\n";
+    return;
+  }
+
+  text_t &current_page = args["p"];
+  text_t currentname = args[macro_prefix+"dirname"];
+  if (current_page == "srce") currentname = args[macro_prefix + "clonecol"];
+
+  text_tarray dirnames;
+  text_tarray fullnames;
+  vector<bool> write_protected;
+  text_tarray  build_type;
+
+  bool is_selected = false;
+  int selected_index = 0;
+  int index = 0;
+
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    if ((*rprotolist_here).p != NULL) {
+
+      // don't include z39.50 collections
+      comerror_t err = noError;
+      if ((*rprotolist_here).p->get_protocol_name (err) == "z3950proto") {
+	++rprotolist_here;
+	continue;
+      }
+
+      text_tarray collist;
+      (*rprotolist_here).p->get_collection_list (collist, err, logout);
+      if (err == noError) {
+	text_tarray::iterator collist_here = collist.begin();
+	text_tarray::iterator collist_end = collist.end();
+	while (collist_here != collist_end) {
+	  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
+	  if (cinfo != NULL) {
+	    text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
+	    if (collectionname.empty()) {
+	      collectionname = *collist_here;
+	    }
+	    dirnames.push_back(*collist_here);
+	    fullnames.push_back(collectionname);
+	    // check to see if the collection is writable
+	    if (collection_protected (*collist_here)) write_protected.push_back(true);
+	    else write_protected.push_back(false);
+
+	    // remember build type for collection (mg, mgpp, lucene, ...)
+	    // used to determine cgi arg 'ct' later on and minus option to 'build'
+	    build_type.push_back(cinfo->buildType);
+
+	    if (*collist_here == currentname) {
+	      is_selected = true;
+	      selected_index = index; 	    
+	    }
+	    ++index;
+	  }
+	  ++collist_here;
+	}
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  bool first = true;
+
+  text_t warnindex;
+  text_t fullnamemenu = "<select name=\""+macro_prefix+"dirname\" onChange=\"menuchange();\">\n";
+  text_t buildtype_jsarray = "var buildtype = new Array(";
+
+  fullnamemenu += "<option value=\"\">select collection ...</option>\n";
+  buildtype_jsarray += "\"\" ";
+
+  for (int i = 0; i < index; ++i) {
+    // don't want write protected collections in list on "change existing
+    // collection" page
+    if (write_protected[i] && current_page == "existing") continue;
+    fullnamemenu += "<option value=\"" + dirnames[i] + "\"";
+    if (is_selected && i == selected_index) {
+      fullnamemenu += " selected";
+      ++selected_index;
+      is_selected = false;
+    }
+    fullnamemenu.push_back ('>');
+    fullnamemenu += fullnames[i] + " ("+dirnames[i]+")";
+    fullnamemenu.push_back ('\n');
+
+    buildtype_jsarray += ", \"" + build_type[i] + "\"";
+    
+    first = false;
+  }
+  fullnamemenu += "</select>\n";
+
+  buildtype_jsarray += ");";
+
+  text_t action_name = get_action_name();
+  disp.setmacro ("fullnamemenu", action_name, fullnamemenu);
+  disp.setmacro ("buildtypearray", action_name, buildtype_jsarray);
+}
+
+// checks to see if any of the plugins in pluginset occur in 
+// collections configuration file
+bool wizardaction::uses_weird_plugin (const text_t &collection) {
+
+  text_tset pluginset;
+  pluginset.insert ("HBPlug");
+
+  text_t cfgfile_content;
+  text_t cfgfile_name = filename_cat (collecthome, collection, "etc", "collect.cfg");
+  text_t pluginstr, pluginname;
+
+  if (read_file (cfgfile_name, cfgfile_content)) {
+    text_t::const_iterator here = cfgfile_content.begin();
+    text_t::const_iterator end = cfgfile_content.end();
+    while (here != end) {
+      here = findchar (here, end, 'p');
+      if (here == end) break;
+      if ((here+6 < end) && (substr (here, here+6) == "plugin")) {
+	getdelimitstr (here+6, end, '\n', pluginstr);
+	text_t::const_iterator hp = pluginstr.begin();
+	text_t::const_iterator ep = pluginstr.end();
+	bool found = false;
+	// remove any leading whitespace, trailing options etc.
+	while (hp != ep) {
+	  if (*hp == '\t' || *hp == ' ' || *hp == '\n') {
+	    if (found) break;
+	  } else {
+	    pluginname.push_back (*hp);
+	    found = true;
+	  }
+	  ++hp;
+	}
+	text_tset::const_iterator it = pluginset.find (pluginname);
+	if (it != pluginset.end()) return true; // found matching plugin
+	pluginname.clear();
+      }
+      ++here;
+    }
+  }
+  return false;
+}
+
+void wizardaction::gsdl_build (cgiargsclass &args, ostream &logout) {
+
+  outconvertclass text_t2ascii;
+
+  //check to see if the tbuild directory exists
+  text_t tmpdir = filename_cat (gsdlhome, "tmp", args[macro_prefix+"tmp"]);
+  if (!directory_exists (tmpdir)) {
+    message = "tmpfail";
+    return;
+  }
+  
+  //check to see if collection name specified
+  text_t &collection = args[macro_prefix+"dirname"];
+  if (collection.empty()) {
+    message = "nocollection";
+    return;
+  }
+
+  // check for a .build file - if it exists then we've already built
+  // the collection (or are in the process of building it)
+  text_t buildfile = filename_cat (tmpdir, ".build");
+  if (file_exists (buildfile)) {
+    return;
+  } else {
+    // create the .build file (this file is just a place holder to let any future
+    // pages know that we've already been here)
+    char *buildfilec = buildfile.getcstr();
+    ofstream bfile_out (buildfilec);
+    delete []buildfilec;
+    if (bfile_out) {
+      bfile_out << "collection building\n";
+      bfile_out.close();
+    } else {
+      message = "tmpfail";
+      return;
+    }
+  }
+
+  //FLAG!
+  const recptconf &rcinfo = recpt->get_configinfo ();
+
+  // create the event header file if LogEvents, EmailEvents or
+  // EmailUserEvents options are turned on.
+  bool logevents = 
+    (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents || 
+     rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents ||
+     rcinfo.EmailUserEvents);
+  text_t ehead_file = filename_cat (tmpdir, "ehead.txt");
+  if (logevents) {
+    if (!create_event_header_file (ehead_file, args, logout)) {
+      logevents = false;
+    }
+  }
+    
+  text_t collectdir = get_collectdir (args);
+
+  // set up build options
+  //text_t options = "-make_writable -remove_import -out \""; 
+  text_t options = "-make_writable  -out \""; 
+  options += filename_cat (tmpdir, collection + ".bld");
+  options += "\" -collectdir \"" + collectdir + "\" -statsfile \"";
+  options += filename_cat(collectdir, collection, "etc", "import.log") + "\"";
+
+  if (args[macro_prefix+"esrce"] == 1) {
+    // we're adding data to an existing collection
+    options += " -save_archives -append -manifest";
+  }
+
+  text_tarray inputvalues, inputtypes;
+  splitchar (args[macro_prefix+"input"].begin(), args[macro_prefix+"input"].end(), ',', inputvalues);
+  splitchar (args[macro_prefix+"inputtype"].begin(), args[macro_prefix+"inputtype"].end(), ',', inputtypes);
+  int numvalues = inputvalues.size();
+  int numtypes = inputtypes.size();
+  for (int i = 0; i < numvalues; ++i) {
+    if (!inputvalues[i].empty()) {
+      text_t type = "file://"; // default
+      if (i < numtypes) type = inputtypes[i];
+      options += " -download \"" + 
+	remove_trailing_slashes(type + format_url(decode_commas(inputvalues[i]))) + "\"";
+    }
+  }
+
+  if (logevents) {
+    if (rcinfo.LogEvents == CollectorEvents || rcinfo.LogEvents == AllEvents) 
+      options += " -log_events";
+    if (rcinfo.EmailEvents == CollectorEvents || rcinfo.EmailEvents == AllEvents) {
+      options += " -mail_server " + rcinfo.MailServer;
+      options += " -email_events " + rcinfo.maintainer;
+      if (rcinfo.EmailUserEvents) options += "," + args[macro_prefix+"contactemail"];
+    } else if (rcinfo.EmailUserEvents) {
+      options += " -mail_server " + rcinfo.MailServer;
+      options += " -email_events " + args[macro_prefix+"contactemail"];
+    }
+    options += " -event_header " + ehead_file;
+  }
+
+  text_t indextype = args[macro_prefix+"buildtype"];
+  if(indextype == "") {
+    indextype = "mg";
+  }
+  options += " -indextype \"" + indextype + "\"";
+
+  text_t optionfile = filename_cat (tmpdir, "build.opt");
+  char *optionfilec = optionfile.getcstr();
+  ofstream ofile_out (optionfilec);
+  delete []optionfilec;
+  if (!ofile_out) {
+    message = "tmpfail";
+    return;
+  }
+  ofile_out << text_t2ascii << options << "\n";
+  ofile_out.close();
+
+  // if we're altering an existing collection we need to kill off
+  // the existing collection server - we do this for the local library
+  // (and any other persistent version of the library) as the existing
+  // database file can't be deleted while the collection server holds it open
+  if ((args[macro_prefix+"econf"] == 1) || (args[macro_prefix+"esrce"] == 1)) {
+    remove_colservr (collection, logout);
+  }    
+
+  // set up the build command - build.bat has some issues with quoting
+  // on win2k when gsdlhome contains spaces so we'll avoid using 
+  // "perl -S" here in favor of calling the "build" perl script explicitly
+  text_t build_cmd = "perl \"" + filename_cat (gsdlhome, "bin", "script", "build");
+  build_cmd += "\" -optionfile \"" + optionfile + "\" " + collection;
+
+  // run build command in background (i.e. asynchronously) 
+  gsdl_system (build_cmd, false, logout);
+
+}
+
+void wizardaction::gsdl_cancel_build (cgiargsclass &args, ostream &logout) {
+  // I really wanted to do what this perl script does from within the library
+  // c++ code. I ran into some problems though (like how do you write a portable
+  // "rm -r" in c++?). One day I'll spend some time sorting it out ... maybe.
+  text_t cancel_cmd = "perl -S cancel_build.pl -collectdir \"";
+  cancel_cmd += filename_cat (gsdlhome, "tmp", args[macro_prefix+"tmp"]) + "\" ";
+  cancel_cmd += args[macro_prefix+"dirname"];
+  // To be on the safe side we'll make this a synchronous call
+  // so that all tidying up is done before the user has a chance
+  // to do anything else (like start rebuilding their collection).
+  // This means that for a big collection where there's lots of 
+  // stuff to delete etc. it might take a while before the "build
+  // cancelled" page appears.
+  gsdl_system (cancel_cmd, true, logout);
+}
+
+text_t wizardaction::get_collectdir (cgiargsclass &args)
+{
+  if ((args[macro_prefix+"econf"] == 1) || (args[macro_prefix+"esrce"] == 1)) {
+    // we're adding to a collection in place
+    return collecthome;
+  }
+  else {
+    return filename_cat (gsdlhome, "tmp", args[macro_prefix+"tmp"]);
+  }
+}
+
+// create and initialize a new collection server and
+// add it to the null protocol.
+void wizardaction::create_colserver (const text_t &collection, ostream &logout) {
+
+  recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    comerror_t err = noError;
+    if ((*rprotolist_here).p != NULL) {
+      if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
+	// create collection server and add it to nullproto
+	(*rprotolist_here).p->add_collection (collection, recpt, gsdlhome, collecthome, dbhome);
+	// make sure gsdlhome is configured
+	text_tarray tmp;
+	tmp.push_back (gsdlhome);
+	(*rprotolist_here).p->configure ("gsdlhome", tmp, err);
+	// re-initialize the null protocol
+	if (!(*rprotolist_here).p->init (err, logout)) {
+	  logout << "wizardaction::create_colserver: nullproto init failed\n";
+	}
+	return;
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  logout << "wizardaction::create_colserver: no valid nullproto found\n";
+}
+
+// delete a collection server from the null protocol
+void wizardaction::remove_colservr (const text_t &collection, ostream &logout) {
+
+  recpt->uncache_collection (collection);
+
+  recptprotolistclass *protos = recpt->get_recptprotolist_ptr();
+  recptprotolistclass::iterator rprotolist_here = protos->begin();
+  recptprotolistclass::iterator rprotolist_end = protos->end();
+  while (rprotolist_here != rprotolist_end) {
+    comerror_t err = noError;
+    if ((*rprotolist_here).p != NULL) {
+      if ((*rprotolist_here).p->get_protocol_name (err) == "nullproto") {
+	(*rprotolist_here).p->remove_collection (collection, logout);
+	return;
+      }
+    }
+    ++rprotolist_here;
+  }
+
+  logout << "wizardaction::create_colserver: no valid nullproto found\n";
+}
+
+bool wizardaction::create_event_header_file (const text_t &filename, cgiargsclass &args, 
+						ostream &logout) {
+
+  outconvertclass text_t2ascii;
+  char *filenamec = filename.getcstr();
+  ofstream eheadfile (filenamec);
+  delete []filenamec;
+
+  if (eheadfile) {
+    eheadfile << text_t2ascii << get_event_header (args);
+    eheadfile.close();
+    return true;
+  }
+   
+  logout << text_t2ascii << "wizardaction::create_event_header ERROR: Couldn't create "
+	 << "Event Header file " << filename << ". Event logging disabled\n";
+  return false;
+}
+
+text_t wizardaction::get_event_header (cgiargsclass &args) {
+  text_t header = "Greenstone Username: " + args["un"] + "\n";
+  header += "Collection: " + args[macro_prefix+"dirname"] + "\n";
+  header += "Collection Creator: " + args[macro_prefix+"contactemail"] + "\n";
+  header += "GSDLHOME: " + gsdlhome + "\n";
+  header += "Build Location: " + get_collectdir(args) + "\n";
+
+  return header;
+}
+
+
+// format_url simply strips "http://", "ftp://", or "file://" off the
+// beginning of url if they're there
+text_t wizardaction::format_url (const text_t &url) {
+  text_t::const_iterator begin = url.begin();
+  text_t::const_iterator end = url.end();
+
+  if (url.size() >= 7) {
+    text_t prefix = substr(begin, begin+7);
+    if (prefix == "http://" || prefix == "file://") {
+      return substr(begin+7, end);
+    }
+  }
+  if (url.size() >= 6) {
+    if (substr(begin, begin+6) == "ftp://") {
+      return substr(begin+6, end);
+    }
+  }
+  return url;
+}
+
+text_t wizardaction::remove_trailing_slashes (text_t str) {
+
+  while (*(str.end()-1) == '\\') {
+    str.pop_back();
+  }
+  return str;
+}
+
Index: /gsdl/trunk/gs2runtime/src/recpt/wizardaction.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/recpt/wizardaction.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/recpt/wizardaction.h	(revision 16561)
@@ -0,0 +1,111 @@
+/**********************************************************************
+ *
+ * wizardaction.h -- 
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "gsdl_modules_cfg.h"
+
+#ifndef WIZARDACTION_H
+#define WIZARDACTION_H
+
+#include "action.h"
+#include "receptionist.h"
+
+class wizardaction : public action {
+  
+
+public:
+  wizardaction ();
+  virtual ~wizardaction ();
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  bool init (ostream &logout);
+
+  void set_receptionist (receptionist *therecpt) {recpt=therecpt;}
+
+  text_t get_action_name () {return "wizard";}
+
+  void get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
+			 response_t &response,text_t &response_data, 
+			 ostream &logout);
+
+  bool check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, 
+		      recptprotolistclass * protos, ostream &logout);
+
+
+protected:
+  bool disabled;
+  receptionist *recpt;
+  char *gsdlosc;
+  char *gsdlhomec;
+  char *pathc;
+
+  text_t macro_prefix;
+
+  text_t message;
+  char failcode;
+
+  virtual text_t get_button (cgiargsclass &args, const text_t &thispage, 
+			     const text_t &color, 
+			     const text_t &type, bool enabled)=0;
+			    
+
+  text_t carriage_replace (const text_t &intext, int sw);
+
+  text_t get_directory_name (const text_t &fullname);
+
+  bool collection_protected (const text_t &collection);
+
+  bool assign_tmpname (cgiargsclass &args, ostream &logout);
+
+
+  void set_statusline (displayclass &disp, cgiargsclass &args, ostream &logout);
+  void gsdl_build (cgiargsclass &args, ostream &logout);
+
+  void gsdl_cancel_build (cgiargsclass &args, ostream &logout);
+
+  text_t get_collectdir (cgiargsclass &args);
+
+  void create_colserver (const text_t &collection, ostream &logout);
+
+  void remove_colservr (const text_t &collection, ostream &logout);
+
+  bool create_event_header_file (const text_t &filename, cgiargsclass &args, 
+				 ostream &logout);
+
+  text_t get_event_header (cgiargsclass &args);
+
+  text_t format_url (const text_t &url);
+
+  text_t remove_trailing_slashes (text_t str);
+
+  void set_fullnamemenu (displayclass &disp, cgiargsclass &args,
+			 recptprotolistclass *protos, ostream &logout);
+
+  bool uses_weird_plugin (const text_t &collection);
+
+};
+
+#endif //!WIZARDACTION_H
+
Index: /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.cpp	(revision 16561)
@@ -0,0 +1,985 @@
+/**********************************************************************
+ *
+ * cgiwrapper.cpp -- windows local library cgiwrapper
+ * 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <direct.h>
+#include "cgiwrapper.h"
+#include "netio.h"
+#include "wincgiutils.h"
+#include "settings.h"
+#include "fileutil.h"
+#include "parse.h"
+
+#include "gsdlconf.h"
+#include "maincfg.h"
+
+#if defined (GSDL_USE_OBJECTSPACE)
+#include <ospace\std\iostream>
+#include <ospace\std\fstream>
+#elif defined (GSDL_USE_IOS_H)
+#include <iostream.h>
+#include <fstream.h>
+#else
+#include <iostream>
+#include <fstream>
+#endif
+
+#include "receptionist.h"
+#include "nullproto.h"
+
+// actions
+#include "statusaction.h"
+#include "pageaction.h"
+#include "pingaction.h"
+#include "queryaction.h"
+#include "documentaction.h"
+#include "dynamicclassifieraction.h"
+#include "tipaction.h"
+#include "authenaction.h"
+#include "usersaction.h"
+#include "userdb.h"
+#include "extlinkaction.h"
+#include "collectoraction.h"
+#include "phindaction.h"
+#include "configaction.h"
+
+// browsers
+#include "vlistbrowserclass.h"
+#include "hlistbrowserclass.h"
+#include "datelistbrowserclass.h"
+#include "invbrowserclass.h"
+#include "pagedbrowserclass.h"
+#include "htmlbrowserclass.h"
+#include "phindbrowserclass.h"
+
+// the number of times the library has been accessed
+int libaccessnum = 0;
+
+// used to output the text from receptionist
+class textstreambuf : public streambuf
+{
+public:
+  textstreambuf ();
+  int sync ();
+  int overflow (int ch);
+  int underflow () {return EOF;}
+  
+  void tsbreset() {RInfo=NULL;casostr=NULL;}
+  void setrequestinfo (RequestInfoT *theRInfo) {RInfo=theRInfo;}
+  void cascadeoutput (ostream *thecasostr) {casostr=thecasostr;}
+  
+private:
+  RequestInfoT *RInfo;
+  ostream *casostr;
+#if !defined (GSDL_USE_IOS_H)
+  char buffer[256];
+#endif
+};
+
+textstreambuf::textstreambuf() {
+  tsbreset();
+#if !defined (GSDL_USE_IOS_H)
+  setp (&buffer[0], &buffer[255]);
+#else
+  if (base() == ebuf()) allocate();
+  setp (base(), ebuf());
+#endif
+};
+
+int textstreambuf::sync () {
+  if ((RInfo != NULL) && 
+      (Send_String_N(pbase(), pptr()-pbase(), RInfo) < 0)) {
+    RInfo = NULL;
+  }
+  
+  if (casostr != NULL) {
+    char *thepbase=pbase();
+    for (int i=0;i<(pptr()-pbase());++i) (*casostr).put(thepbase[i]);
+  }
+  
+  setp (pbase(), epptr());
+  
+  return 0;
+}
+
+int textstreambuf::overflow (int ch) {
+  if (sync () == EOF) return EOF;
+  if (ch != EOF) sputc (ch);
+  return 0;
+}
+
+
+// used to output all the log and error messages
+// from receptionist
+class logstreambuf : public streambuf
+{
+public:
+  logstreambuf ();
+  int sync ();
+  int overflow (int ch);
+  int underflow () {return EOF;}
+
+#if !defined (GSDL_USE_IOS_H)
+private:
+  char buffer[256];
+#endif
+};
+
+logstreambuf::logstreambuf () {
+#if !defined (GSDL_USE_IOS_H)
+  setp (&buffer[0], &buffer[255]);
+#else
+  if (base() == ebuf()) allocate();
+  setp (base(), ebuf());
+#endif
+}
+
+int logstreambuf::sync () {
+  if (gsdl_keep_log || gsdl_show_console) {
+    log_message ("LOCAL LIB MESSAGE: ");
+		log_message_N (pbase(), pptr()-pbase());
+  }
+
+  setp (pbase(), epptr());
+  return 0;
+}
+
+int logstreambuf::overflow (int ch) {
+  if (sync () == EOF) return EOF;
+  if (ch != EOF) sputc (ch);
+  return 0;
+}
+
+static void page_errormaincfg (const text_t &gsdlhome, const text_t &collection) {
+
+  if (collection.empty()) {
+    text_t message = "Error\n\n"
+      "The main.cfg configuration file could not be found. This file\n"
+      "should contain configuration information relating to the\n"
+      "setup of the interface. As this program is not being run\n"
+      "in collection specific mode the file should reside at\n" +
+      gsdlhome + "\\etc\\main.cfg.\n";
+
+    MessageBox(NULL, message.getcstr(),
+	       "Greenstone Digital Library Software"
+	       ,MB_OK|MB_SYSTEMMODAL);
+  } else {
+    text_t message = "Neither the collect.cfg or main.cfg configuration files could\n"
+      "be found. This file should contain configuration information\n"
+      "relating to the setup of the interface. As this cgi script is\n"
+      "being run in collection specific mode the file should reside\n"
+      "at either " + gsdlhome + "\\collect\\" + collection + "\\etc\\collect.cfg,\n" +
+      gsdlhome + "\\etc\\collect.cfg or " + gsdlhome + "\\etc\\main.cfg.\n";
+
+    MessageBox(NULL, message.getcstr(),
+	       "Greenstone Digital Library Software"
+	       ,MB_OK|MB_SYSTEMMODAL);
+  }
+}
+
+static void page_errorinit (const text_t &/*gsdlhome*/) {
+
+  text_t message = "Error\n\n"
+    "An error occurred during the initialisation of the Greenstone Digital\n"
+    "Library software. It is likely that the software has not been setup\n"
+    "correctly.\n";
+
+  MessageBox(NULL, message.getcstr(),
+	     "Greenstone Digital Library Software"
+	     ,MB_OK|MB_SYSTEMMODAL);
+}
+
+static void page_errorparseargs (const text_t &/*gsdlhome*/) {
+
+  text_t message = "Error\n\n"
+    "An error occurred during the parsing of the cgi arguments.\n";
+
+  MessageBox(NULL, message.getcstr(),
+	     "Greenstone Digital Library Software"
+	     ,MB_OK|MB_SYSTEMMODAL);
+}
+
+static void page_errorcgipage (const text_t &/*gsdlhome*/) {
+
+  text_t message = "Error\n\n"
+    "An error occurred during the construction of the cgi page.\n";
+
+  MessageBox(NULL, message.getcstr(),
+	     "Greenstone Digital Library Software"
+	     ,MB_OK|MB_SYSTEMMODAL);
+}
+
+// returns 0 if the directories can't be found
+// and the user wants to quit (it returns 1
+// if everything is ok)
+int checkdir (const text_t &thedir) {
+  UINT curerrormode;
+  int drive = _getdrive();
+  char cwd[1024];
+  char rootpath[4];
+  UINT drivetype;
+  char *cstrthedir = thedir.getcstr();
+  int returnvalue = 1;
+  
+  // make sure no rude error messages are presented to the user
+  curerrormode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+  
+  // get the drive name
+  if (thedir.size() >= 2 && thedir[1] == ':') {
+    if (thedir[0] >= 'a' && thedir[0] <= 'z') {
+      drive = thedir[0] - 'a' + 1;
+      
+    } else if (thedir[0] >= 'A' && thedir[0] <= 'Z') {
+      drive = thedir[0] - 'A' + 1;
+    }
+  }
+  
+  // find the drive type
+  rootpath[0] = drive + 'A' - 1;
+  rootpath[1] = ':';
+  rootpath[2] = '\\';
+  rootpath[3] = '\0';
+  drivetype = GetDriveType (rootpath);
+  
+  // try and set this directory to be the current working
+  // directory
+  _getcwd(cwd, 1024);
+  while (_chdir(cstrthedir) != 0) {
+    // failed
+    if (drivetype == DRIVE_CDROM) {
+      // message to insert the cdrom
+      if (MessageBox (NULL, 
+		      "Please insert the Greenstone Digital Library\n"
+		      "CD-ROM into the CD-ROM drive and press 'OK'.\n\n"
+		      "If you don't have the CD-ROM press 'Cancel' to exit\n"
+		      "this program.", "Greenstone Digital Library Software", 
+		      MB_OKCANCEL | MB_TASKMODAL) == IDCANCEL) {
+	
+	returnvalue = 0;
+	break;
+      }
+      
+    } else {
+      // message saying that the system was unable
+      // to find a certain directory
+      text_t message = "Failed to find the directory:\n\n" + thedir;
+      message += "\n\n"
+	"This directory is needed for the successful operation\n"
+	"of this software. Make sure it hasn't been deleted or\n"
+	"moved, and restart the software. You may need to\n"
+	"reinstall this software to correct the problem.";
+      char *cstrmessage = message.getcstr();
+      
+      MessageBox (NULL, cstrmessage, "Greenstone Digital Library Software",
+		  MB_OK | MB_TASKMODAL);
+      
+      delete []cstrmessage;
+      
+      returnvalue = 0;
+      break;
+    }
+  }
+  
+  // revert to the previous error and cwd states
+  _chdir(cwd);
+  SetErrorMode(curerrormode);
+  
+  // free the allocated C string
+  delete []cstrthedir;
+  
+  return returnvalue;
+}
+
+
+// c-string version of checkdir for the outside
+// world
+int cstrcheckdir (char *cstrthedir) {
+  return checkdir (cstrthedir);
+}
+
+
+receptionist recpt;
+nullproto nproto;
+collectset *cservers = NULL;
+
+textstreambuf textstream;
+logstreambuf logstream;
+DWORD lastlibaccesstime;
+DWORD baseavailvirtual;
+text_t current_gsdlhome;
+colinfo_tmap translated_collectinfo;
+  
+statusaction *astatusaction = NULL;
+pageaction *apageaction = NULL;
+pingaction *apingaction = NULL;
+tipaction *atipaction = NULL;
+queryaction *aqueryaction = NULL;
+documentaction *adocumentaction = NULL;
+dynamicclassifieraction *adynamicclassifieraction = NULL;
+usersaction *ausersaction = NULL;
+extlinkaction *anextlinkaction = NULL;
+collectoraction *acollectoraction = NULL;
+authenaction *aauthenaction = NULL;
+phindaction *aphindaction = NULL;
+configaction *aconfigaction = NULL;
+vlistbrowserclass *avlistbrowserclass = NULL;
+hlistbrowserclass *ahlistbrowserclass = NULL;
+datelistbrowserclass *adatelistbrowserclass = NULL;
+invbrowserclass *ainvbrowserclass = NULL;
+pagedbrowserclass *apagedbrowserclass = NULL;
+htmlbrowserclass *ahtmlbrowserclass = NULL;
+phindbrowserclass *aphindbrowserclass = NULL;
+text_t userdbfile = NULL;
+userdbclass *udb = NULL;
+text_t keydbfile = NULL;
+keydbclass *kdb = NULL;
+
+// returns 1 if successful, 0 if unsuccessful (note that as well as being
+// called when the server first starts up this function is called when the
+// "restart library" button is pressed)
+int gsdl_init (bool atStartup) {
+
+  if (atStartup) {
+#if defined (GSDL_USE_IOS_H)
+    cerr = &logstream;
+    cout = &textstream;
+#else
+    cerr.rdbuf(&logstream);
+    cout.rdbuf(&textstream);
+#endif
+  }
+
+  // collection should be set to "" unless in collection specific mode -
+  // changing this to the name of the collection should be all that's
+  // required to create a collection specific receptionist
+  text_t collection = "";
+  text_tset dbhomes;
+  text_tset collections;
+
+  if (atStartup) {
+    // note the current time
+    lastlibaccesstime = GetTickCount();
+    
+    // before we do the init we should make sure
+    // that we can find the relevant directories
+    if (!checkdir (gsdl_gsdlhome + "\\")) return 0;
+    if (!checkdir (gsdl_collecthome + "\\")) return 0;
+    if (!checkdir (gsdl_gsdlhome + "\\macros\\")) return 0;
+  }    
+
+  // deleting cservers clears all the collection servers 
+  if (cservers != NULL) delete cservers;
+
+  cservers = new collectset();
+
+  // get all collections from each gsdlhome (this relies 
+  // on there not being more than one collection with the same 
+  // name)
+  if (!collection.empty()) {
+    // collection specific receptionist - one collection, one gsdlhome
+    collections.insert (collection);
+    dbhomes.insert (gsdl_dbhome);
+    collectioninfo_t tmp;
+    tmp.gsdl_gsdlhome = gsdl_gsdlhome;
+    tmp.gsdl_collecthome = gsdl_collecthome;
+    tmp.gsdl_dbhome = gsdl_dbhome;
+    translated_collectinfo[collection] = tmp;
+
+  } else {
+
+    text_tset::const_iterator colhere;
+    text_tset::const_iterator colend;
+    text_tset these_collections;
+
+    // first volume gsdlhome's
+    colinfo_tmap::const_iterator this_info = gsdl_collectinfo.begin();
+    colinfo_tmap::const_iterator end_info = gsdl_collectinfo.end();
+    while (this_info != end_info) {
+      if (dbhomes.find ((*this_info).second.gsdl_dbhome) == dbhomes.end()) {
+	these_collections.erase (these_collections.begin(), these_collections.end());
+	read_dir ((*this_info).second.gsdl_collecthome, these_collections);
+	colhere = these_collections.begin();
+	colend = these_collections.end();
+	while (colhere != colend) {
+	  if ((collections.find (*colhere)) == collections.end()) {
+	    // make sure the build.cfg file is at gsdlhome (as it's possible that
+	    // the collection appears at this gsdlhome only because it's gdbm
+	    // file is installed here -- it's real gdbm will therefore be 
+	    // somewhere else).
+	    text_t build_cfg = filename_cat ((*this_info).second.gsdl_collecthome,
+					     *colhere, "index", "build.cfg");
+	    if (file_exists (build_cfg)) {
+	      collections.insert (*colhere);
+
+	      // since gsdl_collectinfo keys will be stuff like collection#1
+	      // for a multiple volume collection we want to translate it
+	      // so that the keys are the actual collection names
+	      translated_collectinfo[*colhere] = (*this_info).second;
+	    }
+	  }
+	  ++colhere;
+	}
+	dbhomes.insert ((*this_info).second.gsdl_dbhome);
+      }
+      ++this_info;
+    }
+  
+    // then if necessary the main dbhome (this should only happen if the 
+    // gsdl.ini is a little screwed up and no volume dbhomes occurred)
+    if (dbhomes.find (gsdl_dbhome) == dbhomes.end()) {
+      these_collections.erase (these_collections.begin(), these_collections.end());
+      read_dir (filename_cat (gsdl_dbhome, "collect"), these_collections);
+      colhere = these_collections.begin();
+      colend = these_collections.end();
+      while (colhere != colend) {
+	collections.insert (*colhere);
+	collectioninfo_t tmp;
+	tmp.gsdl_gsdlhome = gsdl_gsdlhome;
+	tmp.gsdl_collecthome = gsdl_collecthome;
+	tmp.gsdl_dbhome = gsdl_dbhome;
+	translated_collectinfo[*colhere] = tmp;
+	++colhere;
+      }
+      dbhomes.insert (gsdl_dbhome);
+    }
+  }
+
+  text_tset::const_iterator thiscol = collections.begin();
+  text_tset::const_iterator endcol = collections.end();
+
+  while (thiscol != endcol) {
+    
+    // ignore the modelcol
+    if (*thiscol == "modelcol") {
+      ++thiscol;
+      continue;
+    }
+    
+    // create collection server and add to null protocol
+    text_t this_gsdlhome = gsdl_gsdlhome;
+    text_t this_collecthome = gsdl_collecthome;
+    text_t this_dbhome = gsdl_dbhome;
+    colinfo_tmap::const_iterator it = translated_collectinfo.find (*thiscol);
+    assert (it != translated_collectinfo.end());
+    this_gsdlhome = (*it).second.gsdl_gsdlhome;
+    this_collecthome = (*it).second.gsdl_collecthome;
+    this_dbhome = (*it).second.gsdl_dbhome;
+
+    cservers->add_collection (*thiscol, this_gsdlhome, this_collecthome);
+
+    ++thiscol;
+  }
+
+  // set up the null protocol
+  nproto.set_collectset(cservers);
+  
+  if ((ausersaction == NULL) && (aauthenaction == NULL)){
+    userdbfile = filename_cat(gsdl_gsdlhome, "etc", "users.db");
+    udb = new userdbclass(userdbfile);
+    keydbfile = filename_cat(gsdl_gsdlhome, "etc", "key.db");
+    kdb = new keydbclass(keydbfile);
+  }
+  // add the protocol to the receptionist
+  if (atStartup) recpt.add_protocol (&nproto);
+  
+  // the list of actions.
+  if (astatusaction == NULL) {
+    astatusaction = new statusaction();
+    astatusaction->set_receptionist (&recpt);
+    recpt.add_action (astatusaction);
+  }
+  
+  if (apageaction == NULL) {
+    apageaction = new pageaction();
+    apageaction->set_receptionist (&recpt);
+    recpt.add_action (apageaction);
+  }
+  
+  if (apingaction == NULL) {
+    apingaction = new pingaction();
+    recpt.add_action (apingaction);
+  }
+  
+  if (atipaction == NULL) {
+    atipaction = new tipaction();
+    recpt.add_action (atipaction);
+  }
+  
+  if (aqueryaction == NULL) {
+    aqueryaction = new queryaction();
+    aqueryaction->set_receptionist (&recpt);
+    recpt.add_action (aqueryaction);
+  }
+  
+  if (adocumentaction == NULL) {
+    adocumentaction = new documentaction();
+    adocumentaction->set_receptionist (&recpt);
+    recpt.add_action (adocumentaction);
+  }
+  
+  if (ausersaction == NULL) {
+    ausersaction = new usersaction();
+    ausersaction->set_userdb(udb);
+    recpt.add_action (ausersaction);
+  }
+
+  if (anextlinkaction == NULL) {
+    anextlinkaction = new extlinkaction();
+    recpt.add_action (anextlinkaction);
+  }
+
+  if (acollectoraction == NULL) {
+    acollectoraction = new collectoraction();
+    acollectoraction->set_receptionist (&recpt);
+    recpt.add_action (acollectoraction);
+  }
+  
+  if (aauthenaction == NULL) {
+    aauthenaction = new authenaction();
+    aauthenaction->set_userdb(udb);
+    aauthenaction->set_keydb(kdb);
+    aauthenaction->set_receptionist(&recpt);
+    recpt.add_action (aauthenaction);
+  }
+
+  if (aphindaction == NULL) {
+    aphindaction = new phindaction();
+    recpt.add_action (aphindaction);
+  }
+  
+  if (aconfigaction == NULL) {
+    aconfigaction = new configaction();
+    aconfigaction->set_receptionist(&recpt);
+    recpt.add_action (aconfigaction);
+  }
+  
+  if (adynamicclassifieraction == NULL) {
+    adynamicclassifieraction = new dynamicclassifieraction();
+    adynamicclassifieraction->set_receptionist(&recpt);
+    recpt.add_action (adynamicclassifieraction);
+  }
+
+
+  // list of browsers
+  if (avlistbrowserclass == NULL) {
+    avlistbrowserclass = new vlistbrowserclass();
+    recpt.add_browser (avlistbrowserclass);
+    recpt.setdefaultbrowser ("VList");
+  }
+
+  if (ahlistbrowserclass == NULL) {
+    ahlistbrowserclass = new hlistbrowserclass();
+    recpt.add_browser (ahlistbrowserclass);
+  }
+
+  if (adatelistbrowserclass == NULL) {
+    adatelistbrowserclass = new datelistbrowserclass();
+    recpt.add_browser (adatelistbrowserclass);
+  }
+
+  if (ainvbrowserclass == NULL) {
+    ainvbrowserclass = new invbrowserclass();
+    recpt.add_browser (ainvbrowserclass);
+  }
+
+  if (apagedbrowserclass == NULL) {
+    apagedbrowserclass = new pagedbrowserclass();
+    recpt.add_browser (apagedbrowserclass);
+  }
+
+  if (ahtmlbrowserclass == NULL) {
+    ahtmlbrowserclass = new htmlbrowserclass();
+    recpt.add_browser (ahtmlbrowserclass);
+  }
+
+  if (aphindbrowserclass == NULL) {
+    aphindbrowserclass = new phindbrowserclass();;
+    recpt.add_browser (aphindbrowserclass);
+  }
+  
+  // set defaults
+  recpt.configure ("gsdlhome", gsdl_gsdlhome);
+  recpt.configure ("collecthome", gsdl_collecthome);
+  recpt.configure ("gdbmhome", gsdl_dbhome);
+  recpt.configure ("collection", collection);
+
+  int maxrequests = 1;
+
+  // configure collections (and receptionist) with collectinfo stuff
+  // different from the default
+  colinfo_tmap::const_iterator this_info = translated_collectinfo.begin();
+  colinfo_tmap::const_iterator end_info = translated_collectinfo.end();
+
+  while (this_info != end_info) {
+    text_tarray tmpconf;
+    tmpconf.push_back ((*this_info).first);
+    tmpconf.push_back ((*this_info).second.gsdl_gsdlhome);
+    tmpconf.push_back ((*this_info).second.gsdl_collecthome);
+    tmpconf.push_back ((*this_info).second.gsdl_dbhome);
+    recpt.configure ("collectinfo", tmpconf);
+    ++this_info;
+  }
+
+  // read in config files of each dbhome (in no particular order)
+  // those read in last will override those read earlier
+  // collections being used together in this way should be 
+  // careful not to have main.cfg files that might 
+  // interfere with each other.
+  text_tset::const_iterator thome = dbhomes.begin();
+  text_tset::const_iterator ehome = dbhomes.end();
+  while (thome != ehome) {
+    if (!main_cfg_read (recpt, *thome, collection)) {
+      // couldn't find the main configuration file
+      page_errormaincfg (*thome, collection);
+      return 0;
+    }
+    ++thome;
+  }
+
+  // w32server relies on gwcgi being set to "gsdl"
+  recpt.configure ("gwcgi", "gsdl");
+	
+  // initialise the library software
+  if (!recpt.init(cerr)) {
+    // an error occurred during the initialisation
+    page_errorinit(gsdl_gsdlhome);
+    return 0;
+  }
+
+  // get memory information
+  MEMORYSTATUS memstatus;
+  memstatus.dwLength = sizeof(MEMORYSTATUS);
+  GlobalMemoryStatus(&memstatus);
+  baseavailvirtual = memstatus.dwAvailVirtual; // save for later comparison
+
+  return 1;
+}
+
+
+static void rememberpref (const text_t &tailstr) {
+  gsdl_enterlib = tailstr;
+}
+
+
+static void send_file_from_disk(text_t filename,
+                                RequestInfoT *RInfo,
+                                RequestFieldsT *RFields) {
+  
+  // select appropriate mime type from file extension
+  text_t ext;
+  text_t::const_iterator end = filename.end();
+  text_t::const_iterator it = filename.begin();
+  text_t::const_iterator lastdot = end;
+  while ((it = findchar(it, end, '.')) != end) {
+    lastdot = it;
+    ++it;
+  }
+  if (lastdot < end) ext = substr(lastdot+1, end);
+
+  text_t mime = "unknown";
+  int len = ext.size();
+  if (len == 2) {
+    if ((ext[0] == 'p' || ext[0] == 'P') &&
+	(ext[1] == 's' || ext[1] == 'S'))
+      mime = "application/postscript";
+  }  else if (len == 3) {
+    if ((ext[0] == 'g' || ext[0] == 'G') &&
+	(ext[1] == 'i' || ext[1] == 'I') &&
+	(ext[2] == 'f' || ext[2] == 'F')) {
+      mime = "image/gif";
+    } else if ((ext[0] == 'j' || ext[0] == 'J') &&
+	       (ext[1] == 'p' || ext[1] == 'P') &&
+	       (ext[2] == 'g' || ext[2] == 'G')) {
+      mime = "image/jpeg";
+    } else if ((ext[0] == 'h' || ext[0] == 'H') &&
+	       (ext[1] == 't' || ext[1] == 'T') &&
+	       (ext[2] == 'm' || ext[2] == 'M')) {
+      mime = "text/html";
+    } else if ((ext[0] == 'p' || ext[0] == 'P') &&
+	       (ext[1] == 'd' || ext[1] == 'D') &&
+	       (ext[2] == 'f' || ext[2] == 'F')) {
+      mime = "application/pdf";
+    } else if ((ext[0] == 'p' || ext[0] == 'P') &&
+	       (ext[1] == 'n' || ext[1] == 'N') &&
+	       (ext[2] == 'g' || ext[2] == 'G')) {
+      mime = "image/png";
+    } else if ((ext[0] == 'd' || ext[0] == 'D') &&
+	       (ext[1] == 'o' || ext[1] == 'O') &&
+	       (ext[2] == 'c' || ext[2] == 'C')) {
+      mime = "application/msword";
+    } else if ((ext[0] == 'r' || ext[0] == 'R') &&
+	       (ext[1] == 't' || ext[1] == 'T') &&
+	       (ext[2] == 'f' || ext[2] == 'F')) {
+      mime = "application/rtf";
+    } else if ((ext[0] == 'x' || ext[0] == 'X') &&
+	       (ext[1] == 'l' || ext[1] == 'L') &&
+	       (ext[2] == 's' || ext[2] == 'S')) {
+      mime = "application/vnd.ms-excel";
+    } else if ((ext[0] == 'p' || ext[0] == 'P') &&
+	       (ext[1] == 'p' || ext[1] == 'P') &&
+	       (ext[2] == 'T' || ext[2] == 'T')) {
+      mime = "application/vnd.ms-powerpoint";
+    } else if ((ext[0] == 'm' || ext[0] == 'M') &&
+	       (ext[1] == 'p' || ext[1] == 'P') &&
+	       (ext[2] == '3')) {
+      mime = "audio/mpeg";
+    }
+  } else if (len == 4) {
+    if ((ext[0] == 'j' || ext[0] == 'J') &&
+	(ext[1] == 'p' || ext[1] == 'P') &&
+	(ext[2] == 'e' || ext[2] == 'E') &&
+	(ext[3] == 'g' || ext[3] == 'G')) {
+      mime = "image/jpeg";
+    } else if ((ext[0] == 'h' || ext[0] == 'H') &&
+	       (ext[1] == 't' || ext[1] == 'T') &&
+	       (ext[2] == 'm' || ext[2] == 'M') &&
+	       (ext[3] == 'l' || ext[3] == 'L')) {
+      mime = "text/html";
+    }
+  }
+	
+  // try to open the file
+  filename = filename_cat (current_gsdlhome, filename);
+  /* Feb 2002 - handle files with spaces in their name. */
+  text_t::iterator space_start;
+  space_start=findword(filename.begin(),filename.end(),"%20");
+  while (space_start != filename.end()) {
+    // we found a space...
+    text_t::iterator after_space=space_start+3;
+    text_t new_filename=substr(filename.begin(), space_start);
+    new_filename += " ";
+    new_filename += substr(after_space,filename.end());
+    filename=new_filename;
+    space_start=findword(filename.begin(),filename.end(),"%20");
+  }
+
+  char *filenamec = filename.getcstr();
+  TranslateEscapeString(filenamec);  // Resolve any %xx values
+  FILE *thefile = fopen(filenamec, "rb");
+  delete []filenamec;
+  if (thefile == NULL) {
+    log_message("file not found\n");
+    send_retrieve_error(404, "File not found",
+			"Could not find the local file requested", RInfo);
+    return;
+  }
+  int nr;
+  char buffer[2048];
+  // send back required information
+  char *mimec = mime.getcstr();
+  if (send_header(mimec, RInfo) >= 0) {
+    if (RFields->MethodStr != "HEAD") {
+      for (;;) {
+        nr = fread(buffer, 1, 2048, thefile);
+        if (nr <= 0) break;
+        if (SendData(RInfo->ClientSocket,
+                     (BYTE *)buffer, nr,
+		     RInfo->ThreadNum) < 0) break;        
+      }
+    }
+  }
+  delete []mimec;
+  fclose(thefile);
+}
+
+static void handle_library_request(const text_t &argstr, RequestInfoT *RInfo,
+				   RequestFieldsT *RequestFields) {
+
+  // parse the cgi arguments and produce the resulting page if there
+  // have been no errors so far
+  cgiargsclass args;
+  fileupload_tmap fileuploads;
+  text_tmap empty; // don't use this (it's for fastcgi on unix)
+  if (!recpt.parse_cgi_args (argstr, fileuploads, args, cerr, empty)) {
+    page_errorparseargs(gsdl_gsdlhome);
+    return;
+  } 
+
+  colinfo_tmap::const_iterator it = translated_collectinfo.find (args["c"]);
+  if (it != translated_collectinfo.end()) {
+    current_gsdlhome = (*it).second.gsdl_gsdlhome;
+  } else {
+    current_gsdlhome = gsdl_gsdlhome;
+  }
+
+  // produce cgi header
+  response_t response;
+  text_t response_data;
+
+  recpt.get_cgihead_info (args, response, response_data, cerr, empty);
+
+  if (response == location) {
+    // location response
+    response_data = "@" + recpt.expandmacros (response_data, args, cerr);
+    char *response_data_c = response_data.getcstr();
+    send_header(response_data_c, RInfo);
+    delete []response_data_c;
+    return;
+  } else if (response == content) {
+    // content response
+    char *response_data_c = response_data.getcstr();
+    if (send_header(response_data_c, RInfo) < 0) {
+      delete []response_data_c;
+      return;
+    }
+    delete []response_data_c;
+  }
+  else if (response == undecided_location) {
+    // We know this is a relocation request but at the moment we don't know exactly where to
+    // Just output the start of the header and wait until later to output the target location
+    // Used for the "I'm feeling lucky" functionality
+    cout << "HTTP/1.0 302 Relocation\r\n";
+    cout << "Server: GSDL\r\n";
+    cout << "Content-type: text/html \r\n";
+  }
+  else {
+    // unknown response
+    cerr << "Error: get_cgihead_info returned an unknown response type.\n";
+    return;
+  }
+
+  textstream.tsbreset();
+  textstream.setrequestinfo (RInfo);
+  if (!recpt.produce_content (args, cout, cerr)) {
+    page_errorcgipage(gsdl_gsdlhome);
+    return;
+  } 
+  recpt.log_cgi_args (args, cerr, empty);
+
+  cout << flush;
+  cerr << flush;
+  
+  ++libaccessnum;
+}
+
+static void handle_server_request(text_t &tailstr,
+				  RequestInfoT *RequestInfo,
+				  RequestFieldsT *RequestFields) {
+  
+  text_t argstr;
+
+  // do any url adjustments necessary
+  if (tailstr.empty() || tailstr == "/") {
+    tailstr = "/gsdl";
+  }
+
+  text_t::const_iterator begin = tailstr.begin();
+  text_t::const_iterator end = tailstr.end();
+  
+  // test to see if this is a library request or a local
+  // file request
+  if ((tailstr == "/gsdl") ||
+      ((tailstr.size() > 5) && (substr(begin, begin+6) == "/gsdl?"))) {
+
+    // library request
+    
+    // argstr is the bit after the '?'
+    if (tailstr != "/gsdl") {
+      argstr = substr(begin+6, end);
+    }
+
+    // log the difference in access times
+    DWORD thislibaccesstime = GetTickCount();
+    if (gsdl_keep_log || gsdl_show_console) {
+      char logstr[256];
+      sprintf(logstr, "DELTA LIB ACCESS TIME: %i\n", (int)(thislibaccesstime - lastlibaccesstime));
+      log_message (logstr);
+    }
+    lastlibaccesstime = thislibaccesstime;
+    
+    // log this request
+    if (gsdl_keep_log || gsdl_show_console) {
+      text_t logstr = "LOCAL LIB: " + tailstr + "\n";
+      char *logstrc = logstr.getcstr();
+      log_message (logstrc);
+      delete []logstrc;
+    }
+    
+    handle_library_request (argstr, RequestInfo, RequestFields);
+    
+    // remember the preferences
+    // rememberpref (tailstr);
+    
+    // log memory information
+    if (gsdl_keep_log || gsdl_show_console) {
+      MEMORYSTATUS memstatus;
+      memstatus.dwLength = sizeof(MEMORYSTATUS);
+      GlobalMemoryStatus(&memstatus);
+      char logstr[256];
+      sprintf (logstr, "BDELTA AVAIL VIRTUAL: %i K\n", 
+	       (int)((baseavailvirtual - memstatus.dwAvailVirtual)/1024));
+      log_message (logstr);
+    }
+    
+  } else {
+    // local file
+    if (gsdl_keep_log || gsdl_show_console) {
+      text_t logstr = "LOCAL FILE: " + tailstr + "\n";
+      char *logstrc = logstr.getcstr();
+      log_message (logstrc);
+      delete []logstrc;
+    }
+    send_file_from_disk (tailstr, RequestInfo, RequestFields);
+  }
+}
+
+int ExamineURIStr(text_t &URIStr, RequestInfoT *RequestInfo,
+		  RequestFieldsT *RequestFields)
+{
+  text_t protocol, machine, rest;
+  int port;
+
+  if (RequestFields->ContentLength > 0) {
+    // POST data
+    URIStr.push_back('?');
+    for (int i = 0; i < RequestFields->ContentLength; ++i) {
+      URIStr.push_back(RequestFields->Content[i]);
+    }
+  }
+  
+  if (parse_url(URIStr, protocol, machine, &port, rest)!=http_ok) {
+    // Alter local file request to address 'gsdl'
+    if (*(URIStr.begin()) != '/') URIStr = "http://gsdl/" + URIStr;
+    else URIStr = "http://gsdl" + URIStr;
+    parse_url(URIStr, protocol, machine, &port, rest);
+  }
+
+  if (machine == "gsdl") {
+    // a local file request
+    handle_server_request(rest, RequestInfo, RequestFields);
+    
+  } else {
+    send_retrieve_error(404, "File not found",
+			"Could not find the local file requested", RequestInfo);
+  }
+  
+  return 1;
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/cgiwrapper.h	(revision 16561)
@@ -0,0 +1,49 @@
+/**********************************************************************
+ *
+ * cgiwrapper.h -- windows local library cgiwrapper
+ * 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.
+ *
+ *********************************************************************/
+
+#ifndef CGIWRAPPER_H
+#define CGIWRAPPER_H
+
+#include "httpreq.h"
+#include "locate.h"
+#include "receptionist.h"
+
+// the number of times the library has been accessed
+extern int libaccessnum;
+
+int ExamineURIStr(text_t &URIStr,RequestInfoT *RequestInfo,
+		  RequestFieldsT *RequestFields);
+
+// reads any configure file that has been set up and
+// sets up the library
+// returns 1 if successful, 0 if unsuccessful
+int gsdl_init (bool atStartup = true);
+
+// returns 0 if the directories cant be found
+// and the user wants to quit (it returns 1
+// if everything is ok)
+int cstrcheckdir (char *cstrthedir);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/conftools.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/conftools.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/conftools.cpp	(revision 16561)
@@ -0,0 +1,55 @@
+/**********************************************************************
+ *
+ * conftools.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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include "conftools.h"
+
+int write_confline (ofstream &fileout, const text_t &key, const text_t value) {
+  if (key.empty() || value.empty()) return -1;
+  outconvertclass text_t2ascii;
+  fileout << text_t2ascii << key << "=" << value << "\n";
+  return 0;
+}
+
+int get_confline (ifstream &filein, text_t &key, text_t &value) {
+  if (filein.eof()) return -1;
+
+  key.clear();
+  value.clear();
+  char c;
+  filein.get(c);
+
+  int foundeq = 0;
+  while (!filein.eof() && c != '\n') {
+    if (!foundeq && c == '=') {foundeq = 1; filein.get(c);}
+
+    if (foundeq) value.push_back(c);
+    else key.push_back(c);
+    filein.get(c);
+  }
+  if (key.empty()) return -1;
+  return 0;
+} 
+
Index: /gsdl/trunk/gs2runtime/src/w32server/conftools.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/conftools.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/conftools.h	(revision 16561)
@@ -0,0 +1,40 @@
+/**********************************************************************
+ *
+ * conftools.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.
+ *
+ *********************************************************************/
+
+#ifndef CONFTOOLS_H
+#define CONFTOOLS_H
+
+#if defined (GSDL_USE_OBJECTSPACE)
+#include <ospace/std/fstream>
+#elif defined (GSDL_USE_IOS_H)
+#include <fstream.h>
+#else
+#include <fstream>
+#endif
+
+int write_confline (ofstream &fileout, const text_t &key, const text_t value);
+int get_confline (ifstream &filein, text_t &key, text_t &value);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/d_winsock.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/d_winsock.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/d_winsock.cpp	(revision 16561)
@@ -0,0 +1,669 @@
+/**********************************************************************
+ *
+ * d_winsock.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include <windows.h>
+#include "d_winsock.h"
+
+
+typedef SOCKET (PASCAL FAR *accept_Type)(SOCKET s, struct sockaddr FAR *addr, 
+					 int FAR *addrlen);
+typedef int (PASCAL FAR *bind_Type)(SOCKET s, const struct sockaddr FAR *addr, int namelen);
+typedef int (PASCAL FAR *closesocket_Type)(SOCKET s);
+typedef int (PASCAL FAR *connect_Type)(SOCKET s, const struct sockaddr FAR *name, 
+				       int namelen);
+typedef int (PASCAL FAR *ioctlsocket_Type)(SOCKET s, long cmd, u_long FAR *argp);
+typedef int (PASCAL FAR *getpeername_Type)(SOCKET s, struct sockaddr FAR *name,
+					   int FAR * namelen);
+typedef int (PASCAL FAR *getsockname_Type)(SOCKET s, struct sockaddr FAR *name,
+					   int FAR * namelen);
+typedef int (PASCAL FAR *getsockopt_Type)(SOCKET s, int level, int optname,
+					  char FAR * optval, int FAR *optlen);
+typedef u_long (PASCAL FAR *htonl_Type)(u_long hostlong);
+typedef u_short (PASCAL FAR *htons_Type)(u_short hostshort);
+typedef unsigned long (PASCAL FAR *inet_addr_Type)(const char FAR * cp);
+typedef char FAR * (PASCAL FAR *inet_ntoa_Type)(struct in_addr in);
+typedef int (PASCAL FAR *listen_Type)(SOCKET s, int backlog);
+typedef u_long (PASCAL FAR *ntohl_Type)(u_long netlong);
+typedef u_short (PASCAL FAR *ntohs_Type)(u_short netshort);
+typedef int (PASCAL FAR *recv_Type)(SOCKET s, char FAR * buf, int len, int flags);
+typedef int (PASCAL FAR *recvfrom_Type)(SOCKET s, char FAR * buf, int len, int flags,
+					struct sockaddr FAR *from, int FAR * fromlen);
+typedef int (PASCAL FAR *select_Type)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds,
+				      fd_set FAR *exceptfds, const struct timeval FAR *timeout);
+typedef int (PASCAL FAR *send_Type)(SOCKET s, const char FAR * buf, int len, int flags);
+typedef int (PASCAL FAR *sendto_Type)(SOCKET s, const char FAR * buf, int len, int flags,
+				      const struct sockaddr FAR *to, int tolen);
+typedef int (PASCAL FAR *setsockopt_Type)(SOCKET s, int level, int optname,
+					  const char FAR * optval, int optlen);
+typedef int (PASCAL FAR *shutdown_Type)(SOCKET s, int how);
+typedef SOCKET (PASCAL FAR *socket_Type)(int af, int type, int protocol);
+typedef struct hostent FAR * (PASCAL FAR *gethostbyaddr_Type)(const char FAR * addr,
+							      int len, int type);
+typedef struct hostent FAR * (PASCAL FAR *gethostbyname_Type)(const char FAR * name);
+typedef int (PASCAL FAR *gethostname_Type) (char FAR * name, int namelen);
+typedef struct servent FAR * (PASCAL FAR *getservbyport_Type)(int port, const char FAR * proto);
+typedef struct servent FAR * (PASCAL FAR *getservbyname_Type)(const char FAR * name,
+							      const char FAR * proto);
+typedef struct protoent FAR * (PASCAL FAR *getprotobynumber_Type)(int proto);
+typedef struct protoent FAR * (PASCAL FAR *getprotobyname_Type)(const char FAR * name);
+typedef int (PASCAL FAR *WSAStartup_Type)(WORD wVersionRequired, LPWSADATA lpWSAData);
+typedef int (PASCAL FAR *WSACleanup_Type)(void);
+typedef void (PASCAL FAR *WSASetLastError_Type)(int iError);
+typedef int (PASCAL FAR *WSAGetLastError_Type)(void);
+typedef BOOL (PASCAL FAR *WSAIsBlocking_Type)(void);
+typedef int (PASCAL FAR *WSAUnhookBlockingHook_Type)(void);
+typedef FARPROC (PASCAL FAR *WSASetBlockingHook_Type)(FARPROC lpBlockFunc);
+typedef int (PASCAL FAR *WSACancelBlockingCall_Type)(void);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetServByName_Type)(HWND hWnd, u_int wMsg,
+							const char FAR * name,
+							const char FAR * proto,
+							char FAR * buf, int buflen);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetServByPort_Type)(HWND hWnd, u_int wMsg, int port,
+							const char FAR * proto, char FAR * buf,
+							int buflen);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetProtoByName_Type)(HWND hWnd, u_int wMsg,
+							 const char FAR * name, char FAR * buf,
+							 int buflen);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetProtoByNumber_Type)(HWND hWnd, u_int wMsg,
+							   int number, char FAR * buf,
+							   int buflen);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetHostByName_Type)(HWND hWnd, u_int wMsg,
+							const char FAR * name, char FAR * buf,
+							int buflen);
+typedef HANDLE (PASCAL FAR *WSAAsyncGetHostByAddr_Type)(HWND hWnd, u_int wMsg,
+							const char FAR * addr, int len, int type,
+							char FAR * buf, int buflen);
+typedef int (PASCAL FAR *WSACancelAsyncRequest_Type)(HANDLE hAsyncTaskHandle);
+typedef int (PASCAL FAR *WSAAsyncSelect_Type)(SOCKET s, HWND hWnd, u_int wMsg,
+					      long lEvent);
+
+
+
+static accept_Type accept_Ptr = NULL;
+static bind_Type bind_Ptr = NULL;
+static closesocket_Type closesocket_Ptr = NULL;
+static connect_Type connect_Ptr = NULL;
+static ioctlsocket_Type ioctlsocket_Ptr = NULL;
+static getpeername_Type getpeername_Ptr = NULL;
+static getsockname_Type getsockname_Ptr = NULL;
+static getsockopt_Type getsockopt_Ptr = NULL;
+static htonl_Type htonl_Ptr = NULL;
+static htons_Type htons_Ptr = NULL;
+static inet_addr_Type inet_addr_Ptr = NULL;
+static inet_ntoa_Type inet_ntoa_Ptr = NULL;
+static listen_Type listen_Ptr = NULL;
+static ntohl_Type ntohl_Ptr = NULL;
+static ntohs_Type ntohs_Ptr = NULL;
+static recv_Type recv_Ptr = NULL;
+static recvfrom_Type recvfrom_Ptr = NULL;
+static select_Type select_Ptr = NULL;
+static send_Type send_Ptr = NULL;
+static sendto_Type sendto_Ptr = NULL;
+static setsockopt_Type setsockopt_Ptr = NULL;
+static shutdown_Type shutdown_Ptr = NULL;
+static socket_Type socket_Ptr = NULL;
+static gethostbyaddr_Type gethostbyaddr_Ptr = NULL;
+static gethostbyname_Type gethostbyname_Ptr = NULL;
+static gethostname_Type gethostname_Ptr = NULL;
+static getservbyport_Type getservbyport_Ptr = NULL;
+static getservbyname_Type getservbyname_Ptr = NULL;
+static getprotobynumber_Type getprotobynumber_Ptr = NULL;
+static getprotobyname_Type getprotobyname_Ptr = NULL;
+static WSAStartup_Type WSAStartup_Ptr = NULL;
+static WSACleanup_Type WSACleanup_Ptr = NULL;
+static WSASetLastError_Type WSASetLastError_Ptr = NULL;
+static WSAGetLastError_Type WSAGetLastError_Ptr = NULL;
+static WSAIsBlocking_Type WSAIsBlocking_Ptr = NULL;
+static WSAUnhookBlockingHook_Type WSAUnhookBlockingHook_Ptr = NULL;
+static WSASetBlockingHook_Type WSASetBlockingHook_Ptr = NULL;
+static WSACancelBlockingCall_Type WSACancelBlockingCall_Ptr = NULL;
+static WSAAsyncGetServByName_Type WSAAsyncGetServByName_Ptr = NULL;
+static WSAAsyncGetServByPort_Type WSAAsyncGetServByPort_Ptr = NULL;
+static WSAAsyncGetProtoByName_Type WSAAsyncGetProtoByName_Ptr = NULL;
+static WSAAsyncGetProtoByNumber_Type WSAAsyncGetProtoByNumber_Ptr = NULL;
+static WSAAsyncGetHostByName_Type WSAAsyncGetHostByName_Ptr = NULL;
+static WSAAsyncGetHostByAddr_Type WSAAsyncGetHostByAddr_Ptr = NULL;
+static WSACancelAsyncRequest_Type WSACancelAsyncRequest_Ptr = NULL;
+static WSAAsyncSelect_Type WSAAsyncSelect_Ptr = NULL;
+
+
+static HINSTANCE wsock32lib = NULL;
+static HINSTANCE winsocklib = NULL;
+
+
+
+// set all the pointers back to null
+static void ResetWSPtrs () {
+  accept_Ptr = NULL;
+  bind_Ptr = NULL;
+  closesocket_Ptr = NULL;
+  connect_Ptr = NULL;
+  ioctlsocket_Ptr = NULL;
+  getpeername_Ptr = NULL;
+  getsockname_Ptr = NULL;
+  getsockopt_Ptr = NULL;
+  htonl_Ptr = NULL;
+  htons_Ptr = NULL;
+  inet_addr_Ptr = NULL;
+  inet_ntoa_Ptr = NULL;
+  listen_Ptr = NULL;
+  ntohl_Ptr = NULL;
+  ntohs_Ptr = NULL;
+  recv_Ptr = NULL;
+  recvfrom_Ptr = NULL;
+  select_Ptr = NULL;
+  send_Ptr = NULL;
+  sendto_Ptr = NULL;
+  setsockopt_Ptr = NULL;
+  shutdown_Ptr = NULL;
+  socket_Ptr = NULL;
+  gethostbyaddr_Ptr = NULL;
+  gethostbyname_Ptr = NULL;
+  gethostname_Ptr = NULL;
+  getservbyport_Ptr = NULL;
+  getservbyname_Ptr = NULL;
+  getprotobynumber_Ptr = NULL;
+  getprotobyname_Ptr = NULL;
+  WSAStartup_Ptr = NULL;
+  WSACleanup_Ptr = NULL;
+  WSASetLastError_Ptr = NULL;
+  WSAGetLastError_Ptr = NULL;
+  WSAIsBlocking_Ptr = NULL;
+  WSAUnhookBlockingHook_Ptr = NULL;
+  WSASetBlockingHook_Ptr = NULL;
+  WSACancelBlockingCall_Ptr = NULL;
+  WSAAsyncGetServByName_Ptr = NULL;
+  WSAAsyncGetServByPort_Ptr = NULL;
+  WSAAsyncGetProtoByName_Ptr = NULL;
+  WSAAsyncGetProtoByNumber_Ptr = NULL;
+  WSAAsyncGetHostByName_Ptr = NULL;
+  WSAAsyncGetHostByAddr_Ptr = NULL;
+  WSACancelAsyncRequest_Ptr = NULL;
+  WSAAsyncSelect_Ptr = NULL;
+}
+
+// get the pointers to all the socket functions
+static void SetupWSPtrs (HINSTANCE socketlib) {
+  if (socketlib == NULL) return;
+
+  accept_Ptr = (accept_Type)GetProcAddress(socketlib,"accept");
+  bind_Ptr = (bind_Type)GetProcAddress(socketlib,"bind");
+  closesocket_Ptr = (closesocket_Type)GetProcAddress(socketlib,"closesocket");
+  connect_Ptr = (connect_Type)GetProcAddress(socketlib,"connect");
+  ioctlsocket_Ptr = (ioctlsocket_Type)GetProcAddress(socketlib,"ioctlsocket");
+  getpeername_Ptr = (getpeername_Type)GetProcAddress(socketlib,"getpeername");
+  getsockname_Ptr = (getsockname_Type)GetProcAddress(socketlib,"getsockname");
+  getsockopt_Ptr = (getsockopt_Type)GetProcAddress(socketlib,"getsockopt");
+  htonl_Ptr = (htonl_Type)GetProcAddress(socketlib,"htonl");
+  htons_Ptr = (htons_Type)GetProcAddress(socketlib,"htons");
+  inet_addr_Ptr = (inet_addr_Type)GetProcAddress(socketlib,"inet_addr");
+  inet_ntoa_Ptr = (inet_ntoa_Type)GetProcAddress(socketlib,"inet_ntoa");
+  listen_Ptr = (listen_Type)GetProcAddress(socketlib,"listen");
+  ntohl_Ptr = (ntohl_Type)GetProcAddress(socketlib,"ntohl");
+  ntohs_Ptr = (ntohs_Type)GetProcAddress(socketlib,"ntohs");
+  recv_Ptr = (recv_Type)GetProcAddress(socketlib,"recv");
+  recvfrom_Ptr = (recvfrom_Type)GetProcAddress(socketlib,"recvfrom");
+  select_Ptr = (select_Type)GetProcAddress(socketlib,"select");
+  send_Ptr = (send_Type)GetProcAddress(socketlib,"send");
+  sendto_Ptr = (sendto_Type)GetProcAddress(socketlib,"sendto");
+  setsockopt_Ptr = (setsockopt_Type)GetProcAddress(socketlib,"setsockopt");
+  shutdown_Ptr = (shutdown_Type)GetProcAddress(socketlib,"shutdown");
+  socket_Ptr = (socket_Type)GetProcAddress(socketlib,"socket");
+  gethostbyaddr_Ptr = (gethostbyaddr_Type)GetProcAddress(socketlib,"gethostbyaddr");
+  gethostbyname_Ptr = (gethostbyname_Type)GetProcAddress(socketlib,"gethostbyname");
+  gethostname_Ptr = (gethostname_Type)GetProcAddress(socketlib,"gethostname");
+  getservbyport_Ptr = (getservbyport_Type)GetProcAddress(socketlib,"getservbyport");
+  getservbyname_Ptr = (getservbyname_Type)GetProcAddress(socketlib,"getservbyname");
+  getprotobynumber_Ptr = (getprotobynumber_Type)GetProcAddress(socketlib,"getprotobynumber");
+  getprotobyname_Ptr = (getprotobyname_Type)GetProcAddress(socketlib,"getprotobyname");
+  WSAStartup_Ptr = (WSAStartup_Type)GetProcAddress(socketlib,"WSAStartup");
+  WSACleanup_Ptr = (WSACleanup_Type)GetProcAddress(socketlib,"WSACleanup");
+  WSASetLastError_Ptr = (WSASetLastError_Type)GetProcAddress(socketlib,"WSASetLastError");
+  WSAGetLastError_Ptr = (WSAGetLastError_Type)GetProcAddress(socketlib,"WSAGetLastError");
+  WSAIsBlocking_Ptr = (WSAIsBlocking_Type)GetProcAddress(socketlib,"WSAIsBlocking");
+  WSAUnhookBlockingHook_Ptr = 
+    (WSAUnhookBlockingHook_Type)GetProcAddress(socketlib,"WSAUnhookBlockingHook");
+  WSASetBlockingHook_Ptr = 
+    (WSASetBlockingHook_Type)GetProcAddress(socketlib,"WSASetBlockingHook");
+  WSACancelBlockingCall_Ptr = 
+    (WSACancelBlockingCall_Type)GetProcAddress(socketlib,"WSACancelBlockingCall");
+  WSAAsyncGetServByName_Ptr = 
+    (WSAAsyncGetServByName_Type)GetProcAddress(socketlib,"WSAAsyncGetServByName");
+  WSAAsyncGetServByPort_Ptr = 
+    (WSAAsyncGetServByPort_Type)GetProcAddress(socketlib,"WSAAsyncGetServByPort");
+  WSAAsyncGetProtoByName_Ptr = 
+    (WSAAsyncGetProtoByName_Type)GetProcAddress(socketlib,"WSAAsyncGetProtoByName");
+  WSAAsyncGetProtoByNumber_Ptr = 
+    (WSAAsyncGetProtoByNumber_Type)GetProcAddress(socketlib,"WSAAsyncGetProtoByNumber");
+  WSAAsyncGetHostByName_Ptr = 
+    (WSAAsyncGetHostByName_Type)GetProcAddress(socketlib,"WSAAsyncGetHostByName");
+  WSAAsyncGetHostByAddr_Ptr = 
+    (WSAAsyncGetHostByAddr_Type)GetProcAddress(socketlib,"WSAAsyncGetHostByAddr");
+  WSACancelAsyncRequest_Ptr = 
+    (WSACancelAsyncRequest_Type)GetProcAddress(socketlib,"WSACancelAsyncRequest");
+  WSAAsyncSelect_Ptr = (WSAAsyncSelect_Type)GetProcAddress(socketlib,"WSAAsyncSelect");
+}
+
+
+
+// expects path to be NULL, a empty string, or a path which ends in a slash
+// returns D_NOERROR on success,
+// D_ERR_UNEXPECTED or D_ERR_LOADFAILED on failure
+int d_LoadWinsock (char *path) {
+  char dllname[2048]; // HARD LIMIT!!!!
+  OSVERSIONINFO osver;
+
+  if ((wsock32lib != NULL) || (winsocklib != NULL)) return D_ERR_UNEXPECTED;
+
+  // set the error mode so that no nasty dialog boxes come
+  // up when things aren't installed
+  int olderrormode = SetErrorMode(SEM_NOOPENFILEERRORBOX|SEM_FAILCRITICALERRORS);
+
+  // get the operating system version information
+  memset(&osver, 0, sizeof(OSVERSIONINFO));
+  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  if (!GetVersionEx(&osver)) {
+    SetErrorMode(olderrormode);
+    return D_ERR_UNEXPECTED;
+  }
+
+  // see if we should load winsock before wsock32
+  if ((osver.dwPlatformId == VER_PLATFORM_WIN32s) && (path != NULL)) {
+    strcpy (dllname, path);
+    strcat (dllname, "winsock.dll");
+
+    // attempt to load the library
+    winsocklib = LoadLibrary (dllname);
+    if (winsocklib == NULL) {
+      SetErrorMode(olderrormode);
+      return D_ERR_LOADFAILED;
+    }
+  }
+
+  // attempt to load wsock32
+  int err = D_NOERROR;
+  dllname[0] = '\0';
+  if ((osver.dwPlatformId != VER_PLATFORM_WIN32s) && (path != NULL)) {
+    strcpy (dllname, path);
+  }
+  strcat (dllname, "wsock32.dll");
+  wsock32lib = LoadLibrary (dllname);
+  if (wsock32lib == NULL) err = D_ERR_LOADFAILED;
+  else SetupWSPtrs (wsock32lib);
+
+
+  // perform a few tests
+
+  // first try starting-up (and check the szDescription field)
+  if (err == D_NOERROR) {
+    WSADATA Data;
+    memset(&Data, 0, sizeof(WSADATA));
+    if ((d_WSAStartup(257, &Data) != 0) ||
+	(Data.szDescription == NULL) || 
+	(Data.szDescription[0] == '\0'))
+      err = D_ERR_LOADFAILED;
+  }
+
+  // next try setting and getting an error
+  if (err == D_NOERROR) {
+    d_WSASetLastError (WSAENOTSOCK);
+    if (d_WSAGetLastError () != WSAENOTSOCK) err = D_ERR_LOADFAILED;
+    d_WSASetLastError (0);
+  }
+
+  // last try cleaning up (if an error occurs here we might
+  // not be able to unload anything)
+  if ((err == D_NOERROR) && (d_WSACleanup() == SOCKET_ERROR))
+    err = D_ERR_LOADFAILED;
+
+  // if an error occurred then we need to unload everything
+  if (err != D_NOERROR) d_UnloadWinsock ();
+
+  SetErrorMode(olderrormode);
+  return err;
+}
+
+// returns D_NOERROR on success,
+// D_ERR_FREELIBFAILED on failure
+int d_UnloadWinsock () {
+  int err = D_NOERROR;
+
+  // unload wsock32
+  if ((wsock32lib != NULL) && !FreeLibrary (wsock32lib)) {
+    err = D_ERR_FREELIBFAILED;
+  }
+  wsock32lib = NULL;
+
+  // unload winsock
+  if ((winsocklib != NULL) && !FreeLibrary (winsocklib)) {
+    err = D_ERR_FREELIBFAILED;
+  }
+  winsocklib = NULL;
+  
+  // reset all the function pointers
+  ResetWSPtrs ();
+
+  return err;
+}
+
+
+
+
+// socket functions, these just call the dynamically loaded version
+
+SOCKET d_accept (SOCKET s, struct sockaddr *addr, int *addrlen) {
+  if (accept_Ptr != NULL) 
+    return (*accept_Ptr)(s, addr, addrlen);
+  return INVALID_SOCKET; // error winsock not loaded
+}
+
+int d_bind (SOCKET s, const struct sockaddr *addr, int namelen) {
+  if (bind_Ptr != NULL) 
+    return (*bind_Ptr)(s, addr, namelen);
+  return WSANOTINITIALISED; // error winsock not loaded
+}
+
+int d_closesocket (SOCKET s) {
+  if (closesocket_Ptr != NULL) 
+    return (*closesocket_Ptr)(s);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_connect (SOCKET s, const struct sockaddr *name, int namelen) {
+  if (connect_Ptr != NULL) 
+    return (*connect_Ptr)(s, name, namelen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_ioctlsocket (SOCKET s, long cmd, u_long *argp) {
+  if (ioctlsocket_Ptr != NULL) 
+    return (*ioctlsocket_Ptr)(s, cmd, argp);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_getpeername (SOCKET s, struct sockaddr *name, int *namelen) {
+  if (getpeername_Ptr != NULL) 
+    return (*getpeername_Ptr)(s, name, namelen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_getsockname (SOCKET s, struct sockaddr *name, int *namelen) {
+  if (getsockname_Ptr != NULL) 
+    return (*getsockname_Ptr)(s, name, namelen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_getsockopt (SOCKET s, int level, int optname, char *optval, int *optlen) {
+  if (getsockopt_Ptr != NULL) 
+    return (*getsockopt_Ptr)(s, level, optname, optval, optlen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+u_long d_htonl (u_long hostlong) {
+  if (htonl_Ptr != NULL) 
+    return (*htonl_Ptr)(hostlong);
+  return hostlong; // error winsock not loaded
+}
+
+u_short d_htons (u_short hostshort) {
+  if (htons_Ptr != NULL) 
+    return (*htons_Ptr)(hostshort);
+  return hostshort; // error winsock not loaded
+}
+
+unsigned long d_inet_addr (const char *cp) {
+  if (inet_addr_Ptr != NULL) 
+    return (*inet_addr_Ptr)(cp);
+  return INADDR_NONE; // error winsock not loaded
+}
+
+char *d_inet_ntoa (struct in_addr in) {
+  if (inet_ntoa_Ptr != NULL) 
+    return (*inet_ntoa_Ptr)(in);
+  return NULL; // error winsock not loaded
+}
+
+int d_listen (SOCKET s, int backlog) {
+  if (listen_Ptr != NULL) 
+    return (*listen_Ptr)(s, backlog);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+u_long d_ntohl (u_long netlong) {
+  if (ntohl_Ptr != NULL) 
+    return (*ntohl_Ptr)(netlong);
+  return netlong; // error winsock not loaded
+}
+
+u_short d_ntohs (u_short netshort) {
+  if (ntohs_Ptr != NULL) 
+    return (*ntohs_Ptr)(netshort);
+  return netshort; // error winsock not loaded
+}
+
+int d_recv (SOCKET s, char *buf, int len, int flags) {
+  if (recv_Ptr != NULL) 
+    return (*recv_Ptr)(s, buf, len, flags);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_recvfrom (SOCKET s, char *buf, int len, int flags,
+		struct sockaddr *from, int *fromlen) {
+  if (recvfrom_Ptr != NULL) 
+    return (*recvfrom_Ptr)(s, buf, len, flags, from, fromlen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_select (int nfds, fd_set *readfds, fd_set *writefds,
+	      fd_set *exceptfds, const struct timeval *timeout) {
+  if (select_Ptr != NULL) 
+    return (*select_Ptr)(nfds, readfds, writefds, exceptfds, timeout);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_send (SOCKET s, const char *buf, int len, int flags) {
+  if (send_Ptr != NULL) 
+    return (*send_Ptr)(s, buf, len, flags);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_sendto (SOCKET s, const char *buf, int len, int flags,
+	      const struct sockaddr *to, int tolen) {
+  if (sendto_Ptr != NULL) 
+    return (*sendto_Ptr)(s, buf, len, flags, to, tolen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_setsockopt (SOCKET s, int level, int optname,
+		  const char *optval, int optlen) {
+  if (setsockopt_Ptr != NULL) 
+    return (*setsockopt_Ptr)(s, level, optname, optval, optlen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_shutdown (SOCKET s, int how) {
+  if (shutdown_Ptr != NULL) 
+    return (*shutdown_Ptr)(s, how);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+SOCKET d_socket (int af, int type, int protocol) {
+  if (socket_Ptr != NULL) 
+    return (*socket_Ptr)(af, type, protocol);
+  return INVALID_SOCKET; // error winsock not loaded
+}
+
+hostent *d_gethostbyaddr(const char *addr, int len, int type) {
+  if (gethostbyaddr_Ptr != NULL) 
+    return (*gethostbyaddr_Ptr)(addr, len, type);
+  return NULL; // error winsock not loaded
+}
+
+hostent *d_gethostbyname(const char *name) {
+  if (gethostbyname_Ptr != NULL) 
+    return (*gethostbyname_Ptr)(name);
+  return NULL; // error winsock not loaded
+}
+
+int d_gethostname (char *name, int namelen) {
+  if (gethostname_Ptr != NULL) 
+    return (*gethostname_Ptr)(name, namelen);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+servent *d_getservbyport(int port, const char *proto) {
+  if (getservbyport_Ptr != NULL) 
+    return (*getservbyport_Ptr)(port, proto);
+  return NULL; // error winsock not loaded
+}
+
+servent *d_getservbyname(const char *name, const char *proto) {
+  if (getservbyname_Ptr != NULL) 
+    return (*getservbyname_Ptr)(name, proto);
+  return NULL; // error winsock not loaded
+}
+
+protoent *d_getprotobynumber(int proto) {
+  if (getprotobynumber_Ptr != NULL) 
+    return (*getprotobynumber_Ptr)(proto);
+  return NULL; // error winsock not loaded
+}
+
+protoent *d_getprotobyname(const char *name) {
+  if (getprotobyname_Ptr != NULL) 
+    return (*getprotobyname_Ptr)(name);
+  return NULL; // error winsock not loaded
+}
+
+int d_WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData) {
+  if (WSAStartup_Ptr != NULL) 
+    return (*WSAStartup_Ptr)(wVersionRequired, lpWSAData);
+  return WSASYSNOTREADY; // error winsock not loaded
+}
+
+int d_WSACleanup(void) {
+  if (WSACleanup_Ptr != NULL) 
+    return (*WSACleanup_Ptr)();
+  return WSANOTINITIALISED; // error winsock not loaded
+}
+
+void d_WSASetLastError(int iError) {
+  if (WSASetLastError_Ptr != NULL) (*WSASetLastError_Ptr)(iError);
+}
+
+int d_WSAGetLastError(void) {
+  if (WSAGetLastError_Ptr != NULL) 
+    return (*WSAGetLastError_Ptr)();
+  return WSANOTINITIALISED; // error winsock not loaded
+}
+
+BOOL d_WSAIsBlocking(void) {
+  if (WSAIsBlocking_Ptr != NULL) 
+    return (*WSAIsBlocking_Ptr)();
+  return FALSE; // error winsock not loaded
+}
+
+int d_WSAUnhookBlockingHook(void) {
+  if (WSAUnhookBlockingHook_Ptr != NULL) 
+    return (*WSAUnhookBlockingHook_Ptr)();
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+FARPROC d_WSASetBlockingHook(FARPROC lpBlockFunc) {
+  if (WSASetBlockingHook_Ptr != NULL) 
+    return (*WSASetBlockingHook_Ptr)(lpBlockFunc);
+  return NULL; // error winsock not loaded
+}
+
+int d_WSACancelBlockingCall(void) {
+  if (WSACancelBlockingCall_Ptr != NULL) 
+    return (*WSACancelBlockingCall_Ptr)();
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
+			       const char *name,
+			       const char *proto,
+			       char *buf, int buflen) {
+  if (WSAAsyncGetServByName_Ptr != NULL) 
+    return (*WSAAsyncGetServByName_Ptr)(hWnd, wMsg, name, proto, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
+			       const char *proto, char *buf,
+			       int buflen) {
+  if (WSAAsyncGetServByPort_Ptr != NULL) 
+    return (*WSAAsyncGetServByPort_Ptr)(hWnd, wMsg, port, proto, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
+				const char *name, char *buf,
+				int buflen) {
+  if (WSAAsyncGetProtoByName_Ptr != NULL) 
+    return (*WSAAsyncGetProtoByName_Ptr)(hWnd, wMsg, name, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
+				  int number, char *buf,
+				  int buflen) {
+  if (WSAAsyncGetProtoByNumber_Ptr != NULL) 
+    return (*WSAAsyncGetProtoByNumber_Ptr)(hWnd, wMsg, number, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
+			       const char *name, char *buf,
+			       int buflen) {
+  if (WSAAsyncGetHostByName_Ptr != NULL) 
+    return (*WSAAsyncGetHostByName_Ptr)(hWnd, wMsg, name, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+HANDLE d_WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
+                                        const char *addr, int len, int type,
+                                        char *buf, int buflen) {
+  if (WSAAsyncGetHostByAddr_Ptr != NULL) 
+    return (*WSAAsyncGetHostByAddr_Ptr)(hWnd, wMsg, addr, len, type, buf, buflen);
+  return NULL; // error winsock not loaded
+}
+
+int d_WSACancelAsyncRequest(HANDLE hAsyncTaskHandle) {
+  if (WSACancelAsyncRequest_Ptr != NULL) 
+    return (*WSACancelAsyncRequest_Ptr)(hAsyncTaskHandle);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
+int d_WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
+                               long lEvent) {
+  if (WSAAsyncSelect_Ptr != NULL) 
+    return (*WSAAsyncSelect_Ptr)(s, hWnd, wMsg, lEvent);
+  return SOCKET_ERROR; // error winsock not loaded
+}
+
Index: /gsdl/trunk/gs2runtime/src/w32server/d_winsock.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/d_winsock.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/d_winsock.h	(revision 16561)
@@ -0,0 +1,120 @@
+/**********************************************************************
+ *
+ * d_winsock.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#ifndef D_WINSOCK_H
+#define D_WINSOCK_H
+
+/* get most definitions from the real winsock */
+#include <winsock.h>
+
+#define D_NOERROR           0
+#define D_ERR_UNEXPECTED    1
+#define D_ERR_LOADFAILED    2
+#define D_ERR_FREELIBFAILED 3
+
+// d_LoadWinsock must be called before an normal winsock
+// functions
+
+// expects path to be NULL, a empty string, or a path which ends in a slash
+// returns D_NOERROR on success,
+// D_ERR_UNEXPECTED or D_ERR_LOADFAILED on failure
+int d_LoadWinsock (char *path);
+
+// returns D_NOERROR on success,
+// D_ERR_FREELIBFAILED on failure
+int d_UnloadWinsock ();
+
+// dynamically loaded versions of the winsock functions
+
+SOCKET d_accept (SOCKET s, struct sockaddr *addr, int *addrlen);
+int d_bind (SOCKET s, const struct sockaddr *addr, int namelen);
+int d_closesocket (SOCKET s);
+int d_connect (SOCKET s, const struct sockaddr *name, int namelen);
+int d_ioctlsocket (SOCKET s, long cmd, u_long *argp);
+int d_getpeername (SOCKET s, struct sockaddr *name, int *namelen);
+int d_getsockname (SOCKET s, struct sockaddr *name, int *namelen);
+int d_getsockopt (SOCKET s, int level, int optname, char *optval, int *optlen);
+u_long d_htonl (u_long hostlong);
+u_short d_htons (u_short hostshort);
+unsigned long d_inet_addr (const char *cp);
+char *d_inet_ntoa (struct in_addr in);
+int d_listen (SOCKET s, int backlog);
+u_long d_ntohl (u_long netlong);
+u_short d_ntohs (u_short netshort);
+int d_recv (SOCKET s, char *buf, int len, int flags);
+int d_recvfrom (SOCKET s, char *buf, int len, int flags,
+		struct sockaddr *from, int *fromlen);
+int d_select (int nfds, fd_set *readfds, fd_set *writefds,
+	      fd_set *exceptfds, const struct timeval *timeout);
+int d_send (SOCKET s, const char *buf, int len, int flags);
+int d_sendto (SOCKET s, const char *buf, int len, int flags,
+	      const struct sockaddr *to, int tolen);
+int d_setsockopt (SOCKET s, int level, int optname,
+		  const char *optval, int optlen);
+int d_shutdown (SOCKET s, int how);
+SOCKET d_socket (int af, int type, int protocol);
+
+hostent *d_gethostbyaddr(const char *addr, int len, int type);
+hostent *d_gethostbyname(const char *name);
+int d_gethostname (char *name, int namelen);
+servent *d_getservbyport(int port, const char *proto);
+servent *d_getservbyname(const char *name, const char *proto);
+protoent *d_getprotobynumber(int proto);
+protoent *d_getprotobyname(const char *name);
+
+int d_WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
+int d_WSACleanup(void);
+void d_WSASetLastError(int iError);
+int d_WSAGetLastError(void);
+BOOL d_WSAIsBlocking(void);
+int d_WSAUnhookBlockingHook(void);
+FARPROC d_WSASetBlockingHook(FARPROC lpBlockFunc);
+int d_WSACancelBlockingCall(void);
+HANDLE d_WSAAsyncGetServByName(HWND hWnd, u_int wMsg,
+			       const char *name,
+			       const char *proto,
+			       char *buf, int buflen);
+HANDLE d_WSAAsyncGetServByPort(HWND hWnd, u_int wMsg, int port,
+			       const char *proto, char *buf,
+			       int buflen);
+HANDLE d_WSAAsyncGetProtoByName(HWND hWnd, u_int wMsg,
+				const char *name, char *buf,
+				int buflen);
+HANDLE d_WSAAsyncGetProtoByNumber(HWND hWnd, u_int wMsg,
+				  int number, char *buf,
+				  int buflen);
+HANDLE d_WSAAsyncGetHostByName(HWND hWnd, u_int wMsg,
+			       const char *name, char *buf,
+			       int buflen);
+HANDLE d_WSAAsyncGetHostByAddr(HWND hWnd, u_int wMsg,
+			       const char *addr, int len, int type,
+			       char *buf, int buflen);
+int d_WSACancelAsyncRequest(HANDLE hAsyncTaskHandle);
+int d_WSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
+		     long lEvent);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/fnord.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/fnord.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/fnord.cpp	(revision 16561)
@@ -0,0 +1,1191 @@
+/**********************************************************************
+ *
+ * fnord.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#pragma hdrstop
+#include "netio.h"
+#include "settings.h"
+#include "httpsrv.h"
+#include "resource.h"
+#include "locate.h"
+#include "cgiwrapper.h"
+#include "startbrowser.h"
+#include "d_winsock.h"
+
+#define MAINWINDOWWIDTH 400
+#define MAINWINDOWHEIGHT 200
+
+#define WASTEHEIGHT 44  // Error in ht due to caption and menu bar
+#define WASTEWIDTH 6
+#define SPACERWIDTH 5
+
+// size and position of the collection title
+#define COLTITLEWIDTH 351
+#define COLTITLEHEIGHT 35
+#define COLTITLEX ((MAINWINDOWWIDTH-COLTITLEWIDTH)/2)
+#define COLTITLEY 10
+
+// size and position of the version string
+#define VERSIONWIDTH MAINWINDOWWIDTH
+#define VERSIONHEIGHT 20
+#define VERSIONX 0
+#define VERSIONY (COLTITLEY+COLTITLEHEIGHT)
+
+// size and position of the Greenstone digital library logo
+#define LOGOWIDTH 135
+#define LOGOHEIGHT 55
+#define LOGOX (MAINWINDOWWIDTH-(LOGOWIDTH+SPACERWIDTH))
+#define LOGOY (MAINWINDOWHEIGHT-(LOGOHEIGHT+SPACERWIDTH))
+
+// size and position of the enter button
+#define ENTERBUTTONWIDTH 160
+#define ENTERBUTTONHEIGHT 30
+#define ENTERBUTTONX SPACERWIDTH
+#define ENTERBUTTONY ((MAINWINDOWHEIGHT-(LOGOHEIGHT+SPACERWIDTH))-(ENTERBUTTONHEIGHT+SPACERWIDTH))
+
+// size and position of the enter string
+#define ENTERSTRINGX (ENTERBUTTONX+ENTERBUTTONWIDTH+SPACERWIDTH)
+#define ENTERSTRINGY ENTERBUTTONY
+#define ENTERSTRINGWIDTH (MAINWINDOWWIDTH-(ENTERBUTTONWIDTH+SPACERWIDTH+SPACERWIDTH+SPACERWIDTH))
+#define ENTERSTRINGHEIGHT ENTERBUTTONHEIGHT
+
+// size and position of the info string
+#define INFOWIDTH MAINWINDOWWIDTH
+#define INFOHEIGHT (MAINWINDOWHEIGHT-INFOY)
+#define INFOX 0
+#define INFOY ENTERBUTTONY
+
+// size and position of restricted buttons
+#define ALTERNATIVEBUTTONWIDTH 160
+#define ALTERNATIVEBUTTONHEIGHT 30
+#define ALTERNATIVEBUTTONX SPACERWIDTH
+#define ALTERNATIVEBUTTONY LOGOY
+
+#define INFOBUTTONWIDTH 30
+
+// size and position of restart button
+#define RESTARTBUTTONWIDTH 160
+#define RESTARTBUTTONHEIGHT 30
+#define RESTARTBUTTONX 50
+#define RESTARTBUTTONY (MAINWINDOWHEIGHT-RESTARTBUTTONHEIGHT-15)
+
+const char versionstring[] = "version " GSDL_VERSION;
+
+const char strnothing[] = "";
+const char strinit[] = "Initialising...";
+const char strsb[] = "Starting browser...";
+const char strrestartlib[] = "Press the 'Restart Library' button to start a browser\n"
+"and enter library";
+const char strenterlib[] = "Press this button to begin using\n"
+"the library";
+const char *enterstring = NULL; // points to the current enter string
+const char *infostring = NULL; // points to the current info string
+
+
+// globals to do with networking and browsers
+int have_networking = 0;
+int netscapeneeded = 0;
+char startbrowserdir[1024] = ""; // HARD LIMIT!!!!!
+
+
+// stats to do with the last time the enter library button was pressed
+// these stats are needed to make suggestions about the proxy
+int enterlib_libaccessnum = -1; // -1 = NA
+DWORD enterlib_time = 0;
+
+
+enum { undefined_window, console_window, plain_window }
+window_state;
+
+HDC coltitledc = NULL;
+HBITMAP defcoltitlebitmap = NULL, coltitlebitmap=NULL;
+
+HDC logodc=NULL;
+HBITMAP deflogobitmap=NULL, logobitmap=NULL;
+
+HWND GoButton = NULL;
+HWND Enter = NULL;
+// HWND EnterRestricted = NULL;
+// HWND InfoRestricted = NULL;
+int init_type = 0;
+int init_done = 0;
+
+
+// Added an argument so you can specify not to do any of the windows orientated clean up - jmt12 18/11/2003
+void finish_up(bool init_only) {
+  // remember the current preferences
+  write_settings("");
+
+  if(init_only) {
+    return;
+  }
+   
+ // Shutdown the HTTP server
+  EndHTTPServer();
+  
+  // Shutdown the main server modules
+  CleanUpNetIO();
+  //    RemoveFnordFromTray();
+  
+  // Unload the wsock32 dll
+  d_UnloadWinsock();
+  
+  // Clean up graphics stuff from window
+  
+  if (defcoltitlebitmap != NULL)
+    SelectObject (coltitledc, defcoltitlebitmap);
+  if (coltitlebitmap != NULL)
+    DeleteObject (coltitlebitmap);
+  if (coltitledc != NULL)
+    DeleteDC (coltitledc);
+  
+  if (deflogobitmap != NULL)
+    SelectObject (logodc, deflogobitmap);
+  if (logobitmap != NULL)
+    DeleteObject (logobitmap);
+  if (logodc != NULL)
+    DeleteDC (logodc);
+  
+  // Shutdown the main window
+  PostQuitMessage(0);
+}
+
+
+int overlap(int left, int top, int right, int bottom,
+            RECT &r2)
+{
+  if (left <= r2.right && r2.left <= right &&
+      top <= r2.bottom && r2.top <= bottom)
+    return 1;
+  else
+    return 0;
+}
+
+
+void paint_window (HDC pdc, RECT &updateRect) {
+  // make sure the various DCs are set up
+  if (coltitledc == NULL) {
+    coltitledc = CreateCompatibleDC(pdc);
+    defcoltitlebitmap = (HBITMAP)SelectObject (coltitledc, coltitlebitmap);
+  }
+  
+  if (logodc == NULL) {
+    logodc = CreateCompatibleDC(pdc);
+    deflogobitmap = (HBITMAP)SelectObject (logodc, logobitmap);
+  }
+
+  // remove any remaining sign of initial buttons
+  if (init_done && (overlap (ENTERBUTTONX, ENTERBUTTONY, ENTERBUTTONX+ENTERBUTTONWIDTH,
+		ALTERNATIVEBUTTONY+ALTERNATIVEBUTTONHEIGHT, updateRect))) {
+    RECT initRect;
+    initRect.left = ENTERBUTTONX;
+    initRect.top = ENTERBUTTONY;
+    initRect.right = ENTERBUTTONX+ENTERBUTTONWIDTH;
+    initRect.bottom = ALTERNATIVEBUTTONY+ALTERNATIVEBUTTONHEIGHT;
+    FillRect(pdc, &initRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+  }
+
+  // update the collection title if needed
+  if (overlap(COLTITLEX, COLTITLEY,
+	      COLTITLEX+COLTITLEWIDTH, COLTITLEY+COLTITLEHEIGHT, 
+	      updateRect)) {
+    BitBlt (pdc, COLTITLEX, COLTITLEY, 
+	    COLTITLEX+COLTITLEWIDTH, COLTITLEY+COLTITLEHEIGHT,
+	    coltitledc, 0, 0, SRCCOPY);
+  }
+  
+  // update the version string if needed
+  if (overlap (VERSIONX, VERSIONY, 
+	       VERSIONX+VERSIONWIDTH, VERSIONY+VERSIONHEIGHT,
+	       updateRect)) {
+    RECT versionRect;
+    versionRect.left = VERSIONX;
+    versionRect.top = VERSIONY;
+    versionRect.right = VERSIONX+VERSIONWIDTH;
+    versionRect.bottom = VERSIONY+VERSIONHEIGHT;
+    DrawText(pdc, versionstring, -1, &versionRect, DT_CENTER);
+  }
+  
+  // decide what we want to draw
+  if (init_done && gsdl_show_console) {
+    // we want to draw a "console" window
+    if (window_state != console_window ||
+	overlap (text_rect.left, text_rect.top,
+		 text_rect.right, text_rect.bottom,
+		 updateRect)) {
+      refresh_console (pdc);
+    }
+    
+    window_state = console_window;
+    
+  } else {
+    // we want to draw a "plain" window
+    
+    // update the enter string if needed
+    if (overlap (ENTERSTRINGX, ENTERSTRINGY, ENTERSTRINGX+ENTERSTRINGWIDTH, 
+		 ENTERSTRINGY+ENTERSTRINGHEIGHT, updateRect)) {
+
+	RECT enterstringRect;
+	enterstringRect.left = ENTERSTRINGX;
+	enterstringRect.top = ENTERSTRINGY;
+	enterstringRect.right = ENTERSTRINGX+ENTERSTRINGWIDTH;
+	enterstringRect.bottom = ENTERSTRINGY+ENTERSTRINGHEIGHT;
+	FillRect(pdc, &enterstringRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+
+	if ((enterstring != NULL) && (strlen(enterstring) > 0)) {
+      
+	  int cury = ENTERSTRINGY;
+	  int startline = 0, here = 0;
+	  while (enterstring[here] != '\0') {
+	    if (enterstring[here] < ' ') {
+	      if (here - startline > 0) {
+		// output the text
+		TextOut (pdc, ENTERSTRINGX, cury, 
+			 &enterstring[startline], here-startline);
+		cury += line_spacing;
+	      }
+	      startline = here+1;
+	    }
+	    ++here;
+	  }
+	  // output any remaining text
+	  if (here - startline > 0) {
+	    TextOut (pdc, ENTERSTRINGX, cury, &enterstring[startline], here-startline);
+	  }
+	}
+    }
+    
+    window_state = plain_window;
+  }
+  
+  // update the info string if needed
+  if (init_done && !gsdl_show_console && 
+      ((infostring != NULL) && overlap (INFOX, INFOY, 
+					INFOX+INFOWIDTH, INFOY+INFOHEIGHT,
+					updateRect))) {
+    RECT infoRect;
+    infoRect.left = INFOX;
+    infoRect.top = INFOY;
+    infoRect.right = INFOX+INFOWIDTH;
+    infoRect.bottom = INFOY+INFOHEIGHT;
+    FillRect(pdc, &infoRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
+    DrawText(pdc, infostring, -1, &infoRect, DT_CENTER);
+  }
+
+  // update the logo (always)
+  BitBlt (pdc, LOGOX, LOGOY, LOGOX+LOGOWIDTH, LOGOY+LOGOHEIGHT,
+	  logodc, 0, 0, SRCCOPY);
+}
+
+
+void handle_painting (HWND Window) {
+  HDC pdc;  PAINTSTRUCT ps;
+  pdc = BeginPaint(Window, &ps);
+  
+  paint_window (pdc, ps.rcPaint);
+  
+  EndPaint(Window, &ps);
+}
+
+
+// returns 1 on success, 0 otherwise
+int enter_library (HWND Window) {
+  int res;
+  
+  if (infostring == strrestartlib) {
+
+    // the "restart library" button was pressed so we want to reinitialize
+    // greenstone, read in all the macrofiles again, etc.
+    gsdl_init(false);
+  }
+
+  // get the url and attempt to start a browser
+  if (have_networking) {
+    // There are now four address resolution methods:
+    //   0: Standard -- get local IP then resolve to a name
+    //   1: IP only -- as above, but use IP only -- don't resolve to a name
+    //   2: localhost -- always use localhost
+    //   3: 127.0.0.1 -- always use 127.0.0.1
+    char gsdl_address[100];
+
+    DWORD local_ip = GetLocalIP();
+    if (local_ip == INADDR_ANY || gsdl_address_resolution_method == 3) {
+      local_ip = 127 + (0 << 8) + (0 << 16) + (1 << 24);
+    }
+
+    if (gsdl_address_resolution_method == 1 || gsdl_address_resolution_method == 3) {
+      sprintf(gsdl_address, "%d.%d.%d.%d", local_ip & 0xFF, (local_ip >> 8) & 0xFF, (local_ip >> 16) & 0xFF, (local_ip >> 24) & 0xFF);
+    }
+    else if (gsdl_address_resolution_method == 2) {
+      sprintf(gsdl_address, "localhost");
+    }
+    else {
+      strcpy(gsdl_address, GetLocalName(NULL));
+    }
+
+    text_t url = "http://" + text_t(gsdl_address);
+
+    gsdl_url = url;
+    write_settings(gsdl_url);
+
+    if (gsdl_port_num != 80)
+      url += ":" + text_t(gsdl_port_num);
+
+    url += gsdl_enterlib;
+
+    // add a unique ending so it will always result in a request
+    text_t::const_iterator it = 
+      findchar (gsdl_enterlib.begin(), gsdl_enterlib.end(), '?');
+    if (it == gsdl_enterlib.end()) url.push_back ('?');
+    else url.push_back ('&');
+    int tcount = GetTickCount();
+    url += "uq=" + text_t(tcount);
+    
+    // remember the library access number for later proxy checking
+    enterlib_libaccessnum = libaccessnum;
+    
+    if (gsdl_start_browser) {
+
+      // do a quick check to make sure we're using netscape when
+      // we should be (it might have been just installed)
+      if (netscapeneeded) gsdl_check_browser_settings (netscapeneeded);
+    
+      char *cstr_url = url.getcstr();
+      if (strlen (startbrowserdir) <= 0) {
+	res = startbrowser (cstr_url, gsdl_browser_exe, NULL);
+	
+      } else res = startbrowser (cstr_url, gsdl_browser_exe, startbrowserdir);
+    
+      delete []cstr_url;
+
+    } else {
+      return 1;
+    }
+  }
+  
+  if (res == SB_NOERROR) {
+    // success !!
+    
+    // remember the time the browser was started for later proxy checking
+    if (have_networking) enterlib_time = GetTickCount();
+    
+    return 1;
+  }
+  
+  // no browser was started
+  enterlib_libaccessnum = -1;
+  
+  if ((res == SB_FAIL_BADFORMAT) || (res == SB_FAIL_NOTFOUND)) {
+    if (!netscapeneeded) { // any browser
+      MessageBox(Window,
+		 "Failed to start your chosen browser. It seems that your\n"
+		 "chosen browser has been removed or corrupted.\n"
+		 "Please check your browser choice by going to the\n"
+		 "'Settings...' item under the 'File' menu.",
+		 "Greenstone Digital Library Software", MB_OK);
+    } else { // netscape used
+      MessageBox(Window,
+		 "Failed to start Netscape. It seems that Netscape has\n"
+		 "been removed or corrupted. You need Netscape\n"
+		 "installed to use the Greenstone Digital Library\n"
+		 "software on non-networked machines.",
+		 "Greenstone Digital Library Software", MB_OK);
+    }
+    
+  } else {
+    MessageBox(Window,
+	       "Failed to start your browser. This may have been\n"
+	       "because there was not enough memory available. Shut\n"
+	       "down all other applications you have running, and\n"
+	       "try again.",
+	       "Greenstone Digital Library Software", MB_OK);
+  }
+  
+  return 0; // failed
+}
+
+
+void install_netscape (HWND Window) {
+  char installpath[256];
+  
+  // try to find the browser directory
+  char *gsdlhome = gsdl_gsdlhome.getcstr();
+  strcpy (installpath, gsdlhome); // use gsdlhome to find the CD-ROM drive...
+  delete []gsdlhome;
+  
+  // remove any slashes from the pathname
+  int len = strlen (installpath);
+  while ((len > 0) && ((installpath[len-1] == '\\') || (installpath[len-1] == '/'))) {
+    --len;
+  }
+  // remove '\gsdl'
+  len = len - 5;
+  installpath[len] = '\0';
+  
+  strcat (installpath, "\\netscape\\");
+  
+  // check this directory
+  if (!cstrcheckdir(installpath)) {
+    return;
+  }
+  
+  // get the operating system information
+  OSVERSIONINFO osver;
+  memset(&osver, 0, sizeof(OSVERSIONINFO));
+  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  GetVersionEx(&osver);
+
+  // get the install program for this operating system
+  if (osver.dwPlatformId == VER_PLATFORM_WIN32s) {
+    strcat (installpath, "n16e405.exe");
+  } else {
+    strcat (installpath, "n32e405.exe");
+  }
+  
+  // run the install program
+  int res = WinExec (installpath, SW_SHOW);
+  if (res == 0) {
+    // out of resources or memory
+    MessageBox (Window,
+		"Did not have enough resources or memory to run the\n"
+		"install program. Try shutting down all other programs\n"
+		"and trying again.",
+		"Greenstone Digital Library Software",
+		MB_OK|MB_APPLMODAL);
+    
+  } else if (res == ERROR_BAD_FORMAT) {
+    // executable image is corrupt -- ????
+    MessageBox (Window,
+		"The install program seems to be corrupt.",
+		"Greenstone Digital Library Software",
+		MB_OK|MB_APPLMODAL);
+    
+  } else if ((res == ERROR_FILE_NOT_FOUND) || (res == ERROR_PATH_NOT_FOUND)) {
+    // couldn't find the executable -- ????
+    MessageBox (Window,
+		"Could not find the install program. Try installing\n"
+		"Netscape from the Greenstone Digital Library\n"
+		"software's install program.",
+		"Greenstone Digital Library Software",
+		MB_OK|MB_APPLMODAL);
+  }
+}
+
+void open_help () {
+  char topdir[256], cmd[256];
+  
+  // try to find the browser directory
+  char *gsdlhome = gsdl_gsdlhome.getcstr();
+  strcpy (topdir, gsdlhome); // use gsdlhome to find the CD-ROM drive...
+  delete []gsdlhome;
+  strcpy (cmd, "notepad ");
+  
+  // remove any slashes from the pathname
+  int len = strlen (topdir);
+  while ((len > 0) && ((topdir[len-1] == '\\') || (topdir[len-1] == '/'))) {
+    --len;
+  }
+  // remove the '\gsdl'
+  len = len - 5;
+  topdir[len] = '\0';
+
+  strcat (topdir, "\\README.txt");
+  strcat (cmd, topdir);
+  
+  WinExec (cmd, SW_SHOW);
+}
+
+
+long __stdcall WndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) {
+  switch(Message) {
+    // Constant Messages
+  case WM_CREATE:
+    break;
+    
+  case WM_COMMAND:
+    if ((GoButton != NULL) && ((HWND)LParam == GoButton)) {
+      // attempt to start a browser
+      gsdl_start_browser = 1;
+      if (enter_library (Window) && !gsdl_show_console) 
+    	ShowWindow(Window,SW_MINIMIZE);
+
+    } else if ((Enter != NULL) && ((HWND)LParam == Enter)) {
+      init_done = 1;
+      init_type = 1;
+
+//      } else if ((EnterRestricted != NULL) && ((HWND)LParam == EnterRestricted)) {
+//        init_done = 1;
+//        init_type = 0;
+
+//      } else if ((InfoRestricted != NULL) && ((HWND)LParam == InfoRestricted)) {
+//        MessageBox (NULL, "The Greenstone system automatically determines whether your computer has\n"
+//  		  "network software installed or is connected to a network.  It operates correctly\n"
+//  		  "under any of these conditions.\n\n"
+//  		  "The restricted version is intended for use when the standard one\n\n"
+//  		  "   (a) causes an unwanted telephone dialup operation;\n"
+//  		  "   (b) fails to run because network software is installed, but installed\n"
+//  		  "         incorrectly.\n\n"
+//  		  "The restricted version only works with Netscape (not Internet Explorer).\n\n"
+//  		  "Unless these problems arise, you should always use the standard version\n"
+//  		  "(press the 'Enter Library' button).\n",
+//  		  "Greenstone Digital Library Software", 
+//  		  MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
+
+    } else {
+      switch (LOWORD(WParam)) {
+      case ID_PROJECT_SETTINGS:
+	Settings_Dialog(Window, netscapeneeded);
+	break;
+      case ID_INSTALL_NETSCAPE:
+	install_netscape(Window);
+	break;
+      case IDHELP:
+	open_help();
+	break;
+      case ID_PROJECT_EXIT:
+        finish_up(false);
+        break;
+      default:
+        break;
+      }
+    }
+    break;
+    
+  case WM_PAINT:
+    handle_painting(Window);
+    break;
+    
+  case HTTP_SERVER_MSG:
+    ProcessHTTPServerMsg(WParam, LParam);
+    break;
+    
+  case WM_DESTROY:
+    finish_up(false);
+    break;
+    
+  default:
+    //Unhandled Messages end up here (DefWindowProc)
+    return DefWindowProc(Window, Message, WParam, LParam);
+  }
+  
+  return(0);
+}
+
+// returns 0 if successful,
+// otherwise a WSA error number
+int tryinitnetwork (HANDLE Instance, HWND MsgWindow, char *path) {
+  // try to load winsock
+  int err = d_LoadWinsock (path);
+  
+  if (err == D_NOERROR) {
+    // next, try to init netio
+    err = InitNetIO();
+    
+    if (err == 0) {
+      // next, try to start the http server
+      err = StartHTTPServer(MsgWindow);
+      
+      if (err == 0) {
+	// finally, get the host name (no error value
+	// is returned from this function)
+	GetLocalName((HINSTANCE)Instance);
+	
+      } else {
+	// couldn't start the http server, 
+	// deinit netio and unload winsock
+	CleanUpNetIO();
+	d_UnloadWinsock();
+      }
+      
+    } else {
+      // couldn't init netio, unload winsock
+      d_UnloadWinsock();
+    }
+    
+  } else {
+    // couldn't load winsock
+    err = WSASYSNOTREADY;
+  }
+  
+  return err;
+}
+
+
+// inits all the network functionality
+// returns 0 if an unrecoverable error occurred,
+// and 1 if everything successfully initialised.
+int initnetwork (HANDLE Instance, HWND MsgWindow) {
+  OSVERSIONINFO osver;
+  
+  // get the operating system information
+  memset(&osver, 0, sizeof(OSVERSIONINFO));
+  osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  GetVersionEx(&osver);
+  
+  // first try to initialise the network with system installed
+  // networking software
+  startbrowserdir[0] = '\0';
+  netscapeneeded = 0;
+  int err = 1;
+  if (init_type == 1) err = tryinitnetwork (Instance, MsgWindow, NULL);
+  
+  // if an error occurred, try again with billsock
+  if (err != 0) {
+    // get the path of billsock (current directory + ("net16" | "net32"))
+    char winsockpath[1024]; // HARD LIMIT!!!!
+    
+    find_location();
+    char *data_location_c = data_location.getcstr();
+    strcpy (winsockpath, data_location_c);
+    delete []data_location_c;
+    
+    // remove all the slashes at the end of billpath
+    int len = strlen (winsockpath);
+    while ((len > 0) && ((winsockpath[len-1] == '\\') || (winsockpath[len-1] == '/'))) {
+      --len;
+    }
+    winsockpath[len] = '\0';
+    
+    if (osver.dwPlatformId == VER_PLATFORM_WIN32s)
+      strcat (winsockpath, "\\net16\\");
+    else {
+      strcat (winsockpath, "\\net32\\");
+      strcpy (startbrowserdir, winsockpath);
+    }
+    
+    // try again
+    err = tryinitnetwork (Instance, MsgWindow, winsockpath);
+    
+    if (err == 0) {
+      // we will need to use netscape on 95/98/NT machines
+      if (osver.dwPlatformId != VER_PLATFORM_WIN32s) {
+	netscapeneeded = 1;
+	// make sure the browser state is in step with the network condition
+	gsdl_check_browser_settings (netscapeneeded);
+      }
+      
+      // check to see if a browser is running
+      if (browserrunning(gsdl_browser_exe) == NO_ERROR) {
+	MessageBox (NULL, 
+		    "The Greenstone Digital Library software has detected a\n"
+		    "running browser. To let our networking software\n"
+		    "correctly initialize, please shut down your browser\n"
+		    "and restart the Greenstone Digital Library software.",
+		    "Greenstone Digital Library Software", MB_OK|MB_APPLMODAL);
+	PostMessage(MsgWindow,WM_CLOSE,0,0);
+	finish_up(false); // will unload winsock
+	exit(0);      // nothing more to do
+      }
+    }
+  }
+  
+  // if an error occurred display a nice error message
+  if (err != 0) {
+    
+    // WSASYSNOTREADY	Couldn't load winsock
+    // WSAVERNOTSUPPORTED	The version of Windows Sockets support requested 
+    //			is not provided by this particular Windows Sockets implementation.
+    // WSAEINVAL	The Windows Sockets version specified by the application 
+    //			is not supported by this DLL.
+    // WSAEPROTONOSUPPORT	The specified protocol is not supported.
+    // WSAEPROTOTYPE	The specified protocol is the wrong type for this socket.
+    // WSAESOCKTNOSUPPORT	The specified socket type is not supported in 
+    //		this address family.
+    // WSANOTINITIALISED	A successful WSAStartup must occur before 
+    //		using this function.
+    // WSAENETDOWN	The Windows Sockets implementation 
+    //		has detected that the network subsystem has failed.
+    // WSAEADDRINUSE 	The specified address 
+    //		is already in use. (See the SO_REUSEADDR socket option under setsockopt.)
+    // WSAEFAULT	The namelen argument is too small (less 
+    //		than the size of a struct sockaddr).
+    // WSAEINPROGRESS	A blocking Windows Sockets call is in progress.
+    // WSAEAFNOSUPPORT	The specified address family is not supported 
+    //		by this protocol.
+    // WSAENOBUFS	Not enough buffers available, too many connections.
+    // WSAENOTSOCK	The descriptor is not a socket.
+    // WSAEISCONN	The socket is already connected.
+    // WSAEMFILE	No more file descriptors are available.
+    // WSAEOPNOTSUPP	The referenced socket is not of a type that 
+    //		supports the listen operation.
+    
+    // get a text version of the error number
+    char errstr[256];
+    
+    switch (err) {
+    case WSASYSNOTREADY: strcpy(errstr, "WSASYSNOTREADY"); 
+      break;
+    case WSAVERNOTSUPPORTED: strcpy (errstr, "WSAVERNOTSUPPORTED");
+      break;
+    case WSAEINVAL: strcpy (errstr, "WSAEINVAL");
+      break;
+    case WSAEPROTONOSUPPORT: strcpy (errstr, "WSAEPROTONOSUPPORT");
+      break;
+    case WSAEPROTOTYPE: strcpy (errstr, "WSAEPROTOTYPE");
+      break;
+    case WSAESOCKTNOSUPPORT: strcpy (errstr, "WSAESOCKTNOSUPPORT");
+      break;
+    case WSANOTINITIALISED: strcpy (errstr, "WSANOTINITIALISED");
+      break;
+    case WSAEFAULT: strcpy (errstr, "WSAEFAULT");
+      break;
+    case WSAEAFNOSUPPORT: strcpy (errstr, "WSAEAFNOSUPPORT");
+      break;
+    case WSAENOTSOCK: strcpy (errstr, "WSAENOTSOCK");
+      break;
+    case WSAEISCONN: strcpy (errstr, "WSAEISCONN");
+      break;
+    case WSAEOPNOTSUPP: strcpy (errstr, "WSAEOPNOTSUPP");
+      break;
+    case WSAENETDOWN: strcpy (errstr, "WSAENETDOWN");
+      break;
+    case WSAEADDRINUSE: strcpy (errstr, "WSAEADDRINUSE");
+      break;
+    case WSAEINPROGRESS: strcpy (errstr, "WSAEINPROGRESS");
+      break;
+    case WSAENOBUFS: strcpy (errstr, "WSAENOBUFS");
+      break;
+    case WSAEMFILE: strcpy (errstr, "WSAEMFILE");
+      break;
+    default: sprintf (errstr, "WSA ERROR: %i", err);
+      break;
+    }
+    
+    // create a nice error message
+    char message[2048];
+    
+    switch (err) {
+    case WSAEADDRINUSE:
+      // couldn't bind socket
+      sprintf (message, "Could not find a free socket.", errstr);
+      break;
+      
+    default:
+      // cannot init winsock
+      sprintf (message, "Could not initialize the network\n"
+	       " (Reason: %s).", errstr);
+      break;
+    }
+    
+    strcat (message, "\n\n"
+	    "Please submit a bug report using the support.htm file\n"
+	    "on the CD-ROM.");
+    
+    MessageBox (NULL, message, "Greenstone Digital Library Software", MB_OK);
+  }
+  
+  return (err == 0);
+}
+
+
+void log_computer_info () {
+  char tmpstr[1024];
+  
+  // get various information about this computer
+  if (gsdl_keep_log || gsdl_show_console) {
+    // get operating system information
+    OSVERSIONINFO osver;
+    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    
+    if (GetVersionEx(&osver)) {
+      if (osver.dwPlatformId == VER_PLATFORM_WIN32s) {
+	log_message ("Platform: win32s\n");
+      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+	log_message ("Platform: windows 95\n");
+      } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+	log_message ("Platform: windows NT\n");
+      }
+      
+      sprintf (tmpstr, "MajorVersion: %i\n", (int)osver.dwMajorVersion);
+      log_message(tmpstr);
+      sprintf (tmpstr, "MinorVersion: %i\n", (int)osver.dwMinorVersion);
+      log_message(tmpstr);
+      sprintf (tmpstr, "BuildNumber: %i\n", (int)osver.dwBuildNumber);
+      log_message(tmpstr);
+      sprintf (tmpstr, "CSDVersion: %s\n\n", osver.szCSDVersion);
+      log_message(tmpstr);
+    }
+    
+    // get memory information
+    MEMORYSTATUS memstatus;
+    memstatus.dwLength = sizeof(MEMORYSTATUS);
+    GlobalMemoryStatus(&memstatus);
+    
+    sprintf (tmpstr, "TotalPhys: %i Meg\n", (int)(memstatus.dwTotalPhys/1024/1024));
+    log_message (tmpstr);
+    sprintf (tmpstr, "AvailPhys: %i Meg\n", (int)(memstatus.dwAvailPhys/1024/1024));
+    log_message (tmpstr);
+    sprintf (tmpstr, "TotalPageFile: %i Meg\n", (int)(memstatus.dwTotalPageFile/1024/1024));
+    log_message (tmpstr);
+    sprintf (tmpstr, "AvailPageFile: %i Meg\n", (int)(memstatus.dwAvailPageFile/1024/1024));
+    log_message (tmpstr);
+    sprintf (tmpstr, "TotalVirtual: %i Meg\n", (int)(memstatus.dwTotalVirtual/1024/1024));
+    log_message (tmpstr);
+    sprintf (tmpstr, "AvailVirtual: %i Meg\n\n", (int)(memstatus.dwAvailVirtual/1024/1024));
+    log_message (tmpstr);
+    
+    // log the version number
+    log_message("GSDL version: " GSDL_VERSION "\n");
+  }
+  
+}
+
+// possible command line options are 
+// --location=directory (the path to GSDLHOME which defaults to the
+// directory where server.exe lives)
+// --config=file (the path to the configuration file to use which defaults
+// to GSDLHOME\gsdlsite.cfg)
+// --cmd=init  Used to initialize the gsdlsite.cfg file onlt - jmt12 18/11/2003
+static void parse_args(const text_t cmdline, text_t &location, text_t &config_file, text_t &command) {
+
+  location.clear();
+  config_file.clear();
+  command.clear(); // jmt12 18/11/2003
+
+  if (cmdline.size() < 3) return;
+
+  text_t name, val;
+  bool foundname = false;
+  text_t::const_iterator here = cmdline.begin();
+  text_t::const_iterator end = cmdline.end();
+  while (here != end) {
+    if (*here == '-' && ((here+1) != end) && (*(here+1) == '-')) {
+      ++here;
+      if (name == "location") {
+	location = val;
+      } else if (name == "config") {
+	config_file = val;
+      } 
+      // jmt12 18/11/2003
+      else if(name == "cmd") {
+	command = val;
+      }
+      foundname = false;
+      name.clear();
+      val.clear();
+    } else if (*here == '=') {
+      foundname = true;
+    } else {
+      if (foundname) {
+	val.push_back(*here);
+      } else {
+	name.push_back(*here);
+      }
+    }
+    ++here;
+  }
+  if (name == "location") {
+    location = val;
+  } else if (name == "config") {
+    config_file = val;
+  }
+  // jmt12 18/11/2003
+  else if(name == "cmd") {
+    command = val;
+  }
+}
+
+// Added code to allow for an 'init' run of the server which just creates the gsdlsite.cfg file
+int __stdcall WinMain(HINSTANCE Instance, HINSTANCE /*PrevInstance*/, LPSTR CmdLineStr, int /*CmdShow*/) {
+  HWND MainWindow;  MSG Message;  WNDCLASS MainClass;  
+
+  // One instance of the local library server is quite enough, thank you very much
+  HWND gw = FindWindow("Greenstone Digital Library Software",
+		       "Greenstone Digital Library Software");
+  if (gw != NULL) {
+    exit(1);
+  }
+
+  // parse arguments
+  text_t location, config_file, command;
+  parse_args(CmdLineStr, location, config_file, command);
+  gsdl_conffile = config_file;
+
+  // jmt12 18/11/2003
+  if(command == "init") {
+    // init various modules
+    read_settings(0); // don't know if netscape is needed at this point
+    finish_up(true);
+    exit(0);
+  }
+
+  //Create a window class
+  MainClass.style = CS_HREDRAW | CS_VREDRAW;
+  MainClass.lpfnWndProc = WndProc;
+  MainClass.cbClsExtra = 0;
+  MainClass.cbWndExtra = 0;
+  MainClass.hInstance = Instance;
+  MainClass.hIcon     = LoadIcon(Instance, MAKEINTRESOURCE(TRAY_ICON));
+  MainClass.hCursor   = LoadCursor(NULL, IDC_ARROW);
+  MainClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+  MainClass.lpszMenuName = MAKEINTRESOURCE(Main_Menu);
+  MainClass.lpszClassName = "Greenstone Digital Library Software";
+  if (!RegisterClass(&MainClass))
+    return 0;
+  
+  
+  // Load various bitmaps
+  coltitlebitmap = LoadBitmap (Instance, MAKEINTRESOURCE(GSDL_COL_TITLE));
+  logobitmap= LoadBitmap (Instance, MAKEINTRESOURCE(GSDL_LOGO));
+  
+  // Create the main window
+  MainWindow = CreateWindow("Greenstone Digital Library Software", 
+			    "Greenstone Digital Library Software",
+			    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
+			    WS_MINIMIZEBOX,
+			    CW_USEDEFAULT, CW_USEDEFAULT, 
+			    MAINWINDOWWIDTH + WASTEWIDTH, 
+			    MAINWINDOWHEIGHT + WASTEHEIGHT,
+			    NULL, NULL, Instance, NULL);
+  
+  text_rect.left = 0;  
+  text_rect.top = VERSIONY+VERSIONHEIGHT + 5;
+  text_rect.right = MAINWINDOWWIDTH;
+  text_rect.bottom = LOGOY-SPACERWIDTH;
+  
+  window_state = undefined_window;
+  
+  ShowWindow(MainWindow, SW_SHOW); 
+  set_location(location);
+  
+  // get ready to draw the main window
+  RECT windowRect;
+  GetClientRect (MainWindow, &windowRect);
+  HDC pdc = GetDC (MainWindow);
+  
+  // retrieve and save the text metrics
+  TEXTMETRIC tm;
+  GetTextMetrics(pdc, &tm);
+  line_spacing = tm.tmHeight + tm.tmExternalLeading;
+  GSDL_Window = MainWindow;  
+
+  // init various modules
+  read_settings (0); // don't know if netscape is needed at this point
+  
+  if (!gsdl_auto_enter) {
+    
+    // add the select version checkbox
+    Enter = CreateWindow("BUTTON",             // predefined class 
+			 "Enter Library",      // button text 
+			 WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_DEFPUSHBUTTON,  // styles 
+			 ENTERBUTTONX,         // starting x position 
+			 ENTERBUTTONY,         // starting y position 
+			 ENTERBUTTONWIDTH,     // button width 
+			 ENTERBUTTONHEIGHT,    // button height 
+			 MainWindow,           // parent window 
+			 NULL,                 // No menu 
+			 Instance, 
+			 NULL);                // pointer not needed 
+    
+//      EnterRestricted = CreateWindow("Button",                // predefined class 
+//  				   "Restricted Version",    // button text 
+//  				   WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_PUSHBUTTON,   // styles 
+//  				   ALTERNATIVEBUTTONX,      // starting x position 
+//  				   ALTERNATIVEBUTTONY,      // starting y position 
+//  				   ALTERNATIVEBUTTONWIDTH-(INFOBUTTONWIDTH+SPACERWIDTH),  // button width 
+//  				   ALTERNATIVEBUTTONHEIGHT, // button height 
+//  				   MainWindow,              // parent window 
+//  				   (HMENU)NULL,             // No menu 
+//  				   Instance, 
+//  				   NULL);                   // pointer not needed 
+    
+//      InfoRestricted = CreateWindow("Button",                // predefined class 
+//  				  NULL,
+//  				  WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_BITMAP,   // styles 
+//  				  ALTERNATIVEBUTTONWIDTH-INFOBUTTONWIDTH+SPACERWIDTH, // starting x position 
+//  				  ALTERNATIVEBUTTONY,      // starting y position 
+//  				  INFOBUTTONWIDTH,         // button width 
+//  				  ALTERNATIVEBUTTONHEIGHT, // button height 
+//  				  MainWindow,              // parent window 
+//  				  (HMENU)NULL,             // No menu 
+//  				  Instance, 
+//  				  NULL);                   // pointer not needed 
+    
+//      HANDLE i_icon = LoadImage (Instance, MAKEINTRESOURCE(GSDL_ICBMP), IMAGE_BITMAP, 
+//  			       INFOBUTTONWIDTH-4, ALTERNATIVEBUTTONHEIGHT-4, LR_DEFAULTCOLOR);
+//      SendMessage (InfoRestricted, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)i_icon);
+    
+    SetFocus (Enter);
+    
+    enterstring = strenterlib;
+    paint_window (pdc, windowRect);
+    
+    // message loop for init buttons
+    while (!init_done) {
+      if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
+	if (Message.message == WM_QUIT) return Message.wParam;
+	if (!IsDialogMessage (MainWindow, &Message)) {
+	  TranslateMessage(&Message); /* translate keyboard messages */
+	  DispatchMessage(&Message);  /* return control to Windows NT */
+	}
+      } else {
+	Sleep (1);
+      }
+    }
+    
+    // don't want these buttons anymore
+    DestroyWindow (Enter);
+    // DestroyWindow (EnterRestricted);
+    // DestroyWindow (InfoRestricted);
+    
+    enterstring = strnothing;
+    paint_window (pdc, windowRect);
+    
+  } else {
+    // auto enter is enabled - start up standard version
+    init_type = 1;
+    init_done = 1;
+  }
+  
+  // draw the main window containing an init message
+  infostring = strinit;
+  paint_window (pdc, windowRect);
+  DWORD lastcheck = GetTickCount();
+  
+  have_networking = initnetwork (Instance, MainWindow);
+  if (!have_networking) {
+    MessageBox(NULL,
+	       "Failed to initialize networking.",
+	       "Greenstone Digital Library Software",MB_OK|MB_SYSTEMMODAL);	
+    exit (0);
+  }
+
+  if (!gsdl_init()) // quit if can't initialise the library correctly
+    exit (0);
+  initstartbrowser();
+  log_computer_info ();
+  
+  // show the initialising message for at least 1 second
+  while (DiffTickCounts (lastcheck, GetTickCount()) < 1000) {
+    Sleep (1);
+  }
+  
+  // attempt to start a browser
+  if (gsdl_show_console || !gsdl_start_browser) infostring = strnothing;
+  else infostring = strsb;
+  paint_window (pdc, windowRect);
+  lastcheck = GetTickCount();
+  if (enter_library (MainWindow) && !gsdl_show_console) {
+    // stay maximised for at least 1 second
+    while (DiffTickCounts (lastcheck, GetTickCount()) < 1000) Sleep (1);
+    ShowWindow(MainWindow,SW_MINIMIZE);
+  }
+  
+  // add the "restart library" button
+  GoButton = CreateWindow("BUTTON",              // predefined class 
+			  "Restart Library",     // button text 
+			  WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // styles 
+			  RESTARTBUTTONX,        // starting x position 
+			  RESTARTBUTTONY,        // starting y position 
+			  RESTARTBUTTONWIDTH,    // button width 
+			  RESTARTBUTTONHEIGHT,   // button height 
+			  MainWindow,            // parent window 
+			  NULL,                  // No menu 
+			  Instance, 
+			  NULL);                 // pointer not needed 
+  
+  infostring = strrestartlib;
+  paint_window (pdc, windowRect);
+  
+  // release the DC used to display the init messages
+  ReleaseDC (MainWindow, pdc);
+  
+  //Message loop
+  lastcheck = GetTickCount();
+  int seenbrowser = 0;	// if we see a browser then it disappears
+                        // we will ask if they want to close the
+                        // the library down
+  char last_gsdl_browser_exe[MAX_FILENAME_SIZE]; // need to notice when the
+                                                 // browser setting changes
+  strcpy (last_gsdl_browser_exe, gsdl_browser_exe);
+  
+  for (;;) {
+    if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
+      if (Message.message == WM_QUIT) break;
+      if (!IsDialogMessage (MainWindow, &Message)) {
+	TranslateMessage(&Message); /* translate keyboard messages */
+	DispatchMessage(&Message);  /* return control to Windows NT */
+      }
+      
+    } else {
+      if (DiffTickCounts (lastcheck, GetTickCount()) > 500) {
+	// make sure the browser hasn't changed
+	if (strcmp (last_gsdl_browser_exe, gsdl_browser_exe) != 0) {
+	  seenbrowser = 0;
+	  strcpy (last_gsdl_browser_exe, gsdl_browser_exe);
+	}
+	
+	// do check
+	lastcheck = GetTickCount();
+	if (gsdl_start_browser && browserrunning(gsdl_browser_exe) == NO_ERROR) {
+	  // we were able to connect to a browser
+	  seenbrowser = 1;
+	  
+	  // see if the enter library button has been pressed
+	  // with nothing happening for 20 seconds
+	  if ((enterlib_libaccessnum >= 0) && 
+	      (enterlib_libaccessnum == libaccessnum) &&
+	      (DiffTickCounts (enterlib_time, GetTickCount()) > 20000)) {
+	    // something could be wrong!! (most likely cause is a proxy)
+	    MessageBox(NULL,
+		       "Your browser does not seem to be responding to the 'Enter Library'\n"
+		       "request. Try turning off all proxy servers in your browser settings.\n"
+		       "If you are using Internet Explorer make sure BOTH dialup settings\n"
+		       "AND LAN settings have their proxy servers disabled.",
+		       "Greenstone Digital Library Software",MB_OK|MB_SYSTEMMODAL);
+	    enterlib_libaccessnum = -1;
+	  }
+	  
+	  
+	} else {
+	  // no browser was found
+	  
+	  if (seenbrowser) {
+	    // we have seen a browser in the past
+	    if (MessageBox(NULL,
+			   "The Greenstone Digital Library software has noticed that you\n"
+			   "shut down your browser. Would you also like to shut down the\n"
+			   "Greenstone Digital Library software?",
+			   "Greenstone Digital Library Software",MB_YESNO|MB_SYSTEMMODAL) == IDYES)
+	      PostMessage(MainWindow,WM_CLOSE,0,0);
+	  }
+	  seenbrowser = 0;
+	}
+      }
+      
+      Sleep (1); // millisecond
+    }
+  }
+  
+  return Message.wParam;
+}
+
Index: /gsdl/trunk/gs2runtime/src/w32server/httpreq.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpreq.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpreq.cpp	(revision 16561)
@@ -0,0 +1,445 @@
+/**********************************************************************
+ *
+ * httpreq.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include "httpreq.h"
+#include "parse.h"
+#include "netio.h"
+#include "settings.h"
+#include "httpsrv.h"
+#include "httpsend.h"
+#include "cgiwrapper.h"
+#include "d_winsock.h"
+
+/*
+Implementation Notes:
+
+HTTP field names, method and version strings are converted to upper case
+right after being read from the client in order to allow case insensitive
+string comparisons to be done on them.  Since these fields are worked with a
+lot, this should help performance.
+*/
+
+//Private Data and declarations
+#define IO_BUFFER_SIZE 16384        //16K IO Buffer
+#define MAX_HTTP_LINE_LEN 1024      //Max length of line in a header of 1024
+
+//Private Function Declarations with Return Contstants
+
+/*
+Function Name: DispatchRequest
+Purpose: Manages having the request parsed, then sent to the right function
+         to send a response or handle an error.
+Parameters:
+         ClientSocket - Socket the client is on
+	 ClientSockAddr - Address of client
+	 AddrLen - Length of address of client
+	 IOBuffer - Pointer to buffer allocated for IO operations
+	 ThreadNum - Number of thread that called this function for debugging purposes
+Notes:   I'm still playing with the keep alive support.  I commented out
+	 the stuff for giving a client a timeout because I was unable to detect
+	 disconnects.
+More Notes: Not sure if this organization will allow me to easily add support
+	    for ISAPI filter DLLs.
+*/
+void DispatchRequest(SOCKET ClientSocket, SOCKADDR_IN ClientSockAddr, int AddrLen, BYTE *IOBuffer);
+
+/*
+Function Name: Get HTTP Headers
+Purpose: Manages having the request parsed, then sent to the right function
+         to send a response or handle an error.
+Parameters:
+	RequestInfo - Request information structure (see httpreq.h)
+	RequestFields - HTTP request fields structure (see httpreq.h)
+Returns: GH_ERROR on error (diconnect, bad data, Windows in a bad mood, etc.)
+			GH_UNKNOWN_VERSION if the version number is not HTTP/0.9 or HTTP/1.x
+			GH_SIMPLE_REQUEST on a properly formated HTTP/0.9 request
+			GH_10_REQUEST on a properly formated HTTP/1.x request
+*/
+int GetHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
+#define GH_BAD_METHOD	   -2
+#define GH_ERROR           -1
+#define GH_UNKNOWN_VERSION  0
+#define GH_SIMPLE_REQUEST   1
+#define GH_10_REQUEST       2
+
+/*
+Function Name: Clean Up HTTP Headers
+Purpose: Cleans up memory dynamicly allocated for headers
+Parameters:
+	RequestInfo - Request information structure (see httpreq.h)
+	RequestFields - HTTP request fields structure (see httpreq.h)
+Returns: Nothing
+*/
+void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
+
+/*
+Function Name: Split Query
+Purpose: Splits the file and query part of a URI.  In other words, it
+	 puts the parts before and after the "?" in differnet strings.
+Parameters:
+	 URIStr - The requested URI
+	 FileStr - String to contain the name of the path + file part of the URI
+	 QueryStr - String to contain the query part of the URI
+Returns: TRUE if there is a query, else FALSE
+*/
+BOOL SplitQuery(char *URIStr, char *FileStr, char *QueryStr, int ThreadNum);
+
+/*
+Function Name: Get File
+Purpose: Attempts to find a given file, including looking for index.html.
+	 Updates the given URI string so it points to the true document location
+Parameters:
+	 FilePath - Path of file, may be modified to best reflect the retrived file
+				  or directory
+	 URIStr - URI string, minus the query
+Returns: GF_ERROR on error
+			GF_FILE_FOUND on success
+			GF_INDEX_FOUND if file is a directory with an index.html file in it
+			GF_DIRECTORY if file is a directory
+			GF_FILE_NOT_FOUND if file was found
+*/
+
+/*
+Function Name: Process Simple Request
+Purpose: Sends a reply to a HTTP 0.9 "simple" request
+Parameters:
+	ClientSocket - Socket the client is on
+	RequestInfo - Structure storing the parsed headers
+	IOBuffer - Pointer to buffer allocated for IO operations
+	TheadNum - Number of calling thread for debugging
+Notes: I should really test this and see if it works...
+*/
+void ProcessSimpleRequest(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
+
+//Public Functions
+/******************************************************************************/
+void RequestThread(RequestThreadMessageT *Parameters) {
+  SOCKADDR_IN ClientSockAddr;
+  SOCKET ClientSocket;
+  int AddrLen;
+  //Allocate an IO buffer for this thread
+  BYTE *IOBuffer = new BYTE[IO_BUFFER_SIZE];
+
+  //Get the parameters for the request
+  ClientSocket = Parameters->ClientSocket;
+  ClientSockAddr = Parameters->ClientSockAddr;
+  AddrLen = Parameters->AddrLen;
+  DispatchRequest(ClientSocket, ClientSockAddr, AddrLen, IOBuffer);
+}
+/******************************************************************************/
+
+//Private Functions
+
+/******************************************************************************/
+void DispatchRequest(SOCKET ClientSocket, SOCKADDR_IN ClientSockAddr, int AddrLen,  BYTE *IOBuffer) {
+  RequestInfoT RequestInfo;
+  RequestFieldsT RequestFields;
+
+  // TrayAddConnection();
+
+  //Setup the RequestInfo structure
+  memset(&RequestInfo, 0, sizeof(RequestInfoT));
+  RequestInfo.ThreadNum = 0;
+  RequestInfo.IOBuffer = IOBuffer;
+  RequestInfo.IOBufferSize = IO_BUFFER_SIZE;
+  RequestInfo.ClientSocket = ClientSocket;
+  RequestInfo.ClientSockAddr = ClientSockAddr;
+  RequestInfo.AddrLen = AddrLen;
+  RequestInfo.KeepAlive = FALSE;
+  
+  int GetHeadersResult;
+  do {
+    //Get Headers
+    GetHeadersResult = GetHTTPHeaders(RequestInfo, RequestFields);
+
+    //Figure out what version we're dealing with and deal with it
+    switch (GetHeadersResult) {
+    case GH_SIMPLE_REQUEST :
+      SendHTTPError(400, "HTTP Request not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
+      // TrayIncNumServed();
+      break;
+    case GH_10_REQUEST :
+      ExamineURIStr(RequestFields.URIStr,&RequestInfo,&RequestFields);
+      // TrayIncNumServed();
+      break;
+    case GH_UNKNOWN_VERSION :
+      SendHTTPError(400, "HTTP Version not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
+      // TrayIncNumServed();
+      break;
+/* added Feb 2002 to handle stupid MS behaviour */
+    case GH_BAD_METHOD :
+      SendHTTPError(501, "Not implemented", "Only GET and POST currently implemented", RequestInfo, RequestFields);
+      break;
+    case GH_ERROR:
+      //Disconnect
+      RequestInfo.KeepAlive = FALSE;
+      break;
+    }
+    CleanUpHTTPHeaders(RequestInfo, RequestFields);
+  } while (0/*RequestInfo.KeepAlive == TRUE*/);
+  //Close connection
+  CloseSocket(RequestInfo.ClientSocket);
+  // TrayRemoveConnection();
+}
+
+/******************************************************************************/
+int GetHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
+  //Parsing and IO buffers
+  text_t CurLine;
+  text_t NextLine;
+  text_t FieldNameStr;
+  text_t FieldValStr;
+  
+  //Parsing and IO working vars
+  int ReadBufferIndex;
+  int DataInBuffer;
+  text_t::const_iterator next;
+  text_t::const_iterator end;
+
+  //Clear all the fields
+  memset(&RequestFields, 0, sizeof(RequestFieldsT));
+
+  ReadBufferIndex = 0;
+  DataInBuffer = 0;
+  
+  //Get First Line
+  if (GetLine(CurLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
+	      RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
+	      RequestInfo.ThreadNum) != 0) return GH_ERROR;
+  do {//Get Next Line, append it if the first charactor is space
+    if(GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
+	       RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
+	       RequestInfo.ThreadNum) != 0) return GH_ERROR;
+    if ((*(NextLine.begin()) == ' ') || (*(NextLine.begin()) == '\t')) {
+      CurLine += NextLine;
+    }
+  } while ((*(NextLine.begin()) == ' ') || (*(NextLine.begin()) == '\t'));
+  //Method String (first word)
+  GetWord(RequestFields.MethodStr, CurLine.begin(), CurLine.end(), next);
+  uc(RequestFields.MethodStr);
+
+  /* Added Feb 2002 - IE since about version 5 send stupid frontpage requests
+     for MS Document formats eg "GET /_vti_inf.html" */ 
+  if (RequestFields.MethodStr == "OPTIONS") {
+    return GH_BAD_METHOD;
+  } 
+  //Version String (last word)
+  GetLastWord(RequestFields.VersionStr, CurLine.begin(), CurLine.end(), end);
+  uc(RequestFields.VersionStr);
+  text_t::const_iterator versionbegin = RequestFields.VersionStr.begin();
+
+  if ((RequestFields.VersionStr.size() > 5) && (substr(versionbegin, versionbegin+5) != "HTTP/")) {
+    //No version, assume simple request
+    //part after method is URI
+    RequestFields.URIStr = CurLine;
+    return GH_SIMPLE_REQUEST;
+  }
+
+  //URI String (in between End of first and Start of last)
+  //<Method> <WhiteSpace> <URI> <WhiteSpace> <Version> <CRLF>
+  //                  next^             end^
+  text_t spacebuffer;
+  text_t::const_iterator here = next;
+  while (here != end) {
+    // do this to remove trailing space
+    if (*here == ' ' || *here == '\t') {
+      spacebuffer.push_back(*here);
+    } else {
+      if (!spacebuffer.empty()) {
+	RequestFields.URIStr += spacebuffer;
+	spacebuffer.clear();
+      }
+      RequestFields.URIStr.push_back(*here);
+    }
+    ++here;
+  }
+
+  //Only accept requests from HTTP/0.9 or HTTP/1.X clients, we'll
+  //assume that anything else will require an upgrade or patch
+  if ((RequestFields.VersionStr.size() > 7) && (substr(versionbegin, versionbegin+7) != "HTTP/1.")) {
+    return GH_UNKNOWN_VERSION;
+  }
+  
+  //Get the rest of the lines
+  CurLine = NextLine;
+  
+  while (!CurLine.empty()) {//Blank Line, we're done
+    do {//Get Next Line, append it if the first charactor is space
+      if (GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
+		  RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
+		  RequestInfo.ThreadNum) != 0)
+	return GH_ERROR;
+      if (NextLine.empty())
+	break;
+      if ((*(NextLine.begin()) == ' ') || (*(NextLine.begin()) == '\t')) {
+	CurLine += NextLine;
+      }
+    } while ((*(NextLine.begin()) == ' ') || (*(NextLine.begin()) == '\t'));
+    
+    GetWord(FieldNameStr, CurLine.begin(), CurLine.end(), next);
+    uc(FieldNameStr);
+    
+    FieldValStr = substr(next, CurLine.end());
+    
+    //Process it
+    //In order of expected commonality
+    //All constants are in canonized, thus in upper case and case sensitive
+    //comparisons are used
+
+    //--Just About Always--
+    if (FieldNameStr == "ACCEPT:") {
+      if (!RequestFields.AcceptStr.empty()) {
+	RequestFields.AcceptStr += ", ";
+      }
+      RequestFields.AcceptStr += FieldValStr;
+    }
+    else if (FieldNameStr == "DATE:") {
+      RequestFields.DateStr = FieldValStr;
+    }
+    else if (FieldNameStr == "USER-AGENT:") {
+      RequestFields.UserAgentStr = FieldValStr;
+    }
+    else if (FieldNameStr == "CONNECTION:") {
+      RequestFields.ConnectionStr = FieldValStr;
+    }
+    //--Sometimes--
+    else if (FieldNameStr == "ACCEPT-LANGUAGE:") {
+      RequestFields.AcceptLangStr = FieldValStr;
+    }
+    else if (FieldNameStr == "REFERER:") {
+      RequestFields.RefererStr = FieldValStr;
+    }
+    else if (FieldNameStr == "IF-MODIFIED-SINCE:") {
+      RequestFields.IfModSinceStr = FieldValStr;
+    }
+    //--Uncommon--
+    else if (FieldNameStr == "FROM:") {
+      RequestFields.FromStr = FieldValStr;
+    }
+    else if (FieldNameStr == "MIME-VERSION:") {
+      RequestFields.MIMEVerStr = FieldValStr;
+    }
+    else if (FieldNameStr == "PRAGMA:") {
+      RequestFields.PragmaStr = FieldValStr;
+    }
+    //--Special case--
+    else if (FieldNameStr == "AUTHORIZATION:") {
+      RequestFields.AuthorizationStr = FieldValStr;
+    }
+    else if (FieldNameStr == "CONTENT-LENGTH:") {
+      RequestFields.ContentLengthStr = FieldValStr;
+    }
+    else if (FieldNameStr == "CONTENT-TYPE:") {
+      RequestFields.ContentTypeStr = FieldValStr;
+    }
+    else if (FieldNameStr == "CONTENT-ENCODING:") {
+      RequestFields.ContentEncodingStr = FieldValStr;
+    }
+    else if (!FieldNameStr.empty()) {
+      //Add it to the other headers
+
+      //Remove the colon
+      if (*(FieldNameStr.end()-1) == ':') {
+	FieldNameStr.pop_back();
+      }
+      RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var = FieldNameStr;
+      RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val = FieldValStr;
+      ++RequestFields.NumOtherHeaders;
+    }
+    CurLine = NextLine;
+  }
+  
+  if (!RequestFields.ContentLengthStr.empty()) { //Do we have attached data?
+    unsigned int NumRecv;
+    
+    RequestFields.ContentLength = RequestFields.ContentLengthStr.getint();
+    if (RequestFields.ContentLength > 0) {
+
+      //Allocate memory
+      RequestFields.Content = new BYTE[RequestFields.ContentLength];
+      
+      //Get rest of data from get lines
+      NumRecv = DataInBuffer - ReadBufferIndex;
+      
+      if (NumRecv >RequestFields.ContentLength) {
+	//Overflow, only read what they said they'd send
+	NumRecv = RequestFields.ContentLength;
+      }
+      memcpy(RequestFields.Content, RequestInfo.IOBuffer + ReadBufferIndex,
+	     NumRecv);
+      
+      while (NumRecv < RequestFields.ContentLength) {
+	NumRecv += GetData(RequestInfo.ClientSocket,
+			   RequestFields.Content + NumRecv,
+			   RequestFields.ContentLength - NumRecv,
+			   RequestInfo.ThreadNum);
+	if (NumRecv < 0) return GH_ERROR;
+      }
+
+      // It seems to be important on NT that all available data was read
+      // from the socket before the socket is closed (otherwise netscape
+      // throws a "connection reset by peer" error). Since netscape seems
+      // to send a few extra bytes in certain situations we'll make sure we
+      // slurp it all up here.
+      char *tmpbuffer = new char[100]; // this had new char(100)????
+      //      unsigned long int nonblockmode=1;
+      //      ioctlsocket(RequestInfo.ClientSocket, FIONBIO, &nonblockmode);
+      d_recv(RequestInfo.ClientSocket, tmpbuffer, 100, 0);
+      delete []tmpbuffer;
+
+    }
+    else {
+      RequestFields.Content = NULL;
+      RequestFields.ContentLength = 0;
+    }
+  }
+  else {
+    RequestFields.Content = NULL;
+    RequestFields.ContentLength = 0;
+  }
+  
+  return GH_10_REQUEST;
+}
+
+/******************************************************************************/
+void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
+  //Clean up memory allocated for the Content
+  if (RequestFields.Content != NULL) {
+    delete[] RequestFields.Content;
+  }
+  
+  // clean up memory allocated for the IOBuffer
+  if (RequestInfo.IOBuffer != NULL) {
+    delete[] RequestInfo.IOBuffer;
+    RequestInfo.IOBuffer = NULL;
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/httpreq.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpreq.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpreq.h	(revision 16561)
@@ -0,0 +1,143 @@
+/**********************************************************************
+ *
+ * httpreq.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#ifndef HTTPREQ_H
+#define HTTPREQ_H
+
+// need this to avoid bizarre compiler problems under VC++ 6.0
+#if !defined (GSDL_NAMESPACE_BROKEN) && !defined (GSDL_USE_IOS_H)
+# include <iostream>
+# include <fstream>
+using namespace std;
+#endif
+
+#include "locate.h"
+#include "text_t.h"
+
+/*
+Module Name: HTTP Request
+Purpose: Parses HTTP requests and then calls the appropriate function
+	to respond to the request
+Public Functions:
+	Request Thread
+*/
+
+// Public Data Structures
+
+//Used for sending information to the request thread
+struct RequestThreadMessageT {
+  SOCKADDR_IN ClientSockAddr;
+  SOCKET ClientSocket;
+  int AddrLen;
+};
+
+#define MAX_OTHER_HEADERS 100
+
+struct RequestHeaderT {
+  text_t Var;
+  text_t Val;
+};
+
+struct RequestFieldsT {
+  //Simple request line info v0.9
+  text_t MethodStr;
+  text_t URIStr;
+  //added v1.0
+  text_t VersionStr;
+  //General Header
+  text_t DateStr;
+  text_t MIMEVerStr;
+  text_t PragmaStr;
+  //Request Header
+  text_t AuthorizationStr;
+  text_t FromStr;
+  text_t IfModSinceStr;
+  text_t RefererStr;
+  text_t UserAgentStr;
+  //Entity Header (Only CGI stuff)
+  text_t ContentEncodingStr;
+  text_t ContentTypeStr;
+  text_t ContentLengthStr;
+  //v1.0 Optional (the more common ones)
+  text_t AcceptStr;
+  text_t AcceptLangStr;
+  //v1.1 Exentions
+  text_t ConnectionStr;
+  //Pointer to buffer containing the content
+  DWORD ContentLength;
+  BYTE *Content;
+  
+  //Other Headers
+  int NumOtherHeaders;
+  RequestHeaderT OtherHeaders[MAX_OTHER_HEADERS];
+};
+
+struct RequestInfoT {
+  int ThreadNum;
+  
+  //Buffer for IO operations (so we're not constantly reallocating buffers)
+  BYTE *IOBuffer;
+  int IOBufferSize;
+  
+  //Socket the request is on and its address
+  SOCKET ClientSocket;
+  SOCKADDR_IN ClientSockAddr;
+  int AddrLen;
+  
+  //Should we keep the connection alive?
+  BOOL KeepAlive;
+};
+
+
+// Public Functions
+
+/*
+Function Name: RequestThread
+Purpose: Was the HTTP request processing thread
+         Now just a standard procedure called to process a request
+Parameters:
+	       Pointer to packed parameter structure
+*/
+void RequestThread(RequestThreadMessageT *Parameters);
+
+/*
+Function Name: Process 1.0 Request
+Purpose: Sends a HTTP 1.0 (plus some) reply to a HTTP 1.x request
+Parameters:
+	ClientSocket - Socket the client is on
+	ClientSockAddr - Address of client
+	AddrLen - Length of client address
+	RequestInfo - Structure storing the parsed headers
+	KeepAlive - To be set to true if we are maintainig the connection
+	IOBuffer - Pointer to buffer allocated for IO operations
+	TheadNum - Number of calling thread for debugging
+Notes: The function uses "Connection: Keep-Alive" as written in the HTTP/1.1
+	draft and implemented by Netscape and MSIE
+*/
+void Process10Request(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/httpsend.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpsend.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpsend.cpp	(revision 16561)
@@ -0,0 +1,109 @@
+/**********************************************************************
+ *
+ * httpsend.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#pragma hdrstop
+#include "netio.h"
+#include "httpreq.h"
+#include "httpsend.h"
+
+//Private Function Declarations
+/*
+Function Name: Check IMS Date
+Purpose: Checks the string date of an "If Modified Since" header against
+	a file date stamp.
+Parameters:
+	FileTime - Time stamp on file
+	IMSStr - "If Modified Since" date string
+Returns: TRUE if the file has been modified since the given date or
+	the data can not be resolved (the string to date function will log
+	any errors)
+*/
+BOOL CheckIMSDate(SYSTEMTIME &FileTime, char *IMSStr);
+
+//Public Functions
+/******************************************************************************/
+
+void SendHTTPError(int ErrorNum, char *ErrorTitleStr, char *ErrorDescStr, 
+		   RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
+  char Header[512];
+  char Body[512];
+
+  char ErrorNumStr[17];
+  
+  DWORD BodyLength;
+  char BodyLengthStr[17];
+
+  int HeaderLength;
+  
+  itoa(ErrorNum, ErrorNumStr, 10);
+  
+  // Didn't find it, build the message manually
+  Body[0] = 0;
+  strcat(Body, "<HTML><HEAD><TITLE>Server Error</TITLE></HEAD><BODY>\n");
+  strcat(Body, "<H1>Error ");
+  strcat(Body, ErrorNumStr);
+  strcat(Body, ": ");
+  strcat(Body, ErrorTitleStr);
+  strcat(Body, "</H1>\n");
+  strcat(Body, ErrorDescStr);
+  strcat(Body, "\n</BODY></HTML>");
+  BodyLength = (DWORD) strlen(Body);
+
+  //Build Header
+  Header[0] = 0;
+  //Status Line
+  strcat(Header, "HTTP/1.0 ");
+  strcat(Header, ErrorNumStr);
+  strcat(Header, " ");
+  strcat(Header, ErrorDescStr);
+  strcat(Header, "\r\n");
+  //Server
+  strcat(Header, "Server: GSDL\r\n");
+  //Content Type (assume HTML)
+  strcat(Header, "Content-Type: text/html\r\n");
+  //Content Length
+  itoa(BodyLength, BodyLengthStr, 10);
+  strcat(Header, "Content-Length: ");
+  strcat(Header, BodyLengthStr);
+  strcat(Header, "\r\n");
+  //Single CRLF to end header
+  strcat(Header, "\r\n");
+  
+  HeaderLength = strlen(Header);
+  
+  //Send header
+  SendData(RequestInfo.ClientSocket, (BYTE *) Header, strlen(Header), RequestInfo.ThreadNum);
+  //Send generated data
+  SendData(RequestInfo.ClientSocket, (BYTE *) Body, BodyLength, RequestInfo.ThreadNum);
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/httpsend.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpsend.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpsend.h	(revision 16561)
@@ -0,0 +1,51 @@
+/**********************************************************************
+ *
+ * httpsend.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+/*
+Module Name: HTTP Send
+Purpose: Functions for sending HTTP responses to 1.X requests
+Public Functions:
+	SendHTTPError
+Notes: Considering adding a version number parameter to these functions.  Right
+	now the way people are implementing HTTP/1.1 is by adding the new stuff
+	to 1.0 requests.  I don't know of any browsers sending 1.1 requests yet.
+*/
+
+/*
+Function Name: Send HTTP Error
+Purpose: Sends a given error message to the client with either the error file
+			as a body or a default error message body.  Common and refenece log
+			entries are written, but not to the count log.
+Parameters:
+	ErrorNum - HTTP error number to send
+	ErrorTitleStr - Title of the error
+	ErrorDescStr - String describing the error
+	RequestInfo - HTTP request information (see httpreq.h)
+	RequestFields - Parsed HTTP header info (see httpreq.h)
+*/
+void SendHTTPError(int ErrorNum, char *ErrorTitleStr, char *ErrorDescStr, 
+		   RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
Index: /gsdl/trunk/gs2runtime/src/w32server/httpsrv.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpsrv.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpsrv.cpp	(revision 16561)
@@ -0,0 +1,103 @@
+/**********************************************************************
+ *
+ * httpsrc.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#pragma hdrstop
+#include "netio.h"
+#include "settings.h"
+#include "httpreq.h"
+#include "httpsrv.h"
+#include "locate.h"
+
+//Private Functions
+
+/*
+Function Name: HTTP Server Answer
+Purpose: Answers messages saying that a connection is ready to start
+*/
+void HTTPServerAnswer();
+
+//Module Vars
+static SOCKET ServerSocket = INVALID_SOCKET;
+
+static HWND MsgWindow;
+
+/******************************************************************************/
+// returns 0 on success, and a WSA error otherwise
+int StartHTTPServer(HWND PassedMsgWindow) {
+  
+  MsgWindow = PassedMsgWindow;
+  //Create the server socket
+  return CreateListeningSocket(gsdl_port_num, MsgWindow, HTTP_SERVER_MSG, ServerSocket);
+}
+
+/******************************************************************************/
+void EndHTTPServer()
+{
+  //Stop the listening socket
+  DestroyListeningSocket(ServerSocket, MsgWindow);
+}
+
+/******************************************************************************/
+void ProcessHTTPServerMsg(WPARAM Socket, LPARAM MsgInfo) {
+  log_message("accept message arrived\n");
+  if (Socket != ServerSocket) {
+    if (gsdl_keep_log || gsdl_show_console) log_message("Incorrect socket sent in message");
+  }
+  switch (WSAGETSELECTEVENT(MsgInfo)) {
+  case FD_ACCEPT:
+    switch (WSAGETSELECTERROR(MsgInfo)) {
+    case WSAENETDOWN:
+      LogCriticalError("1","Network Down");
+      break;
+    default: //No Error
+      HTTPServerAnswer();
+    }
+    break;
+  }
+}
+
+/******************************************************************************/
+void HTTPServerAnswer() {
+  SOCKADDR_IN ClientSockAddr;
+  SOCKET ClientSocket;
+  int AddrLen = sizeof(SOCKADDR_IN);
+  RequestThreadMessageT Parameters;
+  while (AnswerListeningSocket(ServerSocket, ClientSocket, ClientSockAddr, AddrLen) == 0) {
+    log_message("Answering socket\n");
+    //Call the thread procedure to handle the connection
+    Parameters.ClientSockAddr = ClientSockAddr;
+    Parameters.ClientSocket = ClientSocket;
+    Parameters.AddrLen = AddrLen;
+    RequestThread(&Parameters);
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/httpsrv.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/httpsrv.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/httpsrv.h	(revision 16561)
@@ -0,0 +1,57 @@
+/**********************************************************************
+ *
+ * httpsrv.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#define HTTP_SERVER_MSG WM_USER + 23
+
+/*
+Function Name: Start HTTP Server
+
+Purpose: Starts the HTTP server.  Inits Winsock, MIME, loads registry
+         stuff, etc.
+
+Parameters:
+	PassedMsgWindow - The handle of the window that handles messages.  Needed
+                          to setup WSASelect notification of socket events and
+                          to send a shutdown message on error.  */
+
+// returns 0 on success, and a WSA error otherwise
+int StartHTTPServer(HWND PassedMsgWindow);
+
+/*
+Function Name: End HTTP Server
+Purpose: Ends the HTTP server.  Cleans up MIME, users, etc.
+*/
+void EndHTTPServer();
+
+/*
+Function Name: Process HTTP Server Message
+Purpose: Processes HTTP_SERVER_MSG messages.
+Parameters:
+	Socket - The socket the message is regarding (WPARAM)
+	MsgInfo - The messsage (LPARAM)
+*/
+void ProcessHTTPServerMsg(WPARAM Socket, LPARAM MsgInfo);
Index: /gsdl/trunk/gs2runtime/src/w32server/locate.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/locate.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/locate.cpp	(revision 16561)
@@ -0,0 +1,274 @@
+/**********************************************************************
+ *
+ * locate.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include <windows.h>
+#include <stdio.h>
+#include "locate.h"
+#include "settings.h"
+
+/* The following values are inserted or used by FNORD.CPP */
+HWND GSDL_Window;
+int line_spacing;
+RECT text_rect;
+
+
+static FILE *log_file=NULL;
+
+static int location_found=0;
+text_t data_location;
+
+void set_location(text_t possible)
+{
+  if (location_found != 0) return;
+  if (!possible.empty()) {
+    data_location = possible;
+    if (*(data_location.end()-1) != '\\') {
+      data_location.push_back('\\');
+    }
+    location_found = 1;
+  }
+}
+
+void find_location(void)
+{
+  if (location_found != 0) return;
+  
+  char here[MAX_FILENAME_SIZE];
+  char *send;
+
+  if (GetModuleFileName(NULL,here,MAX_FILENAME_SIZE) <= 0) {
+    // failed
+    data_location.clear();
+  } else {
+    send = strrchr(here,'\\');
+    if (send != NULL) {
+      ++send;
+    } else {
+      send = strchr(here,':');
+      if (send != NULL) {
+        ++send;
+      } else {
+        send = here;
+      }
+    }
+  }
+  *send = 0;
+  data_location.setcstr(here);
+  location_found = 1;
+}
+
+void open_log_file()
+{
+  if (log_file != NULL) fclose(log_file);
+  log_file = fopen(gsdl_log_name, "a");
+  if (log_file != NULL) {
+	  fprintf (log_file, "\n\n-----------\n");
+	  fprintf (log_file, "log started\n");
+	  fprintf (log_file, "-----------\n\n");
+  }
+}
+
+void close_log_file()
+{
+  if (log_file != NULL) {
+    fclose(log_file);
+    log_file = NULL;
+  }
+}
+
+#define CONSOLE_BUFFER_SIZE 2048
+char console_buffer[CONSOLE_BUFFER_SIZE];
+char *next_console, *last_console;
+int console_top, console_y;
+
+void activate_console(void)
+{
+  gsdl_show_console = 1;
+  next_console = console_buffer;
+  *next_console = 0;
+  last_console = next_console;
+  console_y = console_top = text_rect.top +
+    (text_rect.bottom - text_rect.top + 1) % line_spacing / 2;
+  InvalidateRect(GSDL_Window,NULL,TRUE);
+}
+
+void deactivate_console(void)
+{
+  gsdl_show_console = 0;
+  InvalidateRect(GSDL_Window,NULL,TRUE);
+}
+
+void refresh_console(HDC dc)
+{
+  int loc;  char *b, *e;
+  loc = console_top;
+  for (e = console_buffer;;) {
+    b = e;
+    while (*e >= ' ') ++e;
+    TextOut(dc,0,loc,b,e-b);
+    loc += line_spacing;
+    if (*e == 0) break;
+    ++e;
+    if (*e == 0) break;
+    }
+}
+
+void scroll(HDC dc, int nbits)
+{
+  RECT scroll, clip, update;
+
+  scroll.left = clip.left = text_rect.left;
+  scroll.right = clip.right = text_rect.right;
+  clip.top = console_top;
+  clip.bottom = text_rect.bottom;
+  scroll.top = clip.top + nbits;
+  scroll.bottom = text_rect.bottom;
+  ScrollDC(dc,0,-nbits,&scroll,&clip,NULL,&update);
+  FillRect(dc,&update, (HBRUSH)GetStockObject(WHITE_BRUSH));
+}
+
+int remove_line()
+{
+  char *bin, *bout;
+  bin = bout = console_buffer;
+  while (*bin >= ' ') ++bin;
+  if (*bin != 0) {
+    ++bin;
+    if (*bin != 0) {
+      while (*bin != 0) *bout++ = *bin++;
+      *bout = *bin;
+      last_console -= (bin - bout);
+      next_console = bout;
+      return line_spacing;
+      }
+    }
+  next_console = console_buffer;
+  *next_console = 0;
+  last_console = next_console;
+  return 0;
+}
+
+void display_line(HDC dc, char *line, int nch)
+{
+  int scroll_needed = 0;
+  while (next_console + nch + 1 
+         > console_buffer + CONSOLE_BUFFER_SIZE)
+    scroll_needed += remove_line();
+  console_y -= scroll_needed;
+  if (*last_console == '\n') {
+    console_y += line_spacing;
+    if (console_y + line_spacing > text_rect.bottom) {
+      scroll_needed += remove_line();
+      console_y -= line_spacing;
+      }
+    last_console = next_console;
+    }
+  if (scroll_needed != 0) scroll(dc, scroll_needed);
+  while (nch-- > 0) *next_console++ = *line++;
+  TextOut(dc,0,console_y,last_console,next_console-last_console);
+  *next_console = *line;
+  if (*next_console != 0) {
+    last_console = next_console;
+    ++next_console;
+    *next_console = 0;
+    }
+}
+
+void display_text(HDC dc, char *buffer)
+{
+  char *bl, *bp;
+  for (bl = buffer;;) {
+    for (bp = bl;;) {
+      if (*bp == '\t') *bp = ' ';
+      if (*bp < ' ') break;
+      ++bp;
+      }
+    if (*bp != 0) *bp = '\n';
+    display_line(dc, bl, bp-bl);
+    if (*bp == 0) break;
+    bl = bp+1;
+    if (*bl == 0) break;
+    }
+}
+
+void log_message(char *msg)
+{
+  if (log_file != NULL) {
+    fprintf(log_file,"%s",msg);
+    fflush(log_file);
+  }
+  if (gsdl_show_console) {
+    char buffer[1024];  HDC dc;
+    sprintf(buffer,"%s",msg);
+    dc = GetDC(GSDL_Window);
+    display_text(dc,buffer);
+    ReleaseDC(GSDL_Window,dc);
+  }
+}
+
+void log_message_N(char*msg, int n)
+{
+  if (log_file != NULL) {
+    int len = 0;
+    while (len < n) {
+      fputc (msg[len], log_file);
+      ++len;
+    }
+    fflush(log_file);
+  }
+  
+  // these messages are not send to the console
+  if (gsdl_show_console) {
+    HDC dc;
+    dc = GetDC(GSDL_Window);
+    display_text(dc,"message only written to log file\n");
+    ReleaseDC(GSDL_Window,dc);
+  }
+}
+
+void LogCriticalError(char *cderr, char *Msg)
+{
+  char MsgBoxStr[200];
+  
+  strcpy(MsgBoxStr, "Critical Error Number ");
+  strcat(MsgBoxStr, cderr);
+  strcat(MsgBoxStr, "\n");
+  strcat(MsgBoxStr, Msg);
+  strcat(MsgBoxStr, "\nGreenstone Digital Library software\nshutting down");
+  MessageBox(GSDL_Window, MsgBoxStr, "Greenstone Digital Library Software",
+	     MB_OK | MB_ICONERROR);
+  PostMessage(GSDL_Window, WM_DESTROY, 0, 0);
+}
+
+
+/* returns second-first taking into account wrap around */
+DWORD DiffTickCounts (DWORD first, DWORD second) 
+{
+  if (second >= first) return (second-first);
+  return (MAXDWORD-first+1+second);
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/locate.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/locate.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/locate.h	(revision 16561)
@@ -0,0 +1,56 @@
+/**********************************************************************
+ *
+ * locate.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#ifndef MAX_FILENAME_SIZE
+#define MAX_FILENAME_SIZE 2048
+#endif
+
+#include "text_t.h"
+
+extern HWND GSDL_Window;
+extern int line_spacing;
+extern RECT text_rect;
+
+void refresh_console(HDC dc);
+
+extern text_t data_location;
+
+extern void set_location(text_t possible);
+extern void find_location(void);
+
+extern void open_log_file(void);
+extern void close_log_file(void);
+extern void log_message(char *msg);
+extern void log_message_N(char*msg, int n);
+
+extern void activate_console(void);
+extern void deactivate_console(void);
+
+extern void LogCriticalError(char *cderr, char *Msg);
+
+/* returns first-second taking into account wrap around */
+extern DWORD DiffTickCounts (DWORD first, DWORD second);
Index: /gsdl/trunk/gs2runtime/src/w32server/netio.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/netio.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/netio.cpp	(revision 16561)
@@ -0,0 +1,465 @@
+/**********************************************************************
+ *
+ * netio.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#pragma hdrstop
+#include "netio.h"
+#include "locate.h"
+#include "d_winsock.h"
+
+//Private Variables and Declarations
+
+//Max string size for a host name
+#define MAXHOSTNAME 100
+
+//Timeout for waiting between "packets" in seconds
+#define NETIO_CONN_TIMEOUT 20 //3 sec  was 180 @@@@@ WJR
+
+#define HTTP_DNS_MSG WM_USER + 1
+int dns_msg_received = 0;
+int dns_msg_error = 0;
+
+DWORD LocalIPNumber;
+char LocalName[MAXHOSTNAME];
+
+//Private Function Declarations
+DWORD GetHostID();
+
+// returns 0 on success,
+// WSASYSNOTREADY, WSAVERNOTSUPPORTED, or WSAEINVAL on failure
+int InitNetIO() {
+  WSADATA Data;
+  
+  // Load Winsock
+  int err = d_WSAStartup(MAKEWORD(1, 1), &Data);
+  
+  // get local IP number
+  if (err == 0) LocalIPNumber = GetHostID();
+  else LocalIPNumber = INADDR_ANY;
+  
+  // set the Local Name to "" for now
+  LocalName[0] = 0;
+  
+  return err;
+}
+
+
+void CleanUpNetIO() {
+  //Experimental, heard this will better clean up, especialy if a call is
+  //blocking....
+  d_WSAUnhookBlockingHook();
+
+  //Clean up Winsock
+  d_WSACleanup();
+}
+
+
+void ResetNetIO() {
+  //Figure out the local name on first querry, set to "" until then...
+  LocalName[0] = 0;
+}
+
+
+DWORD GetLocalIP() {
+  return LocalIPNumber;
+}
+
+long __stdcall DNSWindowProc(HWND hWnd,UINT wMsg, WPARAM wParam, LPARAM lParam) {
+  if (wMsg == HTTP_DNS_MSG) {
+    dns_msg_received = 1;
+    dns_msg_error = WSAGETASYNCERROR(lParam);
+    return 0;
+  }
+  
+  return DefWindowProc(hWnd, wMsg, wParam, lParam);
+}
+
+char *GetLocalName(HINSTANCE hInstance) {
+  // static in case it is written to after the function has finished
+  // (I did not error checking on WSACancelAsyncRequest)
+  static char buf[MAXGETHOSTSTRUCT];
+	
+  if (LocalName[0] == 0) {
+    hostent *DNSResult = NULL;
+    in_addr LocalInAddr;
+    
+    // if we failed to get the local IP number
+    // use the loop-back device
+    if (LocalIPNumber == INADDR_ANY) {
+      strcpy(LocalName, "127.0.0.1"); // loop-back device
+      return LocalName;
+    }
+    
+    //Convert the number to an in_addr struct
+    LocalInAddr.s_addr = LocalIPNumber;
+    
+    // if we fail to find the domain name we will
+    // still want the IP address
+    strcpy(LocalName, d_inet_ntoa(LocalInAddr));
+    
+    // make sure they actually passed in an instance handle
+    if (hInstance == NULL) return LocalName;
+    
+    // do a async domain name lookup so that we
+    // can control the timeout
+    
+    // create a window class and window to handle the async messages
+    WNDCLASS wc; HWND hwndDNS;
+    
+    wc.style = 0;
+    wc.lpfnWndProc = DNSWindowProc;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = 0;
+    wc.hInstance = hInstance;
+    wc.hIcon = NULL;
+    wc.hCursor = NULL;
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = "GSDL DNS Window";
+    if (!RegisterClass(&wc)) return LocalName;
+    
+    hwndDNS = CreateWindow("GSDL DNS Window", "", 
+			   WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 
+			   NULL, NULL, hInstance, NULL);
+    if (!hwndDNS) return LocalName;
+    
+    // process all messages currently on the queue
+    MSG Message;
+    while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
+      TranslateMessage(&Message); /* translate keyboard messages */
+      DispatchMessage(&Message);  /* return control to Windows NT */
+    }
+    
+    //Do a async DNS lookup on the IP number
+    dns_msg_received = 0;
+    dns_msg_error = 0;
+    HANDLE asyncGetHostReq = d_WSAAsyncGetHostByAddr(
+						     hwndDNS, HTTP_DNS_MSG, (char *)&(LocalInAddr), 
+						     4, PF_INET, buf, MAXGETHOSTSTRUCT);
+    
+    if (asyncGetHostReq != NULL) {
+      // wait 5 seconds for the request to complete
+      int now = GetTickCount();
+      while ((DiffTickCounts(now, GetTickCount()) < 5000) && !dns_msg_received) {
+	if (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) {
+	  TranslateMessage(&Message); /* translate keyboard messages */
+	  DispatchMessage(&Message);  /* return control to Windows NT */
+	} else {
+	  Sleep(1);
+	}
+      }
+      if (!dns_msg_received)
+	d_WSACancelAsyncRequest(asyncGetHostReq);
+    }
+    
+    DestroyWindow(hwndDNS);
+    
+    if (dns_msg_received && (dns_msg_error == 0)) {
+      //Worked, use the primary name
+      strcpy(LocalName, ((hostent *)(buf))->h_name);
+      //Convert it to lower case for cosmedic reasons
+      CharLower(LocalName);
+    }
+  }
+  return LocalName;
+}
+
+
+// returns 0 on success, and a WSA error message on failure.
+// possible error messages include:
+// WSANOTINITIALISED, WSAENETDOWN, WSAEAFNOSUPPORT, WSAEINPROGRESS, WSAEMFILE,
+// WSAENOBUFS, WSAEPROTONOSUPPORT, WSAEPROTOTYPE, WSAESOCKTNOSUPPORT,
+// WSAEADDRINUSE, WSAEINVAL, WSAEISCONN, WSAENOTSOCK, WSAEOPNOTSUPP
+
+#define MAXBINDCOUNT 4096
+int CreateListeningSocket(int &PortNum, HWND MsgWindow, 
+			  WORD SocketMsg, SOCKET &ServerSocket) {
+  int err = 0;
+  SOCKADDR_IN ServerSockAddr;
+  int bind_port, bind_count;
+  
+  //Create the Server Socket
+  ServerSocket = d_socket(AF_INET, SOCK_STREAM, 0);
+  if (ServerSocket == INVALID_SOCKET) return d_WSAGetLastError();
+  
+  bind_port = PortNum;
+  for (bind_count=0; bind_count < MAXBINDCOUNT; ++bind_count) {
+    // Set up the Server Socket Address
+    memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); //Needed?
+    ServerSockAddr.sin_port = d_htons( (WORD) bind_port);
+    ServerSockAddr.sin_family = AF_INET;
+    ServerSockAddr.sin_addr.s_addr = d_htonl(INADDR_ANY);
+    
+    // Try to bind the socket with the address
+    if (d_bind(ServerSocket, (LPSOCKADDR) &ServerSockAddr,
+	       sizeof(ServerSockAddr)) != SOCKET_ERROR) {
+      PortNum = bind_port;
+      break;
+    }
+    
+    // make sure it failed to bind because it was
+    // already bound
+    err = d_WSAGetLastError ();
+    if (err != WSAEADDRINUSE) return err;
+    
+    // Or choose another port number to try
+    if (bind_port == 80) bind_port = IPPORT_RESERVED+1;
+    else if (bind_count == 0) bind_port = 80;
+    else ++bind_port;
+  }
+  
+  // return an error in we couldn't find a valid port
+  if (bind_count == MAXBINDCOUNT) return WSAEADDRINUSE;
+  
+  // Start listening for connections
+  if (d_listen(ServerSocket, SOMAXCONN) == SOCKET_ERROR)
+    return d_WSAGetLastError ();	
+  
+  // Set up event for new connections
+  if (d_WSAAsyncSelect(ServerSocket, MsgWindow, SocketMsg, FD_ACCEPT) == SOCKET_ERROR)
+    return d_WSAGetLastError ();
+  
+  return 0;
+}
+
+
+int AnswerListeningSocket(SOCKET ServerSocket, SOCKET &ClientSocket, 
+			  SOCKADDR_IN &ClientSockAddr, int AddrLen) {
+  if (d_WSAIsBlocking()) {
+    log_message("rejected connect due blocking\n");
+    return -1;
+  }
+  
+  ClientSocket = d_accept(ServerSocket, (LPSOCKADDR) &ClientSockAddr, &AddrLen);
+  if (ClientSocket == INVALID_SOCKET) {
+    log_message("accept failed - connection lost\n");
+    return -1;
+  }
+  
+  log_message("accept success - connection made\n");
+  return 0;
+}
+
+void DestroyListeningSocket(SOCKET &ServerSocket, HWND MsgWindow) {
+  //Remove any message notification
+  d_WSAAsyncSelect(ServerSocket, MsgWindow, 0, 0);
+  
+  //Close the socket
+  CloseSocket(ServerSocket);
+}
+
+void CloseSocket(SOCKET &TargetSocket) {
+  if (TargetSocket != INVALID_SOCKET) {
+    //Since we're closing the socket, there's not much I can do about errors
+    //now so I'm not gonna bother checking...
+    
+    //Shutdown both ends, assume we have all data...
+    d_shutdown(TargetSocket, 2);
+    
+    d_closesocket(TargetSocket);
+    
+    //Make sure we can't use the old handle again...
+    TargetSocket = INVALID_SOCKET;
+  }
+}
+
+int GetData(SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize,
+	    int ThreadNum) {
+  
+  int NumRecv;
+  int Error;
+  struct timeval Timeout;
+  fd_set SocketSet;
+  
+  //Set up a socket set structure with just ClientSocket for  select(..)
+  FD_ZERO(&SocketSet);
+  FD_SET(ClientSocket, &SocketSet);
+  
+  //set timeout
+  Timeout.tv_sec = NETIO_CONN_TIMEOUT;
+  Timeout.tv_usec = 0;
+  
+  do {
+    NumRecv = d_recv(ClientSocket, (char *) IOBuffer, IOBufferSize, 0);
+    if (NumRecv == 0) {
+      //Lost connect
+      return -1;
+      
+    } else if (NumRecv == SOCKET_ERROR) {
+      Error = d_WSAGetLastError();
+      if (Error == WSAEWOULDBLOCK) {
+	NumRecv = 0;
+	//Wait for socket to be readable
+	if (d_select(0, &SocketSet, NULL, NULL, &Timeout) != 1) {
+	  //Timeout
+	  return -1;
+	}
+	
+      } else {
+	//Assume connection terminated
+	return -1;
+      }
+    }
+  } while(NumRecv == 0);
+  return NumRecv;
+}
+
+int GetLine(text_t &OutStr, SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize,
+	    int &BufferIndex, int &DataInBuffer, int ThreadNum) {
+
+  OutStr.clear();
+  char CurChar;
+  
+  do {
+    if (BufferIndex == DataInBuffer) { //Need more data
+      DataInBuffer = GetData(ClientSocket, IOBuffer, IOBufferSize, ThreadNum);
+      if (DataInBuffer == -1) {
+	//Lost connect
+	return -1;
+      }
+      BufferIndex = 0;
+    }
+    CurChar = IOBuffer[BufferIndex];
+    ++BufferIndex;
+    if ((CurChar != 10) && (CurChar != 13))  {
+      OutStr.push_back(CurChar);
+    }
+  } while (CurChar != 10);
+  
+  return 0;
+}
+
+int SendData(SOCKET ClientSocket, BYTE *SendBuffer, int NumToSend,
+	     int ThreadNum) {
+  
+  int NumSent = 0;
+  int Error;
+  struct timeval Timeout;
+  fd_set SocketSet;
+  
+  char NumSentStr[50];
+  itoa(NumToSend, NumSentStr, 10);
+  
+  //Set up a socket set structure with just ClientSocket for  select(..)
+  FD_ZERO(&SocketSet);
+  FD_SET(ClientSocket, &SocketSet);
+  //set timeout
+  Timeout.tv_sec = NETIO_CONN_TIMEOUT;
+  Timeout.tv_usec = 0;
+  
+  while (NumToSend > 0) {
+    NumSent = d_send(ClientSocket, (char *) SendBuffer + NumSent, NumToSend, 0);
+    if (NumSent == 0) {
+      //Lost connect
+      return -1;
+    }
+    else if (NumSent == SOCKET_ERROR) {
+      Error = d_WSAGetLastError();
+      if (Error == WSAEWOULDBLOCK) {
+	NumSent = 0;
+	if (d_select(0, NULL, &SocketSet, NULL, &Timeout) != 1) {
+	  //Timeout
+	  return -1;
+	}
+      }
+      else {
+	//Lost Connection
+	return -1;
+      }
+    }
+    NumToSend -= NumSent;
+  }
+  return 0;
+}
+
+
+/******************************************************************************/
+//Private Functions
+/******************************************************************************/
+/*-----------------------------------------------------------
+ * Function: GetHostID()
+ *
+ * Description:
+ *  Get the Local IP address using the following algorithm:
+ *    - get local hostname with gethostname()
+ *    - attempt to resolve local hostname with gethostbyname()
+ *    if that fails:
+ *    - get a UDP socket
+ *    - connect UDP socket to arbitrary address and port
+ *    - use getsockname() to get local address
+ *
+ * Notes: Copyright by Bob Quinn, 1995, taken from his Winsock library
+ *        was removed from its original module unmodified
+ */
+DWORD GetHostID () {
+  char szLclHost [MAXHOSTNAME];
+  LPHOSTENT lpstHostent;
+  SOCKADDR_IN stLclAddr;
+  SOCKADDR_IN stRmtAddr;
+  int nAddrSize = sizeof(SOCKADDR);
+  SOCKET hSock;
+  int nRet;
+  
+  /* Init local address (to zero) */
+  stLclAddr.sin_addr.s_addr = INADDR_ANY;
+  
+  /* Get the local hostname */
+  nRet = d_gethostname(szLclHost, MAXHOSTNAME);
+  if (nRet != SOCKET_ERROR) {
+    /* Resolve hostname for local address */
+    lpstHostent = d_gethostbyname((LPSTR)szLclHost);
+    if (lpstHostent)
+      stLclAddr.sin_addr.s_addr = *((u_long FAR*) (lpstHostent->h_addr));
+  }
+  
+  /* If still not resolved, then try second strategy */
+  if (stLclAddr.sin_addr.s_addr == INADDR_ANY) {
+    /* Get a UDP socket */
+    hSock = d_socket(AF_INET, SOCK_DGRAM, 0);
+    if (hSock != INVALID_SOCKET)  {
+      /* Connect to arbitrary port and address (NOT loopback) */
+      stRmtAddr.sin_family = AF_INET;
+      stRmtAddr.sin_port   = d_htons(IPPORT_ECHO);
+      stRmtAddr.sin_addr.s_addr = d_inet_addr("128.127.50.1");
+      nRet = d_connect(hSock,
+		       (LPSOCKADDR)&stRmtAddr,
+		       sizeof(SOCKADDR));
+      if (nRet != SOCKET_ERROR) {
+	/* Get local address */
+	d_getsockname(hSock,
+		      (LPSOCKADDR)&stLclAddr,
+		      (int FAR*)&nAddrSize);
+      }
+      d_closesocket(hSock);   /* we're done with the socket */
+    }
+  }
+  return (stLclAddr.sin_addr.s_addr);
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/netio.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/netio.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/netio.h	(revision 16561)
@@ -0,0 +1,160 @@
+/**********************************************************************
+ *
+ * netio.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+
+/*
+Module Name: Net IO
+Purpose: High level Socket IO and Misc socket routines
+Public Functions:
+	InitNetIO
+   CleanUpNetIO
+
+	GetLocalIP
+	GetLocalName
+
+   CreateListeningSocket
+   AnswerListeningSocket
+   DestroyListeningSocket
+
+   CloseSocket
+
+	GetData
+	GetLine
+
+	SendData
+	SendFile
+*/
+
+//Public Functions
+
+/*
+Function Name: Init Net IO
+Purpose: Initializes the module so calls can be made querying network
+         information for the local macnine.
+Notes: MUST be called before GetLocalIP and GetLocalName
+*/
+
+// returns 0 on success,
+// WSASYSNOTREADY, WSAVERNOTSUPPORTED, or WSAEINVAL on failure
+int InitNetIO();
+
+/*
+Function Name: Cleanup Net IO
+Purpose: Cleans up the module and unloads Winsock
+*/
+void CleanUpNetIO();
+
+/*
+Function Name: Reset Net IO
+Purpose: Resets internal data in NetIO to reflect registry changes, etc.
+*/
+void ResetNetIO();
+
+/*
+Function Name: Get Local IP
+Purpose: Gets the local IP number
+Returns: The local IP number as an double word (4 bytes)
+*/
+DWORD GetLocalIP();
+
+/*
+Function Name: Get Local Name
+Purpose: Gets the local Domain name, or if there is none, then the server
+address in dotted format.
+Returns: A string containing the local address
+*/
+char *GetLocalName(HINSTANCE hInstance);
+
+// returns 0 on success, and a WSA error message on failure.
+// possible error messages include:
+// WSANOTINITIALISED, WSAENETDOWN, WSAEAFNOSUPPORT, WSAEINPROGRESS, WSAEMFILE,
+// WSAENOBUFS, WSAEPROTONOSUPPORT, WSAEPROTOTYPE, WSAESOCKTNOSUPPORT,
+// WSAEADDRINUSE, WSAEINVAL, WSAEISCONN, WSAENOTSOCK, WSAEOPNOTSUPP
+int CreateListeningSocket(int &PortNum, HWND MsgWindow, 
+			  WORD SocketMsg, SOCKET &ServerSocket);
+
+int AnswerListeningSocket(SOCKET ServerSocket, SOCKET &ClientSocket, 
+			  SOCKADDR_IN &ClientSockAddr, int AddrLen);
+
+void DestroyListeningSocket(SOCKET &ServerSocket, HWND MsgWindow);
+
+void CloseSocket(SOCKET &TargetSocket);
+
+/*
+Function Name: Get Data
+Purpose: Gets data from a socket with muli-threaded debugging
+Parameters:
+	ClientSocket - The socket to get data from
+	IOBuffer - A pointer to a chunk of memory to put data in
+	IOBufferSize - Size of the buffer
+	ThreadNum - Number of the thread that called this functions used for debugging
+
+Returns: -1 on error, else the number of bytes received
+
+Notes:
+Removed socket error checking because I was unable to isolate what
+errors I was getting when getting a "rude" disconnect
+*/
+int GetData(SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize, int ThreadNum);
+
+/*
+Function Name: Get Line
+Purpose: Gets a line of text (ended by a LF (10)) from a socket with buffered
+			input.  If the buffer is empty this function fills the buffer, else it
+			just changes an index. Also has the ability to do multi-threaded debuging
+Parameters:
+	OutStr - Varible were output is put
+	ClientSocket - The socket to get data from
+	IOBuffer - A pointer to a chunk of memory to put data in
+	IOBufferSize - Size of the buffer
+	BufferIndex - A zero based index of what charactor in the buffer we're at
+	DataInBuffer - Amount of data in the buffer
+	ThreadNum - Number of the thread that called this functions used for debugging
+
+Returns: -1 on disconnect or error (abort connection), 0 on success
+*/
+int GetLine(text_t &OutStr, SOCKET ClientSocket, BYTE *IOBuffer, int IOBufferSize, 
+	    int &BufferIndex, int &DataInBuffer, int ThreadNum);
+
+/*
+Function Name: Send Data
+Purpose: Sends data to a socket with muli-thread debugging
+Parameters:
+	ClientSocket - The socket to get data from
+	SendBuffer - A pointer to a chunk of memory to put data in
+	NumToSend - Number of bytes to send from buffer
+	ThreadNum - Number of the thread that called this functions used for debugging
+
+Returns: -1 on error, 0 on success
+
+Notes:
+Removed socket error checking because I was unable to isolate what
+errors I was getting when getting a "rude" disconnect
+*/
+int SendData(SOCKET ClientSocket, BYTE *SendBuffer, 
+	     int NumToSend, int ThreadNum);
Index: /gsdl/trunk/gs2runtime/src/w32server/parse.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/parse.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/parse.cpp	(revision 16561)
@@ -0,0 +1,205 @@
+/**********************************************************************
+ *
+ * parse.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#pragma hdrstop
+
+#include "parse.h"
+
+//Public Functions
+/******************************************************************************/
+void Left(char *DestStr, const char *SourceStr, const int Len) {
+  memcpy(DestStr, SourceStr, Len);
+  DestStr[Len] = 0;
+}
+/******************************************************************************/
+void Right(char *DestStr, const char *SourceStr, const int Len) {
+  int SourceLen, Offset;
+  
+  SourceLen = strlen(SourceStr);
+  Offset = SourceLen - Len;
+  
+  memcpy(DestStr, SourceStr + Offset, Len + 1);
+  //The null is already copied
+}
+/******************************************************************************/
+void TrimLeft(char *TargetStr) {
+  int i, NewStrSize;
+  
+  i = 0;
+  //Find the first non-space charactor
+  while ( (TargetStr[i] == ' ') || (TargetStr[i] == '\t') ) {
+    ++i;
+  }
+  //If we have space to get rid of, move the string
+  if (i > 0) {
+    NewStrSize = strlen(TargetStr) - i + 1; //Include the NULL in the size
+    memmove(TargetStr, TargetStr + i, NewStrSize);
+  }
+}
+/******************************************************************************/
+void TrimRight(char *TargetStr) {
+  int i;
+  
+  //Find the last non-space charactor (index i)
+  i = strlen(TargetStr) - 1;
+  while ((i >= 0) && ((TargetStr[i] == ' ') || (TargetStr[i] == '\t')) ) {
+    --i;
+  }
+  //Set the NULL 1 after the last non-space charactor
+  TargetStr[i+1] = 0;
+}
+/******************************************************************************/
+void Trim(char *TargetStr) {
+  TrimRight(TargetStr);
+  TrimLeft(TargetStr);
+}
+/******************************************************************************/
+void GetWord(text_t &DestStr, text_t::const_iterator first,
+	     text_t::const_iterator last, text_t::const_iterator &next) {
+
+  DestStr.clear();
+  next = last;
+
+  while (first != last) {
+    if (*first == ' ' || *first == '\t') {
+      ++first;
+      while ((first != last) && ((*first == ' ') || (*first == '\t'))) {
+	++first;
+      }
+      next = first;
+      break;
+    } else {
+      DestStr.push_back(*first);
+    }
+    ++first;
+  }
+}
+/******************************************************************************/
+
+void GetLastWord(text_t &DestStr, text_t::const_iterator first, text_t::const_iterator last, 
+		 text_t::const_iterator &Start) {
+
+  DestStr.clear();
+  Start = first;
+
+  while (first != last) {
+    if ((*first == ' ') || (*first == '\t')) {
+      DestStr.clear();
+      Start = first+1;
+    } else {
+      DestStr.push_back(*first);
+    }
+    ++first;
+  }
+}
+/******************************************************************************/
+void SplitPath(char *Path, char *Dir, char *FileName) {
+  int SplitPoint;
+  SplitPoint = strlen(Path) - 1;
+  while ((SplitPoint > 0) && (Path[SplitPoint] != '\\')) {
+    --SplitPoint;
+  }
+  if ((SplitPoint == 0) && (Path[SplitPoint] != '\\')) {
+    //No directory, just a file name
+    Dir[0] = 0;
+    strcpy(FileName, Path);
+  }
+  else {
+    //Move past the last slash
+    ++SplitPoint;
+    
+    //Directory
+    int i = 0;
+    while (i < SplitPoint) {
+      Dir[i] = Path[i];
+      ++i;
+    }
+    Dir[i] = 0;
+    
+    //File name
+    int j = 0;
+    while (Path[i] != 0) {
+      FileName[j] = Path[i];
+      ++j;
+      ++i;
+    }
+    FileName[j] = 0;
+  }
+}
+/******************************************************************************/
+void GetExtention(char *Path, char *Extention) {
+  int Dot;
+  int PathLen;
+  
+  PathLen = strlen(Path) - 1;
+  Dot = PathLen;
+  while ((Dot > 0) && (Path[Dot] != '.')) {
+    --Dot;
+  }
+  if (Dot > 0) {
+    strcpy(Extention, Path + Dot + 1);
+  }
+  else {
+    Extention[0] = 0;
+  }
+}
+/******************************************************************************/
+void TranslateEscapeString(char *TargetStr) {
+  int i = 0;
+  int j = 0;
+  
+  while (TargetStr[i] != 0) {
+    if ((TargetStr[i] == '%') && (TargetStr[i+1] != 0) && (TargetStr[i+2] != 0)) {
+      //Escaped value (did checking to make sure there are 2 more chars)
+      //         #pragma warn -sig //Suppress significant digit loss warning (BC++)
+      TargetStr[j] = 16 * HexVal(TargetStr[i+1]) + HexVal(TargetStr[i+2]);
+      //         #pragma warn +sig   //Restore significant digit loss warning (BC++)
+      i += 3;
+    }
+    else {
+      TargetStr[j] = TargetStr[i];
+      ++i;
+    }
+    ++j;
+  }
+  TargetStr[j] = 0;
+}
+/******************************************************************************/
+int HexVal(char c) {
+  //   #pragma warn -sig //Suppress significant digit loss warning (BC++)
+  if (c >= '0' && c <= '9') return c - '0';
+  else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+  else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+  else return 0;
+  //   #pragma warn +sig   //Restore significant digit loss warning (BC++)
+}
+/******************************************************************************/
Index: /gsdl/trunk/gs2runtime/src/w32server/parse.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/parse.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/parse.h	(revision 16561)
@@ -0,0 +1,162 @@
+/**********************************************************************
+ *
+ * parse.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+
+/*
+Module Name: Parse
+Purpose: General string parsing.  These are functions that are not in string.h,
+	but are found in many other libraries and high level languages.
+Public Functions:
+	Left
+	Right
+
+	Trim
+	TrimLeft
+	TrimRight
+
+	GetWord
+	GetLastWord
+
+   Split Path
+   GetExtention
+*/
+
+/*
+Function Name: Left
+Purpose: Copies the leftmost x number of charactors of a string to a buffer
+Parameters:
+	DestStr - Destination buffer for substring
+	SourceStr - Source string to make substing from
+	Len - Length of substring
+
+Notes: If Len is greater than strlen(SourceStr) a memory error may occur.
+*/
+void Left(char *DestStr, const char *SourceStr, const int Len);
+
+/*
+Function Name: Right
+Purpose: Copies the rightmost x number of charactors of a string to a buffer
+Parameters:
+	DestStr - Destination buffer for substring
+	SourceStr - Source string to make substing from
+	Len - Length of substring
+
+Notes: If Len is greater than strlen(SourceStr) then a memory error may
+		 occur.
+*/
+void Right(char *DestStr, const char *SourceStr, const int Len);
+
+/*
+Function Name: TrimLeft
+Purpose: Removes spaces and tabs from the left end of a string.
+Parameters:
+	TargetString - String to be trimmed
+*/
+void TrimLeft(char *TargetStr);
+
+/*
+Function Name: TrimRight
+Purpose: Removes spaces and tabs from the right end of a string.
+Parameters:
+	TargetString - String to be trimmed
+*/
+void TrimRight(char *TargetStr);
+
+/*
+Function Name: Trim
+Purpose: Removes spaces and tabs from both ends of a string.
+Parameters:
+	TargetString - String to be trimmed
+*/
+void Trim(char *TargetStr);
+
+/*
+Function Name: GetWord
+Purpose: Gets leftmost substring without a space in the string between the iterators
+         'here' and 'end'. Sets 'DestStr' to the substring. Sets the 'next' iterator 
+	 to the next non-space charactor in the string.  ' ' and '\t' and considered
+         spaces
+Parameters:
+	 DestString - Destination buffer for substring
+	 first - First character in source string
+	 last - end iterator (one past last character) of source string
+	 next - At end of function call is set to the location of next non-space
+               charactor in source string
+*/
+void GetWord(text_t &DestStr, text_t::const_iterator first,
+	     text_t::const_iterator last, text_t::const_iterator &next);
+
+/*
+Function Name: GetLastWord
+Purpose: Gets rightmost substring without a space sets Start to be the first
+         character of the substring in the original string.
+Parameters:
+	DestString - Destination buffer for substring
+	first and last - Source string to make substing from
+	Start - where the DestString starts at in the source string
+*/
+void GetLastWord(text_t &DestStr, text_t::const_iterator first, text_t::const_iterator last, 
+		 text_t::const_iterator &Start);
+
+
+/*
+Function Name: Split Path
+Purpose: Splits a path string into directory and file name
+Parameters:
+	Path - Path string (example "c:\Fnord\SomeFile.Ext")
+	Dir - Directory part of the Path (example "c:\Fnord\")
+	FileName - File name part of the path (example "SomeFile.Ext")
+*/
+void SplitPath(char *Path, char *Dir, char *FileName);
+
+/*
+Function Name: Get Extention
+Purpose: Splits a path string into directory and file name
+Parameters:
+	Path - Path string or file name (example "c:\Fnord\SomeFile.Ext")
+	Extention - Extention part of the path (example "Ext")
+*/
+void GetExtention(char *Path, char *Extention);
+
+/*
+Function Name: Translate Escape String
+Purpose: Translates all URL escape sequences, such as "%20" where "20" is
+	the resulting charactor in hexidecimal
+Parameters:
+	TargetStr - String that may contain URL escape sequences.  All sequences
+   	are decoded on return.
+*/
+void TranslateEscapeString(char *TargetStr);
+
+/*
+Function Name: Hex Value
+Purpose: Returns the value of a hexidecimal digit character as an integer
+Parameters:
+	c - Charactor to translate
+*/
+int HexVal(char c);
Index: /gsdl/trunk/gs2runtime/src/w32server/resource.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/resource.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/resource.h	(revision 16561)
@@ -0,0 +1,55 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by server.rc
+//
+#define TRAY_ICON                       101
+#define COMMS_DIALOG                    102
+#define COMMS_DIALOG_DEBUG              102
+#define Main_Menu                       103
+#define COMMS_DIALOG2                   103
+#define GSDL_LOGO                       108
+#define GSDL_COL_TITLE                  109
+#define IDD_DIALOG1                     118
+#define GSDL_ICBMP                      126
+#define APPLY_BUTTON                    1000
+#define REMOTE_CHECK                    1001
+#define PROXY_CHECK                     1002
+#define CONSOLE_CHECK                   1003
+#define PROXY_EDIT_BOX                  1004
+#define PROXY_PORT_EDIT_BOX             1005
+#define TrayID                          1006
+#define LOG_DEBUG_CHECK                 1007
+#define SERVER_PORT_EDIT_BOX            1008
+#define LOG_LOCAL_CHECK                 1009
+#define LOG_REMOTE_CHECK                1010
+#define LOG_NAME_BOX                    1011
+#define LOG_CHECK                       1012
+#define SAVE_NAME_BOX                   1013
+#define SAVE_CHECK                      1014
+#define ID_AUTO_ENTER_LIBRARY           1015
+#define ID_RADIO_NETSCAPE               1016
+#define ID_RADIO_IE                     1017
+#define ID_RADIO_OTHER                  1018
+#define ID_OTHER_NAME                   1019
+#define ID_BROWSE_BUTTON                1020
+#define ID_OK_BUTTON                    1021
+#define ID_CANCEL_BUTTON                1022
+#define ID_RADIO_DEFAULT                1023
+#define ID_ARM_NAME                     1024
+#define ID_ARM_IP                       1025
+#define ID_ARM_127_0_0_1                1026
+#define ID_ARM_LOCALHOST                1027
+#define ID_PROJECT_SETTINGS             40001
+#define ID_PROJECT_EXIT                 40002
+#define ID_INSTALL_NETSCAPE             40007
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        127
+#define _APS_NEXT_COMMAND_VALUE         40011
+#define _APS_NEXT_CONTROL_VALUE         1030
+#define _APS_NEXT_SYMED_VALUE           102
+#endif
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/settings.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/settings.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/settings.cpp	(revision 16561)
@@ -0,0 +1,924 @@
+/**********************************************************************
+ *
+ * settings.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include "fileutil.h"
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\fstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <fstream.h>
+#else
+#  include <fstream>
+#endif
+
+#include <windows.h>
+#include "httpsrv.h"
+#include "settings.h"
+#include "locate.h"
+#include "resource.h"
+#include "cgiwrapper.h"
+
+// library settings
+text_t gsdl_enterlib;
+text_t gsdl_gsdlhome;
+text_t gsdl_collecthome;
+text_t gsdl_dbhome;
+text_t gsdl_collections;
+colinfo_tmap gsdl_collectinfo;
+
+char gsdl_staticpages[MAX_FILENAME_SIZE];
+
+// debug settings
+char gsdl_log_name[MAX_FILENAME_SIZE] = "c:\\gsdl.log";
+int gsdl_keep_log = 0;
+int gsdl_show_console = 0;
+
+// general settings
+int gsdl_port_num = 80;
+int gsdl_auto_enter = 0;
+int gsdl_browser = GS_DEFAULT;
+char gsdl_browser_exe[MAX_FILENAME_SIZE] = "";
+text_t gsdl_url;
+text_t gsdl_conffile;
+int gsdl_start_browser = 1;
+int gsdl_address_resolution_method = 1;  // Get an IP, but don't resolve to a name
+
+// private data
+
+// pathnames of the latest installed versions of netscape and
+// internet explorer
+static char netscape_exe[MAX_FILENAME_SIZE]="";
+static char iexplore_exe[MAX_FILENAME_SIZE]="";
+
+// pathname of the default browser
+static char default_browser_exe[MAX_FILENAME_SIZE]="";
+
+// whether netscape is needed (used in the settings dialog box)
+static int gsdl_netscapeneeded = 0;
+
+static void remove_end_slashes(char *str) {
+  int len = strlen (str);
+  while ((len > 0) && ((str[len-1] == '\\') || (str[len-1] == '/'))) {
+    --len;
+  }
+  str[len] = '\0';
+}
+
+static void remove_end_slashes (text_t &str) {
+  char *cstr = str.getcstr();
+  remove_end_slashes (cstr);
+  str = cstr;
+  delete []cstr;
+}
+
+// returns 1 if successful, 0 otherwise
+// checks for a file's existence
+static int check_program_exe (char *path) {
+  UINT curerrormode;
+  OFSTRUCT reOpenBuff;
+  int retvalue = 0;
+  
+  reOpenBuff.cBytes = sizeof (OFSTRUCT);
+  
+  curerrormode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+  if (OpenFile(path, &reOpenBuff, OF_EXIST) != HFILE_ERROR) retvalue = 1;
+  SetErrorMode(curerrormode);
+  
+  return retvalue;
+}
+
+
+// returns 1 if successful, 0 otherwise
+static int get_default_browser_path (char *providedhtmltype, char *path, int maxsize) {
+  HKEY hk1, hk2;
+  char htmltype[256];
+  long size;
+  int retval = 0;
+  
+  htmltype[0] = '\0';
+  path[0] = '\0';
+  
+  // try and get information about the default browser from the 
+  // registry
+  
+  if (providedhtmltype != NULL) strcpy (htmltype, providedhtmltype);
+  
+  else if (RegOpenKey(HKEY_CLASSES_ROOT, ".htm", &hk1) == ERROR_SUCCESS) {
+    size = 256;
+    RegQueryValue(hk1, NULL, htmltype, &size);
+    RegCloseKey(hk1);
+  }
+  
+  if (RegOpenKey(HKEY_CLASSES_ROOT, htmltype, &hk1) == ERROR_SUCCESS) {
+    if (RegOpenKey(hk1, "shell\\open", &hk2) == ERROR_SUCCESS) {
+      // look for the command to start the browser
+      size = maxsize;
+      if (RegQueryValue(hk2,"command",path,&size) == ERROR_SUCCESS) {
+				// strip all excess stuff off the command
+	char endchar = ' ';
+	int i = 0, len = 0;
+	int state = 0; // 0=strip initial white-space and quotes, 1=grab path
+	while (path[i] != '\0') {
+	  if (state == 0) {
+	    if (path[i] == ' ') {
+	      // do nothing
+	    } else if (path[i] == '"') {
+	      endchar = '"';
+	      state = 1;
+	    } else {
+	      state = 1;
+	      path[len] = path[i];
+	      ++len;
+	    }
+	  } else {
+	    if (path[i] == endchar) break;
+	    path[len] = path[i];
+	    ++len;
+	  }
+	  ++i;
+	}
+	path[len] = '\0';
+	
+	// check the resulting pathname
+	if (check_program_exe (path)) {
+	  retval = 1; // succeeded
+	} else {
+	  path[0] = '\0'; // failed (must have been deleted or something...)
+	}
+      }
+      RegCloseKey(hk2);
+    }
+    RegCloseKey(hk1);
+  }
+  
+  return retval;
+}
+
+
+// returns 1 if successful, 0 otherwise
+// note that appname should include .exe (e.g. netscape.exe)
+static int registry_get_app_paths (char *appname, char *path, int maxsize) {
+  HKEY hk1;
+  long size;
+  int retval = 0;
+  
+  path[0] = '\0';
+  
+  if (RegOpenKey(HKEY_LOCAL_MACHINE, 
+		 "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths", 
+		 &hk1) == ERROR_SUCCESS) {
+    size = maxsize;
+    if (RegQueryValue (hk1, appname, path, &size) == ERROR_SUCCESS &&
+	check_program_exe (path));
+    retval = 1;
+    
+    RegCloseKey(hk1);
+  }
+  
+  return retval;
+}
+
+static void check_installed_browsers (int netscapeneeded) {
+  netscape_exe[0] = '\0';
+  iexplore_exe[0] = '\0';
+  default_browser_exe[0] = '\0';
+  
+  // update the default browser information (if netscape isn't needed)
+  if (!netscapeneeded) {
+    get_default_browser_path (NULL, default_browser_exe, MAX_FILENAME_SIZE);
+    _strlwr (default_browser_exe);
+  }
+  
+  // look for netscape
+  
+  // first look for netscape in the registry
+  if (!registry_get_app_paths("netscape.exe", netscape_exe, MAX_FILENAME_SIZE)) {
+    // next look for "NetscapeMarkup"
+    if (get_default_browser_path ("NetscapeMarkup", netscape_exe, MAX_FILENAME_SIZE)) {
+      _strlwr (netscape_exe);
+      if (!strstr (netscape_exe, "netscape.exe")) {
+	netscape_exe[0] = '\0';
+	
+	// next look for \netscape\netscape.exe
+	
+	// get drive letter of the windows directory
+	GetWindowsDirectory(netscape_exe, MAX_FILENAME_SIZE);
+	netscape_exe[1] = '\0';
+	strcat (netscape_exe, ":\\netscape\\netscape.exe");
+	
+	if (!check_program_exe (netscape_exe)) {
+	  // lastly look for \netscape\program\netscape.exe
+	  netscape_exe[1] = '\0';
+	  strcat (netscape_exe, ":\\netscape\\program\\netscape.exe");
+	  
+	}
+      }
+    }
+  }
+  _strlwr (netscape_exe);
+  if ((strlen (netscape_exe) > 0) && !check_program_exe (netscape_exe)) {
+    netscape_exe[0] = '\0';  // couldn't find netscape
+  }
+  
+  // look for internet explorer (if netscape isn't needed)
+  
+  // first look for iexplore in the registry
+  if (!netscapeneeded && !registry_get_app_paths("iexplore.exe", 
+						 iexplore_exe, MAX_FILENAME_SIZE)) {
+    // next look for "htmlfile"
+    if (get_default_browser_path ("htmlfile", iexplore_exe, MAX_FILENAME_SIZE)) {
+      _strlwr (iexplore_exe);
+      if (!strstr (iexplore_exe, "iexplore.exe")) {
+	iexplore_exe[0] = '\0';
+	
+	// next look for \iexplore\iexplore.exe
+	
+	// get drive letter of the windows directory
+	GetWindowsDirectory(iexplore_exe, MAX_FILENAME_SIZE);
+	iexplore_exe[1] = '\0';
+	strcat (iexplore_exe, ":\\iexplore\\iexplore.exe");
+      }
+    }
+  }
+  _strlwr (iexplore_exe);
+  if ((strlen (iexplore_exe) > 0) && !check_program_exe (iexplore_exe)) {
+    iexplore_exe[0] = '\0';  // couldn't find internet explorer
+  }
+}
+
+void write_settings (const text_t url) {
+  
+  if (gsdl_conffile.empty()) {
+    find_location();
+    gsdl_conffile = filename_cat(data_location, "gsdlsite.cfg");
+  }
+
+  char *conffile_c = gsdl_conffile.getcstr();
+  ofstream fout (conffile_c);
+  delete []conffile_c;
+  if (fout) {
+    
+    outconvertclass text_t2ascii;
+
+    // top (gsdl) level stuff
+    fout << "[gsdl]\n";
+    
+    if (!url.empty()) {
+      // url entry should only be written to gsdlsite.cfg when the
+      // server is actually running, not when it's stopped (since we
+      // can't be sure what the url is at that stage). That is,
+      // write_settings() is called with the url argument set when the
+      // server first starts up, it's then called without url set as
+      // the server shuts down.
+      write_ini_line(fout, "url", url);      
+    }
+
+    write_ini_line(fout, "enterlib", gsdl_enterlib);
+    write_ini_line(fout, "gsdlhome", gsdl_gsdlhome);
+    write_ini_line(fout, "collecthome", gsdl_collecthome);
+    write_ini_line(fout, "gdbmhome", gsdl_dbhome);
+    
+    write_ini_line(fout, "logfilename", gsdl_log_name);
+    write_ini_line(fout, "keeplog", text_t(gsdl_keep_log));
+    write_ini_line(fout, "consolelog", text_t(gsdl_show_console));
+    
+    write_ini_line(fout, "portnumber", text_t(gsdl_port_num));
+    write_ini_line(fout, "autoenter", text_t(gsdl_auto_enter));
+    write_ini_line(fout, "browser", text_t(gsdl_browser));
+    write_ini_line(fout, "browserexe", gsdl_browser_exe);
+    write_ini_line(fout, "collections", gsdl_collections);
+    write_ini_line(fout, "start_browser", text_t(gsdl_start_browser));
+    write_ini_line(fout, "address_resolution_method", text_t(gsdl_address_resolution_method));
+
+    // collection levels
+    colinfo_tmap::iterator here = gsdl_collectinfo.begin();
+    colinfo_tmap::iterator end = gsdl_collectinfo.end();
+    while (here != end) {
+      fout << text_t2ascii << "[" << (*here).first << "]\n";
+      if (!(*here).second.gsdl_gsdlhome.empty()) 
+	write_ini_line(fout, "gsdlhome", (*here).second.gsdl_gsdlhome);
+      if (!(*here).second.gsdl_collecthome.empty()) 
+	write_ini_line(fout, "collecthome", (*here).second.gsdl_collecthome);
+      if (!(*here).second.gsdl_dbhome.empty()) 
+	write_ini_line(fout, "gdbmhome", (*here).second.gsdl_dbhome);
+      
+      ++here;
+    }
+    fout.close();
+  }
+}
+
+
+// browser_compare assumes that check_exe is in lowercase
+static int browser_compare (char *browser_exe_path, char *check_exe) {
+  char tmp_exe[MAX_FILENAME_SIZE];
+  strcpy (tmp_exe, browser_exe_path);
+  _strlwr (tmp_exe);
+  return (strstr (tmp_exe, check_exe) != NULL);
+}
+
+// This function assumes that check_installed_browsers
+// has been recently called
+// It also assumes that browser_exe is large
+// enough to receive any path names to any browser
+static void check_browser_settings (int &browser, char *browser_exe, 
+				    int netscapeneeded) {
+  // sort out which browser to use
+  if ((browser == GS_NETSCAPE) && (netscape_exe[0] == '\0'))
+    browser = GS_IEXPLORE;
+  if ((browser == GS_IEXPLORE) && (netscapeneeded || (iexplore_exe[0] == '\0'))) {
+    if (netscape_exe[0] != '\0') browser = GS_NETSCAPE;
+    else browser = GS_OTHER;
+  }
+  if ((browser == GS_OTHER) && netscapeneeded && 
+      !browser_compare (browser_exe, "netscape.exe") && (netscape_exe[0] != '\0')) {
+    // this other browser isn't netscape and netscape is available
+    browser = GS_NETSCAPE;
+  }
+  
+  
+  // get the browser's executable
+  if (browser == GS_DEFAULT) {
+    strcpy (browser_exe, default_browser_exe);
+  } else if (browser == GS_NETSCAPE) {
+    strcpy (browser_exe, netscape_exe);
+  } else if (browser == GS_IEXPLORE) {
+    strcpy (browser_exe, iexplore_exe);
+  } else {
+    browser = GS_OTHER;
+    
+    // if the browser should be netscape then check to
+    // make sure it is
+    if (netscapeneeded && !browser_compare (browser_exe, "netscape.exe")) {
+      // not netscape, set to no browser
+      browser_exe[0] = '\0';
+    }
+  }
+}
+
+
+static void revert_defaults (int netscapeneeded) {
+  find_location();
+  
+  // library settings
+  gsdl_enterlib = "/gsdl";
+  gsdl_gsdlhome = data_location;
+  gsdl_collecthome = filename_cat(data_location,"collect");
+  gsdl_dbhome = data_location;
+  
+  // debug settings
+  gsdl_keep_log = 0;
+  strcpy (gsdl_log_name, "c:\\gsdl.log");
+  gsdl_show_console = 0;
+  
+  // general settings
+  gsdl_port_num = 80;
+  gsdl_auto_enter = 0;
+  gsdl_start_browser = 1;
+
+  check_installed_browsers (netscapeneeded);
+  gsdl_browser = GS_DEFAULT;
+  strcpy (gsdl_browser_exe, default_browser_exe);
+  check_browser_settings (gsdl_browser, gsdl_browser_exe, netscapeneeded);
+}
+
+void read_settings (int netscapeneeded) {
+  
+  if (gsdl_conffile.empty()) {
+    find_location();
+    gsdl_conffile = filename_cat(data_location, "gsdlsite.cfg");
+  }
+  
+  // set up defaults
+  revert_defaults (netscapeneeded);
+  
+  text_t key, value, section;
+  char *cstr_value;
+  char *conffile_c = gsdl_conffile.getcstr();
+#if defined (GSDL_USE_IOS_H)
+  ifstream conf (conffile_c, ios::nocreate);
+#else
+  ifstream conf (conffile_c);
+#endif
+  delete []conffile_c;
+  if (conf) {
+    while (read_ini_line(conf, key, value) >= 0) {
+      if (key.empty()) continue;
+      if (value.empty()) {
+	// should be a section title
+	section = key;
+	// remove square brackets
+	section.erase (section.begin());
+	section.erase (section.end()-1);
+
+      } else {
+
+	cstr_value = value.getcstr ();
+      
+	if (section == "gsdl") {
+
+	  if (key == "enterlib") {
+	    gsdl_enterlib = value;
+	  
+	    // 'logfilename' should occur in the configuration file
+	    // before 'keeplog'
+	  } else if (key == "logfilename") {
+	    strcpy (gsdl_log_name, cstr_value);
+	  
+	  } else if (key == "keeplog") {
+	    gsdl_keep_log = value.getint();
+	    if (gsdl_keep_log) open_log_file();
+	
+	  } else if (key == "consolelog") {
+	    if (value.getint()) activate_console();
+	    else deactivate_console();
+	
+	  } else if (key == "portnumber") {
+	    gsdl_port_num = value.getint();
+	    
+	  } else if (key == "autoenter") {
+	    gsdl_auto_enter = value.getint();
+	    
+	  } else if (key == "start_browser") {
+	    gsdl_start_browser = value.getint();
+	
+	  } else if (key == "browser") {
+	    gsdl_browser = value.getint();
+	
+	  } else if (key == "browserexe") {
+	    strcpy (gsdl_browser_exe, cstr_value);
+
+	  } else if (key == "address_resolution_method") {
+	    gsdl_address_resolution_method = value.getint();
+
+	  } else if (key == "collections") {
+	    gsdl_collections = value;
+	  } 
+	  // gsdlhome must occur in file before dbhome	    
+	  else if (key == "gsdlhome") {
+	    gsdl_gsdlhome = value;
+	    gsdl_collecthome = filename_cat(value,"collect");
+	    gsdl_dbhome = value;	  
+	  } 
+	  else if (key == "collecthome") {
+	    gsdl_collecthome = value;
+	  } 
+	  else if (key == "gdbmhome") {
+	    gsdl_dbhome = value;
+	  }
+	} else {
+	  
+	  // gsdlhome must occur in file before dbhome
+	  if (key == "gsdlhome") {
+	    gsdl_collectinfo[section].gsdl_gsdlhome = value;
+	    gsdl_collectinfo[section].gsdl_collecthome = filename_cat(value,"collecct");
+	    gsdl_collectinfo[section].gsdl_dbhome = value;
+	  } 
+	  else if (key == "collecthome") {
+	    gsdl_collectinfo[section].gsdl_collecthome = value;
+	  }
+	  else if (key == "gdbmhome") {
+	    gsdl_collectinfo[section].gsdl_dbhome = value;
+	  }
+	}
+	delete []cstr_value;
+      }
+    }
+    conf.close();
+  }
+  
+  // check the homes to make sure they don't contain
+  // extra slashes at the end
+  remove_end_slashes (gsdl_gsdlhome);
+  remove_end_slashes (gsdl_collecthome);
+  remove_end_slashes (gsdl_dbhome);
+  colinfo_tmap::iterator here = gsdl_collectinfo.begin();
+  colinfo_tmap::iterator end = gsdl_collectinfo.end();
+  while (here != end) {
+    remove_end_slashes ((*here).second.gsdl_gsdlhome);
+    remove_end_slashes ((*here).second.gsdl_collecthome);
+    remove_end_slashes ((*here).second.gsdl_dbhome);
+    ++here;
+  }
+
+  // check the browser settings
+  check_browser_settings (gsdl_browser, gsdl_browser_exe, netscapeneeded);
+}
+
+void gsdl_check_browser_settings (int netscapeneeded) {
+  check_installed_browsers (netscapeneeded);
+  check_browser_settings (gsdl_browser, gsdl_browser_exe, netscapeneeded);
+}
+
+
+static void dialog_update_enables (HWND hwndDlg) {
+  int res;
+  
+  // if they want to use another browser, they should be able
+  // edit its filename
+  res = SendDlgItemMessage(hwndDlg, ID_RADIO_OTHER, BM_GETCHECK,0,0);
+  SendDlgItemMessage(hwndDlg, ID_OTHER_NAME, WM_ENABLE, (res == 1), 0);
+}
+
+
+static int read_dialog_browser_field (HWND hwndDlg) {
+  if (SendDlgItemMessage(hwndDlg, ID_RADIO_DEFAULT,
+			 BM_GETCHECK, 0, 0) == 1) return GS_DEFAULT;
+  if (SendDlgItemMessage(hwndDlg, ID_RADIO_NETSCAPE,
+			 BM_GETCHECK, 0, 0) == 1) return GS_NETSCAPE;
+  if (SendDlgItemMessage(hwndDlg, ID_RADIO_IE,
+			 BM_GETCHECK, 0, 0) == 1) return GS_IEXPLORE;
+  if (SendDlgItemMessage(hwndDlg, ID_RADIO_OTHER,
+			 BM_GETCHECK, 0, 0) == 1) return GS_OTHER;
+  return GS_NETSCAPE;
+}
+
+
+static void set_dialog_browser_field (HWND hwndDlg, int browser, char *othername) {
+  // if we are trying to set the browser to default, netscape or
+  // internet explorer and we can't find them, set the browser
+  // to 'other'
+  if ((browser == GS_DEFAULT) && (default_browser_exe[0] == '\0'))
+    browser = GS_NETSCAPE;
+  if ((browser == GS_NETSCAPE) && (netscape_exe[0] == '\0'))
+    browser = GS_IEXPLORE;
+  if ((browser == GS_IEXPLORE) && (iexplore_exe[0] == '\0')) {
+    if (netscape_exe[0] != '\0') browser = GS_NETSCAPE;
+    else browser = GS_OTHER;
+  }
+  
+  // update the radio buttons
+  SendDlgItemMessage(hwndDlg, ID_RADIO_DEFAULT, BM_SETCHECK,
+		     (browser == GS_DEFAULT) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_RADIO_NETSCAPE, BM_SETCHECK,
+		     (browser == GS_NETSCAPE) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_RADIO_IE, BM_SETCHECK,
+		     (browser == GS_IEXPLORE) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_RADIO_OTHER, BM_SETCHECK,
+		     (browser == GS_OTHER) ? BST_CHECKED : BST_UNCHECKED, 0);
+  
+  // update the other name field
+  if (browser == GS_DEFAULT) {
+    SetDlgItemText(hwndDlg, ID_OTHER_NAME, default_browser_exe);
+  } else if (browser == GS_NETSCAPE) {
+    SetDlgItemText(hwndDlg, ID_OTHER_NAME, netscape_exe);
+  } else if (browser == GS_IEXPLORE) {
+    SetDlgItemText(hwndDlg, ID_OTHER_NAME, iexplore_exe);
+  } else if (othername != NULL) {
+    SetDlgItemText(hwndDlg, ID_OTHER_NAME, othername);
+  }
+}
+
+
+static void read_browser_exe_field (HWND hwndDlg, char *filename, int maxsize) {
+  filename[0] = '\0';
+  GetDlgItemText(hwndDlg, ID_OTHER_NAME, filename, maxsize);
+}
+
+static int read_address_resolution_field (HWND hwndDlg) {
+  if (SendDlgItemMessage(hwndDlg, ID_ARM_NAME,
+			 BM_GETCHECK, 0, 0) == 1) return ARM_NAME;
+  if (SendDlgItemMessage(hwndDlg, ID_ARM_IP,
+			 BM_GETCHECK, 0, 0) == 1) return ARM_IP;
+  if (SendDlgItemMessage(hwndDlg, ID_ARM_LOCALHOST,
+			 BM_GETCHECK, 0, 0) == 1) return ARM_LOCALHOST;
+  if (SendDlgItemMessage(hwndDlg, ID_ARM_127_0_0_1,
+			 BM_GETCHECK, 0, 0) == 1) return ARM_127_0_0_1;
+  return ARM_NAME;
+}
+
+static void set_address_resolution_field (HWND hwndDlg, 
+					  int address_res_method) {
+  // update the radio buttons
+  SendDlgItemMessage(hwndDlg, ID_ARM_NAME, BM_SETCHECK,
+		     (address_res_method == ARM_NAME) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_ARM_IP, BM_SETCHECK,
+		     (address_res_method == ARM_IP) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_ARM_LOCALHOST, BM_SETCHECK,
+		     (address_res_method == ARM_LOCALHOST) ? BST_CHECKED : BST_UNCHECKED, 0);
+  SendDlgItemMessage(hwndDlg, ID_ARM_127_0_0_1, BM_SETCHECK,
+		     (address_res_method == ARM_127_0_0_1) ? BST_CHECKED : BST_UNCHECKED, 0);
+
+}
+
+static void read_dialog_fields (HWND hwndDlg) {
+  int res;  BOOL bres;
+  
+  // port number
+  gsdl_port_num = GetDlgItemInt(hwndDlg, SERVER_PORT_EDIT_BOX, &bres, 0);
+  if (!bres) gsdl_port_num = 80;
+  
+  // whether to enter the library automatically on startup
+  res = SendDlgItemMessage(hwndDlg, ID_AUTO_ENTER_LIBRARY, BM_GETCHECK, 0, 0);
+  gsdl_auto_enter = (res == 1);
+  
+  // which browser to use
+  gsdl_browser = read_dialog_browser_field (hwndDlg);
+  
+  // find out where the browser exe lives
+  read_browser_exe_field (hwndDlg, gsdl_browser_exe, MAX_FILENAME_SIZE);
+
+  // which address resolution method
+  gsdl_address_resolution_method = read_address_resolution_field(hwndDlg);
+}
+
+
+
+
+static BOOL CALLBACK dialog_proc(HWND hwndDlg, UINT uMsg, 
+				 WPARAM wParam,LPARAM  lParam) {
+  int wNotifyCode = HIWORD(wParam);
+  int wID = LOWORD(wParam);
+  char *filefilter = "Program Files (*.exe)\0*.exe\0All Files (*.*)\0*.*\0\0";
+  OPENFILENAME fname;
+  char filename[MAX_FILENAME_SIZE];
+  
+  switch (uMsg) {
+  case WM_INITDIALOG:
+    // set current values
+    SetDlgItemInt(hwndDlg, SERVER_PORT_EDIT_BOX, gsdl_port_num, FALSE);
+    SendDlgItemMessage(hwndDlg, ID_AUTO_ENTER_LIBRARY, BM_SETCHECK,
+		       gsdl_auto_enter ? 1 : 0, 0);
+    set_dialog_browser_field (hwndDlg, gsdl_browser, gsdl_browser_exe);
+    set_address_resolution_field(hwndDlg, gsdl_address_resolution_method);
+    dialog_update_enables(hwndDlg);
+    
+    // make sure that the default browser, netscape and internet explorer
+    // radio buttons are only enabled if they could be found
+    if (default_browser_exe[0] == '\0')
+      EnableWindow (GetDlgItem (hwndDlg, ID_RADIO_DEFAULT), FALSE);
+    if (netscape_exe[0] == '\0')
+      EnableWindow (GetDlgItem (hwndDlg, ID_RADIO_NETSCAPE), FALSE);
+    if (iexplore_exe[0] == '\0')
+      EnableWindow (GetDlgItem (hwndDlg, ID_RADIO_IE), FALSE);
+    
+    return 1;
+    
+  case WM_COMMAND:
+    if (wNotifyCode == BN_CLICKED) {
+      switch (wID) {
+      case ID_CANCEL_BUTTON:
+	EndDialog(hwndDlg, 0);
+	return 1;
+	
+      case ID_OK_BUTTON:
+	// check to make sure we can find the browser
+	filename[0] = '\0';
+	read_browser_exe_field (hwndDlg, filename, MAX_FILENAME_SIZE);
+	if (check_program_exe (filename)) { 
+	  // the file exists
+	  // if netscape is needed, make sure the browser is netscape
+	  _strlwr(filename);
+	  if (!gsdl_netscapeneeded || strstr (filename, "netscape.exe") != NULL) {
+	    // everything is ok
+	    read_dialog_fields(hwndDlg);
+	    EndDialog(hwndDlg, 1);
+	    
+	  } else {
+	    // message: must use netscape with this version
+	    MessageBox(hwndDlg,
+		       "You are using this software on a machine which\n"
+		       "currently does not have a working TCP/IP network layer.\n"
+		       "To allow the Greenstone Digital Library software to use\n"
+		       "its own TCP/IP network layer you must use a Netscape\n"
+		       "web browser.",
+		       "Greenstone Digital Library Software",
+		       MB_OK|MB_APPLMODAL);
+	  }
+	} else {
+	  // message: the file did not exist
+	  MessageBox(hwndDlg,
+		     "Could not find the selected browser. You must have a\n"
+		     "web browser installed to use the Greenstone Digital\n"
+		     "Library software.",
+		     "Greenstone Digital Library Software",
+		     MB_OK|MB_APPLMODAL);
+	}
+	return 1;
+
+      case ID_RADIO_DEFAULT:
+	set_dialog_browser_field (hwndDlg, GS_DEFAULT, NULL);
+	dialog_update_enables(hwndDlg);
+	return 1;
+	
+      case ID_RADIO_NETSCAPE:
+	set_dialog_browser_field (hwndDlg, GS_NETSCAPE, NULL);
+	dialog_update_enables(hwndDlg);
+	return 1;
+	
+      case ID_RADIO_IE:
+	set_dialog_browser_field (hwndDlg, GS_IEXPLORE, NULL);
+	dialog_update_enables(hwndDlg);
+	return 1;
+	
+      case ID_RADIO_OTHER:
+	set_dialog_browser_field (hwndDlg, GS_OTHER, NULL);
+	dialog_update_enables(hwndDlg);
+	return 1;
+	
+      case ID_BROWSE_BUTTON:
+	filename[0] = '\0';
+	memset(&fname, 0, sizeof(OPENFILENAME));
+	fname.lStructSize = sizeof(OPENFILENAME);
+	fname.hwndOwner = hwndDlg;
+	fname.hInstance = NULL;
+	fname.lpstrFilter = filefilter;
+	fname.lpstrCustomFilter = NULL;
+	fname.nMaxCustFilter = 0;
+	fname.nFilterIndex = 1;
+	fname.lpstrFile = filename;
+	fname.nMaxFile = MAX_FILENAME_SIZE-1;
+	fname.lpstrFileTitle = NULL;
+	fname.nMaxFileTitle = 0;
+	fname.lpstrInitialDir = NULL;
+	fname.lpstrTitle = "Choose Web Browser";
+	fname.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+	
+	if(GetOpenFileName(&fname)) {
+	  set_dialog_browser_field (hwndDlg, GS_OTHER, filename);
+	  dialog_update_enables(hwndDlg);
+	}
+	return 1;
+
+      case ID_ARM_NAME:
+	set_address_resolution_field (hwndDlg, ARM_NAME);
+	dialog_update_enables(hwndDlg);
+	return 1;
+     case ID_ARM_IP:
+	set_address_resolution_field (hwndDlg, ARM_IP);
+	dialog_update_enables(hwndDlg);
+	return 1;
+    case ID_ARM_LOCALHOST:
+	set_address_resolution_field (hwndDlg, ARM_LOCALHOST);
+	dialog_update_enables(hwndDlg);
+	return 1;
+     case ID_ARM_127_0_0_1:
+	set_address_resolution_field (hwndDlg, ARM_127_0_0_1);
+	dialog_update_enables(hwndDlg);
+	return 1;
+
+      }
+    }
+    return 0;
+    
+  case WM_CLOSE:
+  case WM_DESTROY:
+    EndDialog(hwndDlg, 0);
+    return 1;
+  }
+  
+  return 0;
+}
+
+static void dialog_debug_update_enables (HWND hwndDlg) {
+  int res;
+  
+  // if they want a log of program use allow them to
+  // edit its filename
+  res = SendDlgItemMessage(hwndDlg, LOG_CHECK, BM_GETCHECK,0,0);
+  SendDlgItemMessage(hwndDlg, LOG_NAME_BOX, WM_ENABLE, (res == 1), 0);
+}
+
+
+static void read_dialog_debug_fields (HWND hwndDlg) {
+  int res;
+  
+  // whether to log program use
+  res = SendDlgItemMessage(hwndDlg, LOG_CHECK, BM_GETCHECK,0,0);
+  gsdl_keep_log = (res == 1);
+  
+  // log filename
+  res = SendDlgItemMessage(hwndDlg, LOG_NAME_BOX, EM_GETMODIFY,0,0);
+  if (res != 0) {
+    res = GetDlgItemText(hwndDlg, LOG_NAME_BOX, gsdl_log_name, MAX_FILENAME_SIZE);
+  }
+  
+  // whether to display log information on console
+  res = SendDlgItemMessage(hwndDlg, CONSOLE_CHECK, BM_GETCHECK,0,0);
+  gsdl_show_console = (res == 1);
+}
+
+
+static BOOL CALLBACK dialog_debug_proc(HWND hwndDlg, UINT uMsg, 
+				       WPARAM wParam,LPARAM  lParam) {
+  int wNotifyCode = HIWORD(wParam);
+  int wID = LOWORD(wParam);
+  
+  switch (uMsg) {
+  case WM_INITDIALOG:
+    // set current values
+    SendDlgItemMessage(hwndDlg, LOG_CHECK, BM_SETCHECK,
+		       gsdl_keep_log ? 1 : 0, 0);
+    SetDlgItemText(hwndDlg, LOG_NAME_BOX, gsdl_log_name);
+    SendDlgItemMessage(hwndDlg, CONSOLE_CHECK, BM_SETCHECK,
+		       gsdl_show_console ? 1 : 0, 0);
+    dialog_debug_update_enables(hwndDlg);
+    return 1;
+    
+  case WM_COMMAND:
+    if (wNotifyCode == BN_CLICKED) {
+      switch (wID) {
+      case ID_CANCEL_BUTTON:
+	EndDialog(hwndDlg, 0);
+	return 1;
+	
+      case ID_OK_BUTTON:
+	read_dialog_debug_fields(hwndDlg);
+	EndDialog(hwndDlg, 1);
+	return 1;
+	
+      case LOG_CHECK:
+	dialog_debug_update_enables(hwndDlg);
+	return 1;
+      }
+    }
+    return 0;
+    
+  case WM_CLOSE:
+  case WM_DESTROY:
+    EndDialog(hwndDlg, 0);
+    return 1;
+  }
+  
+  return 0;
+}
+
+
+void Settings_Dialog(HWND window, int netscapeneeded)
+{
+  int old_gsdl_show_console = gsdl_show_console;
+  int old_gsdl_port_num = gsdl_port_num;
+  
+  int res = 0;
+  
+  gsdl_netscapeneeded = netscapeneeded;
+  
+  if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) && 
+      (GetAsyncKeyState(VkKeyScan('d')) & 0x8000)) {
+    // control-D was pressed, display debug settings
+    // dialog box
+    res = DialogBox (NULL, MAKEINTRESOURCE(COMMS_DIALOG_DEBUG),
+		     window, (DLGPROC)dialog_debug_proc);
+    
+    if (res == 1) {
+      // only update if exited via the OK button
+      if (gsdl_keep_log) open_log_file(); 
+      else close_log_file();
+      
+      // turn the console on/off if we need to
+      if (gsdl_show_console != old_gsdl_show_console) {
+	if (gsdl_show_console) activate_console ();
+	else deactivate_console ();
+      }
+    }
+    
+  } else {
+    // display normal settings dialog box
+    
+    // make sure everything is up-to-date
+    gsdl_check_browser_settings (netscapeneeded);
+    
+    res = DialogBox (NULL, MAKEINTRESOURCE(COMMS_DIALOG2),
+		     window, (DLGPROC)dialog_proc);
+
+    if (res == 1) {
+      // only update if exited via the OK button
+      if (gsdl_port_num != old_gsdl_port_num) {
+	EndHTTPServer();
+	StartHTTPServer(window);
+      }
+    }
+  }
+  
+  // save the settings if we exited via the ok button
+  if (res == 1) {
+    write_settings(gsdl_url);
+    // reset httpprefix in case port number was changed
+    //    configure_httpprefix ();
+  }
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/settings.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/settings.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/settings.h	(revision 16561)
@@ -0,0 +1,79 @@
+/**********************************************************************
+ *
+ * settings.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#define GS_NETSCAPE 0
+#define GS_IEXPLORE 1
+#define GS_OTHER    2
+#define GS_DEFAULT  3
+
+// address resolution methods
+#define ARM_NAME 0
+#define ARM_IP 1
+#define ARM_LOCALHOST 2
+#define ARM_127_0_0_1 3
+
+#include "text_t.h"
+#include "cfgread.h"
+#include "receptionist.h"
+#include "locate.h"
+
+// library settings
+extern text_t gsdl_enterlib;
+extern text_t gsdl_gsdlhome;
+extern text_t gsdl_collecthome;
+extern text_t gsdl_dbhome;
+extern text_t gsdl_collections;
+extern colinfo_tmap gsdl_collectinfo;
+
+extern char gsdl_staticpages[MAX_FILENAME_SIZE];
+
+// debug settings
+extern char gsdl_log_name[MAX_FILENAME_SIZE];
+extern int gsdl_keep_log;
+extern int gsdl_show_console;
+
+// general settings
+extern int gsdl_port_num;
+extern int gsdl_auto_enter;
+extern int gsdl_browser;
+extern char gsdl_browser_exe[MAX_FILENAME_SIZE];
+extern text_t gsdl_url;
+extern text_t gsdl_conffile;
+extern int gsdl_start_browser;
+extern int gsdl_address_resolution_method;
+
+void Settings_Dialog(HWND window, int netscapeneeded);
+
+void read_settings (int netscapeneeded);
+void write_settings (const text_t url);
+
+void gsdl_check_browser_settings (int netscapeneeded);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/w32server/startbrowser.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/startbrowser.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/startbrowser.cpp	(revision 16561)
@@ -0,0 +1,287 @@
+/**********************************************************************
+ *
+ * startbrowser.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.
+ *
+ *********************************************************************/
+
+#include "startbrowser.h"
+#include <windows.h>
+#include <DDE.H>
+#include <DDEML.H>
+#include <string.h>
+#include <direct.h>
+#include "locate.h"
+
+#define SB_COMLEN 1024
+#define SB_DDETIMEOUT 60000UL  // One minute in milliseconds
+
+int numbrowserstarted = 0;
+
+static int inited = 0;
+
+static DWORD idInst = 0;
+static HINSTANCE hinst = NULL;
+static HSZ hszNetscapeName = NULL;
+static HSZ hszIExploreName = NULL;
+static HSZ hszOpenURLTopic = NULL;
+static HSZ hszActivateTopic = NULL;
+
+// budget callback
+HDDEDATA CALLBACK DdeCallback(UINT uType,     /* transaction type                 */
+			      UINT uFmt,      /* clipboard data format            */
+			      HCONV hconv,    /* handle of conversation           */
+			      HSZ hsz1,       /* handle of string                 */
+			      HSZ hsz2,       /* handle of string                 */
+			      HDDEDATA hdata, /* handle of global memory object   */
+			      DWORD dwData1,  /* transaction-specific data        */
+			      DWORD dwData2)  /* transaction-specific data        */ 
+{
+  return (HDDEDATA) NULL; 
+}
+
+// returns 0 on failure
+static int talkdde (HSZ hszServName, HSZ hszSysTopic, char *requeststr) {
+  HSZ hszRequest;
+  HCONV hConv;
+  HDDEDATA result;
+  
+  // try to connect to the newly started browser
+  hConv = DdeConnect (idInst, hszServName, hszSysTopic, (PCONVCONTEXT) NULL);
+  if (hConv == NULL) return 0;
+  
+  hszRequest = DdeCreateStringHandle (idInst, requeststr, CP_WINANSI);
+  result = DdeClientTransaction (
+				 NULL,          // pointer to data to pass to server
+				 0,             // length of data
+				 hConv,         // handle to conversation
+				 hszRequest,    // handle to item name string
+				 CF_TEXT,       // clipboard data format
+				 XTYP_REQUEST,  // transaction type
+				 SB_DDETIMEOUT, // time-out duration
+				 NULL           // pointer to transaction result
+				 );
+
+  if (result != NULL) DdeFreeDataHandle (result);
+  DdeDisconnect(hConv);
+  DdeFreeStringHandle (idInst, hszRequest);
+
+  return 1;
+}
+
+// returns 0 on failure
+static int openurl(HSZ hszServName, HSZ hszActivateTopic, HSZ hszOpenURLTopic, char *url) {
+  char requeststr[65600];
+  
+  // bring the browser to the front, don't worry if this fails
+  talkdde (hszServName, hszActivateTopic, "0xFFFFFFFF,0x0");
+  
+  // tell the browser to open a url
+  strcpy (requeststr, "\"");
+  strcat (requeststr, url);
+  strcat (requeststr, "\",,0xFFFFFFFF,0x0,,,");
+  
+  return talkdde (hszServName, hszOpenURLTopic, requeststr);
+}
+
+// returns 0 on failure
+static int tryconnect(HSZ hszServName, HSZ hszSysTopic) {
+  // try to connect to a running browser
+  HCONV hConv = DdeConnect (idInst, hszServName, hszSysTopic, (PCONVCONTEXT) NULL);
+  if (hConv == NULL) return 0;
+  DdeDisconnect(hConv);
+  return 1;
+}
+
+static void freestrings () {
+  // free up any currently allocated strings
+  if (hszNetscapeName != NULL) DdeFreeStringHandle (idInst, hszNetscapeName);
+  hszNetscapeName = NULL;
+  if (hszIExploreName != NULL) DdeFreeStringHandle (idInst, hszIExploreName);
+  hszIExploreName = NULL;
+  if (hszOpenURLTopic != NULL) DdeFreeStringHandle (idInst, hszOpenURLTopic);
+  hszOpenURLTopic = NULL;
+  if (hszActivateTopic != NULL) DdeFreeStringHandle (idInst, hszActivateTopic);
+  hszActivateTopic = NULL;
+}
+
+static void getbrowserinfo() {
+  freestrings();
+  
+  hszNetscapeName = DdeCreateStringHandle(idInst,"NETSCAPE",CP_WINANSI);
+  hszIExploreName = DdeCreateStringHandle(idInst,"IExplore",CP_WINANSI);
+  
+  hszOpenURLTopic = DdeCreateStringHandle(idInst,"WWW_OpenURL",CP_WINANSI);
+  hszActivateTopic = DdeCreateStringHandle(idInst,"WWW_Activate",CP_WINANSI);
+}
+
+// returns SB_NOERROR on success, 
+// SB_ALREADYINIT or SB_DDE_FAILINIT on failure
+int initstartbrowser () {
+  if (inited) return SB_ALREADYINIT;
+  
+  // from nstest if(DdeInitialize(&CDDEObject::m_dwidInst, NstestDdeCallBack, APPCLASS_STANDARD, 0ul))
+  if (DdeInitialize (&idInst, (PFNCALLBACK) DdeCallback, 
+		     APPCLASS_STANDARD, 0)
+      != DMLERR_NO_ERROR) {
+    freestrings();
+    inited = 0;
+    return SB_DDE_FAILINIT;
+  }
+
+  getbrowserinfo();
+  
+  inited = 1;
+  return SB_NOERROR;
+}
+
+
+// returns SB_NOERROR on success,
+// SB_DDE_FAILDEINIT of failure
+int deinitstartbrowser () {
+  // make sure this has been inited
+  // not being inited here, however, is not
+  // important
+  if (!inited) return SB_NOERROR;
+  inited = 0;
+  
+  freestrings();
+  
+  if (DdeUninitialize(idInst)) return SB_NOERROR;
+  
+  return SB_DDE_FAILDEINIT;
+}
+
+
+// startbrowser will try to start a browser (or connect to a running browser).
+//
+// Returns SB_NOERROR on success,
+// SB_NOTINITED, SB_FAIL_BADFORMAT, SB_FAIL_NOTFOUND, SB_FAIL_NORESOURCES, or
+// SB_FAILCONNECTBROWSER on failure
+int startbrowser (char *url, char *browser_exe, char *startdir) {
+  char newcommand[SB_COMLEN];
+  int usingnetscape = 0;
+  int usingiexplore = 0;
+  
+  if (!inited) return SB_NOTINITED; // has to be inited first
+  
+  // find out which browser we are dealing with
+  strcpy (newcommand, browser_exe);
+  _strlwr (newcommand);
+  if (strstr (newcommand, "netscape.exe") != NULL) { 
+    // netscape
+    usingnetscape = 1;
+  } else if (strstr (newcommand, "iexplore.exe") != NULL) { 
+    // internet explorer
+    usingiexplore = 1;
+  }
+  
+  // only try to communicate with a running browser if a startdir
+  // is not specified
+  if (startdir == NULL) {
+    if (usingnetscape && hszNetscapeName != NULL && 
+	openurl(hszNetscapeName, hszActivateTopic, hszOpenURLTopic, url)) {
+      return SB_NOERROR;
+    }
+    if (usingiexplore && hszIExploreName != NULL && 
+	openurl(hszIExploreName, hszActivateTopic, hszOpenURLTopic, url)) {
+      return SB_NOERROR;
+    }
+  }
+  
+  strcpy (newcommand, browser_exe);
+  
+  // don't put the url on the command line for internet explorer
+  if (usingiexplore) {
+    strcat (newcommand, " -nohome");
+    
+  } else {
+    // will have to put the url on the command line
+    strcat (newcommand, " \"");
+    strcat (newcommand, url);
+    strcat (newcommand, "\"");
+  }
+  
+  // change the working directory (if needed)
+  char cwd[1024];
+  cwd[0] = '\0';
+  if (startdir != NULL) {
+    _getcwd(cwd, 1024);
+    _chdir(startdir);
+  }
+  
+  // attempt to start the browser
+  int res = WinExec(newcommand, SW_SHOW);
+  
+  // change the working directory back (if needed)
+  if (startdir != NULL) _chdir(cwd);
+  
+  if (res == ERROR_BAD_FORMAT) return SB_FAIL_BADFORMAT;
+  if ((res == ERROR_FILE_NOT_FOUND) || (res == ERROR_PATH_NOT_FOUND)) 
+    return SB_FAIL_NOTFOUND;
+  if (res <= 31) return SB_FAIL_NORESOURCES;
+  
+  ++numbrowserstarted;
+  
+  // if we aren't using iexplore then the url was
+  // put on the command line and there is nothing left to do
+  if (!usingiexplore) return SB_NOERROR;
+  
+  // connect to the browser and get it to open a page,
+  // time out after 1 minute
+  DWORD lastcheck = GetTickCount ();
+  while (DiffTickCounts (lastcheck, GetTickCount()) <= 60000) {
+    if (usingnetscape && hszNetscapeName != NULL && 
+	openurl(hszNetscapeName, hszActivateTopic, hszOpenURLTopic, url)) {
+      return SB_NOERROR;
+    }
+    if (usingiexplore && hszIExploreName != NULL && 
+	openurl(hszIExploreName, hszActivateTopic, hszOpenURLTopic, url)) {
+      return SB_NOERROR;
+    }
+  }
+  
+  // timed out trying to connect to the browser
+  return SB_FAILCONNECTBROWSER;
+}
+
+// returns SB_NOERROR on success,
+// SB_NOTINITED, SB_FAILCONNECTBROWSER on failure
+int browserrunning (char *browser_exe) {
+  char newcommand[SB_COMLEN];
+  if (!inited) return SB_NOTINITED; // has to be inited first
+  
+  strcpy (newcommand, browser_exe);
+  _strlwr (newcommand);
+  
+  if ((strstr (newcommand, "netscape.exe") != NULL) && (hszNetscapeName != NULL) && 
+      tryconnect(hszNetscapeName, hszOpenURLTopic)) {
+    // succeeded connecting to netscape
+    return SB_NOERROR;
+    
+  } else if ((strstr (newcommand, "iexplore.exe") != NULL) && (hszIExploreName != NULL) && 
+	     tryconnect(hszIExploreName, hszOpenURLTopic)) {
+    // succeeded connecting to internet explorer
+    return SB_NOERROR;
+  }
+  
+  return SB_FAILCONNECTBROWSER;
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/startbrowser.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/startbrowser.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/startbrowser.h	(revision 16561)
@@ -0,0 +1,58 @@
+/**********************************************************************
+ *
+ * startbrowser.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.
+ *
+ *********************************************************************/
+
+#define SB_NOERROR            0
+#define SB_ALREADYINIT        1
+#define SB_DDE_FAILINIT       2
+#define SB_DDE_FAILDEINIT     3
+#define SB_NOTINITED          4
+#define SB_FAIL_BADFORMAT     5
+#define SB_FAIL_NOTFOUND      6
+#define SB_FAIL_NORESOURCES   7
+#define SB_FAILCONNECTBROWSER 9
+
+
+extern int numbrowserstarted;
+
+// initstartbrowser should be called before startbrowser
+//
+// returns SB_NOERROR on success, 
+// SB_ALREADYINIT or SB_DDE_FAILINIT on failure
+int initstartbrowser ();
+
+// returns SB_NOERROR on success,
+// SB_DDE_FAILDEINIT of failure
+int deinitstartbrowser ();
+
+// startbrowser will try to start a browser (or connect to a running browser).
+//
+// Returns SB_NOERROR on success,
+// SB_NOTINITED, SB_FAIL_BADFORMAT, SB_FAIL_NOTFOUND, or SB_FAIL_NORESOURCES, 
+//  on failure
+int startbrowser (char *url, char *browser_exe, char *startdir);
+
+// returns SB_NOERROR on success,
+// SB_NOTINITED, SB_FAILCONNECTBROWSER on failure
+int browserrunning (char *browser_exe);
Index: /gsdl/trunk/gs2runtime/src/w32server/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/win32.mak	(revision 16561)
@@ -0,0 +1,174 @@
+###########################################################################
+#
+# win32 makefile -- src/w32server
+# 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  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+COLSERVR_DIR = $(GSDL_DIR)\src\colservr
+PROTOCOL_DIR = $(GSDL_DIR)\src\protocol
+RECPT_DIR = $(GSDL_DIR)\src\recpt
+
+
+GSDL_VC4 = 0
+ENABLE_ACCENTFOLD = 1
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(ENABLE_ACCENTFOLD)
+ACCENTFOLD_LIBS = "$(INDEXERS_DIR)\packages\unac\libunac.lib" "$(INDEXERS_DIR)\packages\unac\iconv.lib"
+!ELSE
+ACCENTFOLD_LIBS = 
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_LIBS = "$(PACKAGES_DIR)\sqlite\lib\sqlite3.lib"
+!ELSE
+SQLITE_LIBS = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -D_LITTLE_ENDIAN -DPARADOCNUM -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+INCLUDES = -I"$(GSDL_DIR)" -I"$(GSDL_DIR)\src\lib" -I"$(COLSERVR_DIR)" -I"$(PROTOCOL_DIR)" -I"$(RECPT_DIR)" \
+	-I"$(GSDL_DIR)\src\colservr" -I"$(GSDL_DIR)\indexers\mg" \
+	-I"$(PACKAGES_DIR)\windows\gdbm\gdbm" \
+        -I"$(GSDL_DIR)\indexers\mg\lib" -I"$(GSDL_DIR)\indexers\mg\src\text" \
+    	-I"$(GSDL_DIR)\indexers\mgpp\lib" -I"$(GSDL_DIR)\indexers\mgpp\text"
+LDFLAGS =
+LIBS = "$(PACKAGES_DIR)\windows\crypt\crypt\crypt.lib" "$(PACKAGES_DIR)\windows\expat\expat\libexpat.lib" \
+        "$(PACKAGES_DIR)\windows\gdbm\gdbm\gdbm.lib" $(ACCENTFOLD_LIBS) $(SQLITE_LIBS)
+RSC = rc
+WINLIBS = kernel32.lib user32.lib gdi32.lib winspool.lib \
+          comdlg32.lib advapi32.lib shell32.lib ole32.lib \
+          oleaut32.lib uuid.lib odbc32.lib odbccp32.lib 
+#wsock32.lib
+
+
+!IF $(GSDL_VC4)
+DEFS = $(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo $(LDFLAGS) -Fe"$@"
+
+
+.SUFFIXES:
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+SOURCES = \
+	cgiwrapper.cpp \
+	conftools.cpp \
+	d_winsock.cpp \
+	fnord.cpp \
+	httpreq.cpp \
+	httpsend.cpp \
+        httpsrv.cpp \
+	locate.cpp \
+	netio.cpp \
+	parse.cpp \
+	settings.cpp \
+	startbrowser.cpp \
+	wincgiutils.cpp
+
+OBJECTS = \
+	cgiwrapper.obj \
+	conftools.obj \
+	d_winsock.obj \
+	fnord.obj \
+	httpreq.obj \
+	httpsend.obj \
+        httpsrv.obj \
+	locate.obj \
+	netio.obj \
+	parse.obj \
+	settings.obj \
+	startbrowser.obj \
+	wincgiutils.obj
+
+RESOURCES = \
+	server.res
+
+GSDL_LIBS = \
+	"$(RECPT_DIR)\gsdlrecpt.lib" \
+	"$(PROTOCOL_DIR)\gsdlprotocol.lib" \
+	"$(COLSERVR_DIR)\gsdlcolservr.lib" \
+	"$(INDEXERS_DIR)\mg\src\text\libmgtext.lib" \
+	"$(INDEXERS_DIR)\mg\lib\libmglib.lib" \
+	"$(INDEXERS_DIR)\mgpp\text\libmgpptext.lib" \
+	"$(INDEXERS_DIR)\mgpp\lib\libmgpplib.lib" \
+	"$(GSDL_DIR)\src\lib\gsdllib.lib"
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(RESOURCES) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = server.exe
+
+
+# Default target: make the local library executable
+all: $(EXECUTABLE)
+
+server.exe: $(EXECUTABLE_OBJECTS)
+	$(LINK) $(EXECUTABLE_OBJECTS) $(LIBS) $(WINLIBS) 
+install:
+	copy $(EXECUTABLE) "$(GSDL_DIR)"
+
+clean:
+	del *.obj
+	del $(EXECUTABLE)
+
+
+# Resources stuff
+SOURCE = "$(GSDL_DIR)\src\w32server\newgsdl.bmp"
+SOURCE = "$(GSDL_DIR)\src\w32server\resource.h"
+SOURCE = "$(GSDL_DIR)\src\w32server\server.ico"
+SOURCE = "$(GSDL_DIR)\src\w32server\gsdlcol.bmp"
+SOURCE = "$(GSDL_DIR)\src\w32server\server.rc"
+DEP_RSC_SERVE=\
+	"$(GSDL_DIR)\src\w32server\gsdlcol.bmp"\
+	"$(GSDL_DIR)\src\w32server\newgsdl.bmp"\
+	"$(GSDL_DIR)\src\w32server\server.ico"
+
+server.res : $(SOURCE) $(DEP_RSC_SERVE) "$(GSDL_DIR)\src\w32server"
+   $(RSC) /l 0x409 /fo"$(GSDL_DIR)\src\w32server\server.res" /i "$(GSDL_DIR)\src\w32server" \
+ /d "NDEBUG" $(SOURCE)
+
+"$(GSDL_DIR)\src\w32server\server.res" : $(SOURCE) $(DEP_RSC_SERVE) "$(GSDL_DIR)\src\w32server"
+   $(RSC) /l 0x409 /fo"$(GSDL_DIR)\src\w32server\server.res" /i "$(GSDL_DIR)\src\w32server" \
+ /d "NDEBUG" $(SOURCE)
Index: /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.cpp	(revision 16561)
@@ -0,0 +1,167 @@
+/**********************************************************************
+ *
+ * wincgiutils.cpp
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#include "text_t.h"
+#include <windows.h>
+#include <string.h>
+#include "netio.h"
+#include "httpreq.h"
+#include "wincgiutils.h"
+
+#define default_http_port 80
+
+// Attempt to break a url into segments. If the parsing fails, 'name' is
+// left pointing to the unparsed portion.
+int parse_url(const text_t &url, text_t &protocol, text_t &machine, 
+	      int *port, text_t &name) {
+
+  protocol.clear();
+  machine.clear();
+  *port = default_http_port;
+  name.clear();
+  
+  text_t::const_iterator begin = url.begin();
+  text_t::const_iterator end = url.end();
+  text_t::const_iterator it = findchar(begin, end, ':');
+  if (it == end) {name = url; return http_error_url_invalid;}
+  if (substr(it+1, it+3) != "//") {name = url; return http_error_url_invalid;}
+  protocol = substr(begin, it);
+  it += 3;
+  while (it < end) {
+    if (*it == ':') {
+      ++it;
+      text_t portstr;
+      while (it != end && (*it >= '0' && *it <= '9')) {
+	portstr.push_back(*it);
+	++it;
+      }
+      *port = portstr.getint();
+      if (it == end) break;
+    }
+
+    if (*it == '/') {
+      while (it != end) {
+	name.push_back(*it);
+	++it;
+      }
+      break;
+    }
+
+    machine.push_back(*it);
+    ++it;
+  }
+  return http_ok;
+}
+
+int Send_String(char *str, RequestInfoT *RInfo)
+{
+  return
+    SendData(RInfo->ClientSocket, (BYTE *)str, strlen(str), RInfo->ThreadNum);
+}
+
+int Send_String_N(char *str, int n,  RequestInfoT *RInfo)
+{
+  return
+    SendData(RInfo->ClientSocket, (BYTE *)str, n, RInfo->ThreadNum);
+}
+
+int send_header(char *content, RequestInfoT *RequestInfo)
+{
+  char Header[1536];  int len, lenc;
+  //Build Header
+  Header[0] = 0;
+  //Status Line
+  if (content[0] != '@')          /*No redirection needed*/
+    strcat(Header, "HTTP/1.0 200 OK\r\n");
+  else                            /*Redirection*/
+    strcat(Header, "HTTP/1.0 302 Relocation\r\n");
+  //Server
+  strcat(Header, "Server: GSDL\r\n");
+  //Content Type
+  strcat(Header, "Content-type: ");
+  if (content[0] != '@')          /*No redirection needed*/
+    strcat(Header, content);
+  else {                          /*Redirection entry*/
+    strcat(Header, "text/html \r\n");
+    strcat(Header, "Location: ");
+    len = strlen(Header);
+    lenc = strlen(content) - 1;
+    strncpy(&Header[len], &content[1], lenc);
+    Header[len+lenc] = 0;
+  }
+  strcat(Header, " \r\n");
+  //Single CRLF to end header
+  strcat(Header, "\r\n");
+  
+  return Send_String(Header, RequestInfo);
+}
+
+void send_retrieve_error(int error_num, char *msg1, char *msg2, RequestInfoT *RequestInfo)
+{
+  char Header[512], Body[512], ErrorNumStr[17], BodyLengthStr[17];
+  DWORD BodyLength; int HeaderLength;
+  
+  itoa(error_num, ErrorNumStr, 10);
+  
+  //Build Data (so we can determine length for the header)
+  Body[0] = 0;
+  strcat(Body, "<HTML><HEAD><TITLE>Server Error</TITLE></HEAD><BODY>\n");
+  strcat(Body, "<H1>Error ");
+  strcat(Body, ErrorNumStr);
+  strcat(Body, ": ");
+  strcat(Body, msg1);
+  strcat(Body, "</H1>\n");
+  strcat(Body, msg2);
+  strcat(Body, "\n</BODY></HTML>");
+  BodyLength = (DWORD) strlen(Body);
+
+  //Build Header
+  Header[0] = 0;
+  //Status Line
+  strcat(Header, "HTTP/1.0 ");
+  strcat(Header, ErrorNumStr);
+  strcat(Header, " ");
+  strcat(Header, msg1);
+  strcat(Header, "\r\n");
+  //Server
+  strcat(Header, "Server: GSDL\r\n");
+  strcat(Header, "Content-Type: text/html\r\n");
+  //Content Length
+  itoa(BodyLength, BodyLengthStr, 10);
+  strcat(Header, "Content-Length: ");
+  strcat(Header, BodyLengthStr);
+  strcat(Header, "\r\n");
+  //Single CRLF to end header
+  strcat(Header, "\r\n");
+  HeaderLength = strlen(Header);
+
+   //Send the file if we have one (and if it's not a HEAD request)
+  SendData(RequestInfo->ClientSocket, (BYTE *) Header, strlen(Header), RequestInfo->ThreadNum);
+  //   if ((FileFound == FALSE) && (strcmpi(RequestFields.MethodStr, "HEAD") != 0)) {
+  //Send generated data
+  SendData(RequestInfo->ClientSocket, (BYTE *) Body, BodyLength, RequestInfo->ThreadNum);
+}
Index: /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/w32server/wincgiutils.h	(revision 16561)
@@ -0,0 +1,62 @@
+/**********************************************************************
+ *
+ * wincgiutils.h
+ * Copyright (C) 1996
+ * 
+ * A component of the fnord webserver written by bmorin@wpi.edu.
+ *
+ * Altered for use with the Greenstone digital library software by 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.
+ *
+ *********************************************************************/
+
+#define http_ok 0
+#define http_error_url_invalid 11
+
+int parse_url(const text_t &url, text_t &protocol, text_t &machine, 
+	      int *port, text_t &name);
+
+struct param_vals {char *name, *value; int vsize; char *dfault; };
+
+
+int Send_String(char *str, RequestInfoT *RInfo);
+int Send_String_N(char *str, int n,  RequestInfoT *RInfo);
+void send_retrieve_error(int error_num, char *msg1, char *msg2, RequestInfoT *RequestInfo);
+int send_header(char *content, RequestInfoT *RequestInfo);
+void decode(char *st);
+void decode_template(int ntemp, param_vals tmplate[]);
+void parse_query_string(char *qstring, int ntemp, param_vals tmplate[]);
+int mg_sender(char *UDoc, int ULen, int DocNum, float Weight, void *RInfo);
+
+#define OUT_CONST_A(t) \
+{if (Send_String_N(t, sizeof(t), RInfo) < 0) return;}
+
+#define OUT_CONST_B(t) \
+{if (Send_String_N(t, sizeof(t), (RequestInfoT *)RInfo) < 0) return -1;}
+
+#define OUT_STRING_A(t) \
+{if (Send_String(t, RInfo) < 0) return;}
+
+#define OUT_STRING_B(t) \
+{if (Send_String(t, (RequestInfoT *)RInfo) < 0) return -1;}
+
+#define OUT_STRING_N_A(t,n) \
+{if (Send_String_N(t, n, RInfo) < 0) return;}
+
+#define OUT_STRING_N_B(t,n) \
+{if (Send_String_N(t, n, (RequestInfoT *)RInfo) < 0) return -1;}
Index: /gsdl/trunk/gs2runtime/src/z3950/Makefile.in
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/Makefile.in	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/Makefile.in	(revision 16561)
@@ -0,0 +1,157 @@
+###########################################################################
+#
+# Makefile -- src/z3950
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ../..
+INDEXERS_DIR = $(GSDL_DIR)/indexers
+PACKAGES_DIR = $(GSDL_DIR)/packages
+COLSERVR_DIR = $(GSDL_DIR)/src/colservr
+PROTOCOL_DIR = $(GSDL_DIR)/src/protocol
+
+
+ENABLE_ACCENTFOLD = @ENABLE_ACCENTFOLD@
+ifeq ($(ENABLE_ACCENTFOLD), 1)
+ACCENTFOLD_LIBS = $(INDEXERS_DIR)/packages/unac/libunac.a 
+else
+ACCENTFOLD_LIBS = 
+endif
+
+USE_SQLITE = @USE_SQLITE@
+ifeq ($(USE_SQLITE), 1)
+SQLITE_LIBS = $(PACKAGES_DIR)/sqlite/lib/libsqlite3.a -ldl -lpthread
+else
+SQLITE_LIBS = 
+endif
+
+USE_Z3950 = @USE_Z3950@
+ifeq ($(USE_Z3950), 1)
+AR = ar
+CC = @CC@
+CPP = @CXX@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CXXFLAGS@
+DEFS = @DEFS@ -DNZDL -DQUIET -DSHORT_SUFFIX -DPARADOCNUM -DGSDLSERVER
+GSDLOS = @gsdlos@
+# Do NOT add the "recpt" directory here: the z39.50 code should be independent of the receptionist!
+INCLUDES = -I. -I$(GSDL_DIR) -I$(GSDL_DIR)/src/lib -I$(COLSERVR_DIR) -I$(PROTOCOL_DIR) \
+	-I$(INDEXERS_DIR)/mgpp/text -I$(PACKAGES_DIR)/yaz/yaz-2.1.4/include \
+	-I$(PACKAGES_DIR)/yaz/yaz-2.1.4/client -I$(PACKAGES_DIR)/d2m
+INSTALL = @INSTALL@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@ @GDBM_LIBPATH@ -lgdbm -L$(PACKAGES_DIR)/expat/lib -lexpat $(ACCENTFOLD_LIBS) $(SQLITE_LIBS) \
+	$(PACKAGES_DIR)/yaz/lib/libyaz.a $(PACKAGES_DIR)/d2m/libd2m.a -lxml2
+RANLIB = @RANLIB@
+
+
+COMPILE = $(CC) -c $(CFLAGS) $(DEFS) $(INCLUDES)
+CPPCOMPILE = $(CPP) -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+CPPLINK = $(CPP) $(LDFLAGS) -o $@
+
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+.c.o:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .o
+.cpp.o:
+	$(CPPCOMPILE) $< 
+
+
+LIBRARY_SOURCES = \
+	yaz_zclient.c \
+	z3950proto.cpp \
+	z3950proxy.cpp \
+	zparse.tab.c \
+	zparse.yy.c
+
+LIBRARY_OBJECTS = \
+	yaz_zclient.o \
+	z3950proto.o \
+	z3950proxy.o \
+	zparse.tab.o \
+	zparse.yy.o
+
+LIBRARY = gsdlz3950.a
+
+SOURCES = \
+	z3950explain.cpp \
+	z3950parser.cpp \
+	z3950server.cpp \
+	z3950_to_gsdl.cpp
+
+OBJECTS = \
+	z3950explain.o \
+	z3950parser.o \
+	z3950server.o \
+	z3950_to_gsdl.o
+
+GSDL_LIBS = \
+	gsdlz3950.a \
+	$(PROTOCOL_DIR)/gsdlprotocol.a \
+	$(COLSERVR_DIR)/gsdlcolservr.a \
+	$(INDEXERS_DIR)/mg/src/text/libmgtext.a \
+	$(INDEXERS_DIR)/mg/lib/libmglib.a \
+	$(INDEXERS_DIR)/mgpp/text/libmgpptext.a \
+	$(INDEXERS_DIR)/mgpp/lib/libmgpplib.a \
+	$(GSDL_DIR)/src/lib/gsdllib.a
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = z3950server
+
+
+# Default target: make both gsdlz3950.a and the z3950server executable
+all: $(LIBRARY) $(EXECUTABLE)
+
+gsdlz3950.a: $(LIBRARY_OBJECTS)
+	rm -f $(LIBRARY)
+	$(AR) cru $(LIBRARY) $(LIBRARY_OBJECTS) $(PACKAGES_DIR)/yaz/yaz-2.1.4/client/tabcomplete.o
+	$(RANLIB) $(LIBRARY)
+
+z3950server: $(EXECUTABLE_OBJECTS)
+	$(CPPLINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	$(INSTALL) -m 755 -s $(EXECUTABLE) $(GSDL_DIR)/bin/$(GSDLOS)
+
+clean:
+	rm -f $(OBJECTS) $(LIBRARY) $(EXECUTABLE)
+
+distclean:
+	rm -f $(OBJECTS) $(LIBRARY) $(EXECUTABLE) Makefile
+
+depend:
+	makedepend -Y -- $(DEFS) $(INCLUDES) $(CPPFLAGS) -- $(SOURCES)
+
+else
+all:
+install:
+clean:
+distclean:
+depend:
+endif
Index: /gsdl/trunk/gs2runtime/src/z3950/win32.mak
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/win32.mak	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/win32.mak	(revision 16561)
@@ -0,0 +1,163 @@
+###########################################################################
+#
+# win32 makefile -- src/z3950
+# 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-2008  The 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.
+#
+###########################################################################
+
+GSDL_DIR = ..\..
+INDEXERS_DIR = $(GSDL_DIR)\indexers
+PACKAGES_DIR = $(GSDL_DIR)\packages
+COLSERVR_DIR = $(GSDL_DIR)\src\colservr
+PROTOCOL_DIR = $(GSDL_DIR)\src\protocol
+
+
+GSDL_VC4 = 0
+DLL = 0
+DLLDEBUG = 0
+ENABLE_ACCENTFOLD = 1
+USE_SQLITE = 0
+
+
+!IF $(GSDL_VC4)
+CPPFLAGS = -GX
+!ELSE
+CPPFLAGS = -EHsc
+!ENDIF
+
+!IF $(DLL)
+CPPFLAGS = $(CPPFLAGS) -MD
+!ELSE
+!IF ($(DLLDEBUG))
+CPPFLAGS = $(CPPFLAGS) -MDd
+!ENDIF
+!ENDIF
+
+!IF $(ENABLE_ACCENTFOLD)
+ACCENTFOLD_LIBS = "$(INDEXERS_DIR)\packages\unac\libunac.lib" "$(INDEXERS_DIR)\packages\unac\iconv.lib"
+!ELSE
+ACCENTFOLD_LIBS = 
+!ENDIF
+
+!IF $(USE_SQLITE)
+SQLITE_LIBS = "$(PACKAGES_DIR)\sqlite\lib\sqlite3.lib"
+!ELSE
+SQLITE_LIBS = 
+!ENDIF
+
+
+AR = lib
+CC = cl
+DEFS = -D__WIN32__ -DHAVE_CONFIG_H -DPARADOCNUM -D_LITTLE_ENDIAN -DSHORT_SUFFIX -D_CRT_SECURE_NO_DEPRECATE
+# Do NOT add the "recpt" directory here: the z39.50 code should be independent of the receptionist!
+INCLUDES = -I"$(GSDL_DIR)" -I"$(GSDL_DIR)\src\lib" -I"$(COLSERVR_DIR)" -I"$(PROTOCOL_DIR)" \
+	-I"$(INDEXERS_DIR)\mgpp\text" -I"$(PACKAGES_DIR)\yaz\yaz-2.1.4\include" \
+	-I"$(PACKAGES_DIR)\yaz\yaz-2.1.4\client" -I"$(PACKAGES_DIR)\d2m" \
+	-I"$(PACKAGES_DIR)\windows\gdbm\gdbm"
+LDFLAGS = 
+LIBS = "$(PACKAGES_DIR)\windows\expat\expat\libexpat.lib" "$(PACKAGES_DIR)\windows\gdbm\gdbm\gdbm.lib" \
+        "$(PACKAGES_DIR)\d2m\libd2m.lib" "$(PACKAGES_DIR)\yaz\yaz-2.1.4\lib\yaz.lib" $(ACCENTFOLD_LIBS) \
+	$(SQLITE_LIBS)
+
+!IF $(GSDL_VC4)
+DEFS=$(DEFS) -DGSDL_NAMESPACE_BROKEN -DGSDL_USE_IOS_H -D__STL_NO_NEW_IOSTREAMS
+INCLUDES = $(INCLUDES) -I"$(PACKAGES_DIR)\windows\stlport\stlport"
+!ENDIF
+
+
+COMPILE = $(CC) -nologo -c $(CPPFLAGS) $(DEFS) $(INCLUDES)
+LINK = $(CC) -nologo -Fe"$@" $(LDFLAGS)
+
+
+.SUFFIXES:
+.SUFFIXES: .c .obj
+.c.obj:
+	$(COMPILE) $<
+
+.SUFFIXES: .cpp .obj
+.cpp.obj:
+	$(COMPILE) $< 
+
+
+LIBRARY_SOURCES = \
+	yaz_zclient.c \
+	z3950proto.cpp \
+	z3950proxy.cpp \
+	zparse.tab.c \
+	zparse.yy.c
+
+LIBRARY_OBJECTS = \
+	yaz_zclient.obj \
+	z3950proto.obj \
+	z3950proxy.obj \
+	zparse.tab.obj \
+	zparse.yy.obj
+
+LIBRARY = gsdlz3950.lib
+
+
+SOURCES = \
+	z3950explain.cpp \
+	z3950parser.cpp \
+	z3950server.cpp \
+	z3950_to_gsdl.cpp
+
+OBJECTS = \
+	z3950explain.obj \
+	z3950parser.obj \
+	z3950server.obj \
+	z3950_to_gsdl.obj
+
+GSDL_LIBS = \
+	gsdlz3950.lib \
+	$(PROTOCOL_DIR)\gsdlprotocol.lib \
+	$(COLSERVR_DIR)\gsdlcolservr.lib \
+	$(INDEXERS_DIR)\mg/src/text/libmgtext.lib \
+	$(INDEXERS_DIR)\mg/lib/libmglib.lib \
+	$(INDEXERS_DIR)\mgpp/text/libmgpptext.lib \
+	$(INDEXERS_DIR)\mgpp/lib/libmgpplib.lib \
+	$(GSDL_DIR)\src\lib\gsdllib.lib
+
+EXECUTABLE_OBJECTS = \
+	$(OBJECTS) \
+	$(GSDL_LIBS)
+
+EXECUTABLE = z3950server.exe
+
+
+# Default target: make both gsdlz3950.lib and z3950server.exe
+all: $(LIBRARY) $(EXECUTABLE)
+
+gsdlz3950.lib: $(LIBRARY_OBJECTS)
+	if EXIST $(LIBRARY) del $(LIBRARY)
+	$(AR) -nologo /out:$(LIBRARY) $(LIBRARY_OBJECTS) $(PACKAGES_DIR)/yaz/yaz-2.1.4/win/obj/tabcomplete.obj
+
+z3950server.exe: $(EXECUTABLE_OBJECTS)
+	$(LINK) $(EXECUTABLE_OBJECTS) $(LIBS)
+
+install: $(EXECUTABLE)
+	copy $(EXECUTABLE) "$(GSDL_DIR)\bin\windows"
+
+clean:
+	del *.obj
+	del $(LIBRARY)
+	del $(EXECUTABLE)
Index: /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.c
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.c	(revision 16561)
@@ -0,0 +1,5410 @@
+/* 
+ * Copyright (C) 1995-2005, Index Data 
+ * See the file LICENSE for details.
+ *
+ */
+
+/* Modified for the Greenstone Digital Library project, based on yaz 2's client.c
+ kjdon, copying version 1.6 mods done by johnmcp */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#if HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/* this one needs to be there
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+*/
+#include <sys/stat.h>
+
+#if HAVE_OPENSSL_SSL_H
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#ifdef __WIN32__
+#include <sys/stat.h>
+#include <io.h>
+#include <windows.h>
+#define S_ISREG(x) (x & _S_IFREG)
+#define S_ISDIR(x) (x & _S_IFDIR)
+#endif
+
+#include <yaz/yaz-util.h>
+
+#include <yaz/comstack.h>
+
+#include <yaz/proto.h>
+#include <yaz/marcdisp.h>
+#include <yaz/diagbib1.h>
+#include <yaz/otherinfo.h>
+#include <yaz/charneg.h>
+
+#include <yaz/pquery.h>
+#include <yaz/sortspec.h>
+
+#include <yaz/ill.h>
+#include <yaz/srw.h>
+#include <yaz/yaz-ccl.h>
+#include <yaz/cql.h>
+#include <yaz/log.h>
+
+#if HAVE_READLINE_READLINE_H
+#include <readline/readline.h>
+#endif
+#if HAVE_READLINE_HISTORY_H
+#include <readline/history.h>
+#endif
+
+/* these files are from the yaz client */
+#include "tabcomplete.h"
+
+#include "yaz_zclient.h"
+
+#define C_PROMPT "Z> "
+
+static char *codeset = 0;               /* character set for output */
+static int hex_dump = 0;
+static char *dump_file_prefix = 0;
+static ODR out, in, print;              /* encoding and decoding streams */
+#if HAVE_XML2
+static ODR srw_sr_odr_out = 0;
+static Z_SRW_PDU *srw_sr = 0;
+#endif
+static FILE *apdu_file = 0;
+static FILE *ber_file = 0;
+static COMSTACK conn = 0;               /* our z-association */
+static Z_IdAuthentication *auth = 0;    /* our current auth definition */
+char *databaseNames[128];
+int num_databaseNames = 0;
+static Z_External *record_last = 0;
+static int setnumber = -1;              /* current result set number */
+static int smallSetUpperBound = 0;
+static int largeSetLowerBound = 1;
+static int mediumSetPresentNumber = 0;
+static Z_ElementSetNames *elementSetNames = 0; 
+static int setno = 1;                   /* current set offset */
+static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
+static enum oid_value recordsyntax = VAL_USMARC;
+static char *record_schema = 0;
+static int sent_close = 0;
+static NMEM session_mem = NULL;         /* memory handle for init-response */
+static Z_InitResponse *session = 0;     /* session parameters */
+static char last_scan_line[512] = "0";
+static char last_scan_query[512] = "0";
+static char ccl_fields[512] = "default.bib";
+/* ### How can I set this path to use wherever YAZ is installed? */
+/* ############## change */
+static char cql_fields[512] = "/research/kjdon/home/z3950/gsdl/packages/yaz2/etc/pqf.properties";
+static char *esPackageName = 0;
+static char *yazProxy = 0;
+static int kilobytes = 1024;
+static char *negotiationCharset = 0;
+static int  negotiationCharsetRecords = 1;
+static int  negotiationCharsetVersion = 3;
+static char *outputCharset = 0;
+static char *marcCharset = 0;
+static char* yazLang = 0;
+static char* http_version = "1.1";
+
+static char last_cmd[32] = "?";
+static FILE *marc_file = 0;
+static char *refid = NULL;
+static char *last_open_command = NULL;
+static int auto_reconnect = 0;
+static Odr_bitmask z3950_options;
+static int z3950_version = 3;
+static int scan_stepSize = 0;
+static int scan_position = 1;
+static int scan_size = 20;
+static char cur_host[200];
+
+typedef enum {
+    QueryType_Prefix,
+    QueryType_CCL,
+    QueryType_CCL2RPN,
+    QueryType_CQL,
+    QueryType_CQL2RPN
+} QueryType;
+
+static QueryType queryType = QueryType_Prefix;
+
+static CCL_bibset bibset;               /* CCL bibset handle */
+static cql_transform_t cqltrans;	/* CQL context-set handle */
+
+#if HAVE_READLINE_COMPLETION_OVER
+
+#else
+/* readline doesn't have this var. Define it ourselves. */
+int rl_attempted_completion_over = 0;
+#endif
+
+/* set this one to 1, to avoid decode of unknown MARCs  */
+#define AVOID_MARC_DECODE 1
+
+#define maxOtherInfosSupported 10
+struct {
+    int oidval;
+    char* value;
+} extraOtherInfos[maxOtherInfosSupported];
+	
+
+void process_cmd_line(char* line);
+char ** readline_completer(char *text, int start, int end);
+char *command_generator(const char *text, int state);
+char** curret_global_list=NULL;
+int cmd_register_tab(const char* arg);
+
+static void close_session (void);
+
+ODR getODROutputStream()
+{
+    return out;
+}
+
+const char* query_type_as_string(QueryType q) 
+{
+    switch (q) { 
+    case QueryType_Prefix: return "prefix (RPN sent to server)";
+    case QueryType_CCL: return "CCL (CCL sent to server) ";
+    case QueryType_CCL2RPN: return "CCL -> RPN (RPN sent to server)";
+    case QueryType_CQL: return "CQL (CQL sent to server)";
+    case QueryType_CQL2RPN: return "CQL -> RPN (RPN sent to server)";
+    default: 
+        return "unknown Query type internal yaz-client error";
+    }
+}
+
+static void do_hex_dump(const char* buf, int len) 
+{
+    if (hex_dump)
+    {
+	int i,x;
+	for( i=0; i<len ; i=i+16 ) 
+	{			
+	    printf(" %4.4d ",i);
+	    for(x=0 ; i+x<len && x<16; ++x) 
+	    {
+		printf("%2.2X ",(unsigned int)((unsigned char)buf[i+x]));
+	    }
+	    printf("\n");
+	}
+    }
+    if (dump_file_prefix)
+    {
+	static int no = 0;
+	if (++no < 1000 && strlen(dump_file_prefix) < 500)
+	{
+	    char fname[1024];
+	    FILE *of;
+	    sprintf (fname, "%s.%03d.raw", dump_file_prefix, no);
+	    of = fopen(fname, "wb");
+	    
+	    fwrite (buf, 1, len, of);
+	    
+	    fclose(of);
+	}
+    }
+}
+
+void add_otherInfos(Z_APDU *a) 
+{
+    Z_OtherInformation **oi;
+    int i;
+		
+    yaz_oi_APDU(a, &oi);
+    for(i=0; i<maxOtherInfosSupported; ++i) 
+    {
+        if(extraOtherInfos[i].oidval != -1) 
+            yaz_oi_set_string_oidval(oi, out, extraOtherInfos[i].oidval,
+                                     1, extraOtherInfos[i].value);
+    }   
+}
+
+int send_apdu(Z_APDU *a)
+{
+    char *buf;
+    int len;
+    
+    add_otherInfos(a);
+    
+    if (apdu_file)
+    {
+        z_APDU(print, &a, 0, 0);
+        odr_reset(print);
+    }
+    if (!z_APDU(out, &a, 0, 0))
+    {
+        odr_perror(out, "Encoding APDU");
+        close_session();
+        return 0;
+    }
+    buf = odr_getbuf(out, &len, 0);
+    if (ber_file)
+        odr_dumpBER(ber_file, buf, len);
+    /* printf ("sending APDU of size %d\n", len); */
+    do_hex_dump(buf, len);
+    if (cs_put(conn, buf, len) < 0)
+    {
+        fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
+        close_session();
+        return 0;
+    }
+    odr_reset(out); /* release the APDU structure  */
+    return 1;
+}
+
+static void print_stringn(const unsigned char *buf, size_t len)
+{
+    size_t i;
+    for (i = 0; i<len; i++)
+        if ((buf[i] <= 126 && buf[i] >= 32) || strchr ("\n\r\t\f", buf[i]))
+            printf ("%c", buf[i]);
+        else
+            printf ("\\X%02X", buf[i]);
+}
+
+static void print_refid (Z_ReferenceId *id)
+{
+    if (id)
+    {
+        printf ("Reference Id: ");
+        print_stringn (id->buf, id->len);
+        printf ("\n");
+    }
+}
+
+static Z_ReferenceId *set_refid (ODR out)
+{
+    Z_ReferenceId *id;
+    if (!refid)
+        return 0;
+    id = (Z_ReferenceId *) odr_malloc (out, sizeof(*id));
+    id->size = id->len = strlen(refid);
+    id->buf = (unsigned char *) odr_malloc (out, id->len);
+    memcpy (id->buf, refid, id->len);
+    return id;
+}   
+
+/* INIT SERVICE ------------------------------- */
+
+static void send_initRequest(const char* type_and_host)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
+    Z_InitRequest *req = apdu->u.initRequest;
+    int i;
+
+    req->options = &z3950_options;
+
+    ODR_MASK_ZERO(req->protocolVersion);
+    for (i = 0; i<z3950_version; i++)
+	ODR_MASK_SET(req->protocolVersion, i);
+
+    *req->maximumRecordSize = 1024*kilobytes;
+    *req->preferredMessageSize = 1024*kilobytes;
+
+    req->idAuthentication = auth;
+
+    req->referenceId = set_refid (out);
+
+    if (yazProxy && type_and_host) 
+        yaz_oi_set_string_oidval(&req->otherInfo, out, VAL_PROXY,
+        1, type_and_host);
+    
+    if (negotiationCharset || yazLang) {
+    	Z_OtherInformation **p;
+    	Z_OtherInformationUnit *p0;
+    	
+    	yaz_oi_APDU(apdu, &p);
+    	
+    	if ((p0=yaz_oi_update(p, out, NULL, 0, 0))) {
+	    ODR_MASK_SET(req->options, Z_Options_negotiationModel);
+	    
+	    p0->which = Z_OtherInfo_externallyDefinedInfo;
+	    p0->information.externallyDefinedInfo =
+		yaz_set_proposal_charneg(
+		    out,
+		    (const char**)&negotiationCharset, 
+		    negotiationCharset ? 1 : 0,
+		    (const char**)&yazLang, yazLang ? 1 : 0, 
+		    negotiationCharsetRecords);
+    	}
+    }
+    
+    send_apdu(apdu);
+    /* (send_apdu(apdu))
+	intf("Sent initrequest.\n");*/
+}
+
+/* in old version, John had got rid of process_initResponse, and added
+   z_get_initResponse. I'll add this in but leave the other one alone 
+ this returns the info that is used in the description for the 'collection'*/
+char *z_get_initResponse()
+{
+    
+    char* buffer; /* we are returning some info in this buffer */
+    char *text_id="ID: ";
+    char *text_name="<br>\nName: ";
+    char *text_ver="<br>\nVersion: ";
+    size_t initial_length;
+    
+    if (!*session->result)
+	return NULL; /* no init info to return */
+    
+   
+    initial_length = strlen(text_id)+strlen(text_name)+strlen(text_ver)+
+	(session->implementationId?strlen(session->implementationId):0) +
+	(session->implementationName?strlen(session->implementationName):0) +
+	(session->implementationVersion?strlen(session->implementationVersion)
+	 :0)  + 1; /* for null char */
+    
+    if ((buffer = malloc((sizeof(char *))*initial_length)) == NULL) {
+	fprintf(stderr,"Malloc failed while initialising z39.50 server\n");
+	return (NULL);
+    }
+    /* can't pass NULL to sprintf as a (char *) */
+    sprintf(buffer,"%s%s%s%s%s%s",
+	    session->implementationId?text_id:"",
+	    session->implementationId?session->implementationId:"",
+	    session->implementationName?text_name:"",
+	    session->implementationName?session->implementationName:"",
+	    session->implementationVersion?text_ver:"",
+	    session->implementationVersion?session->implementationVersion:""
+	    );
+    return (buffer);
+    
+}
+
+char *z_get_initResponseOld()
+{
+    char *buffer; 
+    size_t needed_length;
+    char *text_id="ID: ";
+    char *text_name="<br>\nName: ";
+    char *text_ver="<br>\nVersion: ";
+    int counter;
+    
+    buffer=NULL;
+    if (!*session->result)
+	return NULL;
+    
+    /* work out total string length needed. Note strlen(NULL) is a bad thing
+       to do. */
+    needed_length=strlen(text_id)+strlen(text_name)+strlen(text_ver)+
+	(session->implementationId?strlen(session->implementationId):0) +
+	(session->implementationName?strlen(session->implementationName):0) +
+	(session->implementationVersion?strlen(session->implementationVersion)
+	 :0) +
+	(session->userInformationField?
+	 strlen(session->userInformationField->u.octet_aligned->buf):0) +
+	1 /* for null char */ ;
+    if ((buffer=malloc((sizeof(char *)) * needed_length))==NULL) {
+	fprintf(stderr,"Malloc failed while initialising z39.50 server\n");
+	return (NULL);
+    }
+    /* can't pass NULL to sprintf as a (char *) */
+    sprintf(buffer,"%s%s%s%s%s%s",
+	    session->implementationId?text_id:"",
+	    session->implementationId?session->implementationId:"",
+	    session->implementationName?text_name:"",
+	    session->implementationName?session->implementationName:"",
+	    session->implementationVersion?text_ver:"",
+	    session->implementationVersion?session->implementationVersion:""
+	    );
+    
+    /* if version 3, also check the other-information parameter of the
+       response. (But check version is 3 first) */
+    if (session->otherInfo)
+	/**** From prt-proto.h *********** (comment added by johnmcp)
+	      typedef struct Z_OtherInformationUnit
+       .   {  Z_InfoCategory *category;        / * OPTIONAL * /
+       .      int which;
+       #define Z_OtherInfo_characterInfo 0
+       #define Z_OtherInfo_binaryInfo 1
+       #define Z_OtherInfo_externallyDefinedInfo 2
+       #define Z_OtherInfo_oid 3
+       .      union
+       .        {
+       .          char *characterInfo; 
+       .          Odr_oct *binaryInfo;
+       .          Z_External *externallyDefinedInfo;
+       .          Odr_oid *oid;
+       .        } information;
+       .   } Z_OtherInformationUnit;
+       
+       typedef struct Z_OtherInformation
+       {
+       int num_elements;
+       Z_OtherInformationUnit **list;
+       } Z_OtherInformation;
+	************/
+	for (counter=0;counter<session->otherInfo->num_elements;counter++) {
+	    if (session->otherInfo->list[counter]->which ==
+		Z_OtherInfo_characterInfo) {
+		
+		/* add this extra string to our buffer */
+		int where=strlen(buffer);
+		buffer=realloc(buffer,where+
+			       strlen(session->otherInfo->list[counter]->
+				      information.characterInfo));
+		strcpy(buffer+where,
+		       session->otherInfo->list[counter]->
+		       information.characterInfo);
+	    }
+	}
+    return (buffer);
+}
+
+
+/* These two are used only from process_initResponse() */
+static void render_initUserInfo(Z_OtherInformation *ui1);
+static void render_diag(Z_DiagnosticFormat *diag);
+
+static void pr_opt(const char *opt, void *clientData)
+{
+    printf (" %s", opt);
+}
+
+static int process_initResponse(Z_InitResponse *res)
+{
+    int ver = 0;
+    /* save session parameters for later use */
+    session_mem = odr_extract_mem(in);
+    session = res;
+
+    for (ver = 0; ver < 8; ver++)
+        if (!ODR_MASK_GET(res->protocolVersion, ver))
+            break;
+
+    if (!*res->result)
+        printf("Connection rejected by v%d target.\n", ver);
+    else
+        printf("Connection accepted by v%d target.\n", ver);
+    if (res->implementationId)
+        printf("ID     : %s\n", res->implementationId);
+    if (res->implementationName)
+        printf("Name   : %s\n", res->implementationName);
+    if (res->implementationVersion)
+        printf("Version: %s\n", res->implementationVersion);
+    if (res->userInformationField)
+    {
+	Z_External *uif = res->userInformationField;
+	if (uif->which == Z_External_userInfo1) {
+	    render_initUserInfo(uif->u.userInfo1);
+	} else {
+	    printf("UserInformationfield:\n");
+	    if (!z_External(print, (Z_External**)&uif, 0, 0))
+	    {
+		odr_perror(print, "Printing userinfo\n");
+		odr_reset(print);
+	    }
+	    if (uif->which == Z_External_octet) {
+		printf("Guessing visiblestring:\n");
+		printf("'%.*s'\n", uif->u.octet_aligned->len,
+		       uif->u.octet_aligned->buf);
+	    }
+	    else if (uif->which == Z_External_single) 
+	    {
+		Odr_any *sat = uif->u.single_ASN1_type;
+		oident *oid = oid_getentbyoid(uif->direct_reference);
+		if (oid->value == VAL_OCLCUI) {
+		    Z_OCLC_UserInformation *oclc_ui;
+		    ODR decode = odr_createmem(ODR_DECODE);
+		    odr_setbuf(decode, sat->buf, sat->len, 0);
+		    if (!z_OCLC_UserInformation(decode, &oclc_ui, 0, 0))
+			printf ("Bad OCLC UserInformation:\n");
+		    else
+			printf ("OCLC UserInformation:\n");
+		    if (!z_OCLC_UserInformation(print, &oclc_ui, 0, 0))
+			printf ("Bad OCLC UserInformation spec\n");
+		    odr_destroy(decode);
+		}
+		else
+		{
+		    /* Peek at any private Init-diagnostic APDUs */
+		    printf("### NAUGHTY: External is '%.*s'\n",
+			   sat->len, sat->buf);
+		}
+	    }
+	    odr_reset (print);
+	}
+    }
+    printf ("Options:");
+    yaz_init_opt_decode(res->options, pr_opt, 0);
+    printf ("\n");
+
+    if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
+        setnumber = 0;
+    
+    if (ODR_MASK_GET(res->options, Z_Options_negotiationModel)) {
+    
+    	Z_CharSetandLanguageNegotiation *p =
+    		yaz_get_charneg_record(res->otherInfo);
+    	
+    	if (p) {
+            
+            char *charset=NULL, *lang=NULL;
+
+            int selected;
+            
+            yaz_get_response_charneg(session_mem, p, &charset, &lang,
+                                     &selected);
+
+	    if (outputCharset && negotiationCharset) {
+		    odr_set_charset (out, charset, outputCharset);
+		    odr_set_charset (in, outputCharset, charset);
+	    }
+	    else {
+		    odr_set_charset (out, 0, 0);
+		    odr_set_charset (in, 0, 0);
+	    }
+            
+            printf("Accepted character set : %s\n", charset);
+            printf("Accepted code language : %s\n", lang ? lang : "none");
+            printf("Accepted records in ...: %d\n", selected );
+    	}
+    }
+    fflush (stdout);
+    return 0;
+}
+
+
+static void render_initUserInfo(Z_OtherInformation *ui1) {
+    int i;
+    printf("Init response contains %d otherInfo unit%s:\n",
+	   ui1->num_elements, ui1->num_elements == 1 ? "" : "s");
+
+    for (i = 0; i < ui1->num_elements; i++) {
+	Z_OtherInformationUnit *unit = ui1->list[i];
+	printf("  %d: otherInfo unit contains ", i+1);
+	if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
+	    unit->information.externallyDefinedInfo &&
+	    unit->information.externallyDefinedInfo->which ==
+	    Z_External_diag1) {
+	    render_diag(unit->information.externallyDefinedInfo->u.diag1);
+	} 
+	else if (unit->which != Z_OtherInfo_externallyDefinedInfo)
+	{
+	    printf("unsupported otherInfo unit->which = %d\n", unit->which);
+	}
+	else 
+	{
+	    printf("unsupported otherInfo unit external %d\n",
+		   unit->information.externallyDefinedInfo ? 
+		   unit->information.externallyDefinedInfo->which : -2);
+	}
+    }
+}
+
+
+/* ### should this share code with display_diagrecs()? */
+static void render_diag(Z_DiagnosticFormat *diag) {
+    int i;
+
+    printf("%d diagnostic%s:\n", diag->num, diag->num == 1 ? "" : "s");
+    for (i = 0; i < diag->num; i++) {
+	Z_DiagnosticFormat_s *ds = diag->elements[i];
+	printf("    %d: ", i+1);
+	switch (ds->which) {
+	case Z_DiagnosticFormat_s_defaultDiagRec: {
+	    Z_DefaultDiagFormat *dd = ds->u.defaultDiagRec;
+	    /* ### should check `dd->diagnosticSetId' */
+	    printf("code=%d (%s)", *dd->condition,
+		   diagbib1_str(*dd->condition));
+	    /* Both types of addinfo are the same, so use type-pun */
+	    if (dd->u.v2Addinfo != 0)
+		printf(",\n\taddinfo='%s'", dd->u.v2Addinfo);
+	    break;
+	}
+	case Z_DiagnosticFormat_s_explicitDiagnostic:
+	    printf("Explicit diagnostic (not supported)");
+	    break;
+	default:
+	    printf("Unrecognised diagnostic type %d", ds->which);
+	    break;
+	}
+
+	if (ds->message != 0)
+	    printf(", message='%s'", ds->message);
+	printf("\n");
+    }
+}
+
+
+static int set_base(const char *arg)
+{
+    int i;
+    const char *cp;
+
+    for (i = 0; i<num_databaseNames; i++)
+        xfree (databaseNames[i]);
+    num_databaseNames = 0;
+    while (1)
+    {
+        char *cp1;
+        if (!(cp = strchr(arg, ' ')))
+            cp = arg + strlen(arg);
+        if (cp - arg < 1)
+            break;
+        databaseNames[num_databaseNames] = (char *)xmalloc (1 + cp - arg);
+        memcpy (databaseNames[num_databaseNames], arg, cp - arg);
+        databaseNames[num_databaseNames][cp - arg] = '\0';
+
+        for (cp1 = databaseNames[num_databaseNames]; *cp1 ; cp1++)
+            if (*cp1 == '+')
+                *cp1 = ' ';
+        num_databaseNames++;
+
+        if (!*cp)
+            break;
+        arg = cp+1;
+    }
+    if (num_databaseNames == 0)
+    {
+        num_databaseNames = 1;
+        databaseNames[0] = xstrdup("");
+    }
+    return 1;
+}
+
+static int cmd_base(const char *arg)
+{
+    if (!*arg)
+    {
+        printf("Usage: base <database> <database> ...\n");
+        return 0;
+    }
+    return set_base(arg);
+}
+
+void cmd_open_remember_last_open_command(const char* arg, char* new_open_command)
+{
+    if(last_open_command != arg) 
+    {
+        if(last_open_command) xfree(last_open_command);
+        last_open_command = xstrdup(new_open_command);
+    }
+}
+
+int session_connect(const char *arg)
+{
+    void *add;
+    char type_and_host[101];
+    const char *basep = 0;
+#if HAVE_OPENSSL_SSL_H
+    SSL *ssl;
+#endif
+    if (conn)
+    {
+        cs_close (conn);
+        conn = NULL;
+        if (session_mem)
+        {
+            nmem_destroy (session_mem);
+            session_mem = NULL;
+        }
+    }   
+    cs_get_host_args(arg, &basep);
+
+    strncpy(type_and_host, arg, sizeof(type_and_host)-1);
+    type_and_host[sizeof(type_and_host)-1] = '\0';
+
+    cmd_open_remember_last_open_command(arg, type_and_host);
+
+    if (yazProxy)
+        conn = cs_create_host(yazProxy, 1, &add);
+    else
+        conn = cs_create_host(arg, 1, &add);
+    if (!conn)
+    {
+        printf ("Couldn't create comstack\n");
+        return 0;
+    }
+#if HAVE_XML2
+    if (conn->protocol == PROTO_HTTP)
+	queryType = QueryType_CQL;
+#else
+    if (conn->protocol == PROTO_HTTP)
+    {
+        printf ("SRW/HTTP not enabled in this YAZ\n");
+        cs_close(conn);
+        conn = 0;
+        return 0;
+    }
+#endif
+    protocol = conn->protocol;
+    if (conn->protocol == PROTO_HTTP)
+        set_base("");
+    else
+        set_base("Default");
+    /*intf("Connecting...");*/
+    fflush(stdout);
+    if (cs_connect(conn, add) < 0)
+    {
+        printf ("error = %s\n", cs_strerror(conn));
+        if (conn->cerrno == CSYSERR)
+	{
+	    char msg[256];
+	    yaz_strerror(msg, sizeof(msg));
+	    printf ("%s\n", msg);
+	}
+        cs_close(conn);
+        conn = 0;
+        return 0;
+    }
+    /*intf("OK.\n");*/
+#if HAVE_OPENSSL_SSL_H
+    if ((ssl = (SSL *) cs_get_ssl(conn)))
+    {
+	X509 *server_cert = SSL_get_peer_certificate (ssl);
+
+	if (server_cert)
+	{
+	    char *pem_buf;
+	    int pem_len;
+	    BIO *bio = BIO_new(BIO_s_mem());
+
+	    /* get PEM buffer in memory */
+	    PEM_write_bio_X509(bio, server_cert);
+	    pem_len = BIO_get_mem_data(bio, &pem_buf);
+	    fwrite(pem_buf, pem_len, 1, stdout);
+	
+	    /* print all info on screen .. */
+	    X509_print_fp(stdout, server_cert);
+	    BIO_free(bio);
+
+	    X509_free (server_cert);
+	}
+    }
+#endif
+    if (basep && *basep)
+        set_base (basep);
+    if (protocol == PROTO_Z3950)
+    {
+        send_initRequest(type_and_host);
+        return 2;
+    }
+    return 0;
+}
+
+static int cmd_open(const char *arg)
+{
+    if (arg)
+    {
+        strncpy (cur_host, arg, sizeof(cur_host)-1);
+        cur_host[sizeof(cur_host)-1] = 0;
+    }
+    return session_connect(cur_host);
+}
+
+int z_cmd_open(char *host_and_port, char *base)
+{
+    char * full_command = (char *) xmalloc (strlen(host_and_port)+strlen(base)+6);
+    strcpy(full_command, "tcp:");
+    strcat(full_command, host_and_port);
+    strcat(full_command, "/");
+    strcat(full_command, base);
+    return cmd_open(full_command);
+}
+
+
+void try_reconnect() 
+{
+    char* open_command;
+	
+    if(!( auto_reconnect && last_open_command) ) return ;
+
+    open_command = (char *) xmalloc (strlen(last_open_command)+6);
+    strcpy (open_command, "open ");
+	
+    strcat (open_command, last_open_command);
+
+    process_cmd_line(open_command);
+	
+    xfree(open_command);				
+}
+
+int cmd_authentication(const char *arg)
+{
+    static Z_IdAuthentication au;
+    static char user[40], group[40], pass[40];
+    static Z_IdPass idPass;
+    int r;
+
+    if (!*arg)
+    {
+        printf("Auth field set to null\n");
+        auth = 0;
+        return 1;
+    }
+    r = sscanf (arg, "%39s %39s %39s", user, group, pass);
+    if (r == 0)
+    {
+        printf("Authentication set to null\n");
+        auth = 0;
+    }
+    if (r == 1)
+    {
+        auth = &au;
+	if (!strcmp(user, "-")) {
+	    au.which = Z_IdAuthentication_anonymous;
+	    printf("Authentication set to Anonymous\n");
+	} else {
+	    au.which = Z_IdAuthentication_open;
+	    au.u.open = user;
+	    printf("Authentication set to Open (%s)\n", user);
+	}
+    }
+    if (r == 2)
+    {
+        auth = &au;
+        au.which = Z_IdAuthentication_idPass;
+        au.u.idPass = &idPass;
+        idPass.groupId = NULL;
+        idPass.userId = user;
+        idPass.password = group;
+	printf("Authentication set to User (%s), Pass (%s)\n", user, group);
+    }
+    if (r == 3)
+    {
+        auth = &au;
+        au.which = Z_IdAuthentication_idPass;
+        au.u.idPass = &idPass;
+        idPass.groupId = group;
+        idPass.userId = user;
+        idPass.password = pass;
+	printf("Authentication set to User (%s), Group (%s), Pass (%s)\n",
+	       user, group, pass);
+    }
+    return 1;
+}
+
+/* SEARCH SERVICE ------------------------------ */
+static void display_record(Z_External *r);
+
+static void print_record(const unsigned char *buf, size_t len)
+{
+    size_t i = len;
+    print_stringn (buf, len);
+    /* add newline if not already added ... */
+    if (i <= 0 || buf[i-1] != '\n')
+        printf ("\n");
+}
+
+static void display_record(Z_External *r)
+{
+    oident *ent = oid_getentbyoid(r->direct_reference);
+
+    record_last = r;
+    /*
+     * Tell the user what we got.
+     */
+    if (r->direct_reference)
+    {
+        printf("Record type: ");
+        if (ent)
+            printf("%s\n", ent->desc);
+        else if (!odr_oid(print, &r->direct_reference, 0, 0))
+        {
+            odr_perror(print, "print oid");
+            odr_reset(print);
+        }
+    }
+    /* Check if this is a known, ASN.1 type tucked away in an octet string */
+    if (ent && r->which == Z_External_octet)
+    {
+        Z_ext_typeent *type = z_ext_getentbyref(ent->value);
+        char *rr;
+
+        if (type)
+        {
+            /*
+             * Call the given decoder to process the record.
+             */
+            odr_setbuf(in, (char*)r->u.octet_aligned->buf,
+                r->u.octet_aligned->len, 0);
+            if (!(*type->fun)(in, &rr, 0, 0))
+            {
+                odr_perror(in, "Decoding constructed record.");
+                fprintf(stdout, "[Near %d]\n", odr_offset(in));
+                fprintf(stdout, "Packet dump:\n---------\n");
+                odr_dumpBER(stdout, (char*)r->u.octet_aligned->buf,
+                            r->u.octet_aligned->len);
+                fprintf(stdout, "---------\n");
+                
+		/* note just ignores the error ant print the bytes form the octet_aligned later */
+            } else {
+                /*
+                 * Note: we throw away the original, BER-encoded record here.
+                 * Do something else with it if you want to keep it.
+                 */
+                r->u.sutrs = (Z_SUTRS *) rr; /* we don't actually check the type here. */
+                r->which = type->what;
+            }
+        }
+    }
+    if (ent && ent->value == VAL_SOIF)
+    {
+        print_record((const unsigned char *) r->u.octet_aligned->buf,
+                     r->u.octet_aligned->len);
+        if (marc_file)
+            fwrite (r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, marc_file);
+    }
+    else if (r->which == Z_External_octet)
+    {
+        const char *octet_buf = (char*)r->u.octet_aligned->buf;
+        if (ent->oclass == CLASS_RECSYN && 
+		(ent->value == VAL_TEXT_XML || 
+		 ent->value == VAL_APPLICATION_XML ||
+		 ent->value == VAL_HTML))
+        {
+            print_record((const unsigned char *) octet_buf,
+                         r->u.octet_aligned->len);
+        }
+        else if (ent->value == VAL_POSTSCRIPT)
+        {
+            int size = r->u.octet_aligned->len;
+            if (size > 100)
+                size = 100;
+            print_record((const unsigned char *) octet_buf, size);
+        }
+        else
+        {
+            if ( 
+#if AVOID_MARC_DECODE
+                /* primitive check for a marc OID 5.1-29 except 16 */
+                ent->oidsuffix[0] == 5 && ent->oidsuffix[1] < 30 &&
+                ent->oidsuffix[1] != 16
+#else
+                1
+#endif
+                )
+            {
+                char *result;
+                int rlen;
+                yaz_iconv_t cd = 0;
+                yaz_marc_t mt = yaz_marc_create();
+                    
+                if (yaz_marc_decode_buf(mt, octet_buf,r->u.octet_aligned->len,
+                                        &result, &rlen)> 0)
+                {
+                    char *from = 0;
+                    if (marcCharset && !strcmp(marcCharset, "auto"))
+                    {
+                        if (ent->value == VAL_USMARC)
+                        {
+                            if (octet_buf[9] == 'a')
+                                from = "UTF-8";
+                            else
+                                from = "MARC-8";
+                        }
+                        else
+                            from = "ISO-8859-1";
+                    }
+		    else if (marcCharset)
+			from = marcCharset;
+                    if (outputCharset && from)
+                    {   
+                        cd = yaz_iconv_open(outputCharset, from);
+                        printf ("convert from %s to %s", from, 
+                                outputCharset);
+			if (!cd)
+			    printf (" unsupported\n");
+			else
+			    printf ("\n");
+                    }
+                    if (!cd)
+                        fwrite (result, 1, rlen, stdout);
+                    else
+                    {
+                        char outbuf[6];
+                        size_t inbytesleft = rlen;
+                        const char *inp = result;
+                        
+                        while (inbytesleft)
+                        {
+                            size_t outbytesleft = sizeof(outbuf);
+                            char *outp = outbuf;
+			    size_t r;
+
+                            r = yaz_iconv (cd, (char**) &inp,
+					   &inbytesleft, 
+					   &outp, &outbytesleft);
+                            if (r == (size_t) (-1))
+                            {
+                                int e = yaz_iconv_error(cd);
+                                if (e != YAZ_ICONV_E2BIG)
+                                    break;
+                            }
+                            fwrite (outbuf, outp - outbuf, 1, stdout);
+                        }
+                    }
+                }
+		else
+                {
+                    printf ("bad MARC. Dumping as it is:\n");
+                    print_record((const unsigned char*) octet_buf,
+                                  r->u.octet_aligned->len);
+		}	
+                yaz_marc_destroy(mt);
+                if (cd)
+                    yaz_iconv_close(cd);
+            }
+            else
+            {
+                print_record((const unsigned char*) octet_buf,
+                             r->u.octet_aligned->len);
+            }
+        }
+        if (marc_file)
+            fwrite (octet_buf, 1, r->u.octet_aligned->len, marc_file);
+    }
+    else if (ent && ent->value == VAL_SUTRS)
+    {
+        if (r->which != Z_External_sutrs)
+        {
+            printf("Expecting single SUTRS type for SUTRS.\n");
+            return;
+        }
+        print_record(r->u.sutrs->buf, r->u.sutrs->len);
+        if (marc_file)
+            fwrite (r->u.sutrs->buf, 1, r->u.sutrs->len, marc_file);
+    }
+    else if (ent && ent->value == VAL_GRS1)
+    {
+        WRBUF w;
+        if (r->which != Z_External_grs1)
+        {
+            printf("Expecting single GRS type for GRS.\n");
+            return;
+        }
+        w = wrbuf_alloc();
+        yaz_display_grs1(w, r->u.grs1, 0);
+        puts (wrbuf_buf(w));
+        wrbuf_free(w, 1);
+    }
+    else if (ent && ent->value == VAL_OPAC)
+    {
+	int i;
+	if (r->u.opac->bibliographicRecord)
+	    display_record(r->u.opac->bibliographicRecord);
+	for (i = 0; i<r->u.opac->num_holdingsData; i++)
+	{
+	    Z_HoldingsRecord *h = r->u.opac->holdingsData[i];
+	    if (h->which == Z_HoldingsRecord_marcHoldingsRecord)
+	    {
+		printf ("MARC holdings %d\n", i);
+		display_record(h->u.marcHoldingsRecord);
+	    }
+	    else if (h->which == Z_HoldingsRecord_holdingsAndCirc)
+	    {
+		int j;
+
+		Z_HoldingsAndCircData *data = h->u.holdingsAndCirc;
+
+		printf ("Data holdings %d\n", i);
+		if (data->typeOfRecord)
+		    printf ("typeOfRecord: %s\n", data->typeOfRecord);
+		if (data->encodingLevel)
+		    printf ("encodingLevel: %s\n", data->encodingLevel);
+		if (data->receiptAcqStatus)
+		    printf ("receiptAcqStatus: %s\n", data->receiptAcqStatus);
+		if (data->generalRetention)
+		    printf ("generalRetention: %s\n", data->generalRetention);
+		if (data->completeness)
+		    printf ("completeness: %s\n", data->completeness);
+		if (data->dateOfReport)
+		    printf ("dateOfReport: %s\n", data->dateOfReport);
+		if (data->nucCode)
+		    printf ("nucCode: %s\n", data->nucCode);
+		if (data->localLocation)
+		    printf ("localLocation: %s\n", data->localLocation);
+		if (data->shelvingLocation)
+		    printf ("shelvingLocation: %s\n", data->shelvingLocation);
+		if (data->callNumber)
+		    printf ("callNumber: %s\n", data->callNumber);
+		if (data->shelvingData)
+		    printf ("shelvingData: %s\n", data->shelvingData);
+		if (data->copyNumber)
+		    printf ("copyNumber: %s\n", data->copyNumber);
+		if (data->publicNote)
+		    printf ("publicNote: %s\n", data->publicNote);
+		if (data->reproductionNote)
+		    printf ("reproductionNote: %s\n", data->reproductionNote);
+		if (data->termsUseRepro)
+		    printf ("termsUseRepro: %s\n", data->termsUseRepro);
+		if (data->enumAndChron)
+		    printf ("enumAndChron: %s\n", data->enumAndChron);
+		for (j = 0; j<data->num_volumes; j++)
+		{
+		    printf ("volume %d\n", j);
+		    if (data->volumes[j]->enumeration)
+			printf (" enumeration: %s\n",
+				data->volumes[j]->enumeration);
+		    if (data->volumes[j]->chronology)
+			printf (" chronology: %s\n",
+				data->volumes[j]->chronology);
+		    if (data->volumes[j]->enumAndChron)
+			printf (" enumAndChron: %s\n",
+				data->volumes[j]->enumAndChron);
+		}
+		for (j = 0; j<data->num_circulationData; j++)
+		{
+		    printf ("circulation %d\n", j);
+		    if (data->circulationData[j]->availableNow)
+			printf (" availableNow: %d\n",
+				*data->circulationData[j]->availableNow);
+		    if (data->circulationData[j]->availablityDate)
+			printf (" availabiltyDate: %s\n",
+				data->circulationData[j]->availablityDate);
+		    if (data->circulationData[j]->availableThru)
+			printf (" availableThru: %s\n",
+				data->circulationData[j]->availableThru);
+		    if (data->circulationData[j]->restrictions)
+			printf (" restrictions: %s\n",
+				data->circulationData[j]->restrictions);
+		    if (data->circulationData[j]->itemId)
+			printf (" itemId: %s\n",
+				data->circulationData[j]->itemId);
+		    if (data->circulationData[j]->renewable)
+			printf (" renewable: %d\n",
+				*data->circulationData[j]->renewable);
+		    if (data->circulationData[j]->onHold)
+			printf (" onHold: %d\n",
+				*data->circulationData[j]->onHold);
+		    if (data->circulationData[j]->enumAndChron)
+			printf (" enumAndChron: %s\n",
+				data->circulationData[j]->enumAndChron);
+		    if (data->circulationData[j]->midspine)
+			printf (" midspine: %s\n",
+				data->circulationData[j]->midspine);
+		    if (data->circulationData[j]->temporaryLocation)
+			printf (" temporaryLocation: %s\n",
+				data->circulationData[j]->temporaryLocation);
+		}
+	    }
+	}
+    }
+    else 
+    {
+        printf("Unknown record representation.\n");
+        if (!z_External(print, &r, 0, 0))
+        {
+            odr_perror(print, "Printing external");
+            odr_reset(print);
+        }
+    }
+}
+
+static void display_diagrecs(Z_DiagRec **pp, int num)
+{
+    int i;
+    oident *ent;
+    Z_DefaultDiagFormat *r;
+
+    printf("Diagnostic message(s) from database:\n");
+    for (i = 0; i<num; i++)
+    {
+        Z_DiagRec *p = pp[i];
+        if (p->which != Z_DiagRec_defaultFormat)
+        {
+            printf("Diagnostic record not in default format.\n");
+            return;
+        }
+        else
+            r = p->u.defaultFormat;
+        if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
+            ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
+            printf("Missing or unknown diagset\n");
+        printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
+        switch (r->which)
+        {
+        case Z_DefaultDiagFormat_v2Addinfo:
+            printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
+            break;
+        case Z_DefaultDiagFormat_v3Addinfo:
+            printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
+            break;
+        }
+    }
+}
+
+
+static void display_nameplusrecord(Z_NamePlusRecord *p)
+{
+    if (p->databaseName)
+        printf("[%s]", p->databaseName);
+    if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
+        display_diagrecs(&p->u.surrogateDiagnostic, 1);
+    else if (p->which == Z_NamePlusRecord_databaseRecord)
+        display_record(p->u.databaseRecord);
+}
+
+static void display_records(Z_Records *p)
+{
+    int i;
+
+    if (p->which == Z_Records_NSD)
+    {
+        Z_DiagRec dr, *dr_p = &dr;
+        dr.which = Z_DiagRec_defaultFormat;
+        dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
+        display_diagrecs (&dr_p, 1);
+    }
+    else if (p->which == Z_Records_multipleNSD)
+        display_diagrecs (p->u.multipleNonSurDiagnostics->diagRecs,
+                          p->u.multipleNonSurDiagnostics->num_diagRecs);
+    else 
+    {
+        printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
+        for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
+            display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
+    }
+}
+
+static int send_deleteResultSetRequest(const char *arg)
+{
+    char names[8][32];
+    int i;
+
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_deleteResultSetRequest);
+    Z_DeleteResultSetRequest *req = apdu->u.deleteResultSetRequest;
+
+    req->referenceId = set_refid (out);
+
+    req->num_resultSetList =
+        sscanf (arg, "%30s %30s %30s %30s %30s %30s %30s %30s",
+                names[0], names[1], names[2], names[3],
+                names[4], names[5], names[6], names[7]);
+
+    req->deleteFunction = (int *)
+        odr_malloc (out, sizeof(*req->deleteFunction));
+    if (req->num_resultSetList > 0)
+    {
+        *req->deleteFunction = Z_DeleteResultSetRequest_list;
+        req->resultSetList = (char **)
+            odr_malloc (out, sizeof(*req->resultSetList)*
+                        req->num_resultSetList);
+        for (i = 0; i<req->num_resultSetList; i++)
+            req->resultSetList[i] = names[i];
+    }
+    else
+    {
+        *req->deleteFunction = Z_DeleteResultSetRequest_all;
+        req->resultSetList = 0;
+    }
+    
+    send_apdu(apdu);
+    printf("Sent deleteResultSetRequest.\n");
+    return 2;
+}
+
+#if HAVE_XML2
+static int send_srw(Z_SRW_PDU *sr)
+{
+    const char *charset = negotiationCharset;
+    const char *host_port = cur_host;
+    char *path = 0;
+    char ctype[50];
+    Z_SOAP_Handler h[2] = {
+        {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec},
+        {0, 0, 0}
+    };
+    ODR o = odr_createmem(ODR_ENCODE);
+    int ret;
+    Z_SOAP *p = odr_malloc(o, sizeof(*p));
+    Z_GDU *gdu;
+
+    path = odr_malloc(out, strlen(databaseNames[0])+2);
+    *path = '/';
+    strcpy(path+1, databaseNames[0]);
+
+    gdu = z_get_HTTP_Request(out);
+    gdu->u.HTTP_Request->version = http_version;
+    gdu->u.HTTP_Request->path = odr_strdup(out, path);
+
+    if (host_port)
+    {
+        const char *cp0 = strstr(host_port, "://");
+        const char *cp1 = 0;
+        if (cp0)
+            cp0 = cp0+3;
+        else
+            cp0 = host_port;
+
+        cp1 = strchr(cp0, '/');
+        if (!cp1)
+            cp1 = cp0+strlen(cp0);
+
+        if (cp0 && cp1)
+        {
+            char *h = odr_malloc(out, cp1 - cp0 + 1);
+            memcpy (h, cp0, cp1 - cp0);
+            h[cp1-cp0] = '\0';
+            z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
+                              "Host", h);
+        }
+    }
+
+    strcpy(ctype, "text/xml");
+    if (charset && strlen(charset) < 20)
+    {
+        strcat(ctype, "; charset=");
+        strcat(ctype, charset);
+    }
+    z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
+                      "Content-Type", ctype);
+    z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
+                      "SOAPAction", "\"\"");
+    p->which = Z_SOAP_generic;
+    p->u.generic = odr_malloc(o, sizeof(*p->u.generic));
+    p->u.generic->no = 0;
+    p->u.generic->ns = 0;
+    p->u.generic->p = sr;
+    p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
+
+    ret = z_soap_codec_enc(o, &p,
+                           &gdu->u.HTTP_Request->content_buf,
+                           &gdu->u.HTTP_Request->content_len, h,
+                           charset);
+
+    if (z_GDU(out, &gdu, 0, 0))
+    {
+        /* encode OK */
+        char *buf_out;
+        int len_out;
+        int r;
+	if (apdu_file)
+	{
+	    if (!z_GDU(print, &gdu, 0, 0))
+		printf ("Failed to print outgoing APDU\n");
+	    odr_reset(print);
+	}
+        buf_out = odr_getbuf(out, &len_out, 0);
+        
+        /* we don't odr_reset(out), since we may need the buffer again */
+
+	do_hex_dump(buf_out, len_out);
+
+        r = cs_put(conn, buf_out, len_out);
+
+        odr_destroy(o);
+        
+        if (r >= 0)
+            return 2;
+    }
+    return 0;
+}
+#endif
+
+#if HAVE_XML2
+static int send_SRW_scanRequest(const char *arg, int pos, int num)
+{
+    Z_SRW_PDU *sr = 0;
+    
+    /* regular requestse .. */
+    sr = yaz_srw_get(out, Z_SRW_scan_request);
+
+    switch(queryType)
+    {
+    case QueryType_CQL:
+	sr->u.scan_request->query_type = Z_SRW_query_type_cql;
+	sr->u.scan_request->scanClause.cql = odr_strdup(out, arg);
+	break;
+    case QueryType_Prefix:
+	sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
+	sr->u.scan_request->scanClause.pqf = odr_strdup(out, arg);
+	break;
+    default:
+	printf ("Only CQL and PQF supported in SRW\n");
+	return 0;
+    }
+    sr->u.scan_request->responsePosition = odr_intdup(out, pos);
+    sr->u.scan_request->maximumTerms = odr_intdup(out, num);
+    return send_srw(sr);
+}
+
+static int send_SRW_searchRequest(const char *arg)
+{
+    Z_SRW_PDU *sr = 0;
+    
+    if (!srw_sr)
+    {
+        assert(srw_sr_odr_out == 0);
+        srw_sr_odr_out = odr_createmem(ODR_ENCODE);
+    }
+    odr_reset(srw_sr_odr_out);
+
+    setno = 1;
+
+    /* save this for later .. when fetching individual records */
+    srw_sr =  yaz_srw_get(srw_sr_odr_out, Z_SRW_searchRetrieve_request);
+    
+    /* regular request .. */
+    sr = yaz_srw_get(out, Z_SRW_searchRetrieve_request);
+
+    switch(queryType)
+    {
+    case QueryType_CQL:
+	srw_sr->u.request->query_type = Z_SRW_query_type_cql;
+	srw_sr->u.request->query.cql = odr_strdup(srw_sr_odr_out, arg);
+
+	sr->u.request->query_type = Z_SRW_query_type_cql;
+	sr->u.request->query.cql = odr_strdup(out, arg);
+	break;
+    case QueryType_Prefix:
+	srw_sr->u.request->query_type = Z_SRW_query_type_pqf;
+	srw_sr->u.request->query.pqf = odr_strdup(srw_sr_odr_out, arg);
+
+	sr->u.request->query_type = Z_SRW_query_type_pqf;
+	sr->u.request->query.pqf = odr_strdup(out, arg);
+	break;
+    default:
+	printf ("Only CQL and PQF supported in SRW\n");
+	return 0;
+    }
+    sr->u.request->maximumRecords = odr_intdup(out, 0);
+
+    if (record_schema)
+        sr->u.request->recordSchema = record_schema;
+    if (recordsyntax == VAL_TEXT_XML)
+        sr->u.explain_request->recordPacking = "xml";
+    return send_srw(sr);
+}
+#endif
+
+static int send_searchRequest(const char *arg)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
+    Z_SearchRequest *req = apdu->u.searchRequest;
+    Z_Query query;
+    struct ccl_rpn_node *rpn = NULL;
+    int error, pos;
+    char setstring[100];
+    Z_RPNQuery *RPNquery;
+    Odr_oct ccl_query;
+    YAZ_PQF_Parser pqf_parser;
+    Z_External *ext;
+    QueryType myQueryType = queryType;
+    char pqfbuf[512];
+
+    if (myQueryType == QueryType_CCL2RPN)
+    {
+        rpn = ccl_find_str(bibset, arg, &error, &pos);
+        if (error)
+        {
+            printf("CCL ERROR: %s\n", ccl_err_msg(error));
+            return 0;
+        }
+    } else if (myQueryType == QueryType_CQL2RPN) {
+	/* ### All this code should be wrapped in a utility function */
+	CQL_parser parser;
+	struct cql_node *node;
+	const char *addinfo;
+	if (cqltrans == 0) {
+            printf("Can't use CQL: no translation file.  Try set_cqlfile\n");
+	    return 0;
+	}
+	parser = cql_parser_create();
+	if ((error = cql_parser_string(parser, arg)) != 0) {
+            printf("Can't parse CQL: must be a syntax error\n");
+            return 0;
+	}
+	node = cql_parser_result(parser);
+	if ((error = cql_transform_buf(cqltrans, node, pqfbuf,
+				       sizeof pqfbuf)) != 0) {
+	    error = cql_transform_error(cqltrans, &addinfo);
+	    printf ("Can't convert CQL to PQF: %s (addinfo=%s)\n",
+		    cql_strerror(error), addinfo);
+            return 0;
+        }
+	arg = pqfbuf;
+	myQueryType = QueryType_Prefix;
+    }
+
+    req->referenceId = set_refid (out);
+    if (!strcmp(arg, "@big")) /* strictly for troublemaking */
+    {
+        static unsigned char big[2100];
+        static Odr_oct bigo;
+
+        /* send a very big referenceid to test transport stack etc. */
+        memset(big, 'A', 2100);
+        bigo.len = bigo.size = 2100;
+        bigo.buf = big;
+        req->referenceId = &bigo;
+    }
+    
+    if (setnumber >= 0)
+    {
+        sprintf(setstring, "%d", ++setnumber);
+        req->resultSetName = setstring;
+    }
+    *req->smallSetUpperBound = smallSetUpperBound;
+    *req->largeSetLowerBound = largeSetLowerBound;
+    *req->mediumSetPresentNumber = mediumSetPresentNumber;
+    if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
+        mediumSetPresentNumber > 0))
+    {
+        req->preferredRecordSyntax =
+            yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax);
+
+        req->smallSetElementSetNames =
+            req->mediumSetElementSetNames = elementSetNames;
+    }
+    req->num_databaseNames = num_databaseNames;
+    req->databaseNames = databaseNames;
+
+    req->query = &query;
+
+    switch (myQueryType)
+    {
+    case QueryType_Prefix:
+        query.which = Z_Query_type_1;
+        pqf_parser = yaz_pqf_create ();
+        RPNquery = yaz_pqf_parse (pqf_parser, out, arg);
+        if (!RPNquery)
+        {
+            const char *pqf_msg;
+            size_t off;
+            int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
+            printf("%*s^\n", off+4, "");
+            printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
+            
+            yaz_pqf_destroy (pqf_parser);
+            return 0;
+        }
+        yaz_pqf_destroy (pqf_parser);
+        query.u.type_1 = RPNquery;
+        break;
+    case QueryType_CCL:
+        query.which = Z_Query_type_2;
+        query.u.type_2 = &ccl_query;
+        ccl_query.buf = (unsigned char*) arg;
+        ccl_query.len = strlen(arg);
+        break;
+    case QueryType_CCL2RPN:
+        query.which = Z_Query_type_1;
+        RPNquery = ccl_rpn_query(out, rpn);
+        if (!RPNquery)
+        {
+            printf ("Couldn't convert from CCL to RPN\n");
+            return 0;
+        }
+        query.u.type_1 = RPNquery;
+        ccl_rpn_delete (rpn);
+        break;
+    case QueryType_CQL:
+        query.which = Z_Query_type_104;
+        ext = (Z_External *) odr_malloc(out, sizeof(*ext));
+        ext->direct_reference = odr_getoidbystr(out, "1.2.840.10003.16.2");
+        ext->indirect_reference = 0;
+        ext->descriptor = 0;
+        ext->which = Z_External_CQL;
+        ext->u.cql = odr_strdup(out, arg);
+        query.u.type_104 =  ext;
+        break;
+    default:
+        printf ("Unsupported query type\n");
+        return 0;
+    }
+    send_apdu(apdu);
+    /*
+    if (send_apdu(apdu))
+    printf("Sent searchRequest.\n");*/
+    setno = 1;
+    return 2;
+}
+
+/* display Query Expression as part of searchResult-1 */
+/*
+static void display_queryExpression (Z_QueryExpression *qe)
+{
+    if (!qe)
+        return;
+    if (qe->which == Z_QueryExpression_term)
+    {
+        if (qe->u.term->queryTerm)
+        {
+            Z_Term *term = qe->u.term->queryTerm;
+            switch (term->which)
+            {
+            case Z_Term_general:
+                printf (" %.*s", term->u.general->len, term->u.general->buf);
+                break;
+            case Z_Term_characterString:
+                printf (" %s", term->u.characterString);
+                break;
+            case Z_Term_numeric:
+                printf (" %d", *term->u.numeric);
+                break;
+            case Z_Term_null:
+                printf (" null");
+                break;
+            }
+        }
+    }
+}
+*/
+/* see if we can find USR:SearchResult-1 */
+/*
+static void display_searchResult (Z_OtherInformation *o)
+{
+    int i;
+    if (!o)
+        return ;
+    for (i = 0; i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            
+            if (ext->which == Z_External_searchResult1)
+            {
+                int j;
+                Z_SearchInfoReport *sr = ext->u.searchResult1;
+                printf ("SearchResult-1:");
+                for (j = 0; j < sr->num; j++)
+                {
+		if (!sr->elements[j]->subqueryExpression)
+                        printf (" %d", j);
+                    display_queryExpression (
+                        sr->elements[j]->subqueryExpression);
+                    display_queryExpression (
+                        sr->elements[j]->subqueryInterpretation);
+                    display_queryExpression (
+                        sr->elements[j]->subqueryRecommendation);
+                    if (sr->elements[j]->subqueryCount)
+                        printf ("(%d)", *sr->elements[j]->subqueryCount);
+                }
+                printf ("\n");
+            }
+        }
+    }
+}
+*/
+/* kjdon: modified to not print the results, and return the number of hits */
+static int process_searchResponse(Z_SearchResponse *res)
+{
+    print_refid (res->referenceId);
+    if (!(*res->searchStatus)) {
+      /* this should return an error instead of 0 docs found... one day... */
+	return 0;
+    }
+    setno += *res->numberOfRecordsReturned;
+    /* what is this for? */
+    if (res->records)
+        display_records(res->records);
+    return *res->resultCount;
+
+}
+
+static void print_level(int iLevel)
+{
+    int i;
+    for (i = 0; i < iLevel * 4; i++)
+        printf(" ");
+}
+
+static void print_int(int iLevel, const char *pTag, int *pInt)
+{
+    if (pInt != NULL)
+    {
+        print_level(iLevel);
+        printf("%s: %d\n", pTag, *pInt);
+    }
+}
+
+static void print_string(int iLevel, const char *pTag, const char *pString)
+{
+    if (pString != NULL)
+    {
+        print_level(iLevel);
+        printf("%s: %s\n", pTag, pString);
+    }
+}
+
+static void print_oid(int iLevel, const char *pTag, Odr_oid *pOid)
+{
+    if (pOid != NULL)
+    {
+        int *pInt = pOid;
+
+        print_level(iLevel);
+        printf("%s:", pTag);
+        for (; *pInt != -1; pInt++)
+            printf(" %d", *pInt);
+        printf("\n");
+    }
+}
+
+static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
+{
+    if (referenceId != NULL)
+    {
+        int i;
+
+        print_level(iLevel);
+        printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
+        for (i = 0; i < referenceId->len; i++)
+            printf("%c", referenceId->buf[i]);
+        printf("\n");
+    }
+}
+
+static void print_string_or_numeric(int iLevel, const char *pTag, Z_StringOrNumeric *pStringNumeric)
+{
+    if (pStringNumeric != NULL)
+    {
+        switch (pStringNumeric->which)
+        {
+        case Z_StringOrNumeric_string:
+            print_string(iLevel, pTag, pStringNumeric->u.string);
+            break;
+            
+        case Z_StringOrNumeric_numeric:
+            print_int(iLevel, pTag, pStringNumeric->u.numeric);
+            break;
+            
+        default:
+            print_level(iLevel);
+            printf("%s: valid type for Z_StringOrNumeric\n", pTag);
+            break;
+        }
+    }
+}
+
+static void print_universe_report_duplicate(
+    int iLevel,
+    Z_UniverseReportDuplicate *pUniverseReportDuplicate)
+{
+    if (pUniverseReportDuplicate != NULL)
+    {
+        print_level(iLevel);
+        printf("Universe Report Duplicate: \n");
+        iLevel++;
+        print_string_or_numeric(iLevel, "Hit No",
+                                pUniverseReportDuplicate->hitno);
+    }
+}
+
+static void print_universe_report_hits(
+    int iLevel,
+    Z_UniverseReportHits *pUniverseReportHits)
+{
+    if (pUniverseReportHits != NULL)
+    {
+        print_level(iLevel);
+        printf("Universe Report Hits: \n");
+        iLevel++;
+        print_string_or_numeric(iLevel, "Database",
+                                pUniverseReportHits->database);
+        print_string_or_numeric(iLevel, "Hits", pUniverseReportHits->hits);
+    }
+}
+
+static void print_universe_report(int iLevel, Z_UniverseReport *pUniverseReport)
+{
+    if (pUniverseReport != NULL)
+    {
+        print_level(iLevel);
+        printf("Universe Report: \n");
+        iLevel++;
+        print_int(iLevel, "Total Hits", pUniverseReport->totalHits);
+        switch (pUniverseReport->which)
+        {
+        case Z_UniverseReport_databaseHits:
+            print_universe_report_hits(iLevel,
+                                       pUniverseReport->u.databaseHits);
+            break;
+            
+        case Z_UniverseReport_duplicate:
+            print_universe_report_duplicate(iLevel,
+                                            pUniverseReport->u.duplicate);
+            break;
+            
+        default:
+            print_level(iLevel);
+            printf("Type: %d\n", pUniverseReport->which);
+            break;
+        }
+    }
+}
+
+static void print_external(int iLevel, Z_External *pExternal)
+{
+    if (pExternal != NULL)
+    {
+        print_level(iLevel);
+        printf("External: \n");
+        iLevel++;
+        print_oid(iLevel, "Direct Reference", pExternal->direct_reference);
+        print_int(iLevel, "InDirect Reference", pExternal->indirect_reference);
+        print_string(iLevel, "Descriptor", pExternal->descriptor);
+        switch (pExternal->which)
+        {
+        case Z_External_universeReport:
+            print_universe_report(iLevel, pExternal->u.universeReport);
+            break;
+            
+        default:
+            print_level(iLevel);
+            printf("Type: %d\n", pExternal->which);
+            break;
+        }
+    }
+}
+
+static int process_resourceControlRequest (Z_ResourceControlRequest *req)
+{
+    printf ("Received ResourceControlRequest.\n");
+    print_referenceId(1, req->referenceId);
+    print_int(1, "Suspended Flag", req->suspendedFlag);
+    print_int(1, "Partial Results Available", req->partialResultsAvailable);
+    print_int(1, "Response Required", req->responseRequired);
+    print_int(1, "Triggered Request Flag", req->triggeredRequestFlag);
+    print_external(1, req->resourceReport);
+    return 0;
+}
+
+void process_ESResponse(Z_ExtendedServicesResponse *res)
+{
+    printf("Status: ");
+    switch (*res->operationStatus)
+    {
+    case Z_ExtendedServicesResponse_done:
+        printf ("done\n");
+        break;
+    case Z_ExtendedServicesResponse_accepted:
+        printf ("accepted\n");
+        break;
+    case Z_ExtendedServicesResponse_failure:
+        printf ("failure\n");
+        display_diagrecs(res->diagnostics, res->num_diagnostics);
+        break;
+    default:
+        printf ("unknown\n");
+    }
+    if ( (*res->operationStatus != Z_ExtendedServicesResponse_failure) &&
+        (res->num_diagnostics != 0) ) {
+        display_diagrecs(res->diagnostics, res->num_diagnostics);
+    }
+    print_refid (res->referenceId);
+    if (res->taskPackage && 
+        res->taskPackage->which == Z_External_extendedService)
+    {
+        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
+        Odr_oct *id = taskPackage->targetReference;
+        Z_External *ext = taskPackage->taskSpecificParameters;
+        
+        if (id)
+        {
+            printf ("Target Reference: ");
+            print_stringn (id->buf, id->len);
+            printf ("\n");
+        }
+        if (ext->which == Z_External_update)
+        {
+            Z_IUUpdateTaskPackage *utp = ext->u.update->u.taskPackage;
+            if (utp && utp->targetPart)
+            {
+                Z_IUTargetPart *targetPart = utp->targetPart;
+                int i;
+
+                for (i = 0; i<targetPart->num_taskPackageRecords;  i++)
+                {
+
+                    Z_IUTaskPackageRecordStructure *tpr =
+                        targetPart->taskPackageRecords[i];
+                    printf ("task package record %d\n", i+1);
+                    if (tpr->which == Z_IUTaskPackageRecordStructure_record)
+                    {
+                        display_record (tpr->u.record);
+                    }
+                    else
+                    {
+                        printf ("other type\n");
+                    }
+                }
+            }
+        }
+    }
+}
+
+const char *get_ill_element (void *clientData, const char *element)
+{
+    return 0;
+}
+
+static Z_External *create_external_itemRequest()
+{
+    struct ill_get_ctl ctl;
+    ILL_ItemRequest *req;
+    Z_External *r = 0;
+    int item_request_size = 0;
+    char *item_request_buf = 0;
+
+    ctl.odr = out;
+    ctl.clientData = 0;
+    ctl.f = get_ill_element;
+    
+    req = ill_get_ItemRequest(&ctl, "ill", 0);
+    if (!req)
+        printf ("ill_get_ItemRequest failed\n");
+        
+    if (!ill_ItemRequest (out, &req, 0, 0))
+    {
+        if (apdu_file)
+        {
+            ill_ItemRequest(print, &req, 0, 0);
+            odr_reset(print);
+        }
+        item_request_buf = odr_getbuf (out, &item_request_size, 0);
+        if (item_request_buf)
+            odr_setbuf (out, item_request_buf, item_request_size, 1);
+        printf ("Couldn't encode ItemRequest, size %d\n", item_request_size);
+        return 0;
+    }
+    else
+    {
+        oident oid;
+        
+        item_request_buf = odr_getbuf (out, &item_request_size, 0);
+        oid.proto = PROTO_GENERAL;
+        oid.oclass = CLASS_GENERAL;
+        oid.value = VAL_ISO_ILL_1;
+        
+        r = (Z_External *) odr_malloc (out, sizeof(*r));
+        r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
+        r->indirect_reference = 0;
+        r->descriptor = 0;
+        r->which = Z_External_single;
+        
+        r->u.single_ASN1_type = (Odr_oct *)
+            odr_malloc (out, sizeof(*r->u.single_ASN1_type));
+        r->u.single_ASN1_type->buf = (unsigned char *)
+        odr_malloc (out, item_request_size);
+        r->u.single_ASN1_type->len = item_request_size;
+        r->u.single_ASN1_type->size = item_request_size;
+        memcpy (r->u.single_ASN1_type->buf, item_request_buf,
+                item_request_size);
+        
+        do_hex_dump(item_request_buf,item_request_size);
+    }
+    return r;
+}
+
+static Z_External *create_external_ILL_APDU(int which)
+{
+    struct ill_get_ctl ctl;
+    ILL_APDU *ill_apdu;
+    Z_External *r = 0;
+    int ill_request_size = 0;
+    char *ill_request_buf = 0;
+        
+    ctl.odr = out;
+    ctl.clientData = 0;
+    ctl.f = get_ill_element;
+
+    ill_apdu = ill_get_APDU(&ctl, "ill", 0);
+
+    if (!ill_APDU (out, &ill_apdu, 0, 0))
+    {
+        if (apdu_file)
+        {
+            printf ("-------------------\n");
+            ill_APDU(print, &ill_apdu, 0, 0);
+            odr_reset(print);
+            printf ("-------------------\n");
+        }
+        ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
+        if (ill_request_buf)
+            odr_setbuf (out, ill_request_buf, ill_request_size, 1);
+        printf ("Couldn't encode ILL-Request, size %d\n", ill_request_size);
+        return 0;
+    }
+    else
+    {
+        oident oid;
+        ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
+        
+        oid.proto = PROTO_GENERAL;
+        oid.oclass = CLASS_GENERAL;
+        oid.value = VAL_ISO_ILL_1;
+        
+        r = (Z_External *) odr_malloc (out, sizeof(*r));
+        r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
+        r->indirect_reference = 0;
+        r->descriptor = 0;
+        r->which = Z_External_single;
+        
+        r->u.single_ASN1_type = (Odr_oct *)
+            odr_malloc (out, sizeof(*r->u.single_ASN1_type));
+        r->u.single_ASN1_type->buf = (unsigned char *)
+        odr_malloc (out, ill_request_size);
+        r->u.single_ASN1_type->len = ill_request_size;
+        r->u.single_ASN1_type->size = ill_request_size;
+        memcpy (r->u.single_ASN1_type->buf, ill_request_buf, ill_request_size);
+/*         printf ("len = %d\n", ill_request_size); */
+/* 		do_hex_dump(ill_request_buf,ill_request_size); */
+/* 		printf("--- end of extenal\n"); */
+
+    }
+    return r;
+}
+
+
+static Z_External *create_ItemOrderExternal(const char *type, int itemno)
+{
+    Z_External *r = (Z_External *) odr_malloc(out, sizeof(Z_External));
+    oident ItemOrderRequest;
+  
+    ItemOrderRequest.proto = PROTO_Z3950;
+    ItemOrderRequest.oclass = CLASS_EXTSERV;
+    ItemOrderRequest.value = VAL_ITEMORDER;
+ 
+    r->direct_reference = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest)); 
+    r->indirect_reference = 0;
+    r->descriptor = 0;
+
+    r->which = Z_External_itemOrder;
+
+    r->u.itemOrder = (Z_ItemOrder *) odr_malloc(out,sizeof(Z_ItemOrder));
+    memset(r->u.itemOrder, 0, sizeof(Z_ItemOrder));
+    r->u.itemOrder->which=Z_IOItemOrder_esRequest;
+
+    r->u.itemOrder->u.esRequest = (Z_IORequest *) 
+        odr_malloc(out,sizeof(Z_IORequest));
+    memset(r->u.itemOrder->u.esRequest, 0, sizeof(Z_IORequest));
+
+    r->u.itemOrder->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
+        odr_malloc(out,sizeof(Z_IOOriginPartToKeep));
+    memset(r->u.itemOrder->u.esRequest->toKeep, 0, sizeof(Z_IOOriginPartToKeep));
+    r->u.itemOrder->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
+        odr_malloc(out,sizeof(Z_IOOriginPartNotToKeep));
+    memset(r->u.itemOrder->u.esRequest->notToKeep, 0, sizeof(Z_IOOriginPartNotToKeep));
+
+    r->u.itemOrder->u.esRequest->toKeep->supplDescription = NULL;
+    r->u.itemOrder->u.esRequest->toKeep->contact = NULL;
+    r->u.itemOrder->u.esRequest->toKeep->addlBilling = NULL;
+
+    r->u.itemOrder->u.esRequest->notToKeep->resultSetItem =
+        (Z_IOResultSetItem *) odr_malloc(out, sizeof(Z_IOResultSetItem));
+    memset(r->u.itemOrder->u.esRequest->notToKeep->resultSetItem, 0, sizeof(Z_IOResultSetItem));
+    r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->resultSetId = "1";
+
+    r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item =
+        (int *) odr_malloc(out, sizeof(int));
+    *r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item = itemno;
+
+    if (!strcmp (type, "item") || !strcmp(type, "2"))
+    {
+        printf ("using item-request\n");
+        r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
+            create_external_itemRequest();
+    }
+    else if (!strcmp(type, "ill") || !strcmp(type, "1"))
+    {
+        printf ("using ILL-request\n");
+        r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
+            create_external_ILL_APDU(ILL_APDU_ILL_Request);
+    }
+    else if (!strcmp(type, "xml") || !strcmp(type, "3"))
+    {
+    const char *xml_buf =
+        "<itemorder>\n"
+        "  <type>request</type>\n"
+        "  <libraryNo>000200</libraryNo>\n"
+        "  <borrowerTicketNo> 1212 </borrowerTicketNo>\n"
+        "</itemorder>";
+        r->u.itemOrder->u.esRequest->notToKeep->itemRequest =
+            z_ext_record (out, VAL_TEXT_XML, xml_buf, strlen(xml_buf));
+    }
+    else
+        r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 0;
+
+    return r;
+}
+
+static int send_itemorder(const char *type, int itemno)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    oident ItemOrderRequest;
+
+    ItemOrderRequest.proto = PROTO_Z3950;
+    ItemOrderRequest.oclass = CLASS_EXTSERV;
+    ItemOrderRequest.value = VAL_ITEMORDER;
+    req->packageType = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest));
+    req->packageName = esPackageName;
+
+    req->taskSpecificParameters = create_ItemOrderExternal(type, itemno);
+
+    send_apdu(apdu);
+    return 0;
+}
+
+static int only_z3950()
+{
+    if (protocol == PROTO_HTTP)
+    {
+	printf ("Not supported by SRW\n");
+	return 1;
+    }
+    return 0;
+}
+
+
+static int cmd_update_common(const char *arg, int version);
+
+static int cmd_update(const char *arg)
+{
+    return cmd_update_common(arg, 1);
+}
+
+static int cmd_update0(const char *arg)
+{
+    return cmd_update_common(arg, 0);
+}
+
+static int cmd_update_common(const char *arg, int version)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest );
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    Z_External *r;
+    char action[20], recid[20], fname[80];
+    int action_no;
+    Z_External *record_this = 0;
+
+    if (only_z3950())
+	return 0;
+    *action = 0;
+    *recid = 0;
+    *fname = 0;
+    sscanf (arg, "%19s %19s %79s", action, recid, fname);
+
+    if (!strcmp (action, "insert"))
+        action_no = Z_IUOriginPartToKeep_recordInsert;
+    else if (!strcmp (action, "replace"))
+        action_no = Z_IUOriginPartToKeep_recordReplace;
+    else if (!strcmp (action, "delete"))
+        action_no = Z_IUOriginPartToKeep_recordDelete;
+    else if (!strcmp (action, "update"))
+        action_no = Z_IUOriginPartToKeep_specialUpdate;
+    else 
+    {
+        printf ("Bad action: %s\n", action);
+        printf ("Possible values: insert, replace, delete, update\n");
+        return 0;
+    }
+
+    if (*fname)
+    {
+        FILE *inf;
+        struct stat status;
+        stat (fname, &status);
+        if (S_ISREG(status.st_mode) && (inf = fopen(fname, "rb")))
+        {
+            size_t len = status.st_size;
+            char *buf = (char *) xmalloc (len);
+
+            fread (buf, 1, len, inf);
+
+            fclose (inf);
+            
+            record_this = z_ext_record (out, VAL_TEXT_XML, buf, len);
+            
+            xfree (buf);
+        }
+        else
+        {
+            printf ("File %s doesn't exist\n", fname);
+            return 0;
+        }
+    }
+    else
+    {
+        if (!record_last)
+        {
+            printf ("No last record (update ignored)\n");
+            return 0;
+        }
+        record_this = record_last;
+    }
+
+    req->packageType =
+	yaz_oidval_to_z3950oid(out, CLASS_EXTSERV,
+			       version == 0 ? VAL_DBUPDATE0 : VAL_DBUPDATE);
+
+    req->packageName = esPackageName;
+    
+    req->referenceId = set_refid (out);
+
+    r = req->taskSpecificParameters = (Z_External *)
+        odr_malloc (out, sizeof(*r));
+    r->direct_reference = req->packageType;
+    r->indirect_reference = 0;
+    r->descriptor = 0;
+    if (version == 0)
+    {
+	Z_IU0OriginPartToKeep *toKeep;
+	Z_IU0SuppliedRecords *notToKeep;
+
+	r->which = Z_External_update0;
+	r->u.update0 = (Z_IU0Update *) odr_malloc(out, sizeof(*r->u.update0));
+	r->u.update0->which = Z_IUUpdate_esRequest;
+	r->u.update0->u.esRequest = (Z_IU0UpdateEsRequest *)
+	    odr_malloc(out, sizeof(*r->u.update0->u.esRequest));
+	toKeep = r->u.update0->u.esRequest->toKeep = (Z_IU0OriginPartToKeep *)
+	    odr_malloc(out, sizeof(*r->u.update0->u.esRequest->toKeep));
+	
+	toKeep->databaseName = databaseNames[0];
+	toKeep->schema = 0;
+	toKeep->elementSetName = 0;
+
+	toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
+	*toKeep->action = action_no;
+	
+	notToKeep = r->u.update0->u.esRequest->notToKeep = (Z_IU0SuppliedRecords *)
+	    odr_malloc(out, sizeof(*r->u.update0->u.esRequest->notToKeep));
+	notToKeep->num = 1;
+	notToKeep->elements = (Z_IU0SuppliedRecords_elem **)
+	    odr_malloc(out, sizeof(*notToKeep->elements));
+	notToKeep->elements[0] = (Z_IU0SuppliedRecords_elem *)
+	    odr_malloc(out, sizeof(**notToKeep->elements));
+	notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
+	if (*recid)
+	{
+	    notToKeep->elements[0]->u.opaque = (Odr_oct *)
+		odr_malloc (out, sizeof(Odr_oct));
+	    notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
+	    notToKeep->elements[0]->u.opaque->size = strlen(recid);
+	    notToKeep->elements[0]->u.opaque->len = strlen(recid);
+	}
+	else
+	    notToKeep->elements[0]->u.opaque = 0;
+	notToKeep->elements[0]->supplementalId = 0;
+	notToKeep->elements[0]->correlationInfo = 0;
+	notToKeep->elements[0]->record = record_this;
+    }
+    else
+    {
+	Z_IUOriginPartToKeep *toKeep;
+	Z_IUSuppliedRecords *notToKeep;
+
+	r->which = Z_External_update;
+	r->u.update = (Z_IUUpdate *) odr_malloc(out, sizeof(*r->u.update));
+	r->u.update->which = Z_IUUpdate_esRequest;
+	r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
+	    odr_malloc(out, sizeof(*r->u.update->u.esRequest));
+	toKeep = r->u.update->u.esRequest->toKeep = (Z_IUOriginPartToKeep *)
+	    odr_malloc(out, sizeof(*r->u.update->u.esRequest->toKeep));
+	
+	toKeep->databaseName = databaseNames[0];
+	toKeep->schema = 0;
+	toKeep->elementSetName = 0;
+	toKeep->actionQualifier = 0;
+	toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
+	*toKeep->action = action_no;
+
+	notToKeep = r->u.update->u.esRequest->notToKeep = (Z_IUSuppliedRecords *)
+	    odr_malloc(out, sizeof(*r->u.update->u.esRequest->notToKeep));
+	notToKeep->num = 1;
+	notToKeep->elements = (Z_IUSuppliedRecords_elem **)
+	    odr_malloc(out, sizeof(*notToKeep->elements));
+	notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
+	    odr_malloc(out, sizeof(**notToKeep->elements));
+	notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
+	if (*recid)
+	{
+	    notToKeep->elements[0]->u.opaque = (Odr_oct *)
+		odr_malloc (out, sizeof(Odr_oct));
+	    notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
+	    notToKeep->elements[0]->u.opaque->size = strlen(recid);
+	    notToKeep->elements[0]->u.opaque->len = strlen(recid);
+	}
+	else
+	    notToKeep->elements[0]->u.opaque = 0;
+	notToKeep->elements[0]->supplementalId = 0;
+	notToKeep->elements[0]->correlationInfo = 0;
+	notToKeep->elements[0]->record = record_this;
+    }
+    
+    send_apdu(apdu);
+
+    return 2;
+}
+
+
+static int cmd_itemorder(const char *arg)
+{
+    char type[12];
+    int itemno;
+   
+    if (only_z3950())
+	return 0;
+    if (sscanf (arg, "%10s %d", type, &itemno) != 2)
+        return 0;
+
+    printf("Item order request\n");
+    fflush(stdout);
+    send_itemorder(type, itemno);
+    return 2;
+}
+
+static void show_opt(const char *arg, void *clientData)
+{
+    printf ("%s ", arg);
+}
+
+static int cmd_zversion(const char *arg)
+{
+    if (*arg && arg)
+	z3950_version = atoi(arg);
+    else
+	printf ("version is %d\n", z3950_version);
+    return 0;
+}
+
+static int cmd_options(const char *arg)
+{
+    if (*arg)
+    {
+	int r;
+	int pos;
+	r = yaz_init_opt_encode(&z3950_options, arg, &pos);
+    }
+    else
+    {
+	yaz_init_opt_decode(&z3950_options, show_opt, 0);
+	printf ("\n");
+    }
+    return 0;
+}
+
+static int cmd_explain(const char *arg)
+{
+    if (protocol != PROTO_HTTP)
+	return 0;
+#if HAVE_XML2
+    if (!conn)
+	cmd_open(0);
+    if (conn)
+    {
+	Z_SRW_PDU *sr = 0;
+	
+	setno = 1;
+	
+	/* save this for later .. when fetching individual records */
+	sr = yaz_srw_get(out, Z_SRW_explain_request);
+	if (recordsyntax == VAL_TEXT_XML)
+            sr->u.explain_request->recordPacking = "xml";
+	send_srw(sr);
+	return 2;
+    }
+#endif
+    return 0;
+}
+
+static int cmd_init(const char *arg)
+{
+    if (*arg)
+    {
+        strncpy (cur_host, arg, sizeof(cur_host)-1);
+        cur_host[sizeof(cur_host)-1] = 0;
+    }
+    if (!conn || protocol != PROTO_Z3950)
+       return 0;
+    send_initRequest(cur_host);
+    return 2;
+
+}
+
+static int cmd_find(const char *arg)
+{
+    if (!*arg)
+    {
+        printf("Find what?\n");
+        return 0;
+    }
+    if (protocol == PROTO_HTTP)
+    {
+#if HAVE_XML2
+        if (!conn)
+            cmd_open(0);
+	if (!conn)
+	    return 0;
+        if (!send_SRW_searchRequest(arg))
+            return 0;
+#else
+        return 0;
+#endif
+    }
+    else
+    {
+        if (!conn)
+        {
+            try_reconnect(); 
+            
+            if (!conn) {					
+                printf("Not connected yet\n");
+                return 0;
+            }
+        }
+        if (!send_searchRequest(arg))
+            return 0;
+    }
+    return 2;
+}
+
+static int cmd_delete(const char *arg)
+{
+    if (!conn)
+    {
+        printf("Not connected yet\n");
+        return 0;
+    }
+    if (only_z3950())
+	return 0;
+    if (!send_deleteResultSetRequest(arg))
+        return 0;
+    return 2;
+}
+
+static int cmd_ssub(const char *arg)
+{
+    if (!(smallSetUpperBound = atoi(arg)))
+        return 0;
+    return 1;
+}
+
+static int cmd_lslb(const char *arg)
+{
+    if (only_z3950())
+	return 0;
+    if (!(largeSetLowerBound = atoi(arg)))
+        return 0;
+    return 1;
+}
+
+static int cmd_mspn(const char *arg)
+{
+    if (only_z3950())
+	return 0;
+    if (!(mediumSetPresentNumber = atoi(arg)))
+        return 0;
+    return 1;
+}
+
+static int cmd_status(const char *arg)
+{
+    printf("smallSetUpperBound: %d\n", smallSetUpperBound);
+    printf("largeSetLowerBound: %d\n", largeSetLowerBound);
+    printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
+    return 1;
+}
+
+static int cmd_setnames(const char *arg)
+{
+    if (*arg == '1')         /* enable ? */
+        setnumber = 0;
+    else if (*arg == '0')    /* disable ? */
+        setnumber = -1;
+    else if (setnumber < 0)  /* no args, toggle .. */
+        setnumber = 0;
+    else
+        setnumber = -1;
+   
+    if (setnumber >= 0)
+        printf("Set numbering enabled.\n");
+    else
+        printf("Set numbering disabled.\n");
+    return 1;
+}
+
+/* PRESENT SERVICE ----------------------------- */
+/* this one is based on johnmcps version - pretty much a copy of 
+   send_presentRequest, but passing in separate args, not an arg string */
+int z_send_getbriefrecords(int starting, int set, int howmany) {
+
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
+    Z_PresentRequest *req = apdu->u.presentRequest;
+    Z_RecordComposition compo;
+    int nos = 1;
+    char setstring[100];
+
+    req->referenceId = set_refid (out);
+    nos = howmany;
+    setno = starting;
+    sprintf(setstring, "%d", setnumber);
+    req->resultSetId = setstring;
+    req->resultSetStartPoint = &setno;
+    req->numberOfRecordsRequested = &nos;
+    
+    req->preferredRecordSyntax =
+        yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax);
+
+    if (record_schema)
+    {
+        req->recordComposition = &compo;
+        compo.which = Z_RecordComp_complex;
+        compo.u.complex = (Z_CompSpec *)
+            odr_malloc(out, sizeof(*compo.u.complex));
+        compo.u.complex->selectAlternativeSyntax = (bool_t *) 
+            odr_malloc(out, sizeof(bool_t));
+        *compo.u.complex->selectAlternativeSyntax = 0;
+
+        compo.u.complex->generic = (Z_Specification *)
+            odr_malloc(out, sizeof(*compo.u.complex->generic));
+        compo.u.complex->generic->which = Z_Schema_oid;
+
+        compo.u.complex->generic->schema.oid =
+	    yaz_str_to_z3950oid(out, CLASS_SCHEMA, record_schema);
+
+        if (!compo.u.complex->generic->schema.oid)
+        {
+            /* OID wasn't a schema! Try record syntax instead. */
+            compo.u.complex->generic->schema.oid = (Odr_oid *)
+		yaz_str_to_z3950oid(out, CLASS_RECSYN, record_schema);
+        }
+        if (!elementSetNames)
+            compo.u.complex->generic->elementSpec = 0;
+        else
+        {
+            compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
+                odr_malloc(out, sizeof(Z_ElementSpec));
+            compo.u.complex->generic->elementSpec->which =
+                Z_ElementSpec_elementSetName;
+            compo.u.complex->generic->elementSpec->u.elementSetName =
+                elementSetNames->u.generic;
+        }
+        compo.u.complex->num_dbSpecific = 0;
+        compo.u.complex->dbSpecific = 0;
+        compo.u.complex->num_recordSyntax = 0;
+        compo.u.complex->recordSyntax = 0;
+    }
+    else if (elementSetNames)
+    {
+        req->recordComposition = &compo;
+        compo.which = Z_RecordComp_simple;
+        compo.u.simple = elementSetNames;
+    }
+    send_apdu(apdu);
+    return 0;
+}
+
+static void parse_show_args(const char *arg_c, char *setstring,
+                            int *start, int *number)
+{
+    char arg[40];
+    char *p;
+
+    strncpy(arg, arg_c, sizeof(arg)-1);
+    arg[sizeof(arg)-1] = '\0';
+
+    if ((p = strchr(arg, '+')))
+    {
+        *number = atoi(p + 1);
+        *p = '\0';
+    }
+    if (*arg)
+        *start = atoi(arg);
+    if (p && (p=strchr(p+1, '+')))
+        strcpy (setstring, p+1);
+    else if (setnumber >= 0)
+        sprintf(setstring, "%d", setnumber);
+    else
+        *setstring = '\0';
+}
+
+static int send_presentRequest(const char *arg)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
+    Z_PresentRequest *req = apdu->u.presentRequest;
+    Z_RecordComposition compo;
+    int nos = 1;
+    char setstring[100];
+
+    req->referenceId = set_refid (out);
+
+    parse_show_args(arg, setstring, &setno, &nos);
+    if (*setstring)
+        req->resultSetId = setstring;
+
+    req->resultSetStartPoint = &setno;
+    req->numberOfRecordsRequested = &nos;
+
+    req->preferredRecordSyntax =
+        yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax);
+
+    if (record_schema)
+    {
+        req->recordComposition = &compo;
+        compo.which = Z_RecordComp_complex;
+        compo.u.complex = (Z_CompSpec *)
+            odr_malloc(out, sizeof(*compo.u.complex));
+        compo.u.complex->selectAlternativeSyntax = (bool_t *) 
+            odr_malloc(out, sizeof(bool_t));
+        *compo.u.complex->selectAlternativeSyntax = 0;
+
+        compo.u.complex->generic = (Z_Specification *)
+            odr_malloc(out, sizeof(*compo.u.complex->generic));
+        compo.u.complex->generic->which = Z_Schema_oid;
+
+        compo.u.complex->generic->schema.oid =
+	    yaz_str_to_z3950oid(out, CLASS_SCHEMA, record_schema);
+
+        if (!compo.u.complex->generic->schema.oid)
+        {
+            /* OID wasn't a schema! Try record syntax instead. */
+            compo.u.complex->generic->schema.oid = (Odr_oid *)
+		yaz_str_to_z3950oid(out, CLASS_RECSYN, record_schema);
+        }
+        if (!elementSetNames)
+            compo.u.complex->generic->elementSpec = 0;
+        else
+        {
+            compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
+                odr_malloc(out, sizeof(Z_ElementSpec));
+            compo.u.complex->generic->elementSpec->which =
+                Z_ElementSpec_elementSetName;
+            compo.u.complex->generic->elementSpec->u.elementSetName =
+                elementSetNames->u.generic;
+        }
+        compo.u.complex->num_dbSpecific = 0;
+        compo.u.complex->dbSpecific = 0;
+        compo.u.complex->num_recordSyntax = 0;
+        compo.u.complex->recordSyntax = 0;
+    }
+    else if (elementSetNames)
+    {
+        req->recordComposition = &compo;
+        compo.which = Z_RecordComp_simple;
+        compo.u.simple = elementSetNames;
+    }
+    send_apdu(apdu);
+    printf("Sent presentRequest (%d+%d).\n", setno, nos);
+    return 2;
+}
+
+#if HAVE_XML2
+static int send_SRW_presentRequest(const char *arg)
+{
+    char setstring[100];
+    int nos = 1;
+    Z_SRW_PDU *sr = srw_sr;
+
+    if (!sr)
+        return 0;
+    parse_show_args(arg, setstring, &setno, &nos);
+    sr->u.request->startRecord = odr_intdup(out, setno);
+    sr->u.request->maximumRecords = odr_intdup(out, nos);
+    if (record_schema)
+        sr->u.request->recordSchema = record_schema;
+    if (recordsyntax == VAL_TEXT_XML)
+        sr->u.request->recordPacking = "xml";
+    return send_srw(sr);
+}
+#endif
+
+static void close_session (void)
+{
+    if (conn)
+        cs_close (conn);
+    conn = 0;
+    if (session_mem)
+    {
+        nmem_destroy (session_mem);
+        session_mem = NULL;
+    }
+    sent_close = 0;
+    odr_reset(out);
+    odr_reset(in);
+    odr_reset(print);
+}
+
+void process_close(Z_Close *req)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
+    Z_Close *res = apdu->u.close;
+
+    static char *reasons[] =
+    {
+        "finished",
+        "shutdown",
+        "system problem",
+        "cost limit reached",
+        "resources",
+        "security violation",
+        "protocolError",
+        "lack of activity",
+        "peer abort",
+        "unspecified"
+    };
+
+    printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
+        req->diagnosticInformation ? req->diagnosticInformation : "NULL");
+    if (sent_close)
+        close_session ();
+    else
+    {
+        *res->closeReason = Z_Close_finished;
+        send_apdu(apdu);
+        printf("Sent response.\n");
+        sent_close = 1;
+    }
+}
+
+static int cmd_show(const char *arg)
+{
+    if (protocol == PROTO_HTTP)
+    {
+#if HAVE_XML2
+        if (!conn)
+            cmd_open(0);
+	if (!conn)
+	    return 0;
+        if (!send_SRW_presentRequest(arg))
+            return 0;
+#else
+        return 0;
+#endif
+    }
+    else
+    {
+        if (!conn)
+        {
+            printf("Not connected yet\n");
+            return 0;
+        }
+        if (!send_presentRequest(arg))
+            return 0;
+    }
+    return 2;
+}
+
+int cmd_quit(const char *arg)
+{
+    printf("See you later, alligator.\n");
+    xmalloc_trav ("");
+    exit(0);
+    return 0;
+}
+
+int cmd_cancel(const char *arg)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
+    Z_TriggerResourceControlRequest *req =
+        apdu->u.triggerResourceControlRequest;
+    bool_t rfalse = 0;
+    
+    if (!conn)
+    {
+        printf("Session not initialized yet\n");
+        return 0;
+    }
+    if (only_z3950())
+	return 0;
+    if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
+    {
+        printf("Target doesn't support cancel (trigger resource ctrl)\n");
+        return 0;
+    }
+    *req->requestedAction = Z_TriggerResourceControlRequest_cancel;
+    req->resultSetWanted = &rfalse;
+    req->referenceId = set_refid (out);
+
+    send_apdu(apdu);
+    printf("Sent cancel request\n");
+    return 2;
+}
+
+
+int cmd_cancel_find(const char *arg) {
+    int fres;
+    fres=cmd_find(arg);
+    if( fres > 0 ) {
+	return cmd_cancel("");
+    };
+    return fres;
+}
+
+int send_scanrequest(const char *query, int pp, int num, const char *term)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
+    Z_ScanRequest *req = apdu->u.scanRequest;
+    
+    if (only_z3950())
+	return 0;
+    if (queryType == QueryType_CCL2RPN)
+    {
+        int error, pos;
+        struct ccl_rpn_node *rpn;
+
+        rpn = ccl_find_str (bibset,  query, &error, &pos);
+        if (error)
+        {
+            printf("CCL ERROR: %s\n", ccl_err_msg(error));
+            return -1;
+        }
+        req->attributeSet =
+	    yaz_oidval_to_z3950oid(out, CLASS_ATTSET, VAL_BIB1);
+        if (!(req->termListAndStartPoint = ccl_scan_query (out, rpn)))
+        {
+            printf("Couldn't convert CCL to Scan term\n");
+            return -1;
+        }
+        ccl_rpn_delete (rpn);
+    }
+    else
+    {
+        YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
+
+        if (!(req->termListAndStartPoint =
+              yaz_pqf_scan(pqf_parser, out, &req->attributeSet, query)))
+        {
+            const char *pqf_msg;
+            size_t off;
+            int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
+            printf("%*s^\n", off+7, "");
+            printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
+            yaz_pqf_destroy (pqf_parser);
+            return -1;
+        }
+        yaz_pqf_destroy (pqf_parser);
+    }
+    if (term && *term)
+    {
+        if (req->termListAndStartPoint->term &&
+            req->termListAndStartPoint->term->which == Z_Term_general &&
+            req->termListAndStartPoint->term->u.general)
+        {
+            req->termListAndStartPoint->term->u.general->buf =
+                (unsigned char *) odr_strdup(out, term);
+            req->termListAndStartPoint->term->u.general->len =
+                req->termListAndStartPoint->term->u.general->size =
+                strlen(term);
+        }
+    }
+    req->referenceId = set_refid (out);
+    req->num_databaseNames = num_databaseNames;
+    req->databaseNames = databaseNames;
+    req->numberOfTermsRequested = &num;
+    req->preferredPositionInResponse = &pp;
+    req->stepSize = odr_intdup(out, scan_stepSize);
+    send_apdu(apdu);
+    return 2;
+}
+
+int send_sortrequest(const char *arg, int newset)
+{
+    Z_APDU *apdu = zget_APDU(out, Z_APDU_sortRequest);
+    Z_SortRequest *req = apdu->u.sortRequest;
+    Z_SortKeySpecList *sksl = (Z_SortKeySpecList *)
+        odr_malloc (out, sizeof(*sksl));
+    char setstring[32];
+
+    if (only_z3950())
+	return 0;
+    if (setnumber >= 0)
+        sprintf (setstring, "%d", setnumber);
+    else
+        sprintf (setstring, "default");
+
+    req->referenceId = set_refid (out);
+
+    req->num_inputResultSetNames = 1;
+    req->inputResultSetNames = (Z_InternationalString **)
+        odr_malloc (out, sizeof(*req->inputResultSetNames));
+    req->inputResultSetNames[0] = odr_strdup (out, setstring);
+
+    if (newset && setnumber >= 0)
+        sprintf (setstring, "%d", ++setnumber);
+
+    req->sortedResultSetName = odr_strdup (out, setstring);
+
+    req->sortSequence = yaz_sort_spec (out, arg);
+    if (!req->sortSequence)
+    {
+        printf ("Missing sort specifications\n");
+        return -1;
+    }
+    send_apdu(apdu);
+    return 2;
+}
+
+void display_term(Z_TermInfo *t)
+{
+    if (t->displayTerm)
+        printf("%s", t->displayTerm);
+    else if (t->term->which == Z_Term_general)
+    {
+        printf("%.*s", t->term->u.general->len, t->term->u.general->buf);
+        sprintf(last_scan_line, "%.*s", t->term->u.general->len,
+            t->term->u.general->buf);
+    }
+    else
+        printf("Term (not general)");
+    if (t->globalOccurrences)
+        printf (" (%d)\n", *t->globalOccurrences);
+    else
+        printf ("\n");
+}
+
+void process_scanResponse(Z_ScanResponse *res)
+{
+    int i;
+    Z_Entry **entries = NULL;
+    int num_entries = 0;
+   
+    printf("Received ScanResponse\n"); 
+    print_refid (res->referenceId);
+    printf("%d entries", *res->numberOfEntriesReturned);
+    if (res->positionOfTerm)
+        printf (", position=%d", *res->positionOfTerm); 
+    printf ("\n");
+    if (*res->scanStatus != Z_Scan_success)
+        printf("Scan returned code %d\n", *res->scanStatus);
+    if (!res->entries)
+        return;
+    if ((entries = res->entries->entries))
+        num_entries = res->entries->num_entries;
+    for (i = 0; i < num_entries; i++)
+    {
+        int pos_term = res->positionOfTerm ? *res->positionOfTerm : -1;
+        if (entries[i]->which == Z_Entry_termInfo)
+        {
+            printf("%c ", i + 1 == pos_term ? '*' : ' ');
+            display_term(entries[i]->u.termInfo);
+        }
+        else
+            display_diagrecs(&entries[i]->u.surrogateDiagnostic, 1);
+    }
+    if (res->entries->nonsurrogateDiagnostics)
+        display_diagrecs (res->entries->nonsurrogateDiagnostics,
+                          res->entries->num_nonsurrogateDiagnostics);
+}
+
+void process_sortResponse(Z_SortResponse *res)
+{
+    printf("Received SortResponse: status=");
+    switch (*res->sortStatus)
+    {
+    case Z_SortResponse_success:
+        printf ("success"); break;
+    case Z_SortResponse_partial_1:
+        printf ("partial"); break;
+    case Z_SortResponse_failure:
+        printf ("failure"); break;
+    default:
+        printf ("unknown (%d)", *res->sortStatus);
+    }
+    printf ("\n");
+    print_refid (res->referenceId);
+    if (res->diagnostics)
+        display_diagrecs(res->diagnostics,
+                         res->num_diagnostics);
+}
+
+void process_deleteResultSetResponse (Z_DeleteResultSetResponse *res)
+{
+    printf("Got deleteResultSetResponse status=%d\n",
+           *res->deleteOperationStatus);
+    if (res->deleteListStatuses)
+    {
+        int i;
+        for (i = 0; i < res->deleteListStatuses->num; i++)
+        {
+            printf ("%s status=%d\n", res->deleteListStatuses->elements[i]->id,
+                    *res->deleteListStatuses->elements[i]->status);
+        }
+    }
+}
+
+int cmd_sort_generic(const char *arg, int newset)
+{
+    if (!conn)
+    {
+        printf("Session not initialized yet\n");
+        return 0;
+    }
+    if (only_z3950())
+	return 0;
+    if (!ODR_MASK_GET(session->options, Z_Options_sort))
+    {
+        printf("Target doesn't support sort\n");
+        return 0;
+    }
+    if (*arg)
+    {
+        if (send_sortrequest(arg, newset) < 0)
+            return 0;
+        return 2;
+    }
+    return 0;
+}
+
+int cmd_sort(const char *arg)
+{
+    return cmd_sort_generic (arg, 0);
+}
+
+int cmd_sort_newset (const char *arg)
+{
+    return cmd_sort_generic (arg, 1);
+}
+
+int cmd_scanstep(const char *arg)
+{
+    scan_stepSize = atoi(arg);
+    return 0;
+}
+
+int cmd_scanpos(const char *arg)
+{
+    int r = sscanf(arg, "%d", &scan_position);
+    if (r == 0)
+        scan_position = 1;
+    return 0;
+}
+
+int cmd_scansize(const char *arg)
+{
+    int r = sscanf(arg, "%d", &scan_size);
+    if (r == 0)
+        scan_size = 20;
+    return 0;
+}
+
+int cmd_scan(const char *arg)
+{
+    if (protocol == PROTO_HTTP)
+    {
+#if HAVE_XML2
+        if (!conn)
+            cmd_open(0);
+	if (!conn)
+	    return 0;
+	if (*arg)
+	{
+	    if (send_SRW_scanRequest(arg, scan_position, scan_size) < 0)
+		return 0;
+	}
+	else
+	{
+	    if (send_SRW_scanRequest(last_scan_line, 1, scan_size) < 0)
+		return 0;
+	}
+	return 2;
+#else
+        return 0;
+#endif
+    }
+    else
+    {
+	if (!conn)
+	{
+	    try_reconnect();
+	    
+	    if (!conn) {								
+		printf("Session not initialized yet\n");
+		return 0;
+	    }
+	}
+	if (!ODR_MASK_GET(session->options, Z_Options_scan))
+	{
+	    printf("Target doesn't support scan\n");
+	    return 0;
+	}
+	if (*arg)
+	{
+	    strcpy (last_scan_query, arg);
+	    if (send_scanrequest(arg, scan_position, scan_size, 0) < 0)
+		return 0;
+	}
+	else
+	{
+	    if (send_scanrequest(last_scan_query, 1, scan_size, last_scan_line) < 0)
+		return 0;
+	}
+	return 2;
+    }
+}
+
+int cmd_schema(const char *arg)
+{
+    xfree(record_schema);
+    record_schema = 0;
+    if (arg && *arg)
+        record_schema = xstrdup(arg);
+    return 1;
+}
+
+int cmd_format(const char *arg)
+{
+    oid_value nsyntax;
+    if (!arg || !*arg)
+    {
+        printf("Usage: format <recordsyntax>\n");
+        return 0;
+    }
+    nsyntax = oid_getvalbyname (arg);
+    if (strcmp(arg, "none") && nsyntax == VAL_NONE)
+    {
+        printf ("unknown record syntax\n");
+        return 0;
+    }
+    recordsyntax = nsyntax;
+    return 1;
+}
+
+int cmd_elements(const char *arg)
+{
+    static Z_ElementSetNames esn;
+    static char what[100];
+
+    if (!arg || !*arg)
+    {
+        elementSetNames = 0;
+        return 1;
+    }
+    strcpy(what, arg);
+    esn.which = Z_ElementSetNames_generic;
+    esn.u.generic = what;
+    elementSetNames = &esn;
+    return 1;
+}
+
+int cmd_attributeset(const char *arg)
+{
+    char what[100];
+
+    if (!arg || !*arg)
+    {
+        printf("Usage: attributeset <setname>\n");
+        return 0;
+    }
+    sscanf(arg, "%s", what);
+    if (p_query_attset (what))
+    {
+        printf("Unknown attribute set name\n");
+        return 0;
+    }
+    return 1;
+}
+
+int cmd_querytype (const char *arg)
+{
+    if (!strcmp (arg, "ccl"))
+        queryType = QueryType_CCL;
+    else if (!strcmp (arg, "prefix") || !strcmp(arg, "rpn"))
+        queryType = QueryType_Prefix;
+    else if (!strcmp (arg, "ccl2rpn") || !strcmp (arg, "cclrpn"))
+        queryType = QueryType_CCL2RPN;
+    else if (!strcmp(arg, "cql"))
+        queryType = QueryType_CQL;        
+    else if (!strcmp (arg, "cql2rpn") || !strcmp (arg, "cqlrpn"))
+        queryType = QueryType_CQL2RPN;
+    else
+    {
+        printf ("Querytype must be one of:\n");
+        printf (" prefix         - Prefix query\n");
+        printf (" ccl            - CCL query\n");
+        printf (" ccl2rpn        - CCL query converted to RPN\n");
+        printf (" cql            - CQL\n");
+        printf (" cql2rpn        - CQL query converted to RPN\n");
+        return 0;
+    }
+    return 1;
+}
+
+int cmd_refid (const char *arg)
+{
+    xfree (refid);
+    refid = NULL;
+    if (*arg)
+        refid = xstrdup (arg);
+    return 1;
+}
+
+int z_cmd_close(const char *arg)
+{
+    Z_APDU *apdu;
+    Z_Close *req;
+    if (!conn)
+        return 0;
+    if (only_z3950())
+	return 0;
+
+    apdu = zget_APDU(out, Z_APDU_close);
+    req = apdu->u.close;
+    *req->closeReason = Z_Close_finished;
+    send_apdu(apdu);
+    printf("Sent close request.\n");
+    sent_close = 1;
+    return 2;
+}
+
+int cmd_packagename(const char* arg)
+{
+    xfree (esPackageName);
+    esPackageName = NULL;
+    if (*arg)
+        esPackageName = xstrdup(arg);
+    return 1;
+}
+
+int cmd_proxy(const char* arg)
+{
+    xfree (yazProxy);
+    yazProxy = NULL;
+    if (*arg)
+        yazProxy = xstrdup (arg);
+    return 1;
+}
+
+int cmd_marccharset(const char *arg)
+{
+    char l1[30];
+
+    *l1 = 0;
+    if (sscanf(arg, "%29s", l1) < 1)
+    {
+    	printf("MARC character set is `%s'\n", 
+               marcCharset ? marcCharset: "none");
+        return 1;
+    }
+    xfree (marcCharset);
+    marcCharset = 0;
+    if (strcmp(l1, "-"))
+        marcCharset = xstrdup(l1);
+    return 1;
+}
+
+int cmd_displaycharset(const char *arg)
+{
+    char l1[30];
+
+    *l1 = 0;
+    if (sscanf(arg, "%29s", l1) < 1)
+    {
+    	printf("Display character set is `%s'\n", 
+               outputCharset ? outputCharset: "none");
+    }
+    else
+    {
+        xfree (outputCharset);
+        outputCharset = 0;
+        if (!strcmp(l1, "auto") && codeset)
+        {
+            if (codeset)
+            {
+                printf ("Display character set: %s\n", codeset);
+                outputCharset = xstrdup(codeset);
+            }
+            else
+                printf ("No codeset found on this system\n");
+        }
+        else if (strcmp(l1, "-") && strcmp(l1, "none"))
+            outputCharset = xstrdup(l1);
+    } 
+    return 1;
+}
+
+int cmd_negcharset(const char *arg)
+{
+    char l1[30];
+
+    *l1 = 0;
+    if (sscanf(arg, "%29s %d %d", l1, &negotiationCharsetRecords,
+	       &negotiationCharsetVersion) < 1)
+    {
+    	printf("Current negotiation character set is `%s'\n", 
+               negotiationCharset ? negotiationCharset: "none");  
+	printf("Records in charset %s\n", negotiationCharsetRecords ? 
+	       "yes" : "no");
+	printf("Charneg version %d\n", negotiationCharsetVersion);
+    }
+    else
+    {
+	xfree (negotiationCharset);
+	negotiationCharset = NULL;
+	if (*l1 && strcmp(l1, "-") && strcmp(l1, "none"))
+	{
+	    negotiationCharset = xstrdup(l1);
+	    printf ("Character set negotiation : %s\n", negotiationCharset);
+	}
+    }
+    return 1;
+}
+
+int cmd_charset(const char* arg)
+{
+    char l1[30], l2[30], l3[30];
+
+    *l1 = *l2 = *l3 = 0;
+    if (sscanf(arg, "%29s %29s %29s", l1, l2, l3) < 1)
+    {
+	cmd_negcharset("");
+	cmd_displaycharset("");
+	cmd_marccharset("");
+    }
+    else
+    {
+	cmd_negcharset(l1);
+	if (*l2)
+	    cmd_displaycharset(l2);
+	if (*l3)
+	    cmd_marccharset(l3);
+    }
+    return 1;
+}
+
+int cmd_lang(const char* arg)
+{
+    if (*arg == '\0') {
+    	printf("Current language is `%s'\n", (yazLang)?yazLang:NULL);
+    	return 1;
+    }
+    xfree (yazLang);
+    yazLang = NULL;
+    if (*arg)
+        yazLang = xstrdup(arg);
+    return 1;
+}
+
+int cmd_source(const char* arg, int echo ) 
+{
+    /* first should open the file and read one line at a time.. */
+    FILE* includeFile;
+    char line[102400], *cp;
+
+    if(strlen(arg)<1) {
+        fprintf(stderr,"Error in source command use a filename\n");
+        return -1;
+    }
+    
+    includeFile = fopen (arg, "r");
+    
+    if(!includeFile) {
+        fprintf(stderr,"Unable to open file %s for reading\n",arg);
+        return -1;
+    }
+    
+    while(!feof(includeFile)) {
+        memset(line,0,sizeof(line));
+        fgets(line,sizeof(line),includeFile);
+        
+        if(strlen(line) < 2) continue;
+        if(line[0] == '#') continue;
+        
+        if ((cp = strrchr (line, '\n')))
+            *cp = '\0';
+	
+	if( echo ) {
+	    printf( "processing line: %s\n",line );
+	};
+        process_cmd_line(line);
+    }
+    
+    if(fclose(includeFile)<0) {
+        perror("unable to close include file");
+        exit(1);
+    }
+    return 1;
+}
+
+int cmd_source_echo(const char* arg)
+{ 
+    cmd_source(arg, 1);
+    return 1;
+}
+
+int cmd_source_noecho(const char* arg)
+{
+    cmd_source(arg, 0);
+    return 1;
+}
+
+
+int cmd_subshell(const char* args)
+{
+    if(strlen(args)) 
+        system(args);
+    else 
+        system(getenv("SHELL"));
+    
+    printf("\n");
+    return 1;
+}
+
+int cmd_set_berfile(const char *arg)
+{
+    if (ber_file && ber_file != stdout && ber_file != stderr)
+        fclose(ber_file);
+    if (!strcmp(arg, ""))
+        ber_file = 0;
+    else if (!strcmp(arg, "-"))
+        ber_file = stdout;
+    else
+        ber_file = fopen(arg, "a");
+    return 1;
+}
+
+int cmd_set_apdufile(const char *arg)
+{
+    if(apdu_file && apdu_file != stderr && apdu_file != stderr)
+        fclose(apdu_file);
+    if (!strcmp(arg, ""))
+        apdu_file = 0;
+    else if (!strcmp(arg, "-"))
+        apdu_file = stderr;
+    else
+    {
+        apdu_file = fopen(arg, "a");
+        if (!apdu_file)
+            perror("unable to open apdu log file");
+    }
+    if (apdu_file)
+        odr_setprint(print, apdu_file);
+    return 1;
+}
+
+int cmd_set_cclfile(const char* arg)
+{  
+    FILE *inf;
+
+    bibset = ccl_qual_mk (); 
+    inf = fopen (arg, "r");
+    if (!inf)
+        perror("unable to open CCL file");
+    else
+    {
+        ccl_qual_file (bibset, inf);
+        fclose (inf);
+    }
+    strcpy(ccl_fields,arg);
+    return 0;
+}
+
+int cmd_set_cqlfile(const char* arg)
+{
+    cql_transform_t newcqltrans;
+
+    if ((newcqltrans = cql_transform_open_fname(arg)) == 0) {
+        perror("unable to open CQL file");
+	return 0;
+    }
+    if (cqltrans != 0)
+	cql_transform_close(cqltrans);
+
+    cqltrans = newcqltrans;
+    strcpy(cql_fields, arg);
+    return 0;
+}
+
+int cmd_set_auto_reconnect(const char* arg)
+{  
+    if(strlen(arg)==0) {
+        auto_reconnect = ! auto_reconnect;
+    } else if(strcmp(arg,"on")==0) {
+        auto_reconnect = 1;
+    } else if(strcmp(arg,"off")==0) {
+        auto_reconnect = 0;		
+    } else {
+        printf("Error use on or off\n");
+        return 1;
+    }
+    
+    if (auto_reconnect)
+        printf("Set auto reconnect enabled.\n");
+    else
+        printf("Set auto reconnect disabled.\n");
+    
+    return 0;
+}
+
+int cmd_set_marcdump(const char* arg)
+{
+    if(marc_file && marc_file != stderr) { /* don't close stdout*/
+        fclose(marc_file);
+    }
+
+    if (!strcmp(arg, ""))
+        marc_file = 0;
+    else if (!strcmp(arg, "-"))
+        marc_file = stderr;
+    else
+    {
+        marc_file = fopen(arg, "a");
+        if (!marc_file)
+            perror("unable to open marc log file");
+    }
+    return 1;
+}
+
+/* 
+   this command takes 3 arge {name class oid} 
+*/
+int cmd_register_oid(const char* args) {
+    static struct {
+        char* className;
+        oid_class oclass;
+    } oid_classes[] = {
+        {"appctx",CLASS_APPCTX},
+        {"absyn",CLASS_ABSYN},
+        {"attset",CLASS_ATTSET},
+        {"transyn",CLASS_TRANSYN},
+        {"diagset",CLASS_DIAGSET},
+        {"recsyn",CLASS_RECSYN},
+        {"resform",CLASS_RESFORM},
+        {"accform",CLASS_ACCFORM},
+        {"extserv",CLASS_EXTSERV},
+        {"userinfo",CLASS_USERINFO},
+        {"elemspec",CLASS_ELEMSPEC},
+        {"varset",CLASS_VARSET},
+        {"schema",CLASS_SCHEMA},
+        {"tagset",CLASS_TAGSET},
+        {"general",CLASS_GENERAL},
+        {0,(enum oid_class) 0}
+    };
+    char oname_str[101], oclass_str[101], oid_str[101];  
+    char* name;
+    int i;
+    oid_class oidclass = CLASS_GENERAL;
+    int val = 0, oid[OID_SIZE];
+    struct oident * new_oident=NULL;
+    
+    if (sscanf (args, "%100[^ ] %100[^ ] %100s",
+                oname_str,oclass_str, oid_str) < 1) {
+        printf("Error in register command \n");
+        return 0;
+    }
+    
+    for (i = 0; oid_classes[i].className; i++) {
+        if (!strcmp(oid_classes[i].className, oclass_str))
+        {
+            oidclass=oid_classes[i].oclass;
+            break;
+        }
+    }
+    
+    if(!(oid_classes[i].className)) {
+        printf("Unknown oid class %s\n",oclass_str);
+        return 0;
+    }
+    
+    i = 0;
+    name = oid_str;
+    val = 0;
+    
+    while (isdigit (*(unsigned char *) name))
+    {
+        val = val*10 + (*name - '0');
+        name++;
+        if (*name == '.')
+        {
+            if (i < OID_SIZE-1)
+                oid[i++] = val;
+            val = 0;
+            name++;
+        }
+    }
+    oid[i] = val;
+    oid[i+1] = -1;
+    
+    new_oident = oid_addent (oid, PROTO_GENERAL, oidclass, oname_str,
+			     VAL_DYNAMIC);  
+    if(strcmp(new_oident->desc,oname_str))
+    {
+        fprintf(stderr,"oid is already named as %s, registration failed\n",
+                new_oident->desc);
+    }
+    return 1;  
+}
+
+int cmd_push_command(const char* arg) 
+{
+    
+#if HAVE_READLINE_HISTORY_H
+    if(strlen(arg)>1) 
+        add_history(arg);
+#else 
+    fprintf(stderr,"Not compiled with the readline/history module\n");
+#endif
+    return 1;
+}
+
+void source_rcfile() 
+{
+    /*  Look for a $HOME/.yazclientrc and source it if it exists */
+    struct stat statbuf;
+    char buffer[1000];
+    char* homedir=getenv("HOME");
+
+    if( homedir ) {
+	
+	sprintf(buffer,"%s/.yazclientrc",homedir);
+
+	if(stat(buffer,&statbuf)==0) {
+	    cmd_source(buffer, 0 );
+	}
+	
+    };
+    
+    if(stat(".yazclientrc",&statbuf)==0) {
+        cmd_source(".yazclientrc", 0 );
+    }
+}
+
+
+void z_initialize(void)
+{
+    FILE *inf;
+    int i;
+    
+    if (!(out = odr_createmem(ODR_ENCODE)) ||
+        !(in = odr_createmem(ODR_DECODE)) ||
+        !(print = odr_createmem(ODR_PRINT)))
+    {
+        fprintf(stderr, "failed to allocate ODR streams\n");
+        exit(1);
+    }
+    oid_init();
+    
+    setvbuf(stdout, 0, _IONBF, 0);
+    if (apdu_file)
+        odr_setprint(print, apdu_file);
+
+    bibset = ccl_qual_mk (); 
+    inf = fopen (ccl_fields, "r");
+    if (inf)
+    {
+        ccl_qual_file (bibset, inf);
+        fclose (inf);
+    }
+
+    cqltrans = cql_transform_open_fname(cql_fields);
+    /* If this fails, no problem: we detect cqltrans == 0 later */
+
+#if HAVE_READLINE_READLINE_H
+    rl_attempted_completion_function = (CPPFunction*)readline_completer;
+#endif
+    
+    
+    for(i=0; i<maxOtherInfosSupported; ++i) {
+        extraOtherInfos[i].oidval = -1;
+    }
+    
+    source_rcfile();
+}
+
+
+#if HAVE_GETTIMEOFDAY
+struct timeval tv_start, tv_end;
+#endif
+
+#if HAVE_XML2
+static void handle_srw_record(Z_SRW_record *rec)
+{
+    if (rec->recordPosition)
+    {
+	printf ("pos=%d", *rec->recordPosition);
+	setno = *rec->recordPosition + 1;
+    }
+    if (rec->recordSchema)
+	printf (" schema=%s", rec->recordSchema);
+    printf ("\n");
+    if (rec->recordData_buf && rec->recordData_len)
+    {
+	fwrite(rec->recordData_buf, 1, rec->recordData_len, stdout);
+	if (marc_file)
+	    fwrite (rec->recordData_buf, 1, rec->recordData_len, marc_file);
+    }
+    else
+	printf ("No data!");
+    printf("\n");
+}
+
+static void handle_srw_explain_response(Z_SRW_explainResponse *res)
+{
+    handle_srw_record(&res->record);
+}
+
+static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
+{
+    int i;
+
+    printf ("Received SRW SearchRetrieve Response\n");
+    
+    for (i = 0; i<res->num_diagnostics; i++)
+    {
+	if (res->diagnostics[i].uri)
+	    printf ("SRW diagnostic %s\n",
+		    res->diagnostics[i].uri);
+	else
+	    printf ("SRW diagnostic missing or could not be decoded\n");
+	if (res->diagnostics[i].message)
+            printf ("Message: %s\n", res->diagnostics[i].message);
+	if (res->diagnostics[i].details)
+            printf ("Details: %s\n", res->diagnostics[i].details);
+    }
+    if (res->numberOfRecords)
+        printf ("Number of hits: %d\n", *res->numberOfRecords);
+    for (i = 0; i<res->num_records; i++)
+	handle_srw_record(res->records + i);
+}
+
+static void handle_srw_scan_term(Z_SRW_scanTerm *term)
+{
+    if (term->displayTerm)
+	printf("%s:", term->displayTerm);
+    else if (term->value)
+	printf("%s:", term->value);
+    else
+	printf("No value:");
+    if (term->numberOfRecords)
+	printf(" %d", *term->numberOfRecords);
+    if (term->whereInList)
+	printf(" %s", term->whereInList);
+    if (term->value && term->displayTerm)
+	printf(" %s", term->value);
+
+    strcpy(last_scan_line, term->value);
+    printf("\n");
+}
+
+static void handle_srw_scan_response(Z_SRW_scanResponse *res)
+{
+    int i;
+
+    printf ("Received SRW Scan Response\n");
+    
+    for (i = 0; i<res->num_diagnostics; i++)
+    {
+	if (res->diagnostics[i].uri)
+	    printf ("SRW diagnostic %s\n",
+		    res->diagnostics[i].uri);
+	else
+	    printf ("SRW diagnostic missing or could not be decoded\n");
+	if (res->diagnostics[i].message)
+            printf ("Message: %s\n", res->diagnostics[i].message);
+	if (res->diagnostics[i].details)
+            printf ("Details: %s\n", res->diagnostics[i].details);
+    }
+    if (res->terms)
+	for (i = 0; i<res->num_terms; i++)
+	    handle_srw_scan_term(res->terms + i);
+}
+
+static void http_response(Z_HTTP_Response *hres)
+{
+    int ret = -1;
+    const char *content_type = z_HTTP_header_lookup(hres->headers,
+                                                    "Content-Type");
+    const char *connection_head = z_HTTP_header_lookup(hres->headers,
+                                                       "Connection");
+    if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
+    {
+        Z_SOAP *soap_package = 0;
+        ODR o = odr_createmem(ODR_DECODE);
+        Z_SOAP_Handler soap_handlers[2] = {
+            {"http://www.loc.gov/zing/srw/", 0,
+             (Z_SOAP_fun) yaz_srw_codec},
+            {0, 0, 0}
+        };
+        ret = z_soap_codec(o, &soap_package,
+                           &hres->content_buf, &hres->content_len,
+                           soap_handlers);
+        if (!ret && soap_package->which == Z_SOAP_generic &&
+            soap_package->u.generic->no == 0)
+        {
+            Z_SRW_PDU *sr = soap_package->u.generic->p;
+            if (sr->which == Z_SRW_searchRetrieve_response)
+                handle_srw_response(sr->u.response);
+            else if (sr->which == Z_SRW_explain_response)
+                handle_srw_explain_response(sr->u.explain_response);
+            else if (sr->which == Z_SRW_scan_response)
+                handle_srw_scan_response(sr->u.scan_response);
+            else
+                ret = -1;
+        }
+        else if (soap_package && (soap_package->which == Z_SOAP_fault
+                          || soap_package->which == Z_SOAP_error))
+        {
+            printf ("HTTP Error Status=%d\n", hres->code);
+            printf ("SOAP Fault code %s\n",
+                    soap_package->u.fault->fault_code);
+            printf ("SOAP Fault string %s\n", 
+                    soap_package->u.fault->fault_string);
+	    if (soap_package->u.fault->details)
+		printf ("SOAP Details %s\n", 
+			soap_package->u.fault->details);
+        }
+        else
+            ret = -1;
+        odr_destroy(o);
+    }
+    if (ret)
+    {
+        if (hres->code != 200)
+        {
+	    printf ("HTTP Error Status=%d\n", hres->code);
+        }
+        else
+        {
+            printf ("Decoding of SRW package failed\n");
+        }
+        close_session();
+    }
+    else
+    {
+	if (!strcmp(hres->version, "1.0"))
+	{
+	    /* HTTP 1.0: only if Keep-Alive we stay alive.. */
+	    if (!connection_head || strcmp(connection_head, "Keep-Alive"))
+		close_session();
+	}
+	else 
+	{
+	    /* HTTP 1.1: only if no close we stay alive .. */
+	    if (connection_head && !strcmp(connection_head, "close"))
+		close_session();
+	}
+    }
+}
+#endif
+
+void wait_and_handle_response() 
+{
+    int reconnect_ok = 1;
+    int res;
+    char *netbuffer= 0;
+    int netbufferlen = 0;
+    Z_GDU *gdu;
+    
+    while(conn)
+    {
+        res = cs_get(conn, &netbuffer, &netbufferlen);
+        if (reconnect_ok && res <= 0 && protocol == PROTO_HTTP)
+        {
+            cs_close(conn);
+            conn = 0;
+            cmd_open(0);
+            reconnect_ok = 0;
+            if (conn)
+            {
+                char *buf_out;
+                int len_out;
+                
+                buf_out = odr_getbuf(out, &len_out, 0);
+                
+		do_hex_dump(buf_out, len_out);
+
+                cs_put(conn, buf_out, len_out);
+                
+                odr_reset(out);
+                continue;
+            }
+        }
+        else if (res <= 0)
+        {
+            printf("Target closed connection\n");
+            close_session();
+            break;
+        }
+        odr_reset(out);
+        odr_reset(in); /* release APDU from last round */
+        record_last = 0;
+        do_hex_dump(netbuffer, res);
+        odr_setbuf(in, netbuffer, res, 0);
+        
+        if (!z_GDU(in, &gdu, 0, 0))
+        {
+            FILE *f = ber_file ? ber_file : stdout;
+            odr_perror(in, "Decoding incoming APDU");
+            fprintf(f, "[Near %d]\n", odr_offset(in));
+            fprintf(f, "Packet dump:\n---------\n");
+            odr_dumpBER(f, netbuffer, res);
+            fprintf(f, "---------\n");
+            if (apdu_file)
+	    {
+                z_GDU(print, &gdu, 0, 0);
+		odr_reset(print);
+	    }
+            if (conn && cs_more(conn))
+                continue;
+	    break;
+        }
+        if (ber_file)
+            odr_dumpBER(ber_file, netbuffer, res);
+        if (apdu_file && !z_GDU(print, &gdu, 0, 0))
+        {
+            odr_perror(print, "Failed to print incoming APDU");
+            odr_reset(print);
+	    continue;
+        }
+        if (gdu->which == Z_GDU_Z3950)
+        {
+            Z_APDU *apdu = gdu->u.z3950;
+            switch(apdu->which)
+            {
+            case Z_APDU_initResponse:
+		// kjdon copying johnmcp
+		/* save session parameters for later use */
+		session_mem = odr_extract_mem(in);
+		session = apdu->u.initResponse;
+		// greenstone will call z_get_initResponse to get the info
+		// about what happened with the connection
+                //process_initResponse(apdu->u.initResponse);
+                break;
+            case Z_APDU_searchResponse:
+                process_searchResponse(apdu->u.searchResponse);
+                break;
+            case Z_APDU_scanResponse:
+                process_scanResponse(apdu->u.scanResponse);
+                break;
+            case Z_APDU_presentResponse:
+                print_refid (apdu->u.presentResponse->referenceId);
+                setno +=
+                    *apdu->u.presentResponse->numberOfRecordsReturned;
+                if (apdu->u.presentResponse->records)
+                    display_records(apdu->u.presentResponse->records);
+                else
+                    printf("No records.\n");
+                printf ("nextResultSetPosition = %d\n",
+                        *apdu->u.presentResponse->nextResultSetPosition);
+                break;
+            case Z_APDU_sortResponse:
+                process_sortResponse(apdu->u.sortResponse);
+                break;
+            case Z_APDU_extendedServicesResponse:
+                printf("Got extended services response\n");
+                process_ESResponse(apdu->u.extendedServicesResponse);
+                break;
+            case Z_APDU_close:
+                printf("Target has closed the association.\n");
+                process_close(apdu->u.close);
+                break;
+            case Z_APDU_resourceControlRequest:
+                process_resourceControlRequest
+                    (apdu->u.resourceControlRequest);
+                break;
+            case Z_APDU_deleteResultSetResponse:
+                process_deleteResultSetResponse(apdu->u.
+                                                deleteResultSetResponse);
+                break;
+            default:
+                printf("Received unknown APDU type (%d).\n", 
+                       apdu->which);
+                close_session ();
+            }
+        }
+#if HAVE_XML2
+        else if (gdu->which == Z_GDU_HTTP_Response)
+        {
+            http_response(gdu->u.HTTP_Response);
+        }
+#endif
+        if (conn && !cs_more(conn))
+            break;
+    } // while conn
+    if (conn)
+    {
+#if HAVE_GETTIMEOFDAY
+        gettimeofday (&tv_end, 0);
+#if 0
+        printf ("S/U S/U=%ld/%ld %ld/%ld",
+                (long) tv_start.tv_sec,
+                (long) tv_start.tv_usec,
+                (long) tv_end.tv_sec,
+                (long) tv_end.tv_usec);
+#endif
+        printf ("Elapsed: %.6f\n",
+                (double) tv_end.tv_usec / 1e6 + tv_end.tv_sec -
+                ((double) tv_start.tv_usec / 1e6 + tv_start.tv_sec));
+#endif
+    }
+    xfree (netbuffer);
+}
+
+
+int cmd_cclparse(const char* arg) 
+{
+    int error, pos;
+    struct ccl_rpn_node *rpn=NULL;
+    
+    
+    rpn = ccl_find_str (bibset, arg, &error, &pos);
+    
+    if (error) {
+        printf ("%*s^ - ", 3+strlen(last_cmd)+1+pos, " ");
+        printf ("%s\n", ccl_err_msg (error));
+    }
+    else
+    {
+        if (rpn)
+        {	
+            ccl_pr_tree(rpn, stdout); 
+        }
+    }
+    if (rpn)
+        ccl_rpn_delete(rpn);
+    
+    printf ("\n");
+    
+    return 0;
+}
+
+
+int cmd_set_otherinfo(const char* args)
+{
+    char oidstr[101], otherinfoString[101];
+    int otherinfoNo;
+    int sscan_res;
+    int oidval;
+    
+    sscan_res = sscanf (args, "%d %100[^ ] %100s", &otherinfoNo, oidstr, otherinfoString);
+    if (sscan_res==1) {
+        /* reset this otherinfo */
+        if(otherinfoNo>=maxOtherInfosSupported) {
+            printf("Error otherinfo index to large (%d>%d)\n",
+		   otherinfoNo,maxOtherInfosSupported);
+        }
+        extraOtherInfos[otherinfoNo].oidval = -1;
+        if (extraOtherInfos[otherinfoNo].value)
+	    free(extraOtherInfos[otherinfoNo].value);	 		
+        return 0;
+    }
+    if (sscan_res<3) {
+        printf("Error in set_otherinfo command \n");
+        return 0;
+    }
+    
+    if (otherinfoNo>=maxOtherInfosSupported) {
+        printf("Error otherinfo index to large (%d>%d)\n",
+	       otherinfoNo,maxOtherInfosSupported);
+    }
+    
+    oidval = oid_getvalbyname (oidstr);
+    if (oidval == -1 ) {
+        printf("Error in set_otherinfo command unknown oid %s \n",oidstr);
+        return 0;
+    }
+    extraOtherInfos[otherinfoNo].oidval = oidval;
+    if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value);
+    extraOtherInfos[otherinfoNo].value = strdup(otherinfoString);
+    
+    return 0;
+}
+
+int cmd_sleep(const char* args ) 
+{
+    int sec=atoi(args);
+    if( sec > 0 ) {
+#ifdef WIN32
+	Sleep(sec*1000);
+#else
+	sleep(sec);
+#endif
+	printf("Done sleeping %d seconds\n", sec);	
+    }
+    return 1;    
+}
+
+int cmd_list_otherinfo(const char* args)
+{
+    int i;	   
+    
+    if(strlen(args)>0) {
+        i = atoi(args);
+        if( i >= maxOtherInfosSupported ) {
+            printf("Error otherinfo index to large (%d>%d)\n",i,maxOtherInfosSupported);
+            return 0;
+        }
+
+        if(extraOtherInfos[i].oidval != -1) 
+            printf("  otherinfo %d %s %s\n",
+                   i,
+                   yaz_z3950_oid_value_to_str(
+                       (enum oid_value) extraOtherInfos[i].oidval,
+                       CLASS_RECSYN),
+                   extraOtherInfos[i].value);
+        
+    } else {		
+        for(i=0; i<maxOtherInfosSupported; ++i) {
+            if(extraOtherInfos[i].oidval != -1) 
+                printf("  otherinfo %d %s %s\n",
+                       i,
+                       yaz_z3950_oid_value_to_str(
+                           (enum oid_value) extraOtherInfos[i].oidval,
+                           CLASS_RECSYN),
+                       extraOtherInfos[i].value);
+        }
+        
+    }
+    return 0;
+}
+
+
+int cmd_list_all(const char* args) {
+    int i;
+    
+    /* connection options */
+    if(conn) {
+        printf("Connected to         : %s\n",last_open_command);
+    } else {
+        if(last_open_command) 
+            printf("Not connected to     : %s\n",last_open_command);
+        else 
+            printf("Not connected        : \n");
+        
+    }
+    if(yazProxy) printf("using proxy          : %s\n",yazProxy);		
+    
+    printf("auto_reconnect       : %s\n",auto_reconnect?"on":"off");
+    
+    if (!auth) {
+        printf("Authentication       : none\n");
+    } else {
+        switch(auth->which) {
+        case Z_IdAuthentication_idPass:
+            printf("Authentication       : IdPass\n"); 
+            printf("    Login User       : %s\n",auth->u.idPass->userId?auth->u.idPass->userId:"");
+            printf("    Login Group      : %s\n",auth->u.idPass->groupId?auth->u.idPass->groupId:"");
+            printf("    Password         : %s\n",auth->u.idPass->password?auth->u.idPass->password:"");
+            break;
+        case Z_IdAuthentication_open:
+            printf("Authentication       : psOpen\n");			
+            printf("    Open string      : %s\n",auth->u.open);	
+            break;
+        default:
+            printf("Authentication       : Unknown\n");
+        }
+    }
+    if (negotiationCharset)
+        printf("Neg. Character set   : `%s'\n", negotiationCharset);
+    
+    /* bases */
+    printf("Bases                : ");
+    for (i = 0; i<num_databaseNames; i++) printf("%s ",databaseNames[i]);
+    printf("\n");
+    
+    /* Query options */
+    printf("CCL file             : %s\n",ccl_fields);
+    printf("CQL file             : %s\n",cql_fields);
+    printf("Query type           : %s\n",query_type_as_string(queryType));
+    
+    printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
+    
+    /* piggy back options */
+    printf("ssub/lslb/mspn       : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber);
+    
+    /* print present related options */
+    printf("Format               : %s\n",yaz_z3950_oid_value_to_str(recordsyntax,CLASS_RECSYN));
+    printf("Schema               : %s\n",record_schema ? record_schema : "not set");
+    printf("Elements             : %s\n",elementSetNames?elementSetNames->u.generic:"");
+    
+    /* loging options */
+    printf("APDU log             : %s\n",apdu_file?"on":"off");
+    printf("Record log           : %s\n",marc_file?"on":"off");
+    
+    /* other infos */
+    printf("Other Info: \n");
+    cmd_list_otherinfo("");
+    
+    return 0;
+}
+
+int cmd_clear_otherinfo(const char* args) 
+{
+    if(strlen(args)>0) {
+        int otherinfoNo;
+        otherinfoNo = atoi(args);
+        if( otherinfoNo >= maxOtherInfosSupported ) {
+            printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
+            return 0;
+        }
+        
+        if(extraOtherInfos[otherinfoNo].oidval != -1) {			
+            /* only clear if set. */
+            extraOtherInfos[otherinfoNo].oidval=-1;
+            free(extraOtherInfos[otherinfoNo].value);
+        }
+    } else {
+        int i;
+	
+        for(i=0; i<maxOtherInfosSupported; ++i) {
+            if (extraOtherInfos[i].oidval!=-1 ) {				
+                extraOtherInfos[i].oidval=-1;
+                free(extraOtherInfos[i].value);
+            }
+        }
+    }
+    return 0;
+}
+
+static int cmd_help (const char *line);
+
+typedef char *(*completerFunctionType)(const char *text, int state);
+
+static struct {
+    char *cmd;
+    int (*fun)(const char *arg);
+    char *ad;
+    completerFunctionType rl_completerfunction;
+    int complete_filenames;
+    char **local_tabcompletes;
+} cmd_array[] = {
+    {"open", cmd_open, "('tcp'|'ssl')':<host>[':'<port>][/<db>]",NULL,0,NULL},
+    {"quit", cmd_quit, "",NULL,0,NULL},
+    {"find", cmd_find, "<query>",NULL,0,NULL},
+    {"delete", cmd_delete, "<setname>",NULL,0,NULL},
+    {"base", cmd_base, "<base-name>",NULL,0,NULL},
+    {"show", cmd_show, "<rec#>['+'<#recs>['+'<setname>]]",NULL,0,NULL},
+    {"scan", cmd_scan, "<term>",NULL,0,NULL},
+    {"scanstep", cmd_scanstep, "<size>",NULL,0,NULL},
+    {"scanpos", cmd_scanpos, "<size>",NULL,0,NULL},
+    {"scansize", cmd_scansize, "<size>",NULL,0,NULL},
+    {"sort", cmd_sort, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
+    {"sort+", cmd_sort_newset, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
+    {"authentication", cmd_authentication, "<acctstring>",NULL,0,NULL},
+    {"lslb", cmd_lslb, "<largeSetLowerBound>",NULL,0,NULL},
+    {"ssub", cmd_ssub, "<smallSetUpperBound>",NULL,0,NULL},
+    {"mspn", cmd_mspn, "<mediumSetPresentNumber>",NULL,0,NULL},
+    {"status", cmd_status, "",NULL,0,NULL},
+    {"setnames", cmd_setnames, "",NULL,0,NULL},
+    {"cancel", cmd_cancel, "",NULL,0,NULL},
+    {"cancel_find", cmd_cancel_find, "<query>",NULL,0,NULL},
+    {"format", cmd_format, "<recordsyntax>",complete_format,0,NULL},
+    {"schema", cmd_schema, "<schema>",complete_schema,0,NULL},
+    {"elements", cmd_elements, "<elementSetName>",NULL,0,NULL},
+    {"close", z_cmd_close, "",NULL,0,NULL},
+    {"attributeset", cmd_attributeset, "<attrset>",complete_attributeset,0,NULL},
+    {"querytype", cmd_querytype, "<type>",complete_querytype,0,NULL},
+    {"refid", cmd_refid, "<id>",NULL,0,NULL},
+    {"itemorder", cmd_itemorder, "ill|item <itemno>",NULL,0,NULL},
+    {"update", cmd_update, "<action> <recid> [<file>]",NULL,0,NULL},
+    {"update0", cmd_update0, "<action> <recid> [<file>]",NULL,0,NULL},
+    {"packagename", cmd_packagename, "<packagename>",NULL,0,NULL},
+    {"proxy", cmd_proxy, "[('tcp'|'ssl')]<host>[':'<port>]",NULL,0,NULL},
+    {"charset", cmd_charset, "<nego_charset> <output_charset>",NULL,0,NULL},
+    {"negcharset", cmd_negcharset, "<nego_charset>",NULL,0,NULL},
+    {"displaycharset", cmd_displaycharset, "<output_charset>",NULL,0,NULL},
+    {"marccharset", cmd_marccharset, "<charset_name>",NULL,0,NULL},
+    {"lang", cmd_lang, "<language_code>",NULL,0,NULL},
+    {".", cmd_source_echo, "<filename>",NULL,1,NULL},
+    {"!", cmd_subshell, "Subshell command",NULL,1,NULL},
+    {"set_apdufile", cmd_set_apdufile, "<filename>",NULL,1,NULL},
+    {"set_berfile", cmd_set_berfile, "<filename>",NULL,1,NULL},
+    {"set_marcdump", cmd_set_marcdump," <filename>",NULL,1,NULL},
+    {"set_cclfile", cmd_set_cclfile," <filename>",NULL,1,NULL},
+    {"set_cqlfile", cmd_set_cqlfile," <filename>",NULL,1,NULL},
+    {"set_auto_reconnect", cmd_set_auto_reconnect," on|off",complete_auto_reconnect,1,NULL},
+    {"set_otherinfo", cmd_set_otherinfo,"<otherinfoinddex> <oid> <string>",NULL,0,NULL},
+    {"sleep", cmd_sleep,"<seconds>",NULL,0,NULL},
+    {"register_oid", cmd_register_oid,"<name> <class> <oid>",NULL,0,NULL},
+    {"push_command", cmd_push_command,"<command>",command_generator,0,NULL},
+    {"register_tab", cmd_register_tab,"<commandname> <tab>",command_generator,0,NULL},
+    {"cclparse", cmd_cclparse,"<ccl find command>",NULL,0,NULL},
+    {"list_otherinfo",cmd_list_otherinfo,"[otherinfoinddex]",NULL,0,NULL},
+    {"list_all",cmd_list_all,"",NULL,0,NULL},
+    {"clear_otherinfo",cmd_clear_otherinfo,"",NULL,0,NULL},
+    /* Server Admin Functions */
+    /*    {"adm-reindex", cmd_adm_reindex, "<database-name>",NULL,0,NULL},
+    {"adm-truncate", cmd_adm_truncate, "('database'|'index')<object-name>",NULL,0,NULL},
+    {"adm-create", cmd_adm_create, "",NULL,0,NULL},
+    {"adm-drop", cmd_adm_drop, "('database'|'index')<object-name>",NULL,0,NULL},
+    {"adm-import", cmd_adm_import, "<record-type> <dir> <pattern>",NULL,0,NULL},
+    {"adm-refresh", cmd_adm_refresh, "",NULL,0,NULL},
+    {"adm-commit", cmd_adm_commit, "",NULL,0,NULL},
+    {"adm-shutdown", cmd_adm_shutdown, "",NULL,0,NULL},
+    {"adm-startup", cmd_adm_startup, "",NULL,0,NULL},*/
+    {"explain", cmd_explain, "", NULL, 0, NULL},
+    {"options", cmd_options, "", NULL, 0, NULL},
+    {"zversion", cmd_zversion, "", NULL, 0, NULL},
+    {"help", cmd_help, "", NULL,0,NULL},
+    {"init", cmd_init, "", NULL,0,NULL},
+    {0,0,0,0,0,0}
+};
+
+static int cmd_help (const char *line)
+{
+    int i;
+    char topic[21];
+    
+    *topic = 0;
+    sscanf (line, "%20s", topic);
+
+    if (*topic == 0)
+        printf("Commands:\n");
+    for (i = 0; cmd_array[i].cmd; i++)
+        if (*topic == 0 || strcmp (topic, cmd_array[i].cmd) == 0)
+            printf("   %s %s\n", cmd_array[i].cmd, cmd_array[i].ad);
+    if (strcmp (topic, "find") == 0)
+    {
+        printf ("RPN:\n");
+        printf (" \"term\"                        Simple Term\n");
+        printf (" @attr [attset] type=value op  Attribute\n");
+        printf (" @and opl opr                  And\n");
+        printf (" @or opl opr                   Or\n");
+        printf (" @not opl opr                  And-Not\n");
+        printf (" @set set                      Result set\n");
+        printf ("\n");
+        printf ("Bib-1 attribute types\n");
+        printf ("1=Use:         ");
+        printf ("4=Title 7=ISBN 8=ISSN 30=Date 62=Abstract 1003=Author 1016=Any\n");
+        printf ("2=Relation:    ");
+        printf ("1<   2<=  3=  4>=  5>  6!=  102=Relevance\n");
+        printf ("3=Position:    ");
+        printf ("1=First in Field  2=First in subfield  3=Any position\n");
+        printf ("4=Structure:   ");
+        printf ("1=Phrase  2=Word  3=Key  4=Year  5=Date  6=WordList\n");
+        printf ("5=Truncation:  ");
+        printf ("1=Right  2=Left  3=L&R  100=No  101=#  102=Re-1  103=Re-2\n");
+        printf ("6=Completeness:");
+        printf ("1=Incomplete subfield  2=Complete subfield  3=Complete field\n");
+    }
+    return 1;
+}
+
+int cmd_register_tab(const char* arg) {
+	
+    char command[101], tabargument[101];
+    int i;
+    int num_of_tabs;
+    char** tabslist;
+    
+    if (sscanf (arg, "%100s %100s", command, tabargument) < 1) {
+        return 0;
+    }
+    
+    /* locate the amdn in the list */
+    for (i = 0; cmd_array[i].cmd; i++) {
+        if (!strncmp(cmd_array[i].cmd, command, strlen(command))) {
+            break;
+        }
+    }
+    
+    if(!cmd_array[i].cmd) { 
+        fprintf(stderr,"Unknown command %s\n",command);
+        return 1;
+    }
+    
+        
+    if(!cmd_array[i].local_tabcompletes)
+        cmd_array[i].local_tabcompletes = (char **) calloc(1,sizeof(char**));
+    
+    num_of_tabs=0;		
+    
+    tabslist = cmd_array[i].local_tabcompletes;
+    for(;tabslist && *tabslist;tabslist++) {
+        num_of_tabs++;
+    }
+    
+    cmd_array[i].local_tabcompletes =  (char **)
+        realloc(cmd_array[i].local_tabcompletes,(num_of_tabs+2)*sizeof(char**));
+    tabslist=cmd_array[i].local_tabcompletes;
+    tabslist[num_of_tabs]=strdup(tabargument);
+    tabslist[num_of_tabs+1]=NULL;
+    return 1;
+}
+
+
+void process_cmd_line(char* line)
+{  
+    int i,res;
+    char word[32], arg[10240];
+    
+#if HAVE_GETTIMEOFDAY
+    gettimeofday (&tv_start, 0);
+#endif
+    
+    if ((res = sscanf(line, "%31s %10239[^;]", word, arg)) <= 0)
+    {
+        strcpy(word, last_cmd);
+        *arg = '\0';
+    }
+    else if (res == 1)
+        *arg = 0;
+    strcpy(last_cmd, word);
+    
+    /* removed tailing spaces from the arg command */
+    { 
+        char* p = arg;
+        char* lastnonspace=NULL;
+        
+        for(;*p; ++p) {
+            if(!isspace(*(unsigned char *) p)) {
+                lastnonspace = p;
+            }
+        }
+        if(lastnonspace) 
+            *(++lastnonspace) = 0;
+    }
+    
+    for (i = 0; cmd_array[i].cmd; i++)
+        if (!strncmp(cmd_array[i].cmd, word, strlen(word)))
+        {
+            res = (*cmd_array[i].fun)(arg);
+            break;
+        }
+    
+    if (!cmd_array[i].cmd) /* dump our help-screen */
+    {
+        printf("Unknown command: %s.\n", word);
+        printf("use help for list of commands\n");
+        /* cmd_help (""); */
+        res = 1;
+    }
+    
+    if(apdu_file) fflush(apdu_file);
+    
+    if (res >= 2)
+        wait_and_handle_response();
+    
+    if(apdu_file)
+        fflush(apdu_file);
+    if(marc_file)
+        fflush(marc_file);
+}
+
+
+char *command_generator(const char *text, int state) 
+{
+    static int idx; 
+    if (state==0) {
+        idx = 0;
+    }
+    for( ; cmd_array[idx].cmd; ++idx) {
+        if (!strncmp(cmd_array[idx].cmd,text,strlen(text))) {
+            ++idx;  /* skip this entry on the next run */
+            return strdup(cmd_array[idx-1].cmd);
+        }
+    }
+    return NULL;
+}
+
+
+/* 
+   This function only known how to complete on the first word
+*/
+char ** readline_completer(char *text, int start, int end) {
+#if HAVE_READLINE_READLINE_H
+
+	completerFunctionType completerToUse;
+	
+    if(start == 0) {
+#if HAVE_READLINE_RL_COMPLETION_MATCHES
+        char** res=rl_completion_matches(text,
+                                      command_generator); 
+#else
+        char** res=completion_matches(text,
+                                      (CPFunction*)command_generator); 
+#endif
+        rl_attempted_completion_over = 1;
+        return res;
+    } else {
+        char arg[10240],word[32];
+        int i=0 ,res;
+        if ((res = sscanf(rl_line_buffer, "%31s %10239[^;]", word, arg)) <= 0) {     
+            rl_attempted_completion_over = 1;
+            return NULL;
+        }
+        
+        for (i = 0; cmd_array[i].cmd; i++) {
+            if (!strncmp(cmd_array[i].cmd, word, strlen(word))) {
+                break;
+            }
+        }
+        
+        if(!cmd_array[i].cmd) return NULL;
+        
+        curret_global_list = cmd_array[i].local_tabcompletes;
+        
+        completerToUse = cmd_array[i].rl_completerfunction;
+        if(completerToUse==NULL)  /* if no pr. command completer is defined use the default completer */
+            completerToUse = default_completer;
+        
+        if(completerToUse) {
+#ifdef HAVE_READLINE_RL_COMPLETION_MATCHES
+            char** res=
+                rl_completion_matches(text,
+                                      completerToUse);
+#else
+            char** res=
+                completion_matches(text,
+                                   (CPFunction*)completerToUse);
+#endif
+            if(!cmd_array[i].complete_filenames) 
+                rl_attempted_completion_over = 1;
+            return res;
+        } else {
+            if(!cmd_array[i].complete_filenames) 
+                rl_attempted_completion_over = 1;
+            return 0;
+        }
+    }
+#else 
+    return 0;
+#endif 
+}
+
+
+static void client(void)
+{
+    char line[10240];
+
+    line[10239] = '\0';
+
+#if HAVE_GETTIMEOFDAY
+    gettimeofday (&tv_start, 0);
+#endif
+
+    while (1)
+    {
+        char *line_in = NULL;
+#if HAVE_READLINE_READLINE_H
+        if (isatty(0))
+        {
+            line_in=readline(C_PROMPT);
+            if (!line_in)
+                break;
+#if HAVE_READLINE_HISTORY_H
+            if (*line_in)
+                add_history(line_in);
+#endif
+            strncpy(line, line_in, 10239);
+            free (line_in);
+        }
+#endif 
+        if (!line_in)
+        {
+            char *end_p;
+            printf (C_PROMPT);
+            fflush(stdout);
+            if (!fgets(line, 10239, stdin))
+                break;
+            if ((end_p = strchr (line, '\n')))
+                *end_p = '\0';
+        }
+        process_cmd_line(line);
+    }
+}
+
+static void show_version(void)
+{
+    char vstr[20];
+
+    yaz_version(vstr, 0);
+    printf ("YAZ version: %s\n", YAZ_VERSION);
+    if (strcmp(vstr, YAZ_VERSION))
+	printf ("YAZ DLL/SO: %s\n", vstr);
+    exit(0);
+}
+
+/*
+int main(int argc, char **argv)
+{
+    char *prog = *argv;
+    char *open_command = 0;
+    char *auth_command = 0;
+    char *arg;
+    int ret;
+    
+#if HAVE_LOCALE_H
+    if (!setlocale(LC_CTYPE, ""))
+        fprintf (stderr, "setlocale failed\n");
+#endif
+#if HAVE_LANGINFO_H
+#ifdef CODESET
+    codeset = nl_langinfo(CODESET);
+#endif
+#endif
+    if (codeset)
+	outputCharset = xstrdup(codeset);
+    
+    ODR_MASK_SET(&z3950_options, Z_Options_search);
+    ODR_MASK_SET(&z3950_options, Z_Options_present);
+    ODR_MASK_SET(&z3950_options, Z_Options_namedResultSets);
+    ODR_MASK_SET(&z3950_options, Z_Options_triggerResourceCtrl);
+    ODR_MASK_SET(&z3950_options, Z_Options_scan);
+    ODR_MASK_SET(&z3950_options, Z_Options_sort);
+    ODR_MASK_SET(&z3950_options, Z_Options_extendedServices);
+    ODR_MASK_SET(&z3950_options, Z_Options_delSet);
+
+    while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2)
+    {
+        switch (ret)
+        {
+        case 0:
+            if (!open_command)
+            {
+                open_command = (char *) xmalloc (strlen(arg)+6);
+                strcpy (open_command, "open ");
+                strcat (open_command, arg);
+            }
+	    else
+	    {
+		fprintf(stderr, "%s: Specify at most one server address\n",
+			prog);
+		exit(1);
+	    }
+            break;
+	case 'd':
+	    dump_file_prefix = arg;
+	    break;
+        case 'k':
+            kilobytes = atoi(arg);
+            break;
+        case 'm':
+            if (!(marc_file = fopen (arg, "a")))
+            {
+                perror (arg);
+                exit (1);
+            }
+            break;
+        case 't':
+            outputCharset = xstrdup(arg);
+            break;
+        case 'c':
+            strncpy (ccl_fields, arg, sizeof(ccl_fields)-1);
+            ccl_fields[sizeof(ccl_fields)-1] = '\0';
+            break;
+        case 'q':
+            strncpy (cql_fields, arg, sizeof(cql_fields)-1);
+            cql_fields[sizeof(cql_fields)-1] = '\0';
+            break;
+        case 'b':
+            if (!strcmp(arg, "-"))
+                ber_file=stderr;
+            else
+                ber_file=fopen(arg, "a");
+            break;
+        case 'a':
+            if (!strcmp(arg, "-"))
+                apdu_file=stderr;
+            else
+                apdu_file=fopen(arg, "a");
+            break;
+	case 'x':
+	    hex_dump = 1;
+	    break;
+        case 'p':
+            yazProxy=strdup(arg);
+            break;
+        case 'u':
+            if (!auth_command)
+            {
+                auth_command = (char *) xmalloc (strlen(arg)+6);
+                strcpy (auth_command, "auth ");
+                strcat (auth_command, arg);
+            }
+            break;
+        case 'v':
+            yaz_log_init(yaz_log_mask_str(arg), "", 0);
+            break;
+        case 'V':
+	    show_version();
+            break;
+        default:
+            fprintf (stderr, "Usage: %s [-m <marclog>] [ -a <apdulog>] "
+                     "[-b berdump] [-c cclfields] \n"
+		     "[-q cqlfields] [-p <proxy-addr>] [-u <auth>] "
+                     "[-k size] [-d dump] [-V] [<server-addr>]\n",
+                     prog);
+            exit (1);
+        }      
+    }
+    z_initialize();
+    if (auth_command)
+    {
+#ifdef HAVE_GETTIMEOFDAY
+        gettimeofday (&tv_start, 0);
+#endif
+        process_cmd_line (auth_command);
+#if HAVE_READLINE_HISTORY_H
+        add_history(auth_command);
+#endif
+        xfree(auth_command);
+    }
+    if (open_command)
+    {
+#ifdef HAVE_GETTIMEOFDAY
+        gettimeofday (&tv_start, 0);
+#endif
+        process_cmd_line (open_command);
+#if HAVE_READLINE_HISTORY_H
+        add_history(open_command);
+#endif
+        xfree(open_command);
+    }
+    client ();
+    exit (0);
+}
+*/
+/*
+ * Local variables:
+ * tab-width: 8
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=8 fdm=marker
+ * vim<600: sw=4 ts=8
+ */
+
+static int z_getAPDU (Z_APDU **ret_apdu) {
+    int res;
+
+    char *netbuffer= 0;
+    int netbufferlen = 0;
+    Z_APDU *apdu;
+    Z_GDU *gdu;
+    
+    if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0) {
+	close_session();
+	perror("cs_get");
+	exit(1);
+    }
+    if (!res) {
+	printf("Target closed connection.\n");
+	close_session();
+	exit(1);
+    }
+    odr_reset(in); /* release APDU from last round */
+    record_last = 0;
+    do_hex_dump(netbuffer, res);
+    odr_setbuf(in, netbuffer, res, 0);
+    
+    if (!z_GDU(in, &gdu, 0, 0)) {
+	
+	odr_perror(in, "Decoding incoming APDU");
+	fprintf(stderr, "[Near %d]\n", odr_offset(in));
+	fprintf(stderr, "Packet dump:\n---------\n");
+	odr_dumpBER(stderr, netbuffer, res);
+	fprintf(stderr, "---------\n");
+	if (apdu_file) {
+	    z_GDU(print, &gdu, 0, 0);
+	    odr_reset(print);
+	}
+	xfree(netbuffer);
+	exit(1); /* was break */
+    }
+    xfree(netbuffer);
+    if (apdu_file && !z_GDU(print, &gdu, 0, 0)) {
+	
+	odr_perror(print, "Failed to print incoming APDU");
+	odr_reset(print);
+	return -1; /* was continue */
+    }
+    if (gdu->which == Z_GDU_Z3950) {
+        
+	Z_APDU *apdu = gdu->u.z3950;
+	(*ret_apdu)=apdu;
+	return 0;
+    }
+    /* else a differnet kind of response, which we don't handle */
+    return -1;
+}
+
+/* gets a generic response from the server */
+int z_getnextAPDU () {
+  Z_APDU *apdu;
+  apdu=NULL;
+  
+  z_getAPDU(&apdu);
+
+  switch(apdu->which) {
+      
+  case Z_APDU_initResponse:
+      // kjdon copying johnmcp
+      /* save session parameters for later use */
+      session_mem = odr_extract_mem(in);
+      session = apdu->u.initResponse;
+      // greenstone will call z_get_initResponse to get the info
+      // about what happened with the connection
+      //process_initResponse(apdu->u.initResponse);
+      break;
+  case Z_APDU_searchResponse:
+      process_searchResponse(apdu->u.searchResponse);
+      break;
+  case Z_APDU_scanResponse:
+      process_scanResponse(apdu->u.scanResponse);
+      break;
+  case Z_APDU_presentResponse:
+      print_refid (apdu->u.presentResponse->referenceId);
+      setno +=
+	  *apdu->u.presentResponse->numberOfRecordsReturned;
+      if (apdu->u.presentResponse->records) {
+	  display_records(apdu->u.presentResponse->records);
+	  printf ("nextResultSetPosition = %d\n",
+		  *apdu->u.presentResponse->nextResultSetPosition);
+      }
+      else {
+	  printf("No records.\n");
+      }
+      break;
+  case Z_APDU_sortResponse:
+      process_sortResponse(apdu->u.sortResponse);
+      break;
+  case Z_APDU_extendedServicesResponse:
+      printf("Got extended services response\n");
+      process_ESResponse(apdu->u.extendedServicesResponse);
+      break;
+  case Z_APDU_close:
+      printf("Target has closed the association.\n");
+      process_close(apdu->u.close);
+      break;
+  case Z_APDU_resourceControlRequest:
+      process_resourceControlRequest
+	  (apdu->u.resourceControlRequest);
+      break;
+  case Z_APDU_deleteResultSetResponse:
+      process_deleteResultSetResponse(apdu->u.
+				      deleteResultSetResponse);
+      break;
+  default:
+      printf("Received unknown APDU type (%d).\n", 
+	     apdu->which);
+      close_session ();
+  }
+  return 0;
+ 
+}
+
+/* returns number of records found */
+/*int z_cmd_dosearch(char *search){
+
+}
+*/
+/* returns an array of strings containing record titles. The first element
+   is an int, saying how many titles follow (can be zero). NULL on error. */
+/*char **z_getrecordTitles(int start,int count){
+
+}
+*/
+/* returns a string containing the whole marc record */
+/*char *z_getfullRecord(int record){
+
+}
+*/
+
+
+/* returns number found, arg is query string */
+int z_cmd_dosearch(char *arg)
+{
+  Z_APDU *apdu;
+  int matching;
+  int ret_val;
+  ret_val=send_searchRequest(arg);
+  if (ret_val==-1) {
+    /* prefix query error */
+    return (-1);
+  }
+  z_getAPDU(&apdu);
+  /* check return value??? */
+  if (apdu->which != Z_APDU_searchResponse)
+    {
+      printf("sendsearchRequest() was not replied with a searchResponse!\n");
+      return (-2);
+    }
+  /* let's keep going anyway... */
+  matching=process_searchResponse(apdu->u.searchResponse);
+  /* this assumes we want to take action on failure (-1) here rather than
+     whereever we were called from */
+  return (matching);
+}
+
+/* src must be NULL-terminated */
+static char *safeappendstr(char *dest, size_t *allocsize, 
+			  size_t *offset, char *src) {
+  /* allocsize is the amount of space currently allocated to dest,
+     offset is how far into dest we want to append src */
+  if (*offset + strlen(src) >= *allocsize) {
+    *allocsize+=strlen(src)+64; /* add some extra space to save on reallocs */
+    if ((dest=realloc(dest,*allocsize))==NULL) {
+      fprintf(stderr, "malloc failed while decoding marc record.\n");
+      return (NULL);
+    }
+  }
+  strcpy(dest+(*offset), src);
+  *offset+=strlen(src);
+  return (dest);
+}
+
+static char *getmarcfields(Z_External *p, int titleonly) {
+  /* this code is based on marc_display(), with a couple of lines taken from
+     display_record() in the original client. */
+  /* if titleonly is non-zero, we only get a (the?) title field, otherwise
+     return the whole thing */
+  const char *buf=(char *)p->u.octet_aligned->buf;
+  char *title;      /* our string to return */
+  int title_maxlen=0; /* amount of space currently allocated */
+  int title_curlen=0;    /* amount of space actually used */
+  int entry_p;
+  int record_length;
+  int indicator_length;
+  int identifier_length;
+  int base_address;
+  int length_data_entry;
+  int length_starting;
+  int length_implementation;
+  FILE *outf;
+
+  /*  if (!outf) */
+  outf = stdout;
+
+  /* initialise our title. Start with 64 chars */
+  title_maxlen=64;
+  if((title=malloc(title_maxlen))==NULL) {
+    /* not quite sure where stderr will be going, but.... */
+    fprintf(stderr,"Malloc failed while decoding marc record\n");
+    return NULL;
+  }
+
+  if (!titleonly) {
+    strcpy(title,"<table border=1>\n");
+    title_curlen=strlen(title);
+  }
+  else  title_curlen=0;
+
+  record_length = atoi_n (buf, 5);
+  if (record_length < 25)
+    return (NULL); /* -1 */
+  if (isdigit(buf[10]))
+    indicator_length = atoi_n (buf+10, 1);
+  else
+    indicator_length = 2;
+  if (isdigit(buf[11]))
+    identifier_length = atoi_n (buf+11, 1);
+  else
+    identifier_length = 2;
+  base_address = atoi_n (buf+12, 4);
+  
+  length_data_entry = atoi_n (buf+20, 1);
+  length_starting = atoi_n (buf+21, 1);
+  length_implementation = atoi_n (buf+22, 1);
+  
+  if (0) /* debug */
+    {
+      fprintf (outf, "Record length         %5d\n", record_length);
+      fprintf (outf, "Indicator length      %5d\n", indicator_length);
+      fprintf (outf, "Identifier length     %5d\n", identifier_length);
+      fprintf (outf, "Base address          %5d\n", base_address);
+      fprintf (outf, "Length data entry     %5d\n", length_data_entry);
+      fprintf (outf, "Length starting       %5d\n", length_starting);
+      fprintf (outf, "Length implementation %5d\n", length_implementation);
+    }
+  for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
+    {
+      entry_p += 3+length_data_entry+length_starting;
+      if (entry_p >= record_length)
+	return (NULL); /* -1 */
+    }
+  base_address = entry_p+1;
+  for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
+    {
+      int data_length;
+      int data_offset;
+      int end_offset;
+      int i /*, j*/ ;
+      int startofstring;
+      int whichtag;
+      short int abbrtitle=0;
+      char tag[4];
+      memcpy (tag, buf+entry_p, 3);
+      entry_p += 3;
+      tag[3] = '\0';
+      whichtag=atoi(tag);
+      if (0) /* debug */
+	fprintf (outf, "Tag: ");
+      /* johnmcp 
+	 Relevant tags (for "main entry") - assume only one of these is set,
+	 as we will only return the first one found.
+	 100=personal name
+	 110=corporate name
+	 111=meeting name
+	 130=uniform title
+	 subfields: $a=personal name, $c=title/other, $d=date
+
+	 210=Abbreviated Title
+	 222=Key title
+	 240=uniform title
+	 243=collective uniform title
+	 245=title statement
+	 246=varying form of title
+	 247=former title or title variations
+	 subfields: $a=abbrev. title $b=qualifying info $2=source
+	 .          $6=linkage $8=link field and sequence number.
+      */
+      /*fprintf (outf, "%s ", tag); */
+      data_length = atoi_n (buf+entry_p, length_data_entry);
+      entry_p += length_data_entry;
+      data_offset = atoi_n (buf+entry_p, length_starting);
+      entry_p += length_starting;
+      i = data_offset + base_address;
+      end_offset = i+data_length-1;
+
+      if (0) /* debug */
+	fprintf (outf, " Ind: ");
+
+      /* memcmp (tag,"00",2) is true, then we DON'T have a control tag
+	 ie less than 10. */
+      /*if (memcmp (tag, "00", 2) && indicator_length)
+	{
+	for (j = 0; j<indicator_length; j++)
+	fprintf (outf, "%c", buf[i++]);
+	}
+      */
+      if (whichtag>=10) i+=indicator_length;
+      /* the control fields (<10) don't have leading chars. */
+
+
+      if (0) /* debug */
+	fprintf (outf, " Fields: ");
+
+      /* If we only want the title, then skip other fields */
+      if (titleonly &&(whichtag<200||whichtag>249)) {
+	/* skip this record */
+	continue;
+	i=end_offset;
+      }
+      
+      /* either titleonly is 0, or titleonly is 1 and whichtag is between
+	 200 and 249... */
+      if (!titleonly) {
+	/* This should probably be read in from a separate file.
+	   This is also hard-coded for USMARC/MARC21 fieldnames!!! */
+
+	/* print out what kind of tag this is */
+	char *tagname;
+	char *field_control1="<tr><td>Marc Control Number:</td><td>";
+	char *field_control3="<tr><td>Marc Organisation Code:</td><td>";
+	char *field_control5="<tr><td>Marc record Date Info.:</td><td>";
+	char *field_control8="<tr><td>Control Field (Marc info):</td><td>";
+	char *field_loc_nr="<tr><td>Lib. of Congress control #:</td><td>";
+	char *field_issn="<tr><td>ISSN Number:</td><td>";
+	char *field_catalog="<tr><td>Catalog agency:</td><td>";
+	char *field_lang="<tr><td>3-letter language code(s):</td><td>";
+	char *field_areacode="<tr><td>Geographic Area code:</td><td>";
+	char *field_cn_loc="<tr><td>Lib. of Congress Call #:</td><td>";
+	char *field_cn_dewdec="<tr><td>Dewey Decimal Call #:</td><td>";
+	char *field_cn_other="<tr><td>Other Classification Info.:</td><td>";
+	char *field_cn_gd="<tr><td>Govt. Document Call #:</td><td>";
+	char *field_personalname="<tr><td>Personal Name:</td><td>";
+	char *field_meetingname="<tr><td>Meeting Name:</td><td>";
+	char *field_maintitle="<tr><td>Title:</td><td>";
+	char *field_edition="<tr><td><Edition Information:</td><td>";
+	char *field_publication="<tr><td>Publication Info.:</td><td>";
+	char *field_physdesc="<tr><td>Physical Description:</td><td>";
+	char *field_series_title="<tr><td>Series Title:</td><td>";
+	char *field_series_statement="<tr><td>Series Statement:</td><td>";
+	char *field_note_general="<tr><td>Note:</td><td>";
+	char *field_note_bib="<tr><td>Bibliographic Note:</td><td>";
+	char *field_note_summary="<tr><td>Summary Note:</td><td>";
+	char *field_sub_note="<tr><td>Subject Notes:</td><td>";
+	char *field_sub_topic="<tr><td>Subject - Topic:</td><td>";
+	char *field_sub_geog="<tr><td>Subject - Location:</td><td>";
+	char *field_added_persname="<tr><td>Author Note - Name:</td><td>";
+	char *field_added_corpname="<tr><td>Author - Organisation:</td><td>";
+	char *field_uniform_title="<tr><td>Extra Title Information:</td><td>";
+	char *field_host_item="<tr><td>In:</td><td>";
+	char *field_series_corpname="<tr><td>Series - Organisation:</td><td>";
+	/*char *field_url="<tr><td>Electronic Access</td><td><a href=\"";*/
+	char *field_url="<tr><td>Electronic Access</td><td>";
+	/*char *field_other="<tr><td>(other field):</td><td>";*/
+	char tag_num[100];
+	switch (whichtag) {
+	case (1): {tagname=field_control1;break;}
+	case (3): {tagname=field_control3;break;}
+	case (5): {tagname=field_control5;break;}
+	case (8): {tagname=field_control8;break;}
+	case (10): {tagname=field_loc_nr;break;}
+	case (20): {tagname=field_issn;break;}
+	case (40): {tagname=field_catalog;break;}
+	case (41): {tagname=field_lang;break;}
+	case (43): {tagname=field_areacode;break;}
+	case (50): {tagname=field_cn_loc;break;}
+	case (82): {tagname=field_cn_dewdec;break;}
+	case (84): {tagname=field_cn_other;break;}
+	case (86): {tagname=field_cn_gd;break;}
+	case (100): {tagname=field_personalname;break;}
+	case (111): {tagname=field_meetingname;break;}
+	case (245): {tagname=field_maintitle;break;}
+	case (250): {tagname=field_edition;break;}
+	case (260): {tagname=field_publication;break;}
+	case (300): {tagname=field_physdesc;break;}
+	case (440): {tagname=field_series_title;break;}
+	case (490): {tagname=field_series_statement;break;}
+	case (500): {tagname=field_note_general;break;}
+	case (504): {tagname=field_note_bib;break;}
+	case (520): {tagname=field_note_summary;break;}
+	case (630): {tagname=field_sub_note;break;}
+	case (650): {tagname=field_sub_topic;break;}
+	case (651): {tagname=field_sub_geog;break;}
+	case (700): {tagname=field_added_persname;break;}
+	case (710): {tagname=field_added_corpname;break;}
+	case (730): {tagname=field_uniform_title;break;}
+	case (773): {tagname=field_host_item;break;}
+	case (810): {tagname=field_series_corpname;break;}
+	case (856): {tagname=field_url;break;}
+	default: 
+	  if (whichtag>=90&&whichtag<=99)
+	    tagname="<tr><td>Local Call #:</td><td>";
+	  else {
+	    /*tagname=field_other;*/
+	    
+	    /* The trailing '\0' seems to be necessary...?!? */
+	    sprintf(tag_num,"<tr><td>(field %d)</td><td>\n\0",whichtag);
+	    tagname=tag_num;
+	  }
+	}
+
+	/* add the field type */
+	title=safeappendstr(title, &title_maxlen, &title_curlen, tagname);
+      }
+
+      /* go through current field in current record */
+      while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset)
+	{
+	  /* this loop goes through byte by byte, to find end-of-field or
+	     end-of-record separator tags */
+
+	  /*	  if (memcmp (tag, "00", 2) && identifier_length)*/
+	  if ( ((titleonly==1&&whichtag==245)||(titleonly==0))
+	       && identifier_length)
+	    {
+	      if (buf[i]==ISO2709_IDFS) {
+		/*  this implies sub-fields for this field */
+		/* skip sub-field tag, but wrap $a sub-field with <b> & </b> 
+		   for HTML if titleonly==1 */
+		if (buf[i+1]=='a' && titleonly) {
+		  abbrtitle=1;
+		  title=safeappendstr(title,&title_maxlen,&title_curlen,"<b>");
+		}
+		i+=identifier_length; 
+	      }
+	      /* find end of this (sub-)field */
+	      startofstring=i;
+	      while (buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
+		     buf[i] != ISO2709_FS && i < end_offset)
+		i++;
+		/*fprintf (outf, "%c", buf[i++]);*/
+
+	      /* this only happens the first time around */
+	      if (i==startofstring) continue;
+
+	      /* now put from $startofstring$ until $i$ into our 
+		 string to return */
+
+	      if (title_curlen+(i-startofstring)>=title_maxlen) {
+		/* we need to make more room - add max(64,needed) bytes */
+		if (title_curlen+(i-startofstring)-title_maxlen>63)
+		  title_maxlen+=(i-startofstring)+1;/*+1 for trailing \0 */
+		else
+		  title_maxlen+=64;
+		if ((title=realloc(title,title_maxlen))==NULL) {
+		  fprintf(stderr,"malloc failed decoding marc record\n");
+		  return (NULL);
+		}
+	      }
+
+	      strncpy(title+title_curlen,buf+startofstring,i-startofstring);
+	      title_curlen+=(i-startofstring);
+	      *(title+title_curlen)='\0';
+	      /* overwrite ISO2709_?S with \0 */
+	      /* *(title+title_curlen+i-startofstring)='\0';*/
+	      /*safeappendstr(title,&title_maxlen,
+		&title_curlen,buf+startofstring);*/
+
+	      /* if 'main' title, close HTML </B> tag */
+	      if (abbrtitle) {
+		title=safeappendstr(title,&title_maxlen,&title_curlen,"</b>");
+		abbrtitle=0;
+	      } 
+	      /* end of this marc sub--field                 
+		 buf[i]==ISO2709_RS if end of  record.
+		 buf[i]==ISO2709_FS if end of  field,
+		 buf[i]==ISO2709_IDFS if end of sub-field. */
+	      if (titleonly==0) {
+		if (buf[i]==ISO2709_IDFS) {
+		  /* there is a following sub-field, so only add whitespace */
+		  title=safeappendstr(title,&title_maxlen,&title_curlen," ");
+		}
+		else {
+		  /* end of record, so close HTML table row */
+#if 0
+		  if (whichtag==856) /* special case: this is a url! */
+		    title=safeappendstr(title,&title_maxlen,&title_curlen,
+					"\">Uniform Resource Locator</a>");
+#endif
+		  title=safeappendstr(title, &title_maxlen, &title_curlen,
+				      "</td></tr>\n");
+		}
+	      }  /* end of  if (titleonly==0)                */
+	    }    /* end of  if ( ((titleonly==1&&.......     */
+	  else { /* whichtag not matched  (if titleonly)     */
+	    /*fprintf (outf, "%c", buf[i++]);*/
+	    /* skip this char for now - should ideally skip whole field */
+	    i++;
+	  }
+	} /* end of while loop - at end of field or record */
+      /*fprintf (outf, "\n");*/
+      /*if (i < end_offset)
+	fprintf (outf, "-- separator but not at end of field\n");
+	if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
+	fprintf (outf, "-- no separator at end of field\n");
+      */
+    }
+
+  /* at end of whole record. */
+  if (!titleonly) 
+    title=safeappendstr(title, &title_maxlen, &title_curlen, "</table>\n");
+  return (title);
+
+}
+static char *getrecordtitle(Z_External *record) {
+  return(getmarcfields(record,1));
+}
+
+char *z_getfullRecord(int start) {
+  Z_APDU *apdu;
+  Z_External *record;
+  char *fullrecord;
+
+  fullrecord=NULL;
+  z_send_getbriefrecords(start,1,1); /* for now, use getbriefrecords */
+  z_getAPDU(&apdu);
+  
+  if (apdu->which != Z_APDU_presentResponse) {
+    /* out-of-order packets.... */
+    fprintf(stderr,"Not a present response to present request\n");
+    return NULL;
+  }
+  
+  /* just do some double checking */
+  if (*apdu->u.presentResponse->numberOfRecordsReturned!=1 ||
+      !apdu->u.presentResponse->records ) {
+    /* something bad has happened... johnmcp */
+    fprintf (stderr,"Error occured in yaz while getting record (GSDL)\n");
+  }
+
+  switch (apdu->u.presentResponse->records->which) {
+  case Z_Records_DBOSD: break; /* do nothing - this is a record */
+
+    /* otherwise we got a diagnostic message... */
+  case Z_Records_NSD:
+    switch (apdu->u.presentResponse->records->u.nonSurrogateDiagnostic->which){
+      /* these are both represented as char* in yaz, so either is OK */
+      /*    case Z_DefaultDiagForm_v2Addinfo:
+	    case Z_DefaultDiagForm_v3Addinfo: --wrong names*/
+    case Z_DiagnosticFormat_s_defaultDiagRec:
+    case Z_DiagnosticFormat_s_explicitDiagnostic:
+      return apdu->u.presentResponse->records->u.nonSurrogateDiagnostic
+	->u.v2Addinfo; /* return the error message */
+    default: ; /* this should happen for v3 of the spec... */
+    }
+  case Z_Records_multipleNSD: ; /* should handle one day... */
+  }
+
+  record=apdu->u.presentResponse->records->u.databaseOrSurDiagnostics->
+    records[0]->u.databaseRecord;
+
+  return(getmarcfields(record,0));
+}
+
+char **z_getrecordTitles(/*int resultset,*/ int starting, int howmany) {
+  Z_APDU *apdu;
+  char **titles;
+  /* titles will be an array of strings. The first element will really
+     be an int, saying how many strings follow. Ie 1st string in titles[1] */
+  int count;
+  int i;
+
+  titles=NULL;
+
+  /* WE SHOULD SET THE RECORD FORMAT SOMEWHERE..... USMARC (default)
+     but may also want SUTRS support. */
+
+  /* sends a presentRequest - we need to know the set number - johnmcp */
+  z_send_getbriefrecords(starting, 1, howmany);
+  /* CHECK RETURN VALUE (currently only 0) */
+  
+
+  z_getAPDU(&apdu);
+  if (apdu->which != Z_APDU_presentResponse) {
+    /* we got out-of-sync.... */
+    fprintf(stderr,"Not a present response to present request\n");
+    return NULL;
+  }
+  
+  print_refid (apdu->u.presentResponse->referenceId);
+  count=*apdu->u.presentResponse->numberOfRecordsReturned;
+  setno += count;
+  if (apdu->u.presentResponse->records) {
+    /* extract the titles from each record, and return it as an array of 
+       strings */
+    if (count==1 &&
+	apdu->u.presentResponse->records->which!=Z_Records_DBOSD) {
+	fprintf(stderr,"only one returned");
+      /* apdu->u.presentResponse->records->which gives a type:
+	 1=databaseOrSurDiagnostics | 2=nonSurrogateDiagnostic | 
+	 3=multipleNonSurDiagnostics.
+	 Type 1 implies it is a database record. FROM z-core.h,
+      NOT prt-proto.h!!!!! */
+
+      /* for type 2:
+	 apdu->u.presentResponse->records->u.nonSurrogateDiagnostic->which:
+	 1=v2Addinfo | 2=v3Addinfo, and u.v[23]Addinfo is a (char *).
+
+      */
+      titles=malloc(sizeof(char *));
+      titles[0]=(char *)0;
+      /* could put error message and titles[1], and set titles[0] to -1. */
+
+
+    }
+    else /* more than 1 rec returned, so ?has? to be db records (dblcheck!!)*/
+      {
+	  fprintf(stderr,"more than one rec returned, count=%d\n", count);
+	titles=malloc(sizeof(char *)*count + 1);
+	/* check malloc succeeded... */
+	titles[0]=(char *)count;
+	for (i=1;i<=count;i++) {
+	  titles[i]=getrecordtitle(apdu->u.presentResponse->records->
+				   u.databaseOrSurDiagnostics->records[i-1]->
+				   u.databaseRecord);
+	}
+      } 
+  }
+  else {
+      fprintf(stderr,"no records retruend");
+    titles=malloc(sizeof(char *));
+    titles[0]=(char *)0;
+  }
+  /*  printf ("nextResultSetPosition = %d\n",
+   *apdu->u.presentResponse->nextResultSetPosition); */
+  return (titles);
+}
+
Index: /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/yaz_zclient.h	(revision 16561)
@@ -0,0 +1,32 @@
+#ifndef YAZ_ZCLIENT_H
+#define YAZ_ZCLIENT_H
+/*#include "../include/yaz/z-core.h"*/
+
+
+/* the new yaz_zclient made for use with Greenstone. Based on client.c 
+ * (version 2.1.4) and the old yaz_zclient (version 1.6 for greenstone)
+ */
+void z_initialize(void);
+
+/* creates a connection, given a host and port, and database name */
+int z_cmd_open(char *host_and_port, char *base);
+
+/* returns number of records found */
+int z_cmd_dosearch(char *search);
+
+/* returns an array of strings containing record titles. The first element
+   is an int, saying how many titles follow (can be zero). NULL on error. */
+char **z_getrecordTitles(int start,int count);
+
+/* returns a string containing the whole marc record */
+char *z_getfullRecord(int record);
+
+/* close the connection */
+int z_cmd_close(const char *arg);
+
+/* returns a string containing name, ID, etc, or NULL if not connected */
+char *z_get_initResponse(); 
+
+/* gets a generic response from the server */
+int z_getnextAPDU ();
+#endif 
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.cpp	(revision 16561)
@@ -0,0 +1,345 @@
+/**********************************************************************
+ *
+ * z3950_to_gsdl.cpp --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "z3950parser.h"
+#include "z3950server.h"
+#include "z3950_to_gsdl.h"
+
+// converts a char* to uppercase, I wouldn't be
+// writing this if I could find an existing
+// function to do it
+void toUpperString(char *&text)
+{
+  for (int i = 0; text[i] != '\0'; i++) {
+    text[i] = toupper(text[i]);
+  }
+}
+
+
+text_t gsdlCollection::getName() 
+{
+  return name;
+}
+
+bool gsdlCollection::z3950Capeable() const
+{
+  return z3950ready;
+}
+
+
+bool gsdlCollection::bib1_to_dc_mapping(text_t& filename)
+{
+    ifstream inFile;
+    int input_Bib1;
+    text_t input_DCES;
+    text_t input_Short;
+
+    char* filename_cstr = filename.getcstr();
+    inFile.open(filename_cstr, ios::in);
+    delete [] filename_cstr;
+
+    if (!inFile) {
+      return false;
+    }
+
+    const int BUFFERSIZE = 1024;
+    char buffer[BUFFERSIZE];
+
+    // read in pairs of values (mappings from Bib-1 to DCES)
+    while (!inFile.eof()) {
+      char input_DCES_tmp[BUFFERSIZE];
+
+      inFile.getline(buffer, BUFFERSIZE, '\n');
+      if (buffer[0] == '#') {
+	// ignore comments (# ...)
+	continue;
+      }
+
+      sscanf(buffer,"%d %s",&input_Bib1, input_DCES_tmp);
+
+      input_DCES = input_DCES_tmp;
+
+      Bib1_to_DCES[input_Bib1] = input_DCES;
+
+      if (z3950_verbosity_>2) {
+	cerr << "(" << input_Bib1 << "/" << input_DCES << ") ";
+      }
+    }
+
+    if (z3950_verbosity_>2) {
+      cerr << endl;
+    }
+    inFile.close();
+
+    return true;
+}
+
+
+bool gsdlCollection::dc_to_short_mapping(text_t& filename, 
+					 text_t& collectionName) 
+{
+    ifstream inFileBuild;
+    text_t input_DCES;
+    text_t input_Short;
+  
+    char* filename_cstr = filename.getcstr();
+    inFileBuild.open(filename_cstr, ios::in);
+    delete [] filename_cstr;
+
+    if (!inFileBuild) {
+      if (z3950_verbosity_>2) {
+	cerr << "Warning: Unable to open: " << filename << endl;
+	cerr << "         Collection " << collectionName << " might not be built" << endl;
+      }
+      return false;
+    }
+
+    const int BUFFERSIZE = 1024;
+    char buffer[BUFFERSIZE];
+
+    // find the indexfieldmap line
+    do {
+      inFileBuild >> buffer;
+      if (inFileBuild.eof()) {
+	if (z3950_verbosity_>1) {
+	  cerr << "Warning: Did not find indexfieldmap in build.cfg for " 
+	       << collectionName << endl;
+	}
+	return false;
+      }
+    } while (strcmp(buffer, "indexfieldmap") != 0);
+
+    // at this stage, buffer contains "indexfieldmap",
+    // if the file didn't contain it, would have returned above
+    inFileBuild >> buffer;
+    while (strstr(buffer, "->") != NULL) {
+      // buffer contains a DCES->Short mapping
+      input_DCES.clear();
+      input_Short.clear();
+
+      // copy in DCES
+      int i;
+      for (i = 0; buffer[i] != '-'; i++) {
+	input_DCES.appendcarr(&(buffer[i]), 1);
+      }
+
+      // skip over "->"
+      i+=2;
+
+      // copy in Short
+      for (; isalpha(buffer[i]); i++) {
+	input_Short.appendcarr(&(buffer[i]), 1);
+      }
+
+      // add to map
+      DCES_to_Short[input_DCES] = input_Short;
+
+      if (z3950_verbosity_>1) {
+	cerr << "<" << input_DCES << "/" << input_Short << "> ";
+      }
+
+      inFileBuild >> buffer;
+    }
+
+    inFileBuild.close();
+
+    if (z3950_verbosity_>1) {
+      cerr << endl;
+    }
+
+    return true;
+}    
+
+
+
+
+  // converts a Bib1 field number to a short index name,
+  // returns true if a mapping existed, false otherwise
+bool gsdlCollection::getFieldArg(int Bib1, text_t &Short) 
+{
+    if (Bib1_to_DCES.count(Bib1) < 1) return false;
+    if (DCES_to_Short.count(Bib1_to_DCES[Bib1]) < 1) return false;
+    Short = DCES_to_Short[Bib1_to_DCES[Bib1]];
+    return true;
+}
+  
+gsdlCollection::~gsdlCollection(void) 
+{
+    //cerr << "gsdlCollection destructor called" << endl;
+}
+
+gsdlCollection::gsdlCollection(void) 
+{
+    //cerr << "gsdlCollection void constructor called" << endl;
+}
+ 
+gsdlCollection::gsdlCollection(text_t collectionName, text_t gsdlhome) 
+{
+
+    //char *filename_cstr;
+    //ifstream inFile;
+    //int inFileSize = 0;
+    z3950ready = true;
+
+    // int input_Bib1;
+    //text_t input_DCES;
+    //text_t input_Short;
+
+    name = collectionName;
+
+    // read in the cfg file for Bib1_to_DCES
+    text_t col_filename = gsdlhome;
+    col_filename.appendcstr("/collect/");
+    col_filename.append(collectionName);
+    col_filename.appendcstr("/etc/bib1_to_dces.cfg");
+
+    if (!bib1_to_dc_mapping(col_filename)) {
+      text_t site_filename = gsdlhome;
+      site_filename.appendcstr("/etc/bib1_to_dces.cfg");
+
+      if (!bib1_to_dc_mapping(site_filename)) {
+	cerr << "Unable to find bib1->dublin core mapping for " 
+	     << collectionName << endl;
+	cerr << "Looked for:\n  " << col_filename << "\n  " 
+	     << site_filename << endl;
+	z3950ready = false;
+	return;
+      }
+    }
+
+    // read in the cfg file for mapping Dublin Core to Short (internal name)
+    text_t build_filename = gsdlhome;
+    build_filename.appendcstr("/collect/");
+    build_filename.append(collectionName);
+    build_filename.appendcstr("/index/build.cfg");
+
+    if (!dc_to_short_mapping(build_filename,collectionName)) {
+      // explicitly check for error (in case in future more work is done after
+      // this
+      z3950ready = false;
+      return;
+    }
+
+}
+
+
+void z3950Server::openLogfile(text_t extension, ofstream &out)
+{
+    char *cfilename;
+    text_t filename;
+
+    filename = filename_cat(gsdlhome, extension);
+    cfilename = filename.getcstr();
+
+    out.open(cfilename);
+    delete cfilename;
+}
+
+z3950Server::z3950Server(nullproto *protocol, text_t home)
+{ 
+
+    text_t dirname;
+    char * cdirname;
+    if (home.empty()) {
+      cerr  << "Z3950Server Error: gsdlhome not set. \nPlease make sure"
+	    << " you have a valid gsdlsite.cfg file in the directory \n"
+	    << "you are running z3950server\n";
+      cerr << "Exiting..."<<endl;
+      exit(1);
+    } 
+    cerr << "Z3950Server: gsdlhome = " << home << endl;
+
+    this->gsdlhome = home;
+    //cerr << gsdlhome.getcstr() << endl;
+    dirname = filename_cat(gsdlhome, "/collect");
+    cdirname = dirname.getcstr();
+    if (!read_dir (cdirname, collections)) { 
+      cerr << "Z3950Server Error: could not read directory "<<cdirname<<endl;
+      cerr << "Exiting..."<<endl;
+      exit (1);
+    }
+    delete cdirname;
+
+    cerr << "Constructing set list server" << endl;
+    this->protocol = protocol;
+
+    // initialise Yaz stuff for z39.50 server 
+}
+  
+bool z3950Server::initialise() 
+{
+      ofstream      logout;
+      int           reply;
+      comerror_t    error = noError;
+
+      this->openLogfile("/etc/z3950out.txt", logout);
+      reply = protocol->init(error, logout);
+      logout.close();
+      return (reply != 0 ? 1 : 0);
+}
+
+void z3950Server::configure(const text_t &key, const text_tarray &cfgline, 
+			    comerror_t &err)
+{
+      err = noError;
+
+      // ****
+      cerr << "Recieved " << key << " = ";
+      for (int unsigned i=0; i<cfgline.size(); i++)
+	{
+	  cout << cfgline[i] << " ";
+	}
+      cout << endl;
+
+      // DB // ****
+      if (key=="gsdlhome")
+	{  
+	  text_tarray cfgline;
+	  cfgline.push_back (gsdlhome);
+	  cerr << "Changing gsdlhome to " << gsdlhome << endl;
+	  protocol->configure(key, cfgline, err);
+	}
+      else if (key=="httpdomain")
+	{
+	  // Only let gsdlhome through !!!! // ****
+	  cerr << "Supressing httpdomain" << endl;
+	}
+      else if (key=="httpprefix")
+	{
+	  cerr << "Supressing httpprefix" << endl;
+	}
+      else
+	{ 
+	  protocol->configure(key, cfgline,err);
+	}
+}
+
+int z3950Server::run_z3950( int argc, char **argv ) 
+{
+
+    const int statserv_var =  statserv_main(argc, argv, bend_init, bend_close);
+    cerr << "statserv_main returns: " << statserv_var << endl;
+    return statserv_var;
+}
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950_to_gsdl.h	(revision 16561)
@@ -0,0 +1,87 @@
+/**********************************************************************
+ *
+ * z3950_to_gsdl.h --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950_TO_GSDL_H
+#define Z3950_TO_GSDL_H
+
+
+void toUpperString(char *&text);
+
+class gsdlCollection
+{
+ public:
+  gsdlCollection(); 
+  gsdlCollection(text_t collectionName, text_t gsdlhome);
+  ~gsdlCollection(void);
+
+  bool z3950Capeable() const;
+
+  // converts a Bib1 field number to a short index name,
+  // returns true if a mapping existed, false otherwise
+  bool getFieldArg(int Bib1, text_t &Short);
+
+  text_t getName(); // returns the name of the collection in short form
+  
+ protected:
+
+
+  bool bib1_to_dc_mapping(text_t& filename);
+  bool dc_to_short_mapping(text_t& filename, text_t& collectionName);
+
+ protected:
+  text_t name;
+  map<int, text_t>    Bib1_to_DCES;  // Bib-1 to DC metadata names
+  map<text_t, text_t> DCES_to_Short; // DC to short (2-3 letter) internal name
+
+  bool z3950ready; // is the collection Z39.50-ready ?
+                   // (i.e. does it have the cfg files for mappings)
+
+
+};
+
+class z3950Server 
+{
+protected:
+  text_tarray collections;
+  nullproto *protocol;
+  text_t      gsdlhome;
+
+  // save particular YAZ variables ...
+
+  void openLogfile(text_t extension, ofstream &out);
+
+
+public:
+  z3950Server(nullproto *protocol, text_t home);
+  
+  bool initialise();
+
+  void configure(const text_t &key, const text_tarray &cfgline, comerror_t &err);
+
+  int run_z3950( int argc, char **argv );
+
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950cfg.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950cfg.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950cfg.h	(revision 16561)
@@ -0,0 +1,47 @@
+/*
+  This defines a data structure shared between:
+  * the C++ z3950proto class defined in z3950proto.cpp, and
+  * the C function yyparse() created by bison from the file zparse.fl
+  */
+
+#ifndef _Z3950CFG_H
+#define _Z3950CFG_H
+
+#include <stdio.h> // for FILE* type
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct z3950aboutlist {
+  char *lang;
+  char *text;
+  struct z3950aboutlist *next;
+};
+struct z3950cfg {
+  char *shortname;
+  char *hostname;
+  int port;
+  char *dbname;
+  char *longname;
+  char *icon;
+  char *smallicon;
+  struct z3950aboutlist *about;
+  struct z3950cfg *next;
+};
+extern struct z3950cfg *zserver_list;
+extern FILE *errfile;
+
+/* can't use default yyparse(), as this name is used elsewhere (eg mg)*/
+#define yyin	zconfigin
+#define yyout	zconfigout
+#define yyparse	zconfigparse
+#define yylex	zconfiglex
+
+int yyparse ();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950explain.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950explain.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950explain.cpp	(revision 16561)
@@ -0,0 +1,209 @@
+/**********************************************************************
+ *
+ * z3950explain.cpp --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "z3950explain.h"
+
+void getExplainInfo(bend_search_rr *rr, FilterResponse_t &response, comerror_t &err)
+{
+  text_t GSQuery = ZQueryToGSQuery(rr);
+  char *GSQuery_char = GSQuery.getcstr(); 
+
+  int i;
+  if (strstr(GSQuery_char, "TargetInfo") != NULL) {
+    // should be one only
+    // real-life example:
+    /*
+      62
+      explain-category targetInfo
+      date-of-info Sep 22, 1999
+      language eng
+    */
+    ResultDocInfo_t docinfo;
+    docinfo.metadata["explain-category"].values.push_back("TargetInfo");
+    docinfo.metadata["name"].values.push_back("Greenstone");
+    docinfo.metadata["namedResultSets"].values.push_back("false");
+    docinfo.metadata["multipleDBsearch"].values.push_back("true");
+    docinfo.metadata["language"].values.push_back("eng");
+    response.docInfo.push_back(docinfo);
+    response.numDocs++;
+  }
+  if (strstr(GSQuery_char, "DatabaseInfo") != NULL) {
+    // should be one per collection
+    // real-life example:
+    /*
+      3
+      dbnum 0
+      explain-category DatabaseInfo
+      database-name.1 0
+      title Z39.50 EXPLAIN DB
+      database-name.2 ir-explain-1
+      date-of-info Sep 22, 1999
+      language eng
+      dbpath /usr/wald/z39.50root/explain
+      dbname explain
+    */
+    ResultDocInfo_t docinfo;
+    docinfo.metadata["explain-category"].values.push_back("DatabaseInfo");
+    docinfo.metadata["database-name"].values.push_back("IR-Explain-1");
+    docinfo.metadata["title"].values.push_back("Z39.50 EXPLAIN DB");
+    docinfo.metadata["language"].values.push_back("eng");
+    docinfo.metadata["dbname"].values.push_back("explain");
+    response.docInfo.push_back(docinfo);
+    response.numDocs++;
+    // now get the info for each collection
+    text_tarray collectlist;
+    Protocol->get_collection_list(collectlist, err, cerr);
+    text_tarray::iterator COLHERE = collectlist.begin();
+    text_tarray::iterator COLEND = collectlist.end();
+    while (COLHERE != COLEND) {
+      // don't include information for non-active collections
+      bool collection_active;
+      Protocol->ping(*COLHERE, collection_active, err, cerr);
+      if (collection_active != true) {
+	COLHERE++;
+	continue;
+      }
+      ColInfoResponse_t collectinfo;
+      Protocol->get_collectinfo(*COLHERE, collectinfo, err, cerr);
+      docinfo.clear();
+      docinfo.metadata["name"].values.push_back(collectinfo.shortInfo.name);
+      docinfo.metadata["host"].values.push_back(collectinfo.shortInfo.host);
+      if (collectinfo.isPublic) {
+	docinfo.metadata["isPublic"].values.push_back("true");
+      } else {
+	docinfo.metadata["isPublic"].values.push_back("false");
+      }
+      if (collectinfo.isBeta) {
+	docinfo.metadata["isBeta"].values.push_back("true");
+      } else {
+	docinfo.metadata["isBeta"].values.push_back("false");
+      }
+      if (collectinfo.useBook == true) {
+	docinfo.metadata["useBook"].values.push_back("true");
+      } else {
+	docinfo.metadata["useBook"].values.push_back("false");
+      }
+      docinfo.metadata["buildDate"].values.push_back(collectinfo.buildDate);
+      text_tarray::iterator TEXTT_HERE = collectinfo.ccsCols.begin();
+      text_tarray::iterator TEXTT_END = collectinfo.ccsCols.end();
+      for (i = 1; TEXTT_HERE != TEXTT_END; i++, TEXTT_HERE++) {
+	text_t tmp = "ccsCols.";
+	tmp.appendint(i);
+	docinfo.metadata[tmp].values.push_back(*TEXTT_HERE);
+      }
+      TEXTT_HERE = collectinfo.languages.begin();
+      TEXTT_END = collectinfo.languages.end();
+      for (i = 1; TEXTT_HERE != TEXTT_END; i++, TEXTT_HERE++) {
+	text_t tmp = "languages.";
+	tmp.appendint(i);
+	docinfo.metadata[tmp].values.push_back(*TEXTT_HERE);
+      }
+      docinfo.metadata["numDocs"].values.push_back(collectinfo.numDocs);
+      docinfo.metadata["numSections"].values.push_back(collectinfo.numSections);
+      docinfo.metadata["numWords"].values.push_back(collectinfo.numWords);
+      docinfo.metadata["numBytes"].values.push_back(collectinfo.numBytes);
+
+      collectionmeta_map::iterator COLLMETA_MAP_HERE = collectinfo.collectionmeta.begin();
+      collectionmeta_map::iterator COLLMETA_MAP_END = collectinfo.collectionmeta.end();
+      for (i = 1; COLLMETA_MAP_HERE != COLLMETA_MAP_END; i++, COLLMETA_MAP_HERE++) {
+	text_t tmp = "collectionmeta.";
+	tmp.appendint(i);
+	text_t tmp2 = COLLMETA_MAP_HERE->first;
+	tmp2.appendcstr(" -> ");
+	tmp2.append(COLLMETA_MAP_HERE->second[g_EmptyText]);
+	docinfo.metadata[tmp].values.push_back(tmp2);
+      }
+      text_tmap::iterator TEXTT_MAP_HERE;
+      text_tmap::iterator TEXTT_MAP_END;
+      // uncomment if you want to include this in the explain record,
+      // it's a bit messy to include by default
+      /*
+      TEXTT_MAP_HERE = collectinfo.format.begin();
+      TEXTT_MAP_END = collectinfo.format.end();
+      for (i = 1; TEXTT_MAP_HERE != TEXTT_MAP_END; i++, TEXTT_MAP_HERE++) {
+	text_t tmp = "format.";
+	tmp.appendint(i);
+	text_t tmp2 = TEXTT_MAP_HERE->first;
+	tmp2.appendcstr(" -> ");
+	tmp2.append(TEXTT_MAP_HERE->second);
+	docinfo.metadata[tmp].values.push_back(tmp2);
+      }
+      */
+      TEXTT_MAP_HERE = collectinfo.building.begin();
+      TEXTT_MAP_END = collectinfo.building.end();
+      for (i = 1; TEXTT_MAP_HERE != TEXTT_MAP_END; i++, TEXTT_MAP_HERE++) {
+	text_t tmp = "building.";
+	tmp.appendint(i);
+	text_t tmp2 = TEXTT_MAP_HERE->first;
+	tmp2.appendcstr(" -> ");
+	tmp2.append(TEXTT_MAP_HERE->second);
+	docinfo.metadata[tmp].values.push_back(tmp2);
+      }
+      docinfo.metadata["receptionist"].values.push_back(collectinfo.receptionist);
+      docinfo.metadata["buildType"].values.push_back(collectinfo.buildType);
+      response.docInfo.push_back(docinfo);
+      response.numDocs++;
+      COLHERE++;
+    }
+  }
+  if (strstr(GSQuery_char, "SchemaInfo") != NULL) {
+    // should be one per collection
+    // real-life example:
+    /*
+      10
+      dbnum 1
+      explain-category schemaInfo
+      database-name.1 1
+      database-name.2 books
+      database-name.3 default
+      database-name.4 cat
+      date-of-info Sep 22, 1999
+      language eng
+      schema.1 1.2.840.10003.13.1000.3.1
+      dbpath /usr/wald/z39.50root/books
+      dbname books
+     */
+  }
+  if (strstr(GSQuery_char, "AttributeSetInfo") != NULL) {
+    // should be one per collection
+    // real-life example:
+    /*
+      9
+      dbnum 1
+      explain-category AttributeSetInfo
+      database-name.1 1
+      database-name.2 books
+      database-name.3 default
+      database-name.4 cat
+      date-of-info Sep 22, 1999
+      language eng
+      attribute-set.1 1.2.840.10003.13.1000.3.1
+      dbpath /usr/wald/z39.50root/books
+      dbname books
+    */
+  }
+}
+
+
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950explain.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950explain.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950explain.h	(revision 16561)
@@ -0,0 +1,35 @@
+/**********************************************************************
+ *
+ * z3950proto.h --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950EXPLAIN_H
+#define Z3950EXPLAIN_H
+
+
+#include "z3950parser.h"
+
+void getExplainInfo(bend_search_rr *rr, FilterResponse_t &response, comerror_t &err);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950parser.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950parser.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950parser.cpp	(revision 16561)
@@ -0,0 +1,373 @@
+
+#include "z3950parser.h"
+
+void Z_AttributeList_parse(bend_search_rr *rr, Z_AttributeList *Z, text_t &GSQuery)
+{
+  /*
+    struct Z_AttributeList {
+    int num_attributes;
+    Z_AttributeElement **attributes;
+    };
+  */
+
+  /*
+    struct Z_AttributeElement {
+    Z_AttributeSetId *attributeSet; // OPT
+    int *attributeType;
+    int which;
+    union {
+    int *numeric;
+    Z_ComplexAttribute *complex;
+    #define Z_AttributeValue_numeric 1
+    #define Z_AttributeValue_complex 2
+    } value;
+    };
+  */
+
+  
+
+}
+
+void Z_Term_parse(bend_search_rr *rr, Z_Term *Z, text_t &GSQuery)
+{
+  /*
+    struct Z_Term {
+    int which;
+    union {
+    Odr_oct *general;
+    int *numeric;
+    Z_InternationalString *characterString;
+    Odr_oid *oid;
+    char *dateTime;
+    Z_External *external;
+    Z_IntUnit *integerAndUnit;
+    Odr_null *null;
+    #define Z_Term_general 1
+    #define Z_Term_numeric 2
+    #define Z_Term_characterString 3
+    #define Z_Term_oid 4
+    #define Z_Term_dateTime 5
+    #define Z_Term_external 6
+    #define Z_Term_integerAndUnit 7
+    #define Z_Term_null 8
+    } u;
+    };
+  */
+
+  char *term;
+
+  switch(Z->which){
+  case Z_Term_general: {
+    // must copy no more than len bytes from buf, or else previous query terms may be included
+    int buflen = Z->u.general->len;
+    term = new char[buflen+1];
+    memset(term, '\0', buflen+1);
+    strncpy(term, (const char*)(Z->u.general->buf), buflen);
+    GSQuery += term;
+    break; }
+  case Z_Term_numeric:
+    GSQuery += *(Z->u.numeric); // int*
+    break;
+  case Z_Term_characterString:
+    GSQuery += Z->u.characterString; // what is this type?
+    break;
+  case Z_Term_oid:
+    GSQuery += *(Z->u.oid); // int*
+    break;
+  case Z_Term_dateTime:
+    GSQuery += Z->u.dateTime; // char*
+    break;
+  case Z_Term_external:
+    GSQuery += " "; // ignore this for now
+    break;
+  case Z_Term_integerAndUnit:
+    GSQuery += " "; // ignore this for now
+    break;
+  case Z_Term_null:
+    GSQuery += " "; // void
+    break;
+  default:
+    cerr << "Error in Z_Term_parse" << endl;
+    exit(1);
+  }
+}
+
+void Z_ProximityOperator_parse(bend_search_rr *rr, Z_ProximityOperator *Z, text_t &GSQuery)
+{
+  /*
+    struct Z_ProximityOperator {
+    bool_t *exclusion; // OPT
+    int *distance;
+    bool_t *ordered;
+    #define Z_ProximityOperator_Prox_lessThan 1
+    #define Z_ProximityOperator_Prox_lessThanOrEqual 2
+    #define Z_ProximityOperator_Prox_equal 3
+    #define Z_ProximityOperator_Prox_greaterThanOrEqual 4
+    #define Z_ProximityOperator_Prox_greaterThan 5
+    #define Z_ProximityOperator_Prox_notEqual 6
+    int *relationType;
+    int which;
+    union {
+    Z_ProxUnit *known;
+    int *zprivate;
+    #define Z_ProximityOperator_known 1
+    #define Z_ProximityOperator_private 2
+    } u;
+    };
+    
+    #define Z_ProxUnit_character 1
+    #define Z_ProxUnit_word 2
+    #define Z_ProxUnit_sentence 3
+    #define Z_ProxUnit_paragraph 4
+    #define Z_ProxUnit_section 5
+    #define Z_ProxUnit_chapter 6
+    #define Z_ProxUnit_document 7
+    #define Z_ProxUnit_element 8
+    #define Z_ProxUnit_subelement 9
+    #define Z_ProxUnit_elementType 10
+    #define Z_ProxUnit_byte 11
+  */
+
+  // Greenstone (actually mgpp) supports proximity, but only 
+  // to n words either side.
+  // A more complete implementation of proximity can be added
+  // by parsing the other fields in Z_ProximityOperator.
+  // Note: this only works properly with MGPP collections
+  
+  GSQuery += " NEAR"; 
+  GSQuery += *(Z->distance);
+  GSQuery += " ";
+}
+
+void Z_Operator_parse(bend_search_rr *rr, Z_Operator *Z, text_t &GSQuery)
+{
+  /*
+    struct Z_Operator {
+    int which;
+    union {
+    Odr_null *and;
+    Odr_null *or;
+    Odr_null *and_not;
+    Z_ProximityOperator *prox;
+    #define Z_Operator_and 1
+    #define Z_Operator_or 2
+    #define Z_Operator_and_not 3
+    #define Z_Operator_prox 4
+    } u;
+    };
+  */
+  
+  switch(Z->which){
+  case Z_Operator_and:
+    GSQuery += " & ";
+    break;
+  case Z_Operator_or:
+    GSQuery += " | ";
+    break;
+  case Z_Operator_and_not:
+    GSQuery += " ! ";
+    break;
+  case Z_Operator_prox:
+    Z_ProximityOperator_parse(rr, Z->u.prox, GSQuery);
+    break;
+  default:
+    cerr << "Error in Z_Operator_parse" << endl;
+    exit(1);
+  }
+}
+
+void Z_Operand_parse(bend_search_rr *rr, Z_Operand *Z, text_t &GSQuery)
+{
+    /*
+      struct Z_Operand {
+      int which;
+      union {
+      Z_AttributesPlusTerm *attributesPlusTerm;
+      Z_ResultSetId *resultSetId;
+      Z_ResultSetPlusAttributes *resultAttr;
+      #define Z_Operand_APT 1
+      #define Z_Operand_resultSetId 2
+      #define Z_Operand_resultAttr 3
+      } u;
+      };
+    */
+  
+  switch(Z->which) {
+  case Z_Operand_APT: {
+    /*
+      struct Z_AttributesPlusTerm {
+      Z_AttributeList *attributes;
+      Z_Term *term;
+      };
+    */
+    
+    /*
+      struct Z_AttributeList {
+      int num_attributes;
+      Z_AttributeElement **attributes;
+      };
+    */
+    
+    /*
+      struct Z_AttributeElement {
+      Z_AttributeSetId *attributeSet; // OPT
+      int *attributeType;
+      int which;
+      union {
+      int *numeric;
+      Z_ComplexAttribute *complex;
+      #define Z_AttributeValue_numeric 1
+      #define Z_AttributeValue_complex 2
+      } value;
+      };
+    */
+    
+    char *term;
+    
+    if (Z->u.attributesPlusTerm->attributes->num_attributes > 0) {
+      int Bib1_relation = *(Z->u.attributesPlusTerm->attributes->attributes[0]->attributeType);
+      int Bib1_use = *(Z->u.attributesPlusTerm->attributes->attributes[0]->value.numeric);
+
+      // if use value is 1010 ("Body of text"), is not a fielded search
+      if (Bib1_use == 1010) {
+	// if relation value is 6 ("not"), add a "not" to GSQuery
+	if (Bib1_relation == 6) {
+	  GSQuery += "! ";
+	}
+	Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
+      }
+      // for other use values, is a fielded search
+      else {
+	text_t Short;
+	// only add [ ]: if the index exists in the collection,
+	// note: only the index names in the first collection of a
+	// multi-collection search are used at the moment
+	if ((Collection_map[rr->basenames[0]]).getFieldArg(Bib1_use, Short)) {
+	  GSQuery += "[";
+	  cerr << "Supressing square brackets for the moment!\n";
+
+	  Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
+	  GSQuery += "]";
+	  GSQuery += ":";
+	  GSQuery += Short;
+	}
+	// if it doesn't, add it as an unfielded search term
+	else {
+	  cerr << "[" << rr->basenames[0] << "]: failed to find index" << endl;
+	  Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
+	}
+      }
+    }
+    else {
+      Z_Term_parse(rr, Z->u.attributesPlusTerm->term, GSQuery);
+    }
+    break;
+  }
+  case Z_Operand_resultSetId: {
+    GSQuery += "(";
+    GSQuery += Resultsets[Z->u.resultSetId];
+    GSQuery += ")";
+    break; 
+  }
+  case Z_Operand_resultAttr:
+    GSQuery += " "; // ignore this for now
+    break;
+  default:
+    cerr << "Error in Z_Operand_parse" << endl;
+    exit(1);
+  } 
+}
+
+void Z_RPNStructure_parse(bend_search_rr *rr, Z_RPNStructure *Z, text_t &GSQuery)
+{
+  /*
+    struct Z_RPNStructure {
+    int which;
+    union {
+    Z_Operand *simple;
+    Z_Complex *complex;
+    #define Z_RPNStructure_simple 1
+    #define Z_RPNStructure_complex 2
+    } u;
+    };
+  */
+
+  switch(Z->which){
+  case Z_RPNStructure_simple:
+    Z_Operand_parse(rr, Z->u.simple, GSQuery);
+    break;
+  case Z_RPNStructure_complex:
+    GSQuery += "(";
+    Z_RPNStructure_parse(rr, Z->u.complex->s1, GSQuery);
+    Z_Operator_parse(rr, Z->u.complex->roperator, GSQuery);
+    Z_RPNStructure_parse(rr, Z->u.complex->s2, GSQuery);
+    GSQuery += ")";
+    break;
+  default:
+    cerr << "Error in Z_RPNStructure_parse" << endl;
+    exit(1);
+  }
+}
+
+text_t ZQueryToGSQuery(bend_search_rr *rr)
+{
+  text_t GSQuery;
+
+  switch(rr->query->which){
+  case Z_Query_type_0:
+    cerr << "querytype = 0" << endl;
+    // this is a void*, pretend it's a Greenstone-type query
+    GSQuery += (char*)(rr->query->u.type_0);
+    break;
+  case Z_Query_type_1: {
+    cerr << "querytype = 1" << endl;
+    /*
+      struct Z_RPNQuery {
+      Z_AttributeSetId *attributeSetId;
+      Z_RPNStructure *RPNStructure;
+      };
+    */
+    // parse attributeSetId
+    cerr << "attributeSetId = ";
+    int c, d = 0;
+    do {
+      c = rr->query->u.type_1->attributeSetId[d];
+      cerr << c;
+      d++;
+    } while (c >= 0);
+    cerr << endl;
+
+    Z_RPNStructure_parse(rr, rr->query->u.type_1->RPNStructure, GSQuery);
+    break; }
+  case Z_Query_type_2: {
+    cerr << "querytype = 2" << endl;
+    // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
+    GSQuery += (char*)(rr->query->u.type_2->buf);
+    break; }
+  case Z_Query_type_100: {
+    cerr << "querytype = 100" << endl;
+     // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
+    GSQuery += (char*)(rr->query->u.type_100->buf);
+    break; }
+  case Z_Query_type_101: {
+    cerr << "querytype = 101" << endl;
+    // check: is this really the same as a type 1 query?
+    Z_RPNStructure_parse(rr, rr->query->u.type_101->RPNStructure, GSQuery);
+    break; }
+  case Z_Query_type_102: {
+    cerr << "querytype = 102" << endl;
+     // this is just a buf (unsigned char*), pretend it's a Greenstone-type query
+    GSQuery += (char*)(rr->query->u.type_102->buf);
+    break; }
+  default: {
+    cerr << "Error in ZQueryToGSQuery" << endl;
+    exit(1); }
+  }
+
+  // override GSQuery (test code)
+  //GSQuery = "[the]:TX";
+
+  cerr << "GSQuery = `" << GSQuery << "'" << endl;
+  return GSQuery;
+}
+
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950parser.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950parser.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950parser.h	(revision 16561)
@@ -0,0 +1,77 @@
+/**********************************************************************
+ *
+ * z3950parser.h -- convert Z39.50's RPN query syntax into Greenstone
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950PARSER_H
+#define Z3950PARSER_H
+
+
+#include <string>
+#include <list>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+
+// Greenstone headers
+#include "fileutil.h"
+#include "comtypes.h"
+#include "nullproto.h"
+#include "z3950_to_gsdl.h"
+
+// YAZ headers
+extern "C" {
+#include <yaz/backend.h>
+#include <yaz/log.h>
+#ifdef ASN_COMPILED
+#include <yaz/ill.h>
+#endif
+}
+
+extern text_t gsdlhome;
+extern list<FilterResponse_t> Response_tlist;
+extern list<text_t> Response_tlist_colnames;
+extern list<int> Response_tlist_sizes;
+extern z3950Server *Server;
+extern collectset *Cservers;
+extern nullproto *Protocol;
+extern map<text_t, gsdlCollection> Collection_map;
+extern map<text_t, text_t> Resultsets;
+
+void Z_AttributeList_parse(bend_search_rr *rr, Z_AttributeList *Z, text_t &GSQuery);
+
+void Z_Term_parse(bend_search_rr *rr, Z_Term *Z, text_t &GSQuery);
+
+void Z_ProximityOperator_parse(bend_search_rr *rr, Z_ProximityOperator *Z, text_t &GSQuery);
+
+void Z_Operator_parse(bend_search_rr *rr, Z_Operator *Z, text_t &GSQuery);
+
+void Z_Operand_parse(bend_search_rr *rr, Z_Operand *Z, text_t &GSQuery);
+
+void Z_RPNStructure_parse(bend_search_rr *rr, Z_RPNStructure *Z, text_t &GSQuery);
+
+text_t ZQueryToGSQuery(bend_search_rr *rr);
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950proto.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950proto.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950proto.cpp	(revision 16561)
@@ -0,0 +1,611 @@
+/**********************************************************************
+ *
+ * z3950proto.cpp --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "z3950proto.h"
+#include "comtypes.h"
+#include "browsefilter.h"
+#include "queryfilter.h"
+#include "filter.h"
+// z39.50 yaz stuff
+
+#include "z3950cfg.h" // for reading in config files - 
+// defines "struct z3950cfg *zserver_list" as the head of the list.
+
+// note! yyin is hash-defined in z3950cfg.h to something else, to avoid
+// name conflicts with "other" yyins.
+extern FILE *yyin;
+extern FILE *yyout; // redirect for `errors' (unmatched tokens in flex)
+
+
+
+z3950proto::z3950proto() {
+  zserver_count=0;
+}
+
+z3950proto::~z3950proto() {
+}
+
+void z3950proto::add_server (z3950_proxy& zserver) {
+
+  // append the new server
+  ++zserver_count;
+  zservers.push_back(&zserver);
+}
+
+void z3950proto::read_config_file(const text_t &filename, const text_t &errf) {
+  struct z3950cfg *here;
+  struct z3950cfg *oldhere;
+  z3950_proxy *zserver;
+  ShortColInfo_t *tempinfo;
+
+  //  FILE *errfile declared in z3950cfg.h, defined in zparse.y
+
+  char *errf_str=errf.getcstr();
+  if ((errfile=fopen(errf_str,"a"))==NULL) {
+    // what do we do if we can't open the error file?
+    // this means that errors will go to stderr, which may stuff up 
+    // any cgi headers and the page.
+    errfile=stderr;
+  }
+  delete []errf_str;
+  yyout=errfile;
+
+  
+  char *filename_str=filename.getcstr();
+  yyin=fopen(filename_str,"r");
+  if (yyin==NULL) {
+    cerr << "Could not open "<<filename_str<<" for reading.\n";
+    delete []filename_str;
+    return;
+  }
+  delete []filename_str;
+
+  // this is in zparse.tab.c,
+  // which is the bison output of zparse.y
+  yyparse();
+
+  if (errfile!=stderr)
+    fclose(errfile);
+
+  // we now have the config files in the ptr zserver_list
+  if (zserver_list==NULL)
+    return; // no valid servers found in the config file - note that
+  // the parser will have already spat out any errors.
+
+  // now create z3950 proxies for each structure in server_list
+  here=zserver_list;
+  while (here!=NULL) {
+    zserver=new z3950_proxy;
+    tempinfo=new ShortColInfo_t;
+
+    tempinfo->host.setcstr(here->hostname);
+    tempinfo->port=here->port;
+    tempinfo->name.setcstr(here->dbname);
+    zserver->setInfo(tempinfo);
+    zserver->setName(here->shortname);
+    // now collection metadata.
+    zserver->setMeta("collectionname",here->longname);
+    if (here->icon!=NULL)
+      zserver->setMeta("iconcollection",here->icon);
+    if (here->smallicon!=NULL)
+      zserver->setMeta("iconcollectionsmall",here->smallicon);
+    
+    /*    filterclass *filter = new filterclass ();
+	  zserver->add_filter (filter);
+	  browsefilterclass *browsefilter = new browsefilterclass();
+	  zserver->add_filter (browsefilter);
+	  queryfilterclass *queryfilter = new queryfilterclass();
+	  zserver->add_filter (queryfilter);
+    */
+
+    // About list
+    if (here->about!=NULL) {
+      struct z3950aboutlist *about_here=here->about;
+      struct z3950aboutlist *oldabout;
+
+      while (about_here!=NULL) {
+	// problem with default lang (null): can't add ("",..)
+	if (about_here->lang==NULL)
+	  zserver->addcfgAbout("en",about_here->text);
+	else
+	  zserver->addcfgAbout(about_here->lang, about_here->text);
+	oldabout=about_here;
+	about_here=about_here->next;
+	free(oldabout->lang);
+	free(oldabout->text);
+	free(oldabout);
+      }
+    }
+
+    oldhere=here;
+    here=here->next;
+    free(oldhere->shortname); // these 4 strings should all be non-NULL...
+    free(oldhere->hostname);
+    free(oldhere->dbname);
+    free(oldhere->longname);
+    if (oldhere->icon) free(oldhere->icon);     // these 2 may be NULL
+    if (oldhere->smallicon) free(oldhere->smallicon);
+    free(oldhere);
+
+    add_server(*zserver);
+  } // end of while loop.
+
+}
+
+void z3950proto::configure (const text_t &/*key*/,
+			    const text_tarray &/*cfgline*/) {
+  // this is called for each line in the gsdlsite.cfg file
+}
+
+
+bool z3950proto::init (ostream &/*logout*/) {
+  // set up tcp connection to server here?
+  // we might also read in the config file here (instead of librarymain.cpp)
+
+  // 
+
+  // logout goes to initout.txt
+  //  logout <<"zdebug:init:Number of z3950 servers: "<< zserver_count << "\n";
+  //logout << "\t1st server name: " << zservers[0].getName().getcstr() << "\n";
+  return true;
+
+}
+
+/*text_t z3950proto::get_protocol_name () {
+  return "z3950proto";
+}
+*/
+
+void z3950proto::get_collection_list (text_tarray &collist, 
+				      comerror_t &/*err*/,
+				      ostream &/*logout*/) {
+
+  z3950_proxy_array::iterator here = zservers.begin();
+  z3950_proxy_array::iterator end = zservers.end();
+  while (here != end) {
+    collist.push_back((*here)->getName());
+    ++here;
+  }
+}
+
+void z3950proto::has_collection (const text_t &collection, bool &hascollection,
+				 comerror_t &/*err*/, ostream &/*logout*/) {
+  z3950_proxy_array::iterator here = zservers.begin();
+  z3950_proxy_array::iterator end = zservers.end();
+  while (here != end) {
+    if((*here)->getName()==collection) {
+      hascollection=true;
+      return;
+    }
+    ++here;
+  }
+  hascollection=false;
+}
+
+void z3950proto::ping (const text_t &/*collection*/, bool &wassuccess, 
+		       comerror_t &/*err*/, ostream &/*logout*/) {
+  // should we just ping the server, or actually create a connection
+  // to the z39.50 server process on the machine ?
+  wassuccess = true;
+}
+
+void z3950proto::get_collectinfo (const text_t &collection,
+				  ColInfoResponse_t &collectinfo,
+				  comerror_t &err, ostream &logout) {
+
+  // set err to protocolError if something goes wrong...
+  err=noError;
+
+  z3950_proxy_array::iterator here = zservers.begin();
+  z3950_proxy_array::iterator end = zservers.end();
+  while (here != end) {
+    if((*here)->getName()==collection) {
+      break;
+    }
+    ++here;
+  }
+
+  if (here==end) {
+    err=protocolError;
+    char *coll_str=collection.getcstr();
+    logout << "z39.50: couldn't find collection" 
+	   << coll_str
+	   << endl;
+    delete []coll_str;
+    return;
+  }
+
+  const ShortColInfo_t *colinfo=(*here)->getInfo();
+  collectinfo.shortInfo.name=colinfo->name;
+  collectinfo.shortInfo.host=colinfo->host;
+  collectinfo.shortInfo.port=colinfo->port;
+
+  collectinfo.isPublic=true;
+  // don't use beta field
+  /*collectinfo.isBeta=false;*/
+  collectinfo.buildDate=1;
+  // leave ccsCols empty (no cross-coll. searching - for now)
+  /*collectinfo.ccsCols=(text_tarray);*/ //not like this!!!
+  // This info is available from the config file -- johnmcp
+  /*******collectinfo.languages.push_back("en");
+	  collectinfo.languages.push_back("fr");********/
+  collectinfo.numDocs=0;
+  collectinfo.numWords=0;
+  collectinfo.numBytes=0;
+  // copy the text maps over.
+  //  collectinfo.collectionmeta; // text_tmap
+  text_tmap collmeta = *((*here)->getMeta());
+  text_tmap::iterator mhere = collmeta.begin();
+  text_tmap::iterator mend = collmeta.end();
+  while (mhere != mend) {
+    collectinfo.collectionmeta[(*mhere).first][g_EmptyText] = (*mhere).second;
+    mhere ++;
+  }
+  //collectinfo.collectionmeta=*((*here)->getMeta());
+  collectinfo.format=*((*here)->getFormat()); //text_tmap
+  /*    collectinfo.building; //text_tmap  */
+
+  ////collectinfo.receptionist="z3950"; 
+  /* for now... this is a url,  relative to .../cgi-bin.
+     NOTE: if this is empty, it defaults to _gwcgi_?a=p&p=about&c=<colname>
+  */
+}
+
+void z3950proto::get_filterinfo (const text_t &/*collection*/,
+				 InfoFiltersResponse_t &response,
+				 comerror_t &/*err*/, ostream &/*logout*/) {
+  // we'll fake it here, and say we have set up some filters
+  response.filterNames.insert("BrowseFilter");
+  response.filterNames.insert("QueryFilter");
+  response.filterNames.insert("NullFilter");
+
+}
+
+void z3950proto::get_filteroptions (const text_t &/*collection*/,
+				    const InfoFilterOptionsRequest_t &/*req*/,
+				    InfoFilterOptionsResponse_t &response, 
+				    comerror_t &err, ostream &/*logout*/) {
+  // for now, assume all servers have the same characteristics
+  /*  if (request.filterName=="QueryFilter") { } 
+      else if (request.filterName=="BrowseFilter") { } 
+      else if (request.filterName=="NullFilter") {  } */
+  response.filterOptions["Index"].type=FilterOption_t::stringt;
+  response.filterOptions["Index"].repeatable=FilterOption_t::onePerQuery;
+  response.filterOptions["Index"].defaultValue="any";
+  response.filterOptions["Index"].validValues.push_back(".any");
+  response.filterOptions["Index"].validValues.push_back(".title");
+  response.filterOptions["Index"].validValues.push_back(".author");
+  // and maybe ["Language"] option as well?
+  err=noError;
+}
+
+void z3950proto::filter (const text_t &collection,
+			 FilterRequest_t &request,
+			 FilterResponse_t &response,
+			 comerror_t &err, ostream &logout) {
+  // this function is called when:
+  //  *  creating the title page,(looking for iconcoll* & collectname metadata)
+  //  *  creating the about page (looking for "Title" metadata)
+  //  * doing the query - (note that a request for metadata comes first, then
+  // filterOptions = FRmetadata | FROID | FRtermFreq (64+4+1)
+
+  // metadata-only requests have filterName="NullFilter", else "QueryFilter".
+  // For the title page, we should not create a connection to the target
+  // (target means the actual z39.50 server, origin means us), but
+  // for the about page and query pages, we need to get information from the
+  // origin. (eg for the about page, we will print out some info such as ID,
+  // name and version.
+
+  // cerr now goes to errout.txt in etc directory
+  err=noError;
+
+  // get relevant "collection"
+  z3950_proxy_array::iterator zserver = zservers.begin();
+  z3950_proxy_array::iterator zend = zservers.end();
+  while (zserver != zend) {
+    if((*zserver)->getName()==collection) {
+      break;
+    }
+    ++zserver;
+  }
+  // now have collection in zserver.
+  
+  ColInfoResponse_t info;
+  ResultDocInfo_t *docInfo;
+  
+  // leave response.termInfo empty
+  // response.termInfo.push_back(g_EmptyText); ??????? (should be empty if not req.)
+
+  // See if this is for a query action
+  if (request.filterName=="QueryFilter") {
+    /* Sample OptionValue pairs
+      `StartResults'=`1'
+      `EndResults'=`20'
+      `Term'=`firstword secondword' (term is just whatever the user typed in)
+      `QueryType'=ranked|boolean   -> OR|AND
+      //`MatchMode'=`some' => 'OR'
+      //`MatchMode' = `all' => 'AND'
+      `Casefold'=`true'
+      `Stem'=`false'
+      `Maxdocs'=`50'
+     */
+    // go through options
+    text_t opt_term;            // the term(s) that the user entered 
+    int opt_querytype=0;        // 1=>ranked (or), 2=>boolean (and)
+    text_t opt_fields;          // which fields to search on
+    int opt_start=1, opt_end=20; // default values
+    int nummatches=0, maxdocs=50; // default values
+    OptionValue_tarray::iterator ov_here=request.filterOptions.begin();
+    OptionValue_tarray::iterator ov_end=request.filterOptions.end();
+    while (ov_here != ov_end) {
+      if (ov_here->name=="Term") {
+	opt_term=ov_here->value;
+      } else if (ov_here->name=="QueryType") {
+	if (ov_here->value=="ranked") opt_querytype=1;
+	else if (ov_here->value=="boolean") opt_querytype=2;
+	else { /* error - shouldn't happen */
+	  /* currently unhandled */
+	}
+      } else if (ov_here->name=="Index") {
+	opt_fields=ov_here->value;
+      } else if (ov_here->name=="StartResults") {
+	opt_start=ov_here->value.getint();
+      } else if (ov_here->name=="EndResults") {
+	opt_end=ov_here->value.getint(); 
+      } else if (ov_here->name=="Maxdocs") {
+	maxdocs=ov_here->value.getint();
+      } 
+      ++ov_here;
+    }
+    err=noError;
+    text_tarray *titles=(*zserver)->getrecordTitles(opt_term,
+						    opt_querytype,
+						    opt_fields,
+						    opt_start, // first to get
+						    opt_end-opt_start, //count
+						    &nummatches,err);
+    if (err!=noError) {
+      // can we return an err msg in a response, or just use
+      // the more drastic Greenstone error mechanism?
+      docInfo=new ResultDocInfo_t;
+      response.docInfo.push_back(*docInfo);
+      docInfo->metadata["Title"].values.push_back("Error - query err?");
+      logout << "\nz3950 filter query: error connecting to server\n";
+      // for now, DON'T use GSDL protocol err.
+      err=noError;
+      return;
+    }
+    // check if (titles==NULL) - only happens on error?
+    if (nummatches>0) {
+      text_tarray::iterator titles_here=titles->begin();
+      text_tarray::iterator titles_end=titles->end();
+      int counter=1;
+      while (titles_here!=titles_end) {
+	docInfo=new ResultDocInfo_t; 
+	docInfo->metadata["Title"].values.push_back(*titles_here);
+	docInfo->result_num=counter;
+	// we need to give some OID, so we'll just use counter for now...
+	// make it the number into the whole possible retrieved set.
+	docInfo->OID=counter+opt_start-1;
+	response.docInfo.push_back(*docInfo);
+	++counter;
+	++titles_here;
+      }
+    }
+
+    if (request.filterResultOptions & FRtermFreq) {
+      if (nummatches>maxdocs) {
+      	response.numDocs=maxdocs; // eg "more than 50" (if Maxdocs==50)
+	response.isApprox=MoreThan;
+      } else {
+	response.numDocs=nummatches; // eg "36 documents"
+	response.isApprox=Exact; // Exact | Approximate | MoreThan
+      }
+    } // end of if (... & FRtermFreq)
+    
+  } // end of if (... == "QueryFilter")
+  else {
+    // this wasn't a query action
+
+    if (request.filterOptions.size()>0 &&
+	request.filterOptions[0].name=="ParentNode") {
+      // don't want to return anything
+      return;
+      /*    } else if (request.docSet.size() &&
+	    request.docSet[0]!="collection") {
+	    // documentaction
+	    // if docSet is not empty, it is either "collection", or an array
+	    // of OIDs 
+	    docInfo=new ResultDocInfo_t;
+	    response.docInfo.push_back(*docInfo);
+      */
+    } else {
+      // in case we need to return only metadata
+      docInfo=new ResultDocInfo_t;
+      response.docInfo.push_back(*docInfo); 
+    }
+  } // end of not a query action
+
+
+
+  // Fill in metadata for each response.docInfo (if wanted)
+  if (request.filterResultOptions & FRmetadata) {
+    get_collectinfo (collection, info, err, logout);
+    // should check err returned here....
+
+    if (!request.fields.empty()) {
+      // currently, this is only true for NullFilter when getting the "Title"
+      // for a documentaction.
+
+      // loop on each document being returned
+      ResultDocInfo_tarray::iterator docs_here=response.docInfo.begin();
+      ResultDocInfo_tarray::iterator docs_end=response.docInfo.end();
+      while (docs_here!=docs_end) {
+	// loop on all the metadata fields in request.fields (type text_tset)
+	text_tset::iterator fields_here=request.fields.begin();
+	text_tset::iterator fields_end=request.fields.end();
+	//text_tmap::iterator it;
+	collectionmeta_map::iterator it;
+	while (fields_here!=fields_end) {
+	  it=info.collectionmeta.find(*fields_here);
+	  if (it!=info.collectionmeta.end())
+	    docs_here->metadata[*fields_here].values.push_back(((*it).second)[g_EmptyText]);
+	  else if (*fields_here=="Title" && !request.docSet.empty()) { 
+	    // We only do this for a document action.
+	    // (This comes through as a NullQuery).
+	    // hopefully docSet is only not empty for documentaction...
+	    text_t doctitle;
+	    int i;
+	    // check that docSet isn't empty first!!!!!!
+	    i=request.docSet[0].getint();
+	    text_t doctext="unneeded";
+	    /* following variables aren't used, as our query result has been
+	       cached in z3950proxy.cpp (but really we shouldn't know that
+	       here...) But for the NullFilter, we don't get given these
+	       again in the request, so for now we'll take advantage of this.*/
+
+	    int querytype=0; text_t field=g_EmptyText;
+
+	    // get the Query out of the filterOptions. (we need get the Title)
+	    text_t query=g_EmptyText;
+	    OptionValue_tarray::iterator opthere=request.filterOptions.begin();
+	    OptionValue_tarray::iterator opt_end=request.filterOptions.end();
+	    while (opthere!=opt_end) {
+	      if (opthere->name=="Term") {
+		query=opthere->value;
+	      } else if (opthere->name=="Index") {
+		field=opthere->value;
+	      } else if (opthere->name=="QueryType") {
+		if (opthere->value=="ranked") querytype=1;
+		else if (opthere->value=="boolean") querytype=2;
+		else { /* error - shouldn't happen */
+		  /* currently unhandled */
+		}
+	      }
+	      ++opthere;
+	    }
+	    (*zserver)->getfullrecord(query, querytype, field, i, doctitle, doctext, err);
+	    // check err value!
+	    docs_here->metadata["Title"].values.push_back(doctitle);	    
+	  } else {
+	    docs_here->metadata[*fields_here].values.push_back(g_EmptyText);
+	  }
+	  ++fields_here;
+	} // end of inner while loop
+	++docs_here;
+      } // end of outer while loop
+    } // end of if (!request.fields.empty())
+    
+    else { // request.fields empty: return all metadata for about page or query
+      // we'll only put it in the first docInfo.
+      collectionmeta_map::iterator colmeta_here=info.collectionmeta.begin();
+      collectionmeta_map::iterator colmeta_end=info.collectionmeta.end();
+      while (colmeta_here!=colmeta_end) {
+	response.docInfo[0].metadata[(*colmeta_here).first].
+	  values.push_back(((*colmeta_here).second)[g_EmptyText]);
+	++colmeta_here;
+      }
+
+      // check if "collectionextra" metadata is set. If it isn't, we should
+      // create connection to target to get it.
+      if (info.collectionmeta.find("collectionextra")==colmeta_end) {
+	// it hasn't been set yet...
+	text_t abouttext="<B>Server Online</B><br>\n";
+	abouttext+=(*zserver)->getzAbout();
+	// add in the "About" text we read in from config file.
+	// how do we incorporate multi-lingual metadata?
+	abouttext+="<P>\n";
+	text_t tmpabout;
+
+	if ((*zserver)->getcfgAbout("en", tmpabout)==true)
+	  abouttext+=tmpabout;
+	
+	(*zserver)->setMeta("collectionextra",abouttext);
+	response.docInfo[0].metadata["collectionextra"].values.push_back(abouttext);
+      }
+    } // end of else
+
+    // do indices' names, regardless of whether asked for or not...
+    if (!response.docInfo.empty()) {
+     response.docInfo[0].metadata[".author"].values.push_back("author fields");
+      response.docInfo[0].metadata[".title"].values.push_back("title fields");
+      response.docInfo[0].metadata[".any"].values.push_back("any fields");
+    }
+  } //end of  if (... & FRmetadata) ...
+}
+
+
+
+void z3950proto::get_document (const text_t &collection,
+			       const DocumentRequest_t &request,
+			       DocumentResponse_t &response,
+			       comerror_t &err, ostream &logout) {
+
+  err=noError;
+
+  // get relevant "collection"
+  z3950_proxy_array::iterator zserver = zservers.begin();
+  z3950_proxy_array::iterator zend = zservers.end();
+  while (zserver != zend) {
+    if((*zserver)->getName()==collection) {
+      break;
+    }
+    ++zserver;
+  }
+  // now have collection in zserver.
+
+  /* docresponse consists of
+     text_t response.doc */
+  text_t title="unneeded";
+  text_t doctext;
+  text_t query; // this should not be needed, as we have already connected to 
+  // get the  title....
+  int querytype = 1; //ditto...
+  text_t field;  // ditto...
+  (*zserver)->getfullrecord(query,querytype,field,request.OID.getint(),
+			    title,doctext,err);
+  // check return value of above? (false=>not connected) 
+  if (err==noError)
+    response.doc=doctext;
+  else {
+    // could print out different messages based on error type....
+    response.doc="<h2>Error</h2>There was an error while connecting to the ";
+    response.doc+="z39.50 server (ie target). Most likely this was a \n";
+    response.doc+="\"Connection Refused\" error.\n";
+
+  }
+  if (0) {
+    err=protocolError;
+    logout << "Some error\n";
+  }
+}
+
+// sets issearchable to true if the given colection is searchable
+void z3950proto::is_searchable (const text_t &/*collection*/, bool &issearchable, 
+				comerror_t &err, ostream &/*logout*/) {
+  issearchable = true; // assume all collections are searchable?
+  err = noError;
+}
+
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950proto.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950proto.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950proto.h	(revision 16561)
@@ -0,0 +1,98 @@
+/**********************************************************************
+ *
+ * z3950proto.h --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950PROTO_H
+#define Z3950PROTO_H
+
+#include "recptproto.h"
+#include "z3950proxy.h"
+
+class z3950proto : public recptproto {
+protected:
+  int zserver_count;
+  z3950_proxy_array zservers;
+public:
+  z3950proto();
+  virtual ~z3950proto();
+
+  int getServerCount() { return zserver_count;}
+  /*
+  void add_collectserver (collectserver *thecserver) 
+        {cservers.addcollectserver(thecserver);}
+  */
+  
+  void add_server(z3950_proxy& zserver);
+
+  void read_config_file(const text_t &filename, const text_t &errfile);
+
+  void configure (const text_t &key, const text_tarray &cfgline);
+
+  bool init (ostream &logout);
+
+  text_t get_protocol_name (comerror_t &/*err*/) {return "z3950proto";}
+
+  void get_collection_list (text_tarray &collist, comerror_t &err, 
+			    ostream &logout);
+
+  void has_collection (const text_t &collection, bool &hascollection, 
+		       comerror_t &err, ostream &logout);
+  
+  void ping (const text_t &collection, bool &wassuccess, 
+	     comerror_t &err, ostream &logout);
+  
+  void get_collectinfo (const text_t &collection, 
+			ColInfoResponse_t &collectinfo,
+			comerror_t &err, ostream &logout);
+  
+  void get_filterinfo (const text_t &collection,
+		       InfoFiltersResponse_t &response,
+		       comerror_t &err, ostream &logout);
+  
+  /*  void get_info (const text_t &collection, const text_tset &metadata,
+      FilterResponse_t &response, ostream &logout);
+  */
+
+  void get_filteroptions (const text_t &collection,
+                          const InfoFilterOptionsRequest_t &request,
+                          InfoFilterOptionsResponse_t &response, 
+                          comerror_t &err, ostream &logout);
+  
+  void filter (const text_t &collection,
+	       FilterRequest_t &request,
+	       FilterResponse_t &response,
+	       comerror_t &err, ostream &logout);
+  
+  void get_document (const text_t &collection,
+		     const DocumentRequest_t &request,
+		     DocumentResponse_t &response,
+		     comerror_t &err, ostream &logout);
+  
+  void is_searchable (const text_t &collection, 
+			      bool &issearchable, 
+			      comerror_t &err, ostream &logout);
+
+};
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.cpp	(revision 16561)
@@ -0,0 +1,365 @@
+/**********************************************************************
+ *
+ * z3950proxy.cpp --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#include "z3950proxy.h"
+#include "comtypes.h"
+#include <stdio.h>
+
+// z39.50 yaz stuff
+#define min(a,b) (((a)<(b))?(a):(b))
+
+extern "C" {
+#include "yaz_zclient.h"
+}
+
+/*** 
+     each z39.50 server+database pair is a GSDL collection.
+***/
+
+z3950_proxy::z3950_proxy() {
+  info=NULL;
+  connected=false;
+  titles=NULL;
+  gotsinglerecord=false;
+  // for now, assume that all records will have text associated with them.
+  meta["hastxt"]="1";
+  // for now, assume we don't want ANY DocumentButtons.
+  format["DocumentButtons"]=g_EmptyText;
+}
+
+z3950_proxy::~z3950_proxy() {
+}
+
+void z3950_proxy::setMeta(const text_t &key, const text_t &value) {
+  meta[key]=value;
+}
+
+void z3950_proxy::setName(const text_t &newname) {
+  title=newname;
+  meta["collectionname"]=newname;
+}
+
+void z3950_proxy::addcfgAbout(const text_t &lang, const text_t &abouttext) {
+    about[lang]=abouttext;
+}
+
+bool z3950_proxy::getcfgAbout(const text_t &lang, text_t &abouttxt) {
+  text_tmap::iterator it;
+  it=about.find(lang);
+  if (it==about.end()) return (false);
+  abouttxt=((*it).second);
+  return (true);
+}
+
+
+// now functions that actually talk over the tcp connection.
+
+// create a tcp connection to the associated target. Currently, this will
+// re-initialise if we are already connected.
+bool z3950_proxy::connect() {
+  text_t server_and_port;
+  char *zserverinfo;
+
+  server_and_port=info->host+":"+info->port;
+  // remember that info.name is the database name
+
+  z_initialize();
+  char* serv_str=server_and_port.getcstr();
+  char* name_str=info->name.getcstr();
+  int retval=z_cmd_open(serv_str,name_str);
+  delete []serv_str;
+  delete []name_str;
+  if (retval==1)
+    // we got a connection error
+    return false;
+
+  // get initialisation response.
+  z_getnextAPDU();
+  zserverinfo=z_get_initResponse();
+  if (zserverinfo!=NULL) {
+    z_initstr.appendcstr(zserverinfo);
+  }
+  free(zserverinfo);
+
+  connected=true;
+  return true;
+}
+
+void z3950_proxy::parseQuery(const text_t &query, 
+			      const int querytype,
+			      const text_t &fields,
+			      text_t &parsed_query) {
+  /****** FIXME *****/
+  // We need to format the query string into RPN - 
+  // by just passing it like this, it will only work for simple queries.
+  // This will require us to actually come up with a query syntax and 
+  // a parser. For now, we'll just do an "AND" query for all terms
+  // But look at Common Command Language (CCL) query syntax (ISO 8777).
+
+  // need to remove " chars from the query. We should really tell the server
+  // to do a phrase search on the terms that are between the "s, but we
+  // can't (easily) tell if the server can do that or not, 
+  // so we'll currently just do a query and then post-process. (not yet
+  // implemented........)
+
+  // we need to count number of terms separated by a space
+  char *ptr=query.getcstr();
+  int strlength=strlen(ptr);
+  bool inword=false;
+  int num_terms=0;
+
+
+  for (int i=0;i<strlength;++i) {
+    if (*(ptr+i)=='"') { // convert " to SPACE...
+      *(ptr+i)=' '; 
+    }
+    if (*(ptr+i)!=' ') {
+      if (inword==false) {
+	inword=true;
+	++num_terms;
+      }
+    }
+    else { // ptr+i is a space
+      inword=false;
+      }
+  }
+  
+  // set the field(s) to search on - main ones include:
+  //  1016  => Any
+  //  1     => (Personal) Name
+  //  4     => Title
+  //  21    => Subject Heading
+  //  45    => Subject precis
+  // Note I have no idea how these actually work - I think some servers
+  // only have limited fields, and map all subject-type requests into that
+  // subject field, etc.
+
+  parsed_query="@attr 1=";
+  if (fields==".author")
+    parsed_query+="1 ";
+  else if (fields==".title")
+    parsed_query+="4 ";
+  else // fields==".any"
+    parsed_query+="1016 ";
+
+  // querytype=1 => ranked/or,  =2 => boolean/and
+  // append "@and" for each term after the first
+  {
+    char and_str[]="@and ";
+    char or_str[]="@or ";
+    char *q_type;
+    if (querytype==1) q_type=or_str; else q_type=and_str;
+    for (int i=1;i<num_terms;++i)
+      parsed_query+=q_type;
+    // append the actual query
+    parsed_query+=ptr;
+  }
+  delete []ptr;
+}
+
+text_tarray *z3950_proxy::getrecordTitles(const text_t &query, 
+					   const int querytype,
+					   const text_t &fields,
+					   int first, int count,
+					   int *nummatches, comerror_t &err) {
+  /* NOTE!!!!!! Because this code currently only works in cgi-bin mode,
+     we only ever do one request. Therefore, it is CURRENTLY OK to store
+     (cache) the retrieved titles, because if this function is ever called
+     more than once, the arguments will be the same each time.
+     (I think :)
+  */
+
+  char **c_str_titles; 
+  int i;
+  int last;
+
+  if (gotsinglerecord==true) {
+    /* If true, then this whole execution was done to retrieve a single
+       document. Therefore, the list of titles of all records matching the
+       query isn't actually required. It's just that for some reason our
+       filter (z3950proto::filter) gets called at least 7 times, with the
+       7th being a "QueryFilter" for some reason... */
+    nummatches=0;
+    return NULL; /* shouldn't really return NULL, but nummatches is checked
+		    first (I hope) */
+  }
+
+  //  if (titles!=NULL) delete (titles);
+  if (titles!=NULL) return titles;
+  titles=new text_tarray;
+
+  /* check if connected */
+  if (connected==false) 
+    if (connect()==false) {
+      // we could not connect.
+      err=protocolError;
+      return (NULL);
+    }
+  
+
+  text_t expanded_query=g_EmptyText;
+  parseQuery(query,querytype,fields,expanded_query);
+
+  char* query_str=expanded_query.getcstr();
+  // following functions defined in yaz_zclient.c
+  *nummatches=z_cmd_dosearch(query_str); // returns # found, -1 on err.
+  delete []query_str;
+  if (*nummatches<=0) {
+    if (*nummatches==0) {
+      // no matches
+      return (NULL);
+    } else  if (*nummatches==-1) {
+      // prefix query error
+      err=protocolError;
+      return (NULL);
+    }  else if (*nummatches==-2) {
+      // sendsearchRequest not answered by searchResponse
+      err=protocolError;
+      return (NULL);
+    }
+  }
+  // could do a sort eventually, eg on date, title, etc.
+  // (non-existent function)    z_sort(field, asc|desc);
+  /* min of (count, first + (*nummatches) ) */
+  // z_getrecordTitles ( first, howmany )
+  c_str_titles=z_getrecordTitles(first,min(count,*nummatches-first+1));
+  if (c_str_titles==NULL) {
+    // an error occurred. we need a logout/err as an arg
+    return (NULL);
+  }
+  if (c_str_titles[0]==0) {
+    // no matches.
+    return (NULL);
+  }
+  last=(int)c_str_titles[0];
+  for (i=1;i<=last;++i) {
+    titles->push_back(c_str_titles[i]);
+    free(c_str_titles[i]);
+  }
+  free(c_str_titles);
+  return (titles);
+  
+}
+
+bool z3950_proxy::getfullrecord(const text_t &query, 
+				 const int querytype,
+				 const text_t &fields,
+				 const int ID, 
+				 text_t &rettitle, 
+				 text_t &rettext, comerror_t &err) {
+
+  static char **c_str_titles=NULL;
+  static char *fulltext=NULL;
+  /* NOTE!!!!!! Because this code currently only works in cgi-bin mode,
+     we only ever do one request. Therefore, it is CURRENTLY OK to store
+     (cache) the retrieved titles, because if this function is ever called
+     more than once, the arguments will be the same each time.
+     (I think :)
+  */
+
+  gotsinglerecord=true; // well, not yet, but we've been called...
+
+  if (connected==false) {
+    if (connect()==false) {
+      // error connecting...
+      err=protocolError;
+      return (false);
+    }
+    // since we have just re-connected, we need to do the
+    // query again.
+
+    text_t expanded_query=g_EmptyText;
+    parseQuery(query,querytype,fields,expanded_query);
+
+    char* query_str=expanded_query.getcstr();
+    int returned=z_cmd_dosearch(query_str);
+    delete []query_str;
+    if (returned<=0) {
+      // 0 => none.
+      // <0 => error
+      err=protocolError;
+      return (false);
+    }
+  }
+
+  if (c_str_titles==NULL)
+    c_str_titles=z_getrecordTitles(ID,1); // check this return value.
+  
+  if (rettitle!="unneeded") {
+    //int dummy;
+    if (c_str_titles!=NULL && (int)c_str_titles[0]==1) {
+      rettitle.setcstr(c_str_titles[1]); // and check this 
+      //////      free (c_str_titles); - we want to "cache" it
+    } else {
+      // we didn't get something....
+      rettitle="Nothing Returned...";
+    }
+  }
+
+  if (fulltext==NULL)
+    // get the text
+    fulltext=z_getfullRecord(ID);
+
+  if (rettext!="unneeded") {
+    rettext.setcstr(fulltext);
+  }
+  return (true);
+  }
+
+text_t &z3950_proxy::getzAbout() {
+  text_t zserverresp;
+
+  // Assume we have not yet connected, so that must be done here.
+  if (connected==true)
+    return (z_initstr);
+  
+  // we need to create the tcp connection to the target (server)  
+  //  z_initstr=new text_t;
+  
+  if (connect()==false) {
+    z_initstr.setcstr("<H2>Server offline</H2>Error - could not connect to server <B>");
+    z_initstr += info->host;
+    z_initstr += "</B> on port ";
+    z_initstr += info->port;
+    z_initstr += "\n";
+    return (z_initstr);
+  }
+  
+  // z_initstr currently contains the target's response. We want to
+  // PREPEND the following information.
+  zserverresp=z_initstr;
+  z_initstr="Internet server: <b>";
+  z_initstr+=info->host;
+  z_initstr+="</b> on port ";
+  z_initstr+=info->port;
+  z_initstr+=".<br>\n";
+  z_initstr+=zserverresp;
+
+  // should close /******* WHAT IF DOING A QUERY!??!?!? ********/
+  // z_cmd_close(0);
+  // connected=false;
+  return (z_initstr);
+}
+
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950proxy.h	(revision 16561)
@@ -0,0 +1,79 @@
+/**********************************************************************
+ *
+ * z3950proxy.h --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950PROXY_H
+#define Z3050PROXY_H
+
+#include "comtypes.h"
+
+// each "proxy to server" (i.e. collection) consists of a {z39.50 server + database} pair.
+class z3950_proxy {
+ protected:
+  text_t title;    // Descriptive name
+  text_t z_initstr; // a string returned when the connection is initialised.
+  text_tmap meta;  // metadata for this collection (ie server/database pair)
+  text_tmap format;
+  text_tmap about; // langauge/abouttext pair for collection.
+  ShortColInfo_t *info; // has  (database) name, host and port
+  text_tarray *titles; 
+  bool connect();
+  bool connected; // tcp connection to the z39.50 server (target)
+  bool gotsinglerecord; // if we do, then we don't need all titles...
+
+ public:
+  z3950_proxy();
+  ~z3950_proxy();
+  void setName(const text_t &newname);
+  const text_t getName() {return title;}
+  void setMeta(const text_t &key, const text_t &value);
+  const text_tmap *getMeta() {return &meta;}
+  const text_tmap *getFormat() {return &format;}
+  void setInfo(ShortColInfo_t *newinfo) {info=newinfo;}
+  const ShortColInfo_t *getInfo() {return info;}
+  void addcfgAbout(const text_t &lang, const text_t &abouttext);
+  bool getcfgAbout(const text_t &lang, text_t &abouttext);
+  // now the functions that actually talk to the target (ie zserver) over tcp
+  text_t &getzAbout(); // gets any name, id, MOTD, etc from server.
+  text_tarray *getrecordTitles(const text_t &query,
+			       const int andor_querytype,
+			       const text_t &fields,
+			       const int first, const int count,
+			       int *nummatches, comerror_t &err);
+  /* only get one record at a time? (Eventually we could buffer them, but
+     for now... */
+  bool getfullrecord(const text_t &query, const int andor_querytype,
+		     const text_t &fields,
+		     const int ID, text_t &rettitle, 
+		     text_t &rettext,comerror_t &err);
+ private:
+  void parseQuery(const text_t &query, const int andor_querytype, 
+		  const text_t &fields, 
+		  text_t &parsed_query);
+  text_t previousquery; // for caching purposes
+};
+
+typedef  vector<z3950_proxy *>  z3950_proxy_array;
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950server.cpp
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950server.cpp	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950server.cpp	(revision 16561)
@@ -0,0 +1,927 @@
+
+
+// Standard headers
+
+#if defined(GSDL_USE_OBJECTSPACE)
+#  include <ospace\std\iostream>
+#  include <ospace\std\fstream>
+#  include <ospace\std\sstream>
+#elif defined(GSDL_USE_IOS_H)
+#  include <iostream.h>
+#  include <fstream.h>
+#  include <strstream.h>
+#else
+#  include <iostream>
+#  include <fstream>
+#  include <sstream>
+#endif
+
+#include <string>
+#include <list>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+
+// Greenstone headers
+#include "fileutil.h"
+#include "comtypes.h"
+#include "nullproto.h"
+
+// Z39.50 server headers
+#include "z3950parser.h"
+#include "z3950explain.h"
+
+#include "z3950server.h"
+
+// Dublin Core -> MARC (d2m) headers and source files
+#include "d2m4gs.h"
+
+// these globals are local to each thread,
+// one Z39.50 connection = one thread.
+text_t gsdlhome; // this is specified externally when Greenstone is installed
+list<FilterResponse_t> Response_tlist; // list of response sets
+list<text_t> Response_tlist_colnames;  // collection names for the above
+list<int> Response_tlist_sizes;        // sizes (number of records) for the above
+z3950Server *Server   = NULL;
+collectset  *Cservers = NULL;
+nullproto   *Protocol = NULL;
+
+map<text_t, gsdlCollection> Collection_map; // info and tools for collections
+map<text_t, text_t> Resultsets;             // mapping from ResultSetId to GSQuery 
+
+int z3950_verbosity_ = 3;
+
+// *** initialize things here ***
+bend_initresult *bend_init(bend_initrequest *q)
+{
+  // *** called when client connects to the server ***
+  if (z3950_verbosity_ > 1) {
+    cerr << "Entering bend_init" << endl;
+  }
+
+  Protocol = new nullproto();  
+  Cservers = new collectset(gsdlhome);
+  Protocol->set_collectset(Cservers);
+  
+  cerr << "Starting Z39.50 Server ..." << endl;
+  
+  Server = new z3950Server(Protocol, gsdlhome);
+  cerr << "Server constructed" << endl;
+  
+  const bool status = Server->initialise();
+  cerr << "Initialised server: return status = " << status << endl;
+  
+  text_tarray collist;
+  comerror_t err;
+  Protocol->get_collection_list( collist, err, cout );
+
+  // display the list of available Greenstone collections
+  text_tarray::iterator cols_here=collist.begin();
+  text_tarray::iterator cols_end=collist.end();
+  while (cols_here != cols_end) {
+    gsdlCollection tmpcol(*cols_here, gsdlhome);
+    
+    Collection_map[*cols_here] = tmpcol;
+    
+    if (tmpcol.z3950Capeable()) {
+      cerr << "  " << tmpcol.getName() << "\tis Z39.50 capable" << endl;
+    }
+    cols_here++;
+  }
+
+    
+  bend_initresult *r = (bend_initresult *) odr_malloc (q->stream, sizeof(*r));
+  //static char *dummy = "Hej fister"; // what's this for?
+  
+  r->errcode = 0;
+  r->errstring = 0;
+  r->handle = Protocol;
+  q->bend_sort = ztest_sort;       /* register sort handler */
+  q->bend_search = ztest_search;   /* register search handler */
+  q->bend_present = ztest_present; /* register present handle */
+  q->bend_esrequest = ztest_esrequest;
+  q->bend_delete = ztest_delete;
+  q->bend_fetch = ztest_fetch;
+  q->bend_scan = ztest_scan;
+
+  return r;
+}
+
+// *** perform query, get number of hits ***
+int ztest_search (void *handle, bend_search_rr *rr)
+{
+  // *** called when client issues a "find" command
+  if (z3950_verbosity_>1) {
+    cerr << "Entering ztest_search" << endl;
+  }
+
+  Response_tlist.clear();
+  Response_tlist_colnames.clear();
+  Response_tlist_sizes.clear();
+ 
+  comerror_t err;
+  FilterRequest_t request;
+
+  request.filterName = "QueryFilter";
+  request.filterResultOptions = FROID | FRtermFreq | FRmetadata;  
+
+  OptionValue_t option;
+
+  // how many results to get. grouping all results together into
+  // one set instead of breaking up into "pages". 
+  // note: see if Z39.50 has a concept of pages.
+  option.name = "StartResults";
+  option.value = "1";
+  request.filterOptions.push_back (option);
+  option.name = "EndResults";
+  option.value = MAXHITS;
+  request.filterOptions.push_back (option);
+  option.name = "Maxdocs";
+  option.value = MAXHITS;
+  request.filterOptions.push_back (option);
+
+  text_t GSQuery = ZQueryToGSQuery(rr);
+  option.name = "Term";
+  option.value = GSQuery;
+  request.filterOptions.push_back (option);
+  Resultsets[Resultsets.size()+1] = GSQuery;
+
+  option.name = "QueryType";
+  option.value = "boolean";
+  request.filterOptions.push_back (option);
+
+  option.name = "MatchMode";
+  option.value = "all";
+  request.filterOptions.push_back (option);
+
+  option.name = "Casefold";
+  option.value = "true";
+  request.filterOptions.push_back (option);
+
+  option.name = "Stem";
+  option.value = "false";
+  request.filterOptions.push_back (option);
+
+  option.name = "AccentFold";
+  option.value = "false";
+  request.filterOptions.push_back (option);
+  
+  // MG queries don't need this, MGPP queries do.
+  // doesn't do any harm to include it anyway.
+  // note: not all collections have document-level granularity...
+  option.name = "Level";
+  option.value = "Doc";
+  request.filterOptions.push_back (option);
+
+  // Z39.50 supports the searching of multiple databases
+  rr->hits = 0;
+  for (int i = 0; i < rr->num_bases; i++) {
+    FilterResponse_t response;
+    cerr << "Searching basename = " << rr->basenames[i] << endl;
+    // intercept Explain requests
+    if (strcmp(rr->basenames[i], "IR-Explain-1")==0) {
+      //ColInfoResponse_t collectinfo;
+      //Protocol->get_collectinfo("csbib", collectinfo, err, cerr);
+      //response.numDocs = 1;
+      //ResultDocInfo_t docinfo;
+      //docinfo.metadata["foo"].values.push_back("bar");
+      getExplainInfo(rr, response, err);
+      //response.docInfo.push_back(docinfo);
+    } else {
+      cerr << "calling filter..." << endl;
+      Protocol->filter(rr->basenames[i], request, response, err, cerr);
+      cerr << "returned from filter." << endl;
+      if (response.numDocs > MAXHITS) {
+	response.numDocs = MAXHITS;
+      }
+    }
+    rr->hits += response.numDocs;
+    Response_tlist.push_back(response);
+    text_t basename = rr->basenames[i];
+    Response_tlist_colnames.push_back(basename);
+    Response_tlist_sizes.push_back(response.numDocs);
+    cerr << "search complete." << endl;
+  }    
+
+  if (rr->hits > MAXHITS) rr->hits = MAXHITS;
+  return 0;
+}
+
+// *** extra code for showing results, not needed ***
+int ztest_present (void *handle, bend_present_rr *rr)
+{
+  // *** called when client issues a "show" command, 1st of 5
+  if (z3950_verbosity_>1) {
+    cerr << "entering ztest_present" << endl;
+  }
+  return 0;
+}
+
+int ztest_esrequest (void *handle, bend_esrequest_rr *rr)
+{
+
+    if (z3950_verbosity_>1) {
+      cerr << "Entering ztest_esrequest" << endl;
+    }
+    yaz_log(LOG_LOG, "function: %d", *rr->esr->function);
+    if (rr->esr->packageName)
+    	yaz_log(LOG_LOG, "packagename: %s", rr->esr->packageName);
+    yaz_log(LOG_LOG, "Waitaction: %d", *rr->esr->waitAction);
+
+    if (!rr->esr->taskSpecificParameters)
+    {
+        yaz_log (LOG_WARN, "No task specific parameters");
+    }
+    else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder)
+    {
+    	Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder;
+	yaz_log (LOG_LOG, "Received ItemOrder");
+	switch (it->which)
+	{
+#ifdef ASN_COMPILED
+	case Z_IOItemOrder_esRequest:
+#else
+	case Z_ItemOrder_esRequest:
+#endif
+	{
+	    Z_IORequest *ir = it->u.esRequest;
+	    Z_IOOriginPartToKeep *k = ir->toKeep;
+	    Z_IOOriginPartNotToKeep *n = ir->notToKeep;
+	    
+	    if (k && k->contact)
+	    {
+	        if (k->contact->name)
+		    yaz_log(LOG_LOG, "contact name %s", k->contact->name);
+		if (k->contact->phone)
+		    yaz_log(LOG_LOG, "contact phone %s", k->contact->phone);
+		if (k->contact->email)
+		    yaz_log(LOG_LOG, "contact email %s", k->contact->email);
+	    }
+	    if (k->addlBilling)
+	    {
+	        yaz_log(LOG_LOG, "Billing info (not shown)");
+	    }
+	    
+	    if (n->resultSetItem)
+	    {
+	        yaz_log(LOG_LOG, "resultsetItem");
+		yaz_log(LOG_LOG, "setId: %s", n->resultSetItem->resultSetId);
+		yaz_log(LOG_LOG, "item: %d", *n->resultSetItem->item);
+	    }
+#ifdef ASN_COMPILED
+	    if (n->itemRequest)
+	    {
+		Z_External *r = (Z_External*) n->itemRequest;
+		ILL_ItemRequest *item_req = 0;
+		ILL_Request *ill_req = 0;
+		if (r->direct_reference)
+		{
+		    oident *ent = oid_getentbyoid(r->direct_reference);
+		    if (ent)
+			yaz_log(LOG_LOG, "OID %s", ent->desc);
+		    if (ent && ent->value == VAL_ISO_ILL_1)
+		    {
+			yaz_log (LOG_LOG, "ItemRequest");
+			if (r->which == ODR_EXTERNAL_single)
+			{
+			    odr_setbuf(rr->decode,
+				       (char*)(r->u.single_ASN1_type->buf),
+				       r->u.single_ASN1_type->len, 0);
+			    
+			    if (!ill_ItemRequest (rr->decode, &item_req, 0, 0))
+			    {
+				yaz_log (LOG_LOG,
+                                    "Couldn't decode ItemRequest %s near %d",
+                                       odr_errmsg(odr_geterror(rr->decode)),
+                                       odr_offset(rr->decode));
+                                yaz_log(LOG_LOG, "PDU dump:");
+                                odr_dumpBER(yaz_log_file(),
+                                     (const char*)(r->u.single_ASN1_type->buf),
+                                     r->u.single_ASN1_type->len);
+                            }
+			    if (rr->print)
+			    {
+				ill_ItemRequest (rr->print, &item_req, 0,
+                                    "ItemRequest");
+				odr_reset (rr->print);
+ 			    }
+			}
+			if (!item_req && r->which == ODR_EXTERNAL_single)
+			{
+			    yaz_log (LOG_LOG, "ILLRequest");
+			    odr_setbuf(rr->decode,
+				       (char*)(r->u.single_ASN1_type->buf),
+				       r->u.single_ASN1_type->len, 0);
+			    
+			    if (!ill_Request (rr->decode, &ill_req, 0, 0))
+			    {
+				yaz_log (LOG_LOG,
+                                    "Couldn't decode ILLRequest %s near %d",
+                                       odr_errmsg(odr_geterror(rr->decode)),
+                                       odr_offset(rr->decode));
+                                yaz_log(LOG_LOG, "PDU dump:");
+                                odr_dumpBER(yaz_log_file(),
+                                     (const char*)(r->u.single_ASN1_type->buf),
+                                     r->u.single_ASN1_type->len);
+                            }
+			    if (rr->print)
+                            {
+				ill_Request (rr->print, &ill_req, 0,
+                                    "ILLRequest");
+				odr_reset (rr->print);
+			    }
+			}
+		    }
+		}
+		if (item_req)
+		{
+		    yaz_log (LOG_LOG, "ILL protocol version = %d",
+			     *item_req->protocol_version_num);
+		}
+	    }
+#endif
+	}
+	break;
+	}
+    }
+    else if (rr->esr->taskSpecificParameters->which == Z_External_update)
+    {
+    	Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update;
+	yaz_log (LOG_LOG, "Received DB Update");
+	if (up->which == Z_IUUpdate_esRequest)
+	{
+	    Z_IUUpdateEsRequest *esRequest = up->u.esRequest;
+	    Z_IUOriginPartToKeep *toKeep = esRequest->toKeep;
+	    Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep;
+	    
+	    yaz_log (LOG_LOG, "action");
+	    if (toKeep->action)
+	    {
+		switch (*toKeep->action)
+		{
+		case Z_IUOriginPartToKeep_recordInsert:
+		    yaz_log (LOG_LOG, " recordInsert");
+		    break;
+		case Z_IUOriginPartToKeep_recordReplace:
+		    yaz_log (LOG_LOG, " recordUpdate");
+		    break;
+		case Z_IUOriginPartToKeep_recordDelete:
+		    yaz_log (LOG_LOG, " recordDelete");
+		    break;
+		case Z_IUOriginPartToKeep_elementUpdate:
+		    yaz_log (LOG_LOG, " elementUpdate");
+		    break;
+		case Z_IUOriginPartToKeep_specialUpdate:
+		    yaz_log (LOG_LOG, " specialUpdate");
+		    break;
+		default:
+		    yaz_log (LOG_LOG, " unknown (%d)", *toKeep->action);
+		}
+	    }
+	    if (toKeep->databaseName)
+	    {
+		yaz_log (LOG_LOG, "database: %s", toKeep->databaseName);
+		if (!strcmp(toKeep->databaseName, "fault"))
+		{
+		    rr->errcode = 109;
+		    rr->errstring = toKeep->databaseName;
+		}
+		if (!strcmp(toKeep->databaseName, "accept"))
+		    rr->errcode = -1;
+	    }
+	    if (notToKeep)
+	    {
+		int i;
+		for (i = 0; i < notToKeep->num; i++)
+		{
+		    Z_External *rec = notToKeep->elements[i]->record;
+
+		    if (rec->direct_reference)
+		    {
+			struct oident *oident;
+			oident = oid_getentbyoid(rec->direct_reference);
+			if (oident)
+			    yaz_log (LOG_LOG, "record %d type %s", i,
+				     oident->desc);
+		    }
+		    switch (rec->which)
+		    {
+		    case Z_External_sutrs:
+			if (rec->u.octet_aligned->len > 170)
+			    yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
+				     rec->u.sutrs->len,
+				     rec->u.sutrs->buf);
+			else
+			    yaz_log (LOG_LOG, "%d bytes:\n%s",
+				     rec->u.sutrs->len,
+				     rec->u.sutrs->buf);
+                        break;
+		    case Z_External_octet        :
+			if (rec->u.octet_aligned->len > 170)
+			    yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
+				     rec->u.octet_aligned->len,
+				     rec->u.octet_aligned->buf);
+			else
+			    yaz_log (LOG_LOG, "%d bytes\n%s",
+				     rec->u.octet_aligned->len,
+				     rec->u.octet_aligned->buf);
+		    }
+		}
+	    }
+	}
+    }
+    else
+    {
+        yaz_log (LOG_WARN, "Unknown Extended Service(%d)",
+		 rr->esr->taskSpecificParameters->which);
+	
+    }
+    return 0;
+}
+
+int ztest_delete (void *handle, bend_delete_rr *rr)
+{
+  // *** called when client issues a "delete" command
+  if (z3950_verbosity_>1) {
+    cerr << "Entering ztest_delete" << endl;
+  }
+  if (rr->num_setnames == 1 && !strcmp (rr->setnames[0], "1"))
+    rr->delete_status = Z_DeleteStatus_success;
+  else
+    rr->delete_status = Z_DeleteStatus_resultSetDidNotExist;
+  return 0;
+}
+
+// *** do we want to sort the results? ***
+/* Our sort handler really doesn't sort... */
+int ztest_sort (void *handle, bend_sort_rr *rr)
+{
+  if (z3950_verbosity_>1) {
+    cerr << "entering ztest_sort" << endl;
+  }
+  rr->errcode = 0;
+  rr->sort_status = Z_SortStatus_success;
+  return 0;
+}
+
+static int atoin (const char *buf, int n)
+{
+  // *** called when client issues a "show" command, 5th of 5
+  // *** NOT called if the "show" command has an invalid argument
+    if (z3950_verbosity_>1) {
+      cerr << "Entering atoin" << endl;
+    }
+    int val = 0;
+    while (--n >= 0)
+    {
+        if (isdigit(*buf))
+            val = val*10 + (*buf - '0');
+        buf++;
+    }
+    return val;
+}
+
+
+bool get_sutrs_record (int recnum, text_t &MetadataResult, text_t &CollectionName)
+{
+  if (z3950_verbosity_>1) {
+    cerr << "Entering get_sutrs_record" << endl;
+  }
+  int total_records = 0;
+
+  list<FilterResponse_t>::iterator RESPONSE_here = Response_tlist.begin();
+  list<FilterResponse_t>::iterator RESPONSE_end = Response_tlist.end();
+  list<text_t>::iterator RESPONSE_COLNAMES_here = Response_tlist_colnames.begin();
+  list<text_t>::iterator RESPONSE_COLNAMES_end = Response_tlist_colnames.end();
+  list<int>::iterator RESPONSE_SIZES_here = Response_tlist_sizes.begin();
+  list<int>::iterator RESPONSE_SIZES_end = Response_tlist_sizes.end();
+
+  RESPONSE_here = Response_tlist.begin();
+  RESPONSE_end = Response_tlist.end();
+  while (RESPONSE_here != RESPONSE_end) {
+    total_records += (*RESPONSE_here).numDocs;
+    RESPONSE_here++;
+  }
+
+  // check that the index is within bounds
+  if (recnum < 1 || recnum > total_records) {
+    cerr << "get_sutrs_record failed" << endl;
+    return false; // failed
+  } else {
+    FilterResponse_t response;
+    // iterate through Response_tlist to find the right response
+    RESPONSE_here = Response_tlist.begin();
+    RESPONSE_end = Response_tlist.end();
+    RESPONSE_COLNAMES_here = Response_tlist_colnames.begin();
+    RESPONSE_COLNAMES_end = Response_tlist_colnames.end();
+    RESPONSE_SIZES_here = Response_tlist_sizes.begin();
+    RESPONSE_SIZES_end = Response_tlist_sizes.end();
+    int n = 0;
+
+    while (RESPONSE_here != RESPONSE_end) {
+      n += *RESPONSE_SIZES_here;
+      if (n >= recnum) {
+	response = *RESPONSE_here;
+	CollectionName = *RESPONSE_COLNAMES_here;
+	recnum = *RESPONSE_SIZES_here - (n - recnum);
+	break;
+      }
+      // these should all have the same number of elements,
+      // so only need to check that one is within bounds.
+      // may want to combine these into a larger data structure later.
+      RESPONSE_here++;
+      RESPONSE_COLNAMES_here++;
+      RESPONSE_SIZES_here++;
+    }
+
+    // locate the desired record in the response 
+   
+    MetadataInfo_tmap::iterator METAFIELD_here = response.docInfo[recnum-1].metadata.begin();
+    MetadataInfo_tmap::iterator METAFIELD_end = response.docInfo[recnum-1].metadata.end();
+
+    while (METAFIELD_here!=METAFIELD_end) {
+      text_tarray::iterator METAVALUE_here = METAFIELD_here->second.values.begin();
+      text_tarray::iterator METAVALUE_end = METAFIELD_here->second.values.end();
+      while (METAVALUE_here!=METAVALUE_end) {
+	// construct a text_t containing the record:
+	// there are multiple metadata fields,
+	// each field can have multiple values.
+	/*
+	// don't include fields starting with a lowercase letter,
+	// these are system fields.
+	// later: include these anyway, since Explain fields start with lowercase letters
+	if (METAFIELD_here->first[0] >= 'A' &&
+	    METAFIELD_here->first[0] <= 'Z') {
+	*/
+	  MetadataResult += METAFIELD_here->first; // field name
+	  MetadataResult += " ";
+	  MetadataResult += *METAVALUE_here; // field contents
+	  MetadataResult += "\n";
+	  //}
+	METAVALUE_here++;
+      }
+      METAFIELD_here++;
+    }
+    return true; // succeeded
+  }
+}
+
+bool get_marc_record (int recnum, list<metatag*> &Metatag_list, text_t &CollectionName)
+{
+  if (z3950_verbosity_>1) {
+    cerr << "Entering get_marc_record" << endl;
+  }
+  int total_records = 0;
+
+  list<FilterResponse_t>::iterator RESPONSE_here = Response_tlist.begin();
+  list<FilterResponse_t>::iterator RESPONSE_end = Response_tlist.end();
+  list<text_t>::iterator RESPONSE_COLNAMES_here = Response_tlist_colnames.begin();
+  list<text_t>::iterator RESPONSE_COLNAMES_end = Response_tlist_colnames.end();
+  list<int>::iterator RESPONSE_SIZES_here = Response_tlist_sizes.begin();
+  list<int>::iterator RESPONSE_SIZES_end = Response_tlist_sizes.end();
+
+  RESPONSE_here = Response_tlist.begin();
+  RESPONSE_end = Response_tlist.end();
+  while (RESPONSE_here != RESPONSE_end) {
+    total_records += (*RESPONSE_here).numDocs;
+    RESPONSE_here++;
+  }
+
+  // check that the index is within bounds
+  if (recnum < 1 || recnum > total_records) {
+    cerr << "get_marc_record failed" << endl;
+    return false; // failed
+  } else {
+    FilterResponse_t response;
+    // iterate through Response_tlist to find the right response
+    RESPONSE_here = Response_tlist.begin();
+    RESPONSE_end = Response_tlist.end();
+    RESPONSE_COLNAMES_here = Response_tlist_colnames.begin();
+    RESPONSE_COLNAMES_end = Response_tlist_colnames.end();
+    RESPONSE_SIZES_here = Response_tlist_sizes.begin();
+    RESPONSE_SIZES_end = Response_tlist_sizes.end();
+    int n = 0;
+    while (RESPONSE_here != RESPONSE_end) {
+      n += *RESPONSE_SIZES_here;
+      if (n >= recnum) {
+	response = *RESPONSE_here;
+	CollectionName = *RESPONSE_COLNAMES_here;
+	recnum = *RESPONSE_SIZES_here - (n - recnum);
+	break;
+      }
+      // these should all have the same number of elements,
+      // so only need to check that one is within bounds.
+      // may want to combine these into a larger data structure later.
+      RESPONSE_here++;
+      RESPONSE_COLNAMES_here++;
+      RESPONSE_SIZES_here++;
+    }
+
+    // locate the desired record in the response
+    
+    MetadataInfo_tmap::iterator METAFIELD_here = response.docInfo[recnum-1].metadata.begin();
+    MetadataInfo_tmap::iterator METAFIELD_end = response.docInfo[recnum-1].metadata.end();
+    
+    while (METAFIELD_here!=METAFIELD_end) {
+      text_tarray::iterator METAVALUE_here = METAFIELD_here->second.values.begin();
+      text_tarray::iterator METAVALUE_end = METAFIELD_here->second.values.end();
+      while (METAVALUE_here!=METAVALUE_end) {
+	if (METAFIELD_here->first[0] >= 'A' &&
+	    METAFIELD_here->first[0] <= 'Z') {
+	  metatag *tmptag = new metatag();
+	  tmptag->name = METAFIELD_here->first.getcstr();
+	  tmptag->type = "";
+	  tmptag->scheme = "";
+	  tmptag->value = (*METAVALUE_here).getcstr();
+	  Metatag_list.push_back(tmptag);
+	}
+	METAVALUE_here++;
+      }
+      METAFIELD_here++;
+    }
+    return true; // succeeded
+  }
+}
+
+// *** implement this ***
+int ztest_fetch(void *handle, bend_fetch_rr *r)
+{
+  // *** called when client issues a "show" command, 2nd of 5
+    if (z3950_verbosity_>1) {
+      cerr << "Entering ztest_fetch" << endl;
+    }
+    char *cp;
+    r->errstring = 0;
+    r->last_in_set = 0;
+    r->basename = "DUMMY"; // this is set below
+    r->output_format = r->request_format;
+
+    // *** use this until found a good way of mapping Greenstone
+    // *** metadata fields to USMARC fields
+    if (r->request_format == VAL_SUTRS)
+    {
+      // copies record into r, errcode will be 13 if fails
+      text_t MetadataResult;
+      text_t CollectionName;
+      if (!get_sutrs_record(r->number, MetadataResult, CollectionName)) {
+	r->errcode = 13;
+	return 0;
+      } else {			
+	r->len = MetadataResult.size();
+	r->record = (char*) odr_malloc(r->stream, r->len+1);
+	char *tmptxt = MetadataResult.getcstr();
+	strcpy(r->record, tmptxt);
+	delete tmptxt;
+	r->basename = (char*) odr_malloc(r->stream, CollectionName.size()+1);
+	tmptxt = CollectionName.getcstr();
+	strcpy(r->basename, tmptxt);
+	delete tmptxt;
+      }
+    }
+    // *** do we want to use this format? ***
+    else if (r->request_format == VAL_GRS1)
+    {
+      // bail out, since we don't support grs (yet?)
+      r->errcode = 107; // "Query type not supported"
+      return 0;
+      
+    } 
+    // assume that here on in is some sort of MARC format
+    else {
+      marcrec *mrec = new marcrec;
+
+      mrec = (marcrec*) malloc(sizeof(*mrec));
+      
+      mrec->marcline = (char*) malloc(100000);
+      mrec->partitle = (char*) malloc(10000);
+      mrec->subtitle = (char*) malloc(10000);
+      mrec->year     = (char*) malloc(1000);
+      mrec->url      = (char*) malloc(1000);
+      mrec->fmat     = (char*) malloc(1000);
+      mrec->s008     = (char*) malloc(41);
+      
+      mrec->ncreators = 0;
+      mrec->ntitles   = 0;
+      *mrec->marcline = 0;
+      *mrec->subtitle = 0;
+      *mrec->partitle = 0;
+      *mrec->year     = 0;
+      *mrec->url      = 0;
+      *mrec->fmat     = 0;
+      strcpy(mrec->s008, "                                        ");
+
+      text_t CollectionName;
+      list<metatag*> Metatag_list;
+
+      if (!get_marc_record(r->number, Metatag_list, CollectionName)) {
+	r->errcode = 13;
+	return 0;
+      } else {
+	list<metatag*>::iterator METATAG_here = Metatag_list.begin();
+	list<metatag*>::iterator METATAG_end = Metatag_list.end();
+	while (METATAG_here != METATAG_end) {
+	  usMARC(*METATAG_here, mrec);
+	  METATAG_here++;
+	}
+
+	// set the date in the marc record to the current date
+	char today[32];
+	time_t d;
+	struct tm *time_struct;
+	//putenv("TZ=NFT-1DFT");
+	d = time(NULL);
+	time_struct = localtime(&d);
+	strftime(today, 16, "%y%m%d", time_struct);
+	put008(mrec->s008, today, F008_DATE_ENTERED);
+	put008(mrec->s008, "s", F008_TYPE_OF_DATE);
+
+	// to do: make this safer + more efficient
+	cp = new char[1000000];
+	char *cp2 = new char[1000000];
+	
+	switch(r->request_format) {
+	case VAL_DANMARC: {
+	  MARCtidy(mrec, cp, DANMARC);
+	  r->output_format = VAL_DANMARC;
+	  break; }
+	case VAL_FINMARC: {
+	  MARCtidy(mrec, cp, FINMARC);
+	  r->output_format = VAL_FINMARC;
+	  break; }
+	// YAZ doesn't know about ISMARC
+	/*
+	case VAL_ISMARC: {
+	  put008(mrec->s008, "k", F008_FORM_OF_PUBLICATION);
+	  MARCtidy(mrec, cp, ISMARC);
+	  r->output_format = VAL_ISMARC;
+	  break; }
+	*/
+	case VAL_NORMARC: {
+	  MARCtidy(mrec, cp, NORMARC);
+	  r->output_format = VAL_NORMARC;
+	  break; }
+	case VAL_SWEMARC: {
+	  MARCtidy(mrec, cp, SWEMARC);
+	  r->output_format = VAL_SWEMARC;
+	  break; }
+	default: {
+	  MARCtidy(mrec, cp, USMARC);
+	  r->output_format = VAL_USMARC;
+	  break; }
+	}
+	
+	sprintf(cp2, "%s", (char*) MARC2709(cp, 0, 6, 'n', 'm', ' '));
+	r->len = strlen(cp2);
+	r->record = (char*) odr_malloc(r->stream, r->len+1);
+	strcpy(r->record, cp2);     
+	r->basename = (char*) odr_malloc(r->stream, CollectionName.size()+1);
+	char *tmptxt;
+	tmptxt = CollectionName.getcstr();
+	strcpy(r->basename, tmptxt);
+	delete tmptxt;
+	delete cp2;      
+	cerr << "dumping record:`" << cp << "'" << endl;
+	delete cp;
+      }
+    }
+
+    r->errcode = 0;
+    return 0;
+}
+
+/*
+ * silly dummy-scan what reads words from a file.
+ */
+int ztest_scan(void *handle, bend_scan_rr *q)
+{
+  // *** called when client issues a "scan" command
+  if (z3950_verbosity_>1) {
+    cerr << "Entering ztest_scan" << endl;
+  }
+    
+  // bail out, since we don't support scan (yet?)
+  q->errcode = 107; // "Query type not supported"
+  return 0;
+
+    static FILE *f = 0;
+    static struct scan_entry list[200];
+    static char entries[200][80];
+    int hits[200];
+    char term[80], *p;
+    int i, pos;
+    int term_position_req = q->term_position;
+    int num_entries_req = q->num_entries;
+
+    q->errcode = 0;
+    q->errstring = 0;
+    q->entries = list;
+    q->status = BEND_SCAN_SUCCESS;
+    if (!f && !(f = fopen("dummy-words", "r")))
+    {
+	perror("dummy-words");
+	exit(1);
+    }
+    if (q->term->term->which != Z_Term_general)
+    {
+    	q->errcode = 229; /* unsupported term type */
+	return 0;
+    }
+    if (*q->step_size != 0)
+    {
+	q->errcode = 205; /*Only zero step size supported for Scan */
+	return 0;
+    }
+    if (q->term->term->u.general->len >= 80)
+    {
+    	q->errcode = 11; /* term too long */
+	return 0;
+    }
+    if (q->num_entries > 200)
+    {
+    	q->errcode = 31;
+	return 0;
+    }
+    memcpy(term, q->term->term->u.general->buf, q->term->term->u.general->len);
+    term[q->term->term->u.general->len] = '\0';
+    for (p = term; *p; p++)
+    	if (islower(*p))
+	    *p = toupper(*p);
+
+    fseek(f, 0, SEEK_SET);
+    q->num_entries = 0;
+
+    for (i = 0, pos = 0; fscanf(f, " %79[^:]:%d", entries[pos], &hits[pos]) == 2;
+	i++, pos < 199 ? pos++ : (pos = 0))
+    {
+    	if (!q->num_entries && strcmp(entries[pos], term) >= 0) /* s-point fnd */
+	{
+	    if ((q->term_position = term_position_req) > i + 1)
+	    {
+	    	q->term_position = i + 1;
+		q->status = BEND_SCAN_PARTIAL;
+	    }
+	    for (; q->num_entries < q->term_position; q->num_entries++)
+	    {
+	    	int po;
+
+		po = pos - q->term_position + q->num_entries+1; /* find pos */
+		if (po < 0)
+		    po += 200;
+
+		if (!strcmp (term, "SD") && q->num_entries == 2)
+		{
+		    list[q->num_entries].term = entries[pos];
+		    list[q->num_entries].occurrences = -1;
+		    list[q->num_entries].errcode = 233;
+		    list[q->num_entries].errstring = "SD for Scan Term";
+		}
+		else
+		{
+		    list[q->num_entries].term = entries[po];
+		    list[q->num_entries].occurrences = hits[po];
+		}
+	    }
+	}
+	else if (q->num_entries)
+	{
+	    list[q->num_entries].term = entries[pos];
+	    list[q->num_entries].occurrences = hits[pos];
+	    q->num_entries++;
+	}
+	if (q->num_entries >= num_entries_req)
+	    break;
+    }
+    if (feof(f))
+    	q->status = BEND_SCAN_PARTIAL;
+    return 0;
+}
+
+
+
+void bend_close(void *handle)
+{
+  if (z3950_verbosity_>1) {
+    cerr << "Entering bend_close" << endl;
+  }
+  delete Server;
+  delete Cservers;
+  delete Protocol;
+  if (z3950_verbosity_>1) {
+    cerr << "heap cleaned up, exiting bend_close" << endl;
+  }
+  return;
+}
+
+int main (int argc, char *argv[])
+{
+  
+  const int statserv_var =  statserv_main(argc, argv, bend_init, bend_close);
+  cerr << "statserv_main returns: " << statserv_var << endl;
+  return statserv_var;
+  
+  return 0;
+}
+
+
Index: /gsdl/trunk/gs2runtime/src/z3950/z3950server.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/z3950server.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/z3950server.h	(revision 16561)
@@ -0,0 +1,71 @@
+/**********************************************************************
+ *
+ * z3950server.h --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+#ifndef Z3950SERVER_H
+#define Z3950ESRVER_H
+
+#include <string>
+#include <list>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+
+// Greenstone headers
+#include "fileutil.h"
+
+// the maximum number of hits allowed
+#define MAXHITS 10000
+
+// These globals are local to each thread: one Z39.50 connection = one thread.
+
+extern text_t gsdlhome; // this is specified externally when Greenstone is installed
+extern list<FilterResponse_t> Response_tlist; // list of response sets
+extern list<text_t> Response_tlist_colnames;  // collection names for the above
+extern list<int> Response_tlist_sizes;        // sizes (number of records) for the above
+extern z3950Server *Server;
+extern collectset *Cservers;
+extern nullproto *Protocol;
+extern map<text_t, gsdlCollection> Collection_map; // info and tools for collections
+extern map<text_t, text_t> Resultsets;             // mapping from ResultSetId to GSQuery 
+
+extern int z3950_verbosity_;
+
+bend_initresult *bend_init(bend_initrequest *q);
+void bend_close(void *handle);
+
+Z_GenericRecord *read_grs1(FILE *f, ODR o);
+
+int ztest_search (void *handle, bend_search_rr *rr);
+int ztest_sort (void *handle, bend_sort_rr *rr);
+int ztest_present (void *handle, bend_present_rr *rr);
+int ztest_esrequest (void *handle, bend_esrequest_rr *rr);
+int ztest_delete (void *handle, bend_delete_rr *rr);
+int ztest_fetch(void *handle, bend_fetch_rr *r);
+int ztest_scan(void *handle, bend_scan_rr *q);
+
+
+#endif
Index: /gsdl/trunk/gs2runtime/src/z3950/zparse.fl
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/zparse.fl	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/zparse.fl	(revision 16561)
@@ -0,0 +1,112 @@
+/* This is the lexical analyser - it returns tokens to the parsers.
+   This is used by both parsers - one for the build-time config files
+   and one for the run-time config files. Note the included file "parse.h"
+   is a symlink to the relevant header file. */
+
+%{
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*#define YYSTYPE char * - YYSTYPE is defined in zparse.tab.h */
+#include "zparse.tab.h"
+
+/* forward declarations for std functions not picked up. */
+/*int fileno(FILE *);*/
+/*char *strdup(char *);*/
+
+int lineno=1;
+%}
+
+%option noyywrap
+
+COMMENT  \#.*(\n|\r)
+
+%x  STRINGCOND
+%%
+	char *string;
+	int string_len;
+
+{COMMENT}		lineno++;/*yylval.string=yytext;*/yyleng=0;
+,			return (',');
+:			return (':');
+\_			return ('_');
+\[GSDL[0-9]+\.[0-9]+\] yylval.string=yytext;return(GSDLVERSION);
+\[General\]		return (GENERAL_SECTION);
+\[Search\ [fF]ields\]	return (SEARCH_SECTION);
+\[Browse\ [fF]ields\]	return (BROWSE_SECTION);
+\[Macros\]		return (MACROS_SECTION);
+maintainer		return (MAINTAINER);
+Languages		return(LANGUAGES);
+About			return(ABOUT);
+Browse			return(BROWSE);
+Date[lL]ist		return(DATELIST);
+Document[aA]rrows[bB]ottom return(DOCUMENTARROWSBOTTOM);
+Document[bB]uttons	return(DOCUMENTBUTTONS);
+Document[hH]eader	return(DOCUMENTHEADER);
+Document[iI]mages	return(DOCUMENTIMAGES);
+Document[tT]ext		return(DOCUMENTTEXT);
+Icon[lL]ink		return(ICONLINK);
+List			return(LIST);
+Format			return(FORMAT);
+Name			return(NAME);
+Search[tT]ext		return(SEARCHTEXT);
+Section[lL]ist		return(SECTIONLIST);
+Sorted[lL]ist		return(SORTEDLIST);
+Sorted[sS]ection[lL]ist	return(SORTEDSECTIONLIST);
+Text[lL]ink		return(TEXTLINK);
+Type			return(TYPE);
+public			return (PUBLIC);
+true			return (TRUE);
+false			return (FALSE);
+^document		return (DOCUMENT);
+^section		return (SECTION);
+[iI]con			return (ICON);
+[sS]mall[iI]con		return (SMALLICON);
+\"			{string=NULL;BEGIN(STRINGCOND);}
+<STRINGCOND>{
+  ([^\"\\\n])*		{/* append this to our current string EXC NEWLINE */
+			if (string==NULL) string_len=0; else
+			string_len=strlen(string);
+			string=realloc(string,string_len+strlen(yytext)+1);
+			/* +1 is for trailing \0 */
+			strcpy(string+string_len,yytext);
+			}
+  ([^\"\\])*\n		{/* append this to our current string - INC NEWLINE */
+			lineno++;
+			if (string==NULL) string_len=0; else
+			string_len=strlen(string);
+			string=realloc(string,string_len+strlen(yytext)+1);
+			/* +1 is for trailing \0 */
+			strcpy(string+string_len,yytext);
+			}
+  "\\"\"		|
+  \"\"		{       /*replace quoted quote with one quote*/
+			string_len=strlen(string);
+			string=realloc(string,string_len+2);
+			string[string_len]='\"';
+			string[string_len+1]='\0';
+			}
+  \"		{
+			BEGIN(INITIAL);
+			yylval.string=string;
+			return(STRING);
+			  /* note that the string may have long bits of 
+			     whitespace in it that we could remove. */
+			}
+}
+[[:alnum:]@\.-]+	yylval.string=strdup(yytext);return (DATA);
+(" "|\t)		;
+(\n|\r)			lineno++;
+%%
+/******** deleted rules: *******
+en			return(EN);
+fr			return(FR);
+mi			return(MI);
+zh			return(ZH);
+de			return(DE);
+***************/
+
+
+
+
Index: /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.c
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.c	(revision 16561)
@@ -0,0 +1,1531 @@
+/* A Bison parser, made by GNU Bison 1.875.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+   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, 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     GSDLVERSION = 257,
+     GENERAL_SECTION = 258,
+     SEARCH_SECTION = 259,
+     BROWSE_SECTION = 260,
+     MACROS_SECTION = 261,
+     MAINTAINER = 262,
+     PUBLIC = 263,
+     TRUE = 264,
+     FALSE = 265,
+     LANGUAGES = 266,
+     EN = 267,
+     FR = 268,
+     MI = 269,
+     ZH = 270,
+     DE = 271,
+     NAME = 272,
+     STRING = 273,
+     ICON = 274,
+     SMALLICON = 275,
+     ABOUT = 276,
+     SEARCHTEXT = 277,
+     DOCUMENT = 278,
+     SECTION = 279,
+     BROWSE = 280,
+     TYPE = 281,
+     FORMAT = 282,
+     LIST = 283,
+     SORTEDLIST = 284,
+     SECTIONLIST = 285,
+     SORTEDSECTIONLIST = 286,
+     DATELIST = 287,
+     ICONLINK = 288,
+     TEXTLINK = 289,
+     DOCUMENTIMAGES = 290,
+     DOCUMENTHEADER = 291,
+     DOCUMENTTEXT = 292,
+     DOCUMENTBUTTONS = 293,
+     DOCUMENTARROWSBOTTOM = 294,
+     DATA = 295
+   };
+#endif
+#define GSDLVERSION 257
+#define GENERAL_SECTION 258
+#define SEARCH_SECTION 259
+#define BROWSE_SECTION 260
+#define MACROS_SECTION 261
+#define MAINTAINER 262
+#define PUBLIC 263
+#define TRUE 264
+#define FALSE 265
+#define LANGUAGES 266
+#define EN 267
+#define FR 268
+#define MI 269
+#define ZH 270
+#define DE 271
+#define NAME 272
+#define STRING 273
+#define ICON 274
+#define SMALLICON 275
+#define ABOUT 276
+#define SEARCHTEXT 277
+#define DOCUMENT 278
+#define SECTION 279
+#define BROWSE 280
+#define TYPE 281
+#define FORMAT 282
+#define LIST 283
+#define SORTEDLIST 284
+#define SECTIONLIST 285
+#define SORTEDSECTIONLIST 286
+#define DATELIST 287
+#define ICONLINK 288
+#define TEXTLINK 289
+#define DOCUMENTIMAGES 290
+#define DOCUMENTHEADER 291
+#define DOCUMENTTEXT 292
+#define DOCUMENTBUTTONS 293
+#define DOCUMENTARROWSBOTTOM 294
+#define DATA 295
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 27 "zparse.y"
+
+  /* verbatim C code - functions, etc */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "z3950cfg.h" /* structure types, and #define of yyin/yyparse */
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+char defLanguage=0;
+
+/* defined in parse.fl */
+extern int lineno;
+
+#ifndef YYRECOVERING
+int YYRECOVERING=0; /* this is supposed to already be defined as a macro */
+#endif
+
+/* defined in z3950proto.cpp */
+struct z3950cfg *zserver_list=NULL;
+
+/* this is set in z3950proto.cpp */
+FILE *errfile;
+
+int yylex(void);
+
+int yyerror(char *string);
+
+void errormsg(char *str);
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 61 "zparse.y"
+typedef union YYSTYPE {
+  char *string;
+  struct z3950cfg *cfg;
+  struct z3950aboutlist *about;
+  int number;
+} YYSTYPE;
+/* Line 191 of yacc.c.  */
+#line 197 "zparse.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 214 of yacc.c.  */
+#line 209 "zparse.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# if YYSTACK_USE_ALLOCA
+#  define YYSTACK_ALLOC alloca
+# else
+#  ifndef YYSTACK_USE_ALLOCA
+#   if defined (alloca) || defined (_ALLOCA_H)
+#    define YYSTACK_ALLOC alloca
+#   else
+#    ifdef __GNUC__
+#     define YYSTACK_ALLOC __builtin_alloca
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC malloc
+#  define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+	 || (YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short) + sizeof (YYSTYPE))				\
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  register YYSIZE_T yyi;		\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  4
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   44
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  44
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  13
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  28
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  37
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   296
+
+#define YYTRANSLATE(YYX) 						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,    42,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,    43,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     3,     4,     5,
+       6,     7,     8,     9,    10,    11,    12,    13,    14,    15,
+      16,    17,    18,    19,    20,    21,    22,    23,    24,    25,
+      26,    27,    28,    29,    30,    31,    32,    33,    34,    35,
+      36,    37,    38,    39,    40,    41,     2
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned char yyprhs[] =
+{
+       0,     0,     3,     6,     8,    10,    11,    14,    16,    25,
+      27,    29,    30,    33,    34,    37,    40,    41,    44,    47,
+      48,    50,    51,    53,    56,    60,    63,    67,    69
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      45,     0,    -1,    46,    47,    -1,    46,    -1,     3,    -1,
+      -1,    47,    48,    -1,    48,    -1,    41,    41,    50,    41,
+      49,    51,    52,    53,    -1,     1,    -1,    19,    -1,    -1,
+      42,    41,    -1,    -1,    20,    19,    -1,    20,    41,    -1,
+      -1,    21,    19,    -1,    21,    41,    -1,    -1,    54,    -1,
+      -1,    55,    -1,    54,    55,    -1,    22,    56,    19,    -1,
+      22,    19,    -1,    41,    43,    41,    -1,    41,    -1,     1,
+      -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned char yyrline[] =
+{
+       0,   106,   106,   107,   110,   111,   113,   114,   116,   137,
+     141,   142,   147,   148,   151,   152,   155,   156,   157,   162,
+     164,   165,   167,   169,   172,   183,   205,   213,   221
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "GSDLVERSION", "GENERAL_SECTION", 
+  "SEARCH_SECTION", "BROWSE_SECTION", "MACROS_SECTION", "MAINTAINER", 
+  "PUBLIC", "TRUE", "FALSE", "LANGUAGES", "EN", "FR", "MI", "ZH", "DE", 
+  "NAME", "STRING", "ICON", "SMALLICON", "ABOUT", "SEARCHTEXT", 
+  "DOCUMENT", "SECTION", "BROWSE", "TYPE", "FORMAT", "LIST", "SORTEDLIST", 
+  "SECTIONLIST", "SORTEDSECTIONLIST", "DATELIST", "ICONLINK", "TEXTLINK", 
+  "DOCUMENTIMAGES", "DOCUMENTHEADER", "DOCUMENTTEXT", "DOCUMENTBUTTONS", 
+  "DOCUMENTARROWSBOTTOM", "DATA", "':'", "'_'", "$accept", "z3950", 
+  "version", "zserverlist", "zserver", "name", "port", "icon", 
+  "smallicon", "about", "aboutList", "aboutLang", "lang", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short yytoknum[] =
+{
+       0,   256,   296,   257,   258,   259,   260,   261,   262,   263,
+     264,   265,   266,   267,   268,   269,   270,   271,   272,   273,
+     274,   275,   276,   277,   278,   279,   280,   281,   282,   283,
+     284,   285,   286,   287,   288,   289,   290,   291,   292,   293,
+     294,   295,    58,    95
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    44,    45,    45,    46,    46,    47,    47,    48,    48,
+      49,    49,    50,    50,    51,    51,    51,    52,    52,    52,
+      53,    53,    54,    54,    55,    55,    56,    56,    56
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     2,     1,     1,     0,     2,     1,     8,     1,
+       1,     0,     2,     0,     2,     2,     0,     2,     2,     0,
+       1,     0,     1,     2,     3,     2,     3,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       5,     4,     0,     0,     1,     9,     0,     0,     7,    13,
+       6,     0,     0,    12,    11,    10,    16,     0,    19,    14,
+      15,     0,    21,    17,    18,     0,     8,    20,    22,    28,
+      25,    27,     0,    23,     0,    24,    26
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+      -1,     2,     3,     7,     8,    16,    12,    18,    22,    26,
+      27,    28,    32
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -33
+static const yysigned_char yypact[] =
+{
+       4,   -33,     8,     0,   -33,   -33,   -32,     2,   -33,   -31,
+     -33,   -29,   -28,   -33,    -9,   -33,    -6,   -14,    -5,   -33,
+     -33,   -13,    -7,   -33,   -33,     3,   -33,    -7,   -33,   -33,
+     -33,   -26,    -1,   -33,   -22,   -33,   -33
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yysigned_char yypgoto[] =
+{
+     -33,   -33,   -33,   -33,    13,   -33,   -33,   -33,   -33,   -33,
+     -33,    -4,   -33
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -4
+static const yysigned_char yytable[] =
+{
+      -3,     5,    -2,     5,    29,    19,    23,     1,     4,     9,
+      15,    11,    13,    14,    17,    25,    21,    34,    35,    36,
+      10,     0,    30,    33,     0,     0,     0,    20,    24,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     6,     0,     6,    31
+};
+
+static const yysigned_char yycheck[] =
+{
+       0,     1,     0,     1,     1,    19,    19,     3,     0,    41,
+      19,    42,    41,    41,    20,    22,    21,    43,    19,    41,
+       7,    -1,    19,    27,    -1,    -1,    -1,    41,    41,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    41,    -1,    41,    41
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,     3,    45,    46,     0,     1,    41,    47,    48,    41,
+      48,    42,    50,    41,    41,    19,    49,    20,    51,    19,
+      41,    21,    52,    19,    41,    22,    53,    54,    55,     1,
+      19,    41,    56,    55,    43,    19,    41
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrlab1
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { 								\
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;							\
+    }								\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)         \
+  Current.first_line   = Rhs[1].first_line;      \
+  Current.first_column = Rhs[1].first_column;    \
+  Current.last_line    = Rhs[N].last_line;       \
+  Current.last_column  = Rhs[N].last_column;
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (0)
+
+# define YYDSYMPRINT(Args)			\
+do {						\
+  if (yydebug)					\
+    yysymprint Args;				\
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)		\
+do {								\
+  if (yydebug)							\
+    {								\
+      YYFPRINTF (stderr, "%s ", Title);				\
+      yysymprint (stderr, 					\
+                  Token, Value);	\
+      YYFPRINTF (stderr, "\n");					\
+    }								\
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (cinluded).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short *bottom;
+    short *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylineno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylineno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (Rule);		\
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    {
+      YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short	yyssa[YYINITDEPTH];
+  short *yyss = yyssa;
+  register short *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack. Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	short *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	short *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyoverflowlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 106 "zparse.y"
+    {zserver_list=yyvsp[0].cfg;}
+    break;
+
+  case 3:
+#line 107 "zparse.y"
+    {zserver_list=NULL;}
+    break;
+
+  case 4:
+#line 110 "zparse.y"
+    {;}
+    break;
+
+  case 5:
+#line 111 "zparse.y"
+    {fprintf(errfile,"No version - not GSDL config file?\n");return(1);}
+    break;
+
+  case 6:
+#line 113 "zparse.y"
+    {if (yyvsp[0].cfg!=NULL) {yyvsp[0].cfg->next=yyvsp[-1].cfg;yyval.cfg=yyvsp[0].cfg;}}
+    break;
+
+  case 8:
+#line 117 "zparse.y"
+    {
+		     if (YYRECOVERING)
+		       {yyval.cfg=NULL;YYRECOVERING=0;} 
+		     else {
+		       if((yyval.cfg=malloc(sizeof(struct z3950cfg)))==NULL)
+			 {
+			   fprintf(errfile,"malloc failed\n");
+			   return(1);
+			 }
+		       yyval.cfg->shortname=yyvsp[-7].string;
+		       yyval.cfg->hostname=yyvsp[-6].string;
+		       yyval.cfg->port=yyvsp[-5].number;
+		       yyval.cfg->dbname=yyvsp[-4].string;
+		       yyval.cfg->longname=yyvsp[-3].string;
+		       yyval.cfg->icon=yyvsp[-2].string;
+		       yyval.cfg->smallicon=yyvsp[-1].string;
+		       yyval.cfg->about=yyvsp[0].about;
+		       yyval.cfg->next=NULL;
+		     }
+		   }
+    break;
+
+  case 9:
+#line 137 "zparse.y"
+    {yyval.cfg=NULL;
+	      fprintf(errfile,"discarding zserver (line %d)\n",lineno);}
+    break;
+
+  case 10:
+#line 141 "zparse.y"
+    {;}
+    break;
+
+  case 11:
+#line 142 "zparse.y"
+    {errormsg("Database name needs to be followed by a short "
+			  "descriptive name (enclosed in \" marks)");
+	         yyval.string=NULL;
+	         YYRECOVERING=1;}
+    break;
+
+  case 12:
+#line 147 "zparse.y"
+    {yyval.number=atoi(yyvsp[0].string);}
+    break;
+
+  case 13:
+#line 148 "zparse.y"
+    {yyval.number=210;}
+    break;
+
+  case 14:
+#line 151 "zparse.y"
+    {yyval.string=yyvsp[0].string;}
+    break;
+
+  case 15:
+#line 152 "zparse.y"
+    {yyval.string=yyvsp[0].string;
+                  errormsg("Icon must be enclosed in quotes");
+	      yylex();yylex();}
+    break;
+
+  case 16:
+#line 155 "zparse.y"
+    {yyval.string=NULL;}
+    break;
+
+  case 17:
+#line 156 "zparse.y"
+    {yyval.string=yyvsp[0].string;}
+    break;
+
+  case 18:
+#line 157 "zparse.y"
+    {yyval.string=yyvsp[0].string;
+	      /* this is because of the ':' */
+              errormsg("Smallicon must be enclosed in quotes.\n");
+	      yylex();yylex();
+	      }
+    break;
+
+  case 19:
+#line 162 "zparse.y"
+    {yyval.string=NULL;}
+    break;
+
+  case 20:
+#line 164 "zparse.y"
+    {defLanguage=0;}
+    break;
+
+  case 21:
+#line 165 "zparse.y"
+    {yyval.about=NULL;}
+    break;
+
+  case 22:
+#line 167 "zparse.y"
+    {;}
+    break;
+
+  case 23:
+#line 169 "zparse.y"
+    {yyval.about=yyvsp[0].about;yyvsp[0].about->next=yyvsp[-1].about;}
+    break;
+
+  case 24:
+#line 173 "zparse.y"
+    {
+			       if((yyval.about=malloc(sizeof(struct z3950aboutlist)))
+				  ==NULL) {
+				 fprintf(errfile,"Malloc failed\n");
+				 return(1);
+			       }
+			       yyval.about->lang=yyvsp[-1].string;
+			       yyval.about->text=yyvsp[0].string;
+			       yyval.about->next=NULL;
+			     }
+    break;
+
+  case 25:
+#line 184 "zparse.y"
+    {
+			       if (defLanguage)
+		                 {
+				   fprintf(errfile,"warning: (line %d): already have a default language in config file\n",lineno);
+				 }
+			       else defLanguage=1;
+			       
+			       if((yyval.about=malloc(sizeof(struct z3950aboutlist)))
+				  ==NULL) {
+				 fprintf(errfile,"Malloc failed\n");
+				 return(1);
+			       }
+			       yyval.about->lang=NULL; /* default lang... */
+			       yyval.about->text=yyvsp[0].string;
+			       yyval.about->next=NULL;
+			     }
+    break;
+
+  case 26:
+#line 206 "zparse.y"
+    {
+			       yyval.string=malloc(strlen(yyvsp[-2].string)+strlen(yyvsp[0].string)+1);
+			       strncpy(yyval.string,yyvsp[-2].string,strlen(yyvsp[-2].string));
+			       yyval.string[strlen(yyvsp[-2].string)]='_';
+			       strncpy(yyval.string+strlen(yyvsp[-2].string)+1,yyvsp[0].string,strlen(yyvsp[0].string));
+			       free(yyvsp[-2].string);free(yyvsp[0].string);
+			     }
+    break;
+
+  case 27:
+#line 213 "zparse.y"
+    {;}
+    break;
+
+  case 28:
+#line 221 "zparse.y"
+    {errormsg("missing language");}
+    break;
+
+
+    }
+
+/* Line 999 of yacc.c.  */
+#line 1309 "zparse.tab.c"
+
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+	{
+	  YYSIZE_T yysize = 0;
+	  int yytype = YYTRANSLATE (yychar);
+	  char *yymsg;
+	  int yyx, yycount;
+
+	  yycount = 0;
+	  /* Start YYX at -YYN if negative to avoid negative indexes in
+	     YYCHECK.  */
+	  for (yyx = yyn < 0 ? -yyn : 0;
+	       yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+	    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	      yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+	  yysize += yystrlen ("syntax error, unexpected ") + 1;
+	  yysize += yystrlen (yytname[yytype]);
+	  yymsg = (char *) YYSTACK_ALLOC (yysize);
+	  if (yymsg != 0)
+	    {
+	      char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+	      yyp = yystpcpy (yyp, yytname[yytype]);
+
+	      if (yycount < 5)
+		{
+		  yycount = 0;
+		  for (yyx = yyn < 0 ? -yyn : 0;
+		       yyx < (int) (sizeof (yytname) / sizeof (char *));
+		       yyx++)
+		    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+		      {
+			const char *yyq = ! yycount ? ", expecting " : " or ";
+			yyp = yystpcpy (yyp, yyq);
+			yyp = yystpcpy (yyp, yytname[yyx]);
+			yycount++;
+		      }
+		}
+	      yyerror (yymsg);
+	      YYSTACK_FREE (yymsg);
+	    }
+	  else
+	    yyerror ("syntax error; also virtual memory exhausted");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      /* Return failure if at end of input.  */
+      if (yychar == YYEOF)
+        {
+	  /* Pop the error token.  */
+          YYPOPSTACK;
+	  /* Pop the rest of the stack.  */
+	  while (yyss < yyssp)
+	    {
+	      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+	      yydestruct (yystos[*yyssp], yyvsp);
+	      YYPOPSTACK;
+	    }
+	  YYABORT;
+        }
+
+      YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+      yydestruct (yytoken, &yylval);
+      yychar = YYEMPTY;
+
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action.  |
+`----------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      yyvsp--;
+      yystate = *--yyssp;
+
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+#line 106 "zparse.y"
+
+/*int strncasecmp (const char *, const char *,size_t);*/
+int yyerror(char *string) {
+  fprintf(errfile,"Parse error (line %d) near \n\"%s\"<---:%s\n",lineno,
+	  yylval.string,string);
+  return(1);
+}
+
+void errormsg(char *str) {
+  fprintf(errfile,"Err (line %d, near \"%s\"): %s.\n",lineno,
+	  yylval.string,str);
+}
+
+/* *
+int main (int argc, char *argv[]) {
+  if (argc==2)
+    if (argv[1][0]=='-'&&argv[1][1]=='d')
+      yydebug=1;
+
+  yyparse();
+  return(0);
+}
+* */
+
Index: /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.h
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.h	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/zparse.tab.h	(revision 16561)
@@ -0,0 +1,134 @@
+/* A Bison parser, made by GNU Bison 1.875.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+   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, 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     GSDLVERSION = 257,
+     GENERAL_SECTION = 258,
+     SEARCH_SECTION = 259,
+     BROWSE_SECTION = 260,
+     MACROS_SECTION = 261,
+     MAINTAINER = 262,
+     PUBLIC = 263,
+     TRUE = 264,
+     FALSE = 265,
+     LANGUAGES = 266,
+     EN = 267,
+     FR = 268,
+     MI = 269,
+     ZH = 270,
+     DE = 271,
+     NAME = 272,
+     STRING = 273,
+     ICON = 274,
+     SMALLICON = 275,
+     ABOUT = 276,
+     SEARCHTEXT = 277,
+     DOCUMENT = 278,
+     SECTION = 279,
+     BROWSE = 280,
+     TYPE = 281,
+     FORMAT = 282,
+     LIST = 283,
+     SORTEDLIST = 284,
+     SECTIONLIST = 285,
+     SORTEDSECTIONLIST = 286,
+     DATELIST = 287,
+     ICONLINK = 288,
+     TEXTLINK = 289,
+     DOCUMENTIMAGES = 290,
+     DOCUMENTHEADER = 291,
+     DOCUMENTTEXT = 292,
+     DOCUMENTBUTTONS = 293,
+     DOCUMENTARROWSBOTTOM = 294,
+     DATA = 295
+   };
+#endif
+#define GSDLVERSION 257
+#define GENERAL_SECTION 258
+#define SEARCH_SECTION 259
+#define BROWSE_SECTION 260
+#define MACROS_SECTION 261
+#define MAINTAINER 262
+#define PUBLIC 263
+#define TRUE 264
+#define FALSE 265
+#define LANGUAGES 266
+#define EN 267
+#define FR 268
+#define MI 269
+#define ZH 270
+#define DE 271
+#define NAME 272
+#define STRING 273
+#define ICON 274
+#define SMALLICON 275
+#define ABOUT 276
+#define SEARCHTEXT 277
+#define DOCUMENT 278
+#define SECTION 279
+#define BROWSE 280
+#define TYPE 281
+#define FORMAT 282
+#define LIST 283
+#define SORTEDLIST 284
+#define SECTIONLIST 285
+#define SORTEDSECTIONLIST 286
+#define DATELIST 287
+#define ICONLINK 288
+#define TEXTLINK 289
+#define DOCUMENTIMAGES 290
+#define DOCUMENTHEADER 291
+#define DOCUMENTTEXT 292
+#define DOCUMENTBUTTONS 293
+#define DOCUMENTARROWSBOTTOM 294
+#define DATA 295
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 61 "zparse.y"
+typedef union YYSTYPE {
+  char *string;
+  struct z3950cfg *cfg;
+  struct z3950aboutlist *about;
+  int number;
+} YYSTYPE;
+/* Line 1240 of yacc.c.  */
+#line 125 "zparse.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
+
+
Index: /gsdl/trunk/gs2runtime/src/z3950/zparse.y
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/zparse.y	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/zparse.y	(revision 16561)
@@ -0,0 +1,252 @@
+/* RUN-TIME CONFIGURATION FILE PARSER */
+/**********************************************************************
+ *
+ * zparse.y --
+ * Copyright (C) 2000  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.
+ *
+ *********************************************************************/
+
+%{
+  /* verbatim C code - functions, etc */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "z3950cfg.h" /* structure types, and #define of yyin/yyparse */
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+char defLanguage=0;
+
+/* defined in parse.fl */
+extern int lineno;
+
+#ifndef YYRECOVERING
+int YYRECOVERING=0; /* this is supposed to already be defined as a macro */
+#endif
+
+/* defined in z3950proto.cpp */
+struct z3950cfg *zserver_list=NULL;
+
+/* this is set in z3950proto.cpp */
+FILE *errfile;
+
+int yylex(void);
+
+int yyerror(char *string);
+
+void errormsg(char *str);
+
+%}
+
+
+%union {
+  char *string;
+  struct z3950cfg *cfg;
+  struct z3950aboutlist *about;
+  int number;
+}
+
+
+/* keywords */
+/***** NOTE: can explicitly give values for each token, AND
+give a string for the token name eg 
+%token GSDLVERSION 257  "Version"
+*******/
+%token GSDLVERSION 257
+%token GENERAL_SECTION 258 SEARCH_SECTION 259 
+%token BROWSE_SECTION 260 MACROS_SECTION 261
+%token MAINTAINER 262
+%token PUBLIC 263
+%token TRUE 264 FALSE 265
+%token LANGUAGES 266 EN 267 FR 268 MI 269 ZH 270 DE 271
+%token NAME 272
+%token <string> STRING 273
+%token ICON 274 SMALLICON 275
+%token ABOUT 276
+%token SEARCHTEXT 277
+%token DOCUMENT 278 SECTION 279
+%token BROWSE 280 TYPE 281 FORMAT 282
+/* classifiers */
+%token  LIST 283 SORTEDLIST 284 SECTIONLIST 285 SORTEDSECTIONLIST 286 
+%token DATELIST 287
+/* formats */
+%token  ICONLINK 288 TEXTLINK 289
+/* layouts */
+%token  DOCUMENTIMAGES 290 DOCUMENTHEADER 291 DOCUMENTTEXT 292
+%token  DOCUMENTBUTTONS 293 DOCUMENTARROWSBOTTOM 294
+
+/* all other text */
+%token <string> DATA 295
+
+%type <cfg>      zserver zserverlist
+%type <number>  port
+%type <string>   icon smallicon lang name
+%type <about>    about aboutLang aboutList
+%%
+/* grammar */
+z3950         : version zserverlist {zserver_list=$2;}
+              | version {zserver_list=NULL;} /* if all commented out or none... */
+
+
+version       : GSDLVERSION {;}
+              | {fprintf(errfile,"No version - not GSDL config file?\n");return(1);}
+
+zserverlist   : zserverlist zserver {if ($2!=NULL) {$2->next=$1;$$=$2;}}
+              | zserver ;
+
+zserver       : DATA DATA port DATA name icon smallicon about 
+                   {
+		     if (YYRECOVERING)
+		       {$$=NULL;YYRECOVERING=0;} 
+		     else {
+		       if(($$=malloc(sizeof(struct z3950cfg)))==NULL)
+			 {
+			   fprintf(errfile,"malloc failed\n");
+			   return(1);
+			 }
+		       $$->shortname=$1;
+		       $$->hostname=$2;
+		       $$->port=$3;
+		       $$->dbname=$4;
+		       $$->longname=$5;
+		       $$->icon=$6;
+		       $$->smallicon=$7;
+		       $$->about=$8;
+		       $$->next=NULL;
+		     }
+		   }
+              | error {$$=NULL;
+	      fprintf(errfile,"discarding zserver (line %d)\n",lineno);}
+              ;
+
+name          : STRING {;}
+              | {errormsg("Database name needs to be followed by a short "
+			  "descriptive name (enclosed in \" marks)");
+	         $$=NULL;
+	         YYRECOVERING=1;} /* we should scrap this server */
+
+port          : ':' DATA {$$=atoi($2);}
+              | {$$=210;} /* default z39.50 server port */
+              ;
+
+icon          : ICON STRING {$$=$2;}
+              | ICON DATA {$$=$2;
+                  errormsg("Icon must be enclosed in quotes");
+	      yylex();yylex();}
+              | {$$=NULL;}
+smallicon     : SMALLICON STRING {$$=$2;}
+              | SMALLICON DATA {$$=$2;
+	      /* this is because of the ':' */
+              errormsg("Smallicon must be enclosed in quotes.\n");
+	      yylex();yylex();
+	      }
+              | {$$=NULL;}
+
+about         : aboutList {defLanguage=0;}
+              | {$$=NULL;}
+
+aboutList     : aboutLang {;}
+                      /* note this action reverses the order of the list */
+              | aboutList aboutLang {$$=$2;$2->next=$1;} 
+
+
+aboutLang     : ABOUT lang STRING 
+                             {
+			       if(($$=malloc(sizeof(struct z3950aboutlist)))
+				  ==NULL) {
+				 fprintf(errfile,"Malloc failed\n");
+				 return(1);
+			       }
+			       $$->lang=$2;
+			       $$->text=$3;
+			       $$->next=NULL;
+			     }
+              | ABOUT STRING 
+                             {
+			       if (defLanguage)
+		                 {
+				   fprintf(errfile,"warning: (line %d): already have a default language in config file\n",lineno);
+				 }
+			       else defLanguage=1;
+			       
+			       if(($$=malloc(sizeof(struct z3950aboutlist)))
+				  ==NULL) {
+				 fprintf(errfile,"Malloc failed\n");
+				 return(1);
+			       }
+			       $$->lang=NULL; /* default lang... */
+			       $$->text=$2;
+			       $$->next=NULL;
+			     }
+/* this can only happen once (default lang) */
+
+
+/* languages should be done using a lookup, rather than hard-coding 
+   them as tokens. Also '_' token for en_BR */
+lang          : DATA '_' DATA 
+                             {
+			       $$=malloc(strlen($1)+strlen($3)+1);
+			       strncpy($$,$1,strlen($1));
+			       $$[strlen($1)]='_';
+			       strncpy($$+strlen($1)+1,$3,strlen($3));
+			       free($1);free($3);
+			     }
+              | DATA {;}
+/*lang '_' DATA {;} */
+/*              | EN {;}
+		| FR {;}
+		| MI {;}
+		| ZH {;}
+		| DE {;}
+		| DATA {errormsg("unknown language");}*/
+              | error {errormsg("missing language");}
+
+/*documentHeader : DOCUMENTHEADER STRING {;}
+              | {;}
+
+documentText  : DOCUMENTTEXT STRING {;}
+              | {;}
+*/
+
+%%
+/*int strncasecmp (const char *, const char *,size_t);*/
+int yyerror(char *string) {
+  fprintf(errfile,"Parse error (line %d) near \n\"%s\"<---:%s\n",lineno,
+	  yylval.string,string);
+  return(1);
+}
+
+void errormsg(char *str) {
+  fprintf(errfile,"Err (line %d, near \"%s\"): %s.\n",lineno,
+	  yylval.string,str);
+}
+
+/* *
+int main (int argc, char *argv[]) {
+  if (argc==2)
+    if (argv[1][0]=='-'&&argv[1][1]=='d')
+      yydebug=1;
+
+  yyparse();
+  return(0);
+}
+* */
Index: /gsdl/trunk/gs2runtime/src/z3950/zparse.yy.c
===================================================================
--- /gsdl/trunk/gs2runtime/src/z3950/zparse.yy.c	(revision 16561)
+++ /gsdl/trunk/gs2runtime/src/z3950/zparse.yy.c	(revision 16561)
@@ -0,0 +1,1969 @@
+#define yy_create_buffer zconfig_create_buffer
+#define yy_delete_buffer zconfig_delete_buffer
+#define yy_scan_buffer zconfig_scan_buffer
+#define yy_scan_string zconfig_scan_string
+#define yy_scan_bytes zconfig_scan_bytes
+#define yy_flex_debug zconfig_flex_debug
+#define yy_init_buffer zconfig_init_buffer
+#define yy_flush_buffer zconfig_flush_buffer
+#define yy_load_buffer_state zconfig_load_buffer_state
+#define yy_switch_to_buffer zconfig_switch_to_buffer
+#define yyin zconfigin
+#define yyleng zconfigleng
+#define yylex zconfiglex
+#define yyout zconfigout
+#define yyrestart zconfigrestart
+#define yytext zconfigtext
+
+#line 19 "zparse.yy.c"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header$
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ * 	if ( condition_holds )
+ *		yyless( 5 );
+ *	else
+ *		do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		*yy_cp = yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+	};
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+
+#define yywrap() 1
+#define YY_SKIP_YYWRAP
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 46
+#define YY_END_OF_BUFFER 47
+static yyconst short int yy_accept[274] =
+    {   0,
+        0,    0,   38,   38,   47,   46,   44,   45,   37,   46,
+        2,   43,    3,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   46,    4,   43,   43,   43,   43,   43,   43,
+       43,   43,   38,   39,   42,   46,    0,    1,    1,   43,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,    0,    0,    0,    0,   43,   43,
+       43,   43,   43,   43,   43,   38,   39,    0,   41,   40,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,    0,    0,    0,    0,    0,
+       43,   43,   43,   43,   43,   43,   43,   43,   43,   43,
+
+       43,   43,   35,   43,   21,   23,   43,   43,   43,   43,
+       43,   29,    0,    0,    0,    0,    0,   43,   35,   43,
+       43,   31,   43,   43,   12,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,    0,    0,    0,    0,
+        0,   32,   43,   43,   43,   43,   13,   43,   43,   22,
+       43,   43,   43,   43,   43,   43,   43,    0,    0,    0,
+        0,    0,   43,   30,   43,   43,   43,   43,   43,   43,
+       43,   43,   43,   43,   43,   43,    0,    0,    0,    0,
+        0,   43,   43,   34,   14,   43,   20,   43,   43,   43,
+       43,   43,   43,   28,    0,    0,    0,    9,    0,   43,
+
+       33,   43,   43,   43,   43,   43,   11,   43,   43,   36,
+       43,   43,    0,    5,    6,    0,   43,   43,   43,   43,
+       43,   43,   24,   43,   26,   43,    0,    0,   10,   43,
+       43,   43,   43,   43,   25,   43,    0,    0,   43,   43,
+       43,   43,   19,   43,    0,    0,   43,   43,   43,   43,
+       43,    0,    0,   43,   43,   17,   18,   43,    0,    0,
+       43,   16,   43,    8,    7,   43,   43,   43,   27,   43,
+       43,   15,    0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    4,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    5,    1,    6,    7,    1,    1,    1,    1,    1,
+        1,    1,    1,    8,    9,   10,    1,   11,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,   12,    1,    1,
+        1,    1,    1,    9,   13,   14,    9,   15,    9,   16,
+       17,   18,   19,    9,    9,   20,   21,   22,    9,    9,
+        9,    9,   23,   24,    9,    9,    9,    9,    9,    9,
+       25,   26,   27,    1,   28,    1,   29,   30,   31,   32,
+
+       33,   34,   35,   36,   37,    9,   38,   39,   40,   41,
+       42,   43,    9,   44,   45,   46,   47,    9,   48,   49,
+       50,    9,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[51] =
+    {   0,
+        1,    1,    1,    1,    1,    2,    1,    1,    3,    3,
+        3,    1,    3,    3,    3,    3,    3,    3,    3,    3,
+        3,    3,    3,    3,    1,    2,    1,    1,    3,    3,
+        3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
+        3,    3,    3,    3,    3,    3,    3,    3,    3,    3
+    } ;
+
+static yyconst short int yy_base[279] =
+    {   0,
+        0,   19,   49,   50,  331,  332,  332,  332,  332,   54,
+      332,    0,  332,  300,  285,   25,  286,  296,   31,  297,
+       29,   28,   49,  332,  296,  293,  294,  275,  281,  276,
+      277,   40,  315,  314,  310,  309,   78,  332,   80,    0,
+      272,  271,  266,  280,  266,  267,  267,  262,  266,   48,
+      276,  260,  254,  259,  257,   62,  271,  266,  259,  255,
+      259,  265,  247,  262,  261,  288,  287,  286,  332,  332,
+      241,  239,  253,  238,  244,  242,  247,  235,  247,  235,
+      232,  238,  230,  229,  241,  231,  257,  230,  239,  240,
+      223,  226,  225,  226,  231,  216,  216,  215,  215,   54,
+
+      219,  229,   66,  210,    0,    0,  225,  218,  215,  220,
+       67,    0,  204,  231,  217,  205,  204,  214,    0,  200,
+      208,    0,  204,  206,    0,  209,  204,  207,  193,  201,
+      208,  200,  193,   70,  202,  196,  187,  220,  186,  187,
+      197,    0,  198,  195,  192,  182,    0,  178,  181,    0,
+      180,  185,   64,  178,  187,   74,  176,  183,   80,  186,
+      169,  177,  175,    0,  170,  169,  163,  162,  169,  173,
+      172,   72,  162,  166,  169,  163,  195,  188,  159,  170,
+      191,  154,  148,    0,    0,  107,    0,  148,  143,  154,
+      149,  144,  157,    0,   80,   88,  160,  332,   82,  153,
+
+        0,  141,  137,  150,  142,  148,    0,  134,  134,    0,
+      132,  131,  139,  332,  332,  138,  130,  129,  126,  142,
+      141,  120,    0,  122,    0,  130,  133,  131,    0,  119,
+      106,  119,  115,  103,    0,  106,  108,  107,   97,  100,
+      108,  107,    0,   98,  106,  103,   89,   92,   88,   85,
+       83,   84,   83,   87,   82,    0,    0,   87,   96,   91,
+       70,    0,   64,  332,  332,   62,   58,   56,    0,   58,
+       25,    0,  332,  153,  156,   56,  159,  162
+    } ;
+
+static yyconst short int yy_def[279] =
+    {   0,
+      273,    1,  274,  274,  273,  273,  273,  273,  273,  275,
+      273,  276,  273,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  273,  273,  276,  276,  276,  276,  276,  276,
+      276,  276,  277,  278,  273,  273,  275,  273,  275,  276,
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  273,  273,  273,  273,  276,  276,
+      276,  276,  276,  276,  276,  277,  278,  278,  273,  273,
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  276,  273,  273,  273,  273,  273,
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  273,  273,  273,  273,  273,  276,  276,  276,
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  276,  276,  273,  273,  273,  273,
+      273,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  276,  276,  276,  273,  273,  273,
+      273,  273,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  276,  276,  273,  273,  273,  273,
+      273,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  276,  276,  273,  273,  273,  273,  273,  276,
+
+      276,  276,  276,  276,  276,  276,  276,  276,  276,  276,
+      276,  276,  273,  273,  273,  273,  276,  276,  276,  276,
+      276,  276,  276,  276,  276,  276,  273,  273,  276,  276,
+      276,  276,  276,  276,  276,  276,  273,  273,  276,  276,
+      276,  276,  276,  276,  273,  273,  276,  276,  276,  276,
+      276,  273,  273,  276,  276,  276,  276,  276,  273,  273,
+      276,  276,  276,  273,  273,  276,  276,  276,  276,  276,
+      276,  276,    0,  273,  273,  273,  273,  273
+    } ;
+
+static yyconst short int yy_nxt[383] =
+    {   0,
+        6,    7,    8,    8,    7,    9,   10,   11,   12,   12,
+       12,   13,   14,   15,   16,   17,   12,   12,   18,   19,
+       12,   20,   21,   22,   23,    6,    6,   24,   12,   12,
+       12,   12,   12,   25,   12,   12,   26,   12,   12,   27,
+       12,   12,   28,   12,   29,   30,   12,   12,   12,   12,
+       31,   34,   34,   43,   35,   35,   38,   39,   40,   47,
+       53,   50,   55,   32,  272,   56,   44,   48,   51,   57,
+       52,   58,   65,  127,   36,   36,   80,   54,   81,   51,
+       38,   39,   38,   39,   87,  130,  136,  171,  155,  178,
+      159,  190,  127,  174,   88,  213,  175,  216,  196,  271,
+
+      261,  270,  258,  269,  130,  136,  155,  268,  267,  171,
+      190,  266,  174,  213,  214,  216,  261,  265,  175,  202,
+      203,  258,  264,  263,  204,  205,  262,  260,  259,  257,
+      206,  256,  255,  254,  253,  202,  203,  252,  251,  250,
+      249,  248,  204,  205,  247,  246,  245,  244,  243,  242,
+      241,  240,  206,   33,   33,   33,   37,   37,   37,   66,
+      239,   66,   68,  238,   68,  237,  236,  235,  234,  233,
+      232,  231,  230,  229,  228,  227,  226,  225,  224,  223,
+      222,  221,  220,  219,  218,  217,  215,  212,  211,  210,
+      209,  208,  207,  201,  200,  199,  198,  197,  196,  195,
+
+      194,  193,  192,  191,  189,  188,  187,  186,  185,  184,
+      183,  182,  181,  180,  179,  177,  176,  173,  172,  170,
+      169,  168,  167,  166,  165,  164,  163,  162,  161,  160,
+      159,  158,  157,  156,  154,  153,  152,  151,  150,  149,
+      148,  147,  146,  145,  144,  143,  142,  141,  140,  139,
+      138,  137,  135,  134,  133,  132,  131,  129,  128,  126,
+      125,  124,  123,  122,  121,  120,  119,  118,  117,  116,
+      115,  114,  113,  112,  111,  110,  109,  108,  107,  106,
+      105,  104,  103,  102,  101,  100,   99,   98,   67,   67,
+       67,   97,   96,   95,   94,   93,   92,   91,   90,   89,
+
+       86,   85,   84,   83,   82,   79,   78,   77,   76,   75,
+       74,   73,   72,   71,   70,   69,   67,   67,   64,   63,
+       51,   62,   61,   60,   59,   49,   46,   45,   42,   41,
+      273,    5,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273
+    } ;
+
+static yyconst short int yy_chk[383] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        2,    3,    4,   16,    3,    4,   10,   10,  276,   19,
+       22,   21,   23,    2,  271,   23,   16,   19,   21,   23,
+       21,   23,   32,  100,    3,    4,   50,   22,   50,   32,
+       37,   37,   39,   39,   56,  103,  111,  153,  134,  159,
+      159,  172,  100,  156,   56,  195,  156,  199,  196,  270,
+
+      254,  268,  251,  267,  103,  111,  134,  266,  263,  153,
+      172,  261,  156,  195,  196,  199,  254,  260,  156,  186,
+      186,  251,  259,  258,  186,  186,  255,  253,  252,  250,
+      186,  249,  248,  247,  246,  186,  186,  245,  244,  242,
+      241,  240,  186,  186,  239,  238,  237,  236,  234,  233,
+      232,  231,  186,  274,  274,  274,  275,  275,  275,  277,
+      230,  277,  278,  228,  278,  227,  226,  224,  222,  221,
+      220,  219,  218,  217,  216,  213,  212,  211,  209,  208,
+      206,  205,  204,  203,  202,  200,  197,  193,  192,  191,
+      190,  189,  188,  183,  182,  181,  180,  179,  178,  177,
+
+      176,  175,  174,  173,  171,  170,  169,  168,  167,  166,
+      165,  163,  162,  161,  160,  158,  157,  155,  154,  152,
+      151,  149,  148,  146,  145,  144,  143,  141,  140,  139,
+      138,  137,  136,  135,  133,  132,  131,  130,  129,  128,
+      127,  126,  124,  123,  121,  120,  118,  117,  116,  115,
+      114,  113,  110,  109,  108,  107,  104,  102,  101,   99,
+       98,   97,   96,   95,   94,   93,   92,   91,   90,   89,
+       88,   87,   86,   85,   84,   83,   82,   81,   80,   79,
+       78,   77,   76,   75,   74,   73,   72,   71,   68,   67,
+       66,   65,   64,   63,   62,   61,   60,   59,   58,   57,
+
+       55,   54,   53,   52,   51,   49,   48,   47,   46,   45,
+       44,   43,   42,   41,   36,   35,   34,   33,   31,   30,
+       29,   28,   27,   26,   25,   20,   18,   17,   15,   14,
+        5,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273,  273,  273,  273,  273,  273,  273,  273,  273,
+      273,  273
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "zparse.fl"
+#define INITIAL 0
+/* This is the lexical analyser - it returns tokens to the parsers.
+   This is used by both parsers - one for the build-time config files
+   and one for the run-time config files. Note the included file "parse.h"
+   is a symlink to the relevant header file. */
+#line 7 "zparse.fl"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*#define YYSTYPE char * - YYSTYPE is defined in zparse.tab.h */
+#include "zparse.tab.h"
+
+/* forward declarations for std functions not picked up. */
+/*int fileno(FILE *);*/
+/*char *strdup(char *);*/
+
+int lineno=1;
+#define STRINGCOND 1
+
+#line 576 "zparse.yy.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( yy_current_buffer->yy_is_interactive ) \
+		{ \
+		int c = '*', n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+		  && ferror( yyin ) ) \
+		YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	if ( yyleng > 0 ) \
+		yy_current_buffer->yy_at_bol = \
+				(yytext[yyleng - 1] == '\n'); \
+	YY_USER_ACTION
+
+YY_DECL
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp = NULL, *yy_bp = NULL;
+	register int yy_act;
+
+#line 26 "zparse.fl"
+
+	char *string;
+	int string_len;
+
+#line 735 "zparse.yy.c"
+
+	if ( yy_init )
+		{
+		yy_init = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yy_start )
+			yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! yy_current_buffer )
+			yy_current_buffer =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+
+		yy_load_buffer_state();
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yy_start;
+		yy_current_state += YY_AT_BOL();
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yy_last_accepting_state = yy_current_state;
+				yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 274 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 332 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+
+do_action:	/* This label is used only to access EOF actions. */
+
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yy_hold_char;
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 30 "zparse.fl"
+lineno++;/*yylval.string=yytext;*/yyleng=0;
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 31 "zparse.fl"
+return (',');
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 32 "zparse.fl"
+return (':');
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 33 "zparse.fl"
+return ('_');
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 34 "zparse.fl"
+yylval.string=yytext;return(GSDLVERSION);
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 35 "zparse.fl"
+return (GENERAL_SECTION);
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 36 "zparse.fl"
+return (SEARCH_SECTION);
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 37 "zparse.fl"
+return (BROWSE_SECTION);
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 38 "zparse.fl"
+return (MACROS_SECTION);
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 39 "zparse.fl"
+return (MAINTAINER);
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 40 "zparse.fl"
+return(LANGUAGES);
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 41 "zparse.fl"
+return(ABOUT);
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 42 "zparse.fl"
+return(BROWSE);
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 43 "zparse.fl"
+return(DATELIST);
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 44 "zparse.fl"
+return(DOCUMENTARROWSBOTTOM);
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 45 "zparse.fl"
+return(DOCUMENTBUTTONS);
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 46 "zparse.fl"
+return(DOCUMENTHEADER);
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 47 "zparse.fl"
+return(DOCUMENTIMAGES);
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 48 "zparse.fl"
+return(DOCUMENTTEXT);
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 49 "zparse.fl"
+return(ICONLINK);
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 50 "zparse.fl"
+return(LIST);
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 51 "zparse.fl"
+return(FORMAT);
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 52 "zparse.fl"
+return(NAME);
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 53 "zparse.fl"
+return(SEARCHTEXT);
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 54 "zparse.fl"
+return(SECTIONLIST);
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 55 "zparse.fl"
+return(SORTEDLIST);
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 56 "zparse.fl"
+return(SORTEDSECTIONLIST);
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 57 "zparse.fl"
+return(TEXTLINK);
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 58 "zparse.fl"
+return(TYPE);
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 59 "zparse.fl"
+return (PUBLIC);
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 60 "zparse.fl"
+return (TRUE);
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 61 "zparse.fl"
+return (FALSE);
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 62 "zparse.fl"
+return (DOCUMENT);
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 63 "zparse.fl"
+return (SECTION);
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 64 "zparse.fl"
+return (ICON);
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 65 "zparse.fl"
+return (SMALLICON);
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 66 "zparse.fl"
+{string=NULL;BEGIN(STRINGCOND);}
+	YY_BREAK
+
+case 38:
+YY_RULE_SETUP
+#line 68 "zparse.fl"
+{/* append this to our current string EXC NEWLINE */
+			if (string==NULL) string_len=0; else
+			string_len=strlen(string);
+			string=realloc(string,string_len+strlen(yytext)+1);
+			/* +1 is for trailing \0 */
+			strcpy(string+string_len,yytext);
+			}
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 75 "zparse.fl"
+{/* append this to our current string - INC NEWLINE */
+			lineno++;
+			if (string==NULL) string_len=0; else
+			string_len=strlen(string);
+			string=realloc(string,string_len+strlen(yytext)+1);
+			/* +1 is for trailing \0 */
+			strcpy(string+string_len,yytext);
+			}
+	YY_BREAK
+case 40:
+#line 84 "zparse.fl"
+case 41:
+YY_RULE_SETUP
+#line 84 "zparse.fl"
+{       /*replace quoted quote with one quote*/
+			string_len=strlen(string);
+			string=realloc(string,string_len+2);
+			string[string_len]='\"';
+			string[string_len+1]='\0';
+			}
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 90 "zparse.fl"
+{
+			BEGIN(INITIAL);
+			yylval.string=string;
+			return(STRING);
+			  /* note that the string may have long bits of 
+			     whitespace in it that we could remove. */
+			}
+	YY_BREAK
+
+case 43:
+YY_RULE_SETUP
+#line 98 "zparse.fl"
+yylval.string=strdup(yytext);return (DATA);
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 99 "zparse.fl"
+;
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 100 "zparse.fl"
+lineno++;
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 101 "zparse.fl"
+ECHO;
+	YY_BREAK
+#line 1072 "zparse.yy.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(STRINGCOND):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between yy_current_buffer and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yy_n_chars = yy_current_buffer->yy_n_chars;
+			yy_current_buffer->yy_input_file = yyin;
+			yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state();
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer() )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap() )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yy_c_buf_p =
+					yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yy_c_buf_p =
+				&yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+	{
+	register char *dest = yy_current_buffer->yy_ch_buf;
+	register char *source = yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( yy_current_buffer->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+	else
+		{
+		int num_to_read =
+			yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = yy_current_buffer;
+
+			int yy_c_buf_p_offset =
+				(int) (yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yy_flex_realloc( (void *) b->yy_ch_buf,
+							 b->yy_buf_size + 2 );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = yy_current_buffer->yy_buf_size -
+						number_to_move - 1;
+#endif
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+			yy_n_chars, num_to_read );
+
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	if ( yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			yy_current_buffer->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	yy_n_chars += number_to_move;
+	yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+	return ret_val;
+	}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = yy_start;
+	yy_current_state += YY_AT_BOL();
+
+	for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yy_last_accepting_state = yy_current_state;
+			yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 274 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+	}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+	{
+	register int yy_is_jam;
+	register char *yy_cp = yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yy_last_accepting_state = yy_current_state;
+		yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 274 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 273);
+
+	return yy_is_jam ? 0 : yy_current_state;
+	}
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+	{
+	register char *yy_cp = yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yy_hold_char;
+
+	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = yy_n_chars + 2;
+		register char *dest = &yy_current_buffer->yy_ch_buf[
+					yy_current_buffer->yy_buf_size + 2];
+		register char *source =
+				&yy_current_buffer->yy_ch_buf[number_to_move];
+
+		while ( source > yy_current_buffer->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		yy_current_buffer->yy_n_chars =
+			yy_n_chars = yy_current_buffer->yy_buf_size;
+
+		if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+
+	yytext_ptr = yy_bp;
+	yy_hold_char = *yy_cp;
+	yy_c_buf_p = yy_cp;
+	}
+#endif	/* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+	{
+	int c;
+
+	*yy_c_buf_p = yy_hold_char;
+
+	if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			/* This was really a NUL. */
+			*yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yy_c_buf_p - yytext_ptr;
+			++yy_c_buf_p;
+
+			switch ( yy_get_next_buffer() )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/* fall through */
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap() )
+						return EOF;
+
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yy_c_buf_p = yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yy_c_buf_p;	/* cast for 8-bit char's */
+	*yy_c_buf_p = '\0';	/* preserve yytext */
+	yy_hold_char = *++yy_c_buf_p;
+
+	yy_current_buffer->yy_at_bol = (c == '\n');
+
+	return c;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+	{
+	if ( ! yy_current_buffer )
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+	yy_init_buffer( yy_current_buffer, input_file );
+	yy_load_buffer_state();
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+	{
+	if ( yy_current_buffer == new_buffer )
+		return;
+
+	if ( yy_current_buffer )
+		{
+		/* Flush out information for old buffer. */
+		*yy_c_buf_p = yy_hold_char;
+		yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	yy_current_buffer = new_buffer;
+	yy_load_buffer_state();
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yy_did_buffer_switch_on_eof = 1;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+	{
+	yy_n_chars = yy_current_buffer->yy_n_chars;
+	yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+	yyin = yy_current_buffer->yy_input_file;
+	yy_hold_char = *yy_c_buf_p;
+	}
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+	{
+	if ( ! b )
+		return;
+
+	if ( b == yy_current_buffer )
+		yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yy_flex_free( (void *) b->yy_ch_buf );
+
+	yy_flex_free( (void *) b );
+	}
+
+
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+	{
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+	b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+	b->yy_is_interactive = 0;
+#else
+	b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+	{
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == yy_current_buffer )
+		yy_load_buffer_state();
+	}
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b );
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+	{
+	int len;
+	for ( len = 0; yy_str[len]; ++len )
+		;
+
+	return yy_scan_bytes( yy_str, len );
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+	{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) yy_flex_alloc( n );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+	{
+	if ( yy_start_stack_ptr >= yy_start_stack_depth )
+		{
+		yy_size_t new_size;
+
+		yy_start_stack_depth += YY_START_STACK_INCR;
+		new_size = yy_start_stack_depth * sizeof( int );
+
+		if ( ! yy_start_stack )
+			yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+		else
+			yy_start_stack = (int *) yy_flex_realloc(
+					(void *) yy_start_stack, new_size );
+
+		if ( ! yy_start_stack )
+			YY_FATAL_ERROR(
+			"out of memory expanding start-condition stack" );
+		}
+
+	yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+	BEGIN(new_state);
+	}
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+	{
+	if ( --yy_start_stack_ptr < 0 )
+		YY_FATAL_ERROR( "start-condition stack underflow" );
+
+	BEGIN(yy_start_stack[yy_start_stack_ptr]);
+	}
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+	{
+	return yy_start_stack[yy_start_stack_ptr - 1];
+	}
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+	{
+	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+	}
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		yytext[yyleng] = yy_hold_char; \
+		yy_c_buf_p = yytext + n; \
+		yy_hold_char = *yy_c_buf_p; \
+		*yy_c_buf_p = '\0'; \
+		yyleng = n; \
+		} \
+	while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+	{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+	}
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+	{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+	}
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+	{
+	return (void *) malloc( size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+	{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+	{
+	free( ptr );
+	}
+
+#if YY_MAIN
+int main()
+	{
+	yylex();
+	return 0;
+	}
+#endif
+#line 101 "zparse.fl"
+
+/******** deleted rules: *******
+en			return(EN);
+fr			return(FR);
+mi			return(MI);
+zh			return(ZH);
+de			return(DE);
+***************/
+
+
+
+
